From f4f0ebda4107ad68bc9c6d0616599d2e4ff8edc6 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 14 Nov 2024 00:49:17 +0000 Subject: [PATCH 0001/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 39ca48c43fae..94ddf666f2df 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -130,12 +130,6 @@

MistakeNot4892 updated:

  • Bookcases are a bit fancier.
  • Many more intolerances have been added to character traits.
  • - -

    12 September 2024

    -

    MistakeNot4892 updated:

    - From 08422c30810f0cf31079191927e8e6805c5a5c1d Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 16 Nov 2024 00:51:25 +0000 Subject: [PATCH 0002/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 94ddf666f2df..5316ba93c902 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -119,17 +119,6 @@

    MistakeNot4892 updated:

    - -

    14 September 2024

    -

    Atermonera updated:

    - -

    MistakeNot4892 updated:

    - From 25d3e0684d060193d6ec860ff6dbea94852ce63d Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 14 Nov 2024 23:11:07 +1100 Subject: [PATCH 0003/1331] Adding Doe's new barrel icons and implementing stackable casks. --- code/__defines/inventory_sizes.dm | 19 ++- code/_onclick/adjacent.dm | 4 +- .../structures/{ => barrels}/barrel.dm | 31 +++- code/game/objects/structures/barrels/cask.dm | 42 +++++ .../objects/structures/barrels/cask_rack.dm | 156 ++++++++++++++++++ .../crafting/stack_recipes/recipes_planks.dm | 10 ++ icons/obj/structures/barrel.dmi | Bin 589 -> 0 bytes icons/obj/structures/barrels/barrel.dmi | Bin 0 -> 833 bytes icons/obj/structures/barrels/cask.dmi | Bin 0 -> 490 bytes icons/obj/structures/barrels/cask_rack.dmi | Bin 0 -> 304 bytes .../structures/barrels/cask_rack_large.dmi | Bin 0 -> 316 bytes maps/shaded_hills/shaded_hills-inn.dmm | 16 +- nebula.dme | 4 +- 13 files changed, 254 insertions(+), 28 deletions(-) rename code/game/objects/structures/{ => barrels}/barrel.dm (74%) create mode 100644 code/game/objects/structures/barrels/cask.dm create mode 100644 code/game/objects/structures/barrels/cask_rack.dm delete mode 100644 icons/obj/structures/barrel.dmi create mode 100644 icons/obj/structures/barrels/barrel.dmi create mode 100644 icons/obj/structures/barrels/cask.dmi create mode 100644 icons/obj/structures/barrels/cask_rack.dmi create mode 100644 icons/obj/structures/barrels/cask_rack_large.dmi diff --git a/code/__defines/inventory_sizes.dm b/code/__defines/inventory_sizes.dm index 39842fd90992..0432f304bd8f 100644 --- a/code/__defines/inventory_sizes.dm +++ b/code/__defines/inventory_sizes.dm @@ -16,16 +16,17 @@ 20 - things that take up an entire turf, like wall girders or door assemblies */ -#define ITEM_SIZE_TINY 1 -#define ITEM_SIZE_SMALL 2 -#define ITEM_SIZE_NORMAL 3 -#define ITEM_SIZE_LARGE 4 -#define ITEM_SIZE_HUGE 5 -#define ITEM_SIZE_GARGANTUAN 6 -#define ITEM_SIZE_STRUCTURE 20 +#define ITEM_SIZE_TINY 1 +#define ITEM_SIZE_SMALL 2 +#define ITEM_SIZE_NORMAL 3 +#define ITEM_SIZE_LARGE 4 +#define ITEM_SIZE_HUGE 5 +#define ITEM_SIZE_GARGANTUAN 6 +#define ITEM_SIZE_STRUCTURE 20 +#define ITEM_SIZE_LARGE_STRUCTURE 30 -#define ITEM_SIZE_MIN ITEM_SIZE_TINY -#define ITEM_SIZE_MAX ITEM_SIZE_STRUCTURE +#define ITEM_SIZE_MIN ITEM_SIZE_TINY +#define ITEM_SIZE_MAX ITEM_SIZE_LARGE_STRUCTURE #define BASE_STORAGE_COST(w_class) (2**(w_class-1)) //1,2,4,8,16,... diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index 70e40cb6eb36..328becddbbfe 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -103,7 +103,7 @@ Quick adjacency (to turf): return TRUE if(istype(loc, /obj/item) || istype(loc, /obj/structure)) if(recurse > 0) - return loc.Adjacent(neighbor,recurse - 1) + return loc.Adjacent(neighbor, recurse - 1) return FALSE return ..() @@ -112,7 +112,7 @@ Quick adjacency (to turf): return TRUE if(istype(loc, /obj/item) || istype(loc, /obj/structure)) if(recurse > 0) - return loc.Adjacent(neighbor,recurse - 1) + return loc.Adjacent(neighbor, recurse - 1) return FALSE return ..() diff --git a/code/game/objects/structures/barrel.dm b/code/game/objects/structures/barrels/barrel.dm similarity index 74% rename from code/game/objects/structures/barrel.dm rename to code/game/objects/structures/barrels/barrel.dm index 20d0c9d6eb23..c8668066624e 100644 --- a/code/game/objects/structures/barrel.dm +++ b/code/game/objects/structures/barrels/barrel.dm @@ -1,7 +1,7 @@ /obj/structure/reagent_dispensers/barrel name = "barrel" desc = "A stout barrel for storing large amounts of liquids or substances." - icon = 'icons/obj/structures/barrel.dmi' + icon = 'icons/obj/structures/barrels/barrel.dmi' icon_state = ICON_STATE_WORLD anchored = TRUE atom_flags = ATOM_FLAG_CLIMBABLE @@ -16,10 +16,19 @@ volume = 7500 movable_flags = MOVABLE_FLAG_WHEELED throwpass = TRUE + // Should we draw our lid and liquid contents as overlays? + var/show_liquid_contents = TRUE + // Rivets, bands, etc. Currently just cosmetic. + var/decl/material/metal_material = /decl/material/solid/metal/iron /obj/structure/reagent_dispensers/barrel/Initialize() - ..() - return INITIALIZE_HINT_LATELOAD + if(ispath(metal_material)) + metal_material = GET_DECL(metal_material) + if(!istype(metal_material)) + metal_material = null + . = ..() + if(. == INITIALIZE_HINT_NORMAL && storage) + return INITIALIZE_HINT_LATELOAD // we want to grab our turf contents. /obj/structure/reagent_dispensers/barrel/attackby(obj/item/W, mob/user) . = ..() @@ -48,15 +57,29 @@ update_icon() /obj/structure/reagent_dispensers/barrel/on_update_icon() + . = ..() - if(ATOM_IS_OPEN_CONTAINER(src)) + + // Layer below lid/lid metal. + if(metal_material) + add_overlay(overlay_image(icon, "[icon_state]-metal", metal_material.color, RESET_COLOR)) + + // Add lid/reagents overlay/lid metal. + if(show_liquid_contents && ATOM_IS_OPEN_CONTAINER(src)) if(reagents) var/overlay_amount = NONUNIT_CEILING(reagents.total_liquid_volume / reagents.maximum_volume * 100, 10) var/image/filling_overlay = overlay_image(icon, "[icon_state]-[overlay_amount]", reagents.get_color(), RESET_COLOR | RESET_ALPHA) add_overlay(filling_overlay) add_overlay(overlay_image(icon, "[icon_state]-lidopen", material.color, RESET_COLOR)) + if(metal_material) + add_overlay(overlay_image(icon, "[icon_state]-lidopen-metal", metal_material.color, RESET_COLOR)) else add_overlay(overlay_image(icon, "[icon_state]-lidclosed", material.color, RESET_COLOR)) + if(metal_material) + add_overlay(overlay_image(icon, "[icon_state]-lidclosed-metal", metal_material.color, RESET_COLOR)) + + if(istype(loc, /obj/structure/cask_rack)) + loc.update_icon() /obj/structure/reagent_dispensers/barrel/ebony material = /decl/material/solid/organic/wood/ebony diff --git a/code/game/objects/structures/barrels/cask.dm b/code/game/objects/structures/barrels/cask.dm new file mode 100644 index 000000000000..04dbb4ca1ba3 --- /dev/null +++ b/code/game/objects/structures/barrels/cask.dm @@ -0,0 +1,42 @@ +/obj/structure/reagent_dispensers/barrel/cask + name = "cask" + desc = "A small barrel used to store moderate amounts of liquids or substances." + icon = 'icons/obj/structures/barrels/cask.dmi' + anchored = FALSE + show_liquid_contents = FALSE + storage = null // Intended for storing liquids. + +// Horrible workaround for physical interaction checks. +/obj/structure/reagent_dispensers/barrel/cask/nano_host() + return istype(loc, /obj/structure/cask_rack) ? loc : src + +/obj/structure/reagent_dispensers/barrel/cask/receive_mouse_drop(atom/dropping, mob/user, params) + if(istype(loc, /obj/structure/cask_rack)) + return loc.receive_mouse_drop(dropping, user, params) + return ..() + +/obj/structure/reagent_dispensers/barrel/cask/handle_mouse_drop(atom/over, mob/user, params) + var/obj/structure/cask_rack/rack = loc + if(istype(rack) && isturf(over) && user.Adjacent(over) && rack.Adjacent(over) && rack.try_unstack_barrel(src, over, user)) + return + return ..() + +/obj/structure/reagent_dispensers/barrel/cask/ebony + material = /decl/material/solid/organic/wood/ebony + color = /decl/material/solid/organic/wood/ebony::color + +/obj/structure/reagent_dispensers/barrel/cask/ebony/water/populate_reagents() + . = ..() + add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + +/obj/structure/reagent_dispensers/barrel/cask/ebony/beer/populate_reagents() + . = ..() + add_to_reagents(/decl/material/liquid/ethanol/beer, reagents.maximum_volume) + +/obj/structure/reagent_dispensers/barrel/cask/ebony/wine/populate_reagents() + . = ..() + add_to_reagents(/decl/material/liquid/ethanol/wine, reagents.maximum_volume) + +/obj/structure/reagent_dispensers/barrel/cask/ebony/oil/populate_reagents() + . = ..() + add_to_reagents(/decl/material/liquid/nutriment/plant_oil, reagents.maximum_volume) diff --git a/code/game/objects/structures/barrels/cask_rack.dm b/code/game/objects/structures/barrels/cask_rack.dm new file mode 100644 index 000000000000..f284886622f8 --- /dev/null +++ b/code/game/objects/structures/barrels/cask_rack.dm @@ -0,0 +1,156 @@ +/obj/structure/cask_rack + name = "cask rack" + desc = "A flat rack used to stop a cask from rolling around." + icon = 'icons/obj/structures/barrels/cask_rack.dmi' + icon_state = ICON_STATE_WORLD + anchored = TRUE + opacity = FALSE + density = FALSE // Recalculated when barrels added or removed + w_class = ITEM_SIZE_STRUCTURE + material = /decl/material/solid/organic/wood + color = /decl/material/solid/organic/wood::color + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC + var/max_stack = 1 + +/obj/structure/cask_rack/Initialize(ml, _mat, _reinf_mat) + . = ..() + if(isturf(loc)) + for(var/atom/movable/stackable in loc) + if(try_stack_barrel(stackable) && length(contents) >= max_stack) + return + +/obj/structure/cask_rack/examine(mob/user, distance, infix, suffix) + . = ..() + if(length(contents)) + to_chat(user, SPAN_NOTICE("It contains [english_list(contents)].")) + +/obj/structure/cask_rack/handle_mouse_drop(atom/over, mob/user, params) + if(isturf(over) && user.Adjacent(over) && Adjacent(over) && try_unstack_barrel(target = over, user = user)) + return + return ..() + +/obj/structure/cask_rack/receive_mouse_drop(atom/dropping, mob/user, params) + . = ..() + if(!. && user.Adjacent(src) && dropping.Adjacent(src) && user.Adjacent(dropping)) + return try_stack_barrel(dropping, user) + +/obj/structure/cask_rack/on_update_icon() + . = ..() + if(length(contents)) + // Workaround for base color getting applied to vis_contents. + var/base_color = get_color() + color = null + var/image/I = image(icon, icon_state) + I.color = base_color + add_overlay(I) + // Reposition/update our contents. + var/i = 0 + var/list/stackable_types = get_stackable_barrel_types() + for(var/atom/movable/barrel in contents) + if(is_type_in_list(barrel, stackable_types)) + i++ + adjust_barrel_offsets(barrel, i) + else + color = get_color() + compile_overlays() // Avoid wonky flickering on contents changes + +/obj/structure/cask_rack/proc/adjust_barrel_offsets(atom/movable/barrel, barrel_position) + barrel.reset_offsets(anim_time = 0) + barrel.vis_flags |= (VIS_INHERIT_LAYER | VIS_INHERIT_PLANE) + +/obj/structure/cask_rack/Entered(atom/movable/AM, atom/old_loc) + . = ..() + if(istype(AM) && !QDELETED(AM) && is_type_in_list(AM, get_stackable_barrel_types())) + vis_contents |= AM + recalculate_barrel_values() + +/obj/structure/cask_rack/Exited(atom/movable/AM, atom/new_loc) + . = ..() + if(istype(AM) && is_type_in_list(AM, get_stackable_barrel_types())) + vis_contents -= AM + AM.vis_flags = initial(AM.vis_flags) + AM.reset_offsets(anim_time = 0) + recalculate_barrel_values() + +/obj/structure/cask_rack/proc/recalculate_barrel_values() + if(length(contents)) + density = TRUE + anchored = TRUE + obj_flags &= ~OBJ_FLAG_ANCHORABLE + atom_flags |= ATOM_FLAG_CLIMBABLE + else + density = FALSE + obj_flags |= OBJ_FLAG_ANCHORABLE + atom_flags &= ~ATOM_FLAG_CLIMBABLE + update_icon() + +/obj/structure/cask_rack/proc/try_unstack_barrel(atom/movable/barrel, turf/target, mob/user) + if(!loc) + return FALSE + if(!barrel) + if(!length(contents)) + to_chat(user, SPAN_WARNING("\The [src] has nothing stacked on it.")) + return FALSE + barrel = contents[length(contents)] + if(!istype(barrel) || !barrel.simulated) + return FALSE + if(target && (!isturf(target) || !loc.Adjacent(target))) // TODO: Enter() or CanPass() checks instead of relying on step_towards() below. + to_chat(user, SPAN_NOTICE("You cannot move \the [barrel] to \the [target].")) + return FALSE + to_chat(user, SPAN_NOTICE("You move \the [barrel] off \the [src].")) + barrel.dropInto(loc) + if(target) + step_towards(barrel, target) + return TRUE + +/obj/structure/cask_rack/proc/try_stack_barrel(atom/movable/barrel, mob/user) + if(!istype(barrel) || !barrel.simulated || barrel.anchored) + return FALSE + if(length(contents) >= max_stack) + to_chat(user, SPAN_WARNING("\The [src] is already stacked to capacity.")) + return FALSE + var/list/stackable_types = get_stackable_barrel_types() + if(!is_type_in_list(barrel, stackable_types)) + to_chat(user, SPAN_WARNING("\The [src] is cannot hold \the [barrel].")) + return FALSE + barrel.forceMove(src) + to_chat(user, SPAN_NOTICE("You stack \the [barrel] onto \the [src].")) + return TRUE + +/obj/structure/cask_rack/proc/get_stackable_barrel_types() + var/static/list/_stackable_barrel_types = list( + /obj/structure/reagent_dispensers/barrel/cask + ) + return _stackable_barrel_types + +// A larger stack, used to arrange up to three casks. +/obj/structure/cask_rack/large + desc = "A flat rack used to stop casks from rolling around." + max_stack = 3 + w_class = ITEM_SIZE_LARGE_STRUCTURE + icon = 'icons/obj/structures/barrels/cask_rack_large.dmi' + +// We want 'large wooden cask rack' not 'wooden large cask rack' +/obj/structure/cask_rack/large/update_material_name(override_name) + . = ..() + SetName("large [name]") + +/obj/structure/cask_rack/large/adjust_barrel_offsets(atom/movable/barrel, barrel_position) + ..() + switch(barrel_position) + if(1) + barrel.pixel_x -= 7 + if(2) + barrel.pixel_x += 7 + if(3) + barrel.pixel_y += 8 + +/obj/structure/cask_rack/large/mapped + material = /decl/material/solid/organic/wood/ebony + color = /decl/material/solid/organic/wood/ebony::color + +/obj/structure/cask_rack/large/mapped/Initialize(ml, _mat, _reinf_mat) + new /obj/structure/reagent_dispensers/barrel/cask/ebony/water(src) + new /obj/structure/reagent_dispensers/barrel/cask/ebony/beer(src) + new /obj/structure/reagent_dispensers/barrel/cask/ebony/wine(src) + . = ..() diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index c3dd35a7e40a..f8a750754998 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -204,6 +204,16 @@ result_type = /obj/structure/reagent_dispensers/barrel difficulty = MAT_VALUE_HARD_DIY +/decl/stack_recipe/planks/furniture/barrel/cask + result_type = /obj/structure/reagent_dispensers/barrel/cask + +/decl/stack_recipe/planks/furniture/barrel/cask_rack + result_type = /obj/structure/cask_rack + +/decl/stack_recipe/planks/furniture/barrel/large_cask_rack + name = "cask rack, large" + result_type = /obj/structure/cask_rack/large + /decl/stack_recipe/planks/furniture/table_frame result_type = /obj/structure/table/frame category = "furniture" diff --git a/icons/obj/structures/barrel.dmi b/icons/obj/structures/barrel.dmi deleted file mode 100644 index 4adfb3e502e217a7a8f281384003369001913a09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 589 zcmV-T0D@x2wg|Jao>E>jn2iT^P2zw0ZK_kK~z|U?Uhk(!ypWX zK~>+IR5?IEes6&tU`h_Kxbpq4GMuzcB;fKbDOfTjo_1Q@3Y>%+Ju zm<7Z+013e*ou>~%P8#TDXpndUVOaR@FaGoT$s3@ryQOg)F^h!2;d#3XjSn2bhgJy4 zZP%?%UCg9T2mnwK#MEax5y2qR_E|R0bA%vrFi47^zLiZz7!-l^A6%tNkO!x2$+|cq zYoZI#C(e1*8U%`fIP2hKYoJzE(~PY_qDc85G=ZJERa$j{@=xzkX$=+oM diff --git a/icons/obj/structures/barrels/barrel.dmi b/icons/obj/structures/barrels/barrel.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1fec40d535cbfcab80b2ee5d4199bf9c73e15acf GIT binary patch literal 833 zcmV-H1HSx;P)-_`txxI9)0A00001bW%=J06^y0W&i*Ho_bVRbVOxyV{&P5 zbZKvH004NLQ&wi?^C?`dUi!&v&s2IpMPfQ2(7=F5m6pURv>;ZYIk5`Oa4J+*aP@NmCky}y8=bGwk{mVw z00J#ZL_t(oh3%HhvZEjnhCx$jt>6Pl%j_ePeFQ4|nVPeo#i{fDk1(ha8>Tc?&1NqA z3(1$HyXod)6Oy%xL3PNl1X`>1fM3S947jNf6H=PK00LFfPM`Sww1ZySz8;*ou3ubc zPQz5YY-+HB5e$X2LmHn-Rqe65eFY}o)s-BGjY90Papv}}oJoU5Y80qFHrhC6oLU{E z<1E>1%QSPfu^c7`RtH{&3;sXt_}I0WD;n5XrJWi=3Wc4XrNI*@tQ|#w)gBX(`SFM$FH^Y2h}dW z7WMtX5ocv(Wo2dkZ948iPS_uJASd+ryhj|r|9^jmSy@?G|Al@6waZ@Lf9%n;00000 LNkvXXu0mjfXY_zv literal 0 HcmV?d00001 diff --git a/icons/obj/structures/barrels/cask.dmi b/icons/obj/structures/barrels/cask.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a2eba5fa0d9bce1911452b71973895bb77267f8a GIT binary patch literal 490 zcmVo=HbsWny;!|NjUmiwXb$00DGTPE!Ct=GbNc0047(R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq5uYmL%rjQmd@s>gNJ>0stRoCpjlI zL(u>L0S8G$K~y-6)sandgdh-w+mQoI)4ES92aulwh|C32x&K`P8lg>UCX1}nRyBOo z>xPKHw};DIYEZ<23~#P=%!+Y40eBP=#9rFfJi&p=MAnLB9!$IZe|v zrz+^v*}iIXh^Srl-1fiO7_7|>Y?sPQ>qjQS7^$U71aa*}N@b_Gad^rA5AS?fpL4Ew g#UBjaFZ_r41jdslL4u`Ex&QzG07*qoM6N<$f<_q9jsO4v literal 0 HcmV?d00001 diff --git a/icons/obj/structures/barrels/cask_rack.dmi b/icons/obj/structures/barrels/cask_rack.dmi new file mode 100644 index 0000000000000000000000000000000000000000..2ee0ca5985bab0c3f5c921e0bea5939db0b7dbac GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*c)G`Qb>knv?>FNHaeCLNg+q7b~&X1|8IYiGBy z>gE*^C(NHb^3nD>L7%p=CqF|j&6Rv-etiEDpe+oZu6{1-oD!ML*c)G`Qb>knv?>FNHaeCLNg+q7b~&X1|8IYiGBy z>gE*^C(NHb^3nD>)L zE4URb7*iQMZ`PYduduE-bo_PeACU*=)i*OsORB{5&aB*WfLU;An7FZm_Flu8ybnIK z88dG^Db=lS%JF8gUXta;wM{LXFWWC>=XC8_HEq`fmTK#_`|j{B*rRxwoja!-Xd{EC LtDnm{r-UW|0NZ$a literal 0 HcmV?d00001 diff --git a/maps/shaded_hills/shaded_hills-inn.dmm b/maps/shaded_hills/shaded_hills-inn.dmm index 0f8e896ad49c..8b73c5e461e6 100644 --- a/maps/shaded_hills/shaded_hills-inn.dmm +++ b/maps/shaded_hills/shaded_hills-inn.dmm @@ -61,10 +61,6 @@ unique_merge_identifier = "outer wall" }, /area/shaded_hills/outside/downlands) -"cy" = ( -/obj/structure/reagent_dispensers/barrel/ebony/water, -/turf/floor/wood/walnut, -/area/shaded_hills/shrine/kitchen) "cT" = ( /obj/structure/railing/mapped/wooden/walnut{ dir = 1 @@ -149,7 +145,7 @@ /turf/wall/brick/basalt/shutter, /area/shaded_hills/slaughterhouse) "fK" = ( -/obj/structure/reagent_dispensers/barrel/ebony/water, +/obj/structure/cask_rack/large/mapped, /turf/floor/path/herringbone/basalt, /area/shaded_hills/inn/kitchen) "fR" = ( @@ -939,10 +935,6 @@ "By" = ( /turf/floor/grass, /area/shaded_hills/outside/shrine) -"BD" = ( -/obj/structure/reagent_dispensers/barrel/ebony/wine, -/turf/floor/wood/walnut, -/area/shaded_hills/shrine/kitchen) "BG" = ( /obj/structure/bed/chair/bench/ebony{ dir = 1 @@ -1647,7 +1639,7 @@ /turf/floor/path/herringbone/basalt, /area/shaded_hills/shrine) "VE" = ( -/obj/structure/reagent_dispensers/barrel/ebony/beer, +/obj/structure/cask_rack/large/mapped, /turf/floor/wood/walnut, /area/shaded_hills/shrine/kitchen) "VM" = ( @@ -20379,9 +20371,9 @@ iK kv By px -cy +ge VE -BD +ge Sy Aq QB diff --git a/nebula.dme b/nebula.dme index 8298d371b918..dd67ff9ef8e4 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1414,7 +1414,6 @@ #include "code\game\objects\structures\_structure_materials.dm" #include "code\game\objects\structures\ai_decoy.dm" #include "code\game\objects\structures\banners.dm" -#include "code\game\objects\structures\barrel.dm" #include "code\game\objects\structures\barricade.dm" #include "code\game\objects\structures\barsign.dm" #include "code\game\objects\structures\bedsheet_bin.dm" @@ -1488,6 +1487,9 @@ #include "code\game\objects\structures\windoor_assembly.dm" #include "code\game\objects\structures\window.dm" #include "code\game\objects\structures\window_spawner.dm" +#include "code\game\objects\structures\barrels\barrel.dm" +#include "code\game\objects\structures\barrels\cask.dm" +#include "code\game\objects\structures\barrels\cask_rack.dm" #include "code\game\objects\structures\chemistry\_chemistry.dm" #include "code\game\objects\structures\crates_lockers\crates.dm" #include "code\game\objects\structures\crates_lockers\largecrate.dm" From 0b9bb490e6059fd06492dd83b5ba8d475b5eed1b Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 18 Nov 2024 01:48:47 +1100 Subject: [PATCH 0004/1331] Automatic changelog generation for PR #4562 [ci skip] --- html/changelogs/AutoChangeLog-pr-4562.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4562.yml diff --git a/html/changelogs/AutoChangeLog-pr-4562.yml b/html/changelogs/AutoChangeLog-pr-4562.yml new file mode 100644 index 000000000000..bbad92aba175 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4562.yml @@ -0,0 +1,4 @@ +author: MistakeNot4892 +changes: + - {tweak: Swapped barrel icons out for Doe's much nicer barrels.} +delete-after: true From 1b632c9f6cdb875c51ef30b30283e23aeb16f2e0 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 18 Nov 2024 00:54:15 +0000 Subject: [PATCH 0005/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-4562.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4562.yml diff --git a/html/changelog.html b/html/changelog.html index 5316ba93c902..35c3254d6205 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    18 November 2024

    +

    MistakeNot4892 updated:

    +
      +
    • Swapped barrel icons out for Doe's much nicer barrels.
    • +
    +

    13 November 2024

    Penelope Haze updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 74940df8c734..ad59bb30dc1b 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14921,3 +14921,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: Crayons are now slowly used up while writing on paper, at a rate of one charge per 25 characters. (Crayons have a default of 30 charges.) - tweak: Added new furniture to the Shaded Hills inn. +2024-11-18: + MistakeNot4892: + - tweak: Swapped barrel icons out for Doe's much nicer barrels. diff --git a/html/changelogs/AutoChangeLog-pr-4562.yml b/html/changelogs/AutoChangeLog-pr-4562.yml deleted file mode 100644 index bbad92aba175..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4562.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: MistakeNot4892 -changes: - - {tweak: Swapped barrel icons out for Doe's much nicer barrels.} -delete-after: true From da7c7677d80ea7f5888928d52518df19e28e6bef Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 24 Nov 2024 12:51:46 +1100 Subject: [PATCH 0006/1331] Implementing Doe's new banners. --- code/game/objects/structures/banners.dm | 128 ++++++++++++++++-- .../stack_recipes/recipes_textiles.dm | 21 +++ icons/obj/banner.dmi | Bin 679 -> 0 bytes icons/obj/banner_woven.dmi | Bin 694 -> 0 bytes icons/obj/items/banners/banner.dmi | Bin 0 -> 704 bytes icons/obj/items/banners/banner_forked.dmi | Bin 0 -> 700 bytes icons/obj/items/banners/banner_pointed.dmi | Bin 0 -> 703 bytes icons/obj/items/banners/banner_rounded.dmi | Bin 0 -> 765 bytes icons/obj/items/banners/banner_square.dmi | Bin 0 -> 687 bytes icons/obj/items/banners/banner_symbols.dmi | Bin 0 -> 3245 bytes icons/obj/items/banners/banner_tasselled.dmi | Bin 0 -> 743 bytes icons/obj/items/banners/banner_woven.dmi | Bin 0 -> 728 bytes icons/obj/structures/banner_frame.dmi | Bin 483 -> 641 bytes 13 files changed, 137 insertions(+), 12 deletions(-) delete mode 100644 icons/obj/banner.dmi delete mode 100644 icons/obj/banner_woven.dmi create mode 100644 icons/obj/items/banners/banner.dmi create mode 100644 icons/obj/items/banners/banner_forked.dmi create mode 100644 icons/obj/items/banners/banner_pointed.dmi create mode 100644 icons/obj/items/banners/banner_rounded.dmi create mode 100644 icons/obj/items/banners/banner_square.dmi create mode 100644 icons/obj/items/banners/banner_symbols.dmi create mode 100644 icons/obj/items/banners/banner_tasselled.dmi create mode 100644 icons/obj/items/banners/banner_woven.dmi diff --git a/code/game/objects/structures/banners.dm b/code/game/objects/structures/banners.dm index 11f37de7bfd0..21f09be9f6e4 100644 --- a/code/game/objects/structures/banners.dm +++ b/code/game/objects/structures/banners.dm @@ -14,10 +14,14 @@ tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR max_health = 50 + var/force_south_facing = TRUE var/base_icon_state = "banner_stand" /// Reference to any banner currently hung on the frame. var/obj/item/banner/banner +/obj/structure/banner_frame/set_dir(ndir) + return ..(force_south_facing ? SOUTH : ndir) + /obj/structure/banner_frame/Initialize(ml, _mat, _reinf_mat) if(ispath(banner)) set_banner(new banner(src)) @@ -64,38 +68,116 @@ /obj/structure/banner_frame/on_update_icon() . = ..() + icon_state = base_icon_state - if(istype(banner)) - var/image/I = image(banner.icon, "banner_base") + if(!istype(banner)) + return + + var/image/I = image(banner.icon, "[banner.icon_state]-hanging") + I.appearance_flags |= RESET_COLOR + I.color = banner.color + add_overlay(I) + + if(banner.trim_color) + I = image(banner.icon, "[banner.icon_state]-trim") + I.appearance_flags |= RESET_COLOR + I.color = banner.trim_color + add_overlay(I) + + for(var/decal in banner.decals) + I = image(banner.decal_icon, decal) I.appearance_flags |= RESET_COLOR - I.color = banner.color + I.color = banner.decals[decal] add_overlay(I) - for(var/decal in banner.decals) - I = image(banner.icon, decal) - I.appearance_flags |= RESET_COLOR - I.color = banner.decals[decal] - add_overlay(I) /obj/structure/banner_frame/Destroy() if(istype(banner)) QDEL_NULL(banner) return ..() +var/global/list/_banner_decals = list() + /obj/item/banner name = "banner" desc = "A furled-up banner." - icon = 'icons/obj/banner.dmi' + icon = 'icons/obj/items/banners/banner.dmi' icon_state = "banner" material = /decl/material/solid/organic/cloth color = /decl/material/solid/organic/cloth::color max_health = 20 material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME w_class = ITEM_SIZE_NORMAL - var/hung_desc = "The banner is rather plain, with no markings." + var/hung_desc = "The banner is rather unremarkable." + var/name_prefix + var/decal_icon = 'icons/obj/items/banners/banner_symbols.dmi' var/list/decals + var/trim_color + +// TODO: PROPER EMBROIDERY AND ITEM DECORATION. +/obj/item/banner/attackby(obj/item/used_item, mob/user) + + if(istype(used_item, /obj/item/stack/material/thread)) + + if((!decal_icon || length(decals)) && trim_color) + to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) + return TRUE + + var/obj/item/stack/material/thread/used_stack = used_item + if(used_stack.get_amount() < 5) + to_chat(user, SPAN_WARNING("You need at least five lengths of thread to embroider a banner.")) + return TRUE + + if(!trim_color) + user.visible_message("\The [user] sews a trim onto \the [src].") + trim_color = used_item.color + used_stack.use(5) + return TRUE + + if(decal_icon && !length(decals)) + if(!global._banner_decals[decal_icon]) + global._banner_decals[decal_icon] = icon_states(decal_icon) + var/decal_color = used_item.color + var/decal_to_sew = input(user, "Which symbol do you wish to add to \the [src]?", "Banner Symbol") as null|anything in global._banner_decals[decal_icon] + if(decal_to_sew && CanPhysicallyInteract(user) && !length(decals) && user.get_active_held_item() == used_item && used_stack.use(5)) + decals[decal_to_sew] = decal_color + return TRUE + + . = ..() + +// 'woven grass banner', 'forked linen banner' +/obj/item/banner/update_name() + . = ..() + SetName("[name_prefix] [name]") + +/obj/item/banner/forked + name_prefix = "forked" + hung_desc = "The banner splits into two tails at the bottom." + icon = 'icons/obj/items/banners/banner_forked.dmi' + decal_icon = null // Current decals do not work nicely with the fork + +/obj/item/banner/pointed + name_prefix = "pointed" + hung_desc = "The banner narrows to a point at the bottom." + icon = 'icons/obj/items/banners/banner_pointed.dmi' + +/obj/item/banner/rounded + name_prefix = "rounded" + hung_desc = "The banner has a rounded lower edge." + icon = 'icons/obj/items/banners/banner_rounded.dmi' + +/obj/item/banner/square + name_prefix = "square" + hung_desc = "The banner has a squared-off lower edge." + icon = 'icons/obj/items/banners/banner_square.dmi' + +/obj/item/banner/tasselled + name_prefix = "tasselled" + hung_desc = "The banner has several dangling tassels at the bottom." + icon = 'icons/obj/items/banners/banner_tasselled.dmi' /obj/item/banner/woven - icon = 'icons/obj/banner_woven.dmi' + name_prefix = "woven" + icon = 'icons/obj/items/banners/banner_woven.dmi' material = /decl/material/solid/organic/plantmatter/grass/dry color = /decl/material/solid/organic/plantmatter/grass/dry::color material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC @@ -136,6 +218,7 @@ icon_state = "banner_hanging_preview" base_icon_state = "banner_hanging" directional_offset = @'{"NORTH":{"y":-32},"SOUTH":{"y":-32},"EAST":{"x":-32},"WEST":{"x":-32}}' + force_south_facing = FALSE /obj/structure/banner_frame/wall/ebony material = /decl/material/solid/organic/wood/ebony @@ -155,4 +238,25 @@ /obj/structure/banner_frame/wall/ebony/woven banner = /obj/item/banner/woven - color = /obj/item/banner/woven::color \ No newline at end of file + color = /obj/item/banner/woven::color + +// Debug item. +/obj/structure/banner_frame/random/Initialize(ml, _mat, _reinf_mat) + . = ..() + var/banner_type = pick(list( + /obj/item/banner/pointed, + /obj/item/banner/rounded, + /obj/item/banner/square, + /obj/item/banner/tasselled, + /obj/item/banner/woven + )) + banner = new banner_type(src) + banner.set_color(get_random_colour()) + banner.trim_color = get_random_colour() + if(banner.decal_icon) + if(!global._banner_decals[banner.decal_icon]) + global._banner_decals[banner.decal_icon] = icon_states(banner.decal_icon) + var/decal = pick(global._banner_decals[banner.decal_icon]) + var/decal_color = get_random_colour() + LAZYSET(banner.decals, decal, decal_color) + update_icon() diff --git a/code/modules/crafting/stack_recipes/recipes_textiles.dm b/code/modules/crafting/stack_recipes/recipes_textiles.dm index ee608b17555b..c43dffe7ce14 100644 --- a/code/modules/crafting/stack_recipes/recipes_textiles.dm +++ b/code/modules/crafting/stack_recipes/recipes_textiles.dm @@ -12,11 +12,32 @@ category = "clothing" /decl/stack_recipe/textiles/banner + name = "banner" result_type = /obj/item/banner category = "furniture" crafting_extra_cost_factor = 1.1 // less material is lost because it's relatively simple difficulty = MAT_VALUE_NORMAL_DIY // Slightly easier than making actual clothing. +/decl/stack_recipe/textiles/banner/forked + name = "banner, forked" + result_type = /obj/item/banner/forked + +/decl/stack_recipe/textiles/banner/pointed + name = "banner, pointed" + result_type = /obj/item/banner/pointed + +/decl/stack_recipe/textiles/banner/rounded + name = "banner, rounded" + result_type = /obj/item/banner/rounded + +/decl/stack_recipe/textiles/banner/square + name = "banner, square" + result_type = /obj/item/banner/square + +/decl/stack_recipe/textiles/banner/tasselled + name = "banner, tasselled" + result_type = /obj/item/banner/tasselled + /decl/stack_recipe/textiles/sack result_type = /obj/item/bag/sack category = "storage" diff --git a/icons/obj/banner.dmi b/icons/obj/banner.dmi deleted file mode 100644 index fdcb38c72c6f6cff486635460577960075421cb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 679 zcmV;Y0$BZtP)gwvz(b1lsp1Zrd-rnBm=;*7ftH8j(?d|QMp`lz{T(GdPzP`Ssq@=^c!vN;< z5dZ)H0d!JMQvg8b*k%9#0CRd&Sad{Xb7OL8aCB*JZU6vyoKseCa&`CgQ*iP1ZAnByR7i=vu8$wl1gwPljqk!1(b=Am^|U#Q3OK$(PjyWo(-nt*X7h<1jTrg?1|tsw{=$bg|xN;w?y4JBQjx!Fo#$+K3V8lR7i z`zfU#7qFJ2564Tlymy-G%_7VNtVMye0kw#1z^V`IE^sjk zKT9D^ASlRTsQuu21u2qtP7hNmpi(l{-}=vsHDAW?K0OZ6aoRje&cW<=PXWv06byd0 ztO`a6Eb(9%M_c|_cAXGLbD25MW`_Oz-LRJ*dSUqP& z+|10(>gwvz(b1lsp6KZ4tE;QPz`*V8?V+KeTwGkRu&}V=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ> zRWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5DJd~8FSSUCi!&v&s2C_<$i;?cy$0v`Yd3p2z00DwYL_t(YiOrPZa)Tfcgd=dI zP|u{&AU0{!_kYSBpb=0rqn+vWqL@GXu`C>i!{1ev)Gje&*|3e`H3@(|Y@3Uq@KQct z*v4fHu1Uyw^uN_X6nQxx64(JCE%yZeEJFbxmH})~2Lf9G2zh)_T1uD!qWk1s%s{Bu z8nMxMlJa~$1Hc3bt51Im03lH|S|e)x*0$KT#=rx>viTPs2(20t2gJLs>EA`#)Iz$3 zxn7{cCWtW(EuzT*8KWxYa@6{70Ch&1G=wJ^3e+mdlt++{K)N(WX4|luAuV3}XPB7= z`g8`LU{J-4>8GK c$p2^k07~By4jC2ii~s-t07*qoM6N<$f-eG7wEzGB diff --git a/icons/obj/items/banners/banner.dmi b/icons/obj/items/banners/banner.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7edc1596436d869c47ba263fbc7c9849d8f8d4c5 GIT binary patch literal 704 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 literal 0 HcmV?d00001 diff --git a/icons/obj/items/banners/banner_tasselled.dmi b/icons/obj/items/banners/banner_tasselled.dmi new file mode 100644 index 0000000000000000000000000000000000000000..271fbce4232c567ea6db69d9e19c3e0d18aca9a5 GIT binary patch literal 743 zcmV?P)rW61G00DGTPE!Ct=GbNc004e^R9JLG zWpiV4X>fFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TB zGg33tGfE(w;*!LYR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq19V=A~!mr6W|E z;8R^vl$nc5sj`Brp9|Q%0ESUCw;^cOQUCw}?MXyIR9J=WS5c0GAP`0I0Ill*6ng+F z#AN?WA!HA*H;{0Gy?=H9>qadooBh}iUt6j1y?F(OnG~TD4%x+z*TaIQwnNh#7V!Ig z4FVH7f6s*=C~w6h#^;}7Hw(BHj7i-^1pLbw%~(gAKq_P35eKZ4F-A(sYUf}qVh+9K z8&Jt%a}}Y=^NliEi28s7(?)6IH4N>~0R!LDjqscu2RxGAM^xM` z((W9%eAroNIgUQ&-W)KOvTY$C=gt7K$U6pe$tolv*V_~_`th-f{|IhaAe+P#81T2OUsv#~JssA9KXXW)Lui#U z?uKGh{zLnaxn_*E{UW&26$J&|_KQ$1LBF7sZu>XF#EIDXc#Zbw z1jhbJEKm&oa~!scr0`LJP?7<%l`u(wh9ZrR%ZBc={Q1NSQgXH?pNA8_ Z$2Z0FjUaH;k3s+d002ovPDHLkV1iQ$PrCpB literal 0 HcmV?d00001 diff --git a/icons/obj/items/banners/banner_woven.dmi b/icons/obj/items/banners/banner_woven.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4178722e7b0506c1e8cdfdb70f39e61519b510c0 GIT binary patch literal 728 zcmV;}0w?{6P)fFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TB zGg33tGfE(w;*!LYR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq19V=A~!mr6W|E z;8R^vl$nc5sj`Brp9|Q%0ESUCw;^cOQUCw}-bqA3R9J=WRzYrqFc35*>N!z(05w*m z-bmm90ajKY&=*7&FX;c(UE3rI$i}XeUdnJFR(3oL_Ksm}gF#lATmBwRI6K5ESOgV# zQ{I2)y~Pl!mtVK)PLO)UOIHDc{2rASqG}8Q)QA@Z6u2S)fT)eaXe0s(Cxrz_8R2Rb z4BxLHlvWBEdX#012dX#a(D!wO2SLROjD}$A9=N9Is6zF&X2k=)H7BE?l{7DD)q{jV z05afqvvaO_;1b|+!3m`_0xWYLynBfl&@_kEO_@0losYn{=s!gdP`hz!J=9(TzFP5t zBGafK4B1%x-CwDqNUEMpW_sE)cO02BIz2Xz$qUGlSyTq z0#?G%V$6<_3Y7X?LCGJffEH9&=p+x&6_DgX53Ne_KyPW`A#=m|Uh=L(=7#fh_O3K* z?S%96^zJ2ASi=0g@a|STFQ|TTUU>U~&I|UoofjMaHL^#4gul%u@J9!h(JI~4(6QLn zQ;NkB_ERXuVrbLq(+ORg=f5Dl+;s9fB(HSRUP|7r$^Ee5_xJ$=@0eQB+M+-J0000< KMNUMnLSTZpC{mOF literal 0 HcmV?d00001 diff --git a/icons/obj/structures/banner_frame.dmi b/icons/obj/structures/banner_frame.dmi index 986bf0821e7009de1461d786cfdd6554f1aa951f..7379e9201d2e9ff902dfb4f57cae4fdfe9084128 100644 GIT binary patch delta 542 zcmV+(0^$AR1AzroiBL{Q4GJ0x0000DNk~Le0001h0001B1Oos704G#udH?_b6;Mo6 zMZmzoNk?2|Vs?s(p2ES&|NsBMz`%ovaBlzr00DGTPE!Ct=GbNc0054WN^gG=%1!Vp z2b&#VP?TDhnOaVw9hlls?NU~7^>YDx8~}Y}P3`qaV_*OP0f0$FK~z|U?N(cIgdh+U zynwI#18e?Q-5fxJ2S`Qkf0YF_TLN1;I!RfZqQ7j#o(7?vC<@RZV!ogp#68feo>0{w z?mOTQfcT6oiB>287q&(L;C6q>a<@Q?bwGoIUw82S`VRIB{67aLd2oG&8^Iu!{>w! z$jz;*C2$**Qo~RKxpBZO0om&TN_Ig0#DRIP78Td|UpNSBaQ+&Qd5(V-kb4fuT%iSW z&p}j9b7WBNI*5v?LIUNU10n~F7R=o|(EsYb?^_1vP9m8R(t5thiI=aO?c**&zYzL` z*Tu5;_0I+X%kJAdoe=O;h_P@cM4D}leuDTSrOeDxm$JTYkM}@}DW(cHZQu^{o03tJ zcLyDqtWJnY+=CAEn|(qzX8v&psw)-B!O)IB4TOWXINJes4bWc!w-3-e5q%brrxBe8 g=tM$iV!&bC0X3P?`?cL_-2eap07*qoM6N<$f}7~=rT_o{ delta 383 zcmV-_0f7F21>*xsiBL{Q4GJ0x0000DNk~Le0000$0000$0s{a50M`eZO#lD@3{Xr| zMZmzo-QC^)|NpYGvdjE5Bme*a0d!JMQvg8b*k%9#0Ev-PZ-0qZ4mLZ!peVI0Gqs#V zI|yl0R&e!m0ec((dyq}%gb0Ys0002+Nklb<83Y+7(>3?W8y?f6tp4RBcX!fJ=z{Edq{KXdzCPM1hFWb|*ug8%jzWUB!KS))g ziQ+7@Y)ynH6qAWCzwD05M4*p1NhWeFD{V~V!c2{c{KUL%OoWoTN+tr|BALU9tUJ`^ zLyZOVzBZ9{7phI*!o2h1%)IoXdz2SaR^){)_Uc7(oGiCqOoTqY2&&18jHLDg-(P)^ dH+_G*A6H9!)U*7ux5NMd002ovPDHLkV1iM~ss#W5 From 5a846bf3459485582fb5047c84967071e4527436 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 25 Nov 2024 00:53:53 +0000 Subject: [PATCH 0007/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 35c3254d6205..b65c2e11537e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -119,12 +119,6 @@

      Penelope Haze updated:

    • added overlays to show when a crafted or dried waterskin is closed
    • added a new sprite for waterskins made from dried ruminant stomachs
    - -

    23 September 2024

    -

    MistakeNot4892 updated:

    -
      -
    • You can now resist out of straightjackets.
    • -
    From af8f42da675f768d78cc3cdc5679b1ab96e464ed Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 25 Nov 2024 14:25:55 -0500 Subject: [PATCH 0008/1331] Fix mapped large casks having invisible contents --- code/game/objects/structures/barrels/cask_rack.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/game/objects/structures/barrels/cask_rack.dm b/code/game/objects/structures/barrels/cask_rack.dm index f284886622f8..20d6a1e61fe9 100644 --- a/code/game/objects/structures/barrels/cask_rack.dm +++ b/code/game/objects/structures/barrels/cask_rack.dm @@ -150,7 +150,7 @@ color = /decl/material/solid/organic/wood/ebony::color /obj/structure/cask_rack/large/mapped/Initialize(ml, _mat, _reinf_mat) - new /obj/structure/reagent_dispensers/barrel/cask/ebony/water(src) - new /obj/structure/reagent_dispensers/barrel/cask/ebony/beer(src) - new /obj/structure/reagent_dispensers/barrel/cask/ebony/wine(src) . = ..() + try_stack_barrel(new /obj/structure/reagent_dispensers/barrel/cask/ebony/water) + try_stack_barrel(new /obj/structure/reagent_dispensers/barrel/cask/ebony/beer) + try_stack_barrel(new /obj/structure/reagent_dispensers/barrel/cask/ebony/wine) From 9f4738a7194a749e1b417ceaf51dffdd37dd7ef6 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 25 Nov 2024 14:44:20 -0500 Subject: [PATCH 0009/1331] Add athletics check to barrel stacking --- .../objects/structures/barrels/cask_rack.dm | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/code/game/objects/structures/barrels/cask_rack.dm b/code/game/objects/structures/barrels/cask_rack.dm index 20d6a1e61fe9..1fc406b86fbe 100644 --- a/code/game/objects/structures/barrels/cask_rack.dm +++ b/code/game/objects/structures/barrels/cask_rack.dm @@ -32,7 +32,7 @@ /obj/structure/cask_rack/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() if(!. && user.Adjacent(src) && dropping.Adjacent(src) && user.Adjacent(dropping)) - return try_stack_barrel(dropping, user) + return try_stack_barrel_timed(dropping, user) /obj/structure/cask_rack/on_update_icon() . = ..() @@ -97,13 +97,16 @@ if(target && (!isturf(target) || !loc.Adjacent(target))) // TODO: Enter() or CanPass() checks instead of relying on step_towards() below. to_chat(user, SPAN_NOTICE("You cannot move \the [barrel] to \the [target].")) return FALSE + if(user && !user.do_skilled(3 SECONDS, SKILL_HAULING, src)) + to_chat(user, SPAN_NOTICE("You stop moving \the [barrel] off of \the [src].")) + return FALSE to_chat(user, SPAN_NOTICE("You move \the [barrel] off \the [src].")) barrel.dropInto(loc) if(target) step_towards(barrel, target) return TRUE -/obj/structure/cask_rack/proc/try_stack_barrel(atom/movable/barrel, mob/user) +/obj/structure/cask_rack/proc/can_stack_barrel(atom/movable/barrel, mob/user) if(!istype(barrel) || !barrel.simulated || barrel.anchored) return FALSE if(length(contents) >= max_stack) @@ -111,12 +114,27 @@ return FALSE var/list/stackable_types = get_stackable_barrel_types() if(!is_type_in_list(barrel, stackable_types)) - to_chat(user, SPAN_WARNING("\The [src] is cannot hold \the [barrel].")) + to_chat(user, SPAN_WARNING("\The [src] cannot hold \the [barrel].")) + return FALSE + return TRUE + +/obj/structure/cask_rack/proc/try_stack_barrel(atom/movable/barrel, mob/user) + if(!can_stack_barrel(barrel, user)) return FALSE barrel.forceMove(src) - to_chat(user, SPAN_NOTICE("You stack \the [barrel] onto \the [src].")) return TRUE +/obj/structure/cask_rack/proc/try_stack_barrel_timed(atom/movable/barrel, mob/user) + if(!can_stack_barrel(barrel, user)) + return FALSE + if(user && !user.do_skilled(3 SECONDS, SKILL_HAULING, src)) + to_chat(user, SPAN_NOTICE("You stop stacking \the [barrel] onto \the [src].")) + return FALSE + if(try_stack_barrel(barrel, user)) + to_chat(user, SPAN_NOTICE("You stack \the [barrel] onto \the [src].")) + return TRUE + return FALSE + /obj/structure/cask_rack/proc/get_stackable_barrel_types() var/static/list/_stackable_barrel_types = list( /obj/structure/reagent_dispensers/barrel/cask From cb99092f7846537835baf6208f48865f17e5126e Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Tue, 26 Nov 2024 11:25:05 +1100 Subject: [PATCH 0010/1331] Adding Doe's signs. --- code/game/objects/structures/banners.dm | 239 ++++++++++++------ .../recipes_hardness_integrity.dm | 9 + icons/obj/items/banners/sign.dmi | Bin 0 -> 682 bytes icons/obj/items/banners/sign_symbols.dmi | Bin 0 -> 2038 bytes icons/obj/structures/sign_post.dmi | Bin 0 -> 769 bytes 5 files changed, 176 insertions(+), 72 deletions(-) create mode 100644 icons/obj/items/banners/sign.dmi create mode 100644 icons/obj/items/banners/sign_symbols.dmi create mode 100644 icons/obj/structures/sign_post.dmi diff --git a/code/game/objects/structures/banners.dm b/code/game/objects/structures/banners.dm index 21f09be9f6e4..fbf65eac37d4 100644 --- a/code/game/objects/structures/banners.dm +++ b/code/game/objects/structures/banners.dm @@ -1,23 +1,28 @@ +// Assoc list of icon to icon_states to avoid constantly extracting them +var/global/list/_banner_decals = list() + /obj/structure/banner_frame - name = "banner frame" - desc = "A sturdy frame suitable for hanging a banner." - icon = 'icons/obj/structures/banner_frame.dmi' - icon_state = "banner_stand_preview" - material = /decl/material/solid/organic/wood - color = /decl/material/solid/organic/wood::color - anchored = TRUE - density = FALSE - opacity = FALSE - atom_flags = ATOM_FLAG_CLIMBABLE - layer = ABOVE_WINDOW_LAYER - obj_flags = OBJ_FLAG_ANCHORABLE + name = "banner frame" + desc = "A sturdy frame suitable for hanging a banner." + icon = 'icons/obj/structures/banner_frame.dmi' + icon_state = "banner_stand_preview" + material = /decl/material/solid/organic/wood + color = /decl/material/solid/organic/wood::color + anchored = TRUE + opacity = FALSE + atom_flags = ATOM_FLAG_CLIMBABLE + layer = ABOVE_WINDOW_LAYER + obj_flags = OBJ_FLAG_ANCHORABLE tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) - material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR - max_health = 50 + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR + max_health = 50 + density = TRUE + var/force_south_facing = TRUE var/base_icon_state = "banner_stand" /// Reference to any banner currently hung on the frame. var/obj/item/banner/banner + var/accepts_banner_type = /obj/item/banner /obj/structure/banner_frame/set_dir(ndir) return ..(force_south_facing ? SOUTH : ndir) @@ -54,6 +59,12 @@ if(banner) to_chat(user, SPAN_WARNING("There is already a banner hung on \the [src].")) return TRUE + + var/obj/item/banner/other_banner = O + if(other_banner.banner_type != accepts_banner_type) + to_chat(user, SPAN_WARNING("\The [src] is not suitable for hanging \the [O].")) + return TRUE + if(user.try_unequip(O, src)) user.visible_message(SPAN_NOTICE("\The [user] hangs \the [O] from \the [src]."), SPAN_NOTICE("You hang \the [O] from \the [src]."), SPAN_NOTICE("You hear the rustling of fabric.")) set_banner(O) @@ -87,7 +98,10 @@ for(var/decal in banner.decals) I = image(banner.decal_icon, decal) I.appearance_flags |= RESET_COLOR - I.color = banner.decals[decal] + if(banner.colourise_decal) + I.color = banner.decals[decal] + else + I.color = banner.color add_overlay(I) /obj/structure/banner_frame/Destroy() @@ -95,28 +109,45 @@ QDEL_NULL(banner) return ..() -var/global/list/_banner_decals = list() - /obj/item/banner - name = "banner" - desc = "A furled-up banner." - icon = 'icons/obj/items/banners/banner.dmi' - icon_state = "banner" - material = /decl/material/solid/organic/cloth - color = /decl/material/solid/organic/cloth::color - max_health = 20 - material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME - w_class = ITEM_SIZE_NORMAL - var/hung_desc = "The banner is rather unremarkable." + name = "banner" + desc = "A furled-up banner." + icon = 'icons/obj/items/banners/banner.dmi' + icon_state = ICON_STATE_WORLD + material = /decl/material/solid/organic/cloth + color = /decl/material/solid/organic/cloth::color + max_health = 20 + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME + w_class = ITEM_SIZE_NORMAL + var/colourise_decal = TRUE + var/hung_desc = "The banner is rather unremarkable." + var/decal_icon = 'icons/obj/items/banners/banner_symbols.dmi' + var/banner_type = /obj/item/banner + var/embroiderable = TRUE var/name_prefix - var/decal_icon = 'icons/obj/items/banners/banner_symbols.dmi' var/list/decals var/trim_color +/obj/item/banner/on_update_icon() + . = ..() + if(trim_color) + var/image/I = image(icon, "[icon_state]-trim") + I.appearance_flags |= RESET_COLOR + I.color = trim_color + add_overlay(I) + for(var/decal in decals) + var/image/I = image(decal_icon, decal) + if(colourise_decal) + I.appearance_flags |= RESET_COLOR + I.color = decals[decal] + add_overlay(I) + // TODO: PROPER EMBROIDERY AND ITEM DECORATION. /obj/item/banner/attackby(obj/item/used_item, mob/user) - if(istype(used_item, /obj/item/stack/material/thread)) + if(embroiderable && istype(used_item, /obj/item/stack/material/thread)) + + // TODO: check material crafting skill and do a do_after() if((!decal_icon || length(decals)) && trim_color) to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) @@ -150,95 +181,96 @@ var/global/list/_banner_decals = list() SetName("[name_prefix] [name]") /obj/item/banner/forked - name_prefix = "forked" - hung_desc = "The banner splits into two tails at the bottom." - icon = 'icons/obj/items/banners/banner_forked.dmi' - decal_icon = null // Current decals do not work nicely with the fork + name_prefix = "forked" + hung_desc = "The banner splits into two tails at the bottom." + icon = 'icons/obj/items/banners/banner_forked.dmi' + decal_icon = null // Current decals do not work nicely with the fork /obj/item/banner/pointed - name_prefix = "pointed" - hung_desc = "The banner narrows to a point at the bottom." - icon = 'icons/obj/items/banners/banner_pointed.dmi' + name_prefix = "pointed" + hung_desc = "The banner narrows to a point at the bottom." + icon = 'icons/obj/items/banners/banner_pointed.dmi' /obj/item/banner/rounded - name_prefix = "rounded" - hung_desc = "The banner has a rounded lower edge." - icon = 'icons/obj/items/banners/banner_rounded.dmi' + name_prefix = "rounded" + hung_desc = "The banner has a rounded lower edge." + icon = 'icons/obj/items/banners/banner_rounded.dmi' /obj/item/banner/square - name_prefix = "square" - hung_desc = "The banner has a squared-off lower edge." - icon = 'icons/obj/items/banners/banner_square.dmi' + name_prefix = "square" + hung_desc = "The banner has a squared-off lower edge." + icon = 'icons/obj/items/banners/banner_square.dmi' /obj/item/banner/tasselled - name_prefix = "tasselled" - hung_desc = "The banner has several dangling tassels at the bottom." - icon = 'icons/obj/items/banners/banner_tasselled.dmi' + name_prefix = "tasselled" + hung_desc = "The banner has several dangling tassels at the bottom." + icon = 'icons/obj/items/banners/banner_tasselled.dmi' /obj/item/banner/woven - name_prefix = "woven" - icon = 'icons/obj/items/banners/banner_woven.dmi' - material = /decl/material/solid/organic/plantmatter/grass/dry - color = /decl/material/solid/organic/plantmatter/grass/dry::color + name_prefix = "woven" + icon = 'icons/obj/items/banners/banner_woven.dmi' + material = /decl/material/solid/organic/plantmatter/grass/dry + color = /decl/material/solid/organic/plantmatter/grass/dry::color material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC - hung_desc = "The woven banner is rustic and uneven." + hung_desc = "The woven banner is rustic and uneven." /obj/item/banner/green - name = "green banner" + name = "green banner" paint_color = COLOR_GREEN - color = COLOR_GREEN + color = COLOR_GREEN /obj/item/banner/red - name = "red banner" + name = "red banner" paint_color = COLOR_RED - color = COLOR_RED + color = COLOR_RED /obj/item/banner/blue - name = "blue banner" + name = "blue banner" paint_color = COLOR_BLUE - color = COLOR_BLUE + color = COLOR_BLUE // Mapping helpers below. /obj/structure/banner_frame/blue banner = /obj/item/banner/blue - color = /obj/item/banner/blue::color // Mapping preview colour. + color = /obj/item/banner/blue::color // Mapping preview colour. /obj/structure/banner_frame/red banner = /obj/item/banner/red - color = /obj/item/banner/red::color + color = /obj/item/banner/red::color /obj/structure/banner_frame/green banner = /obj/item/banner/green - color = /obj/item/banner/green::color + color = /obj/item/banner/green::color // A wall-mounted banner frame with no stand. /obj/structure/banner_frame/wall - name = "hanging banner frame" - desc = "A sturdy frame suitable for hanging a banner." - icon_state = "banner_hanging_preview" - base_icon_state = "banner_hanging" + name = "hanging banner frame" + desc = "A sturdy frame suitable for hanging a banner." + icon_state = "banner_hanging_preview" + base_icon_state = "banner_hanging" directional_offset = @'{"NORTH":{"y":-32},"SOUTH":{"y":-32},"EAST":{"x":-32},"WEST":{"x":-32}}' force_south_facing = FALSE + density = FALSE /obj/structure/banner_frame/wall/ebony material = /decl/material/solid/organic/wood/ebony - color = /decl/material/solid/organic/wood/ebony::color + color = /decl/material/solid/organic/wood/ebony::color /obj/structure/banner_frame/wall/ebony/red - banner = /obj/item/banner/red - color = /obj/item/banner/red::color // Mapping preview colour. + banner = /obj/item/banner/red + color = /obj/item/banner/red::color // Mapping preview colour. /obj/structure/banner_frame/wall/ebony/blue - banner = /obj/item/banner/blue - color = /obj/item/banner/blue::color + banner = /obj/item/banner/blue + color = /obj/item/banner/blue::color /obj/structure/banner_frame/wall/ebony/green - banner = /obj/item/banner/green - color = /obj/item/banner/green::color + banner = /obj/item/banner/green + color = /obj/item/banner/green::color /obj/structure/banner_frame/wall/ebony/woven - banner = /obj/item/banner/woven - color = /obj/item/banner/woven::color + banner = /obj/item/banner/woven + color = /obj/item/banner/woven::color // Debug item. /obj/structure/banner_frame/random/Initialize(ml, _mat, _reinf_mat) @@ -259,4 +291,67 @@ var/global/list/_banner_decals = list() var/decal = pick(global._banner_decals[banner.decal_icon]) var/decal_color = get_random_colour() LAZYSET(banner.decals, decal, decal_color) + banner.update_icon() update_icon() + +// what is a sign, if not a wooden banner +/obj/structure/banner_frame/sign + name = "sign post" + desc = "A post for hanging a sign." + icon = 'icons/obj/structures/sign_post.dmi' + desc = "A post for hanging a sign." + base_icon_state = "sign" + accepts_banner_type = /obj/item/banner/sign + icon_state = "sign_preview" + density = TRUE + +/obj/structure/banner_frame/sign/wall + base_icon_state = "sign_hanging" + icon_state = "sign_hanging_preview" + force_south_facing = FALSE + density = FALSE + +/obj/item/banner/sign + name = "sign" + banner_type = /obj/item/banner/sign + embroiderable = FALSE + icon = 'icons/obj/items/banners/sign.dmi' + material = /decl/material/solid/organic/wood + color = /decl/material/solid/organic/wood::color + hung_desc = "The sign is unadorned." + decal_icon = 'icons/obj/items/banners/sign_symbols.dmi' + colourise_decal = FALSE + +/obj/item/banner/sign/attackby(obj/item/used_item, mob/user) + if(IS_KNIFE(used_item) && user.a_intent == I_HELP) + if(!decal_icon || length(decals)) + to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) + return TRUE + if(!global._banner_decals[decal_icon]) + global._banner_decals[decal_icon] = icon_states(decal_icon) + var/decal_to_add = input(user, "Which symbol do you wish to add to \the [src]?", "Sign Symbol") as null|anything in global._banner_decals[decal_icon] + if(decal_to_add && CanPhysicallyInteract(user) && !length(decals) && user.get_active_held_item() == used_item) + decals[decal_to_add] = COLOR_WHITE + return TRUE + . = ..() + +/obj/item/banner/sign/random/Initialize(ml, material_key) + material = pick(typesof(/decl/material/solid/organic/wood)) + . = ..() + +/obj/structure/banner_frame/sign/random/Initialize(ml, _mat, _reinf_mat) + material = pick(typesof(/decl/material/solid/organic/wood)) + ..() + banner = new /obj/item/banner/sign/random(src) + return INITIALIZE_HINT_LATELOAD + +/obj/structure/banner_frame/sign/random/LateInitialize() + . = ..() + if(banner) + if(banner.decal_icon) + if(!global._banner_decals[banner.decal_icon]) + global._banner_decals[banner.decal_icon] = icon_states(banner.decal_icon) + var/decal = pick(global._banner_decals[banner.decal_icon]) + LAZYSET(banner.decals, decal, COLOR_WHITE) + banner.update_icon() + update_icon() diff --git a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm index 92e580a6d0e2..6277f73b606b 100644 --- a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm +++ b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm @@ -2,6 +2,9 @@ abstract_type = /decl/stack_recipe/hardness/integrity required_integrity = 50 +/decl/stack_recipe/hardness/integrity/sign + result_type = /obj/item/banner/sign + /decl/stack_recipe/hardness/integrity/furniture abstract_type = /decl/stack_recipe/hardness/integrity/furniture one_per_turf = TRUE @@ -19,6 +22,12 @@ /decl/stack_recipe/hardness/integrity/furniture/banner_frame result_type = /obj/structure/banner_frame +/decl/stack_recipe/hardness/integrity/furniture/sign_hook + result_type = /obj/structure/banner_frame/sign + +/decl/stack_recipe/hardness/integrity/furniture/sign_hook/wall + result_type = /obj/structure/banner_frame/sign/wall + /decl/stack_recipe/hardness/integrity/furniture/coatrack result_type = /obj/structure/coatrack diff --git a/icons/obj/items/banners/sign.dmi b/icons/obj/items/banners/sign.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a2fb9ec0361db2cb49207526d2bbd78c1186cdef GIT binary patch literal 682 zcmV;b0#*HqP)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq19V=A~!mr6W|E;8R^vl$nc5sj`Br zp9|Q%0ESUCw;^cOQUCw}yh%hsR9J=Wmr;(wAP|N@yYJL+fL-+fMtcBC>-#?F{jahu z6wCZWTbk@9Cfj+K<_A7twEh!?*djCJsZ0B>e4x-F8` zovsGxmHr-(jhn;oA$E8Q_|4F2iDCPfwoutX@!97ynPcx|?C=P5b657}6Nk5P2|dBr zQ-^O)Vo!Qn@|P7m{BnPoqqo>%i^-7W5kgHryhR9fLYH?Yp`-`U{{0e_P^*E}S9(K0 z`Umz4znMxY8}Ai@8aP#}0jGGsAiEG%*o#Q@i`en0TJ9I@KhR>ocq{+^0rt9#&X%2^ QHUIzs07*qoM6N<$f}52=q5uE@ literal 0 HcmV?d00001 diff --git a/icons/obj/items/banners/sign_symbols.dmi b/icons/obj/items/banners/sign_symbols.dmi new file mode 100644 index 0000000000000000000000000000000000000000..16ce7891c0c98a6469bb14be61fb3c602c6581e4 GIT binary patch literal 2038 zcmVRtk3n73dAFBh_BOl4oQ_gB@SV#aLs@57WHG+FLJI4V)`%1>>Je6b_%dYl z0K{Tr#H>;2U{wOj1F(tYO~3Kv;sgWBx7zTf27wHO%z~5G{76bM@>J4ucw+1U`NBD( z{bPwp{Kbt&^ZU%Dx;54I6mPv!n!-$@hK>LL2FFQ6K~#90?OaWBqc{)+vD_mY?%1Gv z4Ay;d@ZA#T|NoT*&#dX!EjxD2)Yeq3o^!}U^cRSfo*N}eA|fIpA|fJQLaIYY^~bCB zn^vC6t%2(y;5!{i8fm@5LCUvdJ#q(W(LeTPcDhdl3cd07(`{=H)EszTeHv!0ye=RI z>MilU4_s4SnAMHX)P)WL&yvrK8Rw?KYYN^j<8(*_wv)~8WcK(;2Z0|xKMycE2h1&A zW%TCzS_W(jbI&{5X4#gEA>UV*eTBwZ-fSQT>Mb$d)XISM)ZMST+UIId#*pie3!jr} zv?Js|z0G&{L4|jdROj&MFurfW`K<_9Avq_T*ewtTRlSEn;A*UW!ePh9-d=!dZS|1v zwANMfTcKu`n}=N8ets{i+s|Vj5fKp)5fKsbr$H6{ZrJdn^P!@z-tj{f7Pm&en;~4Q z&5XC=QH8~=k*lC<$fBGSUFc>$pM=B&g~e+CZuTbwqqi)4vtPBwSk*mQN*}%|x%j3E z3#TgVgW&6fwkai)8JPKKhijz?pa)v!&|m+77!H{wU)d+ zh2&d9MC4o{4065z6c$(uerv~6h8)a%U8%gWG_IKQDH z3Ja_S_j78Z#C(>W_b)z_pMS_He&O8Wp>=?Ni#c^e0!~%bixCkK5fKp)e>~o8_odUt zh?Ol9!6v!`j26h^AG?EkB3~i(3QVj!yIkXT4d{4qrX4C8gsfyPmlLD6G`l z68nSHTiNYO8PIJyx7MUj>oPO9WnPMI7AUNAY_%hU=&jSn$y-u--QAfh$O(Dn9Jaa^ zfWpcSn+e)=;BD-7TCT+PrnC3+^@$E8rWEl|VdY(`K`T&iNjC?S70#!0>-l)goRBsN zDy(b{2o+Lqi8)xAv2x>f6*m|#FQv3Wg{8(VC`|B{WaAEdb2r$Hy_b!bbat&|iKwt_ zTf#-7-sTA=8<2@)O$e}eXX;jy^&_a=TiQ&_xf%=veq z4}L^ML_|bHL`1}IfwG-E*h&5I(Ei(HBYIP=*vp0SzNhQ?r_07>2;`;)vie4Ex&8`e zcq9@DtOY+vZL=)*LG)5@ha!;R?6hAkm^I^BIAO?wdUHiUOwEP&MDiS24Zw4urx#qo zEA{qDz$-ficoVurs)xHky}fWrnKg3NomAG$-M(Rur2?O6ulX!VD zKee<~g5Hw-G1MyhT7`9LrU_r3q})^&@dB)>&-iNJ7nSMVIn+J7t`|Z1T;U6_glArP z#V0{_WZknXTv8W!&(DW(z}pOJIxHa(iD?Ip9XiFwhSWhc_ z{~p?A2yM$H3hU`Te1DdH{~SJ_rEfon>HJ4U9tDN<^f~*pv=a}35`LwW!GH-v!DdYJtCIEi102aUkco_h6Br_t|kvsvJ1yn%*Vby^4+TapT z#oPLY6{`M0`GxHjMqg(qzhJk;(0+lb5p(hjCJ3tjS^0&UtC6sN!Bq699Aaf(y`8Lv z0UWGfXe)9o2l@k5eE|;G9}Lj(RRzF4hm!aC1_20k1~3M$N3a+H2y_O>YX;Gj_7V)x z8IY>=Sz#+cU@!pZ?w8OmD1Qxr^M3?jdO;os1E~Jn>h4iCz+eE*odLRL^%D#bu%hk& z2y_Pk0oHc_1M~nyy0dnEp#{3dv|sSh?Zq!F@h5TFr9YrM#ro7gyF!a_m<0&ImGtuA zLr2jf%zKoy5$clwlY_^ephY;(0+K|k?+20(0ca6ij9yjZp%)&C{!yuqFx7E*)wc!?PZWP8+Qnqn_)b=tQSxqucEv$WH@W)fmf$haA2DfK)nn{2X{Fx}4g}N06Te z=-j(h4@IYtTii7cA4R9~e!ZWf)5*s5Hp+Sf$hpbrp*C)V00000NkvXXu0mjf!@*f` literal 0 HcmV?d00001 From c960b102067f7742e07ccfd9ead9fb80c26c4043 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 29 Nov 2024 00:53:44 +0000 Subject: [PATCH 0011/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 7 ------- 1 file changed, 7 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index b65c2e11537e..11782eae61ac 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -112,13 +112,6 @@

    Atermonera updated:

    • Fixed some grammatical issues in location event strings.
    - -

    27 September 2024

    -

    Penelope Haze updated:

    -
      -
    • added overlays to show when a crafted or dried waterskin is closed
    • -
    • added a new sprite for waterskins made from dried ruminant stomachs
    • -
    From 0a99bb6d2923bbea00e0ec0a79619ba671841992 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 30 Nov 2024 08:49:35 +1100 Subject: [PATCH 0012/1331] Reworking banners to address feedback. --- code/game/objects/structures/banners.dm | 357 ------------------ code/modules/banners/__banner.dm | 133 +++++++ code/modules/banners/_banner_frame.dm | 126 +++++++ code/modules/banners/_banner_symbols.dm | 201 ++++++++++ .../banners/banner_frame_definitions.dm | 57 +++ code/modules/banners/sign.dm | 25 ++ code/modules/banners/sign_post.dm | 32 ++ nebula.dme | 7 +- 8 files changed, 580 insertions(+), 358 deletions(-) delete mode 100644 code/game/objects/structures/banners.dm create mode 100644 code/modules/banners/__banner.dm create mode 100644 code/modules/banners/_banner_frame.dm create mode 100644 code/modules/banners/_banner_symbols.dm create mode 100644 code/modules/banners/banner_frame_definitions.dm create mode 100644 code/modules/banners/sign.dm create mode 100644 code/modules/banners/sign_post.dm diff --git a/code/game/objects/structures/banners.dm b/code/game/objects/structures/banners.dm deleted file mode 100644 index fbf65eac37d4..000000000000 --- a/code/game/objects/structures/banners.dm +++ /dev/null @@ -1,357 +0,0 @@ -// Assoc list of icon to icon_states to avoid constantly extracting them -var/global/list/_banner_decals = list() - -/obj/structure/banner_frame - name = "banner frame" - desc = "A sturdy frame suitable for hanging a banner." - icon = 'icons/obj/structures/banner_frame.dmi' - icon_state = "banner_stand_preview" - material = /decl/material/solid/organic/wood - color = /decl/material/solid/organic/wood::color - anchored = TRUE - opacity = FALSE - atom_flags = ATOM_FLAG_CLIMBABLE - layer = ABOVE_WINDOW_LAYER - obj_flags = OBJ_FLAG_ANCHORABLE - tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) - material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR - max_health = 50 - density = TRUE - - var/force_south_facing = TRUE - var/base_icon_state = "banner_stand" - /// Reference to any banner currently hung on the frame. - var/obj/item/banner/banner - var/accepts_banner_type = /obj/item/banner - -/obj/structure/banner_frame/set_dir(ndir) - return ..(force_south_facing ? SOUTH : ndir) - -/obj/structure/banner_frame/Initialize(ml, _mat, _reinf_mat) - if(ispath(banner)) - set_banner(new banner(src)) - . = ..() - update_icon() - -/obj/structure/banner_frame/proc/set_banner(var/new_banner) - if(banner == new_banner) - return - banner = new_banner - if(banner) - name = banner.name - desc = "[initial(desc)] [banner.hung_desc]" - else - name = initial(name) - desc = initial(desc) - update_icon() - -/obj/structure/banner_frame/attack_hand(mob/user) - if(banner && user.check_dexterity(DEXTERITY_HOLD_ITEM)) - user.put_in_hands(banner) - var/old_banner = banner - set_banner(null) - user.visible_message(SPAN_NOTICE("\The [user] removes \the [old_banner] from \the [src]."), SPAN_NOTICE("You remove \the [old_banner] from \the [src]."), SPAN_NOTICE("You hear the rustling of fabric.")) - return TRUE - return ..() - -/obj/structure/banner_frame/attackby(obj/item/O, mob/user) - if(istype(O, /obj/item/banner)) - if(banner) - to_chat(user, SPAN_WARNING("There is already a banner hung on \the [src].")) - return TRUE - - var/obj/item/banner/other_banner = O - if(other_banner.banner_type != accepts_banner_type) - to_chat(user, SPAN_WARNING("\The [src] is not suitable for hanging \the [O].")) - return TRUE - - if(user.try_unequip(O, src)) - user.visible_message(SPAN_NOTICE("\The [user] hangs \the [O] from \the [src]."), SPAN_NOTICE("You hang \the [O] from \the [src]."), SPAN_NOTICE("You hear the rustling of fabric.")) - set_banner(O) - return TRUE - return ..() - -/obj/structure/banner_frame/dump_contents(atom/forced_loc = loc, mob/user) - if(istype(banner)) - banner.dropInto(forced_loc) - banner = null - . = ..() - -/obj/structure/banner_frame/on_update_icon() - . = ..() - - icon_state = base_icon_state - if(!istype(banner)) - return - - var/image/I = image(banner.icon, "[banner.icon_state]-hanging") - I.appearance_flags |= RESET_COLOR - I.color = banner.color - add_overlay(I) - - if(banner.trim_color) - I = image(banner.icon, "[banner.icon_state]-trim") - I.appearance_flags |= RESET_COLOR - I.color = banner.trim_color - add_overlay(I) - - for(var/decal in banner.decals) - I = image(banner.decal_icon, decal) - I.appearance_flags |= RESET_COLOR - if(banner.colourise_decal) - I.color = banner.decals[decal] - else - I.color = banner.color - add_overlay(I) - -/obj/structure/banner_frame/Destroy() - if(istype(banner)) - QDEL_NULL(banner) - return ..() - -/obj/item/banner - name = "banner" - desc = "A furled-up banner." - icon = 'icons/obj/items/banners/banner.dmi' - icon_state = ICON_STATE_WORLD - material = /decl/material/solid/organic/cloth - color = /decl/material/solid/organic/cloth::color - max_health = 20 - material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME - w_class = ITEM_SIZE_NORMAL - var/colourise_decal = TRUE - var/hung_desc = "The banner is rather unremarkable." - var/decal_icon = 'icons/obj/items/banners/banner_symbols.dmi' - var/banner_type = /obj/item/banner - var/embroiderable = TRUE - var/name_prefix - var/list/decals - var/trim_color - -/obj/item/banner/on_update_icon() - . = ..() - if(trim_color) - var/image/I = image(icon, "[icon_state]-trim") - I.appearance_flags |= RESET_COLOR - I.color = trim_color - add_overlay(I) - for(var/decal in decals) - var/image/I = image(decal_icon, decal) - if(colourise_decal) - I.appearance_flags |= RESET_COLOR - I.color = decals[decal] - add_overlay(I) - -// TODO: PROPER EMBROIDERY AND ITEM DECORATION. -/obj/item/banner/attackby(obj/item/used_item, mob/user) - - if(embroiderable && istype(used_item, /obj/item/stack/material/thread)) - - // TODO: check material crafting skill and do a do_after() - - if((!decal_icon || length(decals)) && trim_color) - to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) - return TRUE - - var/obj/item/stack/material/thread/used_stack = used_item - if(used_stack.get_amount() < 5) - to_chat(user, SPAN_WARNING("You need at least five lengths of thread to embroider a banner.")) - return TRUE - - if(!trim_color) - user.visible_message("\The [user] sews a trim onto \the [src].") - trim_color = used_item.color - used_stack.use(5) - return TRUE - - if(decal_icon && !length(decals)) - if(!global._banner_decals[decal_icon]) - global._banner_decals[decal_icon] = icon_states(decal_icon) - var/decal_color = used_item.color - var/decal_to_sew = input(user, "Which symbol do you wish to add to \the [src]?", "Banner Symbol") as null|anything in global._banner_decals[decal_icon] - if(decal_to_sew && CanPhysicallyInteract(user) && !length(decals) && user.get_active_held_item() == used_item && used_stack.use(5)) - decals[decal_to_sew] = decal_color - return TRUE - - . = ..() - -// 'woven grass banner', 'forked linen banner' -/obj/item/banner/update_name() - . = ..() - SetName("[name_prefix] [name]") - -/obj/item/banner/forked - name_prefix = "forked" - hung_desc = "The banner splits into two tails at the bottom." - icon = 'icons/obj/items/banners/banner_forked.dmi' - decal_icon = null // Current decals do not work nicely with the fork - -/obj/item/banner/pointed - name_prefix = "pointed" - hung_desc = "The banner narrows to a point at the bottom." - icon = 'icons/obj/items/banners/banner_pointed.dmi' - -/obj/item/banner/rounded - name_prefix = "rounded" - hung_desc = "The banner has a rounded lower edge." - icon = 'icons/obj/items/banners/banner_rounded.dmi' - -/obj/item/banner/square - name_prefix = "square" - hung_desc = "The banner has a squared-off lower edge." - icon = 'icons/obj/items/banners/banner_square.dmi' - -/obj/item/banner/tasselled - name_prefix = "tasselled" - hung_desc = "The banner has several dangling tassels at the bottom." - icon = 'icons/obj/items/banners/banner_tasselled.dmi' - -/obj/item/banner/woven - name_prefix = "woven" - icon = 'icons/obj/items/banners/banner_woven.dmi' - material = /decl/material/solid/organic/plantmatter/grass/dry - color = /decl/material/solid/organic/plantmatter/grass/dry::color - material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC - hung_desc = "The woven banner is rustic and uneven." - -/obj/item/banner/green - name = "green banner" - paint_color = COLOR_GREEN - color = COLOR_GREEN - -/obj/item/banner/red - name = "red banner" - paint_color = COLOR_RED - color = COLOR_RED - -/obj/item/banner/blue - name = "blue banner" - paint_color = COLOR_BLUE - color = COLOR_BLUE - -// Mapping helpers below. -/obj/structure/banner_frame/blue - banner = /obj/item/banner/blue - color = /obj/item/banner/blue::color // Mapping preview colour. - -/obj/structure/banner_frame/red - banner = /obj/item/banner/red - color = /obj/item/banner/red::color - -/obj/structure/banner_frame/green - banner = /obj/item/banner/green - color = /obj/item/banner/green::color - -// A wall-mounted banner frame with no stand. -/obj/structure/banner_frame/wall - name = "hanging banner frame" - desc = "A sturdy frame suitable for hanging a banner." - icon_state = "banner_hanging_preview" - base_icon_state = "banner_hanging" - directional_offset = @'{"NORTH":{"y":-32},"SOUTH":{"y":-32},"EAST":{"x":-32},"WEST":{"x":-32}}' - force_south_facing = FALSE - density = FALSE - -/obj/structure/banner_frame/wall/ebony - material = /decl/material/solid/organic/wood/ebony - color = /decl/material/solid/organic/wood/ebony::color - -/obj/structure/banner_frame/wall/ebony/red - banner = /obj/item/banner/red - color = /obj/item/banner/red::color // Mapping preview colour. - -/obj/structure/banner_frame/wall/ebony/blue - banner = /obj/item/banner/blue - color = /obj/item/banner/blue::color - -/obj/structure/banner_frame/wall/ebony/green - banner = /obj/item/banner/green - color = /obj/item/banner/green::color - -/obj/structure/banner_frame/wall/ebony/woven - banner = /obj/item/banner/woven - color = /obj/item/banner/woven::color - -// Debug item. -/obj/structure/banner_frame/random/Initialize(ml, _mat, _reinf_mat) - . = ..() - var/banner_type = pick(list( - /obj/item/banner/pointed, - /obj/item/banner/rounded, - /obj/item/banner/square, - /obj/item/banner/tasselled, - /obj/item/banner/woven - )) - banner = new banner_type(src) - banner.set_color(get_random_colour()) - banner.trim_color = get_random_colour() - if(banner.decal_icon) - if(!global._banner_decals[banner.decal_icon]) - global._banner_decals[banner.decal_icon] = icon_states(banner.decal_icon) - var/decal = pick(global._banner_decals[banner.decal_icon]) - var/decal_color = get_random_colour() - LAZYSET(banner.decals, decal, decal_color) - banner.update_icon() - update_icon() - -// what is a sign, if not a wooden banner -/obj/structure/banner_frame/sign - name = "sign post" - desc = "A post for hanging a sign." - icon = 'icons/obj/structures/sign_post.dmi' - desc = "A post for hanging a sign." - base_icon_state = "sign" - accepts_banner_type = /obj/item/banner/sign - icon_state = "sign_preview" - density = TRUE - -/obj/structure/banner_frame/sign/wall - base_icon_state = "sign_hanging" - icon_state = "sign_hanging_preview" - force_south_facing = FALSE - density = FALSE - -/obj/item/banner/sign - name = "sign" - banner_type = /obj/item/banner/sign - embroiderable = FALSE - icon = 'icons/obj/items/banners/sign.dmi' - material = /decl/material/solid/organic/wood - color = /decl/material/solid/organic/wood::color - hung_desc = "The sign is unadorned." - decal_icon = 'icons/obj/items/banners/sign_symbols.dmi' - colourise_decal = FALSE - -/obj/item/banner/sign/attackby(obj/item/used_item, mob/user) - if(IS_KNIFE(used_item) && user.a_intent == I_HELP) - if(!decal_icon || length(decals)) - to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) - return TRUE - if(!global._banner_decals[decal_icon]) - global._banner_decals[decal_icon] = icon_states(decal_icon) - var/decal_to_add = input(user, "Which symbol do you wish to add to \the [src]?", "Sign Symbol") as null|anything in global._banner_decals[decal_icon] - if(decal_to_add && CanPhysicallyInteract(user) && !length(decals) && user.get_active_held_item() == used_item) - decals[decal_to_add] = COLOR_WHITE - return TRUE - . = ..() - -/obj/item/banner/sign/random/Initialize(ml, material_key) - material = pick(typesof(/decl/material/solid/organic/wood)) - . = ..() - -/obj/structure/banner_frame/sign/random/Initialize(ml, _mat, _reinf_mat) - material = pick(typesof(/decl/material/solid/organic/wood)) - ..() - banner = new /obj/item/banner/sign/random(src) - return INITIALIZE_HINT_LATELOAD - -/obj/structure/banner_frame/sign/random/LateInitialize() - . = ..() - if(banner) - if(banner.decal_icon) - if(!global._banner_decals[banner.decal_icon]) - global._banner_decals[banner.decal_icon] = icon_states(banner.decal_icon) - var/decal = pick(global._banner_decals[banner.decal_icon]) - LAZYSET(banner.decals, decal, COLOR_WHITE) - banner.update_icon() - update_icon() diff --git a/code/modules/banners/__banner.dm b/code/modules/banners/__banner.dm new file mode 100644 index 000000000000..3301504725f6 --- /dev/null +++ b/code/modules/banners/__banner.dm @@ -0,0 +1,133 @@ +/obj/item/banner + name = "banner" + desc = "A furled-up banner." + icon = 'icons/obj/items/banners/banner.dmi' + icon_state = ICON_STATE_WORLD + material = /decl/material/solid/organic/cloth + color = /decl/material/solid/organic/cloth::color + max_health = 20 + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME + w_class = ITEM_SIZE_NORMAL + var/colourise_decal = TRUE + var/hung_desc = "The banner is rather unremarkable." + var/banner_type = /obj/item/banner + var/embroiderable = TRUE + var/name_prefix + var/list/decals + var/trim_color + +var/global/list/banner_type_to_symbols = list() +/obj/item/banner/proc/get_available_decals() + . = global.banner_type_to_symbols[banner_type] + if(!.) + . = list() + for(var/decl/banner_symbol/symbol in decls_repository.get_decls_of_type_unassociated(/decl/banner_symbol)) + if(banner_type in symbol.usable_by_banner_type) + . += symbol + global.banner_type_to_symbols[banner_type] = . + +// TODO: PROPER EMBROIDERY AND ITEM DECORATION. +/obj/item/banner/attackby(obj/item/used_item, mob/user) + + if(embroiderable && istype(used_item, /obj/item/stack/material/thread)) + + // TODO: check material crafting skill and do a do_after() + + if((!length(get_available_decals()) || length(decals)) && trim_color) + to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) + return TRUE + + var/obj/item/stack/material/thread/used_stack = used_item + if(used_stack.get_amount() < 5) + to_chat(user, SPAN_WARNING("You need at least five lengths of thread to embroider a banner.")) + return TRUE + + if(!trim_color) + user.visible_message("\The [user] sews a trim onto \the [src].") + trim_color = used_item.color + used_stack.use(5) + return TRUE + + if(length(get_available_decals()) && !length(decals)) + var/list/available_decals = get_available_decals() + var/decal_color = used_item.color + var/decal_to_sew = input(user, "Which symbol do you wish to add to \the [src]?", "Banner Symbol") as null|anything in available_decals + if(decal_to_sew && CanPhysicallyInteract(user) && !length(decals) && user.get_active_held_item() == used_item && used_stack.use(5)) + decals[decal_to_sew] = decal_color + return TRUE + + . = ..() + +// 'woven grass banner', 'forked linen banner' +/obj/item/banner/update_name() + . = ..() + SetName("[name_prefix] [name]") + +/obj/item/banner/examine(mob/user, distance, infix, suffix) + . = ..() + var/decorations = get_decal_string() + if(decorations) + to_chat(user, "\The [src] is decorated with [decorations].") + +/obj/item/banner/proc/get_decal_string() + for(var/decl/banner_symbol/decal in decals) + if(colourise_decal) + LAZYADD(., "\a [decal.name]") + else + LAZYADD(., "\a [decal.name]") + if(trim_color) + // This is weak but I'm not sure how else to phrase it without a color-to-string system. + LAZYADD(., "a trim") + if(.) + return english_list(.) + +/obj/item/banner/forked + name_prefix = "forked" + hung_desc = "The banner splits into two tails at the bottom." + icon = 'icons/obj/items/banners/banner_forked.dmi' + +/obj/item/banner/forked/get_available_decals() + return null // Current decals do not work nicely with the fork + +/obj/item/banner/pointed + name_prefix = "pointed" + hung_desc = "The banner narrows to a point at the bottom." + icon = 'icons/obj/items/banners/banner_pointed.dmi' + +/obj/item/banner/rounded + name_prefix = "rounded" + hung_desc = "The banner has a rounded lower edge." + icon = 'icons/obj/items/banners/banner_rounded.dmi' + +/obj/item/banner/square + name_prefix = "square" + hung_desc = "The banner has a squared-off lower edge." + icon = 'icons/obj/items/banners/banner_square.dmi' + +/obj/item/banner/tasselled + name_prefix = "tasselled" + hung_desc = "The banner has several dangling tassels at the bottom." + icon = 'icons/obj/items/banners/banner_tasselled.dmi' + +/obj/item/banner/woven + name_prefix = "woven" + icon = 'icons/obj/items/banners/banner_woven.dmi' + material = /decl/material/solid/organic/plantmatter/grass/dry + color = /decl/material/solid/organic/plantmatter/grass/dry::color + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC + hung_desc = "The woven banner is rustic and uneven." + +/obj/item/banner/green + name = "green banner" + paint_color = COLOR_GREEN + color = COLOR_GREEN + +/obj/item/banner/red + name = "red banner" + paint_color = COLOR_RED + color = COLOR_RED + +/obj/item/banner/blue + name = "blue banner" + paint_color = COLOR_BLUE + color = COLOR_BLUE diff --git a/code/modules/banners/_banner_frame.dm b/code/modules/banners/_banner_frame.dm new file mode 100644 index 000000000000..cda9521373e5 --- /dev/null +++ b/code/modules/banners/_banner_frame.dm @@ -0,0 +1,126 @@ +/obj/structure/banner_frame + name = "banner frame" + desc = "A sturdy frame suitable for hanging a banner." + icon = 'icons/obj/structures/banner_frame.dmi' + icon_state = "banner_stand_preview" + material = /decl/material/solid/organic/wood + color = /decl/material/solid/organic/wood::color + anchored = TRUE + opacity = FALSE + atom_flags = ATOM_FLAG_CLIMBABLE + layer = ABOVE_WINDOW_LAYER + obj_flags = OBJ_FLAG_ANCHORABLE + tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR + max_health = 50 + density = TRUE + + var/force_south_facing = TRUE + var/base_icon_state = "banner_stand" + /// Reference to any banner currently hung on the frame. + var/obj/item/banner/banner + var/accepts_banner_type = /obj/item/banner + +/obj/structure/banner_frame/set_dir(ndir) + return ..(force_south_facing ? SOUTH : ndir) + +/obj/structure/banner_frame/Initialize(ml, _mat, _reinf_mat) + if(ispath(banner)) + set_banner(new banner(src)) + . = ..() + update_icon() + +/obj/structure/banner_frame/proc/set_banner(var/new_banner) + if(banner == new_banner) + return + banner = new_banner + if(banner) + name = banner.name + var/list/desc_strings = list(initial(desc), banner.hung_desc) + var/decorations = banner.get_decal_string() + if(decorations) + desc_strings += "It is decorated with [decorations]." + desc = jointext(desc_strings, " ") + else + name = initial(name) + desc = initial(desc) + update_icon() + +/obj/structure/banner_frame/attack_hand(mob/user) + if(banner && user.check_dexterity(DEXTERITY_HOLD_ITEM)) + user.put_in_hands(banner) + var/old_banner = banner + set_banner(null) + user.visible_message( + SPAN_NOTICE("\The [user] removes \the [old_banner] from \the [src]."), + SPAN_NOTICE("You remove \the [old_banner] from \the [src]."), + SPAN_NOTICE("You hear the rustling of fabric.") + ) + return TRUE + return ..() + +/obj/structure/banner_frame/attackby(obj/item/O, mob/user) + if(istype(O, /obj/item/banner)) + if(banner) + to_chat(user, SPAN_WARNING("There is already a banner hung on \the [src].")) + return TRUE + + var/obj/item/banner/other_banner = O + if(other_banner.banner_type != accepts_banner_type) + to_chat(user, SPAN_WARNING("\The [src] is not suitable for hanging \the [O].")) + return TRUE + + if(user.try_unequip(O, src)) + user.visible_message(SPAN_NOTICE("\The [user] hangs \the [O] from \the [src]."), SPAN_NOTICE("You hang \the [O] from \the [src]."), SPAN_NOTICE("You hear the rustling of fabric.")) + set_banner(O) + return TRUE + return ..() + +/obj/structure/banner_frame/dump_contents(atom/forced_loc = loc, mob/user) + if(istype(banner)) + banner.dropInto(forced_loc) + banner = null + . = ..() + +/obj/structure/banner_frame/on_update_icon() + . = ..() + + icon_state = base_icon_state + if(!istype(banner)) + return + + var/image/I = image(banner.icon, "[banner.icon_state]-hanging") + I.appearance_flags |= RESET_COLOR + I.color = banner.color + add_overlay(I) + + for(var/decl/banner_symbol/decal as anything in banner.decals) + I = image(decal.icon, decal.icon_state) + I.appearance_flags |= RESET_COLOR + I.blend_mode = BLEND_INSET_OVERLAY // Masks us to the banner icon. + if(banner.colourise_decal) + I.color = banner.decals[decal] + else + I.color = banner.color + add_overlay(I) + + if(banner.trim_color) + I = image(banner.icon, "[banner.icon_state]-trim") + I.appearance_flags |= RESET_COLOR + I.color = banner.trim_color + add_overlay(I) + +/obj/structure/banner_frame/Destroy() + if(istype(banner)) + QDEL_NULL(banner) + return ..() + +// A wall-mounted banner frame with no stand. +/obj/structure/banner_frame/wall + name = "hanging banner frame" + desc = "A sturdy frame suitable for hanging a banner." + icon_state = "banner_hanging_preview" + base_icon_state = "banner_hanging" + directional_offset = @'{"NORTH":{"y":-32},"SOUTH":{"y":-32},"EAST":{"x":-32},"WEST":{"x":-32}}' + force_south_facing = FALSE + density = FALSE diff --git a/code/modules/banners/_banner_symbols.dm b/code/modules/banners/_banner_symbols.dm new file mode 100644 index 000000000000..2de8b8a6521d --- /dev/null +++ b/code/modules/banners/_banner_symbols.dm @@ -0,0 +1,201 @@ +/decl/banner_symbol + abstract_type = /decl/banner_symbol + decl_flags = DECL_FLAG_MANDATORY_UID + /// Icon to draw from when rendering on a banner. + var/icon = 'icons/obj/items/banners/banner_symbols.dmi' + /// Icon state to draw from the icon. + var/icon_state + /// String used to select/describe a symbol + var/name + var/usable_by_banner_type = list( + /obj/item/banner + ) + +/decl/banner_symbol/validate() + . = ..() + if(!icon) + . += "null icon" + if(!istext(icon_state)) + . += "invalid/null icon_state" + if(!istext(name)) + . += "invalid/null name" + if(icon && icon_state && !check_state_in_icon(icon_state, icon)) + . += "missing icon_state '[icon_state]' from icon '[icon]'" + +// Default definitions below. +/decl/banner_symbol/starburst + name = "starburst" + icon_state = "starburst" + uid = "symbol_starburst" + +/decl/banner_symbol/fern + name = "fern" + icon_state = "fern" + uid = "symbol_fern" + +/decl/banner_symbol/snowflake + name = "snowflake" + icon_state = "snowflake" + uid = "symbol_snowflake" + +/decl/banner_symbol/sun + name = "sun" + icon_state = "sun" + uid = "symbol_sun" + +/decl/banner_symbol/scarab + name = "scarab" + icon_state = "scarab" + uid = "symbol_scarab" + +/decl/banner_symbol/triangle_chevron + name = "triangle with chevron" + icon_state = "triangle with chevron" + uid = "symbol_triangle_chevron" + +/decl/banner_symbol/triangle_down + name = "downward triangle" + icon_state = "downward triangle" + uid = "symbol_triangle_down" + +/decl/banner_symbol/triangle_up + name = "upward triangle" + icon_state = "upward triangle" + uid = "symbol_triangle_up" + +/decl/banner_symbol/hand + name = "hand" + icon_state = "hand" + uid = "symbol_hand" + +/decl/banner_symbol/sword + name = "sword" + icon_state = "sword" + uid = "symbol_sword" + +/decl/banner_symbol/knot + name = "knot" + icon_state = "knot" + uid = "symbol_knot" + +/decl/banner_symbol/circled_cup + name = "circled cup" + icon_state = "circled cup" + uid = "symbol_cup_circle" + +/decl/banner_symbol/aquila + name = "aquila" + icon_state = "aquila" + uid = "symbol_aquila" + +/decl/banner_symbol/orb + name = "orb" + icon_state = "orb" + uid = "symbol_orb" + +/decl/banner_symbol/bird_head + name = "bird head" + icon_state = "bird head" + uid = "symbol_bird_head" + +/decl/banner_symbol/deer + name = "deer" + icon_state = "deer" + uid = "symbol_deer" + +/decl/banner_symbol/deer_antler + name = "antlered deer" + icon_state = "antlered deer" + uid = "symbol_deer_antler" + +/decl/banner_symbol/duck + name = "duck head" + icon_state = "duck head" + uid = "symbol_duck_head" + +/decl/banner_symbol/frog + name = "frog" + icon_state = "frog" + uid = "symbol_frog" + +/decl/banner_symbol/fish + name = "fish" + icon_state = "fish" + uid = "symbol_fish" + +/decl/banner_symbol/bird + name = "bird" + icon_state = "bird" + uid = "symbol_bird" + +/decl/banner_symbol/cross + name = "cross" + icon_state = "cross" + uid = "symbol_cross" + +/decl/banner_symbol/sign + icon = 'icons/obj/items/banners/sign_symbols.dmi' + abstract_type = /decl/banner_symbol/sign + usable_by_banner_type = list( + /obj/item/banner/sign + ) + +/decl/banner_symbol/sign/mug + name = "mug" + icon_state = "mug" + uid = "symbol_sign_mug" + +/decl/banner_symbol/sign/scales + name = "scales" + icon_state = "scales" + uid = "symbol_sign_scales" + +/decl/banner_symbol/sign/mortar_pestle + name = "mortar and pestle" + icon_state = "mortar and pestle" + uid = "symbol_sign_mortar_pestle" + +/decl/banner_symbol/sign/pick_shovel + name = "pick and shovel" + icon_state = "pick and shovel" + uid = "symbol_sign_pick_shovel" + +/decl/banner_symbol/sign/face + name = "face" + icon_state = "face" + uid = "symbol_sign_face" + +/decl/banner_symbol/sign/crescent + name = "crescent" + icon_state = "crescent" + uid = "symbol_sign_crescent" + +/decl/banner_symbol/sign/vial + name = "vial" + icon_state = "vial" + uid = "symbol_sign_vial" + +/decl/banner_symbol/sign/spool + name = "spool" + icon_state = "spool" + uid = "symbol_sign_spool" + +/decl/banner_symbol/sign/pawnbroker + name = "pawnbroker" + icon_state = "pawnbroker" + uid = "symbol_sign_pawnbroker" + +/decl/banner_symbol/sign/sword + name = "sword" + icon_state = "sword" + uid = "symbol_sign_sword" + +/decl/banner_symbol/sign/cross + name = "cross" + icon_state = "cross" + uid = "symbol_sign_cross" + +/decl/banner_symbol/sign/circle + name = "circle" + icon_state = "circle" + uid = "symbol_sign_circle" diff --git a/code/modules/banners/banner_frame_definitions.dm b/code/modules/banners/banner_frame_definitions.dm new file mode 100644 index 000000000000..cf09eaf4652a --- /dev/null +++ b/code/modules/banners/banner_frame_definitions.dm @@ -0,0 +1,57 @@ +// Mapping helpers below. +/obj/structure/banner_frame/blue + banner = /obj/item/banner/blue + color = /obj/item/banner/blue::color // Mapping preview colour. + +/obj/structure/banner_frame/red + banner = /obj/item/banner/red + color = /obj/item/banner/red::color + +/obj/structure/banner_frame/green + banner = /obj/item/banner/green + color = /obj/item/banner/green::color + +/obj/structure/banner_frame/wall/ebony + material = /decl/material/solid/organic/wood/ebony + color = /decl/material/solid/organic/wood/ebony::color + +/obj/structure/banner_frame/wall/ebony/red + banner = /obj/item/banner/red + color = /obj/item/banner/red::color // Mapping preview colour. + +/obj/structure/banner_frame/wall/ebony/blue + banner = /obj/item/banner/blue + color = /obj/item/banner/blue::color + +/obj/structure/banner_frame/wall/ebony/green + banner = /obj/item/banner/green + color = /obj/item/banner/green::color + +/obj/structure/banner_frame/wall/ebony/woven + banner = /obj/item/banner/woven + color = /obj/item/banner/woven::color + +// Debug item. +/obj/structure/banner_frame/random/Initialize(ml, _mat, _reinf_mat) + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/structure/banner_frame/random/LateInitialize() + ..() + var/banner_type = pick(list( + /obj/item/banner/pointed, + /obj/item/banner/rounded, + /obj/item/banner/square, + /obj/item/banner/tasselled, + /obj/item/banner/woven + )) + var/obj/item/banner/new_banner = new banner_type(src) + new_banner.set_color(get_random_colour()) + new_banner.trim_color = get_random_colour() + var/list/available_decals = new_banner.get_available_decals() + if(length(available_decals)) + var/decal = pick(available_decals) + var/decal_color = get_random_colour() + LAZYSET(new_banner.decals, decal, decal_color) + new_banner.update_icon() + set_banner(new_banner) diff --git a/code/modules/banners/sign.dm b/code/modules/banners/sign.dm new file mode 100644 index 000000000000..cfc15e3310c2 --- /dev/null +++ b/code/modules/banners/sign.dm @@ -0,0 +1,25 @@ +/obj/item/banner/sign + name = "sign" + banner_type = /obj/item/banner/sign + embroiderable = FALSE + icon = 'icons/obj/items/banners/sign.dmi' + material = /decl/material/solid/organic/wood + color = /decl/material/solid/organic/wood::color + hung_desc = "The sign is unadorned." + colourise_decal = FALSE + +/obj/item/banner/sign/attackby(obj/item/used_item, mob/user) + if(IS_KNIFE(used_item) && user.a_intent == I_HELP) + var/available_decals = get_available_decals() + if(!length(available_decals) || length(decals)) + to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) + return TRUE + var/decal_to_add = input(user, "Which symbol do you wish to add to \the [src]?", "Sign Symbol") as null|anything in available_decals + if(decal_to_add && CanPhysicallyInteract(user) && !length(decals) && user.get_active_held_item() == used_item) + decals[decal_to_add] = COLOR_WHITE + return TRUE + . = ..() + +/obj/item/banner/sign/random/Initialize(ml, material_key) + material = pick(typesof(/decl/material/solid/organic/wood)) + . = ..() diff --git a/code/modules/banners/sign_post.dm b/code/modules/banners/sign_post.dm new file mode 100644 index 000000000000..d1d0f959023e --- /dev/null +++ b/code/modules/banners/sign_post.dm @@ -0,0 +1,32 @@ +// what is a sign, if not a wooden banner +/obj/structure/banner_frame/sign + name = "sign post" + desc = "A post for hanging a sign." + icon = 'icons/obj/structures/sign_post.dmi' + desc = "A post for hanging a sign." + base_icon_state = "sign" + accepts_banner_type = /obj/item/banner/sign + icon_state = "sign_preview" + density = TRUE + +/obj/structure/banner_frame/sign/wall + base_icon_state = "sign_hanging" + icon_state = "sign_hanging_preview" + force_south_facing = FALSE + density = FALSE + +/obj/structure/banner_frame/sign/random/Initialize(ml, _mat, _reinf_mat) + material = pick(typesof(/decl/material/solid/organic/wood)) + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/structure/banner_frame/sign/random/LateInitialize() + . = ..() + var/obj/item/banner/new_banner = new /obj/item/banner/sign/random(src) + if(new_banner) + var/list/available_decals = new_banner.get_available_decals() + if(length(available_decals)) + var/decal = pick(available_decals) + LAZYSET(new_banner.decals, decal, COLOR_WHITE) + new_banner.update_icon() + set_banner(new_banner) diff --git a/nebula.dme b/nebula.dme index 7c4b88297fe0..c0d587451052 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1414,7 +1414,6 @@ #include "code\game\objects\structures\_structure_icon.dm" #include "code\game\objects\structures\_structure_materials.dm" #include "code\game\objects\structures\ai_decoy.dm" -#include "code\game\objects\structures\banners.dm" #include "code\game\objects\structures\barricade.dm" #include "code\game\objects\structures\barsign.dm" #include "code\game\objects\structures\bedsheet_bin.dm" @@ -1820,6 +1819,12 @@ #include "code\modules\backgrounds\location\locations_other.dm" #include "code\modules\backgrounds\religion\_religion.dm" #include "code\modules\backgrounds\religion\religions_human.dm" +#include "code\modules\banners\__banner.dm" +#include "code\modules\banners\_banner_frame.dm" +#include "code\modules\banners\_banner_symbols.dm" +#include "code\modules\banners\banner_frame_definitions.dm" +#include "code\modules\banners\sign.dm" +#include "code\modules\banners\sign_post.dm" #include "code\modules\blob\blob.dm" #include "code\modules\blood\blood.dm" #include "code\modules\blood\blood_types.dm" From d19df16ae3d25d455e283bf816df0037c79e9ef3 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 30 Nov 2024 12:57:07 +1100 Subject: [PATCH 0013/1331] Updating modpack banners. --- code/modules/banners/__banner.dm | 8 ++++++ mods/content/corporate/icons/banner.dmi | Bin 816 -> 0 bytes .../corporate/icons/banner_symbols.dmi | Bin 0 -> 318 bytes mods/content/corporate/items/clutter.dm | 11 +++++--- mods/content/government/icons/banner.dmi | Bin 913 -> 0 bytes .../government/icons/banner_symbols.dmi | Bin 0 -> 461 bytes mods/content/government/items/clutter.dm | 24 +++++++++++++----- 7 files changed, 34 insertions(+), 9 deletions(-) delete mode 100644 mods/content/corporate/icons/banner.dmi create mode 100644 mods/content/corporate/icons/banner_symbols.dmi delete mode 100644 mods/content/government/icons/banner.dmi create mode 100644 mods/content/government/icons/banner_symbols.dmi diff --git a/code/modules/banners/__banner.dm b/code/modules/banners/__banner.dm index 3301504725f6..647f3adc73a3 100644 --- a/code/modules/banners/__banner.dm +++ b/code/modules/banners/__banner.dm @@ -16,6 +16,14 @@ var/list/decals var/trim_color +/obj/item/banner/Initialize(ml, material_key) + for(var/decal in decals) + if(ispath(decal)) + var/decl/banner_symbol/decal_decl = GET_DECL(decal) + decals[decal_decl] = decals[decal] + decals -= decal + . = ..() + var/global/list/banner_type_to_symbols = list() /obj/item/banner/proc/get_available_decals() . = global.banner_type_to_symbols[banner_type] diff --git a/mods/content/corporate/icons/banner.dmi b/mods/content/corporate/icons/banner.dmi deleted file mode 100644 index cc4c3c95e9efe5a0757947e8cdd7f50eefeec807..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 816 zcmV-01JC@4P)gwvz(b1lsp1Zrd-rnBm=;*7ftH8j(?d|QMp`lz{T(GdPzP`Ssq@=^c!}$34 zK#A6s00001bW%=J06^y0W&i*Hg?dz2bVOxyV{&P5bZKvH004NLQ&wgNLXApo+4 zL~1A&{UQJW0pUqRK~z|U<&+C=f-n$(OJ9x~onuTGFx|ud{~vd40o#I$ZIdO7r-B&2 zkLz(gLjEoaCz4=Fy+fP@8R`o3MO+3m&)95;78I7Zh9X)}Ec|4METqfNhV6fHA^5g{ zE<76O6#l;tN;|ila%}YUI=j!upqVsT&fajZFsKhm%45mu?TL+ zfJ3R2(m22?RQT;6+iNMTdES+vru##!KT0*X3wS5NhUP1Mc5Dc*%V9?vBnp}G}_|FvhIXsG#8Qc%rm6( z7egvf!{?iN(g2DS)lnGgXNPAZ05BgT4CnI<xI0&B<+5M#&@7(RE^yL diff --git a/mods/content/corporate/icons/banner_symbols.dmi b/mods/content/corporate/icons/banner_symbols.dmi new file mode 100644 index 0000000000000000000000000000000000000000..8d534a69a4839161b048e58b6f63ff7859d65205 GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0L3?#3!&-4XSoB=)|t_Kbr`2YX^p@S#ypJ>|v z6l5$3@(X5gcy=QV$jPq?i70VNElw`VEGWs$&tqVym=hdUP*nQ;OK`!*uTQkRb+yi& zIUl?s)Zn7=gGV~&eKb!p6!rA(urLlXzHIEJFz3;vBa=cDf;IIG_4hB)*l1zu-`Q=f zx_O1flF8l1Yc&kbMUR+02?{ItTFf|`m*G*ijC*;+@>rnN8J;eVAr`&K2@;nMG_p=T zZ_&u>o4~otL4B9QWa(KBhfPWtO-mT{Ov+6P1WI0MNC+I*($%PVh0(LYbWLOCRhAT0 zA%{@D3RWQr35$t8dw6+xv?^F{gqa%n@;ZbGo;t8$RbxgvzbMd;CH(A5?zDnJiow&> K&t;ucLK6VHb#jpa literal 0 HcmV?d00001 diff --git a/mods/content/corporate/items/clutter.dm b/mods/content/corporate/items/clutter.dm index 4de07358d1b1..19ba76ee4557 100644 --- a/mods/content/corporate/items/clutter.dm +++ b/mods/content/corporate/items/clutter.dm @@ -23,14 +23,19 @@ /obj/item/banner/nanotrasen name = "\improper NanoTrasen banner" hung_desc = "The banner is emblazoned with the NanoTrasen logo." - icon = 'mods/content/corporate/icons/banner.dmi' desc = "A banner emblazoned with the NanoTrasen logo." material_alteration = MAT_FLAG_ALTERATION_NONE color = COLOR_NAVY_BLUE + trim_color = COLOR_GOLD decals = list( - "banner_trim" = COLOR_GOLD, - "banner_nanotrasen" = COLOR_WHITE + /decl/banner_symbol/nanotrasen = COLOR_WHITE ) /obj/structure/banner_frame/nanotrasen banner = /obj/item/banner/nanotrasen + +/decl/banner_symbol/nanotrasen + icon = 'mods/content/corporate/icons/banner_symbols.dmi' + name = "NanoTrasen logo" + icon_state = "nanotrasen" + uid = "symbol_corporate_nanotrasen" diff --git a/mods/content/government/icons/banner.dmi b/mods/content/government/icons/banner.dmi deleted file mode 100644 index 88a40994837056f868696a063bba2b920b1aeb47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 913 zcmV;C18)3@P)gwvz(b1lsp1Zrd-rnBm=;*7ftH8j(?d|QMp`lz{T(GdPzP`Ssq@=^c!@$76 z|9L>)-{1edKmg4D!27VT00001bW%=J06^y0W&i*Hhk8_4bVOxyV{&P5bZKvH004NL zQ&wawp0006sNklvEeg5JmxAQnn~ArGWx<(sn3$ z|2M3JBoIi#Mv7-uVlKr%zvUpp;kw6R#BRE8GpvzZL)t;F;3PyufFPxaj*yN zfFNm#qOM8TJm7^}nxO{im*Jzuk_E?atcj?h{A{?*8^X#NO^^ zgM9W_|7;pOd@Uh1em1B_r|TYC410WkQvIkDN9Rqd4Pkt3@blH9G7IG|2LBX`Cb=JF zcN&CZe~4r7QgLjLICJLAna`PF|Ihz;!r>!-4a4-d!w)wI?8u`&dilWyP94Vg_iI#Mj9$DUh~e)n nAbJk&$fNnOo86f+XC9RQRqQFcdVEF&00000NkvXXu0mjfF{R0n diff --git a/mods/content/government/icons/banner_symbols.dmi b/mods/content/government/icons/banner_symbols.dmi new file mode 100644 index 0000000000000000000000000000000000000000..ae958dd1f6d3cd8922d86cbc4a89b1246b995d86 GIT binary patch literal 461 zcmV;;0W$uHP)00DGTPE!Ct=GbNc003@!R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSI6p^;i!&v&s2HS$i!-e#F*g;&Hbkf?%PhjBMp?nt&joBT0BD{gPC*sySO5S3 z?@2^KR9J=Wl}!$WAPj}E;Q+u13Uh-wfs5Y%OjBn>j8c9!E++jFjnVgLOHDCGQJT0B zQNsKL-~qaNFpwQ+QgqnR<}~uExV)n28o(bK^qgKlLWol6r;#;Wap)J#X=D-X zR~uN$#;~=U5#!3LIG{iEVUW^kMN$48TIFw$?(Zy-tgnq>A~^&P;z~vh0~mjeU@6~f z#Bi&jZGiB;o``nKM=Tp5`Rh9tTd|1q)Gr}O-dC(_QqwTqhv`n&Y5hxzq9{AR`8zQ% zzVlQbs(1amg`m}rI(TW?JmSglykWYA>0O%dgQ65Zp Date: Mon, 2 Dec 2024 00:55:55 +0000 Subject: [PATCH 0014/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 11782eae61ac..af37f2ad8369 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -106,12 +106,6 @@

    MistakeNot4892 updated:

    • Sledgehammers can demolish non-reinforced walls.
    - -

    30 September 2024

    -

    Atermonera updated:

    -
      -
    • Fixed some grammatical issues in location event strings.
    • -
    From 7ac5722f031a3d8392ac99d80f20e2c0bffdf912 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 30 Nov 2024 12:13:57 +1100 Subject: [PATCH 0015/1331] Adding a priority list system for mouse cursor icons. --- code/_helpers/cmp.dm | 5 +- code/_helpers/visual_filters.dm | 5 +- .../client/mouse_pointer/_mouse_pointer.dm | 67 ++++++++++++++++++ .../mouse_pointer_definitions.dm | 31 ++++++++ code/modules/mob/login.dm | 1 + code/modules/mob/mob.dm | 9 ++- code/modules/mob/mob_defines.dm | 3 - .../mouse_pointers/examine_pointer.dmi | Bin 430 -> 609 bytes nebula.dme | 2 + 9 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 code/modules/client/mouse_pointer/_mouse_pointer.dm create mode 100644 code/modules/client/mouse_pointer/mouse_pointer_definitions.dm diff --git a/code/_helpers/cmp.dm b/code/_helpers/cmp.dm index e84ecc341fcc..32637fecad8d 100644 --- a/code/_helpers/cmp.dm +++ b/code/_helpers/cmp.dm @@ -154,4 +154,7 @@ if(a == prerequisite) return -1 // goes after return cmp_skill_asc(a, GET_DECL(b.prerequisites[1])) - return cmp_name_or_type_asc(a, b) \ No newline at end of file + return cmp_name_or_type_asc(a, b) + +/proc/cmp_priority_list(list/A, list/B) + return A["priority"] - B["priority"] diff --git a/code/_helpers/visual_filters.dm b/code/_helpers/visual_filters.dm index deb7162f1964..ad1fc9104209 100644 --- a/code/_helpers/visual_filters.dm +++ b/code/_helpers/visual_filters.dm @@ -5,9 +5,6 @@ /atom/movable var/list/filter_data // For handling persistent filters -/proc/cmp_filter_data_priority(list/A, list/B) - return A["priority"] - B["priority"] - // Defining this for future proofing and ease of searching for erroneous usage. /image/proc/add_filter(filter_name, priority, list/params) filters += filter(arglist(params)) @@ -35,7 +32,7 @@ /atom/movable/proc/update_filters() filters = null - filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE) + filter_data = sortTim(filter_data, /proc/cmp_priority_list, TRUE) for(var/f in filter_data) var/list/data = filter_data[f] var/list/arguments = data.Copy() diff --git a/code/modules/client/mouse_pointer/_mouse_pointer.dm b/code/modules/client/mouse_pointer/_mouse_pointer.dm new file mode 100644 index 000000000000..3f42c8b3f4e8 --- /dev/null +++ b/code/modules/client/mouse_pointer/_mouse_pointer.dm @@ -0,0 +1,67 @@ +/* + * Adds pointer entries to clients to allow for multiple sources wanting to modify the cursor at once. + * - add_mouse_pointer(pointer_type, pointer_priority, icon_index) will add or replace a pointer of the specified /decl/mouse_pointer type. + * - remove_mouse_pointer(pointer_type) will clear that entry. + * - Updates are handled automatically by adding/removing, other procs should not be used without a good reason. + */ + +/client + VAR_PRIVATE/list/_mouse_pointers + +/client/proc/clear_mouse_pointers() + if(LAZYLEN(_mouse_pointers)) + LAZYCLEARLIST(_mouse_pointers) + update_mouse_pointer() + return TRUE + return FALSE + +/client/proc/set_mouse_pointer_icon(new_cursor) + if(isnull(new_cursor)) + new_cursor = initial(mouse_pointer_icon) + if(mouse_pointer_icon != new_cursor) + mouse_pointer_icon = new_cursor + return TRUE + return FALSE + +/client/proc/add_mouse_pointer(pointer_type, pointer_priority = 1, icon_index = 1) + + // Is an identical pointer already being tracked? + var/decl/mouse_pointer/pointer_decl = ispath(pointer_type) ? GET_DECL(pointer_type) : pointer_type + if(!isnum(icon_index) || icon_index < 1 || icon_index > length(pointer_decl.icons)) + CRASH("Invalid icon_index passed to add_mouse_pointer() for [pointer_type].") + + var/pointer_icon = pointer_decl.icons[icon_index] + var/list/comparing = _mouse_pointers?[pointer_type] + if(islist(comparing) && comparing["icon"] == pointer_icon && comparing["priority"] == pointer_priority) + return FALSE + + // Update our list entry. If we have multiple pointers, sort by priority. + var/need_update = !(pointer_type in _mouse_pointers) + LAZYSET(_mouse_pointers, pointer_type, list("icon" = pointer_icon, "priority" = pointer_priority)) + if(LAZYLEN(_mouse_pointers) > 1) + _mouse_pointers = sortTim(_mouse_pointers, /proc/cmp_priority_list, TRUE) + need_update = TRUE + + // Refresh if needed. + if(need_update) + update_mouse_pointer() + +/client/proc/remove_mouse_pointer(pointer_type) + if(!_mouse_pointers?[pointer_type]) + return FALSE + var/current_pointer = _mouse_pointers[1] + LAZYREMOVE(_mouse_pointers, pointer_type) + if(pointer_type == current_pointer) + update_mouse_pointer() + return TRUE + +/client/proc/update_mouse_pointer() + if(!LAZYLEN(_mouse_pointers)) + return set_mouse_pointer_icon() + var/list/pointer = _mouse_pointers[_mouse_pointers[1]] + if(!islist(pointer)) + return set_mouse_pointer_icon() + var/set_pointer = pointer["icon"] + if(isicon(set_pointer)) + return set_mouse_pointer_icon(set_pointer) + return set_mouse_pointer_icon() diff --git a/code/modules/client/mouse_pointer/mouse_pointer_definitions.dm b/code/modules/client/mouse_pointer/mouse_pointer_definitions.dm new file mode 100644 index 000000000000..f03d328975a1 --- /dev/null +++ b/code/modules/client/mouse_pointer/mouse_pointer_definitions.dm @@ -0,0 +1,31 @@ +/decl/mouse_pointer + abstract_type = /decl/mouse_pointer + /// Icon to set on the client for this cursor. + var/list/icons + +/decl/mouse_pointer/Initialize() + . = ..() + if(icons && !islist(icons)) + icons = list(icons) + +/decl/mouse_pointer/validate() + . = ..() + if(length(icons)) + var/static/list/check_states = list( + "", + "over", + "drag", + "drop", + "all" + ) + for(var/icon in icons) + for(var/check_state in check_states) + if(!check_state_in_icon(check_state, icon)) + . += "missing state '[check_state]' from icon '[icon]'" + else + . += "null or empty icon list" + +// Subtypes for use in add_mouse_pointer() below. +/decl/mouse_pointer/examine + uid = "pointer_examine" + icons = 'icons/effects/mouse_pointers/examine_pointer.dmi' diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index a2d7f65ab163..7903631bb68a 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -59,6 +59,7 @@ /mob/Login() + client.clear_mouse_pointers() // in case we are transferring mobs. global.player_list |= src update_Login_details() world.update_status() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 7554d0e91f55..edf74c14dd0f 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1135,11 +1135,10 @@ /mob/proc/update_mouse_pointer() if(!client) return - - client.mouse_pointer_icon = initial(client.mouse_pointer_icon) - - if(examine_cursor_icon && client.keys_held["Shift"]) - client.mouse_pointer_icon = examine_cursor_icon + if(client.keys_held["Shift"]) + client.add_mouse_pointer(/decl/mouse_pointer/examine) + else + client.remove_mouse_pointer(/decl/mouse_pointer/examine) /mob/keybind_face_direction(direction) facedir(direction) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index c6b35aa28723..957cdba36a3b 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -70,9 +70,6 @@ */ var/obj/screen/zone_selector/zone_sel = null - /// Cursor icon used when holding shift over things. - var/examine_cursor_icon = 'icons/effects/mouse_pointers/examine_pointer.dmi' - var/damageoverlaytemp = 0 var/obj/machinery/machine = null diff --git a/icons/effects/mouse_pointers/examine_pointer.dmi b/icons/effects/mouse_pointers/examine_pointer.dmi index 41a9cec13b07fc8954d21e7877b2b1f0ba7a1374..a565a554fa7dc443874466b4e8034248c13b3a32 100644 GIT binary patch delta 450 zcmV;z0X_b%1K|WEiBL{Q4GJ0x0000DNk~Le0001B0000$2m=5B0MhD$JOBUyMUf>& z7$GDwVtogp00001bW%=J06^y0W&i*Hh>?J3e-S$JfmV>NE2Stgog7{H1*Gdr%*i2I zld^)Vp9?tT07Y_FeahjX4FCWEHc3Q5R9J=WmceeqFcbt$TjIPF6Oz*Z|JC}rE#i=5 zElqp@mIH@bd81&Y3GWBbTUZO_;qZ~uke5PB?uDj27uxc$_519>!yP|*_U3oEi=J++ ze}Z|8y_xm7(~8@WKW57f%0n0J^ORCLd7}Y8#(r?_jeem)c`n$H(s?|mwD<^=d%;o~ zFXQ?Fi2oHfpAd0>hbG=J;^h{YxFv{}d;FUirv^NPc>5<#iv`L{qh!jN8}R>1{+{Nq z#RlcK%-8GpR)g}~n8gW@Kg_~rfpTxOc;&A=Qn3H4VE?ZfK>lAb!2DkiK>wEm)c;lX z^M4iU|MGn3{}tym|5u^@uY&zw4?zE~7-0TiHvs%!4^aPC!Tztp{a=Oqf5igz|Eh_w sga4~=|CbBcou6a@`oCUa{FNul4+@+uh`ls(cmMzZ07*qoM6N<$f^%-yT>t<8 delta 270 zcmaFJvW{7`Gr-TCmrII^fq{Y7)59eQNGpIa2Q!d#S<-b3NI6YZbrY4^>wXl-W-JNv z3ubV5b|VeQDVo@vQNMGFXw2W317QpdIS*u}2Ao~V2$Y%Q>Eak-(R=p7S)oG;JS+*T zoIG;7E`0kNf0f0$kA2fUnd!{!f7Sl9yiVE4Vt(0|`4?Y6KEpSz0{IJntL%Pdd`$I8 z@s#UynK^T_)cWU<;+vI)KGq6OJbI+SVcuHviSA0T7?rXkU5_b?Y;gCe6ybT$a^?fy zH!ipJFaCev3Y2Yhct3wlvcLvM~b%-r_=xcJ376@!4r-}_Gq{|Mxisabe* QJJ6vFp00i_>zopr0E!xE@c;k- diff --git a/nebula.dme b/nebula.dme index 7c4b88297fe0..7de4fd806009 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1870,6 +1870,8 @@ #include "code\modules\client\preferences_spawnpoints.dm" #include "code\modules\client\preferences_storage.dm" #include "code\modules\client\preferences_toggle.dm" +#include "code\modules\client\mouse_pointer\_mouse_pointer.dm" +#include "code\modules\client\mouse_pointer\mouse_pointer_definitions.dm" #include "code\modules\client\preference_setup\_defines.dm" #include "code\modules\client\preference_setup\preference_setup.dm" #include "code\modules\client\preference_setup\antagonism\01_candidacy.dm" From 2aa5e644906c3376786a296d4da7d6ff04fbfc8d Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 2 Dec 2024 19:54:14 -0500 Subject: [PATCH 0016/1331] Add on-examine and alt interaction functionality to stock parts --- .../machinery/_machines_base/machinery.dm | 2 + .../_machines_base/machinery_components.dm | 9 ++++ .../stock_parts/_stock_parts.dm | 10 ++++ .../_machines_base/stock_parts/card_reader.dm | 7 +++ .../_machines_base/stock_parts/disk_reader.dm | 5 ++ .../_machines_base/stock_parts/item_holder.dm | 48 ++++++++++++++++++- code/modules/paperwork/faxmachine.dm | 18 ------- 7 files changed, 79 insertions(+), 20 deletions(-) diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index 290540f78f58..6736ad221bbe 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -433,6 +433,8 @@ Class Procs: var/obj/item/fake_thing = type parts += "[num2text(missing[type])] [initial(fake_thing.name)]" to_chat(user, "\The [src] is missing [english_list(parts)], rendering it inoperable.") + for(var/obj/item/stock_parts/part in component_parts) + part.on_machine_examined(user) // This is really pretty crap and should be overridden for specific machines. /obj/machinery/fluid_act(var/datum/reagents/fluids) diff --git a/code/game/machinery/_machines_base/machinery_components.dm b/code/game/machinery/_machines_base/machinery_components.dm index f60963589294..379a5dea9479 100644 --- a/code/game/machinery/_machines_base/machinery_components.dm +++ b/code/game/machinery/_machines_base/machinery_components.dm @@ -352,3 +352,12 @@ Standard helpers for users interacting with machinery parts. var/present = number_of_components(required_type, only_functional) if(present < needed) LAZYSET(., required_type, needed - present) + +/obj/machinery/get_alt_interactions(mob/user) + . = ..() + for(var/obj/item/stock_parts/component in component_parts) + if(!components_are_accessible(component.type)) + continue + var/list/machine_alt_interactions = component.get_machine_alt_interactions(user) + if(LAZYLEN(machine_alt_interactions)) + LAZYADD(., machine_alt_interactions) diff --git a/code/game/machinery/_machines_base/stock_parts/_stock_parts.dm b/code/game/machinery/_machines_base/stock_parts/_stock_parts.dm index f4aab2938f7e..7ce4792bc9fa 100644 --- a/code/game/machinery/_machines_base/stock_parts/_stock_parts.dm +++ b/code/game/machinery/_machines_base/stock_parts/_stock_parts.dm @@ -109,3 +109,13 @@ if(istype(M) && (src in M.component_parts)) return ..() + +/// Alt-click interactions provided to our parent machine. +/obj/item/stock_parts/proc/get_machine_alt_interactions(mob/user) + SHOULD_CALL_PARENT(TRUE) + SHOULD_BE_PURE(TRUE) + RETURN_TYPE(/list) + . = list() + +/// A stub for showing messages based on part status when a machine is examined. +/obj/item/stock_parts/proc/on_machine_examined(mob/user) \ No newline at end of file diff --git a/code/game/machinery/_machines_base/stock_parts/card_reader.dm b/code/game/machinery/_machines_base/stock_parts/card_reader.dm index f78430de17d5..aecd5af4e4f9 100644 --- a/code/game/machinery/_machines_base/stock_parts/card_reader.dm +++ b/code/game/machinery/_machines_base/stock_parts/card_reader.dm @@ -14,6 +14,7 @@ /decl/material/solid/fiberglass = MATTER_AMOUNT_TRACE, ) max_health = ITEM_HEALTH_NO_DAMAGE + eject_handler = /decl/interaction_handler/remove_held_item/card var/should_swipe = FALSE //Whether the card should only be swiped instead of being inserted var/obj/item/card/inserted_card //Card currently in the slot @@ -67,3 +68,9 @@ /obj/item/stock_parts/item_holder/card_reader/proc/get_emag_card() return istype(inserted_card, /obj/item/card/emag) ? inserted_card : null + +/decl/interaction_handler/remove_held_item/card + name = "Eject Card" + icon = 'icons/screen/radial.dmi' + icon_state = "radial_eject_id" + expected_component_type = /obj/item/stock_parts/item_holder/card_reader \ No newline at end of file diff --git a/code/game/machinery/_machines_base/stock_parts/disk_reader.dm b/code/game/machinery/_machines_base/stock_parts/disk_reader.dm index eb6a5b8fe72a..e61cf6be322d 100644 --- a/code/game/machinery/_machines_base/stock_parts/disk_reader.dm +++ b/code/game/machinery/_machines_base/stock_parts/disk_reader.dm @@ -14,6 +14,7 @@ /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, ) max_health = ITEM_HEALTH_NO_DAMAGE + eject_handler = /decl/interaction_handler/remove_held_item/disk var/obj/item/disk/disk //Disk currently inserted /obj/item/stock_parts/item_holder/disk_reader/buildable @@ -38,3 +39,7 @@ /obj/item/stock_parts/item_holder/disk_reader/get_description_insertable() return "disk" + +/decl/interaction_handler/remove_held_item/disk + name = "Eject Disk" + expected_component_type = /obj/item/stock_parts/item_holder/disk_reader \ No newline at end of file diff --git a/code/game/machinery/_machines_base/stock_parts/item_holder.dm b/code/game/machinery/_machines_base/stock_parts/item_holder.dm index 170d90d0f392..9f6d75f6e6ce 100644 --- a/code/game/machinery/_machines_base/stock_parts/item_holder.dm +++ b/code/game/machinery/_machines_base/stock_parts/item_holder.dm @@ -5,8 +5,12 @@ name = null desc = null icon = 'icons/obj/items/stock_parts/modular_components.dmi' + var/decl/interaction_handler/eject_handler // The interaction handler type used for alt-interactions. var/datum/callback/on_insert_target //Callback to call when an item is inserted var/datum/callback/on_eject_target //Callback to call when an item is ejected + /// The verb used when a player inserts an item. + /// e.g. You [insert/place/attach] the cup in the cupholder. + var/place_verb = "insert" /obj/item/stock_parts/item_holder/Destroy() unregister_on_insert() @@ -38,6 +42,10 @@ /obj/item/stock_parts/item_holder/proc/get_inserted() return +/obj/item/stock_parts/item_holder/on_machine_examined(mob/user) + if(is_item_inserted()) + to_chat(user, SPAN_INFO("It has \a [get_inserted()] in \the [src].")) + ///Handle putting the object in the component's contents. Doesn't trigger any callbacks, or messages. /obj/item/stock_parts/item_holder/proc/set_inserted(var/obj/O) return @@ -55,7 +63,7 @@ if(user) if(user.try_unequip(O, src)) - to_chat(user, SPAN_NOTICE("You insert \the [O] into \the [src].")) + to_chat(user, SPAN_NOTICE("You [place_verb] \the [O] into \the [src].")) else return FALSE else @@ -111,4 +119,40 @@ QDEL_NULL(on_insert_target) /obj/item/stock_parts/item_holder/proc/unregister_on_eject() - QDEL_NULL(on_eject_target) \ No newline at end of file + QDEL_NULL(on_eject_target) + +// Alt interaction handling +/decl/interaction_handler/remove_held_item + abstract_type = /decl/interaction_handler/remove_held_item + expected_target_type = /obj/machinery + name = "Eject Item" + icon = 'icons/screen/radial.dmi' + icon_state = "radial_eject" + var/obj/item/stock_parts/item_holder/expected_component_type + +/decl/interaction_handler/remove_held_item/validate() + . = ..() + if(!ispath(expected_component_type)) + . += "Expected component type was [isnull(expected_component_type) ? "NULL" : expected_component_type], expected /obj/item/stock_parts/item_holder subtype" + +/decl/interaction_handler/remove_held_item/is_possible(atom/target, mob/user, obj/item/prop) + . = ..() + if(.) + var/obj/machinery/target_machine = target + // Check all components, not just the first one. Maybe we have multiple! + for(var/obj/item/stock_parts/item_holder/holder in target_machine.get_all_components_of_type(expected_component_type)) + if(holder.is_item_inserted()) + return TRUE + +/decl/interaction_handler/remove_held_item/invoked(atom/target, mob/user, obj/item/prop) + var/obj/machinery/target_machine = target + // Check all components, not just the first one. Maybe we have multiple! + for(var/obj/item/stock_parts/item_holder/holder in target_machine.get_all_components_of_type(expected_component_type)) + if(holder.is_item_inserted()) + holder.eject_item(user) + return + +/obj/item/stock_parts/item_holder/get_machine_alt_interactions(mob/user) + . = ..() + if(ispath(eject_handler)) + LAZYADD(., eject_handler) diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 1a5bbeb9e745..7aad7aace238 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -716,21 +716,3 @@ var/global/list/adminfaxes = list() //cache for faxes that have been sent to for(var/uri in global.using_map.map_admin_faxes) var/list/contact_info = global.using_map.map_admin_faxes[uri] add_quick_dial_contact(contact_info["name"], uri) - -/obj/machinery/faxmachine/get_alt_interactions(mob/user) - . = ..() - LAZYADD(., /decl/interaction_handler/fax_remove_card) - -/decl/interaction_handler/fax_remove_card - name = "Remove ID Card" - expected_target_type = /obj/machinery/faxmachine - -/decl/interaction_handler/fax_remove_card/is_possible(atom/target, mob/user, obj/item/prop) - . = ..() - if(.) - var/obj/machinery/faxmachine/fax = target - return !!(fax.card_reader?.get_inserted()) - -/decl/interaction_handler/fax_remove_card/invoked(atom/target, mob/user, obj/item/prop) - var/obj/machinery/faxmachine/fax = target - fax.eject_card(user) From 4f559f25f502817d7827172cccfc16bae0bda6d4 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 2 Dec 2024 20:54:36 -0500 Subject: [PATCH 0017/1331] Make research machines use disk reader components --- .../_machines_base/stock_parts/item_holder.dm | 2 +- .../circuitboards/computer/computer.dm | 4 + .../circuitboards/machinery/research.dm | 1 + code/modules/research/design_console.dm | 73 +++++----- code/modules/research/design_database.dm | 125 +++++++++--------- nano/templates/design_database.tmpl | 14 +- 6 files changed, 109 insertions(+), 110 deletions(-) diff --git a/code/game/machinery/_machines_base/stock_parts/item_holder.dm b/code/game/machinery/_machines_base/stock_parts/item_holder.dm index 9f6d75f6e6ce..3462f18f5605 100644 --- a/code/game/machinery/_machines_base/stock_parts/item_holder.dm +++ b/code/game/machinery/_machines_base/stock_parts/item_holder.dm @@ -83,7 +83,7 @@ return var/obj/O = get_inserted() - if(user) + if(user && loc.Adjacent(user)) // Check adjacency in case this is called via UI stuff from a distance. user.put_in_hands(O) to_chat(user, SPAN_NOTICE("You remove \the [O] from \the [src].")) else diff --git a/code/game/objects/items/weapons/circuitboards/computer/computer.dm b/code/game/objects/items/weapons/circuitboards/computer/computer.dm index 9c8b800d3c1a..c013c88bdf5c 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/computer.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/computer.dm @@ -91,6 +91,10 @@ /obj/item/stock_parts/circuitboard/design_console name = "circuitboard (design database console)" build_path = /obj/machinery/computer/design_console + additional_spawn_components = list( + /obj/item/stock_parts/power/apc/buildable = 1, + /obj/item/stock_parts/item_holder/disk_reader/buildable = 1, + ) /obj/item/stock_parts/circuitboard/central_atmos name = "circuitboard (central atmospherics computer)" diff --git a/code/game/objects/items/weapons/circuitboards/machinery/research.dm b/code/game/objects/items/weapons/circuitboards/machinery/research.dm index 5d31dd554daa..efe256ceef44 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/research.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/research.dm @@ -265,6 +265,7 @@ req_components = list() additional_spawn_components = list( /obj/item/stock_parts/power/apc/buildable = 1, + /obj/item/stock_parts/item_holder/disk_reader/buildable = 1, /obj/item/stock_parts/console_screen = 1, /obj/item/stock_parts/keyboard = 1 ) \ No newline at end of file diff --git a/code/modules/research/design_console.dm b/code/modules/research/design_console.dm index 73b6abc3255a..5e2e43978ad3 100644 --- a/code/modules/research/design_console.dm +++ b/code/modules/research/design_console.dm @@ -1,11 +1,16 @@ /obj/machinery/computer/design_console name = "design database console" desc = "A console for interfacing with a research and development design network." + maximum_component_parts = list( + /obj/item/stock_parts/item_holder/disk_reader = 1, + /obj/item/stock_parts = 15, + ) + /// A cached reference to our disk reader part, if present. + var/obj/item/stock_parts/item_holder/disk_reader/disk_reader var/initial_network_id var/initial_network_key var/list/local_cache - var/obj/item/disk/design_disk/disk var/obj/machinery/design_database/viewing_database var/showing_designs = FALSE @@ -13,36 +18,34 @@ . = ..() set_extension(src, /datum/extension/network_device, initial_network_id, initial_network_key, RECEIVER_STRONG_WIRELESS) +/obj/machinery/computer/design_console/Destroy() + viewing_database = null + disk_reader = null + return ..() + /obj/machinery/computer/design_console/modify_mapped_vars(map_hash) ..() ADJUST_TAG_VAR(initial_network_id, map_hash) +/obj/machinery/computer/design_console/RefreshParts() + . = ..() + disk_reader = get_component_of_type(/obj/item/stock_parts/item_holder/disk_reader) + if(disk_reader) + disk_reader.register_on_insert(CALLBACK(src, PROC_REF(update_ui))) + disk_reader.register_on_eject(CALLBACK(src, PROC_REF(update_ui))) + +/obj/machinery/computer/design_console/proc/try_get_disk() + return disk_reader?.get_inserted() + +/obj/machinery/computer/design_console/proc/update_ui() + SSnano.update_uis(src) + /obj/machinery/computer/design_console/handle_post_network_connection() ..() sync_network() -/obj/machinery/computer/design_console/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/disk/design_disk)) - if(disk) - to_chat(user, SPAN_WARNING("\The [src] already has a disk inserted.")) - return TRUE - if(user.try_unequip(I, src)) - visible_message("\The [user] slots \the [I] into \the [src].") - disk = I - return TRUE - . = ..() - /obj/machinery/computer/design_console/proc/eject_disk(var/mob/user) - if(disk) - disk.dropInto(loc) - if(user) - if(!issilicon(user)) - user.put_in_hands(disk) - if(Adjacent(user, src)) - visible_message(SPAN_NOTICE("\The [user] removes \the [disk] from \the [src].")) - disk = null - return TRUE - return FALSE + return !!disk_reader.eject_item(user) /obj/machinery/computer/design_console/interface_interact(mob/user) ui_interact(user) @@ -55,9 +58,13 @@ var/datum/computer_network/network = device.get_network() data["network_id"] = device.network_tag + var/obj/item/disk/design_disk/disk = try_get_disk() if(disk) data["disk_name"] = disk.name - data["disk_tech"] = disk.blueprint ? disk.blueprint.name : "no design saved" + if(istype(disk)) + data["disk_tech"] = disk.blueprint ? disk.blueprint.name : "no design saved" + else + data["disk_tech"] = "invalid data format" else data["disk_name"] = "no disk loaded" @@ -159,7 +166,8 @@ if(href_list["save_design"]) var/datum/fabricator_recipe/design = locate(href_list["save_design"]) - if(istype(design) && disk) + var/obj/item/disk/design_disk/disk = try_get_disk() + if(istype(design) && istype(disk)) disk.blueprint = design disk.SetName("[initial(disk.name)] ([disk.blueprint.name])") return TOPIC_REFRESH @@ -219,20 +227,3 @@ var/list/techs = get_network_tech_levels() for(var/obj/machinery/fabricator/fab in network.get_devices_by_type(/obj/machinery/fabricator)) fab.refresh_design_cache(techs) - -/obj/machinery/computer/design_console/get_alt_interactions(var/mob/user) - . = ..() - LAZYADD(., /decl/interaction_handler/remove_disk/console) - -/decl/interaction_handler/remove_disk/console - expected_target_type = /obj/machinery/computer/design_console - -/decl/interaction_handler/remove_disk/console/is_possible(atom/target, mob/user, obj/item/prop) - . = ..() - if(.) - var/obj/machinery/computer/design_console/D = target - . = !!D.disk - -/decl/interaction_handler/remove_disk/console/invoked(atom/target, mob/user, obj/item/prop) - var/obj/machinery/computer/design_console/D = target - D.eject_disk(user) diff --git a/code/modules/research/design_database.dm b/code/modules/research/design_database.dm index 11e53477ef5d..5c5a6b3cc410 100644 --- a/code/modules/research/design_database.dm +++ b/code/modules/research/design_database.dm @@ -17,12 +17,17 @@ var/global/list/default_initial_tech_levels construct_state = /decl/machine_construction/default/panel_closed uncreated_component_parts = null stat_immune = 0 + maximum_component_parts = list( + /obj/item/stock_parts/item_holder/disk_reader = 1, + /obj/item/stock_parts = 15, + ) + /// A cached reference to our disk reader part, if present. + var/obj/item/stock_parts/item_holder/disk_reader/disk_reader var/initial_network_id var/initial_network_key var/list/tech_levels var/need_disk_operation = FALSE - var/obj/item/disk/tech_disk/disk var/sync_policy = SYNC_PULL_NETWORK|SYNC_PUSH_NETWORK|SYNC_PULL_DISK /obj/machinery/design_database/proc/toggle_sync_policy_flag(var/sync_flag) @@ -36,13 +41,17 @@ var/global/list/default_initial_tech_levels var/list/data = list() var/datum/extension/network_device/device = get_extension(src, /datum/extension/network_device) data["network_id"] = device.network_tag + var/obj/item/disk/tech_disk/disk = try_get_disk() if(disk) data["disk_name"] = disk.name - var/list/tech_data = list() - for(var/tech in disk.stored_tech) - var/decl/research_field/field = SSfabrication.get_research_field_by_id(tech) - tech_data += list(list("field" = field.name, "desc" = field.desc, "level" = "[disk.stored_tech[tech]].0 GQ")) - data["disk_tech"] = tech_data + if(istype(disk)) + var/list/tech_data = list() + for(var/tech in disk.stored_tech) + var/decl/research_field/field = SSfabrication.get_research_field_by_id(tech) + tech_data += list(list("field" = field.name, "desc" = field.desc, "level" = "[disk.stored_tech[tech]].0 GQ")) + data["disk_tech"] = tech_data + else + data["disk_error"] = "invalid data format" else data["disk_name"] = "no disk loaded" @@ -116,26 +125,30 @@ var/global/list/default_initial_tech_levels return // Read or write from a loaded disk. + var/obj/item/disk/tech_disk/disk = try_get_disk() if(disk && need_disk_operation) - if(sync_policy & SYNC_PULL_DISK) - var/new_tech = FALSE - for(var/tech in disk.stored_tech) - if(tech_levels[tech] < disk.stored_tech[tech]) - tech_levels[tech] = disk.stored_tech[tech] - new_tech = TRUE - if(new_tech) - visible_message(SPAN_NOTICE("\The [src] clicks and chirps as it reads from \the [disk].")) - if((sync_policy & SYNC_PUSH_NETWORK) && !sync_design_consoles()) - visible_message(SPAN_WARNING("\The [src] flashes an error light from its network interface.")) - - if(sync_policy & SYNC_PUSH_DISK) - var/new_tech - for(var/tech in tech_levels) - if(tech_levels[tech] > LAZYACCESS(disk.stored_tech, tech)) - new_tech = TRUE - LAZYSET(disk.stored_tech, tech, tech_levels[tech]) - if(new_tech) - visible_message(SPAN_NOTICE("\The [src] whirrs and drones as it writes to \the [disk].")) + if(!istype(disk)) // wrong type of disk! + visible_message(SPAN_WARNING("\The [src] whirrs and drones, before emitting an ominous grinding sound.")) + else + if(sync_policy & SYNC_PULL_DISK) + var/new_tech = FALSE + for(var/tech in disk.stored_tech) + if(tech_levels[tech] < disk.stored_tech[tech]) + tech_levels[tech] = disk.stored_tech[tech] + new_tech = TRUE + if(new_tech) + visible_message(SPAN_NOTICE("\The [src] clicks and chirps as it reads from \the [disk].")) + if((sync_policy & SYNC_PUSH_NETWORK) && !sync_design_consoles()) + visible_message(SPAN_WARNING("\The [src] flashes an error light from its network interface.")) + + if(sync_policy & SYNC_PUSH_DISK) + var/new_tech + for(var/tech in tech_levels) + if(tech_levels[tech] > LAZYACCESS(disk.stored_tech, tech)) + new_tech = TRUE + LAZYSET(disk.stored_tech, tech, tech_levels[tech]) + if(new_tech) + visible_message(SPAN_NOTICE("\The [src] whirrs and drones as it writes to \the [disk].")) visible_message("The I/O light on \the [src] stops blinking.") need_disk_operation = FALSE @@ -148,49 +161,35 @@ var/global/list/default_initial_tech_levels /obj/machinery/design_database/Destroy() design_databases -= src - QDEL_NULL(disk) + disk_reader = null . = ..() -/obj/machinery/design_database/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/disk/tech_disk)) - if(disk) - to_chat(user, SPAN_WARNING("\The [src] already has a disk inserted.")) - return TRUE - if(user.try_unequip(I, src)) - visible_message("\The [user] slots \the [I] into \the [src].") - visible_message(SPAN_NOTICE("\The [src]'s I/O light begins to blink.")) - disk = I - need_disk_operation = TRUE - return TRUE +/obj/machinery/design_database/proc/on_insert_disk(obj/item/disk/D, mob/user) + visible_message(SPAN_NOTICE("\The [src]'s I/O light begins to blink.")) + need_disk_operation = TRUE + update_ui() - . = ..() +/obj/machinery/design_database/proc/on_eject_disk(obj/item/disk/D, mob/user) + need_disk_operation = FALSE + update_ui() -/obj/machinery/design_database/proc/eject_disk(var/mob/user) - if(disk) - disk.dropInto(loc) - need_disk_operation = FALSE - if(user) - if(!issilicon(user)) - user.put_in_hands(disk) - if(Adjacent(user, src)) - visible_message(SPAN_NOTICE("\The [user] removes \the [disk] from \the [src].")) - disk = null - return TRUE - return FALSE - -/obj/machinery/design_database/get_alt_interactions(var/mob/user) +/obj/machinery/design_database/RefreshParts() . = ..() - LAZYADD(., /decl/interaction_handler/remove_disk/designs) + disk_reader = get_component_of_type(/obj/item/stock_parts/item_holder/disk_reader) + if(disk_reader) + disk_reader.register_on_insert(CALLBACK(src, PROC_REF(on_insert_disk))) + disk_reader.register_on_eject(CALLBACK(src, PROC_REF(on_eject_disk))) -/decl/interaction_handler/remove_disk/designs - expected_target_type = /obj/machinery/design_database +/obj/machinery/design_database/proc/try_get_disk() + return disk_reader?.get_inserted() -/decl/interaction_handler/remove_disk/designs/is_possible(atom/target, mob/user, obj/item/prop) - . = ..() - if(.) - var/obj/machinery/design_database/D = target - . = !!D.disk +/obj/machinery/design_database/proc/update_ui() + SSnano.update_uis(src) -/decl/interaction_handler/remove_disk/designs/invoked(atom/target, mob/user, obj/item/prop) - var/obj/machinery/design_database/D = target - D.eject_disk(user) +// used for, specifically, removing a disk via the UI +/obj/machinery/design_database/proc/eject_disk(var/mob/user) + if(!disk_reader) + to_chat(user, SPAN_WARNING("\The [src] has no disk drive installed.")) + return FALSE + . = !isnull(disk_reader.eject_item(user)) + update_ui() diff --git a/nano/templates/design_database.tmpl b/nano/templates/design_database.tmpl index f09a5415a942..db1dd81ea572 100644 --- a/nano/templates/design_database.tmpl +++ b/nano/templates/design_database.tmpl @@ -6,12 +6,16 @@
    External storage
    - {{if data.disk_tech}} + {{if data.disk_tech || data.disk_error}} - - {{for data.disk_tech}} - - {{/for}} + {{if data.disk_error}} + invalid data format + {{else}} + + {{for data.disk_tech}} + + {{/for}} + {{/if}} {{else}} {{/if}} From 4cde3d4974eec510b4cb41e427c2e49a6d657e20 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 2 Dec 2024 20:59:43 -0500 Subject: [PATCH 0018/1331] Add cupholder stock part --- .../_machines_base/stock_parts/cupholder.dm | 50 ++++++++++++++++++ .../items/stock_parts/modular_components.dmi | Bin 2686 -> 2814 bytes nebula.dme | 1 + 3 files changed, 51 insertions(+) create mode 100644 code/game/machinery/_machines_base/stock_parts/cupholder.dm diff --git a/code/game/machinery/_machines_base/stock_parts/cupholder.dm b/code/game/machinery/_machines_base/stock_parts/cupholder.dm new file mode 100644 index 000000000000..23f56f7772cb --- /dev/null +++ b/code/game/machinery/_machines_base/stock_parts/cupholder.dm @@ -0,0 +1,50 @@ +/obj/item/stock_parts/item_holder/cupholder + name = "cupholder" + desc = "A holder for cups." + icon = 'icons/obj/items/stock_parts/modular_components.dmi' + icon_state = "cupholder" + material = /decl/material/solid/organic/plastic + part_flags = PART_FLAG_HAND_REMOVE + place_verb = "place" + eject_handler = /decl/interaction_handler/remove_held_item/cup + var/image/cupholder_overlay + var/obj/item/cup + +/obj/item/stock_parts/item_holder/cupholder/Destroy() + QDEL_NULL(cup) + . = ..() + +/obj/item/stock_parts/item_holder/cupholder/is_item_inserted() + return !isnull(cup) + +/obj/item/stock_parts/item_holder/cupholder/is_accepted_type(obj/O) + var/static/allowed_cup_types + if(!allowed_cup_types) + allowed_cup_types = typecacheof(list( + /obj/item/chems/drinks/cans, + /obj/item/chems/drinks/bottle, + /obj/item/chems/glass/bottle, + /obj/item/chems/drinks/juicebox, + /obj/item/chems/drinks/glass2, + /obj/item/chems/drinks/h_chocolate, + /obj/item/chems/drinks/dry_ramen, + /obj/item/chems/drinks/tea, + /obj/item/chems/glass/handmade/cup, + /obj/item/chems/glass/handmade/mug, + /obj/item/chems/drinks/shaker, + /obj/item/chems/drinks/flask + )) + return is_type_in_typecache(O, allowed_cup_types) + +/obj/item/stock_parts/item_holder/cupholder/get_inserted() + return cup + +/obj/item/stock_parts/item_holder/cupholder/set_inserted(obj/O) + cup = O + +/obj/item/stock_parts/item_holder/cupholder/get_description_insertable() + return "cup" + +/decl/interaction_handler/remove_held_item/cup + name = "Remove Cup" + expected_component_type = /obj/item/stock_parts/item_holder/cupholder \ No newline at end of file diff --git a/icons/obj/items/stock_parts/modular_components.dmi b/icons/obj/items/stock_parts/modular_components.dmi index 570e13bbf6529630534e9b607d59071f52a44259..1a471de37faf2a0b2a2c827164ce61fc60ed43ae 100644 GIT binary patch delta 2315 zcmV+m3H0{<6#f;E7Y@J(0{{R3WdfWv0005qu_ey|C&0kKS65eme}9salBK1kZfLEG3X&d7>m8X}s zxiAcPkN*;YnZRo_k7&{uk*fj?Y#6}^(8$hb0p~H#RTXB7ToSQBX3I*8J<)Mge`x?B zIjN!zIe0-gv&083q6biUd25UsoaP`RWxt$BVaqcr_Ic?*G;}kmX-tx7>LC~Grabt% z2`enK?6YBQZA$^G$AlpZne3;bmr{C5)d(xAmDxa^~M}A07etWr3mbBx?kHf>W0tDqL zn!{03Q{!+zFClqtZ7q}+*tMF?&)>Hm;VPe2>(G4F49%OAvjIvAbfew&U zNc8-jor;_Y|JbYkEaRg)X=X>3Y2m=9ylFq|e{w&*w9~59s>IlP6(C zdI0JLhahvZdMC~WGW%NyK@bE%5ClOG1VNDhD@{#)6CgizN}QiQ-E0EnXU>T8OlfIt zJ=@lH?%etFE&Tk#1z{d&Z}0eq5#Z++FADRFX}NSMe;5p2zI+Z6xN=392d-Z2?Ck95 z00pjH6XpbQhZTR5ze(@{Q-D?|5`c44pi?3M=VXMlXK9at14QE1v#p|2#1sfh1i;)9 zAQcJ)z?_W0_TL8VpTw8QCOAcML?9>@5S$`ira-U_$gf`!=7Dxopc9;~3ax%gVYTyS2Zgd5^f`0G?E#G$0x5N2&*M)iD`*u=Yb7wOo@WVx# zf;kz1U1GZZkN~?1b%QTxVK*{C&TeFI9$+`JZn6OaIX}%rGZ`~5+&9`p{0)b|_@$)-(%oNC#e@yQKdH<&bf*=TjAP9mW2!iA$cTMiy zy+;6EqpE6;+SAhm?StgKYHx3^An#NA`ue~$J^*(JGrzCirzF?^z~teBhXlR>UEQSL z+27v}7|?vcIWRB)7|?vsIXF1THL%uBVFU1nu<}RFN0cOk6a+#NfsjNXlx=~qL?A2? ze+UZ&LK1He&kj_muDGukB*LxabuQ`yT`}JC*XY2Jvli!#R)^!`a$dn4GsMS1V-kboi

    8^*UtdWCc9e_-Nd3ehdji(_L9E5xCY$+7<0U02B?6Re3S>qq zlQ_3|YVzXg3qrPwK@bE%5ClOG1VOeJ+3NV)wR?E~)6>&4(=#(O@J@asKfgl*e>kgr z9<@qy2y8J(>e|C0uZgy^NZU)>7chT|02vuzLp(>HU{QP`M>gpb-+Y0iPuPhYl8X(814@rXa_!ibwxB5h((jS-DyyOK0nj|;44yOK0n zH(L;m<*Ps>wn3-BMojeZMKza+#MUv=1n#5VpWY~<@4R;U3XkLy=rv;%o9fE~y- l|HNW>T|a^#2!iAr{{rp=s;_ZEmH7Yw002ovPDHLkV1kbAK(+t? delta 2186 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 Date: Tue, 3 Dec 2024 19:30:16 +1100 Subject: [PATCH 0019/1331] Cleaning up a lot of sprite_sheets setting. --- mods/content/fantasy/_fantasy.dme | 2 +- .../content/fantasy/datum/kobaloi/clothing.dm | 6 +- .../fantasy/items/clothing/_overrides.dm | 15 ++++ mods/content/fantasy/items/clothing/jerkin.dm | 2 +- .../fantasy/items/clothing/loincloth.dm | 2 +- .../fantasy/items/clothing/overrides.dm | 4 - .../fantasy/items/clothing/trousers.dm | 8 +- mods/species/ascent/_ascent.dme | 1 + mods/species/ascent/items/_overrides.dm | 10 +++ mods/species/ascent/items/clothing.dm | 12 +-- mods/species/ascent/items/guns.dm | 2 +- mods/species/ascent/items/rig.dm | 11 ++- .../bayliens/{bayliens.dm => _bayliens.dm} | 13 ++- mods/species/bayliens/_bayliens.dme | 3 +- mods/species/bayliens/_overrides.dm | 7 ++ mods/species/bayliens/adherent/_adherent.dm | 2 - mods/species/bayliens/tajaran/_tajaran.dm | 2 - .../bayliens/tajaran/machinery/suit_cycler.dm | 85 +++++++----------- mods/species/drakes/_drakes.dme | 1 + mods/species/drakes/_overrides.dm | 10 +++ mods/species/drakes/clothing.dm | 28 ++---- mods/species/neoavians/_neoavians.dme | 1 + mods/species/neoavians/_overrides.dm | 7 ++ mods/species/neoavians/clothing.dm | 45 +++++----- .../neoavians/machinery/suit_cycler.dm | 90 ++++++++----------- 25 files changed, 180 insertions(+), 189 deletions(-) create mode 100644 mods/content/fantasy/items/clothing/_overrides.dm delete mode 100644 mods/content/fantasy/items/clothing/overrides.dm create mode 100644 mods/species/ascent/items/_overrides.dm rename mods/species/bayliens/{bayliens.dm => _bayliens.dm} (77%) create mode 100644 mods/species/bayliens/_overrides.dm create mode 100644 mods/species/drakes/_overrides.dm create mode 100644 mods/species/neoavians/_overrides.dm diff --git a/mods/content/fantasy/_fantasy.dme b/mods/content/fantasy/_fantasy.dme index 3cd8559ddf0f..1bf5230189d7 100644 --- a/mods/content/fantasy/_fantasy.dme +++ b/mods/content/fantasy/_fantasy.dme @@ -28,12 +28,12 @@ #include "datum\kobaloi\species.dm" #include "items\material_overrides.dm" #include "items\clothing\_loadout.dm" +#include "items\clothing\_overrides.dm" #include "items\clothing\_recipes.dm" #include "items\clothing\armor.dm" #include "items\clothing\glasses.dm" #include "items\clothing\jerkin.dm" #include "items\clothing\loincloth.dm" -#include "items\clothing\overrides.dm" #include "items\clothing\trousers.dm" #include "props\signpost.dm" // END_INCLUDE diff --git a/mods/content/fantasy/datum/kobaloi/clothing.dm b/mods/content/fantasy/datum/kobaloi/clothing.dm index 1bd63942fbf5..71346517d9c2 100644 --- a/mods/content/fantasy/datum/kobaloi/clothing.dm +++ b/mods/content/fantasy/datum/kobaloi/clothing.dm @@ -1,4 +1,2 @@ -/obj/item/bag/sack/Initialize() - . = ..() - if(!(BODYTYPE_KOBALOI in sprite_sheets)) - LAZYSET(sprite_sheets, BODYTYPE_KOBALOI, 'mods/content/fantasy/icons/clothing/sack_kobaloi.dmi') \ No newline at end of file +/obj/item/bag/sack + _kobaloi_onmob_icon = 'mods/content/fantasy/icons/clothing/sack_kobaloi.dmi' diff --git a/mods/content/fantasy/items/clothing/_overrides.dm b/mods/content/fantasy/items/clothing/_overrides.dm new file mode 100644 index 000000000000..0ecd3b43e1cb --- /dev/null +++ b/mods/content/fantasy/items/clothing/_overrides.dm @@ -0,0 +1,15 @@ +/obj/item + var/_kobaloi_onmob_icon + var/_hnoll_onmob_icon + +/obj/item/setup_sprite_sheets() + . = ..() + if(_kobaloi_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_KOBALOI, _kobaloi_onmob_icon) + if(_hnoll_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_HNOLL, _hnoll_onmob_icon) + +/obj/item/clothing/gloves/setup_equip_flags() + . = ..() + if(!isnull(bodytype_equip_flags) && !(bodytype_equip_flags & BODY_EQUIP_FLAG_EXCLUDE)) + bodytype_equip_flags |= BODY_EQUIP_FLAG_HNOLL \ No newline at end of file diff --git a/mods/content/fantasy/items/clothing/jerkin.dm b/mods/content/fantasy/items/clothing/jerkin.dm index 4944bae19765..dc437db931dd 100644 --- a/mods/content/fantasy/items/clothing/jerkin.dm +++ b/mods/content/fantasy/items/clothing/jerkin.dm @@ -2,6 +2,6 @@ name = "jerkin" desc = "A sturdy jerkin, worn on the upper body." icon = 'mods/content/fantasy/icons/clothing/jerkin.dmi' - sprite_sheets = list(BODYTYPE_HNOLL = 'mods/content/fantasy/icons/clothing/jerkin_hnoll.dmi') + _hnoll_onmob_icon = 'mods/content/fantasy/icons/clothing/jerkin_hnoll.dmi' material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC material = /decl/material/solid/organic/leather diff --git a/mods/content/fantasy/items/clothing/loincloth.dm b/mods/content/fantasy/items/clothing/loincloth.dm index 168127ba6add..5cb8288038e1 100644 --- a/mods/content/fantasy/items/clothing/loincloth.dm +++ b/mods/content/fantasy/items/clothing/loincloth.dm @@ -3,7 +3,7 @@ gender = NEUTER desc = "A simple garment that is worn around the legs and lower body." icon = 'mods/content/fantasy/icons/clothing/loincloth.dmi' - sprite_sheets = list(BODYTYPE_HNOLL = 'mods/content/fantasy/icons/clothing/loincloth_hnoll.dmi') + _hnoll_onmob_icon = 'mods/content/fantasy/icons/clothing/loincloth_hnoll.dmi' material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC material = /decl/material/solid/organic/skin/fur diff --git a/mods/content/fantasy/items/clothing/overrides.dm b/mods/content/fantasy/items/clothing/overrides.dm deleted file mode 100644 index 8ca8f71eea1e..000000000000 --- a/mods/content/fantasy/items/clothing/overrides.dm +++ /dev/null @@ -1,4 +0,0 @@ -/obj/item/clothing/gloves/setup_equip_flags() - . = ..() - if(!isnull(bodytype_equip_flags) && !(bodytype_equip_flags & BODY_EQUIP_FLAG_EXCLUDE)) - bodytype_equip_flags |= BODY_EQUIP_FLAG_HNOLL \ No newline at end of file diff --git a/mods/content/fantasy/items/clothing/trousers.dm b/mods/content/fantasy/items/clothing/trousers.dm index 37d757d3abe5..cbe30375bff0 100644 --- a/mods/content/fantasy/items/clothing/trousers.dm +++ b/mods/content/fantasy/items/clothing/trousers.dm @@ -5,9 +5,7 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC material = /decl/material/solid/organic/leather color = /decl/material/solid/organic/leather::color - sprite_sheets = list( - BODYTYPE_HNOLL = 'mods/content/fantasy/icons/clothing/trousers_hnoll.dmi' - ) + _hnoll_onmob_icon = 'mods/content/fantasy/icons/clothing/trousers_hnoll.dmi' /obj/item/clothing/pants/trousers/jerkin/Initialize() . = ..() @@ -20,6 +18,4 @@ name = "braies" desc = "Some short trousers. Comfortable and easy to wear." icon = 'mods/content/fantasy/icons/clothing/braies.dmi' - sprite_sheets = list( - BODYTYPE_HNOLL = 'mods/content/fantasy/icons/clothing/braies_hnoll.dmi' - ) + _hnoll_onmob_icon = 'mods/content/fantasy/icons/clothing/braies_hnoll.dmi' diff --git a/mods/species/ascent/_ascent.dme b/mods/species/ascent/_ascent.dme index 63d6f95300dc..461ef828f3a8 100644 --- a/mods/species/ascent/_ascent.dme +++ b/mods/species/ascent/_ascent.dme @@ -14,6 +14,7 @@ #include "datum\species_bodytypes.dm" #include "datum\traits.dm" #include "effects\razorweb.dm" +#include "items\_overrides.dm" #include "items\cell.dm" #include "items\clothing.dm" #include "items\clustertool.dm" diff --git a/mods/species/ascent/items/_overrides.dm b/mods/species/ascent/items/_overrides.dm new file mode 100644 index 000000000000..3cc8b68959b6 --- /dev/null +++ b/mods/species/ascent/items/_overrides.dm @@ -0,0 +1,10 @@ +/obj/item + var/_alate_onmob_icon + var/_gyne_onmob_icon + +/obj/item/setup_sprite_sheets() + . = ..() + if(_alate_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_MANTID_SMALL, _alate_onmob_icon) + if(_gyne_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_MANTID_LARGE, _gyne_onmob_icon) diff --git a/mods/species/ascent/items/clothing.dm b/mods/species/ascent/items/clothing.dm index 9962664265ae..64641d04adf5 100644 --- a/mods/species/ascent/items/clothing.dm +++ b/mods/species/ascent/items/clothing.dm @@ -22,7 +22,7 @@ desc = "An alien facemask with chunky gas filters and a breathing valve." filter_water = TRUE icon = 'mods/species/ascent/icons/clothing/mask.dmi' - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/clothing/mask_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/clothing/mask_gyne.dmi' bodytype_equip_flags = BODY_EQUIP_FLAG_GYNE | BODY_EQUIP_FLAG_ALATE filtered_gases = list( /decl/material/gas/nitrous_oxide, @@ -38,9 +38,7 @@ desc = "A set of powerful gripping claws." icon = 'mods/species/ascent/icons/magboots/boots.dmi' bodytype_equip_flags = BODY_EQUIP_FLAG_GYNE | BODY_EQUIP_FLAG_ALATE - sprite_sheets = list( - BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/magboots/boots_gyne.dmi' - ) + _gyne_onmob_icon = 'mods/species/ascent/icons/magboots/boots_gyne.dmi' /obj/item/clothing/jumpsuit/ascent name = "mantid undersuit" @@ -48,9 +46,7 @@ bodytype_equip_flags = BODY_EQUIP_FLAG_GYNE | BODY_EQUIP_FLAG_ALATE icon = 'mods/species/ascent/icons/clothing/under.dmi' color = COLOR_DARK_GUNMETAL - sprite_sheets = list( - BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/clothing/under_gyne.dmi' - ) + _gyne_onmob_icon = 'mods/species/ascent/icons/clothing/under_gyne.dmi' /obj/item/clothing/suit/ascent name = "mantid gear harness" @@ -58,7 +54,7 @@ bodytype_equip_flags = BODY_EQUIP_FLAG_GYNE | BODY_EQUIP_FLAG_ALATE icon_state = ICON_STATE_WORLD icon = 'mods/species/ascent/icons/clothing/under_harness.dmi' - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/clothing/under_harness_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/clothing/under_harness_gyne.dmi' body_parts_covered = 0 slot_flags = SLOT_OVER_BODY | SLOT_LOWER_BODY storage = /datum/storage/pockets/suit diff --git a/mods/species/ascent/items/guns.dm b/mods/species/ascent/items/guns.dm index 83fdda10ffce..99a76ce53514 100644 --- a/mods/species/ascent/items/guns.dm +++ b/mods/species/ascent/items/guns.dm @@ -20,7 +20,7 @@ list(mode_name="shock", projectile_type = /obj/item/projectile/beam/stun/shock), list(mode_name="lethal", projectile_type = /obj/item/projectile/beam/particle) ) - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/particle_rifle/inhands_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/particle_rifle/inhands_gyne.dmi' /obj/item/gun/energy/particle/small name = "particle projector" diff --git a/mods/species/ascent/items/rig.dm b/mods/species/ascent/items/rig.dm index 35697954e7b4..214f60caa99a 100644 --- a/mods/species/ascent/items/rig.dm +++ b/mods/species/ascent/items/rig.dm @@ -3,7 +3,6 @@ name = "alate support exosuit" desc = "A powerful support exosuit with integrated power supply, weapon and atmosphere. It's closer to a mech than a rig." icon = 'mods/species/ascent/icons/rig/rig.dmi' - suit_type = "support exosuit" armor = list( ARMOR_MELEE = ARMOR_MELEE_MAJOR, @@ -28,7 +27,7 @@ gloves = /obj/item/clothing/gloves/rig/mantid update_visible_name = TRUE - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/rig/rig_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/rig/rig_gyne.dmi' initial_modules = list( /obj/item/rig_module/vision/thermal, /obj/item/rig_module/ai_container, @@ -241,7 +240,7 @@ desc = "More like a torpedo casing than a helmet." bodytype_equip_flags = BODY_EQUIP_FLAG_GYNE | BODY_EQUIP_FLAG_ALATE icon = 'mods/species/ascent/icons/rig/rig_helmet.dmi' - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/rig/rig_helmet_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/rig/rig_helmet_gyne.dmi' /obj/item/clothing/suit/space/rig/mantid desc = "It's closer to a mech than a suit." @@ -255,16 +254,16 @@ /obj/item/stack/medical/resin, /obj/item/chems/drinks/cans/waterbottle/ascent ) - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/rig/rig_chest_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/rig/rig_chest_gyne.dmi' /obj/item/clothing/shoes/magboots/rig/mantid icon = 'mods/species/ascent/icons/rig/rig_boots.dmi' desc = "It's like a highly advanced forklift." bodytype_equip_flags = BODY_EQUIP_FLAG_GYNE | BODY_EQUIP_FLAG_ALATE - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/rig/rig_boots_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/rig/rig_boots_gyne.dmi' /obj/item/clothing/gloves/rig/mantid icon = 'mods/species/ascent/icons/rig/rig_gloves.dmi' desc = "They look like a cross between a can opener and a Swiss army knife the size of a shoebox." bodytype_equip_flags = BODY_EQUIP_FLAG_GYNE | BODY_EQUIP_FLAG_ALATE - sprite_sheets = list(BODYTYPE_MANTID_LARGE = 'mods/species/ascent/icons/rig/rig_gloves_gyne.dmi') + _gyne_onmob_icon = 'mods/species/ascent/icons/rig/rig_gloves_gyne.dmi' diff --git a/mods/species/bayliens/bayliens.dm b/mods/species/bayliens/_bayliens.dm similarity index 77% rename from mods/species/bayliens/bayliens.dm rename to mods/species/bayliens/_bayliens.dm index a1ddf384ed92..68d625b481ff 100644 --- a/mods/species/bayliens/bayliens.dm +++ b/mods/species/bayliens/_bayliens.dm @@ -1,7 +1,12 @@ -#define SPECIES_SKRELL "Skrell" -#define SPECIES_TAJARA "Tajara" -#define SPECIES_LIZARD "Unathi" -#define SPECIES_ADHERENT "Adherent" +#define SPECIES_SKRELL "Skrell" +#define SPECIES_TAJARA "Tajara" +#define SPECIES_LIZARD "Unathi" +#define SPECIES_ADHERENT "Adherent" + +#define BODYTYPE_FELINE "feline body" +#define BODYTYPE_ADHERENT "adherent body" + +#define BODY_EQUIP_FLAG_FELINE BITFLAG(7) /decl/modpack/bayliens name = "Baystation 12 Aliens" diff --git a/mods/species/bayliens/_bayliens.dme b/mods/species/bayliens/_bayliens.dme index e8de7a35d0b6..014aba6cf5d8 100644 --- a/mods/species/bayliens/_bayliens.dme +++ b/mods/species/bayliens/_bayliens.dme @@ -1,7 +1,8 @@ #ifndef MODPACK_BAYLIENS #define MODPACK_BAYLIENS // BEGIN_INCLUDE -#include "bayliens.dm" +#include "_bayliens.dm" +#include "_overrides.dm" #include "adherent\_adherent.dm" #include "adherent\datum\culture.dm" #include "adherent\datum\emotes.dm" diff --git a/mods/species/bayliens/_overrides.dm b/mods/species/bayliens/_overrides.dm new file mode 100644 index 000000000000..5eb6f5a85583 --- /dev/null +++ b/mods/species/bayliens/_overrides.dm @@ -0,0 +1,7 @@ +/obj/item + var/_feline_onmob_icon + +/obj/item/setup_sprite_sheets() + . = ..() + if(_feline_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_FELINE, _feline_onmob_icon) diff --git a/mods/species/bayliens/adherent/_adherent.dm b/mods/species/bayliens/adherent/_adherent.dm index 997107674c85..d70f337115d7 100644 --- a/mods/species/bayliens/adherent/_adherent.dm +++ b/mods/species/bayliens/adherent/_adherent.dm @@ -1,6 +1,4 @@ -#define BODYTYPE_ADHERENT "adherent body" #define LANGUAGE_ADHERENT "Protocol" - #define BP_FLOAT "floatation disc" #define BP_JETS "maneuvering jets" #define BP_COOLING_FINS "cooling fins" diff --git a/mods/species/bayliens/tajaran/_tajaran.dm b/mods/species/bayliens/tajaran/_tajaran.dm index b4e664e999bb..151b2b1c8a7a 100644 --- a/mods/species/bayliens/tajaran/_tajaran.dm +++ b/mods/species/bayliens/tajaran/_tajaran.dm @@ -1,6 +1,4 @@ #define LANGUAGE_TAJARA "Siik'maas" -#define BODYTYPE_FELINE "feline body" -#define BODY_EQUIP_FLAG_FELINE BITFLAG(7) /obj/item/clothing/setup_equip_flags() . = ..() diff --git a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm index af8842c7b19b..b6c5366944c3 100644 --- a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm +++ b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm @@ -2,70 +2,53 @@ LAZYDISTINCTADD(available_bodytypes, BODYTYPE_FELINE) . = ..() -/obj/item/clothing/suit/space/void/merc/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/merc/suit.dmi') +/obj/item/clothing/suit/space/void/merc + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/merc/suit.dmi' -/obj/item/clothing/suit/space/void/swat/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/deathsquad/suit.dmi') +/obj/item/clothing/suit/space/void/swat + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/deathsquad/suit.dmi' -/obj/item/clothing/suit/space/void/engineering/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/engineering/suit.dmi') +/obj/item/clothing/suit/space/void/engineering + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/engineering/suit.dmi' -/obj/item/clothing/suit/space/void/mining/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/mining/suit.dmi') +/obj/item/clothing/suit/space/void/mining + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/mining/suit.dmi' -/obj/item/clothing/suit/space/void/medical/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/medical/suit.dmi') +/obj/item/clothing/suit/space/void/medical + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/medical/suit.dmi' -/obj/item/clothing/suit/space/void/security/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/sec/suit.dmi') +/obj/item/clothing/suit/space/void/security + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/sec/suit.dmi' -/obj/item/clothing/suit/space/void/atmos/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/atmos/suit.dmi') +/obj/item/clothing/suit/space/void/atmos + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/atmos/suit.dmi' -/obj/item/clothing/suit/space/void/engineering/alt/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/engineering_alt/suit.dmi') +/obj/item/clothing/suit/space/void/engineering/alt + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/engineering_alt/suit.dmi' -/obj/item/clothing/suit/space/void/mining/alt/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/mining_alt/suit.dmi') +/obj/item/clothing/suit/space/void/mining/alt + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/mining_alt/suit.dmi' -/obj/item/clothing/suit/space/void/medical/alt/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/medical_alt/suit.dmi') +/obj/item/clothing/suit/space/void/medical/alt + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/medical_alt/suit.dmi' -/obj/item/clothing/suit/space/void/security/alt/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/sec_alt/suit.dmi') +/obj/item/clothing/suit/space/void/security/alt + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/sec_alt/suit.dmi' -/obj/item/clothing/suit/space/void/atmos/alt/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/atmos_alt/suit.dmi') +/obj/item/clothing/suit/space/void/atmos/alt + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/atmos_alt/suit.dmi' -/obj/item/clothing/suit/space/void/engineering/salvage/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/salvage/suit.dmi') +/obj/item/clothing/suit/space/void/engineering/salvage + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/salvage/suit.dmi' -/obj/item/clothing/suit/space/void/expedition/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/pilot/suit.dmi') +/obj/item/clothing/suit/space/void/expedition + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/pilot/suit.dmi' -/obj/item/clothing/suit/space/void/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/nasa/suit.dmi') +/obj/item/clothing/suit/space/void + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/nasa/suit.dmi' -/obj/item/clothing/suit/space/void/wizard/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/wizard/suit.dmi') +/obj/item/clothing/suit/space/void/wizard + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/wizard/suit.dmi' -/obj/item/clothing/suit/space/void/excavation/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/excavation/suit.dmi') +/obj/item/clothing/suit/space/void/excavation + _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/excavation/suit.dmi' diff --git a/mods/species/drakes/_drakes.dme b/mods/species/drakes/_drakes.dme index 44fa41725be3..1143d6a510ee 100644 --- a/mods/species/drakes/_drakes.dme +++ b/mods/species/drakes/_drakes.dme @@ -7,6 +7,7 @@ // BEGIN_INCLUDE #include "_drakes.dm" +#include "_overrides.dm" #include "clothing.dm" #include "culture.dm" #include "drake_abilities.dm" diff --git a/mods/species/drakes/_overrides.dm b/mods/species/drakes/_overrides.dm new file mode 100644 index 000000000000..a4c564def3ce --- /dev/null +++ b/mods/species/drakes/_overrides.dm @@ -0,0 +1,10 @@ +/obj/item + var/_drake_onmob_icon + var/_drake_hatchling_onmob_icon + +/obj/item/backpack/setup_sprite_sheets() + . = ..() + if(_drake_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA, _drake_onmob_icon) + if(_drake_hatchling_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA_HATCHLING, _drake_hatchling_onmob_icon) diff --git a/mods/species/drakes/clothing.dm b/mods/species/drakes/clothing.dm index ac23b01ba190..63c803bc1dbe 100644 --- a/mods/species/drakes/clothing.dm +++ b/mods/species/drakes/clothing.dm @@ -1,21 +1,11 @@ +/obj/item/backpack + _drake_onmob_icon = 'mods/species/drakes/icons/clothing/backpack.dmi' + _drake_hatchling_onmob_icon = 'mods/species/drakes/icons/clothing/hatchling_backpack.dmi' -/obj/item/backpack/setup_sprite_sheets() - . = ..() - if(!(BODYTYPE_GRAFADREKA in sprite_sheets)) - LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA, 'mods/species/drakes/icons/clothing/backpack.dmi') - if(!(BODYTYPE_GRAFADREKA_HATCHLING in sprite_sheets)) - LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA_HATCHLING, 'mods/species/drakes/icons/clothing/hatchling_backpack.dmi') +/obj/item/card/id + _drake_onmob_icon = 'mods/species/drakes/icons/clothing/id.dmi' + _drake_hatchling_onmob_icon = 'mods/species/drakes/icons/clothing/hatchling_id.dmi' -/obj/item/card/id/setup_sprite_sheets() - . = ..() - if(!(BODYTYPE_GRAFADREKA in sprite_sheets)) - LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA, 'mods/species/drakes/icons/clothing/id.dmi') - if(!(BODYTYPE_GRAFADREKA_HATCHLING in sprite_sheets)) - LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA_HATCHLING, 'mods/species/drakes/icons/clothing/hatchling_id.dmi') - -/obj/item/bag/setup_sprite_sheets() - . = ..() - if(!(BODYTYPE_GRAFADREKA in sprite_sheets)) - LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA, 'mods/species/drakes/icons/clothing/sack.dmi') - if(!(BODYTYPE_GRAFADREKA_HATCHLING in sprite_sheets)) - LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA_HATCHLING, 'mods/species/drakes/icons/clothing/hatchling_backpack.dmi') +/obj/item/bag + _drake_onmob_icon = 'mods/species/drakes/icons/clothing/sack.dmi' + _drake_hatchling_onmob_icon = 'mods/species/drakes/icons/clothing/hatchling_backpack.dmi' diff --git a/mods/species/neoavians/_neoavians.dme b/mods/species/neoavians/_neoavians.dme index e7c8d5263994..6c5b71cb056f 100644 --- a/mods/species/neoavians/_neoavians.dme +++ b/mods/species/neoavians/_neoavians.dme @@ -2,6 +2,7 @@ #define CONTENT_PACK_NEOAVIANS // BEGIN_INCLUDE #include "_neoavians.dm" +#include "_overrides.dm" #include "clothing.dm" #include "datum\accessory.dm" #include "datum\language.dm" diff --git a/mods/species/neoavians/_overrides.dm b/mods/species/neoavians/_overrides.dm new file mode 100644 index 000000000000..1d745db0a6fd --- /dev/null +++ b/mods/species/neoavians/_overrides.dm @@ -0,0 +1,7 @@ +/obj/item + var/_avian_onmob_icon + +/obj/item/setup_sprite_sheets() + . = ..() + if(_avian_onmob_icon) + LAZYSET(sprite_sheets, BODYTYPE_AVIAN, _avian_onmob_icon) diff --git a/mods/species/neoavians/clothing.dm b/mods/species/neoavians/clothing.dm index 98f5b8dac918..e7d90e623eaa 100644 --- a/mods/species/neoavians/clothing.dm +++ b/mods/species/neoavians/clothing.dm @@ -1,11 +1,9 @@ //Shoes -/obj/item/clothing/shoes/magboots/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/feet/magboots.dmi') +/obj/item/clothing/shoes/magboots + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/feet/magboots.dmi' -/obj/item/clothing/shoes/galoshes/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/feet/galoshes.dmi') +/obj/item/clothing/shoes/galoshes + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/feet/galoshes.dmi' //Gloves /obj/item/clothing/gloves/setup_equip_flags() @@ -13,34 +11,31 @@ if(!isnull(bodytype_equip_flags) && !(bodytype_equip_flags & BODY_EQUIP_FLAG_EXCLUDE)) bodytype_equip_flags |= BODY_EQUIP_FLAG_AVIAN -/obj/item/clothing/gloves/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/gloves.dmi') +/obj/item/clothing/gloves + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/gloves.dmi' + +/obj/item/clothing/gloves/ring + _avian_onmob_icon = null //Backpacks & tanks -/obj/item/backpack/satchel/Initialize() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/satchel.dmi') +/obj/item/backpack/satchel + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/satchel.dmi' //Radsuits (theyre essential?) -/obj/item/clothing/head/radiation/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/head/rad_helm.dmi') +/obj/item/clothing/head/radiation + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/head/rad_helm.dmi' -/obj/item/clothing/suit/radiation/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/suit/rad_suit.dmi') +/obj/item/clothing/suit/radiation + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/suit/rad_suit.dmi' //cloaks -/obj/item/clothing/suit/cloak/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/accessory/cloak.dmi') +/obj/item/clothing/suit/cloak + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/accessory/cloak.dmi' -/obj/item/clothing/suit/cloak/hide/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/accessory/cloak_hide.dmi') +/obj/item/clothing/suit/cloak/hide + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/accessory/cloak_hide.dmi' //clothing /obj/item/clothing/dress/avian_smock @@ -49,6 +44,7 @@ icon = 'mods/species/neoavians/icons/clothing/under/smock.dmi' icon_state = ICON_STATE_WORLD bodytype_equip_flags = BODY_EQUIP_FLAG_AVIAN + _avian_onmob_icon = null /obj/item/clothing/dress/avian_smock/worker name = "worker's smock" @@ -86,6 +82,7 @@ icon = 'mods/species/neoavians/icons/clothing/feet/shoes.dmi' color = COLOR_GRAY bodytype_equip_flags = BODY_EQUIP_FLAG_AVIAN + _avian_onmob_icon = null /obj/item/clothing/shoes/avian/footwraps name = "cloth footwraps" diff --git a/mods/species/neoavians/machinery/suit_cycler.dm b/mods/species/neoavians/machinery/suit_cycler.dm index 9b368c305771..2fd6323e6edc 100644 --- a/mods/species/neoavians/machinery/suit_cycler.dm +++ b/mods/species/neoavians/machinery/suit_cycler.dm @@ -4,86 +4,68 @@ //mining -/obj/item/clothing/suit/space/void/mining/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/suit.dmi') +/obj/item/clothing/suit/space/void/mining + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/suit.dmi' -/obj/item/clothing/head/helmet/space/void/mining/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/mining + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi' //excavation -/obj/item/clothing/suit/space/void/excavation/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/suit.dmi') +/obj/item/clothing/suit/space/void/excavation + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/suit.dmi' -/obj/item/clothing/head/helmet/space/void/excavation/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/excavation + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi' //engineering -/obj/item/clothing/head/helmet/space/void/engineering/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/engineering/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/engineering + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/engineering/helmet.dmi' -/obj/item/clothing/suit/space/void/engineering/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/engineering/suit.dmi') +/obj/item/clothing/suit/space/void/engineering + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/engineering/suit.dmi' -/obj/item/clothing/head/helmet/space/void/atmos/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/atmos/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/atmos + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/atmos/helmet.dmi' -/obj/item/clothing/suit/space/void/atmos/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/atmos/suit.dmi') +/obj/item/clothing/suit/space/void/atmos + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/atmos/suit.dmi' //medical -/obj/item/clothing/suit/space/void/medical/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/suit.dmi') +/obj/item/clothing/suit/space/void/medical + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/suit.dmi' -/obj/item/clothing/head/helmet/space/void/medical/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/medical + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi' //security -/obj/item/clothing/head/helmet/space/void/security/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/security + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/helmet.dmi' -/obj/item/clothing/suit/space/void/security/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/suit.dmi') +/obj/item/clothing/suit/space/void/security + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/suit.dmi' //salvage -/obj/item/clothing/head/helmet/space/void/engineering/salvage/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/salvage/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/engineering/salvage + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/salvage/helmet.dmi' -/obj/item/clothing/suit/space/void/engineering/salvage/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/salvage/suit.dmi') +/obj/item/clothing/suit/space/void/engineering/salvage + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/salvage/suit.dmi' //pilot -/obj/item/clothing/head/helmet/space/void/expedition/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/pilot/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/expedition + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/pilot/helmet.dmi' -/obj/item/clothing/suit/space/void/expedition/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/pilot/suit.dmi') +/obj/item/clothing/suit/space/void/expedition + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/pilot/suit.dmi' //merc -/obj/item/clothing/head/helmet/space/void/merc/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/merc/helmet.dmi') +/obj/item/clothing/head/helmet/space/void/merc + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/merc/helmet.dmi' -/obj/item/clothing/suit/space/void/merc/setup_sprite_sheets() - . = ..() - LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/merc/suit.dmi') \ No newline at end of file +/obj/item/clothing/suit/space/void/merc + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/merc/suit.dmi' \ No newline at end of file From 3e0ac944bc4f1f1c7dac410e13f4aaad9527885c Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Wed, 4 Dec 2024 14:05:40 +1100 Subject: [PATCH 0020/1331] Automatic changelog generation for PR #4597 [ci skip] --- html/changelogs/AutoChangeLog-pr-4597.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4597.yml diff --git a/html/changelogs/AutoChangeLog-pr-4597.yml b/html/changelogs/AutoChangeLog-pr-4597.yml new file mode 100644 index 000000000000..30daa8a51a72 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4597.yml @@ -0,0 +1,6 @@ +author: ophelia +changes: + - {tweak: 'You can now put any disk into the research design database or research + design console, but only the correct disk type (tech disk or design disk) will + function.'} +delete-after: true From d248f32207f6af7526c32cc09c7e714e20497c4e Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 4 Dec 2024 17:43:02 +1100 Subject: [PATCH 0021/1331] Adding Doe's crafted backpacks. --- code/datums/outfits/equipment/backpacks.dm | 8 ++++++++ .../objects/items/weapons/storage/backpack.dm | 17 +++++++++++++++++ .../crafting/stack_recipes/recipes_textiles.dm | 11 +++++++++++ .../storage/backpack/backpack_crafted.dmi | Bin 0 -> 901 bytes .../storage/backpack/backpack_haversack.dmi | Bin 0 -> 844 bytes maps/shaded_hills/shaded_hills_define.dm | 12 +++++++----- 6 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 icons/obj/items/storage/backpack/backpack_crafted.dmi create mode 100644 icons/obj/items/storage/backpack/backpack_haversack.dmi diff --git a/code/datums/outfits/equipment/backpacks.dm b/code/datums/outfits/equipment/backpacks.dm index 13b7df63e46c..2a4aed715eab 100644 --- a/code/datums/outfits/equipment/backpacks.dm +++ b/code/datums/outfits/equipment/backpacks.dm @@ -43,6 +43,14 @@ name = "Sack" path = /obj/item/bag/sack +/decl/backpack_outfit/haversack + name = "Haversack" + path = /obj/item/backpack/crafted + +/decl/backpack_outfit/backpack/crafted + name = "Handmade Backpack" + path = /obj/item/backpack/crafted/backpack + /* Code */ /decl/backpack_outfit var/flags diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 6b9c600b9594..1bc1144dc870 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -445,3 +445,20 @@ name = "security messenger bag" desc = "A tactical backpack worn over one shoulder. This one is in Security colors." icon = 'icons/obj/items/storage/backpack/messenger_sec.dmi' + +// Crafted backpacks. +/obj/item/backpack/crafted + name = "haversack" + desc = "A rather rough handmade haversack." + icon = 'icons/obj/items/storage/backpack/backpack_haversack.dmi' + material = /decl/material/solid/organic/leather + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC | MAT_FLAG_ALTERATION_COLOR + +/obj/item/backpack/crafted/backpack + name = "backpack" + desc = "A rather rough handmade backpack." + icon = 'icons/obj/items/storage/backpack/backpack_crafted.dmi' + +/obj/item/backpack/crafted/backpack/update_name() + . = ..() + SetName("handmade [name]") diff --git a/code/modules/crafting/stack_recipes/recipes_textiles.dm b/code/modules/crafting/stack_recipes/recipes_textiles.dm index ee608b17555b..7fdbd1b56aff 100644 --- a/code/modules/crafting/stack_recipes/recipes_textiles.dm +++ b/code/modules/crafting/stack_recipes/recipes_textiles.dm @@ -60,6 +60,17 @@ crafting_extra_cost_factor = 1.1 // less material is lost because it's relatively simple difficulty = MAT_VALUE_NORMAL_DIY // Slightly easier than making clothing. +/decl/stack_recipe/textiles/leather/sack + result_type = /obj/item/bag/sack + difficulty = MAT_VALUE_HARD_DIY + +/decl/stack_recipe/textiles/leather/backpack + result_type = /obj/item/backpack/crafted/backpack + difficulty = MAT_VALUE_VERY_HARD_DIY + +/decl/stack_recipe/textiles/leather/backpack/haversack + result_type = /obj/item/backpack/crafted + /decl/stack_recipe/textiles/leather/waterskin result_type = /obj/item/chems/waterskin/crafted required_material = /decl/material/solid/organic/leather diff --git a/icons/obj/items/storage/backpack/backpack_crafted.dmi b/icons/obj/items/storage/backpack/backpack_crafted.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e565e4862a1bceac42c1741494324f05a464eeea GIT binary patch literal 901 zcmV;01A6?4P)D@x2wg|OkOGD>q3^YSxO6q52&Ds_u<@=M~A z5|gtL`b>z{mlL0nn3qD1&LX@zl@(n5T);s90COHt%wU^Zr~m*0e@R3^RA_EMenM@{=`KJlfzlr}-)t)d7xOu=v2gW)4Ckd)o>d@{ z?z{r3SHd*lYUqv-t0H4w0o6W(CbWCmk0*&fQ%+t1)msXn-CJ!@1fZawhXV}SJ(mJS z01BLkgG?rq`N_~&Bl-^P@;l9d*eAbYpW)hlS^}=!CmGz^eOd;t-6t8`+kKjWYxije b|2KaCLi$`=a6(Yk00000NkvXXu0mjfW=){1 literal 0 HcmV?d00001 diff --git a/icons/obj/items/storage/backpack/backpack_haversack.dmi b/icons/obj/items/storage/backpack/backpack_haversack.dmi new file mode 100644 index 0000000000000000000000000000000000000000..ca148f6a4eac4bdaae099b0ea1d8c63e11db1738 GIT binary patch literal 844 zcmV-S1GD^zP)D@x2wg|OkOGD>q3^YSxO6q52&Ds_u<@=M~A z5|gtL`b>z{mlL0nn3qD1&LX@zl@(n5T);s90COHt%wU^Zr~m*0MoC0LRA_ zAP|N5IkS(D?8VZ3fb;_-()-bl@Bb=eCQ%B-c4pXR_*O`j;$yN0y zdIVb(p!IO~_32;^Jj_r-H*`ogUkriPSLX{bcOoRrFhx-aB#H$BA9HYXUZ8a6#UprQ zwf#WzvK#`_MPa<&;y_p!m*Wt=1d_Zcz(HGIUyk$1gZQ`#{T;6Y4euwk=?5QfzLxJC z16Lf|YP$fdySopc+Hx6}m;P=A*#@ljH!;+Hp{%uf)r+7N{?@A1XTwBQeJ5ZaR;1mZ z$z(F`h0!X(qKumt5hlT+JfJWnNg$+iWm*C5Qpb?GxiFck;G<7eaOn~YthfEYgI2+% zF6q;&aY1A(U!7yUUXKKLu8J}yW35o0kFrh{UJxCZeC_pfa&J==bJ;*-1F8jO7Cx8&^Oz-NveK!zZ5p+11{ugEh=@4=?-C(y7?Dk25p#e{K->=m z-Ps&uGMUUf!&8s;)!uLL9<=$HR$z1lX$3|{kf;Enr!0TY=m^pZjE*3&z=k9EPxuAK WLqd%9JxMG80000 Date: Wed, 4 Dec 2024 19:31:43 +1100 Subject: [PATCH 0022/1331] Added a large square woven basket. --- .../extensions/storage/subtypes_basket.dm | 3 +++ .../objects/items/weapons/storage/basket.dm | 17 ++++++++++++++--- .../obj/items/storage/baskets/basket_large.dmi | Bin 0 -> 1156 bytes .../{basket.dmi => baskets/basket_round.dmi} | Bin 940 -> 940 bytes 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 icons/obj/items/storage/baskets/basket_large.dmi rename icons/obj/items/storage/{basket.dmi => baskets/basket_round.dmi} (93%) diff --git a/code/datums/extensions/storage/subtypes_basket.dm b/code/datums/extensions/storage/subtypes_basket.dm index 26b00fd25da4..e5601ca91158 100644 --- a/code/datums/extensions/storage/subtypes_basket.dm +++ b/code/datums/extensions/storage/subtypes_basket.dm @@ -3,3 +3,6 @@ use_sound = 'sound/effects/storage/box.ogg' max_w_class = ITEM_SIZE_LARGE max_storage_space = DEFAULT_BOX_STORAGE + +/datum/storage/basket/large + max_storage_space = DEFAULT_BACKPACK_STORAGE diff --git a/code/game/objects/items/weapons/storage/basket.dm b/code/game/objects/items/weapons/storage/basket.dm index d7eada187651..65d8c1a3ec69 100644 --- a/code/game/objects/items/weapons/storage/basket.dm +++ b/code/game/objects/items/weapons/storage/basket.dm @@ -1,10 +1,21 @@ /obj/item/basket - name = "woven basket" + name = "basket" desc = "A simple woven basket. Very rustic." - icon = 'icons/obj/items/storage/basket.dmi' + icon = 'icons/obj/items/storage/baskets/basket_round.dmi' icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_HUGE - slot_flags = SLOT_BACK material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC storage = /datum/storage/basket material = /decl/material/solid/organic/plantmatter/grass/dry + var/name_prefix = "woven" + +/obj/item/basket/update_name() + . = ..() + SetName("[name_prefix] [name]") + +/obj/item/basket/large + slot_flags = SLOT_BACK + icon = 'icons/obj/items/storage/baskets/basket_large.dmi' + name_prefix = "large woven" + storage = /datum/storage/basket/large + slowdown_general = 1 // Large and unwieldly diff --git a/icons/obj/items/storage/baskets/basket_large.dmi b/icons/obj/items/storage/baskets/basket_large.dmi new file mode 100644 index 0000000000000000000000000000000000000000..af9d0140ca7e4391ace607f2666842ae4817a899 GIT binary patch literal 1156 zcmV-~1bh35P)005u_0{{R3dEt5<0000LP)t-sz`(#| zVs?s(p2ES&|Ns9K5nZm&E79XC&q!=`zm#6x;WVF~6KlE3 z52f!a){1S{+4Jc+v6clM$`nLeQL0y1hp8yI;Kgmu#aeMvudoh-s5mdJ7167Lbs(au zrj~{2sGxKp1uv#2?zAF$Rj>|Ze?EWj6T`>7uGh@}oNs@o`ACQ1rS&3_NF);ZcWf8s za;kQAkd0T;@nE#n&TgS4AnAB8YMdP;<4`dm>3A?|oE@aDpj8n-((z!_I6JGu0$P;< zNID*j8fOQw-5E4%bUYZPot^E@pjo5i!6@zQYZYkXa(ass>9mMjJtm|OIj9PW${{sAE+E|;?Qo0Go{{{HT z@jIc!l5T?We<6Ny7_1P=nHm2V;wJ+z{x3SgNF)-8{BqXp4<;dQZO#5*65=O;y(^#X z4@z%gf8o#E@BbOFceMYrM)z-)ci6u<;R4t@+W*1PG`GIkmC^~f_6JG-2S)?^>FX`TSKJnwy*_Dla81+!|jAuX> z2~KBy;yXI0xT%++uMS41!%j^JNy~%{#AA47r(T8uK0yV)p?{Vg%9`ANq=}XCV-gPJ zO=~Lj)hXHFf@QMxp1D->sb8)8`e2qQZ*F)aYIG54?K>|hyPtk_!F$u1f*F0fDQ$?| z9p405eJ~4@HzgIcS?JI~c4)flYjXE*+pIVVCp$EE;T0V+c;b_BdJSKE&MOj${D3df W^V(n5M!k6e0000DHg!Mz{0`F|NsArik@X+c1cHEoa$Scu|I7CFn`4gUZD From 48f02aa8ef1d324a0afa9f07eeda6b4519c2af5f Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 4 Dec 2024 19:42:21 +1100 Subject: [PATCH 0023/1331] Generalised the name_prefix system. --- code/game/objects/items/__item.dm | 3 ++ code/game/objects/items/_item_materials.dm | 9 +++-- .../objects/items/blades/spear_improvised.dm | 5 +-- .../objects/items/weapons/storage/basket.dm | 34 ++++++++++-------- .../objects/items/weapons/tools/wrench.dm | 5 +-- .../stool_bed_chair_nest_sofa/stools.dm | 13 +++---- .../crafting/stack_recipes/recipes_grass.dm | 3 ++ code/modules/detectivework/tools/rag.dm | 8 ++--- .../reagents/reagent_containers/beaker.dm | 5 +-- .../items/storage/baskets/basket_large.dmi | Bin 1156 -> 943 bytes .../items/storage/baskets/basket_round.dmi | Bin 940 -> 776 bytes 11 files changed, 43 insertions(+), 42 deletions(-) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 69b1449b4193..562b26a9d0cb 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -6,6 +6,9 @@ abstract_type = /obj/item temperature_sensitive = TRUE + /// Set to prefix name with this string ('woven' for 'woven basket' etc) + var/name_prefix + /// Set to false to skip state checking and never draw an icon on the mob (except when held) var/draw_on_mob_when_equipped = TRUE diff --git a/code/game/objects/items/_item_materials.dm b/code/game/objects/items/_item_materials.dm index 95bc6402777c..7aaf5d6bb267 100644 --- a/code/game/objects/items/_item_materials.dm +++ b/code/game/objects/items/_item_materials.dm @@ -104,10 +104,13 @@ queue_icon_update() /obj/item/proc/update_name() + var/list/new_name = list(base_name || initial(name)) if(material_alteration & MAT_FLAG_ALTERATION_NAME) - SetName("[material.adjective_name] [base_name || initial(name)]") - else - SetName(base_name || initial(name)) + new_name.Insert(1, material.adjective_name) + if(name_prefix) + new_name.Insert(1, name_prefix) + if(length(new_name)) + SetName(jointext(new_name, " ")) /obj/item/get_matter_amount_modifier() . = ..() diff --git a/code/game/objects/items/blades/spear_improvised.dm b/code/game/objects/items/blades/spear_improvised.dm index e2dba3cc5987..b6295462833b 100644 --- a/code/game/objects/items/blades/spear_improvised.dm +++ b/code/game/objects/items/blades/spear_improvised.dm @@ -1,4 +1,5 @@ /obj/item/bladed/polearm/spear/improvised + name_prefix = "improvised" material = /decl/material/solid/glass hilt_material = /decl/material/solid/metal/steel guard_material = /decl/material/solid/metal/copper @@ -11,10 +12,6 @@ force_binding_color = pick(global.cable_colors) . = ..(ml, material_key, _hilt_mat, _guard_mat) -/obj/item/bladed/polearm/spear/improvised/update_name() - . = ..() - SetName("improvised [name]") - /obj/item/bladed/polearm/spear/improvised/get_guard_color() return force_binding_color || ..() diff --git a/code/game/objects/items/weapons/storage/basket.dm b/code/game/objects/items/weapons/storage/basket.dm index 65d8c1a3ec69..8c7661451a91 100644 --- a/code/game/objects/items/weapons/storage/basket.dm +++ b/code/game/objects/items/weapons/storage/basket.dm @@ -1,21 +1,25 @@ /obj/item/basket - name = "basket" - desc = "A simple woven basket. Very rustic." - icon = 'icons/obj/items/storage/baskets/basket_round.dmi' - icon_state = ICON_STATE_WORLD - w_class = ITEM_SIZE_HUGE + name_prefix = "woven" + name = "handbasket" + desc = "A simple woven basket. Very rustic." + icon = 'icons/obj/items/storage/baskets/basket_round.dmi' + icon_state = ICON_STATE_WORLD + w_class = ITEM_SIZE_HUGE material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC - storage = /datum/storage/basket - material = /decl/material/solid/organic/plantmatter/grass/dry - var/name_prefix = "woven" + storage = /datum/storage/basket + material = /decl/material/solid/organic/plantmatter/grass/dry -/obj/item/basket/update_name() +/obj/item/basket/on_update_icon() . = ..() - SetName("[name_prefix] [name]") + icon_state = get_world_inventory_state() + if(storage?.opened) + icon_state = "[icon_state]-open" /obj/item/basket/large - slot_flags = SLOT_BACK - icon = 'icons/obj/items/storage/baskets/basket_large.dmi' - name_prefix = "large woven" - storage = /datum/storage/basket/large - slowdown_general = 1 // Large and unwieldly + name_prefix = "large woven" + name = "basket" + slot_flags = SLOT_BACK + icon = 'icons/obj/items/storage/baskets/basket_large.dmi' + w_class = ITEM_SIZE_GARGANTUAN + storage = /datum/storage/basket/large + slowdown_general = 1 // Large and unwieldly diff --git a/code/game/objects/items/weapons/tools/wrench.dm b/code/game/objects/items/weapons/tools/wrench.dm index e3f8e9e9d7a0..c117895cea1b 100644 --- a/code/game/objects/items/weapons/tools/wrench.dm +++ b/code/game/objects/items/weapons/tools/wrench.dm @@ -30,6 +30,7 @@ // Twohanded wrench. /obj/item/wrench/pipe + name_prefix = "enormous" name = "pipe wrench" desc = "You are no longer asking nicely." icon = 'icons/obj/items/tool/pipewrench.dmi' @@ -47,10 +48,6 @@ /obj/item/wrench/pipe/get_handle_color() return null -/obj/item/wrench/pipe/update_name() - . = ..() - SetName("enormous [name]") - /obj/item/wrench/pipe/Initialize() . = ..() set_extension(src, /datum/extension/tool, list(TOOL_WRENCH = TOOL_QUALITY_DEFAULT)) diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm index 59ccd454a5d3..b9e169c20ed8 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm @@ -157,14 +157,11 @@ return ..() /obj/item/stool/rustic - name = "stool" - icon = 'icons/obj/stool_rustic.dmi' - material = /decl/material/solid/organic/wood/walnut - color = /decl/material/solid/organic/wood/walnut::color - -/obj/item/stool/rustic/update_name() - ..() - SetName("rustic [name]") // rustic oaken stool, not oaken rustic stool + name_prefix = "rustic" + name = "stool" + icon = 'icons/obj/stool_rustic.dmi' + material = /decl/material/solid/organic/wood/walnut + color = /decl/material/solid/organic/wood/walnut::color /obj/item/stool/rustic/can_be_padded() return FALSE diff --git a/code/modules/crafting/stack_recipes/recipes_grass.dm b/code/modules/crafting/stack_recipes/recipes_grass.dm index d6f262e90bc2..1005af46bfc9 100644 --- a/code/modules/crafting/stack_recipes/recipes_grass.dm +++ b/code/modules/crafting/stack_recipes/recipes_grass.dm @@ -11,6 +11,9 @@ /decl/stack_recipe/woven/basket result_type = /obj/item/basket +/decl/stack_recipe/woven/large_basket + result_type = /obj/item/basket/large + /decl/stack_recipe/woven/banner result_type = /obj/item/banner/woven diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 1097f54f9bc5..5487388845dc 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -50,13 +50,13 @@ return ..() /obj/item/chems/glass/rag/update_name() - . = ..() if(on_fire) - SetName("burning [name]") + name_prefix = "burning" else if(reagents && reagents.total_volume) - SetName("damp [name]") + name_prefix = "damp" else - SetName("dry [name]") + name_prefix = "dry" + . = ..() /obj/item/chems/glass/rag/on_update_icon() . = ..() diff --git a/code/modules/reagents/reagent_containers/beaker.dm b/code/modules/reagents/reagent_containers/beaker.dm index 258146c77c42..b593c3f968f3 100644 --- a/code/modules/reagents/reagent_containers/beaker.dm +++ b/code/modules/reagents/reagent_containers/beaker.dm @@ -65,6 +65,7 @@ take_damage(rand(4,8)) /obj/item/chems/glass/beaker/large + name_prefix = "large" name = "beaker" // see update_name override below desc = "A large beaker." icon = 'icons/obj/items/chem/beakers/large.dmi' @@ -74,10 +75,6 @@ possible_transfer_amounts = @"[5,10,15,25,30,60,120]" w_class = ITEM_SIZE_LARGE -/obj/item/chems/glass/beaker/large/update_name() - . = ..() - SetName("large [name]") // large glass beaker, not glass large beaker - /obj/item/chems/glass/beaker/bowl name = "mixing bowl" desc = "A large mixing bowl." diff --git a/icons/obj/items/storage/baskets/basket_large.dmi b/icons/obj/items/storage/baskets/basket_large.dmi index af9d0140ca7e4391ace607f2666842ae4817a899..71c23d7b6ce115ac70839991c4a2174f636cb779 100644 GIT binary patch delta 876 zcmV-y1C#uO39koGiBL{Q4GJ0x0000DNk~Le0001h0001h1Oos705K4J_y7O^5>QN3 zMZmzoWny-Uik`y3$^ZZVNk?3a@YVPL0004WQchCg*a8jS)Eauo0ylMnWB)CpHit?oReP?pA(;v zn1^JE3DG)>$kv&Zn4FDEpR$6hp9?q|02Q!!XnZSl*bvAs@0qm{0em1vK(H7l|oh_GFKCn9svcuPL@)#^G zP-v0Q=t6rtx;Wp1=9>8)v`YZrrN^xa0UYv{GJr0;n89-wrZIqj0F!r4QC z9c1uD7ucx({#nR|Y}(6t3BZ{D4mkUebv3-;EKr`~g_xX`dW`<{k0k)(&*0zwlz20P zIbMj#*^kmh%cyg9!GDU$**BO70#M%0An9_`nf$&4V7#5dhB`Q6F7b8%IASjGrsf`n zUU80!ii(Pg`V)G8TgAcBwofIj-70z&9mIOet=pFT3K*n^-orPCUp$u{Q$9l?%&B;wSOnB@_qnYlZ@QR zdnB&%egIo_S9Yk%BXO1Y1K6tD7Vl^@8+CaIG?k-I+CZhBq-sduEFfdc=!cXJ4KC2sO;mVWR@c@N-v+&ed&!51q zIzSh*`u=>UTd1Uh^(mP@A;6<6h<%<;l6v07mVV40MlvCfdkj@Xi17qN4r@{Q`><6StI{^r3|S00000001>1Oos70D0kh9smFU6;Mo6 zMZmzoWny-Uik`y3$^ZZVNk?43z`(>+yAA*V00DGTPE!Ct=GbNc006U*P*Q)1i!&v& zs2C__$i8Y7U$%b z#OK6kB<3OMG$C4N5!pH+c4ByqRI4d4ASp398<#$11y?^8aAE)eXfdQb6w_|B000AC zNklLV5Qd2k_XdCD9;Dt|@H{}pb8oP`|En}aNH&`dhSoVVJ(I7tSdM+8?)gRyGeC)P6zt0(1Bk=!2tmPCUlH65(^JmV$8u6*~yT6oP-QhH!o)c@i$`7UQD%Of^*V*&wIkA=n9?BF% zT2ZQ3Scj=7xZuTY&c%OPaZ<0a4uhyTFRc~PtAcePqN=8rh3TlEbRY#UrYG*SB6?M@ z4rG5mfA15+$GxuC%>SHkf2R3JhvB95B9TZW68U#*7v*xQc6N}BSJLrdwA9XSp(P;c zcra?59VFvWF(B!9Flw9~q^_V<5kS)MVAMD}tHT0Xl>$gQ9*ln)X9uy}88mBjJQ$^& zo$bz`S)=2@DDCWQcLvQG9S=rnXJ@-JXx8X>FuK;+(ecqAMk0|&Hjm^NeRe7YR;hed0Shr?{z?p|1`` zr^8N72}#R@4a8%3W~W|;0X{(mzoCDY9m<;Af24_(@?#PXn+a07*qo IM6N<$f^EqXfdBvi diff --git a/icons/obj/items/storage/baskets/basket_round.dmi b/icons/obj/items/storage/baskets/basket_round.dmi index 378fe0f2a2f09a711b1df90f79d6e01be84f619a..c84fe4b7e607201c0f120d47b6fd4b1913f11e25 100644 GIT binary patch delta 669 zcmV;O0%HBF2Z#nCiBL{Q4GJ0x0000DNk~Le0001h0001B1Oos704G#udXXV60i%&n zPJcJQAT>{ki!&v&s2C_{$i~?OP0Y*B zOi@V6PpQ-`&dD!{&xy}S%tLaL3DG)>@aj}naP@Nm2PXjS8)n6`+7S%^00H$$L_t(o zh3%Npj-wzHhUtp;UFhB~?L0uSJ^(7-cYm_@{;#rNCwP7abgnkD316DF-vJTP#6K`r z`Bw-b&U=1@(w^UCKjO!FHc|9LqN2P0{&yyV~f1lTvUD z_~~x_n)OCUAFf@eqJPW|CIrr%3|w6FPTa1dcjSQc`XO%3=FOlVkB8RN=IW}Hrhk{v zTMIMjZ*ODHQwL6VZr!1eu;4&Z)XHg9ad5@!&W#4ie+!E9&4O3vTyG);DJ zn};PC=nO_7U;yxT68<)haxQq_U2C`m@YXMyN6!ThtPe`jKW^i*{G_sO8EeUNiq$VP|7q`DwRs*TXEjgd>+7R^0vLFmTq|)<6hJS z+7iv@0jQ3hp>1OaUGsT>R@(t~T)aZpd>)`x?qK80D|F500b1os(L}?ORnO-ET5YBL zMVh=q*L)tJRc^x$CL;=6^LYTO%L1p^-F4cpenb8OHKTBoeln^F00000NkvXXu0mjf Do?$#_ delta 835 zcmV-J1HAl*2CN4miBL{Q4GJ0x0000DNk~Le0001>0001h1Oos70Det>D3Kv90jrTv zPJfAuGbOXA7|1u|;!G<_%uR)`QB~}jt?&M3`I%*)SA zQAo;9snjjb$uEh|iO)#PL(*wNw9X>3bwcdKa5Sk_6EHwo!PUyZlf>|M#*CCvyk4Gq<;?pga_cq+^1Ue{;yKwt?U^yc5HElmjeWqI}GSn5{t>CUnIQL8DPIhsoS&abWOxVB6D+9KOpnL~j6ee$%bo8xoD$Bu{i@B7K%~UOpO!3{eFJ~^~O^~#UBU7jc*O`AG zo0lsS6zl5A;{Q!}xw69E8$JpypMRQuab|_RH+-;FUV=q9v%=mRUiiAMVsPgTdvCls zqxYsM?7eXqdv6@Z-W!Lp_r_s}%X$oZZzK|lL?Uq>F5c)rlr+2C8R>pdrOnR0Lb@MR zX|tO-ObkT2A5>|x!^$f_r29dYHoFe^>@!pF21I(-(P0clrFA$0ujeKR?A7EzXXlkxOGg&6kU|+`l0cKW?rsf*tldP`c zf*EzyjsFGsWKmTg8H)P@jDPTDmp{y7vp~+KDn-_g)%eae<3~@fbqX%f{8>T zkvKUq^T8~{)?(&^S%^;py?Z+!lx}fD?^#imfZoymhpJOI!1tzZ*DiqG(f$Xk#fZM~ zmvPsQ=7XgF!D@g{M$HF9d@^c27~+#r^T80GjG7My`K06I@2V%{2NW4(pfn+To`3)V N002ovPDHLkV1h3AmfZjV From 71a5a78eda6f405f08ab7a7f23f698427a036b01 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 4 Dec 2024 20:08:38 +1100 Subject: [PATCH 0024/1331] Adding a lounge. --- .../stool_bed_chair_nest_sofa/pew.dm | 18 +++++++++++++++++- .../recipes_hardness_integrity.dm | 4 ++++ icons/obj/structures/benches.dmi | Bin 1969 -> 1836 bytes icons/obj/structures/lounge.dmi | Bin 0 -> 3905 bytes 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 icons/obj/structures/lounge.dmi diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm index 9e849a85d382..0cae0394ad38 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm @@ -132,4 +132,20 @@ /obj/structure/bed/chair/bench/pew/ebony color = /decl/material/solid/organic/wood/ebony::color - material = /decl/material/solid/organic/wood/ebony \ No newline at end of file + material = /decl/material/solid/organic/wood/ebony + +/obj/structure/bed/chair/bench/lounge + name = "lounge" + desc = "An elegant lounge, perfect for reclining on." + icon = 'icons/obj/structures/lounge.dmi' + icon_state = "lounge_standing" + base_icon = "lounge" + +/obj/structure/bed/chair/bench/lounge/get_material_icon() + return icon + +/obj/structure/bed/chair/bench/lounge/mapped + color = /decl/material/solid/organic/wood/mahogany::color + material = /decl/material/solid/organic/wood/mahogany + reinf_material = /decl/material/solid/organic/cloth + padding_color = COLOR_RED_GRAY diff --git a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm index 92e580a6d0e2..7da43a8ec2dc 100644 --- a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm +++ b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm @@ -43,6 +43,10 @@ /decl/stack_recipe/hardness/integrity/furniture/bench/pew/single result_type = /obj/structure/bed/chair/bench/pew/single +/decl/stack_recipe/hardness/integrity/furniture/bench/lounge + result_type = /obj/structure/bed/chair/bench/lounge + difficulty = MAT_VALUE_VERY_HARD_DIY + /decl/stack_recipe/hardness/integrity/furniture/closet result_type = /obj/structure/closet diff --git a/icons/obj/structures/benches.dmi b/icons/obj/structures/benches.dmi index 70510d9d975a7e3eb0096cbe90db04d2e01867ec..5a56743224bcfa6cf5aa46aa523c298eae2e3a06 100644 GIT binary patch delta 1574 zcmV+>2HE+s53CNb8UcR={7FPXRCt{2T#cHeAPiizKUQ73@Bg%i)hcS31d-PEGX1{O z6PFl942*`KAH@LEqbN`A-@pl{vKUT#_0TR#olAo%;Ig zV_`18m#ekTfxD$0uy^NxwXHkxkPdVV$Q)1xvOpPlH@=90I`n@St&h(@b-fkVfpVZb zUff)$*qZ?Z5L3;>Kut4H(+nW{k}EJ5fCvw~K5(ZUxRZUroFN2;xdsrn&(^09w$Gq9 zj2Y<0Fb~+KS?$hRa@$jgVSq5Yuy1Mr(^KD}w(iR8xck7&fvpT6`}JT5*>@E`FdTJz z-3k4%?u5~Rb)A26+8rGv7>MfwgZ<`lf!+3Tz+Hs%*xJdJko^;C;gc&b08SJGHspY% z81U^ATV%brUm_1nSAGL12?m<50}a7|Z-2RU*MqG0_OXkuyy_{oS3BalTNUZI5Vl{~ z;l2*rH^{@T4_pDrP8=7=p#9qoS?}!|)WU{&%q=_dhM|AN%D%UCO^+Y44+q-69&T%= z-F|E2k$&r7ZghayItTbjW?Fz)2DSt;WWQ|%44B{X=j{#$`0_Oau|tVvr89d2fPHhm zj6ZL;{~5kKnSoWM-tW>kG_>z(3!--bmq#+-?urEip#FymSoo+QG`;9cxbo>%Sah!C z*@5zK{eypdpsX(pPa2G<{P`bd9uX|ZlNMCCuR>`qQ~T1l&{TfNIkDeXw7AQZ|Mzg+ z<=j0*%7z|{y}*9=6l+8FyTM>nwr+@Vrh7K)fXS=x@8f_(-w#!gh~fQ0i|w~z`z{cj zMUnJ+&Z4Y|haw^S&s#`PK|Tgj>?;F_PzJt;0cL+nn4N)*_WL*>n3QLY0}1v$2CytL zZvi+Pi3!nJ~8VQ;1_g%+R}@LL37Pq3ZieKgGaH;{aCo zJ$-+Yzh^pl76xMOL0bcT-}?IgB!By&KgB?5A7J$Tc?;a@6n(!aeEYKZ3oHYizCUk) zSp$C)TdY0bZfgeO>=O>eQCO5xeSea_eI0!5K>95d6-c-i#`^yC>f7HTki~~``$y>d z-c}sw_8dyY45sWK(DyB??^A&+WiS^XNU*xk>HA9t-Vn$P()WL-mR<<_;|{^V%ZCy` z-yfR!vJ&{mot+tQKovCW7QpI0qwkY_poxF7@0~!`>s4@nR$yih5OsNf#g%&wJOdd} z=GOMT6F7aJu7I1dh-WlAI54&UrV0{~>;R|VAKwAaz^6Nqicub~@~9fg7`jg|f8h8I zIA$yM{X*+bIjRFL5LJ>$TIEsplJQ_b=72Jg1PvkaIBeLu%jh|6EV>c06I^9LmQehvn> z`~@KGFY*Tj`hJc+z~wIhXT-^u1o!nkkoE@$q668!gE`JT zR`+8W$PvgG1{isNYypwJpCynn`3qRye=;D{_wz7-^B36f^BIMI%>dB%H!;BEFR0jOu=fK8!hBSX68LR_G&mZ6%IDGy9>wtekp-?Ck z3WY-PYQXvECk$Ho^??o!etn>WWA=4BTA%2sz~c9bui)Ll>UYUk@NV$y105Xv`alN< z*0w*u0UXFaz=3aJfM1{Js5q0a7l`$VjtYK#po2rOKG3njzkg6D6bgkxp-?CkicDZ+ zkrzx!677rE2g-K+iT1_o1Lc2nAkBVF2>fLJKsE;w?K5Au=a-kI`3oUnec#I;$OUQs zLI~pZfpS5bzYv0WeV|;B<}ZYR)#ZqV@HRV;X#eejEY<$kFu>*yWOE?Vey{Hf@&~dx zK)zs*+ZV47l+S_0K7dbog#)tuh3#kLhQ38mC=?2XLZMJ76bgkxp->(u6bgkxp-}9K Ye<0`hs1HdMb^rhX07*qoM6N<$f@HxEMF0Q* delta 1708 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 Date: Sun, 24 Nov 2024 13:27:22 +1100 Subject: [PATCH 0025/1331] Replacing wooden plank furniture with chipboard laminate furniture. --- code/game/objects/structures/benches.dm | 4 + code/game/objects/structures/tables.dm | 78 +++++++ .../designs/general/designs_general.dm | 58 +++++ .../solids/materials_solid_wood.dm | 43 ++++ .../materials/material_sheets_mapping.dm | 6 + .../reagents/reactions/reaction_synthesis.dm | 54 +++++ maps/antag_spawn/ert/ert_base.dmm | 10 +- maps/away/bearcat/bearcat-2.dmm | 4 +- maps/away/casino/casino.dmm | 38 ++-- maps/away/errant_pisces/errant_pisces.dmm | 14 +- maps/away/liberia/liberia.dmm | 64 +++--- maps/away/magshield/magshield.dmm | 26 +-- maps/away/mining/mining-signal.dmm | 18 +- maps/away/slavers/slavers_base.dmm | 12 +- maps/away/smugglers/smugglers.dmm | 4 +- maps/away/unishi/unishi-2.dmm | 20 +- maps/away/unishi/unishi-3.dmm | 26 +-- maps/away/yacht/yacht.dmm | 6 +- maps/exodus/exodus-2.dmm | 210 +++++++++--------- maps/exodus/exodus-admin.dmm | 42 ++-- maps/ministation/ministation-0.dmm | 26 +-- maps/ministation/ministation-1.dmm | 60 ++--- maps/ministation/ministation-2.dmm | 52 ++--- maps/tradeship/tradeship-0.dmm | 4 +- maps/tradeship/tradeship-2.dmm | 4 +- .../away_sites/lar_maria/lar_maria-2.dmm | 30 +-- .../government/away_sites/icarus/icarus-1.dmm | 14 +- .../government/away_sites/icarus/icarus-2.dmm | 18 +- mods/gamemodes/heist/heist_base.dmm | 16 +- 29 files changed, 602 insertions(+), 359 deletions(-) diff --git a/code/game/objects/structures/benches.dm b/code/game/objects/structures/benches.dm index ed9805ff78bb..0cf79b941736 100644 --- a/code/game/objects/structures/benches.dm +++ b/code/game/objects/structures/benches.dm @@ -38,6 +38,10 @@ material = /decl/material/solid/organic/wood reinf_material = /decl/material/solid/organic/wood +/obj/structure/table/bench/wooden/chipboard + material = /decl/material/solid/organic/wood/chipboard + reinf_material = /decl/material/solid/organic/wood/chipboard + /obj/structure/table/bench/padded icon_state = "padded_preview" material = /decl/material/solid/metal/steel diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index c66281e59f21..3700b2f4e488 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -778,6 +778,84 @@ /obj/structure/table/woodentable_reinforced/ebony/walnut additional_reinf_material = /decl/material/solid/organic/wood/walnut +// Wood laminate tables; chipboard basically. +// Smooth texture like plastic etc for a less rustic vibe on spacer maps. +/obj/structure/table/laminatetable + icon_state = "solid_preview" + color = WOOD_COLOR_GENERIC + material = /decl/material/solid/organic/wood/chipboard + reinf_material = /decl/material/solid/organic/wood/chipboard + +/obj/structure/table/laminatetable/mahogany + color = WOOD_COLOR_RICH + material = /decl/material/solid/organic/wood/chipboard/mahogany + reinf_material = /decl/material/solid/organic/wood/chipboard/mahogany + +/obj/structure/table/laminatetable/maple + color = WOOD_COLOR_PALE + material = /decl/material/solid/organic/wood/chipboard/maple + reinf_material = /decl/material/solid/organic/wood/chipboard/maple + +/obj/structure/table/laminatetable/ebony + color = WOOD_COLOR_BLACK + material = /decl/material/solid/organic/wood/chipboard/ebony + reinf_material = /decl/material/solid/organic/wood/chipboard/ebony + +/obj/structure/table/laminatetable/walnut + color = WOOD_COLOR_CHOCOLATE + material = /decl/material/solid/organic/wood/chipboard/walnut + reinf_material = /decl/material/solid/organic/wood/chipboard/walnut + +/obj/structure/table/laminatetable_reinforced + icon_state = "reinf_preview" + color = WOOD_COLOR_GENERIC + material = /decl/material/solid/organic/wood/chipboard + reinf_material = /decl/material/solid/organic/wood/chipboard + additional_reinf_material = /decl/material/solid/organic/wood/chipboard + +/obj/structure/table/laminatetable_reinforced/walnut + color = WOOD_COLOR_CHOCOLATE + material = /decl/material/solid/organic/wood/chipboard/walnut + reinf_material = /decl/material/solid/organic/wood/chipboard/walnut + additional_reinf_material = /decl/material/solid/organic/wood/chipboard/walnut + +/obj/structure/table/laminatetable_reinforced/walnut/maple + additional_reinf_material = /decl/material/solid/organic/wood/chipboard/maple + +/obj/structure/table/laminatetable_reinforced/mahogany + color = WOOD_COLOR_RICH + material = /decl/material/solid/organic/wood/chipboard/mahogany + reinf_material = /decl/material/solid/organic/wood/chipboard/mahogany + additional_reinf_material = /decl/material/solid/organic/wood/chipboard/mahogany + +/obj/structure/table/laminatetable_reinforced/mahogany/walnut + additional_reinf_material = /decl/material/solid/organic/wood/chipboard/walnut + +/obj/structure/table/laminatetable_reinforced/ebony + color = WOOD_COLOR_BLACK + material = /decl/material/solid/organic/wood/chipboard/ebony + reinf_material = /decl/material/solid/organic/wood/chipboard/ebony + additional_reinf_material = /decl/material/solid/organic/wood/chipboard/ebony + +/obj/structure/table/laminatetable_reinforced/ebony/walnut + additional_reinf_material = /decl/material/solid/organic/wood/chipboard/walnut + + + + + + + + + + + + + + + + + // A table that doesn't smooth, intended for bedside tables or otherwise standalone tables. // TODO: make table legs use material and tabletop use reinf_material // theoretically, this could also be made to use the normal table icon system, unlike desks? diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index 47eebe2b4542..ae37f50f4d94 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -188,3 +188,61 @@ /datum/fabricator_recipe/fishing_line_high_quality path = /obj/item/fishing_line/high_quality +/datum/fabricator_recipe/chipboard + path = /obj/item/stack/material/sheet/mapped/chipboard_pine + category = "Textiles" + fabricator_types = list( + FABRICATOR_CLASS_GENERAL, + FABRICATOR_CLASS_TEXTILE + ) + +/datum/fabricator_recipe/chipboard/get_resources() + resources = list( + /decl/material/solid/organic/wood = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), + /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) + ) + +/datum/fabricator_recipe/chipboard + path = /obj/item/stack/material/sheet/mapped/chipboard_pine + +/datum/fabricator_recipe/chipboard/get_resources() + resources = list( + /decl/material/solid/organic/wood = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), + /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) + ) + +/datum/fabricator_recipe/chipboard/maple + path = /obj/item/stack/material/sheet/mapped/chipboard_maple + +/datum/fabricator_recipe/chipboard/maple/get_resources() + resources = list( + /decl/material/solid/organic/wood/maple = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), + /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) + ) + +/datum/fabricator_recipe/chipboard/mahogany + path = /obj/item/stack/material/sheet/mapped/chipboard_mahogany + +/datum/fabricator_recipe/chipboard/mahogany/get_resources() + resources = list( + /decl/material/solid/organic/wood/mahogany = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), + /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) + ) + +/datum/fabricator_recipe/chipboard/ebony + path = /obj/item/stack/material/sheet/mapped/chipboard_ebony + +/datum/fabricator_recipe/chipboard/ebony/get_resources() + resources = list( + /decl/material/solid/organic/wood/ebony = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), + /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) + ) + +/datum/fabricator_recipe/chipboard/walnut + path = /obj/item/stack/material/sheet/mapped/chipboard_walnut + +/datum/fabricator_recipe/chipboard/walnut/get_resources() + resources = list( + /decl/material/solid/organic/wood/walnut = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), + /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) + ) diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index 01629ab0acff..fd8f36268599 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -133,3 +133,46 @@ /decl/material/liquid/heartstopper = 0.1 ) value = 1.8 + +// Used solely to give the old smooth table icons for spacer tables. +// Easy to work, not very strong or valuable. +/decl/material/solid/organic/wood/chipboard + name = "pine chipboard" + adjective_name = "pine laminate" + uid = "solid_wood_chipboard_pine" + lore_text = "Also known as particle board, this material is made from various kinds of pine wood chips and resin, with a plastic laminate." + bench_icon = 'icons/obj/structures/benches.dmi' + pew_icon = 'icons/obj/structures/pews.dmi' + door_icon_base = "metal" + table_icon_base = "metal" + color = WOOD_COLOR_GENERIC + value = 1.1 + default_solid_form = /obj/item/stack/material/sheet + +/decl/material/solid/organic/wood/chipboard/maple + name = "maple chipboard" + lore_text = "Also known as particle board, this material is made from various kinds of maple wood chips and resin, with a plastic laminate." + adjective_name = "maple laminate" + uid = "solid_wood_chipboard_maple" + color = WOOD_COLOR_PALE + +/decl/material/solid/organic/wood/chipboard/mahogany + name = "mahogany chipboard" + lore_text = "Also known as particle board, this material is made from various kinds of mahogany wood chips and resin, with a plastic laminate." + adjective_name = "mahogany laminate" + uid = "solid_wood_chipboard_mahogany" + color = WOOD_COLOR_RICH + +/decl/material/solid/organic/wood/chipboard/ebony + name = "ebony chipboard" + lore_text = "Also known as particle board, this material is made from various kinds of ebony wood chips and resin, with a plastic laminate." + adjective_name = "ebony laminate" + uid = "solid_wood_chipboard_ebony" + color = WOOD_COLOR_BLACK + +/decl/material/solid/organic/wood/chipboard/walnut + name = "walnut chipboard" + lore_text = "Also known as particle board, this material is made from various kinds of walnut wood chips and resin, with a plastic laminate." + adjective_name = "walnut laminate" + uid = "solid_wood_chipboard_walnut" + color = WOOD_COLOR_CHOCOLATE diff --git a/code/modules/materials/material_sheets_mapping.dm b/code/modules/materials/material_sheets_mapping.dm index d852c202c26f..33dfdcb211c3 100644 --- a/code/modules/materials/material_sheets_mapping.dm +++ b/code/modules/materials/material_sheets_mapping.dm @@ -117,4 +117,10 @@ STACK_SUBTYPES(titanium, "titanium", solid/metal/tita STACK_SUBTYPES(cotton, "cotton", solid/organic/cloth, thread, null) STACK_SUBTYPES(dried_gut, "dried gut", solid/organic/leather/gut, thread, null) +STACK_SUBTYPES(chipboard_pine, "pine chipboard", solid/organic/wood/chipboard, sheet, null) +STACK_SUBTYPES(chipboard_maple, "maple chipboard", solid/organic/wood/chipboard/maple, sheet, null) +STACK_SUBTYPES(chipboard_mahogany, "mahogany chipboard", solid/organic/wood/chipboard/mahogany, sheet, null) +STACK_SUBTYPES(chipboard_ebony, "ebony chipboard", solid/organic/wood/chipboard/ebony, sheet, null) +STACK_SUBTYPES(chipboard_walnut, "walnut chipboard", solid/organic/wood/chipboard/walnut, sheet, null) + #undef STACK_SUBTYPES \ No newline at end of file diff --git a/code/modules/reagents/reactions/reaction_synthesis.dm b/code/modules/reagents/reactions/reaction_synthesis.dm index 90f7a0e19577..f30d0c4b049e 100644 --- a/code/modules/reagents/reactions/reaction_synthesis.dm +++ b/code/modules/reagents/reactions/reaction_synthesis.dm @@ -146,3 +146,57 @@ return for(var/i = 1 to create_soap) new /obj/item/soap/crafted(T) + +// Making chipboard out of wood scraps/recycled wood. +/decl/chemical_reaction/synthesis/chipboard + name = "Pine Chipboard" + required_reagents = list( + /decl/material/solid/organic/wood = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2), + /decl/material/solid/organic/plastic = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2) + ) + result_amount = 1 + mix_message = "The wood particulate binds with the plastic to form laminated chipboard." + minimum_temperature = 100 CELSIUS + var/chipboard_type = /decl/material/solid/organic/wood/chipboard + +/decl/chemical_reaction/synthesis/chipboard/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) + ..() + var/turf/T = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) + if(!istype(T)) + return + var/create_sheets = floor(created_volume) + if(create_sheets <= 0) + return + new /obj/item/stack/material/sheet(T, create_sheets, chipboard_type) + +/decl/chemical_reaction/synthesis/chipboard/maple + name = "Maple Chipboard" + required_reagents = list( + /decl/material/solid/organic/wood/maple = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2), + /decl/material/solid/organic/plastic = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2) + ) + chipboard_type = /decl/material/solid/organic/wood/chipboard/maple + +/decl/chemical_reaction/synthesis/chipboard/mahogany + name = "Mahogany Chipboard" + required_reagents = list( + /decl/material/solid/organic/wood/mahogany = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2), + /decl/material/solid/organic/plastic = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2) + ) + chipboard_type = /decl/material/solid/organic/wood/chipboard/mahogany + +/decl/chemical_reaction/synthesis/chipboard/ebony + name = "Ebony Chipboard" + required_reagents = list( + /decl/material/solid/organic/wood/ebony = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2), + /decl/material/solid/organic/plastic = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2) + ) + chipboard_type = /decl/material/solid/organic/wood/chipboard/ebony + +/decl/chemical_reaction/synthesis/chipboard/walnut + name = "Walnut Chipboard" + required_reagents = list( + /decl/material/solid/organic/wood/walnut = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2), + /decl/material/solid/organic/plastic = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2) + ) + chipboard_type = /decl/material/solid/organic/wood/chipboard/walnut diff --git a/maps/antag_spawn/ert/ert_base.dmm b/maps/antag_spawn/ert/ert_base.dmm index 30c9701f6a83..e1e1f3f16a8f 100644 --- a/maps/antag_spawn/ert/ert_base.dmm +++ b/maps/antag_spawn/ert/ert_base.dmm @@ -1555,7 +1555,7 @@ /turf/unsimulated/floor/vault, /area/map_template/rescue_base/base) "ev" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/machinery/button/blast_door{ @@ -1567,7 +1567,7 @@ /turf/unsimulated/floor/cult, /area/map_template/rescue_base/base) "ew" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/radio/phone{ @@ -1577,7 +1577,7 @@ /turf/unsimulated/floor/cult, /area/map_template/rescue_base/base) "ex" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/machinery/button/blast_door{ @@ -1747,7 +1747,7 @@ /turf/unsimulated/floor/dark, /area/map_template/rescue_base/base) "eH" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/ashtray, @@ -1761,7 +1761,7 @@ /turf/unsimulated/floor/cult, /area/map_template/rescue_base/base) "eJ" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /turf/unsimulated/floor/cult, diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index c167b8ef9e01..10ca7b3af294 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -302,7 +302,7 @@ "aM" = ( /obj/item/paper_bin, /obj/item/pen, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/light_switch{ pixel_y = 25 }, @@ -479,7 +479,7 @@ /turf/floor/wood, /area/ship/scrap/command/captain) "bc" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable{ icon_state = "0-8" }, diff --git a/maps/away/casino/casino.dmm b/maps/away/casino/casino.dmm index b1d54db6ab7c..34f3ab6aa0d7 100644 --- a/maps/away/casino/casino.dmm +++ b/maps/away/casino/casino.dmm @@ -1293,7 +1293,7 @@ /turf/floor/plating, /area/casino/casino_storage) "dQ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/cigar{ pixel_y = 5 }, @@ -1317,7 +1317,7 @@ /turf/floor/plating, /area/casino/casino_storage) "dS" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/toolbox/mechanical, /obj/item/stack/cable_coil, /turf/floor/plating, @@ -2244,7 +2244,7 @@ /turf/floor/tiled, /area/casino/casino_mainfloor) "gu" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/clothing/mask/smokable/pipe, /obj/machinery/light{ dir = 1 @@ -2253,7 +2253,7 @@ /turf/floor/carpet, /area/casino/casino_mainfloor) "gv" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/cigar, /obj/item/box/fancy/cigar{ pixel_y = 5 @@ -2726,13 +2726,13 @@ /turf/floor/carpet, /area/casino/casino_mainfloor) "ia" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/plate, /obj/item/utensil/fork, /turf/floor/carpet, /area/casino/casino_mainfloor) "ib" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/carpet, /area/casino/casino_mainfloor) "ic" = ( @@ -2855,13 +2855,13 @@ /turf/floor/carpet, /area/casino/casino_mainfloor) "iq" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/plate, /obj/item/food/applepie, /turf/floor/carpet, /area/casino/casino_mainfloor) "ir" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/plate, /obj/item/food/bigbiteburger, /turf/floor/carpet, @@ -3048,7 +3048,7 @@ /turf/floor/tiled, /area/casino/casino_kitchen) "iR" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/plate, /obj/item/utensil/spoon, /turf/floor/carpet, @@ -3385,7 +3385,7 @@ /turf/floor/carpet, /area/casino/casino_mainfloor) "jJ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/ashtray, /obj/item/food/cubancarp, /turf/floor/carpet, @@ -3463,7 +3463,7 @@ /turf/floor/tiled, /area/casino/casino_mainfloor) "jU" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/plate, /obj/item/food/waffles, /obj/item/chems/drinks/cans/iced_tea, @@ -3631,7 +3631,7 @@ /turf/floor/tiled, /area/casino/casino_mainfloor) "kt" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pizzabox/meat, /turf/floor/carpet, /area/casino/casino_mainfloor) @@ -4219,14 +4219,14 @@ /turf/floor/wood, /area/casino/casino_private_vip) "lZ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/cigar{ pixel_y = 5 }, /turf/floor/wood, /area/casino/casino_private_vip) "ma" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/ashtray, /obj/random/drinkbottle, /turf/floor/wood, @@ -4281,7 +4281,7 @@ /turf/floor/tiled/white, /area/casino/casino_patron_bathroom) "mj" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/drinks/bottle/agedwhiskey, /obj/item/clothing/mask/smokable/cigarette/cigar/havana, /obj/item/clothing/mask/smokable/cigarette/cigar/havana, @@ -4328,12 +4328,12 @@ /turf/floor/wood, /area/casino/casino_private_vip) "mq" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/drinkbottle, /turf/floor/wood, /area/casino/casino_private_vip) "mr" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood, /area/casino/casino_private_vip) "ms" = ( @@ -4348,7 +4348,7 @@ /turf/floor/wood, /area/casino/casino_private_vip) "mu" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/drinkbottle, /obj/random/coin, /obj/random/coin, @@ -4368,7 +4368,7 @@ /turf/floor/wood, /area/casino/casino_private1) "mx" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/drinkbottle, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 diff --git a/maps/away/errant_pisces/errant_pisces.dmm b/maps/away/errant_pisces/errant_pisces.dmm index 7e62686eb0d9..dd44f08ac770 100644 --- a/maps/away/errant_pisces/errant_pisces.dmm +++ b/maps/away/errant_pisces/errant_pisces.dmm @@ -2562,7 +2562,7 @@ /turf/floor/wood, /area/errant_pisces/rooms) "gw" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/unary/vent_pump/on{ level = 2 }, @@ -2742,7 +2742,7 @@ /turf/floor/tiled, /area/errant_pisces/dorms) "gZ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood, /area/errant_pisces/rooms) "ha" = ( @@ -2752,7 +2752,7 @@ /turf/floor/wood, /area/errant_pisces/rooms) "hb" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /obj/item/flashlight/lamp, /turf/floor/wood, @@ -2769,7 +2769,7 @@ /turf/floor/wood, /area/errant_pisces/rooms) "he" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/ashtray, /obj/random/smokes, /turf/floor/wood, @@ -2941,7 +2941,7 @@ name = "south bump"; pixel_y = -24 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/smokes, /obj/structure/cable/green, /obj/machinery/atmospherics/unary/vent_scrubber/on{ @@ -5784,7 +5784,7 @@ /turf/floor/wood, /area/errant_pisces/bridge) "pn" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood, /area/errant_pisces/bridge) "po" = ( @@ -5870,7 +5870,7 @@ /turf/floor/wood, /area/errant_pisces/bridge) "pC" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/toy, /turf/floor/wood, /area/errant_pisces/bridge) diff --git a/maps/away/liberia/liberia.dmm b/maps/away/liberia/liberia.dmm index 35999ef09ff5..5808decc8c01 100644 --- a/maps/away/liberia/liberia.dmm +++ b/maps/away/liberia/liberia.dmm @@ -1656,7 +1656,7 @@ /turf/floor/tiled/techfloor, /area/liberia/engineeringengines) "cZ" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/machinery/chemical_dispenser/bar_coffee{ dir = 1 }, @@ -3301,7 +3301,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 1 }, -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /turf/floor/carpet, /area/liberia/bar) @@ -3325,7 +3325,7 @@ /turf/floor/wood/ebony, /area/liberia/bar) "fS" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/unary/vent_scrubber/on, /obj/machinery/light{ dir = 1 @@ -3348,7 +3348,7 @@ /turf/floor/wood/ebony, /area/liberia/bar) "fU" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/random/drinkbottle, /obj/random/drinkbottle, /obj/structure/window/reinforced{ @@ -3357,12 +3357,12 @@ /turf/floor/wood/ebony, /area/liberia/bar) "fV" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/machinery/chemical_dispenser/bar_soft/full, /turf/floor/wood/ebony, /area/liberia/bar) "fW" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/machinery/light{ dir = 4 }, @@ -3486,7 +3486,7 @@ /area/liberia/bar) "gg" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/deck/cards, /turf/floor/carpet, /area/liberia/bar) @@ -3513,7 +3513,7 @@ /turf/floor/wood/ebony, /area/liberia/bar) "gl" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/box/glasses/pint, /turf/floor/wood/ebony, /area/liberia/bar) @@ -3640,7 +3640,7 @@ /turf/floor/wood/ebony, /area/liberia/bar) "gx" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/structure/window/reinforced{ dir = 8 }, @@ -3653,7 +3653,7 @@ /turf/floor/wood/ebony, /area/liberia/bar) "gy" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/ashtray/glass, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -3665,7 +3665,7 @@ /turf/floor/wood/ebony, /area/liberia/bar) "gz" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/chems/glass/rag, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -4194,7 +4194,7 @@ /turf/floor/wood/walnut, /area/liberia/bar) "hA" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/scanner/price, /obj/item/scanner/price, /obj/machinery/light/small, @@ -4206,21 +4206,21 @@ /turf/floor/tiled/techfloor/grid, /area/liberia/engineeringreactor) "hC" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/ashtray, /obj/item/box/matches, /obj/random/smokes, /turf/floor/carpet/red, /area/liberia/captain) "hD" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /mob/living/simple_animal/tindalos{ name = "Eddy" }, /turf/floor/carpet/red, /area/liberia/captain) "hE" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /turf/floor/carpet/red, /area/liberia/captain) @@ -4257,7 +4257,7 @@ /turf/floor/carpet, /area/liberia/personellroom2) "hI" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 8 @@ -4312,7 +4312,7 @@ /turf/floor/tiled/techfloor/grid, /area/liberia/merchantstorage) "hN" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/random/action_figure, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 @@ -5178,7 +5178,7 @@ /turf/floor/carpet, /area/liberia/library) "jX" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/lava/orange, /obj/effect/floor_decal/spline/fancy/wood{ dir = 4 @@ -5186,7 +5186,7 @@ /turf/floor/carpet, /area/liberia/library) "jY" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/structure/flora/pottedplant/deskleaf{ pixel_x = -5; pixel_y = 2 @@ -5222,7 +5222,7 @@ /turf/floor/carpet/magenta, /area/liberia/guestroom2) "ka" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/structure/flora/pottedplant/smallcactus{ pixel_x = -5; pixel_y = 9 @@ -5246,7 +5246,7 @@ /turf/floor/carpet, /area/liberia/library) "kc" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/box/fancy/donut, /obj/effect/floor_decal/spline/fancy/wood{ dir = 6 @@ -5424,7 +5424,7 @@ /turf/floor/carpet, /area/liberia/library) "kr" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/modular_computer/laptop/preset/custom_loadout/standard{ dir = 8 }, @@ -5472,7 +5472,7 @@ /turf/floor/carpet, /area/liberia/library) "ky" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/effect/floor_decal/spline/fancy/wood, /obj/machinery/power/apc/liberia{ name = "south bump"; @@ -5482,7 +5482,7 @@ /turf/floor/carpet, /area/liberia/library) "kz" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /obj/item/pen, /obj/machinery/light/small{ @@ -6241,7 +6241,7 @@ /turf/floor/tiled/techfloor/grid, /area/liberia/merchantstorage) "os" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/effect/floor_decal/spline/fancy/wood, /turf/floor/carpet/red, /area/liberia/traidingroom) @@ -6421,7 +6421,7 @@ /turf/floor/tiled/techfloor, /area/liberia/cryo) "pZ" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/machinery/fabricator/micro/bartender{ pixel_x = 4 }, @@ -6757,7 +6757,7 @@ pixel_x = 4; pixel_y = -4 }, -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/effect/floor_decal/spline/fancy/wood{ dir = 6 }, @@ -7120,7 +7120,7 @@ /turf/wall/r_wall/prepainted, /area/liberia/captain) "Af" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/effect/floor_decal/borderfloor{ dir = 10 }, @@ -7748,7 +7748,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 8 }, -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /turf/floor/carpet/blue, /area/liberia/personellroom1) "Kh" = ( @@ -7862,7 +7862,7 @@ /turf/wall/r_wall/prepainted, /area/liberia/officeroom) "Ml" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/ashtray/glass, /obj/effect/floor_decal/borderfloor{ dir = 6 @@ -8106,7 +8106,7 @@ /turf/floor/tiled/techfloor, /area/liberia/merchantstorage) "QK" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/effect/floor_decal/borderfloor, /obj/effect/floor_decal/corner/green{ dir = 5 @@ -8512,7 +8512,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 10 }, -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /turf/floor/carpet/blue, /area/liberia/personellroom1) diff --git a/maps/away/magshield/magshield.dmm b/maps/away/magshield/magshield.dmm index d6e4edc107af..ab90cfc20bcd 100644 --- a/maps/away/magshield/magshield.dmm +++ b/maps/away/magshield/magshield.dmm @@ -2268,7 +2268,7 @@ /turf/floor/carpet/blue, /area/magshield/west) "gH" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 10 }, /obj/item/box/checkers, @@ -2278,20 +2278,20 @@ /turf/floor/carpet/blue, /area/magshield/west) "gI" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 10 }, /obj/item/belt/champion, /turf/floor/carpet/blue, /area/magshield/west) "gJ" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 10 }, /turf/floor/carpet/blue, /area/magshield/west) "gK" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 10 }, /obj/item/bible/booze, @@ -2385,7 +2385,7 @@ /turf/floor/tiled, /area/magshield/west) "gY" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/action_figure, /turf/floor/tiled, /area/magshield/west) @@ -2483,7 +2483,7 @@ /turf/floor/tiled, /area/magshield/west) "hn" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/sword/replica, /turf/floor/tiled, /area/magshield/west) @@ -2523,7 +2523,7 @@ /turf/floor/plating/airless, /area/space) "hw" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/plushie/large, /turf/floor/tiled, /area/magshield/west) @@ -2565,7 +2565,7 @@ /turf/floor/tiled, /area/magshield/west) "hC" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/toy/shipmodel, /turf/floor/tiled, /area/magshield/west) @@ -2614,7 +2614,7 @@ /turf/floor/carpet/blue, /area/magshield/west) "hK" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/book/manual/supermatter_engine, /turf/floor/carpet/blue, /area/magshield/west) @@ -2665,7 +2665,7 @@ /turf/floor/carpet/blue, /area/magshield/west) "hT" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/book/manual/evaguide, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -2727,7 +2727,7 @@ /turf/floor/tiled, /area/magshield/west) "hZ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/accessory, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 @@ -2735,7 +2735,7 @@ /turf/floor/carpet/blue, /area/magshield/west) "ia" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flame/fuelled/lighter/zippo/random, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 @@ -2743,7 +2743,7 @@ /turf/floor/carpet/blue, /area/magshield/west) "ib" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/ashtray, /obj/random/smokes, /obj/machinery/light, diff --git a/maps/away/mining/mining-signal.dmm b/maps/away/mining/mining-signal.dmm index 9876407c3682..0d101569e586 100644 --- a/maps/away/mining/mining-signal.dmm +++ b/maps/away/mining/mining-signal.dmm @@ -667,18 +667,18 @@ /turf/floor/wood/broken/three, /area/outpost/abandoned) "co" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/effect/decal/cleanable/dirt, /obj/random/trash, /turf/floor/wood, /area/outpost/abandoned) "cp" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/board, /turf/floor/wood, /area/outpost/abandoned) "cq" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/loot, /turf/floor/wood, /area/outpost/abandoned) @@ -773,7 +773,7 @@ /turf/floor/carpet/blue, /area/outpost/abandoned) "cO" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/loot, /turf/floor/carpet/blue, /area/outpost/abandoned) @@ -975,13 +975,13 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 10 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/loot, /turf/floor/carpet, /area/outpost/abandoned) "dx" = ( /obj/effect/floor_decal/spline/fancy/wood, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/carpet, /area/outpost/abandoned) "dy" = ( @@ -994,7 +994,7 @@ /area/outpost/abandoned) "dz" = ( /obj/effect/floor_decal/spline/fancy/wood, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/loot, /turf/floor/carpet/broken, /area/outpost/abandoned) @@ -1002,7 +1002,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 6 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/carpet, /area/outpost/abandoned) "dC" = ( @@ -1100,7 +1100,7 @@ /turf/wall/titanium, /area/outpost/abandoned) "dR" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ icon_state = "solid_flip0" }, /turf/floor/carpet/blue, diff --git a/maps/away/slavers/slavers_base.dmm b/maps/away/slavers/slavers_base.dmm index f9648ba28634..660b97f4fde2 100644 --- a/maps/away/slavers/slavers_base.dmm +++ b/maps/away/slavers/slavers_base.dmm @@ -2841,12 +2841,12 @@ /turf/floor/tiled/airless, /area/slavers_base/demo) "hO" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/radio/shortwave, /turf/floor/tiled/airless, /area/slavers_base/demo) "hP" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/secure_storage/briefcase/money, /obj/random/cash, /obj/random/cash, @@ -2976,12 +2976,12 @@ /turf/floor/tiled/airless, /area/slavers_base/demo) "ij" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/bag/cash, /turf/floor/tiled/airless, /area/slavers_base/demo) "ik" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/cash, /turf/floor/tiled/airless, /area/slavers_base/demo) @@ -3052,7 +3052,7 @@ /turf/floor/tiled, /area/slavers_base/dorms) "iy" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper{ info = "

    Contract

    This contract describes exchanging of monetary pieces for the right o? the ownership for following examples: <*> Human, age 17. Price - 1500cr. <*> Human, age 49. Price - 1100cr. <*> Human, age 28. Good fist fighter. Price - 2400cr. <*> Human, age 34. Expirienced medic. Price - 6800cr. Overall price: 11800cr
    Place for signatures"; name = "Contract" @@ -3060,7 +3060,7 @@ /turf/floor/tiled/airless, /area/slavers_base/demo) "iz" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/coin, /obj/item/pen, /turf/floor/tiled/airless, diff --git a/maps/away/smugglers/smugglers.dmm b/maps/away/smugglers/smugglers.dmm index 808d13e14434..5e6ccae998bd 100644 --- a/maps/away/smugglers/smugglers.dmm +++ b/maps/away/smugglers/smugglers.dmm @@ -611,7 +611,7 @@ /turf/floor/tiled, /area/smugglers/office) "bD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper, /obj/item/pen, /obj/item/flashlight/lamp, @@ -665,7 +665,7 @@ /turf/floor/tiled, /area/smugglers/office) "bK" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/coin{ pixel_x = -5; pixel_y = -3 diff --git a/maps/away/unishi/unishi-2.dmm b/maps/away/unishi/unishi-2.dmm index b63e93afd205..d3cc41c6a3bc 100644 --- a/maps/away/unishi/unishi-2.dmm +++ b/maps/away/unishi/unishi-2.dmm @@ -13,7 +13,7 @@ /turf/wall/titanium, /area/space) "ae" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/advdevice, /obj/item/flashlight/lamp, /turf/floor/carpet/red, @@ -32,7 +32,7 @@ /turf/floor/carpet/red, /area/unishi/library) "ai" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp, /turf/floor/carpet/red, /area/unishi/library) @@ -960,18 +960,18 @@ /turf/floor/tiled, /area/unishi/meeting) "cN" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /obj/item/pen/fancy, /turf/floor/tiled, /area/unishi/meeting) "cO" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pen/fancy, /turf/floor/tiled, /area/unishi/meeting) "cP" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/folder/yellow, /obj/item/pen/green, /turf/floor/tiled, @@ -1023,7 +1023,7 @@ /turf/floor/tiled, /area/unishi/meeting) "cX" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -1033,7 +1033,7 @@ /turf/floor/tiled, /area/unishi/meeting) "cY" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -1042,7 +1042,7 @@ /turf/floor/tiled, /area/unishi/meeting) "cZ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -1982,7 +1982,7 @@ /turf/floor/tiled/dark, /area/unishi/common) "fy" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/snack, /obj/item/flashlight/lamp, /turf/floor/tiled/dark, @@ -2008,7 +2008,7 @@ /turf/floor/tiled/dark, /area/unishi/common) "fC" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /obj/item/flashlight/lamp, /turf/floor/tiled/dark, diff --git a/maps/away/unishi/unishi-3.dmm b/maps/away/unishi/unishi-3.dmm index 8ad33994f7dc..43eb73530886 100644 --- a/maps/away/unishi/unishi-3.dmm +++ b/maps/away/unishi/unishi-3.dmm @@ -896,7 +896,7 @@ /turf/floor/wood, /area/unishi/lounge) "cH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/cell/crap, /obj/item/stock_parts/circuitboard/gyrotron_control, /obj/item/paper_bin, @@ -910,7 +910,7 @@ /turf/space, /area/space) "cJ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/trash/raisins, /obj/item/pizzabox/margherita, /obj/random/advdevice, @@ -920,7 +920,7 @@ /turf/floor/tiled, /area/unishi/living) "cK" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/dice, /obj/item/deck/cards, /obj/item/pen/fancy, @@ -928,14 +928,14 @@ /turf/floor/wood, /area/unishi/lounge) "cL" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/board, /obj/item/book/manual/mass_spectrometry, /obj/item/book/fluff/stasis, /turf/floor/tiled, /area/unishi/living) "cM" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, @@ -1024,7 +1024,7 @@ /turf/floor/tiled, /area/unishi/living) "cY" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/trash/raisins, /obj/item/book/manual/nuclear, /turf/floor/tiled, @@ -1091,7 +1091,7 @@ /turf/floor/tiled, /area/unishi/kitchen) "dg" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/trash/candy, /obj/machinery/recharger, /turf/floor/tiled, @@ -1268,7 +1268,7 @@ /turf/floor/tiled, /area/unishi/living) "dD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/cane/fancy/sword, /obj/item/clothing/suit/radiation, /turf/floor/tiled, @@ -1441,7 +1441,7 @@ /turf/floor/tiled, /area/unishi/living) "eb" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/poster, /obj/item/grooming/comb/colorable/random, /obj/random/advdevice, @@ -1641,7 +1641,7 @@ /turf/floor/wood, /area/unishi/living) "eB" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, @@ -1720,7 +1720,7 @@ /turf/floor/wood, /area/unishi/living) "eJ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/grooming/brush, /obj/item/cosmetics/lipstick/black, /obj/item/cosmetics/lipstick/green, @@ -1780,7 +1780,7 @@ /turf/floor/wood, /area/unishi/living) "eS" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pen/fancy, /obj/random/drinkbottle, /turf/floor/wood, @@ -1796,7 +1796,7 @@ /turf/floor/wood, /area/unishi/living) "eV" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /turf/floor/wood, /area/unishi/living) diff --git a/maps/away/yacht/yacht.dmm b/maps/away/yacht/yacht.dmm index 3e56a19c9e63..84dd4a4fe613 100644 --- a/maps/away/yacht/yacht.dmm +++ b/maps/away/yacht/yacht.dmm @@ -56,7 +56,7 @@ "am" = ( /obj/item/folder/blue, /obj/item/form_printer, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/drinks/glass2/coffeecup, /obj/item/newspaper, /obj/effect/spider/stickyweb, @@ -540,7 +540,7 @@ }, /obj/item/bible, /obj/item/pen/blue, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper{ info = "Let me introduce myself. My name is Commander Archibald McKinley, although the Fleet fucks think that I do not deserve the title. Well, fuck them. They accused me of facilitating a destruction of the ship I was CO of. A small missile cruiser, with zero to fucking none point defenses was attacked, and they first accuse me of escaping before any of my crew. What kind of person wouldn't expect someone to escape when their ship is on fire? It's lunacy. But it only got worse. Then they said that I helped the Terrans take the ship. Well fuck them, they are wrong. That's non sense. I have no connection to the Terrans. They said that I got a large sum of money from them in order to betray my ship's position, which is once again total nonsense. I've got about 20 years of life left here, and I know that I will be safe with my ship here. I need to resupply once every 5 years, given how much food and fuel I have, and I've got enough range to go to pretty much any known part of the galaxy, in complete silence, as we are totally solar powered. I wish I could afford ion thrusters to not have to rely on gas for propulsion, but, our gas heaters should provide us with enough pressure to get anywhere in relative decent speed. Also, I got a robot butler who cooks delicious food for me! No more crappy lance corporal food, eh" }, @@ -1168,7 +1168,7 @@ /obj/item/clothing/suit/det_trench/grey, /obj/item/clothing/gloves/ring/cti, /obj/item/clothing/costume/oldman, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/effect/spider/stickyweb, /obj/random/cash, /obj/random/cash, diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 1b0190807cf8..655a78cc42f2 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -60,7 +60,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/security/detectives_office) "aae" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/drinks/flask/barflask{ pixel_x = -4; pixel_y = 8 @@ -1539,7 +1539,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/meeting) "adu" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flame/candle{ pixel_x = -5; pixel_y = 5 @@ -3533,7 +3533,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/main) "ahH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/megaphone, /obj/item/radio/off, /turf/floor/tiled/dark, @@ -3587,7 +3587,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/heads/hos) "ahO" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/keycard_auth{ dir = 8 }, @@ -3790,7 +3790,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/heads/hos) "air" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable/green{ icon_state = "1-4" }, @@ -3805,7 +3805,7 @@ name = "east bump"; pixel_x = 24 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/recharger{ pixel_y = 4 }, @@ -3985,7 +3985,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/main) "aiP" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/taperecorder, /turf/floor/tiled/dark, /area/exodus/crew_quarters/heads/hos) @@ -3993,7 +3993,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/heads/hos) "aiR" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable/green{ icon_state = "4-8" }, @@ -4004,7 +4004,7 @@ dir = 8; pixel_x = 24 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = -3; pixel_y = 7 @@ -4507,7 +4507,7 @@ /turf/floor/tiled/dark, /area/exodus/crew_quarters/heads/hos) "ajL" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/newscaster{ pixel_x = 28; pixel_y = 1 @@ -4951,7 +4951,7 @@ /turf/floor/lino, /area/exodus/security/detectives_office) "akD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /obj/item/secure_storage/safe{ pixel_x = 6; @@ -4986,7 +4986,7 @@ /obj/machinery/alarm{ pixel_y = 22 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flash, /obj/item/clothing/glasses/sunglasses, /obj/item/chems/spray/pepper, @@ -5132,7 +5132,7 @@ /turf/floor/carpet, /area/exodus/security/detectives_office) "akU" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/ashtray/plastic, /obj/item/box/fancy/cigarettes/dromedaryco, /obj/item/clothing/gloves/forensic, @@ -5495,7 +5495,7 @@ /obj/structure/cable/green{ icon_state = "4-8" }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/button/alternate/door{ id_tag = "detdoor"; name = "Office Door" @@ -6350,7 +6350,7 @@ /obj/item/secure_storage/safe{ pixel_x = -23 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /turf/floor/lino, /area/exodus/security/detectives_office) @@ -6362,7 +6362,7 @@ /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/button/alternate/door{ id_tag = "detdoor"; name = "Office Door" @@ -6619,7 +6619,7 @@ dir = 8; pixel_x = 22 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/photo_album{ pixel_y = -10 }, @@ -6732,7 +6732,7 @@ /turf/floor/carpet, /area/exodus/security/detectives_office) "aof" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flash, /obj/item/chems/spray/pepper, /obj/item/clothing/glasses/sunglasses, @@ -11368,7 +11368,7 @@ /turf/floor/plating, /area/exodus/maintenance/security_port) "axY" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/clothing/glasses/threedglasses, /turf/floor/tiled/dark, /area/exodus/crew_quarters/sleep) @@ -11381,7 +11381,7 @@ /turf/floor/tiled/dark, /area/exodus/crew_quarters/sleep) "aya" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/coin, /turf/floor/tiled/dark, /area/exodus/crew_quarters/sleep) @@ -11797,7 +11797,7 @@ /turf/floor/tiled/dark, /area/exodus/crew_quarters/sleep) "ayQ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paicard, /obj/structure/cable/green{ icon_state = "4-8" @@ -11810,7 +11810,7 @@ /turf/floor/tiled/dark, /area/exodus/crew_quarters/sleep) "ayR" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable/green{ icon_state = "4-8" }, @@ -13764,7 +13764,7 @@ /turf/floor/tiled/dark, /area/shuttle/arrival/station) "aDi" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/fabricator/book, /obj/item/stack/material/panel/mapped/plastic/ten, /obj/item/stack/material/plank/mapped/wood/ten, @@ -13891,7 +13891,7 @@ /obj/machinery/status_display{ pixel_y = 32 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = 1; pixel_y = 9 @@ -14224,7 +14224,7 @@ /turf/floor/tiled/dark/monotile, /area/exodus/gateway) "aEh" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/reagentgrinder, /obj/item/chems/drinks/shaker, /obj/item/stack/package_wrap, @@ -14348,7 +14348,7 @@ /turf/floor/plating, /area/exodus/maintenance/bar) "aEv" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/gun/projectile/shotgun/doublebarrel, /obj/machinery/atmospherics/unary/vent_pump/on, /turf/floor/wood/walnut, @@ -14463,7 +14463,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/maintenance/auxsolarport) "aEF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -15576,7 +15576,7 @@ /turf/floor/plating, /area/exodus/maintenance/library) "aGM" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/dice/d20, /obj/item/dice, /obj/item/radio/intercom{ @@ -16646,7 +16646,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/hallway/secondary/entry/starboard) "aIN" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/recharger, /turf/floor/tiled/dark, @@ -16805,7 +16805,7 @@ /turf/wall/r_wall/prepainted, /area/exodus/hallway/primary/central_two) "aJj" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = -2; pixel_y = 5 @@ -17658,7 +17658,7 @@ /turf/wall/prepainted, /area/exodus/crew_quarters/kitchen) "aLa" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp{ pixel_y = 10 }, @@ -17669,7 +17669,7 @@ /turf/floor/lino, /area/exodus/chapel/office) "aLb" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/nullrod, /obj/item/eftpos{ eftpos_name = "Chapel EFTPOS scanner" @@ -17677,7 +17677,7 @@ /turf/floor/lino, /area/exodus/chapel/office) "aLc" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pen, /obj/item/chems/drinks/bottle/holywater, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -17752,7 +17752,7 @@ /turf/floor/tiled/dark, /area/exodus/chapel/main) "aLl" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/folder/yellow, /obj/item/pen, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -18471,7 +18471,7 @@ /turf/floor/tiled/dark, /area/exodus/chapel/main) "aMR" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/recharger, /turf/floor/wood/walnut, /area/exodus/library) @@ -18541,7 +18541,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/bar) "aNa" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green{ pixel_x = 1; pixel_y = 5 @@ -19130,7 +19130,7 @@ /turf/floor/wood/walnut, /area/exodus/library) "aOg" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/food/junk/chips, /obj/random/single{ name = "randomly spawned cola"; @@ -19445,13 +19445,13 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/secondary/entry/port) "aOL" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/disposalpipe/segment, /obj/item/deck/cards, /turf/floor/wood/walnut, /area/exodus/library) "aOM" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/ashtray/plastic{ pixel_x = -1; pixel_y = 1 @@ -20127,7 +20127,7 @@ /turf/floor/plating, /area/exodus/maintenance/arrivals) "aQj" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/cigarettes{ pixel_y = 2 }, @@ -20220,7 +20220,7 @@ /area/exodus/security/prison/restroom) "aQw" = ( /obj/machinery/photocopier, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood/walnut, /area/exodus/library) "aQx" = ( @@ -20536,7 +20536,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/library) "aRm" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = -3; pixel_y = 7 @@ -20657,7 +20657,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/bar) "aRx" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/single{ name = "randomly spawned deck of cards"; spawn_object = /obj/item/deck/cards @@ -20925,7 +20925,7 @@ /obj/machinery/camera/network/civilian_east{ c_tag = "Bar East" }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /obj/item/pen/blue{ pixel_x = -3; @@ -21910,7 +21910,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) "aUp" = ( @@ -22265,7 +22265,7 @@ /turf/floor/plating, /area/exodus/maintenance/bar) "aVi" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/carpet, /area/exodus/chapel/main) "aVj" = ( @@ -22655,7 +22655,7 @@ pixel_x = 2; pixel_y = 6 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/condiment/small/saltshaker{ pixel_x = -2; pixel_y = 4 @@ -22696,7 +22696,7 @@ /obj/structure/cable/green{ icon_state = "4-8" }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) "aWi" = ( @@ -22890,7 +22890,7 @@ /turf/floor/wood/walnut, /area/exodus/library) "aWH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/light/small{ dir = 4 }, @@ -23262,7 +23262,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/research/mixing) "aXz" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/utensil/fork, /obj/item/utensil/fork, /turf/floor/wood/walnut, @@ -23275,13 +23275,13 @@ /turf/floor/tiled/white, /area/exodus/crew_quarters/kitchen) "aXB" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/board, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) "aXD" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/checkers/chess/red, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) @@ -23304,7 +23304,7 @@ /area/exodus/crew_quarters/kitchen) "aXF" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/disposalpipe/segment, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) @@ -23565,7 +23565,7 @@ /turf/floor/plating, /area/exodus/hallway/secondary/entry/port) "aYl" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green{ pixel_x = 1; pixel_y = 5 @@ -23749,7 +23749,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/locker) "aYF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/checkers, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) @@ -24120,7 +24120,7 @@ /turf/floor/tiled/white, /area/exodus/crew_quarters/kitchen) "aZm" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) "aZn" = ( @@ -24147,7 +24147,7 @@ /area/exodus/crew_quarters/kitchen) "aZp" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/disposalpipe/segment, /obj/item/camera, /turf/floor/wood/walnut, @@ -24263,7 +24263,7 @@ /turf/floor/carpet, /area/exodus/library) "aZD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper, /turf/floor/wood/walnut, /area/exodus/library) @@ -24278,13 +24278,13 @@ /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) "aZF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/camera_film, /obj/item/camera_film, /turf/floor/wood/walnut, /area/exodus/library) "aZG" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pen/red{ pixel_x = 2; pixel_y = 6 @@ -24296,11 +24296,11 @@ /turf/floor/wood/walnut, /area/exodus/library) "aZH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood/walnut, /area/exodus/library) "aZI" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = 1; pixel_y = 9 @@ -24953,7 +24953,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/locker) "bbd" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/condiment/small/peppermill{ pixel_x = 2; pixel_y = 6 @@ -24982,7 +24982,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/hallway/primary/starboard) "bbg" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/utensil/fork, /obj/item/utensil/fork, /obj/item/utensil/fork, @@ -25054,7 +25054,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/starboard) "bbo" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flame/candle, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) @@ -26531,7 +26531,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/office) "beA" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pen, /turf/floor/wood/walnut, /area/exodus/library) @@ -26552,7 +26552,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/captain) "beD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green{ pixel_x = 1; pixel_y = 5 @@ -27056,7 +27056,7 @@ /area/exodus/crew_quarters/captain) "bfG" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/checkers/chess, /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) @@ -28096,7 +28096,7 @@ /turf/floor/bluegrid, /area/exodus/turret_protected/ai) "bhU" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/donut, /obj/structure/cable/green{ icon_state = "4-8" @@ -28290,7 +28290,7 @@ /obj/item/eftpos{ eftpos_name = "Bridge EFTPOS scanner" }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable/green{ icon_state = "4-8" }, @@ -28313,14 +28313,14 @@ /area/exodus/quartermaster/storage) "bir" = ( /obj/item/folder/red, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable/green{ icon_state = "4-8" }, /turf/floor/carpet, /area/exodus/bridge/meeting_room) "bis" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable/green{ icon_state = "4-8" }, @@ -28349,7 +28349,7 @@ /turf/floor/wood/walnut, /area/exodus/bridge/meeting_room) "biv" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/faxmachine/mapped, /turf/floor/wood/walnut, /area/exodus/bridge/meeting_room) @@ -28405,7 +28405,7 @@ /turf/floor/bluegrid, /area/exodus/turret_protected/ai) "biE" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random_multi/single_item/captains_spare_id, /turf/floor/carpet, /area/exodus/crew_quarters/captain) @@ -29031,12 +29031,12 @@ pixel_y = 7 }, /obj/item/pen, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/carpet, /area/exodus/bridge/meeting_room) "bjJ" = ( /obj/item/folder/blue, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/carpet, /area/exodus/bridge/meeting_room) "bjK" = ( @@ -29532,14 +29532,14 @@ /turf/floor/carpet, /area/exodus/bridge/meeting_room) "bkH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/recharger{ pixel_y = 4 }, /turf/floor/wood/walnut, /area/exodus/bridge/meeting_room) "bkI" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = -3; pixel_y = 7 @@ -29605,7 +29605,7 @@ /turf/floor/bluegrid, /area/exodus/turret_protected/ai) "bkN" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/recharger{ pixel_y = 4 }, @@ -29985,7 +29985,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/hallway/primary/central_one) "blG" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/folder/blue, /obj/item/stamp/captain{ pixel_x = -4; @@ -30010,7 +30010,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/office) "blI" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /turf/floor/wood/walnut, /area/exodus/crew_quarters/captain) @@ -30022,7 +30022,7 @@ /turf/floor/wood/walnut, /area/exodus/crew_quarters/captain) "blK" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pinpointer, /obj/item/disk/nuclear, /obj/item/secure_storage/safe{ @@ -30488,7 +30488,7 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/donut, /turf/floor/wood/walnut, /area/exodus/bridge/meeting_room) @@ -30602,14 +30602,14 @@ /turf/floor/wood/walnut, /area/exodus/crew_quarters/bar) "bmR" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/eftpos{ eftpos_name = "Captain EFTPOS scanner" }, /turf/floor/wood/walnut, /area/exodus/crew_quarters/captain) "bmS" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/whip/chainofcommand, /obj/machinery/alarm{ dir = 8; @@ -31431,7 +31431,7 @@ /area/exodus/research/chargebay) "boF" = ( /obj/machinery/faxmachine/mapped, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/floor/wood/walnut, @@ -31956,7 +31956,7 @@ /turf/floor/plating, /area/exodus/storage/emergency) "bpG" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random_multi/single_item/captains_spare_id{ weight = 10 }, @@ -32382,7 +32382,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/research/robotics) "bqw" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /turf/floor/carpet, /area/exodus/crew_quarters/captain) @@ -33858,7 +33858,7 @@ dir = 1; pixel_y = -30 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/deck/cards{ pixel_y = 4 }, @@ -35557,7 +35557,7 @@ /turf/floor/tiled/white, /area/exodus/medical/medbay2) "bwU" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/matches, /obj/item/clothing/mask/smokable/cigarette/cigar, /obj/item/chems/drinks/flask{ @@ -35724,7 +35724,7 @@ /turf/floor/tiled/white, /area/exodus/medical/medbay2) "bxn" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/camera, /obj/item/photo_album{ pixel_y = -10 @@ -47700,7 +47700,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/medical/patient_wing) "bVc" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/dice, /turf/floor/wood/walnut, /area/exodus/engineering/break_room) @@ -47734,7 +47734,7 @@ /turf/floor/tiled/white, /area/exodus/research/misc_lab) "bVf" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /turf/floor/wood/walnut, /area/exodus/engineering/break_room) @@ -47950,7 +47950,7 @@ /turf/floor/plating, /area/exodus/engineering/engine_waste) "bVC" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/book/manual/engineering_guide{ pixel_x = 3; pixel_y = 2 @@ -47962,7 +47962,7 @@ /turf/floor/wood/walnut, /area/exodus/engineering/break_room) "bVD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/book/manual/engineering_construction, /obj/item/book/manual/evaguide{ pixel_x = -2; @@ -48520,7 +48520,7 @@ /turf/floor/tiled/white, /area/exodus/research) "bWO" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /obj/item/clipboard, /obj/item/folder/blue{ @@ -49407,7 +49407,7 @@ /turf/floor/reinforced, /area/exodus/research/mixing) "bYx" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/book/manual/supermatter_engine{ pixel_x = -3 }, @@ -49606,7 +49606,7 @@ /turf/floor/carpet, /area/exodus/engineering/break_room) "bYR" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = -3; pixel_y = 7 @@ -50243,12 +50243,12 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/engine_smes) "caj" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/folder/yellow, /turf/floor/carpet, /area/exodus/engineering/break_room) "cak" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/food/junk/chips, /turf/floor/carpet, /area/exodus/engineering/break_room) @@ -50464,7 +50464,7 @@ /turf/floor/wood, /area/exodus/medical/psych) "caG" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -50472,7 +50472,7 @@ /turf/floor/wood, /area/exodus/medical/psych) "caH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/computer/modular/preset/medical, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 8 @@ -52263,7 +52263,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/engine_eva) "cei" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood, /area/exodus/medical/psych) "cej" = ( @@ -53272,7 +53272,7 @@ /turf/floor/tiled/white, /area/exodus/medical/virology) "cgu" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/recharger, /turf/floor/carpet, /area/exodus/hallway/secondary/entry/starboard) @@ -56547,7 +56547,7 @@ /turf/wall/prepainted, /area/exodus/maintenance/arrivals) "cnn" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/taperecorder, /obj/item/camera, /obj/item/eftpos{ diff --git a/maps/exodus/exodus-admin.dmm b/maps/exodus/exodus-admin.dmm index c25765329809..f7b4411a7a1d 100644 --- a/maps/exodus/exodus-admin.dmm +++ b/maps/exodus/exodus-admin.dmm @@ -1440,14 +1440,14 @@ /turf/unsimulated/floor/steel, /area/centcom/holding) "aMY" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/amanita_pie, /turf/unsimulated/floor/wood, /area/centcom/holding) "aMZ" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/bigbiteburger, @@ -1466,13 +1466,13 @@ /turf/unsimulated/floor/wood, /area/centcom/holding) "aNc" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /turf/unsimulated/floor/wood, /area/centcom/holding) "aNh" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/chems/glass/bowl/mapped/stew, @@ -1483,7 +1483,7 @@ /turf/unsimulated/floor/lino, /area/centcom/holding) "aNk" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/book/manual/barman_recipes, @@ -1491,14 +1491,14 @@ /turf/unsimulated/floor/lino, /area/centcom/holding) "aNl" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/machinery/chemical_dispenser/bar_alc/full, /turf/unsimulated/floor/lino, /area/centcom/holding) "aNm" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/machinery/chemical_dispenser/bar_soft/full, @@ -1536,28 +1536,28 @@ /turf/floor/tiled/monotile, /area/shuttle/escape_shuttle) "aNB" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/boiledrice, /turf/unsimulated/floor/wood, /area/centcom/holding) "aNC" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/chems/glass/bowl/mapped/beet, /turf/unsimulated/floor/wood, /area/centcom/holding) "aNI" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/stuffing, /turf/unsimulated/floor/wood, /area/centcom/holding) "aNJ" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/soylenviridians, @@ -1713,14 +1713,14 @@ /turf/floor/tiled/techfloor/grid, /area/shuttle/escape_shuttle) "aPR" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/chems/glass/bowl/mapped/blood, /turf/unsimulated/floor/wood, /area/centcom/holding) "aPS" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/skewer/tofu, @@ -1730,7 +1730,7 @@ /turf/unsimulated/wall, /area/centcom/holding) "aRm" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/poppypretzel, @@ -1740,7 +1740,7 @@ /obj/structure/bed/chair/comfy/brown{ dir = 1 }, -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/clothing/pants/slacks/outfit, @@ -1754,13 +1754,13 @@ /obj/structure/bed/chair/comfy/brown{ dir = 1 }, -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /turf/unsimulated/floor/lino, /area/centcom/holding) "bgJ" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /turf/unsimulated/floor/lino, @@ -1831,7 +1831,7 @@ /turf/floor/tiled/dark/monotile, /area/shuttle/escape_shuttle) "bvb" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/spesslaw, @@ -1845,14 +1845,14 @@ /turf/floor/tiled, /area/shuttle/escape_shuttle) "byb" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/candiedapple, /turf/unsimulated/floor/wood, /area/centcom/holding) "bzm" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/chems/glass/bowl/mapped/mushroom, @@ -1864,7 +1864,7 @@ /turf/unsimulated/floor/lino, /area/tdome/tdomeadmin) "bMb" = ( -/obj/structure/table/woodentable{ +/obj/structure/table/laminatetable{ dir = 5 }, /obj/item/food/meatsteak, diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index c715e45dc03c..b427b70ebd3d 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -31,7 +31,7 @@ /turf/wall/natural/random/ministation, /area/space) "ai" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/pool, /area/ministation/dorms) "ak" = ( @@ -1727,7 +1727,7 @@ /turf/floor/reinforced/airmix, /area/ministation/atmospherics) "gF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/light{ dir = 4 }, @@ -3756,7 +3756,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/disposalpipe/segment{ dir = 4 }, @@ -7267,7 +7267,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/structure/table/woodentable_reinforced/walnut/maple, +/obj/structure/table/laminatetable_reinforced/walnut/maple, /turf/floor/wood, /area/ministation/engine) "EN" = ( @@ -7277,7 +7277,7 @@ /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 1 }, -/obj/structure/table/woodentable_reinforced/walnut/maple, +/obj/structure/table/laminatetable_reinforced/walnut/maple, /obj/item/chems/chem_disp_cartridge/coffee{ name = "coffee canister" }, @@ -7289,7 +7289,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/structure/table/woodentable_reinforced/walnut/maple, +/obj/structure/table/laminatetable_reinforced/walnut/maple, /turf/floor/wood, /area/ministation/engine) "EP" = ( @@ -7317,7 +7317,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = 1; pixel_y = 9 @@ -7405,7 +7405,7 @@ "Fc" = ( /obj/item/wrench, /obj/item/clothing/gloves/insulated, -/obj/structure/table/woodentable_reinforced/walnut/maple, +/obj/structure/table/laminatetable_reinforced/walnut/maple, /turf/floor/wood, /area/ministation/engine) "Fd" = ( @@ -7414,7 +7414,7 @@ }, /obj/item/stack/tape_roll/barricade_tape/atmos, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/table/woodentable_reinforced/walnut/maple, +/obj/structure/table/laminatetable_reinforced/walnut/maple, /obj/item/chems/spray/cleaner, /turf/floor/wood, /area/ministation/engine) @@ -7424,7 +7424,7 @@ }, /obj/item/chems/drinks/glass2/coffeecup/metal, /obj/item/chems/drinks/glass2/coffeecup/metal, -/obj/structure/table/woodentable_reinforced/walnut/maple, +/obj/structure/table/laminatetable_reinforced/walnut/maple, /obj/item/chems/drinks/glass2/coffeecup/tall, /turf/floor/wood, /area/ministation/engine) @@ -11756,7 +11756,7 @@ /turf/floor/tiled, /area/ministation/cargo) "Ro" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/clothing/mask/snorkel, /obj/item/clothing/mask/snorkel, /obj/machinery/light{ @@ -12544,7 +12544,7 @@ /turf/space, /area/space) "Ua" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/clothing/mask/snorkel, /obj/item/clothing/mask/snorkel, /turf/floor/pool, @@ -13830,7 +13830,7 @@ dir = 4 }, /obj/machinery/camera/autoname, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/poster, /obj/item/pen, /turf/floor/wood/walnut, diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index ce6e50124ac1..61ac63b60d6b 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -474,7 +474,7 @@ /turf/floor/plating, /area/ministation/hop) "cA" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green, /obj/machinery/firealarm{ dir = 4; @@ -1218,7 +1218,7 @@ /turf/open, /area/ministation/hall/e2) "gB" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/item/pen/blue, /turf/floor/carpet/red, /area/ministation/security) @@ -1500,7 +1500,7 @@ /turf/floor/tiled/white, /area/ministation/medical) "hC" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/unary/vent_pump/on, /obj/item/scanner/spectrometer, /obj/item/handcuffs, @@ -1633,7 +1633,7 @@ /turf/floor/tiled, /area/ministation/security) "id" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = -3; pixel_y = 7 @@ -3020,7 +3020,7 @@ /turf/floor/tiled/white, /area/ministation/medical) "pf" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/unary/vent_scrubber/on, /turf/floor/tiled, /area/ministation/hall/w2) @@ -3069,7 +3069,7 @@ /turf/floor/plating, /area/ministation/maint/l2centraln) "px" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/glass/beaker, /turf/floor/lino, /area/ministation/cafe) @@ -3081,7 +3081,7 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/structure/window/reinforced{ dir = 4 }, @@ -3269,7 +3269,7 @@ /turf/floor/tiled/white, /area/ministation/detective) "qe" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/tiled, /area/ministation/hall/w2) "qf" = ( @@ -3644,7 +3644,7 @@ /turf/open, /area/ministation/hall/w2) "rA" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, @@ -4864,7 +4864,7 @@ /turf/floor/plating, /area/ministation/medical) "vq" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/book/manual/detective, /turf/floor/carpet/red, /area/ministation/detective) @@ -5415,7 +5415,7 @@ /turf/floor/plating, /area/ministation/maint/l2centrals) "xn" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/item/paper_bin, /turf/floor/carpet/red, /area/ministation/security) @@ -6062,7 +6062,7 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/structure/window/reinforced{ dir = 8 }, @@ -6150,7 +6150,7 @@ pixel_x = -3; pixel_y = 6 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/button/blast_door{ id_tag = "Kitchen1"; name = "Kitchen Shutter"; @@ -6523,7 +6523,7 @@ /turf/floor/plating, /area/ministation/hall/w2) "Bk" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin{ pixel_x = 1; pixel_y = 9 @@ -6533,7 +6533,7 @@ /turf/floor/carpet, /area/ministation/hall/w2) "Bm" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/lino, /area/ministation/cafe) "Br" = ( @@ -6708,7 +6708,7 @@ pixel_y = -24; dir = 1 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/reagent_temperature, /turf/floor/lino, /area/ministation/cafe) @@ -6873,7 +6873,7 @@ /turf/floor/tiled/dark, /area/ministation/security) "Dj" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/condiment/enzyme, /obj/item/mollusc/clam, /obj/item/mollusc/clam, @@ -7508,7 +7508,7 @@ /turf/floor/tiled, /area/ministation/hydro) "Hx" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/reagent_temperature/cooler, /turf/floor/lino, /area/ministation/cafe) @@ -7639,7 +7639,7 @@ /turf/floor/tiled/dark, /area/ministation/security) "Ia" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/machinery/faxmachine/mapped, /turf/floor/carpet/red, /area/ministation/security) @@ -7832,7 +7832,7 @@ /turf/floor/fake_grass, /area/ministation/hydro) "Jj" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/camera/autoname{ dir = 8 }, @@ -7879,7 +7879,7 @@ /turf/floor/tiled/dark, /area/ministation/security) "Jo" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/cigarettes, /obj/item/pen, /obj/structure/cable{ @@ -8195,7 +8195,7 @@ /turf/floor/tiled, /area/ministation/security) "Lk" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/item/pen/multi, /turf/floor/carpet/red, /area/ministation/security) @@ -8214,7 +8214,7 @@ /turf/floor/tiled, /area/ministation/security) "Lr" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/reagentgrinder/juicer, /obj/item/chems/glass/beaker, /turf/floor/lino, @@ -8269,7 +8269,7 @@ /turf/floor/carpet/red, /area/ministation/security) "LM" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/knife/kitchen/cleaver, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 @@ -8903,7 +8903,7 @@ /turf/floor/plating, /area/ministation/medical) "Pd" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/cigarettes{ pixel_y = 2 }, @@ -9215,7 +9215,7 @@ /turf/floor/tiled, /area/ministation/hall/e2) "QH" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/item/pen, /turf/floor/carpet/red, /area/ministation/security) @@ -9755,7 +9755,7 @@ /turf/floor/tiled, /area/ministation/security) "TX" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/eftpos, /obj/item/chems/spray/cleaner, /obj/item/chems/packet/honey, @@ -9816,7 +9816,7 @@ /turf/floor/plating, /area/ministation/maint/secmaint) "Ur" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /turf/floor/carpet/red, /area/ministation/security) "Ut" = ( @@ -10132,7 +10132,7 @@ /turf/floor/plating, /area/ministation/medical) "Wa" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp/green{ pixel_x = 1; pixel_y = 5 @@ -10451,7 +10451,7 @@ /turf/floor/tiled, /area/ministation/hall/w2) "XW" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/ammo/beanbags, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index 12a062972d29..07e596175cf5 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -350,7 +350,7 @@ /area/ministation/bridge) "bt" = ( /obj/item/flashlight/lamp/green, -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 @@ -497,7 +497,7 @@ /turf/floor/wood/walnut, /area/ministation/bridge) "bI" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/item/modular_computer/tablet/lease/preset/command, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 9 @@ -900,7 +900,7 @@ /turf/floor/tiled, /area/ministation/bridge) "dn" = ( -/obj/structure/table/woodentable_reinforced/walnut, +/obj/structure/table/laminatetable_reinforced/walnut, /obj/item/paper_bin, /obj/item/pen/retractable, /turf/floor/carpet/red, @@ -3295,7 +3295,7 @@ /turf/floor, /area/ministation/science) "ps" = ( -/obj/structure/table/woodentable/ebony, +/obj/structure/table/laminatetable/ebony, /obj/item/paper_bundle, /obj/effect/floor_decal/stoneborder{ dir = 6 @@ -4129,7 +4129,7 @@ /turf/floor/lino, /area/ministation/hall/s3) "wY" = ( -/obj/structure/table/woodentable/ebony, +/obj/structure/table/laminatetable/ebony, /obj/item/pen/fancy, /obj/item/box/fancy/crayons, /obj/effect/floor_decal/stoneborder{ @@ -5275,7 +5275,7 @@ /turf/floor/plating, /area/ministation/maint/l3central) "DD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/stack/material/panel/mapped/plastic/ten, /obj/item/stack/material/plank/mapped/wood/ten, /obj/item/stack/material/plank/mapped/wood/ten, @@ -5295,7 +5295,7 @@ /turf/floor/tiled/white, /area/ministation/science) "DQ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pill_bottle/dice, /turf/floor/carpet/green, /area/ministation/library) @@ -5335,7 +5335,7 @@ /turf/floor/plating, /area/ministation/maint/l3nw) "Ef" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/carpet/green, /area/ministation/library) "Eg" = ( @@ -5548,7 +5548,7 @@ /turf/floor/tiled/white, /area/ministation/science) "FV" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/mollusc/clam, /obj/machinery/light/small, /turf/floor/wood/mahogany, @@ -5666,7 +5666,7 @@ /turf/floor/tiled, /area/ministation/hall/s3) "Hl" = ( -/obj/structure/table/woodentable/ebony, +/obj/structure/table/laminatetable/ebony, /obj/item/paper_bin, /obj/effect/floor_decal/stoneborder{ dir = 6 @@ -6064,7 +6064,7 @@ /turf/floor/carpet/green, /area/ministation/library) "Le" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/drinks/cans/waterbottle, /turf/floor/carpet/green, /area/ministation/library) @@ -6264,7 +6264,7 @@ dir = 8; pixel_x = 22 }, -/obj/structure/table/woodentable_reinforced/walnut, +/obj/structure/table/laminatetable_reinforced/walnut, /obj/item/folder, /turf/floor/carpet/red, /area/ministation/court) @@ -6597,7 +6597,7 @@ /turf/floor/tiled, /area/ministation/hall/s3) "Ov" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable/walnut, /obj/machinery/light, /turf/floor/wood/yew, /area/ministation/court) @@ -6731,7 +6731,7 @@ /turf/floor/tiled, /area/ministation/hall/s3) "Pk" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable/walnut, /obj/item/folder/blue, /obj/machinery/atmospherics/unary/vent_pump/on, /turf/floor/wood/yew, @@ -6763,7 +6763,7 @@ /turf/floor/tiled, /area/ministation/hall/n3) "Px" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable/walnut, /obj/item/folder/red, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 @@ -6843,7 +6843,7 @@ /turf/floor/tiled, /area/ministation/hall/n3) "PT" = ( -/obj/structure/table/woodentable_reinforced/walnut, +/obj/structure/table/laminatetable_reinforced/walnut, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -6974,7 +6974,7 @@ /turf/floor/tiled, /area/ministation/shuttle/outgoing) "QD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/book/printable_red, /obj/item/pen, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -7023,7 +7023,7 @@ /obj/machinery/firealarm{ pixel_y = 24 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood/mahogany, /area/ministation/library) "Re" = ( @@ -7271,7 +7271,7 @@ /turf/floor/tiled, /area/ministation/tradehouse_rep) "Sf" = ( -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable/walnut, /obj/machinery/light{ dir = 1 }, @@ -7375,7 +7375,7 @@ /turf/floor/plating, /area/ministation/maint/l3ne) "Tk" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood/mahogany, /area/ministation/library) "Tm" = ( @@ -7424,7 +7424,7 @@ /obj/structure/railing/mapped{ dir = 8 }, -/obj/structure/table/woodentable/ebony, +/obj/structure/table/laminatetable/ebony, /obj/item/pen, /obj/item/pen/blue, /obj/item/pen/retractable, @@ -7502,7 +7502,7 @@ /turf/floor/tiled, /area/ministation/bridge) "TI" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/cash, /obj/item/chems/drinks/cans/waterbottle, /turf/floor/carpet/green, @@ -7735,7 +7735,7 @@ /obj/structure/cable{ icon_state = "2-8" }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/knife/kitchen, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -7788,7 +7788,7 @@ /turf/floor/carpet/red, /area/ministation/court) "Vw" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/deck/cards, /turf/floor/carpet/green, /area/ministation/library) @@ -7865,7 +7865,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/fabricator/book, /turf/floor/wood/mahogany, /area/ministation/library) @@ -8584,7 +8584,7 @@ /turf/floor/wood/mahogany, /area/ministation/library) "ZZ" = ( -/obj/structure/table/woodentable_reinforced/walnut, +/obj/structure/table/laminatetable_reinforced/walnut, /obj/item/bell, /turf/floor/carpet/red, /area/ministation/court) diff --git a/maps/tradeship/tradeship-0.dmm b/maps/tradeship/tradeship-0.dmm index 4ff16d85d3a4..96d7b8d05c55 100644 --- a/maps/tradeship/tradeship-0.dmm +++ b/maps/tradeship/tradeship-0.dmm @@ -1074,7 +1074,7 @@ /area/ship/trade/disused) "hc" = ( /obj/item/radio, -/obj/structure/table/woodentable/walnut, +/obj/structure/table/laminatetable/walnut, /turf/floor/wood/walnut, /area/ship/trade/disused) "hf" = ( @@ -2017,7 +2017,7 @@ /turf/floor, /area/ship/trade/aft_starboard_underside_maint) "Qh" = ( -/obj/structure/table/woodentable/mahogany, +/obj/structure/table/laminatetable/mahogany, /obj/item/cell/crap/empty, /turf/floor/wood/walnut, /area/ship/trade/disused) diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 5861bc931e5e..bba2a2d9e6cd 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -58,7 +58,7 @@ "af" = ( /obj/item/paper_bin, /obj/item/pen, -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/light_switch{ pixel_y = 25 }, @@ -7363,7 +7363,7 @@ /turf/floor/tiled/techmaint, /area/ship/trade/maintenance/hallway) "Rb" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/structure/cable{ icon_state = "0-8" }, diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm index cc042aa3f948..c67143981165 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm @@ -854,26 +854,26 @@ /turf/floor/plating, /area/lar_maria/atmos) "cD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood, /area/lar_maria/dorms) "cE" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/smokes, /turf/floor/wood, /area/lar_maria/dorms) "cF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/toy, /turf/floor/wood, /area/lar_maria/dorms) "cG" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/glowsticks, /turf/floor/wood, /area/lar_maria/dorms) "cH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/smokes, /obj/random/drinkbottle, /turf/floor/wood, @@ -1482,7 +1482,7 @@ /turf/floor/tiled, /area/lar_maria/office) "eC" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper_bin, /obj/item/pen/blue, /turf/floor/tiled, @@ -1498,18 +1498,18 @@ /turf/floor/tiled, /area/lar_maria/office) "eF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/handcuffs, /turf/floor/wood, /area/lar_maria/dorms) "eG" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/smokes, /obj/random/snack, /turf/floor/wood, /area/lar_maria/dorms) "eH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/beakers, /turf/floor/wood, /area/lar_maria/dorms) @@ -1588,7 +1588,7 @@ /turf/floor/tiled, /area/lar_maria/office) "eU" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/tiled, /area/lar_maria/office) "eV" = ( @@ -1812,29 +1812,29 @@ /turf/floor/tiled, /area/lar_maria/office) "fF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/cane/aluminium, /turf/floor/wood, /area/lar_maria/dorms) "fG" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/clothing/head/soft/zhp_cap, /turf/floor/wood, /area/lar_maria/dorms) "fH" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/drinkbottle, /obj/random/contraband, /turf/floor/wood, /area/lar_maria/dorms) "fI" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/toolbox, /obj/item/scalpel, /turf/floor/wood, /area/lar_maria/dorms) "fJ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/snack, /turf/floor/wood, /area/lar_maria/dorms) diff --git a/mods/content/government/away_sites/icarus/icarus-1.dmm b/mods/content/government/away_sites/icarus/icarus-1.dmm index b04503e93339..de66dd98384c 100644 --- a/mods/content/government/away_sites/icarus/icarus-1.dmm +++ b/mods/content/government/away_sites/icarus/icarus-1.dmm @@ -68,13 +68,13 @@ /turf/floor/wood, /area/icarus/vessel) "au" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flame/fuelled/lighter/zippo/random, /obj/random/smokes, /turf/floor/wood, /area/icarus/vessel) "av" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/wood, /area/icarus/vessel) "aw" = ( @@ -90,7 +90,7 @@ /turf/floor/wood, /area/icarus/vessel) "az" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/smokes, /turf/floor/wood, /area/icarus/vessel) @@ -105,17 +105,17 @@ /turf/floor/wood, /area/icarus/vessel) "aD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/lunchbox/mars, /turf/floor/wood, /area/icarus/vessel) "aE" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/photo_album, /turf/floor/wood, /area/icarus/vessel) "aF" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/drinkbottle, /obj/random/drinkbottle, /obj/random/snack, @@ -153,7 +153,7 @@ /turf/floor/wood, /area/icarus/vessel) "aL" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/fancy/cigar, /turf/floor/wood, /area/icarus/vessel) diff --git a/mods/content/government/away_sites/icarus/icarus-2.dmm b/mods/content/government/away_sites/icarus/icarus-2.dmm index e32afe37e27f..b17f4dd06a20 100644 --- a/mods/content/government/away_sites/icarus/icarus-2.dmm +++ b/mods/content/government/away_sites/icarus/icarus-2.dmm @@ -484,12 +484,12 @@ /turf/floor/tiled, /area/icarus/vessel) "bI" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/loot, /turf/floor/tiled, /area/icarus/vessel) "bJ" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /turf/floor/tiled, /area/icarus/vessel) "bK" = ( @@ -647,7 +647,7 @@ /turf/floor/tiled, /area/icarus/vessel) "cl" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/backpack/captain, /obj/machinery/alarm{ alarm_id = "xenobio3_alarm"; @@ -657,7 +657,7 @@ /turf/floor/tiled, /area/icarus/vessel) "cm" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/paper/icarus/crew_roster, /obj/item/folder/blue, /turf/floor/tiled, @@ -751,7 +751,7 @@ /turf/floor/tiled, /area/icarus/vessel) "cA" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/drinks/bottle/whiskey, /turf/floor/tiled, /area/icarus/vessel) @@ -768,12 +768,12 @@ /turf/floor/tiled, /area/icarus/vessel) "cD" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/toy/ship_model, /turf/floor/tiled, /area/icarus/vessel) "cE" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/action_figure, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 @@ -903,7 +903,7 @@ /turf/floor/tiled, /area/icarus/vessel) "cW" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/disk/secret_project/science, /turf/floor/tiled, /area/icarus/vessel) @@ -1075,7 +1075,7 @@ /turf/floor/tiled, /area/icarus/vessel) "du" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/flashlight/lamp, /turf/floor/tiled, /area/icarus/vessel) diff --git a/mods/gamemodes/heist/heist_base.dmm b/mods/gamemodes/heist/heist_base.dmm index cd92f5dc9096..812287b73ee6 100644 --- a/mods/gamemodes/heist/heist_base.dmm +++ b/mods/gamemodes/heist/heist_base.dmm @@ -371,7 +371,7 @@ /turf/unsimulated/floor/freezer, /area/map_template/syndicate_mothership/raider_base) "bo" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/effect/floor_decal/carpet{ dir = 8 }, @@ -380,7 +380,7 @@ /turf/unsimulated/floor/carpet, /area/map_template/syndicate_mothership/raider_base) "bp" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/random/action_figure, /obj/random/contraband, /obj/random/junk, @@ -511,7 +511,7 @@ /turf/unsimulated/floor/wood/broken, /area/map_template/syndicate_mothership/raider_base) "bL" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/plate/tray{ pixel_y = 5 }, @@ -519,12 +519,12 @@ /turf/unsimulated/floor/wood, /area/map_template/syndicate_mothership/raider_base) "bM" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/box/glasses/rocks, /turf/unsimulated/floor/wood, /area/map_template/syndicate_mothership/raider_base) "bN" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/machinery/chemical_dispenser/bar_soft/full, /turf/unsimulated/floor/wood/broken6, /area/map_template/syndicate_mothership/raider_base) @@ -592,18 +592,18 @@ /turf/unsimulated/floor/dark, /area/map_template/syndicate_mothership/raider_base) "ca" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/pizzabox/meat, /turf/unsimulated/floor/wood, /area/map_template/syndicate_mothership/raider_base) "cb" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/ashtray, /obj/item/trash/cigbutt/cigarbutt, /turf/unsimulated/floor/wood, /area/map_template/syndicate_mothership/raider_base) "cc" = ( -/obj/structure/table/woodentable, +/obj/structure/table/laminatetable, /obj/item/chems/glass/rag, /obj/random/loot, /turf/unsimulated/floor/wood, From 7242145b9b530e39fb8f3ff612a76f6cbefaf426 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 24 Nov 2024 13:52:17 +1100 Subject: [PATCH 0026/1331] Adding wood laminate flooring for space maps. --- .../objects/items/stacks/tiles/tile_types.dm | 147 ++++-- code/game/turfs/flooring/flooring_wood.dm | 50 ++ code/game/turfs/floors/subtypes/floor_wood.dm | 54 ++ code/game/turfs/unsimulated/floor.dm | 17 + .../crafting/stack_recipes/recipes_stacks.dm | 23 + .../designs/general/designs_general.dm | 9 + .../solids/materials_solid_wood.dm | 7 + .../materials/material_sheets_mapping.dm | 1 + .../reagents/reactions/reaction_synthesis.dm | 8 + icons/turf/flooring/laminate.dmi | Bin 0 -> 2093 bytes maps/antag_spawn/wizard/wizard_base.dmm | 50 +- maps/away/bearcat/bearcat-2.dmm | 20 +- maps/away/casino/casino.dmm | 98 ++-- maps/away/errant_pisces/errant_pisces.dmm | 80 +-- maps/away/liberia/liberia.dmm | 128 ++--- maps/away/mining/mining-signal.dmm | 26 +- maps/away/unishi/unishi-3.dmm | 72 +-- maps/away/yacht/yacht.dmm | 134 ++--- maps/exodus/exodus-1.dmm | 6 +- maps/exodus/exodus-2.dmm | 490 +++++++++--------- maps/exodus/exodus-admin.dmm | 40 +- maps/ministation/ministation-0.dmm | 222 ++++---- maps/ministation/ministation-1.dmm | 14 +- maps/ministation/ministation-2.dmm | 168 +++--- .../exoplanet_ruins/playablecolony/colony.dmm | 102 ++-- maps/tradeship/tradeship-0.dmm | 20 +- maps/tradeship/tradeship-2.dmm | 44 +- .../away_sites/lar_maria/lar_maria-2.dmm | 122 ++--- .../government/away_sites/icarus/icarus-1.dmm | 48 +- mods/gamemodes/heist/heist_base.dmm | 28 +- 30 files changed, 1223 insertions(+), 1005 deletions(-) create mode 100644 icons/turf/flooring/laminate.dmi diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index a53c61d5bad1..6fed8b04b2b0 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -70,70 +70,119 @@ * Wood */ /obj/item/stack/tile/wood - name = "wood floor tile" - singular_name = "wood floor tile" - desc = "An easy to fit wooden floor tile." - icon_state = "tile-wood" - color = WOOD_COLOR_GENERIC - material = /decl/material/solid/organic/wood + name = "wood floor tile" + singular_name = "wood floor tile" + desc = "An easy-to-fit wooden floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood::color + material = /decl/material/solid/organic/wood /obj/item/stack/tile/wood/cyborg - name = "wood floor tile synthesizer" - desc = "A device that makes wood floor tiles." - uses_charge = 1 - charge_costs = list(250) - stack_merge_type = /obj/item/stack/tile/wood - build_type = /obj/item/stack/tile/wood - max_health = ITEM_HEALTH_NO_DAMAGE + name = "wood floor tile synthesizer" + desc = "A device that makes wood floor tiles." + uses_charge = 1 + charge_costs = list(250) + stack_merge_type = /obj/item/stack/tile/pine_laminate + build_type = /obj/item/stack/tile/pine_laminate + material = /decl/material/solid/organic/wood/chipboard + max_health = ITEM_HEALTH_NO_DAMAGE is_spawnable_type = FALSE /obj/item/stack/tile/mahogany - name = "mahogany floor tile" - singular_name = "mahogany floor tile" - desc = "An easy to fit mahogany wood floor tile." - icon_state = "tile-wood" - color = WOOD_COLOR_RICH - material = /decl/material/solid/organic/wood + name = "mahogany floor tile" + singular_name = "mahogany floor tile" + desc = "An easy-to-fit mahogany wood floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/mahogany::color + material = /decl/material/solid/organic/wood/mahogany /obj/item/stack/tile/maple - name = "maple floor tile" - singular_name = "maple floor tile" - desc = "An easy to fit maple wood floor tile." - icon_state = "tile-wood" - color = WOOD_COLOR_PALE - material = /decl/material/solid/organic/wood + name = "maple floor tile" + singular_name = "maple floor tile" + desc = "An easy-to-fit maple wood floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/maple::color + material = /decl/material/solid/organic/wood/maple /obj/item/stack/tile/ebony - name = "ebony floor tile" - singular_name = "ebony floor tile" - desc = "An easy to fit ebony floor tile." - icon_state = "tile-wood" - color = WOOD_COLOR_BLACK - material = /decl/material/solid/organic/wood + name = "ebony floor tile" + singular_name = "ebony floor tile" + desc = "An easy-to-fit ebony floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/ebony::color + material = /decl/material/solid/organic/wood/ebony /obj/item/stack/tile/walnut - name = "walnut floor tile" - singular_name = "walnut floor tile" - desc = "An easy to fit walnut wood floor tile." - icon_state = "tile-wood" - color = WOOD_COLOR_CHOCOLATE - material = /decl/material/solid/organic/wood + name = "walnut floor tile" + singular_name = "walnut floor tile" + desc = "An easy-to-fit walnut wood floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/walnut::color + material = /decl/material/solid/organic/wood/walnut /obj/item/stack/tile/bamboo - name = "bamboo floor tile" - singular_name = "bamboo floor tile" - desc = "An easy to fit bamboo wood floor tile." - icon_state = "tile-wood" - color = WOOD_COLOR_PALE2 - material = /decl/material/solid/organic/wood + name = "bamboo floor tile" + singular_name = "bamboo floor tile" + desc = "An easy-to-fit bamboo wood floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/bamboo::color + material = /decl/material/solid/organic/wood/bamboo /obj/item/stack/tile/yew - name = "yew floor tile" - singular_name = "yew floor tile" - desc = "An easy to fit yew wood floor tile." - icon_state = "tile-wood" - color = WOOD_COLOR_YELLOW - material = /decl/material/solid/organic/wood + name = "yew floor tile" + singular_name = "yew floor tile" + desc = "An easy-to-fit yew wood floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/yew::color + material = /decl/material/solid/organic/wood/yew + +/obj/item/stack/tile/pine_laminate + name = "pine laminate floor tile" + singular_name = "pine laminate floor tile" + desc = "An easy-to-fit pine laminate floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/chipboard::color + material = /decl/material/solid/organic/wood/chipboard + +/obj/item/stack/tile/mahogany_laminate + name = "mahogany laminate floor tile" + singular_name = "mahogany laminate floor tile" + desc = "An easy-to-fit mahogany laminate floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/chipboard/mahogany::color + material = /decl/material/solid/organic/wood/chipboard/mahogany + +/obj/item/stack/tile/maple_laminate + name = "maple laminate floor tile" + singular_name = "maple laminate floor tile" + desc = "An easy-to-fit maple laminate floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/chipboard/maple::color + material = /decl/material/solid/organic/wood/chipboard/maple + +/obj/item/stack/tile/ebony_laminate + name = "ebony laminate floor tile" + singular_name = "ebony laminate floor tile" + desc = "An easy-to-fit ebony laminate floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/chipboard/ebony::color + material = /decl/material/solid/organic/wood/chipboard/ebony + +/obj/item/stack/tile/walnut_laminate + name = "walnut laminate floor tile" + singular_name = "walnut laminate floor tile" + desc = "An easy-to-fit walnut laminate floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/chipboard/walnut::color + material = /decl/material/solid/organic/wood/chipboard/walnut + +/obj/item/stack/tile/yew_laminate + name = "yew laminate floor tile" + singular_name = "yew laminate floor tile" + desc = "An easy-to-fit yew laminate floor tile." + icon_state = "tile-wood" + color = /decl/material/solid/organic/wood/chipboard/yew::color + material = /decl/material/solid/organic/wood/chipboard/yew /obj/item/stack/tile/floor name = "steel floor tile" diff --git a/code/game/turfs/flooring/flooring_wood.dm b/code/game/turfs/flooring/flooring_wood.dm index a9c136bfcb57..fa503f0f5a84 100644 --- a/code/game/turfs/flooring/flooring_wood.dm +++ b/code/game/turfs/flooring/flooring_wood.dm @@ -52,3 +52,53 @@ color = /decl/material/solid/organic/wood/yew::color build_type = /obj/item/stack/tile/yew force_material = /decl/material/solid/organic/wood/yew + +// Chipboard/wood laminate floors. Uses older icons. +/decl/flooring/laminate + name = "wooden laminate floor" + desc = "A stretch of closely-fitted sections of chipboard with a laminated veneer." + icon = 'icons/turf/flooring/laminate.dmi' + icon_base = "wood" + damage_temperature = T0C+200 + descriptor = "sections" + build_type = /obj/item/stack/tile/pine_laminate + flooring_flags = TURF_IS_FRAGILE | TURF_REMOVE_SCREWDRIVER + footstep_type = /decl/footsteps/wood + color = /decl/material/solid/organic/wood/chipboard::color + force_material = /decl/material/solid/organic/wood/chipboard + constructed = TRUE + gender = NEUTER + broken_states = list( + "broken0", + "broken1", + "broken2", + "broken3", + "broken4", + "broken5", + "broken6" + ) + +/decl/flooring/laminate/mahogany + color = /decl/material/solid/organic/wood/chipboard/mahogany::color + build_type = /obj/item/stack/tile/mahogany_laminate + force_material = /decl/material/solid/organic/wood/chipboard/mahogany + +/decl/flooring/laminate/maple + color = /decl/material/solid/organic/wood/chipboard/maple::color + build_type = /obj/item/stack/tile/maple_laminate + force_material = /decl/material/solid/organic/wood/chipboard/maple + +/decl/flooring/laminate/ebony + color = /decl/material/solid/organic/wood/chipboard/ebony::color + build_type = /obj/item/stack/tile/ebony_laminate + force_material = /decl/material/solid/organic/wood/chipboard/ebony + +/decl/flooring/laminate/walnut + color = /decl/material/solid/organic/wood/chipboard/walnut::color + build_type = /obj/item/stack/tile/walnut_laminate + force_material = /decl/material/solid/organic/wood/chipboard/yew + +/decl/flooring/laminate/yew + color = /decl/material/solid/organic/wood/chipboard/yew::color + build_type = /obj/item/stack/tile/yew_laminate + force_material = /decl/material/solid/organic/wood/chipboard/yew diff --git a/code/game/turfs/floors/subtypes/floor_wood.dm b/code/game/turfs/floors/subtypes/floor_wood.dm index 9ab163825a6f..4b1069772dbd 100644 --- a/code/game/turfs/floors/subtypes/floor_wood.dm +++ b/code/game/turfs/floors/subtypes/floor_wood.dm @@ -54,3 +54,57 @@ /turf/floor/wood/yew color = /decl/material/solid/organic/wood/yew::color _flooring = /decl/flooring/wood/yew + +// Laminate floor; basically identical to wood, but uses older smoother icons. +/turf/floor/laminate + name = "wooden laminate floor" + icon = 'icons/turf/flooring/laminate.dmi' + icon_state = "wood0" + color = /decl/material/solid/organic/wood/chipboard::color + _flooring = /decl/flooring/laminate + +/turf/floor/laminate/broken + icon_state = "wood_broken0" + _floor_broken = TRUE + +/turf/floor/laminate/broken/Initialize() + . = ..() + var/setting_broken = _floor_broken + _floor_broken = null + set_floor_broken(setting_broken) + +/turf/floor/laminate/broken/one + icon_state = "wood_broken1" + _floor_broken = "broken1" + +/turf/floor/laminate/broken/two + icon_state = "wood_broken2" + _floor_broken = "broken2" + +/turf/floor/laminate/broken/three + icon_state = "wood_broken3" + _floor_broken = "broken3" + +/turf/floor/laminate/broken/four + icon_state = "wood_broken4" + _floor_broken = "broken4" + +/turf/floor/laminate/mahogany + color = /decl/material/solid/organic/wood/chipboard/mahogany::color + _flooring = /decl/flooring/laminate/mahogany + +/turf/floor/laminate/maple + color = /decl/material/solid/organic/wood/chipboard/maple::color + _flooring = /decl/flooring/laminate/maple + +/turf/floor/laminate/ebony + color = /decl/material/solid/organic/wood/chipboard/ebony::color + _flooring = /decl/flooring/laminate/ebony + +/turf/floor/laminate/walnut + color = /decl/material/solid/organic/wood/chipboard/walnut::color + _flooring = /decl/flooring/laminate/walnut + +/turf/floor/laminate/yew + color = /decl/material/solid/organic/wood/chipboard/yew::color + _flooring = /decl/flooring/laminate/yew diff --git a/code/game/turfs/unsimulated/floor.dm b/code/game/turfs/unsimulated/floor.dm index 449745e5e122..3ec6296308df 100644 --- a/code/game/turfs/unsimulated/floor.dm +++ b/code/game/turfs/unsimulated/floor.dm @@ -52,6 +52,23 @@ /turf/unsimulated/floor/wood/broken6 icon_state = "wood_broken6" +/turf/unsimulated/floor/wood + name = "wooden laminate floor" + icon = 'icons/turf/flooring/laminate.dmi' + icon_state = "wood0" + +/turf/unsimulated/floor/wood/broken + icon_state = "wood_broken0" + +/turf/unsimulated/floor/wood/broken1 + icon_state = "wood_broken1" + +/turf/unsimulated/floor/wood/broken2 + icon_state = "wood_broken2" + +/turf/unsimulated/floor/wood/broken6 + icon_state = "wood_broken6" + /turf/unsimulated/floor/vault icon_state = "vault" diff --git a/code/modules/crafting/stack_recipes/recipes_stacks.dm b/code/modules/crafting/stack_recipes/recipes_stacks.dm index 0e97225f58de..2a929e693f93 100644 --- a/code/modules/crafting/stack_recipes/recipes_stacks.dm +++ b/code/modules/crafting/stack_recipes/recipes_stacks.dm @@ -26,6 +26,29 @@ result_type = /obj/item/stack/tile/walnut required_material = /decl/material/solid/organic/wood/walnut +/decl/stack_recipe/tile/wood/pine_laminate + result_type = /obj/item/stack/tile/pine_laminate + required_material = /decl/material/solid/organic/wood/chipboard + +/decl/stack_recipe/tile/wood/mahogany_laminate + result_type = /obj/item/stack/tile/mahogany_laminate + required_material = /decl/material/solid/organic/wood/chipboard/mahogany + +/decl/stack_recipe/tile/wood/maple_laminate + result_type = /obj/item/stack/tile/maple_laminate + required_material = /decl/material/solid/organic/wood/chipboard/maple + +/decl/stack_recipe/tile/wood/ebony_laminate + result_type = /obj/item/stack/tile/ebony_laminate + required_material = /decl/material/solid/organic/wood/chipboard/ebony + +/decl/stack_recipe/tile/wood/walnut_laminate + result_type = /obj/item/stack/tile/walnut + required_material = /decl/material/solid/organic/wood/chipboard/walnut + +/decl/stack_recipe/tile/wood/yew_laminate + result_type = /obj/item/stack/tile/yew_laminate + required_material = /decl/material/solid/organic/wood/chipboard/yew /decl/stack_recipe/tile/steel abstract_type = /decl/stack_recipe/tile/steel diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index ae37f50f4d94..e390a749c29b 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -246,3 +246,12 @@ /decl/material/solid/organic/wood/walnut = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) ) + +/datum/fabricator_recipe/chipboard/yew + path = /obj/item/stack/material/sheet/mapped/chipboard_yew + +/datum/fabricator_recipe/chipboard/yew/get_resources() + resources = list( + /decl/material/solid/organic/wood/yew = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), + /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) + ) diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index fd8f36268599..0285a6f6facf 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -176,3 +176,10 @@ adjective_name = "walnut laminate" uid = "solid_wood_chipboard_walnut" color = WOOD_COLOR_CHOCOLATE + +/decl/material/solid/organic/wood/chipboard/yew + name = "yew chipboard" + lore_text = "Also known as particle board, this material is made from various kinds of yew wood chips and resin, with a plastic laminate." + adjective_name = "yew laminate" + uid = "solid_wood_chipboard_yew" + color = WOOD_COLOR_YELLOW \ No newline at end of file diff --git a/code/modules/materials/material_sheets_mapping.dm b/code/modules/materials/material_sheets_mapping.dm index 33dfdcb211c3..1c2d2147e084 100644 --- a/code/modules/materials/material_sheets_mapping.dm +++ b/code/modules/materials/material_sheets_mapping.dm @@ -122,5 +122,6 @@ STACK_SUBTYPES(chipboard_maple, "maple chipboard", solid/organic/wood/chip STACK_SUBTYPES(chipboard_mahogany, "mahogany chipboard", solid/organic/wood/chipboard/mahogany, sheet, null) STACK_SUBTYPES(chipboard_ebony, "ebony chipboard", solid/organic/wood/chipboard/ebony, sheet, null) STACK_SUBTYPES(chipboard_walnut, "walnut chipboard", solid/organic/wood/chipboard/walnut, sheet, null) +STACK_SUBTYPES(chipboard_yew, "yew chipboard", solid/organic/wood/chipboard/yew, sheet, null) #undef STACK_SUBTYPES \ No newline at end of file diff --git a/code/modules/reagents/reactions/reaction_synthesis.dm b/code/modules/reagents/reactions/reaction_synthesis.dm index f30d0c4b049e..8a3ee19771d7 100644 --- a/code/modules/reagents/reactions/reaction_synthesis.dm +++ b/code/modules/reagents/reactions/reaction_synthesis.dm @@ -200,3 +200,11 @@ /decl/material/solid/organic/plastic = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2) ) chipboard_type = /decl/material/solid/organic/wood/chipboard/walnut + +/decl/chemical_reaction/synthesis/chipboard/yew + name = "Yew Chipboard" + required_reagents = list( + /decl/material/solid/organic/wood/yew = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2), + /decl/material/solid/organic/plastic = (REAGENT_UNITS_PER_MATERIAL_SHEET / 2) + ) + chipboard_type = /decl/material/solid/organic/wood/chipboard/yew diff --git a/icons/turf/flooring/laminate.dmi b/icons/turf/flooring/laminate.dmi new file mode 100644 index 0000000000000000000000000000000000000000..83d4933c155fe0b5b6ff17fe9b578128bc0a0356 GIT binary patch literal 2093 zcmV+|2-5e7P)C0000~P)t-sz`($J zdwYh4hKPuWVq#*iudk}As;#Z9TU%S7pPz(;gs`x%qobp;va+YAr!X)uBqJkXXL75n ztC^XZo12@Rot=k=hn$?8+1c6f@bG_sf3vf*R9L4#00001bW%=J06^y0W&i*Hta?;f zbVOxyV{&P5bZKvH004NLjncge!Y~lP@i}>l(C*SN-CU$aI@ni`YEBc12_(V3zQKar zB6r)}@tgi!sZPUroK$}q9m=?<)TS8g1H=|Tw8nGmL1AxsBD zn68HK-*lxKrvCfSANIr23O@Xj{{R36&q+if$bm6OFmpLjQwl)h?vq{(aJ4&B8iSv_!NDDT{B(r4&GrKd z3>5(YWm`K`6|HO+#)pt*jnl9rnC*xRJyZgqf>;2Gki-V_6xH(lSO6M;FN~|BGXw;yk|rWJUnV5d^KeNRU&%3JzLD zu3CEcXRQtaDA)mAG1&SSv;CQI0oM-z0744DnZ>fNF9%PIK1UMqoI8*%7&+U!MY$u0 z6cEVO8^}9FR~-SsOb{ZH)4xp%44{{Az&b$4Kq{ApQD%lD9QOj34RXQ?v~Smx0==)@ zkv=Ev@A|(O6E47n)c|}jf@?+DuoN+X`u5`(oKTo=0iY6LD?kjWUg-y}sB!0yD+?n;)c0?b6nyp=a5nSbOH4rPa{!fWQSFrn zK<-!ynN0%BxEvSd0|7z+AY}V6{~$TEFj@@D=!#yE517@vR&;hX14Fdk!v#a533<7X zCj`;$(AOQ*fJe*>DIQ(dwcXcOCxltM{p!qBwC&$*E13y^2z^)Z%Law`eu!YuC#OR^ zMEBJ_%)<=x#KP$wt+k7}wz6mVek0}RuSp3fn`oBDN#LRmm>2G{K4yh@*#p4Xz^qwoIj5W9iAq&lr2UuGc2j zBZnof^M!H26JGP1%)avp5ifY-wB+mLEIr&F0Nce$(c1$6g!)_NhCB=}i32Q=SeEjH zR3xw9DW7)`7I|6C5iH;^*?+;x?>_Ws0Dm!rod5&~J1kwQ5v?6X0EJ&0uAUbGl==tO z%8LL>{R3;|-+}|wKfw9}V!Mv?7nI}^On`Rl_XnhQ9_lYB$tM6HJNEknm|Y8Yp4xx# zHXMj^t(hAe)Rf4h`onntLF8~t{R4eDhKxiW)gMOt4`L0}h5ms^4qr2oNA-tU{Rc1S zfY$`+VVRew{(wY|uY0x(^dB_1I3}r?-D~YxYdz@oLw`Ub2QIj|u~n}BK$-PfW*7Fd zGd|tD&i50*`iZx2a`-6Ckqqz40XLlO*e^m(S|=MF_BqKK-52gzV1Zf))1l8Uof-(VAupT^$+y>1CHld9`_fN z2rFvVy03b2jdwmvZBeBWE&x{xjF15?iTQGdX;^?`Tb zz^d~-lN$}e%&2NceZbje>G__?UCysnh9`sdfwkxRAn)}DOs@~T1qY5U5wdY??Zz(5 z=|~vr18{!l$)M6B=l{A;ADAGw_Tlw`8qV(wfgU~o#{`%yk5h&HIk2s<`)PGrEMmo^ z2Isl+|B8A4V4?HxH|d0(HwykffrOFqmd>O8fZY4B{@~~Pb71q0K68F3o!_4=KX6;- z&hO8bzX=DzE{6U9JFx}Nq{`@Jch0Q`B=v{e+i+mo`M*)h$tdf~*9V3^Kk)YnsGlk4e>{!hFa#Q^ z4-9^O;O`UMOXc%F*>6x@`g~r#KL;*%-XAdke80LrVAT*H%s=0+l@kXXg!$+Dweo)f X&(}Q(BtDS500000NkvXXu0mjfY
    {{:helper.link(data.disk_name, null, {'eject_disk' : 1})}}
    DataStored
    {{:value.field}}{{:value.level}}
    DataStored
    {{:value.field}}{{:value.level}}
    {{:data.disk_name}}
    " show_browser(linked, jointext(html,null), "window=herald") -/datum/phenomena/create_gateway +/datum/phenomenon/create_gateway name = "Create Gateway" desc = "Creates a gateway from this world to the next. Gateways syphon absurd amounts of power but can be sacrificed to summon powerful minions." cost = 200 flags = PHENOMENA_NEAR_STRUCTURE expected_type = /atom -/datum/phenomena/create_gateway/can_activate(var/atom/a) +/datum/phenomenon/create_gateway/can_activate(var/atom/a) if(!..()) return 0 if(istype(a, /obj/structure/deity/gateway)) @@ -115,20 +115,20 @@ return 0 return 1 -/datum/phenomena/create_gateway/activate(var/atom/a) +/datum/phenomenon/create_gateway/activate(var/atom/a) ..() if(istype(a, /obj/structure/deity/gateway)) qdel(a) else new /obj/structure/deity/gateway(get_turf(a), linked) -/datum/phenomena/flickering_whisper +/datum/phenomenon/flickering_whisper name = "Flickering Whisper" desc = "Whisper to a non-believer, allowing you to intrude on their thoughts and see what they see." flags = PHENOMENA_NONFOLLOWER expected_type = /mob/living -/datum/phenomena/flickering_whisper/activate(var/mob/living/L) +/datum/phenomenon/flickering_whisper/activate(var/mob/living/L) var/atom/whisper_from for(var/obj/structure/deity/radiant_statue/rs in view(3, L)) whisper_from = rs @@ -142,12 +142,12 @@ events_repository.register(/decl/observ/destroyed, L, src, PROC_REF(deactivate_look)) addtimer(CALLBACK(src, PROC_REF(deactivate_look), L), 30 SECONDS) -/datum/phenomena/flickering_whisper/proc/deactivate_look(var/mob/viewer) +/datum/phenomenon/flickering_whisper/proc/deactivate_look(var/mob/viewer) if(!linked.is_follower(viewer)) //Don't remove if they are follower linked.eyenet.remove_source(viewer) events_repository.unregister(/decl/observ/destroyed, viewer, src) -/datum/phenomena/burning_glare +/datum/phenomenon/burning_glare name = "Burning Glare" desc = "Burn a victim. If they are burnt enough, you'll set them ablaze." cost = 100 @@ -155,7 +155,7 @@ cooldown = 30 SECONDS expected_type = /mob/living -/datum/phenomena/burning_glare/activate(var/mob/living/L) +/datum/phenomenon/burning_glare/activate(var/mob/living/L) ..() to_chat(L, SPAN_DANGER("You feel yourself burn!")) L.take_damage(10, BURN) @@ -163,7 +163,7 @@ L.fire_stacks += 50 L.IgniteMob() -/datum/phenomena/divine_right +/datum/phenomenon/divine_right name = "Divine Right" desc = "Trigger your rebirth into the body of someone wearing a herald's uniform. This takes time, requires 3 open gateways, and if the body is destroyed during the ritual... so are you. But once complete, you become an unstoppable demigod of unnatural power." cost = 300 @@ -171,7 +171,7 @@ flags = PHENOMENA_FOLLOWER|PHENOMENA_NEAR_STRUCTURE expected_type = /mob/living -/datum/phenomena/divine_right/can_activate(var/mob/living/L) +/datum/phenomenon/divine_right/can_activate(var/mob/living/L) if(!..()) return FALSE var/active_gateways = 0 @@ -190,7 +190,7 @@ to_chat(linked, SPAN_WARNING("\The [L] is not wearing a herald's uniform.")) return FALSE -/datum/phenomena/divine_right/activate(var/mob/living/L) +/datum/phenomenon/divine_right/activate(var/mob/living/L) ..() to_chat(L, SPAN_OCCULT("Your soul is ripped from your body as your master prepares to possess it.")) to_chat(linked, SPAN_OCCULT("You prepare the body for possession. Keep it safe. If it is totally destroyed, you will die.")) @@ -209,10 +209,10 @@ else to_chat(player, SPAN_NOTICE("Your Master is being reborn into the body of \the [L]. Protect it at all costs.")) -/datum/phenomena/divine_right/proc/fail_ritual(var/mob/living/L) +/datum/phenomenon/divine_right/proc/fail_ritual(var/mob/living/L) qdel(linked) -/datum/phenomena/divine_right/proc/succeed_ritual(var/mob/living/L) +/datum/phenomenon/divine_right/proc/succeed_ritual(var/mob/living/L) to_chat(linked, SPAN_OCCULT("You have been reborn! Your power is limited here, focused on your body, but in return you are both eternal and physical.")) for(var/mob/living/player in global.player_list) sound_to(player, 'sound/effects/cascade.ogg') @@ -221,12 +221,12 @@ L.SetName("[linked] Incarnate") L.real_name = "[linked] Incarnate" -/datum/phenomena/movable_object/wisp +/datum/phenomenon/movable_object/wisp name = "Wisp" desc = "Creates or moves a small ball of light for your followers to use." cost = 30 object_type = /obj/item/flashlight/slime -/datum/phenomena/movable_object/wisp/add_object() +/datum/phenomenon/movable_object/wisp/add_object() ..() object_to_move.SetName("wisp") \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/transmutation.dm b/mods/gamemodes/deity/mobs/phenomena/transmutation.dm index e86c92ec4d4e..381e885d3187 100644 --- a/mods/gamemodes/deity/mobs/phenomena/transmutation.dm +++ b/mods/gamemodes/deity/mobs/phenomena/transmutation.dm @@ -1,4 +1,4 @@ -/datum/phenomena/warp +/datum/phenomenon/warp name = "Warp Body" desc = "Corrupt a mortal being, causing their DNA to break and their body to fail on them." cost = 90 @@ -6,20 +6,20 @@ flags = PHENOMENA_NEAR_STRUCTURE|PHENOMENA_MUNDANE|PHENOMENA_FOLLOWER|PHENOMENA_NONFOLLOWER expected_type = /mob/living -/datum/phenomena/warp/activate(var/mob/living/L) +/datum/phenomenon/warp/activate(var/mob/living/L) ..() L.take_damage(20, CLONE) SET_STATUS_MAX(L, STAT_WEAK, 2) to_chat(L, SPAN_DANGER("You feel your body warp and change underneath you!")) -/datum/phenomena/rock_form +/datum/phenomenon/rock_form name = "Rock Form" desc = "Convert your mortal followers into immortal stone beings." cost = 300 flags = PHENOMENA_NEAR_STRUCTURE|PHENOMENA_FOLLOWER expected_type = /mob/living/human -/datum/phenomena/rock_form/activate(var/mob/living/human/H) +/datum/phenomenon/rock_form/activate(var/mob/living/human/H) ..() to_chat(H, SPAN_DANGER("You feel your body harden as it rapidly is transformed into living crystal!")) H.change_species(SPECIES_GOLEM) diff --git a/mods/gamemodes/deity/screen/intent.dm b/mods/gamemodes/deity/screen/intent.dm index 80b3a423170e..9f5b18b6aaa8 100644 --- a/mods/gamemodes/deity/screen/intent.dm +++ b/mods/gamemodes/deity/screen/intent.dm @@ -9,24 +9,25 @@ compile_overlays() /obj/screen/intent/deity/proc/sync_to_mob(var/mob) - var/mob/living/deity/D = mob - for(var/i in 1 to D.control_types.len) - var/obj/screen/deity_marker/S = new(null, D) - desc_screens[D.control_types[i]] = S + var/mob/living/deity/deity = mob + for(var/i in 1 to deity.control_types.len) + var/obj/screen/deity_marker/S = new(null, deity) + desc_screens[deity.control_types[i]] = S S.screen_loc = screen_loc //This sets it up right. Trust me. S.maptext_y = 33/2*i - i*i/2 - 10 - D.client.screen += S - + deity.client.screen += S update_text() /obj/screen/intent/deity/proc/update_text() - if(!isdeity(usr)) + var/mob/living/deity/deity = usr + var/mob/owner = owner_ref?.resolve() + if(!istype(deity) || !istype(owner) || owner != deity) return - var/mob/living/deity/D = usr - for(var/i in D.control_types) + var/decl/intent/intent = owner.get_intent() + for(var/i in deity.control_types) var/obj/screen/deity_marker/S = desc_screens[i] - var/datum/phenomena/P = D.intent_phenomenas[intent][i] + var/datum/phenomenon/P = deity.intent_phenomena[intent][i] if(P) S.maptext = "[P.name]" else diff --git a/nano/templates/deity.tmpl b/nano/templates/deity.tmpl index b249a8649600..5d1148e300e2 100644 --- a/nano/templates/deity.tmpl +++ b/nano/templates/deity.tmpl @@ -7,7 +7,7 @@ Deity Menu. Three seperate modes within it, followers, shop, and phenomena. {{:data.name}} the {{:data.form_name}}
    - Current Boon: + Current Boon: {{if data.boon_name}} {{:data.boon_name}} {{else}} @@ -78,13 +78,13 @@ Deity Menu. Three seperate modes within it, followers, shop, and phenomena. {{:helper.link('Bindings', 'key', {'switchMenu' : 1, 'menu' : 'phenomenaMenu'}, data.phenomenaMenu == 1 ? 'disabled' : null)}}
    {{if data.phenomenaMenu == 0}} - {{for data.phenomenas}} + {{for data.phenomena}}
    {{:helper.link(value.name, null, {'select_phenomena' : value.name})}}
    - Use Cost: {{:value.cost}} + Use Cost: {{:value.cost}} {{if value.cooldown}} Cooldown: {{:value.cooldown}} {{/if}} From 46606cf24c56275283f751ec53f9b8d9193abda2 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 28 Nov 2024 13:39:00 +1100 Subject: [PATCH 0035/1331] Intent selector is now dynamically generated. --- code/__defines/hud.dm | 1 - code/_helpers/cmp.dm | 3 + code/_onclick/hud/animal.dm | 2 +- code/_onclick/hud/human.dm | 2 +- code/_onclick/hud/robot.dm | 2 +- code/_onclick/hud/screen/screen_intent.dm | 97 ++++++++++-------- code/datums/repositories/decls.dm | 2 + code/modules/client/ui_styles/_helpers.dm | 1 - code/modules/client/ui_styles/_ui_style.dm | 1 - .../client/ui_styles/_ui_style_states.dm | 9 -- .../client/ui_styles/ui_style_subtypes.dm | 1 - code/modules/mob/mob_intent.dm | 56 +++++++--- icons/mob/screen/styles/intents.dmi | Bin 1512 -> 286 bytes icons/mob/screen/styles/intents_wide.dmi | Bin 0 -> 259 bytes .../mob/screen/styles/minimalist/intents.dmi | Bin 2760 -> 0 bytes icons/screen/intents.dmi | Bin 0 -> 1087 bytes icons/screen/intents_wide.dmi | Bin 0 -> 658 bytes mods/content/xenobiology/slime/slime_hud.dm | 2 +- mods/gamemodes/deity/mobs/deity_hud.dm | 2 +- mods/mobs/borers/mob/borer/borer_hud.dm | 2 +- mods/mobs/dionaea/icons/ui_intent_overlay.dmi | Bin 0 -> 565 bytes mods/mobs/dionaea/icons/ui_intents.dmi | Bin 701 -> 422 bytes mods/mobs/dionaea/mob/_nymph.dm | 10 ++ mods/mobs/dionaea/mob/nymph_ui.dm | 12 ++- .../ascent/icons/ui_intent_overlay.dmi | Bin 0 -> 569 bytes mods/species/ascent/icons/ui_intents.dmi | Bin 695 -> 422 bytes mods/species/ascent/mobs/nymph/_nymph.dm | 10 ++ mods/species/ascent/mobs/nymph/nymph_ui.dm | 18 ++-- 28 files changed, 151 insertions(+), 82 deletions(-) create mode 100644 icons/mob/screen/styles/intents_wide.dmi delete mode 100644 icons/mob/screen/styles/minimalist/intents.dmi create mode 100644 icons/screen/intents.dmi create mode 100644 icons/screen/intents_wide.dmi create mode 100644 mods/mobs/dionaea/icons/ui_intent_overlay.dmi create mode 100644 mods/species/ascent/icons/ui_intent_overlay.dmi diff --git a/code/__defines/hud.dm b/code/__defines/hud.dm index 24e3be0d394a..7cb67e0fd28a 100644 --- a/code/__defines/hud.dm +++ b/code/__defines/hud.dm @@ -11,7 +11,6 @@ #define UI_ICON_NUTRITION "icon_nutrition" #define UI_ICON_HYDRATION "icon_hydration" #define UI_ICON_FIRE_INTENT "icon_fire_intent" -#define UI_ICON_INTENT "icon_intent" #define UI_ICON_UP_HINT "icon_uphint" #define UI_ICON_STATUS "icon_status" #define UI_ICON_STATUS_FIRE "icon_status_fire" diff --git a/code/_helpers/cmp.dm b/code/_helpers/cmp.dm index 32637fecad8d..3a04928449ee 100644 --- a/code/_helpers/cmp.dm +++ b/code/_helpers/cmp.dm @@ -137,6 +137,9 @@ /proc/cmp_decl_uid_asc(decl/a, decl/b) return sorttext(b.uid, a.uid) +/proc/cmp_decl_sort_value_asc(decl/a, decl/b) + return a.sort_order - b.sort_order + /proc/cmp_inventory_slot_desc(datum/inventory_slot/a, datum/inventory_slot/b) return b.quick_equip_priority - a.quick_equip_priority diff --git a/code/_onclick/hud/animal.dm b/code/_onclick/hud/animal.dm index 5c1d9173a3a6..70b61dffe77b 100644 --- a/code/_onclick/hud/animal.dm +++ b/code/_onclick/hud/animal.dm @@ -11,6 +11,6 @@ move_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_MOVEMENT) move_intent.icon_state = mymob.move_intent.hud_icon_state adding += move_intent - action_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_INTENT) + action_intent = new(null, mymob, ui_style, ui_color, ui_alpha) adding += action_intent ..() diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 96f20aa1c3a0..44722373ecbd 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -17,7 +17,7 @@ // Draw the attack intent dialogue. if(hud_data.show_intent_selector) - action_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_INTENT) + action_intent = new(null, mymob) src.adding += action_intent hud_elements |= action_intent diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 41ad1300f871..6408ee430382 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -51,7 +51,7 @@ var/global/obj/screen/robot_inventory adding += R.ui_drop_grab //Intent - action_intent = new /obj/screen/intent/robot(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_INTENT) + action_intent = new /obj/screen/intent/robot(null, mymob) action_intent.icon_state = R.get_intent().icon_state adding += action_intent diff --git a/code/_onclick/hud/screen/screen_intent.dm b/code/_onclick/hud/screen/screen_intent.dm index accd7b27c4f0..4682cc14836c 100644 --- a/code/_onclick/hud/screen/screen_intent.dm +++ b/code/_onclick/hud/screen/screen_intent.dm @@ -1,52 +1,67 @@ /obj/screen/intent - name = "intent" - icon = 'icons/mob/screen/styles/intents.dmi' - icon_state = "intent_help" - screen_loc = ui_acti - -/obj/screen/intent/proc/get_intent_state_map() - var/static/list/intent_state_map = list( - GET_DECL(I_DISARM) = "intent_disarm", - GET_DECL(I_HELP) = "intent_help", - GET_DECL(I_HARM) = "intent_harm", - GET_DECL(I_GRAB) = "intent_grab" - ) - return intent_state_map + name = "intent" + icon = 'icons/mob/screen/styles/intents.dmi' + icon_state = "intents" + screen_loc = ui_acti + requires_ui_style = FALSE + +/obj/screen/intent/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) + . = ..() + update_icon() /obj/screen/intent/handle_click(mob/user, params) var/mob/owner = owner_ref?.resolve() if(!istype(owner) || QDELETED(owner) || user != owner || !params) return - var/list/P = params2list(params) - var/icon_x = text2num(P["icon-x"]) - var/icon_y = text2num(P["icon-y"]) - if(icon_x <= world.icon_size/2) - if(icon_y <= world.icon_size/2) - owner.set_intent(I_HARM) - else - owner.set_intent(I_HELP) - else if(icon_y <= world.icon_size/2) - owner.set_intent(I_GRAB) - else - owner.set_intent(I_DISARM) - update_icon() + params = params2list(params) + if(owner.set_intent(get_intent_by_position(owner.get_available_intents(), text2num(params["icon-x"]), text2num(params["icon-y"])))) + update_icon() + +/obj/screen/intent/proc/get_intent_by_position(list/intents, icon_x, icon_y) + if(icon_y <= 16) + if(icon_x <= 16) + return intents[1] + return intents[2] + else if(icon_x <= 16) + return intents[3] + return intents[4] + +/obj/screen/intent/proc/apply_intent_overlay_offset(image/overlay, index) + switch(index) + if(2) + overlay.pixel_x = 16 + if(3) + overlay.pixel_y = 16 + if(4) + overlay.pixel_x = 16 + overlay.pixel_y = 16 /obj/screen/intent/on_update_icon() + cut_overlays() var/mob/owner = owner_ref?.resolve() - if(istype(owner) && !QDELETED(owner)) - var/list/intent_state_map = get_intent_state_map() - icon_state = intent_state_map[owner.get_intent()] || intent_state_map[intent_state_map[1]] - else - icon_state = initial(icon_state) + if(!istype(owner) || QDELETED(owner)) + return + var/decl/intent/owner_intent = owner.get_intent() + var/i = 0 + var/image/I + for(var/decl/intent/intent as anything in owner.get_available_intents()) + if(intent == owner_intent) + I = image(intent.icon, intent.icon_state) + else + I = image(intent.icon, "[intent.icon_state]_off") + i++ + apply_intent_overlay_offset(I, i) + add_overlay(I) + compile_overlays() /obj/screen/intent/binary - icon_state = "intent_harm" - -/obj/screen/intent/binary/get_intent_state_map() - var/static/list/intent_state_map = list( - GET_DECL(I_DISARM) = "intent_help", - GET_DECL(I_HELP) = "intent_help", - GET_DECL(I_HARM) = "intent_harm", - GET_DECL(I_GRAB) = "intent_harm" - ) - return intent_state_map + icon = 'icons/mob/screen/styles/intents_wide.dmi' + +/obj/screen/intent/binary/get_intent_by_position(list/intents, icon_x, icon_y) + if(icon_y <= 16) + return intents[1] + return intents[2] + +/obj/screen/intent/binary/apply_intent_overlay_offset(image/overlay, index) + if(index == 2) + overlay.pixel_y = 16 diff --git a/code/datums/repositories/decls.dm b/code/datums/repositories/decls.dm index e7898fa23c34..73a9f14fb610 100644 --- a/code/datums/repositories/decls.dm +++ b/code/datums/repositories/decls.dm @@ -149,6 +149,8 @@ var/global/repository/decls/decls_repository = new var/uid var/decl_flags = null // DECL_FLAG_ALLOW_ABSTRACT_INIT, DECL_FLAG_MANDATORY_UID var/initialized = FALSE + /// General purpose sort value. + var/sort_order /decl/proc/Initialize() SHOULD_CALL_PARENT(TRUE) diff --git a/code/modules/client/ui_styles/_helpers.dm b/code/modules/client/ui_styles/_helpers.dm index 36685c53f351..83ceb172a415 100644 --- a/code/modules/client/ui_styles/_helpers.dm +++ b/code/modules/client/ui_styles/_helpers.dm @@ -13,7 +13,6 @@ UI_ICON_HEALTH = 'icons/mob/screen/styles/health.dmi', UI_ICON_CRIT_MARKER = 'icons/mob/screen/styles/crit_markers.dmi', UI_ICON_HYDRATION = 'icons/mob/screen/styles/hydration.dmi', - UI_ICON_INTENT = 'icons/mob/screen/styles/intents.dmi', UI_ICON_INTERACTION = 'icons/mob/screen/styles/midnight/interaction.dmi', UI_ICON_INTERNALS = 'icons/mob/screen/styles/internals.dmi', UI_ICON_INVENTORY = 'icons/mob/screen/styles/midnight/inventory.dmi', diff --git a/code/modules/client/ui_styles/_ui_style.dm b/code/modules/client/ui_styles/_ui_style.dm index 24e3d9085160..5e0a5ba34c41 100644 --- a/code/modules/client/ui_styles/_ui_style.dm +++ b/code/modules/client/ui_styles/_ui_style.dm @@ -13,7 +13,6 @@ UI_ICON_HEALTH = 'icons/mob/screen/styles/health.dmi', UI_ICON_CRIT_MARKER = 'icons/mob/screen/styles/crit_markers.dmi', UI_ICON_HYDRATION = 'icons/mob/screen/styles/hydration.dmi', - UI_ICON_INTENT = 'icons/mob/screen/styles/intents.dmi', UI_ICON_INTERACTION = 'icons/mob/screen/styles/midnight/interaction.dmi', UI_ICON_INTERNALS = 'icons/mob/screen/styles/internals.dmi', UI_ICON_INVENTORY = 'icons/mob/screen/styles/midnight/inventory.dmi', diff --git a/code/modules/client/ui_styles/_ui_style_states.dm b/code/modules/client/ui_styles/_ui_style_states.dm index bac1d053713e..881a423335e8 100644 --- a/code/modules/client/ui_styles/_ui_style_states.dm +++ b/code/modules/client/ui_styles/_ui_style_states.dm @@ -10,7 +10,6 @@ var/global/list/_ui_all_keys = list( UI_ICON_NUTRITION, UI_ICON_HYDRATION, UI_ICON_FIRE_INTENT, - UI_ICON_INTENT, UI_ICON_UP_HINT, UI_ICON_STATUS, UI_ICON_STATUS_FIRE, @@ -73,14 +72,6 @@ var/global/list/_ui_expected_states "hydration3", "hydration4" ), - UI_ICON_INTENT = list( - "intent_all", - "intent_help", - "intent_disarm", - "intent_grab", - "intent_harm", - "intent_none" - ), UI_ICON_INTERACTION = list( "act_resist", "act_throw_off", diff --git a/code/modules/client/ui_styles/ui_style_subtypes.dm b/code/modules/client/ui_styles/ui_style_subtypes.dm index 7fbd15f6c0db..cff4d996c43b 100644 --- a/code/modules/client/ui_styles/ui_style_subtypes.dm +++ b/code/modules/client/ui_styles/ui_style_subtypes.dm @@ -66,7 +66,6 @@ UI_ICON_ATTACK = 'icons/mob/screen/styles/minimalist/attack_selector.dmi', UI_ICON_FIRE_INTENT = 'icons/mob/screen/styles/minimalist/fire_intent.dmi', UI_ICON_HANDS = 'icons/mob/screen/styles/minimalist/hands.dmi', - UI_ICON_INTENT = 'icons/mob/screen/styles/minimalist/intents.dmi', UI_ICON_INTERACTION = 'icons/mob/screen/styles/minimalist/interaction.dmi', UI_ICON_INVENTORY = 'icons/mob/screen/styles/minimalist/inventory.dmi', UI_ICON_MOVEMENT = 'icons/mob/screen/styles/minimalist/movement.dmi', diff --git a/code/modules/mob/mob_intent.dm b/code/modules/mob/mob_intent.dm index 7771df5dba1e..b73a189e9ac6 100644 --- a/code/modules/mob/mob_intent.dm +++ b/code/modules/mob/mob_intent.dm @@ -28,6 +28,8 @@ var/intent_flags = 0 /// Descriptive string used in status panel. var/name + /// Icon used to draw this intent in the selector. + var/icon = 'icons/screen/intents.dmi' /// State used to update intent selector. var/icon_state @@ -37,31 +39,53 @@ . += "null or invalid name" if(!istext(icon_state)) . += "null or invalid icon_state" + if(!icon) + . += "null icon" + if(icon && istext(icon_state)) + if(!check_state_in_icon(icon_state, icon)) + . += "missing icon_state '[icon_state]' from icon '[icon]'" + if(!check_state_in_icon("[icon_state]_off", icon)) + . += "missing icon_state '[icon_state]_off' from icon '[icon]'" // Basic subtypes. -/decl/intent/help - name = "help" - uid = "intent_help" - intent_flags = I_FLAG_HELP - icon_state = "intent_help" - /decl/intent/harm name = "harm" uid = "intent_harm" intent_flags = I_FLAG_HARM icon_state = "intent_harm" + sort_order = 1 // Bottom left of intent selector. /decl/intent/grab name = "grab" uid = "intent_grab" intent_flags = I_FLAG_GRAB icon_state = "intent_grab" + sort_order = 2 // Bottom left of the intent selector. + +/decl/intent/help + name = "help" + uid = "intent_help" + intent_flags = I_FLAG_HELP + icon_state = "intent_help" + sort_order = 3 // Top left of the intent selector. /decl/intent/disarm name = "disarm" uid = "intent_disarm" intent_flags = I_FLAG_DISARM icon_state = "intent_disarm" + sort_order = 4 // Top right of the intent selector. + +// Used by nymphs. +/decl/intent/harm/binary + icon = 'icons/screen/intents_wide.dmi' + uid = "intent_harm_simple" + intent_flags = (I_FLAG_HARM|I_FLAG_DISARM) + +/decl/intent/help/binary + icon = 'icons/screen/intents_wide.dmi' + uid = "intent_help_simple" + intent_flags = (I_FLAG_HARM|I_FLAG_GRAB) /mob /// Decl for current 'intent' of mob; hurt, harm, etc. Initialized by get_intent(). @@ -88,16 +112,22 @@ /mob/proc/get_intent() RETURN_TYPE(/decl/intent) if(!_a_intent) - _a_intent = get_available_intents()[1] + _a_intent = get_default_intent() return _a_intent +/mob/proc/get_default_intent() + return GET_DECL(I_HELP) + /mob/proc/get_available_intents() - var/static/list/available_intents = list( - GET_DECL(I_HELP), - GET_DECL(I_DISARM), - GET_DECL(I_GRAB), - GET_DECL(I_HARM) - ) + var/static/list/available_intents + if(!available_intents) + available_intents = list( + GET_DECL(I_HELP), + GET_DECL(I_DISARM), + GET_DECL(I_GRAB), + GET_DECL(I_HARM) + ) + available_intents = sortTim(available_intents, /proc/cmp_decl_sort_value_asc) return available_intents /mob/proc/cycle_intent(input) diff --git a/icons/mob/screen/styles/intents.dmi b/icons/mob/screen/styles/intents.dmi index 3c26214b74e2ae700720b3916e181bbbf7943f7f..3b0b33f849097d5e990704dff20e96350ecb11d9 100644 GIT binary patch delta 218 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 O0000qnpT3Vosj8UKe zfPH8>Mmae@M!>+p(b>?@)Y&{rJVR1OPhn4JbbyPMpr5C)vRn4<00001bW%=J06^y0 zW&i*HmwHrKbVOxyV{&P5bZKvH004NLQ&w6ZucgD#^TO= z4}u@^BsXs&MBtSWGp~f0dnH7$@G6+Scr}~9dNm(F;Da#a(}O6w2XSmk5R71%yevoX zmfnLbwv(^25hVXyAzo#uMUagvgh?2Nb_sweO(PG)t2m3}I9Q<^NS5JpxlAZ#^C*3L zgGi(F3NmI{2U&*K3~>^>!WBeO%I5~eKe%VO3?O8rgufnO4|t6r%T_VlggvuSj1(@p z=>aehH(~`qfbop9S`TgI`0yop4=rFC#D}A>U|~VFSodv3c1(`(;k)gU?JKszYy^vY zko15@L12YlD2)iP?U->$ZvX>@;oV(|<`b9CjdTZn4_Hxhw`y+xgX@9zp4?AgfAN z0F`tABT-vbk1Oa>Yi*YxkX2DgAg7SzfT5@Z%7JpP_xpWGF#t?m$YKCBvm8}j0Gg3X{v0SUv9!%aFo3!~DCKs-Mlq7!bJGJra@(K$7hEBc zT`5L79EY}Yy#6RZ^#EK$c@7`|TMO##v2QD~V{(kwpBCgju>Yi;)(2$|coYPjxNzfn z4vNW-O7-VG0tV9h;a$49d~T#WXuWytgy>SWGdeHQ>t^CbAX+yP_G zyYsm<|Gh%nop%#8*dcr;0RwR(R-7=r!+}{~CIQnRJ{-jf>5c8r`43z>*(KXoY=zkfTtj&d zQ+MOU;SIZFQ=OQ`0sC+A;BJb56Sr>sgOh&l@;!UK{{kG|r7vx;@qz#V002ovPDHLk FV1o0x>Zj6FSl^c%GR z0004WQchCV=-0C=2JR&a84_w-Y6@%7{?OD!tS z%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$qd978j%;*!LYR3K9+Gp{5yuOvPrHK#y{ zi!&v&s2HSxi!-e#F*g;$#!#DBluNW)ptP+qS3|sjj-jJztuVFanhvD378pg}d z!|)6MoC$$P;QJ&`1I)oZF|PeSS4N0^Ic4p4py%um3>S;ztrm(Nty1^*x5=`ekLoZU7I=h858{yvN?y@!z)*e=M% zW8?d-&jOf}c*a-Kc^i;tnR8}JnrcZl(no;2-vqc5a@l7LHRSu=iWK*9h3{{R4<007+Zx2fAr#V-J%00001 zbW%=J06^y0W&i*HgL+h0bVOxyV{&P5bZKvH004NLQ&w?Vi1Gq`RT3I zw(*jW*&wQ0j2*!<`-%fWQn#2|g6p7`fd4G4g|Co*IYCG8!JrV>oZyy)6f)-owh}0a z9l@d!B=wL|LvYW+8ZCkPnp%Qva4(UIs}cmYQp1yAr_iRAHZAp8t2QPvWXZykMn&ICMzSs{QF!diQEdzT*00000NkvXXu0mjf D^gQST literal 0 HcmV?d00001 diff --git a/mods/mobs/dionaea/icons/ui_intents.dmi b/mods/mobs/dionaea/icons/ui_intents.dmi index d8aaeba136e219bc65d494ebe04e3db60c24cef6..7a7f29b4d21c75c396e026db96834dca91efc29d 100644 GIT binary patch delta 321 zcmdnXx{O)1Gr-TCmrII^fq{Y7)59eQNGpIa3p0>x3`su@q!cHrs)`mo`A`dFGnNGT z1v5B2yO9RuWKYcVt6vh)*=?-4d4+^V(ut!p!=}ub&6TLFz2n!;23|IX+LO`(^P^T; z0Tu7@ba4#Pn3y}!knfNIkLy`pDgTr$>S;$<6O#G@n6-s_6c!%173^ZGTPW0Z^-7@e zpM5v%e=@j*vu{X>Wog)buUbA%vEu&xZL$vxk69esB|E7gOx;Y6v#R{+<+xS{rAL20 zMs@Uaq@*PO)?v{X%JgLsZPQ-3oa5s)yG;w`c6~7TvRhJ8@J_>4=0^{v^_lqI-Z*(< zN^Fbwwm6s9T#Em1ny~!s^wemXr*y>k_lf*fbGbHN5A@Re{A5@Uy1vUXmHw@j8$ R69syN!PC{xWt~$(69A1HA<$iBL{Q4GJ0x0000DNk~Le0001B0000$2m=5B0MhD$JOBUyFp(uC zB;4?~soPD$009300GBn z0Mlz<9f1mzVgLXEp-DtRR9J=WSKE@qAP5aMH?vLm|NnLY(Oep;6V`oMWlU@iLm`Mc zbX^Df=xCU6rw{lQfBex8<1mrnkPFH<^bgdq;;7KiKFH!vtDX(ma8wv(CBS)L12!BL zMz5sEk8Hq(vj>X8K$>R{bNCj4%yR^6;3<=f=jdS$-#nMqAp&I=e{79$pkuF-}S1 zQh)8`_2N>OB<_xa!&y^=ZxKZASGbNKO}LIg!IKByC;8|hWFmlz2rmBM{T&|-6yd=G zWfvq54LE&Mf7N{~-&_;U=ebsi@w~U-#A}oY0fhIM@cPWIIybG)uIrE55w!l`|MUkM zzqjI9es9II{N9RZ`Mnj-@_Q@Z`h(UVwEkenO;3gL#y#)$55@fdZvRls|L^t>#r*$n p|4_{T@AeO^KWP0y>kqo_2TATpxAp|1H~;_u07*qo1w^hwV1l;@6fXb( diff --git a/mods/mobs/dionaea/mob/_nymph.dm b/mods/mobs/dionaea/mob/_nymph.dm index 4c8b1a9f4967..4e13e530ef9a 100644 --- a/mods/mobs/dionaea/mob/_nymph.dm +++ b/mods/mobs/dionaea/mob/_nymph.dm @@ -76,6 +76,16 @@ heal_damage(OXY, rads, do_update_health = FALSE) heal_damage(TOX, rads) +/mob/living/simple_animal/alien/diona/get_default_intent() + return GET_DECL(/decl/intent/help/binary/diona) + +/mob/living/simple_animal/alien/diona/get_available_intents() + var/static/list/available_intents = list( + GET_DECL(/decl/intent/harm/binary/diona), + GET_DECL(/decl/intent/help/binary/diona) + ) + return available_intents + /decl/bodytype/diona name = "nymph" bodytype_flag = 0 diff --git a/mods/mobs/dionaea/mob/nymph_ui.dm b/mods/mobs/dionaea/mob/nymph_ui.dm index b337856bd458..0930bb184cdd 100644 --- a/mods/mobs/dionaea/mob/nymph_ui.dm +++ b/mods/mobs/dionaea/mob/nymph_ui.dm @@ -1,6 +1,13 @@ /obj/screen/intent/binary/diona + icon = 'mods/mobs/dionaea/icons/ui_intents.dmi' screen_loc = DIONA_SCREEN_LOC_INTENT +/decl/intent/harm/binary/diona + icon = 'mods/mobs/dionaea/icons/ui_intent_overlay.dmi' + +/decl/intent/help/binary/diona + icon = 'mods/mobs/dionaea/icons/ui_intent_overlay.dmi' + /decl/ui_style/diona name = "Diona" restricted = TRUE @@ -8,7 +15,6 @@ override_icons = list( UI_ICON_HEALTH = 'mods/mobs/dionaea/icons/ui_health.dmi', UI_ICON_HANDS = 'mods/mobs/dionaea/icons/ui_hands.dmi', - UI_ICON_INTENT = 'mods/mobs/dionaea/icons/ui_intents.dmi', UI_ICON_INTERACTION = 'mods/mobs/dionaea/icons/ui_interactions.dmi', UI_ICON_INVENTORY = 'mods/mobs/dionaea/icons/ui_inventory.dmi' ) @@ -27,8 +33,8 @@ var/ui_color = get_ui_color() var/ui_alpha = get_ui_alpha() - action_intent = new /obj/screen/intent/binary/diona(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_INTENT) - mymob.healths = new /obj/screen/diona_health( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) + action_intent = new /obj/screen/intent/binary/diona(null, mymob) + mymob.healths = new /obj/screen/diona_health( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) src.other = list() src.adding = list(mymob.healths, action_intent) ..() diff --git a/mods/species/ascent/icons/ui_intent_overlay.dmi b/mods/species/ascent/icons/ui_intent_overlay.dmi new file mode 100644 index 0000000000000000000000000000000000000000..d49275852d3485b58a6fd646fb89f8727e5e5633 GIT binary patch literal 569 zcmV-90>=G`P)oSPNev|8jzk;J}~|*qq=u3n^sI32Y@$ z5IX|SzTgo-QV%IL1bY_NXbIHU)DmQay+kgqN)Xga4KIR|LYr3FwA5#{KL6MzUmF?l zk&S6+1+f^M4?09}@_ajJ#X-D=cux3-`T@v6N#yy>e$Xi!-RuW7JK#WYu^)VB=m(YT zO*jyI8g%_Ygk6GL{h&YKj^J89coUSk+Yg4<4FCP$i~ZmUHr^jAv|?*m00000NkvXX Hu0mjffgtj~ literal 0 HcmV?d00001 diff --git a/mods/species/ascent/icons/ui_intents.dmi b/mods/species/ascent/icons/ui_intents.dmi index 1e027dcfcdddd90d6999f10bb9aea31c4f5b0bf4..0e8d07ec2fecf6f82ef9e05347c6baffdb6dcbf7 100644 GIT binary patch delta 321 zcmdnax{O)1Gr-TCmrII^fq{Y7)59eQNGpIa3p0>x3`su@q!cHrs*1Mip2`HW8B2ov zf*Bm1-ADs+vM1*G)h~(Y>^4^2yh1`F>BP~QVN+(z=1SDo-tlW^11}px?MZ2Y`B5va zfQt8cx;TbtOw65V$alzq$Mr0)lz++=^|T|b2}ykc%-X^|3JVY13U;y8EfngydL_{K z&%PV>KN;M@**7G`vNY_zS1li>SaE;;HrWS;$1INRlATl#rf#OkSyg`Za$Ku}(xX2g zqdNLIQc{wC>#*nxW%{y+wrMY1&hhb@-KGU|yFM6v*)1t4c&A}2^P>mT`b>OpZ=Ad_ zCAP(TTb#>lF2(;hO<4YRdTO-HQ##`N`$Yb#xm+8s2YTs!elja(_Lt{hSv#-9TP91W Ri2^;s;OXk;vd$@?2>`N`d>#M* delta 596 zcmV-a0;~O|1GfbwiBL{Q4GJ0x0000DNk~Le0001B0000$2m=5B0MhD$JOBUyFp(uC zB=EQ9+fBj$nE?OH0RLtHZ;Jj?00001bW%=J06^y0W&i*Hj*(wZeShacid8xS6Dl549xqw3s z0M@Qw9Wh5kPyhe{n@L1LR9J=Wm+N-JAP9iP=4RGp@Bg*~G&XVJl*!zmDaYJCv`{nw z=N!n*Q8RNdH~1C)e;CGToJp{y1IjcG57e-7R2Y^JWevsEz!TUwDvXQZ;4<~pYc()kZxGc=YmV)QDF{r_rU_#<261CGfxnC6oBKZ;(ziNH4k$8WqPxbc&i^4(#=M) zrH-QI6r|Iu;PS2sDTvJXv+TQ?zdXnt?malH+u+^Ef^4CIe$zKqT*v8~YvTMqmv-XuzPE77YXlDgQr=_Y?K8XPxoP|Cx_xWyL9Y+~ zPkrz%|F7lh{J)l~^Z#0|&i`wbb&V2!bWl5#OWJM_e0000 Date: Thu, 28 Nov 2024 16:15:43 +1100 Subject: [PATCH 0036/1331] Updating set_intent() to use flags instead of types. --- code/__defines/intent.dm | 7 +---- code/_onclick/other_mobs.dm | 2 +- code/datums/ai/aggressive.dm | 8 +++--- code/datums/ai/hunter.dm | 2 +- code/game/objects/effects/gateway.dm | 2 +- code/modules/keybindings/mob.dm | 8 +++--- code/modules/mechs/mech_interaction.dm | 2 +- code/modules/mob/living/bot/secbot.dm | 4 +-- .../simple_animal/friendly/farm_animals.dm | 2 +- .../simple_animal/hostile/faithful_hound.dm | 2 +- code/modules/mob/mob_intent.dm | 26 +++++++++++------ code/modules/mob/transform_procs.dm | 6 ++-- code/modules/projectiles/projectile/change.dm | 4 +-- .../system/psionics/faculties/_faculty.dm | 8 ++---- .../system/psionics/faculties/coercion.dm | 2 +- .../system/psionics/faculties/energistics.dm | 2 +- .../psionics/faculties/psychokinesis.dm | 2 +- .../system/psionics/faculties/redaction.dm | 2 +- .../system/psionics/mob/mob_interactions.dm | 6 ++-- mods/content/psionics/system/subsystem_psi.dm | 28 ++++++++++++++----- mods/content/xenobiology/slime/slime_AI.dm | 12 ++++---- mods/gamemodes/deity/mobs/deity_phenomena.dm | 10 +++---- mods/mobs/dionaea/mob/nymph_ui.dm | 2 ++ mods/species/ascent/mobs/nymph/nymph_ui.dm | 2 ++ 24 files changed, 86 insertions(+), 65 deletions(-) diff --git a/code/__defines/intent.dm b/code/__defines/intent.dm index f4e895d4af9e..2fa914a8b4c2 100644 --- a/code/__defines/intent.dm +++ b/code/__defines/intent.dm @@ -1,14 +1,9 @@ -// Intent decls for use in set_intent() -#define I_HELP /decl/intent/help -#define I_DISARM /decl/intent/disarm -#define I_GRAB /decl/intent/grab -#define I_HARM /decl/intent/harm - // Intent bitflags for use in check_intent() #define I_FLAG_HELP BITFLAG(0) #define I_FLAG_DISARM BITFLAG(1) #define I_FLAG_GRAB BITFLAG(2) #define I_FLAG_HARM BITFLAG(3) +#define I_FLAG_ALL (I_FLAG_HELP|I_FLAG_DISARM|I_FLAG_GRAB|I_FLAG_HARM) //NOTE: INTENT_HOTKEY_* defines are not actual intents! //they are here to support hotkeys diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 36e4b7b0420d..870865a831ff 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -97,7 +97,7 @@ if(check_intent(I_FLAG_HELP) || !attacking_with) return A.attack_animal(src) - set_intent(I_HARM) + set_intent(I_FLAG_HARM) . = A.attackby(attacking_with, src) // attack effects are handled in natural_weapon's apply_hit_effect() instead of here if(!.) diff --git a/code/datums/ai/aggressive.dm b/code/datums/ai/aggressive.dm index e823de88f43d..62316d4d1977 100644 --- a/code/datums/ai/aggressive.dm +++ b/code/datums/ai/aggressive.dm @@ -10,7 +10,7 @@ /datum/mob_controller/aggressive/New() ..() if(isliving(body) && !QDELETED(body) && !QDELETED(src)) - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) /datum/mob_controller/aggressive/set_target(atom/new_target) var/weakref/new_target_ref = weakref(new_target) @@ -113,7 +113,7 @@ SET_STATUS_MAX(victim, STAT_WEAK, 3) return target if(body.Adjacent(target)) - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) body.ClickOn(target) return target @@ -140,7 +140,7 @@ // Attack anything on the target turf. var/obj/effect/shield/S = locate(/obj/effect/shield) in targ if(S && S.gen && S.gen.check_flag(MODEFLAG_NONHUMANS)) - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) body.ClickOn(S) return @@ -161,7 +161,7 @@ for(var/type in valid_obstacles_by_priority) var/obj/obstacle = locate(type) in targ if(obstacle) - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) body.ClickOn(obstacle) return diff --git a/code/datums/ai/hunter.dm b/code/datums/ai/hunter.dm index b5b4425ca895..e60294d4a87e 100644 --- a/code/datums/ai/hunter.dm +++ b/code/datums/ai/hunter.dm @@ -17,7 +17,7 @@ return !victim.isSynthetic() && (victim.stat == DEAD || victim.get_object_size() < body.get_object_size()) /datum/mob_controller/passive/hunter/proc/try_attack_prey(mob/living/prey) - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) body.ClickOn(prey) /datum/mob_controller/passive/hunter/proc/consume_prey(mob/living/prey) diff --git a/code/game/objects/effects/gateway.dm b/code/game/objects/effects/gateway.dm index 87418769e154..2b6be989f274 100644 --- a/code/game/objects/effects/gateway.dm +++ b/code/game/objects/effects/gateway.dm @@ -60,7 +60,7 @@ qdel(W) var/mob/living/new_mob = new /mob/living/simple_animal/corgi(AM.loc) - new_mob.set_intent(I_HARM) + new_mob.set_intent(I_FLAG_HARM) if(victim.mind) victim.mind.transfer_to(new_mob) else diff --git a/code/modules/keybindings/mob.dm b/code/modules/keybindings/mob.dm index 370af83db5dd..4ab7f0f14f96 100644 --- a/code/modules/keybindings/mob.dm +++ b/code/modules/keybindings/mob.dm @@ -52,7 +52,7 @@ full_name = "Select Help Intent" /datum/keybinding/mob/select_help_intent/down(client/user) - user.mob.set_intent(I_HELP) + user.mob.set_intent(I_FLAG_HELP) return TRUE /datum/keybinding/mob/select_disarm_intent @@ -61,7 +61,7 @@ full_name = "Select Disarm Intent" /datum/keybinding/mob/select_disarm_intent/down(client/user) - user.mob.set_intent(I_DISARM) + user.mob.set_intent(I_FLAG_DISARM) return TRUE /datum/keybinding/mob/select_grab_intent @@ -70,7 +70,7 @@ full_name = "Select Grab Intent" /datum/keybinding/mob/select_grab_intent/down(client/user) - user.mob.set_intent(I_GRAB) + user.mob.set_intent(I_FLAG_GRAB) return TRUE /datum/keybinding/mob/select_harm_intent @@ -79,7 +79,7 @@ full_name = "Select Harm Intent" /datum/keybinding/mob/select_harm_intent/down(client/user) - user.mob.set_intent(I_HARM) + user.mob.set_intent(I_FLAG_HARM) return TRUE /datum/keybinding/mob/cycle_intent_right diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm index efe8754fc57b..77fc243c7057 100644 --- a/code/modules/mechs/mech_interaction.dm +++ b/code/modules/mechs/mech_interaction.dm @@ -328,7 +328,7 @@ user.client.screen -= hud_elements user.client.eye = user if(user in pilots) - set_intent(I_HARM) + set_intent(I_FLAG_HARM) LAZYREMOVE(pilots, user) UNSETEMPTY(pilots) update_pilots() diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index a817ab7e7075..903677e4a811 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -209,9 +209,9 @@ return TRUE if(isanimal(M)) - set_intent(I_HARM) + set_intent(I_FLAG_HARM) else - set_intent(I_GRAB) + set_intent(I_FLAG_GRAB) stun_baton.use_on_mob(M, src) //robots and turrets aim for center of mass flick(attack_state, src) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index d260e451868c..ae8da0e797f3 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -59,7 +59,7 @@ var/atom/food = find_edible_atom(view(1, body.loc)) if(istype(food)) body.stop_automove() - body.set_intent(I_HELP) + body.set_intent(I_FLAG_HELP) body.ClickOn(food) else if(!LAZYLEN(body.grabbed_by)) food = find_edible_atom(oview(5, body.loc)) diff --git a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm index 19765360632c..345b7137994b 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm @@ -37,7 +37,7 @@ var/new_aggress = 1 var/dist = get_dist(mailman, body) if(dist < 2) //Attack! Attack! - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) body.ClickOn(mailman) return if(dist == 2) diff --git a/code/modules/mob/mob_intent.dm b/code/modules/mob/mob_intent.dm index b73a189e9ac6..39a1a9c5606a 100644 --- a/code/modules/mob/mob_intent.dm +++ b/code/modules/mob/mob_intent.dm @@ -2,7 +2,8 @@ // - Use mob.get_intent() to retrieve the entire decl structure. // - Use mob.check_intent(I_FOO) for 1:1 intent type checking. // - Use mob.check_intent(I_FLAG_FOO) for 'close enough for government work' flag checking. -// - Use mob.set_intent(I_FOO) to set intent to a type - does not accept flags. +// - Use mob.set_intent(I_FOO) to set intent to a type +// - Use mob.set_intent(I_FLAG_FOO) to set intent to whatever available type has the flag. // - Use mob.cycle_intent(INTENT_HOTKEY_LEFT) or mob.cycle_intent(INTENT_HOTKEY_RIGHT) to step up or down the mob intent list. // - Override mob.get_available_intents() if you want to change the intents from the default four. @@ -24,7 +25,7 @@ /decl/intent abstract_type = /decl/intent decl_flags = DECL_FLAG_MANDATORY_UID - /// Replacing the old usage of I_HURT etc. in attackby() and such. Refer to /mob/proc/check_intent(). + /// Replacing the old usage of I_HARM etc. in attackby() and such. Refer to /mob/proc/check_intent(). var/intent_flags = 0 /// Descriptive string used in status panel. var/name @@ -101,12 +102,21 @@ return (intent == resolve_intent(checking_intent)) /mob/proc/set_intent(decl/intent/new_intent) - new_intent = resolve_intent(new_intent) + + if(!isnum(new_intent)) + new_intent = resolve_intent(new_intent) + else // Retrieve intent decl based on flag. + for(var/decl/intent/intent as anything in get_available_intents()) + if(intent.intent_flags & new_intent) + new_intent = intent + break + if(istype(new_intent) && get_intent() != new_intent) _a_intent = new_intent if(istype(hud_used) && hud_used.action_intent) hud_used.action_intent.update_icon() return TRUE + return FALSE /mob/proc/get_intent() @@ -116,16 +126,16 @@ return _a_intent /mob/proc/get_default_intent() - return GET_DECL(I_HELP) + return GET_DECL(/decl/intent/help) /mob/proc/get_available_intents() var/static/list/available_intents if(!available_intents) available_intents = list( - GET_DECL(I_HELP), - GET_DECL(I_DISARM), - GET_DECL(I_GRAB), - GET_DECL(I_HARM) + GET_DECL(/decl/intent/help), + GET_DECL(/decl/intent/disarm), + GET_DECL(/decl/intent/grab), + GET_DECL(/decl/intent/harm) ) available_intents = sortTim(available_intents, /proc/cmp_decl_sort_value_asc) return available_intents diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index cb58eb00bfe0..e0c1d4468761 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -154,7 +154,7 @@ qdel(t) var/mob/living/simple_animal/corgi/new_corgi = new /mob/living/simple_animal/corgi (loc) - new_corgi.set_intent(I_HARM) + new_corgi.set_intent(get_intent()) new_corgi.key = key to_chat(new_corgi, "You are now a Corgi. Yap Yap!") @@ -186,7 +186,7 @@ var/mob/new_mob = new mobpath(src.loc) new_mob.key = key - new_mob.set_intent(I_HARM) + new_mob.set_intent(get_intent()) to_chat(new_mob, "You suddenly feel more... animalistic.") @@ -206,7 +206,7 @@ var/mob/new_mob = new mobpath(src.loc) new_mob.key = key - new_mob.set_intent(I_HARM) + new_mob.set_intent(get_intent()) to_chat(new_mob, "You feel more... animalistic.") qdel(src) diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index a2a200522e4e..8e241456d22b 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -54,11 +54,11 @@ var/choice = pick(get_random_transformation_options(M)) var/mob/living/new_mob = apply_transformation(M, choice) if(new_mob) - new_mob.set_intent(I_HARM) + new_mob.set_intent(I_FLAG_HARM) if(M.mind) for (var/spell/S in M.mind.learned_spells) new_mob.add_spell(new S.type) - new_mob.set_intent(I_HARM) + new_mob.set_intent(I_FLAG_HARM) transfer_key_from_mob_to_mob(M, new_mob) to_chat(new_mob, "Your form morphs into that of \a [choice].") else diff --git a/mods/content/psionics/system/psionics/faculties/_faculty.dm b/mods/content/psionics/system/psionics/faculties/_faculty.dm index 16cd63a7e71c..03d9fc7a2363 100644 --- a/mods/content/psionics/system/psionics/faculties/_faculty.dm +++ b/mods/content/psionics/system/psionics/faculties/_faculty.dm @@ -1,18 +1,16 @@ /decl/psionic_faculty var/id var/name - var/decl/intent/associated_intent + var/associated_intent_flag var/list/armour_types = list() var/list/powers = list() /decl/psionic_faculty/validate() . = ..() - if(associated_intent && !istype(associated_intent)) - . += "non-/decl/intent associated_intent value set" + if(!isnull(associated_intent_flag) && !isnum(associated_intent_flag)) + . += "non-bitflag associated_intent_flag value set" /decl/psionic_faculty/Initialize() - if(ispath(associated_intent)) - associated_intent = GET_DECL(associated_intent) . = ..() for(var/atype in armour_types) SSpsi.armour_faculty_by_type[atype] = id diff --git a/mods/content/psionics/system/psionics/faculties/coercion.dm b/mods/content/psionics/system/psionics/faculties/coercion.dm index 0cddc48bf322..2af44aa9335a 100644 --- a/mods/content/psionics/system/psionics/faculties/coercion.dm +++ b/mods/content/psionics/system/psionics/faculties/coercion.dm @@ -1,7 +1,7 @@ /decl/psionic_faculty/coercion id = PSI_COERCION name = "Coercion" - associated_intent = I_DISARM + associated_intent_flag = I_FLAG_DISARM armour_types = list(PSIONIC) /decl/psionic_power/coercion diff --git a/mods/content/psionics/system/psionics/faculties/energistics.dm b/mods/content/psionics/system/psionics/faculties/energistics.dm index 2ab1a789936c..2396a1e71193 100644 --- a/mods/content/psionics/system/psionics/faculties/energistics.dm +++ b/mods/content/psionics/system/psionics/faculties/energistics.dm @@ -1,7 +1,7 @@ /decl/psionic_faculty/energistics id = PSI_ENERGISTICS name = "Energistics" - associated_intent = I_HARM + associated_intent_flag = I_FLAG_HARM armour_types = list(ARMOR_BOMB, ARMOR_LASER, ARMOR_ENERGY) /decl/psionic_power/energistics diff --git a/mods/content/psionics/system/psionics/faculties/psychokinesis.dm b/mods/content/psionics/system/psionics/faculties/psychokinesis.dm index 49c77a70ae0f..8d0dd5c61f88 100644 --- a/mods/content/psionics/system/psionics/faculties/psychokinesis.dm +++ b/mods/content/psionics/system/psionics/faculties/psychokinesis.dm @@ -1,7 +1,7 @@ /decl/psionic_faculty/psychokinesis id = PSI_PSYCHOKINESIS name = "Psychokinesis" - associated_intent = I_GRAB + associated_intent_flag = I_FLAG_GRAB armour_types = list(ARMOR_MELEE, ARMOR_BULLET) /decl/psionic_power/psychokinesis diff --git a/mods/content/psionics/system/psionics/faculties/redaction.dm b/mods/content/psionics/system/psionics/faculties/redaction.dm index 97354f41a059..8879259a72ac 100644 --- a/mods/content/psionics/system/psionics/faculties/redaction.dm +++ b/mods/content/psionics/system/psionics/faculties/redaction.dm @@ -1,7 +1,7 @@ /decl/psionic_faculty/redaction id = PSI_REDACTION name = "Redaction" - associated_intent = I_HELP + associated_intent_flag = I_FLAG_HELP armour_types = list(ARMOR_BIO, ARMOR_RAD) /decl/psionic_power/redaction diff --git a/mods/content/psionics/system/psionics/mob/mob_interactions.dm b/mods/content/psionics/system/psionics/mob/mob_interactions.dm index 94806af788ff..366adb308823 100644 --- a/mods/content/psionics/system/psionics/mob/mob_interactions.dm +++ b/mods/content/psionics/system/psionics/mob/mob_interactions.dm @@ -25,18 +25,18 @@ return TRUE /datum/ability_handler/psionics/do_grabbed_invocation(mob/user, atom/target) - INVOKE_PSI_POWERS(user, get_grab_powers(SSpsi.faculties_by_intent[user.get_intent()]), target) + INVOKE_PSI_POWERS(user, get_grab_powers(SSpsi.get_faculty_by_intent(user.get_intent())), target) /datum/ability_handler/psionics/can_do_melee_invocation(mob/user, atom/target) return TRUE /datum/ability_handler/psionics/do_melee_invocation(mob/user, atom/target) - INVOKE_PSI_POWERS(user, get_melee_powers(SSpsi.faculties_by_intent[user.get_intent()]), target) + INVOKE_PSI_POWERS(user, get_melee_powers(SSpsi.get_faculty_by_intent(user.get_intent())), target) /datum/ability_handler/psionics/can_do_ranged_invocation(mob/user, atom/target) return TRUE /datum/ability_handler/psionics/do_ranged_invocation(mob/user, atom/target) - INVOKE_PSI_POWERS(user, get_ranged_powers(SSpsi.faculties_by_intent[user.get_intent()]), target) + INVOKE_PSI_POWERS(user, get_ranged_powers(SSpsi.get_faculty_by_intent(user.get_intent())), target) #undef INVOKE_PSI_POWERS \ No newline at end of file diff --git a/mods/content/psionics/system/subsystem_psi.dm b/mods/content/psionics/system/subsystem_psi.dm index 806f911873b5..e6621835ce9d 100644 --- a/mods/content/psionics/system/subsystem_psi.dm +++ b/mods/content/psionics/system/subsystem_psi.dm @@ -5,13 +5,27 @@ PROCESSING_SUBSYSTEM_DEF(psi) priority = SS_PRIORITY_PSYCHICS flags = SS_POST_FIRE_TIMING | SS_BACKGROUND - var/list/faculties_by_id = list() - var/list/faculties_by_name = list() - var/list/all_aura_images = list() - var/list/psi_dampeners = list() - var/list/psi_monitors = list() + var/list/faculties_by_id = list() + var/list/faculties_by_name = list() + var/list/all_aura_images = list() + var/list/psi_dampeners = list() + var/list/psi_monitors = list() var/list/armour_faculty_by_type = list() - var/list/faculties_by_intent = list() + var/list/faculties_by_intent = new(I_FLAG_ALL) + +/datum/controller/subsystem/processing/psi/proc/get_faculty_by_intent(decl/intent/intent) + var/static/list/intent_flags = list( + I_FLAG_HELP, + I_FLAG_DISARM, + I_FLAG_GRAB, + I_FLAG_HARM + ) + . = faculties_by_intent[intent.intent_flags] + if(!.) + for(var/flag in intent_flags) + if(flag & intent.intent_flags) + . = faculties_by_intent[flag] + faculties_by_intent[intent.intent_flags] = . /datum/controller/subsystem/processing/psi/proc/get_faculty(var/faculty) return faculties_by_name[faculty] || faculties_by_id[faculty] @@ -24,7 +38,7 @@ PROCESSING_SUBSYSTEM_DEF(psi) var/decl/psionic_faculty/faculty = faculties[ftype] faculties_by_id[faculty.id] = faculty faculties_by_name[faculty.name] = faculty - faculties_by_intent[faculty.associated_intent] = faculty.id + faculties_by_intent[faculty.associated_intent_flag] = faculty.id var/list/powers = decls_repository.get_decls_of_subtype(/decl/psionic_power) for(var/ptype in powers) diff --git a/mods/content/xenobiology/slime/slime_AI.dm b/mods/content/xenobiology/slime/slime_AI.dm index 3cade1ddc496..7260e124070c 100644 --- a/mods/content/xenobiology/slime/slime_AI.dm +++ b/mods/content/xenobiology/slime/slime_AI.dm @@ -37,13 +37,13 @@ /datum/mob_controller/slime/proc/update_mood() if(!slime || !body) return - body.set_intent(I_HELP) + body.set_intent(I_FLAG_HELP) var/new_mood if(HAS_STATUS(body, STAT_CONFUSE)) new_mood = "pout" else if(rabid || attacked) new_mood = "angry" - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) else if(current_target) new_mood = "mischevous" @@ -146,19 +146,19 @@ if(prob(1)) for(var/mob/living/slime/frenemy in range(1, body)) if(frenemy != body && body.Adjacent(frenemy)) - body.set_intent((frenemy.slime_type == slime.slime_type) ? I_HELP : I_HARM) + body.set_intent((frenemy.slime_type == slime.slime_type) ? I_FLAG_HELP : I_FLAG_HARM) body.UnarmedAttack(frenemy, TRUE) added_delay = 10 else if(slime.Adjacent(current_target)) var/do_attack = FALSE if(issilicon(current_target)) - body.set_intent(I_HARM) + body.set_intent(I_FLAG_HARM) do_attack = TRUE else if(current_target.client && !current_target.current_posture.prone && prob(60 + slime.powerlevel * 4)) - body.set_intent(I_DISARM) + body.set_intent(I_FLAG_DISARM) do_attack = TRUE else if(slime.check_valid_feed_target(current_target) == FEED_RESULT_VALID) - body.set_intent(I_GRAB) + body.set_intent(I_FLAG_GRAB) do_attack = TRUE if(do_attack) body.UnarmedAttack(current_target, TRUE) diff --git a/mods/gamemodes/deity/mobs/deity_phenomena.dm b/mods/gamemodes/deity/mobs/deity_phenomena.dm index eef27a6bdb60..a5abe64946a2 100644 --- a/mods/gamemodes/deity/mobs/deity_phenomena.dm +++ b/mods/gamemodes/deity/mobs/deity_phenomena.dm @@ -9,11 +9,11 @@ . = ..() for(var/decl/intent/intent as anything in decls_repository.get_decls_of_type_unassociated(/decl/intent)) //Just in case we somehow remove/add a new intent #futureproofing populate_intent(intent) - set_phenomenon(add_phenomenon(/datum/phenomenon/communicate), GET_DECL(I_HELP), "shift") - set_phenomenon(add_phenomenon(/datum/phenomenon/punish), GET_DECL(I_HELP), "control") - set_phenomenon(add_phenomenon(/datum/phenomenon/point), GET_DECL(I_HELP), "controlshift") - set_phenomenon(add_phenomenon(/datum/phenomenon/conversion), GET_DECL(I_GRAB), "shift") - set_phenomenon(add_phenomenon(/datum/phenomenon/forced_conversion), GET_DECL(I_GRAB), "control") + set_phenomenon(add_phenomenon(/datum/phenomenon/communicate), GET_DECL(/decl/intent/disarm), "shift") + set_phenomenon(add_phenomenon(/datum/phenomenon/punish), GET_DECL(/decl/intent/help), "control") + set_phenomenon(add_phenomenon(/datum/phenomenon/point), GET_DECL(/decl/intent/help), "controlshift") + set_phenomenon(add_phenomenon(/datum/phenomenon/conversion), GET_DECL(/decl/intent/grab), "shift") + set_phenomenon(add_phenomenon(/datum/phenomenon/forced_conversion), GET_DECL(/decl/intent/grab), "control") /mob/living/deity/proc/silence(amount) if(!silenced) diff --git a/mods/mobs/dionaea/mob/nymph_ui.dm b/mods/mobs/dionaea/mob/nymph_ui.dm index 0930bb184cdd..f970c5978862 100644 --- a/mods/mobs/dionaea/mob/nymph_ui.dm +++ b/mods/mobs/dionaea/mob/nymph_ui.dm @@ -4,9 +4,11 @@ /decl/intent/harm/binary/diona icon = 'mods/mobs/dionaea/icons/ui_intent_overlay.dmi' + uid = "intent_harm_binary_diona" /decl/intent/help/binary/diona icon = 'mods/mobs/dionaea/icons/ui_intent_overlay.dmi' + uid = "intent_help_binary_diona" /decl/ui_style/diona name = "Diona" diff --git a/mods/species/ascent/mobs/nymph/nymph_ui.dm b/mods/species/ascent/mobs/nymph/nymph_ui.dm index a22964614730..9f1430297d46 100644 --- a/mods/species/ascent/mobs/nymph/nymph_ui.dm +++ b/mods/species/ascent/mobs/nymph/nymph_ui.dm @@ -4,9 +4,11 @@ /decl/intent/harm/binary/ascent icon = 'mods/species/ascent/icons/ui_intent_overlay.dmi' + uid = "intent_harm_binary_ascent" /decl/intent/help/binary/ascent icon = 'mods/species/ascent/icons/ui_intent_overlay.dmi' + uid = "intent_help_binary_ascent" /obj/screen/ascent_nymph_molt name = "molt" From 7348e0aec044132c92e38b2a046743742d3c217a Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 28 Nov 2024 16:54:10 +1100 Subject: [PATCH 0037/1331] Commenting out nymph intent selection/subtypes due to issues with interactions. --- code/modules/banners/sign.dm | 2 +- mods/mobs/dionaea/mob/_nymph.dm | 2 ++ mods/mobs/dionaea/mob/nymph_ui.dm | 6 ++++-- mods/species/ascent/mobs/nymph/_nymph.dm | 2 ++ mods/species/ascent/mobs/nymph/nymph_ui.dm | 5 +++-- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/code/modules/banners/sign.dm b/code/modules/banners/sign.dm index cfc15e3310c2..f7babc034d0f 100644 --- a/code/modules/banners/sign.dm +++ b/code/modules/banners/sign.dm @@ -9,7 +9,7 @@ colourise_decal = FALSE /obj/item/banner/sign/attackby(obj/item/used_item, mob/user) - if(IS_KNIFE(used_item) && user.a_intent == I_HELP) + if(IS_KNIFE(used_item) && user.check_intent(I_FLAG_HELP)) var/available_decals = get_available_decals() if(!length(available_decals) || length(decals)) to_chat(user, SPAN_WARNING("\The [src] is already as decorated as it can be.")) diff --git a/mods/mobs/dionaea/mob/_nymph.dm b/mods/mobs/dionaea/mob/_nymph.dm index 4e13e530ef9a..6ec480cda3be 100644 --- a/mods/mobs/dionaea/mob/_nymph.dm +++ b/mods/mobs/dionaea/mob/_nymph.dm @@ -76,6 +76,7 @@ heal_damage(OXY, rads, do_update_health = FALSE) heal_damage(TOX, rads) +/* /mob/living/simple_animal/alien/diona/get_default_intent() return GET_DECL(/decl/intent/help/binary/diona) @@ -85,6 +86,7 @@ GET_DECL(/decl/intent/help/binary/diona) ) return available_intents +*/ /decl/bodytype/diona name = "nymph" diff --git a/mods/mobs/dionaea/mob/nymph_ui.dm b/mods/mobs/dionaea/mob/nymph_ui.dm index f970c5978862..cdd3a3f8c6da 100644 --- a/mods/mobs/dionaea/mob/nymph_ui.dm +++ b/mods/mobs/dionaea/mob/nymph_ui.dm @@ -1,3 +1,4 @@ +/* Commented out due to issues with interactions and combined intent flags. /obj/screen/intent/binary/diona icon = 'mods/mobs/dionaea/icons/ui_intents.dmi' screen_loc = DIONA_SCREEN_LOC_INTENT @@ -9,6 +10,7 @@ /decl/intent/help/binary/diona icon = 'mods/mobs/dionaea/icons/ui_intent_overlay.dmi' uid = "intent_help_binary_diona" +*/ /decl/ui_style/diona name = "Diona" @@ -35,8 +37,8 @@ var/ui_color = get_ui_color() var/ui_alpha = get_ui_alpha() - action_intent = new /obj/screen/intent/binary/diona(null, mymob) - mymob.healths = new /obj/screen/diona_health( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) + action_intent = new /obj/screen/intent( null, mymob) // Swap to /obj/screen/intent/binary/diona when interaction code supports it. + mymob.healths = new /obj/screen/diona_health( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) src.other = list() src.adding = list(mymob.healths, action_intent) ..() diff --git a/mods/species/ascent/mobs/nymph/_nymph.dm b/mods/species/ascent/mobs/nymph/_nymph.dm index 2c0a4f3646b2..16447b0c51dc 100644 --- a/mods/species/ascent/mobs/nymph/_nymph.dm +++ b/mods/species/ascent/mobs/nymph/_nymph.dm @@ -52,6 +52,7 @@ /mob/living/simple_animal/alien/kharmaan/get_dexterity(var/silent) return (DEXTERITY_EQUIP_ITEM) +/* /mob/living/simple_animal/alien/kharmaan/get_default_intent() return GET_DECL(/decl/intent/help/binary/ascent) @@ -61,3 +62,4 @@ GET_DECL(/decl/intent/help/binary/ascent) ) return available_intents +*/ \ No newline at end of file diff --git a/mods/species/ascent/mobs/nymph/nymph_ui.dm b/mods/species/ascent/mobs/nymph/nymph_ui.dm index 9f1430297d46..c100c95b4cd2 100644 --- a/mods/species/ascent/mobs/nymph/nymph_ui.dm +++ b/mods/species/ascent/mobs/nymph/nymph_ui.dm @@ -1,3 +1,4 @@ +/* Commented out due to issues with interactions and combined intent flags. /obj/screen/intent/binary/ascent icon = 'mods/species/ascent/icons/ui_intents.dmi' screen_loc = ANYMPH_SCREEN_LOC_INTENT @@ -9,7 +10,7 @@ /decl/intent/help/binary/ascent icon = 'mods/species/ascent/icons/ui_intent_overlay.dmi' uid = "intent_help_binary_ascent" - +*/ /obj/screen/ascent_nymph_molt name = "molt" icon = 'mods/species/ascent/icons/ui_molt.dmi' @@ -53,7 +54,7 @@ molt = new( null, mymob, ui_style, ui_color, ui_alpha) food = new /obj/screen/food( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_NUTRITION) drink = new /obj/screen/drink( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HYDRATION) - action_intent = new /obj/screen/intent/binary/ascent(null) + action_intent = new /obj/screen/intent( null) // Swap to /obj/screen/intent/binary/ascent when interaction code supports it. mymob.healths = new /obj/screen/ascent_nymph_health( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) src.other = list() src.adding = list(mymob.healths, molt, food, drink, action_intent) From 85b9c56a37464cbf9900889b630dc00697ad6dc8 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 6 Dec 2024 20:25:45 +1100 Subject: [PATCH 0038/1331] Automatic changelog generation for PR #4583 [ci skip] --- html/changelogs/AutoChangeLog-pr-4583.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4583.yml diff --git a/html/changelogs/AutoChangeLog-pr-4583.yml b/html/changelogs/AutoChangeLog-pr-4583.yml new file mode 100644 index 000000000000..9c592814efdd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4583.yml @@ -0,0 +1,5 @@ +author: MistakeNot4892 +changes: + - {tweak: Most wooden floors and tables on space maps are now chipboard laminate + instead.} +delete-after: true From c23b2c84997fb5e0d1778dd81c1babc205b61c16 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 7 Dec 2024 00:53:42 +0000 Subject: [PATCH 0039/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 12 ++++++------ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-4583.yml | 5 ----- 3 files changed, 10 insertions(+), 11 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4583.yml diff --git a/html/changelog.html b/html/changelog.html index 526d388b528b..7aab615f9423 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    07 December 2024

    +

    MistakeNot4892 updated:

    +
      +
    • Most wooden floors and tables on space maps are now chipboard laminate instead.
    • +
    +

    05 December 2024

    ophelia updated:

      @@ -106,12 +112,6 @@

      Sutures updated:

    • Added a wood plank texture to wood tables, used if the fantasy modpack is included.
    • Fixed wall banners not starting with banners.
    - -

    05 October 2024

    -

    MistakeNot4892 updated:

    -
      -
    • Sledgehammers can demolish non-reinforced walls.
    • -
    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 6bcd5bb49501..3906fe0882f2 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14929,3 +14929,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: You can now put any disk into the research design database or research design console, but only the correct disk type (tech disk or design disk) will function. +2024-12-07: + MistakeNot4892: + - tweak: Most wooden floors and tables on space maps are now chipboard laminate + instead. diff --git a/html/changelogs/AutoChangeLog-pr-4583.yml b/html/changelogs/AutoChangeLog-pr-4583.yml deleted file mode 100644 index 9c592814efdd..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4583.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: MistakeNot4892 -changes: - - {tweak: Most wooden floors and tables on space maps are now chipboard laminate - instead.} -delete-after: true From da85b88111ad77a74c992bcaa26fb0e691ec95f3 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 7 Dec 2024 12:19:21 +1100 Subject: [PATCH 0040/1331] Adding a large cauldron that cannot be picked up. --- code/modules/food/cooking/cooking_vessels/pot.dm | 13 +++++++++++++ icons/obj/food/cooking_vessels/cauldron.dmi | Bin 0 -> 1078 bytes 2 files changed, 13 insertions(+) create mode 100644 icons/obj/food/cooking_vessels/cauldron.dmi diff --git a/code/modules/food/cooking/cooking_vessels/pot.dm b/code/modules/food/cooking/cooking_vessels/pot.dm index 93c6a4585eb3..9465db82d300 100644 --- a/code/modules/food/cooking/cooking_vessels/pot.dm +++ b/code/modules/food/cooking/cooking_vessels/pot.dm @@ -40,3 +40,16 @@ if(. == PROCESS_KILL) last_boil_temp = null + +/obj/item/chems/cooking_vessel/cauldron + name = "cauldron" + desc = "A large round-bodied vessel for making large quantities of potion or soup." + material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color + icon = 'icons/obj/food/cooking_vessels/cauldron.dmi' + volume = 1000 + w_class = ITEM_SIZE_STRUCTURE + density = TRUE + +/obj/item/chems/cooking_vessel/cauldron/can_be_picked_up(mob/user) + return FALSE diff --git a/icons/obj/food/cooking_vessels/cauldron.dmi b/icons/obj/food/cooking_vessels/cauldron.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7be32a10bde46e4549464f184f1c1bf40bc7b108 GIT binary patch literal 1078 zcmV-61j+k}P)005u_0{{R3dEt5<0000jP)t-sz`(%5 z!O8#s|4Bz&Wny;t`1p#7o}!|;RZ?j3^z-=p^x);`+S}g%|1-eAz%rM_zW@LL0d!JM zQvg8b*k%9#0O@*ESad{Xb7OL8aCB*JZU6vyoSl=w4uUWYhR^IN7Q7mRN{kmT24gtr zE0Dq}ENskdL*v_9;G|Il9+v!F`Za&sZN~S9V|wPvDP=dbPMU`Ac)W;MaF@bY6s%V) zs8E)b9|GsWBgyb?tP&v$gtU(0B?~SVG!FVTE3*hCHa^S2;U7qr8>wVdenvNRiwYhj zqs4aq;ry&!RBcl~{&6*szs<#8&Apd)roCnw!RMfq8|kNqM60izgGo?tei}i|>#=i^ z1y0&_mtVBjj$MSn!dUS1V5t+D|42-OtG}hr_~D%NU*QSE-SHiYfi-ae00P2EL_t(& zf$f>kZrVT)#!Zbul^V$|?3!a~UZ9CB&L9)3N9N$DM^9+N}4bK%_g=qvQldoF!} zyi3MJz^o~MtZpyO{3Im4+40ACv8_+wdCcqK{m0O$Uq4B%J~;KuvdpDpCq)u>C*3Fq z#1Tma0IZIB`c0bV`8d7a76MTeh482r6H=(C z%AIZ|-5eQr(oLl;keHm&H`EV1ePZ3u`q~(+F0*av0l6oeGLPeUO7!t{KTrC`sLMe{ zI_C6v&s!|+Eq!!7Gb-svmse&s^qi26RD^d*q{g2d85$WOQfgO*xl(ATjH?MVZDfgd zwTFI=BPwO`>~!yWhACc#(rBGHGQ1w(ePZx+?Pd@CZbz5*@_5$mI58Xy$BD*Ud6o`y z7bE(*d^{ldXlTw{7?xA{*~^o`)z^X-ch}KHy}@^Jvj5i5-R?0Ab2fGJV0%8FAJc!z ze?=w#(LC5H!_wlgUgy2yaNgU?i?NWR7l#LR{X7UuP;gk+&x1ntWOzi^&x5Bl!!QiP zFwFV177Loj_VQkJ{SN#P+tVidp)(Z@}E#@Np%1d5!mwu*2H(K$k=&L3aam9&(b00V`_l&g?2uy_4{Cn zea1Mn$1n`TFwB2UaJq#cO=K6E%e|gD|4%D`VoD32BfWS0QNy?2scMyfA1mr7Z{%ud z9}?JxVbhX79;q@0L=o-cR$~boOl2aN67y-JvZ;?hscHb+(%(!29Poxtysi;L4_aQ+ z`oMR;tZspkmafBBsO}*cTMv(aN29L2F7v6~(5ei~FbwnRc`A8%d$D`VhHS`&Y*_NU w>ixHdYnuy~hEE$mXNFtYkPX?84PU9@GbAMo@~I=ung9R*07*qoM6N<$f>5askpKVy literal 0 HcmV?d00001 From 7e9b86efe877a4ce8005f0adc2c9c131f156cffe Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 7 Dec 2024 12:55:57 +1100 Subject: [PATCH 0041/1331] Adding a candelabra. --- code/game/objects/items/candelabra.dm | 54 ++++++++++++++++++ code/game/objects/items/flame/_flame.dm | 4 +- code/game/objects/items/flame/flame_candle.dm | 10 +++- icons/obj/items/candelabra.dmi | Bin 0 -> 355 bytes icons/obj/items/flame/candle.dmi | Bin 1059 -> 1057 bytes nebula.dme | 1 + 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 code/game/objects/items/candelabra.dm create mode 100644 icons/obj/items/candelabra.dmi diff --git a/code/game/objects/items/candelabra.dm b/code/game/objects/items/candelabra.dm new file mode 100644 index 000000000000..1f82ec443aa7 --- /dev/null +++ b/code/game/objects/items/candelabra.dm @@ -0,0 +1,54 @@ +/datum/storage/candelabra + can_hold = list(/obj/item/flame/candle) + storage_slots = 3 + var/static/list/candle_offsets = list( + list("x" = -4, "y" = 17), + list("x" = 1, "y" = 19), + list("x" = 6, "y" = 17) + ) + +/datum/storage/candelabra/can_be_inserted(obj/item/W, mob/user, stop_messages, click_params) + . = ..() && holder && length(holder.get_stored_inventory()) < length(candle_offsets) + +/obj/item/candelabra + name = "candelabra" + desc = "A three-tined candle stand." + icon = 'icons/obj/items/candelabra.dmi' + icon_state = ICON_STATE_WORLD + storage = /datum/storage/candelabra + material = /decl/material/solid/metal/brass + material_alteration = MAT_FLAG_ALTERATION_ALL + +/obj/item/candelabra/attackby(obj/item/used_item, mob/user) + if(!user.check_intent(I_FLAG_HARM) && (used_item.isflamesource() || used_item.get_heat() > T100C)) + for(var/obj/item/flame/candle/candle in contents) + if(!candle.lit && candle.attackby(used_item, user)) + return TRUE + . = ..() + +/obj/item/candelabra/filled/Initialize(ml, material_key) + new /obj/item/flame/candle/random(src) + new /obj/item/flame/candle/random(src) + new /obj/item/flame/candle/random(src) + . = ..() + update_icon() + +// Workaround for vis_contents propagating color. +/obj/item/candelabra/on_update_icon() + ..() + var/datum/storage/candelabra/candles_storage = storage + if(istype(candles_storage)) + var/i = 1 + for(var/obj/item/flame/candle/candle in get_stored_inventory()) + var/offsets = candles_storage.candle_offsets[i] + candle.set_dir(SOUTH) + for(var/image/candle_overlay in candle.get_sconce_overlay()) + candle_overlay.pixel_x = offsets["x"] + candle_overlay.pixel_y = offsets["y"] + candle_overlay.dir = SOUTH + candle_overlay.appearance_flags |= RESET_COLOR|RESET_ALPHA + add_overlay(candle_overlay) + i++ + if(i > length(candles_storage.candle_offsets)) + break + compile_overlays() diff --git a/code/game/objects/items/flame/_flame.dm b/code/game/objects/items/flame/_flame.dm index 52c0cb31cc7b..1078285da3b8 100644 --- a/code/game/objects/items/flame/_flame.dm +++ b/code/game/objects/items/flame/_flame.dm @@ -205,9 +205,9 @@ if(!other.can_manually_light) other.light(user) -/obj/item/flame/attackby(obj/item/W, mob/user) +/obj/item/flame/attackby(obj/item/used_item, mob/user) - if(!user.check_intent(I_FLAG_HARM) && !can_manually_light && (W.isflamesource() || W.get_heat() > T100C)) + if(!user.check_intent(I_FLAG_HARM) && !can_manually_light && (used_item.isflamesource() || used_item.get_heat() > T100C)) light(user) return TRUE diff --git a/code/game/objects/items/flame/flame_candle.dm b/code/game/objects/items/flame/flame_candle.dm index dcf647104f65..906824d52bcd 100644 --- a/code/game/objects/items/flame/flame_candle.dm +++ b/code/game/objects/items/flame/flame_candle.dm @@ -41,7 +41,7 @@ /obj/item/flame/candle/get_sconce_overlay() . = list(overlay_image(icon, "[icon_state]-sconce", color = color, flags = RESET_COLOR)) if(lit) - . += overlay_image(icon, "[icon_state]-sconce-lit", color = color, flags = RESET_COLOR) + . += overlay_image(icon, "[icon_state]-sconce-lit", color = COLOR_WHITE, flags = RESET_COLOR) /obj/item/flame/candle/on_update_icon() @@ -63,6 +63,9 @@ // TODO: emissives add_overlay(overlay_image(icon, "[icon_state]_lit", flags = RESET_COLOR)) + if(istype(loc, /obj/item/candelabra)) + loc.queue_icon_update() + /obj/item/flame/candle/proc/get_available_colors() return null @@ -83,6 +86,11 @@ /obj/item/flame/candle/get_available_scents() return null +/obj/item/flame/candle/set_dir(ndir) + if(istype(loc, /obj/item/candelabra)) + ndir = SOUTH + . = ..() + /obj/item/flame/candle/scented name = "scented candle" desc = "A candle which releases pleasant-smelling oils into the air when burned." diff --git a/icons/obj/items/candelabra.dmi b/icons/obj/items/candelabra.dmi new file mode 100644 index 0000000000000000000000000000000000000000..3d0b0010945249def7f6a4317e5a1437c54b48a5 GIT binary patch literal 355 zcmV-p0i6DcP)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+DJL;6TZxM^C9|j)$T#HTOe;#vO@*-Gs><_=a#C=qQdV&Fa{*fp02BWt+1k96 zrT_o{fk{L`R5*?8kFgSiAP__kA-#v#2N?S(rq2wf*Ma~47U`Yad_de{n|*V8OZ3-L z`~$F|M=|%FpDZIP_{Gk_i$KR#$7Uj+eKf9_{KN7xu9*zz!&Ft|oXp$RaRmV7RMH0l zRE8bqk2p*a_}VbSiuK?MR<@nnmSF{@3=iD8=mcK#3vrzggcm8Cd}N_w0b_bg*^j9?4d`GrrkXH JX0b9OGXOxC7Ayb& delta 55 zcmZ3;v6y2*BIC}9Nwd^hmd`dgJVoVokH*$>Gk}!l)^jZlrKh;o)#*KmVPx>!_4wzj Lz{lo`6&aZU!!sF% diff --git a/nebula.dme b/nebula.dme index 44929d112015..340b6ddc06f4 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1086,6 +1086,7 @@ #include "code\game\objects\items\blueprints.dm" #include "code\game\objects\items\bodybag.dm" #include "code\game\objects\items\buttons.dm" +#include "code\game\objects\items\candelabra.dm" #include "code\game\objects\items\christmas.dm" #include "code\game\objects\items\contraband.dm" #include "code\game\objects\items\crutches.dm" From dc462371a9431bf80853a15dcb7be443a702ecef Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 7 Dec 2024 14:24:29 +1100 Subject: [PATCH 0042/1331] Adding a small circular rug. --- code/game/objects/structures/rug.dm | 12 ++++++++++++ .../crafting/stack_recipes/recipes_grass.dm | 6 ++++++ .../crafting/stack_recipes/recipes_textiles.dm | 16 +++++++++++----- icons/obj/structures/rug.dmi | Bin 0 -> 352 bytes nebula.dme | 1 + 5 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 code/game/objects/structures/rug.dm create mode 100644 icons/obj/structures/rug.dmi diff --git a/code/game/objects/structures/rug.dm b/code/game/objects/structures/rug.dm new file mode 100644 index 000000000000..b4bf628184f6 --- /dev/null +++ b/code/game/objects/structures/rug.dm @@ -0,0 +1,12 @@ +/obj/structure/rug + name = "rug" + desc = "A small, circular floor covering." + icon = 'icons/obj/structures/rug.dmi' + material = /decl/material/solid/organic/cloth/wool + icon_state = ICON_STATE_WORLD + paint_color = COLOR_GRAY20 + color = COLOR_GRAY20 + +/obj/structure/rug/crafted + paint_color = null + color = null diff --git a/code/modules/crafting/stack_recipes/recipes_grass.dm b/code/modules/crafting/stack_recipes/recipes_grass.dm index d6f262e90bc2..a492af45bb25 100644 --- a/code/modules/crafting/stack_recipes/recipes_grass.dm +++ b/code/modules/crafting/stack_recipes/recipes_grass.dm @@ -8,6 +8,12 @@ return FALSE return ..() +/decl/stack_recipe/woven/rug + result_type = /obj/structure/rug/crafted + one_per_turf = TRUE + on_floor = TRUE + category = "furniture" + /decl/stack_recipe/woven/basket result_type = /obj/item/basket diff --git a/code/modules/crafting/stack_recipes/recipes_textiles.dm b/code/modules/crafting/stack_recipes/recipes_textiles.dm index c43dffe7ce14..b9dba122da80 100644 --- a/code/modules/crafting/stack_recipes/recipes_textiles.dm +++ b/code/modules/crafting/stack_recipes/recipes_textiles.dm @@ -7,9 +7,15 @@ crafting_extra_cost_factor = 1.5 // measure twice, cut once; material is lost. todo: produce scraps? abstract_type = /decl/stack_recipe/textiles +/decl/stack_recipe/textiles/rug + result_type = /obj/structure/rug/crafted + one_per_turf = TRUE + on_floor = TRUE + category = "furniture" + /decl/stack_recipe/textiles/cloak - result_type = /obj/item/clothing/suit/cloak/hide - category = "clothing" + result_type = /obj/item/clothing/suit/cloak/hide + category = "clothing" /decl/stack_recipe/textiles/banner name = "banner" @@ -60,9 +66,9 @@ category = "clothing" /decl/stack_recipe/textiles/leather/bedroll - result_type = /obj/item/bedroll - difficulty = MAT_VALUE_NORMAL_DIY // Slightly easier than making clothing. - category = "bedding" + result_type = /obj/item/bedroll + difficulty = MAT_VALUE_NORMAL_DIY // Slightly easier than making clothing. + category = "bedding" /decl/stack_recipe/textiles/leather/shoes result_type = /obj/item/clothing/shoes/craftable diff --git a/icons/obj/structures/rug.dmi b/icons/obj/structures/rug.dmi new file mode 100644 index 0000000000000000000000000000000000000000..ab64dd8fba6a20b5be1a4790a19316ab78a05240 GIT binary patch literal 352 zcmV-m0iXVfP)i?^ zC?`dUi!&v&s2IpMx4!`(Mp%u<@hb1| ziE$6wxG(dRwefEeN!rj_N|8+k`2{#GMH#LjA{pM0HNl&p-@GxLKvg-LjT&{OB0T@a y+|G|#&wU0%7R^J|2DT6-3n*d%d*ptEc9sjA<$8$FksV_I0000 Date: Sat, 7 Dec 2024 15:11:48 +1100 Subject: [PATCH 0043/1331] Added several wider pillars. --- code/game/objects/structures/pillar.dm | 11 +++++++++++ .../crafting/stack_recipes/recipes_bricks.dm | 13 +++++++++++++ .../obj/structures/pillars/pillar_wide_inset.dmi | Bin 0 -> 387 bytes .../obj/structures/pillars/pillar_wide_round.dmi | Bin 0 -> 390 bytes .../obj/structures/pillars/pillar_wide_square.dmi | Bin 0 -> 347 bytes 5 files changed, 24 insertions(+) create mode 100644 icons/obj/structures/pillars/pillar_wide_inset.dmi create mode 100644 icons/obj/structures/pillars/pillar_wide_round.dmi create mode 100644 icons/obj/structures/pillars/pillar_wide_square.dmi diff --git a/code/game/objects/structures/pillar.dm b/code/game/objects/structures/pillar.dm index 6f625ac789b5..6c77b5c53792 100644 --- a/code/game/objects/structures/pillar.dm +++ b/code/game/objects/structures/pillar.dm @@ -17,3 +17,14 @@ /obj/structure/pillar/triad icon = 'icons/obj/structures/pillars/pillar_triad.dmi' + +/obj/structure/pillar/wide + name = "wide pillar" + w_class = ITEM_SIZE_LARGE_STRUCTURE + icon = 'icons/obj/structures/pillars/pillar_wide_round.dmi' + +/obj/structure/pillar/wide/square + icon = 'icons/obj/structures/pillars/pillar_wide_square.dmi' + +/obj/structure/pillar/wide/inset + icon = 'icons/obj/structures/pillars/pillar_wide_inset.dmi' diff --git a/code/modules/crafting/stack_recipes/recipes_bricks.dm b/code/modules/crafting/stack_recipes/recipes_bricks.dm index 1901075e8e0d..ec8bf11e3b3b 100644 --- a/code/modules/crafting/stack_recipes/recipes_bricks.dm +++ b/code/modules/crafting/stack_recipes/recipes_bricks.dm @@ -71,6 +71,19 @@ name = "pillar, round" result_type = /obj/structure/pillar/round +/decl/stack_recipe/bricks/furniture/pillar/wide_round + name = "pillar, wide round" + result_type = /obj/structure/pillar/wide + +/decl/stack_recipe/bricks/furniture/pillar/wide_square + name = "pillar, wide square" + + result_type = /obj/structure/pillar/wide/square + +/decl/stack_recipe/bricks/furniture/pillar/wide_inset + name = "pillar, wide inset" + result_type = /obj/structure/pillar/wide/inset + /decl/stack_recipe/bricks/furniture/pillar/pedestal name = "pedestal, square" result_type = /obj/structure/pedestal diff --git a/icons/obj/structures/pillars/pillar_wide_inset.dmi b/icons/obj/structures/pillars/pillar_wide_inset.dmi new file mode 100644 index 0000000000000000000000000000000000000000..8f307cd2eb0b4e4ad7be6734a1be1a50b223b3f8 GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{S0LThHSf^Dlb-G|*RJ0B z|NnouyYT^_C}T;GUoeBivm0qZPG(g|M2SmkadJ^+K}lwQ9s@(goZzs6qSEhQf(t%= zeWK;9t99jJaWn(XeIgchCnG~WBywYaBhPrEK zx3TKx6%r@RpFHx>_B!L+x?!ql%-@&;$CwzbnWT&vvb3K9EnVg5;uxYav2;Q&Z?l4c z+g7~`hxH0Lm?hK=jxy$y{14@-VV~2v?}&@t{l6a9?>e-apWCZz&~%VB(aFF{@XnO% zI4uKz$G3`(bAy_eHZ@2++ciDs+Hc>gJn5!_qCA7&$)e52S$?LSkxWf!Gioq02$r;4 z*c+d;{@B6vr1GRASuCkvEd(5EPL}V{C_3==`{%9v3qQ6V{L&f!_^`p6i3esxIhANe fo{X}%eD;RDZn5pj3W2krz-91s^>bP0l+XkKhH{;r literal 0 HcmV?d00001 diff --git a/icons/obj/structures/pillars/pillar_wide_round.dmi b/icons/obj/structures/pillars/pillar_wide_round.dmi new file mode 100644 index 0000000000000000000000000000000000000000..c0625980dd8753fcb66fba404b086387fe7e2d98 GIT binary patch literal 390 zcmV;10eSw3P)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@*+P6 zMdnPMSmN)Hwk%%;E4ZPh#_&{-;g}O*2Qda<$$1!5sGZ56wUmu!V=HC-yJ6MP*WPg% zI1~t6DG8#$R;hp&-z((2$QyKw9f5fnJ8X4r=^myEnvue&)brOD@*Q}*S_M^s5$AYM kNlYK9y!$Dim(^>20RPc0p&`v}N&o-=07*qoM6N<$f)Y=nn*aa+ literal 0 HcmV?d00001 diff --git a/icons/obj/structures/pillars/pillar_wide_square.dmi b/icons/obj/structures/pillars/pillar_wide_square.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a5ea0d80445b27b3f5b3ff849f3e779f4a47951b GIT binary patch literal 347 zcmV-h0i^zkP)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@*+P6ES+`vw;%^cz0PgpMPvYXlYju1a3TX=X%TnjS tNWv9JjOvVmQ*%WpVVf0|N?fV0eE@t%G>Z98YI6Vp002ovPDHLkV1jY6i< Date: Mon, 2 Dec 2024 18:54:24 +1100 Subject: [PATCH 0044/1331] Adding gemstones and basic gemcutting. --- code/game/turfs/flooring/_flooring.dm | 3 + code/game/turfs/flooring/flooring_grass.dm | 4 + code/game/turfs/floors/floor_digging.dm | 29 +++-- code/game/turfs/turf_digging.dm | 38 ++++-- code/game/turfs/walls/wall_natural.dm | 5 + code/modules/gemstones/_gemstone.dm | 89 ++++++++++++++ code/modules/gemstones/gemstone_cuts.dm | 75 ++++++++++++ code/modules/materials/_materials.dm | 5 + .../solids/materials_solid_gemstones.dm | 111 ++++++++++-------- .../solids/materials_solid_mineral.dm | 2 + .../solids/materials_solid_stone.dm | 2 + code/modules/mechs/equipment/utility.dm | 2 +- code/modules/mining/drilling/drill_act.dm | 2 +- icons/obj/items/gemstones/baguette.dmi | Bin 0 -> 423 bytes icons/obj/items/gemstones/hexagon.dmi | Bin 0 -> 419 bytes icons/obj/items/gemstones/octagon.dmi | Bin 0 -> 445 bytes icons/obj/items/gemstones/poor.dmi | Bin 0 -> 382 bytes icons/obj/items/gemstones/round.dmi | Bin 0 -> 401 bytes icons/obj/items/gemstones/uncut.dmi | Bin 0 -> 538 bytes mods/content/fantasy/datum/skills.dm | 5 +- nebula.dme | 2 + 21 files changed, 303 insertions(+), 71 deletions(-) create mode 100644 code/modules/gemstones/_gemstone.dm create mode 100644 code/modules/gemstones/gemstone_cuts.dm create mode 100644 icons/obj/items/gemstones/baguette.dmi create mode 100644 icons/obj/items/gemstones/hexagon.dmi create mode 100644 icons/obj/items/gemstones/octagon.dmi create mode 100644 icons/obj/items/gemstones/poor.dmi create mode 100644 icons/obj/items/gemstones/round.dmi create mode 100644 icons/obj/items/gemstones/uncut.dmi diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index 3fc9cf8adc2e..9d3ce2f33cfd 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -347,3 +347,6 @@ var/global/list/flooring_cache = list() /decl/flooring/proc/handle_environment_proc(turf/floor/target) return PROCESS_KILL + +/decl/flooring/proc/handle_turf_digging(turf/floor/target) + return TRUE diff --git a/code/game/turfs/flooring/flooring_grass.dm b/code/game/turfs/flooring/flooring_grass.dm index 802c509f5738..a9e70bbf7368 100644 --- a/code/game/turfs/flooring/flooring_grass.dm +++ b/code/game/turfs/flooring/flooring_grass.dm @@ -21,6 +21,10 @@ return TRUE return ..() +/decl/flooring/grass/handle_turf_digging(turf/floor/target) + target.set_flooring(null) + return FALSE + /decl/flooring/grass/wild name = "wild grass" icon = 'icons/turf/flooring/wildgrass.dmi' diff --git a/code/game/turfs/floors/floor_digging.dm b/code/game/turfs/floors/floor_digging.dm index 4262e953ad82..3351970e8af6 100644 --- a/code/game/turfs/floors/floor_digging.dm +++ b/code/game/turfs/floors/floor_digging.dm @@ -1,3 +1,6 @@ +/turf/floor + var/gemstone_dropped = FALSE + /turf/floor/proc/is_fundament() var/decl/flooring/flooring = get_topmost_flooring() return flooring ? !flooring.constructed : TRUE @@ -23,21 +26,31 @@ /turf/floor/can_dig_trench(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) return can_be_dug(tool_hardness, using_tool) && get_physical_height() > -(FLUID_DEEP) -/turf/floor/dig_trench(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) - if(!is_fundament()) +/turf/floor/dig_trench(mob/user, tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) + if(is_fundament()) + handle_trench_digging(user) + +/turf/floor/proc/handle_trench_digging(mob/user) + var/decl/flooring/flooring = get_topmost_flooring() + if(!flooring.handle_turf_digging(src)) return - var/new_height = max(get_physical_height()-TRENCH_DEPTH_PER_ACTION, -(FLUID_DEEP)) - var/height_diff = abs(get_physical_height()-new_height) // Only drop mats if we actually changed the turf height sufficiently. + var/old_height = get_physical_height() + var/new_height = max(old_height-TRENCH_DEPTH_PER_ACTION, -(FLUID_DEEP)) + var/height_diff = abs(old_height-new_height) if(height_diff >= TRENCH_DEPTH_PER_ACTION) - drop_diggable_resources() + drop_diggable_resources(user) set_physical_height(new_height) -/turf/floor/dig_pit(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) +/turf/floor/dig_pit(mob/user, tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) return has_flooring() ? null : ..() /turf/floor/get_diggable_resources() var/decl/material/my_material = get_material() if(is_fundament() && istype(my_material) && my_material.dug_drop_type && (get_physical_height() > -(FLUID_DEEP))) - return list(my_material.dug_drop_type = list(3, 2)) - return null + . = list() + .[my_material.dug_drop_type] = list("amount" = 3, "variance" = 2, "material" = my_material.type) + if(!gemstone_dropped && prob(my_material.gemstone_chance) && LAZYLEN(my_material.gemstone_types)) + gemstone_dropped = TRUE + var/gem_mat = pick(my_material.gemstone_types) + .[/obj/item/gemstone] = list("amount" = 1, "material" = gem_mat) diff --git a/code/game/turfs/turf_digging.dm b/code/game/turfs/turf_digging.dm index 6eea34afe2e0..5f832ae8045c 100644 --- a/code/game/turfs/turf_digging.dm +++ b/code/game/turfs/turf_digging.dm @@ -1,5 +1,5 @@ -/// Return an assoc list of resource item type to a base and a random component -/// ex. return list(/obj/item/stack/material/ore/handful/sand = list(3, 2)) +/// Return an assoc list of resource item type to a metadata list containing base amount, random component, and material override +/// ex. return list(/obj/item/stack/material/ore/handful/sand = list("amount" = 3, "variance" = 2, "material" = /decl/material/foo)) /turf/proc/get_diggable_resources() return null @@ -10,17 +10,31 @@ /turf/proc/can_be_dug(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) return FALSE -/turf/proc/drop_diggable_resources() +/turf/proc/drop_diggable_resources(mob/user) SHOULD_CALL_PARENT(TRUE) var/list/diggable_resources = get_diggable_resources() if(!length(diggable_resources)) return for(var/resource_type in diggable_resources) - var/list/resource_amounts = diggable_resources[resource_type] + + var/list/resource_data = diggable_resources[resource_type] + var/list/loot = list() + var/amount = max(1, resource_data["amount"] + resource_data["variance"]) + var/spawn_material = resource_data["material"] + if(ispath(resource_type, /obj/item/stack)) - LAZYADD(., new resource_type(src, resource_amounts[1] + rand(resource_amounts[2]), get_material_type())) + loot += new resource_type(src, amount, spawn_material) else - LAZYADD(., new resource_type(src, get_material_type())) + for(var/i = 1 to amount) + loot += new resource_type(src, spawn_material) + + if(length(loot)) + if(user) + for(var/obj/item/thing in loot) + if(thing.material && thing.material != get_material()) + to_chat(user, SPAN_NOTICE("You unearth \a [thing]!")) + LAZYADD(., loot) + clear_diggable_resources() // Procs for digging pits. @@ -29,10 +43,10 @@ /turf/proc/try_dig_pit(var/mob/user, var/obj/item/tool, using_tool = TOOL_SHOVEL) if((!user && !tool) || tool.do_tool_interaction(using_tool, user, src, 5 SECONDS, check_skill = SKILL_HAULING, set_cooldown = TRUE)) - return dig_pit(tool?.material?.hardness, using_tool) + return dig_pit(user, tool?.material?.hardness, using_tool) return null -/turf/proc/dig_pit(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) +/turf/proc/dig_pit(mob/user, tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) return can_dig_pit(tool_hardness, using_tool) && new /obj/structure/pit(src) // Procs for digging farms. @@ -44,10 +58,10 @@ if(!material?.tillable) return if((!user && !tool) || tool.do_tool_interaction(using_tool, user, src, 5 SECONDS, set_cooldown = TRUE, check_skill = SKILL_BOTANY)) - return dig_farm(tool?.material?.hardness, using_tool) + return dig_farm(user, tool?.material?.hardness, using_tool) return null -/turf/proc/dig_farm(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) +/turf/proc/dig_farm(mob/user, tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) return can_dig_farm(tool_hardness, using_tool) && new /obj/machinery/portable_atmospherics/hydroponics/soil(src) // Proc for digging trenches. @@ -56,8 +70,8 @@ /turf/proc/try_dig_trench(mob/user, obj/item/tool, using_tool = TOOL_SHOVEL) if((!user && !tool) || tool.do_tool_interaction(using_tool, user, src, 2.5 SECONDS, check_skill = SKILL_HAULING, set_cooldown = TRUE)) - return dig_trench(tool?.material?.hardness, using_tool) + return dig_trench(user, tool?.material?.hardness, using_tool) return null -/turf/proc/dig_trench(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) +/turf/proc/dig_trench(mob/user, tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) return FALSE diff --git a/code/game/turfs/walls/wall_natural.dm b/code/game/turfs/walls/wall_natural.dm index e878e59747e5..96967f55e2ef 100644 --- a/code/game/turfs/walls/wall_natural.dm +++ b/code/game/turfs/walls/wall_natural.dm @@ -10,6 +10,7 @@ var/image/ore_overlay var/static/list/exterior_wall_shine_cache = list() var/being_mined = FALSE + var/gem_dropped = FALSE /turf/wall/natural/flooded flooded = /decl/material/liquid/water @@ -150,6 +151,10 @@ if(prob(30) && !ramp_slope_direction && material) var/drop_type = material.ore_type || /obj/item/stack/material/ore pass_geodata_to(new drop_type(src, material.ore_result_amount, material.type)) + if(!gem_dropped && material && prob(material.gemstone_chance) && LAZYLEN(material.gemstone_types)) + gem_dropped = TRUE + new /obj/item/gemstone(get_turf(src), pickweight(material.gemstone_types)) + visible_message(SPAN_NOTICE("A glimmer of colour shines amongst the rubble...")) /turf/wall/natural/proc/pass_geodata_to(obj/O) var/datum/extension/geological_data/ours = get_extension(src, /datum/extension/geological_data) diff --git a/code/modules/gemstones/_gemstone.dm b/code/modules/gemstones/_gemstone.dm new file mode 100644 index 000000000000..087266d34f89 --- /dev/null +++ b/code/modules/gemstones/_gemstone.dm @@ -0,0 +1,89 @@ +var/global/list/_available_gemstone_cuts + +/proc/get_available_gemstone_cuts() + if(!global._available_gemstone_cuts) + global._available_gemstone_cuts = list() + for(var/decl/gemstone_cut/cut as anything in decls_repository.get_decls_of_type_unassociated(/decl/gemstone_cut)) + if(cut.can_be_cut) + global._available_gemstone_cuts += cut + return global._available_gemstone_cuts + +/obj/item/gemstone + name = "uncut gemstone" + desc = "A hunk of uncut gemstone." + icon = 'icons/obj/items/gemstones/uncut.dmi' + w_class = ITEM_SIZE_TINY + material = /decl/material/solid/gemstone/diamond + material_alteration = MAT_FLAG_ALTERATION_COLOR // Name and desc are handled manually. + var/decl/gemstone_cut/cut = /decl/gemstone_cut/uncut + var/work_skill = SKILL_CONSTRUCTION + +/obj/item/gemstone/Initialize(ml, material_key) + cut = GET_DECL(cut) + . = ..() + update_from_cut() + +/obj/item/gemstone/proc/update_from_cut() + icon = cut.icon + desc = cut.desc + update_name() + update_icon() + +/obj/item/gemstone/update_name() + SetName("[cut.adjective] [material.solid_name]") + +/obj/item/gemstone/get_single_monetary_worth() + . = ..() * cut.worth_multiplier + +/obj/item/gemstone/attackby(obj/item/used_item, mob/user) + if(IS_HAMMER(used_item) && !user.check_intent(I_FLAG_HARM)) // TOOL_CHISEL when? + if(!cut.can_attempt_cut) + to_chat(user, SPAN_WARNING("\The [src] has already been cut.")) + return TRUE + var/decl/gemstone_cut/desired_cut = input(user, "What cut would you like to attempt?", "Cut Gemstone") as null|anything in get_available_gemstone_cuts() + if(!desired_cut || QDELETED(src) || QDELETED(user) || !CanPhysicallyInteract(user) || !cut.can_attempt_cut) + return TRUE + user.visible_message(SPAN_NOTICE("\The [user] begins carefully cutting \the [src].")) + if(!user.do_skilled(10 SECONDS, work_skill, src, check_holding = TRUE) || !CanPhysicallyInteract(user)) + if(QDELETED(src) || !cut.can_attempt_cut || QDELETED(user)) + return TRUE + to_chat(user, SPAN_DANGER("You were interrupted, botching the cut!")) + cut = GET_DECL(/decl/gemstone_cut/poor) + else + if(QDELETED(src) || !cut.can_attempt_cut || QDELETED(user)) + return TRUE + user.visible_message(SPAN_NOTICE("\The [user] finishes cutting \the [src].")) + if(user.skill_fail_prob(work_skill, 100, SKILL_EXPERT)) + to_chat(user, SPAN_DANGER("You've done a really poor job...")) + cut = GET_DECL(/decl/gemstone_cut/poor) + else + cut = desired_cut + update_from_cut() + return TRUE + . = ..() + +// Subtypes for mapping/spawning etc. +/obj/item/gemstone/poor + name = "poorly-cut diamond" + cut = /decl/gemstone_cut/poor + icon = 'icons/obj/items/gemstones/poor.dmi' + +/obj/item/gemstone/baguette + name = "baguette-cut diamond" + cut = /decl/gemstone_cut/baguette + icon = 'icons/obj/items/gemstones/baguette.dmi' + +/obj/item/gemstone/hexagon + name = "hexagon-cut diamond" + cut = /decl/gemstone_cut/hexagon + icon = 'icons/obj/items/gemstones/hexagon.dmi' + +/obj/item/gemstone/octagon + name = "octagon-cut diamond" + cut = /decl/gemstone_cut/octagon + icon = 'icons/obj/items/gemstones/octagon.dmi' + +/obj/item/gemstone/round + name = "round-cut diamond" + cut = /decl/gemstone_cut/round + icon = 'icons/obj/items/gemstones/round.dmi' diff --git a/code/modules/gemstones/gemstone_cuts.dm b/code/modules/gemstones/gemstone_cuts.dm new file mode 100644 index 000000000000..86509cc2c177 --- /dev/null +++ b/code/modules/gemstones/gemstone_cuts.dm @@ -0,0 +1,75 @@ +/decl/gemstone_cut + abstract_type = /decl/gemstone_cut + var/worth_multiplier = 1.5 + var/name + var/desc + var/adjective + var/icon + // Can we cut this cut into a new cut? + var/can_attempt_cut = FALSE + // Can we attempt to cut to this cut? + var/can_be_cut = TRUE + +/decl/gemstone_cut/validate() + . = ..() + if(!istext(name)) + . += "invalid or null name" + if(!istext(desc)) + . += "invalid or null desc" + if(!istext(adjective)) + . += "invalid or null adjective" + if(icon) + if(!check_state_in_icon(ICON_STATE_WORLD, icon)) + . += "missing world state from '[icon]'" + if(!check_state_in_icon(ICON_STATE_INV, icon)) + . += "missing inventory state from '[icon]'" + var/check_state = "[ICON_STATE_WORLD]-set" + if(!check_state_in_icon(check_state, icon)) + . += "missing state '[check_state]' from '[icon]'" + check_state = "[ICON_STATE_INV]-set" + if(!check_state_in_icon(check_state, icon)) + . += "missing state '[check_state]' from '[icon]'" + else + . += "null or unset icon" + +// Subtypes below. +/decl/gemstone_cut/uncut + name = "uncut" + adjective = "uncut" + desc = "A rough, uncut gemstone." + icon = 'icons/obj/items/gemstones/uncut.dmi' + can_attempt_cut = TRUE + can_be_cut = FALSE + worth_multiplier = 1 + +/decl/gemstone_cut/poor + name = "poorly-cut" + adjective = "poorly-cut" + desc = "A poorly-cut and uneven gemstone." + icon = 'icons/obj/items/gemstones/poor.dmi' + worth_multiplier = 0.5 + can_be_cut = FALSE + +/decl/gemstone_cut/baguette + name = "baguette" + adjective = "baguette-cut" + desc = "A square-cut gemstone." + icon = 'icons/obj/items/gemstones/baguette.dmi' + +/decl/gemstone_cut/hexagon + name = "hexagon" + adjective = "hexagon-cut" + desc = "A hexagon-cut gemstone." + icon = 'icons/obj/items/gemstones/hexagon.dmi' + +/decl/gemstone_cut/octagon + name = "octagon" + adjective = "octagon-cut" + desc = "A octagon-cut gemstone." + icon = 'icons/obj/items/gemstones/octagon.dmi' + +/decl/gemstone_cut/round + name = "round" + adjective = "round-cut" + desc = "A round-cut gemstone." + icon = 'icons/obj/items/gemstones/round.dmi' diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 01aa188e0cc5..4b12ce62dee1 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -363,6 +363,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /// If an item has a null paint_verb, it automatically sets it based on material. var/paint_verb = "painted" + /// Chance of a natural wall made of this material dropping a gemstone, if the gemstone_types list is populated. + var/gemstone_chance = 5 + /// Assoc weighted list of gemstone material types to weighting. + var/list/gemstone_types + // Placeholders for light tiles and rglass. /decl/material/proc/reinforce(var/mob/user, var/obj/item/stack/material/used_stack, var/obj/item/stack/material/target_stack, var/use_sheets = 1) if(!used_stack.can_use(use_sheets)) diff --git a/code/modules/materials/definitions/solids/materials_solid_gemstones.dm b/code/modules/materials/definitions/solids/materials_solid_gemstones.dm index f0718ad4ccfe..dcb3b5d38a62 100644 --- a/code/modules/materials/definitions/solids/materials_solid_gemstones.dm +++ b/code/modules/materials/definitions/solids/materials_solid_gemstones.dm @@ -1,54 +1,69 @@ /decl/material/solid/gemstone - name = null - flags = MAT_FLAG_UNMELTABLE - cut_delay = 60 - color = COLOR_DIAMOND - opacity = 0.4 - shard_type = SHARD_SHARD - tableslam_noise = 'sound/effects/Glasshit.ogg' - reflectiveness = MAT_VALUE_MIRRORED - conductive = 0 - ore_icon_overlay = "gems" - default_solid_form = /obj/item/stack/material/gemstone - abstract_type = /decl/material/solid/gemstone - sound_manipulate = 'sound/foley/pebblespickup1.ogg' - sound_dropped = 'sound/foley/pebblesdrop1.ogg' + flags = MAT_FLAG_UNMELTABLE + cut_delay = 60 + color = COLOR_DIAMOND + opacity = 0.4 + shard_type = SHARD_SHARD + tableslam_noise = 'sound/effects/Glasshit.ogg' + conductive = 0 + ore_icon_overlay = "gems" + default_solid_form = /obj/item/stack/material/gemstone + abstract_type = /decl/material/solid/gemstone + sound_manipulate = 'sound/foley/pebblespickup1.ogg' + sound_dropped = 'sound/foley/pebblesdrop1.ogg' + exoplanet_rarity_plant = MAT_RARITY_UNCOMMON + exoplanet_rarity_gas = MAT_RARITY_NOWHERE + dissolves_in = MAT_SOLVENT_IMMUNE + dissolves_into = null + hardness = MAT_VALUE_VERY_HARD + reflectiveness = MAT_VALUE_VERY_SHINY + construction_difficulty = MAT_VALUE_VERY_HARD_DIY /decl/material/solid/gemstone/diamond - name = "diamond" - uid = "solid_diamond" - lore_text = "An extremely hard allotrope of carbon. Valued for its use in industrial tools." - melting_point = 4300 - boiling_point = null - ignition_point = null - brute_armor = 10 - burn_armor = 50 // Diamond walls are immune to fire, therefore it makes sense for them to be almost undamageable by burn damage type. - stack_origin_tech = @'{"materials":6}' - hardness = MAT_VALUE_VERY_HARD + 20 - construction_difficulty = MAT_VALUE_VERY_HARD_DIY - ore_name = "rough diamonds" - ore_result_amount = 1 - ore_spread_chance = 10 - ore_scan_icon = "mineral_rare" - xarch_source_mineral = /decl/material/gas/ammonia - value = 1.8 - sparse_material_weight = 5 - rich_material_weight = 5 - ore_type_value = ORE_PRECIOUS - ore_data_value = 2 - exoplanet_rarity_plant = MAT_RARITY_UNCOMMON - exoplanet_rarity_gas = MAT_RARITY_NOWHERE - dissolves_in = MAT_SOLVENT_IMMUNE - dissolves_into = null + name = "diamond" + uid = "solid_diamond" + lore_text = "An extremely hard allotrope of carbon. Valued for its use in industrial tools." + melting_point = 4300 + boiling_point = null + ignition_point = null + brute_armor = 10 + burn_armor = 50 // Diamond walls are immune to fire, therefore it makes sense for them to be almost undamageable by burn damage type. + stack_origin_tech = @'{"materials":6}' + hardness = MAT_VALUE_VERY_HARD + 20 + ore_name = "rough diamonds" + ore_result_amount = 1 + ore_spread_chance = 10 + ore_scan_icon = "mineral_rare" + xarch_source_mineral = /decl/material/gas/ammonia + value = 1.8 + sparse_material_weight = 5 + rich_material_weight = 5 + ore_type_value = ORE_PRECIOUS + ore_data_value = 2 /decl/material/solid/gemstone/crystal - name = "crystal" - uid = "solid_crystal" - hardness = MAT_VALUE_VERY_HARD - reflectiveness = MAT_VALUE_VERY_SHINY + name = "crystal" + uid = "solid_crystal" + value = 2 hidden_from_codex = TRUE - value = 2 - exoplanet_rarity_plant = MAT_RARITY_UNCOMMON - exoplanet_rarity_gas = MAT_RARITY_NOWHERE - dissolves_in = MAT_SOLVENT_IMMUNE - dissolves_into = null + +/decl/material/solid/gemstone/ruby + name = "ruby" + lore_text = "A rich red stone sometimes found in marble." + uid = "solid_ruby" + value = 1.6 + color = "#d00000" + +/decl/material/solid/gemstone/sapphire + name = "sapphire" + lore_text = "A deep blue gemstone sometimes found in clay or other sediment." + uid = "solid_sapphite" + value = 1.6 + color = "#2983de" + +/decl/material/solid/gemstone/topaz + name = "topaz" + lore_text = "A golden gemstone sometimes found in granite." + uid = "solid_topaz" + value = 1.6 + color = "#f7b92d" diff --git a/code/modules/materials/definitions/solids/materials_solid_mineral.dm b/code/modules/materials/definitions/solids/materials_solid_mineral.dm index 2be84c6c5df9..3dea5d14d81e 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mineral.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mineral.dm @@ -258,6 +258,8 @@ // lower than the temperature expected from a kiln so that clay can be used to make bricks to make a high-temperature kiln. bakes_into_at_temperature = 950 CELSIUS can_backfill_turf_type = /turf/floor/clay + gemstone_chance = 0.01 + gemstone_types = list(/decl/material/solid/gemstone/sapphire = 1) /decl/material/solid/soil name = "soil" diff --git a/code/modules/materials/definitions/solids/materials_solid_stone.dm b/code/modules/materials/definitions/solids/materials_solid_stone.dm index 6334b03f1b8e..acf1e65ed86f 100644 --- a/code/modules/materials/definitions/solids/materials_solid_stone.dm +++ b/code/modules/materials/definitions/solids/materials_solid_stone.dm @@ -51,6 +51,7 @@ brute_armor = 15 explosion_resistance = 15 integrity = 500 //granite is very strong + gemstone_types = list(/decl/material/solid/gemstone/topaz = 1) dissolves_into = list( /decl/material/solid/silicon = 0.75, /decl/material/solid/bauxite = 0.15, @@ -88,6 +89,7 @@ brute_armor = 3 integrity = 201 //hack to stop kitchen benches being flippable, todo: refactor into weight system construction_difficulty = MAT_VALUE_HARD_DIY + gemstone_types = list(/decl/material/solid/gemstone/ruby = 1) /decl/material/solid/stone/basalt name = "basalt" diff --git a/code/modules/mechs/equipment/utility.dm b/code/modules/mechs/equipment/utility.dm index f645599c8ac1..f76503b97bbf 100644 --- a/code/modules/mechs/equipment/utility.dm +++ b/code/modules/mechs/equipment/utility.dm @@ -517,7 +517,7 @@ for(var/turf/asteroid as anything in RANGE_TURFS(target, 1)) if (!(get_dir(owner, asteroid) & owner.dir)) continue - if(asteroid.can_be_dug(drill_head.material?.hardness) && asteroid.drop_diggable_resources()) + if(asteroid.can_be_dug(drill_head.material?.hardness) && asteroid.drop_diggable_resources(user)) drill_head.durability -= 1 scoop_ore(asteroid) return diff --git a/code/modules/mining/drilling/drill_act.dm b/code/modules/mining/drilling/drill_act.dm index 6a17a4c5b534..39759df1a4c2 100644 --- a/code/modules/mining/drilling/drill_act.dm +++ b/code/modules/mining/drilling/drill_act.dm @@ -1,7 +1,7 @@ /turf/proc/drill_act() SHOULD_CALL_PARENT(TRUE) drop_diggable_resources() - dig_pit(MAT_VALUE_VERY_HARD) + dig_pit(tool_hardness = MAT_VALUE_VERY_HARD) var/base_turf = get_base_turf_by_area(src) if(!istype(src, base_turf)) return ChangeTurf(base_turf) diff --git a/icons/obj/items/gemstones/baguette.dmi b/icons/obj/items/gemstones/baguette.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1fecb840ccbf5ded0e5e05be39e759c59eaaae30 GIT binary patch literal 423 zcmV;Y0a*TtP)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)1vgK(IJJa0Gx4cZ zR&e!m0ecJpTK_=<$43C`0002CNklz<=fH%o}n5;R(};BJ66nX~yHcCTpUcq72-0cXy9%Lm0?6pg)T RrF;MY002ovPDHLkV1g8@vlIXT literal 0 HcmV?d00001 diff --git a/icons/obj/items/gemstones/hexagon.dmi b/icons/obj/items/gemstones/hexagon.dmi new file mode 100644 index 0000000000000000000000000000000000000000..8b9b72ab45371ba64bd05443aa8b344dbc7aa05e GIT binary patch literal 419 zcmV;U0bKrxP)V=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 zDKoDuHLoPUs8WfGGbOXA7$|1Q#hF%=n41b=!&R2&7v-c7R0TIrw>Y(gI5Y97R90~H za{+q{09yY+1II@I>i_@%wMj%lR7i>KltC7QAPfbea)1IJpuBJZ6Fq>~``@W{$B%tx zZ~eO@lL0n3_g{G$b6*S~$+^N)Mupb`E~rQ-^pp}PJfU!?g)4OpVVMBPG3MMgSQvmn zg{gLpZQE95U^KLbhFW6n4I=6eW8|?nwASE=unT+{kHUK6=F;@fNtbmsS{9DzBKT8RQtO1?X0`A=Tl@DZJ5|aXjh!X$+ N002ovPDHLkV1hDUtkwVk literal 0 HcmV?d00001 diff --git a/icons/obj/items/gemstones/octagon.dmi b/icons/obj/items/gemstones/octagon.dmi new file mode 100644 index 0000000000000000000000000000000000000000..06b5d8617572be65c905bcee8a4f6b25db21fda6 GIT binary patch literal 445 zcmV;u0Yd(XP)p8x;V=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 zDKoDuHLoPUs8WfGGbOXA7$|1Q#hF%=n41b=!&R2&7v-c7R0TIrw>Y(gI5Y97R90~H za{+q{09yY+1II@I>i_@%&q+iKl*)-z0j`1V9a@2Uvoh61;B*B8b@IGXP0py nJ%$~(&Q{t7nB9^)cfRHg>yH^c0zk}a00000NkvXXu0mjfGR?Zi literal 0 HcmV?d00001 diff --git a/icons/obj/items/gemstones/poor.dmi b/icons/obj/items/gemstones/poor.dmi new file mode 100644 index 0000000000000000000000000000000000000000..95fb0bb44f4e3fae2f36829be265e99c5e107d0e GIT binary patch literal 382 zcmV-^0fGLBP)V=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 zDKoDuHLoPUs8WfGGbOXA7$|1Q#hF%=n41b=!&R2&7v-c7R0TIrw>Y(gI5Y97R90~H za{+q{09yY+1II@I>i_@%kV!;AR7i>KlfezcAPfKKuUb_3Fc&7~KhVcQbhWi6pt0_2l~lwJjF#bs`X3p{Nteu=Bz`;cw{i cjY;w+Z;A8@xVZmdk^lez07*qoM6N<$g0dl?b^rhX literal 0 HcmV?d00001 diff --git a/icons/obj/items/gemstones/round.dmi b/icons/obj/items/gemstones/round.dmi new file mode 100644 index 0000000000000000000000000000000000000000..12517ea0b4f5358688b42a8bc61a44ee8f38d4e0 GIT binary patch literal 401 zcmV;C0dD?@P)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)1vgK(IJJa0Gx4cZ zR&e!m0ecJpTK_=<$43C`0001>NklH1vqxoL$SXkyLiM*w{N0 z_qBNJsTU9t8%c!_eBUt0?|^361YoV@0ljU!4!r&-PPqnsQBNlKQ2rt??7gNCe00DGTPE!Ct=GbNc004n{R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)1vgK(IJJa0Gx4cZ zR&e!m0ecJpTK_=<$43C`0003gNkl=Hp3`N1o_K`4s6gdX~a)7XF_wfw6 z|5d^!g=Nngr%37FA>KTs8NitT%FDsNg#!RxTVdISCV|ntg{FWprG^4ya;64z2Ev4jm&^-_jh+Y{2#dihV zTL3DefQV{kBSsH?EK3RdSp&eBr7sF_189Jla+YaYN>q&sz(8Z)i!xk6;}#?2oQoRM zmQf$U2-176fXgImXqyOTnYF zH2@$mtQfCm8P{p<2;nAcy_6K@UrZJYaB*#MW#b?CBb$Ib`9 cnKKXb4eT5(mk}vo3IG5A07*qoM6N<$g0WZT2LJ#7 literal 0 HcmV?d00001 diff --git a/mods/content/fantasy/datum/skills.dm b/mods/content/fantasy/datum/skills.dm index 164ade162b6d..f800e76d7be3 100644 --- a/mods/content/fantasy/datum/skills.dm +++ b/mods/content/fantasy/datum/skills.dm @@ -73,7 +73,7 @@ /decl/skill/crafting/artifice name = "Artifice" uid = "skill_crafting_artifice" - desc = "Your ability to create, install, and comprehend complex devices and mechanisms, as well as your ability to create finely-detailed objects." + desc = "Your ability to create, install, and comprehend complex devices and mechanisms, as well as your ability to create finely-detailed objects like cut gems or jewellery." levels = list( "Unskilled" = "You know that gears turn together when intermeshed and that axles are used to connect spinning things, but you've never done more work on a machine than hitting it if it's broken. You struggle with the precision needed to work on finely-detailed objects.", "Basic" = "You know some basic mechanical principles, like the construction of a basic pulley, or how to put a wheel on an axle. You could fix a broken or stuck well winch, but you'd struggle to deal with a malfunctioning windmill or granary. You have a steadier hand than most, able to place small gems on jewelry and connect small mechanisms.", @@ -306,3 +306,6 @@ "Experienced" = "You work as an pharmacist, or else you are a doctor with training in chemistry. If you are a pharmacist, you can make most medications. At this stage, you're working mostly by-the-book.
    - You can examine held containers for some reagents.", "Master" = "You specialized in chemistry or pharmaceuticals; you are either a medical researcher or professional chemist. You can create custom mixes and make even the trickiest of medications easily. You understand how your pharmaceuticals interact with the bodies of your patients. You are probably the originator of at least one new chemical innovation.
    - You can examine held containers for all reagents." ) + +/obj/item/gemstone + work_skill = SKILL_ARTIFICE diff --git a/nebula.dme b/nebula.dme index 44929d112015..63eab7de9d32 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2456,6 +2456,8 @@ #include "code\modules\games\cards_cag.dm" #include "code\modules\games\spaceball_cards.dm" #include "code\modules\games\tarot.dm" +#include "code\modules\gemstones\_gemstone.dm" +#include "code\modules\gemstones\gemstone_cuts.dm" #include "code\modules\genetics\_gene.dm" #include "code\modules\genetics\plants\_gene_plant.dm" #include "code\modules\genetics\plants\_plant_trait.dm" From 8ca797128c29392912434ab828dee6ac53b11640 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 5 Dec 2024 14:23:41 +1100 Subject: [PATCH 0045/1331] Removed deity mode. --- code/modules/species/outsider/shadow.dm | 48 - code/modules/species/outsider/starlight.dm | 153 --- maps/exodus/exodus.dm | 1 - maps/exodus/exodus_antagonism.dm | 9 - maps/modpack_testing/modpack_testing.dm | 1 - maps/tradeship/tradeship.dm | 1 - mods/gamemodes/cult/_cult.dme | 4 - mods/gamemodes/cult/ghosttrap.dm | 6 - mods/gamemodes/cult/items.dm | 2 +- mods/gamemodes/deity/_defines.dm | 19 - mods/gamemodes/deity/_deity.dm | 2 - mods/gamemodes/deity/_deity.dme | 80 -- mods/gamemodes/deity/codex.dm | 38 - mods/gamemodes/deity/deity_base.dm | 14 - mods/gamemodes/deity/deity_base.dmm | 1035 ----------------- mods/gamemodes/deity/deity_role.dm | 15 - .../deity/extensions/deity_be_near.dm | 67 -- mods/gamemodes/deity/forms/forms.dm | 48 - .../deity/forms/narsie/deity_items/basic.dm | 34 - .../deity/forms/narsie/deity_items/minions.dm | 37 - .../forms/narsie/deity_items/sacrificing.dm | 42 - .../forms/narsie/deity_items/smithing.dm | 29 - mods/gamemodes/deity/forms/narsie/items.dm | 77 -- mods/gamemodes/deity/forms/narsie/narsie.dm | 51 - .../deity/forms/narsie/spells/tear_veil.dm | 37 - .../deity/forms/narsie/structures.dm | 36 - .../forms/starlight/deity_items/artifacts.dm | 32 - .../forms/starlight/deity_items/phenomena.dm | 41 - .../forms/starlight/deity_items/spells.dm | 62 - mods/gamemodes/deity/forms/starlight/items.dm | 159 --- mods/gamemodes/deity/forms/starlight/mobs.dm | 29 - .../forms/starlight/spells/disable_tech.dm | 4 - .../forms/starlight/spells/starlight_aura.dm | 5 - .../forms/starlight/spells/veil_of_shadows.dm | 57 - .../deity/forms/starlight/starlight.dm | 47 - .../deity/forms/starlight/structures.dm | 272 ----- .../forms/tower/deity_items/conjuration.dm | 107 -- .../forms/tower/deity_items/transmutation.dm | 97 -- mods/gamemodes/deity/forms/tower/spells.dm | 67 -- .../gamemodes/deity/forms/tower/structures.dm | 25 - mods/gamemodes/deity/forms/tower/tower.dm | 49 - mods/gamemodes/deity/gamemode.dm | 12 - mods/gamemodes/deity/god_cultist_role.dm | 106 -- mods/gamemodes/deity/mobs/deity.dm | 134 --- mods/gamemodes/deity/mobs/deity_Stat.dm | 14 - mods/gamemodes/deity/mobs/deity_boons.dm | 54 - mods/gamemodes/deity/mobs/deity_click.dm | 24 - mods/gamemodes/deity/mobs/deity_hud.dm | 9 - mods/gamemodes/deity/mobs/deity_items.dm | 33 - mods/gamemodes/deity/mobs/deity_phenomena.dm | 94 -- mods/gamemodes/deity/mobs/deity_power.dm | 21 - mods/gamemodes/deity/mobs/deity_pylon.dm | 21 - mods/gamemodes/deity/mobs/deity_sources.dm | 84 -- mods/gamemodes/deity/mobs/deity_topic.dm | 56 - mods/gamemodes/deity/mobs/deity_tracking.dm | 40 - mods/gamemodes/deity/mobs/freelook/cultnet.dm | 13 - mods/gamemodes/deity/mobs/freelook/mask.dm | 11 - .../deity/mobs/items/blood_crafting.dm | 19 - mods/gamemodes/deity/mobs/items/deity_item.dm | 39 - mods/gamemodes/deity/mobs/items/general.dm | 26 - mods/gamemodes/deity/mobs/items/generic.dm | 25 - mods/gamemodes/deity/mobs/menu/deity_nano.dm | 81 -- .../deity/mobs/phenomena/_defines.dm | 4 - .../deity/mobs/phenomena/communication.dm | 80 -- .../deity/mobs/phenomena/conjuration.dm | 67 -- .../deity/mobs/phenomena/conversion.dm | 51 - .../gamemodes/deity/mobs/phenomena/generic.dm | 36 - mods/gamemodes/deity/mobs/phenomena/narsie.dm | 37 - .../deity/mobs/phenomena/phenomena.dm | 75 -- .../deity/mobs/phenomena/starlight.dm | 232 ---- .../deity/mobs/phenomena/transmutation.dm | 26 - mods/gamemodes/deity/mobs/say.dm | 9 - mods/gamemodes/deity/overrides.dm | 27 - mods/gamemodes/deity/screen/intent.dm | 45 - mods/gamemodes/deity/spells/boon.dm | 11 - mods/gamemodes/deity/spells/construction.dm | 55 - mods/gamemodes/deity/spells/open_gateway.dm | 34 - mods/gamemodes/deity/spells/vision.dm | 21 - mods/gamemodes/deity/structures/altar.dm | 106 -- .../gamemodes/deity/structures/blood_forge.dm | 66 -- mods/gamemodes/deity/structures/pylon.dm | 75 -- mods/gamemodes/deity/structures/structures.dm | 68 -- mods/gamemodes/deity/structures/trap.dm | 30 - nano/templates/deity.tmpl | 116 -- nebula.dme | 2 - test/check-paths.sh | 2 +- 86 files changed, 2 insertions(+), 5128 deletions(-) delete mode 100644 code/modules/species/outsider/shadow.dm delete mode 100644 code/modules/species/outsider/starlight.dm delete mode 100644 mods/gamemodes/deity/_defines.dm delete mode 100644 mods/gamemodes/deity/_deity.dm delete mode 100644 mods/gamemodes/deity/_deity.dme delete mode 100644 mods/gamemodes/deity/codex.dm delete mode 100644 mods/gamemodes/deity/deity_base.dm delete mode 100644 mods/gamemodes/deity/deity_base.dmm delete mode 100644 mods/gamemodes/deity/deity_role.dm delete mode 100644 mods/gamemodes/deity/extensions/deity_be_near.dm delete mode 100644 mods/gamemodes/deity/forms/forms.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/deity_items/basic.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/deity_items/minions.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/deity_items/sacrificing.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/deity_items/smithing.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/items.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/narsie.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm delete mode 100644 mods/gamemodes/deity/forms/narsie/structures.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/deity_items/artifacts.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/deity_items/phenomena.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/deity_items/spells.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/items.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/mobs.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/spells/disable_tech.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/spells/starlight_aura.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/spells/veil_of_shadows.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/starlight.dm delete mode 100644 mods/gamemodes/deity/forms/starlight/structures.dm delete mode 100644 mods/gamemodes/deity/forms/tower/deity_items/conjuration.dm delete mode 100644 mods/gamemodes/deity/forms/tower/deity_items/transmutation.dm delete mode 100644 mods/gamemodes/deity/forms/tower/spells.dm delete mode 100644 mods/gamemodes/deity/forms/tower/structures.dm delete mode 100644 mods/gamemodes/deity/forms/tower/tower.dm delete mode 100644 mods/gamemodes/deity/gamemode.dm delete mode 100644 mods/gamemodes/deity/god_cultist_role.dm delete mode 100644 mods/gamemodes/deity/mobs/deity.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_Stat.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_boons.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_click.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_hud.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_items.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_phenomena.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_power.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_pylon.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_sources.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_topic.dm delete mode 100644 mods/gamemodes/deity/mobs/deity_tracking.dm delete mode 100644 mods/gamemodes/deity/mobs/freelook/cultnet.dm delete mode 100644 mods/gamemodes/deity/mobs/freelook/mask.dm delete mode 100644 mods/gamemodes/deity/mobs/items/blood_crafting.dm delete mode 100644 mods/gamemodes/deity/mobs/items/deity_item.dm delete mode 100644 mods/gamemodes/deity/mobs/items/general.dm delete mode 100644 mods/gamemodes/deity/mobs/items/generic.dm delete mode 100644 mods/gamemodes/deity/mobs/menu/deity_nano.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/_defines.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/communication.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/conjuration.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/conversion.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/generic.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/narsie.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/phenomena.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/starlight.dm delete mode 100644 mods/gamemodes/deity/mobs/phenomena/transmutation.dm delete mode 100644 mods/gamemodes/deity/mobs/say.dm delete mode 100644 mods/gamemodes/deity/overrides.dm delete mode 100644 mods/gamemodes/deity/screen/intent.dm delete mode 100644 mods/gamemodes/deity/spells/boon.dm delete mode 100644 mods/gamemodes/deity/spells/construction.dm delete mode 100644 mods/gamemodes/deity/spells/open_gateway.dm delete mode 100644 mods/gamemodes/deity/spells/vision.dm delete mode 100644 mods/gamemodes/deity/structures/altar.dm delete mode 100644 mods/gamemodes/deity/structures/blood_forge.dm delete mode 100644 mods/gamemodes/deity/structures/pylon.dm delete mode 100644 mods/gamemodes/deity/structures/structures.dm delete mode 100644 mods/gamemodes/deity/structures/trap.dm delete mode 100644 nano/templates/deity.tmpl diff --git a/code/modules/species/outsider/shadow.dm b/code/modules/species/outsider/shadow.dm deleted file mode 100644 index 709f5d38ffe9..000000000000 --- a/code/modules/species/outsider/shadow.dm +++ /dev/null @@ -1,48 +0,0 @@ -/decl/bodytype/starlight/shadow - name = "shadow" - desc = "A wound of darkness inflicted upon the world." - icon_base = 'icons/mob/human_races/species/shadow/body.dmi' - icon_deformed = 'icons/mob/human_races/species/shadow/body.dmi' - body_flags = BODY_FLAG_NO_DNA | BODY_FLAG_NO_DEFIB | BODY_FLAG_NO_STASIS - eye_darksight_range = 8 - uid = "bodytype_starlight_shadow" - -/decl/blood_type/shadowstuff - name = "shadowstuff" - antigen_category = "shadowstuff" - splatter_name = "shadowstuff" - splatter_desc = "A puddle of shadowstuff." - splatter_colour = COLOR_GRAY80 - -/decl/species/starlight/shadow - name = "Shadow" - name_plural = "shadows" - description = "A being of pure darkness, hates the light and all that comes with it." - butchery_data = null - - available_bodytypes = list(/decl/bodytype/starlight/shadow) - - unarmed_attacks = list(/decl/natural_attack/claws/strong, /decl/natural_attack/bite/sharp) - shock_vulnerability = 0 - - blood_types = list( - /decl/blood_type/shadowstuff - ) - flesh_color = "#aaaaaa" - - remains_type = /obj/effect/decal/cleanable/ash - death_message = "dissolves into ash..." - - species_flags = SPECIES_FLAG_NO_SLIP | SPECIES_FLAG_NO_POISON | SPECIES_FLAG_NO_EMBED - -/decl/species/starlight/shadow/handle_environment_special(var/mob/living/human/H) - if(H.is_in_stasis() || H.stat == DEAD || H.isSynthetic()) - return - var/light_amount = 0 - if(isturf(H.loc)) - var/turf/T = H.loc - light_amount = T.get_lumcount() * 10 - if(light_amount > 2) //if there's enough light, start dying - H.take_overall_damage(1,1) - else //heal in the dark - H.heal_overall_damage(1,1) \ No newline at end of file diff --git a/code/modules/species/outsider/starlight.dm b/code/modules/species/outsider/starlight.dm deleted file mode 100644 index 215ba3cf5e9e..000000000000 --- a/code/modules/species/outsider/starlight.dm +++ /dev/null @@ -1,153 +0,0 @@ -/decl/species/starlight - abstract_type = /decl/species/starlight - butchery_data = null - spawn_flags = SPECIES_IS_RESTRICTED - available_pronouns = list(/decl/pronouns/neuter) - force_background_info = list( - /decl/background_category/heritage = /decl/background_detail/heritage/other - ) - hidden_from_codex = TRUE - -/decl/bodytype/starlight - abstract_type = /decl/bodytype/starlight - has_limbs = list( - BP_CHEST = list("path" = /obj/item/organ/external/chest/unbreakable), - BP_GROIN = list("path" = /obj/item/organ/external/groin/unbreakable), - BP_HEAD = list("path" = /obj/item/organ/external/head/unbreakable), - BP_L_ARM = list("path" = /obj/item/organ/external/arm/unbreakable), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right/unbreakable), - BP_L_LEG = list("path" = /obj/item/organ/external/leg/unbreakable), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right/unbreakable), - BP_L_HAND = list("path" = /obj/item/organ/external/hand/unbreakable), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/unbreakable), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot/unbreakable), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/unbreakable) - ) - has_organ = list( - BP_BRAIN = /obj/item/organ/internal/brain/starlight - ) - -/obj/item/organ/internal/brain/starlight - name = "essence of fire" - desc = "A fancy name for ash. Still, it does look a bit different from the regular stuff." - icon = 'icons/obj/objects.dmi' - icon_state = "ash" - -/decl/bodytype/starlight/starborn - name = "starborn" - desc = "A blazing mass of light." - icon_base = 'icons/mob/human_races/species/starborn/body.dmi' - icon_deformed = 'icons/mob/human_races/species/starborn/body.dmi' - husk_icon = 'icons/mob/human_races/species/starborn/husk.dmi' - body_flags = BODY_FLAG_NO_DNA | BODY_FLAG_NO_PAIN | BODY_FLAG_NO_DEFIB | BODY_FLAG_NO_STASIS - uid = "bodytype_starlight_starborn" - cold_level_1 = 260 - cold_level_2 = 250 - cold_level_3 = 235 - heat_level_1 = 20000 - heat_level_2 = 30000 - heat_level_3 = 40000 - cold_discomfort_level = 300 - cold_discomfort_strings = list( - "You feel your fire dying out...", - "Your fire begins to shrink away from the cold.", - "You feel slow and sluggish from the cold." - ) - heat_discomfort_level = 10000 - heat_discomfort_strings = list( - "Surprisingly, you start burning!", - "You're... burning!?!" - ) - -/decl/blood_type/starstuff - name = "starstuff" - antigen_category = "starstuff" - splatter_name = "starstuff" - splatter_desc = "A puddle of starstuff." - splatter_colour = "#ffff00" - -/decl/species/starlight/handle_death(var/mob/living/human/H) - addtimer(CALLBACK(H, TYPE_PROC_REF(/mob, dust)),0) - -/decl/species/starlight/starborn - name = "Starborn" - name_plural = "Starborn" - description = "Beings of fire and light, split off from a sun deity of unbelievable power." - available_bodytypes = list(/decl/bodytype/starlight/starborn) - - blood_types = list( - /decl/blood_type/starstuff - ) - flesh_color = "#ffff00" - - unarmed_attacks = list(/decl/natural_attack/punch/starborn) - - warning_low_pressure = 50 - hazard_low_pressure = 0 - shock_vulnerability = 0 - hunger_factor = 0 - thirst_factor = 0 - death_message = "dissolves into pure flames!" - breath_type = null - - - total_health = 250 - body_temperature = T0C + 500 //We are being of fire and light. - species_flags = SPECIES_FLAG_NO_MINOR_CUT | SPECIES_FLAG_NO_SLIP | SPECIES_FLAG_NO_POISON | SPECIES_FLAG_NO_EMBED | SPECIES_FLAG_NO_TANGLE - - base_auras = list( - /obj/aura/starborn - ) - -/decl/species/starlight/starborn/handle_death(var/mob/living/human/H) - ..() - var/turf/T = get_turf(H) - T.add_to_reagents(/decl/material/liquid/fuel, 20) - T.hotspot_expose(FLAMMABLE_GAS_MINIMUM_BURN_TEMPERATURE) - -/decl/bodytype/starlight/blueforged - name = "blueforged" - desc = "A mass of carved and shaped spacetime." - icon_base = 'icons/mob/human_races/species/blueforged/body.dmi' - icon_deformed = 'icons/mob/human_races/species/blueforged/body.dmi' - eye_icon = 'icons/mob/human_races/species/blueforged/eyes.dmi' - body_flags = BODY_FLAG_NO_DNA | BODY_FLAG_NO_DEFIB | BODY_FLAG_NO_STASIS - override_organ_types = list(BP_EYES = /obj/item/organ/internal/eyes/blueforged) - uid = "bodytype_starlight_blueforged" - -/decl/blood_type/spacestuff - name = "spacestuff" - antigen_category = "spacestuff" - splatter_name = "spacestuff" - splatter_desc = "A puddle of spacestuff." - splatter_colour = "#2222ff" - -/decl/species/starlight/blueforged - name = "Blueforged" - name_plural = "Blueforged" - description = "Living chunks of spacetime, carved out of the original dimension and given life by a being of unbelievable power." - available_bodytypes = list(/decl/bodytype/starlight/blueforged) - - flesh_color = "#2222ff" - - warning_low_pressure = 50 - hazard_low_pressure = 0 - hunger_factor = 0 - thirst_factor = 0 - breath_type = null - - burn_mod = 10 - brute_mod = 0 - oxy_mod = 0 - toxins_mod = 0 - radiation_mod = 0 - species_flags = SPECIES_FLAG_NO_MINOR_CUT | SPECIES_FLAG_NO_SLIP | SPECIES_FLAG_NO_POISON | SPECIES_FLAG_NO_EMBED | SPECIES_FLAG_NO_TANGLE - -/decl/species/starlight/blueforged/handle_death(var/mob/living/human/H) - ..() - new /obj/effect/temporary(get_turf(H),11, 'icons/mob/mob.dmi', "liquify") - -/obj/item/organ/internal/eyes/blueforged - name = "bluespace prism" - desc = "You can see an endless blue plane when looking through it. Your eyes tingle if you stare too hard." - icon = 'icons/mob/human_races/species/blueforged/organs.dmi' diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 6c13c0ddb9ce..44e718bbf096 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -1,7 +1,6 @@ #if !defined(USING_MAP_DATUM) #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/deity/_deity.dme" #include "../../mods/gamemodes/heist/_heist.dme" #include "../../mods/gamemodes/meteor/_meteor.dme" #include "../../mods/gamemodes/ninja/_ninja.dme" diff --git a/maps/exodus/exodus_antagonism.dm b/maps/exodus/exodus_antagonism.dm index 92660ef36082..322bebbf1d34 100644 --- a/maps/exodus/exodus_antagonism.dm +++ b/maps/exodus/exodus_antagonism.dm @@ -3,15 +3,6 @@ LAZYINITLIST(protected_jobs) protected_jobs |= list(/datum/job/officer, /datum/job/warden, /datum/job/detective, /datum/job/captain, /datum/job/lawyer, /datum/job/hos) -/decl/special_role/godcultist/Initialize() - . = ..() - LAZYINITLIST(restricted_jobs) - restricted_jobs |= list(/datum/job/lawyer, /datum/job/captain, /datum/job/hos) - LAZYINITLIST(protected_jobs) - protected_jobs |= list(/datum/job/officer, /datum/job/warden, /datum/job/detective) - LAZYINITLIST(blacklisted_jobs) - blacklisted_jobs |= /datum/job/chaplain - /decl/special_role/cultist/Initialize() . = ..() LAZYINITLIST(restricted_jobs) diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 0e75223cde40..de68e49bc7bd 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -4,7 +4,6 @@ #include "blank.dmm" #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/deity/_deity.dme" #include "../../mods/gamemodes/heist/_heist.dme" #include "../../mods/gamemodes/meteor/_meteor.dme" #include "../../mods/gamemodes/ninja/_ninja.dme" diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 115fdd551c98..f3134a1184a8 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -5,7 +5,6 @@ #endif #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/deity/_deity.dme" #include "../../mods/gamemodes/heist/_heist.dme" #include "../../mods/gamemodes/ninja/_ninja.dme" #include "../../mods/gamemodes/revolution/_revolution.dme" diff --git a/mods/gamemodes/cult/_cult.dme b/mods/gamemodes/cult/_cult.dme index bda33c4bf964..e35ffed9db68 100644 --- a/mods/gamemodes/cult/_cult.dme +++ b/mods/gamemodes/cult/_cult.dme @@ -1,10 +1,6 @@ #ifndef GAMEMODE_PACK_CULT #define GAMEMODE_PACK_CULT -#ifdef GAMEMODE_PACK_DEITY -#warn Deity modpack loaded before Cult modpack, Nar'sie godform will be unavailable! -#endif - // BEGIN_INCLUDE #include "_cult.dm" #include "archaeology.dm" diff --git a/mods/gamemodes/cult/ghosttrap.dm b/mods/gamemodes/cult/ghosttrap.dm index 3e5d7452df6f..07faf87515f2 100644 --- a/mods/gamemodes/cult/ghosttrap.dm +++ b/mods/gamemodes/cult/ghosttrap.dm @@ -20,9 +20,3 @@ var/obj/item/soulstone/stone = new(get_turf(user)) stone.shade = new(stone) request_player(stone.shade, "The soul stone shade summon ritual has been performed. ") - -#ifdef GAMEMODE_PACK_DEITY -/decl/ghosttrap/cult_shade/Initialize() - ban_checks |= /decl/special_role/godcultist - . = ..() -#endif \ No newline at end of file diff --git a/mods/gamemodes/cult/items.dm b/mods/gamemodes/cult/items.dm index 01a8555e665d..1674cdb92eb8 100644 --- a/mods/gamemodes/cult/items.dm +++ b/mods/gamemodes/cult/items.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/items/weapon/swords/cult.dmi' material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME -// separated into a proc so that deity can modify it +// separated into a proc so that subtypes can modify it /obj/item/sword/cultblade/proc/can_use_safely(mob/living/user) return iscultist(user) diff --git a/mods/gamemodes/deity/_defines.dm b/mods/gamemodes/deity/_defines.dm deleted file mode 100644 index 99f85b14f61c..000000000000 --- a/mods/gamemodes/deity/_defines.dm +++ /dev/null @@ -1,19 +0,0 @@ -#define DEITY_TREE_SACRIFICE "Sacrificing" -#define DEITY_TREE_SOUL "Soul Arts" -#define DEITY_TREE_DARK_MINION "Summoning" -#define DEITY_TREE_TRANSMUTATION "Transmutation" -#define DEITY_TREE_CONJURATION "Conjuration" -#define DEITY_TREE_ARTIFACT "Artifacts" -#define DEITY_TREE_FIRECONJ "Fire Conjuration" -#define DEITY_TREE_HERALD "Phenomena" - -#define DEITY_BLOOD_CRAFT "Blood Crafting" -#define DEITY_ARMOR_CRAFT "Armor Crafting" -#define DEITY_VOID_CRAFT "Void Crafting" -#define DEITY_UNLOCK_ARMS "Unlock Armaments" -#define DEITY_UNLOCK_HEAL "Unlock Cleric Spells" - -#define isdeity(A) istype(A, /mob/living/deity) - -#define DEITY_STRUCTURE_NEAR_IMPORTANT 1 //Whether this needs to be near an important structure. -#define DEITY_STRUCTURE_ALONE 2 //Whether this can be near another of the same type. \ No newline at end of file diff --git a/mods/gamemodes/deity/_deity.dm b/mods/gamemodes/deity/_deity.dm deleted file mode 100644 index 6c658e122b37..000000000000 --- a/mods/gamemodes/deity/_deity.dm +++ /dev/null @@ -1,2 +0,0 @@ -/decl/modpack/deity - name = "Deity Gamemode Content" \ No newline at end of file diff --git a/mods/gamemodes/deity/_deity.dme b/mods/gamemodes/deity/_deity.dme deleted file mode 100644 index a1603dd317d8..000000000000 --- a/mods/gamemodes/deity/_deity.dme +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef GAMEMODE_PACK_DEITY -#define GAMEMODE_PACK_DEITY -// BEGIN_INCLUDE -#include "_defines.dm" -#include "_deity.dm" -#include "codex.dm" -#include "deity_base.dm" -#include "deity_role.dm" -#include "gamemode.dm" -#include "god_cultist_role.dm" -#include "overrides.dm" -#include "extensions\deity_be_near.dm" -#include "forms\forms.dm" -// These should only load if cult is loaded. -#ifdef GAMEMODE_PACK_CULT -#include "forms\narsie\items.dm" -#include "forms\narsie\narsie.dm" -#include "forms\narsie\structures.dm" -#include "forms\narsie\deity_items\basic.dm" -#include "forms\narsie\deity_items\minions.dm" -#include "forms\narsie\deity_items\sacrificing.dm" -#include "forms\narsie\deity_items\smithing.dm" -#include "forms\narsie\spells\tear_veil.dm" -#endif -#include "forms\starlight\items.dm" -#include "forms\starlight\mobs.dm" -#include "forms\starlight\starlight.dm" -#include "forms\starlight\structures.dm" -#include "forms\starlight\deity_items\artifacts.dm" -#include "forms\starlight\deity_items\phenomena.dm" -#include "forms\starlight\deity_items\spells.dm" -#include "forms\starlight\spells\disable_tech.dm" -#include "forms\starlight\spells\starlight_aura.dm" -#include "forms\starlight\spells\veil_of_shadows.dm" -#include "forms\tower\spells.dm" -#include "forms\tower\structures.dm" -#include "forms\tower\tower.dm" -#include "forms\tower\deity_items\conjuration.dm" -#include "forms\tower\deity_items\transmutation.dm" -#include "mobs\deity.dm" -#include "mobs\deity_boons.dm" -#include "mobs\deity_click.dm" -#include "mobs\deity_hud.dm" -#include "mobs\deity_items.dm" -#include "mobs\deity_phenomena.dm" -#include "mobs\deity_power.dm" -#include "mobs\deity_pylon.dm" -#include "mobs\deity_sources.dm" -#include "mobs\deity_Stat.dm" -#include "mobs\deity_topic.dm" -#include "mobs\deity_tracking.dm" -#include "mobs\say.dm" -#include "mobs\freelook\cultnet.dm" -#include "mobs\freelook\mask.dm" -#include "mobs\items\blood_crafting.dm" -#include "mobs\items\deity_item.dm" -#include "mobs\items\general.dm" -#include "mobs\items\generic.dm" -#include "mobs\menu\deity_nano.dm" -#include "mobs\phenomena\_defines.dm" -#include "mobs\phenomena\communication.dm" -#include "mobs\phenomena\conjuration.dm" -#include "mobs\phenomena\conversion.dm" -#include "mobs\phenomena\generic.dm" -#include "mobs\phenomena\narsie.dm" -#include "mobs\phenomena\phenomena.dm" -#include "mobs\phenomena\starlight.dm" -#include "mobs\phenomena\transmutation.dm" -#include "screen\intent.dm" -#include "spells\boon.dm" -#include "spells\construction.dm" -#include "spells\open_gateway.dm" -#include "spells\vision.dm" -#include "structures\altar.dm" -#include "structures\blood_forge.dm" -#include "structures\pylon.dm" -#include "structures\structures.dm" -#include "structures\trap.dm" -// END_INCLUDE -#endif \ No newline at end of file diff --git a/mods/gamemodes/deity/codex.dm b/mods/gamemodes/deity/codex.dm deleted file mode 100644 index fae5bfe607a3..000000000000 --- a/mods/gamemodes/deity/codex.dm +++ /dev/null @@ -1,38 +0,0 @@ -/datum/codex_entry/deity - abstract_type = /datum/codex_entry/deity - -/datum/codex_entry/deity/altar - associated_paths = list(/obj/structure/deity/altar) - mechanics_text = "To place someone upon the altar, first have them in an aggressive grab and click the altar while adjacent." - antag_text = "This structure anchors your deity within this realm, granting them additional power to influence it and empower their followers. Additionally, using it as a focus for their powers, they can convert someone laying on top of the altar.
    " - disambiguator = "occult" - -/datum/codex_entry/deity/blood_forge - associated_paths = list(/obj/structure/deity/blood_forge) - antag_text = "Allows creation of special items by feeding your blood into it. Only usable by cultists of the aligned deity." - disambiguator = "occult" - -/datum/codex_entry/deity/gateway - associated_paths = list(/obj/structure/deity/gateway) - antag_text = "Stand within the gateway to be transported to an unknown dimension and transformed into a flaming Starborn, a mysterious Blueforged or a subtle Shadowling." - disambiguator = "occult" - -/datum/codex_entry/deity/radiant_statue - associated_paths = list(/obj/structure/deity/radiant_statue) - antag_text = "Allows storage of power if cultists are nearby. This stored power can be expended to charge Starlight items." - disambiguator = "occult" - -/datum/codex_entry/deity/blood_forge/starlight - associated_paths = list(/obj/structure/deity/blood_forge/starlight) - antag_text = "Allows creation of special Starlight items. Only usable by cultists of the aligned deity. Use of this powerful forge will inflict burns." - disambiguator = "occult" - -/datum/codex_entry/deity/wizard_recharger - associated_paths = list(/obj/structure/deity/wizard_recharger) - antag_text = "A well of arcane power. When charged, a cultist may absorb this power to refresh their spells." - disambiguator = "occult" - -/datum/codex_entry/deity/pylon - associated_paths = list(/obj/structure/deity/pylon) - antag_text = "Serves as a conduit for a deity to speak through, making their will known in this dimension to any who hear it." - disambiguator = "occult" \ No newline at end of file diff --git a/mods/gamemodes/deity/deity_base.dm b/mods/gamemodes/deity/deity_base.dm deleted file mode 100644 index 0d7268ebd643..000000000000 --- a/mods/gamemodes/deity/deity_base.dm +++ /dev/null @@ -1,14 +0,0 @@ -/datum/map_template/ruin/antag_spawn/deity - name = "Deity Base" - mappaths = list( - "mods/gamemodes/deity/deity_base.dmm" - ) - apc_test_exempt_areas = list( - /area/map_template/deity_spawn = NO_SCRUBBER|NO_VENT|NO_APC - ) - -/area/map_template/deity_spawn - name = "\improper Deity Spawn" - icon_state = "yellow" - requires_power = 0 - dynamic_lighting = FALSE diff --git a/mods/gamemodes/deity/deity_base.dmm b/mods/gamemodes/deity/deity_base.dmm deleted file mode 100644 index 7e009c2d0bd7..000000000000 --- a/mods/gamemodes/deity/deity_base.dmm +++ /dev/null @@ -1,1035 +0,0 @@ -//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -"a" = ( -/turf/space/infinity, -/area/map_template/deity_spawn) -"b" = ( -/turf/unsimulated/floor/lava, -/area/map_template/deity_spawn) -"c" = ( -/turf/unsimulated/floor/water, -/area/map_template/deity_spawn) -"d" = ( -/obj/abstract/landmark{ - name = "DeitySpawn" - }, -/turf/unsimulated/floor/lava, -/area/map_template/deity_spawn) -"e" = ( -/obj/abstract/landmark{ - name = "DeitySpawn" - }, -/turf/unsimulated/floor/water, -/area/map_template/deity_spawn) - -(1,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(2,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(3,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(4,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(5,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(6,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(7,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(8,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(9,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(10,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(11,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(12,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(13,1,1) = {" -a -a -a -a -b -b -b -b -b -b -d -b -b -b -b -b -b -a -a -a -a -"} -(14,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(15,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(16,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(17,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(18,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(19,1,1) = {" -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -"} -(20,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(21,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(22,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(23,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(24,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(25,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(26,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(27,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(28,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(29,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(30,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(31,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(32,1,1) = {" -a -a -a -a -c -c -c -c -c -c -e -c -c -c -c -c -c -a -a -a -a -"} -(33,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(34,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(35,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(36,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(37,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(38,1,1) = {" -a -a -a -a -c -c -c -c -c -c -c -c -c -c -c -c -c -a -a -a -a -"} -(39,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(40,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(41,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(42,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(43,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} -(44,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -"} diff --git a/mods/gamemodes/deity/deity_role.dm b/mods/gamemodes/deity/deity_role.dm deleted file mode 100644 index 0426f6e8568e..000000000000 --- a/mods/gamemodes/deity/deity_role.dm +++ /dev/null @@ -1,15 +0,0 @@ -/decl/special_role/deity - name = "Deity" - name_plural = "Deities" - mob_path = /mob/living/deity - welcome_text = "This is not your world. This is not your reality. But here you exist. Use your powers, feed off the faith of others.
    You have to click on yourself to choose your form.
    Everything you say will be heard by your cultists!
    To get points your cultists need to build!
    Build Shrine and Construction are the best starting boons!" - landmark_id = "DeitySpawn" - - flags = ANTAG_OVERRIDE_MOB | ANTAG_OVERRIDE_JOB - - hard_cap = 2 - hard_cap_round = 2 - initial_spawn_req = 1 - initial_spawn_target = 1 - - base_to_load = "Deity Base" diff --git a/mods/gamemodes/deity/extensions/deity_be_near.dm b/mods/gamemodes/deity/extensions/deity_be_near.dm deleted file mode 100644 index 5eed3ae1fe40..000000000000 --- a/mods/gamemodes/deity/extensions/deity_be_near.dm +++ /dev/null @@ -1,67 +0,0 @@ -/datum/extension/deity_be_near - base_type = /datum/extension/deity_be_near - expected_type = /obj/item - var/keep_away_instead = FALSE - var/mob/living/deity/connected_deity - var/threshold_base = 6 - var/expected_helmet - flags = EXTENSION_FLAG_IMMEDIATE - -/datum/extension/deity_be_near/New(var/datum/holder, var/mob/living/deity/connect) - ..() - events_repository.register(/decl/observ/moved, holder,src, PROC_REF(check_movement)) - connected_deity = connect - events_repository.register(/decl/observ/destroyed, holder, src, PROC_REF(dead_deity)) - var/obj/O = holder - O.desc += "
    This item deals damage to its wielder the [keep_away_instead ? "closer" : "farther"] it is from a deity structure" - - -/datum/extension/deity_be_near/Destroy() - events_repository.unregister(/decl/observ/moved, holder,src) - events_repository.unregister(/decl/observ/destroyed, holder, src) - events_repository.unregister(/decl/observ/item_equipped, holder, src) - . = ..() - -/datum/extension/deity_be_near/proc/check_movement() - var/obj/item/I = holder - if(!isliving(I.loc)) - return - var/min_dist = INFINITY - for(var/s in connected_deity.structures) - var/dist = get_dist(holder,s) - if(dist < min_dist) - min_dist = dist - if(keep_away_instead && min_dist < threshold_base) - deal_damage(I.loc, round(threshold_base/min_dist)) - else if(min_dist > threshold_base) - deal_damage(I.loc, round(min_dist/threshold_base)) - - -/datum/extension/deity_be_near/proc/deal_damage(var/mob/living/victim, var/mult) - return - -/datum/extension/deity_be_near/proc/dead_deity() - var/obj/item/I = holder - I.visible_message(SPAN_WARNING("\The [holder]'s power fades!")) - qdel(src) - -/datum/extension/deity_be_near/proc/wearing_full() - var/obj/item/I = holder - - if(!ishuman(I.loc)) - return FALSE - var/mob/living/human/H = I.loc - if(H.get_equipped_slot_for_item(I) != slot_wear_suit_str) - return FALSE - if(expected_helmet && !istype(H.get_equipped_item(slot_head_str), expected_helmet)) - return FALSE - return TRUE - -/datum/extension/deity_be_near/champion/deal_damage(var/mob/living/victim,var/mult) - victim.take_damage(3 * mult, OXY) - -/datum/extension/deity_be_near/oracle/deal_damage(var/mob/living/victim, var/mult) - victim.take_damage(mult, BURN) - -/datum/extension/deity_be_near/traitor/deal_damage(var/mob/living/victim, var/mult) - victim.take_damage(5 * mult, PAIN) \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/forms.dm b/mods/gamemodes/deity/forms/forms.dm deleted file mode 100644 index 6f1f2f135b30..000000000000 --- a/mods/gamemodes/deity/forms/forms.dm +++ /dev/null @@ -1,48 +0,0 @@ -/*A god form basically is a combination of a sprite sheet choice and a gameplay choice. -Each plays slightly different and has different challenges/benefits -*/ - -/datum/god_form - var/name = "Form" - var/info = "This is a form your being can take." - var/desc = "This is the mob's description given." - var/faction = MOB_FACTION_NEUTRAL //For stuff like mobs and shit - var/god_icon_state = "nar-sie" //What you look like - var/pylon_icon_state //What image shows up when appearing in a pylon - var/mob/living/deity/linked_god = null - var/starting_power_min = 10 - var/starting_regeneration = 1 - var/list/buildables = list() //Both a list of var changes and a list of buildables. - var/list/items - -/datum/god_form/New(var/mob/living/deity/D) - ..() - D.icon_state = god_icon_state - D.desc = desc - D.power_min = starting_power_min - D.power_per_regen = starting_regeneration - linked_god = D - if(items && items.len) - var/list/complete_items = list() - for(var/l in items) - var/datum/deity_item/di = new l() - complete_items[di.name] = di - D.set_items(complete_items) - items.Cut() - -// TODO: Make this not a thing. It's so bad. -/datum/god_form/proc/sync_structure(var/obj/O) - var/list/svars = buildables[O.type] - if(!svars) - return - for(var/V in svars) - O.vars[V] = svars[V] - -/datum/god_form/proc/take_charge(var/mob/living/user, var/charge) - return TRUE - -/datum/god_form/Destroy() - if(linked_god) - linked_god.form = null - linked_god = null - return ..() \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/narsie/deity_items/basic.dm b/mods/gamemodes/deity/forms/narsie/deity_items/basic.dm deleted file mode 100644 index 59e8a7d15865..000000000000 --- a/mods/gamemodes/deity/forms/narsie/deity_items/basic.dm +++ /dev/null @@ -1,34 +0,0 @@ -/datum/deity_item/boon/eternal_darkness - name = "Eternal Darkness" - desc = "Allows a follower to cause insanity in a target." - category = "Dark Spells" - base_cost = 40 - boon_path = /spell/targeted/shatter - -/datum/deity_item/boon/torment - name = "Torment" - desc = "Gives a follower the ability to cause mass hysteria and pain." - category = "Dark Spells" - base_cost = 50 - boon_path = /spell/targeted/torment - -/datum/deity_item/boon/blood_shard - name = "Blood Shard" - desc = "Lets a follower cause a target's blood to literally explode out of their skin into dangerous projectiles." - category = "Dark Spells" - base_cost = 75 - boon_path = /spell/hand/charges/blood_shard - -/datum/deity_item/boon/drain_blood - name = "Drain Blood" - desc = "Lets a follower drain blood from all those around them." - category = "Dark Spells" - base_cost = 110 - boon_path = /spell/aoe_turf/drain_blood - -/datum/deity_item/phenomenon/exude_blood - name = "Phenomenon: Exude Blood" - desc = "You extract the raw blood used in your faith and give it to one of your flock." - category = "Dark Spells" - base_cost = 30 - phenomenon_path = /datum/phenomenon/exude_blood \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/narsie/deity_items/minions.dm b/mods/gamemodes/deity/forms/narsie/deity_items/minions.dm deleted file mode 100644 index be4c8fe14edc..000000000000 --- a/mods/gamemodes/deity/forms/narsie/deity_items/minions.dm +++ /dev/null @@ -1,37 +0,0 @@ -/datum/deity_item/minions - name = DEITY_TREE_DARK_MINION - desc = "Unlock abilities that allow your followers to craft and summon useful creatures." - category = DEITY_TREE_DARK_MINION - base_cost = 75 - -/datum/deity_item/boon/soul_shard - name = "Soul Stone Shard" - desc = "Give your follower a sliver of soulstone to capture a life in." - category = DEITY_TREE_DARK_MINION - requirements = list(DEITY_TREE_DARK_MINION = 1) - base_cost = 20 - boon_path = /obj/item/soulstone - -/datum/deity_item/boon/blood_zombie - name = "Blood Plague" - desc = "Give a vessel to a follower filled with infection so vile, it turns all sapient creatures into mindless husks." - category = DEITY_TREE_DARK_MINION - requirements = list(DEITY_TREE_DARK_MINION = 1) - base_cost = 300 - boon_path = /obj/item/chems/drinks/zombiedrink - -/datum/deity_item/boon/tear_veil - name = "Tear Veil" - desc = "Grant your follower the ability to literally rip a hole in this reality, allowing things to pass through." - category = DEITY_TREE_DARK_MINION - requirements = list(DEITY_TREE_DARK_MINION = 1) - base_cost = 100 - boon_path = /spell/tear_veil - -/datum/deity_item/phenomenon/hellscape - name = "Phenomenon: Reveal Hellscape" - desc = "You show a non-believer what their future will be like." - category = DEITY_TREE_DARK_MINION - requirements = list(DEITY_TREE_DARK_MINION = 1) - base_cost = 110 - phenomenon_path = /datum/phenomenon/hellscape \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/narsie/deity_items/sacrificing.dm b/mods/gamemodes/deity/forms/narsie/deity_items/sacrificing.dm deleted file mode 100644 index 7e5d631c7f93..000000000000 --- a/mods/gamemodes/deity/forms/narsie/deity_items/sacrificing.dm +++ /dev/null @@ -1,42 +0,0 @@ -/datum/deity_item/sacrifice - name = DEITY_TREE_SACRIFICE - desc = "Unlocks the tools necessary to allow your followers to sacrifice in your name." - category = DEITY_TREE_SACRIFICE - base_cost = 50 - max_level = 1 - -/datum/deity_item/boon/sac_dagger - name = "Sacrificial Dagger" - desc = "A small dagger embued with your powers. Lets your followers give you power through sacrifices on an altar." - category = DEITY_TREE_SACRIFICE - requirements = list(DEITY_TREE_SACRIFICE = 1) - base_cost = 10 - boon_path = /obj/item/knife/ritual/sacrifice - -/datum/deity_item/boon/sac_spell - name = "Sacrifice Spell" - desc = "This ability makes the user take INCREDIBLE amounts of damage to heal a target for a similar amount of damage." - category = DEITY_TREE_SACRIFICE - requirements = list(DEITY_TREE_SACRIFICE = 1) - base_cost = 10 - boon_path = /spell/targeted/heal_target/sacrifice - -/datum/deity_item/boon/execution_axe - name = "Greedy Axe" - desc = "This axe can store the very souls of those it kills to be later transfered to you through an altar." - category = DEITY_TREE_SACRIFICE - requirements = list(DEITY_TREE_SACRIFICE = 1) - base_cost = 50 - boon_path = /obj/item/bladed/axe/fire/cult - -/datum/deity_item/blood_stone - name = "Bloodied Stone" - desc = "Unlocks the blood stone building, which allows followers to increase your power through ritual and prayer." - category = DEITY_TREE_SACRIFICE - requirements = list(DEITY_TREE_SACRIFICE = 1) - base_cost = 50 - max_level = 1 - -/datum/deity_item/blood_stone/buy(var/mob/living/deity/user) - ..() - user.form.buildables |= /obj/structure/deity/blood_stone \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/narsie/deity_items/smithing.dm b/mods/gamemodes/deity/forms/narsie/deity_items/smithing.dm deleted file mode 100644 index bcb12ff11e34..000000000000 --- a/mods/gamemodes/deity/forms/narsie/deity_items/smithing.dm +++ /dev/null @@ -1,29 +0,0 @@ -/datum/deity_item/blood_crafting/narsie - recipes = list( - /obj/item/sword/cultblade = 50, - /obj/item/clothing/head/culthood/alt = 10, - /obj/item/clothing/suit/cultrobes/alt = 20 - ) - -/datum/deity_item/blood_crafting/armored - name = DEITY_ARMOR_CRAFT - desc = "Unlock the secrets to tempered blood smithing, allowing your followers to smith more powerful and expensive armaments." - category = DEITY_BLOOD_CRAFT - base_cost = 75 - requirements = list(DEITY_BLOOD_CRAFT = 1) - recipes = list( - /obj/item/clothing/suit/cultrobes/magusred = 80, - /obj/item/clothing/head/culthood/magus = 50, - /obj/structure/constructshell/cult = 70 - ) //also shield? - -/datum/deity_item/blood_crafting/space - name = DEITY_VOID_CRAFT - desc = "Allows your followers to craft space suits, allowing you to finally spread across the cosmos." - category = DEITY_BLOOD_CRAFT - base_cost = 100 - requirements = list(DEITY_BLOOD_CRAFT = 1, DEITY_ARMOR_CRAFT = 1) - recipes = list( - /obj/item/clothing/suit/space/cult = 100, - /obj/item/clothing/head/helmet/space/cult = 70 - ) //Probably more too. \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/narsie/items.dm b/mods/gamemodes/deity/forms/narsie/items.dm deleted file mode 100644 index 56d3ffe89f8f..000000000000 --- a/mods/gamemodes/deity/forms/narsie/items.dm +++ /dev/null @@ -1,77 +0,0 @@ - -//SACRIFICE DAGGER -//If used on a person on an altar, causes the user to carve into them, dealing moderate damage and gaining points for the altar's god. -/obj/item/knife/ritual/sacrifice - name = "sacrificial dagger" - desc = "This knife is dull but well used." - material = /decl/material/solid/stone/cult - -/obj/item/knife/ritual/sacrifice/resolve_attackby(var/atom/a, var/mob/user, var/click_params) - var/turf/T = get_turf(a) - var/obj/structure/deity/altar/altar = locate() in T - if(!altar) - return ..() - if(isliving(a)) - var/mob/living/L = a - var/multiplier = 1 - if(L.mind) - multiplier++ - if(ishuman(L)) - var/mob/living/human/H = L - if(H.should_have_organ(BP_HEART)) - multiplier++ - if(L.stat == DEAD) - to_chat(user, SPAN_WARNING("\The [a] is already dead! There is nothing to take!")) - return - - user.visible_message(SPAN_WARNING("\The [user] hovers \the [src] over \the [a], whispering an incantation.")) - if(!do_after(user,200, L)) - return - user.visible_message(SPAN_DANGER("\The [user] plunges the knife down into \the [a]!")) - L.take_damage(20) - if(altar.linked_god) - altar.linked_god.adjust_power_min(2 * multiplier,0,"from a delicious sacrifice!") - - -//EXEC AXE -//If a person hit by this axe within three seconds dies, sucks in their soul to be harvested at altars. -/obj/item/bladed/axe/fire/cult - name = "terrible axe" - desc = "Its head is sharp and stained red with heavy use." - icon = 'icons/obj/items/weapon/bone_axe.dmi' - var/stored_power = 0 - -/obj/item/bladed/axe/fire/cult/examine(mob/user) - . = ..() - if(stored_power) - to_chat(user, SPAN_NOTICE("It exudes a death-like smell.")) - -/obj/item/bladed/axe/fire/cult/resolve_attackby(var/atom/a, var/mob/user, var/click_params) - if(istype(a, /obj/structure/deity/altar)) - var/obj/structure/deity/altar/altar = a - if(stored_power && altar.linked_god) - altar.linked_god.adjust_power_min(stored_power, "from harvested souls.") - altar.visible_message(SPAN_WARNING("\The [altar] absorbs a black mist exuded from \the [src].")) - return - if(ismob(a)) - var/mob/M = a - if(M.stat != DEAD) - events_repository.register(/decl/observ/death, M,src, TYPE_PROC_REF(/obj/item/bladed/axe/fire/cult, gain_power)) - spawn(30) - events_repository.unregister(/decl/observ/death, M,src) - return ..() - -/obj/item/bladed/axe/fire/cult/proc/gain_power() - stored_power += 50 - src.visible_message(SPAN_OCCULT("\The [src] screeches as the smell of death fills the air!")) - -/obj/item/chems/drinks/zombiedrink - name = "well-used urn" - desc = "Said to bring those who drink it back to life, no matter the price." - icon = 'icons/obj/xenoarchaeology.dmi' - icon_state = "urn" - volume = 10 - amount_per_transfer_from_this = 10 - -/obj/item/chems/drinks/zombiedrink/populate_reagents() - add_to_reagents(/decl/material/liquid/zombie, reagents.maximum_volume) diff --git a/mods/gamemodes/deity/forms/narsie/narsie.dm b/mods/gamemodes/deity/forms/narsie/narsie.dm deleted file mode 100644 index 8ebf2dfc818b..000000000000 --- a/mods/gamemodes/deity/forms/narsie/narsie.dm +++ /dev/null @@ -1,51 +0,0 @@ -/datum/god_form/narsie - name = "Nar-Sie" - info = {"The Geometer of Blood, you crave blood and destruction.
    - Benefits:
    - +Can gain power from blood sacrifices.
    - +Ability to forge weapons and armor.
    - Drawbacks:
    - -Servant abilities require copious amounts of their blood. - "} - desc = "A being made of a million nightmares, a billion deaths." - god_icon_state = "nar-sie" - pylon_icon_state = "shade" - faction = "cult" - - buildables = list( - /obj/structure/deity/altar/narsie, - /obj/structure/deity/pylon - ) - items = list( - /datum/deity_item/general/potential, - /datum/deity_item/general/regeneration, - /datum/deity_item/boon/eternal_darkness, - /datum/deity_item/boon/torment, - /datum/deity_item/boon/blood_shard, - /datum/deity_item/boon/drain_blood, - /datum/deity_item/phenomenon/exude_blood, - /datum/deity_item/sacrifice, - /datum/deity_item/boon/sac_dagger, - /datum/deity_item/boon/sac_spell, - /datum/deity_item/boon/execution_axe, - /datum/deity_item/blood_stone, - /datum/deity_item/minions, - /datum/deity_item/boon/soul_shard, - /datum/deity_item/boon/blood_zombie, - /datum/deity_item/boon/tear_veil, - /datum/deity_item/phenomenon/hellscape, - /datum/deity_item/blood_crafting/narsie, - /datum/deity_item/blood_crafting/armored, - /datum/deity_item/blood_crafting/space - ) - -/datum/god_form/narsie/take_charge(var/mob/living/user, var/charge) - charge = min(100, charge * 0.25) - if(prob(charge)) - to_chat(user, SPAN_WARNING("You feel drained...")) - var/mob/living/human/H = user - if(istype(H) && H.should_have_organ(BP_HEART)) - H.vessel.remove_any(charge) - else - user.take_damage(charge) - return 1 \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm b/mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm deleted file mode 100644 index daf6b5d59d9d..000000000000 --- a/mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm +++ /dev/null @@ -1,37 +0,0 @@ -/spell/tear_veil - name = "Tear Veil" - desc = "Use your mental strength to literally tear a hole from this dimension to the next, letting things through..." - - charge_max = 300 - spell_flags = Z2NOCAST - invocation = "none" - invocation_type = SpI_NONE - - number_of_channels = 0 - time_between_channels = 200 - hud_state = "const_floor" - cast_sound = 'sound/effects/meteorimpact.ogg' - var/list/possible_spawns = list( - /mob/living/simple_animal/hostile/scarybat/cult, - /mob/living/simple_animal/hostile/creature/cult, - /mob/living/simple_animal/hostile/revenant/cult - ) - -/spell/tear_veil/choose_targets() - var/turf/T = get_turf(holder) - holder.visible_message(SPAN_NOTICE("A strange portal rips open underneath \the [holder]!")) - var/obj/effect/gateway/hole = new(get_turf(T)) - hole.density = FALSE - return list(hole) - -/spell/tear_veil/cast(var/list/targets, var/mob/holder, var/channel_count) - if(channel_count == 1) - return - var/type = pick(possible_spawns) - var/mob/living/L = new type(get_turf(targets[1])) - L.faction = holder.faction - L.visible_message(SPAN_WARNING("\A [L] escapes from the portal!")) - -/spell/tear_veil/after_spell(var/list/targets) - qdel(targets[1]) - return \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/narsie/structures.dm b/mods/gamemodes/deity/forms/narsie/structures.dm deleted file mode 100644 index af576963a1f0..000000000000 --- a/mods/gamemodes/deity/forms/narsie/structures.dm +++ /dev/null @@ -1,36 +0,0 @@ -/obj/structure/deity/altar/narsie - name = "altar" - desc = "A small desk, covered in blood." - icon_state = "talismanaltar" - -//BLOODLETTING STRUCTURE -//A follower can stand here and mumble prayers as they let their blood flow slowly into the structure. -/obj/structure/deity/blood_stone - name = "bloody stone" - desc = "A jagged stone covered in the various stages of blood, from dried to fresh." - icon_state = "blood_stone" - // TODO: material-based health for deity structures - current_health = 100 //It's a piece of rock. - build_cost = 700 - -/obj/structure/deity/blood_stone/attack_hand(var/mob/user) - if(!linked_god || !linked_god.is_follower(user, silent = 1) || !ishuman(user)) - return ..() - var/mob/living/human/H = user - user.visible_message( - SPAN_WARNING("\The [user] calmly slices their finger on \the [src], smearing their blood over the black stone."), - SPAN_WARNING("You slowly slide your finger down one of \the [src]'s sharp edges, smearing your blood over its smooth surface.") - ) - while(do_after(H, 5 SECONDS, src)) - user.audible_message("\The [user] utters something under their breath.", SPAN_OCCULT("You mutter a dark prayer to your master as you feel the stone eat away at your lifeforce.")) - if(H.should_have_organ(BP_HEART)) - H.drip(5, get_turf(src)) - else - H.take_damage(5) - linked_god.adjust_power_min(1, 1) - return TRUE - -/datum/codex_entry/deity/blood_stone - associated_paths = list(/obj/structure/deity/blood_stone) - antag_text = "Allows the user to feed blood directly to the aligned deity, granting it power." - disambiguator = "occult" \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/deity_items/artifacts.dm b/mods/gamemodes/deity/forms/starlight/deity_items/artifacts.dm deleted file mode 100644 index 286be5afd10f..000000000000 --- a/mods/gamemodes/deity/forms/starlight/deity_items/artifacts.dm +++ /dev/null @@ -1,32 +0,0 @@ -/datum/deity_item/boon/blazing_blade - name = "Blazing Blade" - desc = "A divine blade of burning fury. If it stays too far away from an altar of some sort, it disappears." - base_cost = 250 - category = DEITY_TREE_ARTIFACT - boon_path = /obj/item/sword/blazing - -/datum/deity_item/boon/holy_beacon - name = "Holy Beacon" - desc = "A staff capable of producing an almost harmless bolt of sunlight, capable of blinding anyone in the room, at least for a while." - base_cost = 200 - category = DEITY_TREE_ARTIFACT - boon_path = /obj/item/gun/energy/staff/beacon - -/datum/deity_item/boon/black_death - name = "Black Death" - desc = "A small dagger capable of poisoning those it bites. Careful, if it loses all its charges, it will burn the user. It can be recharged at a radiant statue." - base_cost = 150 - category = DEITY_TREE_ARTIFACT - boon_path = /obj/item/knife/ritual/shadow - -/datum/deity_item/blood_crafting/firecrafting - name = "Fire Crafting" - desc = "Gain the ability for your minions to build smithing stations that can make many rings of power." - base_cost = 300 - category = DEITY_TREE_ARTIFACT - max_level = 1 - forge_type = /obj/structure/deity/blood_forge/starlight - recipes = list(/obj/item/clothing/gloves/ring/aura_ring/talisman_of_starborn = 70, - /obj/item/clothing/gloves/ring/aura_ring/talisman_of_blueforged = 70, - /obj/item/clothing/gloves/ring/aura_ring/talisman_of_shadowling = 70 - ) \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/deity_items/phenomena.dm b/mods/gamemodes/deity/forms/starlight/deity_items/phenomena.dm deleted file mode 100644 index 37f830d27cbe..000000000000 --- a/mods/gamemodes/deity/forms/starlight/deity_items/phenomena.dm +++ /dev/null @@ -1,41 +0,0 @@ -/datum/deity_item/phenomenon/herald - name = "Choose Herald" - desc = "Gives you the ability to choose a herald. Can only be used once so be careful." - phenomenon_path = /datum/phenomenon/herald - base_cost = 100 - category = DEITY_TREE_HERALD - -/datum/deity_item/phenomenon/wisp - name = "Summon Wisp" - desc = "Manipulate around a small light." - phenomenon_path = /datum/phenomenon/movable_object/wisp - base_cost = 100 - category = DEITY_TREE_HERALD - -/datum/deity_item/phenomenon/flickering_whisper - name = "Flickering Whisper" - desc = "Send a subtle message to a non-follower, and see what they see for a while." - phenomenon_path = /datum/phenomenon/flickering_whisper - base_cost = 50 - category = DEITY_TREE_HERALD - -/datum/deity_item/phenomenon/burning_glare - name = "Burning Glare" - desc = "Use your divine power to physically burn a person." - phenomenon_path = /datum/phenomenon/burning_glare - base_cost = 200 - category = DEITY_TREE_HERALD - -/datum/deity_item/phenomenon/open_gateway - name = "Open Gateway" - desc = "Unlocks the ability to open a gateway. Required for rebirth." - phenomenon_path = /datum/phenomenon/create_gateway - base_cost = 250 - category = DEITY_TREE_HERALD - -/datum/deity_item/phenomenon/divine_right - name = "Divine Right" - desc = "Unlocks the ability to possess your Herald, permanently transforming you into a physical god." - phenomenon_path = /datum/phenomenon/divine_right - base_cost = 300 - category = DEITY_TREE_HERALD \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/deity_items/spells.dm b/mods/gamemodes/deity/forms/starlight/deity_items/spells.dm deleted file mode 100644 index 33c6e19a55a5..000000000000 --- a/mods/gamemodes/deity/forms/starlight/deity_items/spells.dm +++ /dev/null @@ -1,62 +0,0 @@ -/datum/deity_item/boon/starburst - name = "Starburst" - desc = "Grant your minion the power to blind non-followers nearby." - base_cost = 60 - boon_path = /spell/targeted/genetic/blind/starburst - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/exchange_wounds - name = "Exchange Wounds" - desc = "Allow a follower to sacrifice their own well-being for that of those around them." - base_cost = 120 - boon_path = /spell/aoe_turf/exchange_wounds - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/radiant_aura - name = "Radiant Aura" - desc = "This spell makes one of your followers immune to laser fire, for a short while at least." - base_cost = 70 - boon_path = /spell/radiant_aura/starlight - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/burning_touch - name = "Burning Touch" - desc = "Sets your minion's hand aflame, allowing them to burn people with an ever-increasing flame." - base_cost = 60 - boon_path = /spell/targeted/equip_item/burning_hand - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/burning_grip - name = "Burning Grip" - desc = "Give your follower the ability to burn an item in someone's hand so badly it causes them to burn." - base_cost = 50 - boon_path = /spell/hand/burning_grip - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/blood_boil - name = "Blood Boil" - desc = "Allow a follower to concentrate so deeply on a target that their body temperature increases, eventually setting them on fire." - base_cost = 90 - boon_path = /spell/targeted/blood_boil - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/fireball - name = "Fireball" - desc = "A classic spell, grants your follower the ability to throw an exploding ball of flame in any direction." - base_cost = 100 - boon_path = /spell/targeted/projectile/dumbfire/fireball - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/emp - name = "Disable Machinery" - desc = "Gives your follower a spell of disabling machinery, and mechanical hearts." - base_cost = 110 - boon_path = /spell/aoe_turf/disable_tech/starlight - category = DEITY_TREE_FIRECONJ - -/datum/deity_item/boon/cure_light - name = "Cure Light Wounds" - desc = "Grant mercy on your followers, giving them the ability to heal themselves slightly." - base_cost = 70 - boon_path = /spell/targeted/heal_target - category = DEITY_TREE_FIRECONJ \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/items.dm b/mods/gamemodes/deity/forms/starlight/items.dm deleted file mode 100644 index 31645b2d3acd..000000000000 --- a/mods/gamemodes/deity/forms/starlight/items.dm +++ /dev/null @@ -1,159 +0,0 @@ -/obj/item/clothing/gloves/ring/aura_ring - var/obj/aura/granted_aura - -/obj/item/clothing/gloves/ring/aura_ring/equipped(var/mob/living/L, var/slot) - ..() - if(granted_aura && slot == slot_gloves_str) - L.add_aura(granted_aura) - -/obj/item/clothing/gloves/ring/aura_ring/dropped(var/mob/living/L) - ..() - if(granted_aura) - L.remove_aura(granted_aura) - -/obj/item/clothing/gloves/ring/aura_ring/Destroy() - QDEL_NULL(granted_aura) - . = ..() - -/obj/item/clothing/gloves/ring/aura_ring/talisman_of_starborn - name = "Talisman of the Starborn" - desc = "This ring seems to shine with more light than is put on it." - icon = 'icons/clothing/accessories/jewelry/rings/ring_star.dmi' - material = /decl/material/solid/metal/blackbronze - -/obj/item/clothing/gloves/ring/aura_ring/talisman_of_starborn/Initialize() - . = ..() - granted_aura = new /obj/aura/starborn() - -/obj/item/clothing/gloves/ring/aura_ring/talisman_of_blueforged - name = "Talisman of the Blueforged" - desc = "The gem on this ring is quite peculiar..." - icon = 'icons/clothing/accessories/jewelry/rings/ring_blue.dmi' - -/obj/item/clothing/gloves/ring/aura_ring/talisman_of_blueforged/Initialize() - . = ..() - granted_aura = new /obj/aura/blueforge_aura() - -/obj/item/clothing/gloves/ring/aura_ring/talisman_of_shadowling - name = "Talisman of the Shadowling" - desc = "If you weren't looking at this, you probably wouldn't have noticed it." - icon = 'icons/clothing/accessories/jewelry/rings/ring_shadow.dmi' - material = /decl/material/solid/metal/blackbronze - -/obj/item/clothing/gloves/ring/aura_ring/talisman_of_shadowling/Initialize() - . = ..() - granted_aura = new /obj/aura/shadowling_aura() - -/obj/item/clothing/suit/armor/sunsuit - name = "knight's armor" - desc = "Now, you can be the knight in shining armor you've always wanted to be. With complementary sun insignia." - icon = 'icons/clothing/suits/deity/star_champion.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_VERY_HIGH, - ARMOR_BULLET = ARMOR_BALLISTIC_AP, - ARMOR_LASER = ARMOR_LASER_HANDGUNS, - ARMOR_ENERGY = ARMOR_ENERGY_RESISTANT, - ARMOR_BOMB = ARMOR_BOMB_PADDED, - ARMOR_BIO = ARMOR_BIO_MINOR - ) - -/obj/item/clothing/head/helmet/sunhelm - name = "knight's helm" - desc = "It's a shiny metal helmet. It looks ripped straight out of the Dark Ages, actually." - icon = 'icons/clothing/head/star_champion.dmi' - flags_inv = HIDEEARS | BLOCK_ALL_HAIR - -/obj/item/clothing/suit/armor/sunrobe - name = "oracle's robe" - desc = "The robes of a priest. One that praises the sun, apparently. Well, it certainly reflects light well." - icon = 'icons/clothing/suits/deity/star_oracle.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_KNIVES, - ARMOR_BULLET = ARMOR_BALLISTIC_SMALL, - ARMOR_LASER = ARMOR_LASER_HANDGUNS, - ARMOR_ENERGY = ARMOR_ENERGY_RESISTANT, - ARMOR_BOMB = ARMOR_BOMB_PADDED, - ARMOR_BIO = ARMOR_BIO_MINOR - ) - -/obj/item/clothing/suit/armor/sunrobe/Initialize() - . = ..() - set_light(4, 0.3) - -/obj/item/clothing/suit/space/shadowsuit - name = "traitor's cloak" - desc = "There is absolutely nothing visible through the fabric. The shadows stick to your skin when you touch it." - item_flags = ITEM_FLAG_THICKMATERIAL | ITEM_FLAG_AIRTIGHT - min_pressure_protection = 0 - icon = 'icons/clothing/suits/deity/star_traitor.dmi' - -/obj/item/clothing/head/helmet/space/shadowhood - name = "traitor's hood" - desc = "No light can pierce this hood. It's unsettling." - icon = 'icons/clothing/head/star_traitor.dmi' - flags_inv = HIDEEARS | BLOCK_ALL_HAIR - -/obj/item/knife/ritual/shadow - name = "black death" - desc = "An obsidian dagger. The singed remains of a green cloth are wrapped around the 'handle.'" - var/charge = 5 - -/obj/item/knife/ritual/shadow/apply_hit_effect(var/mob/living/target, var/mob/living/user, var/hit_zone) - . = ..() - if(charge) - if(target.get_damage(BRUTE) > 15) - var/datum/reagents/R = target.reagents - if(!R) - return - R.add_reagent(/decl/material/liquid/venom, 5) - new /obj/effect/temporary(get_turf(target),3, 'icons/effects/effects.dmi', "fire_goon") - charge-- - else - user.take_damage(5, BURN) - if(prob(5)) - to_chat(user, SPAN_WARNING("\The [src] appears to be out of power!")) - new /obj/effect/temporary(get_turf(user),3, 'icons/effects/effects.dmi', "fire_goon") - -/obj/item/gun/energy/staff/beacon - name = "holy beacon" - desc = "Look closely into its crystal; there's a miniature sun. Or maybe that's just some fancy LEDs. Either way, it looks thoroughly mystical." - icon = 'icons/obj/wizard.dmi' - icon_state = "starstaff" - self_recharge = 0 - max_shots = 10 - projectile_type = /obj/item/projectile/energy/flash - required_antag_type = /decl/special_role/godcultist - -/obj/item/sword/blazing - name = "blazing blade" - icon = 'icons/obj/items/weapon/swords/flaming.dmi' - atom_damage_type = BURN - material_alteration = MAT_FLAG_ALTERATION_NONE - var/last_near_structure = 0 - var/mob/living/deity/linked - -/obj/item/sword/blazing/Initialize(var/maploading, var/material, var/deity) - . = ..() - START_PROCESSING(SSobj, src) - linked = deity - -/obj/item/sword/blazing/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/sword/blazing/Process() - if(!linked || last_near_structure + 10 SECONDS > world.time) - return - - if(linked.near_structure(src,1)) - if(last_near_structure < world.time - 30 SECONDS) - to_chat(loc, SPAN_NOTICE("\The [src] surges with power anew!")) - last_near_structure = world.time - else - if(last_near_structure < world.time - 30 SECONDS) //If it has been at least 30 seconds. - if(prob(5)) - to_chat(loc, SPAN_WARNING("\The [src] begins to fade, its power dimming this far away from a shrine.")) - else if(last_near_structure + 1800 < world.time) - visible_message(SPAN_WARNING("\The [src] disintegrates into a pile of ash!")) - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) diff --git a/mods/gamemodes/deity/forms/starlight/mobs.dm b/mods/gamemodes/deity/forms/starlight/mobs.dm deleted file mode 100644 index 5748cf2e2f40..000000000000 --- a/mods/gamemodes/deity/forms/starlight/mobs.dm +++ /dev/null @@ -1,29 +0,0 @@ -/mob/living/starlight_soul - name = "soul" - desc = "A captured soul." - anchored = TRUE - butchery_data = null - -/mob/living/starlight_soul/Initialize(var/maploading, var/mob/living/old_mob) - . = ..() - if(old_mob) - name = old_mob.real_name - -/mob/living/starlight_soul/proc/set_deity(var/mob/living/deity/deity) - var/mob/observer/eye/freelook/cult/eye - if(eyeobj) - eye = eyeobj - eyeobj.release(src) - else - eye = new(src) - eye.suffix = "Soul" - eyeobj = eye - eye.visualnet = deity.eyenet - var/decl/special_role/godcultist/godcult = GET_DECL(/decl/special_role/godcultist) - godcult.add_antagonist_mind(src.mind,1,"lost soul of [deity]", "You have been captured by \the [deity]! You now can only see into your own reality through the same rips and tears it uses. Your only chance at another body will be one in your captor's image...",specific_god=deity) - eyeobj.possess(src) - -/mob/living/starlight_soul/Destroy() - if(eyeobj) - QDEL_NULL(eyeobj) - . = ..() \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/spells/disable_tech.dm b/mods/gamemodes/deity/forms/starlight/spells/disable_tech.dm deleted file mode 100644 index f6462ae33fed..000000000000 --- a/mods/gamemodes/deity/forms/starlight/spells/disable_tech.dm +++ /dev/null @@ -1,4 +0,0 @@ -/spell/aoe_turf/disable_tech/starlight - hidden_from_codex = TRUE - charge_max = 600 - spell_flags = 0 \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/spells/starlight_aura.dm b/mods/gamemodes/deity/forms/starlight/spells/starlight_aura.dm deleted file mode 100644 index 8ad867e41b3e..000000000000 --- a/mods/gamemodes/deity/forms/starlight/spells/starlight_aura.dm +++ /dev/null @@ -1,5 +0,0 @@ -/spell/radiant_aura/starlight - name = "Starlight Aura" - desc = "This spell makes you immune to laser fire, for a short while at least." - spell_flags = 0 - charge_max = 400 diff --git a/mods/gamemodes/deity/forms/starlight/spells/veil_of_shadows.dm b/mods/gamemodes/deity/forms/starlight/spells/veil_of_shadows.dm deleted file mode 100644 index e47edecd0822..000000000000 --- a/mods/gamemodes/deity/forms/starlight/spells/veil_of_shadows.dm +++ /dev/null @@ -1,57 +0,0 @@ -/spell/veil_of_shadows - name = "Veil of Shadows" - desc = "Become intangable, invisible. Like a ghost." - charge_max = 400 - invocation_type = SpI_EMOTE - invocation = "flickers out of existance" - school = "Divine" - spell_flags = 0 - duration = 100 - var/timer_id - var/light_steps = 4 - - hud_state = "wiz_statue" - -/spell/veil_of_shadows/choose_targets() - if(!timer_id && ishuman(holder)) - return list(holder) - . = null - -/spell/veil_of_shadows/cast(var/list/targets, var/mob/user) - var/mob/living/human/H = user - H.AddMovementHandler(/datum/movement_handler/mob/incorporeal) - if(H.add_cloaking_source(src)) - H.visible_message(SPAN_WARNING("\The [H] shrinks from view!")) - events_repository.register(/decl/observ/moved, H,src,PROC_REF(check_light)) - timer_id = addtimer(CALLBACK(src,PROC_REF(cancel_veil)),duration, TIMER_STOPPABLE) - -/spell/veil_of_shadows/proc/cancel_veil() - var/mob/living/human/H = holder - H.RemoveMovementHandler(/datum/movement_handler/mob/incorporeal) - deltimer(timer_id) - timer_id = null - var/turf/T = get_turf(H) - if(T.get_lumcount() > 0.1) //If we're somewhere somewhat shadowy we can stay invis as long as we stand still - drop_cloak() - else - events_repository.unregister(/decl/observ/moved, H,src) - events_repository.register(/decl/observ/moved, H,src,PROC_REF(drop_cloak)) - -/spell/veil_of_shadows/proc/drop_cloak() - var/mob/living/human/H = holder - if(H.remove_cloaking_source(src)) - H.visible_message(SPAN_NOTICE("\The [H] appears from nowhere!")) - events_repository.unregister(/decl/observ/moved, H,src) - -/spell/veil_of_shadows/proc/check_light() - if(light_steps) - light_steps-- - return - light_steps = initial(light_steps) - for(var/obj/machinery/light/light in view(1,holder)) - light.flicker(20) - -/spell/veil_of_shadows/Destroy() - deltimer(timer_id) - cancel_veil() - .= ..() \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/starlight.dm b/mods/gamemodes/deity/forms/starlight/starlight.dm deleted file mode 100644 index 1469947fe10a..000000000000 --- a/mods/gamemodes/deity/forms/starlight/starlight.dm +++ /dev/null @@ -1,47 +0,0 @@ -/datum/god_form/starlight - name = "Starlight Herald" - info = {"Sun and fire incarnate.
    - Benefits:
    - +Ability to summon powerful minions via sacrifices.
    - +Bless one of your minions as a Herald, giving them species powers and armor.
    - Drawbacks:
    - -Servant's powers will burn them.
    - -You require copious amounts of power regeneration.
    - "} - desc = "The bringer of life, and all that entails." - god_icon_state = "sungod" - pylon_icon_state = "god" - faction = "herald" - - buildables = list(/obj/structure/deity/altar/starlight, - /obj/structure/deity/pylon/starlight, - /obj/structure/deity/radiant_statue, - ) - items = list(/datum/deity_item/general/potential, - /datum/deity_item/general/regeneration, - /datum/deity_item/boon/blazing_blade, - /datum/deity_item/boon/holy_beacon, - /datum/deity_item/boon/black_death, - /datum/deity_item/blood_crafting/firecrafting, - /datum/deity_item/boon/starburst, - /datum/deity_item/boon/exchange_wounds, - /datum/deity_item/boon/radiant_aura, - /datum/deity_item/boon/burning_touch, - /datum/deity_item/boon/burning_grip, - /datum/deity_item/boon/blood_boil, - /datum/deity_item/boon/emp, - /datum/deity_item/boon/cure_light, - /datum/deity_item/phenomenon/herald, - /datum/deity_item/phenomenon/wisp, - /datum/deity_item/phenomenon/flickering_whisper, - /datum/deity_item/phenomenon/burning_glare, - /datum/deity_item/phenomenon/open_gateway, - /datum/deity_item/phenomenon/divine_right - ) - -/datum/god_form/starlight/take_charge(var/mob/living/user, var/charge) - charge = max(5, charge/100) - if(prob(charge)) - to_chat(user, SPAN_DANGER("Your body burns!")) - user.take_damage(charge, BURN) - return 1 \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/starlight/structures.dm b/mods/gamemodes/deity/forms/starlight/structures.dm deleted file mode 100644 index 7134d19ce481..000000000000 --- a/mods/gamemodes/deity/forms/starlight/structures.dm +++ /dev/null @@ -1,272 +0,0 @@ -/obj/structure/deity/altar/starlight - icon_state = "altarcandle" - -/obj/structure/deity/pylon/starlight - name = "sun pylon" - desc = "A miniature sun, floating ontop of a small pillar." - icon_state = "star_pylon" - -/obj/structure/deity/gateway - name = "gateway" - desc = "A gateway into the unknown." - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - power_adjustment = 1 - density = FALSE - var/weakref/target_ref - var/start_time = 0 - var/power_drain = 7 - var/looking_for - var/static/list/possible_forms = list( - "Starborn" = list( - "description" = "A species of hardy fire-wreathed soldiers.", - "message" = "As a Starborn, you are immune to laser-fire. You are a hardy soldier, able to take on the greatest of foes.", - "species" = "Starborn" - ), - "Blueforged" = list( - "description" = "Trans-dimensional beings with a multitude of miraculous abilities.", - "message" = "As a Blueforged, you are immune to all physical damage... except for heat. Not even your god can protect you.", - "species" = "Blueforged", - "spells" = list( - /spell/targeted/ethereal_jaunt, - /spell/targeted/shatter, - /spell/hand/burning_grip, - /spell/aoe_turf/disable_tech, - /spell/targeted/projectile/magic_missile, - /spell/open_gateway - ) - ), - "Shadowling" = list( - "description" = "Beings that come from a place of no light. They sneak from place to place, disabling everyone they touch.", - "message" = "As a Shadow you take damage from the light itself but have the ability to vanish from sight itself.", - "species" = "Shadow", - "spells" = list( - /spell/veil_of_shadows, - /spell/targeted/subjugation, - /spell/targeted/projectile/magic_missile - ) - ) - ) - -/obj/structure/deity/gateway/Initialize() - . = ..() - if(linked_god) - linked_god.power_per_regen -= power_drain - START_PROCESSING(SSobj, src) - -/obj/structure/deity/gateway/Process() - if(!linked_god) - return - if(linked_god.power <= 0) - to_chat(linked_god,SPAN_WARNING("\The [src] disappears from your lack of power!")) - qdel(src) - return - var/mob/living/human/target - if(target_ref) - target = target_ref.resolve() - if(target) - if(get_turf(target) != get_turf(src)) - target = null - target_ref = null - start_time = 0 - return - else if(prob(5)) - to_chat(target,SPAN_DANGER("\The [src] sucks at your lifeforce!")) - if(start_time && world.time > start_time + 300) - start_time = 0 - to_chat(target,SPAN_DANGER("You have been sucked into \the [src], your soul used to fuel \the [linked_god]'s minions.")) - var/mob/living/starlight_soul/ss = new(get_turf(linked_god),target) - if(target.mind) - target.mind.transfer_to(ss) - else - ss.ckey = target.ckey - ss.set_deity(linked_god) - target.dust() - if(power_drain >= 3) - linked_god.power_per_regen += 3 - power_drain -= 3 - else - //Get new target - var/mob/living/human/T = locate() in get_turf(src) - if(T) - target_ref = weakref(T) - start_time = world.time - to_chat(T, SPAN_DANGER("You feel your lifeforce begin to drain into \the [src]!")) - -/obj/structure/deity/gateway/Destroy() - linked_god.power_per_regen += power_drain - . = ..() - -/obj/structure/deity/gateway/attack_deity(var/mob/living/deity/deity) - var/list/html = list() - html += "

    Servant List

    " - html += "
    Select a minion type to summon
    " - html += "" - for(var/a in possible_forms) - var/list/form = possible_forms[a] - html += "" - html += "
    NameDescription
    [a][form["description"]]
    " - show_browser(linked_god, jointext(html, null), "window=gateway") - -/obj/structure/deity/gateway/CanUseTopic(var/mob/user) - if(linked_god && (user == linked_god || user.loc == linked_god.loc)) - return STATUS_INTERACTIVE - return STATUS_CLOSE - -/obj/structure/deity/gateway/proc/stop_looking_for(var/successful) - if(looking_for) - if(!successful) - to_chat(linked_god, SPAN_WARNING("\The [src] did not find any [looking_for]. You may try again if you wish.")) - looking_for = null - -/obj/structure/deity/gateway/OnTopic(var/mob/user, var/list/href_list) - if(href_list["accept"] && istype(user,/mob/living/starlight_soul)) - if(href_list["accept"] != looking_for) - return TOPIC_HANDLED - var/mob/living/human/H = new(get_turf(src)) - user.mind.transfer_to(H) - H.set_species(possible_forms[looking_for]["species"]) - for(var/s in possible_forms[looking_for]["spells"]) - var/spell/S = new s - H.add_spell(S) - var/decl/special_role/godcultist/godcult = GET_DECL(/decl/special_role/godcultist) - godcult.add_antagonist_mind(H.mind, 1, "[looking_for] of [linked_god]", "You are a powerful entity in the service to \the [linked_god]. [possible_forms[looking_for]["species"]]", specific_god = linked_god) - stop_looking_for(TRUE) - - return TOPIC_HANDLED - if(href_list["spawn_type"] && user == linked_god) - if(looking_for) - to_chat(usr, SPAN_WARNING("\The [src] is already looking for a [looking_for].")) - else - looking_for = href_list["spawn_type"] - to_chat(usr, SPAN_NOTICE("\The [src] is now looking for a [looking_for].")) - for(var/l in get_turf(linked_god)) - if(istype(l, /mob/living/starlight_soul)) - to_chat(l, "\The [src] is looking for a soul to become a [looking_for]. Accept? (Yes)") - addtimer(CALLBACK(src, PROC_REF(stop_looking_for), FALSE), 30 SECONDS) - show_browser(linked_god, null, "window=gateway") - return TOPIC_HANDLED - -/obj/structure/deity/radiant_statue - name = "radiant statue" - icon_state = "statue" - build_cost = 750 - power_adjustment = 1 - deity_flags = DEITY_STRUCTURE_NEAR_IMPORTANT|DEITY_STRUCTURE_ALONE - var/charge = 0 - var/charging = 0 //Charging, dispersing, etc. - -/obj/structure/deity/radiant_statue/on_update_icon() - ..() - if(charging) - icon_state = "statue_charging" - else if(charge) - icon_state = "statue_active" - else - icon_state = "statue" - -/obj/structure/deity/radiant_statue/Destroy() - if(charging) - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/structure/deity/radiant_statue/proc/get_followers_nearby() - . = list() - if(linked_god) - for(var/m in linked_god.minions) - var/datum/mind/M = m - if(get_dist(M.current, src) <= 3) - . += M.current - -/obj/structure/deity/radiant_statue/attack_hand(var/mob/L) - SHOULD_CALL_PARENT(FALSE) - var/obj/O = L.get_equipped_item(slot_wear_suit_str) - if(O && has_extension(O,/datum/extension/deity_be_near)) - if(activate_charging()) - to_chat(L, SPAN_NOTICE("You place your hands on \the [src], feeling your master's power course through you.")) - else - to_chat(L, SPAN_WARNING("\The [src] has already been activated.")) - else - to_chat(L, SPAN_WARNING("\The [src] does not recognize you as a herald of \the [linked_god]. You must wear a full set of herald's armor.")) - return TRUE - -/obj/structure/deity/radiant_statue/attack_deity(var/mob/living/deity/deity) - if(activate_charging()) - to_chat(deity,SPAN_NOTICE("You activate \the [src], and it begins to charge as long as at least one of your followers is nearby.")) - else - to_chat(deity,SPAN_WARNING("\The [src] is either already activated, or there are no followers nearby to charge it.")) - -/obj/structure/deity/radiant_statue/proc/activate_charging() - var/list/followers = get_followers_nearby() - if(is_processing || !followers.len) - return 0 - charging = 1 - START_PROCESSING(SSobj, src) - src.visible_message(SPAN_NOTICE("\The [src] hums, activating.")) - update_icon() - return 1 - -/obj/structure/deity/radiant_statue/attackby(var/obj/item/I, var/mob/user) - if(charging && (istype(I, /obj/item/knife/ritual/shadow) || istype(I, /obj/item/gun/energy/staff/beacon)) && charge_item(I, user)) - return - ..() - -/obj/structure/deity/radiant_statue/proc/charge_item(var/obj/item/I, var/mob/user) - . = 0 - if(istype(I, /obj/item/gun/energy)) - var/obj/item/gun/energy/energy = I - var/obj/item/cell/power_supply = energy.get_cell() - if(power_supply) - power_supply.give(energy.charge_cost * energy.max_shots) - . = 1 - else if(istype(I ,/obj/item/knife/ritual/shadow)) - var/obj/item/knife/ritual/shadow/shad = I - shad.charge = initial(shad.charge) - . = 1 - if(.) - to_chat(user, SPAN_NOTICE("\The [src]'s glow envelops \the [I], restoring it to proper use.")) - charge -= 1 - -/obj/structure/deity/radiant_statue/Process() - if(charging) - charge++ - var/list/followers = get_followers_nearby() - if(followers.len == 0) - stop_charging() - return - - if(charge == 40) - src.visible_message(SPAN_NOTICE("\The [src] lights up, pulsing with energy.")) - charging = 0 - update_icon() - else - charge -= 0.5 - var/list/followers = get_followers_nearby() - if(followers.len) - for(var/m in followers) - var/mob/living/L = m - L.heal_damage(BURN, 5) - if(prob(5)) - to_chat(L, SPAN_NOTICE("You feel a pleasant warmth spread throughout your body...")) - for(var/s in L.mind.learned_spells) - var/spell/spell = s - spell.charge_counter = spell.charge_max - if(charge == 0) - stop_charging() - -/obj/structure/deity/radiant_statue/proc/stop_charging() - STOP_PROCESSING(SSobj, src) - src.visible_message(SPAN_NOTICE("\The [src] powers down, returning to its dormant form.")) - charging = 0 - update_icon() - -/obj/structure/deity/blood_forge/starlight - name = "radiant forge" - desc = "a swath of heat and fire permeats from this forge." - recipe_feat_list = "Fire Crafting" - text_modifications = list( - "Cost" = "Burn", - "Dip" = "fire. Pain envelopes you as dark burns mar your hands and you begin to shape it into something more useful", - "Shape" = "You shape the fire, ignoring the painful burns it gives you in the process.", - "Out" = "flames" - ) diff --git a/mods/gamemodes/deity/forms/tower/deity_items/conjuration.dm b/mods/gamemodes/deity/forms/tower/deity_items/conjuration.dm deleted file mode 100644 index 0d6f820540ba..000000000000 --- a/mods/gamemodes/deity/forms/tower/deity_items/conjuration.dm +++ /dev/null @@ -1,107 +0,0 @@ -/datum/deity_item/conjuration - name = DEITY_TREE_CONJURATION - desc = "Conjuration is the school of creation and teleportation, summoning fireballs or teleporting long distances, this school is extremely powerful." - category = DEITY_TREE_CONJURATION - max_level = 3 - base_cost = 50 - -/datum/deity_item/conjuration/get_cost(var/mob/living/deity/D) - return base_cost * (level + 1) - -//Level 1 -/datum/deity_item/boon/single_charge/create_air - name = "Create Air" - desc = "Allows your follower to generate a livable atmosphere in the area they are in." - base_cost = 25 - category = DEITY_TREE_CONJURATION - boon_path = /spell/create_air/tower - requirements = list(DEITY_TREE_CONJURATION = 1) - -/datum/deity_item/boon/single_charge/acid_spray - name = "Acid Spray" - desc = "The simplest form of aggressive conjuration: acid spray is quite effective in melting both man and object." - base_cost = 130 - category = DEITY_TREE_CONJURATION - boon_path = /spell/acid_spray/tower - requirements = list(DEITY_TREE_CONJURATION = 1) - -/datum/deity_item/boon/single_charge/force_wall - name = "Force Wall" - desc = "A temporary invincible wall for followers to summon." - base_cost = 30 - category = DEITY_TREE_CONJURATION - boon_path = /spell/aoe_turf/conjure/forcewall/tower - requirements = list(DEITY_TREE_CONJURATION = 1) - -/datum/deity_item/phenomenon/dimensional_locker - name = "Phenomenon: Dimensional Locker" - desc = "Gain the ability to move a magical locker around. While it cannot move living things, you can move it around as you please, even disappearing it into the nether." - base_cost = 50 - category = DEITY_TREE_CONJURATION - phenomenon_path = /datum/phenomenon/movable_object/dimensional_locker - requirements = list(DEITY_TREE_CONJURATION = 1) - -//Level 2 -/datum/deity_item/boon/single_charge/faithful_hound - name = "Faithful Hound" - desc = "This spell allows a follower to summon a singular spectral dog that guards the nearby area. Anyone without the password is barked at or bitten." - base_cost = 40 - category = DEITY_TREE_CONJURATION - boon_path = /spell/aoe_turf/conjure/faithful_hound/tower - requirements = list(DEITY_TREE_CONJURATION = 2) - -/datum/deity_item/wizard_armaments - name = DEITY_UNLOCK_ARMS - desc = "Unlock spells related to the summoning of weapons and armor. These spells only last a short duration, but are extremely effective." - base_cost = 25 - category = DEITY_TREE_CONJURATION - requirements = list(DEITY_TREE_CONJURATION = 2) - -/datum/deity_item/boon/single_charge/sword - name = "Summon Sword" - desc = "This spell allows your followers to summon a golden firey sword for a short duration." - base_cost = 50 - boon_path = /spell/targeted/equip_item/dyrnwyn/tower - category = DEITY_TREE_CONJURATION - requirements = list(DEITY_UNLOCK_ARMS = 1) - -/datum/deity_item/boon/single_charge/shield - name = "Summon Shield" - desc = "This spell allows your followers to summon a magical shield for a short duration." - base_cost = 20 - boon_path = /spell/targeted/equip_item/shield/tower - category = DEITY_TREE_CONJURATION - requirements = list(DEITY_UNLOCK_ARMS = 1) - -/datum/deity_item/phenomenon/portals - name = "Phenomenon: Portals" - desc = "Gain the ability to create portals for your followers to enter through. You will need to create two for it work. Any created past that will delete the oldest portal." - base_cost = 75 - requirements = list(DEITY_TREE_CONJURATION = 2) - category = DEITY_TREE_CONJURATION - phenomenon_path = /datum/phenomenon/portals - -//Level 3 -/datum/deity_item/boon/single_charge/fireball - name = "Fireball" - desc = "Embue your follower with the power of exploding fire." - base_cost = 85 - boon_path = /spell/targeted/projectile/dumbfire/fireball/tower - category = DEITY_TREE_CONJURATION - requirements = list(DEITY_TREE_CONJURATION = 3) - -/datum/deity_item/boon/single_charge/force_portal - name = "Force Portal" - desc = "This spell allows a follower to summon a force portal. Anything that hits the portal gets sucked inside and is then thrown out when the portal explodes." - base_cost = 45 - boon_path = /spell/aoe_turf/conjure/force_portal/tower - category = DEITY_TREE_CONJURATION - requirements = list(DEITY_TREE_CONJURATION = 3) - -/datum/deity_item/phenomenon/banishing_smite - name = "Phenomenon: Banishing Smite" - desc = "Gain the ability to smite an individual, dealing damage to them. If they are weakened enough, this can cause them to temporarily be transported." - base_cost = 75 - requirements = list(DEITY_TREE_CONJURATION = 3) - category = DEITY_TREE_CONJURATION - phenomenon_path = /datum/phenomenon/banishing_smite \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/tower/deity_items/transmutation.dm b/mods/gamemodes/deity/forms/tower/deity_items/transmutation.dm deleted file mode 100644 index 5eb1dd34785c..000000000000 --- a/mods/gamemodes/deity/forms/tower/deity_items/transmutation.dm +++ /dev/null @@ -1,97 +0,0 @@ -/datum/deity_item/transmutation - name = DEITY_TREE_TRANSMUTATION - desc = "Transmutation is the school of change. It cannot be used to create things, only modify them or even destroy them." - category = DEITY_TREE_TRANSMUTATION - max_level = 3 - base_cost = 50 - -/datum/deity_item/conjuration/get_cost(var/mob/living/deity/D) - return base_cost * (level + 1) - -//Level 1 -/datum/deity_item/boon/single_charge/slippery_surface - name = "Slippery Surface" - desc = "Allows a follower to slicken a small patch of floor. Anyone without sure-footing will find it hard to stay upright." - base_cost = 10 - category = DEITY_TREE_TRANSMUTATION - boon_path = /spell/hand/slippery_surface/tower - -/datum/deity_item/boon/single_charge/smoke - name = "Smoke" - desc = "Allows a follower to distill the nearby air into smoke." - base_cost = 10 - category = DEITY_TREE_TRANSMUTATION - boon_path = /spell/aoe_turf/smoke/tower - -//Level 2 -/datum/deity_item/boon/single_charge/knock - name = "Knock" - desc = "Allows a follower to open nearby doors without the keys." - base_cost = 25 - category = DEITY_TREE_TRANSMUTATION - boon_path = /spell/aoe_turf/knock/tower - requirements = list(DEITY_TREE_TRANSMUTATION = 2) - -/datum/deity_item/boon/single_charge/burning_grip - name = "Burning Grip" - desc = "Allows a follower cause an object to heat up intensly in someone's hand, making them drop it and whatever skin is attached." - base_cost = 15 - boon_path = /spell/hand/burning_grip/tower - category = DEITY_TREE_TRANSMUTATION - requirements = list(DEITY_TREE_TRANSMUTATION = 2) - -/datum/deity_item/phenomenon/warp_body - name = "Phenomenon: Warp Body" - desc = "Gain the ability to warp the very structure of a target's body, wracking pain and weakness." - base_cost = 75 - category = DEITY_TREE_TRANSMUTATION - requirements = list(DEITY_TREE_TRANSMUTATION = 2) - phenomenon_path = /datum/phenomenon/warp - -//Level 3 -/datum/deity_item/boon/single_charge/jaunt - name = "Ethereal Jaunt" - desc = "Allows a follower to liquify for a short duration, letting them pass through all dense objects." - base_cost = 25 - category = DEITY_TREE_TRANSMUTATION - boon_path = /spell/targeted/ethereal_jaunt/tower - requirements = list(DEITY_TREE_TRANSMUTATION = 3) - -/datum/deity_item/healing_spells - name = DEITY_UNLOCK_HEAL - desc = "Of transmutation, healing is perhaps the most immediately effective and useful. This unlocks the healing spells for your followers." - base_cost = 50 - category = DEITY_TREE_TRANSMUTATION - requirements = list(DEITY_TREE_TRANSMUTATION = 3) - -/datum/deity_item/boon/single_charge/heal - name = "Minor Heal" - desc = "Allows your follower to heal themselves, or others, for a slight amount." - base_cost = 15 - category = DEITY_TREE_TRANSMUTATION - requirements = list(DEITY_UNLOCK_HEAL = 1) - boon_path = /spell/targeted/heal_target/tower - -/datum/deity_item/boon/single_charge/heal/major - name = "Major Heal" - desc = "Allows your follower to heal others for a great amount." - base_cost = 25 - category = DEITY_TREE_TRANSMUTATION - requirements = list(DEITY_UNLOCK_HEAL = 1) - boon_path = /spell/targeted/heal_target/major/tower - -/datum/deity_item/boon/single_charge/heal/area - name = "Area Heal" - desc = "Allows your follower to heal everyone in an area for minor damage." - base_cost = 20 - category = DEITY_TREE_TRANSMUTATION - requirements = list(DEITY_UNLOCK_HEAL = 1) - boon_path = /spell/targeted/heal_target/area/tower - -/datum/deity_item/phenomenon/rock_form - name = "Phenomenon: Rock Form" - desc = "Gain the ability to transform your followers into beings of rock and stone." - base_cost = 75 - category = DEITY_TREE_TRANSMUTATION - requirements = list(DEITY_TREE_TRANSMUTATION = 3) - phenomenon_path = /datum/phenomenon/rock_form \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/tower/spells.dm b/mods/gamemodes/deity/forms/tower/spells.dm deleted file mode 100644 index 18b88647edfe..000000000000 --- a/mods/gamemodes/deity/forms/tower/spells.dm +++ /dev/null @@ -1,67 +0,0 @@ -/spell/create_air/tower - desc = "Allows you to generate a livable atmosphere in the area you are in." - charge_max = 5 - -/spell/hand/burning_grip/tower - desc = "Allows you cause an object to heat up intensly in someone's hand, making them drop it and whatever skin is attached." - charge_max = 3 - -/spell/hand/slippery_surface/tower - desc = "Allows you to slicken a small patch of floor. Anyone without sure-footing will find it hard to stay upright." - charge_max = 2 - -/spell/aoe_turf/knock/tower - charge_max = 2 - hidden_from_codex = TRUE - -/spell/aoe_turf/smoke/tower - charge_max = 2 - hidden_from_codex = TRUE - -/spell/aoe_turf/conjure/faithful_hound/tower - desc = "This spell allows you to summon a singular spectral dog that guards the nearby area. Anyone without the password is barked at or bitten." - charge_max = 1 - spell_flags = 0 - -/spell/aoe_turf/conjure/force_portal/tower - desc = "This spell allows you to summon a force portal. Anything that hits the portal gets sucked inside and is then thrown out when the portal explodes." - charge_max = 2 - spell_flags = 0 - -/spell/acid_spray/tower - desc = "The simplest form of aggressive conjuration: acid spray is quite effective in melting both man and object." - charge_max = 2 - -/spell/targeted/heal_target/tower - desc = "Allows you to heal yourself, or others, for a slight amount." - charge_max = 2 - -/spell/targeted/heal_target/major/tower - charge_max = 1 - spell_flags = INCLUDEUSER | SELECTABLE - desc = "Allows you to heal others for a great amount." - -/spell/targeted/heal_target/area/tower - desc = "Allows you to heal everyone in an area for minor damage." - charge_max = 1 - -/spell/targeted/ethereal_jaunt/tower - desc = "Allows you to liquefy for a short duration, letting you pass through all dense objects." - charge_max = 2 - spell_flags = Z2NOCAST | INCLUDEUSER - -/spell/aoe_turf/conjure/forcewall/tower - desc = "A temporary invincible wall for you to summon." - charge_max = 3 - -/spell/targeted/equip_item/dyrnwyn/tower - desc = "This spell allows you to summon a fiery golden sword for a short duration." - charge_max = 1 - -/spell/targeted/equip_item/shield/tower - desc = "This spell allows you to summon a magical shield for a short duration." - charge_max = 1 - -/spell/targeted/projectile/dumbfire/fireball/tower - desc = "Imbue yourself with the power of exploding fire." - charge_max = 2 \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/tower/structures.dm b/mods/gamemodes/deity/forms/tower/structures.dm deleted file mode 100644 index 453caf22de25..000000000000 --- a/mods/gamemodes/deity/forms/tower/structures.dm +++ /dev/null @@ -1,25 +0,0 @@ -/obj/structure/deity/altar/tower - icon_state = "tomealtar" - -/obj/structure/deity/wizard_recharger - name = "fountain of power" - desc = "Refreshing, cool water surrounded by archaic carvings." - icon_state = "fountain" - power_adjustment = 2 - build_cost = 700 - -/obj/structure/deity/wizard_recharger/attack_hand(var/mob/hitter) - SHOULD_CALL_PARENT(FALSE) - if(!length(hitter.mind?.learned_spells)) - to_chat(hitter, SPAN_WARNING("You don't feel as if this will do anything for you.")) - return TRUE - - hitter.visible_message(SPAN_NOTICE("\The [hitter] dips their hands into \the [src], a soft glow emanating from them.")) - if(do_after(hitter,300,src,check_holding=0)) - for(var/s in hitter.mind.learned_spells) - var/spell/spell = s - spell.charge_counter = spell.charge_max - to_chat(hitter, SPAN_NOTICE("You feel refreshed!")) - else - to_chat(hitter, SPAN_WARNING("You need to keep in contact with \the [src]!")) - return TRUE \ No newline at end of file diff --git a/mods/gamemodes/deity/forms/tower/tower.dm b/mods/gamemodes/deity/forms/tower/tower.dm deleted file mode 100644 index 37f63bce440e..000000000000 --- a/mods/gamemodes/deity/forms/tower/tower.dm +++ /dev/null @@ -1,49 +0,0 @@ -/datum/god_form/wizard - name = "The Tower" - info = {"Only from destruction does the Tower grow. Its bricks smelted from crumbled ignorance and the fires of ambition.
    - Benefits:
    - +Learn spells from two different schools.
    - +Deity gains power through each spell use.

    - Drawbacks:
    - -Abilities hold a limited amount of charge and must be charged at a fountain of power. - "} - desc = "A single solitary tower" - god_icon_state = "tower" - pylon_icon_state = "nim" - - buildables = list(/obj/structure/deity/altar/tower, - /obj/structure/deity/pylon, - /obj/structure/deity/wizard_recharger - ) - items = list(/datum/deity_item/general/potential, - /datum/deity_item/general/regeneration, - /datum/deity_item/conjuration, - /datum/deity_item/boon/single_charge/create_air, - /datum/deity_item/boon/single_charge/acid_spray, - /datum/deity_item/boon/single_charge/force_wall, - /datum/deity_item/phenomenon/dimensional_locker, - /datum/deity_item/boon/single_charge/faithful_hound, - /datum/deity_item/wizard_armaments, - /datum/deity_item/boon/single_charge/sword, - /datum/deity_item/boon/single_charge/shield, - /datum/deity_item/phenomenon/portals, - /datum/deity_item/boon/single_charge/fireball, - /datum/deity_item/boon/single_charge/force_portal, - /datum/deity_item/phenomenon/banishing_smite, - /datum/deity_item/transmutation, - /datum/deity_item/boon/single_charge/slippery_surface, - /datum/deity_item/boon/single_charge/smoke, - /datum/deity_item/boon/single_charge/knock, - /datum/deity_item/boon/single_charge/burning_grip, - /datum/deity_item/phenomenon/warp_body, - /datum/deity_item/boon/single_charge/jaunt, - /datum/deity_item/healing_spells, - /datum/deity_item/boon/single_charge/heal, - /datum/deity_item/boon/single_charge/heal/major, - /datum/deity_item/boon/single_charge/heal/area, - /datum/deity_item/phenomenon/rock_form - ) - -/datum/god_form/wizard/take_charge(var/mob/living/user, var/charge) - linked_god.adjust_power_min(max(round(charge/100), 1),silent = 1) - return 1 \ No newline at end of file diff --git a/mods/gamemodes/deity/gamemode.dm b/mods/gamemodes/deity/gamemode.dm deleted file mode 100644 index 9bf79d8ca875..000000000000 --- a/mods/gamemodes/deity/gamemode.dm +++ /dev/null @@ -1,12 +0,0 @@ -/decl/game_mode/godmode - name = "Deity" - round_description = "An otherworldly beast has turned its attention to you and your fellow cremembers." - extended_round_description = "The station has been infiltrated by a fanatical group of death-cultists! They will use powers from beyond your comprehension to subvert you to their cause and ultimately please their gods through sacrificial summons and physical immolation! Try to survive!" - uid = "god" - required_players = 10 - required_enemies = 3 - end_on_antag_death = FALSE - associated_antags = list( - /decl/special_role/deity, - /decl/special_role/godcultist - ) \ No newline at end of file diff --git a/mods/gamemodes/deity/god_cultist_role.dm b/mods/gamemodes/deity/god_cultist_role.dm deleted file mode 100644 index d1c5bced882a..000000000000 --- a/mods/gamemodes/deity/god_cultist_role.dm +++ /dev/null @@ -1,106 +0,0 @@ -/decl/special_role/godcultist - name = "God Cultist" - name_plural = "God Cultists" - blacklisted_jobs = list(/datum/job/submap) - antag_indicator = "hudcultist" - faction_verb = /mob/living/proc/dpray - welcome_text = "You are under the guidance of a powerful otherwordly being. Spread its will and keep your faith.
    Use dpray to communicate directly with your master!
    Ask your master for spells to start building!" - flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE - hard_cap = 5 - hard_cap_round = 6 - initial_spawn_req = 3 - initial_spawn_target = 3 - antaghud_indicator = "hudcultist" - skill_setter = /datum/antag_skill_setter/station - blocked_job_event_categories = list(ASSIGNMENT_ROBOT, ASSIGNMENT_COMPUTER) - -/decl/special_role/godcultist/add_antagonist_mind(var/datum/mind/player, var/ignore_role, var/nonstandard_role_type, var/nonstandard_role_msg, var/mob/living/deity/specific_god) - if(!..()) - return 0 - - if(specific_god) - add_cultist(player, specific_god) - - return 1 - -/decl/special_role/godcultist/post_spawn() - var/decl/special_role/deity = GET_DECL(/decl/special_role/deity) - if(!deity.current_antagonists.len) - return - var/count = 1 - var/deity_count = 1 - while(count <= current_antagonists.len) - if(deity_count > deity.current_antagonists.len) - deity_count = 1 - var/datum/mind/deity_mind = deity.current_antagonists[deity_count] - var/datum/mind/mind = current_antagonists[count] - add_cultist(mind, deity_mind.current) - count++ - deity_count++ - - -/decl/special_role/godcultist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) - var/mob/living/deity/god = get_deity(player) - if(!..()) - return 0 - remove_cultist(player, god) - return 1 - -/decl/special_role/godcultist/get_extra_panel_options(var/datum/mind/player) - return "\[Select Deity\]" - -/decl/special_role/godcultist/Topic(href, href_list) - if(..()) - return 1 - if(href_list["selectgod"]) - var/list/god_list = list() - var/decl/special_role/deity = GET_DECL(/decl/special_role/deity) - if(length(deity.current_antagonists)) - for(var/m in deity.current_antagonists) - var/datum/mind/mind = m - god_list += mind.current - else - for(var/mob/living/deity/specific_deity in global.player_list) - god_list += specific_deity - if(god_list.len) - var/mob/living/deity/D = input(usr, "Select a deity for this cultist.") in null|god_list - if(D) - var/datum/mind/player = locate(href_list["selectgod"]) - remove_cultist(player) //Remove him from any current deity. - add_cultist(player, D) - log_and_message_admins("has set [key_name(player.current)] to be a minion of [key_name(D)]") - else - to_chat(usr, SPAN_WARNING("There are no deities to be linked to.")) - return 1 - -/decl/special_role/godcultist/proc/add_cultist(var/datum/mind/player, var/mob/living/deity/deity) - deity.add_follower(player.current) - player.current.add_language(/decl/language/cultcommon) - -/decl/special_role/godcultist/proc/remove_cultist(var/datum/mind/player, var/mob/living/deity/god) - god.remove_follower(player.current) - player.current.remove_language(/decl/language/cultcommon) - -/decl/special_role/godcultist/proc/get_deity(var/datum/mind/player) - var/decl/special_role/deity = GET_DECL(/decl/special_role/deity) - for(var/m in deity.current_antagonists) - var/datum/mind/mind = m - var/mob/living/deity/god = mind.current - if(god && god.is_follower(player.current,1)) - return god - -/mob/living/proc/dpray(var/msg as text) - set category = "Abilities" - - var/decl/special_role/godcultist/godcult = GET_DECL(/decl/special_role/godcultist) - if(!src.mind || !godcult.is_antagonist(mind)) - return - msg = sanitize(msg) - var/mob/living/deity/D = godcult.get_deity(mind) - if(!D || !msg) - return - - //Make em wait a few seconds. - src.visible_message("\The [src] bows their head down, muttering something.", SPAN_NOTICE("You send the message \"[msg]\" to your master.")) - to_chat(D, "\The [src] (J) prays, \"[msg]\"") - log_and_message_admins("dprayed, \"[msg]\" to \the [key_name(D)]") diff --git a/mods/gamemodes/deity/mobs/deity.dm b/mods/gamemodes/deity/mobs/deity.dm deleted file mode 100644 index 96fc8fb54da6..000000000000 --- a/mods/gamemodes/deity/mobs/deity.dm +++ /dev/null @@ -1,134 +0,0 @@ -/mob/living/deity - name = "shapeless creature" - desc = "A shape of otherworldly matter, not yet ready to be unleashed into this world." - icon = 'icons/mob/deity_big.dmi' - icon_state = "egg" - pixel_x = -128 - pixel_y = -128 - max_health = 100 - universal_understand = TRUE - mob_sort_value = 5 - is_spawnable_type = FALSE - butchery_data = null - - var/eye_type = /mob/observer/eye/freelook/cult - var/datum/visualnet/cultnet/eyenet - var/list/minions = list() //Minds of those who follow him - var/list/structures = list() //The objs that this dude controls. - var/list/feats = list() - var/datum/god_form/form - var/datum/current_boon - var/mob/living/following - -/mob/living/deity/Initialize() - . = ..() - eyenet = new() - eyeobj = new eye_type(get_turf(src), eyenet) - eyeobj.possess(src) - eyenet.add_source(src) - -/mob/living/deity/death(gibbed) - . = ..() - if(.) - for(var/m in minions) - var/datum/mind/M = m - remove_follower_spells(M) - to_chat(M.current, "Your connection has been severed! \The [src] is no more!") - sound_to(M.current, 'sound/hallucinations/far_noise.ogg') - SET_STATUS_MAX(M.current, STAT_WEAK, 10) - for(var/s in structures) - var/obj/structure/deity/S = s - S.linked_god = null - -/mob/living/deity/shared_nano_interaction() - if(stat == DEAD) - return STATUS_CLOSE - return STATUS_INTERACTIVE - -/mob/living/deity/Destroy() - - for(var/phenomenon in phenomena) - remove_phenomenon(phenomenon) - - if(length(items_by_category)) - for(var/cat in items_by_category) - var/list/L = items_by_category[cat] - L.Cut() - items_by_category.Cut() - - if(length(items)) - for(var/i in items) - qdel(items[i]) - items.Cut() - - death() - if(length(minions)) - minions.Cut() - if(length(structures)) - structures.Cut() - - if(eyeobj) - eyeobj.release() - QDEL_NULL(eyeobj) - QDEL_NULL(eyenet) //We do it here as some mobs have eyes that have access to the visualnet and we only want to destroy it when the deity is destroyed - - QDEL_NULL(form) - - return ..() - -/mob/living/deity/verb/return_to_plane() - set category = "Godhood" - - eyeobj.forceMove(get_turf(src)) - -/mob/living/deity/verb/choose_form() - set name = "Choose Form" - set category = "Godhood" - - var/dat = list() - dat += {"

    Choose a Form

    - This choice is permanent. Choose carefully, but quickly. - - - - - - "} - var/list/forms = subtypesof(/datum/god_form) - - for(var/form in forms) - var/datum/god_form/god = form - var/god_name = initial(god.name) - var/icon/god_icon = icon('icons/mob/mob.dmi', initial(god.pylon_icon_state)) - send_rsc(src,god_icon, "[god_name].png") - dat += {" - - - - "} - dat += "
    NameThemeDescription
    [god_name][initial(god.info)]
    " - show_browser(src, JOINTEXT(dat), "window=godform;can_close=0") - -/mob/living/deity/proc/set_form(var/type) - form = new type(src) - to_chat(src, SPAN_NOTICE("You undergo a transformation into your new form!")) - spawn(1) - SetName(form.name) - var/newname = sanitize(input(src, "Choose a name for your new form.", "Name change", form.name) as text, MAX_NAME_LEN) - if(newname) - fully_replace_character_name(newname) - src.verbs -= /mob/living/deity/verb/choose_form - show_browser(src, null, "window=godform") - for(var/m in minions) - var/datum/mind/mind = m - var/mob/living/L = mind.current - L.faction = form.faction - -//Gets the name based on form, or if there is no form name, type. -/mob/living/deity/proc/get_type_name(var/type) - if(form && form.buildables[type]) - var/list/vars = form.buildables[type] - if(vars["name"]) - return vars["name"] - var/atom/movable/M = type - return initial(M.name) \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_Stat.dm b/mods/gamemodes/deity/mobs/deity_Stat.dm deleted file mode 100644 index 6105b7f9af35..000000000000 --- a/mods/gamemodes/deity/mobs/deity_Stat.dm +++ /dev/null @@ -1,14 +0,0 @@ -/mob/living/deity/Stat() - . = ..() - if(statpanel("Status")) - stat("Structure Num", structures.len) - stat("Minion Num", minions.len) - var/boon_name = "None" - if(current_boon) - if(istype(current_boon, /spell)) - var/spell/S = current_boon - boon_name = S.name - else - var/obj/O = current_boon - boon_name = O.name - stat("Current Boon",boon_name) \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_boons.dm b/mods/gamemodes/deity/mobs/deity_boons.dm deleted file mode 100644 index da05d9a5ff2b..000000000000 --- a/mods/gamemodes/deity/mobs/deity_boons.dm +++ /dev/null @@ -1,54 +0,0 @@ -/mob/living/deity/proc/set_boon(var/datum/boon) - if(current_boon) - qdel(current_boon) - current_boon = boon - to_chat(src, SPAN_NOTICE("You now have the boon [boon]")) - if(istype(boon, /atom/movable)) - var/atom/movable/A = boon - nano_data["boon_name"] = A.name - A.forceMove(src) - else if(istype(boon, /spell)) - var/spell/S = boon - nano_data["boon_name"] = S.name - -/mob/living/deity/proc/grant_boon(var/mob/living/L) - if(istype(current_boon, /spell) && !grant_spell(L, current_boon)) - return - else if(istype(current_boon, /obj/item)) - var/obj/item/I = current_boon - I.dropInto(L.loc) - var/origin_text = "on the floor" - if(L.equip_to_appropriate_slot(I)) - origin_text = "on your body" - else if(L.put_in_hands_or_del(I)) - origin_text = "in your hands" - else - var/obj/O = L.equip_to_storage(I) - if(O) - origin_text = "in \the [O]" - to_chat(L, SPAN_NOTICE("It appears [origin_text].")) - - to_chat(L, SPAN_OCCULT("\The [src] grants you a boon of [current_boon]!")) - to_chat(src, SPAN_NOTICE("You give \the [L] a boon of [current_boon].")) - log_and_message_admins("gave [key_name(L)] the boon [current_boon]") - current_boon = null - nano_data["boon_name"] = null - return - -/mob/living/deity/proc/grant_spell(var/mob/living/target, var/spell/spell) - var/datum/mind/M = target.mind - for(var/s in M.learned_spells) - var/spell/S = s - if(istype(S, spell.type)) - to_chat(src, SPAN_WARNING("They already know that spell!")) - return 0 - target.add_spell(spell) - spell.set_connected_god(src) - to_chat(target, SPAN_NOTICE("You feel a surge of power as you learn the art of [current_boon].")) - return 1 - -/* This is a generic proc used by the God to enact a sacrifice from somebody. Power is a value of magnitude. -*/ -/mob/living/deity/proc/take_charge(var/mob/living/L, var/power) - if(form) - form.take_charge(L, power) \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_click.dm b/mods/gamemodes/deity/mobs/deity_click.dm deleted file mode 100644 index aae71fd6257d..000000000000 --- a/mods/gamemodes/deity/mobs/deity_click.dm +++ /dev/null @@ -1,24 +0,0 @@ -/mob/living/deity/ClickOn(var/atom/A, var/params) - if(A == src) - if(form) - ui_interact(src) - else - choose_form() - return - var/list/modifiers = params2list(params) - if(modifiers["shift"] || modifiers["ctrl"]) - if(silenced) - to_chat(src, SPAN_WARNING("You cannot do that as you are silenced!")) - else - var/datum/phenomenon/phenomenon = get_phenomenon(modifiers["shift"], modifiers["ctrl"]) - if(phenomenon) - phenomenon.Click(A) - return - if(current_boon && is_follower(A)) - grant_boon(A) - else if(istype(A, /obj/structure/deity)) - var/obj/structure/deity/D = A - if(D.linked_god == src) - D.attack_deity(src) - return - ..() \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_hud.dm b/mods/gamemodes/deity/mobs/deity_hud.dm deleted file mode 100644 index 12824de99040..000000000000 --- a/mods/gamemodes/deity/mobs/deity_hud.dm +++ /dev/null @@ -1,9 +0,0 @@ -/mob/living/deity - hud_used = /datum/hud/deity - -/datum/hud/deity/FinalizeInstantiation() - action_intent = new /obj/screen/intent/deity(null, mymob) - adding += action_intent - ..() - var/obj/screen/intent/deity/D = action_intent - D.sync_to_mob(mymob) diff --git a/mods/gamemodes/deity/mobs/deity_items.dm b/mods/gamemodes/deity/mobs/deity_items.dm deleted file mode 100644 index 3dfa6e4dc642..000000000000 --- a/mods/gamemodes/deity/mobs/deity_items.dm +++ /dev/null @@ -1,33 +0,0 @@ -/mob/living/deity - var/list/items - var/list/items_by_category - -/mob/living/deity/proc/set_items(var/list/_items) - items = _items - items_by_category = list() - for(var/i in items) - var/datum/deity_item/di = items[i] - if(!items_by_category[di.category]) - items_by_category[di.category] = list() - items_by_category[di.category] += di - -/mob/living/deity/proc/has_item(var/name, var/minimum_level = 1) - if(!(name in items)) - return FALSE - var/datum/deity_item/di = items[name] - . = di.level >= minimum_level - -/mob/living/deity/proc/upgrade_item(var/name) - if(!(name in items)) - return FALSE - var/datum/deity_item/di = items[name] - if(!di.can_buy(src)) - return FALSE - di.buy(src) - . = TRUE - -/mob/living/deity/proc/get_item_level(var/name) - . = 0 - if(items[name]) - var/datum/deity_item/di = items[name] - . = di.level diff --git a/mods/gamemodes/deity/mobs/deity_phenomena.dm b/mods/gamemodes/deity/mobs/deity_phenomena.dm deleted file mode 100644 index a5abe64946a2..000000000000 --- a/mods/gamemodes/deity/mobs/deity_phenomena.dm +++ /dev/null @@ -1,94 +0,0 @@ -/mob/living/deity - var/silenced = 0 - var/list/phenomena = list() - var/list/intent_phenomena = list() - var/static/list/control_types = list("control", "controlshift", "shift") - - -/mob/living/deity/Initialize() - . = ..() - for(var/decl/intent/intent as anything in decls_repository.get_decls_of_type_unassociated(/decl/intent)) //Just in case we somehow remove/add a new intent #futureproofing - populate_intent(intent) - set_phenomenon(add_phenomenon(/datum/phenomenon/communicate), GET_DECL(/decl/intent/disarm), "shift") - set_phenomenon(add_phenomenon(/datum/phenomenon/punish), GET_DECL(/decl/intent/help), "control") - set_phenomenon(add_phenomenon(/datum/phenomenon/point), GET_DECL(/decl/intent/help), "controlshift") - set_phenomenon(add_phenomenon(/datum/phenomenon/conversion), GET_DECL(/decl/intent/grab), "shift") - set_phenomenon(add_phenomenon(/datum/phenomenon/forced_conversion), GET_DECL(/decl/intent/grab), "control") - -/mob/living/deity/proc/silence(amount) - if(!silenced) - to_chat(src, SPAN_WARNING("You've been silenced! Your phenomena are disabled!")) - var/obj/screen/intent/deity/SD = istype(hud_used) && hud_used.action_intent - if(istype(SD)) - SD.color = "#ff0000" - silenced += amount - for(var/phenom in phenomena) //Also make it so that you don't do cooldowns. - var/datum/phenomenon/P = phenomena[phenom] - if(P.refresh_time) - P.refresh_time += amount - -/mob/living/deity/handle_regular_status_updates() - . = ..() - if(.) - if(silenced > 0) - silenced-- - if(!silenced) - to_chat(src, SPAN_NOTICE("You are no longer silenced.")) - var/obj/screen/intent/deity/SD = istype(hud_used) && hud_used.action_intent - if(istype(SD)) - SD.color = null - if(power_per_regen < 0 || power < power_min) - adjust_power(power_per_regen) - -/mob/living/deity/proc/add_phenomenon(phenomena_type) - LAZYINITLIST(phenomena) - for(var/P in phenomena) - if(istype(phenomena[P], phenomena_type)) - return - var/datum/phenomenon/P = new phenomena_type(src) - phenomena[P.name] = P - return P - -/mob/living/deity/proc/remove_phenomena_from_intent(decl/intent/intent, modifier, update = 1) - var/list/intent_list = intent_phenomena[intent] - intent_list[modifier] = null - if(update) - update_phenomena_bindings() - -/mob/living/deity/proc/remove_phenomenon(to_remove) - var/datum/phenomenon/P = phenomena[to_remove] - phenomena -= to_remove // this isn't going to work, is it? - for(var/intent in intent_phenomena) - var/list/intent_list = intent_phenomena[intent] - for(var/mod in intent_list) - if(intent_list[mod] == P) - intent_list[mod] = null - var/obj/screen/intent/deity/SD = istype(hud_used) && hud_used.action_intent - if(istype(SD)) - SD.update_text() - update_phenomena() - update_phenomena_bindings() - if(selected == to_remove) - selected = null - qdel(P) - -/mob/living/deity/proc/populate_intent(decl/intent/intent) - LAZYDISTINCTADD(intent_phenomena[intent], control_types) - -/mob/living/deity/proc/set_phenomenon(datum/phenomenon/phenomenon, decl/intent/intent, modifiers) - if(!LAZYACCESS(intent_phenomena, intent)) - populate_intent(intent) - var/list/intent_list = intent_phenomena[intent] - intent_list[modifiers] = phenomenon - -/mob/living/deity/proc/get_phenomenon(shift = 0, control = 0) - var/list/intent_list = intent_phenomena[get_intent()] - if(intent_list) - var/type = "" - if(shift) - type = "shift" - if(control) - type = "control[type]" - if(intent_list[type]) - return intent_list[type] - return null \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_power.dm b/mods/gamemodes/deity/mobs/deity_power.dm deleted file mode 100644 index ffe073b014a3..000000000000 --- a/mods/gamemodes/deity/mobs/deity_power.dm +++ /dev/null @@ -1,21 +0,0 @@ -/mob/living/deity - var/power = 0 - var/power_min = 10 - var/power_per_regen = 1 - -/mob/living/deity/proc/adjust_power(var/amount) - if(amount) - power = max(0, power + amount) - -/mob/living/deity/proc/adjust_power_min(var/amount, var/silent = 0, var/msg) - if(amount) - power_min = max(initial(power_min), power_min + amount) - if(!silent) - var/feel = "" - if(abs(amount) > 20) - feel = " immensely" - else if(abs(amount) > 10) - feel = " greatly" - if(abs(amount) >= 5) - var/class = amount > 0 ? "notice" : "warning" - to_chat(src, "You feel your power [amount > 0 ? "increase" : "decrease"][feel][msg ? " [msg]" : ""]") \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_pylon.dm b/mods/gamemodes/deity/mobs/deity_pylon.dm deleted file mode 100644 index 7f84dc5b339b..000000000000 --- a/mods/gamemodes/deity/mobs/deity_pylon.dm +++ /dev/null @@ -1,21 +0,0 @@ -/mob/living/deity - var/image/pylon_image - var/obj/structure/deity/pylon/pylon - -/mob/living/deity/set_form(var/type) - ..() - pylon_image = image('icons/mob/mob.dmi', icon_state = form.pylon_icon_state) - pylon_image.alpha = 180 - -/mob/living/deity/proc/possess_pylon(var/obj/structure/deity/pylon/P) - if(pylon) - leave_pylon() - pylon = P - pylon.overlays += pylon_image - playsound(pylon,'sound/effects/phasein.ogg',40,1) - -/mob/living/deity/proc/leave_pylon() - if(!pylon) - return - pylon.overlays -= pylon_image - pylon = null \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_sources.dm b/mods/gamemodes/deity/mobs/deity_sources.dm deleted file mode 100644 index a1ec947983d8..000000000000 --- a/mods/gamemodes/deity/mobs/deity_sources.dm +++ /dev/null @@ -1,84 +0,0 @@ -/mob/living/deity/proc/add_follower(var/mob/living/L) - if(is_follower(L, silent=1)) - return - - adjust_source(3, L) - minions += L.mind - var/spell/construction/C = new() - L.add_spell(C) - C.set_connected_god(src) - if(form) - L.faction = form.faction - update_followers() - events_repository.register(/decl/observ/destroyed, L,src, PROC_REF(dead_follower)) - events_repository.register(/decl/observ/death, L,src, PROC_REF(update_followers)) - -/mob/living/deity/proc/dead_follower(var/mob/living/L) - events_repository.unregister(/decl/observ/death, L,src) - events_repository.unregister(/decl/observ/destroyed, L,src) - -/mob/living/deity/proc/remove_follower_spells(var/datum/mind/M) - if(M.learned_spells) - for(var/s in M.learned_spells) - var/spell/S = s - if(S.connected_god == src) - M.current.remove_spell(S) - qdel(S) - -/mob/living/deity/proc/remove_follower(var/mob/living/L) - if(!is_follower(L, silent=1)) - return - - adjust_source(-3, L) - minions -= L.mind - L.faction = MOB_FACTION_NEUTRAL - if(L.mind) - remove_follower_spells(L.mind) - update_followers() - - -/mob/living/deity/proc/adjust_source(var/amount, var/atom/source, var/silent = 0, var/msg) - adjust_power_min(amount, silent, msg) - if(!ismovable(source)) - return - if(amount > 0) - eyenet.add_source(source) - if(istype(source, /obj/structure/deity)) - structures |= source - else - eyenet.remove_source(source) - if(istype(source, /obj/structure/deity)) - structures -= source - -/mob/living/deity/proc/is_follower(var/mob/living/L, var/silent = 0) - if(istype(L)) - if(L.mind) - if(L.mind in minions) - return 1 - if(!silent) - to_chat(src, SPAN_WARNING("You do not feel a malleable mind behind that frame.")) - return 0 - -/mob/living/deity/fully_replace_character_name(var/new_name, var/in_depth = TRUE) - if(!..()) - return 0 - for(var/m in minions) - var/datum/mind/minion = m - to_chat(minion.current, "Your master is now known as [new_name].") - minion.assigned_special_role = "Servant of [new_name]" - eyeobj.SetName("[src] ([eyeobj.name_sufix])") - nano_data["name"] = new_name - return 1 - -//Whether we are near an important structure. -/mob/living/deity/proc/near_structure(var/atom/A, var/all_structures = 0) - var/turf/T = get_turf(A) - for(var/s in structures) - if(!all_structures) - var/obj/structure/deity/D = s - if(D.deity_flags & DEITY_STRUCTURE_NEAR_IMPORTANT)//If it needs to be near an important structure, it isn't important. - continue - - if(get_dist(T, s) <= 3) - return 1 - return 0 \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_topic.dm b/mods/gamemodes/deity/mobs/deity_topic.dm deleted file mode 100644 index 4ff775043939..000000000000 --- a/mods/gamemodes/deity/mobs/deity_topic.dm +++ /dev/null @@ -1,56 +0,0 @@ -/mob/living/deity/OnSelfTopic(list/href_list) - if(href_list["form"]) - var/type = locate(href_list["form"]) in subtypesof(/datum/god_form) - if(type) - set_form(type) - return TOPIC_HANDLED - if(href_list["select_phenomenon"]) - nano_data["phenomenaMenu"] = 1 - selected = phenomena[href_list["select_phenomenon"]] - nano_data["selectedPhenomenaName"] = selected.name - return TOPIC_HANDLED - if(href_list["clear_selected"]) - nano_data["phenomenaMenu"] = 0 - selected = null - nano_data["selectedPhenomenaName"] = null - return TOPIC_HANDLED - if(href_list["select_intent"]) - var/decl/intent/intent = locate(href_list["select_intent"]) - if(!istype(intent)) - return TOPIC_NOACTION - var/binding = href_list["select_binding"] - var/list/phenomenon = intent_phenomena[intent] - if(phenomenon[binding]) - remove_phenomena_from_intent(intent, binding, 0) - if(selected) - set_phenomenon(selected, intent, binding) - update_phenomena_bindings() - return TOPIC_HANDLED - - if(href_list["jump"]) - var/atom/a = locate(href_list["jump"]) - var/follow = 0 - if(href_list["follow"]) - follow = 1 - if(a) - if(following) - stop_follow() - eyeobj.setLoc(get_turf(a)) - if(follow) - follow_follower(a) - to_chat(src, SPAN_NOTICE("[follow ? "Following" : "Jumping to"] \the [a]")) - return TOPIC_HANDLED - if(href_list["buy"]) - var/datum/deity_item/di = locate(href_list["buy"]) - if(di.can_buy(src)) - di.buy(src) - else - to_chat(di,SPAN_WARNING("You don't meet all the requirements for [di.name]!")) - return TOPIC_HANDLED - if(href_list["switchCategory"]) - set_nano_category(text2num(href_list["switchCategory"])) - return 1 - if(href_list["switchMenu"]) - nano_data[href_list["menu"]] = text2num(href_list["switchMenu"]) - return TOPIC_HANDLED - return ..() \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/deity_tracking.dm b/mods/gamemodes/deity/mobs/deity_tracking.dm deleted file mode 100644 index 27e9d3b552d7..000000000000 --- a/mods/gamemodes/deity/mobs/deity_tracking.dm +++ /dev/null @@ -1,40 +0,0 @@ -/mob/living/deity/verb/jump_to_follower() - set category = "Godhood" - - if(!minions) - return - - var/list/could_follow = list() - for(var/m in minions) - var/datum/mind/M = m - if(M.current && M.current.stat != DEAD) - could_follow += M.current - - if(!could_follow.len) - return - - var/choice = input(src, "Jump to follower", "Teleport") as null|anything in could_follow - if(choice) - follow_follower(choice) - -/mob/living/deity/proc/follow_follower(var/mob/living/L) - if(!L || L.stat == DEAD || !is_follower(L, silent=1)) - return - if(following) - stop_follow() - eyeobj.setLoc(get_turf(L)) - to_chat(src, SPAN_NOTICE("You begin to follow \the [L].")) - following = L - events_repository.register(/decl/observ/moved, L, src, TYPE_PROC_REF(/mob/living/deity, keep_following)) - events_repository.register(/decl/observ/destroyed, L, src, TYPE_PROC_REF(/mob/living/deity, stop_follow)) - events_repository.register(/decl/observ/death, L, src, TYPE_PROC_REF(/mob/living/deity, stop_follow)) - -/mob/living/deity/proc/stop_follow() - events_repository.unregister(/decl/observ/moved, following, src) - events_repository.unregister(/decl/observ/destroyed, following, src) - events_repository.unregister(/decl/observ/death, following,src) - to_chat(src, SPAN_NOTICE("You stop following \the [following].")) - following = null - -/mob/living/deity/proc/keep_following(var/atom/movable/moving_instance, var/atom/old_loc, var/atom/new_loc) - eyeobj.setLoc(new_loc) diff --git a/mods/gamemodes/deity/mobs/freelook/cultnet.dm b/mods/gamemodes/deity/mobs/freelook/cultnet.dm deleted file mode 100644 index 86c718fa0f53..000000000000 --- a/mods/gamemodes/deity/mobs/freelook/cultnet.dm +++ /dev/null @@ -1,13 +0,0 @@ -/datum/visualnet/cultnet - valid_source_types = list(/mob/living/, /obj/structure/deity) - chunk_type = /datum/chunk/cultnet - -/datum/chunk/cultnet/acquire_visible_turfs(var/list/visible) - for(var/source in sources) - if(isliving(source)) - var/mob/living/L = source - if(L.stat == DEAD) - continue - - for(var/turf/t in seen_turfs_in_range(source, world.view)) - visible[t] = t \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/freelook/mask.dm b/mods/gamemodes/deity/mobs/freelook/mask.dm deleted file mode 100644 index 6310effcb1ca..000000000000 --- a/mods/gamemodes/deity/mobs/freelook/mask.dm +++ /dev/null @@ -1,11 +0,0 @@ -/mob/observer/eye/freelook/cult - name = "Mask of God" - desc = "A terrible fracture of reality coinciding into a mirror to another world." - living_eye = FALSE - -/mob/observer/eye/freelook/cult/EyeMove() - if(isdeity(owner)) - var/mob/living/deity/D = owner - if(D.following) - D.stop_follow() - return ..() diff --git a/mods/gamemodes/deity/mobs/items/blood_crafting.dm b/mods/gamemodes/deity/mobs/items/blood_crafting.dm deleted file mode 100644 index 3db0c0e161d2..000000000000 --- a/mods/gamemodes/deity/mobs/items/blood_crafting.dm +++ /dev/null @@ -1,19 +0,0 @@ -/datum/deity_item/blood_crafting - abstract_type = /datum/deity_item/blood_crafting - name = DEITY_BLOOD_CRAFT - desc = "Unlocks the blood smithing structure which allows followers to forge unholy tools from blood and flesh." - category = DEITY_BLOOD_CRAFT - max_level = 1 - base_cost = 75 - var/forge_type = /obj/structure/deity/blood_forge - var/list/recipes = list() - -/datum/deity_item/blood_crafting/buy(var/mob/living/deity/user) - ..() - user.form.buildables |= forge_type //put structure here - var/list/L = user.feats[name] - if(!L) - L = list() - for(var/type in recipes) - L[type] = recipes[type] - user.feats[name] = L \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/items/deity_item.dm b/mods/gamemodes/deity/mobs/items/deity_item.dm deleted file mode 100644 index 7590bbb9037e..000000000000 --- a/mods/gamemodes/deity/mobs/items/deity_item.dm +++ /dev/null @@ -1,39 +0,0 @@ -// todo: declize /datum/deity_item -/datum/deity_item - var/name - var/desc - var/base_cost = 1 - var/category - var/level = 0 - var/max_level = 0 - var/list/requirements //Name of item = level of item - -/datum/deity_item/proc/can_buy(var/mob/living/deity/D) - if(max_level && level == max_level) - return FALSE - var/cost = get_cost(D) - if(cost && D.power < cost) - return FALSE - if(requirements && requirements.len) - for(var/name in requirements) - if(!D.has_item(name,requirements[name])) - return FALSE - return TRUE - -/datum/deity_item/proc/buy(var/mob/living/deity/D) - D.adjust_power(-get_cost(D)) - level++ - -/datum/deity_item/proc/get_cost(var/mob/living/deity/D) - return base_cost - - -/datum/deity_item/proc/print_level() - return "[level][max_level ? "/[max_level]" : ""]" - -/datum/deity_item/proc/print_requirements() - if(!requirements) - return "N/A" - . = "" - for(var/l in requirements) - . += "[l] [requirements[l]]
    " \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/items/general.dm b/mods/gamemodes/deity/mobs/items/general.dm deleted file mode 100644 index d5c217565780..000000000000 --- a/mods/gamemodes/deity/mobs/items/general.dm +++ /dev/null @@ -1,26 +0,0 @@ -/datum/deity_item/general - category = "General" - -/datum/deity_item/general/potential - name = "Increase Potential" - desc = "Increase the amount of natural power you regenerate." - base_cost = 10 - -/datum/deity_item/general/potential/buy(var/mob/living/deity/D) - ..() - D.adjust_power_min(5) - -/datum/deity_item/general/potential/get_cost(var/mob/living/deity/D) - return base_cost + base_cost * level**2 - -/datum/deity_item/general/regeneration - name = "Increase Power Syphon" - desc = "Decreases the time it takes to charge your power." - base_cost = 5 - -/datum/deity_item/general/regeneration/buy(var/mob/living/deity/D) - ..() - D.power_per_regen++ - -/datum/deity_item/general/regeneration/get_cost(var/mob/living/deity/D) - return base_cost + 10 * level \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/items/generic.dm b/mods/gamemodes/deity/mobs/items/generic.dm deleted file mode 100644 index 665814e0788b..000000000000 --- a/mods/gamemodes/deity/mobs/items/generic.dm +++ /dev/null @@ -1,25 +0,0 @@ -/datum/deity_item/boon - var/boon_path - -/datum/deity_item/boon/buy(var/mob/living/deity/D) - ..() - if(boon_path) - . = new boon_path() - D.set_boon(.) - -/datum/deity_item/phenomenon - var/phenomenon_path - max_level = 1 - -/datum/deity_item/phenomenon/buy(var/mob/living/deity/D) - ..() - if(level == 1 && phenomenon_path) - D.add_phenomenon(phenomenon_path) - D.update_phenomena() - -/datum/deity_item/boon/single_charge/buy(var/mob/living/deity/D) - . = ..() - if(istype(.,/spell)) - var/spell/S = . - S.charge_counter = S.charge_max - S.charge_type = Sp_CHARGES \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/menu/deity_nano.dm b/mods/gamemodes/deity/mobs/menu/deity_nano.dm deleted file mode 100644 index 37f488f5b61b..000000000000 --- a/mods/gamemodes/deity/mobs/menu/deity_nano.dm +++ /dev/null @@ -1,81 +0,0 @@ -/mob/living/deity - var/list/nano_data = list() - var/datum/phenomenon/selected - -/mob/living/deity/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/uistate = global.self_topic_state) - if(!nano_data["categories"]) //If we don't have the categories set yet, we should populate our data. - var/list/categories = list() - for(var/cat in items_by_category) - categories += cat - nano_data["name"] = name - nano_data["form_name"] = form.name - nano_data["categories"] = categories - nano_data["menu"] = 0 //0 followers, 1 shop, 2 phenomena - nano_data["phenomenaMenu"] = 0 //0 Phenoms 1 Bindings - set_nano_category(0) - update_followers() - update_phenomena() - update_phenomena_bindings() - else - update_category() - nano_data["power"] = power - nano_data["power_min"] = power_min - nano_data["regen"] = power_per_regen - ui = SSnano.try_update_ui(user, src, ui_key, ui, nano_data, force_open) - if(!ui) - ui = new(user, src, ui_key, "deity.tmpl", "Deity Menu", 650, 600, state = uistate) - ui.set_initial_data(nano_data) - ui.open() - ui.set_auto_update(TRUE) - -/mob/living/deity/proc/set_nano_category(var/num) - nano_data["category"] = num - update_category() - -/mob/living/deity/proc/update_category() - var/actual_cat = nano_data["categories"][nano_data["category"] + 1] - var/list/cat_items = items_by_category[actual_cat] - var/list/item_data = list() - for(var/item in cat_items) - var/datum/deity_item/di = item - item_data[++item_data.len] = list("name" = di.name, "desc" = di.desc, "requirements" = di.print_requirements(), "level" = di.print_level(), "cost" = di.get_cost(), "ref" = "\ref[di]") - nano_data["item_data"] = item_data - -/mob/living/deity/proc/update_followers() - var/list/follower_data = list() - for(var/m in minions) - var/list/minion_data = list() - var/datum/mind/mind = m - if(mind.current) - if(mind.current.stat != DEAD && mind.current.loc) - minion_data["ref"] = "\ref[mind.current]" - minion_data["name"] = "[mind.current.name]" - else - minion_data["name"] = mind.name - follower_data[++follower_data.len] = minion_data - nano_data["followers"] = follower_data - -/mob/living/deity/proc/update_phenomena() - var/list/phenomena_data = list() - for(var/p in phenomena) - var/datum/phenomenon/phenomenon = phenomena[p] - phenomena_data[++phenomena_data.len] = list("name" = p, "description" = phenomenon.desc, "cost" = phenomenon.cost, "cooldown" = phenomenon.cooldown) - nano_data["phenomena"] = phenomena_data - -/mob/living/deity/proc/update_phenomena_bindings() - var/list/phenomena_bindings = list() - for(var/intent in intent_phenomena) - var/list/intent_data = list() - for(var/binding in intent_phenomena[intent]) - var/datum/phenomenon/P = intent_phenomena[intent][binding] - var/list/data = list() - if(P) - data["phenomena_name"] = P.name - data["binding"] = binding - intent_data[++intent_data.len] = data - phenomena_bindings[++phenomena_bindings.len] = list("intent" = "\ref[intent]", "intent_data" = intent_data) - nano_data["bindings"] = phenomena_bindings - //Update the hud as well. - var/obj/screen/intent/deity/SD = istype(hud_used) && hud_used.action_intent - if(istype(SD)) - SD.update_text() \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/_defines.dm b/mods/gamemodes/deity/mobs/phenomena/_defines.dm deleted file mode 100644 index e0268f37fa5c..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/_defines.dm +++ /dev/null @@ -1,4 +0,0 @@ -#define PHENOMENA_NEAR_STRUCTURE 1 //Must be done near a structure -#define PHENOMENA_FOLLOWER 2 //Can be done on a follower -#define PHENOMENA_NONFOLLOWER 4 //Can be done on a nonfollower. -#define PHENOMENA_MUNDANE 8 //Can affect mundane (no mind, no client) things. \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/communication.dm b/mods/gamemodes/deity/mobs/phenomena/communication.dm deleted file mode 100644 index 2b815ce00ede..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/communication.dm +++ /dev/null @@ -1,80 +0,0 @@ -/datum/phenomenon/communicate - name = "Direct Communication" - desc = "Communicate directly with a mortal being. You may communicate with non-followers, but they will find you easier to ignore." - cost = 0 - flags = PHENOMENA_FOLLOWER | PHENOMENA_NONFOLLOWER - expected_type = /mob/living - -/datum/phenomenon/communicate/activate(var/mob/living/L) - var/text_to_send = sanitize(input(linked, "Subjugate a member to your will", "Message a Believer") as text) - if(text_to_send) - var/text_size = 4 - if(!linked.is_follower(L)) - text_size = 1 - to_chat(L, "[text_to_send]") //Note to self: make this go to ghosties - to_chat(linked, SPAN_NOTICE("You send the message [text_to_send] to \the [L]")) - log_and_message_admins("communicated the message \"[text_to_send]\" to [key_name(L)]", linked) - -/datum/phenomenon/point - name = "Point" - desc = "Attract your follower's attentions to something nearby." - cost = 0 - flags = PHENOMENA_MUNDANE|PHENOMENA_FOLLOWER|PHENOMENA_NONFOLLOWER - expected_type = /atom - var/image/arrow - -/datum/phenomenon/point/activate(var/atom/a) - ..() - if(!arrow) - arrow = image('icons/effects/markers.dmi', icon_state = "arrow", layer = POINTER_LAYER) - var/turf/T = get_turf(a) - arrow.loc = T - var/list/view = view(7,T) - for(var/m in linked.minions) - var/datum/mind/mind = m - if(mind.current) - var/mob/M = mind.current - if((M in view) && M.client) - to_chat(M, SPAN_OCCULT("Your attention is eerily drawn to \the [a].")) - M.client.images += arrow - events_repository.register(/decl/observ/logged_out, M, src, TYPE_PROC_REF(/datum/phenomenon/point, remove_image)) - spawn(20) - if(M.client) - remove_image(M) - -/datum/phenomenon/point/proc/remove_image(var/mob/living/L) - L.client.images -= arrow - events_repository.unregister(/decl/observ/logged_out, L, src) - -/datum/phenomenon/punish - name = "Punish" - desc = "Punish your followers for insubordination, the cost to use this phenomenon is based on how deadly you choose the punishment to be." - cost = 0 - flags = PHENOMENA_FOLLOWER - expected_type = /mob/living - var/static/list/punishment_list = list("Pain (0)" = 0, "Light Wound (5)" = 5, "Brain Damage (10)" = 10, "Heavy Wounds (20)" = 20) - -/datum/phenomenon/punish/activate(var/mob/living/L) - var/pain = input(linked, "Choose their punishment.", "Punishment") as null|anything in punishment_list - if(!pain) - return - if(punishment_list[pain] && linked.power < punishment_list[pain]) - to_chat(linked, SPAN_WARNING("[pain] costs too much power for you to use on \the [L]")) - return - ..() - linked.adjust_power(-punishment_list[pain]) - switch(pain) - if("Pain (0)") - L.take_damage(15, PAIN) - to_chat(L, SPAN_WARNING("You feel intense disappointment coming at you from beyond the veil.")) - if("Light Wound (5)") - L.take_damage(5) - to_chat(L, SPAN_WARNING("You feel an ethereal whip graze your very soul!")) - if("Brain Damage (10)") - L.take_damage(5, BRAIN) - to_chat(L, SPAN_DANGER("You feel your mind breaking under a otherwordly hammer...")) - if("Heavy Wounds (20)") - L.take_damage(25) - to_chat(L, SPAN_DANGER("You feel your master turn its destructive potential against you!")) - to_chat(linked, SPAN_NOTICE("You punish \the [L].")) - log_admin("[key_name(linked)] used Punishment [pain] on \the [key_name(L)]") \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/conjuration.dm b/mods/gamemodes/deity/mobs/phenomena/conjuration.dm deleted file mode 100644 index d7dd22f3e269..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/conjuration.dm +++ /dev/null @@ -1,67 +0,0 @@ -/datum/phenomenon/movable_object/dimensional_locker - object_type = /obj/structure/closet - name = "Dimensional Locker" - cost = 10 - desc = "Summon a trans-dimensional locker anywhere within your influence. You may transport objects and things, but not people in it." - -/datum/phenomenon/movable_object/dimensional_locker/activate(var/atom/a, var/mob/living/deity/user) - var/list/mobs_inside = list() - recursive_content_check(object_to_move, mobs_inside, client_check = 0, sight_check = 0, include_objects = 0) - - for(var/i in mobs_inside) - var/mob/M = i - M.dropInto(object_to_move.loc) - to_chat(M,SPAN_WARNING("You are suddenly flung out of \the [object_to_move]!")) - ..() - -/datum/phenomenon/portals - name = "Portals" - desc = "Summon a portal linked to the last portal you've created. The portal will be destroyed if it is not linked when someone crosses it." - cost = 30 - flags = PHENOMENA_NEAR_STRUCTURE|PHENOMENA_MUNDANE|PHENOMENA_FOLLOWER|PHENOMENA_NONFOLLOWER - expected_type = /atom - var/list/portals = list() - -/datum/phenomenon/portals/activate(var/atom/a, var/mob/living/deity/user) - ..() - var/obj/effect/portal/P = new(get_turf(a), null, 0) - P.failchance = 0 - portals += P - events_repository.register(/decl/observ/destroyed, P,src, TYPE_PROC_REF(/datum/phenomenon/portals, remove_portal)) - if(portals.len > 2) - var/removed = portals[1] - remove_portal(removed) - qdel(removed) - if(portals.len > 1) - var/obj/effect/portal/P1 = portals[1] - var/obj/effect/portal/P2 = portals[2] - P1.target = get_turf(P2) - P2.target = get_turf(P1) - -/datum/phenomenon/portals/proc/remove_portal(var/portal) - portals -= portal - events_repository.unregister(/decl/observ/destroyed, portal,src) - var/turf/T = get_turf(portal) - for(var/obj/effect/portal/P in portals) - if(P.target == T) - P.target = null - -/datum/phenomenon/banishing_smite - name = "Banishing Smite" - desc = "Deal a terrible blow to a mortal. If they are hurt enough ,they will find themselves trapped in a rift for 30 seconds." - cost = 70 - cooldown = 300 - flags = PHENOMENA_NEAR_STRUCTURE|PHENOMENA_MUNDANE|PHENOMENA_FOLLOWER|PHENOMENA_NONFOLLOWER - expected_type = /mob/living - -/datum/phenomenon/banishing_smite/activate(var/mob/living/L, var/mob/living/deity/user) - ..() - L.take_overall_damage(rand(5,30),0,0,0,"blunt intrument") //Actual spell does 5d10 but maaaybe too much. - playsound(get_turf(L), 'sound/effects/bamf.ogg', 100, 1) - to_chat(L, SPAN_DANGER("Something hard hits you!")) - if(L.current_health < L.get_max_health()/2) //If it reduces past 50% - var/obj/effect/rift/R = new(get_turf(L)) - L.visible_message(SPAN_DANGER("\The [L] is quickly sucked into \a [R]!")) - L.forceMove(R) - spawn(300) - qdel(R) diff --git a/mods/gamemodes/deity/mobs/phenomena/conversion.dm b/mods/gamemodes/deity/mobs/phenomena/conversion.dm deleted file mode 100644 index 1f0954c91350..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/conversion.dm +++ /dev/null @@ -1,51 +0,0 @@ -/datum/phenomenon/conversion - name = "Conversion" - desc = "Ask a non-follower to convert to your cult. This is completely voluntary. Requires the subject to be close to an altar." - cost = 20 - flags = PHENOMENA_NONFOLLOWER - expected_type = /mob/living - -/datum/phenomenon/conversion/can_activate(var/atom/target) - if(!..()) - return 0 - var/is_good = 0 - for(var/obj/structure/deity/altar/A in linked.structures) - if(get_dist(target, A) < 2) - is_good = 1 - break - if(!is_good) - to_chat(linked,SPAN_WARNING("\The [target] needs to be near \a [linked.get_type_name(/obj/structure/deity/altar)].")) - return 0 - return 1 - -/datum/phenomenon/conversion/activate(var/mob/living/L) - to_chat(src,SPAN_NOTICE("You give \the [L] a chance to willingly convert. May they choose wisely.")) - var/choice = alert(L, "You feel a weak power enter your mind attempting to convert it.", "Conversion", "Allow Conversion", "Deny Conversion") - if(choice == "Allow Conversion") - var/decl/special_role/godcultist/godcult = GET_DECL(/decl/special_role/godcultist) - godcult.add_antagonist_mind(L.mind,1, "Servant of [linked]", "You willingly give your mind to it, may it bring you fortune.", specific_god=linked) - else - to_chat(L, SPAN_WARNING("With little difficulty you force the intrusion out of your mind. May it stay that way.")) - to_chat(src, SPAN_WARNING("\The [L] decides not to convert.")) - -/datum/phenomenon/forced_conversion - name = "Forced Conversion" - desc = "Force a non-follower to join you. They need to be on top of an altar and conscious for this to work. They may resist, but that will hurt them." - cost = 100 - flags = PHENOMENA_NONFOLLOWER - expected_type = /mob/living - -/datum/phenomenon/forced_conversion/can_activate(var/mob/living/L) - if(!..()) - return 0 - var/obj/structure/deity/altar/A = locate() in get_turf(L) - if(!A || A.linked_god != linked) - to_chat(linked,SPAN_WARNING("\The [L] needs to be on \a [linked.get_type_name(/obj/structure/deity/altar)] to be forcefully converted.")) - return 0 - - return 1 - -/datum/phenomenon/forced_conversion/activate(var/mob/living/L) - var/obj/structure/deity/altar/A = locate() in get_turf(L) - A.set_target(L) - to_chat(linked, SPAN_NOTICE("You imbue \the [A] with your power, setting forth to force \the [L] to your will.")) \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/generic.dm b/mods/gamemodes/deity/mobs/phenomena/generic.dm deleted file mode 100644 index 3163ea42ab54..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/generic.dm +++ /dev/null @@ -1,36 +0,0 @@ -/datum/phenomenon/movable_object - var/object_type - var/atom/movable/object_to_move - flags = PHENOMENA_NEAR_STRUCTURE|PHENOMENA_MUNDANE|PHENOMENA_FOLLOWER|PHENOMENA_NONFOLLOWER - expected_type = /atom - -/datum/phenomenon/movable_object/New() - ..() - add_object() - -/datum/phenomenon/movable_object/Destroy() - events_repository.unregister(/decl/observ/destroyed, object_to_move,src) - if(!object_to_move.loc) - QDEL_NULL(object_to_move) - . = ..() - -/datum/phenomenon/movable_object/proc/add_object() - if(object_to_move) - events_repository.unregister(/decl/observ/destroyed, object_to_move,src) - object_to_move = new object_type() - events_repository.register(/decl/observ/destroyed, object_to_move, src, PROC_REF(add_object)) - -/datum/phenomenon/movable_object/activate(var/atom/a, var/mob/living/deity/user) - ..() - if(object_to_move == a) - object_to_move.forceMove(null) //Move to null space - else - var/turf/T = get_turf(a) - //No dense turf/stuff - if(T.density) - return - for(var/i in T) - var/atom/A = i - if(A.density) - return - object_to_move.forceMove(T) \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/narsie.dm b/mods/gamemodes/deity/mobs/phenomena/narsie.dm deleted file mode 100644 index 8a94ae441cbf..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/narsie.dm +++ /dev/null @@ -1,37 +0,0 @@ -/datum/phenomenon/exude_blood - name = "Exhude Blood" - desc = "Take pity on a follower, converting a pitance of your power into blood. Don't let them forget your mercy." - cost = 20 - flags = PHENOMENA_FOLLOWER - expected_type = /mob/living/human - -/datum/phenomenon/exude_blood/can_activate(var/mob/living/human/H) - if(!..()) - return 0 - - if(!H.should_have_organ(BP_HEART) || H.vessel.total_volume == H.species.blood_volume) - to_chat(linked, SPAN_WARNING("\The [H] doesn't require anymore blood.")) - return 0 - return 1 - -/datum/phenomenon/exude_blood/activate(var/mob/living/human/H, var/mob/living/deity/user) - H.adjust_blood(30) - to_chat(H,SPAN_NOTICE("You feel a rush as new blood enters your system.")) - - -/datum/phenomenon/hellscape - name = "Reveal Hellscape" - desc = "Show a non-follower what awaits their souls after you are through with them." - cost = 60 - cooldown = 450 - flags = PHENOMENA_NONFOLLOWER - expected_type = /mob/living - var/static/list/creepy_notes = list("Your knees give out as an unnatural screaming rings your ears.", - "You breathe in ash and decay, your lungs gasping for air as your body gives way to the floor.", - "An extreme pressure comes over you, as if an unknown force has marked you.") - -/datum/phenomenon/hellscape/activate(var/mob/living/L) - to_chat(L, "[pick(creepy_notes)]") - L.damageoverlaytemp = 100 - sound_to(L, 'sound/hallucinations/far_noise.ogg') - SET_STATUS_MAX(L, STAT_WEAK, 2) \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/phenomena.dm b/mods/gamemodes/deity/mobs/phenomena/phenomena.dm deleted file mode 100644 index 8d28ab5fc6e6..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/phenomena.dm +++ /dev/null @@ -1,75 +0,0 @@ -/datum/phenomenon - var/name = "Phenomena" - var/desc = "This has no desc." - var/cost = 0 - var/mob/living/deity/linked - var/flags = 0 - var/cooldown = 10 - var/refresh_time = 0 - var/expected_type - -/datum/phenomenon/New(var/master) - linked = master - ..() - -/datum/phenomenon/Destroy() - linked.remove_phenomenon(src) - return ..() - -/datum/phenomenon/proc/Click(var/atom/target) - if(can_activate(target)) - linked.adjust_power(-cost) - refresh_time = world.time + cooldown - activate(target) - -/datum/phenomenon/proc/can_activate(var/atom/target) - if(!linked) - return 0 - if(refresh_time > world.time) - to_chat(linked, SPAN_WARNING("\The [src] is still on cooldown for [round((refresh_time - world.time)/10)] more seconds!")) - return 0 - - if(!linked.form) - to_chat(linked, SPAN_WARNING("You must choose your form first!")) - return 0 - - if(expected_type && !istype(target,expected_type)) - return 0 - - if(flags & PHENOMENA_NEAR_STRUCTURE) - if(!linked.near_structure(target, 1)) - to_chat(linked, SPAN_WARNING("\The [target] needs to be near a holy structure for your powers to work!")) - return 0 - - if(isliving(target)) - var/mob/living/L = target - if(!L.mind || !L.client) - if(!(flags & PHENOMENA_MUNDANE)) - to_chat(linked, SPAN_WARNING("\The [L]'s mind is too mundane for you to influence.")) - return 0 - else - if(linked.is_follower(target, silent = 1)) - if(!(flags & PHENOMENA_FOLLOWER)) - to_chat(linked, SPAN_WARNING("You can't use [name] on the flock!")) - return 0 - else if(!(flags & PHENOMENA_NONFOLLOWER)) - to_chat(linked, SPAN_WARNING("You can't use [name] on non-believers.")) - return 0 - - if(cost > linked.power) - to_chat(linked, SPAN_WARNING("You need more power to use [name] (Need [cost] power, have [linked.power])!")) - return 0 - - return 1 - -/datum/phenomenon/proc/activate(var/target) - to_chat(linked, SPAN_NOTICE("You use the phenomenon [name] on \the [target]")) - log_and_message_admins("uses the phenomenon [name] on \the [target]", linked, get_turf(target)) - return - -/datum/phenomenon/proc/get_desc() - . = desc - if(cooldown) - . = "Cooldown: [cooldown/10] seconds. [.]" - if(cost) - . = "Cost: [cost] power. [.]" \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/starlight.dm b/mods/gamemodes/deity/mobs/phenomena/starlight.dm deleted file mode 100644 index 1351a50a8920..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/starlight.dm +++ /dev/null @@ -1,232 +0,0 @@ -/datum/phenomenon/herald - name = "Bestow Heraldry" - desc = "Turn one of your followers into a herald of your coming." - cost = 100 - cooldown = 60 SECONDS - flags = PHENOMENA_FOLLOWER - expected_type = /mob/living/human - var/static/list/possible_forms = list( - "Champion" = list("description" = "A protector of the faith. Fully protected by knightly armor, a Champion can shoot fire from their hands.", - "armor" = /obj/item/clothing/suit/armor/sunsuit, - "helm" = /obj/item/clothing/head/helmet/sunhelm, - "extension" = /datum/extension/deity_be_near/champion, - "spells" = list(/spell/hand/duration/sunwrath) - ), - "Oracle" = list("description" = "A preacher of the faith, the Oracle gives off heavenly light that they can use to heal followers and stun enemies.", - "armor" = /obj/item/clothing/suit/armor/sunrobe, - "extension" = /datum/extension/deity_be_near/oracle, - "spells" = list(/spell/targeted/glimpse_of_eternity) - ), - "Traitor" = list("description" = "Believers that reject the sun god's blessings, instead reveling in the shadows. Can turn invisible when its dark, and can move unprotected in space.", - "armor" = /obj/item/clothing/suit/space/shadowsuit, - "helm" = /obj/item/clothing/head/helmet/space/shadowhood, - "extension" = /datum/extension/deity_be_near/traitor, - "spells" = list(/spell/veil_of_shadows) - ) - ) - -/datum/phenomenon/herald/can_activate(var/a) - if(!..()) - return FALSE - return valid_for_herald(a) - -/datum/phenomenon/herald/proc/valid_for_herald(var/a) - var/mob/living/human/H = a - if(!istype(H)) - return FALSE - var/obj/item/I = H.get_equipped_item(slot_wear_suit_str) - if(I) - var/datum/extension/deity_be_near/dbn = get_extension(I, /datum/extension/deity_be_near) - if(dbn) - return FALSE - return TRUE - -/datum/phenomenon/herald/proc/equip_slot(var/mob/living/L, var/slot_id, var/new_item) - var/equipped = L.get_equipped_slot_for_item(slot_id) - if(equipped) - L.try_unequip(equipped, get_turf(L)) - L.equip_to_slot_if_possible(new_item, slot_id) - -/datum/phenomenon/herald/Topic(var/href, var/list/href_list) - if(..()) - return 1 - if(usr != linked) - return 1 - - if(href_list["herald"]) - var/list/form = possible_forms[href_list["herald"]] - var/mob/living/L = locate(href_list["target"]) - var/turf/T = get_turf(L) - if(!L || !valid_for_herald(L) || !form) - return 1 - var/type = form["armor"] - var/obj/item/I = new type(T) - var/datum/extension/deity_be_near/extension = set_extension(I, form["extension"], linked) - L.equip_to_slot_or_store_or_drop(I, slot_wear_suit_str) - if(form["helm"]) - var/h_type = form["helm"] - var/obj/item/helm = new h_type(T) - L.equip_to_slot_or_store_or_drop(helm, slot_head_str) - extension.expected_helmet = helm.type //We only do by type because A. its easier to manage and B the chances of it being non-unique in a normal game is very small - if(form["weapon"]) - var/w_type = form["weapon"] - L.put_in_hands_or_store_or_drop(new w_type(T)) - if(form["spells"]) - for(var/s in form["spells"]) - var/spell/boon = new s - boon.set_connected_god(linked) - L.add_spell(boon) - to_chat(L, "You have been chosen by your master to lead your fellow followers into the next age of rebirth.
    You have been granted powerful armor and a powerful spell. Don't lose them, as they are your key to your divinity and leadership.
    You also have particular sway over your deity's structures.
    ") - to_chat(linked, SPAN_NOTICE("\The [L] is now your herald!")) - linked.remove_phenomenon(name) - show_browser(linked, null, "window=herald") - -/datum/phenomenon/herald/activate(var/mob/living/human/H) - var/list/html = list() - html += "

    Heralds

    " - html += "
    Pick the type of herald you want.
    " - html += "" - for(var/type in possible_forms) - var/list/form = possible_forms[type] - html += "" - html += "
    NameDescription
    [type][form["description"]]
    " - show_browser(linked, jointext(html,null), "window=herald") - -/datum/phenomenon/create_gateway - name = "Create Gateway" - desc = "Creates a gateway from this world to the next. Gateways syphon absurd amounts of power but can be sacrificed to summon powerful minions." - cost = 200 - flags = PHENOMENA_NEAR_STRUCTURE - expected_type = /atom - -/datum/phenomenon/create_gateway/can_activate(var/atom/a) - if(!..()) - return 0 - if(istype(a, /obj/structure/deity/gateway)) - var/obj/structure/deity/gateway/G = a - if(G.linked_god == linked) - return 1 - var/turf/T = get_turf(a) - if(!T || T.density) - return 0 - for(var/i in T) - var/atom/at = i - if(at.density) - return 0 - return 1 - -/datum/phenomenon/create_gateway/activate(var/atom/a) - ..() - if(istype(a, /obj/structure/deity/gateway)) - qdel(a) - else - new /obj/structure/deity/gateway(get_turf(a), linked) - -/datum/phenomenon/flickering_whisper - name = "Flickering Whisper" - desc = "Whisper to a non-believer, allowing you to intrude on their thoughts and see what they see." - flags = PHENOMENA_NONFOLLOWER - expected_type = /mob/living - -/datum/phenomenon/flickering_whisper/activate(var/mob/living/L) - var/atom/whisper_from - for(var/obj/structure/deity/radiant_statue/rs in view(3, L)) - whisper_from = rs - break - var/message = sanitize(input(linked, "What is your message?", null) as null|text) - if(!linked || !message || QDELETED(src)) - return - to_chat(L, SPAN_OCCULT("[whisper_from ? "The [whisper_from] speaks to you" : "You hear a whisper say"] \"[message]\"")) - - linked.eyenet.add_source(L) - events_repository.register(/decl/observ/destroyed, L, src, PROC_REF(deactivate_look)) - addtimer(CALLBACK(src, PROC_REF(deactivate_look), L), 30 SECONDS) - -/datum/phenomenon/flickering_whisper/proc/deactivate_look(var/mob/viewer) - if(!linked.is_follower(viewer)) //Don't remove if they are follower - linked.eyenet.remove_source(viewer) - events_repository.unregister(/decl/observ/destroyed, viewer, src) - -/datum/phenomenon/burning_glare - name = "Burning Glare" - desc = "Burn a victim. If they are burnt enough, you'll set them ablaze." - cost = 100 - flags = PHENOMENA_NONFOLLOWER|PHENOMENA_NEAR_STRUCTURE - cooldown = 30 SECONDS - expected_type = /mob/living - -/datum/phenomenon/burning_glare/activate(var/mob/living/L) - ..() - to_chat(L, SPAN_DANGER("You feel yourself burn!")) - L.take_damage(10, BURN) - if(L.get_damage(BURN) > 60) - L.fire_stacks += 50 - L.IgniteMob() - -/datum/phenomenon/divine_right - name = "Divine Right" - desc = "Trigger your rebirth into the body of someone wearing a herald's uniform. This takes time, requires 3 open gateways, and if the body is destroyed during the ritual... so are you. But once complete, you become an unstoppable demigod of unnatural power." - cost = 300 - cooldown = 180 SECONDS - flags = PHENOMENA_FOLLOWER|PHENOMENA_NEAR_STRUCTURE - expected_type = /mob/living - -/datum/phenomenon/divine_right/can_activate(var/mob/living/L) - if(!..()) - return FALSE - var/active_gateways = 0 - for(var/obj/structure/deity/gateway/G in linked.structures) - active_gateways += 1 - - if(active_gateways < 3) - to_chat(linked, SPAN_WARNING("You do not have enough gateways activated.")) - return FALSE - - var/obj/O = L.get_equipped_item(slot_wear_suit_str) - if(O && has_extension(O, /datum/extension/deity_be_near)) - var/datum/extension/deity_be_near/dbn = get_extension(O, /datum/extension/deity_be_near) - if(dbn.wearing_full()) - return TRUE - to_chat(linked, SPAN_WARNING("\The [L] is not wearing a herald's uniform.")) - return FALSE - -/datum/phenomenon/divine_right/activate(var/mob/living/L) - ..() - to_chat(L, SPAN_OCCULT("Your soul is ripped from your body as your master prepares to possess it.")) - to_chat(linked, SPAN_OCCULT("You prepare the body for possession. Keep it safe. If it is totally destroyed, you will die.")) - L.ghostize() - SET_STATUS_MAX(L, STAT_WEAK, 1) - new /obj/aura/starborn(L) - L.status_flags |= GODMODE - events_repository.register(/decl/observ/destroyed, L,src,PROC_REF(fail_ritual)) - addtimer(CALLBACK(src, PROC_REF(succeed_ritual), L), 600 SECONDS) //6 minutes - for(var/mob/living/player in global.player_list) - sound_to(player, 'sound/effects/cascade.ogg') - if(player?.mind?.assigned_job?.is_holy) - to_chat(player, SPAN_OCCULT("Something bad is coming.... you know you don't have much time. Find and destroy the vessel, before its too late.")) - else if(player != linked && !linked.is_follower(player, silent = 1)) - to_chat(player, SPAN_WARNING("The world swims around you for just a moment... something is wrong. Very wrong.")) - else - to_chat(player, SPAN_NOTICE("Your Master is being reborn into the body of \the [L]. Protect it at all costs.")) - -/datum/phenomenon/divine_right/proc/fail_ritual(var/mob/living/L) - qdel(linked) - -/datum/phenomenon/divine_right/proc/succeed_ritual(var/mob/living/L) - to_chat(linked, SPAN_OCCULT("You have been reborn! Your power is limited here, focused on your body, but in return you are both eternal and physical.")) - for(var/mob/living/player in global.player_list) - sound_to(player, 'sound/effects/cascade.ogg') - to_chat(player, SPAN_OCCULT("\The [linked] has been born into flesh. Kneel to its authority or else.")) - linked.mind.transfer_to(L) - L.SetName("[linked] Incarnate") - L.real_name = "[linked] Incarnate" - -/datum/phenomenon/movable_object/wisp - name = "Wisp" - desc = "Creates or moves a small ball of light for your followers to use." - cost = 30 - object_type = /obj/item/flashlight/slime - -/datum/phenomenon/movable_object/wisp/add_object() - ..() - object_to_move.SetName("wisp") \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/phenomena/transmutation.dm b/mods/gamemodes/deity/mobs/phenomena/transmutation.dm deleted file mode 100644 index 381e885d3187..000000000000 --- a/mods/gamemodes/deity/mobs/phenomena/transmutation.dm +++ /dev/null @@ -1,26 +0,0 @@ -/datum/phenomenon/warp - name = "Warp Body" - desc = "Corrupt a mortal being, causing their DNA to break and their body to fail on them." - cost = 90 - cooldown = 300 - flags = PHENOMENA_NEAR_STRUCTURE|PHENOMENA_MUNDANE|PHENOMENA_FOLLOWER|PHENOMENA_NONFOLLOWER - expected_type = /mob/living - -/datum/phenomenon/warp/activate(var/mob/living/L) - ..() - L.take_damage(20, CLONE) - SET_STATUS_MAX(L, STAT_WEAK, 2) - to_chat(L, SPAN_DANGER("You feel your body warp and change underneath you!")) - -/datum/phenomenon/rock_form - name = "Rock Form" - desc = "Convert your mortal followers into immortal stone beings." - cost = 300 - flags = PHENOMENA_NEAR_STRUCTURE|PHENOMENA_FOLLOWER - expected_type = /mob/living/human - -/datum/phenomenon/rock_form/activate(var/mob/living/human/H) - ..() - to_chat(H, SPAN_DANGER("You feel your body harden as it rapidly is transformed into living crystal!")) - H.change_species(SPECIES_GOLEM) - SET_STATUS_MAX(H, STAT_WEAK, 5) \ No newline at end of file diff --git a/mods/gamemodes/deity/mobs/say.dm b/mods/gamemodes/deity/mobs/say.dm deleted file mode 100644 index 1dab1a38aa3b..000000000000 --- a/mods/gamemodes/deity/mobs/say.dm +++ /dev/null @@ -1,9 +0,0 @@ -/mob/living/deity/say(var/message, var/decl/language/speaking = null, var/verb="says") - if(!..()) - return 0 - if(pylon) - pylon.audible_message("\The [pylon] reverberates, \"[message]\"") - else - for(var/m in minions) - var/datum/mind/mind = m - to_chat(mind.current, "[message]") \ No newline at end of file diff --git a/mods/gamemodes/deity/overrides.dm b/mods/gamemodes/deity/overrides.dm deleted file mode 100644 index c052abdf9a24..000000000000 --- a/mods/gamemodes/deity/overrides.dm +++ /dev/null @@ -1,27 +0,0 @@ -/obj/item/sword/cultblade/can_use_safely(mob/living/user) - var/decl/special_role/godcult = GET_DECL(/decl/special_role/godcultist) - return ..() || (user.mind in godcult.current_antagonists) - -/datum/reagents/Topic(href, href_list) - . = ..() - if(!. && href_list["deconvert"]) - var/list/data = REAGENT_DATA(src, /decl/material/liquid/water) - if(LAZYACCESS(data, "holy")) - var/mob/living/target = locate(href_list["deconvert"]) - if(istype(target) && !QDELETED(target) && target.mind) - var/decl/special_role/godcult = GET_DECL(/decl/special_role/godcultist) - godcult.remove_antagonist(target.mind, TRUE) - -/decl/material/liquid/water/affect_holy(mob/living/M, removed, datum/reagents/holder) - . = ..() - if(.) - return . - var/decl/special_role/godcult = GET_DECL(/decl/special_role/godcultist) - if(M.mind && godcult.is_antagonist(M.mind)) - if(REAGENT_VOLUME(holder, type) > 5) - M.take_damage(5, PAIN, do_update_health = FALSE) - M.take_damage(1, BRUTE) - if(prob(10)) //Only annoy them a /bit/ - to_chat(M, SPAN_DANGER("You feel your insides curdle and burn! \[Give Into Purity\]")) - return TRUE - return FALSE \ No newline at end of file diff --git a/mods/gamemodes/deity/screen/intent.dm b/mods/gamemodes/deity/screen/intent.dm deleted file mode 100644 index 9f5b18b6aaa8..000000000000 --- a/mods/gamemodes/deity/screen/intent.dm +++ /dev/null @@ -1,45 +0,0 @@ -/obj/screen/intent/deity - var/list/desc_screens = list() - screen_loc = "RIGHT-5:122,BOTTOM:8" - -/obj/screen/intent/deity/on_update_icon() - . = ..() - cut_overlays() - add_overlay(image('icons/mob/screen/phenomena.dmi', icon_state = "hud", pixel_x = -138, pixel_y = -1)) - compile_overlays() - -/obj/screen/intent/deity/proc/sync_to_mob(var/mob) - var/mob/living/deity/deity = mob - for(var/i in 1 to deity.control_types.len) - var/obj/screen/deity_marker/S = new(null, deity) - desc_screens[deity.control_types[i]] = S - S.screen_loc = screen_loc - //This sets it up right. Trust me. - S.maptext_y = 33/2*i - i*i/2 - 10 - deity.client.screen += S - update_text() - -/obj/screen/intent/deity/proc/update_text() - var/mob/living/deity/deity = usr - var/mob/owner = owner_ref?.resolve() - if(!istype(deity) || !istype(owner) || owner != deity) - return - var/decl/intent/intent = owner.get_intent() - for(var/i in deity.control_types) - var/obj/screen/deity_marker/S = desc_screens[i] - var/datum/phenomenon/P = deity.intent_phenomena[intent][i] - if(P) - S.maptext = "[P.name]" - else - S.maptext = null - -/obj/screen/intent/deity/handle_click(mob/user, params) - ..() - update_text() - -/obj/screen/deity_marker - name = "" //Don't want them to be able to actually right click it. - mouse_opacity = MOUSE_OPACITY_UNCLICKABLE - icon_state = "blank" - maptext_width = 128 - maptext_x = -125 \ No newline at end of file diff --git a/mods/gamemodes/deity/spells/boon.dm b/mods/gamemodes/deity/spells/boon.dm deleted file mode 100644 index 269a0b4bc4cf..000000000000 --- a/mods/gamemodes/deity/spells/boon.dm +++ /dev/null @@ -1,11 +0,0 @@ -/spell - var/mob/living/deity/connected_god //Do we have this spell based off a boon from a god? - -/spell/proc/set_connected_god(var/mob/living/deity/god) - connected_god = god - -// todo: godform check_charge to parallel take_charge. currently a boon always succeeds -/spell/take_charge(mob/user, skipcharge) - if(connected_god) - return connected_god.take_charge(user, max(1, charge_max/10)) - return ..() \ No newline at end of file diff --git a/mods/gamemodes/deity/spells/construction.dm b/mods/gamemodes/deity/spells/construction.dm deleted file mode 100644 index 0005154ad6ea..000000000000 --- a/mods/gamemodes/deity/spells/construction.dm +++ /dev/null @@ -1,55 +0,0 @@ -#define CONSTRUCT_SPELL_COST 1 -#define CONSTRUCT_SPELL_TYPE 2 - -/spell/construction - name = "Basic Construction" - desc = "This ability will let you summon a structure of your choosing." - - cast_delay = 10 - charge_max = 100 - spell_flags = Z2NOCAST - invocation = "none" - invocation_type = SpI_NONE - - hud_state = "const_wall" - cast_sound = 'sound/effects/meteorimpact.ogg' - -/spell/construction/choose_targets() - var/list/possible_targets = list() - if(connected_god && connected_god.form) - for(var/type in connected_god.form.buildables) - var/cost = 10 - if(ispath(type, /obj/structure/deity)) - var/obj/structure/deity/D = type - cost = initial(D.build_cost) - possible_targets["[connected_god.get_type_name(type)] - [cost]"] = list(cost, type) - var/choice = input("Construct to build.", "Construction") as null|anything in possible_targets - if(!choice) - return - if(locate(/obj/structure/deity) in get_turf(holder)) - return - - return possible_targets[choice] - else - return - -/spell/construction/cast_check(var/skipcharge, var/mob/user, var/list/targets) - if(!..()) - return 0 - var/turf/T = get_turf(user) - if(skipcharge && !valid_deity_structure_spot(targets[CONSTRUCT_SPELL_TYPE], T, connected_god, user)) - return 0 - else - for(var/obj/O in T) - if(O.density) - to_chat(user, SPAN_WARNING("Something here is blocking your construction!")) - return 0 - return 1 - -/spell/construction/cast(var/target, mob/user) - charge_max = target[CONSTRUCT_SPELL_COST] - target = target[CONSTRUCT_SPELL_TYPE] - var/turf/T = get_turf(user) - new target(T, connected_god) -#undef CONSTRUCT_SPELL_COST -#undef CONSTRUCT_SPELL_TYPE \ No newline at end of file diff --git a/mods/gamemodes/deity/spells/open_gateway.dm b/mods/gamemodes/deity/spells/open_gateway.dm deleted file mode 100644 index db83a05c8917..000000000000 --- a/mods/gamemodes/deity/spells/open_gateway.dm +++ /dev/null @@ -1,34 +0,0 @@ -/spell/open_gateway - name = "Open Gateway" - desc = "Open a gateway for your master. Don't do it for too long, or you will die." - - charge_max = 600 - spell_flags = Z2NOCAST - invocation = "none" - invocation_type = SpI_NONE - - number_of_channels = 0 - time_between_channels = 200 - hud_state = "const_wall" - cast_sound = 'sound/effects/meteorimpact.ogg' - -/spell/open_gateway/choose_targets() - var/mob/living/spellcaster = holder - var/turf/source_turf = get_turf(spellcaster) - holder.visible_message(SPAN_NOTICE("A gateway opens up underneath \the [spellcaster]!")) - var/deity - var/decl/special_role/godcultist/godcult = GET_DECL(/decl/special_role/godcultist) - if(spellcaster.mind && (spellcaster.mind in godcult.current_antagonists)) - deity = godcult.get_deity(spellcaster.mind) - return list(new /obj/structure/deity/gateway(source_turf, deity)) - -/spell/open_gateway/cast(var/list/targets, var/mob/holder, var/channel_count) - if(prob((channel_count / 5) * 100)) - to_chat(holder, SPAN_DANGER("If you hold the portal open for much longer you'll be ripped apart!")) - if(channel_count == 6) - to_chat(holder, SPAN_DANGER("The gateway consumes you... leaving nothing but dust.")) - holder.dust() - - -/spell/open_gateway/after_spell(var/list/targets) - QDEL_NULL_LIST(targets) \ No newline at end of file diff --git a/mods/gamemodes/deity/spells/vision.dm b/mods/gamemodes/deity/spells/vision.dm deleted file mode 100644 index dabe6cf4fcaf..000000000000 --- a/mods/gamemodes/deity/spells/vision.dm +++ /dev/null @@ -1,21 +0,0 @@ -/spell/camera_connection/god_vision - name = "All Seeing Eye" - desc = "See what your master sees." - - charge_max = 10 - spell_flags = Z2NOCAST - invocation = "none" - invocation_type = SpI_NONE - - extension_type = /datum/extension/eye/freelook - - hud_state = "gen_mind" - -/spell/camera_connection/god_vision/set_connected_god(var/mob/living/deity/god) - ..() - - var/datum/extension/eye/freelook/fl = get_extension(src, /datum/extension/eye) - if(!fl) - return - fl.set_visualnet(god.eyenet) - diff --git a/mods/gamemodes/deity/structures/altar.dm b/mods/gamemodes/deity/structures/altar.dm deleted file mode 100644 index 888e4c935a9d..000000000000 --- a/mods/gamemodes/deity/structures/altar.dm +++ /dev/null @@ -1,106 +0,0 @@ -/obj/structure/deity/altar - name = "altar" - desc = "A structure made for the express purpose of religion." - current_health = 50 - power_adjustment = 5 - deity_flags = DEITY_STRUCTURE_ALONE - build_cost = 1000 - var/mob/living/target - var/cycles_before_converted = 5 - var/next_cycle = 0 - -/obj/structure/deity/altar/Destroy() - if(target) - remove_target() - if(linked_god) - to_chat(src, SPAN_DANGER("You've lost an altar!")) - return ..() - -/obj/structure/deity/altar/grab_attack(obj/item/grab/grab, mob/user) - var/mob/living/victim = grab.get_affecting_mob() - if(grab.force_danger() && istype(victim)) - victim.dropInto(loc) - SET_STATUS_MAX(victim, STAT_WEAK, 1) - user.visible_message(SPAN_WARNING("\The [user] throws \the [victim] onto \the [src]!")) - qdel(grab) - return TRUE - return ..() - -/obj/structure/deity/altar/Process() - if(!target || world.time < next_cycle) - return - if(!linked_god || target.stat) - to_chat(linked_god, SPAN_WARNING("\The [target] has lost consciousness, breaking \the [src]'s hold on their mind!")) - remove_target() - return - - next_cycle = world.time + 10 SECONDS - cycles_before_converted-- - if(!cycles_before_converted) - src.visible_message("For one thundering moment, \the [target] cries out in pain before going limp and broken.") - var/decl/special_role/godcultist/godcult = GET_DECL(/decl/special_role/godcultist) - godcult.add_antagonist_mind(target.mind,1, "Servant of [linked_god]","Your loyalty may be faulty, but you know that it now has control over you...", specific_god=linked_god) - remove_target() - return - - switch(cycles_before_converted) - if(4) - text = "You can't think straight..." - if(3) - text = "You feel like your thought are being overriden..." - if(2) - text = "You can't... concentrate... must... resist!" - if(1) - text = "Can't... resist... anymore." - to_chat(linked_god, SPAN_WARNING("\The [target] is getting close to conversion!")) - to_chat(target, "[text]. Resist Conversion") - - -//Used for force conversion. -/obj/structure/deity/altar/proc/set_target(var/mob/living/L) - if(target || !linked_god) - return - cycles_before_converted = initial(cycles_before_converted) - START_PROCESSING(SSobj, src) - target = L - update_icon() - events_repository.register(/decl/observ/destroyed, L,src, TYPE_PROC_REF(/obj/structure/deity/altar, remove_target)) - events_repository.register(/decl/observ/moved, L, src, TYPE_PROC_REF(/obj/structure/deity/altar, remove_target)) - events_repository.register(/decl/observ/death, L, src, TYPE_PROC_REF(/obj/structure/deity/altar, remove_target)) - -/obj/structure/deity/altar/proc/remove_target() - STOP_PROCESSING(SSobj, src) - events_repository.unregister(/decl/observ/destroyed, target, src) - events_repository.unregister(/decl/observ/moved, target, src) - events_repository.unregister(/decl/observ/death, target, src) - target = null - update_icon() - -/obj/structure/deity/altar/OnTopic(var/user, var/list/href_list) - if(href_list["resist"]) - var/mob/living/M = locate(href_list["resist"]) - if(!istype(M) || target != M || M.stat || M.is_on_special_ability_cooldown()) - return TOPIC_HANDLED - - M.set_special_ability_cooldown(10 SECONDS) - M.visible_message(SPAN_WARNING("\The [M] writhes on top of \the [src]!"), SPAN_NOTICE("You struggle against the intruding thoughts, keeping them at bay!")) - to_chat(linked_god, SPAN_WARNING("\The [M] slows its conversion through willpower!")) - cycles_before_converted++ - if(prob(50)) - to_chat(M, SPAN_DANGER("The mental strain is too much for you! You feel your body weakening!")) - M.take_damage(15, TOX, do_update_health = FALSE) - M.take_damage(30, PAIN) - return TOPIC_REFRESH - -/obj/structure/deity/altar/on_update_icon() - ..() - if(target) - add_overlay(image('icons/effects/effects.dmi', icon_state = "summoning")) - -/obj/structure/deity/altar/nullrod_act(mob/user, obj/item/nullrod/rod) - if(!linked_god.silenced) //Don't want them to infinity spam it. - linked_god.silence(10) - new /obj/effect/temporary(get_turf(src),'icons/effects/effects.dmi',"purple_electricity_constant", 10) - visible_message(SPAN_NOTICE("\The [src] groans in protest as reality settles around \the [rod].")) - return TRUE - return FALSE \ No newline at end of file diff --git a/mods/gamemodes/deity/structures/blood_forge.dm b/mods/gamemodes/deity/structures/blood_forge.dm deleted file mode 100644 index 3b23da40630e..000000000000 --- a/mods/gamemodes/deity/structures/blood_forge.dm +++ /dev/null @@ -1,66 +0,0 @@ -/obj/structure/deity/blood_forge - name = "unholy forge" - desc = "This forge gives off no heat, no light, its flames look almost unnatural." - icon_state = "forge" - build_cost = 1000 - current_health = 50 - var/busy = 0 - var/recipe_feat_list = "Blood Crafting" - var/text_modifications = list( - "Cost" = "Blood", - "Dip" = "fire. Pain envelops you as blood seeps out of your hands and you begin to shape it into something more useful", - "Shape" = "You shape the fire as more and more blood comes out.", - "Out" = "flames" - ) - - power_adjustment = 2 - -/obj/structure/deity/blood_forge/attack_hand(var/mob/user) - if(!linked_god || !linked_god.is_follower(user, silent = 1) || !ishuman(user)) - return ..() - var/list/recipes = linked_god.feats[recipe_feat_list] - if(!recipes) - return TRUE - var/dat = "
    Recipes


    Item - [text_modifications["Cost"]] Cost
    " - for(var/type in recipes) - var/atom/a = type - var/cost = recipes[type] - dat += "[initial(a.name)] - [cost]
    [initial(a.desc)]

    " - show_browser(user, dat, "window=forge") - return TRUE - -/obj/structure/deity/blood_forge/CanUseTopic(var/user) - if(!linked_god || !linked_god.is_follower(user, silent = 1) || !ishuman(user)) - return STATUS_CLOSE - return ..() - -/obj/structure/deity/blood_forge/OnTopic(var/user, var/list/href_list) - if(href_list["make_recipe"]) - var/list/recipes = linked_god.feats[recipe_feat_list] - var/type = locate(href_list["make_recipe"]) in recipes - if(type) - var/cost = recipes[type] - craft_item(type, cost, user) - return TOPIC_REFRESH - -/obj/structure/deity/blood_forge/proc/craft_item(var/path, var/blood_cost, var/mob/user) - if(busy) - to_chat(user, SPAN_WARNING("Someone is already using \the [src]!")) - return - - busy = 1 - to_chat(user, SPAN_NOTICE("You dip your hands into \the [src]'s [text_modifications["Dip"]]")) - for(var/count = 0, count < blood_cost/10, count++) - if(!do_after(user, 50,src)) - busy = 0 - return - user.visible_message("\The [user] swirls their hands in \the [src].", text_modifications["Shape"]) - if(linked_god) - linked_god.take_charge(user, 10) - var/obj/item/I = new path(get_turf(src)) - user.visible_message("\The [user] pull out \the [I] from the [text_modifications["Out"]].", "You pull out the completed [I] from the [text_modifications["Out"]].") - busy = 0 - -/obj/structure/deity/blood_forge/proc/take_charge(var/mob/living/user, var/charge) - if(linked_god) - linked_god.take_charge(user, charge) \ No newline at end of file diff --git a/mods/gamemodes/deity/structures/pylon.dm b/mods/gamemodes/deity/structures/pylon.dm deleted file mode 100644 index 4b977e2726d6..000000000000 --- a/mods/gamemodes/deity/structures/pylon.dm +++ /dev/null @@ -1,75 +0,0 @@ - -/obj/structure/deity/pylon - name = "pylon" - desc = "A crystal platform used to communicate with the deity." - build_cost = 400 - icon = 'icons/obj/structures/pylon.dmi' - icon_state = "pylon" - var/list/intuned = list() - -/obj/structure/deity/pylon/attack_deity(var/mob/living/deity/D) - if(D.pylon == src) - D.leave_pylon() - else - D.possess_pylon(src) - -/obj/structure/deity/pylon/Destroy() - if(linked_god && linked_god.pylon == src) - linked_god.leave_pylon() - return ..() - -/obj/structure/deity/pylon/attack_hand(var/mob/L) - SHOULD_CALL_PARENT(FALSE) - if(!linked_god) - return FALSE - if(L in intuned) - remove_intuned(L) - else - add_intuned(L) - return TRUE - -/obj/structure/deity/pylon/proc/add_intuned(var/mob/living/L) - if(L in intuned) - return - to_chat(L, SPAN_NOTICE("You place your hands on \the [src], feeling yourself intune to its vibrations.")) - intuned += L - events_repository.register(/decl/observ/destroyed, L,src, TYPE_PROC_REF(/obj/structure/deity/pylon, remove_intuned)) - -/obj/structure/deity/pylon/proc/remove_intuned(var/mob/living/L) - if(!(L in intuned)) - return - to_chat(L, SPAN_WARNING("You no longer feel intuned to \the [src].")) - intuned -= L - events_repository.unregister(/decl/observ/destroyed, L, src) - -/obj/structure/deity/pylon/OnTopic(var/mob/living/human/user, var/href_list) - if(href_list["vision_jump"]) - if(istype(user)) - to_chat(user,SPAN_WARNING("You feel your body lurch uncomfortably as your consciousness jumps to \the [src]")) - if(prob(5)) - user.vomit() - else - to_chat(user, SPAN_NOTICE("You jump to \the [src]")) - if(user.eyeobj) - user.eyeobj.setLoc(locate(href_list["vision_jump"])) - else - CRASH("[user] does not have an eyeobj") - . = TOPIC_REFRESH - . = ..() - -/obj/structure/deity/pylon/hear_talk(mob/M, text, verb, decl/language/speaking) - if(!linked_god) - return - if(linked_god.pylon != src) - if(!(M in intuned)) - return - for(var/obj/structure/deity/pylon/P in linked_god.structures) - if(P == src || linked_god.pylon == P) - continue - P.audible_message("\The [P] resonates, \"[text]\"") - to_chat(linked_god, "[html_icon(src)] [M] (P) [verb], [linked_god.pylon == src ? "" : ""]\"[text]\"[linked_god.pylon == src ? "" : ""]") - if(linked_god.minions.len) - for(var/minion in linked_god.minions) - var/datum/mind/mind = minion - if(mind.current && mind.current.eyeobj) //If it is currently having a vision of some sort - to_chat(mind.current,"[html_icon(src)] [M] (J) [verb], \"[text]\"") diff --git a/mods/gamemodes/deity/structures/structures.dm b/mods/gamemodes/deity/structures/structures.dm deleted file mode 100644 index c3d9ea65b30b..000000000000 --- a/mods/gamemodes/deity/structures/structures.dm +++ /dev/null @@ -1,68 +0,0 @@ -/proc/valid_deity_structure_spot(var/type, var/turf/target, var/mob/living/deity/deity, var/mob/living/user) - var/obj/structure/deity/D = type - var/flags = initial(D.deity_flags) - - if(flags & DEITY_STRUCTURE_NEAR_IMPORTANT && !deity.near_structure(target)) - if(user) - to_chat(user, SPAN_WARNING("You need to be near \a [deity.get_type_name(/obj/structure/deity/altar)] to build this!")) - return 0 - - if(flags & DEITY_STRUCTURE_ALONE) - for(var/structure in deity.structures) - if(istype(structure,type) && get_dist(target,structure) <= 3) - if(user) - to_chat(user, SPAN_WARNING("You are too close to another [deity.get_type_name(type)]!")) - return 0 - return 1 - -/obj/structure/deity - icon = 'icons/obj/cult.dmi' - max_health = 10 - density = TRUE - anchored = TRUE - icon_state = "tomealtar" - is_spawnable_type = FALSE // will usually runtime without a linked god - - var/mob/living/deity/linked_god - var/power_adjustment = 1 //How much power we get/lose - var/build_cost = 0 //How much it costs to build this item. - var/deity_flags = DEITY_STRUCTURE_NEAR_IMPORTANT - -/obj/structure/deity/Initialize(mapload, var/god) - . = ..(mapload) - if(god) - linked_god = god - linked_god.form.sync_structure(src) - linked_god.adjust_source(power_adjustment, src) - -/obj/structure/deity/Destroy() - if(linked_god) - linked_god.adjust_source(-power_adjustment, src) - linked_god = null - return ..() - -/obj/structure/deity/attackby(obj/item/W, mob/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - user.do_attack_animation(src) - playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 50, 1) - user.visible_message( - SPAN_DANGER("[user] hits \the [src] with \the [W]!"), - SPAN_DANGER("You hit \the [src] with \the [W]!"), - SPAN_DANGER("You hear something breaking!") - ) - take_damage(W.get_attack_force(user), W.atom_damage_type) - -/obj/structure/deity/physically_destroyed(mob/user) - SHOULD_CALL_PARENT(FALSE) - qdel(src) - . = TRUE - -/obj/structure/deity/physically_destroyed(var/skip_qdel) - visible_message(SPAN_DANGER("\The [src] crumbles!")) - . = ..() - -/obj/structure/deity/bullet_act(var/obj/item/projectile/P) - take_damage(P.damage, P.atom_damage_type) - -/obj/structure/deity/proc/attack_deity(var/mob/living/deity/deity) - return \ No newline at end of file diff --git a/mods/gamemodes/deity/structures/trap.dm b/mods/gamemodes/deity/structures/trap.dm deleted file mode 100644 index 240050d33b9c..000000000000 --- a/mods/gamemodes/deity/structures/trap.dm +++ /dev/null @@ -1,30 +0,0 @@ -/obj/structure/deity/trap - density = FALSE - current_health = 1 - var/triggered = 0 - -/obj/structure/deity/trap/Initialize() - . = ..() - events_repository.register(/decl/observ/entered, get_turf(src),src, TYPE_PROC_REF(/obj/structure/deity/trap, trigger)) - -/obj/structure/deity/trap/Destroy() - events_repository.unregister(/decl/observ/entered, get_turf(src),src) - return ..() - -/obj/structure/deity/trap/Move() - events_repository.unregister(/decl/observ/entered, get_turf(src),src) - . = ..() - events_repository.register(/decl/observ/entered, get_turf(src), src, TYPE_PROC_REF(/obj/structure/deity/trap, trigger)) - -/obj/structure/deity/trap/attackby(obj/item/W, mob/user) - trigger(user) - return ..() - -/obj/structure/deity/trap/bullet_act() - return - -/obj/structure/deity/trap/proc/trigger(var/atom/entered, var/atom/movable/enterer) - if(triggered > world.time || !isliving(enterer)) - return - - triggered = world.time + 30 SECONDS \ No newline at end of file diff --git a/nano/templates/deity.tmpl b/nano/templates/deity.tmpl deleted file mode 100644 index 5d1148e300e2..000000000000 --- a/nano/templates/deity.tmpl +++ /dev/null @@ -1,116 +0,0 @@ - -
    -

    Deity Menu

    -
    - {{:data.name}} the {{:data.form_name}} -
    -
    - Current Boon: - {{if data.boon_name}} - {{:data.boon_name}} - {{else}} - N/A - {{/if}} -
    -
    - Power: {{:data.power}} Power Minimum: {{:data.power_min}} Regen: {{:data.regen}} -
    -
    - {{:helper.link('Followers', 'person', {'switchMenu' : 0, 'menu' : 'menu'}, data.menu == 0 ? 'disabled' : null)}} - {{:helper.link('Shop', 'suitcase', {'switchMenu' : 1, 'menu' : 'menu'}, data.menu == 1 ? 'disabled' : null)}} - {{:helper.link('Phenomena', 'star', {'switchMenu' : 2, 'menu' : 'menu'}, data.menu == 2 ? 'disabled' : null)}} -
    -
    -{{if data.menu == 0}} -

    Followers

    - {{for data.followers}} -
    - {{:value.name}} -
    -
    - {{:helper.link('Jump', 'zoomin', {'jump' : value.ref}, value.ref ? null : 'disabled')}} - {{:helper.link('Follow', 'zoomout', {'jump' : value.ref, 'follow' : 1}, value.ref ? null : 'disabled')}} -
    - {{empty}} - You have no minions! - {{/for}} -{{else data.menu == 1}} -

    Shop

    -
    - {{for data.categories}} - {{:helper.link(value, null, {'switchCategory' : index}, index == data.category ? 'disabled' : null)}} - {{/for}} -
    -
    - {{for data.item_data}} -
    -
    - {{:helper.link(value.name, null, {'buy' : value.ref})}} (Level {{:value.level}}) -
    -
    - Costs: {{:value.cost}} Requires: {{:value.requirements}} -
    -
    - {{:value.desc}} -
    -
    - {{empty}} - There is nothing there! - {{/for}} -{{else data.menu == 2}} -

    Phenomena

    -
    -
    - Selected Phenomena: -
    -
    - {{if data.selectedPhenomenaName}} - {{:helper.link(data.selectedPhenomenaName, null, {'clear_selected' : 1})}} - {{else}} - N/A - {{/if}} -
    -
    -
    - {{:helper.link('Phenomena', 'star', {'switchMenu' : 0, 'menu' : 'phenomenaMenu'}, data.phenomenaMenu == 0 ? 'disabled' : null)}} - {{:helper.link('Bindings', 'key', {'switchMenu' : 1, 'menu' : 'phenomenaMenu'}, data.phenomenaMenu == 1 ? 'disabled' : null)}} -
    - {{if data.phenomenaMenu == 0}} - {{for data.phenomena}} -
    -
    - {{:helper.link(value.name, null, {'select_phenomena' : value.name})}} -
    -
    - Use Cost: {{:value.cost}} - {{if value.cooldown}} - Cooldown: {{:value.cooldown}} - {{/if}} -
    -
    - {{:value.description}} -
    -
    - {{empty}} - You don't have any phenomena! - {{/for}} - {{else data.phenomenaMenu == 1}} - {{for data.bindings}} -

    {{:value.intent}}

    - {{for value.intent_data :intentValue:intentKey}} -
    -
    - {{:intentValue.binding}} -
    -
    - {{:helper.link(intentValue.phenomena_name ? intentValue.phenomena_name : 'N/A', null, {'select_intent' : value.intent, 'select_binding' : intentValue.binding})}} -
    -
    - {{/for}} -
    - {{/for}} - {{/if}} -{{/if}} -
    \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index 44929d112015..b8ec27833411 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3763,8 +3763,6 @@ #include "code\modules\species\species_shapeshifter.dm" #include "code\modules\species\species_shapeshifter_bodytypes.dm" #include "code\modules\species\outsider\random.dm" -#include "code\modules\species\outsider\shadow.dm" -#include "code\modules\species\outsider\starlight.dm" #include "code\modules\species\station\golem.dm" #include "code\modules\species\station\human.dm" #include "code\modules\species\station\human_bodytypes.dm" diff --git a/test/check-paths.sh b/test/check-paths.sh index 161216458247..77f46145433e 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -47,7 +47,7 @@ exactly 3 "unmarked globally scoped variables" '^(/|)var/(?!global)' -P exactly 0 "global-marked member variables" '\t(/|)var.*/global/.+' -P exactly 0 "static-marked globally scoped variables" '^(/|)var.*/static/.+' -P exactly 1 "direct usage of decls_repository.get_decl()" 'decls_repository\.get_decl\(' -P -exactly 20 "direct loc set" '(\t|;|\.)loc\s*=(?!=)' -P +exactly 19 "direct loc set" '(\t|;|\.)loc\s*=(?!=)' -P exactly 0 "magic number mouse opacity set" 'mouse_opacity\s*=\s*[0-2]' -P exactly 1 "magic number density set" '\bdensity\s*=\s*[01]' -P exactly 0 "magic number anchored set" '\banchored\s*=\s*[01]' -P From 88d1e5722de79d1c0ecc210e95f758bb04f59b76 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 4 Dec 2024 17:21:27 +1100 Subject: [PATCH 0046/1331] Adding Doe's room dividers. --- code/game/objects/structures/divider.dm | 48 ++++++++++++++++++ .../crafting/stack_recipes/recipes_planks.dm | 5 ++ icons/obj/structures/divider.dmi | Bin 0 -> 545 bytes nebula.dme | 1 + 4 files changed, 54 insertions(+) create mode 100644 code/game/objects/structures/divider.dm create mode 100644 icons/obj/structures/divider.dmi diff --git a/code/game/objects/structures/divider.dm b/code/game/objects/structures/divider.dm new file mode 100644 index 000000000000..d0cb78e85dfd --- /dev/null +++ b/code/game/objects/structures/divider.dm @@ -0,0 +1,48 @@ +/obj/structure/divider + name = "room divider" + desc = "A thin, somewhat flimsy folding room divider." + icon = 'icons/obj/structures/divider.dmi' + icon_state = ICON_STATE_WORLD + "-closed" + material = /decl/material/solid/organic/wood/bamboo + color = /decl/material/solid/organic/wood/bamboo::color + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC | MAT_FLAG_ALTERATION_COLOR + var/extended = FALSE + +/obj/structure/divider/extended + icon_state = ICON_STATE_WORLD + extended = TRUE + +/obj/structure/divider/wood + material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color + +/obj/structure/divider/extended/wood + material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color + +/obj/structure/divider/attack_hand(mob/user) + if(user.check_intent(I_FLAG_HELP) && user.check_dexterity(DEXTERITY_SIMPLE_MACHINES, silent = TRUE)) + extended = !extended + if(material.dooropen_noise) + playsound(loc, material.dooropen_noise, 50, 1) + update_divider() + visible_message(SPAN_NOTICE("\The [user] [extended ? "extends" : "collapses"] \the [src].")) + return TRUE + . = ..() + +/obj/structure/divider/Initialize() + . = ..() + update_divider() + +/obj/structure/divider/proc/update_divider() + anchored = extended + density = extended + opacity = extended || (material.opacity < 0.5) + update_icon() + +/obj/structure/divider/on_update_icon() + . = ..() + if(extended) + icon_state = ICON_STATE_WORLD + else + icon_state = ICON_STATE_WORLD + "-closed" diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index f8a750754998..f58f931cf5bb 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -222,3 +222,8 @@ /decl/stack_recipe/planks/furniture/gravemarker result_type = /obj/item/gravemarker difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/planks/furniture/divider + result_type = /obj/structure/divider + difficulty = MAT_VALUE_HARD_DIY + diff --git a/icons/obj/structures/divider.dmi b/icons/obj/structures/divider.dmi new file mode 100644 index 0000000000000000000000000000000000000000..5016482a2a1a740de5245e13695a1c7006702be2 GIT binary patch literal 545 zcmV++0^a?JP)p8x;sVP5=M^0d!JMQvg8b*k%9#0Csv*Sad{Xb7OL8aCB*J zZU6vyoKseCa&`CgQ*iP1D@x2wg|Jao=_cpo7pEdAHbGIWtl;YB0(Juc zO!z0Gl`RsA0003vNkl^!8U}@2U1G7cTS7iVkAW? z0#k7An1Zh6XjTNa)7!M2rmm@35eRFsCaegUW-xMeu_MHE`1GElN?f{OebOzFr0smPX zc%^2?Rs_E2-$r2t5x8)jNmyqQprJCi$uhH*#?0eGeh+R!eh>aWAif8;arcK3r^E|* jRzRE*FW^~$hrb8ksLQZ5vNt~K00000NkvXXu0mjf`5Eii literal 0 HcmV?d00001 diff --git a/nebula.dme b/nebula.dme index 44929d112015..d3da872dbb52 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1440,6 +1440,7 @@ #include "code\game\objects\structures\curtains.dm" #include "code\game\objects\structures\defensive_barrier.dm" #include "code\game\objects\structures\displaycase.dm" +#include "code\game\objects\structures\divider.dm" #include "code\game\objects\structures\dogbed.dm" #include "code\game\objects\structures\door_assembly.dm" #include "code\game\objects\structures\double_sign.dm" From 9e4b0f9c631f73600fd5e7fb4032d9c96cd82faa Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 7 Dec 2024 12:10:36 -0500 Subject: [PATCH 0047/1331] Fix banner merge skew --- code/modules/banners/__banner.dm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/modules/banners/__banner.dm b/code/modules/banners/__banner.dm index 647f3adc73a3..34727eaa11b7 100644 --- a/code/modules/banners/__banner.dm +++ b/code/modules/banners/__banner.dm @@ -1,5 +1,6 @@ /obj/item/banner name = "banner" + base_name = "banner" // necessary for premapped subtypes desc = "A furled-up banner." icon = 'icons/obj/items/banners/banner.dmi' icon_state = ICON_STATE_WORLD @@ -12,7 +13,6 @@ var/hung_desc = "The banner is rather unremarkable." var/banner_type = /obj/item/banner var/embroiderable = TRUE - var/name_prefix var/list/decals var/trim_color @@ -66,11 +66,6 @@ var/global/list/banner_type_to_symbols = list() . = ..() -// 'woven grass banner', 'forked linen banner' -/obj/item/banner/update_name() - . = ..() - SetName("[name_prefix] [name]") - /obj/item/banner/examine(mob/user, distance, infix, suffix) . = ..() var/decorations = get_decal_string() From 1a62c5dbcb9d2ec4d893ead791d96aef67f74d6a Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 5 Dec 2024 14:53:07 +1100 Subject: [PATCH 0048/1331] Removing wizard. --- code/__defines/gamemode.dm | 23 +- code/_onclick/hud/screen/screen_abilities.dm | 6 +- .../datums/config/config_types/config_mode.dm | 7 +- code/datums/mind/mind.dm | 1 - code/datums/outfits/wizardry.dm | 6 +- code/datums/supplypacks/nonessent.dm | 8 +- code/datums/trading/traders/ai.dm | 1 - code/datums/trading/traders/goods.dm | 1 - code/datums/trading/traders/misc.dm | 4 +- code/datums/trading/traders/unique.dm | 48 - code/game/antagonist/outsider/wizard.dm | 115 -- .../wizard/servant_items/caretaker.dm | 34 - .../wizard/servant_items/champion.dm | 85 - .../wizard/servant_items/familiar.dm | 15 - .../gamemodes/wizard/servant_items/fiend.dm | 49 - .../wizard/servant_items/infiltrator.dm | 42 - .../wizard/servant_items/overseer.dm | 40 - code/game/gamemodes/wizard/wizard.dm | 10 - code/game/gamemodes/wizard/wizard_props.dm | 21 - code/game/jobs/access_datum.dm | 6 - code/game/objects/effects/landmarks.dm | 12 +- .../objects/items/devices/radio/intercom.dm | 4 - code/game/objects/items/hourglass.dm | 6 +- code/game/objects/items/weapons/scrolls.dm | 50 - code/game/objects/items/weapons/staff.dm | 3 +- code/modules/clothing/_clothing.dm | 1 - code/modules/clothing/head/wizard.dm | 32 + code/modules/clothing/masks/miscellaneous.dm | 10 +- code/modules/clothing/shoes/miscellaneous.dm | 1 - .../clothing/spacesuits/void/wizard.dm | 66 - code/modules/clothing/suits/misc.dm | 5 + code/modules/clothing/suits/wiz_robe.dm | 113 -- code/modules/clothing/suits/wizard.dm | 25 + code/modules/ghosttrap/trap.dm | 12 - code/modules/mob/death.dm | 2 +- .../simple_animal/familiars/familiars.dm | 133 -- code/modules/organs/internal/species/golem.dm | 5 +- .../modules/projectiles/guns/energy/staves.dm | 12 +- code/modules/spells/aoe_turf/aoe_turf.dm | 4 +- code/modules/spells/aoe_turf/blink.dm | 44 - code/modules/spells/aoe_turf/charge.dm | 72 - .../spells/aoe_turf/conjure/conjure.dm | 5 +- .../spells/aoe_turf/conjure/druidic_spells.dm | 113 -- .../spells/aoe_turf/conjure/faithful_hound.dm | 28 - .../spells/aoe_turf/conjure/force_portal.dm | 12 - .../spells/aoe_turf/conjure/forcewall.dm | 48 - code/modules/spells/aoe_turf/conjure/grove.dm | 75 - code/modules/spells/aoe_turf/disable_tech.dm | 33 - code/modules/spells/aoe_turf/drain_blood.dm | 66 - .../spells/aoe_turf/exchange_wounds.dm | 43 - code/modules/spells/aoe_turf/knock.dm | 37 - code/modules/spells/aoe_turf/smoke.dm | 26 - code/modules/spells/aoe_turf/summons.dm | 72 - code/modules/spells/artifacts.dm | 41 - .../spells/artifacts/spellbound_servants.dm | 284 --- code/modules/spells/artifacts/storage.dm | 30 - code/modules/spells/contracts.dm | 142 -- code/modules/spells/general/acid_spray.dm | 26 - code/modules/spells/general/area_teleport.dm | 60 - code/modules/spells/general/camera_vision.dm | 36 - .../modules/spells/general/contract_spells.dm | 68 - code/modules/spells/general/create_air.dm | 24 - code/modules/spells/general/invisibility.dm | 24 - code/modules/spells/general/mark_recall.dm | 88 - .../modules/spells/general/portal_teleport.dm | 67 - code/modules/spells/general/radiant_aura.dm | 21 - code/modules/spells/general/return_master.dm | 23 - code/modules/spells/general/toggle_armor.dm | 121 -- code/modules/spells/hand/blood_shards.dm | 39 - code/modules/spells/hand/burning_grip.dm | 39 - code/modules/spells/hand/entangle.dm | 51 - code/modules/spells/hand/hand.dm | 83 - code/modules/spells/hand/hand_item.dm | 68 - code/modules/spells/hand/slippery_surface.dm | 19 - code/modules/spells/hand/sunwrath.dm | 32 - code/modules/spells/no_clothes.dm | 5 - code/modules/spells/racial_wizard.dm | 81 - code/modules/spells/spell_code.dm | 28 +- code/modules/spells/spell_projectile.dm | 56 - code/modules/spells/spellbook.dm | 322 --- code/modules/spells/spellbook/battlemage.dm | 44 - code/modules/spells/spellbook/cleric.dm | 50 - code/modules/spells/spellbook/druid.dm | 43 - code/modules/spells/spellbook/spatial.dm | 48 - code/modules/spells/spellbook/standard.dm | 54 - code/modules/spells/spellbook/student.dm | 28 - code/modules/spells/targeted/analyze.dm | 19 - code/modules/spells/targeted/blood_boil.dm | 25 - code/modules/spells/targeted/cleric_spells.dm | 248 --- .../spells/targeted/equip/burning_touch.dm | 69 - code/modules/spells/targeted/equip/dyrnwyn.dm | 37 - code/modules/spells/targeted/equip/equip.dm | 38 - .../spells/targeted/equip/holy_relic.dm | 34 - .../spells/targeted/equip/horsemask.dm | 48 - .../spells/targeted/equip/party_hardy.dm | 36 - code/modules/spells/targeted/equip/seed.dm | 21 - code/modules/spells/targeted/equip/shield.dm | 41 - .../modules/spells/targeted/ethereal_jaunt.dm | 2 +- .../spells/targeted/exude_pleasantness.dm | 19 - code/modules/spells/targeted/genetic.dm | 73 - .../spells/targeted/glimpse_of_eternity.dm | 26 - .../spells/targeted/projectile/dumbfire.dm | 13 - .../spells/targeted/projectile/fireball.dm | 66 - .../targeted/projectile/magic_missile.dm | 56 - .../spells/targeted/projectile/passage.dm | 46 - .../spells/targeted/projectile/projectile.dm | 45 - .../spells/targeted/projectile/stuncuff.dm | 47 - code/modules/spells/targeted/shapeshift.dm | 203 -- code/modules/spells/targeted/shatter_mind.dm | 29 - code/modules/spells/targeted/shift.dm | 2 +- code/modules/spells/targeted/subjugate.dm | 35 - code/modules/spells/targeted/swap.dm | 41 - code/modules/spells/targeted/targeted.dm | 45 +- code/modules/spells/targeted/torment.dm | 34 - config/example/configuration.txt | 39 +- maps/antag_spawn/wizard/wizard.dm | 13 - maps/antag_spawn/wizard/wizard_base.dmm | 1747 ----------------- mods/content/corporate/items/stamps.dm | 4 +- mods/content/psionics/_psionics.dme | 1 + .../psionics/datum/antagonists/paramount.dm | 2 +- mods/content/psionics/datum/chems.dm | 8 +- mods/content/psionics/items/clothing.dm | 16 + mods/gamemodes/cult/_cult.dme | 1 - mods/gamemodes/cult/items.dm | 6 +- mods/gamemodes/cult/overrides.dm | 13 - mods/gamemodes/cult/wizard.dm | 47 - .../bayliens/tajaran/machinery/suit_cycler.dm | 3 - mods/species/vox/_vox.dme | 1 - mods/species/vox/datum/antagonism.dm | 4 - mods/species/vox/gear/gun.dm | 24 - nebula.dme | 87 +- 131 files changed, 171 insertions(+), 7080 deletions(-) delete mode 100644 code/game/antagonist/outsider/wizard.dm delete mode 100644 code/game/gamemodes/wizard/servant_items/caretaker.dm delete mode 100644 code/game/gamemodes/wizard/servant_items/champion.dm delete mode 100644 code/game/gamemodes/wizard/servant_items/familiar.dm delete mode 100644 code/game/gamemodes/wizard/servant_items/fiend.dm delete mode 100644 code/game/gamemodes/wizard/servant_items/infiltrator.dm delete mode 100644 code/game/gamemodes/wizard/servant_items/overseer.dm delete mode 100644 code/game/gamemodes/wizard/wizard.dm delete mode 100644 code/game/gamemodes/wizard/wizard_props.dm delete mode 100644 code/game/objects/items/weapons/scrolls.dm create mode 100644 code/modules/clothing/head/wizard.dm delete mode 100644 code/modules/clothing/spacesuits/void/wizard.dm create mode 100644 code/modules/clothing/suits/misc.dm delete mode 100644 code/modules/clothing/suits/wiz_robe.dm create mode 100644 code/modules/clothing/suits/wizard.dm delete mode 100644 code/modules/mob/living/simple_animal/familiars/familiars.dm delete mode 100644 code/modules/spells/aoe_turf/blink.dm delete mode 100644 code/modules/spells/aoe_turf/charge.dm delete mode 100644 code/modules/spells/aoe_turf/conjure/druidic_spells.dm delete mode 100644 code/modules/spells/aoe_turf/conjure/faithful_hound.dm delete mode 100644 code/modules/spells/aoe_turf/conjure/force_portal.dm delete mode 100644 code/modules/spells/aoe_turf/conjure/forcewall.dm delete mode 100644 code/modules/spells/aoe_turf/conjure/grove.dm delete mode 100644 code/modules/spells/aoe_turf/disable_tech.dm delete mode 100644 code/modules/spells/aoe_turf/drain_blood.dm delete mode 100644 code/modules/spells/aoe_turf/exchange_wounds.dm delete mode 100644 code/modules/spells/aoe_turf/knock.dm delete mode 100644 code/modules/spells/aoe_turf/smoke.dm delete mode 100644 code/modules/spells/aoe_turf/summons.dm delete mode 100644 code/modules/spells/artifacts.dm delete mode 100644 code/modules/spells/artifacts/spellbound_servants.dm delete mode 100644 code/modules/spells/artifacts/storage.dm delete mode 100644 code/modules/spells/contracts.dm delete mode 100644 code/modules/spells/general/acid_spray.dm delete mode 100644 code/modules/spells/general/area_teleport.dm delete mode 100644 code/modules/spells/general/camera_vision.dm delete mode 100644 code/modules/spells/general/contract_spells.dm delete mode 100644 code/modules/spells/general/create_air.dm delete mode 100644 code/modules/spells/general/invisibility.dm delete mode 100644 code/modules/spells/general/mark_recall.dm delete mode 100644 code/modules/spells/general/portal_teleport.dm delete mode 100644 code/modules/spells/general/radiant_aura.dm delete mode 100644 code/modules/spells/general/return_master.dm delete mode 100644 code/modules/spells/general/toggle_armor.dm delete mode 100644 code/modules/spells/hand/blood_shards.dm delete mode 100644 code/modules/spells/hand/burning_grip.dm delete mode 100644 code/modules/spells/hand/entangle.dm delete mode 100644 code/modules/spells/hand/hand.dm delete mode 100644 code/modules/spells/hand/hand_item.dm delete mode 100644 code/modules/spells/hand/slippery_surface.dm delete mode 100644 code/modules/spells/hand/sunwrath.dm delete mode 100644 code/modules/spells/no_clothes.dm delete mode 100644 code/modules/spells/racial_wizard.dm delete mode 100644 code/modules/spells/spell_projectile.dm delete mode 100644 code/modules/spells/spellbook.dm delete mode 100644 code/modules/spells/spellbook/battlemage.dm delete mode 100644 code/modules/spells/spellbook/cleric.dm delete mode 100644 code/modules/spells/spellbook/druid.dm delete mode 100644 code/modules/spells/spellbook/spatial.dm delete mode 100644 code/modules/spells/spellbook/standard.dm delete mode 100644 code/modules/spells/spellbook/student.dm delete mode 100644 code/modules/spells/targeted/analyze.dm delete mode 100644 code/modules/spells/targeted/blood_boil.dm delete mode 100644 code/modules/spells/targeted/cleric_spells.dm delete mode 100644 code/modules/spells/targeted/equip/burning_touch.dm delete mode 100644 code/modules/spells/targeted/equip/dyrnwyn.dm delete mode 100644 code/modules/spells/targeted/equip/equip.dm delete mode 100644 code/modules/spells/targeted/equip/holy_relic.dm delete mode 100644 code/modules/spells/targeted/equip/horsemask.dm delete mode 100644 code/modules/spells/targeted/equip/party_hardy.dm delete mode 100644 code/modules/spells/targeted/equip/seed.dm delete mode 100644 code/modules/spells/targeted/equip/shield.dm delete mode 100644 code/modules/spells/targeted/exude_pleasantness.dm delete mode 100644 code/modules/spells/targeted/genetic.dm delete mode 100644 code/modules/spells/targeted/glimpse_of_eternity.dm delete mode 100644 code/modules/spells/targeted/projectile/dumbfire.dm delete mode 100644 code/modules/spells/targeted/projectile/fireball.dm delete mode 100644 code/modules/spells/targeted/projectile/magic_missile.dm delete mode 100644 code/modules/spells/targeted/projectile/passage.dm delete mode 100644 code/modules/spells/targeted/projectile/projectile.dm delete mode 100644 code/modules/spells/targeted/projectile/stuncuff.dm delete mode 100644 code/modules/spells/targeted/shapeshift.dm delete mode 100644 code/modules/spells/targeted/shatter_mind.dm delete mode 100644 code/modules/spells/targeted/subjugate.dm delete mode 100644 code/modules/spells/targeted/swap.dm delete mode 100644 code/modules/spells/targeted/torment.dm delete mode 100644 maps/antag_spawn/wizard/wizard.dm delete mode 100644 maps/antag_spawn/wizard/wizard_base.dmm create mode 100644 mods/content/psionics/items/clothing.dm delete mode 100644 mods/gamemodes/cult/wizard.dm delete mode 100644 mods/species/vox/datum/antagonism.dm diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 9493ae7aa125..94c9d41639ae 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -33,26 +33,11 @@ #define DEFAULT_TELECRYSTAL_AMOUNT 130 #define IMPLANT_TELECRYSTAL_AMOUNT(x) (round(x * 0.49)) // If this cost is ever greater than half of DEFAULT_TELECRYSTAL_AMOUNT then it is possible to buy more TC than you spend -///////////////// -////WIZARD ////// -///////////////// +// SPELL FLAGS +#define Z2NOCAST BITFLAG(0) //if this is added, the spell can't be cast at centcomm +#define INCLUDEUSER BITFLAG(1) //does the spell include the caster in its target selection? +#define IGNOREDENSE BITFLAG(2) //are dense turfs ignored in selection? -/* WIZARD SPELL FLAGS */ -#define GHOSTCAST BITFLAG(0) //can a ghost cast it? -#define NEEDSCLOTHES BITFLAG(1) //does it need the wizard garb to cast? Nonwizard spells should not have this -#define NEEDSHUMAN BITFLAG(2) //does it require the caster to be human? -#define Z2NOCAST BITFLAG(3) //if this is added, the spell can't be cast at centcomm -#define NO_SOMATIC BITFLAG(4) //spell will go off if the person is incapacitated or stunned -#define IGNOREPREV BITFLAG(5) //if set, each new target does not overlap with the previous one -//The following flags only affect different types of spell, and therefore overlap -//Targeted spells -#define INCLUDEUSER BITFLAG(6) //does the spell include the caster in its target selection? -#define SELECTABLE BITFLAG(7) //can you select each target for the spell? -#define NOFACTION BITFLAG(8) //Don't do the same as our faction -#define NONONFACTION BITFLAG(9) //Don't do people other than our faction -//AOE spells -#define IGNOREDENSE BITFLAG(10) //are dense turfs ignored in selection? -#define IGNORESPACE BITFLAG(11) //are space turfs ignored in selection? //End split flags #define CONSTRUCT_CHECK BITFLAG(12) //used by construct spells - checks for nullrods #define NO_BUTTON BITFLAG(13) //spell won't show up in the HUD with this diff --git a/code/_onclick/hud/screen/screen_abilities.dm b/code/_onclick/hud/screen/screen_abilities.dm index 03961a964233..414458af058e 100644 --- a/code/_onclick/hud/screen/screen_abilities.dm +++ b/code/_onclick/hud/screen/screen_abilities.dm @@ -195,7 +195,6 @@ if(object) object.Click() -// Wizard /obj/screen/ability/spell var/spell/spell var/spell_base @@ -223,10 +222,7 @@ A.SetName(spell.name) if(!spell.override_base) //if it's not set, we do basic checks - if(spell.spell_flags & CONSTRUCT_CHECK) - A.spell_base = "const" //construct spells - else - A.spell_base = "wiz" //wizard spells + A.spell_base = "const" //construct spells else A.spell_base = spell.override_base A.update_charge(1) diff --git a/code/datums/config/config_types/config_mode.dm b/code/datums/config/config_types/config_mode.dm index 7db16b2ca346..975b84ebca92 100644 --- a/code/datums/config/config_types/config_mode.dm +++ b/code/datums/config/config_types/config_mode.dm @@ -6,7 +6,6 @@ /decl/config/lists/mode_allowed, /decl/config/lists/mode_votable, /decl/config/lists/mode_probabilities, - /decl/config/toggle/feature_object_spell_system, /decl/config/toggle/traitor_scaling, /decl/config/toggle/protect_roles_from_antagonist, /decl/config/toggle/continuous_rounds, @@ -78,10 +77,6 @@ var/decl/game_mode/game_mode = all_modes[mode_type] game_mode.probability = max(0, value[game_mode.uid]) -/decl/config/toggle/feature_object_spell_system - uid = "feature_object_spell_system" - desc = "Spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard." - /decl/config/toggle/traitor_scaling uid = "traitor_scaling" desc = "If amount of traitors scales or not." @@ -93,7 +88,7 @@ /decl/config/toggle/continuous_rounds uid = "continuous_rounds" desc = list( - "Remove the # to make rounds which end instantly (Rev, Wizard, Malf) to continue until the shuttle is called or the station is nuked.", + "Remove the # to make rounds which end instantly to continue until the shuttle is called or the station is nuked.", "Malf and Rev will let the shuttle be called when the antags/protags are dead." ) diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 355cd25efdc7..377c2dced75d 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -78,7 +78,6 @@ /datum/mind/proc/handle_mob_deletion(mob/living/deleted_mob) if (current == deleted_mob) - current.spellremove() current = null if (original == deleted_mob) diff --git a/code/datums/outfits/wizardry.dm b/code/datums/outfits/wizardry.dm index a0f1a5881950..b3da295036d5 100644 --- a/code/datums/outfits/wizardry.dm +++ b/code/datums/outfits/wizardry.dm @@ -2,10 +2,8 @@ uniform = /obj/item/clothing/jumpsuit/lightpurple shoes = /obj/item/clothing/shoes/sandal l_ear = /obj/item/radio/headset - r_pocket = /obj/item/paper/scroll/teleportation hands = list( - /obj/item/staff/crystal, - /obj/item/book/spell + /obj/item/staff/crystal ) back = /obj/item/backpack backpack_contents = list(/obj/item/box = 1) @@ -26,4 +24,4 @@ name = "Wizard - Marisa" head = /obj/item/clothing/head/wizard/marisa suit = /obj/item/clothing/suit/wizrobe/marisa - shoes = /obj/item/clothing/shoes/sandal/marisa + shoes = /obj/item/clothing/shoes/sandal/marisa \ No newline at end of file diff --git a/code/datums/supplypacks/nonessent.dm b/code/datums/supplypacks/nonessent.dm index 6575ca30fd0c..cee09fc29b68 100644 --- a/code/datums/supplypacks/nonessent.dm +++ b/code/datums/supplypacks/nonessent.dm @@ -74,9 +74,9 @@ /decl/hierarchy/supply_pack/nonessent/wizard name = "Costume - Wizard" contains = list(/obj/item/staff/crystal, - /obj/item/clothing/suit/wizrobe/fake, + /obj/item/clothing/suit/wizrobe, /obj/item/clothing/shoes/sandal, - /obj/item/clothing/head/wizard/fake) + /obj/item/clothing/head/wizard/beard) containername = "wizard costume crate" /decl/hierarchy/supply_pack/nonessent/costume @@ -170,9 +170,9 @@ /decl/hierarchy/supply_pack/nonessent/witch name = "Costume - Witch" - contains = list(/obj/item/clothing/suit/wizrobe/marisa/fake, + contains = list(/obj/item/clothing/suit/wizrobe/marisa, /obj/item/clothing/shoes/sandal, - /obj/item/clothing/head/wizard/marisa/fake, + /obj/item/clothing/head/wizard/marisa, /obj/item/staff/broom) containername = "witch costume crate" containertype = /obj/structure/closet diff --git a/code/datums/trading/traders/ai.dm b/code/datums/trading/traders/ai.dm index ca86637d32d7..05b15422e1d8 100644 --- a/code/datums/trading/traders/ai.dm +++ b/code/datums/trading/traders/ai.dm @@ -37,7 +37,6 @@ They sell generic supplies and ask for generic supplies. ) possible_trading_items = list( /obj/item/bag = TRADER_SUBTYPES_ONLY, - /obj/item/bag/cash/infinite = TRADER_BLACKLIST, /obj/item/backpack = TRADER_ALL, /obj/item/backpack/cultpack = TRADER_BLACKLIST, /obj/item/backpack/holding = TRADER_BLACKLIST, diff --git a/code/datums/trading/traders/goods.dm b/code/datums/trading/traders/goods.dm index d260685de193..81d294268dcd 100644 --- a/code/datums/trading/traders/goods.dm +++ b/code/datums/trading/traders/goods.dm @@ -47,7 +47,6 @@ /obj/item/deck = TRADER_SUBTYPES_ONLY, /obj/item/pack = TRADER_SUBTYPES_ONLY, /obj/item/dice = TRADER_ALL, - /obj/item/dice/d20/cursed = TRADER_BLACKLIST, /obj/item/gun/launcher/money = TRADER_THIS_TYPE ) diff --git a/code/datums/trading/traders/misc.dm b/code/datums/trading/traders/misc.dm index 4366e0fede48..dd3e0df898f1 100644 --- a/code/datums/trading/traders/misc.dm +++ b/code/datums/trading/traders/misc.dm @@ -167,8 +167,8 @@ /obj/item/clothing/suit/hastur = TRADER_THIS_TYPE, /obj/item/clothing/suit/imperium_monk = TRADER_THIS_TYPE, /obj/item/clothing/suit/judgerobe = TRADER_THIS_TYPE, - /obj/item/clothing/suit/wizrobe/magusred = TRADER_THIS_TYPE, - /obj/item/clothing/suit/wizrobe/magusblue = TRADER_THIS_TYPE, + /obj/item/clothing/suit/wizrobe/magus = TRADER_THIS_TYPE, + /obj/item/clothing/suit/wizrobe/magus/red = TRADER_THIS_TYPE, /obj/item/clothing/costume/gladiator = TRADER_THIS_TYPE, /obj/item/clothing/costume/kilt = TRADER_THIS_TYPE, /obj/item/clothing/costume/redcoat = TRADER_THIS_TYPE, diff --git a/code/datums/trading/traders/unique.dm b/code/datums/trading/traders/unique.dm index d5c98f8e392d..e71995049218 100644 --- a/code/datums/trading/traders/unique.dm +++ b/code/datums/trading/traders/unique.dm @@ -90,51 +90,3 @@ TRADER_BRIBE_ACCEPT = "Blub will stay for " + TRADER_TOKEN_TIME + " binutes bonger.", TRADER_BRIBE_REFUSAL = "Blub must go. Blub's beople beed blem." ) - -//probably could stick soem Howl references in here but like, eh. Haven't seen it in years. -/datum/trader/ship/unique/wizard - name = "Sorcerer" - origin = "A moving castle" - possible_origins = list( - "An indistinct location", - "Unknown location", - "The Diamond Sphere", - "Beyond the Veil", - "Deadverse" - ) - - possible_wanted_items = list( - /mob/living/simple_animal/familiar = TRADER_SUBTYPES_ONLY, - /mob/living/simple_animal/familiar/pet = TRADER_BLACKLIST, - /mob/living/simple_animal/hostile/mimic = TRADER_ALL - ) - possible_trading_items = list( - /obj/item/clothing/gloves/wizard = TRADER_THIS_TYPE, - /obj/item/clothing/head/helmet/space/void/wizard = TRADER_THIS_TYPE, - /obj/item/clothing/head/wizard = TRADER_ALL, - /obj/item/clothing/suit/space/void/wizard = TRADER_THIS_TYPE, - /obj/item/toy/figure/wizard = TRADER_THIS_TYPE, - /obj/item/staff = TRADER_ALL, - ) //Probably see about getting some more wizard based shit - - speech = list( - TRADER_HAIL_GENERIC = "Hello! Are you here on pleasure or business?", - TRADER_HAIL_DENY = "I'm sorry, but I REALLY don't want to speak to you.", - TRADER_TRADE_COMPLETE = "Pleasure doing business with you!", - TRADER_NO_MONEY = "Cash? Ha! What's cash to a man like me?", - TRADER_NOT_ENOUGH = "Hm, well I do enjoy what you're offering, I prefer a fair trade.", - TRADER_FOUND_UNWANTED = "What? I want oddities! Don't you understand?", - TRADER_HOW_MUCH = "I want dark things, brooding things... things that go bump in the night. Things that bleed wrong, live wrong, are wrong.", - TRADER_WHAT_WANT = "Have anything from a broodish cult?", - TRADER_COMPLIMENT_DENY = "Like I haven't heard that one before!", - TRADER_COMPLIMENT_ACCEPT = "Haha! Aren't you nice.", - TRADER_INSULT_GOOD = "Naughty naughty.", - TRADER_INSULT_BAD = "Now where do you get off talking to me like that?", - TRADER_BRIBE_ACCEPT = "Well, if you're not pulling the knob on my staff, I can stay for another " + TRADER_TOKEN_TIME + " minutes.", - TRADER_BRIBE_REFUSAL = "A wizard does not depart early or late, but precisely when they intend to. No.", - TRADER_NO_BLACKLISTED = "I cannot accept such a thing. No trade." - ) - -/datum/trader/ship/unique/wizard/New() - speech[TRADER_HAIL_START + SPECIES_GOLEM] = "Interesting... how incredibly interesting... come! Let us do business!" - ..() diff --git a/code/game/antagonist/outsider/wizard.dm b/code/game/antagonist/outsider/wizard.dm deleted file mode 100644 index 9aa819de8431..000000000000 --- a/code/game/antagonist/outsider/wizard.dm +++ /dev/null @@ -1,115 +0,0 @@ -/decl/special_role/wizard - name = "Wizard" - name_plural = "Wizards" - landmark_id = "wizard" - welcome_text = "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.
    In your pockets you will find a teleport scroll. Use it as needed." - flags = ANTAG_OVERRIDE_JOB | ANTAG_OVERRIDE_MOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_VOTABLE | ANTAG_SET_APPEARANCE - antaghud_indicator = "hudwizard" - default_access = list(access_wizard) - hard_cap = 1 - hard_cap_round = 3 - initial_spawn_req = 1 - initial_spawn_target = 1 - min_player_age = 18 - - faction = "wizard" - base_to_load = "Wizard Base" - -/decl/special_role/wizard/create_objectives(var/datum/mind/wizard) - - if(!..()) - return - - var/kill - var/escape - var/steal - var/hijack - - switch(rand(1,100)) - if(1 to 30) - escape = 1 - kill = 1 - if(31 to 60) - escape = 1 - steal = 1 - if(61 to 99) - kill = 1 - steal = 1 - else - hijack = 1 - - if(kill) - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = wizard - kill_objective.find_target() - wizard.objectives |= kill_objective - if(steal) - var/datum/objective/steal/steal_objective = new - steal_objective.owner = wizard - steal_objective.find_target() - wizard.objectives |= steal_objective - if(escape) - var/datum/objective/survive/survive_objective = new - survive_objective.owner = wizard - wizard.objectives |= survive_objective - if(hijack) - var/datum/objective/hijack/hijack_objective = new - hijack_objective.owner = wizard - wizard.objectives |= hijack_objective - return - -/decl/special_role/wizard/update_antag_mob(var/datum/mind/wizard) - ..() - wizard.StoreMemory("Remember: do not forget to prepare your spells.", /decl/memory_options/system) - wizard.current.real_name = "[pick(global.wizard_first)] [pick(global.wizard_second)]" - wizard.current.SetName(wizard.current.real_name) - -/decl/special_role/wizard/equip_role(var/mob/living/human/wizard_mob) - default_outfit = pick(decls_repository.get_decl_paths_of_subtype(/decl/outfit/wizard)) - . = ..() - -/decl/special_role/wizard/print_player_summary() - ..() - for(var/p in current_antagonists) - var/datum/mind/player = p - var/text = "[player.name]'s spells were:" - if(!player.learned_spells || !player.learned_spells.len) - text += "
    None!" - else - for(var/s in player.learned_spells) - var/spell/spell = s - text += "
    [spell.name] - " - text += "Speed: [spell.spell_levels["speed"]] Power: [spell.spell_levels["power"]]" - text += "
    " - to_world(text) - - -//To batch-remove wizard spells. Linked to mind.dm. -/mob/proc/spellremove() - if(!mind || !mind.learned_spells) - return - for(var/spell/spell_to_remove in mind.learned_spells) - remove_spell(spell_to_remove) - -// Does this clothing slot count as wizard garb? (Combines a few checks) -/proc/is_wiz_garb(var/obj/item/clothing/C) - return istype(C) && C.wizard_garb - -/*Checks if the wizard is wearing the proper attire. -Made a proc so this is not repeated 14 (or more) times.*/ -/mob/proc/wearing_wiz_garb() - to_chat(src, "Silly creature, you're not a human. Only humans can cast this spell.") - return 0 - -// Humans can wear clothes. -/mob/living/human/wearing_wiz_garb() - if(!is_wiz_garb(get_equipped_item(slot_wear_suit_str)) && (!istype(species.species_hud) || (slot_wear_suit_str in species.species_hud.equip_slots))) - to_chat(src, "I don't feel strong enough without my robe.") - return 0 - if(!is_wiz_garb(get_equipped_item(slot_shoes_str)) && (!istype(species.species_hud) || (slot_shoes_str in species.species_hud.equip_slots))) - to_chat(src, "I don't feel strong enough without my sandals.") - return 0 - if(!is_wiz_garb(get_equipped_item(slot_head_str)) && (!istype(species.species_hud) || (slot_head_str in species.species_hud.equip_slots))) - to_chat(src, "I don't feel strong enough without my hat.") - return 0 - return 1 diff --git a/code/game/gamemodes/wizard/servant_items/caretaker.dm b/code/game/gamemodes/wizard/servant_items/caretaker.dm deleted file mode 100644 index c5adb272efec..000000000000 --- a/code/game/gamemodes/wizard/servant_items/caretaker.dm +++ /dev/null @@ -1,34 +0,0 @@ -/obj/item/clothing/head/caretakerhood - name = "holy hood" - desc = "The hood of a shining white robe, with blue trim. Who would possess this robe and still want to hide themself away?" - icon = 'icons/clothing/head/caretaker.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_KNIVES, - ARMOR_BULLET = ARMOR_BALLISTIC_MINOR, - ARMOR_LASER = ARMOR_LASER_SMALL, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_RAD = ARMOR_RAD_SHIELDED - ) - bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID - flags_inv = HIDEEARS | BLOCK_HEAD_HAIR - -/obj/item/clothing/suit/caretakercloak - name = "holy cloak" - desc = "A shining white and blue robe. For some reason, it reminds you of the holidays." - icon = 'icons/clothing/suits/wizard/servant/caretaker.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT, - ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL, - ARMOR_LASER = ARMOR_LASER_HANDGUNS, - ARMOR_ENERGY = ARMOR_ENERGY_RESISTANT, - ARMOR_RAD = ARMOR_RAD_SHIELDED - ) - -/obj/item/clothing/shoes/dress/caretakershoes - name = "black leather shoes" - desc = "Dress shoes. These aren't as shiny as usual." - inset_color = COLOR_SKY_BLUE - shine = 30 - armor = list( - ARMOR_RAD = ARMOR_RAD_SHIELDED - ) \ No newline at end of file diff --git a/code/game/gamemodes/wizard/servant_items/champion.dm b/code/game/gamemodes/wizard/servant_items/champion.dm deleted file mode 100644 index 80e04ee1bfdc..000000000000 --- a/code/game/gamemodes/wizard/servant_items/champion.dm +++ /dev/null @@ -1,85 +0,0 @@ -/obj/item/clothing/head/champhelm - name = "champion's crown" - desc = "A spiky, golden crown. It's probably worth more than your bank account." - - icon = 'icons/clothing/head/champion.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_VERY_HIGH, - ARMOR_BULLET = ARMOR_BALLISTIC_AP, - ARMOR_LASER = ARMOR_LASER_HANDGUNS, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_BOMB = ARMOR_BOMB_RESISTANT, - ARMOR_BIO = ARMOR_BIO_MINOR - ) - bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID - -/obj/item/clothing/suit/champarmor - name = "champion's armor" - desc = "A mighty suit of silver and gold armor, with a gleaming blue crystal inlaid into its left gaunlet." - icon = 'icons/clothing/suits/wizard/servant/champion.dmi' - siemens_coefficient = 0.5 - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_FEET|SLOT_ARMS|SLOT_HANDS|SLOT_TAIL - armor = list( - ARMOR_MELEE = ARMOR_MELEE_VERY_HIGH, - ARMOR_BULLET = ARMOR_BALLISTIC_AP, - ARMOR_LASER = ARMOR_LASER_HANDGUNS, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_BOMB = ARMOR_BOMB_RESISTANT, - ARMOR_BIO = ARMOR_BIO_MINOR - ) - -/obj/item/clothing/pants/champion - name = "champion's garb" - desc = "Some dark, archaic leggings." - icon = 'icons/clothing/pants/leggings/leggings_champion.dmi' - siemens_coefficient = 0.8 - armor = list( - ARMOR_MELEE = ARMOR_MELEE_MINOR - ) - starting_accessories = list( - /obj/item/clothing/shirt/tunic/blue/champion - ) - -/obj/item/clothing/shoes/jackboots/medievalboots - name = "leather boots" - desc = "Old-fashioned leather boots. Probably not something you want to get kicked with." - material = /decl/material/solid/organic/leather - armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT, - ARMOR_BULLET = ARMOR_BALLISTIC_MINOR, - ARMOR_LASER = ARMOR_LASER_MINOR, - ARMOR_ENERGY = ARMOR_ENERGY_MINOR, - ARMOR_BOMB = ARMOR_BOMB_PADDED - ) - artificail_shine = 0 - -/obj/item/sword/excalibur - name = "champion's blade" - desc = "For at his belt hung Excalibur, the finest sword that there was, which sliced through iron as through wood." - icon = 'icons/obj/items/weapon/swords/excalibur.dmi' - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "cleaved", "sundered") - material_alteration = MAT_FLAG_ALTERATION_NONE - -/obj/item/sword/excalibur/on_picked_up(var/mob/living/user) - if(user.mind) - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - if(!wizards.is_antagonist(user.mind) || user.mind.assigned_special_role != "Spellbound Servant") - START_PROCESSING(SSobj, src) - to_chat(user,"\The [src] heats up in your hands, burning you!") - -/obj/item/sword/excalibur/Process() - if(isliving(loc)) - if(ishuman(loc)) - var/mob/living/human/H = loc - var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(H, H.get_active_held_item_slot()) - E?.take_external_damage(burn=2,used_weapon="stovetop") - else - var/mob/living/M = loc - M.take_damage(2, BURN) - if(prob(2)) - to_chat(loc,"\The [src] is burning you!") - return 1 - -/obj/item/sword/excalibur/dropped() - . = ..() - STOP_PROCESSING(SSobj, src) \ No newline at end of file diff --git a/code/game/gamemodes/wizard/servant_items/familiar.dm b/code/game/gamemodes/wizard/servant_items/familiar.dm deleted file mode 100644 index 05cc8cdf9610..000000000000 --- a/code/game/gamemodes/wizard/servant_items/familiar.dm +++ /dev/null @@ -1,15 +0,0 @@ -/obj/item/clothing/head/bandana/familiarband - name = "familiar's headband" - desc = "It's a simple headband made of leather." - icon = 'icons/clothing/head/familiar.dmi' - -/obj/item/clothing/pants/familiar - name = "familiar's garb" - desc = "Some rough leather leggings, reinforced here and there. A hasty job." - starting_accessories = list( - /obj/item/clothing/shirt/tunic/green/familiar - ) - -/obj/item/clothing/pants/familiar/Initialize() - . = ..() - LAZYSET(slowdown_per_slot, slot_w_uniform_str, -3) diff --git a/code/game/gamemodes/wizard/servant_items/fiend.dm b/code/game/gamemodes/wizard/servant_items/fiend.dm deleted file mode 100644 index e52606eb62f8..000000000000 --- a/code/game/gamemodes/wizard/servant_items/fiend.dm +++ /dev/null @@ -1,49 +0,0 @@ -/obj/item/clothing/head/fiendhood - name = "fiend's hood" - desc = "A dark hood with blood-red trim. Something about the fabric blocks more light than it should." - icon = 'icons/clothing/head/fiend_hood.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_KNIVES, - ARMOR_BULLET = ARMOR_BALLISTIC_MINOR, - ARMOR_LASER = ARMOR_LASER_SMALL, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_RAD = ARMOR_RAD_SHIELDED - ) - bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID - flags_inv = HIDEEARS | BLOCK_HEAD_HAIR - -/obj/item/clothing/suit/fiendcowl - name = "fiend's cowl" - desc = "A charred black duster with red trim. In its fabric, you can see the faint outline of millions of eyes." - icon = 'icons/clothing/suits/wizard/servant/fiend_cowl.dmi' - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_ARMS|SLOT_TAIL - armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT, - ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL, - ARMOR_LASER = ARMOR_LASER_HANDGUNS, - ARMOR_ENERGY = ARMOR_ENERGY_RESISTANT, - ARMOR_RAD = ARMOR_RAD_SHIELDED - ) - -/obj/item/clothing/costume/fiendsuit - name = "black suit" - desc = "A snappy black suit with red trim. The undershirt's stained with something, though..." - icon = 'icons/clothing/suits/suit_fiend.dmi' - bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID - -/obj/item/clothing/shoes/dress/devilshoes - desc = "Off-colour leather dress shoes. Their footsteps are silent." - inset_color = COLOR_MAROON - item_flags = ITEM_FLAG_SILENT - color = "#2e1e1e" - -/obj/item/clothing/head/fiendhood/fem - name = "fiend's visage" - desc = "To gaze upon this is to gaze into an inferno. Look away, before it looks back of its own accord." - icon = 'icons/clothing/head/fiend_visage.dmi' - flags_inv = HIDEEARS | BLOCK_ALL_HAIR - -/obj/item/clothing/suit/fiendcowl/fem - name = "fiend's robe" - icon = 'icons/clothing/suits/wizard/servant/fiend_robe.dmi' - desc = "A tattered, black and red robe. Nothing is visible through the holes in its fabric, except for a strange, inky blackness. It looks as if it was stitched together with other clothing..." diff --git a/code/game/gamemodes/wizard/servant_items/infiltrator.dm b/code/game/gamemodes/wizard/servant_items/infiltrator.dm deleted file mode 100644 index 846addc5b7c3..000000000000 --- a/code/game/gamemodes/wizard/servant_items/infiltrator.dm +++ /dev/null @@ -1,42 +0,0 @@ -/obj/item/clothing/head/infilhat - name = "immaculate fedora" - desc = "Whoever owns this hat means business. Hopefully, it's just good business." - color = COLOR_SILVER - icon = 'icons/clothing/head/detective.dmi' - markings_state_modifier = "band" - markings_color = COLOR_DARK_GRAY - armor = list( - ARMOR_MELEE = ARMOR_MELEE_MINOR, - ARMOR_BULLET = ARMOR_BALLISTIC_MINOR, - ARMOR_LASER = ARMOR_LASER_MINOR, - ARMOR_ENERGY = ARMOR_ENERGY_MINOR - ) - bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID - -/obj/item/clothing/suit/infilsuit - name = "immaculate suit" - desc = "The clothes of an impeccable diplomat. Or perhaps a businessman. Let's not consider the horrors that might arise if it belongs to a lawyer." - icon = 'icons/clothing/suits/wizard/servant/inf_suit.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_MINOR, - ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL, - ARMOR_LASER = ARMOR_LASER_MINOR, - ARMOR_ENERGY = ARMOR_ENERGY_MINOR - ) - -/obj/item/clothing/shoes/dress/infilshoes - name = "black leather shoes" - desc = "Dress shoes. Their footsteps are dead silent." - inset_color = COLOR_INDIGO - item_flags = ITEM_FLAG_SILENT - -/obj/item/clothing/head/infilhat/fem - name = "maid's headband" - desc = "This dainty, frilled thing is apparently meant to go on your head." - icon = 'icons/clothing/head/inf_hat.dmi' - markings_state_modifier = null - -/obj/item/clothing/suit/infilsuit/fem - name = "maid's uniform" - desc = "The uniform of someone you'd expect to see dusting off the Antique Gun's display case." - icon = 'icons/clothing/suits/wizard/servant/inf_dress.dmi' diff --git a/code/game/gamemodes/wizard/servant_items/overseer.dm b/code/game/gamemodes/wizard/servant_items/overseer.dm deleted file mode 100644 index f8676e0440d6..000000000000 --- a/code/game/gamemodes/wizard/servant_items/overseer.dm +++ /dev/null @@ -1,40 +0,0 @@ -/obj/item/clothing/head/overseerhood - name = "grim hood" - desc = "Darker than dark. What... what is this made of?" - armor = list( - ARMOR_MELEE = ARMOR_MELEE_SHIELDED, - ARMOR_BULLET = ARMOR_BALLISTIC_HEAVY, - ARMOR_LASER = ARMOR_LASER_HEAVY, - ARMOR_ENERGY = ARMOR_ENERGY_SHIELDED, - ARMOR_BOMB = ARMOR_BOMB_SHIELDED - ) - icon = 'icons/clothing/head/necromancer.dmi' - item_flags = ITEM_FLAG_AIRTIGHT - max_pressure_protection = FIRESUIT_MAX_PRESSURE - min_pressure_protection = 0 - bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID - flags_inv = HIDEEARS | BLOCK_HEAD_HAIR - -/obj/item/clothing/suit/straight_jacket/overseercloak - name = "grim cloak" - desc = "The void of space woven into fabric. It's hard to tell where its edges are." - icon = 'icons/clothing/suits/wizard/servant/overseer.dmi' - armor = list( - ARMOR_MELEE = ARMOR_MELEE_SHIELDED, - ARMOR_BULLET = ARMOR_BALLISTIC_HEAVY, - ARMOR_LASER = ARMOR_LASER_HEAVY, - ARMOR_ENERGY = ARMOR_ENERGY_SHIELDED, - ARMOR_BOMB = ARMOR_BOMB_SHIELDED - ) - item_flags = ITEM_FLAG_AIRTIGHT - max_pressure_protection = FIRESUIT_MAX_PRESSURE - min_pressure_protection = 0 - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_FEET|SLOT_ARMS|SLOT_HANDS|SLOT_TAIL - -//These are the ones that it gets when they toggle it off -/obj/item/clothing/shoes/sandal/grimboots - name = "stained boots" - desc = "These boots are stained with blood so dry that it's turned black..." - color = COLOR_BLACK - shine = 10 - item_flags = ITEM_FLAG_SILENT \ No newline at end of file diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm deleted file mode 100644 index 72792403db14..000000000000 --- a/code/game/gamemodes/wizard/wizard.dm +++ /dev/null @@ -1,10 +0,0 @@ -/decl/game_mode/wizard - name = "Wizard" - round_description = "There is a SPACE WIZARD onboard. You can't let the magician achieve their objectives!" - extended_round_description = "A powerful space wizard has made their way on board. They have a wide variety of powers and spells available to them that makes your own simple moral self tremble with fear and excitement. Ultimately, their purpose is unknown. However, it is up to you and your crew to decide if their powers can be used for good or if their arrival foreshadows devastation." - uid = "wizard" - required_players = 5 - required_enemies = 1 - end_on_antag_death = FALSE - associated_antags = list(/decl/special_role/wizard) - probability = 1 diff --git a/code/game/gamemodes/wizard/wizard_props.dm b/code/game/gamemodes/wizard/wizard_props.dm deleted file mode 100644 index b8b834679db1..000000000000 --- a/code/game/gamemodes/wizard/wizard_props.dm +++ /dev/null @@ -1,21 +0,0 @@ -/obj/structure/talisman_altar - name = "Altar" - desc = "A bloodstained altar dedicated to the worship of some unknown dark entity." - icon = 'icons/obj/cult.dmi' - icon_state = "talismanaltar" - density = TRUE - anchored = TRUE - -/obj/structure/fake_pylon - name = "\improper Pylon" - desc = "A floating crystal that hums with an unearthly energy." - icon = 'icons/obj/structures/pylon.dmi' - icon_state = "pylon" - light_power = 0.5 - light_range = 13 - light_color = "#3e0000" - -// A de-culted version of the cult gateway, for the wizard base map. -/obj/effect/gateway/active/spooky - light_range=5 - light_color="#ff0000" \ No newline at end of file diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm index 979f91bd0349..0d6369221804 100644 --- a/code/game/jobs/access_datum.dm +++ b/code/game/jobs/access_datum.dm @@ -460,12 +460,6 @@ var/global/const/access_mercenary = "ACCESS_MERCENARY" desc = "Mercenary" access_type = ACCESS_TYPE_ANTAG -var/global/const/access_wizard = "ACCESS_WIZARD" -/datum/access/wizard - id = access_wizard - desc = "Wizard" - access_type = ACCESS_TYPE_ANTAG - /******* * Misc * *******/ diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 7d695e1f75d0..e2e87ae2470e 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -132,18 +132,18 @@ /obj/abstract/landmark/costume/holiday_priest/make_costumes() new /obj/item/clothing/suit/holidaypriest(loc) -/obj/abstract/landmark/costume/marisawizard/fake/make_costumes() - new /obj/item/clothing/head/wizard/marisa/fake(loc) - new/obj/item/clothing/suit/wizrobe/marisa/fake(loc) +/obj/abstract/landmark/costume/marisawizard/make_costumes() + new /obj/item/clothing/head/wizard/marisa(loc) + new/obj/item/clothing/suit/wizrobe/marisa(loc) /obj/abstract/landmark/costume/cutewitch/make_costumes() new /obj/item/clothing/dress/sun(loc) new /obj/item/clothing/head/witchwig(loc) new /obj/item/staff/broom(loc) -/obj/abstract/landmark/costume/fakewizard/make_costumes() - new /obj/item/clothing/suit/wizrobe/fake(loc) - new /obj/item/clothing/head/wizard/fake(loc) +/obj/abstract/landmark/costume/wizard/make_costumes() + new /obj/item/clothing/suit/wizrobe(loc) + new /obj/item/clothing/head/wizard/beard(loc) new /obj/item/staff/(loc) /obj/abstract/landmark/costume/sexyclown/make_costumes() diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 882706ebc6f8..0726d380a410 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -25,10 +25,6 @@ START_PROCESSING(SSobj, src) update_icon() -/obj/item/radio/intercom/wizard - name = "enchanted intercom" - desc = "Talk into this while you ponder your orb." - /obj/item/radio/intercom/raider name = "piratical intercom" desc = "Pirate radio, but not in the usual sense of the word." diff --git a/code/game/objects/items/hourglass.dm b/code/game/objects/items/hourglass.dm index f12a51077f56..98bb75f12811 100644 --- a/code/game/objects/items/hourglass.dm +++ b/code/game/objects/items/hourglass.dm @@ -1,7 +1,5 @@ -// I considered just putting this in the fantasy modpack, -// but it's probably better in core code, since at the very least -// the Wizard gamemode might want to use the item or icon for something. -// Also, I think it could just be a neat prop. +// I considered just putting this in the fantasy modpack, but +// it's probably better in core code, since it's a nice prop. /obj/item/hourglass name = "hourglass" icon = 'icons/obj/items/hourglass.dmi' diff --git a/code/game/objects/items/weapons/scrolls.dm b/code/game/objects/items/weapons/scrolls.dm deleted file mode 100644 index 0b513dc546ac..000000000000 --- a/code/game/objects/items/weapons/scrolls.dm +++ /dev/null @@ -1,50 +0,0 @@ -/obj/item/paper/scroll/teleportation - name = "scroll of teleportation" - desc = "A scroll for moving around." - origin_tech = @'{"wormholes":4}' - info = {" - Teleportation Scroll:
    -
    - Comes with four charges! Use them wisely.
    - Kind regards,
    Wizards Federation

    P.S. Don't forget to bring your gear, you'll need it to cast most spells. - "} - var/uses = 4 - -/obj/item/paper/scroll/teleportation/examine(mob/user, distance) - . = ..() - if(distance <= 1) - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - if(wizards.is_antagonist(user.mind)) - to_chat(user, SPAN_NOTICE("\The [src] has [uses] charge\s remaining.")) - -/obj/item/paper/scroll/teleportation/attack_self(mob/user) - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - if((user.mind && !wizards.is_antagonist(user.mind))) - to_chat(user, SPAN_WARNING("You stare at \the [src], but cannot make sense of the markings!")) - return TRUE - if(alert(user, "Do you wish to teleport using \the [src]? It has [uses] charge\s remaining.", "Scroll of Teleportation", "No", "Yes") == "Yes") - teleportscroll(user) - return TRUE - -/obj/item/paper/scroll/teleportation/proc/teleportscroll(var/mob/user) - if(uses <= 0) - return - - var/area/thearea = input(user, "Select an area to jump to.", "Scroll of Teleportation") as null|anything in wizteleportlocs - if(!thearea || QDELETED(src) || QDELETED(user) || user.get_active_held_item() != src || CanUseTopic(user) != STATUS_INTERACTIVE) - return - - thearea = thearea ? wizteleportlocs[thearea] : thearea - if(!thearea) - return - - var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() - smoke.set_up(5, 0, user.loc) - smoke.attach(user) - smoke.start() - var/turf/end = user.try_teleport(thearea) - if(!end) - to_chat(user, SPAN_WARNING("The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry!")) - return - smoke.start() - src.uses -= 1 diff --git a/code/game/objects/items/weapons/staff.dm b/code/game/objects/items/weapons/staff.dm index 41ce83c34304..f355815734bd 100644 --- a/code/game/objects/items/weapons/staff.dm +++ b/code/game/objects/items/weapons/staff.dm @@ -42,9 +42,8 @@ return TRUE return ..() -// TODO: move back into wizard modpack when the timelines converge. /obj/item/staff/crystal - name = "wizard's staff" + name = "crystal staff" icon = 'icons/obj/items/staff_crystal.dmi' /obj/item/staff/crystal/can_make_broom_with(mob/user, obj/item/thing) diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index 4c2a1259995f..744bcdb78135 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -9,7 +9,6 @@ icon_state = ICON_STATE_WORLD _base_attack_force = 3 - var/wizard_garb = 0 var/flash_protection = FLASH_PROTECTION_NONE // Sets the item's level of flash protection. var/tint = TINT_NONE // Sets the item's level of visual impairment tint. var/bodytype_equip_flags // Bitfields; if null, checking is skipped. Determine if a given mob can equip this item or not. diff --git a/code/modules/clothing/head/wizard.dm b/code/modules/clothing/head/wizard.dm new file mode 100644 index 000000000000..e4de99269e10 --- /dev/null +++ b/code/modules/clothing/head/wizard.dm @@ -0,0 +1,32 @@ +// Props from wizard mode, preserved as costume pieces. +/obj/item/clothing/head/wizard + name = "wizard hat" + desc = "It has WIZZARD written across it in sequins." + icon = 'icons/clothing/head/wizard/wizard.dmi' + body_parts_covered = 0 + +/obj/item/clothing/head/wizard/red + name = "red wizard hat" + icon = 'icons/clothing/head/wizard/red.dmi' + +/obj/item/clothing/head/wizard/beard + name = "wizard hat" + desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." + icon = 'icons/clothing/head/wizard/fake.dmi' + body_parts_covered = SLOT_HEAD|SLOT_FACE + +/obj/item/clothing/head/wizard/marisa + name = "witch hat" + desc = "Strange-looking hat-wear. Makes you want to cast fireballs." + icon = 'icons/clothing/head/wizard/marisa.dmi' + +/obj/item/clothing/head/wizard/magus + name = "magus helm" + desc = "A mysterious helmet. Hard to see out of." + icon = 'icons/clothing/head/wizard/magus.dmi' + body_parts_covered = SLOT_HEAD|SLOT_FACE|SLOT_EYES + +/obj/item/clothing/head/wizard/cap + name = "gentleman's cap" + desc = "A checkered gray flat cap." + icon = 'icons/clothing/head/flatcap.dmi' diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index e74903d380b7..7ceb955904cb 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -93,12 +93,16 @@ w_class = ITEM_SIZE_SMALL siemens_coefficient = 0.9 -/obj/item/clothing/mask/horsehead/Initialize() - . = ..() - // The horse mask doesn't cause voice changes by default, the wizard spell changes the flag as necessary +/obj/item/clothing/mask/horsehead/cursed + voicechange = TRUE say_messages = list("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") say_verbs = list("whinnies", "neighs", "says") +/obj/item/clothing/mask/horsehead/cursed/equipped(mob/user, slot) + . = ..() + if(slot == slot_wear_mask_str) + canremove = FALSE + /obj/item/clothing/mask/ai name = "camera MIU" desc = "Allows for direct mental connection to accessible camera channels." diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 9466eb0709aa..53579e0dd79a 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -84,7 +84,6 @@ icon = 'icons/clothing/feet/sandals.dmi' bodytype_equip_flags = null body_parts_covered = 0 - wizard_garb = 1 can_add_hidden_item = FALSE can_add_cuffs = FALSE diff --git a/code/modules/clothing/spacesuits/void/wizard.dm b/code/modules/clothing/spacesuits/void/wizard.dm deleted file mode 100644 index 48dfc6d852a3..000000000000 --- a/code/modules/clothing/spacesuits/void/wizard.dm +++ /dev/null @@ -1,66 +0,0 @@ -//Wizard Rig -/obj/item/clothing/head/helmet/space/void/wizard - name = "gem-encrusted voidsuit helmet" - desc = "A bizarre gem-encrusted helmet that radiates magical energies." - icon = 'icons/clothing/spacesuit/void/wizard/helmet.dmi' - material = /decl/material/solid/gemstone/crystal - armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT, - ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL, - ARMOR_LASER = ARMOR_LASER_SMALL, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_BOMB = ARMOR_BOMB_PADDED, - ARMOR_BIO = ARMOR_BIO_SHIELDED, - ARMOR_RAD = ARMOR_RAD_SMALL - ) - siemens_coefficient = 0.7 - wizard_garb = 1 - -/obj/item/clothing/suit/space/void/wizard - name = "gem-encrusted voidsuit" - desc = "A bizarre gem-encrusted suit that radiates magical energies." - icon = 'icons/clothing/spacesuit/void/wizard/suit.dmi' - w_class = ITEM_SIZE_LARGE //normally voidsuits are bulky but this one is magic I suppose - material = /decl/material/solid/gemstone/crystal - armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT, - ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL, - ARMOR_LASER = ARMOR_LASER_SMALL, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_BOMB = ARMOR_BOMB_PADDED, - ARMOR_BIO = ARMOR_BIO_SHIELDED, - ARMOR_RAD = ARMOR_RAD_SMALL - ) - siemens_coefficient = 0.7 - wizard_garb = 1 - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETAIL //For gloves. - body_parts_covered = SLOT_UPPER_BODY | SLOT_LOWER_BODY | SLOT_LEGS | SLOT_FEET | SLOT_ARMS | SLOT_TAIL - cold_protection = SLOT_UPPER_BODY | SLOT_LOWER_BODY | SLOT_LEGS | SLOT_FEET | SLOT_ARMS | SLOT_TAIL - -/obj/item/clothing/suit/space/void/wizard/Initialize() - . = ..() - LAZYSET(slowdown_per_slot, slot_wear_suit_str, 1) - -/obj/item/clothing/gloves/wizard - name = "mystical gloves" - desc = "Reinforced, gem-studded gloves that radiate energy. They look like they go along with a matching voidsuit." - color = COLOR_VIOLET - item_flags = ITEM_FLAG_THICKMATERIAL - body_parts_covered = SLOT_HANDS - cold_protection = SLOT_HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - bodytype_equip_flags = null - gender = PLURAL - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - material = /decl/material/solid/gemstone/crystal - armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT, - ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL, - ARMOR_LASER = ARMOR_LASER_SMALL, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_BOMB = ARMOR_BOMB_PADDED, - ARMOR_BIO = ARMOR_BIO_SHIELDED, - ARMOR_RAD = ARMOR_RAD_SMALL - ) - siemens_coefficient = 0.7 diff --git a/code/modules/clothing/suits/misc.dm b/code/modules/clothing/suits/misc.dm new file mode 100644 index 000000000000..eb58d81b67eb --- /dev/null +++ b/code/modules/clothing/suits/misc.dm @@ -0,0 +1,5 @@ +/obj/item/clothing/suit/gentlecoat + name = "gentleman's coat" + desc = "A heavy-threaded gray tweed jacket." + icon = 'icons/clothing/suits/wizard/gentleman.dmi' + body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm deleted file mode 100644 index 16dd6386ebb9..000000000000 --- a/code/modules/clothing/suits/wiz_robe.dm +++ /dev/null @@ -1,113 +0,0 @@ -/obj/item/clothing/head/wizard - name = "wizard hat" - desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." - icon = 'icons/clothing/head/wizard/wizard.dmi' - //Not given any special protective value since the magic robes are full-body protection --NEO - siemens_coefficient = 0.8 - body_parts_covered = 0 - wizard_garb = 1 - -/obj/item/clothing/head/wizard/red - name = "red wizard hat" - desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." - icon = 'icons/clothing/head/wizard/red.dmi' - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/fake - name = "wizard hat" - desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." - icon = 'icons/clothing/head/wizard/fake.dmi' - body_parts_covered = SLOT_HEAD|SLOT_FACE - -/obj/item/clothing/head/wizard/marisa - name = "witch hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon = 'icons/clothing/head/wizard/marisa.dmi' - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/magus - name = "magus helm" - desc = "A mysterious helmet that hums with an unearthly power." - icon = 'icons/clothing/head/wizard/magus.dmi' - siemens_coefficient = 0.8 - body_parts_covered = SLOT_HEAD|SLOT_FACE|SLOT_EYES - -/obj/item/clothing/head/wizard/cap - name = "gentleman's cap" - desc = "A checkered gray flat cap woven together with the rarest of threads." - icon = 'icons/clothing/head/flatcap.dmi' - siemens_coefficient = 0.8 - -/obj/item/clothing/suit/wizrobe - name = "wizard robe" - desc = "A magnificant, gem-lined robe that seems to radiate power." - icon = 'icons/clothing/suits/wizard/wizard.dmi' - gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE - permeability_coefficient = 0.01 - armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT, - ARMOR_BULLET = ARMOR_BALLISTIC_SMALL, - ARMOR_LASER = ARMOR_LASER_SMALL, - ARMOR_ENERGY = ARMOR_ENERGY_SMALL, - ARMOR_BOMB = ARMOR_BOMB_PADDED, - ARMOR_BIO = ARMOR_BIO_MINOR, - ARMOR_RAD = ARMOR_RAD_MINOR - ) - allowed = list(/obj/item/paper/scroll) - siemens_coefficient = 0.8 - wizard_garb = 1 - -/obj/item/clothing/suit/wizrobe/red - name = "red wizard robe" - desc = "A magnificant, red, gem-lined robe that seems to radiate power." - icon = 'icons/clothing/suits/wizard/red.dmi' - -/obj/item/clothing/suit/wizrobe/marisa - name = "witch robe" - desc = "Magic is all about the spell power, ZE!" - icon = 'icons/clothing/suits/wizard/marisa.dmi' - -/obj/item/clothing/suit/wizrobe/magusblue - name = "magus robe" - desc = "A set of armoured robes that seem to radiate a dark power." - icon = 'icons/clothing/suits/wizard/magusblue.dmi' - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS|SLOT_HANDS|SLOT_LEGS|SLOT_FEET - -/obj/item/clothing/suit/wizrobe/magusred - name = "magus robe" - desc = "A set of armoured robes that seem to radiate a dark power." - icon = 'icons/clothing/suits/wizard/magusred.dmi' - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS|SLOT_HANDS|SLOT_LEGS|SLOT_FEET - -/obj/item/clothing/suit/wizrobe/psypurple - name = "purple robes" - desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." - icon = 'icons/clothing/suits/wizard/psy.dmi' - gender = PLURAL - -/obj/item/clothing/suit/wizrobe/gentlecoat - name = "gentleman's coat" - desc = "A heavy threaded tweed gray jacket. For a different sort of Gentleman." - icon = 'icons/clothing/suits/wizard/gentleman.dmi' - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS - -/obj/item/clothing/suit/wizrobe/fake - name = "wizard robe" - desc = "A rather dull, blue robe meant to mimick real wizard robes." - icon = 'icons/clothing/suits/wizard/fake.dmi' - armor = null - siemens_coefficient = 1.0 - -/obj/item/clothing/head/wizard/marisa/fake - name = "witch hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - armor = null - siemens_coefficient = 1.0 - -/obj/item/clothing/suit/wizrobe/marisa/fake - name = "witch robe" - desc = "Magic is all about the spell power, ZE!" - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS|SLOT_LEGS - armor = null - siemens_coefficient = 1.0 - diff --git a/code/modules/clothing/suits/wizard.dm b/code/modules/clothing/suits/wizard.dm new file mode 100644 index 000000000000..c8f0208fb4d5 --- /dev/null +++ b/code/modules/clothing/suits/wizard.dm @@ -0,0 +1,25 @@ +// Props from wizard mode, preserved as costume pieces. +/obj/item/clothing/suit/wizrobe + name = "wizard robe" + desc = "A rather ratty blue robe." + icon = 'icons/clothing/suits/wizard/wizard.dmi' + allowed = list(/obj/item/paper/scroll) + +/obj/item/clothing/suit/wizrobe/red + name = "red wizard robe" + desc = "A rather ratty red robe." + icon = 'icons/clothing/suits/wizard/red.dmi' + +/obj/item/clothing/suit/wizrobe/marisa + name = "witch robe" + desc = "Magic is all about the spell power, ZE!" + icon = 'icons/clothing/suits/wizard/marisa.dmi' + +/obj/item/clothing/suit/wizrobe/magus + name = "magus robe" + desc = "A set of mysterious armoured robes." + icon = 'icons/clothing/suits/wizard/magusblue.dmi' + body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS|SLOT_HANDS|SLOT_LEGS|SLOT_FEET + +/obj/item/clothing/suit/wizrobe/magus/red + icon = 'icons/clothing/suits/wizard/magusred.dmi' diff --git a/code/modules/ghosttrap/trap.dm b/code/modules/ghosttrap/trap.dm index da2c66433b4b..1f450a9aadff 100644 --- a/code/modules/ghosttrap/trap.dm +++ b/code/modules/ghosttrap/trap.dm @@ -176,18 +176,6 @@ if(istype(drone)) drone.transfer_personality(candidate.client) -/****************** -* Wizard Familiar * -******************/ -/decl/ghosttrap/wizard_familiar - name = "wizard familiar" - pref_check = "ghost_wizard" - ghost_trap_message = "They are occupying a familiar now." - ban_checks = list(/decl/special_role/wizard) - -/decl/ghosttrap/wizard_familiar/welcome_candidate(var/mob/target) - return 0 - // Stub PAI ghost trap so that PAI shows up in the ghost role list. // Actually invoking this ghost trap as normal will not do anything. /decl/ghosttrap/personal_ai diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index 1b7d470abc8a..4986952e1305 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -51,7 +51,7 @@ spawn_gibber(lastloc) //This is the proc for turning a mob into ash. Mostly a copy of gib code (above). -//Originally created for wizard disintegrate. I've removed the virus code since it's irrelevant here. +//Originally created for Disintegrate. I've removed the virus code since it's irrelevant here. //Dusting robots does not eject the brain, so it's a bit more powerful than gib() /N /mob/proc/dust() SHOULD_CALL_PARENT(TRUE) diff --git a/code/modules/mob/living/simple_animal/familiars/familiars.dm b/code/modules/mob/living/simple_animal/familiars/familiars.dm deleted file mode 100644 index 8cefee06d93a..000000000000 --- a/code/modules/mob/living/simple_animal/familiars/familiars.dm +++ /dev/null @@ -1,133 +0,0 @@ -/mob/living/simple_animal/familiar - name = "familiar" - desc = "No wizard is complete without a mystical sidekick." - supernatural = 1 - universal_speak = FALSE - universal_understand = TRUE - - min_gas = list(/decl/material/gas/oxygen = 1) - max_gas = null - unsuitable_atmos_damage = 1 - gene_damage = -1 - base_animal_type = /mob/living/simple_animal/familiar - - var/list/wizardy_spells = list() - -/mob/living/simple_animal/familiar/Initialize() - . = ..() - add_language(/decl/language/human/common) - for(var/spell in wizardy_spells) - src.add_spell(new spell, "const_spell_ready") - -/mob/living/simple_animal/familiar/carcinus - name = "carcinus" - desc = "A small crab said to be made of stone and starlight." - icon = 'icons/mob/simple_animal/evilcrab.dmi' - speak_emote = list("chitters","clicks") - max_health = 200 - natural_weapon = /obj/item/natural_weapon/pincers/strong - resistance = 9 - ai = /datum/mob_controller/familiar_crab - -/datum/mob_controller/familiar_crab - can_escape_buckles = TRUE - -/obj/item/natural_weapon/pincers/strong - _base_attack_force = 15 - -/*familiar version of the Pike w/o all the other hostile/carp stuff getting in the way (namely life) -*/ - -/mob/living/simple_animal/familiar/pike - name = "space pike" - desc = "A bigger, more magical cousin of the space carp." - icon = 'icons/mob/simple_animal/spaceshark.dmi' - pixel_x = -16 - offset_overhead_text_x = 16 - - speak_emote = list("gnashes") - max_health = 100 - natural_weapon = /obj/item/natural_weapon/bite - min_gas = null - wizardy_spells = list(/spell/aoe_turf/conjure/forcewall) - ai = /datum/mob_controller/familiar_pike - -/datum/mob_controller/familiar_pike - can_escape_buckles = TRUE - -/mob/living/simple_animal/familiar/pike/Process_Spacemove() - return 1 //No drifting in space for space carp! //original comments do not steal - -/mob/living/simple_animal/familiar/horror - name = "horror" - desc = "Looking at it fills you with dread." - icon = 'icons/mob/simple_animal/horror.dmi' - speak_emote = list("moans", "groans") - response_help_1p = "You think better of touching $TARGET$." - response_help_3p = "$USER$ thinks better of touching $TARGET$." - max_health = 150 - natural_weapon = /obj/item/natural_weapon/horror - wizardy_spells = list(/spell/targeted/torment) - -/obj/item/natural_weapon/horror - name = "foul touch" - _base_attack_force = 10 - atom_damage_type = BURN - attack_verb = list("touched") - -/mob/living/simple_animal/familiar/horror/get_death_message(gibbed) - return "rapidly deteriorates" -/mob/living/simple_animal/familiar/horror/get_self_death_message(gibbed) - return "The bonds tying you to this mortal plane have been severed." - -/mob/living/simple_animal/familiar/horror/death(gibbed) - . = ..() - if(. && !gibbed) - spawn_gibber(loc) - qdel(src) - -/mob/living/simple_animal/familiar/minor_amaros - name = "minor amaros" - desc = "A small fluffy alien creature." - icon = 'icons/mob/simple_animal/amaros.dmi' - speak_emote = list("entones") - mob_size = MOB_SIZE_SMALL - max_health = 25 - wizardy_spells = list( - /spell/targeted/heal_target, - /spell/targeted/heal_target/area - ) - -/mob/living/simple_animal/familiar/pet/mouse - name = "elderly mouse" - desc = "A small rodent. It looks very old." - icon = 'icons/mob/simple_animal/mouse_gray.dmi' - speak_emote = list("squeeks") - holder_type = /obj/item/holder - pass_flags = PASS_FLAG_TABLE - mob_size = MOB_SIZE_MINISCULE - response_harm = "stamps on" - max_health = 15 - natural_weapon = /obj/item/natural_weapon/bite/mouse - wizardy_spells = list(/spell/aoe_turf/smoke) - ai = /datum/mob_controller/familiar_mouse - -/datum/mob_controller/familiar_mouse - can_escape_buckles = TRUE - -/mob/living/simple_animal/familiar/pet/mouse/Initialize() - . = ..() - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - -/mob/living/simple_animal/familiar/pet/cat - name = "black cat" - desc = "A pitch black cat. Said to be especially unlucky." - icon = 'icons/mob/simple_animal/cat_black.dmi' - speak_emote = list("meows", "purrs") - holder_type = /obj/item/holder - mob_size = MOB_SIZE_SMALL - max_health = 25 - natural_weapon = /obj/item/natural_weapon/claws/weak - wizardy_spells = list(/spell/targeted/subjugation) diff --git a/code/modules/organs/internal/species/golem.dm b/code/modules/organs/internal/species/golem.dm index 93e368c2e646..cbf569bebab9 100644 --- a/code/modules/organs/internal/species/golem.dm +++ b/code/modules/organs/internal/species/golem.dm @@ -1,8 +1,9 @@ /obj/item/organ/internal/brain/golem name = "chem" desc = "A tightly furled roll of paper, covered with indecipherable runes." - icon = 'icons/obj/wizard.dmi' + icon = 'icons/obj/items/paperwork/scroll.dmi' icon_state = "scroll" + color = COLOR_BEIGE /obj/item/organ/internal/brain/golem/can_recover() - return 0 + return FALSE diff --git a/code/modules/projectiles/guns/energy/staves.dm b/code/modules/projectiles/guns/energy/staves.dm index 45ab5f71b6f3..965c8f208e07 100644 --- a/code/modules/projectiles/guns/energy/staves.dm +++ b/code/modules/projectiles/guns/energy/staves.dm @@ -13,15 +13,13 @@ self_recharge = 1 charge_meter = 0 has_safety = FALSE - var/required_antag_type = /decl/special_role/wizard + var/required_antag_type /obj/item/gun/energy/staff/special_check(var/mob/user) - if(required_antag_type) - var/decl/special_role/antag = GET_DECL(required_antag_type) - if(user.mind && !antag.is_antagonist(user.mind)) - to_chat(usr, "You focus your mind on \the [src], but nothing happens!") - return 0 - + var/decl/special_role/antag = GET_DECL(required_antag_type) + if(user.mind && (!antag?.is_antagonist(user.mind))) + to_chat(user, SPAN_WARNING("You focus your mind on \the [src], but nothing happens!")) + return FALSE return ..() /obj/item/gun/energy/staff/handle_click_empty(mob/user = null) diff --git a/code/modules/spells/aoe_turf/aoe_turf.dm b/code/modules/spells/aoe_turf/aoe_turf.dm index 46febbfbe5fd..0f7a721de803 100644 --- a/code/modules/spells/aoe_turf/aoe_turf.dm +++ b/code/modules/spells/aoe_turf/aoe_turf.dm @@ -1,7 +1,7 @@ /* Aoe turf spells target a ring of tiles around the user This ring has an outer radius (range) and an inner radius (inner_radius) -Aoe turf spells have two useful flags: IGNOREDENSE and IGNORESPACE. These are explained in setup.dm +Aoe turf spells have a useful flag: IGNOREDENSE. It is explained in setup.dm */ /spell/aoe_turf //affects all turfs in view or range (depends) @@ -15,8 +15,6 @@ Aoe turf spells have two useful flags: IGNOREDENSE and IGNORESPACE. These are ex if(!(target in view_or_range(inner_radius, holder, selection_type))) if(target.density && (spell_flags & IGNOREDENSE)) continue - if(isspaceturf(target) && (spell_flags & IGNORESPACE)) - continue targets += target if(!targets.len) //doesn't waste the spell diff --git a/code/modules/spells/aoe_turf/blink.dm b/code/modules/spells/aoe_turf/blink.dm deleted file mode 100644 index 3fd742aec243..000000000000 --- a/code/modules/spells/aoe_turf/blink.dm +++ /dev/null @@ -1,44 +0,0 @@ -/spell/aoe_turf/blink - name = "Blink" - desc = "This spell randomly teleports you a short distance." - feedback = "BL" - school = "conjuration" - charge_max = 20 - spell_flags = Z2NOCAST | IGNOREDENSE | IGNORESPACE - invocation = "none" - invocation_type = SpI_NONE - range = 7 - inner_radius = 1 - - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 4, Sp_POWER = 4) - cooldown_min = 5 //4 deciseconds reduction per rank - hud_state = "wiz_blink" - cast_sound = 'sound/magic/blink.ogg' - -/spell/aoe_turf/blink/cast(var/list/targets, mob/user) - if(!targets.len) - return - - var/turf/T = pick(targets) - var/turf/starting = get_turf(user) - if(T) - if(user.buckled) - user.buckled = null - user.forceMove(T) - - var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() - smoke.set_up(3, 0, starting) - smoke.start() - - smoke = new() - smoke.set_up(3, 0, T) - smoke.start() - - return - -/spell/aoe_turf/blink/empower_spell() - if(!..()) - return 0 - inner_radius += 1 - - return "You've increased the inner range of [src]." \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/charge.dm b/code/modules/spells/aoe_turf/charge.dm deleted file mode 100644 index d9c8f95af90a..000000000000 --- a/code/modules/spells/aoe_turf/charge.dm +++ /dev/null @@ -1,72 +0,0 @@ -/spell/aoe_turf/charge - name = "Charge" - desc = "This spell can be used to charge up spent magical artifacts, among other things." - - school = "transmutation" - charge_max = 600 - spell_flags = 0 - invocation = "DIRI CEL" - invocation_type = SpI_WHISPER - range = 0 - cooldown_min = 400 //50 deciseconds reduction per rank - - hud_state = "wiz_charge" - cast_sound = 'sound/magic/charge.ogg' - -/spell/aoe_turf/charge/cast(var/list/targets, mob/user) - for(var/turf/T in targets) - depth_cast(T) - -/spell/aoe_turf/charge/proc/depth_cast(var/list/targets) - for(var/atom/A in targets) - if(A.contents.len) - depth_cast(A.contents) - cast_charge(A) - -/spell/aoe_turf/charge/proc/mob_charge(var/mob/living/M) - if(!M.mind) - return - if(M.mind.learned_spells.len != 0) - for(var/spell/S in M.mind.learned_spells) - if(!istype(S, /spell/aoe_turf/charge)) - S.charge_counter = S.charge_max - to_chat(M, "You feel raw magic flowing through you, it feels good!") - else - to_chat(M, "You feel very strange for a moment, but then it passes.") - return M - -/spell/aoe_turf/charge/proc/cast_charge(var/atom/target) - var/atom/charged_item - - if(isliving(target)) - charged_item = mob_charge(target) - - if(istype(target, /obj/item/grab)) - var/obj/item/grab/grab = target - if(grab.affecting) - var/mob/M = grab.get_affecting_mob() - if(M) - charged_item = mob_charge(M) - - if(istype(target, /obj/item/cell/)) - var/obj/item/cell/C = target - if(prob(80) && C.maxcharge) - C.maxcharge -= 200 - if(C.maxcharge <= 0) //maxcharge of 0! Madness! - C.maxcharge = 0 - C.charge = C.maxcharge - charged_item = C - - if(!charged_item) - return 0 - else - charged_item.visible_message("[charged_item] suddenly sparks with energy!") - return 1 - - -/spell/aoe_turf/charge/blood - name = "Blood Infusion" - desc = "This spell charges things around it using the lifeforce gained by sacrificed blood." - charge_type = Sp_HOLDVAR - holder_var_type = "bruteloss" - holder_var_amount = 30 \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/conjure/conjure.dm b/code/modules/spells/aoe_turf/conjure/conjure.dm index 9291456f41a6..8dd91146310c 100644 --- a/code/modules/spells/aoe_turf/conjure/conjure.dm +++ b/code/modules/spells/aoe_turf/conjure/conjure.dm @@ -20,7 +20,7 @@ How they spawn stuff is decided by behaviour vars, which are explained below var/summon_exclusive = 0 //spawn one of everything, instead of random things var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet - //should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example + //should have format of list("emagged" = 1,"name" = "Justicebot"), for example cast_sound = 'sound/magic/castsummon.ogg' @@ -38,9 +38,6 @@ How they spawn stuff is decided by behaviour vars, which are explained below else summoned_object_type = pick(summon_type) var/turf/spawn_place = pick(targets) - if(spell_flags & IGNOREPREV) - targets -= spawn_place - var/atom/summoned_object if(ispath(summoned_object_type,/turf)) spawn_place.ChangeTurf(summoned_object_type) diff --git a/code/modules/spells/aoe_turf/conjure/druidic_spells.dm b/code/modules/spells/aoe_turf/conjure/druidic_spells.dm deleted file mode 100644 index e6f0b8ea2607..000000000000 --- a/code/modules/spells/aoe_turf/conjure/druidic_spells.dm +++ /dev/null @@ -1,113 +0,0 @@ -/spell/aoe_turf/conjure/summon - var/name_summon = 0 - cast_sound = 'sound/weapons/wave.ogg' - -/spell/aoe_turf/conjure/summon/before_cast() - ..() - if(name_summon) - var/newName = sanitize(input("Would you like to name your summon?") as null|text, MAX_NAME_LEN) - if(newName) - newVars["name"] = newName - -/spell/aoe_turf/conjure/summon/conjure_animation(var/atom/movable/overlay/animation, var/turf/target) - animation.icon_state = "shield2" - flick("shield2",animation) - sleep(10) - ..() - - -/spell/aoe_turf/conjure/summon/bats - name = "Summon Space Bats" - desc = "This spell summons a flock of spooky space bats." - feedback = "SB" - - charge_max = 1200 //2 minutes - spell_flags = NEEDSCLOTHES - invocation = "Bla'yo daya!" - invocation_type = SpI_SHOUT - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 3, Sp_POWER = 3) - cooldown_min = 600 - - range = 1 - - summon_amt = 1 - summon_type = list(/mob/living/simple_animal/hostile/scarybat) - - hud_state = "wiz_bats" - -/spell/aoe_turf/conjure/summon/bats/empower_spell() - if(!..()) - return 0 - - newVars = list("max_health" = 20 + spell_levels[Sp_POWER]*5, "health" = 20 + spell_levels[Sp_POWER]*5, "melee_damage_lower" = 10 + spell_levels[Sp_POWER], "melee_damage_upper" = 10 + spell_levels[Sp_POWER]*2) - - return "Your bats are now stronger." - -/spell/aoe_turf/conjure/summon/bear - name = "Summon Bear" - desc = "This spell summons a permanent bear companion that will follow your orders." - feedback = "BR" - charge_max = 3000 //5 minutes because this is a REALLY powerful spell. May tone it down/up. - spell_flags = NEEDSCLOTHES - invocation = "REA'YO GOR DAYA!" - invocation_type = SpI_SHOUT - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 0, Sp_POWER = 4) - - range = 0 - - name_summon = 1 - - summon_amt = 1 - summon_type = list(/mob/living/simple_animal/hostile/commanded/bear) - newVars = list("max_health" = 15, - "health" = 15, - "melee_damage_lower" = 10, - "melee_damage_upper" = 10, - ) - - hud_state = "wiz_bear" - -/spell/aoe_turf/conjure/summon/bear/apply_vars(atom/summoned_object, mob/caster) - . = ..() - if(isliving(summoned_object)) - var/mob/living/summoned_mob = summoned_object - if(istype(summoned_mob.ai, /datum/mob_controller/aggressive/commanded)) - var/datum/mob_controller/aggressive/commanded/command_ai = summoned_mob.ai - command_ai.master = caster - -/spell/aoe_turf/conjure/summon/bear/empower_spell() - if(!..()) - return 0 - switch(spell_levels[Sp_POWER]) - if(1) - newVars = list("max_health" = 30, - "health" = 30, - "melee_damage_lower" = 15, - "melee_damage_upper" = 15 - ) - return "Your bear has been upgraded from a cub to a whelp." - if(2) - newVars = list("max_health" = 45, - "health" = 45, - "melee_damage_lower" = 20, - "melee_damage_upper" = 20, - "color" = "#d9d9d9" //basically we want them to look different enough that people can recognize it. - ) - return "Your bear has been upgraded from a whelp to an adult." - if(3) - newVars = list("max_health" = 60, - "health" = 60, - "melee_damage_lower" = 25, - "melee_damage_upper" = 25, - "color" = "#8c8c8c" - ) - return "Your bear has been upgraded from an adult to an alpha." - if(4) - newVars = list("max_health" = 75, - "health" = 75, - "melee_damage_lower" = 35, - "melee_damage_upper" = 35, - "resistance" = 3, - "color" = "#0099ff" - ) - return "Your bear is now worshiped as a god amongst bears." \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/conjure/faithful_hound.dm b/code/modules/spells/aoe_turf/conjure/faithful_hound.dm deleted file mode 100644 index 4511e6a07f46..000000000000 --- a/code/modules/spells/aoe_turf/conjure/faithful_hound.dm +++ /dev/null @@ -1,28 +0,0 @@ -/spell/aoe_turf/conjure/faithful_hound - name = "Faithful Hound" - desc = "Summon a spectral watchdog with a special password. Anyone without the password is in for a barking and a biting." - feedback = "FH" - - charge_max = 600 - spell_flags = NEEDSCLOTHES - invocation = "Du korilath tangus" - invocation_type = SpI_WHISPER - range = 0 - - summon_amt = 1 - summon_type = list(/mob/living/simple_animal/faithful_hound) - hud_state = "wiz_hound" - - var/temp_password - -/spell/aoe_turf/conjure/faithful_hound/apply_vars(atom/summoned_object, mob/caster) - . = ..() - var/mob/living/simple_animal/faithful_hound/hound = summoned_object - if(istype(hound) && istype(hound.ai)) - hound.ai.add_friend(caster) - hound.ai.memorise(caster, temp_password) - temp_password = null - -/spell/aoe_turf/conjure/faithful_hound/before_cast() - ..() - temp_password = sanitize(input("What password will this beast listen to?") as text, MAX_NAME_LEN) diff --git a/code/modules/spells/aoe_turf/conjure/force_portal.dm b/code/modules/spells/aoe_turf/conjure/force_portal.dm deleted file mode 100644 index 293e176051f1..000000000000 --- a/code/modules/spells/aoe_turf/conjure/force_portal.dm +++ /dev/null @@ -1,12 +0,0 @@ -/spell/aoe_turf/conjure/force_portal - name = "Force Portal" - desc = "Create a portal that sucks in anything that touches it and then shoots it all out at the end." - school = "conjuration" - feedback = "FP" - summon_type = list(/obj/effect/force_portal) - charge_max = 200 - spell_flags = NEEDSCLOTHES - range = 0 - cast_sound = null - - hud_state = "wiz_force" diff --git a/code/modules/spells/aoe_turf/conjure/forcewall.dm b/code/modules/spells/aoe_turf/conjure/forcewall.dm deleted file mode 100644 index 1c937f3f6d8c..000000000000 --- a/code/modules/spells/aoe_turf/conjure/forcewall.dm +++ /dev/null @@ -1,48 +0,0 @@ -/spell/aoe_turf/conjure/forcewall - name = "Forcewall" - desc = "Create a wall of pure energy at your location." - school = "conjuration" - feedback = "FW" - summon_type = list(/obj/effect/forcefield) - duration = 300 - charge_max = 100 - spell_flags = 0 - range = 0 - cast_sound = 'sound/magic/forcewall.ogg' - - hud_state = "wiz_shield" - -/spell/aoe_turf/conjure/forcewall/mime - name = "Invisible wall" - desc = "Create an invisible wall on your location." - school = "mime" - panel = "Mime" - summon_type = list(/obj/effect/forcefield/mime) - invocation_type = SpI_EMOTE - invocation = "mimes placing their hands on a flat surfacing, and pushing against it." - charge_max = 300 - cast_sound = null - - override_base = "grey" - hud_state = "mime_wall" - -/obj/effect/forcefield - desc = "A space wizard's magic wall." - name = "FORCEWALL" - icon = 'icons/effects/effects.dmi' - icon_state = "m_shield" - anchored = TRUE - opacity = FALSE - density = TRUE - -/obj/effect/forcefield/bullet_act(var/obj/item/projectile/Proj, var/def_zone) - var/turf/T = get_turf(src.loc) - if(T) - for(var/mob/M in T) - Proj.on_hit(M,M.bullet_act(Proj, def_zone)) - return - -/obj/effect/forcefield/mime - icon_state = "empty" - name = "invisible wall" - desc = "You have a bad feeling about this." diff --git a/code/modules/spells/aoe_turf/conjure/grove.dm b/code/modules/spells/aoe_turf/conjure/grove.dm deleted file mode 100644 index 77f68981e9db..000000000000 --- a/code/modules/spells/aoe_turf/conjure/grove.dm +++ /dev/null @@ -1,75 +0,0 @@ -/spell/aoe_turf/conjure/grove - name = "Grove" - desc = "Creates a sanctuary of nature around the wizard as well as creating a healing plant." - - spell_flags = IGNOREDENSE | IGNORESPACE | NEEDSCLOTHES | Z2NOCAST | IGNOREPREV - charge_max = 1200 - school = "transmutation" - - range = 1 - cooldown_min = 600 - - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 3, Sp_POWER = 1) - - summon_amt = 47 - summon_type = list(/turf/floor/fake_grass) - var/spread = 0 - var/datum/seed/seed - var/seed_type = /datum/seed/merlin_tear - cast_sound = 'sound/magic/repulse.ogg' - -/spell/aoe_turf/conjure/grove/New() - ..() - if(seed_type) - seed = new seed_type() - else - seed = SSplants.create_random_seed(1) - -/spell/aoe_turf/conjure/grove/before_cast() - var/turf/T = get_turf(holder) - var/obj/effect/vine/P = new(T,seed) - P.spread_chance = spread - - -/spell/aoe_turf/conjure/grove/sanctuary - name = "Sanctuary" - desc = "Creates a sanctuary of nature around the wizard as well as creating a healing plant." - feedback = "SY" - invocation = "Bo K'Iitan!" - invocation_type = SpI_SHOUT - spell_flags = IGNOREDENSE | IGNORESPACE | NEEDSCLOTHES | Z2NOCAST | IGNOREPREV - cooldown_min = 600 - - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 3, Sp_POWER = 1) - - seed_type = /datum/seed/merlin_tear - newVars = list("name" = "sanctuary", "desc" = "This grass makes you feel comfortable. Peaceful.","blessed" = 1) - - hud_state = "wiz_grove" -/spell/aoe_turf/conjure/grove/sanctuary/empower_spell() - if(!..()) - return 0 - - seed.set_trait(TRAIT_SPREAD,2) //make it grow. - spread = 40 - return "Your sanctuary will now grow beyond that of the grassy perimeter." - -/datum/seed/merlin_tear - name = "merlin tears" - product_name = "merlin tears" - display_name = "merlin tears" - chems = list(/decl/material/liquid/brute_meds = list(3,7), /decl/material/liquid/burn_meds = list(3,7), /decl/material/liquid/antitoxins = list(3,7), /decl/material/liquid/regenerator = list(3,7), /decl/material/liquid/neuroannealer = list(1,2), /decl/material/liquid/eyedrops = list(1,2)) - grown_tag = "berries" - -/datum/seed/merlin_tear/New() - ..() - set_trait(TRAIT_PLANT_ICON,"bush5") - set_trait(TRAIT_PRODUCT_ICON,"berry") - set_trait(TRAIT_PRODUCT_COLOUR,"#4d4dff") - set_trait(TRAIT_PLANT_COLOUR, "#ff6600") - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_IMMUTABLE,1) //no making op plants pls \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/disable_tech.dm b/code/modules/spells/aoe_turf/disable_tech.dm deleted file mode 100644 index d9912e1c3907..000000000000 --- a/code/modules/spells/aoe_turf/disable_tech.dm +++ /dev/null @@ -1,33 +0,0 @@ -/spell/aoe_turf/disable_tech - name = "Disable Tech" - desc = "This spell disables all weapons, cameras and most other technology in range." - feedback = "DT" - charge_max = 400 - spell_flags = NEEDSCLOTHES - invocation = "NEC CANTIO" - invocation_type = SpI_SHOUT - selection_type = "range" - range = 0 - inner_radius = -1 - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 2, Sp_POWER = 2) - cooldown_min = 200 //50 deciseconds reduction per rank - - var/emp_heavy = 3 - var/emp_light = 5 - - hud_state = "wiz_tech" - cast_sound = 'sound/magic/disable_tech.ogg' - -/spell/aoe_turf/disable_tech/cast(list/targets) - - for(var/turf/target in targets) - empulse(get_turf(target), emp_heavy, emp_light) - return - -/spell/aoe_turf/disable_tech/empower_spell() - if(!..()) - return 0 - emp_heavy += 2 - emp_light += 2 - - return "You've increased the range of [src]." diff --git a/code/modules/spells/aoe_turf/drain_blood.dm b/code/modules/spells/aoe_turf/drain_blood.dm deleted file mode 100644 index 8ad4ea6b8a29..000000000000 --- a/code/modules/spells/aoe_turf/drain_blood.dm +++ /dev/null @@ -1,66 +0,0 @@ -/spell/aoe_turf/drain_blood - name = "Drain Blood" - desc = "This spell allows the caster to borrow blood from those around them. Sharing is caring!" - feedback = "DB" - school = "transmutation" - charge_max = 600 - invocation = "whispers something darkly" - invocation_type = SpI_EMOTE - range = 3 - inner_radius = 0 - - time_between_channels = 100 - number_of_channels = 3 - cast_sound = 'sound/effects/squelch2.ogg' - hud_state = "const_rune" - -/spell/aoe_turf/drain_blood/cast(var/list/targets, var/mob/user) - for(var/t in targets) - for(var/mob/living/L in t) - if(L.stat == DEAD || L == user) - continue - //Hurt target - if(ishuman(L)) - var/mob/living/human/H = L - H.vessel.remove_any(10) - else - L.take_damage(10) - to_chat(L, "You feel your lifeforce being ripping out of your body!") - - //Do effect - var/obj/item/projectile/beam/blood_effect/effect = new(get_turf(user)) - effect.pixel_x = 0 - effect.pixel_y = 0 - effect.launch(L, "chest") - - //Heal self - if(ishuman(user)) - var/mob/living/human/H = user - var/amount = min(10, H.species.blood_volume - H.vessel.total_volume) - if(amount > 0) - H.adjust_blood(amount) - continue - L.heal_damage(BRUTE, 5, do_update_health = FALSE) - L.heal_damage(BURN, 2.5, do_update_health = FALSE) - L.heal_damage(TOX, 2.5) - -/obj/item/projectile/beam/blood_effect - name = "blood jet" - icon_state = "blood" - damage = 0 - randpixel = 0 - no_attack_log = 1 - muzzle_type = /obj/effect/projectile/blood - tracer_type = /obj/effect/projectile/blood - impact_type = /obj/effect/projectile/blood - -/obj/item/projectile/beam/blood_effect/Bump(var/atom/a, forced=0) - if(a == original) - on_impact(a) - qdel(src) - return 1 - return 0 - - -/obj/effect/projectile/blood - icon_state = "blood" \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/exchange_wounds.dm b/code/modules/spells/aoe_turf/exchange_wounds.dm deleted file mode 100644 index 7293e960d292..000000000000 --- a/code/modules/spells/aoe_turf/exchange_wounds.dm +++ /dev/null @@ -1,43 +0,0 @@ -/spell/aoe_turf/exchange_wounds - name = "Exchange Wounds" - desc = "Allows you to sacrifice your own well-being for that of those around you." - feedback = "EW" - school = "transmutation" - invocation = "Esh Yek Vai!" - invocation_type = SpI_SHOUT - charge_max = 400 - spell_flags = 0 - - var/amt_healed = 0 - var/heal_max = 100 - range = 4 - inner_radius = 0 - number_of_channels = 0 - time_between_channels = 20 - - hud_state = "wiz_exchange" - -/spell/aoe_turf/exchange_wounds/perform() - amt_healed = 0 - ..() - -/spell/aoe_turf/exchange_wounds/cast(var/list/targets, var/mob/living/user) - new /obj/effect/temporary(get_turf(user),10,'icons/effects/effects.dmi',"purple_electricity_constant") - for(var/t in targets) - for(var/mob/living/L in t) - if(L.faction != user.faction) - continue - new /obj/effect/temporary(get_turf(L),10,'icons/effects/effects.dmi',"green_sparkles") - if(L.get_damage(BRUTE) > 5) - L.heal_damage(BRUTE, 5) - user.take_damage(2) - amt_healed += 5 - if(L.get_damage(BURN) > 5) - L.heal_damage(BURN, 5) - user.take_damage(2, BURN) - amt_healed += 5 - -/spell/aoe_turf/exchange_wounds/check_valid_targets() - if(amt_healed > heal_max) - return FALSE - return ..() \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/knock.dm b/code/modules/spells/aoe_turf/knock.dm deleted file mode 100644 index 3cf8614eeaa7..000000000000 --- a/code/modules/spells/aoe_turf/knock.dm +++ /dev/null @@ -1,37 +0,0 @@ -/spell/aoe_turf/knock - name = "Knock" - desc = "This spell opens nearby doors and does not require wizard garb." - feedback = "KN" - school = "transmutation" - charge_max = 100 - spell_flags = 0 - invocation = "Aulie Oxin Fiera." - invocation_type = SpI_WHISPER - range = 3 - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 4, Sp_POWER = 1) - cooldown_min = 20 //20 deciseconds reduction per rank - - hud_state = "wiz_knock" - cast_sound = 'sound/magic/knock.ogg' - -/spell/aoe_turf/knock/cast(list/targets) - for(var/turf/T in targets) - for(var/obj/machinery/door/door in T.contents) - spawn(1) - if(istype(door,/obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/AL = door //casting is important - AL.locked = 0 - door.open() - return - - -/spell/aoe_turf/knock/empower_spell() - if(!..()) - return 0 - range *= 2 - - return "You've doubled the range of [src]." - -/spell/aoe_turf/knock/slow - charge_max = 200 - hidden_from_codex = TRUE diff --git a/code/modules/spells/aoe_turf/smoke.dm b/code/modules/spells/aoe_turf/smoke.dm deleted file mode 100644 index f9f0138908fb..000000000000 --- a/code/modules/spells/aoe_turf/smoke.dm +++ /dev/null @@ -1,26 +0,0 @@ -/spell/aoe_turf/smoke - name = "Smoke" - desc = "This spell spawns a cloud of choking smoke at your location and does not require wizard garb." - feedback = "SM" - school = "transmutation" - charge_max = 120 - spell_flags = 0 - invocation = "none" - invocation_type = SpI_NONE - range = 1 - inner_radius = -1 - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 3, Sp_POWER = 2) - cooldown_min = 20 //25 deciseconds reduction per rank - - smoke_spread = 2 - smoke_amt = 5 - - hud_state = "wiz_smoke" - cast_sound = 'sound/magic/smoke.ogg' - -/spell/aoe_turf/smoke/empower_spell() - if(!..()) - return 0 - smoke_amt += 2 - - return "[src] will now create more smoke." diff --git a/code/modules/spells/aoe_turf/summons.dm b/code/modules/spells/aoe_turf/summons.dm deleted file mode 100644 index be61819d9eab..000000000000 --- a/code/modules/spells/aoe_turf/summons.dm +++ /dev/null @@ -1,72 +0,0 @@ -/spell/aoe_turf/conjure/summonEdSwarm //test purposes - name = "Dispense Wizard Justice" - desc = "This spell dispenses wizard justice." - - summon_type = list(/mob/living/bot/secbot/ed209) - summon_amt = 10 - range = 3 - newVars = list("emagged" = 1,"name" = "Wizard's Justicebot") - - hud_state = "wiz_ed" - -/spell/aoe_turf/conjure/carp - name = "Summon Carp" - desc = "This spell conjures a simple carp." - - school = "conjuration" - charge_max = 1200 - spell_flags = NEEDSCLOTHES - invocation = "Nouk Fhumm Sacp Risska!" - invocation_type = SpI_SHOUT - range = 1 - cast_sound = 'sound/magic/summon_carp.ogg' - - summon_type = list(/mob/living/simple_animal/hostile/carp) - - hud_state = "wiz_carp" - -/spell/aoe_turf/conjure/creature - name = "Summon Creature Swarm" - desc = "This spell tears the fabric of reality, allowing horrific daemons to spill forth" - - school = "conjuration" - charge_max = 1200 - spell_flags = 0 - invocation = "Ia-Ia! Naomesnalia!" - invocation_type = SpI_SHOUT - summon_amt = 10 - range = 3 - - summon_type = list(/mob/living/simple_animal/hostile/creature) - - hud_state = "wiz_creature" - -/spell/aoe_turf/conjure/mirage - name = "Summon Mirage" - desc = "This spell summons a harmless carp mirage for a few seconds." - feedback = "MR" - school = "illusion" - charge_max = 1200 - spell_flags = NEEDSCLOTHES - invocation = "Nouk Fhunhm Sacp Risska!" - invocation_type = SpI_SHOUT - range = 1 - cast_sound = 'sound/magic/summon_carp.ogg' - - duration = 600 - cooldown_min = 600 - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 2, Sp_POWER = 3) - - summon_type = list(/mob/living/simple_animal/hostile/carp) - - hud_state = "wiz_carp" - - newVars = list("melee_damage_lower" = 0, "melee_damage_upper" = 0, "break_stuff_probability" = 0) - -/spell/aoe_turf/conjure/mirage/empower_spell() - if(!..()) - return 0 - - summon_amt++ - - return "You now summon [summon_amt] mirages per spellcast." \ No newline at end of file diff --git a/code/modules/spells/artifacts.dm b/code/modules/spells/artifacts.dm deleted file mode 100644 index 2c085ff8e2a5..000000000000 --- a/code/modules/spells/artifacts.dm +++ /dev/null @@ -1,41 +0,0 @@ -//////////////////////Scrying orb////////////////////// - -/obj/item/scrying - name = "scrying orb" - desc = "An incandescent orb of otherworldly energy, staring into it gives you vision beyond mortal means." - icon = 'icons/obj/projectiles.dmi' - icon_state = "bluespace" - throw_speed = 3 - throw_range = 7 - atom_damage_type = BURN - hitsound = 'sound/magic/forcewall.ogg' - max_health = ITEM_HEALTH_NO_DAMAGE - _base_attack_force = 10 - -/obj/item/scrying/attack_self(mob/user) - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - if((user.mind && !wizards.is_antagonist(user.mind))) - to_chat(user, "You stare into the orb and see nothing but your own reflection.") - return - - to_chat(user, "You can see... everything!") // This never actually happens. - visible_message("[user] stares into [src], their eyes glazing over.") - - user.teleop = user.ghostize() - announce_ghost_joinleave(user.teleop, 1, "You feel that they used a powerful artifact to [pick("invade","disturb","disrupt","infest","taint","spoil","blight")] this place with their presence.") - return - - - -/////////////////////////Cursed Dice/////////////////////////// -/obj/item/dice/d20/cursed - desc = "A dice with twenty sides said to have an ill effect on those that are unlucky..." - -/obj/item/dice/d20/cursed/attack_self(mob/user) - ..() - if(isliving(user)) - var/mob/living/M = user - if(icon_state == "[name][sides]") - M.heal_damage(BRUTE, 30) - else if(icon_state == "[name]1") - M.take_damage(30) diff --git a/code/modules/spells/artifacts/spellbound_servants.dm b/code/modules/spells/artifacts/spellbound_servants.dm deleted file mode 100644 index 6477080cfdf4..000000000000 --- a/code/modules/spells/artifacts/spellbound_servants.dm +++ /dev/null @@ -1,284 +0,0 @@ -/datum/spellbound_type - var/name = "Stuff" - var/desc = "spells n shit" - var/equipment = list() - var/spells = list() - -/datum/spellbound_type/proc/spawn_servant(var/atom/a, var/mob/master, var/mob/user) - set waitfor = 0 - var/mob/living/human/H = new(a) - H.ckey = user.ckey - H.change_appearance(APPEARANCE_GENDER|APPEARANCE_BODY|APPEARANCE_EYE_COLOR|APPEARANCE_HAIR|APPEARANCE_FACIAL_HAIR|APPEARANCE_HAIR_COLOR|APPEARANCE_FACIAL_HAIR_COLOR|APPEARANCE_SKIN) - - var/obj/item/implant/translator/natural/I = new() - I.implant_in_mob(H, BP_HEAD) - if (length(master.languages)) - var/decl/language/lang = master.languages[1] - H.add_language(lang.type) - H.set_default_language(lang.type) - I.languages[lang.name] = 1 - - modify_servant(equip_servant(H), H) - set_antag(H.mind, master) - var/name_choice = sanitize(input(H, "Choose a name. If you leave this blank, it will be defaulted to your current characters.", "Name change") as null|text, MAX_NAME_LEN) - if(name_choice) - H.SetName(name_choice) - H.real_name = name_choice - -/datum/spellbound_type/proc/equip_servant(var/mob/living/human/H) - for(var/stype in spells) - var/spell/S = new stype() - if(S.spell_flags & NEEDSCLOTHES) - S.spell_flags &= ~NEEDSCLOTHES - H.add_spell(S) - . = list() - for(var/etype in equipment) - var/obj/item/I = new etype(get_turf(H)) - if(istype(I, /obj/item/clothing)) - I.canremove = 0 - H.equip_to_slot_if_possible(I,equipment[etype],0,1,1,1) - . += I - -/datum/spellbound_type/proc/set_antag(var/datum/mind/M, var/mob/master) - return - -/datum/spellbound_type/proc/modify_servant(var/list/items, var/mob/living/human/H) - return - -/datum/spellbound_type/apprentice - name = "Apprentice" - desc = "Summon your trusty apprentice, equipped with their very own spellbook." - equipment = list(/obj/item/clothing/head/wizard = slot_head_str, - /obj/item/clothing/jumpsuit/lightpurple = slot_w_uniform_str, - /obj/item/clothing/shoes/sandal = slot_shoes_str, - /obj/item/staff = BP_R_HAND, - /obj/item/book/spell/apprentice = BP_L_HAND, - /obj/item/clothing/suit/wizrobe = slot_wear_suit_str) - spells = list(/spell/noclothes) - -/datum/spellbound_type/apprentice/set_antag(var/datum/mind/M, var/mob/master) - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - wizards.add_antagonist_mind(M, 1, "Wizard's Apprentice", "You are an apprentice-type Servant! You're just an ordinary Wizard-To-Be, with no special abilities, but do not need robes to cast spells. Follow your teacher's orders!") - -/datum/spellbound_type/servant - var/spiel = "You don't do anything in particular." - -/datum/spellbound_type/servant/set_antag(var/datum/mind/M, var/mob/master) - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - wizards.add_antagonist_mind(M, 1, "Spellbound Servant", "You are a [name]-type Servant! [spiel]") - -/datum/spellbound_type/servant/caretaker - name = "Caretaker" - desc = "A healer, a medic, a shoulder to cry on. This servant will heal you, even from near death." - spiel = "'The last enemy that will be destroyed is death.' You can perceive any injuries with simple sight, and heal them with the Trance spell; potentially even reversing death itself! However, this comes at a price; Trance will become increasingly harder to use as you use it, until you can use it no longer. Be cautious, and aid your Master in any way possible!" - equipment = list(/obj/item/clothing/jumpsuit/caretaker = slot_w_uniform_str, - /obj/item/clothing/shoes/dress/caretakershoes = slot_shoes_str) - spells = list(/spell/toggle_armor/caretaker, - /spell/targeted/heal_target/touch, - /spell/aoe_turf/knock/slow, - /spell/targeted/heal_target/area/slow, - /spell/targeted/analyze, - /spell/targeted/heal_target/trance - ) - -/datum/spellbound_type/servant/champion - name = "Champion" - desc = "A knight in shining armor; a warrior, a protector, and a loyal friend." - spiel = "Your sword and armor are second to none, but you have no unique supernatural powers beyond summoning the sword to your hands. Protect your Master with your life!" - equipment = list( - /obj/item/clothing/pants/champion = slot_w_uniform_str, - /obj/item/clothing/shoes/jackboots/medievalboots = slot_shoes_str - ) - spells = list( - /spell/toggle_armor/champion, - /spell/toggle_armor/excalibur - ) - -/datum/spellbound_type/servant/familiar - name = "Familiar" - desc = "A friend! Or are they a pet? They can transform into animals, and take some particular traits from said creatures." - spiel = "This form of yours is weak in comparison to your transformed form, but that certainly won't pose a problem, considering the fact that you have an alternative. Whatever it is you can turn into, use its powers wisely and serve your Master as well as possible!" - equipment = list( - /obj/item/clothing/head/bandana/familiarband = slot_head_str, - /obj/item/clothing/pants/familiar = slot_w_uniform_str - ) - -/datum/spellbound_type/servant/familiar/modify_servant(var/list/equipment, var/mob/living/human/H) - var/familiar_type - switch(input(H,"Choose your desired animal form:", "Form") as anything in list("Space Pike", "Mouse", "Cat", "Bear")) - if("Space Pike") - H.add_genetic_condition(GENE_COND_NO_BREATH) - H.add_genetic_condition(GENE_COND_SPACE_RESISTANCE) - familiar_type = /mob/living/simple_animal/hostile/carp/pike - if("Mouse") - H.verbs |= /mob/living/proc/ventcrawl - familiar_type = /mob/living/simple_animal/passive/mouse - if("Cat") - H.add_genetic_condition(GENE_COND_RUNNING) - familiar_type = /mob/living/simple_animal/passive/cat - if("Bear") - familiar_type = /mob/living/simple_animal/hostile/bear - var/spell/targeted/shapeshift/familiar/F = new() - F.possible_transformations = list(familiar_type) - H.add_spell(F) - -/datum/spellbound_type/servant/fiend - name = "Fiend" - desc = "A practitioner of dark and evil magics, almost certainly a demon, and possibly a lawyer." - spiel = "The Summoning Ritual has bound you to this world with limited access to your infernal powers; you'll have to be strategic in how you use them. Follow your Master's orders as well as you can!" - spells = list(/spell/targeted/projectile/dumbfire/fireball/firebolt, - /spell/targeted/ethereal_jaunt, - /spell/targeted/torment, - /spell/area_teleport, - /spell/hand/charges/blood_shard - ) - -/datum/spellbound_type/servant/fiend/equip_servant(var/mob/living/human/H) - if(H.gender == MALE) - equipment = list(/obj/item/clothing/costume/fiendsuit = slot_w_uniform_str, - /obj/item/clothing/shoes/dress/devilshoes = slot_shoes_str) - spells += /spell/toggle_armor/fiend - else - equipment = list(/obj/item/clothing/dress/devil = slot_w_uniform_str, - /obj/item/clothing/shoes/dress/devilshoes = slot_shoes_str) - spells += /spell/toggle_armor/fiend/fem - ..() - -/datum/spellbound_type/servant/infiltrator - name = "Infiltrator" - desc = "A spy and a manipulator to the end, capable of hiding in plain sight and falsifying information to your heart's content." - spiel = "On the surface, you are a completely normal person, but is that really all you are? People are so easy to fool, do as your Master says, and do it with style!" - spells = list( - /spell/toggle_armor/infil_items, - /spell/targeted/exude_pleasantness, - /spell/targeted/genetic/blind/hysteria - ) - -/datum/spellbound_type/servant/infiltrator/equip_servant(var/mob/living/human/H) - if(H.gender == MALE) - equipment = list(/obj/item/clothing/pants/slacks/outfit/tie = slot_w_uniform_str, - /obj/item/clothing/shoes/dress/infilshoes = slot_shoes_str) - spells += /spell/toggle_armor/infiltrator - else - equipment = list(/obj/item/clothing/dress/white = slot_w_uniform_str, - /obj/item/clothing/shoes/dress/infilshoes = slot_shoes_str) - spells += /spell/toggle_armor/infiltrator/fem - ..() - -/datum/spellbound_type/servant/overseer - name = "Overseer" - desc = "A ghost, or an imaginary friend; the Overseer is immune to space and can turn invisible at a whim, but has little offensive capabilities." - spiel = "Physicality is not something you are familiar with. Indeed, injuries cannot slow you down, but you can't fight back, either! In addition to this, you can reach into the void and return the soul of a single departed crewmember via the revoke death verb, if so desired; this can even revive your Master, should they fall in combat before you do. Serve them well." - equipment = list( - /obj/item/clothing/pants/casual/blackjeans/outfit = slot_w_uniform_str, - /obj/item/clothing/suit/jacket/hoodie/grim = slot_wear_suit_str, - /obj/item/clothing/shoes/sandal/grimboots = slot_shoes_str, - /obj/item/contract/wizard/xray = BP_L_HAND, - /obj/item/contract/wizard/telepathy = BP_R_HAND - ) - spells = list( - /spell/toggle_armor/overseer, - /spell/targeted/ethereal_jaunt, - /spell/invisibility, - /spell/targeted/revoke - ) - -/datum/spellbound_type/servant/overseer/equip_servant(var/mob/living/human/H) - ..() - H.add_aura(new /obj/aura/regenerating(H)) - -/obj/effect/cleanable/spellbound - name = "strange rune" - desc = "some sort of runic symbol drawn in... crayon?" - icon = 'icons/obj/rune.dmi' - icon_state = "spellbound" - is_spawnable_type = FALSE // invalid without spell_type passed - var/datum/spellbound_type/stype - var/last_called = 0 - -/obj/effect/cleanable/spellbound/Initialize(mapload, var/spell_type) - . = ..(mapload) - stype = new spell_type() - -/obj/effect/cleanable/spellbound/attack_hand(var/mob/user) - SHOULD_CALL_PARENT(FALSE) - if(last_called > world.time) - return TRUE - last_called = world.time + 30 SECONDS - var/decl/ghosttrap/G = GET_DECL(/decl/ghosttrap/wizard_familiar) - for(var/mob/observer/ghost/ghost in global.player_list) - if(G.assess_candidate(ghost,null,FALSE)) - to_chat(ghost, "[SPAN_NOTICE("A wizard is requesting a Spell-Bound Servant!")] (Join)") - return TRUE - -/obj/effect/cleanable/spellbound/CanUseTopic(var/mob) - if(isliving(mob)) - return STATUS_CLOSE - return STATUS_INTERACTIVE - -/obj/effect/cleanable/spellbound/OnTopic(var/mob/user, href_list, state) - if(href_list["master"]) - var/mob/master = locate(href_list["master"]) - stype.spawn_servant(get_turf(src),master,user) - qdel(src) - return TOPIC_HANDLED - -/obj/effect/cleanable/spellbound/Destroy() - qdel(stype) - stype = null - return ..() - -/obj/item/summoning_stone - name = "summoning stone" - desc = "a small non-descript stone of dubious origin." - icon = 'icons/obj/items/summoning_stone.dmi' - icon_state = "stone" - throw_speed = 5 - throw_range = 10 - w_class = ITEM_SIZE_SMALL - material = /decl/material/solid/stone/basalt - -/obj/item/summoning_stone/attack_self(var/mob/user) - if(isAdminLevel(user.z)) - to_chat(user, "You cannot use \the [src] here.") - return - user.set_machine(src) - interact(user) - -/obj/item/summoning_stone/interact(var/mob/user) - var/list/types = subtypesof(/datum/spellbound_type) - /datum/spellbound_type/servant - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - if(user.mind && !wizards.is_antagonist(user.mind)) - use_type(pick(types),user) - return - var/dat = "

    Summoning Stone

    Choose a companion to help you.

    " - for(var/type in types) - var/datum/spellbound_type/SB = type - dat += "
    [initial(SB.name)] - [initial(SB.desc)]" - show_browser(user,dat,"window=summoning") - onclose(user,"summoning") - -/obj/item/summoning_stone/proc/use_type(var/type, var/mob/user) - new /obj/effect/cleanable/spellbound(get_turf(src),type) - if(prob(20)) - var/list/base_areas = maintlocs //Have to do it this way as its a macro - var/list/pareas = base_areas.Copy() - while(pareas.len) - var/a = pick(pareas) - var/area/picked_area = pareas[a] - pareas -= a - var/list/turfs = get_area_turfs(picked_area) - for(var/t in turfs) - var/turf/T = t - if(T.density) - turfs -= T - if(turfs.len) - src.visible_message("\The [src] vanishes!") - src.forceMove(pick(turfs)) - show_browser(user, null, "window=summoning") - qdel(src) - -/obj/item/summoning_stone/OnTopic(user, href_list, state) - if(href_list["type"]) - use_type(href_list["type"],user) - return TOPIC_HANDLED \ No newline at end of file diff --git a/code/modules/spells/artifacts/storage.dm b/code/modules/spells/artifacts/storage.dm deleted file mode 100644 index 4d84cab7d3d5..000000000000 --- a/code/modules/spells/artifacts/storage.dm +++ /dev/null @@ -1,30 +0,0 @@ -/obj/structure/closet/wizard - name = "artifact closet" - desc = "a special lead lined closet used to hold artifacts of immense power." - closet_appearance = /decl/closet_appearance/alien - -/obj/structure/closet/wizard/Initialize() - . = ..() - new /obj/item/parcel(get_turf(src), null, src, "Imported straight from the Wizard Acadamy. Do not lose the contents or suffer a demerit.") - -/obj/structure/closet/wizard/armor - name = "Mastercrafted Armor Set" - desc = "An artefact suit of armor that allows you to cast spells while providing more protection against attacks and the void of space." - -/obj/structure/closet/wizard/armor/WillContain() - return list( - /obj/item/clothing/shoes/sandal, - /obj/item/clothing/gloves/wizard, - /obj/item/clothing/suit/space/void/wizard, - /obj/item/clothing/head/helmet/space/void/wizard - ) - -/obj/structure/closet/wizard/scrying - name = "Scrying Orb" - desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to reconnoiter with ease. In addition, buying it will permanently grant you x-ray vision." - -/obj/structure/closet/wizard/scrying/WillContain() - return list( - /obj/item/scrying, - /obj/item/contract/wizard/xray, - ) \ No newline at end of file diff --git a/code/modules/spells/contracts.dm b/code/modules/spells/contracts.dm deleted file mode 100644 index 1b6ebf527f7e..000000000000 --- a/code/modules/spells/contracts.dm +++ /dev/null @@ -1,142 +0,0 @@ -/obj/item/contract - name = "contract" - desc = "written in the blood of some unfortunate fellow." - icon = 'icons/mob/screen/spells.dmi' - icon_state = "master_open" - material = /decl/material/solid/organic/paper - var/contract_master = null - var/list/contract_spells = list(/spell/contract/reward,/spell/contract/punish,/spell/contract/return_master) - -/obj/item/contract/attack_self(mob/user) - if(contract_master == null) - to_chat(user, "You bind the contract to your soul, making you the recipient of whatever poor fool's soul that decides to contract with you.") - contract_master = user - return - - if(contract_master == user) - to_chat(user, "You can't contract with yourself!") - return - - var/ans = alert(user,"The contract clearly states that signing this contract will bind your soul to \the [contract_master]. Are you sure you want to continue?","[src]","Yes","No") - - if(ans == "Yes") - user.visible_message("\The [user] signs the contract, their body glowing a deep yellow.") - if(!src.contract_effect(user)) - user.visible_message("\The [src] visibly rejects \the [user], erasing their signature from the line.") - return - user.visible_message("\The [src] disappears with a flash of light.") - if(contract_spells.len && isliving(contract_master)) //if it aint text its probably a mob or another user - var/mob/living/M = contract_master - for(var/spell_type in contract_spells) - M.add_spell(new spell_type(user), "const_spell_ready") - log_and_message_admins("signed their soul over to \the [contract_master] using \the [src].", user) - qdel(src) - -/obj/item/contract/proc/contract_effect(mob/user) - to_chat(user, "You've signed your soul over to \the [contract_master] and with that your unbreakable vow of servitude begins.") - return 1 - -/obj/item/contract/apprentice - name = "apprentice wizarding contract" - desc = "a wizarding school contract for those who want to sign their soul for a piece of the magic pie." - color = "#993300" - -/obj/item/contract/apprentice/contract_effect(mob/user) - if(user.mind.assigned_special_role == "Wizard's Apprentice") - to_chat(user, "You are already a wizarding apprentice!") - return 0 - if(user.mind.assigned_special_role == "Spellbound Servant") - to_chat(user, "You are a servant. You have no need of apprenticeship.") - return 0 - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - if(wizards.add_antagonist_mind(user.mind, 1, "Wizard's Apprentice", "You are an apprentice! Your job is to learn the wizarding arts!")) - to_chat(user, "With the signing of this paper you agree to become \the [contract_master]'s apprentice in the art of wizardry.") - return 1 - return 0 - -/obj/item/contract/wizard //contracts that involve making a deal with the Wizard Acadamy (or NON PLAYERS) - contract_master = "\improper Wizard Academy" - -/obj/item/contract/wizard/xray - name = "xray vision contract" - desc = "This contract is almost see-through..." - color = "#339900" - -/obj/item/contract/wizard/xray/contract_effect(mob/user) - ..() - if (user.add_genetic_condition(GENE_COND_XRAY)) - user.set_sight(user.sight|SEE_MOBS|SEE_OBJS|SEE_TURFS) - user.set_see_in_dark(8) - user.set_see_invisible(SEE_INVISIBLE_LEVEL_TWO) - to_chat(user, "The walls suddenly disappear.") - return 1 - return 0 - -/obj/item/contract/wizard/telepathy - name = "telepathy contract" - desc = "The edges of the contract grow blurry when you look away from them. To be fair, actually reading it gives you a headache." - color = "#fcc605" - -/obj/item/contract/wizard/telepathy/contract_effect(mob/user) - ..() - return user.add_genetic_condition(GENE_COND_REMOTE_TALK) - -/obj/item/contract/boon - name = "boon contract" - desc = "this contract grants you a boon for signing it." - var/path - -/obj/item/contract/boon/Initialize(mapload, var/new_path) - . = ..(mapload) - if(new_path) - path = new_path - var/item_name = "" - if(ispath(path,/obj)) - var/obj/O = path - item_name = initial(O.name) - else if(ispath(path,/spell)) - var/spell/S = path - item_name = initial(S.name) - name = "[item_name] contract" - -/obj/item/contract/boon/contract_effect(mob/user) - ..() - if(user.mind.assigned_special_role == "Spellbound Servant") - to_chat(user, "As a servant you find yourself unable to use this contract.") - return 0 - if(ispath(path,/spell)) - user.add_spell(new path) - return 1 - else if(ispath(path,/obj)) - new path(get_turf(user.loc)) - playsound(get_turf(usr),'sound/magic/charge.ogg',50,1) - return 1 - -/obj/item/contract/boon/wizard - contract_master = "\improper Wizard Academy" - -/obj/item/contract/boon/wizard/fireball - path = /spell/targeted/projectile/dumbfire/fireball - desc = "This contract feels warm to the touch." - -/obj/item/contract/boon/wizard/smoke - path = /spell/aoe_turf/smoke - desc = "This contract smells as dank as they come." - -/obj/item/contract/boon/wizard/forcewall - path = /spell/aoe_turf/conjure/forcewall - contract_master = "\improper Mime Federation" - desc = "This contract has a dedication to mimes everywhere at the top." - -/obj/item/contract/boon/wizard/knock - path = /spell/aoe_turf/knock - desc = "This contract is hard to hold still." - -/obj/item/contract/boon/wizard/horsemask - path = /spell/targeted/equip_item/horsemask - desc = "This contract is more horse than your mind has room for." - -/obj/item/contract/boon/wizard/charge - path = /spell/aoe_turf/charge - desc = "This contract is made of 100% post-consumer wizard." - diff --git a/code/modules/spells/general/acid_spray.dm b/code/modules/spells/general/acid_spray.dm deleted file mode 100644 index 752a17eecb72..000000000000 --- a/code/modules/spells/general/acid_spray.dm +++ /dev/null @@ -1,26 +0,0 @@ -/spell/acid_spray - name = "Acid Spray" - desc = "A common spell used to destroy basically anything in front of the wizard." - school = "conjuration" - feedback = "as" - spell_flags = 0 - charge_max = 600 - - invocation = "Tagopar lethodar!" - invocation_type = SpI_SHOUT - var/reagent_type = /decl/material/liquid/acid/hydrochloric - hud_state = "wiz_acid" - cast_sound = 'sound/magic/disintegrate.ogg' - -/spell/acid_spray/choose_targets() - return list(holder) - -/spell/acid_spray/cast(var/list/targets, var/mob/user) - var/atom/target = targets[1] - var/angle = dir2angle(target.dir) - for(var/mod in list(315, 0, 45)) - var/obj/effect/effect/water/chempuff/chem = new(get_turf(target)) - chem.create_reagents(10) - chem.add_to_reagents(reagent_type,10) - spawn(0) - chem.set_up(get_ranged_target_turf(target, angle2dir(angle+mod), 3)) diff --git a/code/modules/spells/general/area_teleport.dm b/code/modules/spells/general/area_teleport.dm deleted file mode 100644 index 68e20f3d6f9a..000000000000 --- a/code/modules/spells/general/area_teleport.dm +++ /dev/null @@ -1,60 +0,0 @@ -/spell/area_teleport - name = "Teleport" - desc = "This spell teleports you to a type of area of your selection." - feedback = "TP" - school = "conjuration" - charge_max = 60 SECONDS - spell_flags = NEEDSCLOTHES - invocation = "Scyar Nila!" - invocation_type = SpI_SHOUT - cooldown_min = 200 //100 deciseconds reduction per rank - - smoke_spread = 1 - smoke_amt = 5 - - var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list - var/invocation_area = 1 //if the invocation appends the selected area - - cast_sound = 'sound/effects/teleport.ogg' - - hud_state = "wiz_tele" - -/spell/area_teleport/before_cast() - return - -/spell/area_teleport/choose_targets() - var/area/thearea - if(!randomise_selection) - thearea = input("Area to teleport to", "Teleport") as null|anything in wizteleportlocs - if(!thearea) - return - else - thearea = pick(wizteleportlocs) - return list(wizteleportlocs[thearea]) - -/spell/area_teleport/cast(area/thearea, mob/user) - playsound(get_turf(user),cast_sound,50,1) - var/turf/end = user.try_teleport(thearea) - - if(!end) - to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - return - -/spell/area_teleport/check_valid_targets(list/targets) - // Teleport should function across z's, so we make sure that happens - // without this check, it only works for teleporting to areas you can see - return islist(targets) && length(targets) - -/spell/area_teleport/after_cast() - return - -/spell/area_teleport/invocation(mob/user, area/chosenarea) - if(!istype(chosenarea)) - return //can't have that, can we - if(!invocation_area || !chosenarea) - ..() - else - invocation += "[uppertext(chosenarea.proper_name)]" - ..() - return diff --git a/code/modules/spells/general/camera_vision.dm b/code/modules/spells/general/camera_vision.dm deleted file mode 100644 index cfd0cfb030e2..000000000000 --- a/code/modules/spells/general/camera_vision.dm +++ /dev/null @@ -1,36 +0,0 @@ -/spell/camera_connection - name = "Camera Connection" - desc = "This spell allows the wizard to connect to the local camera network and see what it sees." - - school = "racial" - - invocation_type = SpI_EMOTE - invocation = "emits a beeping sound before standing very, very still." - - charge_max = 600 //1 minute - charge_type = Sp_RECHARGE - - - spell_flags = Z2NOCAST - hud_state = "wiz_IPC" - - var/extension_type = /datum/extension/eye/cameranet - -/spell/camera_connection/New() - ..() - set_extension(src, extension_type) - -/spell/camera_connection/choose_targets() - var/mob/living/L = holder - if(!istype(L) || L.eyeobj) //no using if we already have an eye on. - return null - return list(holder) - -/spell/camera_connection/cast(var/list/targets, mob/user) - var/mob/living/L = targets[1] - - var/datum/extension/eye/cameranet/cn = get_extension(src, /datum/extension/eye/) - if(!cn) - to_chat(user, SPAN_WARNING("There's a flash of sparks as the spell fizzles out!")) - return - cn.look(L) \ No newline at end of file diff --git a/code/modules/spells/general/contract_spells.dm b/code/modules/spells/general/contract_spells.dm deleted file mode 100644 index fa95cd16e23c..000000000000 --- a/code/modules/spells/general/contract_spells.dm +++ /dev/null @@ -1,68 +0,0 @@ -//These spells are given to the owner of a contract when a victim signs it. -//As such they are REALLY REALLY powerful (because the victim is rewarded for signing it, and signing contracts is completely voluntary) - -/spell/contract - name = "Contract Spell" - desc = "A spell perfecting the techniques of keeping a servant happy and obedient." - - school = "transmutation" - spell_flags = 0 - invocation = "none" - invocation_type = SpI_NONE - - - var/mob/subject - -/spell/contract/New(var/mob/M) - ..() - subject = M - name += " ([M.real_name])" - -/spell/contract/choose_targets() - return list(subject) - -/spell/contract/cast(mob/target,mob/user) - if(!subject) - to_chat(usr, "This spell was not properly given a target. Contact a coder.") - return null - - if(istype(target,/list)) - var/list/target_list = target - target = target_list[1] - return target - - -/spell/contract/reward - name = "Reward Contractee" - desc = "A spell that makes your contracted victim feel better." - - charge_max = 300 - cooldown_min = 100 - - hud_state = "wiz_jaunt_old" - -/spell/contract/reward/cast(mob/living/target,mob/user) - target = ..(target,user) - if(!target) - return - - to_chat(target, SPAN_BLUE("You feel great!")) - target.ExtinguishMob() - -/spell/contract/punish - name = "Punish Contractee" - desc = "A spell that sets your contracted victim ablaze." - - charge_max = 300 - cooldown_min = 100 - - hud_state = "gen_immolate" - -/spell/contract/punish/cast(mob/living/target,mob/user) - target = ..(target,user) - if(!target) - return - - to_chat(target, "You feel punished!") - target.fire_stacks += 15 - target.IgniteMob() \ No newline at end of file diff --git a/code/modules/spells/general/create_air.dm b/code/modules/spells/general/create_air.dm deleted file mode 100644 index 405e161a54a9..000000000000 --- a/code/modules/spells/general/create_air.dm +++ /dev/null @@ -1,24 +0,0 @@ -/spell/create_air - name = "Create Air" - desc = "A much used spell used in the vasteness of space to make it not so killey." - - charge_max = 200 - spell_flags = Z2NOCAST - invocation = "none" - invocation_type = SpI_NONE - - number_of_channels = 0 - time_between_channels = 200 - hud_state = "wiz_air" - var/list/air_change = list(/decl/material/gas/oxygen = ONE_ATMOSPHERE) - -/spell/create_air/choose_targets() - var/air = holder.return_air() - if(air) - return list(air) - return null - -/spell/create_air/cast(var/list/targets, var/mob/holder, var/channel_count) - var/datum/gas_mixture/environment = targets[1] - for(var/gas in air_change) - environment.adjust_gas(gas, air_change[gas]) diff --git a/code/modules/spells/general/invisibility.dm b/code/modules/spells/general/invisibility.dm deleted file mode 100644 index 3fbab715efd1..000000000000 --- a/code/modules/spells/general/invisibility.dm +++ /dev/null @@ -1,24 +0,0 @@ -/spell/invisibility - name = "invisibility" - desc = "A simple spell of invisibility, for when you really just can't afford a paper bag." - feedback = "IV" - spell_flags = 0 - charge_max = 100 - invocation = "Ror Rim Or!" - invocation_type = SpI_SHOUT - var/on = 0 - hud_state = "invisibility" - -/spell/invisibility/choose_targets() - if(ishuman(holder)) - return holder - -/spell/invisibility/cast(var/mob/living/human/H, var/mob/user) - on = !on - if(on) - if(H.add_cloaking_source(src)) - playsound(get_turf(H), 'sound/effects/teleport.ogg', 90, 1) - H.add_genetic_condition(GENE_COND_CLUMSY) - else if(H.remove_cloaking_source(src)) - playsound(get_turf(H), 'sound/effects/stealthoff.ogg', 90, 1) - H.remove_genetic_condition(GENE_COND_CLUMSY) \ No newline at end of file diff --git a/code/modules/spells/general/mark_recall.dm b/code/modules/spells/general/mark_recall.dm deleted file mode 100644 index 11131e72ece2..000000000000 --- a/code/modules/spells/general/mark_recall.dm +++ /dev/null @@ -1,88 +0,0 @@ -/spell/mark_recall - name = "Mark and Recall" - desc = "This spell was created so wizards could get home from the bar without driving. Does not require wizard garb." - feedback = "MK" - school = "conjuration" - charge_max = 600 //1 minutes for how OP this shit is (apparently not as op as I thought) - spell_flags = Z2NOCAST - invocation = "Re-Alki R'natha." - invocation_type = SpI_WHISPER - cooldown_min = 300 - - smoke_amt = 1 - smoke_spread = 5 - - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 4, Sp_POWER = 1) - - cast_sound = 'sound/effects/teleport.ogg' - hud_state = "wiz_mark" - var/mark = null - -/spell/mark_recall/choose_targets() - if(!mark) - return list("magical fairy dust") //because why not - else - return list(mark) - -/spell/mark_recall/cast(var/list/targets,mob/user) - if(!targets.len) - return 0 - var/target = targets[1] - if(istext(target)) - mark = new /obj/effect/cleanable/wizard_mark(get_turf(user),src) - return 1 - if(!istype(target,/obj)) //something went wrong - return 0 - var/turf/T = get_turf(target) - if(!T) - return 0 - user.forceMove(T) - ..() - -/spell/mark_recall/empower_spell() - if(!..()) - return 0 - - spell_flags = NO_SOMATIC - - return "You will always be able to cast this spell, even while unconscious or handcuffed." - -/obj/effect/cleanable/wizard_mark - name = "\improper Mark of the Wizard" - desc = "A strange rune said to be made by wizards. Or its just some shmuck playing with crayons again." - icon = 'icons/obj/rune.dmi' - icon_state = "wizard_mark" - anchored = TRUE - layer = TURF_LAYER - is_spawnable_type = FALSE // invalid without spell passed - var/spell/mark_recall/spell - -/obj/effect/cleanable/wizard_mark/Initialize(mapload,var/mrspell) - . = ..() - spell = mrspell - -/obj/effect/cleanable/wizard_mark/Destroy() - spell.mark = null //dereference pls. - spell = null - return ..() - -/obj/effect/cleanable/wizard_mark/attack_hand(var/mob/user) - if(user != spell.holder) - return ..() - user.visible_message("\The [user] mutters an incantation and \the [src] disappears!") - qdel(src) - return TRUE - -/obj/effect/cleanable/wizard_mark/nullrod_act(mob/user, obj/item/nullrod/rod) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - visible_message("\The [user] dispels \the [src] and it fades away!") - qdel(src) - return TRUE - -/obj/effect/cleanable/wizard_mark/attackby(var/obj/item/I, var/mob/user) - if(istype(I, /obj/item/book/spell)) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - visible_message("\The [src] fades away!") - qdel(src) - return TRUE - return ..() \ No newline at end of file diff --git a/code/modules/spells/general/portal_teleport.dm b/code/modules/spells/general/portal_teleport.dm deleted file mode 100644 index 1f1ed0ad603b..000000000000 --- a/code/modules/spells/general/portal_teleport.dm +++ /dev/null @@ -1,67 +0,0 @@ -/spell/portal_teleport - name = "Create Portal" - desc = "This spell creates a long lasting portal to an area of your selection." - feedback = "TP" - school = "conjuration" - spell_flags = NEEDSCLOTHES - invocation = "Scyar Peranda!" - invocation_type = SpI_SHOUT - charge_max = 30 MINUTES - cooldown_min = 25 MINUTES - - smoke_spread = 1 - smoke_amt = 5 - - var/list/select_areas = list() - - cast_sound = 'sound/effects/teleport.ogg' - - hud_state = "wiz_tele" - -/spell/portal_teleport/before_cast() - return - -/spell/portal_teleport/choose_targets() - var/area/thearea - var/message = alert("Would you like to show station areas?\nNote: it can take up to 5 minutes for the away sites to load in and show up.",, "Yes", "No") - switch(message) - if("Yes") - select_areas = stationlocs - if("No") - select_areas = (stationlocs) ^ (wizportallocs) - - thearea = input("Area to teleport to", "Teleport") as null|anything in select_areas - if(!thearea) return - - return list(select_areas[thearea]) - -/spell/portal_teleport/cast(area/thearea, mob/user) - playsound(get_turf(user),cast_sound,50,1) - var/turf/start = get_turf(user) - var/turf/end = user.try_teleport(thearea) - - if(!end) - to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - - new /obj/effect/portal/wizard(start, end, 35 MINUTES) - new /obj/effect/portal/wizard(end, start, 35 MINUTES) - - return - -/spell/portal_teleport/after_cast() - return - -/spell/portal_teleport/invocation(mob/user, area/chosenarea) - if(!chosenarea || !istype(chosenarea)) - ..() - else - invocation += "[uppertext(chosenarea.proper_name)]" - ..() - return - -/obj/effect/portal/wizard - name = "dark anomaly" - desc = "It pulls on the edges of reality as if trying to draw them in." - icon = 'icons/obj/objects.dmi' - icon_state = "bhole3" diff --git a/code/modules/spells/general/radiant_aura.dm b/code/modules/spells/general/radiant_aura.dm deleted file mode 100644 index 27e85404027e..000000000000 --- a/code/modules/spells/general/radiant_aura.dm +++ /dev/null @@ -1,21 +0,0 @@ -/spell/radiant_aura - name = "Radiant aura" - desc = "Form a protective layer of light around you, making you immune to laser fire." - feedback = "ra" - invocation_type = SpI_EMOTE - invocation = "conjures a sphere of fire around themselves." - school = "conjuration" - spell_flags = NEEDSCLOTHES - charge_max = 300 - cooldown_min = 100 - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 2, Sp_POWER = 0) - cast_sound = 'sound/effects/snap.ogg' - duration = 40 - hud_state = "gen_immolate" - -/spell/radiant_aura/choose_targets() - return list(holder) - -/spell/radiant_aura/cast(var/list/targets, var/mob/user) - var/obj/aura/radiant_aura/A = new(user) - QDEL_IN(A,duration) diff --git a/code/modules/spells/general/return_master.dm b/code/modules/spells/general/return_master.dm deleted file mode 100644 index 119cc93804de..000000000000 --- a/code/modules/spells/general/return_master.dm +++ /dev/null @@ -1,23 +0,0 @@ -/spell/contract/return_master - name = "Return to Master" - desc = "Teleport back to your master" - - school = "conjuration" - charge_max = 600 - spell_flags = 0 - invocation = "none" - invocation_type = SpI_NONE - cooldown_min = 200 - - smoke_spread = 1 - smoke_amt = 5 - - hud_state = "wiz_tele" - - -/spell/contract/return_master/cast(mob/target,mob/user) - target = ..(target,user) - if(!target) - return - - user.forceMove(get_turf(target)) \ No newline at end of file diff --git a/code/modules/spells/general/toggle_armor.dm b/code/modules/spells/general/toggle_armor.dm deleted file mode 100644 index 5da3bf537f83..000000000000 --- a/code/modules/spells/general/toggle_armor.dm +++ /dev/null @@ -1,121 +0,0 @@ -/spell/toggle_armor - name = "Toggle Armor" - spell_flags = 0 - charge_max = 10 - school = "Conjuration" - var/list/armor_pieces - var/equip = 0 - hud_state = "const_shell" - -/spell/toggle_armor/New() - if(armor_pieces) - var/list/nlist = list() - for(var/type in armor_pieces) - var/obj/item/I = new type(null) - nlist[I] = armor_pieces[type] - armor_pieces = nlist - return ..() - -/spell/toggle_armor/proc/drop_piece(var/obj/I) - if(ismob(I.loc)) - var/mob/M = I.loc - M.drop_from_inventory(I) - -/spell/toggle_armor/choose_targets() - return list(holder) - -/spell/toggle_armor/cast(var/list/targets, var/mob/user) - equip = !equip - name = "[initial(name)] ([equip ? "off" : "on"])" - if(equip) - for(var/piece in armor_pieces) - var/slot = armor_pieces[piece] - drop_piece(piece) - user.drop_from_inventory(user.get_equipped_item(slot)) - user.equip_to_slot_if_possible(piece,slot,0,1,1,1) - else - for(var/piece in armor_pieces) - var/obj/item/I = piece - drop_piece(piece) - I.forceMove(null) - -/spell/toggle_armor/greytide_worldwide - name = "Greytide Worldwide" - invocation_type = SpI_EMOTE - invocation = "screams incoherently!" - armor_pieces = list(/obj/item/clothing/jumpsuit/grey = slot_w_uniform_str, - /obj/item/clothing/gloves/insulated/cheap = slot_gloves_str, - /obj/item/clothing/mask/gas = slot_wear_mask_str, - /obj/item/clothing/shoes/color/black = slot_shoes_str, - /obj/item/toolbox/mechanical = BP_R_HAND, - /obj/item/chems/spray/extinguisher = BP_L_HAND) - -/spell/toggle_armor/caretaker - name = "Toggle Armor (Caretaker)" - invocation_type = SpI_EMOTE - invocation = "radiates a holy light" - armor_pieces = list(/obj/item/clothing/head/caretakerhood = slot_head_str, - /obj/item/clothing/suit/caretakercloak = slot_wear_suit_str - ) - hud_state = "caretaker" - -/spell/toggle_armor/champion - name = "Toggle Armor (Champion)" - invocation_type = SpI_EMOTE - invocation = "is covered in golden embers for a moment, before they fade" - armor_pieces = list(/obj/item/clothing/head/champhelm = slot_head_str, - /obj/item/clothing/suit/champarmor = slot_wear_suit_str - ) - hud_state = "champion" - -/spell/toggle_armor/excalibur - name = "Toggle Sword" - invocation_type = SpI_EMOTE - invocation = "thrusts /his hand forward, and it is enveloped in golden embers!" - armor_pieces = list(/obj/item/sword/excalibur = BP_R_HAND) - hud_state = "excalibur" - -/spell/toggle_armor/fiend - name = "Toggle Armor (Fiend)" - invocation_type = SpI_EMOTE - invocation = "snaps /his fingers, and /his clothes begin to shift and change" - armor_pieces = list(/obj/item/clothing/head/fiendhood = slot_head_str, - /obj/item/clothing/suit/fiendcowl = slot_wear_suit_str - ) - hud_state = "fiend" - -/spell/toggle_armor/fiend/fem - armor_pieces = list(/obj/item/clothing/head/fiendhood/fem = slot_head_str, - /obj/item/clothing/suit/fiendcowl/fem = slot_wear_suit_str - ) - -/spell/toggle_armor/infiltrator - name = "Toggle Armor (Infiltrator)" - invocation_type = SpI_EMOTE - invocation = "winks. In an instant, /his clothes change dramatically" - armor_pieces = list(/obj/item/clothing/head/infilhat = slot_head_str, - /obj/item/clothing/suit/infilsuit = slot_wear_suit_str - ) - hud_state = "infiltrator" - -/spell/toggle_armor/infiltrator/fem - armor_pieces = list(/obj/item/clothing/head/infilhat/fem = slot_head_str, - /obj/item/clothing/suit/infilsuit/fem = slot_wear_suit_str - ) - -/spell/toggle_armor/infil_items - name = "Toggle Counterfeit Kit" - invocation_type = SpI_EMOTE - invocation = "flicks /his wrists, one at a time" - armor_pieces = list(/obj/item/stamp/chameleon = BP_L_HAND, - /obj/item/pen/chameleon = BP_R_HAND) - hud_state = "forgery" - -/spell/toggle_armor/overseer - name = "Toggle Armor (Overseer)" - invocation_type = SpI_EMOTE - invocation = " is enveloped in shadows, before /his form begins to shift rapidly" - armor_pieces = list(/obj/item/clothing/head/overseerhood = slot_head_str, - /obj/item/clothing/suit/straight_jacket/overseercloak = slot_wear_suit_str - ) - hud_state = "overseer" \ No newline at end of file diff --git a/code/modules/spells/hand/blood_shards.dm b/code/modules/spells/hand/blood_shards.dm deleted file mode 100644 index 0908ee285201..000000000000 --- a/code/modules/spells/hand/blood_shards.dm +++ /dev/null @@ -1,39 +0,0 @@ -/spell/hand/charges/blood_shard - name = "Blood Shards" - desc = "Invoke a corrupted projectile forward that causes an enemy's blood to fly out in painful shards. Anyone hit by this will have their blood explode out of them in a spray of smaller shards. Stores two charges." - spell_flags = 0 - charge_max = 600 - invocation = "opens their hand, which bursts into vicious red light." - invocation_type = SpI_EMOTE - - range = 7 - max_casts = 2 - compatible_targets = list(/atom) - hud_state = "wiz_bshard" - cast_sound = 'sound/magic/demon_attack1.ogg' - -/spell/hand/charges/blood_shard/cast_hand(var/atom/A,var/mob/user) - var/obj/item/projectile/blood_shard/B = new(get_turf(user)) - B.firer = user - B.launch(A, BP_CHEST) - user.visible_message("\The [user] shoots out a deep red shard from their hand!") - return ..() - -/obj/item/projectile/blood_shard - name = "bloodshard" - damage = 25 - icon_state = "blood" - atom_damage_type = BRUTE - damage_flags = 0 - -/obj/item/projectile/blood_shard/on_hit(var/atom/movable/target, var/blocked = 0) - if(..()) - if(ishuman(target)) - var/mob/living/human/H = target - H.vessel.remove_any(30) - H.visible_message("Tiny red shards burst from \the [H]'s skin!") - fragmentate(get_turf(src), 30, 5, list(/obj/item/projectile/bullet/pellet/blood)) - -/obj/item/projectile/bullet/pellet/blood - name = "blood fragment" - damage = 10 \ No newline at end of file diff --git a/code/modules/spells/hand/burning_grip.dm b/code/modules/spells/hand/burning_grip.dm deleted file mode 100644 index 772b4e6aa9c4..000000000000 --- a/code/modules/spells/hand/burning_grip.dm +++ /dev/null @@ -1,39 +0,0 @@ -/spell/hand/burning_grip - name = "Burning Grip" - desc = "Cause someone to drop a held object by causing it to heat up intensly." - school = "transmutation" - feedback = "bg" - range = 5 - spell_flags = 0 - invocation_type = SpI_NONE - show_message = " throws sparks from their hands" - spell_delay = 120 - hud_state = "wiz_burn" - cast_sound = 'sound/magic/fireball.ogg' - compatible_targets = list(/mob/living/human) - -/spell/hand/burning_grip/valid_target(var/mob/living/L, var/mob/user) - if(!..()) - return 0 - if(length(L.get_held_items())) - return 0 - return 1 - -/spell/hand/burning_grip/cast_hand(var/mob/living/human/H, var/mob/user) - var/list/targets = list() - for(var/hand_slot in H.get_held_item_slots()) - targets |= hand_slot - - var/obj/O = new /obj/effect/temporary(get_turf(H),3, 'icons/effects/effects.dmi', "fire_goon") - O.alpha = 150 - - for(var/organ in targets) - var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(H, organ) - if(!E) - continue - E.take_external_damage(burn=10, used_weapon = "hot iron") - if(E.can_feel_pain()) - E.check_pain_disarm() - else - E.take_external_damage(burn=6, used_weapon = "hot iron") - to_chat(H, SPAN_WARNING("You notice that your [E] is burned.")) diff --git a/code/modules/spells/hand/entangle.dm b/code/modules/spells/hand/entangle.dm deleted file mode 100644 index 37a4776fe28e..000000000000 --- a/code/modules/spells/hand/entangle.dm +++ /dev/null @@ -1,51 +0,0 @@ -/spell/hand/charges/entangle - name = "Entangle" - desc = "This spell creates vines that immediately entangle a nearby victim." - feedback = "ET" - school = "transmutation" - charge_max = 600 - spell_flags = NEEDSCLOTHES | SELECTABLE | IGNOREPREV - invocation = "Bu-Ekel'Inas!" - invocation_type = SpI_SHOUT - range = 3 - max_casts = 1 - - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 2, Sp_POWER = 2) - cooldown_min = 300 - duration = 30 - compatible_targets = list(/mob) - - hud_state = "wiz_entangle" - cast_sound = 'sound/magic/staff_door.ogg' - show_message = " points towards the ground, causing plants to erupt" - var/datum/seed/seed - -/spell/hand/charges/entangle/New() - ..() - seed = new() - seed.set_trait(TRAIT_PLANT_ICON,"flower") - seed.set_trait(TRAIT_PRODUCT_ICON,"flower2") - seed.set_trait(TRAIT_PRODUCT_COLOUR,"#4d4dff") - seed.set_trait(TRAIT_SPREAD,2) - seed.name = "heirlooms" - seed.product_name = "heirloom" - seed.display_name = "vines" - seed.chems = list(/decl/material/liquid/nutriment = list(1,20)) - -/spell/hand/charges/entangle/cast_hand(var/mob/M,var/mob/user) - var/turf/T = get_turf(M) - var/obj/effect/vine/single/P = new(T, seed, null, TRUE) - P.can_buckle = 1 - - P.buckle_mob(M) - M.set_dir(pick(global.cardinal)) - M.visible_message("[P] appear from the floor, spinning around \the [M] tightly!") - return ..() - -/spell/hand/charges/entangle/empower_spell() - if(!..()) - return 0 - - max_casts++ - - return "This spell will now entangle [max_casts] times before running out." \ No newline at end of file diff --git a/code/modules/spells/hand/hand.dm b/code/modules/spells/hand/hand.dm deleted file mode 100644 index 9652cf1cce18..000000000000 --- a/code/modules/spells/hand/hand.dm +++ /dev/null @@ -1,83 +0,0 @@ -/spell/hand - var/min_range = 0 - var/list/compatible_targets = list(/atom) - var/spell_delay = 5 - var/move_delay - var/click_delay - var/hand_state = "spell" - var/obj/item/magic_hand/current_hand - var/show_message - -/spell/hand/choose_targets(mob/user = usr) - return list(user) - -/spell/hand/cast_check(skipcharge = 0,mob/user = usr, var/list/targets) - if(!..()) - return FALSE - if(user.get_active_held_item()) - to_chat(holder, "You need an empty hand to cast this spell.") - return FALSE - return TRUE - -/spell/hand/cast(list/targets, mob/user) - if(current_hand) - cancel_hand() - if(user.get_active_held_item()) - to_chat(user, "You need an empty hand to cast this spell.") - return FALSE - current_hand = new(null, src) - if(!user.put_in_active_hand(current_hand)) - QDEL_NULL(current_hand) - return FALSE - return TRUE - -/spell/hand/proc/cancel_hand() - if(!QDELETED(current_hand)) - QDEL_NULL(current_hand) - -/spell/hand/Destroy() - QDEL_NULL(current_hand) - . = ..() - -/spell/hand/proc/valid_target(var/atom/a,var/mob/user) //we use seperate procs for our target checking for the hand spells. - var/distance = get_dist(a,user) - if((min_range && distance < min_range) || (range && distance > range)) - return FALSE - if(!is_type_in_list(a,compatible_targets)) - return FALSE - return TRUE - -/spell/hand/proc/cast_hand(var/atom/a,var/mob/user) //same for casting. - return TRUE - -/spell/hand/charges - var/casts = 1 - var/max_casts = 1 - -/spell/hand/charges/cast(list/targets, mob/user) - . = ..() - if(.) - casts = max_casts - to_chat(user, "You ready the [name] spell ([casts]/[casts] charges).") - -/spell/hand/charges/cast_hand() - if(..()) - casts-- - to_chat(holder, SPAN_NOTICE("The [name] spell has [casts] out of [max_casts] charges left.")) - cancel_hand() - return TRUE - return FALSE - -/spell/hand/duration - var/hand_timer = null - var/hand_duration = 0 - -/spell/hand/duration/cast(var/list/targets, var/mob/user) - . = ..() - if(.) - hand_timer = addtimer(CALLBACK(src, PROC_REF(cancel_hand)), hand_duration, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_NO_HASH_WAIT|TIMER_OVERRIDE) - -/spell/hand/duration/cancel_hand() - deltimer(hand_timer) - hand_timer = null - ..() \ No newline at end of file diff --git a/code/modules/spells/hand/hand_item.dm b/code/modules/spells/hand/hand_item.dm deleted file mode 100644 index 6141577f2471..000000000000 --- a/code/modules/spells/hand/hand_item.dm +++ /dev/null @@ -1,68 +0,0 @@ -/*much like grab this item is used primarily for the utility it provides. -Basically: I can use it to target things where I click. I can then pass these targets to a spell and target things not using a list. -*/ - -/obj/item/magic_hand - name = "Magic Hand" - icon = 'icons/mob/screen/spells.dmi' - atom_flags = 0 - item_flags = 0 - obj_flags = 0 - simulated = 0 - icon_state = "spell" - max_health = ITEM_HEALTH_NO_DAMAGE - is_spawnable_type = FALSE - obj_flags = OBJ_FLAG_NO_STORAGE - var/next_spell_time = 0 - var/spell/hand/hand_spell - -/obj/item/magic_hand/Initialize(ml, _hand_spell) - . = ..() - hand_spell = _hand_spell - name = "[name] ([hand_spell.name])" - icon_state = hand_spell.hand_state - -// These return values do not look correct... -/obj/item/magic_hand/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - if(hand_spell && hand_spell.valid_target(target, user)) - fire_spell(target, user) - return FALSE - return TRUE - -/obj/item/magic_hand/proc/fire_spell(var/atom/A, mob/living/user) - if(!hand_spell) //no spell? Die. - user.drop_from_inventory(src) - - if(!hand_spell.valid_target(A,user)) - return - if(world.time < next_spell_time) - to_chat(user, "The spell isn't ready yet!") - return - if(user.check_intent(I_FLAG_HELP)) - to_chat(user, "You decide against casting this spell as your intent is set to help.") - return - - if(hand_spell.show_message) - user.visible_message("\The [user][hand_spell.show_message]") - if(hand_spell.cast_hand(A,user)) - next_spell_time = world.time + hand_spell.spell_delay - if(hand_spell.move_delay) - user.ExtraMoveCooldown(hand_spell.move_delay) - if(hand_spell.click_delay) - user.setClickCooldown(hand_spell.move_delay) - -/obj/item/magic_hand/afterattack(var/atom/A, var/mob/user, var/proximity) - if(hand_spell) - fire_spell(A,user) - -/obj/item/magic_hand/throw_at() //no throwing pls - usr.drop_from_inventory(src) - -/obj/item/magic_hand/dropped() //gets deleted on drop - ..() - qdel(src) - -/obj/item/magic_hand/Destroy() //better save than sorry. - hand_spell.current_hand = null - hand_spell = null - . = ..() \ No newline at end of file diff --git a/code/modules/spells/hand/slippery_surface.dm b/code/modules/spells/hand/slippery_surface.dm deleted file mode 100644 index 3b5fc448d034..000000000000 --- a/code/modules/spells/hand/slippery_surface.dm +++ /dev/null @@ -1,19 +0,0 @@ -/spell/hand/slippery_surface - name = "Slippery Surface" - desc = "More of a practical joke than an actual spell." - school = "transmutation" - feedback = "su" - range = 5 - spell_flags = 0 - invocation_type = SpI_NONE - show_message = " snaps their fingers." - spell_delay = 50 - hud_state = "gen_ice" - cast_sound = 'sound/magic/summonitems_generic.ogg' - -/spell/hand/slippery_surface/cast_hand(var/atom/a, var/mob/user) - for(var/turf/T in view(1,a)) - if(T.simulated) - T.wet_floor(50) - new /obj/effect/temporary(T, 3, 'icons/effects/effects.dmi', "sonar_ping") - return ..() diff --git a/code/modules/spells/hand/sunwrath.dm b/code/modules/spells/hand/sunwrath.dm deleted file mode 100644 index 63da9a54c611..000000000000 --- a/code/modules/spells/hand/sunwrath.dm +++ /dev/null @@ -1,32 +0,0 @@ -/spell/hand/duration/sunwrath - name = "Sun God's Wrath" - desc = "Your hands become a gateway of fire, shooting hot plasma from your fingertips." - spell_flags = 0 - charge_max = 600 - invocation_type = SpI_SHOUT - invocation = "Herald! Bless me with your anger!" - show_message = " erupts fire from their hands" - school = "Divine" - hand_duration = 100 - spell_delay = 30 - range = 4 - hud_state = "wiz_immolate" - -/spell/hand/duration/sunwrath/cast_hand(var/atom/A, var/mob/user) - var/turf/T = get_turf(user) - var/list/turfs = getline(T,A) - T - for(var/t in turfs) - var/turf/turf = t - if(turf.density || isspaceturf(turf)) - break - new /obj/effect/fake_fire/sunwrath(t) - return 1 - -/obj/effect/fake_fire/sunwrath - firelevel = 2 - last_temperature = 0 - pressure = 3000 - -/obj/effect/fake_fire/sunwrath/Process() //Override, so we burn mobs only - for(var/mob/living/L in loc) - L.FireBurn(firelevel,last_temperature,pressure) \ No newline at end of file diff --git a/code/modules/spells/no_clothes.dm b/code/modules/spells/no_clothes.dm deleted file mode 100644 index 3b8502923519..000000000000 --- a/code/modules/spells/no_clothes.dm +++ /dev/null @@ -1,5 +0,0 @@ -/spell/noclothes - name = "No Clothes" - desc = "Learn the ancient art of not wearing fancy robes while casting spells." - feedback = "NC" - spell_flags = NO_BUTTON \ No newline at end of file diff --git a/code/modules/spells/racial_wizard.dm b/code/modules/spells/racial_wizard.dm deleted file mode 100644 index ce5c4e651162..000000000000 --- a/code/modules/spells/racial_wizard.dm +++ /dev/null @@ -1,81 +0,0 @@ -//this file is full of all the racial spells/artifacts/etc that each species has. - -/obj/item/magic_rock - name = "magical rock" - desc = "Legends say that this rock will unlock the true potential of anyone who touches it." - icon = 'icons/obj/wizard.dmi' - icon_state = "magic rock" - w_class = ITEM_SIZE_SMALL - throw_speed = 1 - throw_range = 3 - material = /decl/material/solid/stone/basalt - var/list/potentials = list( - SPECIES_HUMAN = /obj/item/bag/cash/infinite - ) - -/obj/item/magic_rock/attack_self(mob/user) - if(!ishuman(user)) - to_chat(user, "\The [src] can do nothing for such a simple being.") - return - var/mob/living/human/H = user - var/reward = potentials[H.species.get_root_species_name(H)] //we get body type because that lets us ignore subspecies. - if(!reward) - to_chat(user, "\The [src] does not know what to make of you.") - return - for(var/spell/S in user.mind.learned_spells) - if(istype(S,reward)) - to_chat(user, "\The [src] can do no more for you.") - return - var/a = new reward() - if(ispath(reward,/spell)) - H.add_spell(a) - else if(ispath(reward,/obj)) - H.put_in_hands(a) - to_chat(user, "\The [src] crumbles in your hands.") - qdel(src) - -/obj/item/bag/cash/infinite - storage = /datum/storage/bag/cash/infinite - -/obj/item/bag/cash/infinite/WillContain() - return list(/obj/item/cash/c1000) - -/spell/messa_shroud/choose_targets() - return list(get_turf(holder)) - -/spell/messa_shroud/cast(var/list/targets, mob/user) - var/turf/T = targets[1] - - if(!istype(T)) - return - - var/obj/O = new /obj(T) - O.set_light(range, -10, "#ffffff") - - spawn(duration) - qdel(O) - -/mob/observer/eye/freelook/wizard_eye - name_sufix = "Wizard Eye" - -/mob/observer/eye/freelook/wizard_eye/Initialize() - . = ..() //we dont use the Ai one because it has AI specific procs imbedded in it. - visualnet = cameranet - -/mob/living/proc/release_eye() - set name = "Release Vision" - set desc = "Return your sight to your body." - set category = "Abilities" - - verbs -= /mob/living/proc/release_eye //regardless of if we have an eye or not we want to get rid of this verb. - - if(!eyeobj) - return - eyeobj.release(src) - -/mob/observer/eye/freelook/wizard_eye/Destroy() - if(isliving(eyeobj.owner)) - var/mob/living/L = eyeobj.owner - L.release_eye() - qdel(eyeobj) - return ..() \ No newline at end of file diff --git a/code/modules/spells/spell_code.dm b/code/modules/spells/spell_code.dm index 653d2b8cae2f..bfcf1f4d8f6c 100644 --- a/code/modules/spells/spell_code.dm +++ b/code/modules/spells/spell_code.dm @@ -26,7 +26,7 @@ var/global/list/spells = typesof(/spell) //needed for the badmin verb for now var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used - var/spell_flags = NEEDSCLOTHES + var/spell_flags = 0 var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell var/invocation_type = SpI_NONE //can be none, whisper, shout, and emote var/range = 7 //the range of the spell; outer radius for aoe spells @@ -241,21 +241,14 @@ var/global/list/spells = typesof(/spell) //needed for the badmin verb for now to_chat(SA, "The null sceptre's power interferes with your own!") return 0 - if(!(spell_flags & GHOSTCAST)) - if(!(spell_flags & NO_SOMATIC)) - var/mob/living/L = user - if(L.incapacitated(INCAPACITATION_STUNNED|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_FORCELYING|INCAPACITATION_KNOCKOUT)) - to_chat(user, "You can't cast spells while incapacitated!") - return 0 - - if(ishuman(user) && !(invocation_type in list(SpI_EMOTE, SpI_NONE)) && user.get_item_blocking_speech()) - to_chat(user, "Mmmf mrrfff!") - return 0 + var/mob/living/L = user + if(L.incapacitated(INCAPACITATION_STUNNED|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_FORCELYING|INCAPACITATION_KNOCKOUT)) + to_chat(user, "You can't cast spells while incapacitated!") + return 0 - var/spell/noclothes/spell = locate() in user.mind.learned_spells - if((spell_flags & NEEDSCLOTHES) && !(spell && istype(spell)))//clothes check - if(!user.wearing_wiz_garb()) - return 0 + if(ishuman(user) && !(invocation_type in list(SpI_EMOTE, SpI_NONE)) && user.get_item_blocking_speech()) + to_chat(user, "Mmmf mrrfff!") + return 0 return 1 @@ -381,10 +374,7 @@ var/global/list/spells = typesof(/spell) //needed for the badmin verb for now if(!user || isnull(user)) return 0 - var/incap_flags = INCAPACITATION_STUNNED|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_FORCELYING - if(!(spell_flags & (GHOSTCAST))) - incap_flags |= INCAPACITATION_KNOCKOUT - + var/incap_flags = INCAPACITATION_STUNNED|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_FORCELYING|INCAPACITATION_KNOCKOUT return do_after(user,delay, incapacitation_flags = incap_flags) /proc/view_or_range(distance = world.view , center = usr , type) diff --git a/code/modules/spells/spell_projectile.dm b/code/modules/spells/spell_projectile.dm deleted file mode 100644 index 1599a59d3a78..000000000000 --- a/code/modules/spells/spell_projectile.dm +++ /dev/null @@ -1,56 +0,0 @@ -/obj/item/projectile/spell_projectile - name = "spell" - icon = 'icons/obj/projectiles.dmi' - - nodamage = 1 //Most of the time, anyways - - var/spell/targeted/projectile/carried - - penetrating = 0 - life_span = 10 //set by the duration of the spell - - var/proj_trail = 0 //if it leaves a trail - var/proj_trail_lifespan = 0 //deciseconds - var/proj_trail_icon = 'icons/obj/wizard.dmi' - var/proj_trail_icon_state = "trail" - var/list/trails = new() - -/obj/item/projectile/spell_projectile/Destroy() - for(var/trail in trails) - qdel(trail) - carried = null - return ..() - -/obj/item/projectile/spell_projectile/explosion_act() - SHOULD_CALL_PARENT(FALSE) - return - -/obj/item/projectile/spell_projectile/before_move() - if(proj_trail && src && src.loc) //pretty trails - var/obj/effect/overlay/trail = new /obj/effect/overlay(loc) - trails += trail - trail.icon = proj_trail_icon - trail.icon_state = proj_trail_icon_state - trail.set_density(0) - spawn(proj_trail_lifespan) - trails -= trail - qdel(trail) - -/obj/item/projectile/spell_projectile/proc/prox_cast(var/list/targets) - if(loc) - carried.prox_cast(targets, src) - qdel(src) - return - -/obj/item/projectile/spell_projectile/Bump(var/atom/A, forced=0) - if(loc && carried) - prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src)) - return 1 - -/obj/item/projectile/spell_projectile/on_impact() - if(loc && carried) - prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src)) - return 1 - -/obj/item/projectile/spell_projectile/seeking - name = "seeking spell" diff --git a/code/modules/spells/spellbook.dm b/code/modules/spells/spellbook.dm deleted file mode 100644 index fa43c3e8d63f..000000000000 --- a/code/modules/spells/spellbook.dm +++ /dev/null @@ -1,322 +0,0 @@ -#define NOREVERT 1 -#define LOCKED 2 -#define CAN_MAKE_CONTRACTS 4 -#define INVESTABLE 8 -#define NO_LOCKING 16 - -//spells/spellbooks have a variable for this but as artefacts are literal items they do not. -//so we do this instead. -var/global/list/artefact_feedback = list( - /obj/structure/closet/wizard/armor = "HS", - /obj/item/gun/energy/staff/focus = "MF", - /obj/item/gun/energy/staff/fire = "FS", - /obj/item/summoning_stone = "ST", - /obj/item/magic_rock = "RA", - /obj/item/contract/apprentice = "CP", - /obj/structure/closet/wizard/scrying = "SO", - /obj/item/paper/scroll/teleportation = "TS", - /obj/item/gun/energy/staff = "ST", - /obj/item/gun/energy/staff/animate = "SA", - /obj/item/dice/d20/cursed = "DW" -) - -/obj/item/book/spell - name = "master spell book" - desc = "The legendary book of spells of the wizard." - throw_speed = 1 - throw_range = 5 - w_class = ITEM_SIZE_NORMAL - material = /decl/material/solid/organic/paper - matter = list(/decl/material/solid/organic/leather = MATTER_AMOUNT_REINFORCEMENT) - unique = TRUE - var/uses = 1 - var/temp = null - var/datum/spellbook/spellbook - var/spellbook_type = /datum/spellbook/ //for spawning specific spellbooks. - var/investing_time = 0 //what time we target forr a return on our spell investment. - var/has_sacrificed = 0 //whether we have already got our sacrifice bonus for the current investment. - -/obj/item/book/spell/Initialize() - . = ..() - set_spellbook(spellbook_type) - -/obj/item/book/spell/try_carve() - return FALSE - -/obj/item/book/spell/proc/set_spellbook(var/type) - if(spellbook) - qdel(spellbook) - spellbook = new type() - uses = spellbook.max_uses - name = spellbook.name - desc = spellbook.desc - -/obj/item/book/spell/attack_self(mob/user) - if(!user.mind) - return - if (user.mind.assigned_special_role != /decl/special_role/wizard) - if (user.mind.assigned_special_role != "Wizard's Apprentice") - to_chat(user, "You can't make heads or tails of this book.") - return - if (spellbook.book_flags & LOCKED) - to_chat(user, "Drat! This spellbook's apprentice-proof lock is on!") - return - else if (spellbook.book_flags & LOCKED) - to_chat(user, "You notice the apprentice-proof lock is on. Luckily you are beyond such things.") - interact(user) - -/obj/item/book/spell/proc/make_sacrifice(obj/item/I, mob/user, var/reagent) - if(has_sacrificed) - to_chat(user, SPAN_WARNING("\The [src] is already sated! Wait for a return on your investment before you sacrifice more to it.")) - return - if(reagent) - if(I.reagents?.has_reagent(reagent, 5)) - I.remove_from_reagents(reagent, 5) - else if(LAZYACCESS(I.matter, reagent) >= (SHEET_MATERIAL_AMOUNT * 5)) - qdel(I) - else - if(istype(I,/obj/item/stack)) - var/obj/item/stack/S = I - if(S.amount < S.max_amount) - to_chat(usr, "You must sacrifice [S.max_amount] stacks of [S]!") - return - qdel(I) - to_chat(user, "Your sacrifice was accepted!") - has_sacrificed = 1 - investing_time = max(investing_time - 6000,1) //subtract 10 minutes. Make sure it doesn't act funky at the beginning of the game. - - -/obj/item/book/spell/attackby(obj/item/I, mob/user) - if(investing_time) - for(var/type in spellbook.sacrifice_objects) - if(istype(I,type)) - make_sacrifice(I, user) - return TRUE - - for(var/mat in spellbook.sacrifice_materials) - if(LAZYACCESS(I.matter, mat) > (SHEET_MATERIAL_AMOUNT * 10)) - make_sacrifice(I, user, mat) - return TRUE - - if(I.reagents) - for(var/id in spellbook.sacrifice_reagents) - if(I.reagents.has_reagent(id, 5)) - make_sacrifice(I, user, id) - return TRUE - return ..() - -/obj/item/book/spell/interact(mob/user) - var/dat = null - if(temp) - dat = "[temp]
    Return" - else - dat = "

    [spellbook.title]

    [spellbook.title_desc]
    You have [uses] spell slot\s left.

    " - dat += "
    Requires Wizard Garb
    Selectable Target
    Spell Charge Type: Recharge, Sacrifice, Charges

    " - dat += "
    To use a contract, first bind it to your soul, then give it to someone to sign. This will bind their soul to you.

    " - for(var/i in 1 to spellbook.spells.len) - var/name = "" //name of target - var/desc = "" //description of target - var/info = "" //additional information - if(ispath(spellbook.spells[i],/datum/spellbook)) - var/datum/spellbook/S = spellbook.spells[i] - name = initial(S.name) - desc = initial(S.book_desc) - info = "[initial(S.max_uses)] Spell Slots" - else if(ispath(spellbook.spells[i],/obj)) - var/obj/O = spellbook.spells[i] - name = "Artefact: [capitalize(initial(O.name))]" //because 99.99% of objects dont have capitals in them and it makes it look weird. - desc = initial(O.desc) - else if(ispath(spellbook.spells[i],/spell)) - var/spell/S = spellbook.spells[i] - name = initial(S.name) - desc = initial(S.desc) - var/testing = initial(S.spell_flags) - if(testing & NEEDSCLOTHES) - info = "W" - var/type = "" - switch(initial(S.charge_type)) - if(Sp_RECHARGE) - type = "R" - if(Sp_HOLDVAR) - type = "S" - if(Sp_CHARGES) - type = "C" - info += "[type]" - dat += "[name]" - if(length(info)) - dat += " ([info])" - dat += " ([spellbook.spells[spellbook.spells[i]]] spell slot[spellbook.spells[spellbook.spells[i]] > 1 ? "s" : "" ])" - if(spellbook.book_flags & CAN_MAKE_CONTRACTS) - dat += " Make Contract" - dat += "
    [desc]

    " - dat += "
    " - dat += "
    Re-memorise your spellbook.
    " - if(spellbook.book_flags & INVESTABLE) - if(investing_time) - dat += "
    Currently investing in a slot...
    " - else - dat += "
    Invest a Spell Slot
    Investing a spellpoint will return two spellpoints back in 15 minutes.
    Some say a sacrifice could even shorten the time...
    " - if(!(spellbook.book_flags & NOREVERT)) - dat += "
    Choose different spellbook.
    " - if(!(spellbook.book_flags & NO_LOCKING)) - dat += "
    [spellbook.book_flags & LOCKED ? "Unlock" : "Lock"] the spellbook.
    " - show_browser(user, dat, "window=spellbook") - -/obj/item/book/spell/CanUseTopic(var/mob/living/human/H) - if(!istype(H)) - return STATUS_CLOSE - - if(H.mind && (spellbook.book_flags & LOCKED) && H.mind.assigned_special_role == "Wizard's Apprentice") //make sure no scrubs get behind the lock - return STATUS_CLOSE - - return ..() - -/obj/item/book/spell/OnTopic(var/mob/living/human/user, href_list) - if(href_list["lock"] && !(spellbook.book_flags & NO_LOCKING)) - if(spellbook.book_flags & LOCKED) - spellbook.book_flags &= ~LOCKED - else - spellbook.book_flags |= LOCKED - . = TOPIC_REFRESH - - else if(href_list["temp"]) - temp = null - . = TOPIC_REFRESH - - else if(href_list["book"]) - if(initial(spellbook.max_uses) != spellbook.max_uses || uses != spellbook.max_uses) - temp = "You've already purchased things using this spellbook!" - else - src.set_spellbook(/datum/spellbook) - temp = "You have reverted back to the Book of Tomes." - . = TOPIC_REFRESH - - else if(href_list["invest"]) - temp = invest() - . = TOPIC_REFRESH - - else if(href_list["path"]) - var/path = locate(href_list["path"]) in spellbook.spells - if(!path) - return TOPIC_HANDLED - if(uses < spellbook.spells[path]) - to_chat(user, "You do not have enough spell slots to purchase this.") - return TOPIC_HANDLED - send_feedback(path) //feedback stuff - if(ispath(path,/datum/spellbook)) - src.set_spellbook(path) - temp = "You have chosen a new spellbook." - else - if(href_list["contract"]) - if(!(spellbook.book_flags & CAN_MAKE_CONTRACTS)) - return //no - uses -= spellbook.spells[path] - spellbook.max_uses -= spellbook.spells[path] //no basksies - var/obj/O = new /obj/item/contract/boon(get_turf(user),path) - temp = "You have purchased \the [O]." - else - if(ispath(path,/spell)) - temp = src.add_spell(user,path) - if(temp) - uses -= spellbook.spells[path] - else - var/obj/O = new path(get_turf(user)) - temp = "You have purchased \a [O]." - uses -= spellbook.spells[path] - spellbook.max_uses -= spellbook.spells[path] - //finally give it a bit of an oomf - playsound(get_turf(user),'sound/effects/phasein.ogg',50,1) - . = TOPIC_REFRESH - - else if(href_list["reset"] && !(spellbook.book_flags & NOREVERT)) - var/area/map_template/wizard_station/A = get_area(user) - if(istype(A)) - uses = spellbook.max_uses - investing_time = 0 - has_sacrificed = 0 - user.spellremove() - temp = "All spells and investments have been removed. You may now memorise a new set of spells." - SSstatistics.add_field_details("wizard_spell_learned","UM") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells - else - to_chat(user, "You must be in the wizard academy to re-memorise your spells.") - . = TOPIC_REFRESH - - src.interact(user) - -/obj/item/book/spell/proc/invest() - if(uses < 1) - return "You don't have enough slots to invest!" - if(investing_time) - return "You can only invest one spell slot at a time." - uses-- - START_PROCESSING(SSobj, src) - investing_time = world.time + (15 MINUTES) - return "You invest a spellslot and will recieve two in return in 15 minutes." - -/obj/item/book/spell/Process() - if(investing_time && investing_time <= world.time) - src.visible_message("\The [src] emits a soft chime.") - uses += 2 - if(uses > spellbook.max_uses) - spellbook.max_uses = uses - investing_time = 0 - has_sacrificed = 0 - STOP_PROCESSING(SSobj, src) - return 1 - -/obj/item/book/spell/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/book/spell/proc/send_feedback(var/path) - if(ispath(path,/datum/spellbook)) - var/datum/spellbook/S = path - SSstatistics.add_field_details("wizard_spell_learned","[initial(S.feedback)]") - else if(ispath(path,/spell)) - var/spell/S = path - SSstatistics.add_field_details("wizard_spell_learned","[initial(S.feedback)]") - else if(ispath(path,/obj)) - SSstatistics.add_field_details("wizard_spell_learned","[artefact_feedback[path]]") - - -/obj/item/book/spell/proc/add_spell(var/mob/user, var/spell_path) - for(var/spell/S in user.mind.learned_spells) - if(istype(S,spell_path)) - if(!S.can_improve()) - return - if(S.can_improve(Sp_SPEED) && S.can_improve(Sp_POWER)) - switch(alert(user, "Do you want to upgrade this spell's speed or power?", "Spell upgrade", "Speed", "Power", "Cancel")) - if("Speed") - return S.quicken_spell() - if("Power") - return S.empower_spell() - else - return - else if(S.can_improve(Sp_POWER)) - return S.empower_spell() - else if(S.can_improve(Sp_SPEED)) - return S.quicken_spell() - - var/spell/S = new spell_path() - user.add_spell(S) - return "You learn the spell [S]" - -/datum/spellbook - var/name = "\improper Book of Tomes" - var/desc = "The legendary book of spells of the wizard." - var/book_desc = "Holds information on the various tomes available to a wizard" - var/feedback = "" //doesn't need one. - var/book_flags = NOREVERT - var/max_uses = 1 - var/title = "Book of Tomes" - var/title_desc = "This tome marks down all the available tomes for use. Choose wisely, there are no refunds." - var/list/spells = list(/datum/spellbook/standard = 1, - /datum/spellbook/cleric = 1, - /datum/spellbook/battlemage = 1, - /datum/spellbook/spatial = 1, - /datum/spellbook/druid = 1 - ) //spell's path = cost of spell - - var/list/sacrifice_reagents - var/list/sacrifice_objects - var/list/sacrifice_materials diff --git a/code/modules/spells/spellbook/battlemage.dm b/code/modules/spells/spellbook/battlemage.dm deleted file mode 100644 index 07cebf63d820..000000000000 --- a/code/modules/spells/spellbook/battlemage.dm +++ /dev/null @@ -1,44 +0,0 @@ -//Battlemage is all about mixing physical with the mystical in head to head combat. -//Things like utility and mobility come second. -/datum/spellbook/battlemage - name = "\improper Battlemage's Bible" - feedback = "BM" - desc = "Smells like blood." - book_desc = "Mix physical with the mystical in head to head combat." - title = "The Art of Magical Combat" - title_desc = "Buy spells using your available spell slots. Artefacts may also be bought however their cost is permanent." - book_flags = CAN_MAKE_CONTRACTS|INVESTABLE - max_uses = 6 - - spells = list( - /spell/targeted/projectile/dumbfire/passage = 1, - /spell/targeted/equip_item/dyrnwyn = 1, - /spell/targeted/equip_item/shield = 1, - /spell/targeted/projectile/dumbfire/fireball = 1, - /spell/targeted/torment = 1, - /spell/targeted/heal_target = 2, - /spell/aoe_turf/conjure/mirage = 1, - /spell/targeted/shapeshift/corrupt_form = 1, - /spell/radiant_aura = 1, - /spell/noclothes = 1, - /obj/structure/closet/wizard/armor = 1, - /obj/item/gun/energy/staff/focus = 1, - /obj/item/gun/energy/staff/fire = 1, - /obj/item/dice/d20/cursed = 1, - /obj/item/summoning_stone = 2, - /obj/item/magic_rock = 1, - /obj/item/contract/wizard/xray = 1, - /obj/item/contract/wizard/telepathy = 1, - /obj/item/contract/apprentice = 1 - ) - - sacrifice_objects = list( - /obj/item/sword, - /obj/item/bladed/axe/fire, - /obj/item/baton, - /obj/item/knife/ritual, - /obj/item/knife/kitchen/cleaver, - /obj/item/knife/folding/combat/balisong, - /obj/item/knife/folding/tacticool, - /obj/item/star - ) diff --git a/code/modules/spells/spellbook/cleric.dm b/code/modules/spells/spellbook/cleric.dm deleted file mode 100644 index 98c5a48f6d52..000000000000 --- a/code/modules/spells/spellbook/cleric.dm +++ /dev/null @@ -1,50 +0,0 @@ -//Cleric is all about healing. Mobility and offense comes at a higher price but not impossible. -/obj/item/book/spell/cleric - spellbook_type = /datum/spellbook/cleric - -/datum/spellbook/cleric - name = "\improper Cleric's Tome" - feedback = "CR" - desc = "For those who do not harm, or at least feel sorry about it." - book_desc = "All about healing. Mobility and offense comes at a higher price but not impossible." - title = "Cleric's Tome of Healing" - title_desc = "Buy spells using your available spell slots. Artefacts may also be bought however their cost is permanent." - book_flags = CAN_MAKE_CONTRACTS|INVESTABLE - max_uses = 7 - - spells = list( - /spell/targeted/heal_target/major = 1, - /spell/targeted/heal_target/area = 1, - /spell/targeted/heal_target/sacrifice = 1, - /spell/targeted/genetic/blind = 1, - /spell/targeted/shapeshift/baleful_polymorph = 1, - /spell/targeted/projectile/dumbfire/stuncuff = 1, - /spell/targeted/ethereal_jaunt = 2, - /spell/aoe_turf/knock = 1, - /spell/radiant_aura = 1, - /spell/targeted/equip_item/holy_relic = 1, - /spell/aoe_turf/conjure/grove/sanctuary = 1, - /spell/targeted/projectile/dumbfire/fireball = 2, - /spell/area_teleport = 2, - /spell/portal_teleport = 2, - /spell/aoe_turf/conjure/forcewall = 1, - /spell/noclothes = 1, - /obj/item/magic_rock = 1, - /obj/structure/closet/wizard/scrying = 2, - /obj/item/summoning_stone = 2, - /obj/item/contract/wizard/telepathy = 1, - /obj/item/contract/apprentice = 1 - ) - - sacrifice_reagents = list( - /decl/material/liquid/adminordrazine - ) - sacrifice_objects = list( - /obj/item/stack/nanopaste, - /obj/item/scanner/health, - /obj/item/scanner/breath, - /obj/item/stack/medical/bandage/advanced, - /obj/item/stack/medical/ointment/advanced, - /obj/item/bodybag/rescue, - /obj/item/defibrillator - ) diff --git a/code/modules/spells/spellbook/druid.dm b/code/modules/spells/spellbook/druid.dm deleted file mode 100644 index 48ee015d3499..000000000000 --- a/code/modules/spells/spellbook/druid.dm +++ /dev/null @@ -1,43 +0,0 @@ -//all about the summons, nature, and a bit o' healin. - -/obj/item/book/spell/druid - spellbook_type = /datum/spellbook/druid - -/datum/spellbook/druid - name = "\improper Druid's Leaflet" - feedback = "DL" - desc = "It smells like an air freshener." - book_desc = "Summons, nature, and a bit o' healin." - title = "Druidic Guide on how to be smug about nature" - title_desc = "Buy spells using your available spell slots. Artefacts may also be bought however their cost is permanent." - book_flags = CAN_MAKE_CONTRACTS|INVESTABLE - max_uses = 6 - - spells = list( - /spell/targeted/heal_target = 1, - /spell/targeted/heal_target/sacrifice = 1, - /spell/aoe_turf/conjure/mirage = 1, - /spell/aoe_turf/conjure/summon/bats = 1, - /spell/aoe_turf/conjure/summon/bear = 1, - /spell/targeted/equip_item/party_hardy = 1, - /spell/targeted/equip_item/seed = 1, - /spell/targeted/shapeshift/avian = 1, - /spell/aoe_turf/disable_tech = 1, - /spell/hand/charges/entangle = 1, - /spell/aoe_turf/conjure/grove/sanctuary = 1, - /spell/aoe_turf/knock = 1, - /spell/area_teleport = 2, - /spell/portal_teleport = 2, - /spell/noclothes = 1, - /obj/item/magic_rock = 1, - /obj/item/summoning_stone = 2, - /obj/item/contract/wizard/telepathy = 1, - /obj/item/contract/apprentice = 1 - ) - sacrifice_objects = list( - /obj/item/seeds, - /obj/item/wirecutters/clippers, - /obj/item/scanner/plant, - /obj/item/tool/axe/hatchet, - /obj/item/tool/hoe/mini - ) diff --git a/code/modules/spells/spellbook/spatial.dm b/code/modules/spells/spellbook/spatial.dm deleted file mode 100644 index def9a6695d25..000000000000 --- a/code/modules/spells/spellbook/spatial.dm +++ /dev/null @@ -1,48 +0,0 @@ -//all about moving around and mobility and being an annoying shit. - -/obj/item/book/spell/spatial - spellbook_type = /datum/spellbook/spatial - -/datum/spellbook/spatial - name = "\improper Spatial Manual" - feedback = "SP" - desc = "You feel like this might disappear from out of under you." - book_desc = "Movement and teleportation. Run from your problems!" - title = "Manual of Spatial Transportation" - title_desc = "Buy spells using your available spell slots. Artefacts may also be bought however their cost is permanent." - book_flags = CAN_MAKE_CONTRACTS|INVESTABLE - max_uses = 11 - - spells = list( - /spell/targeted/ethereal_jaunt = 1, - /spell/aoe_turf/blink = 1, - /spell/area_teleport = 1, - /spell/portal_teleport = 1, - /spell/targeted/projectile/dumbfire/passage = 1, - /spell/mark_recall = 1, - /spell/targeted/swap = 1, - /spell/targeted/shapeshift/avian = 1, - /spell/targeted/projectile/magic_missile = 1, - /spell/targeted/heal_target = 1, - /spell/aoe_turf/conjure/forcewall = 1, - /spell/aoe_turf/smoke = 1, - /spell/aoe_turf/conjure/summon/bats = 3, - /spell/noclothes = 1, - /obj/item/dice/d20/cursed = 1, - /obj/structure/closet/wizard/scrying = 2, - /obj/item/paper/scroll/teleportation = 1, - /obj/item/magic_rock = 1, - /obj/item/summoning_stone = 3, - /obj/item/contract/wizard/telepathy = 1, - /obj/item/contract/apprentice = 1 - ) - - sacrifice_reagents = list( - /decl/material/liquid/amphetamines - ) - sacrifice_objects = list( - /obj/item/stack/telecrystal - ) - sacrifice_materials = list( - /decl/material/solid/gemstone/diamond - ) \ No newline at end of file diff --git a/code/modules/spells/spellbook/standard.dm b/code/modules/spells/spellbook/standard.dm deleted file mode 100644 index 99e8f27c2740..000000000000 --- a/code/modules/spells/spellbook/standard.dm +++ /dev/null @@ -1,54 +0,0 @@ -//the spellbook we know and love. Well, the one we know, at least. - -/obj/item/book/spell/standard - spellbook_type = /datum/spellbook/standard - -/datum/spellbook/standard - name = "\improper Standard Spellbook" - feedback = "SB" - title = "Book of Spells and Artefacts" - title_desc = "Buy spells using your available spell slots. Artefacts may also be bought however their cost is permanent." - book_desc = "A general wizard's spellbook. All its spells are easy to use but hard to master." - book_flags = CAN_MAKE_CONTRACTS|INVESTABLE - max_uses = 6 - - spells = list( - /spell/targeted/projectile/magic_missile = 1, - /spell/targeted/projectile/dumbfire/fireball = 1, - /spell/aoe_turf/disable_tech = 1, - /spell/aoe_turf/smoke = 1, - /spell/targeted/genetic/blind = 1, - /spell/targeted/subjugation = 1, - /spell/aoe_turf/conjure/forcewall = 1, - /spell/aoe_turf/blink = 1, - /spell/area_teleport = 1, - /spell/targeted/ethereal_jaunt = 1, - /spell/targeted/heal_target = 1, - /spell/aoe_turf/knock = 1, - /spell/noclothes = 2, - /obj/item/gun/energy/staff/focus = 1, - /obj/item/gun/energy/staff/fire = 1, - /obj/item/gun/energy/staff/animate = 1, - /obj/structure/closet/wizard/scrying = 1, - /obj/item/summoning_stone = 2, - /obj/item/magic_rock = 1, - /obj/item/contract/wizard/telepathy = 1, - /obj/item/contract/apprentice = 1 - ) - - sacrifice_objects = list( - /obj/item/toolbox, - /obj/item/cane/fancy, - /obj/item/flamethrower, - /obj/item/plastique, - /obj/item/dice, - /obj/item/soap, - /obj/item/flame/candle, - /obj/item/flame/candle/scented/incense, - /obj/item/caution, - /obj/item/towel, - /obj/item/tank/jetpack, - /obj/item/plunger, - /obj/item/megaphone, - /obj/item/deck/cards - ) \ No newline at end of file diff --git a/code/modules/spells/spellbook/student.dm b/code/modules/spells/spellbook/student.dm deleted file mode 100644 index 1c07a5135b13..000000000000 --- a/code/modules/spells/spellbook/student.dm +++ /dev/null @@ -1,28 +0,0 @@ -//wizard's training wheels. Basically. Same shit as in the general one. - -/obj/item/book/spell/student - spellbook_type = /datum/spellbook/student - -/datum/spellbook/student - name = "\improper Student's Spellbook" - feedback = "ST" - desc = "This spell book has a sticker on it that says, 'certified for children 5 and older'." - book_desc = "This spellbook is dedicated to teaching neophytes in the ways of magic." - title = "Book of Spells and Education" - title_desc = "Hello. Congratulations on becoming a wizard. You may be asking yourself: What? A wizard? Already? Of course! Anybody can become a wizard! Learning to be a good one is the hard part.
    Without further adue, let us begin by learning the three concepts of wizardry, 'Spell slots', 'Spells', and 'Artifacts'.
    Firstly lets try to understand the 'spell slot'. A spell slot is the measurable amount of spells and artifacts one tome can give. Most spells will only take up a singular spell slot, however more powerful spells/artifacts can take up more.
    Spells are spells. They can have requirements, such as wizard garb, and most can be upgraded by purchasing additional spell slots for them. Most upgrades fall into two categories, 'Speed' and 'Power'. Speed upgrades decrease the time you have to spend recharging your spell. Power increases the potency of your spells. Spells are also special in that they can be refunded while inside the Wizard Acadamy, so if you want to test a spell out before moving out into the field, feel free to do that in the comfort of our home.
    Artifacts, or 'Artefacts' as we call them, are powerful wizard tools or items made specially for wizards everywhere. Extremely potent, they cannot be refunded like spells, and some of them can be used by non-wizards, so be careful!
    Knowing these three concepts puts you in a league above most wizards, however knowledge of spells is just as important so we've included a list of spells below made specifically for the beginning wizard. Take all of them, or mix and match, remember being creative is half of being a wizard!" - book_flags = CAN_MAKE_CONTRACTS|INVESTABLE - max_uses = 5 - - spells = list( - /spell/aoe_turf/knock = 1, - /spell/targeted/ethereal_jaunt = 1, - /spell/targeted/projectile/magic_missile = 1, - /obj/item/gun/energy/staff/focus = 1, - /obj/item/contract/wizard/xray = 1 - ) - -/datum/spellbook/student/apprentice - book_flags = CAN_MAKE_CONTRACTS|INVESTABLE|NOREVERT|NO_LOCKING - -/obj/item/book/spell/apprentice - spellbook_type = /datum/spellbook/student/apprentice diff --git a/code/modules/spells/targeted/analyze.dm b/code/modules/spells/targeted/analyze.dm deleted file mode 100644 index 705d97399c70..000000000000 --- a/code/modules/spells/targeted/analyze.dm +++ /dev/null @@ -1,19 +0,0 @@ -/spell/targeted/analyze - name = "Analyze" - desc = "Using your wizardly powers, you can detect the inner destructions of a persons body." - - feedback = "AZ" - school = "illusion" - charge_max = 100 - spell_flags = INCLUDEUSER|SELECTABLE - range = 2 - invocation_type = SpI_WHISPER - invocation = "Fu Yi Fim" - compatible_mobs = list(/mob/living/human) - hud_state = "analyze" - -/spell/targeted/analyze/cast(var/list/targets, var/mob/user) - for(var/a in targets) - var/mob/living/human/H = a - new /obj/effect/temporary(get_turf(a),5, 'icons/effects/effects.dmi', "repel_missiles") - to_chat(user,medical_scan_results(H,1)) \ No newline at end of file diff --git a/code/modules/spells/targeted/blood_boil.dm b/code/modules/spells/targeted/blood_boil.dm deleted file mode 100644 index 4c39b3b4620b..000000000000 --- a/code/modules/spells/targeted/blood_boil.dm +++ /dev/null @@ -1,25 +0,0 @@ -/spell/targeted/blood_boil - name = "Blood Boil" - desc = "Allow you to concentrate so deeply on a target that their body temperature increases, eventually setting them on fire." - feedback = "BO" - school = "transmutation" - charge_max = 300 - spell_flags = 0 - invocation_type = SpI_NONE - range = 5 - max_targets = 1 - compatible_mobs = list(/mob/living/human) - - time_between_channels = 50 - number_of_channels = 0 - - hud_state = "wiz_boilblood" - -/spell/targeted/blood_boil/cast(var/list/targets, var/mob/user) - var/mob/living/human/H = targets[1] - H.bodytemperature += 40 - if(prob(10)) - to_chat(H,"\The [user] seems to radiate an uncomfortable amount of heat your direction.") - if(H.bodytemperature > H.get_mob_temperature_threshold(HEAT_LEVEL_3)) //Burst into flames - H.fire_stacks += 50 - H.IgniteMob() \ No newline at end of file diff --git a/code/modules/spells/targeted/cleric_spells.dm b/code/modules/spells/targeted/cleric_spells.dm deleted file mode 100644 index 74061d776dd6..000000000000 --- a/code/modules/spells/targeted/cleric_spells.dm +++ /dev/null @@ -1,248 +0,0 @@ -/spell/targeted/heal_target - name = "Cure Light Wounds" - desc = "a rudimentary spell used mainly by wizards to heal papercuts. Does not require wizard garb." - feedback = "CL" - school = "transmutation" - charge_max = 20 SECONDS - spell_flags = INCLUDEUSER | SELECTABLE - invocation = "Di'Nath!" - invocation_type = SpI_SHOUT - range = 2 - max_targets = 1 - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 1, Sp_POWER = 2) - - cooldown_reduc = 50 - hud_state = "heal_minor" - cast_sound = 'sound/magic/staff_healing.ogg' - - amt_dam_brute = -15 - amt_dam_fire = -5 - amt_dam_robo = -4 - effect_state = "green_sparkles" - effect_duration = 5 - - // Vars expect a constant at compile time, so we can't use macros for spans here - message = "You feel a pleasant rush of heat move through your body." - -/spell/targeted/heal_target/empower_spell() - if(!..()) - return 0 - amt_dam_brute -= 15 - amt_dam_fire -= 15 - amt_dam_robo -= 7 - return "[src] will now heal more." - -/spell/targeted/heal_target/touch - name = "Healing Touch" - desc = "Heals an adjacent target for a reasonable amount of health." - range = 1 - amt_dam_fire = -7 - amt_dam_brute = -7 - amt_dam_robo = -5 - charge_max = 10 SECONDS - spell_flags = SELECTABLE - invocation = "Di'Na!" - - hud_state = "heal_touch" - -/spell/targeted/heal_target/major - name = "Cure Major Wounds" - desc = "A spell used to fix others that cannot be fixed with regular medicine." - feedback = "CM" - charge_max = 30 SECONDS - spell_flags = INCLUDEUSER | SELECTABLE | NEEDSCLOTHES - invocation = "Borv Di'Nath!" - range = 1 - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 1, Sp_POWER = 1) - cooldown_reduc = 100 - hud_state = "heal_major" - - amt_dam_brute = -75 - amt_dam_fire = -50 - amt_dam_robo = -10 - amt_blood = 28 - - message = "Your body feels like a warm, cozy fire." - -/spell/targeted/heal_target/major/empower_spell() - if(!..()) - return 0 - amt_blood = 28 - amt_organ = 5 - amt_brain = -5 - amt_radiation = -25 - amt_dam_tox = -20 - amt_dam_oxy = -14 - amt_dam_brute = -35 - amt_dam_fire = -35 - amt_dam_robo = -15 - - return "[src] heals more, and heals organ damage and radiation." - -/spell/targeted/heal_target/area - name = "Cure Area" - desc = "This spell heals everyone in an area." - feedback = "HA" - charge_max = 1 MINUTE - spell_flags = INCLUDEUSER - invocation = "Nal Di'Nath!" - range = 2 - max_targets = 0 - level_max = list(Sp_TOTAL = 1, Sp_SPEED = 1, Sp_POWER = 1) - cooldown_reduc = 300 - hud_state = "heal_area" - amt_dam_robo = -6 - amt_dam_brute = -25 - amt_dam_fire = -25 - -/spell/targeted/heal_target/area/empower_spell() - if(!..()) - return 0 - amt_dam_brute -= 15 - amt_dam_fire -= 15 - amt_dam_robo -= 4 - range += 2 - - return "[src] now heals more in a wider area." - -/spell/targeted/heal_target/area/slow - charge_max = 2 MINUTES - -/spell/targeted/heal_target/sacrifice - name = "Sacrifice" - desc = "This spell heals immensily. For a price. Does not require wizard garb." - feedback = "SF" - spell_flags = SELECTABLE - invocation = "Ei'Nath Borv Di'Nath!" - charge_type = Sp_HOLDVAR - holder_var_type = "fireloss" - holder_var_amount = 100 - level_max = list(Sp_TOTAL = 1, Sp_SPEED = 0, Sp_POWER = 1) - - amt_dam_brute = -1000 - amt_dam_fire = -1000 - amt_dam_oxy = -100 - amt_dam_tox = -100 - amt_dam_robo = -1000 - amt_blood = 280 - effect_color = "#ff0000" - - hud_state = "gen_dissolve" - cast_sound = 'sound/magic/disintegrate.ogg' - -/spell/targeted/heal_target/sacrifice/empower_spell() - if(!..()) - return 0 - - amt_organ = 25 - amt_brain = -25 - amt_radiation = -100 - - - return "You will now heal organ and brain damage, as well as virtually purge all radiation." - - -/spell/targeted/heal_target/trance - name = "Trance" - desc = "A mighty spell of restoration that briefly forces its target into a deep, dreamless sleep, rapidly repairing their body and soul as their senses are dulled. The users of this mighty art are known for being short lived, slowly devolving into raving madness as the power they once relied on fails them with excessive use." - feedback = "TC" - spell_flags = SELECTABLE - invocation = "Di' Dae Nath!" - charge_max = 2 MINUTES - - amt_dam_brute = -1000 - amt_dam_fire = -1000 - amt_dam_oxy = -100 - amt_dam_tox = -100 - amt_dam_robo = -1000 - hud_state = "trance" - var/obj/effect/effect - -/spell/targeted/heal_target/trance/cast(var/list/targets, var/mob/user) - for(var/t in targets) - var/mob/living/L = t - var/turf/T = get_turf(L) - effect = new /obj/effect/rift(T) - effect.color = "f0e68c" - L.forceMove(effect) - var/time = (L.get_damage(BRUTE) + L.get_damage(BURN)) * 20 - L.status_flags &= GODMODE - to_chat(L,"You will be in stasis for [time/10] second\s.") - addtimer(CALLBACK(src,PROC_REF(cancel_rift)),time) - -/spell/targeted/heal_target/trance/Destroy() - cancel_rift() - return ..() - -/spell/targeted/heal_target/trance/proc/cancel_rift() - if(effect) - var/mob/living/L = locate() in effect - L.status_flags &= ~GODMODE - L.forceMove(get_turf(L)) - apply_spell_damage(L) - charge_max += 300 - QDEL_NULL(effect) - -/obj/effect/rift - name = "rift" - desc = "a tear in space and time." - icon = 'icons/obj/wizard.dmi' - icon_state = "rift" - anchored = TRUE - density = FALSE - -/obj/effect/rift/Destroy() - for(var/o in contents) - var/atom/movable/M = o - M.dropInto(loc) - . = ..() - -/spell/targeted/revoke - name = "Revoke Death" - desc = "Revoke that of death itself. Comes at a cost that may be hard to manage for some." - feedback = "RK" - - spell_flags = SELECTABLE - - charge_type = Sp_CHARGES - charge_max = 1 - invocation = "Di Le Nal Yen Nath!" - invocation_type = SpI_SHOUT - range = 1 - hud_state = "heal_revoke" - -/spell/targeted/revoke/cast(var/list/targets, var/mob/living/user) - if(alert(user, "Are you sure?", "Alert", "Yes", "No") == "Yes" && alert(user, "Are you ABSOLUTELY SURE?", "Alert", "Absolutely!", "No") == "Absolutely!") - var/should_wait = 1 - for(var/t in targets) - var/mob/living/M = t - M.rejuvenate() - if(M.client) //We've got a dude - should_wait = 0 - break //Don't need to check anymore. - if(should_wait) - addtimer(CALLBACK(src,PROC_REF(check_for_revoke),targets), 30 SECONDS) - else - revoke_spells() - - -/spell/targeted/revoke/proc/check_for_revoke(var/list/targets) - for(var/t in targets) - var/mob/M = t - if(M.client) - revoke_spells() - return - charge_counter = charge_max - to_chat(holder,"\The [src] refreshes as it seems it could not bring back the souls of those you healed.") - -/spell/targeted/revoke/proc/revoke_spells() - if(!isliving(holder)) - return - var/mob/living/M = holder - if(M.mind) - for(var/s in M.mind.learned_spells) - if(istype(s, /spell/toggle_armor)) //Can keep the armor n junk. - continue - M.remove_spell(s) - for(var/a in M.auras) - M.remove_aura(a) \ No newline at end of file diff --git a/code/modules/spells/targeted/equip/burning_touch.dm b/code/modules/spells/targeted/equip/burning_touch.dm deleted file mode 100644 index 65ed431693bf..000000000000 --- a/code/modules/spells/targeted/equip/burning_touch.dm +++ /dev/null @@ -1,69 +0,0 @@ -/spell/targeted/equip_item/burning_hand - name = "Burning Hand" - desc = "Bathes your hand in fire, giving you all the perks and disadvantages that brings." - feedback = "BH" - school = "conjuration" - invocation = "Horila Kiha!" - invocation_type = SpI_SHOUT - spell_flags = INCLUDEUSER - range = -1 - duration = 0 - max_targets = 1 - equipped_summons = list("active hand" = /obj/item/burning_hands) - delete_old = 0 - hud_state = "gen_burnhand" - -/obj/item/burning_hands - name = "Burning Hand" - icon = 'icons/mob/screen/grabs.dmi' - icon_state = "grabbed+1" - _base_attack_force = 10 - atom_damage_type = BURN - simulated = 0 - max_health = ITEM_HEALTH_NO_DAMAGE - obj_flags = OBJ_FLAG_NO_STORAGE - var/burn_power = 0 - var/burn_timer - -/obj/item/burning_hands/on_picked_up(var/mob/user) - burn_power = 0 - burn_timer = world.time + 10 SECONDS - START_PROCESSING(SSobj,src) - -/obj/item/burning_hands/get_heat() - return 1000 - -/obj/item/burning_hands/isflamesource() - return TRUE - -/obj/item/burning_hands/Process() - if(world.time < burn_timer) - return - burn_timer = world.time + 5 SECONDS - burn_power++ - set_base_attack_force(get_base_attack_force()+2) - if(!ishuman(src.loc)) - qdel(src) - return - var/mob/living/human/user = src.loc - var/obj/item/organ/external/hand - if(src == user.get_equipped_item(BP_L_HAND)) - hand = GET_EXTERNAL_ORGAN(user, BP_L_HAND) - else if(src == user.get_equipped_item(BP_R_HAND)) - hand = GET_EXTERNAL_ORGAN(user, BP_R_HAND) - if(hand) - hand.take_external_damage(burn = 2 * burn_power) - if(burn_power > 5) - user.fire_stacks += 15 - user.IgniteMob() - user.visible_message("\The [user] bursts into flames!") - user.drop_from_inventory(src) - else - if(burn_power == 5) - to_chat(user, "You begin to lose control of \the [src]'s flames as they rapidly move up your arm...") - else - to_chat(user, "You feel \the [src] grow hotter and hotter!") - -/obj/item/burning_hands/dropped() - ..() - qdel(src) \ No newline at end of file diff --git a/code/modules/spells/targeted/equip/dyrnwyn.dm b/code/modules/spells/targeted/equip/dyrnwyn.dm deleted file mode 100644 index 71d7ace1e4f0..000000000000 --- a/code/modules/spells/targeted/equip/dyrnwyn.dm +++ /dev/null @@ -1,37 +0,0 @@ -/spell/targeted/equip_item/dyrnwyn - name = "Summon Dyrnwyn" - desc = "Summons the legendary sword of Rhydderch Hael, said to draw in flame when held by a worthy man." - feedback = "SD" - charge_type = Sp_HOLDVAR - holder_var_type = "fireloss" - holder_var_amount = 10 - school = "conjuration" - invocation = "Anrhydeddu Fi!" - invocation_type = SpI_SHOUT - spell_flags = INCLUDEUSER - range = -1 - level_max = list(Sp_TOTAL = 1, Sp_SPEED = 0, Sp_POWER = 1) - duration = 300 //30 seconds - max_targets = 1 - equipped_summons = list("active hand" = /obj/item/sword) - delete_old = FALSE - var/material = /decl/material/solid/metal/gold - - hud_state = "gen_immolate" - - -/spell/targeted/equip_item/dyrnwyn/summon_item(var/new_type) - var/obj/item/sword = new new_type(null,material) - sword.SetName("\improper Dyrnwyn") - sword.atom_damage_type = BURN - sword.hitsound = 'sound/items/welder2.ogg' - LAZYSET(sword.slowdown_per_slot, BP_L_HAND, 1) - LAZYSET(sword.slowdown_per_slot, BP_R_HAND, 1) - return sword - -/spell/targeted/equip_item/dyrnwyn/empower_spell() - if(!..()) - return FALSE - - material = /decl/material/solid/metal/silver - return "Dyrnwyn has been made pure: it is now made of silver." diff --git a/code/modules/spells/targeted/equip/equip.dm b/code/modules/spells/targeted/equip/equip.dm deleted file mode 100644 index 7b0304d9e076..000000000000 --- a/code/modules/spells/targeted/equip/equip.dm +++ /dev/null @@ -1,38 +0,0 @@ -//You can set duration to 0 to have the items last forever - -/spell/targeted/equip_item - cast_sound = 'sound/magic/summonitems_generic.ogg' - var/list/equipped_summons = list() //assoc list of text ids and paths to spawn - var/list/summoned_items = list() //list of items we summoned and will dispose when the spell runs out - var/delete_old = 1 //if the item previously in the slot is deleted - otherwise, it's dropped - -/spell/targeted/equip_item/cast(list/targets, mob/user = usr) - ..() - for(var/mob/living/L in targets) - for(var/slot_id in equipped_summons) - var/to_create = equipped_summons[slot_id] - if(cmptext(slot_id,"active hand")) - slot_id = user.get_active_held_item_slot() - else if(cmptext(slot_id, "off hand")) - slot_id = user.get_empty_hand_slot() - else - slot_id = text2num(slot_id) //because the index is text, we access this instead - var/obj/item/new_item = summon_item(to_create) - var/obj/item/old_item = L.get_equipped_item(slot_id) - if(old_item) - L.drop_from_inventory(old_item) - if(delete_old) - qdel(old_item) - L.equip_to_slot(new_item, slot_id) - new_item.on_picked_up(L) - - if(duration) - summoned_items += new_item //we store it in a list to remove later - - if(duration) - spawn(duration) - for(var/obj/item/to_remove in summoned_items) - qdel(to_remove) - -/spell/targeted/equip_item/proc/summon_item(var/newtype) - return new newtype diff --git a/code/modules/spells/targeted/equip/holy_relic.dm b/code/modules/spells/targeted/equip/holy_relic.dm deleted file mode 100644 index 7c6dadf5da26..000000000000 --- a/code/modules/spells/targeted/equip/holy_relic.dm +++ /dev/null @@ -1,34 +0,0 @@ -/spell/targeted/equip_item/holy_relic - name = "Summon Holy Relic" - desc = "This spell summons a relic of purity into your hand for a short while. The relic will disrupt occult and magical energies - be wary, as this includes your own." - feedback = "SR" - school = "conjuration" - charge_type = Sp_RECHARGE - spell_flags = NEEDSCLOTHES | INCLUDEUSER - invocation = "Yee'Ro Su!" - invocation_type = SpI_SHOUT - range = 0 - max_targets = 1 - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 1, Sp_POWER = 1) - charge_max = 60 SECONDS - duration = 25 SECONDS - cooldown_min = 35 SECONDS - delete_old = 0 - compatible_mobs = list(/mob/living/human) - - hud_state = "purge1" - - equipped_summons = list("active hand" = /obj/item/nullrod) - -/spell/targeted/equip_item/holy_relic/cast(list/targets, mob/user = usr) - ..() - for(var/mob/M in targets) - M.visible_message(SPAN_DANGER("A rod of metal appears in \the [M]'s hand!")) - -/spell/targeted/equip_item/holy_relic/empower_spell() - if(!..()) - return 0 - - duration += 50 - - return "The holy relic now lasts for [duration/10] seconds." \ No newline at end of file diff --git a/code/modules/spells/targeted/equip/horsemask.dm b/code/modules/spells/targeted/equip/horsemask.dm deleted file mode 100644 index 31995ba86bd0..000000000000 --- a/code/modules/spells/targeted/equip/horsemask.dm +++ /dev/null @@ -1,48 +0,0 @@ -/spell/targeted/equip_item/horsemask - name = "Curse of the Horseman" - desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." - school = "transmutation" - charge_type = Sp_RECHARGE - charge_max = 150 - charge_counter = 0 - spell_flags = 0 - invocation = "Kn'a Ftaghu, Puck'Bthnk!" - invocation_type = SpI_SHOUT - range = 7 - max_targets = 1 - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 4, Sp_POWER = 1) - cooldown_min = 30 //30 deciseconds reduction per rank - selection_type = "range" - - compatible_mobs = list(/mob/living/human) - - hud_state = "wiz_horse" - cast_sound = 'sound/magic/horsehead_curse.ogg' - -/spell/targeted/equip_item/horsemask/New() - ..() - equipped_summons = list("[slot_wear_mask_str]" = /obj/item/clothing/mask/horsehead) - -/spell/targeted/equip_item/horsemask/cast(list/targets, mob/user = usr) - ..() - for(var/mob/living/target in targets) - target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \ - "Your face burns up, and shortly after the fire you realise you have the face of a horse!") - target.flash_eyes() - -/spell/targeted/equip_item/horsemask/summon_item(var/new_type) - var/obj/item/new_item = new new_type - new_item.canremove = 0 //curses! - if(istype(new_item, /obj/item/clothing/mask/horsehead)) - var/obj/item/clothing/mask/horsehead/magichead = new_item - magichead.flags_inv = null //so you can still see their face - magichead.voicechange = 1 //NEEEEIIGHH - return new_item - -/spell/targeted/equip_item/horsemask/empower_spell() - if(!..()) - return 0 - - spell_flags = SELECTABLE - - return "You can now select your target with [src]" \ No newline at end of file diff --git a/code/modules/spells/targeted/equip/party_hardy.dm b/code/modules/spells/targeted/equip/party_hardy.dm deleted file mode 100644 index f6f7ba54d071..000000000000 --- a/code/modules/spells/targeted/equip/party_hardy.dm +++ /dev/null @@ -1,36 +0,0 @@ -/spell/targeted/equip_item/party_hardy - name = "Summon Party" - desc = "This spell was invented for the sole purpose of getting crunked at 11am on a Tuesday. Does not require wizard garb." - feedback = "PY" - school = "conjuration" - charge_type = Sp_RECHARGE - charge_max = 900 - cooldown_min = 600 - spell_flags = INCLUDEUSER - invocation = "Llet'Su G'iit Rrkned!" //Let's get wrecked. - invocation_type = SpI_SHOUT - range = 6 - max_targets = 0 - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 1, Sp_POWER = 2) - delete_old = 0 - - hud_state = "wiz_party" - - compatible_mobs = list(/mob/living/human) - equipped_summons = list("active hand" = /obj/item/chems/drinks/bottle/small/beer) - -/spell/targeted/equip_item/party_hardy/empower_spell() - if(!..()) - return 0 - switch(spell_levels[Sp_POWER]) - if(1) - equipped_summons = list("active hand" = /obj/item/chems/drinks/bottle/small/beer, - "off hand" = /obj/item/food/poppypretzel) - return "The spell will now give everybody a preztel as well." - if(2) - equipped_summons = list("active hand" = /obj/item/chems/drinks/bottle/absinthe, - "off hand" = /obj/item/food/poppypretzel, - "[slot_head_str]" = /obj/item/clothing/head/collectable/wizard) - return "Woo! Now everybody gets a cool wizard hat and MORE BOOZE!" - - return 0 \ No newline at end of file diff --git a/code/modules/spells/targeted/equip/seed.dm b/code/modules/spells/targeted/equip/seed.dm deleted file mode 100644 index 2252ec75c8c0..000000000000 --- a/code/modules/spells/targeted/equip/seed.dm +++ /dev/null @@ -1,21 +0,0 @@ -/spell/targeted/equip_item/seed - name = "Summon Seed" - desc = "This spell summons a random seed into the hand of the wizard." - feedback = "SE" - delete_old = 0 - - spell_flags = INCLUDEUSER | NEEDSCLOTHES - invocation_type = SpI_WHISPER - invocation = "Ria'li akta." - - equipped_summons = list("active hand" = /obj/item/seeds/random) - compatible_mobs = list(/mob/living/human) - - charge_max = 600 //1 minute - cooldown_min = 200 //20 seconds - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 3, Sp_POWER = 0) - - range = -1 - max_targets = 1 - - hud_state = "wiz_seed" \ No newline at end of file diff --git a/code/modules/spells/targeted/equip/shield.dm b/code/modules/spells/targeted/equip/shield.dm deleted file mode 100644 index b725196ad3a6..000000000000 --- a/code/modules/spells/targeted/equip/shield.dm +++ /dev/null @@ -1,41 +0,0 @@ -/spell/targeted/equip_item/shield - name = "Summon Shield" - desc = "Summons the most holy of shields, the riot shield. Commonly used during wizard riots." - feedback = "SH" - school = "conjuration" - invocation = "Sia helda!" - invocation_type = SpI_SHOUT - spell_flags = INCLUDEUSER | NEEDSCLOTHES - range = -1 - max_targets = 1 - - compatible_mobs = list(/mob/living/human) - - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 2, Sp_POWER = 1) - charge_type = Sp_RECHARGE - charge_max = 900 - cooldown_min = 300 - equipped_summons = list("off hand" = /obj/item/shield/) - duration = 300 - delete_old = 0 - var/item_color = "#6666ff" - var/block_chance = 30 - - hud_state = "wiz_shield" - -/spell/targeted/equip_item/shield/summon_item(var/new_type) - var/obj/item/shield/I = new new_type() - I.icon_state = "buckler" - I.color = item_color - I.SetName("Wizard's Shield") - I.base_block_chance = block_chance - return I - -/spell/targeted/equip_item/shield/empower_spell() - if(!..()) - return 0 - - item_color = "#6600ff" - block_chance = 60 - - return "Your summoned shields will now block more often." diff --git a/code/modules/spells/targeted/ethereal_jaunt.dm b/code/modules/spells/targeted/ethereal_jaunt.dm index e686e7712dc5..fdccd1ce64d1 100644 --- a/code/modules/spells/targeted/ethereal_jaunt.dm +++ b/code/modules/spells/targeted/ethereal_jaunt.dm @@ -4,7 +4,7 @@ feedback = "EJ" school = "transmutation" charge_max = 30 SECONDS - spell_flags = Z2NOCAST | NEEDSCLOTHES | INCLUDEUSER + spell_flags = Z2NOCAST | INCLUDEUSER invocation = "none" invocation_type = SpI_NONE range = 0 diff --git a/code/modules/spells/targeted/exude_pleasantness.dm b/code/modules/spells/targeted/exude_pleasantness.dm deleted file mode 100644 index 74ae51237ab7..000000000000 --- a/code/modules/spells/targeted/exude_pleasantness.dm +++ /dev/null @@ -1,19 +0,0 @@ -/spell/targeted/exude_pleasantness - name = "Exhude Pleasantness" - desc = "A simple spell used to make friends with people. Be warned, this spell only has a subtle effect" - feedback = "AP" - school = "Illusion" - spell_flags = INCLUDEUSER - range = 5 - max_targets = 0 - charge_max = 100 - var/list/possible_messages = list("seems pretty trustworthy!", "makes you feel appreciated.", "looks pretty cool.", "feels like the only decent person here!", "makes you feel safe.") - hud_state = "friendly" - -/spell/targeted/exude_pleasantness/cast(var/list/targets, var/mob/user) - for(var/m in targets) - var/mob/living/L = m - if(L.mind && L.mind.assigned_special_role == "Spellbound Servant") - to_chat(m, SPAN_NOTICE("\The [user] seems relatively harmless.")) - else - to_chat(m, FONT_LARGE(SPAN_NOTICE("\The [user] [pick(possible_messages)]"))) \ No newline at end of file diff --git a/code/modules/spells/targeted/genetic.dm b/code/modules/spells/targeted/genetic.dm deleted file mode 100644 index bcc5233b9b65..000000000000 --- a/code/modules/spells/targeted/genetic.dm +++ /dev/null @@ -1,73 +0,0 @@ -/* -Other mutation or disability spells can be found in -code\game\dna\genes\vg_powers.dm -code\game\dna\genes\goon_disabilities.dm -code\game\dna\genes\goon_powers.dm -*/ -/spell/targeted/genetic - name = "Genetic modifier" - desc = "This spell inflicts a set of genetic conditions upon the target." - duration = 10 SECONDS - var/list/genetic_conditions = list() - -/spell/targeted/genetic/cast(list/targets) - ..() - for(var/mob/living/target in targets) - for(var/x in genetic_conditions) - target.add_genetic_condition(x, duration) - -/spell/targeted/genetic/blind - name = "Blind" - desc = "This spell inflicts a target with temporary blindness. Does not require wizard garb." - feedback = "BD" - school = "illusion" - duration = 300 - charge_max = 300 - spell_flags = 0 - invocation = "Sty Kaly." - invocation_type = SpI_WHISPER - message = "Your eyes cry out in pain!" - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 1, Sp_POWER = 3) - cooldown_min = 50 - range = 7 - max_targets = 0 - amt_eye_blind = 10 - amt_eye_blurry = 20 - hud_state = "wiz_blind" - cast_sound = 'sound/magic/blind.ogg' - genetic_conditions = list(GENE_COND_BLINDED) - -/spell/targeted/genetic/blind/empower_spell() - if(!..()) - return 0 - duration += 10 SECONDS - return "[src] will now blind for a longer period of time." - -/spell/targeted/genetic/blind/hysteria - name = "Hysteria" - desc = "A spell used to make someone look like a blind fool, and also makes them a blind fool." - feedback = "HY" - school = "illusion" - spell_flags = SELECTABLE - charge_max = 600 - invocation_type = SpI_SHOUT - invocation = "Sty Di Kaly!" - amt_dizziness = 10 - hud_state = "hysteria" - -/spell/targeted/genetic/blind/starburst - name = "Starburst" - desc = "Send a jolt of electricity through everyone's nerve center, blinding and stunning them." - feedback = "SB" - school = "transmutation" - invocation = "Tid Caeh Yor!" - spell_flags = NOFACTION - invocation_type = SpI_SHOUT - charge_max = 60 SECONDS - spell_flags = 0 - amt_dizziness = 0 - amt_eye_blurry = 5 - amt_stunned = 1 - effect_state = "electricity_constant" - effect_duration = 5 - hud_state = "wiz_starburst" diff --git a/code/modules/spells/targeted/glimpse_of_eternity.dm b/code/modules/spells/targeted/glimpse_of_eternity.dm deleted file mode 100644 index a4d783245ab9..000000000000 --- a/code/modules/spells/targeted/glimpse_of_eternity.dm +++ /dev/null @@ -1,26 +0,0 @@ -/spell/targeted/glimpse_of_eternity - name = "Glimpse of Eternity" - desc = "Show the non-believers what enlightenment truely means." - feedback = "GE" - school = "illusion" - invocation = "Ghe Tar Yet!" - invocation_type = SpI_SHOUT - spell_flags = INCLUDEUSER - max_targets = 0 - charge_max = 400 - range = 3 - - hud_state = "wiz_glimpse" - -/spell/targeted/glimpse_of_eternity/cast(var/list/targets, var/mob/user) - for(var/t in targets) - var/mob/living/L = t - if(L.faction != user.faction) //Worse for non-allies - SET_STATUS_MAX(L, STAT_BLIND, 5) - SET_STATUS_MAX(L, STAT_STUN, 5) - new /obj/effect/temporary(get_turf(L), 5, 'icons/effects/effects.dmi', "electricity_constant") - else - SET_STATUS_MAX(L, STAT_BLIND, 2) - L.heal_damage(BRUTE, 10, do_update_health = FALSE) - L.heal_damage(BURN, 10) - new /obj/effect/temporary(get_turf(L), 5, 'icons/effects/effects.dmi', "green_sparkles") \ No newline at end of file diff --git a/code/modules/spells/targeted/projectile/dumbfire.dm b/code/modules/spells/targeted/projectile/dumbfire.dm deleted file mode 100644 index 491a5f00f99e..000000000000 --- a/code/modules/spells/targeted/projectile/dumbfire.dm +++ /dev/null @@ -1,13 +0,0 @@ -/spell/targeted/projectile/dumbfire - selection_type = "range" - -/spell/targeted/projectile/dumbfire/choose_targets(mob/user = usr) - var/list/targets = list() - - var/starting_dir = user.dir //where are we facing at the time of casting? - var/turf/starting_turf = get_turf(user) - var/current_turf = starting_turf - for(var/i = 1; i <= src.range; i++) - current_turf = get_step(current_turf, starting_dir) - targets += current_turf - return targets \ No newline at end of file diff --git a/code/modules/spells/targeted/projectile/fireball.dm b/code/modules/spells/targeted/projectile/fireball.dm deleted file mode 100644 index ef65dc885f73..000000000000 --- a/code/modules/spells/targeted/projectile/fireball.dm +++ /dev/null @@ -1,66 +0,0 @@ -/spell/targeted/projectile/dumbfire/fireball - name = "Fireball" - desc = "A classic spell, grants you the ability to throw an exploding ball of flame in any direction. Does not require wizard garb." - feedback = "FB" - proj_type = /obj/item/projectile/spell_projectile/fireball - - school = "conjuration" - charge_max = 10 SECONDS - spell_flags = 0 - invocation = "Oni-Soma!" - invocation_type = SpI_SHOUT - range = 20 - - level_max = list(Sp_TOTAL = 5, Sp_SPEED = 0, Sp_POWER = 5) - - duration = 20 - proj_step_delay = 1 - - amt_dam_brute = 20 - amt_dam_fire = 25 - - var/ex_severe = -1 - var/ex_heavy = 1 - var/ex_light = 2 - var/ex_flash = 5 - - hud_state = "wiz_fireball" - cast_sound = 'sound/magic/fireball.ogg' - -/spell/targeted/projectile/dumbfire/fireball/prox_cast(var/list/targets, spell_holder) - for(var/mob/living/M in targets) - apply_spell_damage(M) - explosion(get_turf(spell_holder), ex_severe, ex_heavy, ex_light, ex_flash) - -/spell/targeted/projectile/dumbfire/fireball/empower_spell() - if(!..()) - return 0 - - if(spell_levels[Sp_POWER]%2 == 1) - ex_severe++ - ex_heavy++ - ex_light++ - ex_flash++ - - return "The spell [src] now has a larger explosion." - -//PROJECTILE - -/obj/item/projectile/spell_projectile/fireball - name = "fireball" - icon_state = "fireball" - -/spell/targeted/projectile/dumbfire/fireball/firebolt - name = "Firebolt" - desc = "A quick-casted fireball. Burns the user, and their enemies, but is much faster to shoot." - feedback = "FO" - charge_type = Sp_HOLDVAR - invocation = "Ignus!" - holder_var_type = "fireloss" - holder_var_amount = 10 - amt_dam_brute = 10 - amt_dam_fire = 15 - ex_heavy = -1 - ex_light = 1 - ex_flash = 3 - hud_state = "firebolt" \ No newline at end of file diff --git a/code/modules/spells/targeted/projectile/magic_missile.dm b/code/modules/spells/targeted/projectile/magic_missile.dm deleted file mode 100644 index 1d72399718a9..000000000000 --- a/code/modules/spells/targeted/projectile/magic_missile.dm +++ /dev/null @@ -1,56 +0,0 @@ -/spell/targeted/projectile/magic_missile - name = "Magic Missile" - desc = "This spell fires several, slow moving, magic projectiles at nearby targets." - feedback = "MM" - school = "conjuration" - charge_max = 150 - spell_flags = NEEDSCLOTHES - invocation = "Forti Gy-Ama!" - invocation_type = SpI_SHOUT - range = 7 - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 3, Sp_POWER = 3) - cooldown_min = 90 //15 deciseconds reduction per rank - - max_targets = 0 - - proj_type = /obj/item/projectile/spell_projectile/seeking/magic_missile - duration = 10 - proj_step_delay = 5 - - hud_state = "wiz_mm" - cast_sound = 'sound/magic/magic_missile.ogg' - amt_paralysis = 3 - amt_stunned = 3 - - amt_dam_fire = 10 - -/spell/targeted/projectile/magic_missile/prox_cast(var/list/targets, atom/spell_holder) - spell_holder.visible_message("\The [spell_holder] pops with a flash!") - playsound(src, 'sound/magic/mm_hit.ogg', 40) - for(var/mob/living/M in targets) - apply_spell_damage(M) - return - -/spell/targeted/projectile/magic_missile/empower_spell() - if(!..()) - return 0 - - if(spell_levels[Sp_POWER] == level_max[Sp_POWER]) - amt_paralysis += 2 - amt_stunned += 2 - return "[src] will now stun people for a longer duration." - amt_dam_fire += 5 - - return "[src] does more damage now." - - - -//PROJECTILE - -/obj/item/projectile/spell_projectile/seeking/magic_missile - name = "magic missile" - icon_state = "magicm" - - proj_trail = 1 - proj_trail_lifespan = 5 - proj_trail_icon_state = "magicmd" diff --git a/code/modules/spells/targeted/projectile/passage.dm b/code/modules/spells/targeted/projectile/passage.dm deleted file mode 100644 index b99e147bee22..000000000000 --- a/code/modules/spells/targeted/projectile/passage.dm +++ /dev/null @@ -1,46 +0,0 @@ -/spell/targeted/projectile/dumbfire/passage - name = "Passage" - desc = "throw a spell towards an area and teleport to it." - feedback = "PA" - proj_type = /obj/item/projectile/spell_projectile/passage - - - school = "conjuration" - charge_max = 250 - invocation = "A'YASAMA" - invocation_type = SpI_SHOUT - range = 15 - - - level_max = list(Sp_TOTAL = 1, Sp_SPEED = 0, Sp_POWER = 1) - spell_flags = NEEDSCLOTHES - duration = 15 - - proj_step_delay = 1 - - hud_state = "gen_project" - cast_sound = 'sound/magic/lightning_bolt.ogg' - -/spell/targeted/projectile/dumbfire/passage/prox_cast(var/list/targets, atom/spell_holder) - for(var/mob/living/L in targets) - apply_spell_damage(L) - - var/turf/T = get_turf(spell_holder) - - holder.forceMove(T) - var/datum/effect/effect/system/smoke_spread/S = new /datum/effect/effect/system/smoke_spread() - S.set_up(3,0,T) - S.start() - playsound(src, 'sound/magic/lightningshock.ogg', 50) - -/spell/targeted/projectile/dumbfire/passage/empower_spell() - if(!..()) - return 0 - - amt_stunned += 3 - - return "[src] now stuns those who get hit by it." - -/obj/item/projectile/spell_projectile/passage - name = "spell" - icon_state = "energy2" \ No newline at end of file diff --git a/code/modules/spells/targeted/projectile/projectile.dm b/code/modules/spells/targeted/projectile/projectile.dm deleted file mode 100644 index 6df791094be2..000000000000 --- a/code/modules/spells/targeted/projectile/projectile.dm +++ /dev/null @@ -1,45 +0,0 @@ -/* -Projectile spells make special projectiles (obj/item/spell_projectile) and fire them at targets -Dumbfire projectile spells fire directly ahead of the user -spell_projectiles call their spell's (carried) prox_cast when they get in range of a target -If the spell_projectile is seeking, it will update its target every process and follow them -*/ - -/spell/targeted/projectile - range = 7 - var/proj_type = /obj/item/projectile/spell_projectile //use these. They are very nice - var/proj_step_delay = 1 //lower = faster - var/cast_prox_range = 1 - -/spell/targeted/projectile/cast(list/targets, mob/user = usr) - for(var/atom/target in targets) - var/obj/item/projectile/projectile = new proj_type(user.loc, user.dir) - - if(!projectile) - return - - if(istype(projectile, /obj/item/projectile/spell_projectile)) - var/obj/item/projectile/spell_projectile/SP = projectile - SP.carried = src //casting is magical - projectile.original = target - projectile.starting = get_turf(user) - projectile.shot_from = user //fired from the user - projectile.current = projectile.original - projectile.yo = target.y - user.y - projectile.xo = target.x - user.x - projectile.life_span = src.duration - projectile.hitscan = !proj_step_delay - projectile.step_delay = proj_step_delay - projectile.launch(target) - return - -/spell/targeted/projectile/proc/choose_prox_targets(mob/user = usr, var/atom/movable/spell_holder) - var/list/targets = list() - for(var/mob/living/M in range(spell_holder, cast_prox_range)) - if(M == user && !(spell_flags & INCLUDEUSER)) - continue - targets += M - return targets - -/spell/targeted/projectile/proc/prox_cast(var/list/targets, var/atom/movable/spell_holder) - return targets \ No newline at end of file diff --git a/code/modules/spells/targeted/projectile/stuncuff.dm b/code/modules/spells/targeted/projectile/stuncuff.dm deleted file mode 100644 index 520463815888..000000000000 --- a/code/modules/spells/targeted/projectile/stuncuff.dm +++ /dev/null @@ -1,47 +0,0 @@ -/spell/targeted/projectile/dumbfire/stuncuff - name = "Stun Cuff" - desc = "This spell fires out a small curse that stuns and cuffs the target." - feedback = "SC" - proj_type = /obj/item/projectile/spell_projectile/stuncuff - - charge_type = Sp_CHARGES - charge_max = 6 - charge_counter = 6 - spell_flags = 0 - invocation = "Fu'Reai Diakan!" - invocation_type = SpI_SHOUT - range = 20 - - level_max = list(Sp_TOTAL = 0, Sp_SPEED = 0, Sp_POWER = 0) - - duration = 20 - proj_step_delay = 1 - - amt_stunned = 6 - - hud_state = "wiz_cuff" - cast_sound = 'sound/magic/wandodeath.ogg' - -/spell/targeted/projectile/dumbfire/stuncuff/prox_cast(var/list/targets, spell_holder) - for(var/mob/living/M in targets) - if(ishuman(M)) - var/mob/living/human/H = M - var/obj/item/handcuffs/wizard/cuffs = new() - H.equip_to_slot(cuffs, slot_handcuffed_str) - H.visible_message("Beams of light form around \the [H]'s hands!") - apply_spell_damage(M) - - -/obj/item/handcuffs/wizard - name = "beams of light" - desc = "Undescribable and unpenetrable. Or so they say." - - breakouttime = 300 //30 seconds - -/obj/item/handcuffs/wizard/dropped(var/mob/user) - ..() - qdel(src) - -/obj/item/projectile/spell_projectile/stuncuff - name = "stuncuff" - icon_state = "spell" \ No newline at end of file diff --git a/code/modules/spells/targeted/shapeshift.dm b/code/modules/spells/targeted/shapeshift.dm deleted file mode 100644 index ae5de55a0f76..000000000000 --- a/code/modules/spells/targeted/shapeshift.dm +++ /dev/null @@ -1,203 +0,0 @@ -//basic transformation spell. Should work for most simple_animals - -/spell/targeted/shapeshift - name = "Shapeshift" - desc = "This spell transforms the target into something else for a short while." - - school = "transmutation" - - charge_type = Sp_RECHARGE - charge_max = 600 - - duration = 0 //set to 0 for permanent. - - var/list/possible_transformations = list() - var/list/newVars = list() //what the variables of the new created thing will be. - - cast_sound = 'sound/magic/charge.ogg' - var/revert_sound = 'sound/magic/charge.ogg' //the sound that plays when something gets turned back. - var/share_damage = 1 //do we want the damage we take from our new form to move onto our real one? (Only counts for finite duration) - var/drop_items = 1 //do we want to drop all our items when we transform? - var/toggle = 0 //Can we toggle this? - var/list/transformed_dudes = list() //Who we transformed. Transformed = Transformation. Both mobs. - -/spell/targeted/shapeshift/cast(var/list/targets, mob/user) - for(var/m in targets) - var/mob/living/M = m - if(M.stat == DEAD) - to_chat(user, "[name] can only transform living targets.") - continue - if(M.buckled) - M.buckled.unbuckle_mob() - if(toggle && transformed_dudes.len && stop_transformation(M)) - continue - var/new_mob = pick(possible_transformations) - - var/mob/living/trans = new new_mob(get_turf(M)) - for(var/varName in newVars) //stolen shamelessly from Conjure - if(varName in trans.vars) - trans.vars[varName] = newVars[varName] - //Give them our languages - for(var/decl/language/lang as anything in M.languages) - trans.add_language(lang.type) - - trans.SetName("[trans.name] ([M])") - if(ishuman(M) && drop_items) - for(var/obj/item/I in M.contents) - M.drop_from_inventory(I) - if(M.mind) - M.mind.transfer_to(trans) - else - trans.key = M.key - new /obj/effect/temporary(get_turf(M), 5, 'icons/effects/effects.dmi', "summoning") - - M.forceMove(trans) //move inside the new dude to hide him. - M.status_flags |= GODMODE //dont want him to die or breathe or do ANYTHING - transformed_dudes[trans] = M - events_repository.register(/decl/observ/death, trans,src, TYPE_PROC_REF(/spell/targeted/shapeshift, stop_transformation)) - events_repository.register(/decl/observ/destroyed, trans,src, TYPE_PROC_REF(/spell/targeted/shapeshift, stop_transformation)) - events_repository.register(/decl/observ/destroyed, M, src, TYPE_PROC_REF(/spell/targeted/shapeshift, destroyed_transformer)) - if(duration) - spawn(duration) - stop_transformation(trans) - -/spell/targeted/shapeshift/proc/destroyed_transformer(var/mob/target) //Juuuuust in case - var/mob/current = transformed_dudes[target] - to_chat(current, "You suddenly feel as if this transformation has become permanent...") - remove_target(target) - -/spell/targeted/shapeshift/proc/stop_transformation(var/mob/living/target) - var/mob/living/transformer = transformed_dudes[target] - if(!transformer) - return FALSE - transformer.status_flags &= ~GODMODE - if(share_damage) - var/transformer_max_health = transformer.get_max_health() - var/damage = transformer.set_max_health(transformer_max_health-round(transformer_max_health*(transformer.get_health_ratio()))) - for(var/i in 1 to ceil(damage/10)) - transformer.take_damage(10) - if(target.mind) - target.mind.transfer_to(transformer) - else - transformer.key = target.key - playsound(get_turf(target), revert_sound, 50, 1) - transformer.forceMove(get_turf(target)) - remove_target(target) - qdel(target) - return TRUE - -/spell/targeted/shapeshift/proc/remove_target(var/mob/living/target) - var/mob/current = transformed_dudes[target] - events_repository.unregister(/decl/observ/destroyed, target,src) - events_repository.unregister(/decl/observ/death, current,src) - events_repository.unregister(/decl/observ/destroyed, current,src) - transformed_dudes[target] = null - transformed_dudes -= target - -/spell/targeted/shapeshift/baleful_polymorph - name = "Baleful Polymorth" - desc = "This spell transforms its target into a small, furry animal." - feedback = "BP" - possible_transformations = list( - /mob/living/simple_animal/lizard, - /mob/living/simple_animal/passive/mouse, - /mob/living/simple_animal/passive/mouse/rat, - /mob/living/simple_animal/corgi - ) - - share_damage = 0 - invocation = "Yo'balada!" - invocation_type = SpI_SHOUT - spell_flags = NEEDSCLOTHES | SELECTABLE - range = 3 - duration = 150 //15 seconds. - cooldown_min = 200 //20 seconds - - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 2, Sp_POWER = 2) - - newVars = list("health" = 50, "max_health" = 50) - - hud_state = "wiz_poly" - - -/spell/targeted/shapeshift/baleful_polymorph/empower_spell() - if(!..()) - return 0 - - duration += 50 - - return "Your target will now stay in their polymorphed form for [duration/10] seconds." - -/spell/targeted/shapeshift/avian - name = "Polymorph" - desc = "This spell transforms the wizard into the common parrot." - feedback = "AV" - possible_transformations = list(/mob/living/simple_animal/hostile/parrot) - - drop_items = 0 - share_damage = 0 - invocation = "Poli'crakata!" - invocation_type = SpI_SHOUT - spell_flags = INCLUDEUSER - range = -1 - duration = 150 - charge_max = 600 - cooldown_min = 300 - level_max = list(Sp_TOTAL = 1, Sp_SPEED = 1, Sp_POWER = 0) - hud_state = "wiz_parrot" - -/spell/targeted/shapeshift/corrupt_form - name = "Corrupt Form" - desc = "This spell shapes the wizard into a terrible, terrible beast." - feedback = "CF" - possible_transformations = list(/mob/living/simple_animal/hostile/revenant) - - invocation = "mutters something dark and twisted as their form begins to twist..." - invocation_type = SpI_EMOTE - spell_flags = INCLUDEUSER - range = -1 - duration = 150 - charge_max = 1200 - cooldown_min = 600 - - drop_items = 0 - share_damage = 0 - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 2, Sp_POWER = 2) - - newVars = list("name" = "corrupted soul") - - hud_state = "wiz_corrupt" - cast_sound = 'sound/magic/disintegrate.ogg' - -/spell/targeted/shapeshift/corrupt_form/empower_spell() - if(!..()) - return 0 - - switch(spell_levels[Sp_POWER]) - if(1) - duration *= 2 - return "You will now stay corrupted for [duration/10] seconds." - if(2) - newVars = list("name" = "\proper corruption incarnate", - "melee_damage_upper" = 25, - "resistance" = 6, - "health" = 125, - "max_health" = 125) - duration = 0 - return "You revel in the corruption. There is no turning back." - -/spell/targeted/shapeshift/familiar - name = "Transform" - desc = "Transform into a familiar form. Literally." - feedback = "FA" - possible_transformations = list() - drop_items = 0 - invocation_type = SpI_EMOTE - invocation = "'s body dissipates into a pale mass of light, then reshapes!" - range = -1 - spell_flags = INCLUDEUSER - duration = 0 - charge_max = 2 MINUTES - toggle = 1 - - hud_state = "wiz_carp" \ No newline at end of file diff --git a/code/modules/spells/targeted/shatter_mind.dm b/code/modules/spells/targeted/shatter_mind.dm deleted file mode 100644 index f84d6765035d..000000000000 --- a/code/modules/spells/targeted/shatter_mind.dm +++ /dev/null @@ -1,29 +0,0 @@ -/spell/targeted/shatter - name = "Shatter Mind" - desc = "Assaults the mind of the target with fear of the unknown, shattering their sanity and causing brain damage." - feedback = "SM" - school = "illusion" - charge_max = 300 - spell_flags = 0 - invocation_type = SpI_NONE - range = 5 - max_targets = 1 - compatible_mobs = list(/mob/living/human) - - time_between_channels = 150 - number_of_channels = 0 - - hud_state = "wiz_statue" - -/spell/targeted/shatter/cast(var/list/targets, var/mob/user) - var/mob/living/human/H = targets[1] - if(prob(50)) - sound_to(user, get_sfx("swing_hit")) - if(prob(5)) - to_chat(H, "You feel unhinged.") - H.adjust_hallucination(5,5) - ADJ_STATUS(H, STAT_CONFUSE, 2) - ADJ_STATUS(H, STAT_DIZZY, 2) - if(H.hallucination_power > 50) - H.take_damage(5, BRAIN) - to_chat(H, "You feel your mind tearing apart!") \ No newline at end of file diff --git a/code/modules/spells/targeted/shift.dm b/code/modules/spells/targeted/shift.dm index 2b53b60d2441..2bcc684939a6 100644 --- a/code/modules/spells/targeted/shift.dm +++ b/code/modules/spells/targeted/shift.dm @@ -21,4 +21,4 @@ flick("phase_shift2",animation) /spell/targeted/ethereal_jaunt/shift/jaunt_steam(var/mobloc) - return \ No newline at end of file + return diff --git a/code/modules/spells/targeted/subjugate.dm b/code/modules/spells/targeted/subjugate.dm deleted file mode 100644 index 700dac35b36d..000000000000 --- a/code/modules/spells/targeted/subjugate.dm +++ /dev/null @@ -1,35 +0,0 @@ -/spell/targeted/subjugation - name = "Subjugation" - desc = "This spell temporarily subjugates a target's mind and does not require wizard garb." - feedback = "SJ" - school = "illusion" - charge_max = 500 - spell_flags = NOFACTION - invocation = "Dii Oda Baji." - invocation_type = SpI_WHISPER - - message = "You suddenly feel completely overwhelmed!" - - max_targets = 1 - - level_max = list(Sp_TOTAL = 3, Sp_SPEED = 0, Sp_POWER = 3) - - amt_dizziness = 100 - amt_confused = 100 - amt_stuttering = 100 - - compatible_mobs = list(/mob/living/human) - - hud_state = "wiz_subj" - -/spell/targeted/subjugation/empower_spell() - if(!..()) - return 0 - - if(spell_levels[Sp_POWER] == level_max[Sp_POWER]) - max_targets = 0 - - return "[src] will now effect everyone in the area." - else - max_targets++ - return "[src] will now effect [max_targets] people." \ No newline at end of file diff --git a/code/modules/spells/targeted/swap.dm b/code/modules/spells/targeted/swap.dm deleted file mode 100644 index 150bd9030772..000000000000 --- a/code/modules/spells/targeted/swap.dm +++ /dev/null @@ -1,41 +0,0 @@ -/spell/targeted/swap - name = "swap" - desc = "This spell swaps the positions of the wizard and a target. Causes brain damage." - feedback = "SW" - school = "conjuration" - - charge_type = Sp_HOLDVAR - holder_var_type = "brainloss" - holder_var_amount = 10 - - invocation = "Joyo!" - invocation_type = SpI_WHISPER - - level_max = list(Sp_TOTAL = 2, Sp_SPEED = 0, Sp_POWER = 2) - - spell_flags = Z2NOCAST - range = 6 - max_targets = 1 - compatible_mobs = list(/mob/living) - - hud_state = "wiz_swap" - - cast_sound = 'sound/magic/mandswap.ogg' - -/spell/targeted/swap/cast(var/list/targets, mob/user) - for(var/mob/T in targets) - var/turf/aT = get_turf(T) - var/turf/bT = get_turf(user) - - T.forceMove(bT) - user.forceMove(aT) - - apply_spell_damage(T) - -/spell/targeted/swap/empower_spell() - if(!..()) - return 0 - - amt_eye_blind += 2 - - return "This spell will now blind the target." diff --git a/code/modules/spells/targeted/targeted.dm b/code/modules/spells/targeted/targeted.dm index 46e6e242b26b..733bbf1e5403 100644 --- a/code/modules/spells/targeted/targeted.dm +++ b/code/modules/spells/targeted/targeted.dm @@ -3,7 +3,6 @@ Targeted spells (with the exception of dumbfire) select from all the mobs in the Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are explained in setup.dm */ - /spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob var/max_targets = 1 //leave 0 for unlimited targets in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast @@ -62,10 +61,6 @@ Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are exp for(var/mob/living/M in starting_targets) if(!(spell_flags & INCLUDEUSER) && M == user) continue - if((spell_flags & NOFACTION) && user.faction == M.faction) - continue - if((spell_flags & NONONFACTION) && user.faction != M.faction) - continue if(compatible_mobs && compatible_mobs.len) if(!is_type_in_list(M, compatible_mobs)) continue if(compatible_mobs && compatible_mobs.len && !is_type_in_list(M, compatible_mobs)) @@ -73,12 +68,7 @@ Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are exp possible_targets += M if(possible_targets.len) - if(spell_flags & SELECTABLE) //if we are allowed to choose. see setup.dm for details - var/mob/temp_target = input(user, "Choose the target for the spell.", "Targeting") as null|mob in possible_targets - if(temp_target) - targets += temp_target - else - targets += pick(possible_targets) + targets += pick(possible_targets) //Adds a safety check post-input to make sure those targets are actually in range. @@ -98,28 +88,15 @@ Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are exp continue possible_targets += target - if(spell_flags & SELECTABLE) - for(var/i = 1; i<=max_targets, i++) - if(!possible_targets.len) - break - var/mob/M = input(user, "Choose the target for the spell.", "Targeting") as null|mob in possible_targets - if(!M) - break - if(range != -2) - if(!(M in view_or_range(range, holder, selection_type))) - continue - targets += M - possible_targets -= M - else - for(var/i=1,i<=max_targets,i++) - if(!possible_targets.len) - break - if(target_ignore_prev) - var/target = pick(possible_targets) - possible_targets -= target - targets += target - else - targets += pick(possible_targets) + for(var/i=1,i<=max_targets,i++) + if(!possible_targets.len) + break + if(target_ignore_prev) + var/target = pick(possible_targets) + possible_targets -= target + targets += target + else + targets += pick(possible_targets) if(!(spell_flags & INCLUDEUSER) && (user in targets)) targets -= user @@ -172,4 +149,4 @@ Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are exp ADJ_STATUS(target, STAT_STUTTER, amt_stuttering) if(effect_state) var/obj/o = new /obj/effect/temporary(get_turf(target), effect_duration, 'icons/effects/effects.dmi', effect_state) - o.color = effect_color \ No newline at end of file + o.color = effect_color diff --git a/code/modules/spells/targeted/torment.dm b/code/modules/spells/targeted/torment.dm deleted file mode 100644 index e98836699f30..000000000000 --- a/code/modules/spells/targeted/torment.dm +++ /dev/null @@ -1,34 +0,0 @@ -/spell/targeted/torment - name = "Torment" - desc = "Darkness stabs at the bodies of those around you. All within a medium range will suffer significant pain." - feedback = "TM" - school = "illusion" - charge_max = 150 - spell_flags = NOFACTION - invocation = "Rai Di-Kaal!" - invocation_type = SpI_SHOUT - range = 5 - level_max = list(Sp_TOTAL = 1, Sp_SPEED = 0, Sp_POWER = 1) - cooldown_min = 50 - message = "So much pain! All you can hear is screaming!" - - max_targets = 0 - compatible_mobs = list(/mob/living/human) - - var/loss = 30 - - hud_state = "wiz_horse" - cast_sound = 'sound/magic/cowhead_curse.ogg' - -/spell/targeted/torment/cast(var/list/targets, var/mob/user) - user.spawn_gibber() - for(var/mob/living/human/H in targets) - H.take_damage(loss, PAIN) - -/spell/targeted/torment/empower_spell() - if(!..()) - return 0 - - loss += 30 - - return "[src] will now cause more pain." \ No newline at end of file diff --git a/config/example/configuration.txt b/config/example/configuration.txt index 13bfd6b1c6db..2c603dbfd791 100644 --- a/config/example/configuration.txt +++ b/config/example/configuration.txt @@ -345,22 +345,19 @@ ROUNDSTART_LEVEL_GENERATION ## If uncommented, votes can be called to add extra antags to the round. Uncomment to enable. #ALLOW_EXTRA_ANTAGS -## Remove the # to make rounds which end instantly (Rev, Wizard, Malf) to continue until the shuttle is called or the station is nuked. +## Remove the # to make rounds which end instantly continue until the shuttle is called or the station is nuked. ## Malf and Rev will let the shuttle be called when the antags/protags are dead. ## Uncomment to enable. #CONTINUOUS_ROUNDS -## Spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard. Uncomment to enable. -#FEATURE_OBJECT_SPELL_SYSTEM - ## Allowed modes. -#MODES ["crossfire","cult","extended","heist","mercenary","ninja","revolution","spyvspy","traitor","wizard"] +#MODES ["crossfire","cult","extended","heist","mercenary","ninja","revolution","spyvspy","traitor"] ## Mode names. -#MODE_NAMES {"calamity":"Calamity","extended":"Extended","mercenary":"Mercenary","wizard":"Wizard","cult":"Cult","heist":"Heist","ninja":"Ninja","revolution":"Revolution","traitor":"Traitor","spyvspy":"Spy v. spy","crossfire":"Crossfire"} +#MODE_NAMES {"calamity":"Calamity","extended":"Extended","mercenary":"Mercenary","cult":"Cult","heist":"Heist","ninja":"Ninja","revolution":"Revolution","traitor":"Traitor","spyvspy":"Spy v. spy","crossfire":"Crossfire"} ## Relative probability of each mode. -#PROBABILITIES {"calamity":0,"extended":1,"mercenary":1,"wizard":1,"cult":1,"heist":0,"ninja":0,"revolution":0,"traitor":0,"spyvspy":0,"crossfire":0} +#PROBABILITIES {"calamity":0,"extended":1,"mercenary":1,"cult":1,"heist":0,"ninja":0,"revolution":0,"traitor":0,"spyvspy":0,"crossfire":0} ## If security is prohibited from being most antagonists. Uncomment to enable. #PROTECT_ROLES_FROM_ANTAGONIST @@ -369,7 +366,7 @@ ROUNDSTART_LEVEL_GENERATION #TRAITOR_SCALING ## A list of modes that should be votable. -#VOTABLE_MODES ["crossfire","cult","extended","heist","mercenary","ninja","revolution","secret","spyvspy","traitor","wizard"] +#VOTABLE_MODES ["crossfire","cult","extended","heist","mercenary","ninja","revolution","secret","spyvspy","traitor"] ## # PROTECTED @@ -377,13 +374,13 @@ ROUNDSTART_LEVEL_GENERATION ## ## Password used for authorizing external tools that can apply bans. -#BAN_COMMS_PASSWORD +#BAN_COMMS_PASSWORD ## Password used for authorizing ircbot and other external tools. -#COMMS_PASSWORD +#COMMS_PASSWORD ## Export address where external tools that monitor logins are located. -#LOGIN_EXPORT_ADDR +#LOGIN_EXPORT_ADDR ## # RESOURCES @@ -413,7 +410,7 @@ ROUNDSTART_LEVEL_GENERATION #ABANDON_ALLOWED 1 ## IRC channel to send adminhelps to. Leave blank to disable adminhelps-to-irc. -#ADMIN_IRC +#ADMIN_IRC ## Add a # infront of this if you want to use the SQL based admin system, the legacy system uses admins.txt. You need to set up your database to use the SQL based system. #ADMIN_LEGACY_SYSTEM 1 @@ -431,7 +428,7 @@ ROUNDSTART_LEVEL_GENERATION #AOOC_ALLOWED 1 ## Ban appeals URL - usually for a forum or wherever people should go to contact your admins. -#BANAPPEALS +#BANAPPEALS ## Add a # infront of this if you want to use the SQL based banning system. The legacy systems use the files in the data folder. You need to set up your database to use the SQL based system. #BAN_LEGACY_SYSTEM 1 @@ -452,7 +449,7 @@ ROUNDSTART_LEVEL_GENERATION #DISABLE_WEBHOOK_EMBEDS ## Discord server permanent invite address. -#DISCORDURL +#DISCORDURL ## Comment to disable the dead OOC channel by default. #DOOC_ALLOWED 1 @@ -473,20 +470,20 @@ ROUNDSTART_LEVEL_GENERATION #FORBID_SINGULO_POSSESSION ## Discussion forum address. -#FORUMURL +#FORUMURL ## Defines world FPS. Defaults to 20. ## Can also accept ticklag values (0.9, 0.5, etc) which will automatically be converted to FPS. #FPS 20 ## GitHub address. -#GITHUBURL +#GITHUBURL ## Determines whether or not people without a registered ckey (i.e. guest-*) can connect to your server. Uncomment to enable. #GUESTS_ALLOWED ## Set a hosted by name for UNIX platforms. -#HOSTEDBY +#HOSTEDBY ## Hub visibility: If you want to be visible on the hub, uncomment the below line and be sure that Dream Daemon is set to visible. This can be changed in-round as well with toggle-hub-visibility if Dream Daemon is set correctly. Uncomment to enable. #HUB_VISIBILITY @@ -495,7 +492,7 @@ ROUNDSTART_LEVEL_GENERATION #IRC_BOT_HOST localhost ## GitHub new issue address. -#ISSUEREPORTURL +#ISSUEREPORTURL ## Add a # here if you wish to use the setup where jobs have more access. This is intended for servers with low populations - where there are not enough players to fill all roles, so players need to do more than just one job. Also for servers where they don't want people to hide in their own departments. #JOBS_HAVE_MINIMAL_ACCESS 1 @@ -554,11 +551,11 @@ ROUNDSTART_LEVEL_GENERATION #RESPAWN_DELAY 30 ## Set a server location for world reboot. Don't include the byond://, just give the address and port. -#SERVER +#SERVER ## Set a server URL for the IRC bot to use; like SERVER, don't include the byond:// ## Unlike SERVER, this one shouldn't break auto-reconnect. -#SERVERURL +#SERVERURL ## Server name: This appears at the top of the screen in-game. #SERVER_NAME Nebula 13 @@ -591,7 +588,7 @@ USEALIENWHITELIST #WAIT_FOR_SIGUSR1_REBOOT ## Wiki address. -#WIKIURL +#WIKIURL ## # VOTING diff --git a/maps/antag_spawn/wizard/wizard.dm b/maps/antag_spawn/wizard/wizard.dm deleted file mode 100644 index 4ca75cd145d4..000000000000 --- a/maps/antag_spawn/wizard/wizard.dm +++ /dev/null @@ -1,13 +0,0 @@ -/datum/map_template/ruin/antag_spawn/wizard - name = "Wizard Base" - suffixes = list("wizard/wizard_base.dmm") - apc_test_exempt_areas = list( - /area/map_template/wizard_station = NO_SCRUBBER|NO_VENT|NO_APC - ) - -/area/map_template/wizard_station - name = "\improper Wizard's Den" - icon_state = "yellow" - requires_power = 0 - dynamic_lighting = FALSE - req_access = list(access_wizard) diff --git a/maps/antag_spawn/wizard/wizard_base.dmm b/maps/antag_spawn/wizard/wizard_base.dmm deleted file mode 100644 index abc19fb4422c..000000000000 --- a/maps/antag_spawn/wizard/wizard_base.dmm +++ /dev/null @@ -1,1747 +0,0 @@ -//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -"aa" = ( -/turf/space/infinity, -/area/space) -"ab" = ( -/turf/unsimulated/wall, -/area/map_template/wizard_station) -"ac" = ( -/obj/structure/table/wood, -/obj/item/backpack/cultpack, -/turf/floor/carpet, -/area/map_template/wizard_station) -"ad" = ( -/turf/unsimulated/wall/fakeglass/alt{ - dir = 8 - }, -/area/map_template/wizard_station) -"ae" = ( -/turf/unsimulated/wall/fakeglass{ - dir = 4 - }, -/area/map_template/wizard_station) -"af" = ( -/obj/structure/table/wood, -/obj/item/backpack/satchel/grey/withwallet, -/turf/floor/carpet, -/area/map_template/wizard_station) -"ag" = ( -/turf/floor/carpet, -/area/map_template/wizard_station) -"ah" = ( -/obj/structure/undies_wardrobe, -/turf/floor/carpet, -/area/map_template/wizard_station) -"ai" = ( -/turf/unsimulated/wall/fakeglass{ - dir = 8 - }, -/area/map_template/wizard_station) -"aj" = ( -/obj/structure/door/wood, -/turf/floor/carpet, -/area/map_template/wizard_station) -"ak" = ( -/obj/structure/table/wood, -/obj/item/flashlight/lamp/green{ - on = 0; - pixel_x = -3; - pixel_y = 8 - }, -/obj/item/chems/drinks/flask/barflask, -/turf/floor/carpet, -/area/map_template/wizard_station) -"al" = ( -/obj/structure/table/wood, -/obj/item/dice, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"am" = ( -/obj/structure/bed/padded, -/obj/item/bedsheet/rd, -/turf/floor/carpet, -/area/map_template/wizard_station) -"an" = ( -/obj/structure/table/wood, -/obj/item/dice/d20, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"ao" = ( -/obj/structure/table/wood, -/obj/item/backpack/satchel/grey/withwallet, -/obj/item/clothing/glasses/eyepatch/monocle, -/turf/floor/carpet, -/area/map_template/wizard_station) -"ap" = ( -/mob/living/human/monkey{ - name = "Murphey" - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aq" = ( -/obj/structure/table/wood, -/obj/item/food/chawanmushi, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"ar" = ( -/obj/structure/table/wood, -/obj/abstract/landmark{ - name = "Teleport-Scroll" - }, -/obj/item/toy/figure/ninja, -/turf/floor/carpet, -/area/map_template/wizard_station) -"as" = ( -/obj/structure/table/wood, -/obj/item/megaphone, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"at" = ( -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"au" = ( -/obj/structure/table/wood, -/obj/item/cash/c1, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"av" = ( -/obj/structure/table/wood, -/obj/machinery/chemical_dispenser/bar_soft/full, -/obj/item/radio/intercom/wizard{ - dir = 8; - pixel_x = 22 - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aw" = ( -/obj/machinery/vending/magivend, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"ax" = ( -/obj/structure/bed/chair/wood/wings{ - dir = 8 - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"ay" = ( -/obj/abstract/landmark/start{ - name = "wizard" - }, -/turf/floor/carpet, -/area/map_template/wizard_station) -"az" = ( -/obj/structure/aliumizer, -/turf/floor/carpet, -/area/map_template/wizard_station) -"aA" = ( -/obj/item/radio/intercom/wizard{ - dir = 8; - pixel_x = 22 - }, -/turf/floor/carpet, -/area/map_template/wizard_station) -"aB" = ( -/obj/structure/bookcase{ - name = "Forbidden Knowledge" - }, -/obj/effect/decal/cleanable/cobweb, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aC" = ( -/obj/structure/bookcase{ - name = "bookcase (Tactics)" - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aD" = ( -/obj/structure/bookcase, -/obj/item/book/manual/nuclear, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aE" = ( -/obj/structure/bookcase{ - name = "Forbidden Knowledge" - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aF" = ( -/turf/unsimulated/wall/fakeglass{ - dir = 1 - }, -/area/map_template/wizard_station) -"aG" = ( -/turf/unsimulated/floor/water, -/area/map_template/wizard_station) -"aH" = ( -/obj/structure/bookcase, -/obj/item/book/manual/robotics_cyborgs, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aI" = ( -/obj/item/bikehorn/rubberducky, -/turf/unsimulated/floor/water, -/area/map_template/wizard_station) -"aJ" = ( -/obj/effect/floor_decal/spline/fancy/wood{ - dir = 8 - }, -/obj/machinery/acting/changer/mirror{ - pixel_y = 32 - }, -/turf/unsimulated/floor/freezer, -/area/map_template/wizard_station) -"aK" = ( -/obj/structure/hygiene/toilet{ - pixel_y = 8 - }, -/turf/unsimulated/floor/freezer, -/area/map_template/wizard_station) -"aL" = ( -/obj/structure/table/wood, -/obj/item/radio/intercom/wizard{ - dir = 4; - pixel_x = -22 - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aM" = ( -/obj/structure/closet/coffin, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"aN" = ( -/obj/structure/bed/chair/wood/wings{ - dir = 4 - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aO" = ( -/obj/structure/table/wood, -/obj/item/box/cups, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aP" = ( -/obj/structure/closet, -/obj/item/clothing/suit/wizrobe/red, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/head/wizard/red, -/obj/item/staff/crystal/beacon, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"aQ" = ( -/obj/structure/table/wood, -/obj/item/bag/cash, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aR" = ( -/obj/structure/closet, -/obj/item/clothing/suit/wizrobe/marisa, -/obj/item/clothing/shoes/sandal/marisa, -/obj/item/clothing/head/wizard/marisa, -/obj/item/staff/broom, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"aS" = ( -/obj/structure/table/wood, -/obj/item/box/candles, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aT" = ( -/obj/structure/closet, -/obj/item/clothing/suit/wizrobe/magusblue, -/obj/item/clothing/head/wizard/magus, -/obj/item/staff/crystal, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"aU" = ( -/obj/structure/closet, -/obj/item/chems/drinks/bottle/pwine, -/obj/item/chems/drinks/bottle/agedwhiskey, -/obj/item/chems/drinks/bottle/cognac, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"aV" = ( -/obj/effect/floor_decal/spline/fancy/wood{ - dir = 8 - }, -/turf/unsimulated/floor/freezer, -/area/map_template/wizard_station) -"aW" = ( -/turf/unsimulated/floor/freezer, -/area/map_template/wizard_station) -"aX" = ( -/obj/structure/door/iron, -/turf/unsimulated/floor/freezer, -/area/map_template/wizard_station) -"aY" = ( -/obj/structure/table/wood, -/obj/item/box/fancy/donut, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"aZ" = ( -/obj/structure/door/wood, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"ba" = ( -/obj/structure/door/silver, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"bb" = ( -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"bc" = ( -/turf/unsimulated/wall/fakeglass, -/area/map_template/wizard_station) -"bd" = ( -/obj/item/soap, -/turf/unsimulated/floor/water, -/area/map_template/wizard_station) -"be" = ( -/obj/effect/floor_decal/spline/fancy/wood{ - dir = 8 - }, -/obj/item/radio/intercom/wizard{ - dir = 1; - pixel_y = -30 - }, -/turf/unsimulated/floor/freezer, -/area/map_template/wizard_station) -"bf" = ( -/obj/structure/hygiene/sink{ - dir = 4; - pixel_x = 11 - }, -/obj/structure/mirror{ - pixel_x = 32 - }, -/turf/unsimulated/floor/freezer, -/area/map_template/wizard_station) -"bg" = ( -/obj/structure/table/wood, -/obj/item/chems/glass/bowl/mapped/meatball, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"bh" = ( -/obj/structure/closet, -/obj/item/clothing/jumpsuit/psysuit, -/obj/item/clothing/suit/wizrobe/psypurple, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"bi" = ( -/turf/unsimulated/wall/fakeglass/alt{ - dir = 1 - }, -/area/map_template/wizard_station) -"bj" = ( -/obj/structure/closet, -/obj/item/clothing/shoes/sandal/marisa{ - desc = "A set of fancy shoes that are as functional as they are comfortable."; - name = "Gentlemans Shoes" - }, -/obj/item/clothing/shirt/button/black, -/obj/item/clothing/suit/jacket/vest/gray, -/obj/item/clothing/suit/wizrobe/gentlecoat, -/obj/item/clothing/head/wizard/cap, -/obj/item/cane/fancy, -/obj/item/radio/intercom/wizard{ - dir = 1; - pixel_y = -30 - }, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"bk" = ( -/obj/structure/closet, -/obj/item/clothing/suit/wizrobe/magusred, -/obj/item/clothing/head/wizard/magus, -/obj/item/staff/crystal/beacon, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"bl" = ( -/obj/structure/closet, -/obj/item/briefcase, -/turf/unsimulated/floor/lino, -/area/map_template/wizard_station) -"bm" = ( -/obj/structure/bookcase, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"bn" = ( -/obj/item/radio/intercom/wizard{ - dir = 1; - pixel_y = -30 - }, -/turf/unsimulated/floor/laminate, -/area/map_template/wizard_station) -"bo" = ( -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"bp" = ( -/obj/item/radio/intercom/wizard{ - pixel_y = 20 - }, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"bq" = ( -/obj/structure/fake_pylon, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"br" = ( -/obj/structure/meat_hook, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"bs" = ( -/obj/item/remains/human, -/turf/unsimulated/floor/lava, -/area/map_template/wizard_station) -"bt" = ( -/turf/unsimulated/floor/lava, -/area/map_template/wizard_station) -"bu" = ( -/turf/unsimulated/floor/grass, -/area/map_template/wizard_station) -"bv" = ( -/obj/structure/flora/bush/fullgrass, -/turf/unsimulated/floor/grass, -/area/map_template/wizard_station) -"bw" = ( -/mob/living/simple_animal/hostile/creature{ - name = "Experiment 35b" - }, -/turf/unsimulated/floor/lava, -/area/map_template/wizard_station) -"bx" = ( -/obj/structure/talisman_altar, -/obj/item/knife/ritual, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"by" = ( -/obj/structure/table/wood, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"bz" = ( -/obj/effect/gateway/active/spooky, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"bA" = ( -/obj/structure/table/marble, -/obj/item/flashlight/slime, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"bB" = ( -/mob/living/simple_animal/hostile/goat{ - name = "Experiment 97d" - }, -/turf/unsimulated/floor/grass, -/area/map_template/wizard_station) -"bC" = ( -/obj/structure/flora/bush/grassybush, -/turf/unsimulated/floor/grass, -/area/map_template/wizard_station) -"bD" = ( -/obj/structure/flora/pottedplant/unusual, -/turf/unsimulated/floor/cult, -/area/map_template/wizard_station) -"bE" = ( -/turf/unsimulated/floor/asteroid, -/area/map_template/wizard_station) -"bF" = ( -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/floor/asteroid, -/area/map_template/wizard_station) -"bG" = ( -/mob/living/simple_animal/crab{ - name = "Experiment 68a" - }, -/turf/unsimulated/floor/asteroid, -/area/map_template/wizard_station) -"bH" = ( -/obj/effect/overlay/coconut, -/turf/unsimulated/floor/asteroid, -/area/map_template/wizard_station) - -(1,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(2,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(3,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(4,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(5,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(6,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(7,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(8,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aF -bi -bc -ab -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(9,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aG -aG -bd -ab -aa -aa -aa -aa -aa -aa -aa -ab -bs -bt -bt -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(10,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aI -aG -aG -ab -aa -aa -aa -aa -aa -aa -aa -ab -bt -bt -bt -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(11,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aJ -aV -be -ab -aa -aa -aa -aa -aa -aa -aa -ab -bt -bw -bt -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(12,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aK -aW -bf -ab -aa -aa -aa -aa -aa -aa -aa -ab -bt -bt -bs -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(13,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -aX -ab -ab -ab -aa -aa -aa -aa -aa -ab -ab -aF -bi -bc -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(14,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -aE -aL -at -aw -aD -ab -ab -aa -aa -aa -ab -ab -bq -bo -bx -bo -bq -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -"} -(15,1,1) = {" -aa -aa -aa -ab -ab -ab -ab -ab -ab -aB -at -at -at -at -at -bm -ab -aF -bi -bc -ab -aM -bo -bo -bo -bo -bo -bD -ab -ab -ab -ab -ab -ab -aa -aa -aa -"} -(16,1,1) = {" -aa -aa -aa -ai -ak -ar -ac -af -ab -al -at -aN -as -aN -at -aQ -ab -at -at -bn -ab -bo -bo -ai -ab -ai -bo -bo -ai -bE -bE -bE -bH -ab -aa -aa -aa -"} -(17,1,1) = {" -aa -aa -aa -ad -am -ay -ag -ag -aj -at -at -aq -aY -bg -at -at -ba -at -at -at -ba -bo -bo -ad -bz -ad -bo -by -ad -bE -bE -bG -bE -ab -aa -aa -aa -"} -(18,1,1) = {" -aa -aa -aa -ae -ao -az -aA -ah -ab -an -ap -ax -au -ax -at -aS -ab -at -at -at -ab -bp -bo -ae -ab -ae -bo -bo -ae -bE -bF -bE -bE -ab -aa -aa -aa -"} -(19,1,1) = {" -aa -aa -aa -ab -ab -ab -ab -ab -ab -aC -at -at -at -at -at -bm -ab -aF -bi -bc -ab -aM -bo -bo -bo -bo -bo -bD -ab -ab -ab -ab -ab -ab -aa -aa -aa -"} -(20,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -aC -aO -at -av -aH -ab -ab -aa -aa -aa -ab -ab -br -bo -bA -bo -br -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -"} -(21,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -aZ -ab -ab -ab -aa -aa -aa -aa -aa -ab -ab -aF -bi -bc -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(22,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aP -bb -bh -ab -aa -aa -aa -aa -aa -aa -aa -ab -bu -bu -bC -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(23,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aR -bb -bj -ab -aa -aa -aa -aa -aa -aa -aa -ab -bu -bu -bu -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(24,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aT -bb -bk -ab -aa -aa -aa -aa -aa -aa -aa -ab -bu -bB -bu -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(25,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aU -bb -bl -ab -aa -aa -aa -aa -aa -aa -aa -ab -bv -bu -bu -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(26,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -aF -bi -bc -ab -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(27,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(28,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(29,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(30,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(31,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(32,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(33,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} diff --git a/mods/content/corporate/items/stamps.dm b/mods/content/corporate/items/stamps.dm index cfc6df4dbbf9..35b358297287 100644 --- a/mods/content/corporate/items/stamps.dm +++ b/mods/content/corporate/items/stamps.dm @@ -4,7 +4,7 @@ /obj/item/stamp/hop name = "head of personnel's rubber stamp" - icon = 'icons/obj/items/stamps/stamp_xo.dmi' + icon = 'icons/obj/items/stamps/stamp_cap.dmi' /obj/item/stamp/ce name = "chief engineer's rubber stamp" @@ -32,7 +32,7 @@ /obj/item/stamp/ward name = "warden's rubber stamp" - icon = 'icons/obj/items/stamps/stamp_cos.dmi' + icon = 'icons/obj/items/stamps/stamp_brig.dmi' /obj/item/stamp/internalaffairs name = "internal affairs' rubber stamp" diff --git a/mods/content/psionics/_psionics.dme b/mods/content/psionics/_psionics.dme index 7bf5f2f680d3..df5544cffe15 100644 --- a/mods/content/psionics/_psionics.dme +++ b/mods/content/psionics/_psionics.dme @@ -17,6 +17,7 @@ #include "items\_items.dm" #include "items\brain.dm" #include "items\cerebro_enhancers.dm" +#include "items\clothing.dm" #include "items\coffee_cup.dm" #include "items\foundation_implanter.dm" #include "items\foundation_labcoat.dm" diff --git a/mods/content/psionics/datum/antagonists/paramount.dm b/mods/content/psionics/datum/antagonists/paramount.dm index 5d4da1a87759..d00ab6c5c979 100644 --- a/mods/content/psionics/datum/antagonists/paramount.dm +++ b/mods/content/psionics/datum/antagonists/paramount.dm @@ -17,7 +17,7 @@ name = "Special Role - Paramount Grandmaster" head = /obj/item/clothing/head/helmet/space/psi_amp uniform = /obj/item/clothing/jumpsuit/psysuit - suit = /obj/item/clothing/suit/wizrobe/psypurple + suit = /obj/item/clothing/suit/paramount shoes = /obj/item/clothing/shoes/jackboots back = /obj/item/backpack/satchel gloves = /obj/item/clothing/gloves/grey diff --git a/mods/content/psionics/datum/chems.dm b/mods/content/psionics/datum/chems.dm index c19db16e091e..1bc713de0eb3 100644 --- a/mods/content/psionics/datum/chems.dm +++ b/mods/content/psionics/datum/chems.dm @@ -1,6 +1,5 @@ /decl/material/liquid/crystal_agent/do_material_check(var/mob/living/M) - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - . = (M.get_ability_handler(/datum/ability_handler/psionics) || (M.mind && wizards.is_antagonist(M.mind))) ? /decl/material/nullglass : ..() + . = !!M.get_ability_handler(/datum/ability_handler/psionics) ? /decl/material/nullglass : ..() /decl/material/liquid/glowsap/gleam/affect_overdose(mob/living/M, total_dose) ..() @@ -13,15 +12,12 @@ required_reagents = list(/decl/material/liquid/blood = 15, /decl/material/liquid/crystal_agent = 1) result_amount = 1 -// TODO: #if defined(GAMEMODE_PACK_CULT) && defined(GAMEMODE_PACK_WIZARD) -// once wizard is modpacked #ifdef GAMEMODE_PACK_CULT /decl/chemical_reaction/synthesis/nullglass/get_alternate_reaction_indicator(var/datum/reagents/holder) var/list/blood_data = REAGENT_DATA(holder, /decl/material/liquid/blood) var/weakref/donor_ref = LAZYACCESS(blood_data, DATA_BLOOD_DONOR) var/mob/living/donor = donor_ref?.resolve() - var/decl/special_role/wizard/wizards = GET_DECL(/decl/special_role/wizard) - . = (istype(donor) && (!!donor.get_ability_handler(/datum/ability_handler/psionics) || (donor.mind && wizards.is_antagonist(donor.mind)))) + . = istype(donor) && !!donor.get_ability_handler(/datum/ability_handler/psionics) #endif /decl/chemical_reaction/synthesis/nullglass/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) diff --git a/mods/content/psionics/items/clothing.dm b/mods/content/psionics/items/clothing.dm new file mode 100644 index 000000000000..32e5786c2440 --- /dev/null +++ b/mods/content/psionics/items/clothing.dm @@ -0,0 +1,16 @@ +/obj/item/clothing/suit/paramount + name = "purple robes" + desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." + icon = 'icons/clothing/suits/wizard/psy.dmi' + gender = PLURAL + permeability_coefficient = 0.01 + armor = list( + ARMOR_MELEE = ARMOR_MELEE_RESISTANT, + ARMOR_BULLET = ARMOR_BALLISTIC_SMALL, + ARMOR_LASER = ARMOR_LASER_SMALL, + ARMOR_ENERGY = ARMOR_ENERGY_SMALL, + ARMOR_BOMB = ARMOR_BOMB_PADDED, + ARMOR_BIO = ARMOR_BIO_MINOR, + ARMOR_RAD = ARMOR_RAD_MINOR + ) + siemens_coefficient = 0.8 \ No newline at end of file diff --git a/mods/gamemodes/cult/_cult.dme b/mods/gamemodes/cult/_cult.dme index e35ffed9db68..82d4e19aed59 100644 --- a/mods/gamemodes/cult/_cult.dme +++ b/mods/gamemodes/cult/_cult.dme @@ -21,7 +21,6 @@ #include "special_role.dm" #include "structures.dm" #include "talisman.dm" -#include "wizard.dm" #include "cultify\de-cultify.dm" #include "cultify\defile.dm" #include "cultify\mob.dm" diff --git a/mods/gamemodes/cult/items.dm b/mods/gamemodes/cult/items.dm index 1674cdb92eb8..2b2e826617c7 100644 --- a/mods/gamemodes/cult/items.dm +++ b/mods/gamemodes/cult/items.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/items/weapon/swords/cult.dmi' material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME -// separated into a proc so that subtypes can modify it +// separated into a proc so that modpacks can modify it /obj/item/sword/cultblade/proc/can_use_safely(mob/living/user) return iscultist(user) @@ -21,9 +21,9 @@ affecting = GET_EXTERNAL_ORGAN(H, zone) if(affecting) - to_chat(user, "An unexplicable force rips through your [affecting.name], tearing the sword from your grasp!") + to_chat(user, SPAN_DANGER("An unexplicable force rips through your [affecting.name], tearing the sword from your grasp!")) else - to_chat(user, "An unexplicable force rips through you, tearing the sword from your grasp!") + to_chat(user, SPAN_DANGER("An unexplicable force rips through you, tearing the sword from your grasp!")) //random amount of damage between half of the blade's force and the full force of the blade. var/force = get_attack_force(user) diff --git a/mods/gamemodes/cult/overrides.dm b/mods/gamemodes/cult/overrides.dm index 874dc92049c7..2077c9f0b1e1 100644 --- a/mods/gamemodes/cult/overrides.dm +++ b/mods/gamemodes/cult/overrides.dm @@ -14,19 +14,6 @@ else playsound(src, 'sound/effects/ghost2.ogg', 10, 5) -/datum/trader/ship/unique/wizard/New() - possible_wanted_items |= list( - /mob/living/simple_animal/construct = TRADER_SUBTYPES_ONLY, - /obj/item/sword/cultblade = TRADER_THIS_TYPE, - /obj/item/clothing/head/culthood = TRADER_ALL, - /obj/item/clothing/suit/space/cult = TRADER_ALL, - /obj/item/clothing/suit/cultrobes = TRADER_ALL, - /obj/item/clothing/head/helmet/space/cult = TRADER_ALL, - /obj/structure/cult = TRADER_SUBTYPES_ONLY, - /obj/structure/constructshell = TRADER_ALL - ) - ..() - /datum/trader/ship/clothingshop/hatglovesaccessories/New() possible_trading_items[/obj/item/clothing/head/culthood] = TRADER_BLACKLIST_ALL diff --git a/mods/gamemodes/cult/wizard.dm b/mods/gamemodes/cult/wizard.dm deleted file mode 100644 index b29fa023f65c..000000000000 --- a/mods/gamemodes/cult/wizard.dm +++ /dev/null @@ -1,47 +0,0 @@ -// #ifdef GAMEMODE_PACK_WIZARD -// todo: add wizard gamemode define check once it's modularized -/decl/modpack/cult/post_initialize() - . = ..() - global.artefact_feedback[/obj/structure/closet/wizard/souls] = "SS" - -/datum/spellbook/standard/New() - spells[/obj/structure/closet/wizard/souls] = 1 - ..() - -/datum/spellbook/druid/New() - spells[/obj/structure/closet/wizard/souls] = 1 - ..() - -/obj/structure/closet/wizard/souls - name = "Soul Shard Belt" - desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot. This also includes the spell Artificer, used to create the shells used in construct creation." - -/obj/structure/closet/wizard/souls/WillContain() - return list( - /obj/item/contract/boon/wizard/artificer, - /obj/item/belt/soulstone/full, - ) - -/datum/storage/belt/soulstone - can_hold = list( - /obj/item/soulstone - ) - -/obj/item/belt/soulstone - name = "soul stone belt" - desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away." - icon = 'icons/clothing/belt/soulstones.dmi' - storage = /datum/storage/belt/soulstone - -/obj/item/belt/soulstone/full/WillContain() - return list(/obj/item/soulstone = max(1, storage?.storage_slots)) - -/obj/item/contract/boon/wizard/artificer - path = /spell/aoe_turf/conjure/construct - desc = "This contract has a passage dedicated to an entity known as 'Nar-Sie'." - -/obj/item/magic_rock - material = /decl/material/solid/stone/cult - -/obj/item/summoning_stone - material = /decl/material/solid/stone/cult \ No newline at end of file diff --git a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm index b6c5366944c3..de0b3b76a702 100644 --- a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm +++ b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm @@ -47,8 +47,5 @@ /obj/item/clothing/suit/space/void _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/nasa/suit.dmi' -/obj/item/clothing/suit/space/void/wizard - _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/wizard/suit.dmi' - /obj/item/clothing/suit/space/void/excavation _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/excavation/suit.dmi' diff --git a/mods/species/vox/_vox.dme b/mods/species/vox/_vox.dme index 99bd0717bac5..83d61f50f51f 100644 --- a/mods/species/vox/_vox.dme +++ b/mods/species/vox/_vox.dme @@ -5,7 +5,6 @@ #include "mobs_vox.dm" #include "organs_vox.dm" #include "datum\accessories.dm" -#include "datum\antagonism.dm" #include "datum\cultures_vox.dm" #include "datum\descriptors_vox.dm" #include "datum\factions_vox.dm" diff --git a/mods/species/vox/datum/antagonism.dm b/mods/species/vox/datum/antagonism.dm deleted file mode 100644 index 4b8c182ee3a0..000000000000 --- a/mods/species/vox/datum/antagonism.dm +++ /dev/null @@ -1,4 +0,0 @@ -// Wizard -/obj/item/magic_rock/Initialize(ml, material_key) - LAZYSET(potentials, SPECIES_VOX, /spell/targeted/shapeshift/true_form) - . = ..() diff --git a/mods/species/vox/gear/gun.dm b/mods/species/vox/gear/gun.dm index c4f62db86c9c..a2e5706de5d8 100644 --- a/mods/species/vox/gear/gun.dm +++ b/mods/species/vox/gear/gun.dm @@ -78,27 +78,3 @@ /decl/material/liquid/sedatives, /decl/material/liquid/paralytics ) - -/spell/targeted/shapeshift/true_form - name = "True Form" - desc = "Pay respect to your heritage. Become what you once were." - - school = "racial" - spell_flags = INCLUDEUSER - invocation_type = SpI_EMOTE - range = -1 - invocation = "begins to grow!" - charge_max = 1200 //2 minutes - duration = 300 //30 seconds - - smoke_amt = 5 - smoke_spread = 1 - - possible_transformations = list(/mob/living/simple_animal/hostile/parrot/space/lesser) - - hud_state = "wiz_vox" - - cast_sound = 'sound/voice/shriek1.ogg' - revert_sound = 'sound/voice/shriek1.ogg' - - drop_items = 0 diff --git a/nebula.dme b/nebula.dme index d286bdf0899f..04deff7ede21 100644 --- a/nebula.dme +++ b/nebula.dme @@ -765,7 +765,6 @@ #include "code\game\antagonist\outsider\actors.dm" #include "code\game\antagonist\outsider\ert.dm" #include "code\game\antagonist\outsider\mercenary.dm" -#include "code\game\antagonist\outsider\wizard.dm" #include "code\game\antagonist\station\provocateur.dm" #include "code\game\antagonist\station\thrall.dm" #include "code\game\area\area_abstract.dm" @@ -801,14 +800,6 @@ #include "code\game\gamemodes\objectives\objective_protect.dm" #include "code\game\gamemodes\objectives\objective_rev.dm" #include "code\game\gamemodes\objectives\objective_steal.dm" -#include "code\game\gamemodes\wizard\wizard.dm" -#include "code\game\gamemodes\wizard\wizard_props.dm" -#include "code\game\gamemodes\wizard\servant_items\caretaker.dm" -#include "code\game\gamemodes\wizard\servant_items\champion.dm" -#include "code\game\gamemodes\wizard\servant_items\familiar.dm" -#include "code\game\gamemodes\wizard\servant_items\fiend.dm" -#include "code\game\gamemodes\wizard\servant_items\infiltrator.dm" -#include "code\game\gamemodes\wizard\servant_items\overseer.dm" #include "code\game\jobs\_access_defs.dm" #include "code\game\jobs\access.dm" #include "code\game\jobs\access_datum.dm" @@ -1260,7 +1251,6 @@ #include "code\game\objects\items\weapons\RCD.dm" #include "code\game\objects\items\weapons\RPD.dm" #include "code\game\objects\items\weapons\RSF.dm" -#include "code\game\objects\items\weapons\scrolls.dm" #include "code\game\objects\items\weapons\secrets_disk.dm" #include "code\game\objects\items\weapons\shields.dm" #include "code\game\objects\items\weapons\soap.dm" @@ -2007,6 +1997,7 @@ #include "code\modules\clothing\head\misc_special.dm" #include "code\modules\clothing\head\security.dm" #include "code\modules\clothing\head\soft_caps.dm" +#include "code\modules\clothing\head\wizard.dm" #include "code\modules\clothing\jumpsuits\_jumpsuit.dm" #include "code\modules\clothing\jumpsuits\color.dm" #include "code\modules\clothing\jumpsuits\job.dm" @@ -2097,7 +2088,6 @@ #include "code\modules\clothing\spacesuits\void\misc.dm" #include "code\modules\clothing\spacesuits\void\station.dm" #include "code\modules\clothing\spacesuits\void\void.dm" -#include "code\modules\clothing\spacesuits\void\wizard.dm" #include "code\modules\clothing\suits\_suit.dm" #include "code\modules\clothing\suits\_suit_hood.dm" #include "code\modules\clothing\suits\alien.dm" @@ -2108,6 +2098,7 @@ #include "code\modules\clothing\suits\jobs.dm" #include "code\modules\clothing\suits\labcoat.dm" #include "code\modules\clothing\suits\mantle.dm" +#include "code\modules\clothing\suits\misc.dm" #include "code\modules\clothing\suits\miscellaneous.dm" #include "code\modules\clothing\suits\poncho.dm" #include "code\modules\clothing\suits\robes.dm" @@ -2116,7 +2107,7 @@ #include "code\modules\clothing\suits\straightjacket.dm" #include "code\modules\clothing\suits\toggles.dm" #include "code\modules\clothing\suits\utility.dm" -#include "code\modules\clothing\suits\wiz_robe.dm" +#include "code\modules\clothing\suits\wizard.dm" #include "code\modules\clothing\suits\armor\_armor.dm" #include "code\modules\clothing\suits\armor\adminbus_and_memes.dm" #include "code\modules\clothing\suits\armor\bulletproof.dm" @@ -3008,7 +2999,6 @@ #include "code\modules\mob\living\simple_animal\aquatic\aquatic_fish.dm" #include "code\modules\mob\living\simple_animal\aquatic\aquatic_sharks.dm" #include "code\modules\mob\living\simple_animal\crow\crow.dm" -#include "code\modules\mob\living\simple_animal\familiars\familiars.dm" #include "code\modules\mob\living\simple_animal\friendly\cat.dm" #include "code\modules\mob\living\simple_animal\friendly\corgi.dm" #include "code\modules\mob\living\simple_animal\friendly\crab.dm" @@ -3773,84 +3763,14 @@ #include "code\modules\species\station\human_bodytypes.dm" #include "code\modules\species\station\monkey.dm" #include "code\modules\species\station\monkey_bodytypes.dm" -#include "code\modules\spells\artifacts.dm" #include "code\modules\spells\construct_spells.dm" -#include "code\modules\spells\contracts.dm" -#include "code\modules\spells\no_clothes.dm" -#include "code\modules\spells\racial_wizard.dm" #include "code\modules\spells\spell_code.dm" -#include "code\modules\spells\spell_projectile.dm" -#include "code\modules\spells\spellbook.dm" #include "code\modules\spells\spells.dm" #include "code\modules\spells\aoe_turf\aoe_turf.dm" -#include "code\modules\spells\aoe_turf\blink.dm" -#include "code\modules\spells\aoe_turf\charge.dm" -#include "code\modules\spells\aoe_turf\disable_tech.dm" -#include "code\modules\spells\aoe_turf\drain_blood.dm" -#include "code\modules\spells\aoe_turf\exchange_wounds.dm" -#include "code\modules\spells\aoe_turf\knock.dm" -#include "code\modules\spells\aoe_turf\smoke.dm" -#include "code\modules\spells\aoe_turf\summons.dm" #include "code\modules\spells\aoe_turf\conjure\conjure.dm" -#include "code\modules\spells\aoe_turf\conjure\druidic_spells.dm" -#include "code\modules\spells\aoe_turf\conjure\faithful_hound.dm" -#include "code\modules\spells\aoe_turf\conjure\force_portal.dm" -#include "code\modules\spells\aoe_turf\conjure\forcewall.dm" -#include "code\modules\spells\aoe_turf\conjure\grove.dm" -#include "code\modules\spells\artifacts\spellbound_servants.dm" -#include "code\modules\spells\artifacts\storage.dm" -#include "code\modules\spells\general\acid_spray.dm" -#include "code\modules\spells\general\area_teleport.dm" -#include "code\modules\spells\general\camera_vision.dm" -#include "code\modules\spells\general\contract_spells.dm" -#include "code\modules\spells\general\create_air.dm" -#include "code\modules\spells\general\invisibility.dm" -#include "code\modules\spells\general\mark_recall.dm" -#include "code\modules\spells\general\portal_teleport.dm" -#include "code\modules\spells\general\radiant_aura.dm" -#include "code\modules\spells\general\return_master.dm" -#include "code\modules\spells\general\toggle_armor.dm" -#include "code\modules\spells\hand\blood_shards.dm" -#include "code\modules\spells\hand\burning_grip.dm" -#include "code\modules\spells\hand\entangle.dm" -#include "code\modules\spells\hand\hand.dm" -#include "code\modules\spells\hand\hand_item.dm" -#include "code\modules\spells\hand\slippery_surface.dm" -#include "code\modules\spells\hand\sunwrath.dm" -#include "code\modules\spells\spellbook\battlemage.dm" -#include "code\modules\spells\spellbook\cleric.dm" -#include "code\modules\spells\spellbook\druid.dm" -#include "code\modules\spells\spellbook\spatial.dm" -#include "code\modules\spells\spellbook\standard.dm" -#include "code\modules\spells\spellbook\student.dm" -#include "code\modules\spells\targeted\analyze.dm" -#include "code\modules\spells\targeted\blood_boil.dm" -#include "code\modules\spells\targeted\cleric_spells.dm" #include "code\modules\spells\targeted\ethereal_jaunt.dm" -#include "code\modules\spells\targeted\exude_pleasantness.dm" -#include "code\modules\spells\targeted\genetic.dm" -#include "code\modules\spells\targeted\glimpse_of_eternity.dm" -#include "code\modules\spells\targeted\shapeshift.dm" -#include "code\modules\spells\targeted\shatter_mind.dm" #include "code\modules\spells\targeted\shift.dm" -#include "code\modules\spells\targeted\subjugate.dm" -#include "code\modules\spells\targeted\swap.dm" #include "code\modules\spells\targeted\targeted.dm" -#include "code\modules\spells\targeted\torment.dm" -#include "code\modules\spells\targeted\equip\burning_touch.dm" -#include "code\modules\spells\targeted\equip\dyrnwyn.dm" -#include "code\modules\spells\targeted\equip\equip.dm" -#include "code\modules\spells\targeted\equip\holy_relic.dm" -#include "code\modules\spells\targeted\equip\horsemask.dm" -#include "code\modules\spells\targeted\equip\party_hardy.dm" -#include "code\modules\spells\targeted\equip\seed.dm" -#include "code\modules\spells\targeted\equip\shield.dm" -#include "code\modules\spells\targeted\projectile\dumbfire.dm" -#include "code\modules\spells\targeted\projectile\fireball.dm" -#include "code\modules\spells\targeted\projectile\magic_missile.dm" -#include "code\modules\spells\targeted\projectile\passage.dm" -#include "code\modules\spells\targeted\projectile\projectile.dm" -#include "code\modules\spells\targeted\projectile\stuncuff.dm" #include "code\modules\sprite_accessories\_accessory.dm" #include "code\modules\sprite_accessories\_accessory_category.dm" #include "code\modules\sprite_accessories\cosmetics\_accessory_cosmetics.dm" @@ -4126,7 +4046,6 @@ #include "maps\_map_include.dm" #include "maps\antag_spawn\ert\ert.dm" #include "maps\antag_spawn\mercenary\mercenary.dm" -#include "maps\antag_spawn\wizard\wizard.dm" #include "maps\away_sites_testing\away_sites_testing_define.dm" #include "maps\example\example_define.dm" #include "maps\exodus\exodus_define.dm" From e34ae1bab19275c65936251ca2659e30ba934c44 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 2 Nov 2024 15:05:53 +1100 Subject: [PATCH 0049/1331] Adding SSpathfinding to handle AStar pathfinding requests. --- .gitignore | 1 + code/__defines/ai.dm | 2 + code/__defines/subsystem-priority.dm | 1 + .../subsystems/mob_ai/auto_movement.dm | 5 +- code/controllers/subsystems/pathfinding.dm | 123 ++++++++++++++ code/datums/ai/_ai.dm | 154 +++--------------- code/datums/ai/_ai_enemies.dm | 35 ++++ code/datums/ai/_ai_friends.dm | 25 +++ code/datums/ai/_ai_memory.dm | 7 + code/datums/ai/_ai_pathfinding.dm | 29 ++++ code/datums/ai/_ai_stance.dm | 39 +++-- code/datums/ai/_ai_targets.dm | 77 +++++++++ code/datums/ai/_ai_wander.dm | 5 + code/datums/ai/aggressive.dm | 104 +++++------- code/datums/ai/beast.dm | 4 +- code/datums/ai/commanded.dm | 10 +- code/datums/movement/automove_controller.dm | 21 ++- code/game/atoms_movable.dm | 3 + code/game/turfs/turf_navigation.dm | 62 +++++++ .../integrated_electronics/subtypes/smart.dm | 46 ++++-- code/modules/mob/living/bot/bot.dm | 101 ++---------- code/modules/mob/living/bot/cleanbot.dm | 9 +- code/modules/mob/living/bot/farmbot.dm | 18 -- code/modules/mob/living/bot/mulebot.dm | 2 +- code/modules/mob/living/living.dm | 5 - .../mob/living/simple_animal/hostile/bear.dm | 4 +- .../hostile/commanded/nanomachines.dm | 4 +- .../hostile/giant_spiders/ai_guard.dm | 2 +- .../simple_animal/hostile/hivebots/megabot.dm | 2 +- .../mob/living/simple_animal/hostile/mimic.dm | 2 +- .../simple_animal/hostile/retaliate/drone.dm | 11 +- .../mob/living/simple_animal/hostile/slug.dm | 13 +- code/modules/mob/mob.dm | 5 + code/modules/mob/mob_automove.dm | 44 ++++- code/procs/{AStar.dm => pathfinding.dm} | 26 ++- nebula.dme | 10 +- 36 files changed, 631 insertions(+), 380 deletions(-) create mode 100644 code/controllers/subsystems/pathfinding.dm create mode 100644 code/datums/ai/_ai_enemies.dm create mode 100644 code/datums/ai/_ai_friends.dm create mode 100644 code/datums/ai/_ai_memory.dm create mode 100644 code/datums/ai/_ai_pathfinding.dm create mode 100644 code/datums/ai/_ai_targets.dm create mode 100644 code/datums/ai/_ai_wander.dm create mode 100644 code/game/turfs/turf_navigation.dm rename code/procs/{AStar.dm => pathfinding.dm} (78%) diff --git a/.gitignore b/.gitignore index 9d2b3ae5aa14..52ee73c38355 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # ignore misc BYOND files Thumbs.db +Thumbs.db:encryptable *.log *.int *.rsc diff --git a/code/__defines/ai.dm b/code/__defines/ai.dm index 2123e2e8839b..03fa03fa9e88 100644 --- a/code/__defines/ai.dm +++ b/code/__defines/ai.dm @@ -6,6 +6,8 @@ #define STANCE_ATTACKING /decl/mob_controller_stance/attacking #define STANCE_TIRED /decl/mob_controller_stance/tired #define STANCE_CONTAINED /decl/mob_controller_stance/contained +#define STANCE_BUSY /decl/mob_controller_stance/busy + //basically 'do nothing' #define STANCE_COMMANDED_STOP /decl/mob_controller_stance/commanded/stop //follows a target diff --git a/code/__defines/subsystem-priority.dm b/code/__defines/subsystem-priority.dm index fc69009e88b8..cd44840622aa 100644 --- a/code/__defines/subsystem-priority.dm +++ b/code/__defines/subsystem-priority.dm @@ -39,6 +39,7 @@ #define SS_PRIORITY_GHOST_IMAGES 10 // Updates ghost client images. #define SS_PRIORITY_ZCOPY 10 // Builds appearances for Z-Mimic. #define SS_PRIORITY_PROJECTILES 10 // Projectile processing! +#define SS_PRIORITY_PATHFINDING 10 // Processing pathfinding requests // SS_BACKGROUND #define SS_PRIORITY_OBJECTS 100 // processing_objects processing. diff --git a/code/controllers/subsystems/mob_ai/auto_movement.dm b/code/controllers/subsystems/mob_ai/auto_movement.dm index 7e2990c1cb8d..843f37ab2d05 100644 --- a/code/controllers/subsystems/mob_ai/auto_movement.dm +++ b/code/controllers/subsystems/mob_ai/auto_movement.dm @@ -51,5 +51,8 @@ SUBSYSTEM_DEF(automove) if(controller.handle_mover(mover, moving_metadata[mover]) == PROCESS_KILL && !QDELETED(mover)) mover.stop_automove() if(MC_TICK_CHECK) - processing_atoms.Cut(1, i+1) + if(i >= length(processing_atoms)) + processing_atoms.Cut() + else + processing_atoms.Cut(1, i+1) return diff --git a/code/controllers/subsystems/pathfinding.dm b/code/controllers/subsystems/pathfinding.dm new file mode 100644 index 000000000000..d6e63f4191f1 --- /dev/null +++ b/code/controllers/subsystems/pathfinding.dm @@ -0,0 +1,123 @@ +SUBSYSTEM_DEF(pathfinding) + name = "Pathfinding" + priority = SS_PRIORITY_PATHFINDING + init_order = SS_INIT_MISC_LATE + wait = 1 + + var/list/pending = list() + var/list/processing = list() + var/list/mover_metadata = list() + + VAR_PRIVATE/static/_default_adjacency_call = TYPE_PROC_REF(/turf, CardinalTurfsWithAccess) + VAR_PRIVATE/static/_default_distance_call = TYPE_PROC_REF(/turf, Distance) + +/atom/movable + var/waiting_for_path + +/atom/movable/proc/path_found(list/path) + SHOULD_CALL_PARENT(TRUE) + waiting_for_path = null + +/atom/movable/proc/path_not_found() + SHOULD_CALL_PARENT(TRUE) + waiting_for_path = null + +/datum/controller/subsystem/pathfinding/proc/dequeue_mover(atom/movable/mover, include_processing = TRUE) + if(!istype(mover)) + return + mover.waiting_for_path = null + pending -= mover + mover_metadata -= mover + if(include_processing) + processing -= mover + +// Hook to allow legacy use of AStar* to reuse the callback refs +/datum/controller/subsystem/pathfinding/proc/find_path_immediate(start, end, max_nodes, max_node_depth = 30, min_target_dist = 0, min_node_dist, id, datum/exclude, check_tick = FALSE) + return find_path_astar(start, end, _default_adjacency_call, _default_distance_call, max_nodes, max_node_depth, min_target_dist, min_node_dist, id, exclude, check_tick) + +/datum/controller/subsystem/pathfinding/proc/enqueue_mover(atom/movable/mover, atom/target, datum/pathfinding_metadata/metadata) + if(!istype(mover) || mover.waiting_for_path) + return FALSE + if(!istype(target)) + return FALSE + pending |= mover + pending[mover] = target + if(istype(metadata)) + mover_metadata[mover] = metadata + mover.waiting_for_path = world.time + return TRUE + +/datum/controller/subsystem/pathfinding/stat_entry(msg) + . = ..("Q:[length(pending)] P:[length(processing)]") + +/datum/controller/subsystem/pathfinding/fire(resumed) + + if(!resumed) + processing = pending?.Copy() + + var/atom/movable/mover + var/atom/target + var/datum/pathfinding_metadata/metadata + var/i = 0 + + while(i < processing.len) + + i++ + mover = processing[i] + target = processing[mover] + metadata = mover_metadata[mover] + dequeue_mover(mover, include_processing = FALSE) + + if(!QDELETED(mover) && !QDELETED(target)) + try_find_path(mover, target, metadata) + + if (MC_TICK_CHECK) + processing.Cut(1, i+1) + return + + processing.Cut() + +/datum/controller/subsystem/pathfinding/proc/try_find_path(atom/movable/mover, atom/target, datum/pathfinding_metadata/metadata, adjacency_call = _default_adjacency_call, distance_call = _default_distance_call) + + var/started_pathing = world.time + mover.waiting_for_path = started_pathing + + var/list/path = find_path_astar( + get_turf(mover), + target, + adjacency_call, + distance_call, + (metadata?.max_nodes || null), + (metadata?.max_node_depth || 250), + metadata?.min_target_dist, + metadata?.min_node_depth, + (metadata?.id || mover.GetIdCard()), + metadata?.obstacle, + check_tick = TRUE + ) + if(mover.waiting_for_path == started_pathing) + if(length(path)) + mover.path_found(path) + else + mover.path_not_found() + +/datum/pathfinding_metadata + var/max_nodes = null + var/max_node_depth = 250 + var/atom/id = null + var/min_target_dist = null + var/min_node_depth = null + var/obstacle = null + +/datum/pathfinding_metadata/New(_max_nodes, _max_node_depth, _id, _min_target_dist, _min_node_depth, _obstacle) + + id = _id + obstacle = _obstacle + max_nodes = _max_nodes + + if(!isnull(_max_node_depth)) + max_node_depth = _max_node_depth + if(!isnull(_min_target_dist)) + min_target_dist = _min_target_dist + if(!isnull(_min_node_depth)) + min_node_depth = _min_node_depth diff --git a/code/datums/ai/_ai.dm b/code/datums/ai/_ai.dm index 6e40fd7ab35c..9c943dadb4fb 100644 --- a/code/datums/ai/_ai.dm +++ b/code/datums/ai/_ai.dm @@ -62,6 +62,23 @@ /// Aggressive AI var; defined here for reference without casting. var/try_destroy_surroundings = FALSE + /// Reference to the atom we are targetting. + var/weakref/target_ref + + /// Current path for A* pathfinding. + var/list/executing_path + /// A counter for times we have failed to progress along our path. + var/path_frustration = 0 + /// A list of any obstacles we should path around in future. + var/list/path_obstacles = null + + /// Radius of target scan area when looking for valid targets. Set to 0 to disable target scanning. + var/target_scan_distance = 0 + /// Time tracker for next target scan. + var/next_target_scan_time + /// How long minimum between scans. + var/target_scan_delay = 1 SECOND + /datum/mob_controller/New(var/mob/living/target_body) body = target_body if(expected_type && !istype(body, expected_type)) @@ -71,6 +88,7 @@ /datum/mob_controller/Destroy() LAZYCLEARLIST(_friends) LAZYCLEARLIST(_enemies) + set_target(null) if(is_processing) STOP_PROCESSING(SSmob_ai, src) if(body) @@ -79,12 +97,6 @@ body = null . = ..() -/datum/mob_controller/proc/get_automove_target(datum/automove_metadata/metadata) - return null - -/datum/mob_controller/proc/can_do_automated_move(variant_move_delay) - return body && !body.client - /datum/mob_controller/proc/can_process() if(!body || !body.loc || ((body.client || body.mind) && !(body.status_flags & ENABLE_AI))) return FALSE @@ -111,13 +123,13 @@ // This is the place to actually do work in the AI. /datum/mob_controller/proc/do_process() SHOULD_CALL_PARENT(TRUE) - if(!QDELETED(body) && !QDELETED(src)) + if(get_stance() != STANCE_BUSY && !QDELETED(body) && !QDELETED(src)) if(!body.stat) try_unbuckle() try_wander() try_bark() // Recheck in case we walked into lava or something during wandering. - return !QDELETED(body) && !QDELETED(src) + return get_stance() != STANCE_BUSY && !QDELETED(body) && !QDELETED(src) return TRUE return FALSE @@ -132,16 +144,6 @@ else if(prob(25)) body.visible_message(SPAN_WARNING("\The [body] struggles against \the [body.buckled]!")) - -/datum/mob_controller/proc/get_activity() - return current_activity - -/datum/mob_controller/proc/set_activity(new_activity) - if(current_activity != new_activity) - current_activity = new_activity - return TRUE - return FALSE - // The mob will periodically sit up or step 1 tile in a random direction. /datum/mob_controller/proc/try_wander() //Movement @@ -185,133 +187,17 @@ else if(ispath(do_emote, /decl/emote)) body.emote(do_emote) -/datum/mob_controller/proc/get_target() - return null - -/datum/mob_controller/proc/set_target(atom/new_target) - return - -/datum/mob_controller/proc/find_target() - return - -/datum/mob_controller/proc/valid_target(var/atom/A) - return - -/datum/mob_controller/proc/move_to_target(var/move_only = FALSE) - return - -/datum/mob_controller/proc/stop_wandering() - stop_wander = TRUE - -/datum/mob_controller/proc/resume_wandering() - stop_wander = FALSE - -/datum/mob_controller/proc/set_stance(new_stance) - if(stance != new_stance) - stance = new_stance - return TRUE - return FALSE - -/datum/mob_controller/proc/get_stance() - return stance - -/datum/mob_controller/proc/list_targets(var/dist = 7) - return - -/datum/mob_controller/proc/open_fire() - return - -/datum/mob_controller/proc/startle() - if(QDELETED(body) || body.stat != UNCONSCIOUS) - return - body.set_stat(CONSCIOUS) - if(body.current_posture?.prone) - body.set_posture(/decl/posture/standing) - -/datum/mob_controller/proc/retaliate(atom/source) - SHOULD_CALL_PARENT(TRUE) - if(!istype(body) || body.stat == DEAD) - return FALSE - startle() - if(isliving(source)) - remove_friend(source) - return TRUE - /datum/mob_controller/proc/destroy_surroundings() return -/datum/mob_controller/proc/lose_target() - return - -/datum/mob_controller/proc/lost_target() - return - /datum/mob_controller/proc/handle_death(gibbed) return -/datum/mob_controller/proc/pacify(mob/user) - lose_target() - add_friend(user) - -// General-purpose memorise proc, used by /commanded -/datum/mob_controller/proc/memorise(mob/speaker, message) - return - -// General-purpose memory checking proc, used by /faithful_hound -/datum/mob_controller/proc/check_memory(mob/speaker, message) - return FALSE - /// General-purpose scooping reaction proc, used by /passive. /// Returns TRUE if the scoop should proceed, FALSE if it should be canceled. /datum/mob_controller/proc/scooped_by(mob/initiator) return TRUE -// Enemy tracking - used on /aggressive -/datum/mob_controller/proc/get_enemies() - return _enemies - -/datum/mob_controller/proc/add_enemy(mob/enemy) - if(istype(enemy)) - LAZYDISTINCTADD(_enemies, weakref(enemy)) - -/datum/mob_controller/proc/add_enemies(list/enemies) - for(var/thing in enemies) - if(ismob(thing)) - add_friend(thing) - else if(istype(thing, /weakref)) - LAZYDISTINCTADD(_enemies, thing) - -/datum/mob_controller/proc/remove_enemy(mob/enemy) - LAZYREMOVE(_enemies, weakref(enemy)) - -/datum/mob_controller/proc/set_enemies(list/new_enemies) - _enemies = new_enemies - -/datum/mob_controller/proc/is_enemy(mob/enemy) - . = istype(enemy) && LAZYLEN(_enemies) && (weakref(enemy) in _enemies) - -/datum/mob_controller/proc/clear_enemies() - LAZYCLEARLIST(_enemies) - -// Friend tracking - used on /aggressive. -/datum/mob_controller/proc/get_friends() - return _friends - -/datum/mob_controller/proc/add_friend(mob/friend) - if(istype(friend)) - LAZYDISTINCTADD(_friends, weakref(friend)) - return TRUE - return FALSE - -/datum/mob_controller/proc/remove_friend(mob/friend) - LAZYREMOVE(_friends, weakref(friend)) - -/datum/mob_controller/proc/set_friends(list/new_friends) - _friends = new_friends - -/datum/mob_controller/proc/is_friend(mob/friend) - . = istype(friend) && LAZYLEN(_friends) && (weakref(friend) in _friends) - // By default, randomize the target area a bit to make armor/combat // a bit more dynamic (and avoid constant organ damage to the chest) /datum/mob_controller/proc/update_target_zone() diff --git a/code/datums/ai/_ai_enemies.dm b/code/datums/ai/_ai_enemies.dm new file mode 100644 index 000000000000..cbb7ddf46443 --- /dev/null +++ b/code/datums/ai/_ai_enemies.dm @@ -0,0 +1,35 @@ +// Enemy tracking - used on /aggressive +/datum/mob_controller/proc/get_enemies() + return _enemies + +/datum/mob_controller/proc/add_enemy(mob/enemy) + if(istype(enemy)) + LAZYDISTINCTADD(_enemies, weakref(enemy)) + +/datum/mob_controller/proc/add_enemies(list/enemies) + for(var/thing in enemies) + if(ismob(thing)) + add_friend(thing) + else if(istype(thing, /weakref)) + LAZYDISTINCTADD(_enemies, thing) + +/datum/mob_controller/proc/remove_enemy(mob/enemy) + LAZYREMOVE(_enemies, weakref(enemy)) + +/datum/mob_controller/proc/set_enemies(list/new_enemies) + _enemies = new_enemies + +/datum/mob_controller/proc/is_enemy(mob/enemy) + . = istype(enemy) && LAZYLEN(_enemies) && (weakref(enemy) in _enemies) + +/datum/mob_controller/proc/clear_enemies() + LAZYCLEARLIST(_enemies) + +/datum/mob_controller/proc/retaliate(atom/source) + SHOULD_CALL_PARENT(TRUE) + if(!istype(body) || body.stat == DEAD) + return FALSE + startle() + if(isliving(source)) + remove_friend(source) + return TRUE diff --git a/code/datums/ai/_ai_friends.dm b/code/datums/ai/_ai_friends.dm new file mode 100644 index 000000000000..3cab9046fced --- /dev/null +++ b/code/datums/ai/_ai_friends.dm @@ -0,0 +1,25 @@ +/datum/mob_controller/proc/pacify(mob/user) + lose_target() + add_friend(user) + +// Friend tracking - used on /aggressive. +/datum/mob_controller/proc/get_friends() + return _friends + +/datum/mob_controller/proc/add_friend(mob/friend) + if(istype(friend)) + LAZYDISTINCTADD(_friends, weakref(friend)) + return TRUE + return FALSE + +/datum/mob_controller/proc/remove_friend(mob/friend) + LAZYREMOVE(_friends, weakref(friend)) + +/datum/mob_controller/proc/set_friends(list/new_friends) + _friends = new_friends + +/datum/mob_controller/proc/is_friend(mob/friend) + . = istype(friend) && LAZYLEN(_friends) && (weakref(friend) in _friends) + +/datum/mob_controller/proc/clear_friends() + LAZYCLEARLIST(_friends) diff --git a/code/datums/ai/_ai_memory.dm b/code/datums/ai/_ai_memory.dm new file mode 100644 index 000000000000..f837a58cb8cd --- /dev/null +++ b/code/datums/ai/_ai_memory.dm @@ -0,0 +1,7 @@ +// General-purpose memorise proc, used by /commanded +/datum/mob_controller/proc/memorise(mob/speaker, message) + return + +// General-purpose memory checking proc, used by /faithful_hound +/datum/mob_controller/proc/check_memory(mob/speaker, message) + return FALSE diff --git a/code/datums/ai/_ai_pathfinding.dm b/code/datums/ai/_ai_pathfinding.dm new file mode 100644 index 000000000000..e01abaa05cec --- /dev/null +++ b/code/datums/ai/_ai_pathfinding.dm @@ -0,0 +1,29 @@ +/datum/mob_controller/proc/can_do_automated_move(variant_move_delay) + return body && !body.client + +/datum/mob_controller/proc/clear_paths() + clear_path() + +/datum/mob_controller/proc/clear_path() + executing_path = null + body?.stop_automove() + +/datum/mob_controller/proc/get_automove_target(datum/automove_metadata/metadata) + var/turf/move_target = (islist(executing_path) && length(executing_path)) ? executing_path[1] : null + if(!istype(move_target) || QDELETED(move_target)) + clear_path() + return null + return move_target + +/datum/mob_controller/proc/handle_post_automoved(atom/old_loc) + if(!islist(executing_path) || length(executing_path) <= 0) + return + var/turf/body_turf = get_turf(body) + if(!istype(body_turf)) + return + if(executing_path[1] != body_turf) + return + if(length(executing_path) > 1) + executing_path.Cut(1, 2) + else + clear_path() diff --git a/code/datums/ai/_ai_stance.dm b/code/datums/ai/_ai_stance.dm index ca018169811a..be3b11f94a72 100644 --- a/code/datums/ai/_ai_stance.dm +++ b/code/datums/ai/_ai_stance.dm @@ -1,30 +1,43 @@ // Stub type for future expansion/logic encapsulation. /decl/mob_controller_stance abstract_type = /decl/mob_controller_stance - /decl/mob_controller_stance/none - /decl/mob_controller_stance/idle - /decl/mob_controller_stance/alert - /decl/mob_controller_stance/attack - /decl/mob_controller_stance/attacking - /decl/mob_controller_stance/tired - /decl/mob_controller_stance/contained - /decl/mob_controller_stance/commanded abstract_type = /decl/mob_controller_stance/commanded - /decl/mob_controller_stance/commanded/stop - /decl/mob_controller_stance/commanded/follow - /decl/mob_controller_stance/commanded/misc - /decl/mob_controller_stance/commanded/heal - /decl/mob_controller_stance/commanded/healing +/decl/mob_controller_stance/busy + +/datum/mob_controller/proc/get_activity() + return current_activity + +/datum/mob_controller/proc/set_activity(new_activity) + if(current_activity != new_activity) + current_activity = new_activity + return TRUE + return FALSE + +/datum/mob_controller/proc/set_stance(new_stance) + if(stance != new_stance) + stance = new_stance + return TRUE + return FALSE + +/datum/mob_controller/proc/get_stance() + return stance + +/datum/mob_controller/proc/startle() + if(QDELETED(body) || body.stat != UNCONSCIOUS) + return + body.set_stat(CONSCIOUS) + if(body.current_posture?.prone) + body.set_posture(/decl/posture/standing) diff --git a/code/datums/ai/_ai_targets.dm b/code/datums/ai/_ai_targets.dm new file mode 100644 index 000000000000..49c5acedb094 --- /dev/null +++ b/code/datums/ai/_ai_targets.dm @@ -0,0 +1,77 @@ +/datum/mob_controller/proc/get_target() + if(isnull(target_ref)) + return null + var/atom/target = target_ref?.resolve() + if(!istype(target) || QDELETED(target)) + set_target(null) + return null + return target + +/datum/mob_controller/proc/set_target(atom/new_target) + var/weakref/new_target_ref = weakref(new_target) + if(target_ref != new_target_ref) + target_ref = new_target_ref + return TRUE + return FALSE + +/datum/mob_controller/proc/find_target() + SHOULD_CALL_PARENT(TRUE) + next_target_scan_time = world.time + target_scan_delay + +/datum/mob_controller/proc/valid_target(var/atom/A) + if(!istype(A)) + return FALSE + if(!A.simulated) + return FALSE + if(A == body) + return FALSE + if(A.invisibility > body.see_invisible) + return FALSE + if(LAZYLEN(_friends) && ismob(A) && (weakref(A) in _friends)) + return FALSE + if(!A.loc) + return FALSE + return TRUE + +/datum/mob_controller/proc/lose_target() + path_frustration = 0 + path_obstacles = null + set_target(null) + lost_target() + +/datum/mob_controller/proc/lost_target() + set_stance(STANCE_IDLE) + body.stop_automove() + +/datum/mob_controller/proc/list_targets() + // By default, we only target designated enemies. + var/list/enemies = get_enemies() + if(!LAZYLEN(enemies)) + return + var/list/possible_targets = get_raw_target_list() + if(!length(possible_targets)) + return + for(var/weakref/enemy in enemies) // Remove all entries that aren't in enemies + var/M = enemy.resolve() + if(M in possible_targets) + LAZYDISTINCTADD(., M) + +/datum/mob_controller/proc/do_target_scan() + . = target_scan_distance > 0 && world.time >= next_target_scan_time + +/datum/mob_controller/proc/move_to_target(var/move_only = FALSE) + return + +/datum/mob_controller/proc/get_raw_target_list() + if(target_scan_distance) + return hearers(body, target_scan_distance)-body + return null + +/datum/mob_controller/proc/get_valid_targets() + . = list() + for(var/target in list_targets(target_scan_distance)) + if(valid_target(target)) + . += target + +/datum/mob_controller/proc/handle_ranged_target(atom/ranged_target) + return FALSE diff --git a/code/datums/ai/_ai_wander.dm b/code/datums/ai/_ai_wander.dm new file mode 100644 index 000000000000..705d3fdcef1e --- /dev/null +++ b/code/datums/ai/_ai_wander.dm @@ -0,0 +1,5 @@ +/datum/mob_controller/proc/stop_wandering() + stop_wander = TRUE + +/datum/mob_controller/proc/resume_wandering() + stop_wander = FALSE diff --git a/code/datums/ai/aggressive.dm b/code/datums/ai/aggressive.dm index 62316d4d1977..e50d4e124ae0 100644 --- a/code/datums/ai/aggressive.dm +++ b/code/datums/ai/aggressive.dm @@ -2,36 +2,17 @@ stance = STANCE_IDLE stop_wander_when_pulled = FALSE try_destroy_surroundings = TRUE + target_scan_distance = 10 + var/attack_same_faction = FALSE var/only_attack_enemies = FALSE var/break_stuff_probability = 10 - var/weakref/target_ref /datum/mob_controller/aggressive/New() ..() if(isliving(body) && !QDELETED(body) && !QDELETED(src)) body.set_intent(I_FLAG_HARM) -/datum/mob_controller/aggressive/set_target(atom/new_target) - var/weakref/new_target_ref = weakref(new_target) - if(target_ref != new_target_ref) - target_ref = new_target_ref - return TRUE - return FALSE - -/datum/mob_controller/aggressive/get_target() - if(isnull(target_ref)) - return null - var/atom/target = target_ref?.resolve() - if(!istype(target) || QDELETED(target)) - set_target(null) - return null - return target - -/datum/mob_controller/aggressive/Destroy() - set_target(null) - return ..() - /datum/mob_controller/aggressive/do_process() if(!(. = ..())) @@ -42,24 +23,36 @@ set_stance(get_target() ? STANCE_ATTACK : STANCE_IDLE) return + if(isnull(stance)) + set_stance(get_target() ? STANCE_ATTACK : STANCE_IDLE) + if(isturf(body.loc) && !body.buckled) switch(stance) if(STANCE_IDLE) - set_target(find_target()) - set_stance(STANCE_ATTACK) + if(do_target_scan()) + set_target(find_target()) + if(get_target()) + set_stance(STANCE_ATTACK) if(STANCE_ATTACK) - body.face_atom(get_target()) - if(try_destroy_surroundings) - destroy_surroundings() - move_to_target() + + if(get_target()) + body.face_atom(get_target()) + if(try_destroy_surroundings) + destroy_surroundings() + move_to_target() + else + set_stance(STANCE_IDLE) if(STANCE_ATTACKING) - body.face_atom(get_target()) - if(try_destroy_surroundings) - destroy_surroundings() - handle_attacking_target() + if(get_target()) + body.face_atom(get_target()) + if(try_destroy_surroundings) + destroy_surroundings() + handle_attacking_target() + else + set_stance(STANCE_IDLE) if(STANCE_CONTAINED) //we aren't inside something so just switch set_stance(STANCE_IDLE) @@ -79,7 +72,7 @@ /datum/mob_controller/aggressive/proc/handle_attacking_target() stop_wandering() var/atom/target = get_target() - if(!istype(target) || !attackable(target) || !(target in list_targets(10))) // consider replacing this list_targets() call with a distance or LOS check + if(!istype(target) || !attackable(target) || !(target in get_raw_target_list())) lose_target() return FALSE if (ishuman(target)) @@ -209,51 +202,40 @@ return stop_wandering() var/atom/target = get_target() - if(!istype(target) || !attackable(target) || !(target in list_targets(10))) + if(!istype(target) || !attackable(target) || !(target in get_raw_target_list())) lose_target() return if(body.has_ranged_attack() && get_dist(body, target) <= body.get_ranged_attack_distance() && !move_only) body.stop_automove() - open_fire() + handle_ranged_target(target) return set_stance(STANCE_ATTACKING) body.start_automove(target) -/datum/mob_controller/aggressive/list_targets(var/dist = 7) +/datum/mob_controller/aggressive/list_targets() // Base hostile mobs will just destroy everything in view. // Mobs with an enemy list will filter the view by their enemies. if(!only_attack_enemies) - return hearers(body, dist)-body - var/list/enemies = get_enemies() - if(!LAZYLEN(enemies)) - return - var/list/possible_targets = hearers(body, dist)-body - if(!length(possible_targets)) - return - for(var/weakref/enemy in enemies) // Remove all entries that aren't in enemies - var/M = enemy.resolve() - if(M in possible_targets) - LAZYDISTINCTADD(., M) + return get_raw_target_list() + return ..() /datum/mob_controller/aggressive/find_target() + . = ..() if(!body.can_act() || !body.faction) return null resume_wandering() - for(var/atom/A in list_targets(10)) - if(valid_target(A)) - set_stance(STANCE_ATTACK) - body.face_atom(A) - return A + for(var/atom/A in get_valid_targets()) + set_stance(STANCE_ATTACK) + body.face_atom(A) + return A /datum/mob_controller/aggressive/valid_target(var/atom/A) - if(A == body) + if(!..()) return FALSE if(ismob(A)) var/mob/M = A if(M.faction == body.faction && !attack_same_faction) return FALSE - else if(weakref(M) in get_friends()) - return FALSE if(M.stat) return FALSE if(ishuman(M)) @@ -262,20 +244,12 @@ return FALSE return TRUE -/datum/mob_controller/aggressive/open_fire() - if(!body.can_act()) +/datum/mob_controller/aggressive/handle_ranged_target(atom/ranged_target) + if(!body.can_act() || !ranged_target) return FALSE - body.handle_ranged_attack(get_target()) + body.handle_ranged_attack(ranged_target) return TRUE -/datum/mob_controller/aggressive/lose_target() - set_target(null) - lost_target() - -/datum/mob_controller/aggressive/lost_target() - set_stance(STANCE_IDLE) - body.stop_automove() - /datum/mob_controller/aggressive/pacify(mob/user) ..() attack_same_faction = FALSE diff --git a/code/datums/ai/beast.dm b/code/datums/ai/beast.dm index 91ad1fec7a5a..9090698f55aa 100644 --- a/code/datums/ai/beast.dm +++ b/code/datums/ai/beast.dm @@ -36,7 +36,7 @@ qdel(S) break -/datum/mob_controller/aggressive/beast/list_targets(var/dist = 7) +/datum/mob_controller/aggressive/beast/list_targets() . = ..() if(!length(.)) if(LAZYLEN(prey)) @@ -46,6 +46,6 @@ if(M) . |= M else if(body.get_nutrition() < body.get_max_nutrition() * 0.75) //time to look for some food - for(var/mob/living/L in view(body, dist)) + for(var/mob/living/L in get_raw_target_list()) if(attack_same_faction || L.faction != body.faction) LAZYDISTINCTADD(prey, weakref(L)) diff --git a/code/datums/ai/commanded.dm b/code/datums/ai/commanded.dm index e4452782a84a..c9e1e7f8e979 100644 --- a/code/datums/ai/commanded.dm +++ b/code/datums/ai/commanded.dm @@ -85,8 +85,8 @@ var/list/targets = get_targets_by_name(message) if(LAZYLEN(targets) != 1) //CONFUSED. WHO DO I FOLLOW? return 0 - var/weakref/target_ref = targets[1] - set_target(target_ref.resolve()) //YEAH GOOD IDEA + var/weakref/single_target_ref = targets[1] + set_target(single_target_ref.resolve()) //YEAH GOOD IDEA set_stance(STANCE_COMMANDED_FOLLOW) //GOT SOMEBODY. BETTER FOLLOW EM. return 1 @@ -98,7 +98,7 @@ return stop_wandering() var/atom/target = get_target() - if(istype(target) && (target in list_targets(10))) + if(istype(target) && (target in get_raw_target_list())) body.start_automove(target) /datum/mob_controller/aggressive/commanded/proc/commanded_stop() //basically a proc that runs whenever we are asked to stay put. Probably going to remain unused. @@ -125,12 +125,14 @@ LAZYADD(., weakref(M)) /datum/mob_controller/aggressive/commanded/find_target() + SHOULD_CALL_PARENT(FALSE) + next_target_scan_time = world.time + target_scan_delay if(!LAZYLEN(_allowed_targets)) return null var/mode = "specific" if(LAZYACCESS(_allowed_targets, 1) == "everyone") //we have been given the golden gift of murdering everything. Except our master, of course. And our friends. So just mostly everyone. mode = "everyone" - for(var/atom/A in list_targets(10)) + for(var/atom/A in get_raw_target_list()) if(A == src) continue if(isliving(A)) diff --git a/code/datums/movement/automove_controller.dm b/code/datums/movement/automove_controller.dm index 08dae443f358..b413d9998497 100644 --- a/code/datums/movement/automove_controller.dm +++ b/code/datums/movement/automove_controller.dm @@ -1,7 +1,8 @@ /// Implements automove logic; can be overridden on mob procs if you want to vary the logic from the below. /decl/automove_controller - var/completion_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom reaches its target. - var/failure_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom fails to move. + var/completion_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom reaches its target. + var/failure_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom fails to move. + var/try_avoid_obstacles = TRUE // Will try to move 90 degrees around an obstacle. /decl/automove_controller/proc/handle_mover(atom/movable/mover, datum/automove_metadata/metadata) @@ -49,14 +50,16 @@ return TRUE // no idea how we would get into this position if(mover.SelfMove(target_dir) && (old_loc != mover.loc)) - return TRUE + mover.handle_post_automoved(old_loc) + return (mover.get_automove_target() == mover.loc) // We may have transitioned to the next step in a path. - // Try to move around any obstacle. - var/static/list/_alt_dir_rot = list(45, -45) - for(var/alt_dir in shuffle(_alt_dir_rot)) - mover.reset_movement_delay() - if(mover.SelfMove(turn(target_dir, alt_dir)) && (old_loc != mover.loc)) - return TRUE + if(try_avoid_obstacles) + // Try to move around any obstacle. + var/static/list/_alt_dir_rot = list(45, -45) + for(var/alt_dir in shuffle(_alt_dir_rot)) + mover.reset_movement_delay() + if(mover.SelfMove(turn(target_dir, alt_dir)) && (old_loc != mover.loc)) + return TRUE mover.failed_automove() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index d2be93779daa..0c98725538ca 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -581,6 +581,9 @@ if(!.) // If we're under or inside shelter, use the z-level rain (for ambience) . = SSweather.weather_by_z[my_turf.z] +/atom/movable/proc/handle_post_automoved(atom/old_loc) + return + /atom/movable/take_vaporized_reagent(reagent, amount) if(ATOM_IS_OPEN_CONTAINER(src)) return loc?.take_vaporized_reagent(reagent, amount) diff --git a/code/game/turfs/turf_navigation.dm b/code/game/turfs/turf_navigation.dm new file mode 100644 index 000000000000..9085f6b99d9a --- /dev/null +++ b/code/game/turfs/turf_navigation.dm @@ -0,0 +1,62 @@ +/******************************************************************/ +// Navigation procs +// Used for A-star pathfinding + +// Returns the surrounding cardinal turfs with open links +// Including through doors openable with the ID +/turf/proc/CardinalTurfsWithAccess(var/obj/item/card/id/ID) + var/L[] = new() + + for(var/d in global.cardinal) + var/turf/T = get_step(src, d) + if(istype(T) && !T.density && T.simulated && !LinkBlockedWithAccess(src, T, ID)) + L.Add(T) + return L + + +// Returns true if a link between A and B is blocked +// Movement through doors allowed if ID has access +/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/card/id/ID) + + if(A == null || B == null) return 1 + var/adir = get_dir(A,B) + var/rdir = get_dir(B,A) + if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal + var/iStep = get_step(A,adir&(NORTH|SOUTH)) + if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID)) + return 0 + + var/pStep = get_step(A,adir&(EAST|WEST)) + if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID)) + return 0 + return 1 + + if(DirBlockedWithAccess(A,adir, ID)) + return 1 + + if(DirBlockedWithAccess(B,rdir, ID)) + return 1 + + for(var/obj/O in B) + if(O.density && !istype(O, /obj/machinery/door) && !(O.atom_flags & ATOM_FLAG_CHECKS_BORDER)) + return 1 + + return 0 + +// Returns true if direction is blocked from loc +// Checks doors against access with given ID +/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/card/id/ID) + for(var/obj/structure/window/D in loc) + if(!D.density) continue + if(D.dir == SOUTHWEST) return 1 + if(D.dir == dir) return 1 + + for(var/obj/machinery/door/D in loc) + if(!D.density) continue + if(istype(D, /obj/machinery/door/window)) + if( dir & D.dir ) return !D.check_access(ID) + + //if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID) + //if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID) + else return !D.check_access(ID) // it's a real, air blocking door + return 0 diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm index 25f9dca52a4e..f92af368dcd8 100644 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -96,6 +96,10 @@ return ..() /obj/item/integrated_circuit/smart/advanced_pathfinder/do_work() + + if(waiting_for_path) + return + if(!assembly) activate_pin(3) return @@ -119,20 +123,30 @@ if(Pl&&islist(Pl)) idc.access = Pl var/turf/a_loc = get_turf(assembly) - var/list/P = AStar(a_loc, locate(get_pin_data(IC_INPUT, 1), get_pin_data(IC_INPUT, 2), a_loc.z), TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT, 3, /atom))) + SSpathfinding.enqueue_mover( + src, + locate(get_pin_data(IC_INPUT, 1), get_pin_data(IC_INPUT, 2), a_loc.z), + new /datum/pathfinding_metadata( + _max_node_depth = 200, + _id = idc, + _obstacle = get_turf(get_pin_data_as_type(IC_INPUT, 3, /atom)) + ) + ) - if(!P) - activate_pin(3) - return - else - var/list/Xn = new/list(P.len) - var/list/Yn = new/list(P.len) - var/turf/T - for(var/i =1 to P.len) - T=P[i] - Xn[i] = T.x - Yn[i] = T.y - set_pin_data(IC_OUTPUT, 1, Xn) - set_pin_data(IC_OUTPUT, 2, Yn) - push_data() - activate_pin(2) +/obj/item/integrated_circuit/smart/advanced_pathfinder/path_not_found() + ..() + activate_pin(3) + +/obj/item/integrated_circuit/smart/advanced_pathfinder/path_found(list/path) + ..() + var/list/Xn = new/list(path.len) + var/list/Yn = new/list(path.len) + var/turf/T + for(var/i = 1 to path.len) + T=path[i] + Xn[i] = T.x + Yn[i] = T.y + set_pin_data(IC_OUTPUT, 1, Xn) + set_pin_data(IC_OUTPUT, 2, Yn) + push_data() + activate_pin(2) diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index fab8219c1698..cfe9577d41c3 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -242,7 +242,7 @@ resetTarget() lookForTargets() if(will_patrol && !LAZYLEN(grabbed_by) && !target) - if(patrol_path && patrol_path.len) + if(length(patrol_path)) for(var/i = 1 to patrol_speed) sleep(20 / (patrol_speed + 1)) handlePatrol() @@ -266,7 +266,7 @@ if(!target || !target.loc) return if(get_dist(src, target) > min_target_dist) - if(!target_path.len || get_turf(target) != target_path[target_path.len]) + if(!length(target_path) || get_turf(target) != target_path[target_path.len]) calcTargetPath() if(makeStep(target_path)) frustration = 0 @@ -297,9 +297,9 @@ return /mob/living/bot/proc/startPatrol() - var/turf/T = getPatrolTurf() - if(T) - patrol_path = AStar(get_turf(loc), T, TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, max_patrol_dist, id = botcard, exclude = obstacle) + var/turf/target_turf = getPatrolTurf() + if(target_turf) + patrol_path = SSpathfinding.find_path_immediate(start = get_turf(loc), end = target_turf, max_node_depth = max_patrol_dist, id = botcard, exclude = obstacle, check_tick = TRUE) if(!patrol_path) patrol_path = list() obstacle = null @@ -331,23 +331,22 @@ return /mob/living/bot/proc/calcTargetPath() - target_path = AStar(get_turf(loc), get_turf(target), TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, max_target_dist, id = botcard, exclude = obstacle) - if(!target_path) - if(target && target.loc) - ignore_list |= target - resetTarget() - obstacle = null - return + target_path = SSpathfinding.find_path_immediate(start = get_turf(loc), end = get_turf(target), max_node_depth = max_target_dist, min_target_dist = min_target_dist, id = botcard, exclude = obstacle, check_tick = TRUE) + if(length(target_path)) + return + if(target?.loc) + ignore_list |= target + resetTarget() + obstacle = null /mob/living/bot/proc/makeStep(var/list/path) - if(!path.len) - return 0 - var/turf/T = path[1] - if(get_turf(src) == T) - path -= T + if(!length(path)) + return FALSE + var/turf/target_turf = path[1] + if(get_turf(src) == target_turf) + path -= target_turf return makeStep(path) - - return step_towards(src, T) + return step_towards(src, target_turf) /mob/living/bot/proc/resetTarget() target = null @@ -371,70 +370,6 @@ set_light(0) update_icon() -/******************************************************************/ -// Navigation procs -// Used for A-star pathfinding - - -// Returns the surrounding cardinal turfs with open links -// Including through doors openable with the ID -/turf/proc/CardinalTurfsWithAccess(var/obj/item/card/id/ID) - var/L[] = new() - - for(var/d in global.cardinal) - var/turf/T = get_step(src, d) - if(istype(T) && !T.density && T.simulated && !LinkBlockedWithAccess(src, T, ID)) - L.Add(T) - return L - - -// Returns true if a link between A and B is blocked -// Movement through doors allowed if ID has access -/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/card/id/ID) - - if(A == null || B == null) return 1 - var/adir = get_dir(A,B) - var/rdir = get_dir(B,A) - if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal - var/iStep = get_step(A,adir&(NORTH|SOUTH)) - if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID)) - return 0 - - var/pStep = get_step(A,adir&(EAST|WEST)) - if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID)) - return 0 - return 1 - - if(DirBlockedWithAccess(A,adir, ID)) - return 1 - - if(DirBlockedWithAccess(B,rdir, ID)) - return 1 - - for(var/obj/O in B) - if(O.density && !istype(O, /obj/machinery/door) && !(O.atom_flags & ATOM_FLAG_CHECKS_BORDER)) - return 1 - - return 0 - -// Returns true if direction is blocked from loc -// Checks doors against access with given ID -/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/card/id/ID) - for(var/obj/structure/window/D in loc) - if(!D.density) continue - if(D.dir == SOUTHWEST) return 1 - if(D.dir == dir) return 1 - - for(var/obj/machinery/door/D in loc) - if(!D.density) continue - if(istype(D, /obj/machinery/door/window)) - if( dir & D.dir ) return !D.check_access(ID) - - //if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID) - //if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID) - else return !D.check_access(ID) // it's a real, air blocking door - return 0 - /mob/living/bot/GetIdCards(list/exceptions) . = ..() if(istype(botcard) && !is_type_in_list(botcard, exceptions)) diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index e989ba53b320..0381d5de33f7 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -43,11 +43,14 @@ /mob/living/bot/cleanbot/confirmTarget(var/obj/effect/decal/cleanable/D) if(!..()) - return 0 + return FALSE + if(istype(D, /obj/effect/decal/cleanable/dirt)) + var/obj/effect/decal/cleanable/dirt/dirt = D + return dirt.dirt_amount >= 25 for(var/T in target_types) if(istype(D, T)) - return 1 - return 0 + return TRUE + return FALSE /mob/living/bot/cleanbot/handleAdjacentTarget() if(get_turf(target) == src.loc) diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm index 86a8a9411890..0e027aa687ba 100644 --- a/code/modules/mob/living/bot/farmbot.dm +++ b/code/modules/mob/living/bot/farmbot.dm @@ -123,24 +123,6 @@ target = source return -/mob/living/bot/farmbot/calcTargetPath() // We need to land NEXT to the tray, because the tray itself is impassable - for(var/trayDir in list(NORTH, SOUTH, EAST, WEST)) - target_path = AStar(get_turf(loc), get_step(get_turf(target), trayDir), TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, max_target_dist, id = botcard) - if(target_path) - break - if(!target_path) - ignore_list |= target - target = null - target_path = list() - return - -/mob/living/bot/farmbot/stepToTarget() // Same reason - var/turf/T = get_turf(target) - if(!target_path.len || !T.Adjacent(target_path[target_path.len])) - calcTargetPath() - makeStep(target_path) - return - /mob/living/bot/farmbot/UnarmedAttack(var/atom/A, var/proximity) . = ..() if(.) diff --git a/code/modules/mob/living/bot/mulebot.dm b/code/modules/mob/living/bot/mulebot.dm index bb9cf31e58e4..437ef6164511 100644 --- a/code/modules/mob/living/bot/mulebot.dm +++ b/code/modules/mob/living/bot/mulebot.dm @@ -183,7 +183,7 @@ /mob/living/bot/mulebot/calcTargetPath() ..() - if(!target_path.len && target != home) // I presume that target is not null + if(!length(target_path) && target != home) // I presume that target is not null resetTarget() target = home targetName = "Home" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 4ca6afa75455..49300a21f14b 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1359,11 +1359,6 @@ default behaviour is: return FALSE return TRUE -//gets name from ID or PDA itself, ID inside PDA doesn't matter -//Useful when player is being seen by other mobs -/mob/living/proc/get_id_name(if_no_id = "Unknown") - return GetIdCard(exceptions = list(/obj/item/holder))?.registered_name || if_no_id - /mob/living/get_default_temperature_threshold(threshold) if(isSynthetic()) switch(threshold) diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index c32b39b1a426..2f79162b6c92 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -64,7 +64,7 @@ stop_wandering() stance_step++ if(stance_step >= 20) - if(target && (target in list_targets(10))) + if(target && (target in get_raw_target_list())) set_stance(STANCE_ATTACK) //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. else set_stance(STANCE_IDLE) @@ -72,7 +72,7 @@ if(STANCE_ALERT) stop_wandering() var/found_mob = 0 - if(target && (target in list_targets(10))) + if(target && (target in get_raw_target_list())) if(!attackable(target)) stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again. stance_step++ diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm b/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm index f504d63398e2..1145b90b6e8b 100644 --- a/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm +++ b/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm @@ -52,8 +52,8 @@ if(LAZYLEN(targets) != 1) body.say("ERROR. TARGET COULD NOT BE PARSED.") return 0 - var/weakref/target_ref = targets[1] - set_target(target_ref.resolve()) + var/weakref/single_target_ref = targets[1] + set_target(single_target_ref.resolve()) set_stance(STANCE_COMMANDED_HEAL) return 1 if(findtext(text,"emergency protocol")) diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_guard.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_guard.dm index be65f62f7076..b828ae9e7b09 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_guard.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_guard.dm @@ -23,7 +23,7 @@ paired_nurse = null /datum/mob_controller/aggressive/giant_spider/guard/proc/find_nurse() - for(var/mob/living/simple_animal/hostile/giant_spider/nurse/nurse in list_targets(10)) + for(var/mob/living/simple_animal/hostile/giant_spider/nurse/nurse in get_raw_target_list()) if(nurse.stat || !istype(nurse.ai, /datum/mob_controller/aggressive/giant_spider/nurse)) continue var/datum/mob_controller/aggressive/giant_spider/nurse/nurse_ai = nurse.ai diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm index 091230eca451..ead786ab340c 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm @@ -26,7 +26,7 @@ /datum/mob_controller/aggressive/megahivebot can_escape_buckles = TRUE -/datum/mob_controller/aggressive/megahivebot/open_fire() +/datum/mob_controller/aggressive/megahivebot/handle_ranged_target(atom/ranged_target) var/mob/living/simple_animal/hostile/hivebot/mega/megabot = body if(!istype(megabot)) return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index c2e2b3c5a18a..88defeeaf285 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -45,7 +45,7 @@ var/global/list/protected_objects = list( var/awake = TRUE // Return a list of targets that isn't the creator -/datum/mob_controller/aggressive/mimic/list_targets(var/dist = 7) +/datum/mob_controller/aggressive/mimic/get_valid_targets() var/mob/living/simple_animal/hostile/mimic/mimic = body . = istype(mimic) && mimic.awake && ..() if(length(.) && mimic.creator) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index 498adac96b38..e3f5b4fe9ff0 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -54,8 +54,15 @@ /mob/living/simple_animal/hostile/malf_drone/has_ranged_attack() return TRUE -/datum/mob_controller/aggressive/malf_drone/list_targets(var/dist = 7) - . = ..(hostile_drone ? hostile_range : dist) +/datum/mob_controller/aggressive/malf_drone/get_raw_target_list() + if(hostile_drone) + target_scan_distance = hostile_range + else + target_scan_distance = initial(target_scan_distance) + . = ..() + +/datum/mob_controller/aggressive/malf_drone/get_valid_targets() + . = ..() for(var/mob/M in .) if(istype(M, body.type)) . -= M diff --git a/code/modules/mob/living/simple_animal/hostile/slug.dm b/code/modules/mob/living/simple_animal/hostile/slug.dm index 8384fc93a6cd..4218f9176bfa 100644 --- a/code/modules/mob/living/simple_animal/hostile/slug.dm +++ b/code/modules/mob/living/simple_animal/hostile/slug.dm @@ -23,13 +23,14 @@ try_destroy_surroundings = FALSE can_escape_buckles = TRUE -/datum/mob_controller/aggressive/slug/list_targets(var/dist = 7) +/datum/mob_controller/aggressive/slug/valid_target(atom/A) . = ..() - var/mob/living/simple_animal/hostile/slug/slug = body - if(istype(slug)) - for(var/mob/living/M in .) - if(slug.check_friendly_species(M)) - . -= M + if(.) + if(!ismob(A)) + return FALSE + var/mob/living/simple_animal/hostile/slug/slug = body + if(slug.check_friendly_species(A)) + return FALSE /mob/living/simple_animal/hostile/slug/proc/check_friendly_species(var/mob/living/M) return istype(M) && M.faction == faction diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index edf74c14dd0f..35df33c80fa8 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1395,5 +1395,10 @@ /mob/proc/handle_footsteps() return +//gets name from ID or PDA itself, ID inside PDA doesn't matter +//Useful when player is being seen by other mobs +/mob/proc/get_id_name(if_no_id = "Unknown") + return GetIdCard(exceptions = list(/obj/item/holder))?.registered_name || if_no_id + /mob/proc/can_twohand_item(obj/item/item) return FALSE diff --git a/code/modules/mob/mob_automove.dm b/code/modules/mob/mob_automove.dm index ff8d1a6d7bae..f552d4f7c1e6 100644 --- a/code/modules/mob/mob_automove.dm +++ b/code/modules/mob/mob_automove.dm @@ -6,18 +6,60 @@ _automove_target = null return ..() +/mob/path_found(list/path) + ..() + if(islist(path) && length(path) > 1) + path.Cut(1, 2) // Remove the first turf since it's going to be our origin. + if(length(path)) + start_automove(path) + +/mob/path_not_found() + ..() + stop_automove() + /// Called by get_movement_delay() to override the current move intent, in cases where an automove has a delay override. /mob/proc/get_automove_delay() var/datum/automove_metadata/metadata = SSautomove.moving_metadata[src] return metadata?.move_delay +/mob/failed_automove() + ..() + stop_automove() + _automove_target = null + return FALSE + /mob/start_automove(target, movement_type, datum/automove_metadata/metadata) _automove_target = target return ..() // The AI datum may decide to track a target instead of using the mob reference. /mob/get_automove_target(datum/automove_metadata/metadata) - . = (istype(ai) && ai.get_automove_target()) || _automove_target || ..() + . = _automove_target || (istype(ai) && ai.get_automove_target()) || ..() + if(islist(.)) + var/list/path = . + while(length(path) && path[1] == get_turf(src)) + path.Cut(1,2) + if(length(path)) + return path[1] + return null + +/mob/handle_post_automoved(atom/old_loc) + if(istype(ai)) + ai.handle_post_automoved(old_loc) + return + if(!islist(_automove_target) || length(_automove_target) <= 0) + return + var/turf/body_turf = get_turf(src) + if(!istype(body_turf)) + return + var/list/_automove_target_list = _automove_target + if(_automove_target_list[1] != body_turf) + return + if(length(_automove_target_list) > 1) + _automove_target_list.Cut(1, 2) + else + _automove_target_list = null + stop_automove() // We do some early checking here to avoid doing the same checks repeatedly by calling SelfMove(). /mob/can_do_automated_move(variant_move_delay) diff --git a/code/procs/AStar.dm b/code/procs/pathfinding.dm similarity index 78% rename from code/procs/AStar.dm rename to code/procs/pathfinding.dm index 01e4213f805d..7732761b8d3c 100644 --- a/code/procs/AStar.dm +++ b/code/procs/pathfinding.dm @@ -5,14 +5,14 @@ A Star pathfinding algorithm Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of windows along the route into account. Use: -your_list = AStar(start location, end location, adjacent turf proc, distance proc) +your_list = find_path_astar(start location, end location, adjacent turf proc, distance proc) For the adjacent turf proc i wrote: /turf/proc/AdjacentTurfs And for the distance one i wrote: /turf/proc/Distance So an example use might be: -src.path_list = AStar(src.loc, target.loc, TYPE_PROC_REF(/turf, AdjacentTurfs), TYPE_PROC_REF(/turf, Distance)) +src.path_list = find_path_astar(src.loc, target.loc, TYPE_PROC_REF(/turf, AdjacentTurfs), TYPE_PROC_REF(/turf, Distance)) Note: The path is returned starting at the END node, so i wrote reverselist to reverse it for ease of use. @@ -60,7 +60,12 @@ length to avoid portals or something i guess?? Not that they're counted right no /proc/PathWeightCompare(PathNode/a, PathNode/b) return a.estimated_cost - b.estimated_cost -/proc/AStar(var/start, var/end, adjacent, dist, var/max_nodes, var/max_node_depth = 30, var/min_target_dist = 0, var/min_node_dist, var/id, var/datum/exclude) +/proc/find_path_astar_async(start, end, adjacent, dist, max_nodes, max_node_depth = 30, min_target_dist = 0, min_node_dist, id, datum/exclude) + set waitfor = FALSE + return find_path_astar(start, end, adjacent, dist, max_nodes, max_node_depth, min_target_dist, min_node_dist, id, exclude, check_tick = TRUE) + +/proc/find_path_astar(start, end, adjacent, dist, max_nodes, max_node_depth = 30, min_target_dist = 0, min_node_dist, id, datum/exclude, check_tick = FALSE) + var/datum/priority_queue/open = new /datum/priority_queue(/proc/PathWeightCompare) var/list/closed = list() var/list/path @@ -85,13 +90,11 @@ length to avoid portals or something i guess?? Not that they're counted right no path[index--] = current.position break - if(min_node_dist && max_node_depth) - if(call(current.position, min_node_dist)(end) + current.nodes_traversed >= max_node_depth) - continue + if(min_node_dist && max_node_depth && (call(current.position, min_node_dist)(end) + current.nodes_traversed >= max_node_depth)) + continue - if(max_node_depth) - if(current.nodes_traversed >= max_node_depth) - continue + if(max_node_depth && current.nodes_traversed >= max_node_depth) + continue for(var/datum/datum in call(current.position, adjacent)(id)) if(datum == exclude) @@ -115,4 +118,9 @@ length to avoid portals or something i guess?? Not that they're counted right no if(max_nodes && open.Length() > max_nodes) open.Remove(open.Length()) + if(check_tick) + CHECK_TICK + if(check_tick) + CHECK_TICK + return path diff --git a/nebula.dme b/nebula.dme index d286bdf0899f..4ea2a638823c 100644 --- a/nebula.dme +++ b/nebula.dme @@ -283,6 +283,7 @@ #include "code\controllers\subsystems\mapping.dm" #include "code\controllers\subsystems\misc_late.dm" #include "code\controllers\subsystems\overlays.dm" +#include "code\controllers\subsystems\pathfinding.dm" #include "code\controllers\subsystems\plants.dm" #include "code\controllers\subsystems\radiation.dm" #include "code\controllers\subsystems\shuttle.dm" @@ -357,7 +358,13 @@ #include "code\datums\type_cloning.dm" #include "code\datums\weakref.dm" #include "code\datums\ai\_ai.dm" +#include "code\datums\ai\_ai_enemies.dm" +#include "code\datums\ai\_ai_friends.dm" +#include "code\datums\ai\_ai_memory.dm" +#include "code\datums\ai\_ai_pathfinding.dm" #include "code\datums\ai\_ai_stance.dm" +#include "code\datums\ai\_ai_targets.dm" +#include "code\datums\ai\_ai_wander.dm" #include "code\datums\ai\aggressive.dm" #include "code\datums\ai\beast.dm" #include "code\datums\ai\commanded.dm" @@ -1574,6 +1581,7 @@ #include "code\game\turfs\turf_footsteps.dm" #include "code\game\turfs\turf_height.dm" #include "code\game\turfs\turf_material.dm" +#include "code\game\turfs\turf_navigation.dm" #include "code\game\turfs\turf_ramps.dm" #include "code\game\turfs\unsimulated.dm" #include "code\game\turfs\flooring\_flooring.dm" @@ -4070,9 +4078,9 @@ #include "code\modules\ZAS\Variable Settings.dm" #include "code\modules\ZAS\Zone.dm" #include "code\procs\announce.dm" -#include "code\procs\AStar.dm" #include "code\procs\dbcore.dm" #include "code\procs\hud.dm" +#include "code\procs\pathfinding.dm" #include "code\procs\radio.dm" #include "code\unit_tests\_defines.dm" #include "code\unit_tests\_includes.dm" From c1e1e68dff6b8e915648310e1c51401896ad1abc Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 7 Dec 2024 14:56:13 +1100 Subject: [PATCH 0050/1331] Adds a grandfather clock. --- .../objects/structures/grandfather_clock.dm | 63 ++++++++++++++++++ icons/obj/structures/grandfather_clock.dmi | Bin 0 -> 721 bytes nebula.dme | 1 + 3 files changed, 64 insertions(+) create mode 100644 code/game/objects/structures/grandfather_clock.dm create mode 100644 icons/obj/structures/grandfather_clock.dmi diff --git a/code/game/objects/structures/grandfather_clock.dm b/code/game/objects/structures/grandfather_clock.dm new file mode 100644 index 000000000000..b1dadeb884a6 --- /dev/null +++ b/code/game/objects/structures/grandfather_clock.dm @@ -0,0 +1,63 @@ +// TODO: buildable with artifice? +// TODO: looping 2 second tick tock sound, somehow aligned with pendulum (may not be possible in DM) +/obj/structure/grandfather_clock + name = "grandfather clock" + desc = "A tall, stately timepiece." + icon = 'icons/obj/structures/grandfather_clock.dmi' + icon_state = ICON_STATE_WORLD + density = TRUE + material = /decl/material/solid/organic/wood/mahogany + var/face_color = "#f0edc7" + var/last_time + var/decl/material/clockwork_mat = /decl/material/solid/metal/brass + +/obj/structure/grandfather_clock/Initialize(ml, _mat, _reinf_mat) + if(ispath(clockwork_mat)) + clockwork_mat = GET_DECL(clockwork_mat) + . = ..() + START_PROCESSING(SSobj, src) + update_icon() + +/obj/structure/grandfather_clock/examine(mob/user, distance, infix, suffix) + . = ..() + // TODO: check literacy? + if(isnull(last_time)) + last_time = stationtime2text() + to_chat(user, SPAN_NOTICE("The face of \the [src] reads [last_time].")) + +// TODO: don't magically make the time update when swinging is restarted +// TODO: alt interaction to interfere with the clock? +/obj/structure/grandfather_clock/attack_hand(mob/user) + . = ..() + if(!.) + if(is_processing) + STOP_PROCESSING(SSobj, src) + user.visible_message(SPAN_NOTICE("\The [user] reaches into \the [src] and stops the pendulum.")) + else + START_PROCESSING(SSobj, src) + user.visible_message(SPAN_NOTICE("\The [user] reaches into \the [src] and sets the pendulum swinging.")) + update_icon() + return TRUE + +/obj/structure/grandfather_clock/Process() + ..() + var/new_time = stationtime2text() + if(new_time != last_time) + last_time = new_time + update_icon() + +/obj/structure/grandfather_clock/on_update_icon() + . = ..() + if(isnull(last_time)) + last_time = stationtime2text() + if(face_color) + add_overlay(overlay_image(icon, "[icon_state]-face", face_color, RESET_COLOR)) + if(!clockwork_mat) + return + if(is_processing) + add_overlay(overlay_image(icon, "[icon_state]-pendulum-swing", clockwork_mat.color, RESET_COLOR)) + else + add_overlay(overlay_image(icon, "[icon_state]-pendulum", clockwork_mat.color, RESET_COLOR)) + var/list/time_stats = splittext(last_time, ":") + add_overlay(overlay_image(icon, "[icon_state]-hour[round(((text2num(time_stats[1]) / 24) * 360) / 45) * 45]"), clockwork_mat.color, RESET_COLOR) + add_overlay(overlay_image(icon, "[icon_state]-minute[round(((text2num(time_stats[2]) / 60) * 360) / 45) * 45]"), clockwork_mat.color, RESET_COLOR) diff --git a/icons/obj/structures/grandfather_clock.dmi b/icons/obj/structures/grandfather_clock.dmi new file mode 100644 index 0000000000000000000000000000000000000000..cbef65c5ac440ee9343dc9001a6fdbbf2aac640a GIT binary patch literal 721 zcmV;?0xtcDP)fFDZ*Bkp zc$}4!F%E+u6hLP<1<_R-MVs28OQUHV>K#l$AyI4qYkK>V;?ktUXX1bIc*9GG$+fw) z9jWgvesOK&qR54mhYX`lGArCtP>ckZqp*H4wzu4k(YEM`MHn%ujoWjK2Hnrx&%abU z^bZb1#j@%wfGv*!@Hxbs7a{7r3UN~!;-;q%cacNgvsA0)P`9VSy*&;t?s0hYod;L< zJiPnPgUfp!-u}rITc>pn#8b0LHU588xm!NY*do4}q!>>xW>80`dNb@L&pblN*=<-Lx#u5(REc_Z2QH$z$c((T~%o zK;y?l1#W})!>2%u?P^7XTP$AKq`i&4{S=7t>ckR(B(|IRyn|xa+xedX`&&f+9W*|K zW6uPrXJclnfHTHX1>_(rF!nvwf&1?J`Od3fyQc%91?PhIv*THC?)g{26c~*$SEfKb zp?F3-fv16JfhX`X;QfOp(7hZOOB3kd@C1Yqhmg$Kdk1fut9OAo*Z=p0>yv>6FIZe4 zgb+dqA%yr9_G7tpJg6E2G3K}9`kGmvy#@4~++i_wNhpL6LI@#*5aLMA Date: Sat, 7 Dec 2024 11:18:14 +1100 Subject: [PATCH 0051/1331] Various clothing and equipment tweaks for Karzerfeste. --- code/datums/outfits/outfit.dm | 173 +++++++++++------- code/modules/clothing/neck/jewelry.dm | 3 + code/modules/clothing/suits/armor/_armor.dm | 9 +- .../clothing/suits/armor/forged/brigandine.dm | 1 + code/modules/clothing/suits/hooded_cloak.dm | 3 +- code/modules/clothing/suits/mantle.dm | 7 +- code/modules/clothing/suits/robes.dm | 7 + code/modules/tools/subtypes/axes.dm | 3 + code/modules/tools/subtypes/pickaxes.dm | 4 + icons/clothing/suits/sleeved_robe.dmi | Bin 0 -> 1083 bytes maps/shaded_hills/outfits/_outfits.dm | 7 +- mods/content/fantasy/_fantasy.dme | 1 + mods/content/fantasy/datum/outfits.dm | 15 +- .../content/fantasy/icons/clothing/jerkin.dmi | Bin 1075 -> 1100 bytes .../fantasy/icons/clothing/trousers.dmi | Bin 939 -> 948 bytes .../fantasy/items/clothing/_loadout.dm | 21 +++ .../fantasy/items/clothing/_recipes.dm | 2 +- mods/content/fantasy/items/clothing/armor.dm | 2 +- mods/content/fantasy/items/clothing/cloak.dm | 17 ++ mods/content/fantasy/items/clothing/jerkin.dm | 12 ++ .../fantasy/items/clothing/trousers.dm | 9 +- 21 files changed, 205 insertions(+), 91 deletions(-) create mode 100644 icons/clothing/suits/sleeved_robe.dmi create mode 100644 mods/content/fantasy/items/clothing/cloak.dm diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm index 572306145f56..c3728f48a2a5 100644 --- a/code/datums/outfits/outfit.dm +++ b/code/datums/outfits/outfit.dm @@ -1,24 +1,26 @@ /decl/outfit abstract_type = /decl/outfit - var/name = "Naked" - var/uniform = null - var/suit = null - var/back = null - var/belt = null - var/gloves = null - var/shoes = null - var/head = null - var/mask = null - var/l_ear = null - var/r_ear = null - var/glasses = null - var/id = null - var/l_pocket = null - var/r_pocket = null + var/name = "Naked And Afraid" + var/uniform = null + var/suit = null + var/back = null + var/belt = null + var/gloves = null + var/shoes = null + var/head = null + var/mask = null + var/l_ear = null + var/r_ear = null + var/glasses = null + var/id = null + var/l_pocket = null + var/r_pocket = null var/suit_store = null - var/holster = null + var/holster = null + /// Linear list of types. Will attempt to place items in hands. var/list/hands - var/list/backpack_contents = list() // In the list(path=count,otherpath=count) format + //. An associative list in list(path=count,otherpath=count) format. Will attempt to place items in storage. + var/list/backpack_contents var/id_type var/id_desc @@ -36,16 +38,21 @@ . = ..() backpack_overrides = backpack_overrides || list() -// This proc is structured slightly strangely because I will be adding pants to it. +// Used for slightly cleaner code around multi-equip records. +/decl/outfit/proc/resolve_equip_to_list(check_type) + if(islist(check_type)) + return check_type + if(ispath(check_type)) + return list(check_type) + return null + /decl/outfit/validate() . = ..() for(var/check_type in list(uniform, suit, back, belt, gloves, shoes, head, mask, l_ear, r_ear, glasses, id, l_pocket, r_pocket, suit_store, pda_type, id_type)) - var/obj/item/thing = check_type - if(isnull(thing)) - continue - if(TYPE_IS_ABSTRACT(thing)) - . += "equipment includes abstract type '[thing]'" + for(var/obj/item/thing as anything in resolve_equip_to_list(check_type)) + if(TYPE_IS_ABSTRACT(thing)) + . += "equipment includes abstract type '[thing]'" for(var/check_type in hands) var/obj/item/thing = check_type @@ -62,18 +69,27 @@ . += "backpack includes abstract type '[thing]'" if(uniform && (outfit_flags & OUTFIT_HAS_VITALS_SENSOR)) - if(!ispath(uniform, /obj/item/clothing)) - . += "outfit is flagged for sensors, but uniform cannot take accessories" - var/succeeded = FALSE - var/obj/item/sensor = new /obj/item/clothing/sensor/vitals - if(uniform) - var/obj/item/clothing/wear_uniform = new uniform // sadly we need to read a list - if(wear_uniform.can_attach_accessory(sensor)) - succeeded = TRUE - qdel(wear_uniform) - if(!succeeded) - . += "outfit is flagged for sensors, but uniform does not accept sensors" - qdel(sensor) + var/list/uniforms = resolve_equip_to_list(uniform) + if(!length(uniforms)) + . += "outfit is flagged for sensors, but has no uniform" + else + var/succeeded = FALSE + var/obj/item/sensor = new /obj/item/clothing/sensor/vitals + for(var/thing in uniforms) + if(!ispath(thing, /obj/item/clothing)) + . += "outfit is flagged for sensors, but uniform [thing] cannot take accessories" + if(thing) + var/obj/item/clothing/wear_uniform = new thing // sadly we need to read a list + if(wear_uniform.can_attach_accessory(sensor)) + succeeded = TRUE + if(!QDELETED(wear_uniform)) + qdel(wear_uniform) + if(succeeded) + break + if(!QDELETED(sensor)) + qdel(sensor) + if(!succeeded) + . += "outfit is flagged for sensors, but uniform does not accept sensors" /decl/outfit/proc/pre_equip(mob/living/wearer) if(outfit_flags & OUTFIT_RESET_EQUIPMENT) @@ -91,8 +107,8 @@ equip_base(wearer, equip_adjustments) equip_id(wearer, assignment, equip_adjustments, job, rank) for(var/path in backpack_contents) - var/number = backpack_contents[path] - for(var/i=0,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 diff --git a/mods/content/fantasy/items/clothing/_loadout.dm b/mods/content/fantasy/items/clothing/_loadout.dm index 9d7f449a9f5c..7815b308a50b 100644 --- a/mods/content/fantasy/items/clothing/_loadout.dm +++ b/mods/content/fantasy/items/clothing/_loadout.dm @@ -34,10 +34,26 @@ slot = slot_w_uniform_str uid = "gear_fantasy_loincloth" +/decl/loadout_option/fantasy/uniform/shirt + name = "shirt" + path = /obj/item/clothing/shirt/crafted + uid = "gear_fantasy_shirt" + available_materials = list( + /decl/material/solid/organic/cloth, + /decl/material/solid/organic/cloth/wool, + /decl/material/solid/organic/cloth/hemp, + /decl/material/solid/organic/cloth/linen + ) + /decl/loadout_option/fantasy/uniform/jerkin name = "jerkin" path = /obj/item/clothing/shirt/jerkin uid = "gear_fantasy_jerkin" + available_materials = list( + /decl/material/solid/organic/leather, + /decl/material/solid/organic/skin/feathers, + /decl/material/solid/organic/skin/fur + ) /decl/loadout_option/fantasy/uniform/tunic name = "tunic" @@ -102,6 +118,11 @@ path = /obj/item/clothing/suit/hooded_cloak uid = "gear_fantasy_cloak_hooded" +/decl/loadout_option/fantasy/suit/winter_cloak + name = "cloak, winter" + path = /obj/item/clothing/suit/hooded_cloak/winter + uid = "gear_fantasy_cloak_hooded_winter" + /decl/loadout_option/fantasy/suit/poncho name = "poncho" path = /obj/item/clothing/suit/poncho/colored diff --git a/mods/content/fantasy/items/clothing/_recipes.dm b/mods/content/fantasy/items/clothing/_recipes.dm index 219a875f5bd0..99942d8918ed 100644 --- a/mods/content/fantasy/items/clothing/_recipes.dm +++ b/mods/content/fantasy/items/clothing/_recipes.dm @@ -1,5 +1,5 @@ /decl/stack_recipe/textiles/jerkin - result_type = /obj/item/clothing/shirt/jerkin + result_type = /obj/item/clothing/shirt/crafted category = "clothing" /decl/stack_recipe/textiles/gown diff --git a/mods/content/fantasy/items/clothing/armor.dm b/mods/content/fantasy/items/clothing/armor.dm index fd475c07f32a..22651d3243d5 100644 --- a/mods/content/fantasy/items/clothing/armor.dm +++ b/mods/content/fantasy/items/clothing/armor.dm @@ -1,3 +1,3 @@ // Override of the base item to add lore to the desc. /obj/item/clothing/suit/armor/forged/banded - desc = "A suit of overlapping armoured plates that covers the upper and lower body. Favoured by the Aegis and many Splinter Kingdoms." + desc = "A suit of overlapping armoured plates that covers the upper and lower body. Historically favoured by the Aegis and loyally adopted by many Splinter Kingdoms." diff --git a/mods/content/fantasy/items/clothing/cloak.dm b/mods/content/fantasy/items/clothing/cloak.dm new file mode 100644 index 000000000000..f61d886efe5a --- /dev/null +++ b/mods/content/fantasy/items/clothing/cloak.dm @@ -0,0 +1,17 @@ +// TODO: big fuckoff fur collar icons +/obj/item/clothing/head/hood/cloak/winter + min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE + material = /decl/material/solid/organic/cloth/wool + +/obj/item/clothing/suit/hooded_cloak/winter + cold_protection = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS|SLOT_LEGS|SLOT_HANDS|SLOT_FEET + min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE + hood = /obj/item/clothing/head/hood/cloak/winter + protects_against_weather = TRUE + material = /decl/material/solid/organic/cloth/wool + +/obj/item/clothing/suit/robe/sleeved/shrine + material = /decl/material/solid/organic/cloth/linen + paint_color = COLOR_DARK_RED + markings_color = COLOR_OFF_WHITE + markings_state_modifier = "_sleeves" diff --git a/mods/content/fantasy/items/clothing/jerkin.dm b/mods/content/fantasy/items/clothing/jerkin.dm index dc437db931dd..942a871a81cb 100644 --- a/mods/content/fantasy/items/clothing/jerkin.dm +++ b/mods/content/fantasy/items/clothing/jerkin.dm @@ -5,3 +5,15 @@ _hnoll_onmob_icon = 'mods/content/fantasy/icons/clothing/jerkin_hnoll.dmi' material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC material = /decl/material/solid/organic/leather + +/obj/item/clothing/shirt/crafted + desc = "A simple shirt, worn on the upper body." + icon = 'mods/content/fantasy/icons/clothing/jerkin.dmi' // TODO state with sleeves + sprite_sheets = list(BODYTYPE_HNOLL = 'mods/content/fantasy/icons/clothing/jerkin_hnoll.dmi') + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC + +/obj/item/clothing/shirt/crafted/wool + material = /decl/material/solid/organic/cloth/wool + +/obj/item/clothing/shirt/crafted/linen + material = /decl/material/solid/organic/cloth/linen diff --git a/mods/content/fantasy/items/clothing/trousers.dm b/mods/content/fantasy/items/clothing/trousers.dm index cbe30375bff0..4fac34236e9b 100644 --- a/mods/content/fantasy/items/clothing/trousers.dm +++ b/mods/content/fantasy/items/clothing/trousers.dm @@ -7,12 +7,9 @@ color = /decl/material/solid/organic/leather::color _hnoll_onmob_icon = 'mods/content/fantasy/icons/clothing/trousers_hnoll.dmi' -/obj/item/clothing/pants/trousers/jerkin/Initialize() - . = ..() - var/obj/item/clothing/shirt/jerkin/jerkin = new - attach_accessory(null, jerkin) - if(!(jerkin in accessories)) - qdel(jerkin) +/obj/item/clothing/pants/trousers/linen + material = /decl/material/solid/organic/cloth/linen + color = /decl/material/solid/organic/cloth/linen::color /obj/item/clothing/pants/trousers/braies name = "braies" From 5bf0757d2fa0e1ddd73f7b3bd8738228b21c3746 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 7 Dec 2024 14:01:37 +1100 Subject: [PATCH 0052/1331] Adding several low tech armor/weapon props. --- code/datums/weakref.dm | 7 +- code/game/objects/structures/armor_stand.dm | 75 ++++++++++++++++++ .../objects/structures/training_dummies.dm | 30 +++++++ .../crafting/stack_recipes/recipes_planks.dm | 4 + icons/obj/structures/archery_butt.dmi | Bin 0 -> 450 bytes icons/obj/structures/armor_stand.dmi | Bin 0 -> 278 bytes icons/obj/structures/training_dummy.dmi | Bin 0 -> 429 bytes nebula.dme | 2 + 8 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 code/game/objects/structures/armor_stand.dm create mode 100644 code/game/objects/structures/training_dummies.dm create mode 100644 icons/obj/structures/archery_butt.dmi create mode 100644 icons/obj/structures/armor_stand.dmi create mode 100644 icons/obj/structures/training_dummy.dmi diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm index a613528d27cb..ab0ecb9c6e2e 100644 --- a/code/datums/weakref.dm +++ b/code/datums/weakref.dm @@ -15,14 +15,15 @@ /weakref var/ref + var/name // Useful for input() on lists of weakrefs. // Handy info for debugging - var/tmp/ref_name + var/tmp/name var/tmp/ref_type /weakref/New(datum/D) ref = "\ref[D]" - ref_name = "[D]" + name = "[D]" ref_type = D.type /weakref/Destroy() @@ -38,7 +39,7 @@ return null /weakref/get_log_info_line() - return "[ref_name] ([ref_type]) ([ref]) (WEAKREF)" + return "[name] ([ref_type]) ([ref]) (WEAKREF)" /weakref/CanClone() return FALSE //Pass weakref as references since they're unique per atom instance \ No newline at end of file diff --git a/code/game/objects/structures/armor_stand.dm b/code/game/objects/structures/armor_stand.dm new file mode 100644 index 000000000000..96b52bc8266a --- /dev/null +++ b/code/game/objects/structures/armor_stand.dm @@ -0,0 +1,75 @@ +/obj/structure/armor_stand + name = "armor stand" + desc = "A simple stand used to hold armor and helmets for display." + icon = 'icons/obj/structures/armor_stand.dmi' + icon_state = ICON_STATE_WORLD + anchored = TRUE + density = TRUE + material = /decl/material/solid/organic/wood/oak + material_alteration = MAT_FLAG_ALTERATION_ALL + var/list/slots_to_gear = list( + (slot_wear_suit_str) = null, + (slot_head_str) = null, + (slot_belt_str) = null, + (slot_shoes_str) = null, + (slot_gloves_str) = null + ) + var/list/gear_to_slot + +/obj/structure/armor_stand/Exited(atom/movable/AM, atom/new_loc) + . = ..() + var/weakref/atom_ref = weakref(AM) + if(atom_ref in gear_to_slot) + var/slot = gear_to_slot[atom_ref] + slots_to_gear[slot] = null + LAZYREMOVE(gear_to_slot, atom_ref) + update_icon() + +/obj/structure/armor_stand/Destroy() + slots_to_gear.Cut() + LAZYCLEARLIST(gear_to_slot) + . = ..() + +/obj/structure/armor_stand/attack_hand(mob/user) + . = ..() + if(. || !LAZYLEN(gear_to_slot)) + return + if(LAZYLEN(gear_to_slot) == 1) + var/weakref/removed_item_ref = gear_to_slot[1] + else + removed_item_ref = input(user, "Which piece of equipment would you like to remove?", "Armor Stand") as null|anything in gear_to_slot + if(!CanPhysicallyInteract(user) || QDELETED(src) || QDELETED(user) || !(removed_item_ref in gear_to_slot)) + return TRUE + var/obj/item/removed_item = removed_item_ref?.resolve() + if(istype(removed_item) && !QDELETED(removed_item) && removed_item.loc == src) + removed_item.dropInto(loc) + user.put_in_hands(removed_item) + return TRUE + +/obj/structure/armor_stand/attackby(obj/item/used_item, mob/user) + if(istype(used_item, /obj/item/clothing)) + var/obj/item/clothing/clothes = used_item + if(!(clothes.fallback_slot in slots_to_gear)) + to_chat(user, SPAN_WARNING("\The [src] cannot hold \the [used_item].")) + else if(slots_to_gear[clothes.fallback_slot]) + var/weakref/atom_ref = slots_to_gear[clothes.fallback_slot] + to_chat(user, SPAN_WARNING("\The [src] is already holding \the [atom_ref.resolve()].")) + else if(user.try_unequip(clothes, src)) + var/weakref/atom_ref = weakref(clothes) + slots_to_gear[clothes.fallback_slot] = atom_ref + LAZYSET(gear_to_slot, atom_ref, clothes.fallback_slot) + to_chat(user, SPAN_NOTICE("You hang \the [clothes] from \the [src].")) + update_icon() + return TRUE + return ..() + +/obj/structure/armor_stand/on_update_icon() + . = ..() + for(var/slot in slots_to_gear) + var/weakref/atom_ref = slots_to_gear[slot] + var/obj/item/thing = atom_ref?.resolve() + if(istype(thing)) + var/image/mob_overlay = thing.get_mob_overlay(null, slot) + mob_overlay.appearance_flags |= RESET_COLOR + add_overlay(mob_overlay) + compile_overlays() diff --git a/code/game/objects/structures/training_dummies.dm b/code/game/objects/structures/training_dummies.dm new file mode 100644 index 000000000000..ed0e7fc0d94f --- /dev/null +++ b/code/game/objects/structures/training_dummies.dm @@ -0,0 +1,30 @@ +// TODO: skill check on melee/ranged hit to show bullseye/heart shot/etc +// TODO: craft the base frame, add cloth/dry grass to replace dummy, remove dummy on damage. +// TODO: add a modern-style version, merge with and replace the magnet dummies? + +/obj/structure/training_dummy + name = "training dummy" + desc = "A simple stand used to prop up a training dummy for practice." + icon = 'icons/obj/structures/training_dummy.dmi' + icon_state = ICON_STATE_WORLD + anchored = TRUE + density = TRUE + material = /decl/material/solid/organic/wood/oak + material_alteration = MAT_FLAG_ALTERATION_ALL + var/decl/material/dummy_mat = /decl/material/solid/organic/plantmatter/grass/dry + +/obj/structure/training_dummy/Initialize(ml, _mat, _reinf_mat) + if(ispath(dummy_mat)) + dummy_mat = GET_DECL(dummy_mat) + . = ..() + update_icon() + +/obj/structure/training_dummy/on_update_icon() + . = ..() + if(dummy_mat) + add_overlay(overlay_image(icon, "[icon_state]-dummy", dummy_mat.color, RESET_COLOR)) + +/obj/structure/training_dummy/archery + name = "archery butt" + desc = "A heavy circular target used for practicing archery." + icon = 'icons/obj/structures/archery_butt.dmi' diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index f58f931cf5bb..de760228f25b 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -227,3 +227,7 @@ result_type = /obj/structure/divider difficulty = MAT_VALUE_HARD_DIY +/decl/stack_recipe/planks/furniture/armor_stand + result_type = /obj/structure/armor_stand + difficulty = MAT_VALUE_NORMAL_DIY + diff --git a/icons/obj/structures/archery_butt.dmi b/icons/obj/structures/archery_butt.dmi new file mode 100644 index 0000000000000000000000000000000000000000..45bbb36c105fbcec1583a324f7fb80fb68061e3b GIT binary patch literal 450 zcmV;z0X_bSP)p8x;fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq3Xb=H^!7Qmd@s>gNJ>0stc0CsMiP z?$Q7N0N+VOK~y-6)sjgL!ypVrLx+6~K0t{$Kv6DGqjcXzx&KuP!47H~s!Cb)Z!F=b z?*Vt+p9uOA75ok;kjJdyQsQA)@@5=;C8k@6r)kN108@z|fa3?_^)y-I1t0;CAH22z z+L{F*n1u`|wAn$E)SxiTats_bW(TrY0q$|ulL4@l9Z<{I>mIzd*+Fa508$EEfP^&w z#N?jp5VZkBqJ6;k66+?RZn6xnWX!$^@S+$Pf~?x{LCC7Pg&1VjE^?RrSxrEd?`|JV sB9cQrxldqqv>U*?_5<-z;yG?4Urd%W0d9rr*#H0l07*qoM6N<$g8g&BtN;K2 literal 0 HcmV?d00001 diff --git a/icons/obj/structures/armor_stand.dmi b/icons/obj/structures/armor_stand.dmi new file mode 100644 index 0000000000000000000000000000000000000000..0431f7f626cc5d7b2d58d74dbb040a759f0f10c1 GIT binary patch literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*96ES1HL0wtYo4cj z%%A49M}Sg{B|(0{3=Yq3qyagZRUr{2E~&-IMVSR9nfZAP3>9;N!wQN@zkdlX`1tjS zmbb3fxija3H-s8oG=A_%=e&>RNrs}H-W?XkLB^Mjy%gp=nsj7Rh(hp6oBbN?&t;ucLK6TI-eUFu literal 0 HcmV?d00001 diff --git a/icons/obj/structures/training_dummy.dmi b/icons/obj/structures/training_dummy.dmi new file mode 100644 index 0000000000000000000000000000000000000000..54cf40de1c9347e605aa2f23069c6ff6f270a8a1 GIT binary patch literal 429 zcmV;e0aE^nP)Z8+u;BJ00DGTPE!Ct=GbNc0047(R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq3Xb=H^!7Qmd@s>gNJ>0stc0CsMiP z?$Q7N0Ln>3K~y-6#gj1(gD?z5i4^wIngbLr&|b9Rhx Xr^Y7VG6Jm+00000NkvXXu0mjfJaM&C literal 0 HcmV?d00001 diff --git a/nebula.dme b/nebula.dme index c635f6a07559..3a5a07def65d 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1427,6 +1427,7 @@ #include "code\game\objects\structures\_structure_icon.dm" #include "code\game\objects\structures\_structure_materials.dm" #include "code\game\objects\structures\ai_decoy.dm" +#include "code\game\objects\structures\armor_stand.dm" #include "code\game\objects\structures\barricade.dm" #include "code\game\objects\structures\barsign.dm" #include "code\game\objects\structures\bedsheet_bin.dm" @@ -1493,6 +1494,7 @@ #include "code\game\objects\structures\tank_dispenser.dm" #include "code\game\objects\structures\target_stake.dm" #include "code\game\objects\structures\town_bell.dm" +#include "code\game\objects\structures\training_dummies.dm" #include "code\game\objects\structures\transit_tubes.dm" #include "code\game\objects\structures\under_wardrobe.dm" #include "code\game\objects\structures\wall_frame.dm" From 91220810eef3ece20f9c484c506e070c1bed640d Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 9 Dec 2024 17:31:29 +1100 Subject: [PATCH 0053/1331] Cleaning up/rewriting target dummies. --- code/__defines/structures.dm | 5 +- code/datums/trading/traders/ai.dm | 44 +- code/datums/weakref.dm | 13 +- code/game/atoms_movable.dm | 3 + code/game/machinery/magnet.dm | 391 ------------------ code/game/objects/items/shooting_range.dm | 142 ------- code/game/objects/items/training_dummy.dm | 176 ++++++++ code/game/objects/objs.dm | 1 - code/game/objects/structures/__structure.dm | 7 + code/game/objects/structures/armor_stand.dm | 3 +- code/game/objects/structures/doors/_door.dm | 26 +- code/game/objects/structures/target_stake.dm | 134 ++++-- .../objects/structures/training_dummies.dm | 30 -- .../crafting/stack_recipes/recipes_grass.dm | 3 + .../crafting/stack_recipes/recipes_planks.dm | 3 + .../crafting/stack_recipes/recipes_steel.dm | 8 + icons/obj/items/training_dummies/alien.dmi | Bin 0 -> 377 bytes icons/obj/items/training_dummies/archery.dmi | Bin 0 -> 352 bytes icons/obj/items/training_dummies/damage.dmi | Bin 0 -> 767 bytes icons/obj/items/training_dummies/standard.dmi | Bin 0 -> 338 bytes icons/obj/items/training_dummies/straw.dmi | Bin 0 -> 358 bytes .../obj/items/training_dummies/syndicate.dmi | Bin 0 -> 368 bytes icons/obj/objects.dmi | Bin 127225 -> 125130 bytes icons/obj/structures/archery_butt.dmi | Bin 450 -> 0 bytes .../structures/target_stakes/archery_butt.dmi | Bin 0 -> 317 bytes .../structures/target_stakes/target_stake.dmi | Bin 0 -> 301 bytes icons/obj/structures/training_dummy.dmi | Bin 429 -> 0 bytes maps/exodus/exodus-2.dmm | 12 +- maps/ministation/ministation-1.dmm | 20 +- maps/ministation/ministation-2.dmm | 2 +- .../government/away_sites/icarus/icarus-1.dmm | 2 +- nebula.dme | 4 +- tools/map_migrations/4615_target_stakes.txt | 2 + 33 files changed, 359 insertions(+), 672 deletions(-) delete mode 100644 code/game/machinery/magnet.dm delete mode 100644 code/game/objects/items/shooting_range.dm create mode 100644 code/game/objects/items/training_dummy.dm delete mode 100644 code/game/objects/structures/training_dummies.dm create mode 100644 icons/obj/items/training_dummies/alien.dmi create mode 100644 icons/obj/items/training_dummies/archery.dmi create mode 100644 icons/obj/items/training_dummies/damage.dmi create mode 100644 icons/obj/items/training_dummies/standard.dmi create mode 100644 icons/obj/items/training_dummies/straw.dmi create mode 100644 icons/obj/items/training_dummies/syndicate.dmi delete mode 100644 icons/obj/structures/archery_butt.dmi create mode 100644 icons/obj/structures/target_stakes/archery_butt.dmi create mode 100644 icons/obj/structures/target_stakes/target_stake.dmi delete mode 100644 icons/obj/structures/training_dummy.dmi create mode 100644 tools/map_migrations/4615_target_stakes.txt diff --git a/code/__defines/structures.dm b/code/__defines/structures.dm index f91ed7aed08f..39849cecebc9 100644 --- a/code/__defines/structures.dm +++ b/code/__defines/structures.dm @@ -1 +1,4 @@ -#define STRUCTURE_FLAG_SURFACE BITFLAG(0) \ No newline at end of file +// Structure counts as a surface for the purposes of placing items on. +#define STRUCTURE_FLAG_SURFACE BITFLAG(0) +// Structure takes damage from thrown objects colliding with it. +#define STRUCTURE_FLAG_THROWN_DAMAGE BITFLAG(1) \ No newline at end of file diff --git a/code/datums/trading/traders/ai.dm b/code/datums/trading/traders/ai.dm index ca86637d32d7..5c7866db044b 100644 --- a/code/datums/trading/traders/ai.dm +++ b/code/datums/trading/traders/ai.dm @@ -98,25 +98,27 @@ They sell generic supplies and ask for generic supplies. origin = "Manifacturing Beacon" possible_trading_items = list( - /obj/structure/aicore = TRADER_THIS_TYPE, - /obj/structure/girder = TRADER_THIS_TYPE, - /obj/structure/grille = TRADER_THIS_TYPE, - /obj/structure/mopbucket = TRADER_THIS_TYPE, - /obj/structure/ore_box = TRADER_THIS_TYPE, - /obj/structure/coatrack = TRADER_THIS_TYPE, - /obj/structure/bookcase = TRADER_THIS_TYPE, - /obj/item/bee_pack = TRADER_THIS_TYPE, - /obj/item/bee_smoker = TRADER_THIS_TYPE, - /obj/item/beehive_assembly = TRADER_THIS_TYPE, - /obj/item/glass_jar = TRADER_THIS_TYPE, - /obj/item/honey_frame = TRADER_THIS_TYPE, - /obj/item/target = TRADER_ALL, - /obj/structure/tank_rack = TRADER_SUBTYPES_ONLY, - /obj/structure/filing_cabinet = TRADER_THIS_TYPE, - /obj/structure/safe = TRADER_THIS_TYPE, - /obj/structure/plushie = TRADER_SUBTYPES_ONLY, - /obj/structure/sign = TRADER_SUBTYPES_ONLY, - /obj/structure/sign/double = TRADER_BLACKLIST_ALL, - /obj/structure/sign/plaque/golden = TRADER_BLACKLIST_ALL, - /obj/structure/sign/poster = TRADER_BLACKLIST + /obj/structure/aicore = TRADER_THIS_TYPE, + /obj/structure/girder = TRADER_THIS_TYPE, + /obj/structure/grille = TRADER_THIS_TYPE, + /obj/structure/mopbucket = TRADER_THIS_TYPE, + /obj/structure/ore_box = TRADER_THIS_TYPE, + /obj/structure/coatrack = TRADER_THIS_TYPE, + /obj/structure/bookcase = TRADER_THIS_TYPE, + /obj/item/bee_pack = TRADER_THIS_TYPE, + /obj/item/bee_smoker = TRADER_THIS_TYPE, + /obj/item/beehive_assembly = TRADER_THIS_TYPE, + /obj/item/glass_jar = TRADER_THIS_TYPE, + /obj/item/honey_frame = TRADER_THIS_TYPE, + /obj/item/training_dummy = TRADER_THIS_TYPE, + /obj/item/training_dummy/syndicate = TRADER_THIS_TYPE, + /obj/item/training_dummy/alien = TRADER_THIS_TYPE, + /obj/structure/tank_rack = TRADER_SUBTYPES_ONLY, + /obj/structure/filing_cabinet = TRADER_THIS_TYPE, + /obj/structure/safe = TRADER_THIS_TYPE, + /obj/structure/plushie = TRADER_SUBTYPES_ONLY, + /obj/structure/sign = TRADER_SUBTYPES_ONLY, + /obj/structure/sign/double = TRADER_BLACKLIST_ALL, + /obj/structure/sign/plaque/golden = TRADER_BLACKLIST_ALL, + /obj/structure/sign/poster = TRADER_BLACKLIST ) \ No newline at end of file diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm index ab0ecb9c6e2e..ce8a7fa4ba63 100644 --- a/code/datums/weakref.dm +++ b/code/datums/weakref.dm @@ -14,16 +14,13 @@ return D.weakref /weakref - var/ref - var/name // Useful for input() on lists of weakrefs. - - // Handy info for debugging - var/tmp/name - var/tmp/ref_type + var/ref //- Actual datum ref. + var/name //- Useful for input() on lists of weakrefs. + var/tmp/ref_type //- Handy info for debugging /weakref/New(datum/D) - ref = "\ref[D]" - name = "[D]" + ref = "\ref[D]" + name = "[D]" ref_type = D.type /weakref/Destroy() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index d2be93779daa..674a0c21f38e 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -40,6 +40,9 @@ // Marker for alpha mask update process. null == never update, TRUE == currently updating, FALSE == finished updating. var/updating_turf_alpha_mask = null + // Damage type from using or throwing this atom. + var/atom_damage_type = BRUTE + // This proc determines if the instance is preserved when the process() despawn of crypods occurs. /atom/movable/proc/preserve_in_cryopod(var/obj/machinery/cryopod/pod) return FALSE diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm deleted file mode 100644 index 87285e914033..000000000000 --- a/code/game/machinery/magnet.dm +++ /dev/null @@ -1,391 +0,0 @@ -// Magnetic attractor, creates variable magnetic fields and attraction. -// Can also be used to emit electron/proton beams to create a center of magnetism on another tile - -// tl;dr: it's magnets lol -// This was created for firing ranges, but I suppose this could have other applications - Doohl - -var/global/list/magnetic_modules = list() - -/obj/machinery/magnetic_module - - icon = 'icons/obj/objects.dmi' - icon_state = "floor_magnet-f" - name = "Electromagnetic Generator" - desc = "A device that uses powernet to create points of magnetic energy." - level = LEVEL_BELOW_PLATING - layer = ABOVE_WIRE_LAYER - anchored = TRUE - idle_power_usage = 50 - - var/freq = 1449 // radio frequency - var/electricity_level = 1 // intensity of the magnetic pull - var/magnetic_field = 1 // the range of magnetic attraction - var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something - var/turf/center // the center of magnetic attraction - var/on = 0 - var/pulling = 0 - - // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down - var/center_x = 0 - var/center_y = 0 - var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer - -/obj/machinery/magnetic_module/Initialize() - . = ..() - global.magnetic_modules += src - var/turf/T = loc - if(!T) - return INITIALIZE_HINT_QDEL - hide(!T.is_plating()) - center = T - - if(radio_controller) - radio_controller.add_object(src, freq, RADIO_MAGNETS) - - magnetic_process() - - // update the invisibility and icon -/obj/machinery/magnetic_module/hide(var/intact) - set_invisibility(intact ? 101 : 0) - update_icon() - - // update the icon_state -/obj/machinery/magnetic_module/on_update_icon() - var/state="floor_magnet" - var/onstate="" - if(!on) - onstate="0" - - if(invisibility) - icon_state = "[state][onstate]-f" // if invisible, set icon to faded version - // in case of being revealed by T-scanner - else - icon_state = "[state][onstate]" - -/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) - - var/command = signal.data["command"] - var/modifier = signal.data["modifier"] - var/signal_code = signal.data["code"] - if(command && (signal_code == code)) - - Cmd(command, modifier) - - - -/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) - - if(command) - switch(command) - if("set-electriclevel") - if(modifier) electricity_level = modifier - if("set-magneticfield") - if(modifier) magnetic_field = modifier - - if("add-elec") - electricity_level++ - if(electricity_level > 12) - electricity_level = 12 - if("sub-elec") - electricity_level-- - if(electricity_level <= 0) - electricity_level = 1 - if("add-mag") - magnetic_field++ - if(magnetic_field > 4) - magnetic_field = 4 - if("sub-mag") - magnetic_field-- - if(magnetic_field <= 0) - magnetic_field = 1 - - if("set-x") - if(modifier) center_x = modifier - if("set-y") - if(modifier) center_y = modifier - - if("N") // NORTH - center_y++ - if("S") // SOUTH - center_y-- - if("E") // EAST - center_x++ - if("W") // WEST - center_x-- - if("C") // CENTER - center_x = 0 - center_y = 0 - if("R") // RANDOM - center_x = rand(-max_dist, max_dist) - center_y = rand(-max_dist, max_dist) - - if("set-code") - if(modifier) code = modifier - if("toggle-power") - on = !on - - if(on) - spawn() - magnetic_process() - - - -/obj/machinery/magnetic_module/Process() - if(stat & NOPOWER) - on = 0 - - // Sanity checks: - if(electricity_level <= 0) - electricity_level = 1 - if(magnetic_field <= 0) - magnetic_field = 1 - - - // Limitations: - if(abs(center_x) > max_dist) - center_x = max_dist - if(abs(center_y) > max_dist) - center_y = max_dist - if(magnetic_field > 4) - magnetic_field = 4 - if(electricity_level > 12) - electricity_level = 12 - - // Update power usage: - if(on) - update_use_power(POWER_USE_ACTIVE) - change_power_consumption(electricity_level*15, POWER_USE_ACTIVE) - else - update_use_power(POWER_USE_IDLE) - -/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling - set waitfor = FALSE - if(pulling) return - while(on) - - pulling = 1 - center = locate(x+center_x, y+center_y, z) - if(center) - for(var/obj/M in orange(magnetic_field, center)) - if(!M.anchored && (M.obj_flags & OBJ_FLAG_CONDUCTIBLE)) - step_towards(M, center) - - for(var/mob/living/silicon/S in orange(magnetic_field, center)) - if(isAI(S)) continue - step_towards(S, center) - - use_power_oneoff(electricity_level * 5) - sleep(13 - electricity_level) - - pulling = 0 - -/obj/machinery/magnetic_module/Destroy() - if(radio_controller) - radio_controller.remove_object(src, freq) - global.magnetic_modules -= src - return ..() - -/obj/machinery/magnetic_controller // TODO: IMPLEMENT OR REMOVE - name = "magnetic control console" - icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! - icon_state = "airlock_control_off" - density = TRUE - anchored = TRUE - idle_power_usage = 45 - var/frequency = 1449 - var/code = 0 - var/list/magnets = list() - var/title = "Magnetic Control Console" - var/autolink = 0 // if set to 1, can't probe for other magnets! - - var/pathpos = 1 // position in the path - var/path = "NULL" // text path of the magnet - var/speed = 1 // lowest = 1, highest = 10 - var/list/rpath = list() // real path of the magnet, used in iterator - - var/moving = 0 // 1 if scheduled to loop - var/looping = 0 // 1 if looping - - var/datum/radio_frequency/radio_connection - - -/obj/machinery/magnetic_controller/Initialize() - . = ..() - if(autolink) - for(var/obj/machinery/magnetic_module/M in global.magnetic_modules) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - if(radio_controller) - radio_connection = radio_controller.add_object(src, frequency, RADIO_MAGNETS) - - if(path) // check for default path - filter_path() // renders rpath - - -/obj/machinery/magnetic_controller/Process() - if(magnets.len == 0 && autolink) - for(var/obj/machinery/magnetic_module/M in global.magnetic_modules) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - -/obj/machinery/magnetic_controller/interface_interact(user) - interact(user) - return TRUE - -/obj/machinery/magnetic_controller/interact(mob/user) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "[title]

    " - if(!autolink) - dat += {" - Frequency: [frequency]
    - Code: [code]
    - Probe Generators
    - "} - - if(magnets.len >= 1) - - dat += "Magnets confirmed:
    " - var/i = 0 - for(var/obj/machinery/magnetic_module/M in magnets) - i++ - dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " - - dat += "
    Speed: - [speed] +
    " - dat += "Path: {[path]}
    " - dat += "Moving: [moving ? "Enabled":"Disabled"]" - - - show_browser(user, dat, "window=magnet;size=400x500") - onclose(user, "magnet") - -/obj/machinery/magnetic_controller/Topic(href, href_list) - if(..()) - return 1 - if(stat & (BROKEN|NOPOWER)) - return - usr.set_machine(src) - - if(href_list["radio-op"]) - - // Prepare signal beforehand, because this is a radio operation - var/datum/signal/signal = new - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - // Apply any necessary commands - switch(href_list["radio-op"]) - if("togglepower") - signal.data["command"] = "toggle-power" - - if("minuselec") - signal.data["command"] = "sub-elec" - if("pluselec") - signal.data["command"] = "add-elec" - - if("minusmag") - signal.data["command"] = "sub-mag" - if("plusmag") - signal.data["command"] = "add-mag" - - - // Broadcast the signal - - radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) - - spawn(1) - updateUsrDialog() // pretty sure this increases responsiveness - - if(href_list["operation"]) - switch(href_list["operation"]) - if("plusspeed") - speed ++ - if(speed > 10) - speed = 10 - if("minusspeed") - speed -- - if(speed <= 0) - speed = 1 - if("setpath") - var/newpath = sanitize(input(usr, "Please define a new path!",,path) as text|null) - if(newpath && newpath != "") - moving = 0 // stop moving - path = newpath - pathpos = 1 // reset position - filter_path() // renders rpath - - if("togglemoving") - moving = !moving - if(moving) - spawn() MagnetMove() - - - updateUsrDialog() - -/obj/machinery/magnetic_controller/proc/MagnetMove() - if(looping) return - - while(moving && rpath.len >= 1) - - if(stat & (BROKEN|NOPOWER)) - break - - looping = 1 - - // Prepare the radio signal - var/datum/signal/signal = new - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! - pathpos = 1 - - var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive - - if(!(nextmove in list("N","S","E","W","C","R"))) - // N, S, E, W are directional - // C is center - // R is random (in magnetic field's bounds) - qdel(signal) - break // break the loop if the character located is invalid - - signal.data["command"] = nextmove - - - pathpos++ // increase iterator - - // Broadcast the signal - spawn() - radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) - - if(speed == 10) - sleep(1) - else - sleep(12-speed) - - looping = 0 - - -/obj/machinery/magnetic_controller/proc/filter_path() - // Generates the rpath variable using the path string, think of this as "string2list" - // Doesn't use params2list() because of the akward way it stacks entities - rpath = list() // clear rpath - var/maximum_character = min( 50, length(path) ) // chooses the maximum length of the iterator. 50 max length - - for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path - - var/nextchar = copytext(path, i, i+1) // find next character - - if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore - rpath += copytext(path, i, i+1) // else, add to list - - // there doesn't HAVE to be separators but it makes paths syntatically visible - -/obj/machinery/magnetic_controller/Destroy() - if(radio_controller) - radio_controller.remove_object(src, frequency) - return ..() \ No newline at end of file diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm deleted file mode 100644 index 18dce99c9e59..000000000000 --- a/code/game/objects/items/shooting_range.dm +++ /dev/null @@ -1,142 +0,0 @@ -// Targets, the things that actually get shot! -/obj/item/target - name = "shooting target" - desc = "A shooting target." - icon = 'icons/obj/objects.dmi' - icon_state = "target_h" - density = FALSE - material = /decl/material/solid/organic/plastic - var/obj/structure/target_stake/stake - var/hp = 1800 - var/icon/virtualIcon - var/list/bulletholes = list() - -/obj/item/target/Destroy() - . = ..() - if (stake) - stake.set_target(null) - -/obj/item/target/attackby(var/obj/item/W, var/mob/user) - if(IS_WELDER(W)) - var/obj/item/weldingtool/WT = W - if(WT.weld(0, user)) - overlays.Cut() - bulletholes.Cut() - hp = initial(hp) - to_chat(user, "You slice off [src]'s uneven chunks of aluminium and scorch marks.") - return TRUE - return ..() - -/obj/item/target/attack_hand(var/mob/user) - // taking pinned targets off! - if(!stake || !user.check_dexterity(DEXTERITY_HOLD_ITEM)) - return ..() - return stake.attack_hand(user) - -/obj/item/target/syndicate - icon_state = "target_s" - desc = "A shooting target that looks like a hostile agent." - hp = 2600 // i guess syndie targets are sturdier? -/obj/item/target/alien - icon_state = "target_q" - desc = "A shooting target with a threatening silhouette." - hp = 2350 // alium onest too kinda - -/obj/item/target/bullet_act(var/obj/item/projectile/Proj) - var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" - var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) - var/decaltype = 1 // 1 - scorch, 2 - bullet - - if(istype(/obj/item/projectile/bullet, Proj)) - decaltype = 2 - - - virtualIcon = new(icon, icon_state) - - if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) - - hp -= Proj.damage - if(hp <= 0) - visible_message(SPAN_WARNING("\The [src] breaks into tiny pieces and collapses!")) - qdel(src) - - // Create a temporary object to represent the damage - var/obj/bmark = new - bmark.pixel_x = p_x - bmark.pixel_y = p_y - bmark.icon = 'icons/effects/effects.dmi' - bmark.layer = ABOVE_OBJ_LAYER - bmark.icon_state = "scorch" - - if(decaltype == 1) - // Energy weapons are hot. they scorch! - - // offset correction - bmark.pixel_x-- - bmark.pixel_y-- - - if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) - bmark.icon_state = "scorch" - bmark.set_dir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design - - - else - bmark.icon_state = "light_scorch" - else - - // Bullets are hard. They make dents! - bmark.icon_state = "dent" - - if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes - if(decaltype == 2) // bullet - if(prob(Proj.damage+30)) // bullets make holes more commonly! - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - else // Lasers! - if(prob(Proj.damage-10)) // lasers make holes less commonly - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - - // draw bullet holes - for(var/datum/bullethole/B in bulletholes) - - virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right - virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom - - overlays += bmark // add the decal - - icon = virtualIcon // apply bulletholes over decals - - return - - return PROJECTILE_CONTINUE // the bullet/projectile goes through the target! - - -// Small memory holder entity for transparent bullet holes -/datum/bullethole - // First box - var/b1x1 = 0 - var/b1x2 = 0 - var/b1y = 0 - - // Second box - var/b2x = 0 - var/b2y1 = 0 - var/b2y2 = 0 - -/datum/bullethole/New(var/obj/item/target/Target, var/pixel_x = 0, var/pixel_y = 0) - if(!Target) return - - // Randomize the first box - b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4) - b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4) - b1y = pixel_y - if(prob(35)) - b1y += rand(-4,4) - - // Randomize the second box - b2x = pixel_x - if(prob(35)) - b2x += rand(-4,4) - b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) - b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) - - Target.bulletholes.Add(src) \ No newline at end of file diff --git a/code/game/objects/items/training_dummy.dm b/code/game/objects/items/training_dummy.dm new file mode 100644 index 000000000000..d870699ddd1d --- /dev/null +++ b/code/game/objects/items/training_dummy.dm @@ -0,0 +1,176 @@ +// Used in conjunction with /obj/structure/target_stake. +/obj/item/training_dummy + name = "shooting target" + desc = "A shooting target." + icon = 'icons/obj/items/training_dummies/standard.dmi' + icon_state = ICON_STATE_WORLD + material = /decl/material/solid/metal/aluminium + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC + max_health = 1800 + appearance_flags = KEEP_TOGETHER // Needed for BLEND_INSET_OVERLAY on the decals. + + // Burn/bullet hole/dent decals to apply to our icon. + var/list/decals + var/const/MAX_DECALS = 250 + + // Very crude bounding box; we do pixel-precise checking within these ranges. + var/decal_min_x = 7 + var/decal_min_y = 3 + var/decal_range_x = 17 + var/decal_range_y = 25 + +/obj/item/training_dummy/proc/get_damage_decal_icon() + return 'icons/obj/items/training_dummies/damage.dmi' + +// Can't repair cloth, straw or plastic targets currently. +/obj/item/training_dummy/proc/can_repair_with(obj/item/thing) + return IS_WELDER(thing) && istype(material, /decl/material/solid/metal) + +/obj/item/training_dummy/proc/perform_repair(mob/user, obj/item/tool) + var/obj/item/weldingtool/welder = tool + if(!istype(welder) || !welder.isOn()) + to_chat(user, SPAN_WARNING("Turn \the [welder] on first.")) + return FALSE + return tool.do_tool_interaction(TOOL_WELDER, user, src, 2 SECONDS) + +/obj/item/training_dummy/proc/repair_target_dummy(obj/item/used_item, mob/user) + + if(!can_repair_with(used_item)) + return FALSE + + var/max_health = get_max_health() + if(current_health >= max_health) + to_chat(user, SPAN_NOTICE("\The [src] doesn't need repairs.")) + return TRUE + + if(perform_repair(user, used_item)) + current_health += rand(100,200) // They have a lot of HP. + if(LAZYLEN(decals)) + if(current_health >= get_max_health()) + current_health = get_max_health() + LAZYCLEARLIST(decals) + else + var/remove_decal = pick(decals) + LAZYREMOVE(decals, remove_decal) + update_icon() + return TRUE + + return FALSE + +/obj/item/training_dummy/attackby(obj/item/used_item, mob/user) + if(repair_target_dummy(used_item, user)) + return TRUE + return ..() + +/obj/item/training_dummy/take_damage(damage, damage_type, damage_flags, inflicter, armor_pen, silent, do_update_health) + + var/old_health = current_health + . = ..() + if(QDELETED(src) || old_health == current_health) + return + + if(damage <= 0) + update_icon() + return + + if(LAZYLEN(decals) >= MAX_DECALS) + return + + var/decal_icon = get_damage_decal_icon() + if(!decal_icon) + return + + var/decal_state + if(damage_type == BURN) + if(damage < 10) + decal_state = "light_scorch" + else + decal_state = "scorch" + else if(damage_type == BRUTE) + if(damage < 10) + decal_state = "dent" + else + decal_state = "bhole" + else + return + + var/image/new_decal = image(decal_icon, decal_state) + new_decal.blend_mode = BLEND_INSET_OVERLAY + new_decal = apply_decal_offsets(new_decal) + if(new_decal) + LAZYADD(decals, new_decal) + update_icon() + +/obj/item/training_dummy/proc/apply_decal_offsets(image/decal) + + // Static list cache for this broke immediately, so seems like we need a fresh icon every time. + // At least it isn't being assigned anywhere so shouldn't cause network overhead. + var/icon/check_icon = icon(icon, icon_state) + if(!check_icon) + return + + var/use_pixel_x + var/use_pixel_y + var/sanity = 100 + while(sanity) + sanity-- + use_pixel_x = decal_min_x + rand(decal_range_x) + use_pixel_y = decal_min_y + rand(decal_range_y) + var/check_pixel = check_icon.GetPixel(use_pixel_x, use_pixel_y) + // Pixel has a colour and no alpha component; we are good. + if(istext(check_pixel) && length(check_pixel) == 7) + break + + if(!sanity || !isnum(use_pixel_x) || use_pixel_x < 0 || !isnum(use_pixel_y) || use_pixel_y < 0) + return + + decal.pixel_x = use_pixel_x + decal.pixel_y = use_pixel_y + return decal + +/obj/item/training_dummy/on_update_icon() + . = ..() + if(LAZYLEN(decals)) + if(current_health >= get_max_health()) + LAZYCLEARLIST(decals) + else + set_overlays(decals.Copy()) + if(istype(loc, /obj/structure/target_stake)) + compile_overlays() // We need to update our loc immediately. + loc.update_icon() + +/obj/item/training_dummy/syndicate + icon = 'icons/obj/items/training_dummies/syndicate.dmi' + desc = "A shooting target that looks like a hostile agent." + decal_min_x = 7 + decal_min_y = 2 + decal_range_x = 18 + decal_range_y = 30 + +/obj/item/training_dummy/alien + icon = 'icons/obj/items/training_dummies/alien.dmi' + desc = "A shooting target with a threatening silhouette." + decal_min_x = 7 + decal_min_y = 2 + decal_range_x = 18 + decal_range_y = 30 + +/obj/item/training_dummy/straw + name = "training dummy" + icon = 'icons/obj/items/training_dummies/straw.dmi' + desc = "A roughly humanoid shape used for melee practice." + material = /decl/material/solid/organic/plantmatter/grass/dry + material_alteration = MAT_FLAG_ALTERATION_ALL + decal_min_x = 8 + decal_min_y = 12 + decal_range_x = 16 + decal_range_y = 17 + +/obj/item/training_dummy/straw/archery + name = "archery butt" + icon = 'icons/obj/items/training_dummies/archery.dmi' + desc = "A thick circular mat used for archery practice." + decal_min_x = 7 + decal_min_y = 5 + decal_range_x = 19 + decal_range_y = 21 diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index b9966cf27e9d..f1838fcfa065 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -17,7 +17,6 @@ var/sharp = 0 // whether this object cuts var/edge = 0 // whether this object is more likely to dismember var/in_use = 0 // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! - var/atom_damage_type = BRUTE var/armor_penetration = 0 var/anchor_fall = FALSE var/holographic = 0 //if the obj is a holographic object spawned by the holodeck diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index b1764d52754a..29c20b4b6d9a 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -322,3 +322,10 @@ Note: This proc can be overwritten to allow for different types of auto-alignmen W.pixel_x = (CELLSIZE * (cell_x + 0.5)) - center["x"] W.pixel_y = (CELLSIZE * (cell_y + 0.5)) - center["y"] W.pixel_z = 0 + +/obj/structure/hitby(var/atom/movable/AM, var/datum/thrownthing/TT) + . = ..() + if(. && (structure_flags & STRUCTURE_FLAG_THROWN_DAMAGE)) + visible_message(SPAN_DANGER("\The [src] was hit by \the [AM].")) + playsound(src.loc, hitsound, 100, 1) + take_damage(AM.get_thrown_attack_force() * (TT.speed/THROWFORCE_SPEED_DIVISOR), AM.atom_damage_type) diff --git a/code/game/objects/structures/armor_stand.dm b/code/game/objects/structures/armor_stand.dm index 96b52bc8266a..7dabc4c24f02 100644 --- a/code/game/objects/structures/armor_stand.dm +++ b/code/game/objects/structures/armor_stand.dm @@ -34,8 +34,9 @@ . = ..() if(. || !LAZYLEN(gear_to_slot)) return + var/weakref/removed_item_ref if(LAZYLEN(gear_to_slot) == 1) - var/weakref/removed_item_ref = gear_to_slot[1] + removed_item_ref = gear_to_slot[1] else removed_item_ref = input(user, "Which piece of equipment would you like to remove?", "Armor Stand") as null|anything in gear_to_slot if(!CanPhysicallyInteract(user) || QDELETED(src) || QDELETED(user) || !(removed_item_ref in gear_to_slot)) diff --git a/code/game/objects/structures/doors/_door.dm b/code/game/objects/structures/doors/_door.dm index 9079662e2438..745ad4bea2d6 100644 --- a/code/game/objects/structures/doors/_door.dm +++ b/code/game/objects/structures/doors/_door.dm @@ -1,18 +1,18 @@ /obj/structure/door - name = "door" - icon = 'icons/obj/doors/material_doors.dmi' - icon_state = "metal" - hitsound = 'sound/weapons/genhit.ogg' - material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC | MAT_FLAG_ALTERATION_COLOR - max_health = 50 - density = TRUE - anchored = TRUE - opacity = TRUE - - var/has_window = FALSE - var/changing_state = FALSE - var/icon_base + name = "door" + icon = 'icons/obj/doors/material_doors.dmi' + icon_state = "metal" + hitsound = 'sound/weapons/genhit.ogg' + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC | MAT_FLAG_ALTERATION_COLOR + max_health = 50 + density = TRUE + anchored = TRUE + opacity = TRUE + structure_flags = STRUCTURE_FLAG_THROWN_DAMAGE + var/has_window = FALSE + var/changing_state = FALSE var/door_sound_volume = 25 + var/icon_base /obj/structure/door/Initialize() ..() diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index d1e4d36df79d..0dc8afefce6a 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -1,49 +1,97 @@ -// Target stakes for the firing range. +// TODO: skill check on melee/ranged hit to show bullseye/heart shot/etc /obj/structure/target_stake - name = "target stake" - desc = "A thin platform with negatively-magnetized wheels." - icon = 'icons/obj/objects.dmi' - icon_state = "target_stake" - density = TRUE - obj_flags = OBJ_FLAG_CONDUCTIBLE - var/obj/item/target/pinned_target - -/obj/structure/target_stake/attackby(var/obj/item/W, var/mob/user) - if (!pinned_target && istype(W, /obj/item/target) && user.try_unequip(W, get_turf(src))) - to_chat(user, "You slide [W] into the stake.") - set_target(W) + name = "target stake" + desc = "A simple stand used to prop up a target for practice." + icon = 'icons/obj/structures/target_stakes/target_stake.dmi' + icon_state = ICON_STATE_WORLD + anchored = TRUE + density = TRUE + material = /decl/material/solid/organic/wood/oak + material_alteration = MAT_FLAG_ALTERATION_ALL + structure_flags = STRUCTURE_FLAG_THROWN_DAMAGE + var/obj/item/training_dummy/dummy + +/obj/structure/target_stake/Destroy() + dummy = null + . = ..() + +/obj/structure/target_stake/attackby(obj/item/used_item, mob/user) + if(dummy?.repair_target_dummy(used_item, user)) return TRUE return ..() -/obj/structure/target_stake/attack_hand(var/mob/user) - if (!pinned_target || !user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) - return ..() - to_chat(user, SPAN_NOTICE("You take \the [pinned_target] off the stake.")) - user.put_in_hands(pinned_target) - set_target(null) - return TRUE - -/obj/structure/target_stake/proc/set_target(var/obj/item/target/T) - if (T) - set_density(0) - T.set_density(1) - T.pixel_x = 0 - T.pixel_y = 0 - T.layer = ABOVE_OBJ_LAYER - events_repository.register(/decl/observ/moved, T, src, TYPE_PROC_REF(/atom/movable, move_to_turf)) - events_repository.register(/decl/observ/moved, src, T, TYPE_PROC_REF(/atom/movable, move_to_turf)) - T.stake = src - pinned_target = T - else - set_density(1) - if(pinned_target) - pinned_target.set_density(0) - pinned_target.layer = OBJ_LAYER - events_repository.unregister(/decl/observ/moved, pinned_target, src) - events_repository.unregister(/decl/observ/moved, src, pinned_target) - pinned_target.stake = null - pinned_target = null +/obj/structure/target_stake/take_damage(damage, damage_type, damage_flags, inflicter, armor_pen, silent, do_update_health) + if(dummy) + . = dummy.take_damage(damage, damage_type, damage_flags, inflicter, armor_pen, silent, do_update_health) + if(QDELETED(dummy)) + dummy = null + update_icon() + return + return ..() -/obj/structure/target_stake/Destroy() +/obj/structure/target_stake/proc/can_hold_dummy(mob/user, obj/item/training_dummy/new_dummy) + return istype(new_dummy) && !istype(new_dummy, /obj/item/training_dummy/straw/archery) + +/obj/structure/target_stake/attack_hand(mob/user) + if(dummy) + dummy.dropInto(loc) + user.put_in_hands(dummy) + dummy = null + update_icon() + return TRUE + return ..() + +/obj/structure/target_stake/attackby(obj/item/used_item, mob/user) + if(istype(used_item, /obj/item/training_dummy) && can_hold_dummy(user, used_item)) + if(dummy) + to_chat(user, SPAN_WARNING("\The [src] is already holding \the [dummy].")) + else if(user.try_unequip(used_item, src)) + dummy = used_item + visible_message(SPAN_NOTICE("\The [user] places \the [dummy] onto \the [src].")) + update_icon() + return TRUE + return ..() + +/obj/structure/target_stake/Initialize(ml, _mat, _reinf_mat) + if(ispath(dummy)) + dummy = new dummy(src) + . = ..() + update_icon() + +/obj/structure/target_stake/on_update_icon() . = ..() - set_target(null) \ No newline at end of file + if(dummy) + // WTB way to stop vis_contents inheriting atom color + var/image/dummy_overlay = new /image + dummy_overlay.appearance = dummy + dummy_overlay.pixel_x = 0 + dummy_overlay.pixel_y = 0 + dummy_overlay.pixel_z = 0 + dummy_overlay.pixel_w = 0 + dummy_overlay.plane = FLOAT_PLANE + dummy_overlay.layer = FLOAT_LAYER + dummy_overlay.appearance_flags |= RESET_COLOR + add_overlay(dummy_overlay) + +// Subtypes below. +/obj/structure/target_stake/steel + material = /decl/material/solid/metal/steel + +/obj/structure/target_stake/archery + name = "archery butt" + desc = "A heavy circular target used for practicing archery." + icon = 'icons/obj/structures/target_stakes/archery_butt.dmi' + +/obj/structure/target_stake/archery/can_hold_dummy(mob/user, obj/item/training_dummy/new_dummy) + return istype(new_dummy, /obj/item/training_dummy/straw/archery) + +// Subtypes with/for dummies. +/obj/structure/target_stake/mapped + dummy = /obj/item/training_dummy/straw + +/obj/structure/target_stake/steel/mapped/Initialize() + dummy = pick(/obj/item/training_dummy, /obj/item/training_dummy/alien, /obj/item/training_dummy/syndicate) + return ..() + +/obj/structure/target_stake/archery/mapped + dummy = /obj/item/training_dummy/straw/archery diff --git a/code/game/objects/structures/training_dummies.dm b/code/game/objects/structures/training_dummies.dm deleted file mode 100644 index ed0e7fc0d94f..000000000000 --- a/code/game/objects/structures/training_dummies.dm +++ /dev/null @@ -1,30 +0,0 @@ -// TODO: skill check on melee/ranged hit to show bullseye/heart shot/etc -// TODO: craft the base frame, add cloth/dry grass to replace dummy, remove dummy on damage. -// TODO: add a modern-style version, merge with and replace the magnet dummies? - -/obj/structure/training_dummy - name = "training dummy" - desc = "A simple stand used to prop up a training dummy for practice." - icon = 'icons/obj/structures/training_dummy.dmi' - icon_state = ICON_STATE_WORLD - anchored = TRUE - density = TRUE - material = /decl/material/solid/organic/wood/oak - material_alteration = MAT_FLAG_ALTERATION_ALL - var/decl/material/dummy_mat = /decl/material/solid/organic/plantmatter/grass/dry - -/obj/structure/training_dummy/Initialize(ml, _mat, _reinf_mat) - if(ispath(dummy_mat)) - dummy_mat = GET_DECL(dummy_mat) - . = ..() - update_icon() - -/obj/structure/training_dummy/on_update_icon() - . = ..() - if(dummy_mat) - add_overlay(overlay_image(icon, "[icon_state]-dummy", dummy_mat.color, RESET_COLOR)) - -/obj/structure/training_dummy/archery - name = "archery butt" - desc = "A heavy circular target used for practicing archery." - icon = 'icons/obj/structures/archery_butt.dmi' diff --git a/code/modules/crafting/stack_recipes/recipes_grass.dm b/code/modules/crafting/stack_recipes/recipes_grass.dm index 398b3ec168b0..badba30d8a47 100644 --- a/code/modules/crafting/stack_recipes/recipes_grass.dm +++ b/code/modules/crafting/stack_recipes/recipes_grass.dm @@ -41,3 +41,6 @@ /obj/item/stack/material/bundle, /obj/item/stack/material/thread ) + +/decl/stack_recipe/woven/dummy + result_type = /obj/item/training_dummy/straw diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index de760228f25b..4d72d242bc60 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -231,3 +231,6 @@ result_type = /obj/structure/armor_stand difficulty = MAT_VALUE_NORMAL_DIY +/decl/stack_recipe/planks/furniture/target_stake + result_type = /obj/structure/target_stake + difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_steel.dm b/code/modules/crafting/stack_recipes/recipes_steel.dm index 8f15300c3677..9befbe3d3f12 100644 --- a/code/modules/crafting/stack_recipes/recipes_steel.dm +++ b/code/modules/crafting/stack_recipes/recipes_steel.dm @@ -108,3 +108,11 @@ /decl/stack_recipe/steel/furniture/drill_brace result_type = /obj/structure/drill_brace + +/decl/stack_recipe/steel/furniture/armor_stand + result_type = /obj/structure/armor_stand + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/steel/furniture/target_stake + result_type = /obj/structure/target_stake + difficulty = MAT_VALUE_NORMAL_DIY diff --git a/icons/obj/items/training_dummies/alien.dmi b/icons/obj/items/training_dummies/alien.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a08ae3ce7fa2246caf256a8a3a130f904bc5776b GIT binary patch literal 377 zcmV-<0fzpGP)V=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 zsXV_ZCq;>iGbOXA7|1u|;!G<_%uR)`l@(n5T)@Tx06PL2GXQEU^ zks6blaB;H9G~p2BV()*Q?o(k2tfB)fJy{|*(+Ew z=Ol@#bVv05zK`LRDxV&K0SMnF9t)6(Jf2mLi(?Z=igjdVlEgi`T(`oy6{(jKQU zm{q}`ZJ`993-~|ak^@J`wM_-?lrq#{9!kI5a^YY90SJeF1-_nkA7uj`rLF1T#=q+Y X31t_O{b;8400000NkvXXu0mjfMX8tX literal 0 HcmV?d00001 diff --git a/icons/obj/items/training_dummies/archery.dmi b/icons/obj/items/training_dummies/archery.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b3e58a34a26c6675d645a6d2b1942223b6170483 GIT binary patch literal 352 zcmV-m0iXVfP)V=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 zsXV_ZCq;>iGbOXA7|1u|;!G<_%uR)`l@(n5T)@Tx06PL2GXQEUnR?nbEn}|$4TO}B z!$><&Hh_)#gjgupbLPPk3b$tiFb{<=a)ntam~%;eVt=9H21Kg5&bePe#Qi&%M?RLW y`EBW-9oFsU>D_Iby8?RKA0rFJ9O^CP-|7UHa2PGP@3?FL0000 zT2F1{I%FWg_Q3MHi2Yl=u-$1Br%YN{%ewOHP3|jflbqx4A9NHr`muTav6Zj?a+p`_ zD%La1-H=gUZPYIjl2B+fL9nm;1-GkuYD7q?U*)tepN^de7O57Wz1+*KGpjuMu~OB) zGiSfcR9xQwQYrIk{AxFIs~ra~3vW`jl@R{0JcogS$-vXaF{EP7+uMqLN{%A!AFt09 z@|d(%Z1FV{&NN#|XX`UNb}}^eJYq<_ew8tR#Za7KE8F!fy$x+FXI6>EGFdK~9J_IY z`-=W0|LvP=^#3ls{48)wZuS;|LmJ0^c)oYkkW)M`Vd@tq=j99rtPPqBK8y)m48aUD zm=1_AEF)5ALpz6nlf*^qfUcW4-yY40{$9Jk)9~Mky$&1?HJ5JresBLViTdaB_D8L? z+w=YSluJq5Z`(ef7jO0a+VaaUXBX$qt==&+(krumZ`}T6Q@-uI-y(46M0Ne17`^;^ zzmw11EKj}vefGk#-2YvB86*9#B=^kMT;b@qA7Z$>||vu`n2xhm^s>D}s{k8`T+*6z7~eD+Q0jSiuq-tYfT zU$tu5kw2e**33BnoIPpV%$dfUb7QZU?e70Suex#F>w9N&Du17SSMcNe8}-SYhV%Z{ zJ6tw-Q8UZ#5Sn$whcis$(wUK$P3m8`wyiSQ{GWe+?ro*-OwIfI_Su&_df0t4eo0Dm Vn%s2<2Vjb3@O1TaS?83{1OOfwMh5@@ literal 0 HcmV?d00001 diff --git a/icons/obj/items/training_dummies/standard.dmi b/icons/obj/items/training_dummies/standard.dmi new file mode 100644 index 0000000000000000000000000000000000000000..d4b367b4db8429c70e6ab26cf917ad1119ef2bd3 GIT binary patch literal 338 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*XliOMUAiZ8+u;BJ00DGTPE!Ct=GbNc003!vR9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@*+P6OZW^*7W$JrO+8 z02Z&z8Gx|>aAbz5zJg(_v@S8f{yNZoFik|w(lPVgWFQhX**V5sdCo24*CP9tZR*1E z2JlfuP-KG5x}v4R>c{h)Um;?L*Br!8jV_;}#Xy{>J z*yy7Y0F+`Z3GxeOaCmkj4amu?3W+FjNi9w;$}A|!%+F(BsF)KRR!~&>{Y!Ac$FEPc zymhtCojD)8A=Kca@qggEfts*E$mTBV83ZXWgmy8b_k8I%4-R-*e zDTCQ@w#L;=m-P*=?)2VoJ4ert(lqT>y3t+E%=Qm@BQE{-^DL8v)Z&>59mq; MPgg&ebxsLQ0P$6bP5=M^ literal 0 HcmV?d00001 diff --git a/icons/obj/objects.dmi b/icons/obj/objects.dmi index 4705a37d998f32d2ff382b7d0b67b31e0a5e0ff7..ec7ad98c685e4799fb99ef98a614d4f843dd5743 100644 GIT binary patch delta 51252 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 diff --git a/icons/obj/structures/archery_butt.dmi b/icons/obj/structures/archery_butt.dmi deleted file mode 100644 index 45bbb36c105fbcec1583a324f7fb80fb68061e3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)p8x;fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq3Xb=H^!7Qmd@s>gNJ>0stc0CsMiP z?$Q7N0N+VOK~y-6)sjgL!ypVrLx+6~K0t{$Kv6DGqjcXzx&KuP!47H~s!Cb)Z!F=b z?*Vt+p9uOA75ok;kjJdyQsQA)@@5=;C8k@6r)kN108@z|fa3?_^)y-I1t0;CAH22z z+L{F*n1u`|wAn$E)SxiTats_bW(TrY0q$|ulL4@l9Z<{I>mIzd*+Fa508$EEfP^&w z#N?jp5VZkBqJ6;k66+?RZn6xnWX!$^@S+$Pf~?x{LCC7Pg&1VjE^?RrSxrEd?`|JV sB9cQrxldqqv>U*?_5<-z;yG?4Urd%W0d9rr*#H0l07*qoM6N<$g8g&BtN;K2 diff --git a/icons/obj/structures/target_stakes/archery_butt.dmi b/icons/obj/structures/target_stakes/archery_butt.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b2e7253cf4bd7a2578f501365c7f8b67f559c538 GIT binary patch literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*96ETit7~3rQkkcF z%vA2mBS0y}k|4ie28U-i(tw=Is*s2hm(=3qqRfJl%=|nChKf1CVFg8{-@gPGeEj-E z%Uf6L+?n&i8$t~(8b5fXbKXbuBtub8?+y#&Amhu%UJ7#_O*%3uL?L*k&3+Aa*UoNZ z)y*p;PMAM=e0iKqJOm1EY3wXp03K^Nx6X&E| zaCQ)Q807ed$;-`PxwbRYk)s^XN>at&FwJ0e;bjzO=Qzu>iG^W7hj@{@Nu@2&N(N6? KKbLh*2~7aM`)@h` literal 0 HcmV?d00001 diff --git a/icons/obj/structures/target_stakes/target_stake.dmi b/icons/obj/structures/target_stakes/target_stake.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7423233493f0af48c8b52886f7bb4c8cee2a3547 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*bal-;bns+qQkkcF z%r7qCvp^}vk|4ie28U-i(tw=Is*s2hm(=3qqRfJl%=|nChKf1CVFg8{-@gPGeEj-E z%Uf6L+?n&i8$t~(8b5fXbKXbuBtub8?+y#&Amhu%UJ7#_O*%3uL?L*k&3+Aa*UoNZ z)y*p;PMAM=FxJ6? z!S}!ghMCVbG&FQHG+r=>8Z8+u;BJ00DGTPE!Ct=GbNc0047(R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq3Xb=H^!7Qmd@s>gNJ>0stc0CsMiP z?$Q7N0Ln>3K~y-6#gj1(gD?z5i4^wIngbLr&|b9Rhx Xr^Y7VG6Jm+00000NkvXXu0mjfJaM&C diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index ae068ece7af6..f94748891179 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -200,7 +200,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/range) "aaD" = ( -/obj/structure/target_stake, +/obj/structure/target_stake/steel, /obj/effect/floor_decal/industrial/hatch/yellow, /turf/floor/tiled/steel_grid, /area/exodus/security/range) @@ -344,11 +344,11 @@ /area/exodus/security/range) "aaV" = ( /obj/structure/closet/crate, -/obj/item/target, -/obj/item/target, -/obj/item/target, -/obj/item/target, -/obj/item/target, +/obj/item/training_dummy, +/obj/item/training_dummy, +/obj/item/training_dummy, +/obj/item/training_dummy, +/obj/item/training_dummy, /obj/machinery/light, /turf/floor/tiled/steel_grid, /area/exodus/security/range) diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 9aac14f4417d..23ce124bb76e 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -7240,7 +7240,7 @@ /turf/floor/tiled/white, /area/ministation/medical) "Ft" = ( -/obj/structure/target_stake, +/obj/structure/target_stake/steel, /turf/floor/tiled, /area/ministation/security) "Fy" = ( @@ -8356,18 +8356,18 @@ /area/ministation/medical) "Me" = ( /obj/structure/rack, -/obj/item/target, -/obj/item/target/alien, -/obj/item/target/syndicate, +/obj/item/training_dummy, +/obj/item/training_dummy/alien, +/obj/item/training_dummy/syndicate, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, -/obj/item/target/syndicate, -/obj/item/target/syndicate, -/obj/item/target/syndicate, -/obj/item/target/alien, -/obj/item/target/alien, -/obj/item/target/alien, +/obj/item/training_dummy/syndicate, +/obj/item/training_dummy/syndicate, +/obj/item/training_dummy/syndicate, +/obj/item/training_dummy/alien, +/obj/item/training_dummy/alien, +/obj/item/training_dummy/alien, /turf/floor/tiled, /area/ministation/security) "Mp" = ( diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index f70c0d369261..feaf95611ccf 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -3345,7 +3345,7 @@ /turf/floor/plating, /area/ministation/maint/l3nw) "qj" = ( -/obj/structure/target_stake, +/obj/structure/target_stake/steel, /obj/item/toy/figure/clown, /turf/floor/reinforced/airless, /area/space) diff --git a/mods/content/government/away_sites/icarus/icarus-1.dmm b/mods/content/government/away_sites/icarus/icarus-1.dmm index 8779b3ce78de..1be37c41159b 100644 --- a/mods/content/government/away_sites/icarus/icarus-1.dmm +++ b/mods/content/government/away_sites/icarus/icarus-1.dmm @@ -1931,7 +1931,7 @@ /turf/floor/plating, /area/icarus/vessel) "go" = ( -/obj/structure/target_stake, +/obj/structure/target_stake/steel, /turf/floor/plating, /area/icarus/vessel) "gp" = ( diff --git a/nebula.dme b/nebula.dme index 3a5a07def65d..fd1d6d087a12 100644 --- a/nebula.dme +++ b/nebula.dme @@ -842,7 +842,6 @@ #include "code\game\machinery\igniter.dm" #include "code\game\machinery\jukebox.dm" #include "code\game\machinery\lightswitch.dm" -#include "code\game\machinery\magnet.dm" #include "code\game\machinery\mass_driver.dm" #include "code\game\machinery\mech_recharger.dm" #include "code\game\machinery\message_server.dm" @@ -1109,10 +1108,10 @@ #include "code\game\objects\items\rescuebag.dm" #include "code\game\objects\items\rock.dm" #include "code\game\objects\items\saddle.dm" -#include "code\game\objects\items\shooting_range.dm" #include "code\game\objects\items\silencer.dm" #include "code\game\objects\items\spirit_board.dm" #include "code\game\objects\items\toys.dm" +#include "code\game\objects\items\training_dummy.dm" #include "code\game\objects\items\trash.dm" #include "code\game\objects\items\umbrella.dm" #include "code\game\objects\items\waterskin.dm" @@ -1494,7 +1493,6 @@ #include "code\game\objects\structures\tank_dispenser.dm" #include "code\game\objects\structures\target_stake.dm" #include "code\game\objects\structures\town_bell.dm" -#include "code\game\objects\structures\training_dummies.dm" #include "code\game\objects\structures\transit_tubes.dm" #include "code\game\objects\structures\under_wardrobe.dm" #include "code\game\objects\structures\wall_frame.dm" diff --git a/tools/map_migrations/4615_target_stakes.txt b/tools/map_migrations/4615_target_stakes.txt new file mode 100644 index 000000000000..8e4677a219f1 --- /dev/null +++ b/tools/map_migrations/4615_target_stakes.txt @@ -0,0 +1,2 @@ +/obj/item/target/@SUBTYPES : /obj/item/target_dummy/@SUBTYPES{@OLD} +/obj/structure/target_stake/@SUBTYPES : /obj/structure/target_stake/steel/@SUBTYPES{@OLD} From 6cbf766e752b1609ba70707683ad7152fce39e56 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 9 Dec 2024 21:35:14 +1100 Subject: [PATCH 0054/1331] Removed slavers' den. --- maps/away/slavers/icons/abolitionist.dmi | Bin 1391 -> 0 bytes maps/away/slavers/icons/areas.dmi | Bin 1878 -> 0 bytes maps/away/slavers/icons/uniform.dmi | Bin 781 -> 0 bytes maps/away/slavers/slavers_base.dm | 185 - maps/away/slavers/slavers_base.dmm | 44306 ---------------- maps/away/slavers/slavers_base_areas.dm | 42 - maps/away_sites_testing/away_sites_testing.dm | 1 - maps/exodus/exodus.dm | 1 - maps/ministation/ministation.dm | 1 - maps/tradeship/tradeship.dm | 1 - 10 files changed, 44537 deletions(-) delete mode 100644 maps/away/slavers/icons/abolitionist.dmi delete mode 100644 maps/away/slavers/icons/areas.dmi delete mode 100644 maps/away/slavers/icons/uniform.dmi delete mode 100644 maps/away/slavers/slavers_base.dm delete mode 100644 maps/away/slavers/slavers_base.dmm delete mode 100644 maps/away/slavers/slavers_base_areas.dm diff --git a/maps/away/slavers/icons/abolitionist.dmi b/maps/away/slavers/icons/abolitionist.dmi deleted file mode 100644 index b56da3bc706557a762e6dc2a9ce92292db970f64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1391 zcmV-#1(5oQP)l}Nj5e%4Gj%CI5-_09fN^^Qc_YR zBO@Ff99&#nz`(%&05ktHGu|Q)ZvX%Q0d!JMQvg8b*k%9#0Caj(Sad{Xb7OL8aCB*J zZU6vyoKseCa&`CgQ*iP1C^0t`#5P1zrJItPh@{jIMX9oatDg(l1pw~y zCb#l?ci#X21MEpeK~z|U-Ir@uqdF9Z6NxDSL!3fFo2o5CXXYHOf}M^o&;S2nXD85l zAz_9p^TEAPT)gl;*)f+Lgg#Z^CcOGd6J9;A(c|B|5cmiLqLuz>j7lL7jISdt_R?(S$h2y*Zl1cA-KlK?`~G!;UmeCpA8YJ+p10peNK z=P&Wp1eR_BmsmEJkDzdtWrF(Xxs(+Asg$|P!RQ#zFd*`|+h4&lkY(CUn9pe{sl@ZS zmpQ(wUAd`R9fHZ^9e| zRSL117Fu^QxCVd55WgYBTpY3Pbbr7Fxuf~`(W8Ej_+5qnA@(Bzpf(l-4a0Z{OdkC>7whXYs#0Q=lMe!_S{ zm|*JZKdCTieDo{{sNM x=Zp8pPjg1tj_03MyOXpY5%6fs`;YCS{{e43X;9b=+i3s*002ovPDHLkV1hK7ac}?t diff --git a/maps/away/slavers/icons/areas.dmi b/maps/away/slavers/icons/areas.dmi deleted file mode 100644 index cc911c14cd2cad34a0f950ba2da383087477268c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1878 zcmah~X;hQf7QJ5>0?1IN7BD=ZAmEY$B0fHWmw+;bA=;=EWp0p3hA;()KoDt^L1Y0X ztxX^bJVdFeg))f-Xc?pgfdUn%2?PmafFffU-e;|=>-~A}$G&IXeeOL!&e?b0tTO~> z1vxD_0DuDC#es;#bOZr1(#SeQrQ(rLPCe%p?{F!O6dOj44~vchAc=Xi&ql7@Tt0o` z74|Ea^?{MO3IjEzZf%F^QE6OuWH?oOS+v8Amxbn7lLmB8jxVb}ha zFhw;VE)o7?xGuFy;o#K_y}PF)^V>Fw_O1AA=T;Z-&Ur(I*M;+8^Vkc+Kl^M>Iq5Cq zuj|F9tl}+p3e}F~lr)lZXyad-6#GYHCbI%A*6sjC(3RMmP76l>kdDMV*q=*cF0wu^ zD1+tcy`H=-W?-r+tU8|guTv?8>w#_#!!8=6YayQ7_kC0!c(~LB&DIyVD~YG}DcYKh zlEg$)Zt24fo?vuskCxEV+|San)f{PP74%KD&({QRzUD11@pv`uG5hdh4Ud+)y0Nju z_(DXJ}N=8Us@Knzv!iAH;lmgZ3$u#VH9R(qt_fsVg6*9Yp__ z(2=sE(g8=CmnYF?|lsZyFE=Dzo}zCV6*5~F--K*ZaaJU1x4r%lj|$<$gP ziW<;Gl2EFj19vvMB3cx>VTqSzSLyUUz9-o0^`*%kt|%R%V2nfNPrs02l=MZ^y1S%8 zSyRc~N0+l#1BLZ6YMv6InQ=ZWCrf6gz;pxNo~^EF6*nqvX+)4h>bY_ z-gp7yPL$n=kHL$_;7p!WdaMa3`a2~InGySCuBOdV_b+AnF5wa>>6gG8109(ak$C0) zjP=ewI7W@WlDtIvoMWBaxm&rry1lEvy(?y3P@q{}9_preqj=7`QXgr7qXgeh`c&pnF3PAb# zk^49!LmIRcKN}O3M)9Q|dExh@&RC}aOf7^UsZS4QN%JMyK{x^TxW_$P?EpRJ#RB-L zzSbcFNQM&8ephMC)zM%K%}#ilWL0WA5?P)G5~z+JkrT8->ju4B!_sYhwxa^7R9CoW zO+OCd*%mUc3(=tYTkB@W%H_vq*9TPFD{{C^G!@pZThYrwZr^+c5u42V!Ev)WmHfTg zqsxJ5l@>2E^d(%vyq}ysSi&=fFq=lkFe=$ zkbLj|7SDg{ik|A<^i2CHCCw(DA}W1o#y(aI6&0o!k})rN>{m7^I9}sYJ()SbuIKK7 zfy@z|Ll=lkUq6LIz86UsLd;TESSmyLGkCp0^8zNm6hY3*3WIi-=8 zeE!0yvKwIR@woC0|8^skb#lhzZY#QM`R{He#YLSMMk8ImqxpZ|=-+MTK5yH)RsGv1 SqAld31b9b+!^4vS8UF!Hwr>;w diff --git a/maps/away/slavers/icons/uniform.dmi b/maps/away/slavers/icons/uniform.dmi deleted file mode 100644 index 841007db55fbe81af2043694a4cf714a65d8003c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 781 zcmV+o1M>WdP)fFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33t zGfE(w;*!LYR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@*-GsxnG*6Z7&jQxuZ&Qz~_f zbMi~#%i~M)GSl*lauJ$MP&6wmxca$(-2?zv%`O`gxq+_$00GrWL_t(oh3%JdZ^9rD z#cA&jT4&1?gZlly=nk4WlX9GE{bR{^sZI19_j^z+MLa(|@0 z>$;v$Aau)EF+i95ivhtRx>SH@K?;ykN&x@~m#a8@7+jAIX&4Y(iJ%2YKG8^wutUio z20%x2U;w&x2Q2_t_G!eCC*VHT1aOA~qz3!}C}h+E0FPt9m;lFP!N(qQ14;l`Eu$8I z7YJbD?levS$92G&x7VYAS#LHqK=LPk2!vC~H}gCj|E>J(cLCBbFfjqADf^$RMKVu- zwX^^U@Z*<{+X>Dgqzg_3R|)Wnf8vdR=@I~105za_6l@Jp_w!a>3bqAMeFGQAJFNIW z@mc`I=fiRVQv+xLY~AMu!~nX;4U_;3sQFxw0hJkO0qWxo&kvT{#sC^XlNvzro0^)M zn%<=7Sha%W#~uA6IpPiigAKMRZYM-%ZS-Es7@hag+8tA1Kt%8DwQOIl_ZZ&+kY?~6 z0Fn&g_pg0`5JId2a>-^uUi*L~0SLpRY&+l_ZW7=Y&ieqlvHS+{?d*95yh3$As@Qdu z
    From 33ebefc92bee2f578e7776619ce7b81fc22afa92 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 11 Dec 2024 11:16:40 +1100 Subject: [PATCH 0056/1331] Update code/datums/config/config_types/config_mode.dm Co-authored-by: Penelope Haze --- code/datums/config/config_types/config_mode.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/config/config_types/config_mode.dm b/code/datums/config/config_types/config_mode.dm index 975b84ebca92..6f91654a6887 100644 --- a/code/datums/config/config_types/config_mode.dm +++ b/code/datums/config/config_types/config_mode.dm @@ -88,7 +88,7 @@ /decl/config/toggle/continuous_rounds uid = "continuous_rounds" desc = list( - "Remove the # to make rounds which end instantly to continue until the shuttle is called or the station is nuked.", + "Remove the # to make rounds which end instantly continue until the shuttle is called or the station is nuked.", "Malf and Rev will let the shuttle be called when the antags/protags are dead." ) From c2e44edacc05c01c82fd219fe079203a57a97570 Mon Sep 17 00:00:00 2001 From: Greenjoe12345 Date: Wed, 11 Dec 2024 00:51:01 +0000 Subject: [PATCH 0057/1331] crutch --- mods/content/fantasy/items/clothing/_loadout.dm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mods/content/fantasy/items/clothing/_loadout.dm b/mods/content/fantasy/items/clothing/_loadout.dm index 9d7f449a9f5c..8f6857ced514 100644 --- a/mods/content/fantasy/items/clothing/_loadout.dm +++ b/mods/content/fantasy/items/clothing/_loadout.dm @@ -288,6 +288,18 @@ "white wine" = /decl/material/liquid/ethanol/wine/premium, )) +/decl/loadout_option/fantasy/utility/crutch + name = "crutch" + path = /obj/item/crutch/wooden/padded + available_materials = list( + /decl/material/solid/organic/wood/oak, + /decl/material/solid/organic/wood/mahogany, + /decl/material/solid/organic/wood/maple, + /decl/material/solid/organic/wood/ebony, + /decl/material/solid/organic/wood/walnut + ) + uid = "gear_fantasy_crutch" + /decl/loadout_option/fantasy/eyes abstract_type = /decl/loadout_option/fantasy/eyes slot = slot_glasses_str From 02495f8993ee0d71b61b2aa8018ae7f8c1170b12 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 2 Dec 2024 18:33:30 +1100 Subject: [PATCH 0058/1331] Reworking necklaces and rings to use Doe's new icons. --- code/_global_vars/lists/jewellery.dm | 23 ++++ code/_onclick/item_attack.dm | 2 +- code/game/objects/auras/blueforge_aura.dm | 16 --- code/game/objects/auras/radiant_aura.dm | 2 +- code/game/objects/auras/shadowling_aura.dm | 21 ---- code/game/objects/auras/starlight.dm | 20 --- code/game/objects/items/__item.dm | 13 ++ code/game/objects/items/stacks/stack.dm | 1 - .../objects/items/weapons/storage/backpack.dm | 5 +- code/game/objects/random/subtypes/misc.dm | 32 ++--- .../loadout/lists/accessories.dm | 26 ++++ .../preference_setup/loadout/lists/gloves.dm | 41 ++++-- .../clothing/gloves/jewelry/rings/_ring.dm | 81 ++++++++++-- .../clothing/gloves/jewelry/rings/material.dm | 24 ---- .../gloves/jewelry/rings/ring_aura.dm | 29 +++++ .../gloves/jewelry/rings/ring_misc.dm | 24 ++++ .../gloves/jewelry/rings/ring_reagent.dm | 37 ++++++ .../gloves/jewelry/rings/ring_seal.dm | 42 +++++++ .../clothing/gloves/jewelry/rings/rings.dm | 118 ------------------ code/modules/clothing/neck/jewelry.dm | 74 ----------- .../clothing/neck/necklace/__necklace.dm | 97 ++++++++++++++ .../clothing/neck/necklace/_pendant.dm | 51 ++++++++ .../clothing/neck/necklace/necklaces.dm | 15 +++ .../clothing/neck/necklace/pendant_locket.dm | 50 ++++++++ .../clothing/neck/necklace/pendant_random.dm | 27 ++++ .../clothing/neck/necklace/pendant_setting.dm | 36 ++++++ code/modules/clothing/neck/prayer_beads.dm | 29 +++++ .../stack_recipes/recipes_hardness.dm | 14 ++- code/modules/decoration/_decoration.dm | 104 +++++++++++++++ code/modules/decoration/decoration_item.dm | 103 +++++++++++++++ code/modules/decoration/decoration_setting.dm | 30 +++++ code/modules/gemstones/_gemstone.dm | 38 ++++++ code/modules/gemstones/gemstone_cuts.dm | 5 +- .../species/species_bodytype_offsets.dm | 15 ++- icons/clothing/accessories/jewelry/locket.dmi | Bin 837 -> 0 bytes .../clothing/accessories/jewelry/necklace.dmi | Bin 752 -> 489 bytes .../accessories/jewelry/pendants/cross.dmi | Bin 0 -> 427 bytes .../accessories/jewelry/pendants/crystal.dmi | Bin 0 -> 369 bytes .../accessories/jewelry/pendants/diamond.dmi | Bin 0 -> 439 bytes .../accessories/jewelry/pendants/frill.dmi | Bin 0 -> 383 bytes .../accessories/jewelry/pendants/locket.dmi | Bin 0 -> 462 bytes .../accessories/jewelry/pendants/ornate.dmi | Bin 0 -> 585 bytes .../accessories/jewelry/pendants/square.dmi | Bin 0 -> 435 bytes .../accessories/jewelry/rings/ring.dmi | Bin 383 -> 0 bytes .../accessories/jewelry/rings/ring_band.dmi | Bin 0 -> 454 bytes .../jewelry/rings/ring_band_split.dmi | Bin 0 -> 448 bytes .../jewelry/rings/ring_band_thick.dmi | Bin 0 -> 467 bytes .../jewelry/rings/ring_band_thin.dmi | Bin 0 -> 442 bytes .../accessories/jewelry/rings/ring_blue.dmi | Bin 1340 -> 0 bytes .../accessories/jewelry/rings/ring_cti.dmi | Bin 417 -> 0 bytes .../jewelry/rings/ring_diamond.dmi | Bin 376 -> 0 bytes .../accessories/jewelry/rings/ring_magic.dmi | Bin 388 -> 0 bytes .../jewelry/rings/ring_mariner.dmi | Bin 396 -> 0 bytes .../accessories/jewelry/rings/ring_seal.dmi | Bin 0 -> 531 bytes .../jewelry/rings/ring_seal_masonic.dmi | Bin 446 -> 547 bytes .../jewelry/rings/ring_seal_secgen.dmi | Bin 437 -> 0 bytes .../jewelry/rings/ring_seal_signet.dmi | Bin 433 -> 524 bytes .../accessories/jewelry/rings/ring_shadow.dmi | Bin 420 -> 0 bytes .../accessories/jewelry/rings/ring_star.dmi | Bin 421 -> 0 bytes icons/obj/items/gemstones/baguette.dmi | Bin 423 -> 427 bytes icons/obj/items/gemstones/hexagon.dmi | Bin 419 -> 423 bytes icons/obj/items/gemstones/octagon.dmi | Bin 445 -> 449 bytes icons/obj/items/gemstones/poor.dmi | Bin 382 -> 386 bytes icons/obj/items/gemstones/round.dmi | Bin 401 -> 405 bytes icons/obj/items/gemstones/uncut.dmi | Bin 538 -> 542 bytes maps/shaded_hills/outfits/shrine.dm | 2 +- .../content/dungeon_loot/subtypes/bookcase.dm | 2 +- .../fantasy/items/clothing/_loadout.dm | 2 +- nebula.dme | 21 +++- 69 files changed, 941 insertions(+), 331 deletions(-) create mode 100644 code/_global_vars/lists/jewellery.dm delete mode 100644 code/game/objects/auras/blueforge_aura.dm delete mode 100644 code/game/objects/auras/shadowling_aura.dm delete mode 100644 code/game/objects/auras/starlight.dm delete mode 100644 code/modules/clothing/gloves/jewelry/rings/material.dm create mode 100644 code/modules/clothing/gloves/jewelry/rings/ring_aura.dm create mode 100644 code/modules/clothing/gloves/jewelry/rings/ring_misc.dm create mode 100644 code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm create mode 100644 code/modules/clothing/gloves/jewelry/rings/ring_seal.dm delete mode 100644 code/modules/clothing/gloves/jewelry/rings/rings.dm delete mode 100644 code/modules/clothing/neck/jewelry.dm create mode 100644 code/modules/clothing/neck/necklace/__necklace.dm create mode 100644 code/modules/clothing/neck/necklace/_pendant.dm create mode 100644 code/modules/clothing/neck/necklace/necklaces.dm create mode 100644 code/modules/clothing/neck/necklace/pendant_locket.dm create mode 100644 code/modules/clothing/neck/necklace/pendant_random.dm create mode 100644 code/modules/clothing/neck/necklace/pendant_setting.dm create mode 100644 code/modules/clothing/neck/prayer_beads.dm create mode 100644 code/modules/decoration/_decoration.dm create mode 100644 code/modules/decoration/decoration_item.dm create mode 100644 code/modules/decoration/decoration_setting.dm delete mode 100644 icons/clothing/accessories/jewelry/locket.dmi create mode 100644 icons/clothing/accessories/jewelry/pendants/cross.dmi create mode 100644 icons/clothing/accessories/jewelry/pendants/crystal.dmi create mode 100644 icons/clothing/accessories/jewelry/pendants/diamond.dmi create mode 100644 icons/clothing/accessories/jewelry/pendants/frill.dmi create mode 100644 icons/clothing/accessories/jewelry/pendants/locket.dmi create mode 100644 icons/clothing/accessories/jewelry/pendants/ornate.dmi create mode 100644 icons/clothing/accessories/jewelry/pendants/square.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring.dmi create mode 100644 icons/clothing/accessories/jewelry/rings/ring_band.dmi create mode 100644 icons/clothing/accessories/jewelry/rings/ring_band_split.dmi create mode 100644 icons/clothing/accessories/jewelry/rings/ring_band_thick.dmi create mode 100644 icons/clothing/accessories/jewelry/rings/ring_band_thin.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_blue.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_cti.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_diamond.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_magic.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_mariner.dmi create mode 100644 icons/clothing/accessories/jewelry/rings/ring_seal.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_seal_secgen.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_shadow.dmi delete mode 100644 icons/clothing/accessories/jewelry/rings/ring_star.dmi diff --git a/code/_global_vars/lists/jewellery.dm b/code/_global_vars/lists/jewellery.dm new file mode 100644 index 000000000000..7364bff8b607 --- /dev/null +++ b/code/_global_vars/lists/jewellery.dm @@ -0,0 +1,23 @@ +var/global/list/random_jewellery_material_types = list( + /decl/material/solid/metal/gold, + /decl/material/solid/metal/silver, + /decl/material/solid/metal/copper, + /decl/material/solid/metal/platinum, + /decl/material/solid/metal/steel, + /decl/material/solid/organic/bone, + /decl/material/solid/organic/wood +) +var/global/list/random_jewellery_gem_types = list( + /obj/item/gemstone/baguette/topaz, + /obj/item/gemstone/baguette/sapphire, + /obj/item/gemstone/baguette/ruby, + /obj/item/gemstone/hexagon/topaz, + /obj/item/gemstone/hexagon/sapphire, + /obj/item/gemstone/hexagon/ruby, + /obj/item/gemstone/octagon/topaz, + /obj/item/gemstone/octagon/sapphire, + /obj/item/gemstone/octagon/ruby, + /obj/item/gemstone/round/topaz, + /obj/item/gemstone/round/sapphire, + /obj/item/gemstone/round/ruby +) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index a3e8714eaffe..1dbed7ab122e 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -48,7 +48,7 @@ avoid code duplication. This includes items that may sometimes act as a standard // Return TRUE if further actions (afterattack, etc) should be prevented, FALSE if they can proceed. /atom/movable/proc/bash(obj/item/weapon, mob/user) - if(isliving(user) && user.check_intent(I_FLAG_HELP)) + if(isliving(user) && !user.check_intent(I_FLAG_HARM)) return FALSE if(!weapon.user_can_attack_with(user)) return FALSE diff --git a/code/game/objects/auras/blueforge_aura.dm b/code/game/objects/auras/blueforge_aura.dm deleted file mode 100644 index ae3925d0e206..000000000000 --- a/code/game/objects/auras/blueforge_aura.dm +++ /dev/null @@ -1,16 +0,0 @@ -/obj/aura/blueforge_aura - name = "Blueforge Aura" - icon = 'icons/mob/human_races/species/blueforged/eyes.dmi' - icon_state = "eyes" - layer = MOB_LAYER - -/obj/aura/blueforge_aura/life_tick() - user.heal_damage(TOX, 10) - return 0 - -/obj/aura/blueforge_aura/bullet_act(var/obj/item/projectile/P) - if(P.atom_damage_type == BURN) - P.damage *=2 - else if(P.agony || P.stun) - return AURA_FALSE - return 0 \ No newline at end of file diff --git a/code/game/objects/auras/radiant_aura.dm b/code/game/objects/auras/radiant_aura.dm index 0968d0570607..d25cfa807b95 100644 --- a/code/game/objects/auras/radiant_aura.dm +++ b/code/game/objects/auras/radiant_aura.dm @@ -17,4 +17,4 @@ if(P.damage_flags() & DAM_LASER) user.visible_message("\The [P] refracts, bending into \the [user]'s aura.") return AURA_FALSE - return 0 \ No newline at end of file + return 0 diff --git a/code/game/objects/auras/shadowling_aura.dm b/code/game/objects/auras/shadowling_aura.dm deleted file mode 100644 index ff081f7dfb10..000000000000 --- a/code/game/objects/auras/shadowling_aura.dm +++ /dev/null @@ -1,21 +0,0 @@ -/obj/aura/shadowling_aura - name = "Shadowling Aura" - var/added_mutation = FALSE - -/obj/aura/shadowling_aura/added_to(var/mob/living/L) - ..() - if(L.add_genetic_condition(GENE_COND_SPACE_RESISTANCE)) - added_mutation = TRUE - -/obj/aura/shadowling_aura/removed() - if(added_mutation) - added_mutation = FALSE - user.remove_genetic_condition(GENE_COND_SPACE_RESISTANCE) - ..() - -/obj/aura/shadowling_aura/bullet_act(var/obj/item/projectile/P) - if(P.damage_flags() & DAM_LASER) - P.damage *= 2 - if(P.agony) - P.agony *= 2 - return 0 \ No newline at end of file diff --git a/code/game/objects/auras/starlight.dm b/code/game/objects/auras/starlight.dm deleted file mode 100644 index 5e6253cbd939..000000000000 --- a/code/game/objects/auras/starlight.dm +++ /dev/null @@ -1,20 +0,0 @@ -/obj/aura/starborn - name = "starborn's gift" - icon = 'icons/effects/effects.dmi' - icon_state = "white_electricity_constant" - color = "#33cc33" - layer = MOB_LAYER - -/obj/aura/starborn/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(P.atom_damage_type == BURN) - user.visible_message("\The [P] seems to only make \the [user] stronger.") - user.heal_damage(BRUTE, P.damage) - return AURA_FALSE - return 0 - -/obj/aura/starborn/attackby(var/obj/item/I, var/mob/i_user) - if(I.atom_damage_type == BURN) - to_chat(i_user, "\The [I] seems to only feed into \the [user]'s flames.") - user.heal_damage(BRUTE, I.get_attack_force(user)) - return AURA_FALSE - return 0 \ No newline at end of file diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 69dd9c22283e..439b62e14b70 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -173,6 +173,16 @@ set_material(material_key) paint_verb ||= "painted" // fallback for the case of no material + // This is a bit gross, but it makes writing rings and necklaces much easier. + // If the decorations list is already populated at this point, we assume it's + // prebaked decorations. Only things handled appropriately at the moment are gems. + if(length(decorations)) + for(var/decoration_type in decorations) + decorations -= decoration_type + if(ispath(decoration_type, /obj/item/gemstone)) + decorations[GET_DECL(/decl/item_decoration/setting)] = list("object" = new decoration_type(src)) + else + PRINT_STACK_TRACE("Item [type] tried to initialize with an unsupported initial decoration type ('[decoration_type]')") . = ..() setup_sprite_sheets() @@ -200,6 +210,9 @@ /obj/item/Destroy() + // May contain object references. + LAZYCLEARLIST(decorations) + if(LAZYLEN(_item_effects)) _item_effects = null SSitem_effects.queued_items -= src diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 2a38b4f01c33..2010f99039de 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -53,7 +53,6 @@ update_name() /obj/item/stack/update_name() - . = ..() if(amount == 1) gender = NEUTER SetName(singular_name) diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 1bc1144dc870..14a20b4125c1 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -456,9 +456,6 @@ /obj/item/backpack/crafted/backpack name = "backpack" + name_prefix = "handmade" desc = "A rather rough handmade backpack." icon = 'icons/obj/items/storage/backpack/backpack_crafted.dmi' - -/obj/item/backpack/crafted/backpack/update_name() - . = ..() - SetName("handmade [name]") diff --git a/code/game/objects/random/subtypes/misc.dm b/code/game/objects/random/subtypes/misc.dm index a4356724b358..e0003d62ef89 100644 --- a/code/game/objects/random/subtypes/misc.dm +++ b/code/game/objects/random/subtypes/misc.dm @@ -515,22 +515,22 @@ /obj/random/jewelry/spawn_choices() var/static/list/spawnable_choices = list( - /obj/item/clothing/ears/stud/wood = 10, - /obj/item/clothing/ears/dangle/wood = 10, - /obj/item/clothing/gloves/bracelet = 10, - /obj/item/clothing/neck/necklace = 10, - /obj/item/clothing/gloves/ring/material/silver = 5, - /obj/item/clothing/gloves/ring/material/bronze = 5, - /obj/item/clothing/gloves/ring/material/gold = 3, - /obj/item/clothing/ears/stud/silver = 3, - /obj/item/clothing/ears/dangle/silver = 3, - /obj/item/clothing/ears/stud/gold = 3, - /obj/item/clothing/ears/dangle/gold = 3, - /obj/item/clothing/gloves/ring/material/platinum = 1, - /obj/item/clothing/ears/stud/platinum = 1, - /obj/item/clothing/ears/dangle/platinum = 1, - /obj/item/clothing/ears/stud/diamond = 1, - /obj/item/clothing/ears/dangle/diamond = 1 + /obj/item/clothing/ears/stud/wood = 10, + /obj/item/clothing/ears/dangle/wood = 10, + /obj/item/clothing/gloves/bracelet = 10, + /obj/item/clothing/neck/necklace = 10, + /obj/item/clothing/gloves/ring/silver = 5, + /obj/item/clothing/gloves/ring/bronze = 5, + /obj/item/clothing/gloves/ring/gold = 3, + /obj/item/clothing/ears/stud/silver = 3, + /obj/item/clothing/ears/dangle/silver = 3, + /obj/item/clothing/ears/stud/gold = 3, + /obj/item/clothing/ears/dangle/gold = 3, + /obj/item/clothing/gloves/ring/platinum = 1, + /obj/item/clothing/ears/stud/platinum = 1, + /obj/item/clothing/ears/dangle/platinum = 1, + /obj/item/clothing/ears/stud/diamond = 1, + /obj/item/clothing/ears/dangle/diamond = 1 ) return spawnable_choices diff --git a/code/modules/client/preference_setup/loadout/lists/accessories.dm b/code/modules/client/preference_setup/loadout/lists/accessories.dm index 7f43717605be..739dc589b36c 100644 --- a/code/modules/client/preference_setup/loadout/lists/accessories.dm +++ b/code/modules/client/preference_setup/loadout/lists/accessories.dm @@ -51,6 +51,32 @@ path = /obj/item/clothing/neck/necklace loadout_flags = GEAR_HAS_COLOR_SELECTION uid = "gear_accessory_necklace" + var/list/available_materials = list( + /decl/material/solid/metal/steel, + /decl/material/solid/metal/bronze, + /decl/material/solid/metal/silver, + /decl/material/solid/metal/gold, + /decl/material/solid/metal/platinum + ) + +/decl/loadout_option/accessory/necklace/get_gear_tweak_options() + . = ..() + LAZYINITLIST(.[/datum/gear_tweak/path]) + .[/datum/gear_tweak/path] |= list( + "necklace" = /obj/item/clothing/neck/necklace, + "necklace, square pendant" = /obj/item/clothing/neck/necklace/square, + "necklace, frill" = /obj/item/clothing/neck/necklace/frill, + "necklace, cross pendant" = /obj/item/clothing/neck/necklace/cross, + "necklace, diamond pendant" = /obj/item/clothing/neck/necklace/diamond, + "necklace, ornate" = /obj/item/clothing/neck/necklace/ornate + ) + if(length(available_materials)) + for(var/mat in available_materials) + var/decl/material/mat_decl = GET_DECL(mat) + available_materials -= mat + available_materials[mat_decl.name] = mat + LAZYINITLIST(.[/datum/gear_tweak/material]) + .[/datum/gear_tweak/material] = available_materials /decl/loadout_option/accessory/bow name = "bowtie, horrible" diff --git a/code/modules/client/preference_setup/loadout/lists/gloves.dm b/code/modules/client/preference_setup/loadout/lists/gloves.dm index 94387e8dee26..c923ba47b6bb 100644 --- a/code/modules/client/preference_setup/loadout/lists/gloves.dm +++ b/code/modules/client/preference_setup/loadout/lists/gloves.dm @@ -24,20 +24,39 @@ path = /obj/item/clothing/gloves/ring cost = 2 uid = "gear_gloves_ring" + var/list/available_materials = list( + /decl/material/solid/metal/steel, + /decl/material/solid/metal/bronze, + /decl/material/solid/metal/silver, + /decl/material/solid/metal/gold, + /decl/material/solid/metal/platinum, + /decl/material/solid/glass, + /decl/material/solid/organic/plastic, + /decl/material/solid/organic/wood/oak, + /decl/material/solid/organic/wood/bamboo, + /decl/material/solid/organic/wood/ebony, + /decl/material/solid/organic/wood/mahogany, + /decl/material/solid/organic/wood/maple, + /decl/material/solid/organic/wood/walnut, + /decl/material/solid/organic/wood/yew + ) /decl/loadout_option/ring/get_gear_tweak_options() . = ..() LAZYINITLIST(.[/datum/gear_tweak/path]) .[/datum/gear_tweak/path] |= list( - "engagement ring" = /obj/item/clothing/gloves/ring/engagement, - "signet ring" = /obj/item/clothing/gloves/ring/seal/signet, - "masonic ring" = /obj/item/clothing/gloves/ring/seal/mason, - "ring, steel" = /obj/item/clothing/gloves/ring/material/steel, - "ring, bronze" = /obj/item/clothing/gloves/ring/material/bronze, - "ring, silver" = /obj/item/clothing/gloves/ring/material/silver, - "ring, gold" = /obj/item/clothing/gloves/ring/material/gold, - "ring, platinum" = /obj/item/clothing/gloves/ring/material/platinum, - "ring, glass" = /obj/item/clothing/gloves/ring/material/glass, - "ring, wood" = /obj/item/clothing/gloves/ring/material/wood, - "ring, plastic" = /obj/item/clothing/gloves/ring/material/plastic + "ring" = /obj/item/clothing/gloves/ring/gold, + "ring, thin" = /obj/item/clothing/gloves/ring/thin, + "ring, thick" = /obj/item/clothing/gloves/ring/thick, + "ring, split" = /obj/item/clothing/gloves/ring/split, + "engagement ring" = /obj/item/clothing/gloves/ring/engagement, + "signet ring" = /obj/item/clothing/gloves/ring/seal/signet, + "masonic ring" = /obj/item/clothing/gloves/ring/seal/mason ) + if(length(available_materials)) + for(var/mat in available_materials) + var/decl/material/mat_decl = GET_DECL(mat) + available_materials -= mat + available_materials[mat_decl.name] = mat + LAZYINITLIST(.[/datum/gear_tweak/material]) + .[/datum/gear_tweak/material] = available_materials diff --git a/code/modules/clothing/gloves/jewelry/rings/_ring.dm b/code/modules/clothing/gloves/jewelry/rings/_ring.dm index 7b39b2596043..b9e58beedf62 100644 --- a/code/modules/clothing/gloves/jewelry/rings/_ring.dm +++ b/code/modules/clothing/gloves/jewelry/rings/_ring.dm @@ -1,11 +1,15 @@ /obj/item/clothing/gloves/ring - name = "ring" - icon = 'icons/clothing/accessories/jewelry/rings/ring.dmi' - w_class = ITEM_SIZE_TINY - gender = NEUTER - material = /decl/material/solid/metal/silver + name = "ring" + icon = 'icons/clothing/accessories/jewelry/rings/ring_band.dmi' + desc = null + w_class = ITEM_SIZE_TINY + gender = NEUTER + material = /decl/material/solid/metal/silver + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_DESC // We handle name manually + sprite_sheets = null + var/use_material_name = TRUE var/can_fit_under_gloves = TRUE - var/can_inscribe = FALSE + var/can_inscribe = TRUE var/inscription var/base_desc @@ -14,6 +18,24 @@ base_desc = desc . = ..() +/obj/item/clothing/gloves/ring/get_decoration_icon(default_icon, obj/item/thing, on_mob = FALSE) + if(!on_mob && istype(thing, /obj/item/gemstone)) + return thing.icon + return ..() + +/obj/item/clothing/gloves/ring/update_name() + var/list/name_comp = list() + if(name_prefix) + name_comp += name_prefix + if(use_material_name) + var/obj/item/gemstone/gem = locate() in contents + if(gem) + name_comp += gem.name + if(material) + name_comp += material.adjective_name + name_comp += base_name || initial(name) + SetName(jointext(name_comp, " ")) + /obj/item/clothing/gloves/ring/set_material(var/new_material) . = ..() update_desc() @@ -32,7 +54,7 @@ desc = "[base_desc] [desc]" /obj/item/clothing/gloves/ring/attackby(var/obj/item/tool, var/mob/user) - if(can_inscribe && tool.sharp) + if(can_inscribe && tool.sharp && user.check_intent(I_FLAG_HELP)) var/new_inscription = sanitize(input("Enter an inscription to engrave.", "Inscription") as null|text) if(user.stat || !user.incapacitated() || !user.Adjacent(src) || tool.loc != user) return TRUE @@ -55,4 +77,47 @@ /obj/item/clothing/gloves/ring/get_examine_line() . = ..() - . += " \[View\]" \ No newline at end of file + . += " \[View\]" + +// Craftable subtypes. +/obj/item/clothing/gloves/ring/thin + name_prefix = "thin" + icon = 'icons/clothing/accessories/jewelry/rings/ring_band_thin.dmi' + +/obj/item/clothing/gloves/ring/thick + name_prefix = "thick" + icon = 'icons/clothing/accessories/jewelry/rings/ring_band_thick.dmi' + +/obj/item/clothing/gloves/ring/split + name_prefix = "split" + icon = 'icons/clothing/accessories/jewelry/rings/ring_band_split.dmi' + +// Material subtypes for mapping etc. +/obj/item/clothing/gloves/ring/wood + material = /decl/material/solid/organic/wood/walnut + +/obj/item/clothing/gloves/ring/plastic + material = /decl/material/solid/organic/plastic + +/obj/item/clothing/gloves/ring/steel + material = /decl/material/solid/metal/steel + +/obj/item/clothing/gloves/ring/silver + material = /decl/material/solid/metal/silver + +/obj/item/clothing/gloves/ring/gold + material = /decl/material/solid/metal/gold + +/obj/item/clothing/gloves/ring/platinum + material = /decl/material/solid/metal/platinum + +/obj/item/clothing/gloves/ring/bronze + material = /decl/material/solid/metal/bronze + +/obj/item/clothing/gloves/ring/glass + material = /decl/material/solid/glass + +/obj/item/clothing/gloves/ring/random/Initialize(ml, material_key) + material_key = pick(global.random_jewellery_material_types) + decorations = list(pick(global.random_jewellery_gem_types)) + . = ..() diff --git a/code/modules/clothing/gloves/jewelry/rings/material.dm b/code/modules/clothing/gloves/jewelry/rings/material.dm deleted file mode 100644 index 1f4492cffc23..000000000000 --- a/code/modules/clothing/gloves/jewelry/rings/material.dm +++ /dev/null @@ -1,24 +0,0 @@ -///////////////////////////////////////// -//Material Rings -//TODO: Repath to /obj/item/clothing/gloves/ring/crafted? -//Also, maybe move can_inscribe = TRUE down to the base ring? -/obj/item/clothing/gloves/ring/material - material_alteration = MAT_FLAG_ALTERATION_ALL - can_inscribe = TRUE - -/obj/item/clothing/gloves/ring/material/wood - material = /decl/material/solid/organic/wood/walnut -/obj/item/clothing/gloves/ring/material/plastic - material = /decl/material/solid/organic/plastic -/obj/item/clothing/gloves/ring/material/steel - material = /decl/material/solid/metal/steel -/obj/item/clothing/gloves/ring/material/silver - material = /decl/material/solid/metal/silver -/obj/item/clothing/gloves/ring/material/gold - material = /decl/material/solid/metal/gold -/obj/item/clothing/gloves/ring/material/platinum - material = /decl/material/solid/metal/platinum -/obj/item/clothing/gloves/ring/material/bronze - material = /decl/material/solid/metal/bronze -/obj/item/clothing/gloves/ring/material/glass - material = /decl/material/solid/glass diff --git a/code/modules/clothing/gloves/jewelry/rings/ring_aura.dm b/code/modules/clothing/gloves/jewelry/rings/ring_aura.dm new file mode 100644 index 000000000000..2a6c167d7feb --- /dev/null +++ b/code/modules/clothing/gloves/jewelry/rings/ring_aura.dm @@ -0,0 +1,29 @@ +/obj/item/clothing/gloves/ring/aura_ring + icon = 'icons/clothing/accessories/jewelry/rings/ring_band_thick.dmi' + can_inscribe = FALSE + material = /decl/material/solid/metal/silver + abstract_type = /obj/item/clothing/gloves/ring/aura_ring + material_alteration = MAT_FLAG_ALTERATION_COLOR + var/obj/aura/granted_aura + +/obj/item/clothing/gloves/ring/aura_ring/update_name() + return + +/obj/item/clothing/gloves/ring/aura_ring/Initialize() + if(ispath(granted_aura)) + granted_aura = new granted_aura + . = ..() + +/obj/item/clothing/gloves/ring/aura_ring/Destroy() + QDEL_NULL(granted_aura) + . = ..() + +/obj/item/clothing/gloves/ring/aura_ring/equipped(var/mob/living/L, var/slot) + ..() + if(istype(granted_aura) && slot == slot_gloves_str) + L.add_aura(granted_aura) + +/obj/item/clothing/gloves/ring/aura_ring/dropped(var/mob/living/L) + ..() + if(istype(granted_aura)) + L.remove_aura(granted_aura) diff --git a/code/modules/clothing/gloves/jewelry/rings/ring_misc.dm b/code/modules/clothing/gloves/jewelry/rings/ring_misc.dm new file mode 100644 index 000000000000..7660a833859d --- /dev/null +++ b/code/modules/clothing/gloves/jewelry/rings/ring_misc.dm @@ -0,0 +1,24 @@ +///////////////////////////////////////// +//Standard Rings +/obj/item/clothing/gloves/ring/engagement + name = "engagement ring" + desc = "An engagement ring. It certainly looks expensive." + material = /decl/material/solid/metal/silver + decorations = list(/obj/item/gemstone/round) + +/obj/item/clothing/gloves/ring/engagement/attack_self(mob/user) + user.visible_message(SPAN_WARNING("\The [user] gets down on one knee, presenting \the [src]."), SPAN_WARNING("You get down on one knee, presenting \the [src].")) + +/obj/item/clothing/gloves/ring/cti + name = "\improper CTI ring" + desc = "A ring commemorating graduation from CTI." + material = /decl/material/solid/metal/silver + decorations = list(/obj/item/gemstone/round/sapphire) + use_material_name = FALSE + +/obj/item/clothing/gloves/ring/mariner + name = "\improper Mariner University ring" + desc = "A ring commemorating graduation from Mariner University." + material = /decl/material/solid/metal/gold + decorations = list(/obj/item/gemstone/round/ruby) + use_material_name = FALSE diff --git a/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm b/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm new file mode 100644 index 000000000000..c19417797489 --- /dev/null +++ b/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm @@ -0,0 +1,37 @@ +///////////////////////////////////////// +//Reagent Rings +/obj/item/clothing/gloves/ring/reagent + atom_flags = ATOM_FLAG_OPEN_CONTAINER + origin_tech = @'{"materials":2,"esoteric":4}' + var/tmp/volume = 15 + +/obj/item/clothing/gloves/ring/reagent/Initialize(ml, material_key) + . = ..() + initialize_reagents() + +/obj/item/clothing/gloves/ring/reagent/initialize_reagents(populate = TRUE) + if(!reagents) + create_reagents(volume) + else + reagents.maximum_volume = max(volume, reagents.maximum_volume) + . = ..() + +/obj/item/clothing/gloves/ring/reagent/equipped(var/mob/living/human/H) + ..() + if(istype(H) && H.get_equipped_item(slot_gloves_str) == src) + to_chat(H, SPAN_DANGER("You feel a prick as you slip on the ring.")) + + if(reagents.total_volume) + if(H.reagents) + var/contained_reagents = reagents.get_reagents() + var/trans = reagents.trans_to_mob(H, reagents.total_volume, CHEM_INJECT) + admin_inject_log(usr, H, src, contained_reagents, trans) + return + +//Sleepy Ring +/obj/item/clothing/gloves/ring/reagent/sleepy + origin_tech = @'{"materials":2,"esoteric":5}' + +/obj/item/clothing/gloves/ring/reagent/sleepy/populate_reagents() + add_to_reagents(/decl/material/liquid/paralytics, 10) + add_to_reagents(/decl/material/liquid/sedatives, 5) diff --git a/code/modules/clothing/gloves/jewelry/rings/ring_seal.dm b/code/modules/clothing/gloves/jewelry/rings/ring_seal.dm new file mode 100644 index 000000000000..9ba92ac56595 --- /dev/null +++ b/code/modules/clothing/gloves/jewelry/rings/ring_seal.dm @@ -0,0 +1,42 @@ +///////////////////////////////////////// +//Seals and Signet Rings +/obj/item/clothing/gloves/ring/seal + name = "signet ring" + desc = "A ring with a heavy setting for pressing into hot wax to seal letters." + icon = 'icons/clothing/accessories/jewelry/rings/ring_seal.dmi' + material = /decl/material/solid/metal/silver + +/obj/item/clothing/gloves/ring/seal/Initialize() + . = ..() + set_extension(src, /datum/extension/tool, list(TOOL_STAMP = TOOL_QUALITY_DEFAULT)) + +/obj/item/clothing/gloves/ring/seal/secretary + name = "\improper Secretary-General's official seal" + desc = "The official seal of the Secretary-General of the Sol Central Government, featured prominently on a silver ring." + use_material_name = FALSE + +/obj/item/clothing/gloves/ring/seal/mason + name = "masonic ring" + desc = "The Square and Compasses feature prominently on this Masonic ring." + icon = 'icons/clothing/accessories/jewelry/rings/ring_seal_masonic.dmi' + material = /decl/material/solid/metal/brass + use_material_name = FALSE + +/obj/item/clothing/gloves/ring/seal/signet + name = "signet ring" + desc = "A signet ring, for when you're too sophisticated to sign letters." + icon = 'icons/clothing/accessories/jewelry/rings/ring_seal_signet.dmi' + use_material_name = FALSE + var/name_set = FALSE + +/obj/item/clothing/gloves/ring/seal/signet/attack_self(mob/user) + if(!user.check_intent(I_FLAG_HELP)) + return ..() + if(name_set) + to_chat(user, SPAN_NOTICE("\The [src] has already been claimed!")) + else + name_set = TRUE + to_chat(user, SPAN_NOTICE("You claim \the [src] as your own!")) + base_name = "\the [user]'s signet ring" + desc = "A signet ring belonging to [user], for when they're too sophisticated to sign letters." + return TRUE diff --git a/code/modules/clothing/gloves/jewelry/rings/rings.dm b/code/modules/clothing/gloves/jewelry/rings/rings.dm deleted file mode 100644 index b0d78e0d7faa..000000000000 --- a/code/modules/clothing/gloves/jewelry/rings/rings.dm +++ /dev/null @@ -1,118 +0,0 @@ -///////////////////////////////////////// -//Standard Rings -/obj/item/clothing/gloves/ring/engagement - name = "engagement ring" - desc = "An engagement ring. It certainly looks expensive." - icon = 'icons/clothing/accessories/jewelry/rings/ring_diamond.dmi' - material = /decl/material/solid/metal/silver - -/obj/item/clothing/gloves/ring/engagement/attack_self(mob/user) - user.visible_message(SPAN_WARNING("\The [user] gets down on one knee, presenting \the [src]."), SPAN_WARNING("You get down on one knee, presenting \the [src].")) - -/obj/item/clothing/gloves/ring/cti - name = "CTI ring" - desc = "A ring commemorating graduation from CTI." - icon = 'icons/clothing/accessories/jewelry/rings/ring_cti.dmi' - material = /decl/material/solid/metal/silver - -/obj/item/clothing/gloves/ring/mariner - name = "Mariner University ring" - desc = "A ring commemorating graduation from Mariner University." - material = /decl/material/solid/metal/gold - icon = 'icons/clothing/accessories/jewelry/rings/ring_mariner.dmi' - -///////////////////////////////////////// -//Magic Rings - -/obj/item/clothing/gloves/ring/magic - name = "magic ring" - desc = "A strange ring with symbols carved on it in some arcane language." - icon = 'icons/clothing/accessories/jewelry/rings/ring_magic.dmi' - material = /decl/material/solid/metal/gold - -/obj/item/clothing/gloves/ring/magic/equipped(var/mob/living/human/H, var/slot) - ..() - if(istype(H) && slot == SLOT_HANDS) - H.add_cloaking_source(src) - -/obj/item/clothing/gloves/ring/magic/dropped(var/mob/living/human/H) - if(!..()) - return 0 - - if(istype(H)) - H.remove_cloaking_source(src) - -///////////////////////////////////////// -//Reagent Rings - -/obj/item/clothing/gloves/ring/reagent - atom_flags = ATOM_FLAG_OPEN_CONTAINER - origin_tech = @'{"materials":2,"esoteric":4}' - var/tmp/volume = 15 - -/obj/item/clothing/gloves/ring/reagent/Initialize(ml, material_key) - . = ..() - initialize_reagents() - -/obj/item/clothing/gloves/ring/reagent/initialize_reagents(populate = TRUE) - if(!reagents) - create_reagents(volume) - else - reagents.maximum_volume = max(volume, reagents.maximum_volume) - . = ..() - -/obj/item/clothing/gloves/ring/reagent/equipped(var/mob/living/human/H) - ..() - if(istype(H) && H.get_equipped_item(slot_gloves_str) == src) - to_chat(H, SPAN_DANGER("You feel a prick as you slip on the ring.")) - - if(reagents.total_volume) - if(H.reagents) - var/contained_reagents = reagents.get_reagents() - var/trans = reagents.trans_to_mob(H, reagents.total_volume, CHEM_INJECT) - admin_inject_log(usr, H, src, contained_reagents, trans) - return - -//Sleepy Ring -/obj/item/clothing/gloves/ring/reagent/sleepy - name = "silver ring" - desc = "A ring made from what appears to be silver." - origin_tech = @'{"materials":2,"esoteric":5}' - -/obj/item/clothing/gloves/ring/reagent/sleepy/populate_reagents() - add_to_reagents(/decl/material/liquid/paralytics, 10) - add_to_reagents(/decl/material/liquid/sedatives, 5) - -///////////////////////////////////////// -//Seals and Signet Rings -/obj/item/clothing/gloves/ring/seal - name = "Secretary-General's official seal" - desc = "The official seal of the Secretary-General of the Sol Central Government, featured prominently on a silver ring." - icon = 'icons/clothing/accessories/jewelry/rings/ring_seal_secgen.dmi' - material = /decl/material/solid/metal/silver - -/obj/item/clothing/gloves/ring/seal/Initialize() - . = ..() - set_extension(src, /datum/extension/tool, list(TOOL_STAMP = TOOL_QUALITY_DEFAULT)) - -/obj/item/clothing/gloves/ring/seal/mason - name = "masonic ring" - desc = "The Square and Compasses feature prominently on this Masonic ring." - icon = 'icons/clothing/accessories/jewelry/rings/ring_seal_masonic.dmi' - material = /decl/material/solid/metal/brass - -/obj/item/clothing/gloves/ring/seal/signet - name = "signet ring" - desc = "A signet ring, for when you're too sophisticated to sign letters." - icon = 'icons/clothing/accessories/jewelry/rings/ring_seal_signet.dmi' - var/nameset = 0 - -/obj/item/clothing/gloves/ring/seal/signet/attack_self(mob/user) - if(nameset) - to_chat(user, SPAN_NOTICE("\The [src] has already been claimed!")) - return - - nameset = 1 - to_chat(user, SPAN_NOTICE("You claim \the [src] as your own!")) - name = "[user]'s signet ring" - desc = "A signet ring belonging to [user], for when you're too sophisticated to sign letters." diff --git a/code/modules/clothing/neck/jewelry.dm b/code/modules/clothing/neck/jewelry.dm deleted file mode 100644 index 8e2a3b326f2f..000000000000 --- a/code/modules/clothing/neck/jewelry.dm +++ /dev/null @@ -1,74 +0,0 @@ -/obj/item/clothing/neck/necklace - name = "necklace" - desc = "A simple necklace." - icon = 'icons/clothing/accessories/jewelry/necklace.dmi' - material = /decl/material/solid/metal/silver - material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC - -/obj/item/clothing/neck/necklace/prayer_beads - name = "prayer beads" - desc = "A string of smooth, polished beads." - icon = 'icons/clothing/accessories/jewelry/prayer_beads.dmi' - gender = PLURAL - material = /decl/material/solid/organic/wood/ebony - -/obj/item/clothing/neck/necklace/prayer_beads/bone - material = /decl/material/solid/organic/bone - -/obj/item/clothing/neck/necklace/prayer_beads/gold - material = /decl/material/solid/metal/gold - -/obj/item/clothing/neck/necklace/prayer_beads/basalt - material = /decl/material/solid/stone/basalt - -/obj/item/clothing/neck/necklace/prayer_beads/random - var/list/random_materials = list( - /decl/material/solid/organic/bone, - /decl/material/solid/stone/marble, - /decl/material/solid/stone/basalt, - /decl/material/solid/organic/wood/mahogany, - /decl/material/solid/organic/wood/ebony - ) - -/obj/item/clothing/neck/necklace/prayer_beads/random/Initialize() - material = pick(random_materials) - return ..() - -/obj/item/clothing/neck/necklace/locket - name = "locket" - desc = "A locket that seems to have space for a photo within." - icon = 'icons/clothing/accessories/jewelry/locket.dmi' - var/open - var/obj/item/held - -/obj/item/clothing/neck/necklace/locket/attack_self(mob/user) - if(!("[get_world_inventory_state()]-open" in icon_states(icon))) - to_chat(user, SPAN_WARNING("\The [src] doesn't seem to open.")) - return TRUE - open = !open - to_chat(user, SPAN_NOTICE("You flip \the [src] [open ? "open" : "closed"].")) - if(open && held) - to_chat(user, SPAN_DANGER("\The [held] falls out!")) - held.dropInto(user.loc) - held = null - update_icon() - return TRUE - -/obj/item/clothing/neck/necklace/locket/on_update_icon() - . = ..() - icon_state = get_world_inventory_state() - if(open && check_state_in_icon("[icon_state]-open", icon)) - icon_state = "[icon_state]-open" - -/obj/item/clothing/neck/necklace/locket/attackby(var/obj/item/O, mob/user) - if(!open) - to_chat(user, SPAN_WARNING("You have to open it first.")) - return TRUE - if(istype(O,/obj/item/paper) || istype(O, /obj/item/photo)) - if(held) - to_chat(usr, SPAN_WARNING("\The [src] already has something inside it.")) - else if(user.try_unequip(O, src)) - to_chat(usr, SPAN_NOTICE("You slip \the [O] into \the [src].")) - held = O - return TRUE - return ..() diff --git a/code/modules/clothing/neck/necklace/__necklace.dm b/code/modules/clothing/neck/necklace/__necklace.dm new file mode 100644 index 000000000000..a6e680313b6f --- /dev/null +++ b/code/modules/clothing/neck/necklace/__necklace.dm @@ -0,0 +1,97 @@ +/obj/item/clothing/neck/necklace + name = "necklace" + desc = "A fine chain worn around the neck." + icon = 'icons/clothing/accessories/jewelry/necklace.dmi' + material = /decl/material/solid/metal/silver + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC | MAT_FLAG_ALTERATION_COLOR + var/obj/item/pendant/pendant + +/obj/item/clothing/neck/necklace/Initialize() + . = ..() + if(ispath(pendant)) + set_pendant(new pendant(src, material?.type)) + +/obj/item/clothing/neck/necklace/Destroy() + set_pendant(null) + return ..() + +/obj/item/clothing/neck/necklace/proc/set_pendant(obj/item/new_pendant) + if(pendant == new_pendant) + return FALSE + pendant = new_pendant + update_icon() + return TRUE + +/obj/item/clothing/neck/necklace/examine(mob/user) + . = ..() + if(istype(pendant)) + to_chat(user, "There is \a [pendant] attached.") + +/obj/item/clothing/neck/necklace/on_update_icon() + . = ..() + if(istype(pendant)) + var/list/pendant_overlays = pendant.get_pendant_overlays(icon_state) + if(length(pendant_overlays)) + add_overlay(pendant_overlays) + +/obj/item/clothing/neck/necklace/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing) + if(overlay && istype(pendant)) + var/pendant_overlays = pendant.get_pendant_overlays(overlay.icon_state) + if(pendant_overlays) + overlay.overlays += pendant_overlays + return ..() + +/obj/item/clothing/neck/necklace/attack_self(mob/user) + if(pendant?.attack_self(user)) + return TRUE + return ..() + +/obj/item/clothing/neck/necklace/attackby(obj/item/used_item, mob/user) + if(istype(used_item, /obj/item/pendant)) + if(istype(pendant)) + to_chat(user, SPAN_WARNING("\The [src] doesn't have room for \the [used_item].")) + else if(user.try_unequip(used_item, src)) + set_pendant(used_item) + update_icon() + to_chat(user, SPAN_NOTICE("You attach \the [pendant] to \the [src].")) + return TRUE + if(pendant?.attackby(used_item, user)) + return TRUE + return ..() + +/obj/item/clothing/neck/necklace/get_alt_interactions(mob/user) + . = ..() + if(istype(pendant)) + LAZYADD(., /decl/interaction_handler/remove_pendant) + +/decl/interaction_handler/remove_pendant + name = "Remove Pendant" + expected_target_type = /obj/item/clothing/neck/necklace + +/decl/interaction_handler/remove_pendant/invoked(atom/target, mob/user, obj/item/prop) + var/obj/item/clothing/neck/necklace/necklace = target + if(!necklace.pendant) + return FALSE + necklace.pendant.dropInto(user.loc) + user.put_in_hands(necklace.pendant) + necklace.set_pendant(null) + return TRUE + +// Loadout subtypes. +/obj/item/clothing/neck/necklace/prism + pendant = /obj/item/pendant/prism + +/obj/item/clothing/neck/necklace/frill + pendant = /obj/item/pendant/frill + +/obj/item/clothing/neck/necklace/square + pendant = /obj/item/pendant/setting/square + +/obj/item/clothing/neck/necklace/cross + pendant = /obj/item/pendant/setting/cross + +/obj/item/clothing/neck/necklace/diamond + pendant = /obj/item/pendant/setting/diamond + +/obj/item/clothing/neck/necklace/ornate + pendant = /obj/item/pendant/setting/ornate diff --git a/code/modules/clothing/neck/necklace/_pendant.dm b/code/modules/clothing/neck/necklace/_pendant.dm new file mode 100644 index 000000000000..d29a9e6af27f --- /dev/null +++ b/code/modules/clothing/neck/necklace/_pendant.dm @@ -0,0 +1,51 @@ +/obj/item/pendant + name = "pendant" + desc = "A simple pendant." + icon = 'icons/clothing/accessories/jewelry/pendants/square.dmi' + abstract_type = /obj/item/pendant + material = /decl/material/solid/metal/silver + material_alteration = MAT_FLAG_ALTERATION_COLOR // We do manual name/desc handling for the gem. + +// I swear we had a proc for this on /obj/item? +/obj/item/pendant/set_material(new_material) + . = ..() + if(material) + desc = "[desc] This one is made of [material.solid_name]." + +/obj/item/pendant/update_name() + var/list/name_comp = get_name_components() + if(length(name_comp)) + SetName(jointext(name_comp, " ")) + +/obj/item/pendant/proc/get_name_components() + . = list() + if(name_prefix) + . += name_prefix + if(material) + . += material.adjective_name + . += base_name || initial(name) + +/obj/item/pendant/proc/get_pendant_base_state(base_state) + return base_state + +/obj/item/pendant/proc/get_pendant_overlays(base_state) + base_state = get_pendant_base_state(base_state) + var/list/overlay_states = list((base_state) = get_color()) + for(var/decl/item_decoration/decoration as anything in decorations) + overlay_states["[base_state]-[decoration.icon_state_modifier]"] = decoration.resolve_color(src) + . = list() + for(var/overlay_state in overlay_states) + if(check_state_in_icon(overlay_state, icon)) + . += overlay_image(icon, overlay_state, overlay_states[overlay_state], RESET_COLOR) + +// Subtypes below. +/obj/item/pendant/prism + name = "prism" + desc = "A large prism-shaped pendant that hangs from a clasp." + icon = 'icons/clothing/accessories/jewelry/pendants/crystal.dmi' + material = /decl/material/solid/gemstone/diamond + +/obj/item/pendant/frill + name = "chain frill" + icon = 'icons/clothing/accessories/jewelry/pendants/frill.dmi' + desc = "A set of fine chain frills." diff --git a/code/modules/clothing/neck/necklace/necklaces.dm b/code/modules/clothing/neck/necklace/necklaces.dm new file mode 100644 index 000000000000..1cc8ef598c26 --- /dev/null +++ b/code/modules/clothing/neck/necklace/necklaces.dm @@ -0,0 +1,15 @@ +// Subtypes for mapping etc. +/obj/item/clothing/neck/necklace/locket + pendant = /obj/item/pendant/locket + +/obj/item/clothing/neck/necklace/random/Initialize(ml, material_key) + material_key = pick(global.random_jewellery_material_types) + pendant = pick(list( + /obj/item/pendant/setting/cross/random, + /obj/item/pendant/setting/square/random, + /obj/item/pendant/setting/diamond/random, + /obj/item/pendant/setting/ornate/random, + /obj/item/pendant/prism/random, + /obj/item/pendant/frill/random + )) + return ..() diff --git a/code/modules/clothing/neck/necklace/pendant_locket.dm b/code/modules/clothing/neck/necklace/pendant_locket.dm new file mode 100644 index 000000000000..b0944c9c000d --- /dev/null +++ b/code/modules/clothing/neck/necklace/pendant_locket.dm @@ -0,0 +1,50 @@ +/obj/item/pendant/locket + name = "locket" + desc = "A locket that seems to have space for a photo within." + icon = 'icons/clothing/accessories/jewelry/pendants/locket.dmi' + var/open + var/obj/item/held + +/obj/item/pendant/locket/attack_self(mob/user) + if(!("[get_world_inventory_state()]-open" in icon_states(icon))) + to_chat(user, SPAN_WARNING("\The [src] doesn't seem to open.")) + return TRUE + open = !open + to_chat(user, SPAN_NOTICE("You flip \the [src] [open ? "open" : "closed"].")) + if(open && held) + to_chat(user, SPAN_DANGER("\The [held] falls out!")) + held.dropInto(user.loc) + held = null + update_icon() + return TRUE + +/obj/item/pendant/locket/on_update_icon() + . = ..() + icon_state = get_pendant_base_state(get_world_inventory_state()) + if(istype(loc, /obj/item)) + loc.update_icon() + +/obj/item/pendant/locket/get_pendant_base_state(base_state) + . = ..() + if(open) + . = "[.]-open" + +/obj/item/pendant/locket/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing) + if(overlay && open) + var/pendant_state = get_pendant_base_state(overlay.icon_state) + if(check_state_in_icon(pendant_state, overlay.icon)) + overlay.icon_state = pendant_state + return ..() + +/obj/item/pendant/locket/attackby(obj/item/used_item, mob/user) + if(!open) + to_chat(user, SPAN_WARNING("You have to open it first.")) + return TRUE + if(istype(used_item, /obj/item/paper) || istype(used_item, /obj/item/photo)) + if(held) + to_chat(usr, SPAN_WARNING("\The [src] already has something inside it.")) + else if(user.try_unequip(used_item, src)) + to_chat(usr, SPAN_NOTICE("You slip \the [used_item] into \the [src].")) + held = used_item + return TRUE + return ..() \ No newline at end of file diff --git a/code/modules/clothing/neck/necklace/pendant_random.dm b/code/modules/clothing/neck/necklace/pendant_random.dm new file mode 100644 index 000000000000..f17171032c6e --- /dev/null +++ b/code/modules/clothing/neck/necklace/pendant_random.dm @@ -0,0 +1,27 @@ +/obj/item/pendant/prism/random/Initialize(ml, material_key) + material_key = list(global.random_jewellery_gem_types) + . = ..() + +/obj/item/pendant/frill/random/Initialize(ml, material_key) + material_key = pick(global.random_jewellery_material_types) + . = ..() + +/obj/item/pendant/setting/square/random/Initialize(ml, material_key) + decorations = list(pick(global.random_jewellery_gem_types)) + material_key = pick(global.random_jewellery_material_types) + . = ..() + +/obj/item/pendant/setting/cross/random/Initialize(ml, material_key) + decorations = list(pick(global.random_jewellery_gem_types)) + material_key = pick(global.random_jewellery_material_types) + . = ..() + +/obj/item/pendant/setting/diamond/random/Initialize(ml, material_key) + decorations = list(pick(global.random_jewellery_gem_types)) + material_key = pick(global.random_jewellery_material_types) + . = ..() + +/obj/item/pendant/setting/ornate/random/Initialize(ml, material_key) + decorations = list(pick(global.random_jewellery_gem_types)) + material_key = pick(global.random_jewellery_material_types) + . = ..() diff --git a/code/modules/clothing/neck/necklace/pendant_setting.dm b/code/modules/clothing/neck/necklace/pendant_setting.dm new file mode 100644 index 000000000000..80e79b13cf2a --- /dev/null +++ b/code/modules/clothing/neck/necklace/pendant_setting.dm @@ -0,0 +1,36 @@ +// Pendant with a setting for a stone. +/obj/item/pendant/setting + abstract_type = /obj/item/pendant/setting + +// Duplicating some logic here to consistently insert the gem name where appropriate. +/obj/item/pendant/setting/get_name_components() + . = list() + if(name_prefix) + . += name_prefix + if(material) + . += material.adjective_name + var/obj/item/gemstone/gem = locate() in contents + if(gem) + . += gem.name + . += name // base_name has already been applied by parent call + +// Subtypes below. +/obj/item/pendant/setting/square + name_prefix = "square" + desc = "A square-shaped pendant." + icon = 'icons/clothing/accessories/jewelry/pendants/square.dmi' + +/obj/item/pendant/setting/cross + name = "cross" + desc = "A hard-edged cross pendant." + icon = 'icons/clothing/accessories/jewelry/pendants/cross.dmi' + +/obj/item/pendant/setting/diamond + name_prefix = "diamond" + desc = "A diamond-shaped pendant." + icon = 'icons/clothing/accessories/jewelry/pendants/diamond.dmi' + +/obj/item/pendant/setting/ornate + name_prefix = "ornate" + desc = "An ornate set of pendants." + icon = 'icons/clothing/accessories/jewelry/pendants/ornate.dmi' diff --git a/code/modules/clothing/neck/prayer_beads.dm b/code/modules/clothing/neck/prayer_beads.dm new file mode 100644 index 000000000000..0c081e351089 --- /dev/null +++ b/code/modules/clothing/neck/prayer_beads.dm @@ -0,0 +1,29 @@ +/obj/item/clothing/neck/prayer_beads + name = "prayer beads" + desc = "A string of smooth, polished beads." + icon = 'icons/clothing/accessories/jewelry/prayer_beads.dmi' + gender = PLURAL + material = /decl/material/solid/organic/wood/ebony + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC + +/obj/item/clothing/neck/prayer_beads/gold + material = /decl/material/solid/metal/gold + +/obj/item/clothing/neck/prayer_beads/basalt + material = /decl/material/solid/stone/basalt + +/obj/item/clothing/neck/prayer_beads/bone + material = /decl/material/solid/organic/bone + +/obj/item/clothing/neck/prayer_beads/random + var/list/random_materials = list( + /decl/material/solid/organic/bone, + /decl/material/solid/stone/marble, + /decl/material/solid/stone/basalt, + /decl/material/solid/organic/wood/mahogany, + /decl/material/solid/organic/wood/ebony + ) + +/obj/item/clothing/neck/prayer_beads/random/Initialize() + material = pick(random_materials) + return ..() diff --git a/code/modules/crafting/stack_recipes/recipes_hardness.dm b/code/modules/crafting/stack_recipes/recipes_hardness.dm index a656ebc868e0..903769244d6e 100644 --- a/code/modules/crafting/stack_recipes/recipes_hardness.dm +++ b/code/modules/crafting/stack_recipes/recipes_hardness.dm @@ -75,7 +75,19 @@ result_type = /obj/item/chems/glass/mortar /decl/stack_recipe/ring - result_type = /obj/item/clothing/gloves/ring/material + result_type = /obj/item/clothing/gloves/ring + +/decl/stack_recipe/ring_thin + name = "ring, thin" + result_type = /obj/item/clothing/gloves/ring/thin + +/decl/stack_recipe/ring_thick + name = "ring, thick" + result_type = /obj/item/clothing/gloves/ring/thick + +/decl/stack_recipe/ring_split + name = "ring, split" + result_type = /obj/item/clothing/gloves/ring/split /decl/stack_recipe/hardness/clipboard result_type = /obj/item/clipboard diff --git a/code/modules/decoration/_decoration.dm b/code/modules/decoration/_decoration.dm new file mode 100644 index 000000000000..0228f99654b5 --- /dev/null +++ b/code/modules/decoration/_decoration.dm @@ -0,0 +1,104 @@ +// Should return a linear list of /decl/item_decoration types that can apply to the target items. +/proc/get_decoration_types_for(mob/user, obj/item/target_item, obj/item/used_item) + // TODO: consider caching this somewhere? + for(var/decl/item_decoration/decoration as anything in decls_repository.get_decls_of_subtype_unassociated(/decl/item_decoration)) + if(decoration in target_item.decorations) + continue + if(!decoration.can_apply_to(user, target_item, used_item)) + continue + LAZYADD(., decoration) + +// Decl types for decorations below. +/decl/item_decoration + abstract_type = /decl/item_decoration + /// A descriptive named used to select this decoration. + var/name + /// State used when searching item icon files for world/inventory/onmob states to draw. + var/icon_state_modifier + /// A list of types that this decoration can be applied to. Must contain at least one entry. + var/list/can_decorate_types + /// A list of types that can be used to apply this decoration. Must contain at least one entry. + var/list/can_decorate_with_types + /// String used to describe this action. + var/decoration_verb = "decorate" + /// String used to describe this action while underway. + var/decoration_action = "decorating" + /// What skill to use when applying this decoration. If null, defaults to the material. + var/work_skill + /// How long this work should take. + var/work_time = 10 SECONDS + /// Factor value supplied to do_skilled() + var/work_factor = 1 + /// Additional monetary worth for the item, as a percentage of the base item value (ie. 0.1 = +10%) + var/decoration_worth_factor = 0.1 + +/decl/item_decoration/validate() + . = ..() + if(!istext(name)) + . += "null or invalid name" + if(!istext(icon_state_modifier)) + . += "null or invalid icon_state_modifier" + if(!length(can_decorate_types)) + . += "no types in can_decorate_types" + if(!length(can_decorate_with_types)) + . += "no types in can_decorate_with_types" + +/decl/item_decoration/proc/can_apply_to(mob/user, obj/item/thing, obj/item/decorating_with) + if(!istype(thing) || !is_type_in_list(thing, can_decorate_types)) + return FALSE + if(!istype(decorating_with) || !is_type_in_list(decorating_with, can_decorate_with_types)) + return FALSE + return TRUE + +/decl/item_decoration/proc/show_work_start_message(mob/user, obj/item/thing, obj/item/decorating_with) + user.visible_message(SPAN_NOTICE("\The [user] begins [decoration_action] \the [thing] with \the [decorating_with].")) + +/decl/item_decoration/proc/show_work_end_message(mob/user, obj/item/thing, obj/item/decorating_with) + user.visible_message(SPAN_NOTICE("\The [user] finishes [decoration_action] \the [thing] with \the [decorating_with].")) + +/decl/item_decoration/proc/apply_to_item(mob/user, obj/item/thing, obj/item/decorating_with) + var/stack_use = 0 + if(istype(decorating_with, /obj/item/stack)) + var/obj/item/stack/stack = decorating_with + for(var/stack_type in can_decorate_with_types) + if(istype(stack, stack_type)) + stack_use = can_decorate_with_types[stack_type] + break + if(stack.get_amount() < stack_use) + to_chat(user, SPAN_WARNING("You need at least [stack_use] [stack_use == 1 ? stack.singular_name : stack.plural_name] to [decoration_verb] \the [thing].")) + return FALSE + show_work_start_message(user, thing, decorating_with) + if(work_time && work_skill) + if(!user.do_skilled(work_time, work_skill, thing, work_factor)) + return FALSE + if(!QDELETED(user) || QDELETED(thing) || QDELETED(decorating_with) || user.get_active_held_item() != decorating_with) + return FALSE + if(stack_use && istype(decorating_with, /obj/item/stack)) + var/obj/item/stack/stack = decorating_with + if(!stack.use(stack_use)) + return FALSE + else if(!user.try_unequip(decorating_with, thing, FALSE)) + return FALSE + show_work_end_message(user, thing, decorating_with) + return TRUE + +// This will need work down the track. Value will be added by the decorations +// in matter[] but the value of the labour isn't reflected. Needs thought. +/decl/item_decoration/proc/get_decoration_value(obj/item/thing, base_value) + return max(1, round(base_value * decoration_worth_factor)) + +/decl/item_decoration/proc/attempt_removal(mob/user, obj/item/target_item, obj/item/used_item) + return FALSE + +/decl/item_decoration/proc/resolve_color(obj/item/thing) + var/list/decoration_data = LAZYACCESS(thing.decorations, src) + if(islist(decoration_data) && length(decoration_data)) + var/obj/item/decoration_object = decoration_data["object"] + if(istype(decoration_object) && decoration_object.paint_color) + return decoration_object.paint_color + var/decl/material/decoration_material = decoration_data["material"] + if(istype(decoration_material)) + return decoration_material.color + if(istype(decoration_object)) + return decoration_object.color + return COLOR_WHITE diff --git a/code/modules/decoration/decoration_item.dm b/code/modules/decoration/decoration_item.dm new file mode 100644 index 000000000000..ae628cf68bf5 --- /dev/null +++ b/code/modules/decoration/decoration_item.dm @@ -0,0 +1,103 @@ +/obj/item + /// Assoc list of decoration instances to metadata, ie. + /// decorations[GET_DECL(/decl/item_decoration/inset)] = list("color" = COLOR_RED, "material" = GET_DECL(/decl/material/foo)) + var/list/decorations + +/obj/item/proc/add_item_decoration(decl/item_decoration/decoration_type, decoration_color, decl/material/decoration_material, obj/item/associated_object) + LAZYINITLIST(decorations) + if(ispath(decoration_type)) + decoration_type = GET_DECL(decoration_type) + if(!istype(decoration_type)) + CRASH("Invalid decoration_type.") + . = list() + if(decoration_color) + // TODO: validate color? + .["color"] = decoration_color + if(decoration_material) + if(ispath(decoration_material)) + decoration_material = GET_DECL(decoration_material) + if(istype(decoration_material)) + .["material"] = decoration_material + if(!associated_object) // Object holds matter, no need to add it to parent. + var/decoration_matter_amount = MATTER_AMOUNT_TRACE * get_matter_amount_modifier() + if(decoration_material.type in matter) + matter[decoration_material.type] += decoration_matter_amount + else + LAZYSET(matter, decoration_material.type, decoration_matter_amount) + else + PRINT_STACK_TRACE("Invalid decoration_material.") + if(associated_object) + .["object"] = associated_object + decorations[decoration_type] = . + queue_icon_update() + update_name() + +/obj/item/proc/get_decoration_icon(default_icon, obj/item/thing, on_mob = FALSE) + return default_icon || icon + +/obj/item/proc/get_decoration_overlays(decoration_state, decoration_icon, on_mob = FALSE) + for(var/decl/item_decoration/decoration as anything in decorations) + var/list/decoration_metadata = decorations[decoration] + if(!islist(decoration_metadata)) + continue + var/obj/item/decoration_object = decoration_metadata["object"] + if(decoration_object) + decoration_icon = get_decoration_icon(decoration_icon, decoration_object, on_mob) + if(!decoration_icon) + continue + decoration_state = "[decoration_state]-[decoration.icon_state_modifier]" + if(check_state_in_icon(decoration_state, decoration_icon)) + LAZYADD(., overlay_image(decoration_icon, decoration_state, decoration.resolve_color(src), RESET_COLOR|RESET_ALPHA)) + +/obj/item/attackby(obj/item/used_item, mob/user) + . = ..() + if(.) + return + + if(user.check_intent(I_FLAG_HELP)) + var/list/possible_decorations = get_decoration_types_for(user, src, used_item) + if(!length(possible_decorations)) + return FALSE + var/decl/item_decoration/decoration + if(length(possible_decorations) == 1) + decoration = possible_decorations[1] + else + decoration = input(user, "Which decoration would you like to apply?", "Item Decoration") as null|anything in possible_decorations + if(!decoration || QDELETED(src) || QDELETED(user) || QDELETED(used_item) || !CanPhysicallyInteract(user) || user.get_active_held_item() != used_item) + return TRUE + if(istype(decoration) && decoration.apply_to_item(user, src, used_item)) + add_item_decoration(decoration, used_item.paint_color, used_item.material, istype(used_item, /obj/item/stack) ? null : used_item) + return TRUE + + for(var/decl/item_decoration/decoration as anything in decorations) + if(decoration.attempt_removal(user, src, used_item)) + return TRUE + +/obj/item/get_single_monetary_worth() + . = ..() + var/base_value = . + for(var/decl/item_decoration/decoration as anything in decorations) + . += decoration.get_decoration_value(src, base_value) + +/obj/item/on_update_icon() + . = ..() + var/list/decoration_overlays = get_decoration_overlays(icon_state) + if(length(decoration_overlays)) + add_overlay(decoration_overlays) + +/obj/item/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing) + if(overlay) + var/list/decoration_overlays = get_decoration_overlays(overlay.icon_state, overlay.icon, on_mob = TRUE) + if(length(decoration_overlays)) + overlay.overlays += decoration_overlays + return ..() + +// Except decorations from storage datums. +/obj/item/get_stored_inventory() + . = ..() + if(length(.) && length(decorations)) + for(var/decl/item_decoration/decoration in decorations) + var/list/decoration_data = decorations[decoration] + var/thing = decoration_data?["object"] + if(isitem(thing)) + . -= thing diff --git a/code/modules/decoration/decoration_setting.dm b/code/modules/decoration/decoration_setting.dm new file mode 100644 index 000000000000..352cb3bdc565 --- /dev/null +++ b/code/modules/decoration/decoration_setting.dm @@ -0,0 +1,30 @@ +/decl/item_decoration/setting + name = "set a gem" + icon_state_modifier = "setting" + can_decorate_types = list( + /obj/item/clothing/gloves/ring, + /obj/item/pendant/setting + ) + can_decorate_with_types = list( + /obj/item/gemstone + ) + +/decl/item_decoration/setting/attempt_removal(mob/user, obj/item/target_item, obj/item/used_item) + if(IS_SCREWDRIVER(used_item)) + var/list/decoration_data = target_item.decorations[src] + var/obj/item/removing = decoration_data?["object"] + if(istype(removing)) + user.visible_message(SPAN_DANGER("\The [user] levers \the [removing] out of \the [target_item] with \the [used_item]!")) + removing.dropInto(user.loc) + user.put_in_hands(removing) + LAZYREMOVE(target_item.decorations, src) + target_item.update_icon() + target_item.update_name() + return TRUE + return FALSE + +/decl/item_decoration/setting/show_work_start_message(mob/user, obj/item/thing, obj/item/decorating_with) + user.visible_message(SPAN_NOTICE("\The [user] begins carefully setting \the [decorating_with] into \the [thing].")) + +/decl/item_decoration/setting/show_work_end_message(mob/user, obj/item/thing, obj/item/decorating_with) + user.visible_message(SPAN_NOTICE("\The [user] finishes setting \the [decorating_with] into \the [thing].")) diff --git a/code/modules/gemstones/_gemstone.dm b/code/modules/gemstones/_gemstone.dm index 087266d34f89..d4e51499dd1f 100644 --- a/code/modules/gemstones/_gemstone.dm +++ b/code/modules/gemstones/_gemstone.dm @@ -87,3 +87,41 @@ var/global/list/_available_gemstone_cuts name = "round-cut diamond" cut = /decl/gemstone_cut/round icon = 'icons/obj/items/gemstones/round.dmi' + + +// Material subtypes. +/obj/item/gemstone/baguette/topaz + material = /decl/material/solid/gemstone/topaz + +/obj/item/gemstone/baguette/sapphire + material = /decl/material/solid/gemstone/sapphire + +/obj/item/gemstone/baguette/ruby + material = /decl/material/solid/gemstone/ruby + +/obj/item/gemstone/hexagon/topaz + material = /decl/material/solid/gemstone/topaz + +/obj/item/gemstone/hexagon/sapphire + material = /decl/material/solid/gemstone/sapphire + +/obj/item/gemstone/hexagon/ruby + material = /decl/material/solid/gemstone/ruby + +/obj/item/gemstone/octagon/topaz + material = /decl/material/solid/gemstone/topaz + +/obj/item/gemstone/octagon/sapphire + material = /decl/material/solid/gemstone/sapphire + +/obj/item/gemstone/octagon/ruby + material = /decl/material/solid/gemstone/ruby + +/obj/item/gemstone/round/topaz + material = /decl/material/solid/gemstone/topaz + +/obj/item/gemstone/round/sapphire + material = /decl/material/solid/gemstone/sapphire + +/obj/item/gemstone/round/ruby + material = /decl/material/solid/gemstone/ruby diff --git a/code/modules/gemstones/gemstone_cuts.dm b/code/modules/gemstones/gemstone_cuts.dm index 86509cc2c177..4b8b15fb7d84 100644 --- a/code/modules/gemstones/gemstone_cuts.dm +++ b/code/modules/gemstones/gemstone_cuts.dm @@ -23,10 +23,11 @@ . += "missing world state from '[icon]'" if(!check_state_in_icon(ICON_STATE_INV, icon)) . += "missing inventory state from '[icon]'" - var/check_state = "[ICON_STATE_WORLD]-set" + var/decl/item_decoration/gem_set = GET_DECL(/decl/item_decoration/setting) + var/check_state = "[ICON_STATE_WORLD]-[gem_set.icon_state_modifier]" if(!check_state_in_icon(check_state, icon)) . += "missing state '[check_state]' from '[icon]'" - check_state = "[ICON_STATE_INV]-set" + check_state = "[ICON_STATE_INV]-[gem_set.icon_state_modifier]" if(!check_state_in_icon(check_state, icon)) . += "missing state '[check_state]' from '[icon]'" else diff --git a/code/modules/species/species_bodytype_offsets.dm b/code/modules/species/species_bodytype_offsets.dm index 6679500a3ebb..e00901891127 100644 --- a/code/modules/species/species_bodytype_offsets.dm +++ b/code/modules/species/species_bodytype_offsets.dm @@ -32,7 +32,8 @@ The slots that you can use are found in items_clothing.dm and are the inventory // Check the cache for previously made icons. var/modifier = mob?.get_overlay_state_modifier() var/image_key = modifier ? "[modifier]-[mob_icon]-[mob_state]-[color]-[slot]" : "generic-[mob_icon]-[mob_state]-[color]-[slot]" - if(!equip_overlays[image_key]) + var/image/copying = equip_overlays[image_key] + if(!copying) var/icon/final_I = new(icon_template) var/list/shifts = use_equip_adjust[slot] @@ -44,9 +45,15 @@ The slots that you can use are found in items_clothing.dm and are the inventory var/icon/equip = new(mob_icon, icon_state = mob_state, dir = use_dir) var/icon/canvas = new(icon_template) canvas.Blend(equip, ICON_OVERLAY, facing_list[1]+1, facing_list[2]+1) - final_I.Insert(canvas, dir = use_dir) - equip_overlays[image_key] = overlay_image(final_I, color = color, flags = RESET_COLOR) + final_I.Insert(canvas, icon_state = mob_state, dir = use_dir) + copying = overlay_image(final_I, mob_state, color, RESET_COLOR) + equip_overlays[image_key] = copying + var/image/I = new() // We return a copy of the cached image, in case downstream procs mutate it. - I.appearance = equip_overlays[image_key] + I.appearance = copying + // For some reason icon_state is coming back null... + I.icon = copying.icon + I.icon_state = copying.icon_state return I + return overlay_image(mob_icon, mob_state, color, RESET_COLOR) diff --git a/icons/clothing/accessories/jewelry/locket.dmi b/icons/clothing/accessories/jewelry/locket.dmi deleted file mode 100644 index eeee8efe7eaa53c85c9abbaf09e8cffca0be376f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 837 zcmeAS@N?(olHy`uVBq!ia0vp^2|(k@;xciv6fE%yAOl6ptr zrQ1v1Br&+}zb(AlPn^5&^28aY@A6J-?Oc&`;Nq1TVs`~D>!lXRbljcvDeYw|xP(sEf|*!%19Q`KoZrKYB>|Mb}W`|O=Z4LE_uiSy@JExA!~vYvr~DaX^rF{EP7 z+gbK`j~rxLkFO39_?#^sJ&AQ8$Fa8)c zDO3b`{nfkupQm*Df)#HZX06z<;!uVe|GV2Q<_vS=kDZ_O_t+Iqj@MEi!cTeG7hmez z#4`7P{FI>V``_hTf2{addV8A|>ypl{^v??Gv!9gMT`LQbzVI_NW9|HAg%h{$Rq^<` zIr^?Lh+8MNRrab-Nu8#b=Icg>wG1Vqi$2F*{$i_;6OgF){_u;M^bkI0-ZYIJF}j~F z|CHckU!1Z1Zv4N$b3zrY?yJtP626!FJ#N;DA0>8er>`pQm-uJ7-l1e4ca*Oy(DH7s zKjl34qFEPx-hE_qtgxQkgogzNU3~fL7robHD%q!dZF1o=*B2kYX)T$u=>0>Wfz#ah zA6lI`njWdPN$2&VldDf{UpV>Yi<1>xdkaeT@y_MAk^B8#ij8QHm!YKp?u<{XKILwx z;}Evqp~MCbX9+F_%)mzy%v3$_{cn8e?3Z4q`4_q(cPbVMtNQbOr0>I i8p$?9`~w9)^SR&fmRij8&JCC^7(8A5T-G@yGywn%>2|;X diff --git a/icons/clothing/accessories/jewelry/necklace.dmi b/icons/clothing/accessories/jewelry/necklace.dmi index 23c9eee006315e05e061380876001fe48fe4c79f..2f5ca2d3cfb138501a6780b1fa14c3b62dd77afb 100644 GIT binary patch delta 475 zcmV<10VMwL1?dBj7=Hu<0000N8gD%S000kAOjJd{z`%-%p2ES&|NsAGVs`y6noR%z z00DGTPE!Ct=GbNc005VIR9JLGWpiV4X>fFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5; z&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LYR3KBSJb%9^Cq;>iGbOXA7|1u| z;!G<_%uR)`;i@w8%2M-6@{1}7s>~?OP0Y*BOi@V6PpQ-`&dD!{FHcP@iqB0f&PHf9 zLD8(N;OgfBc0B;-3_6Pfgg&YO008AlL_t(YiS3n94uc>JhKu6>iaWrZ9zewdF!lat zS_KjcPL!wlz<(FTkoLEKks!dosLI6rwnlXtJV!f$qRVPLMIFH?M^w^-cEh4ugI8pcu51mT^Q{rq z8!BYFoR2S;QjRJ?z*i(8KaYfvZiYHH!jsl8BM;-CoeyE8!IAGZftP*(Ix(>CMnas zF?xz?C%pK^E@T??pJ;uKn4hEnB|!==&$nzptd| zbY#Ey*9!?dW4I3Objm+af1DwKhv679aebfmJ^MR14R|i!&6{2uyKmnf*4n%$Gb-6R z4jbr}PUTj-bIwjjSkiu%!tc_mrBgP!2C~@KzMDPel3SokkKrNRt=H23?ki_dbW!WG z{wpl5HgThG%AIPK_F5DF$;;OJz=F9r!{J!g(YTO*{X8UJ5mp+`-U$Z>0VZRyI zu?KU0H(Ytl@&2!kftc^QrWS$Y+VgJKWSQSCtuo$uHq8Hg{C_SBCY!mn>U*lH_`G7D z&VFk(_v~`Bi60-Iye`8yFUsF_%C1x2l@+Wz4+uV3xq0V;pzwAM=R4dzuFEGEzH{pd zcl;+czvXpJvD32n0xPK=#sd-zXvCV&)(^cmN9Zire_jwZFcD$9mT8Ckk%|8fnQgh4 zrFX(sNzQem#4p3brBh6g@%-Bzn{6xkTuyL$5Z|{&MbP0l+XkKkA*xZ diff --git a/icons/clothing/accessories/jewelry/pendants/cross.dmi b/icons/clothing/accessories/jewelry/pendants/cross.dmi new file mode 100644 index 0000000000000000000000000000000000000000..ca9d50a60f0b5ee90e14b9255b197a1f6a075b18 GIT binary patch literal 427 zcmV;c0aX5pP)BELUjND00DGTPE!Ct=GbNc0063bR9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)1vgK(IJKlCGcTPu zbBR`+QJR~Wm!FxUkd&VSv?C|KB)&W~u_!(_u{ax{*@Se>m@YsyL|MVr&jlRB09>JO zDllyiC;$Kekx4{BR9J=W*Fg${Fc5{|Sna;z1w`w<$Q(fM{&#X~CXzv>0nz{4B}|5w z2!@0J0EJcF21D;#{~CvUL9Hj+PuxJ}d;|HZiEr;r(8USPpMm$#^cE706<{;Dq~0D!BpUS+CcDU}1jlWmD< V2S1Z~MwS2o002ovPDHLkV1kL7u>Jr5 literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/pendants/crystal.dmi b/icons/clothing/accessories/jewelry/pendants/crystal.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a54525da6ab160cdada3e1b98613aef69c83959a GIT binary patch literal 369 zcmV-%0gnEOP)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)l~J0Tn3tcKqL7rI zQmI>BELUjND00DGTPE!Ct=GbNc0063bR9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)1vgK(IJKlCGcTPu zbBR`+QJR~Wm!FxUkd&VSv?C|KB)&W~u_!(_u{ax{*@Se>m@YsyL|MVr&jlRB09>JO zDllyiC;$Keok>JNR9J=W*Fg?~Fc5{|vEW|u0w&1bkU0RM7cg@FySSCX%*0d@1OK<9 zolYNw&>#W;D?5}nNTm%fCx5=O1LygIq`luj?`Gh3Elkjw246peCfG4#Z^5dA^(l{j zK^}qts&0f@HApZ${etP?&r9b95IF|x%U@jpo3Z%L`xW+B`Eb%KE8Z{i?-Kw3fPWMH hm6?iRtsDUM>;+492Y0z5tl|Iw002ovPDHLkV1nD!x3mBN literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/pendants/frill.dmi b/icons/clothing/accessories/jewelry/pendants/frill.dmi new file mode 100644 index 0000000000000000000000000000000000000000..97891de4bc3bc06ed972b3cc2905d27fed821f98 GIT binary patch literal 383 zcmV-_0f7FAP)nwyxHpP8bNl%G z2`V@wwpHMeFfYI%!QFt$X97a3fJs3Ky9Be6GOBQ3j80tKg9q5002ovPDHLkV1iV-k@5fl literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/pendants/locket.dmi b/icons/clothing/accessories/jewelry/pendants/locket.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e8f1bbee40b2fadb8f4da1eb8da25de538da0d01 GIT binary patch literal 462 zcmV;<0WtoGP)00DGTPE!Ct=GbNc0066cR9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)l~J0Tn3tcKqL7rI zQmI>jEdBnMhIHmZUg=PY(+msbt{anBy3jk*% zYew1;(4znV0JBL%K~z|U?UqprgD?<8E#v?iasb760nvY>?FHK2|1AEl5s90DDs7<_*~7l>Kg#=HGNo8S)n1J<6#x;NC{rtzJ> zGyyyVLdYMHuBycTkS+=*MF{b6x~gU={O$({Cm@876{Err)d)QRHUIzs07*qoM6N<$ Ef(q%uTmS$7 literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/pendants/ornate.dmi b/icons/clothing/accessories/jewelry/pendants/ornate.dmi new file mode 100644 index 0000000000000000000000000000000000000000..9e6901f4e2988cbd51d3f4b5b552456097f7ce8d GIT binary patch literal 585 zcmV-P0=E5$P)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)1vgK(IJKlCGcTPu zbBR`+QJR~Wm!FxUkd&VSv?C|KB)&W~u_!(_u{ax{*@Se>m@YsyL|MVr&jlRB09>JO zDllyiC;$KfFG)l}R9J=Wm(6m*AP~nFNbXehPHZ2bj`1GL?AMl00000NkvXXu0mjfir4{f literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/pendants/square.dmi b/icons/clothing/accessories/jewelry/pendants/square.dmi new file mode 100644 index 0000000000000000000000000000000000000000..5c9040c7baa83ef3dbf0260af6ccdc8c1c3c8107 GIT binary patch literal 435 zcmV;k0ZjghP)t<7!okUkik{L{0X_f#00DGTPE!Ct=GbNc0063bR9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+Gp{T)uOz>yQi+Q*C9|j)C}zmTnO2mTn+jpWRhH)$<)jc)1vgK(IJKlCGcTPu zbBR`+QJR~Wm!FxUkd&VSv?C|KB)&W~u_!(_u{ax{*@Se>m@YsyL|MVr&jlRB09>JO zDllyiC;$KenMp)JR9J=W)lCY6Fc5}ee|i8bc3*7|P)giO#eGG*|5e=D1oIJsAohKB zV`jb#E;LC1U}tG;Ko%QN7J&m-Yv6JB_viKldmwm&r?^q7LT|LBO&u4I^&fvj--!V8 z_BWW92pGf$gV-SY9RN5F{VV(Fa6iH0R|Smg;^rJrGGJO4t#drffO)e3004({3G3+g d+5^BpTL5^02%(|dri1_h002ovPDHLkV1gTfv4Q{q literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/rings/ring.dmi b/icons/clothing/accessories/jewelry/rings/ring.dmi deleted file mode 100644 index 5a116c6c820912d9ea27258bd9b07d7946f5a3e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 zcmV-_0f7FAP)V=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRL zOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5sXV_ZCq;>iGbOXA7|1u|;!G<_%uR)`;i@u9 za})FOGgB0j@>42xi*xcz;?r~T%TkLGI!#b?Dl549xqux60J~5u)pi-7>Hq)$aY;l$ zR7iF4>3kWJHkffg3*-(fi4X!T6Bw4|d8lqHQM2d^jk{H@2k)T1uO@X146b-^o z4ID}hq-Zc&(OyJ~1}3B3+bc*=&%h+8U8Df#vEs0+1BtCfVnmOEQ7{Td!6+D-AYz~$ dFbYN<005BX54aCS{dNEV002ovPDHLkV1jLTq3Hks diff --git a/icons/clothing/accessories/jewelry/rings/ring_band.dmi b/icons/clothing/accessories/jewelry/rings/ring_band.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b8f40338dccc9372490ad348da76998887a82a40 GIT binary patch literal 454 zcmV;%0XhDOP)y?%OEqvgcQ_KwVSOJGa7cq9QIN;K124gDOPKtZf6Ri48 z&J?}*Nx8?Q1EEnjGE0*@@zr|DkUq`!&t{W=8H4?{P^*6IUgw`}B~LCFAU3Q3006d0 zL_t(oh3(cs3c@fD1<+{B-lP|(dI3w?6J)Y4n7RK|tfH8biVOj*@ZVwuHRZ$ldx%sf}O1#L;(#kr;v|u@~vcmo|Q7G%T@;N-tR91hFdjRiyqZQ5TN-U zkhSP&f*@d*UiG1;vmn6GIMo#Mp}e4fIxmijKc4BH;pL0b6qzKKF8JvoNiMyR;OqJU w005vg|LQON=54<;2mk;8tW^8!0RaBY6`#xx<5;Xa(EtDd07*qoM6N<$f;q6u`Tzg` literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/rings/ring_band_split.dmi b/icons/clothing/accessories/jewelry/rings/ring_band_split.dmi new file mode 100644 index 0000000000000000000000000000000000000000..fb29ccae72af23ade4c0bab70b7c682e3dba1ba0 GIT binary patch literal 448 zcmV;x0YCnUP)y?%OEqvgcQ_KwVSOJGa7cq9QIN;K124gDOPKtZf6Ri48 z&J?}*Nx8?Q1EEnjGE0*@@zr|DkUq`!&t{W=8H4?{P^*6IUgw`}B~LCFAU3Q3006K_ zL_t(oh3(cs4#FT11<+B-Ug!m?Ucd%=0$|@nxc^n8rinCY%%n-J$$yI>q0H~Fg9u`A$0Fj;n zMT-d1F#?+Ssxu>Qi~#A!Tw^FR8%95o4O7auf9iP^^(T>=E$7k|lS4R{1^@tnEiB)O qZ=QL_jRpY#0Dzt9emwxdpLqa(;|%Mgp}|D}0000y?%OEqvgcQ_KwVSOJGa7cq9QIN;K124gDOPKtZf6Ri48 z&J?}*Nx8?Q1EEnjGE0*@@zr|DkUq`!&t{W=8H4?{P^*6IUgw`}B~LCFAU3Q3006^D zL_t(oh3(iu3WG2Z1<=IQef0zyFVHr60w;T+nfqU*u|u@+Z-Rho zTGu*hF^jqj0t`O`k{08676hDL0U;@kbP)v9w8S;|^iWRFANTW%;*Wc7@5OZ4EkmA3 zGIYkp4@olgn*>+Z4*&oFx$bb)<`3xaoC5#=0De{3pD}>{^8n!L5_U_f7SsR$002ov JPDHLkV1oPu%n<+p literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/rings/ring_band_thin.dmi b/icons/clothing/accessories/jewelry/rings/ring_band_thin.dmi new file mode 100644 index 0000000000000000000000000000000000000000..83c9b2ced56f76a41060a0fc136823650eafea09 GIT binary patch literal 442 zcmV;r0Y(0aP)y?%OEqvgcQ_KwVSOJGa7cq9QIN;K124gDOPKtZf6Ri48 z&J?}*Nx8?Q1EEnjGE0*@@zr|DkUq`!&t{W=8H4?{P^*6IUgw`}B~LCFAU3Q30062< zL_t(oh3(c&3c@fD1>jNBz0?a-@c_0-_mPZyMRWhF*aiwyQjj6wAMaZazcy(qyh37B4a-LXo=}dIoYc9Qz5X<%h002O4{?%XjOIyFG k2mk;8e5vNs1pt1`6P?Knv*O3K%m4rY07*qoM6N<$f{Av+GXMYp literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/rings/ring_blue.dmi b/icons/clothing/accessories/jewelry/rings/ring_blue.dmi deleted file mode 100644 index e607595960ff79462d2973e4800fc67315deef79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1340 zcmV-C1;hG@P)pcQEI4P)>SX7deU z=L=rm4O`I*Sj(=it_xSi6=?DcSH}xj&I?=B3RJ)iTha?uz6w>vwY9YkUfT*(y$e*k zn3$LpY4h9L+sDVp3t7(%T-gm=*a}s{3su7nW%3PN+Y4CA3tQ9*P_hkQ-wIK*3RJ!e zT-XXyw+d3Z3RS=hQn?CHvkFqW2~MmEO{oe^s0vW84PxyJSj-7fwhB?V3sJTTPO1t} zunJYd2}`31O{oV*n+sUT2S$|%N}~x#n+Z#z3Q(;HNSp~rmfQ4P);SX7Lng^9oqc3tZL-POS@4w+&w03QejD zThb9`^T5Esm6estd%`LJ0004WQchCV=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 zsXV_ZCq;>iGbOXA7|1u|;!G<_%uNNcjZA>TsX2+2AaNa%Aj15N(%i(n{LB=Er2Ldh z-Qt}5lKAwT{Ib+ygq-fLM$%#u)o=5<=5%wd=ZnBKU}i$FV2wVed1r_b0L6AHc5bgM&g#1)UH^{6QMJ zK9qny+&7Fo+26Sb!9I|FID%YC-M{^#LHAK)GR7n1*|G5i^v5{^xoOHI_WTHN{~sW` zk08sku00{*nF4epJFf5?^rS7Jj}-IBW11N3K1YN#aqR(3JW=?N!uxu>d>VQzrCmM8 z#l-aoqVBPj|L79FcYoeO_YaI}PlfOwVL?|AZTBB!qgts{vaH+cF)fwSN>=a-MbzCt zp`q8g=sRPKF~%5Uj4^h13w^%d^M0U==kJD8%;yz8nZaI}!k(*QFHb+Kq4|3*@w^D< z^SX49XWguad}elP4tZTxUm$NZ>>8TS&(9YZkUM_J7Z*Jb`O*@;{PME10`~*8h7ITQ za()4O=l288c5XlLDQ6CP-Ki_~1FMSpd_v*6!d-<|6u!p&K%_nN`9Ajpq0jfZA7G3z z#u#IaF~;t0q0jewb!p@Id*&r}^A+}nhy7|g9PF=)ip!g}jppy`8|EhRW~-4wzP06T zBj4F^B=X(eVj2Ga<_4V4zj?d2j(mR~&-V`wJT%|$+ysc^;T2ab-g&nnz?vA0@s63yQ|@7O?Y+sUuUPfoslM}AbE z{(-zuI6Xz)YUU-H&#$z8o*_R!ufXpIy0~b!k=wSriof5;J%#&$UmIg^KJP6YLqD`D z(DT(c^m5IGUfAwe><4};=JRJgzW=|iaMkA*{%}7KX%BtA&;3B?^L_3I7-Nhv#u#Ia yF~%5UjIn>4etDzgmjTCbcr}bM#u#I)r~U#$*^jef^<3)!0000V=-0C=2JR&a84_w-Y6@%7{?OD!tS z%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5sXV_ZCq;>iGbOXA7|1u| z;!G<_%uR)`;i@u9a})FOGgB0j@>42xi*xcz;?r~T%TkLGI!#b?Dl549xqux60J~5u z)pi-7>Hq)$heEJA_qLkhhP_A5Tre!VSm? zhzd$BQ13lLm0#Ezdt5K+e%y*Wu8)(-@7T=D%*@RG>gI2Nnb}L6b2$PPmq1$_00000 LNkvXXu0mjfC&IZG diff --git a/icons/clothing/accessories/jewelry/rings/ring_diamond.dmi b/icons/clothing/accessories/jewelry/rings/ring_diamond.dmi deleted file mode 100644 index e05fcffea0fbf773feb9f16773030232683386ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 376 zcmV-;0f+vHP)V=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRL zOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5sXV_ZCq;>iGbOXA7|1u|;!G<_%uR)`;i@u9 za})FOGgB0j@>42xi*xcz;?r~T%TkLGI!#b?Dl549xqux60J~5u)pi-7>Hq)$YDq*v zR7iF4>3kWJHkfh$kG*E~X^~}lXN+emqVr;^|(nOjK3@npK&>&S(K$-?o1qKEu z2hudKG?Ai#Dagg6g#`5sOoG}?3UD494!c~C*rdh6C>RB!U=)mkp$bw4+5w|rt diff --git a/icons/clothing/accessories/jewelry/rings/ring_magic.dmi b/icons/clothing/accessories/jewelry/rings/ring_magic.dmi deleted file mode 100644 index 885a1d75314eabb0b3b3597ff705b6f45415e81b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 388 zcmV-~0ek+5P)fFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM z;w;ZhDainGjE%TBGg33tGfE(w;*!LYR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@*-G zsxnG*6Z7&jQxuZ&Qz~_fbMi~#({u96Qi~BfO;B_yE4cc(fE@$?yHG6Eb{V1S0001U zNkl2r4O%q@LN;REQ)EZlR_mS-}#PqEuc)ii=vC7^W{GL4!zy0>dm) zG>AG);8dDGf(GHBozsg*(ZFPM=6EFu>KT|sb&3?=Ja!y*O-EuYkr>gVU=)mkQ7{UI iCLpOg83m(&8UO&D@x2w zg|OkOGD>q3^YSxO6q52&Ds_u<@=M~=bMnhlixE0aP;@FQxca$(9RvWoP%PDU8KLR` z004MNL_t(YiDO_G1w$Dy3Mr7LLD*4=Bpr-l#X?OaSrBHZNs<+!$wEr27LlMq(NKv& zY7+?>nt*{MO^Sw2B>_ey0a7$fIw-Y>6b($1?%rBRf_es~4&6lxa2`7jy9ANgN~A^f qC>RB!U=)mkAqoly+5w|rXP)KiiD0Av0004WQchCV=-0C=30kj)ChFc5^#$y0>(T=Az@FVZ4C*jJE9)?}g0O0uT(?F|<6B*oh| z!^|>lsZQ;=>s52) zxCcGL!k^?sVKA#H_Z+n+H0nlXuHry^wF4!H-)8-1vx>ls-mYHAFg|`QELGb#?-#uQ zEbdwH7ZG}y0002?NklGb6Ka_k4UM(PWQ=HdaH1&QMwpkfC1~6fRE?1VIo)PP4kI^B4GcdJqIb z5dT2A%s7Dy9#`)b)_a#m+J8XjzdW!46KxJ^aLu5->Q2A&;<_Y4nTmWoP VB(k3-f!P26002ovPDHLkV1f&>;${E< literal 0 HcmV?d00001 diff --git a/icons/clothing/accessories/jewelry/rings/ring_seal_masonic.dmi b/icons/clothing/accessories/jewelry/rings/ring_seal_masonic.dmi index 12fc05d63a0cba5029ec2470fa07ec94356a9966..c2efcfd5c18659f66c862cc36f855a615b094d49 100644 GIT binary patch literal 547 zcmV+;0^I$HP)pU4&00001bW%=J06^y0W&i*H zq12Wsk-R#pdb5>cLc=bL!(IvpXdN#ivS!FwpikhL z!dysUFvRTbq__t?!or{AL}4(iDfb+;Cp79tX0GBue6<55h~H-YXS0gHjNYzZ$S^*B zEi6^rH}4m{04(lV@fQ(#nE(I){YgYYR9J=Wm`e)6Fcd`-W6`0_3ToVdHgyF_<^`L5 zIMbmsKP%83Sa%?9&Zr2n73A?iP~I5?`nf0frL!_d{1It7K;^koLzF-&r7#sjfkp+M z!V2U;kjE7ytdP`7jSvDpGFodB&A@Xtfd~q3t*fF)rzmoOpfkO#NfZ$!n*jvE-vv=b zIBx)g?n6*Tw+cH32ohU{wQ_MO4akT6T*>^$J)d7mWUWWSI&UFEx6sxa$k1;L8kZ*s zf*=T@P4;pn+b{6%^dJa=AbxxI^e4jti84 lZPw63Z2kp879fbf=L!k`B4XI{khTB-002ovPDHLkV1lPS>lOe2 literal 446 zcmV;v0YUzWP)Udx_mtA$eI%dpn6hbAT_OiWBOGc%xfMTOVOvj6}90d!JM zQvg8b*k%9#0FQc9Sad{Xb7OL8aCB*JZU6vyoKseCa&`CgQ*iP1D@x2w zg|OkOGD>q3^YSxO6q52&Ds_u<@=M~=bMnhlixE0auF4>3J3}b3Xr0n(b&XDkrWN0p~221Ss|Pp>{wn%iiWgc z2lqx2G$M7?q?75uwVCBY?$`*n*@* z^e7kwqhJ(J9qisfntJXtr%BVW=k6Vntl&QP?(G4RG@Q*PO#{Q(w6~;rg6Hm=-6UyX oICC1!(*&zf5-k`7qktd)0HU-a&uz?H$N&HU07*qoM6N<$g1?5j`Tzg` diff --git a/icons/clothing/accessories/jewelry/rings/ring_seal_secgen.dmi b/icons/clothing/accessories/jewelry/rings/ring_seal_secgen.dmi deleted file mode 100644 index bddad5ae7584ea4dc91bb65f1410d3f06e39ba1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 437 zcmV;m0ZRUfP)D@x2wg|OkOGD>q3^YSxO z6q52&Ds_u<@=M~=bMnhlixE0auKkwFT=Fc3wjH3vv4SeG?RcV*@Pk;w@PdVt=*Y}|VTPa$~9){Tgu@K;ipw@TnY z9w9`=k_k-*?JGV+$XE+E10<+!pv1FqDS)ukp4uxQ48hYD5l+!p(IdimYYQUOV=-0C=30kj)ChFc5^#$y0>(T=Az@FVZ4C*jJE9)?}g0O0uT(?F|<6B*oh| z!^|>lsZQ;=>s52) zxCcGL!k^?sVKA#H_Z+n+H0nlXuHry^wF4!H-)8-1vx>ls-mYHAFg|`QELGb#?-#uQ zEbdwH7ZG}y0002*Nklg z=+aOcP#@281M_dseB6>~vRA@7Zy`-*Xlr{&(=QAfS5FWGK@dbv^YkS15BP6#5ClOG z#0OLXDG1?=3otFX2w@2`PB70nLA6XXa0sivKye1hDF*C4R=5BJ@ntT3<{$uJ0`>y{ O0000D@x2wg|OkOGD>q3^YSxO z6q52&Ds_u<@=M~=bMnhlixE0auF5M83+m~kffeb*-(ij9m0k}LPjKM5Ed38$qHdZW1-L>5;Q0(8#0I{k)Xjy zP=G;<6b&Xy0*p!mq-by|6b&Lp1CvunYaj{g8JLVUf)wC9RvdN-BC(Z7i|A1>3P!;w z7=nOvCW#tm&YVD+1+!L>X2r}^Ggp$PVdYBFG;pq(wG!16gsKyuA4v4|Oj5&@lQauP b!H@?4h3^_z?gEeb00000NkvXXu0mjfX?d%= diff --git a/icons/clothing/accessories/jewelry/rings/ring_shadow.dmi b/icons/clothing/accessories/jewelry/rings/ring_shadow.dmi deleted file mode 100644 index 43542dbc9821a5281725fffc13a0ee5026fc4ad1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^2|({Y!Ac$FEPcymhtCojD)8A=Kca@q zTxH}{Ed2SCaj?jwPr+KH-Nm20A6+i~bULV8(8@QUL`!?eubmCDEDXB?bgq_3E_?-a zRF|iVV@O2n+v^+oS`2uYFC25{Wbyd_KcDkbua3u$C6oV5)0=vCn$Z?h!JmfA4rl+z z@b2N7{VRs|T-J&|OQWA|{>BjfJL=`TCAJJ{*B>1=_j9bVFUVQbWc7DN;{E1H@vMEy zN|Qq`ykhD#G>ZJmTlz!!y?*9*_rN-P*(?7eTtFc1>jy~%rBCX93pJVYZf5?K1Ga{R2L}iL|NjaK3Jnbn2?+@wA0HDF6A}^V=-0C=2JR&a84_w-Y6@%7{?OD!tS z%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5sXV_ZCq;>iGbOXA7|1u| z;!G<_%uR)`;i@u9a})FOGgB0j@>42xi*xcz;?r~T%TkLGI!#b?Dl549xqux60J~5u z)pi-7>Hq)$i%CR5R9J=W*Rc-5Fbo9H_)MvBi}U|~iz#cDN*of>y(LTJlO1>H7$Wjx zW&pLm-#UR8Vat7WL$DIem&Z|`)k8Pn?+HbadVu-y#u*zYAo1Cu+*koU{p@4klTyC} z0?*7UR>0^5nsZjM>JRkrXPqzU8kb7N`E`K#9gB#Fh=|BnUHlCY5&07@JHY~ZY9fTH P00000NkvXXu0mjfcM-1} diff --git a/icons/obj/items/gemstones/baguette.dmi b/icons/obj/items/gemstones/baguette.dmi index 1fecb840ccbf5ded0e5e05be39e759c59eaaae30..a10c3dc675450875d0ac8c4ad171e73b28954a2b 100644 GIT binary patch delta 41 xcmZ3^yqbA}KV$2}fMTg7D#rSAEs8l;hJ_V;EoQ71VAx~nl0KiO`s~D;=KwY85Gw!x delta 37 tcmZ3@yqtN0KV##>fMT&03*(O~L&6Ha7Bg1!Fhu=#WIpcB@Ot9ia{v$c4~YN( diff --git a/icons/obj/items/gemstones/hexagon.dmi b/icons/obj/items/gemstones/hexagon.dmi index 8b9b72ab45371ba64bd05443aa8b344dbc7aa05e..a66d147db14263db8078b5c605a276c8472ac382 100644 GIT binary patch delta 41 xcmZ3?yqtN04`b^@-(smHD#rSAEs8l;hJ_V;EoQ71VAx~nl0KiO`s~DOX8|+U5E%df delta 37 tcmZ3^yqI}{4`bs*-(s;A3*(O~L&6Ha7Bg1!Fhu=#WIpcB@Ot8{vj7eq4|f0n diff --git a/icons/obj/items/gemstones/octagon.dmi b/icons/obj/items/gemstones/octagon.dmi index 06b5d8617572be65c905bcee8a4f6b25db21fda6..c216faaadf9fa051c14eb3f587b8ffe3de44fe32 100644 GIT binary patch delta 41 xcmdnXe2{s94`b^@-(smHD#rSAEs8l;hJ_V;EoQ71VAx~nl0KiO`s~DO&jCCU5N7}Y delta 37 tcmX@eyq9@`4`bs*-(s;A3*(O~L&6Ha7Bg1!Fhu=#WIpcB@Ot8{=KvLl55)ig diff --git a/icons/obj/items/gemstones/poor.dmi b/icons/obj/items/gemstones/poor.dmi index 95fb0bb44f4e3fae2f36829be265e99c5e107d0e..97d192b3493e5eaaae4b50e3c779522c1d7a3269 100644 GIT binary patch delta 41 xcmeyz)Wkf&hp}~{Z?V)86=VIm7R8(^!@>%_7Bf~0Fzm5(NuSSCeRkrt6#zVX5Geov delta 37 tcmZo-{>L=Ihp};@Z?Ra5h4IIgAz=kyiy5nV7^40=G9Pzmcs=pf3IG)D4~GB% diff --git a/icons/obj/items/gemstones/round.dmi b/icons/obj/items/gemstones/round.dmi index 12517ea0b4f5358688b42a8bc61a44ee8f38d4e0..69bca88b994be5dcf149bf5e4105d212655323d2 100644 GIT binary patch delta 41 xcmbQpJe7HZKV$2}fMTg7D#rSAEs8l;hJ_V;EoQ71VAx~nl0KiO`s~D;I{+^959t5^ delta 37 tcmbQrJdt^VKV##>fMT&03*(O~L&6Ha7Bg1!Fhu=#WIpcB@Ot9i9RLZk4@Uq1 diff --git a/icons/obj/items/gemstones/uncut.dmi b/icons/obj/items/gemstones/uncut.dmi index 4f75bfda2a22d94a068d2a7b21af2199d41b0a45..46b30ab23434f3dceba97d63f7dedf718e285316 100644 GIT binary patch delta 42 ycmbQmGLL0~KV$2}fMTg7D#rSAEs8l;hJ_V;EoQ71VAx~nl0KiO`s~J=j*I|7q7SD4 delta 38 ucmbQoGK*z`KV##>fMT&03*(O~L&6Ha7Bg1!Fhu=#WIpcB@OtB2M@9e}ZVv?j diff --git a/maps/shaded_hills/outfits/shrine.dm b/maps/shaded_hills/outfits/shrine.dm index 1df7dd8b0a92..4f47ac02e623 100644 --- a/maps/shaded_hills/outfits/shrine.dm +++ b/maps/shaded_hills/outfits/shrine.dm @@ -11,4 +11,4 @@ /decl/outfit/job/shaded_hills/shrine/keeper name = "Shaded Hills - Shrine Keeper" suit = /obj/item/clothing/suit/mantle - mask = /obj/item/clothing/neck/necklace/prayer_beads/basalt + mask = /obj/item/clothing/neck/prayer_beads/basalt diff --git a/mods/content/dungeon_loot/subtypes/bookcase.dm b/mods/content/dungeon_loot/subtypes/bookcase.dm index fbbad10735d1..4752b9d4bbf7 100644 --- a/mods/content/dungeon_loot/subtypes/bookcase.dm +++ b/mods/content/dungeon_loot/subtypes/bookcase.dm @@ -41,7 +41,7 @@ /obj/item/paper/scroll, /obj/item/pen/fancy/quill, /obj/item/pen/fancy/quill, - /obj/item/clothing/neck/necklace/prayer_beads/random, + /obj/item/clothing/neck/prayer_beads/random, /obj/item/hourglass, ) return common_loot diff --git a/mods/content/fantasy/items/clothing/_loadout.dm b/mods/content/fantasy/items/clothing/_loadout.dm index 77dabd83a6c5..1fb533a085df 100644 --- a/mods/content/fantasy/items/clothing/_loadout.dm +++ b/mods/content/fantasy/items/clothing/_loadout.dm @@ -203,7 +203,7 @@ /decl/loadout_option/fantasy/neck/prayer_beads name = "prayer beads" - path = /obj/item/clothing/neck/necklace/prayer_beads + path = /obj/item/clothing/neck/prayer_beads available_materials = list( /decl/material/solid/organic/bone, /decl/material/solid/stone/marble, diff --git a/nebula.dme b/nebula.dme index 8210994f0172..7ee2f17e3de8 100644 --- a/nebula.dme +++ b/nebula.dme @@ -122,6 +122,7 @@ #include "code\_global_vars\sound.dm" #include "code\_global_vars\lists\clothing.dm" #include "code\_global_vars\lists\flavor.dm" +#include "code\_global_vars\lists\jewellery.dm" #include "code\_global_vars\lists\logs.dm" #include "code\_global_vars\lists\mapping.dm" #include "code\_global_vars\lists\names.dm" @@ -1016,11 +1017,8 @@ #include "code\game\objects\objs_interactions.dm" #include "code\game\objects\topic.dm" #include "code\game\objects\auras\aura.dm" -#include "code\game\objects\auras\blueforge_aura.dm" #include "code\game\objects\auras\radiant_aura.dm" #include "code\game\objects\auras\regenerating_aura.dm" -#include "code\game\objects\auras\shadowling_aura.dm" -#include "code\game\objects\auras\starlight.dm" #include "code\game\objects\auras\personal_shields\personal_shield.dm" #include "code\game\objects\compass\_compass.dm" #include "code\game\objects\compass\compass_holder.dm" @@ -1996,8 +1994,10 @@ #include "code\modules\clothing\gloves\thick.dm" #include "code\modules\clothing\gloves\jewelry\bracelet.dm" #include "code\modules\clothing\gloves\jewelry\rings\_ring.dm" -#include "code\modules\clothing\gloves\jewelry\rings\material.dm" -#include "code\modules\clothing\gloves\jewelry\rings\rings.dm" +#include "code\modules\clothing\gloves\jewelry\rings\ring_aura.dm" +#include "code\modules\clothing\gloves\jewelry\rings\ring_misc.dm" +#include "code\modules\clothing\gloves\jewelry\rings\ring_reagent.dm" +#include "code\modules\clothing\gloves\jewelry\rings\ring_seal.dm" #include "code\modules\clothing\head\_head.dm" #include "code\modules\clothing\head\collectable.dm" #include "code\modules\clothing\head\earmuffs.dm" @@ -2032,10 +2032,16 @@ #include "code\modules\clothing\neck\_neck.dm" #include "code\modules\clothing\neck\bowties.dm" #include "code\modules\clothing\neck\brace.dm" -#include "code\modules\clothing\neck\jewelry.dm" +#include "code\modules\clothing\neck\prayer_beads.dm" #include "code\modules\clothing\neck\scarf.dm" #include "code\modules\clothing\neck\stethoscope.dm" #include "code\modules\clothing\neck\ties.dm" +#include "code\modules\clothing\neck\necklace\__necklace.dm" +#include "code\modules\clothing\neck\necklace\_pendant.dm" +#include "code\modules\clothing\neck\necklace\necklaces.dm" +#include "code\modules\clothing\neck\necklace\pendant_locket.dm" +#include "code\modules\clothing\neck\necklace\pendant_random.dm" +#include "code\modules\clothing\neck\necklace\pendant_setting.dm" #include "code\modules\clothing\pants\_pants.dm" #include "code\modules\clothing\pants\detective.dm" #include "code\modules\clothing\pants\misc.dm" @@ -2244,6 +2250,9 @@ #include "code\modules\crafting\working\textiles\loom.dm" #include "code\modules\crafting\working\textiles\spinning_wheel.dm" #include "code\modules\crafting\working\textiles\twisting_bench.dm" +#include "code\modules\decoration\_decoration.dm" +#include "code\modules\decoration\decoration_item.dm" +#include "code\modules\decoration\decoration_setting.dm" #include "code\modules\departments\department.dm" #include "code\modules\detectivework\forensics.dm" #include "code\modules\detectivework\evidence\_evidence_holder.dm" From daa5d7427dd6cfca7d3f948868fa9242bf78d28a Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 12 Dec 2024 10:12:36 +1100 Subject: [PATCH 0059/1331] Added material alteration flag to grandfather clock. --- code/game/objects/structures/grandfather_clock.dm | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/code/game/objects/structures/grandfather_clock.dm b/code/game/objects/structures/grandfather_clock.dm index b1dadeb884a6..76cc0c8295d7 100644 --- a/code/game/objects/structures/grandfather_clock.dm +++ b/code/game/objects/structures/grandfather_clock.dm @@ -1,13 +1,14 @@ // TODO: buildable with artifice? // TODO: looping 2 second tick tock sound, somehow aligned with pendulum (may not be possible in DM) /obj/structure/grandfather_clock - name = "grandfather clock" - desc = "A tall, stately timepiece." - icon = 'icons/obj/structures/grandfather_clock.dmi' - icon_state = ICON_STATE_WORLD - density = TRUE - material = /decl/material/solid/organic/wood/mahogany - var/face_color = "#f0edc7" + name = "grandfather clock" + desc = "A tall, stately timepiece." + icon = 'icons/obj/structures/grandfather_clock.dmi' + icon_state = ICON_STATE_WORLD + density = TRUE + material = /decl/material/solid/organic/wood/mahogany + material_alteration = MAT_FLAG_ALTERATION_ALL + var/face_color = "#f0edc7" var/last_time var/decl/material/clockwork_mat = /decl/material/solid/metal/brass From 6662830f4cb064efabbc61fa8390a9e522da0c60 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 2 Dec 2024 20:08:47 +1100 Subject: [PATCH 0060/1331] Adding various fancy goods from Doe's icon set. --- code/game/objects/items/__item.dm | 5 +- code/modules/crafting/handmade_fancy.dm | 58 ++++++++++++++++++ code/modules/crafting/handmade_items.dm | 19 +++--- .../crafting/stack_recipes/recipes_planks.dm | 20 ++++++ code/modules/decoration/decoration_inset.dm | 14 +++++ .../solids/materials_solid_metal.dm | 1 + .../{pottery => items/handmade}/bottle.dmi | Bin .../handmade}/bottle_tall.dmi | Bin .../handmade}/bottle_wide.dmi | Bin .../obj/{pottery => items/handmade}/bowl.dmi | Bin icons/obj/items/handmade/bowl_fancy.dmi | Bin 0 -> 841 bytes icons/obj/{pottery => items/handmade}/cup.dmi | Bin icons/obj/items/handmade/cup_fancy.dmi | Bin 0 -> 804 bytes icons/obj/items/handmade/decanter.dmi | Bin 0 -> 365 bytes icons/obj/{pottery => items/handmade}/jar.dmi | Bin icons/obj/{pottery => items/handmade}/mug.dmi | Bin .../{pottery => items/handmade}/teapot.dmi | Bin .../obj/{pottery => items/handmade}/vase.dmi | Bin icons/obj/items/handmade/vase_fancy.dmi | Bin 0 -> 378 bytes .../obj/items/handmade/vase_fancy_fluted.dmi | Bin 0 -> 369 bytes nebula.dme | 2 + 21 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 code/modules/crafting/handmade_fancy.dm create mode 100644 code/modules/decoration/decoration_inset.dm rename icons/obj/{pottery => items/handmade}/bottle.dmi (100%) rename icons/obj/{pottery => items/handmade}/bottle_tall.dmi (100%) rename icons/obj/{pottery => items/handmade}/bottle_wide.dmi (100%) rename icons/obj/{pottery => items/handmade}/bowl.dmi (100%) create mode 100644 icons/obj/items/handmade/bowl_fancy.dmi rename icons/obj/{pottery => items/handmade}/cup.dmi (100%) create mode 100644 icons/obj/items/handmade/cup_fancy.dmi create mode 100644 icons/obj/items/handmade/decanter.dmi rename icons/obj/{pottery => items/handmade}/jar.dmi (100%) rename icons/obj/{pottery => items/handmade}/mug.dmi (100%) rename icons/obj/{pottery => items/handmade}/teapot.dmi (100%) rename icons/obj/{pottery => items/handmade}/vase.dmi (100%) create mode 100644 icons/obj/items/handmade/vase_fancy.dmi create mode 100644 icons/obj/items/handmade/vase_fancy_fluted.dmi diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 439b62e14b70..ce862be1c1d3 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -175,12 +175,15 @@ // This is a bit gross, but it makes writing rings and necklaces much easier. // If the decorations list is already populated at this point, we assume it's - // prebaked decorations. Only things handled appropriately at the moment are gems. + // prebaked decorations. + // Only things handled appropriately at the moment are gems and material inlays. if(length(decorations)) for(var/decoration_type in decorations) decorations -= decoration_type if(ispath(decoration_type, /obj/item/gemstone)) decorations[GET_DECL(/decl/item_decoration/setting)] = list("object" = new decoration_type(src)) + else if(ispath(decoration_type, /decl/material)) + decorations[GET_DECL(/decl/item_decoration/inset)] = list("material" = GET_DECL(decoration_type)) else PRINT_STACK_TRACE("Item [type] tried to initialize with an unsupported initial decoration type ('[decoration_type]')") . = ..() diff --git a/code/modules/crafting/handmade_fancy.dm b/code/modules/crafting/handmade_fancy.dm new file mode 100644 index 000000000000..9622296a587c --- /dev/null +++ b/code/modules/crafting/handmade_fancy.dm @@ -0,0 +1,58 @@ +/obj/item/chems/glass/handmade/fancy + abstract_type = /obj/item/chems/glass/handmade/fancy + material = /decl/material/solid/metal/silver + +/obj/item/chems/glass/handmade/fancy/get_single_monetary_worth() + . = ..() * 1.5 // Crafting value, todo proper crafting skill modifying sale price. + +/obj/item/chems/glass/handmade/fancy/decanter + name = "decanter" + desc = "A masterfully made decanter with a fluted neck and graceful handle." + icon = 'icons/obj/items/handmade/decanter.dmi' + amount_per_transfer_from_this = 10 + volume = 120 + obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE + +/obj/item/chems/glass/handmade/fancy/goblet + name = "goblet" + desc = "An elegant goblet with a flared base, likely handmade by some master artisan." + icon = 'icons/obj/items/handmade/cup_fancy.dmi' + amount_per_transfer_from_this = 10 + volume = 60 + +/obj/item/chems/glass/handmade/fancy/bowl + name = "bowl" + desc = "A sleek, polished bowl, likely handmade by some master artisan." + icon = 'icons/obj/items/handmade/bowl_fancy.dmi' + amount_per_transfer_from_this = 10 + volume = 60 + +/obj/item/chems/glass/handmade/fancy/vase + name = "vase" + desc = "An elegant masterwork vase." + icon = 'icons/obj/items/handmade/vase_fancy.dmi' + amount_per_transfer_from_this = 20 + volume = 240 + material = /decl/material/solid/stone/ceramic + +/obj/item/chems/glass/handmade/fancy/vase/fluted + desc = "An elegant masterwork vase with a fluted neck." + icon = 'icons/obj/items/handmade/vase_fancy_fluted.dmi' + +/obj/item/chems/glass/handmade/fancy/vase/fluted/update_name() + . = ..() + SetName("fluted [name]") + +// Decorated subtypes for mapping/ +/obj/item/chems/glass/handmade/fancy/vase/mapped + decorations = list(/decl/material/solid/organic/bone) + +/obj/item/chems/glass/handmade/fancy/vase/fluted/mapped + decorations = list(/decl/material/solid/organic/bone) + +/obj/item/chems/glass/handmade/fancy/goblet/mapped + material = /decl/material/solid/metal/gold + decorations = list(/obj/item/gemstone/octagon/ruby) + +/obj/item/chems/glass/handmade/fancy/bowl/mapped + decorations = list(/obj/item/gemstone/octagon/sapphire) diff --git a/code/modules/crafting/handmade_items.dm b/code/modules/crafting/handmade_items.dm index 4c294d38b78d..5aa1ed2f1cae 100644 --- a/code/modules/crafting/handmade_items.dm +++ b/code/modules/crafting/handmade_items.dm @@ -12,7 +12,7 @@ /obj/item/chems/glass/handmade/teapot name = "teapot" desc = "A handmade, slightly lumpy teapot." - icon = 'icons/obj/pottery/teapot.dmi' + icon = 'icons/obj/items/handmade/teapot.dmi' amount_per_transfer_from_this = 10 volume = 120 obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE @@ -20,14 +20,14 @@ /obj/item/chems/glass/handmade/cup name = "cup" desc = "A handmade, slightly lumpy cup." - icon = 'icons/obj/pottery/cup.dmi' + icon = 'icons/obj/items/handmade/cup.dmi' amount_per_transfer_from_this = 10 volume = 30 /obj/item/chems/glass/handmade/mug name = "mug" desc = "A handmade, slightly lumpy mug." - icon = 'icons/obj/pottery/mug.dmi' + icon = 'icons/obj/items/handmade/mug.dmi' amount_per_transfer_from_this = 10 volume = 60 obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE @@ -35,36 +35,36 @@ /obj/item/chems/glass/handmade/vase name = "vase" desc = "A handmade, slightly lumpy vase." - icon = 'icons/obj/pottery/vase.dmi' + icon = 'icons/obj/items/handmade/vase.dmi' amount_per_transfer_from_this = 20 volume = 240 /obj/item/chems/glass/handmade/jar name = "jar" desc = "A handmade, slightly lumpy jar." - icon = 'icons/obj/pottery/jar.dmi' + icon = 'icons/obj/items/handmade/jar.dmi' amount_per_transfer_from_this = 10 volume = 60 /obj/item/chems/glass/handmade/bottle name = "bottle" desc = "A handmade, slightly lumpy bottle." - icon = 'icons/obj/pottery/bottle.dmi' + icon = 'icons/obj/items/handmade/bottle.dmi' amount_per_transfer_from_this = 10 volume = 120 /obj/item/chems/glass/handmade/bottle/tall name = "tall bottle" - icon = 'icons/obj/pottery/bottle_tall.dmi' + icon = 'icons/obj/items/handmade/bottle_tall.dmi' /obj/item/chems/glass/handmade/bottle/wide name = "wide bottle" - icon = 'icons/obj/pottery/bottle_wide.dmi' + icon = 'icons/obj/items/handmade/bottle_wide.dmi' /obj/item/chems/glass/handmade/bowl name = "bowl" desc = "A handmade, slightly lumpy bowl." - icon = 'icons/obj/pottery/bowl.dmi' + icon = 'icons/obj/items/handmade/bowl.dmi' amount_per_transfer_from_this = 10 volume = 60 @@ -77,7 +77,6 @@ /obj/item/chems/glass/handmade/bowl/wood material = /decl/material/solid/organic/wood/oak - /obj/item/chems/glass/handmade/bottle/beer/populate_reagents() . = ..() add_to_reagents(/decl/material/liquid/ethanol/beer, reagents.maximum_volume) diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index f58f931cf5bb..909f022edc67 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -78,6 +78,26 @@ /decl/stack_recipe/planks/bowl result_type = /obj/item/chems/glass/handmade/bowl +/decl/stack_recipe/planks/fancy + abstract_type = /decl/stack_recipe/planks/fancy + difficulty = MAT_VALUE_VERY_HARD_DIY + +/decl/stack_recipe/planks/fancy/decanter + result_type = /obj/item/chems/glass/handmade/fancy/decanter + +/decl/stack_recipe/planks/fancy/goblet + result_type = /obj/item/chems/glass/handmade/fancy/goblet + +/decl/stack_recipe/planks/fancy/bowl + result_type = /obj/item/chems/glass/handmade/fancy/bowl + +/decl/stack_recipe/planks/fancy/vase + result_type = /obj/item/chems/glass/handmade/fancy/vase + +/decl/stack_recipe/planks/fancy/vase_fluted + name = "vase, fluted" + result_type = /obj/item/chems/glass/handmade/fancy/vase/fluted + /decl/stack_recipe/planks/noticeboard/spawn_result(mob/user, location, amount, decl/material/mat, decl/material/reinf_mat, paint_color, spent_type, spent_amount = 1) . = ..() if(user) diff --git a/code/modules/decoration/decoration_inset.dm b/code/modules/decoration/decoration_inset.dm new file mode 100644 index 000000000000..9ca503ac8e6b --- /dev/null +++ b/code/modules/decoration/decoration_inset.dm @@ -0,0 +1,14 @@ +// Sort of a placeholder for future generalised decorations. +/decl/item_decoration/inset + name = "inlay material" + icon_state_modifier = "-inlay" + can_decorate_types = list( + /obj/item/chems/glass/handmade/fancy/vase + ) + can_decorate_with_types = list( + /obj/item/stack/material/plank = 1, + /obj/item/stack/material/bone = 1, + /obj/item/stack/material/ingot = 1, + /obj/item/stack/material/sheet = 1, + /obj/item/stack/material/brick = 1 + ) diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index d5a796870133..ee7d435e09da 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -78,6 +78,7 @@ /decl/material/solid/metal/gold name = "gold" + adjective_name = "golden" codex_name = "elemental gold" uid = "solid_gold" lore_text = "A heavy, soft, ductile metal. Once considered valuable enough to back entire currencies, now predominantly used in corrosion-resistant electronics." diff --git a/icons/obj/pottery/bottle.dmi b/icons/obj/items/handmade/bottle.dmi similarity index 100% rename from icons/obj/pottery/bottle.dmi rename to icons/obj/items/handmade/bottle.dmi diff --git a/icons/obj/pottery/bottle_tall.dmi b/icons/obj/items/handmade/bottle_tall.dmi similarity index 100% rename from icons/obj/pottery/bottle_tall.dmi rename to icons/obj/items/handmade/bottle_tall.dmi diff --git a/icons/obj/pottery/bottle_wide.dmi b/icons/obj/items/handmade/bottle_wide.dmi similarity index 100% rename from icons/obj/pottery/bottle_wide.dmi rename to icons/obj/items/handmade/bottle_wide.dmi diff --git a/icons/obj/pottery/bowl.dmi b/icons/obj/items/handmade/bowl.dmi similarity index 100% rename from icons/obj/pottery/bowl.dmi rename to icons/obj/items/handmade/bowl.dmi diff --git a/icons/obj/items/handmade/bowl_fancy.dmi b/icons/obj/items/handmade/bowl_fancy.dmi new file mode 100644 index 0000000000000000000000000000000000000000..af43c58133af56788c5f968f3320681a9bda78c3 GIT binary patch literal 841 zcmV-P1GfB$P)fFDZ*Bkpc$}S+v2Fq}42I{+Q$#RRwX|y&1gaRyE1XQ? zUaTgUlkI?bdm?V?Q0c+Tt0z)|}%2tI-IE|l^2wvd!u zramF)8Df+c$d@$5>Z%EP%(j$Za@ev3fe5}GXqst8Qjx$pr#A03rPN~G73QTmdUTmV z>jhUo6zLf(Pom`hU&Y!UPW#8R+yS$80N<-?K%D>p0pdwSK~!jg?U+$Y!$1&*cO}bK zMCfAU`r3*YP!Eu3#PltJ)U*fiJqv~U*g_AWP%q&rJb;21@Gho-COc`fTdfZw^Cd4c z`_JrbUS>9gAjp4$_PV>ppI%(-l;P+)r`^2H?sE+G-sO_><^*@gIZ?CCPJe4pu_`frjZA3>c(Ez&BwYH}`WM z4vp&wf*{CWA+k%1FlN_SD)?y$tGhI#Mp=n7t$S7zLwl>yArx!Bbysn8{olZbc=y(= zxvH5Z%ZLp+QweO(gje@_ZZARR1$a&I+mz5z!&(7KKasrxI;($yfi(s}kblM(M&L!b Td_Poo00000NkvXXu0mjf4nc-s literal 0 HcmV?d00001 diff --git a/icons/obj/pottery/cup.dmi b/icons/obj/items/handmade/cup.dmi similarity index 100% rename from icons/obj/pottery/cup.dmi rename to icons/obj/items/handmade/cup.dmi diff --git a/icons/obj/items/handmade/cup_fancy.dmi b/icons/obj/items/handmade/cup_fancy.dmi new file mode 100644 index 0000000000000000000000000000000000000000..02f64ffe84fa1db979b185240433c12558de9bf7 GIT binary patch literal 804 zcmV+<1Ka$GP)fFDZ*Bkpc$}S+v2Fq}42I{+Q$#RRwX|y&1gaRyE1XQ? zUaTgUlkI?bdm?V?Q0c+Tt0z)|}%2tI-IE|l^2wvd!u zramF)8Df+c$d@$5>Z%EP%(j$Za@ev3fe5}GXqst8Qjx$pr#A03rPN~G73QTmdUTmV z>jhUo6zLf(Pom`hU&Y!UPW#8R+yS$80N<-?K%D>p0li5?K~!jg?U*r7!ax+qFNbZ3 zaS)ARZA&>ARfeuE?6uIA@p40PL=4iAu=pAL7VbLu0sK%B6Y<`GSI))+lJ^VC|J{4< z{u~LfS3(eEpJ<#`tM__WZlwn2y`I*>(QpWL8URhhFuc|^==b|KI!(j*T-zWB0$0|< zAc$n`G_$O!YaoQ+vi8LiIB>k=wxercGX}KPhPswCE(UTS`uZ3!P2vgM!l;~((C=J= z05z+3TG>E*24&Q|*?p@@1ILT?zd|X~#@VDiOT)$?8uL)i0D>UMK4F`s(hw6$6MOCo|G2!i|% zX7|o51!G0qTwCt2r4msuacLXKXJz^BX(U^yvuFV<=>?pz-(d{Bci7$Sw|2AK@J7uIjdlqzTy3buO0l+!uqai z8NMUlXh2+dhjd4MKp^6$)edhLoMoogOWSf@+Mvte69^Hqwo5Hgk-<%D9~^zD1^TEM iuoBp>%mIQRhtDVR`#X{k*EswD0000$J%i{n500DGTPE!Ct=GbNc004D*R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRp}O|mXu`XrQ=eqtl;YB0(Jxde#R&y z8}buNqe*ftd_GU%Ial?KrZ=!0KKu`pD*MCk<<*x$#ymD00000 LNkvXXu0mjfW2Kvp literal 0 HcmV?d00001 diff --git a/icons/obj/pottery/jar.dmi b/icons/obj/items/handmade/jar.dmi similarity index 100% rename from icons/obj/pottery/jar.dmi rename to icons/obj/items/handmade/jar.dmi diff --git a/icons/obj/pottery/mug.dmi b/icons/obj/items/handmade/mug.dmi similarity index 100% rename from icons/obj/pottery/mug.dmi rename to icons/obj/items/handmade/mug.dmi diff --git a/icons/obj/pottery/teapot.dmi b/icons/obj/items/handmade/teapot.dmi similarity index 100% rename from icons/obj/pottery/teapot.dmi rename to icons/obj/items/handmade/teapot.dmi diff --git a/icons/obj/pottery/vase.dmi b/icons/obj/items/handmade/vase.dmi similarity index 100% rename from icons/obj/pottery/vase.dmi rename to icons/obj/items/handmade/vase.dmi diff --git a/icons/obj/items/handmade/vase_fancy.dmi b/icons/obj/items/handmade/vase_fancy.dmi new file mode 100644 index 0000000000000000000000000000000000000000..8a66723be8375ec6377ec2777fded457b48bfc60 GIT binary patch literal 378 zcmV-=0fqjFP)$J%i{n500DGTPE!Ct=GbNc0047(R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq1BtgNJ>0stVtCqun~ z!4m)g0GCNbK~y-6V_+CUKu7?;Jfn#reg#aRN%M*$rG Y0MKj`JJw&Ip#T5?07*qoM6N<$g1i@y7XSbN literal 0 HcmV?d00001 diff --git a/icons/obj/items/handmade/vase_fancy_fluted.dmi b/icons/obj/items/handmade/vase_fancy_fluted.dmi new file mode 100644 index 0000000000000000000000000000000000000000..af3a2270a7661d15ea6aecd22fe958c9fb15b23f GIT binary patch literal 369 zcmV-%0gnEOP)$J%i{n500DGTPE!Ct=GbNc0047(R9JLGWpiV4X>fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRq1BtgNJ>0stVtCqun~ z!4m)g0FFsSK~y-6?T|4JgD?z5e*)brsCN>H9ZK0txj-G~{%2`Mdr60?0}?OOkAI$} z00*MG*5~dq*9(4s{b*>rTfNY**j$cbr~YXQ)Y}J{vsN@MXQQ}t3~5psaquc*8;OG2 z;RYaR!0ml%;fm&%@ P00000NkvXXu0mjfH4d7= literal 0 HcmV?d00001 diff --git a/nebula.dme b/nebula.dme index 7ee2f17e3de8..c8ff24af4479 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2203,6 +2203,7 @@ #include "code\modules\codex\entries\tools.dm" #include "code\modules\codex\entries\turfs.dm" #include "code\modules\codex\entries\weapons.dm" +#include "code\modules\crafting\handmade_fancy.dm" #include "code\modules\crafting\handmade_items.dm" #include "code\modules\crafting\metalwork\metalwork_items.dm" #include "code\modules\crafting\pottery\pottery_moulds.dm" @@ -2251,6 +2252,7 @@ #include "code\modules\crafting\working\textiles\spinning_wheel.dm" #include "code\modules\crafting\working\textiles\twisting_bench.dm" #include "code\modules\decoration\_decoration.dm" +#include "code\modules\decoration\decoration_inset.dm" #include "code\modules\decoration\decoration_item.dm" #include "code\modules\decoration\decoration_setting.dm" #include "code\modules\departments\department.dm" From 8dd039a47c959d145b40a6fdced8bd1656d0631a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 5 Dec 2024 18:52:26 -0500 Subject: [PATCH 0061/1331] Remove remaining hooks --- code/__globals.dm | 8 +- code/_helpers/global_lists.dm | 2 +- code/_helpers/time.dm | 8 -- code/controllers/communications.dm | 6 +- code/controllers/evacuation/evacuation.dm | 2 - code/controllers/hooks-defs.dm | 35 --------- code/controllers/hooks.dm | 39 ---------- .../subsystems/initialization/webhooks.dm | 7 +- code/controllers/subsystems/lighting.dm | 9 +-- code/controllers/subsystems/mapping.dm | 4 - code/controllers/subsystems/ticker.dm | 13 +++- .../config/config_types/config_server.dm | 27 ------- code/datums/mil_ranks.dm | 3 +- code/datums/repositories/admin_pm.dm | 44 ----------- code/datums/traits/_traits.dm | 8 +- code/game/gamemodes/game_mode.dm | 1 - code/game/jobs/server_whitelist.dm | 2 +- code/game/objects/random/subtypes/multi.dm | 4 - code/game/verbs/who.dm | 4 +- code/game/world.dm | 75 +++++++++---------- code/modules/admin/NewBan.dm | 3 - code/modules/admin/admin.dm | 4 +- code/modules/admin/admin_investigate.dm | 4 - code/modules/admin/admin_ranks.dm | 4 - code/modules/admin/banjob.dm | 4 - .../admin/secrets/investigation/admin_pms.dm | 40 ---------- code/modules/admin/ticket.dm | 2 - code/modules/admin/verbs/adminhelp.dm | 4 - code/modules/admin/verbs/adminpm.dm | 29 ------- code/modules/chat_filter/_chat_filter.dm | 2 +- code/modules/client/client_procs.dm | 18 +---- code/modules/error_handler/error_reporting.dm | 23 ------ code/modules/ext_scripts/irc.dm | 55 -------------- code/modules/holidays/holiday_hook.dm | 5 -- .../modules/mob/living/silicon/pai/recruit.dm | 12 ++- .../mob/living/silicon/pai/software.dm | 9 +-- code/modules/mob/new_player/new_player.dm | 4 - code/modules/supermatter/supermatter.dm | 6 +- maps/~mapsystem/maps.dm | 2 +- mods/_modpack.dm | 8 +- mods/content/matchmaking/matchmaker.dm | 32 ++++---- nebula.dme | 6 -- ~code/global_init.dm | 3 +- 43 files changed, 105 insertions(+), 475 deletions(-) delete mode 100644 code/controllers/hooks-defs.dm delete mode 100644 code/controllers/hooks.dm delete mode 100644 code/datums/repositories/admin_pm.dm delete mode 100644 code/modules/admin/secrets/investigation/admin_pms.dm delete mode 100644 code/modules/error_handler/error_reporting.dm delete mode 100644 code/modules/ext_scripts/irc.dm diff --git a/code/__globals.dm b/code/__globals.dm index 12434ad8011c..7c9e5ed5dbea 100644 --- a/code/__globals.dm +++ b/code/__globals.dm @@ -1,15 +1,15 @@ // Defined here due to being used immediately below. #define GET_DECL(D) (ispath(D, /decl) ? (decls_repository.fetched_decls[D] || decls_repository.get_decl(D)) : null) +#define IMPLIED_DECL GET_DECL(__IMPLIED_TYPE__) // Defined here due to compile order; overrides in macros make the compiler complain. /decl/global_vars var/static/list/protected_vars = list("protected_vars") // No editing the protected list! +/decl/global_vars/Initialize() + . = ..() + mark_protected_vars() /decl/global_vars/proc/mark_protected_vars() return -/hook/startup/proc/mark_protected_vars() - var/decl/global_vars/global_vars = GET_DECL(/decl/global_vars) - global_vars.mark_protected_vars() - return TRUE #define GLOBAL_GETTER(NAME, TYPE, VAL) \ var/global##TYPE/##NAME; \ diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index f3379f84c524..84463762e677 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -62,7 +62,7 @@ var/global/list/string_slot_flags = list( . = new /mob/living/human/dummy/mannequin() mannequins_[ckey] = . -/hook/global_init/proc/makeDatumRefLists() +/proc/makeDatumRefLists() // Keybindings for(var/KB in subtypesof(/datum/keybinding)) var/datum/keybinding/keybinding = KB diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index eea2b9b3833f..a59873ee3308 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -72,10 +72,6 @@ var/global/next_duration_update = 0 var/global/last_round_duration = 0 var/global/round_start_time = 0 -/hook/roundstart/proc/start_timer() - round_start_time = world.time - return 1 - /proc/ticks2readable(tick_time) var/hours = round(tick_time / (1 HOUR)) var/minutes = round((tick_time % (1 HOUR)) / (1 MINUTE)) @@ -109,10 +105,6 @@ var/global/round_start_time = 0 next_duration_update = world.time + 1 MINUTES return last_round_duration -/hook/startup/proc/set_roundstart_hour() - roundstart_hour = rand(0, 23) - return TRUE - var/global/midnight_rollovers = 0 var/global/rollovercheck_last_timeofday = 0 /proc/update_midnight_rollover() diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm index 4e599b93113c..d91f03f89eed 100644 --- a/code/controllers/communications.dm +++ b/code/controllers/communications.dm @@ -123,11 +123,7 @@ var/global/list/all_selectable_radio_filters = list( RADIO_MAGNETS ) -var/global/datum/controller/radio/radio_controller - -/hook/startup/proc/createRadioController() - radio_controller = new /datum/controller/radio() - return 1 +var/global/datum/controller/radio/radio_controller = new /datum/controller/radio() //callback used by objects to react to incoming radio signals /obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) diff --git a/code/controllers/evacuation/evacuation.dm b/code/controllers/evacuation/evacuation.dm index 69eeb9bc5bce..90849e6e1935 100644 --- a/code/controllers/evacuation/evacuation.dm +++ b/code/controllers/evacuation/evacuation.dm @@ -131,8 +131,6 @@ evac_waiting.Announce(replacetext(global.using_map.emergency_shuttle_docked_message, "%ETD%", "[estimated_time] minute\s"), new_sound = sound('sound/effects/Evacuation.ogg', volume = 35)) else priority_announcement.Announce(replacetext(replacetext(global.using_map.shuttle_docked_message, "%dock_name%", "[global.using_map.dock_name]"), "%ETD%", "[estimated_time] minute\s")) - if(get_config_value(/decl/config/toggle/announce_shuttle_dock_to_irc)) - send2mainirc("The shuttle has docked with the station. It will depart in approximately [estimated_time] minute\s.") /datum/evacuation_controller/proc/launch_evacuation() diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm deleted file mode 100644 index 696352c85eb0..000000000000 --- a/code/controllers/hooks-defs.dm +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Global init hook. - * Called in global_init.dm when the server is initialized. - */ -/hook/global_init - -/** - * Startup hook. - * Called in world.dm when the server starts. - */ -/hook/startup - -/** - * Roundstart hook. - * Called in ticker.dm when a round starts. - */ -/hook/roundstart - -/** - * Roundend hook. - * Called in ticker.dm when a round ends. - */ -/hook/roundend - -/** - * Shutdown hook. - * Called in world.dm when world/Del is called. - */ -/hook/shutdown - -/** - * Reboot hook. - * Called in world.dm prior to the parent call in world/Reboot. - */ -/hook/reboot diff --git a/code/controllers/hooks.dm b/code/controllers/hooks.dm deleted file mode 100644 index 2e05a0621b0f..000000000000 --- a/code/controllers/hooks.dm +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file hooks.dm - * Implements hooks, a simple way to run code on pre-defined events. - */ - -/** @page hooks Code hooks - * @section hooks Hooks - * A hook is defined under /hook in the type tree. - * - * To add some code to be called by the hook, define a proc under the type, as so: - * @code - hook/foo/proc/bar() - if(1) - return 1 //Sucessful - else - return 0 //Error, or runtime. - * @endcode - * All hooks must return nonzero on success, as runtimes will force return null. - */ - -/** - * Calls a hook, executing every piece of code that's attached to it. - * @param hook Identifier of the hook to call. - * @returns 1 if all hooked code runs successfully, 0 otherwise. - */ -/proc/callHook(hook, list/args=null) - var/hook_path = text2path("/hook/[hook]") - if(!hook_path) - error("Invalid hook '/hook/[hook]' called.") - return 0 - - var/caller = new hook_path - var/status = 1 - for(var/P in typesof("[hook_path]/proc")) - if(!call(caller, P)(arglist(args))) - error("Hook '[P]' failed or runtimed.") - status = 0 - - return status diff --git a/code/controllers/subsystems/initialization/webhooks.dm b/code/controllers/subsystems/initialization/webhooks.dm index 14c560979d41..c773998c9ba5 100644 --- a/code/controllers/subsystems/initialization/webhooks.dm +++ b/code/controllers/subsystems/initialization/webhooks.dm @@ -46,6 +46,9 @@ SUBSYSTEM_DEF(webhooks) else to_world_log("Failed to set up webhook [wid].") +/datum/controller/subsystem/webhooks/proc/is_webhook_configured(wid) + return isnull(webhook_decls[wid]) + /datum/controller/subsystem/webhooks/proc/send(var/wid, var/wdata) var/decl/webhook/webhook = webhook_decls[wid] if(webhook) @@ -88,7 +91,3 @@ SUBSYSTEM_DEF(webhooks) log_and_message_admins("has pinged webhook [choice].", usr) to_world_log("[usr.key] has pinged webhook [choice].") webhook.send() - -/hook/roundstart/proc/run_webhook() - SSwebhooks.send(WEBHOOK_ROUNDSTART, list("url" = get_world_url())) - return 1 diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm index daf0a7a44df8..a7583ae1b2ff 100644 --- a/code/controllers/subsystems/lighting.dm +++ b/code/controllers/subsystems/lighting.dm @@ -40,17 +40,12 @@ SUBSYSTEM_DEF(lighting) ) ..(out.Join()) -#ifdef USE_INTELLIGENT_LIGHTING_UPDATES - -/hook/roundstart/proc/lighting_init_roundstart() - SSlighting.handle_roundstart() - return TRUE - +// If intelligent updates are off, this is just an empty stub. /datum/controller/subsystem/lighting/proc/handle_roundstart() +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES force_queued = FALSE total_ss_updates = 0 total_instant_updates = 0 - #endif /datum/controller/subsystem/lighting/Initialize(timeofday) diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index ecc1ad88b1ed..6e1061636ec6 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -426,7 +426,3 @@ SUBSYSTEM_DEF(mapping) if(!P) continue P.begin_processing() - -/hook/roundstart/proc/start_processing_all_planets() - SSmapping.start_processing_all_planets() - return TRUE diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 4855a9a86c5e..6bccadcb70a8 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -89,7 +89,16 @@ SUBSYSTEM_DEF(ticker) if(job && job.create_record) CreateModularRecord(H) - callHook("roundstart") + // Initialize the roundstart timer + global.round_start_time = world.time + generate_multi_spawn_items() + SSlighting.handle_roundstart() + SSmapping.start_processing_all_planets() + SSwebhooks.send(WEBHOOK_ROUNDSTART, list("url" = get_world_url())) + global.using_map.refresh_lobby_browsers() + for(var/modpack_name in SSmodpacks.loaded_modpacks) + var/decl/modpack/loaded_modpack = SSmodpacks.loaded_modpacks[modpack_name] + loaded_modpack.on_roundstart() spawn(0)//Forking here so we dont have to wait for this to finish mode.post_setup() // Drafts antags who don't override jobs. @@ -102,7 +111,6 @@ SUBSYSTEM_DEF(ticker) global.current_holiday.set_up_holiday() if(!length(global.admins)) - send2adminirc("Round has started with no admins online.") SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Round Started (Game ID: [game_id])", "body" = "Round has started with no admins online.")) /datum/controller/subsystem/ticker/proc/playing_tick() @@ -128,7 +136,6 @@ SUBSYSTEM_DEF(ticker) return if(END_GAME_READY_TO_END) end_game_state = END_GAME_ENDING - callHook("roundend") if (universe_has_ended) if(mode.station_was_nuked) SSstatistics.set_field_details("end_proper","nuke") diff --git a/code/datums/config/config_types/config_server.dm b/code/datums/config/config_types/config_server.dm index 472395682352..4365bbde4bb3 100644 --- a/code/datums/config/config_types/config_server.dm +++ b/code/datums/config/config_types/config_server.dm @@ -16,9 +16,6 @@ /decl/config/num/drone_build_time, /decl/config/num/max_character_traits, /decl/config/num/max_alternate_languages, - /decl/config/text/irc_bot_host, - /decl/config/text/main_irc, - /decl/config/text/admin_irc, /decl/config/text/server_name, /decl/config/text/server, /decl/config/text/serverurl, @@ -54,9 +51,7 @@ /decl/config/toggle/disable_webhook_embeds, /decl/config/toggle/delist_when_no_admins, /decl/config/toggle/wait_for_sigusr1_reboot, - /decl/config/toggle/use_irc_bot, /decl/config/toggle/show_typing_indicator_for_whispers, - /decl/config/toggle/announce_shuttle_dock_to_irc, /decl/config/toggle/guests_allowed, /decl/config/toggle/on/jobs_have_minimal_access, /decl/config/toggle/on/admin_legacy_system, @@ -170,20 +165,6 @@ default_value = 3 desc = "Remove the # to define a different maximum for alternate language selection in chargen." -/decl/config/text/irc_bot_host - uid = "irc_bot_host" - default_value = "localhost" - desc = "Host where the IRC bot is hosted. Port 45678 needs to be open." - -/decl/config/text/main_irc - uid = "main_irc" - default_value = "#main" - desc = "IRC channel to send information to. Leave blank to disable." - -/decl/config/text/admin_irc - uid = "admin_irc" - desc = "IRC channel to send adminhelps to. Leave blank to disable adminhelps-to-irc." - // server name (for world name / status) /decl/config/text/server_name uid = "server_name" @@ -335,18 +316,10 @@ uid = "wait_for_sigusr1_reboot" desc = "Determines if Dream Daemon should refuse to reboot for any reason other than SIGUSR1." -/decl/config/toggle/use_irc_bot - uid = "use_irc_bot" - desc = "Determines if data is sent to the IRC bot. Generally requires MAIN_IRC and associated setup." - /decl/config/toggle/show_typing_indicator_for_whispers uid = "show_typing_indicator_for_whispers" desc = "Determinese if a typing indicator shows overhead for people currently writing whispers." -/decl/config/toggle/announce_shuttle_dock_to_irc - uid = "announce_shuttle_dock_to_irc" - desc = "Determines if announce shuttle dock announcements are sent to the main IRC channel, if MAIN_IRC has also been setup." - /decl/config/toggle/guests_allowed uid = "guests_allowed" desc = "Determines whether or not people without a registered ckey (i.e. guest-*) can connect to your server." diff --git a/code/datums/mil_ranks.dm b/code/datums/mil_ranks.dm index 23a0b69cb662..136559c4aa7c 100644 --- a/code/datums/mil_ranks.dm +++ b/code/datums/mil_ranks.dm @@ -143,10 +143,11 @@ var/global/datum/mil_branches/mil_branches = new() . += spawn_rank +// todo: should this be on /datum/map? this will need heavy reworking if we promote submaps from second to first class map status anyway /** * Populate the global branches list from global.using_map */ -/hook/startup/proc/populate_branches() +/proc/populate_branches() if(!(global.using_map.flags & MAP_HAS_BRANCH) && !(global.using_map.flags & MAP_HAS_RANK)) mil_branches.branches = null mil_branches.spawn_branches_ = null diff --git a/code/datums/repositories/admin_pm.dm b/code/datums/repositories/admin_pm.dm deleted file mode 100644 index 03a48de19abb..000000000000 --- a/code/datums/repositories/admin_pm.dm +++ /dev/null @@ -1,44 +0,0 @@ -var/global/repository/admin_pm/admin_pm_repository = new() - -/repository/admin_pm - var/list/admin_pms_ - var/list/irc_clients_by_name - -/repository/admin_pm/New() - ..() - admin_pms_ = list() - irc_clients_by_name = list() - -/repository/admin_pm/proc/store_pm(var/client/sender, var/client/receiver, var/message) - if(receiver) - if(istype(receiver)) - receiver = client_repository.get_lite_client(receiver) - else if(starts_with(receiver, "IRC-")) - receiver = get_irc_client(receiver) - else - CRASH("Invalid receiver: [log_info_line(receiver)]") - - // Newest messages first - admin_pms_.Insert(1, new/datum/admin_privat_message(client_repository.get_lite_client(sender), receiver, message)) - -/repository/admin_pm/proc/get_irc_client(key) - var/datum/client_lite/cl = irc_clients_by_name[key] - if(!cl) - cl = new/datum/client_lite() - cl.name = "IRC" - cl.key = key - irc_clients_by_name[key] = cl - return cl - -/datum/admin_privat_message - var/station_time - var/datum/client_lite/sender // We don't store the proper client because it gets deleted if banned - var/datum/client_lite/receiver - var/message - -/datum/admin_privat_message/New(var/sender, var/receiver, var/message) - station_time = time_stamp() - src.message = message - src.sender = sender - src.receiver = receiver - diff --git a/code/datums/traits/_traits.dm b/code/datums/traits/_traits.dm index ad1041e32da2..7c188e2b5260 100644 --- a/code/datums/traits/_traits.dm +++ b/code/datums/traits/_traits.dm @@ -2,12 +2,6 @@ // Selectable traits are basically skills + stats + feats all rolled into one. You get to choose a // certain number of them at character generation and they will alter some interactions with the world. -/hook/startup/proc/initialize_trait_trees() - // Precache/build trait trees. - for(var/decl/trait/trait in decls_repository.get_decls_of_type_unassociated(/decl/trait)) - trait.build_references() - return 1 - /mob/living var/list/traits @@ -170,7 +164,7 @@ if(ispath(parent)) parent = GET_DECL(parent) - if(abstract_type != type && category) + if(category) var/datum/trait_category/trait_category = global.trait_categories[category] if(!istype(trait_category)) trait_category = new(category) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 9a95ba9802fd..9bfdd9791429 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -410,7 +410,6 @@ var/global/list/additional_antag_types = list() if(escaped_total > 0) SSstatistics.set_field("escaped_total",escaped_total) - send2mainirc("A round of [src.name] has ended - [surviving_total] survivor\s, [ghosts] ghost\s.") SSwebhooks.send(WEBHOOK_ROUNDEND, list("survivors" = surviving_total, "escaped" = escaped_total, "ghosts" = ghosts, "clients" = clients)) return 0 diff --git a/code/game/jobs/server_whitelist.dm b/code/game/jobs/server_whitelist.dm index 083876ecad32..9c8b84f46b57 100644 --- a/code/game/jobs/server_whitelist.dm +++ b/code/game/jobs/server_whitelist.dm @@ -31,7 +31,7 @@ var/global/list/server_whitelist to_file(write_file, jointext(global.server_whitelist, "\n")) var/global/list/alien_whitelist = list() -/hook/startup/proc/loadAlienWhitelist() +/proc/try_load_alien_whitelist() if(get_config_value(/decl/config/toggle/use_alien_whitelist)) if(get_config_value(/decl/config/toggle/use_alien_whitelist_sql)) if(!load_alienwhitelistSQL()) diff --git a/code/game/objects/random/subtypes/multi.dm b/code/game/objects/random/subtypes/multi.dm index 34cd5d7077ef..a0268faecaeb 100644 --- a/code/game/objects/random/subtypes/multi.dm +++ b/code/game/objects/random/subtypes/multi.dm @@ -1,7 +1,3 @@ -/hook/roundstart/proc/roundstart_multi_spawn() - generate_multi_spawn_items() - return TRUE - /proc/generate_multi_spawn_items() for(var/id in multi_point_spawns) var/list/spawn_points = multi_point_spawns[id] diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm index 4131ea677aef..dacae0436607 100644 --- a/code/game/verbs/who.dm +++ b/code/game/verbs/who.dm @@ -104,7 +104,7 @@ else msg += line - if(get_config_value(/decl/config/text/admin_irc)) - to_chat(src, "Adminhelps are also sent to IRC. If no admins are available in game try anyway and an admin on IRC may see it and respond.") + if(SSwebhooks.is_webhook_configured(WEBHOOK_AHELP_SENT)) + to_chat(src, "Adminhelps are also sent to Discord. If no admins are available in game try anyway and an admin on Discord may see it and join.") to_chat(src, "Current Staff ([active_staff]/[total_staff]):") to_chat(src, jointext(msg,"\n")) diff --git a/code/game/world.dm b/code/game/world.dm index 420c09757f54..079e43ae5439 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -1,6 +1,6 @@ GLOBAL_PROTECTED_UNTYPED(game_id, null) -/hook/global_init/proc/generate_game_id() +/proc/generate_game_id() if(!isnull(global.game_id)) return @@ -70,6 +70,10 @@ GLOBAL_PROTECTED_UNTYPED(game_id, null) return match +// Get the URL used to connect to the server. +/proc/get_world_url() + return "byond://[get_config_value(/decl/config/text/server) || get_config_value(/decl/config/text/serverurl) || "[world.address]:[world.port]"]" + /world/New() //set window title @@ -83,10 +87,23 @@ GLOBAL_PROTECTED_UNTYPED(game_id, null) if(byond_version < REQUIRED_DM_VERSION) to_world_log("Your server's BYOND version does not meet the minimum DM version for this server. Please update BYOND.") - callHook("startup") - //Emergency Fix - load_mods() - //end-emergency fix + // Initialize the global vars decl, which marks vars as protected. + GET_DECL(/decl/global_vars) + // And the offset used for in-game time + global.roundstart_hour = rand(0, 23) + initialise_map_list() + world.load_mode() + world.load_motd() + load_admins() + world.connect_database() + jobban_loadbanfile() + LoadBans() + update_holiday() //Uncommenting ALLOW_HOLIDAYS in configuration will enable this. + try_load_alien_whitelist() + investigate_reset() + // Precache/build trait trees. + for(var/decl/trait/trait in decls_repository.get_decls_of_type_unassociated(/decl/trait)) + trait.build_references() . = ..() @@ -149,18 +166,24 @@ var/global/world_topic_last = world.timeofday game_log("World rebooted at [time_stamp()]") - callHook("reboot") + on_reboot(reason) ..(reason) +/// If you need to add modular functionality on-reboot, override this instead of /world/Reboot(). +/// It runs directly before the parent call in /world/Reboot(). +/world/proc/on_reboot(reason) + return + /world/Del() Master.Shutdown() - callHook("shutdown") + on_shutdown() return ..() -/hook/startup/proc/loadMode() - world.load_mode() - return 1 +/// If you need to add modular functionality on-shutdown, override this instead of /world/Del(). +/// It runs directly before the parent call in /world/Del(). +/world/proc/on_shutdown() + return /world/proc/load_mode() if(!fexists("data/mode.txt")) @@ -177,38 +200,9 @@ var/global/world_topic_last = world.timeofday fdel(F) direct_output(F, the_mode) -/hook/startup/proc/loadMOTD() - world.load_motd() - return 1 - /world/proc/load_motd() join_motd = safe_file2text("config/motd.txt", FALSE) -/hook/startup/proc/loadMods() - world.load_mods() - return 1 - -/world/proc/load_mods() - if(get_config_value(/decl/config/toggle/on/admin_legacy_system)) - var/text = safe_file2text("config/moderators.txt", FALSE) - if (!text) - error("Failed to load config/mods.txt") - else - var/list/lines = splittext(text, "\n") - for(var/line in lines) - if (!line) - continue - - if (copytext(line, 1, 2) == ";") - continue - - var/title = "Moderator" - var/rights = admin_ranks[title] - - var/ckey = copytext(line, 1, length(line)+1) - var/datum/admins/D = new /datum/admins(title, rights, ckey) - D.associate(global.ckey_directory[ckey]) - /world/proc/update_status() var/s = "[station_name()]" @@ -281,12 +275,11 @@ var/global/world_topic_last = world.timeofday #define FAILED_DB_CONNECTION_CUTOFF 5 var/global/failed_db_connections = 0 -/hook/startup/proc/connectDB() +/world/proc/connect_database() if(!setup_database_connection()) to_world_log("Your server failed to establish a connection with the SQL database.") else to_world_log("SQL database connection established.") - return 1 /proc/setup_database_connection() diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 5f2e3c19cc60..ecfb5bd522fe 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -59,9 +59,6 @@ var/global/savefile/Banlist CMinutes = (world.realtime / 10) / 60 return 1 -/hook/startup/proc/loadBans() - return LoadBans() - /proc/LoadBans() Banlist = new("data/banlist.bdb") diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 79c44208bc00..5e06996ca46f 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -789,9 +789,7 @@ var/global/BSACooldown = 0 if (new_vis && !world.reachable) message_admins("WARNING: The server will not show up on the hub because byond is detecting that a firewall is blocking incoming connections.") - var/full_message = "[key_name(src)]" + long_message - send2adminirc(full_message) - SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Hub Visibility Toggled (Game ID: [game_id])", "body" = full_message)) + SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Hub Visibility Toggled (Game ID: [game_id])", "body" = "[key_name(src)]" + long_message)) log_and_message_admins(long_message) SSstatistics.add_field_details("admin_verb","THUB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 66fe7e8daff1..546f5753cee8 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -11,10 +11,6 @@ /proc/investigate_subject2file(var/subject) return file("[INVESTIGATE_DIR][subject].html") -/hook/startup/proc/resetInvestigate() - investigate_reset() - return 1 - /proc/investigate_reset() if(fdel(INVESTIGATE_DIR)) return 1 return 0 diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index 5a6a4d7b17a1..42eab17d7f73 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -52,10 +52,6 @@ var/global/list/admin_ranks = list() //list of all ranks with associated testing(msg) #endif -/hook/startup/proc/loadAdmins() - load_admins() - return 1 - /proc/load_admins() //clear the datums references admin_datums.Cut() diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 79ca30569ee5..373749683248 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -36,10 +36,6 @@ var/global/list/jobban_keylist = list() //to store the keys & ranks return "Reason Unspecified" return 0 -/hook/startup/proc/loadJobBans() - jobban_loadbanfile() - return 1 - /proc/jobban_loadbanfile() if(get_config_value(/decl/config/toggle/on/ban_legacy_system)) var/savefile/S=new("data/job_full.ban") diff --git a/code/modules/admin/secrets/investigation/admin_pms.dm b/code/modules/admin/secrets/investigation/admin_pms.dm deleted file mode 100644 index cd57bf73283a..000000000000 --- a/code/modules/admin/secrets/investigation/admin_pms.dm +++ /dev/null @@ -1,40 +0,0 @@ -/datum/admin_secret_item/investigation/admin_pms - name = "Admin PMs" - -/datum/admin_secret_item/investigation/admin_pms/execute(var/mob/user, var/filter) - . = ..() - if(!.) - return - var/dat = list() - dat += "Refresh Filtering on: " - if(filter) - dat += " [filter] Clear" - else - dat += "None" - dat += "
    " - dat += "" - dat += "" - - for(var/datum/admin_privat_message/pm in admin_pm_repository.admin_pms_) - var/datum/client_lite/sender = pm.sender - var/datum/client_lite/receiver = pm.receiver - - if(filter && !(sender.ckey == filter || (receiver && receiver.ckey == filter))) - continue - - if(receiver) - dat += "" - else - dat += "" - dat += "" - dat += "
    TimeSenderReceiver
    [pm.station_time][sender.key_name(FALSE)] F[receiver.key_name(FALSE)] F
    [pm.station_time][sender.key_name(FALSE)] F
    [pm.message]
    " - - var/datum/browser/popup = new(user, "admin_ahelps", "Admin PMs", 800, 400) - popup.set_content(jointext(dat, null)) - popup.open() - -/datum/admin_secret_item/investigation/admin_pms/Topic(href, href_list) - . = ..() - if(.) - return - execute(usr, href_list["filter"]) diff --git a/code/modules/admin/ticket.dm b/code/modules/admin/ticket.dm index 526c9867927f..8612a5306d92 100644 --- a/code/modules/admin/ticket.dm +++ b/code/modules/admin/ticket.dm @@ -51,7 +51,6 @@ var/global/list/ticket_panels = list() src.closed_by = closed_by to_chat(client_by_ckey(src.owner.ckey), "Your ticket has been closed by [closed_by.key].") message_staff("[src.owner.key_name(0)]'s ticket has been closed by [closed_by.key].") - send2adminirc("[src.owner.key_name(0)]'s ticket has been closed by [closed_by.key].") SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Ticket ([id]) (Game ID: [game_id]) Ticket Closed", "body" = "[src.owner.key_name(0)] 's ticket (ID [id]) has been closed by [closed_by.key].")) var/closed_by_not_assigned = TRUE @@ -100,7 +99,6 @@ var/global/list/ticket_panels = list() ticket_take.Execute() message_staff("[assigned_admin.key] has assigned themself to [src.owner.key_name(0)]'s ticket.") - send2adminirc("[assigned_admin.key] has assigned themself to [src.owner.key_name(0)]'s ticket.") to_chat(client_by_ckey(src.owner.ckey), "[assigned_admin.key] has added themself to your ticket and should respond shortly. Thanks for your patience!") SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Ticket ([id]) (Game ID: [game_id]) Ticked Assigned", "body" = "[assigned_admin.key] has added themself to ticket ID [id].")) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index ee9f73e756b3..77017eee5f6c 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -134,10 +134,6 @@ var/global/list/adminhelp_ignored_words = list("unknown","the","a","an","of","mo to_chat(src, SPAN_BLUE("PM to-Staff (CLOSE): [original_msg]")) var/admin_number_present = global.admins.len - admin_number_afk log_admin("HELP: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") - if(admin_number_present <= 0) - adminmsg2adminirc(src, null, "[html_decode(original_msg)] - !![admin_number_afk ? "All admins AFK ([admin_number_afk])" : "No admins online"]!!") - else - adminmsg2adminirc(src, null, "[html_decode(original_msg)]") SSstatistics.add_field_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index d3e506f63279..7371ff70873f 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -151,7 +151,6 @@ sound_to(C, 'sound/effects/adminhelp.ogg') log_admin("PM: [key_name(src)]->[key_name(C)]: [msg]") - adminmsg2adminirc(src, C, html_decode(msg)) ticket.msgs += new /datum/ticket_msg(src.ckey, C.ckey, msg) update_ticket_panels() @@ -168,31 +167,3 @@ continue if(X.key != key && X.key != C.key && (X.holder.rights & R_ADMIN|R_MOD)) to_chat(X, "" + create_text_tag("pm_other", "PM:", X) + " [key_name(src, X, 0, ticket)] to [key_name(C, X, 0, ticket)] ([(ticket.status == TICKET_OPEN) ? "TAKE" : "JOIN"]) (CLOSE): [msg]") - -/client/proc/cmd_admin_irc_pm(sender) - if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Private-Message: You are unable to use PM-s (muted).") - return - - var/msg = input(src,"Message:", "Reply private message to [sender] on IRC / 400 character limit") as text|null - - if(!msg) - return - - // Handled on Bot32's end, unsure about other bots -// if(length(msg) > 400) // TODO: if message length is over 400, divide it up into seperate messages, the message length restriction is based on IRC limitations. Probably easier to do this on the bots ends. -// to_chat(src, "Your message was not sent because it was more then 400 characters, find your message below for ease of copy/pasting.") -// to_chat(src, "[msg]") -// return - - adminmsg2adminirc(src, sender, html_decode(msg)) - msg = sanitize(msg) - log_admin("PM: [key_name(src)]->IRC-[sender]: [msg]") - admin_pm_repository.store_pm(src, "IRC-[sender]", msg) - - to_chat(src, "" + create_text_tag("pm_out_alt", "PM", src) + " to [sender]: [msg]") - for(var/client/X in global.admins) - if(X == src) - continue - if(X.holder.rights & R_ADMIN|R_MOD) - to_chat(X, "" + create_text_tag("pm_other", "PM:", X) + " [key_name(src, X, 0)] to [sender]: [msg]") diff --git a/code/modules/chat_filter/_chat_filter.dm b/code/modules/chat_filter/_chat_filter.dm index 15beabc9ba0d..7e9ce18e77a0 100644 --- a/code/modules/chat_filter/_chat_filter.dm +++ b/code/modules/chat_filter/_chat_filter.dm @@ -1,7 +1,7 @@ var/global/list/chat_blockers_in_use var/global/list/chat_modifiers_in_use -/hook/startup/proc/build_filter_lists() +/proc/build_filter_lists() global.chat_blockers_in_use = list() global.chat_modifiers_in_use = list() var/list/all_filters = decls_repository.get_decls_of_type(/decl/chat_filter) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 4afacdc2b068..76f96bf08661 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -65,16 +65,6 @@ var/global/list/localhost_addresses = list( cmd_admin_pm(C, null, ticket) return - if(href_list["irc_msg"]) - if(!holder && received_irc_pm < world.time - 6000) //Worst they can do is spam IRC for 10 minutes - to_chat(usr, SPAN_WARNING("You are no longer able to use this, it's been more then 10 minutes since an admin on IRC has responded to you.")) - return - if(mute_irc) - to_chat(usr, SPAN_WARNING("You cannot use this as your client has been muted from sending messages to the admins on IRC.")) - return - cmd_admin_irc_pm(href_list["irc_msg"]) - return - if(href_list["close_ticket"]) var/datum/ticket/ticket = locate(href_list["close_ticket"]) @@ -378,14 +368,10 @@ var/global/list/localhost_addresses = list( if(admin_datums[ckey] && GAME_STATE == RUNLEVEL_GAME) //Only report this stuff if we are currently playing. message_staff("\[[holder.rank]\] [key_name(src)] logged out.") if(!global.admins.len) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell. - var/full_message = "[key_name(src)] logged out - no more staff online." - send2adminirc(full_message) - SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Admin Logout (Game ID: [game_id])", "body" = full_message)) + SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Admin Logout (Game ID: [game_id])", "body" = "[key_name(src)] logged out - no more staff online.")) if(get_config_value(/decl/config/toggle/delist_when_no_admins) && get_config_value(/decl/config/toggle/hub_visibility)) toggle_config_value(/decl/config/toggle/hub_visibility) - full_message = "Toggled hub visibility. The server is now invisible." - send2adminirc(full_message) - SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Automatic Hub Visibility Toggle (Game ID: [game_id])", "body" = full_message)) + SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Automatic Hub Visibility Toggle (Game ID: [game_id])", "body" = "Toggled hub visibility. The server is now invisible.")) //checks if a client is afk //3000 frames = 5 minutes diff --git a/code/modules/error_handler/error_reporting.dm b/code/modules/error_handler/error_reporting.dm deleted file mode 100644 index 773920717cbd..000000000000 --- a/code/modules/error_handler/error_reporting.dm +++ /dev/null @@ -1,23 +0,0 @@ -// this proc will only work with DEBUG enabled -#ifdef DEBUG - -/hook/roundend/proc/send_runtimes_to_ircbot() - if(!revdata.revision) return // we can't do much useful if we don't know what we are - var/list/errors = list() - for(var/erruid in global.error_cache.error_sources) - var/datum/error_viewer/error_source/e = global.error_cache.error_sources[erruid] - var/datum/error_viewer/error_entry/err = e.errors[1] - - var/data = list( - id = erruid, - name = err.info_name, - info = err.info - ) - - errors[++errors.len] = list2params(data) - - runtimes2irc(list2params(errors), revdata.revision) - - return 1 - -#endif diff --git a/code/modules/ext_scripts/irc.dm b/code/modules/ext_scripts/irc.dm deleted file mode 100644 index 9b1887f118fb..000000000000 --- a/code/modules/ext_scripts/irc.dm +++ /dev/null @@ -1,55 +0,0 @@ -/proc/send2irc(var/channel, var/msg) - export2irc(list(type="msg", mesg=msg, chan=channel, pwd=get_config_value(/decl/config/text/comms_password))) - -/proc/export2irc(params) - if(!get_config_value(/decl/config/toggle/use_irc_bot)) - return - var/irc_bot_host = get_config_value(/decl/config/text/irc_bot_host) - if(irc_bot_host) - spawn(-1) // spawn here prevents hanging in the case that the bot isn't reachable - world.Export("http://[irc_bot_host]:45678?[list2params(params)]") - -/proc/runtimes2irc(runtimes, revision) - export2irc(list(pwd=get_config_value(/decl/config/text/comms_password), type="runtime", runtimes=runtimes, revision=revision)) - -/proc/send2mainirc(var/msg) - var/main_irc = get_config_value(/decl/config/text/main_irc) - if(main_irc) - send2irc(main_irc, msg) - return - -/proc/send2adminirc(var/msg) - var/admin_irc = get_config_value(/decl/config/text/admin_irc) - if(admin_irc) - send2irc(admin_irc, msg) - return - -/proc/adminmsg2adminirc(client/source, client/target, msg) - var/admin_irc = get_config_value(/decl/config/text/admin_irc) - if(admin_irc) - var/list/params[0] - - params["pwd"] = get_config_value(/decl/config/text/comms_password) - params["chan"] = admin_irc - params["msg"] = msg - params["src_key"] = source.key - params["src_char"] = source.mob.real_name || source.mob.name - if(!target) - params["type"] = "adminhelp" - else if(istext(target)) - params["type"] = "ircpm" - params["target"] = target - params["rank"] = source.holder ? source.holder.rank : "Player" - else - params["type"] = "adminpm" - params["trg_key"] = target.key - params["trg_char"] = target.mob.real_name || target.mob.name - - export2irc(params) - -/proc/get_world_url() - return "byond://[get_config_value(/decl/config/text/server) || get_config_value(/decl/config/text/serverurl) || "[world.address]:[world.port]"]" - -/hook/startup/proc/ircNotify() - send2mainirc("Server starting up on [get_world_url()]") - return 1 diff --git a/code/modules/holidays/holiday_hook.dm b/code/modules/holidays/holiday_hook.dm index 6b6870c5bb03..231c1a056fdf 100644 --- a/code/modules/holidays/holiday_hook.dm +++ b/code/modules/holidays/holiday_hook.dm @@ -1,8 +1,3 @@ -//Uncommenting ALLOW_HOLIDAYS in configuration will enable this hook. -/hook/startup/proc/updateHoliday() - update_holiday() - return TRUE - /proc/update_holiday() if(!get_config_value(/decl/config/toggle/allow_holidays)) diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm index 6e1a7989b716..0d02001ceae5 100644 --- a/code/modules/mob/living/silicon/pai/recruit.dm +++ b/code/modules/mob/living/silicon/pai/recruit.dm @@ -2,8 +2,6 @@ // Recruiting observers to play as pAIs -var/global/datum/paiController/paiController // Global handler for pAI candidates - /datum/paiCandidate var/name var/key @@ -14,11 +12,7 @@ var/global/datum/paiController/paiController // Global handler for pAI candida var/chassis = "Drone" var/say_verb = "Robotic" - -/hook/startup/proc/paiControllerSetup() - paiController = new /datum/paiController() - return 1 - +var/global/datum/paiController/paiController = new /datum/paiController() // Global handler for pAI candidates /datum/paiController var/inquirer = null @@ -27,6 +21,10 @@ var/global/datum/paiController/paiController // Global handler for pAI candida var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min] +/datum/paiController/New() + ..() + populate_pai_software_list() + /datum/paiController/Topic(href, href_list[]) if(href_list["download"]) var/datum/paiCandidate/candidate = locate(href_list["candidate"]) diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 72c1af676bb0..b53b8a9cfe2d 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -19,19 +19,18 @@ var/global/list/pai_emotions = list( var/global/list/pai_software_by_key = list() var/global/list/default_pai_software = list() -/hook/startup/proc/populate_pai_software_list() - var/r = 1 // I would use ., but it'd sacrifice runtime detection +/proc/populate_pai_software_list() + pai_software_by_key = list() + default_pai_software = list() for(var/type in subtypesof(/datum/pai_software)) var/datum/pai_software/P = new type() if(pai_software_by_key[P.id]) var/datum/pai_software/O = pai_software_by_key[P.id] - log_error("pAI software module [P.name] has the same key as [O.name]!") - r = 0 + PRINT_STACK_TRACE("pAI software module [type] has the same key ([P.id]) as [O.type] [O.id]!") continue pai_software_by_key[P.id] = P if(P.default) default_pai_software[P.id] = P - return r /mob/living/silicon/pai/proc/paiInterface() ui_interact(src) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index f03372203ada..df2fc50f4b34 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -482,10 +482,6 @@ INITIALIZE_IMMEDIATE(/mob/new_player) /mob/new_player/get_admin_job_string() return "New player" -/hook/roundstart/proc/update_lobby_browsers() - global.using_map.refresh_lobby_browsers() - return TRUE - /mob/new_player/change_mob_type(var/new_type, var/turf/location, var/new_name, var/delete_old_mob = FALSE, var/subspecies) to_chat(usr, SPAN_WARNING("You cannot convert players who have not entered the game yet!")) return FALSE diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm index 56b086005c7f..5cb5d84467e5 100644 --- a/code/modules/supermatter/supermatter.dm +++ b/code/modules/supermatter/supermatter.dm @@ -237,13 +237,13 @@ var/global/list/supermatter_delam_accent_sounds = list( else aw_EPR = FALSE -/obj/machinery/power/supermatter/proc/status_adminwarn_check(var/min_status, var/current_state, var/message, var/send_to_irc = FALSE) +/obj/machinery/power/supermatter/proc/status_adminwarn_check(var/min_status, var/current_state, var/message, var/send_webhook = FALSE) var/status = get_status() if(status >= min_status) if(!current_state) log_and_message_admins(message) - if(send_to_irc) - send2adminirc(message) + if(send_webhook) + SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Supermatter Warning", "body" = message)) return TRUE else return FALSE diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index fa0aaa499777..807c786ed518 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -4,7 +4,7 @@ var/global/list/all_maps = list() var/global/const/MAP_HAS_BRANCH = 1 //Branch system for occupations, togglable var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable -/hook/startup/proc/initialise_map_list() +/proc/initialise_map_list() for(var/type in subtypesof(/datum/map)) var/datum/map/M if(type == global.using_map.type) diff --git a/mods/_modpack.dm b/mods/_modpack.dm index 279002ba2515..1e8accdf3061 100644 --- a/mods/_modpack.dm +++ b/mods/_modpack.dm @@ -56,6 +56,10 @@ if(length(credits_nouns)) SSlore.credits_nouns |= credits_nouns +/// This runs on-roundstart after roundstart characters have been created. +/decl/modpack/proc/on_roundstart() + return + /decl/modpack/proc/get_membership_perks() return @@ -70,11 +74,11 @@ . = "

    Modpacks List



    " for(var/modpack in SSmodpacks.loaded_modpacks) var/decl/modpack/M = SSmodpacks.loaded_modpacks[modpack] - + if(M.name) . += "
    " . += "
    [M.name]
    " - + if(M.desc || M.author) . += "
    " if(M.desc) diff --git a/mods/content/matchmaking/matchmaker.dm b/mods/content/matchmaking/matchmaker.dm index 0892ba4c827a..453d885321cb 100644 --- a/mods/content/matchmaking/matchmaker.dm +++ b/mods/content/matchmaking/matchmaker.dm @@ -1,14 +1,12 @@ -var/global/datum/matchmaker/matchmaker = new() - -/hook/roundstart/proc/matchmaking() - matchmaker.do_matchmaking() - return TRUE +/decl/modpack/matchmaking/on_roundstart() + do_matchmaking() -/datum/matchmaker/matchmaker/New() +// It doesn't really matter when this registers during init as long as it's before roundstart. +/decl/modpack/matchmaking/post_initialize() . = ..() events_repository.register_global(/decl/observ/player_latejoin, src, PROC_REF(matchmake_latejoiner)) -/datum/matchmaker/proc/matchmake_latejoiner(mob/living/character, datum/job/job) +/decl/modpack/matchmaking/proc/matchmake_latejoiner(mob/living/character, datum/job/job) if(character.mind && character.client?.prefs.relations.len) for(var/T in character.client.prefs.relations) var/TT = relation_types[T] @@ -30,17 +28,17 @@ var/global/datum/matchmaker/matchmaker = new() QDEL_NULL_LIST(known_connections) . = ..() -/datum/matchmaker +/decl/modpack/matchmaking var/list/relation_types = list() var/list/relations = list() -/datum/matchmaker/New() - ..() +/decl/modpack/matchmaking/Initialize() + . = ..() for(var/T in subtypesof(/datum/relation/)) var/datum/relation/R = T relation_types[initial(R.name)] = T -/datum/matchmaker/proc/do_matchmaking() +/decl/modpack/matchmaking/proc/do_matchmaking() var/list/to_warn = list() for(var/datum/relation/R in relations) if(R.other) @@ -51,13 +49,13 @@ var/global/datum/matchmaker/matchmaker = new() for(var/mob/M in to_warn) to_chat(M,"You have new connections. Use \"See Relationship Info\" to view and finalize them.") -/datum/matchmaker/proc/get_relationships(datum/mind/M, finalized_only) +/decl/modpack/matchmaking/proc/get_relationships(datum/mind/M, finalized_only) . = list() for(var/datum/relation/R in relations) if(R.holder == M && R.other && (R.finalized || !finalized_only)) . += R -/datum/matchmaker/proc/get_relationships_between(datum/mind/holder, datum/mind/target, finalized_only) +/decl/modpack/matchmaking/proc/get_relationships_between(datum/mind/holder, datum/mind/target, finalized_only) . = list() for(var/datum/relation/R in relations) if(R.holder == holder && R.other && R.other.holder == target && (R.finalized || !finalized_only)) @@ -68,6 +66,7 @@ var/global/datum/matchmaker/matchmaker = new() return if(!source.mind || !user.mind || source.name != source.real_name) return + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL if(!length(matchmaker.get_relationships_between(user.mind, source.mind, TRUE))) return return "
    You know them. More...
    " @@ -89,10 +88,12 @@ var/global/datum/matchmaker/matchmaker = new() ..() if(!can_connect_to) can_connect_to = list(type) + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL matchmaker.relations += src /datum/relation/proc/get_candidates() .= list() + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL for(var/datum/relation/R in matchmaker.relations) if(!valid_candidate(R.holder) || !can_connect(R)) continue @@ -112,6 +113,7 @@ var/global/datum/matchmaker/matchmaker = new() return TRUE /datum/relation/proc/can_connect(var/datum/relation/R) + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL for(var/datum/relation/D in matchmaker.relations) //have to check all connections between us and them if(D.holder == R.holder && D.other && D.other.holder == holder) if(D.type in incompatible) @@ -141,6 +143,7 @@ var/global/datum/matchmaker/matchmaker = new() to_chat(holder.current,"Your connection with [other.holder] is no more.") to_chat(other.holder.current,"Your connection with [holder] is no more.") other.other = null + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL matchmaker.relations -= other matchmaker.relations -= src qdel(other) @@ -187,6 +190,7 @@ var/global/datum/matchmaker/matchmaker = new() set desc = "See what connections between people you know of." set category = "IC" + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL var/list/relations = matchmaker.get_relationships(mind) var/list/dat = list() var/editable = 0 @@ -221,6 +225,7 @@ var/global/datum/matchmaker/matchmaker = new() /mob/living/proc/see_relationship_info_with(var/mob/living/other) if(!other.mind) return + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL var/list/relations = matchmaker.get_relationships(mind,other.mind,TRUE) var/list/dat = list("

    [other]

    ") if(mind.gen_relations_info) @@ -268,6 +273,7 @@ var/global/datum/matchmaker/matchmaker = new() var/ok = "Close anyway" ok = alert("HEY! You have some non-finalized relationships. You can terminate them if they do not fit your character, or edit the info tidbit that the other party is given. THIS IS YOUR ONLY CHANCE to do so - after you close the window, they won't be editable.","Finalize relationships","Return to edit", "Close anyway") if(ok == "Close anyway") + var/decl/modpack/matchmaking/matchmaker = IMPLIED_DECL var/list/relations = matchmaker.get_relationships(mind) for(var/datum/relation/R in relations) R.finalize() diff --git a/nebula.dme b/nebula.dme index d28a50a9d58c..5036830ed679 100644 --- a/nebula.dme +++ b/nebula.dme @@ -241,8 +241,6 @@ #include "code\controllers\communications.dm" #include "code\controllers\controller.dm" #include "code\controllers\failsafe.dm" -#include "code\controllers\hooks-defs.dm" -#include "code\controllers\hooks.dm" #include "code\controllers\master.dm" #include "code\controllers\subsystem.dm" #include "code\controllers\verbs.dm" @@ -616,7 +614,6 @@ #include "code\datums\proximity_trigger\proximity_trigger.dm" #include "code\datums\proximity_trigger\turf_selection.dm" #include "code\datums\repositories\_defines.dm" -#include "code\datums\repositories\admin_pm.dm" #include "code\datums\repositories\areas.dm" #include "code\datums\repositories\atom_info.dm" #include "code\datums\repositories\attack_logs.dm" @@ -1705,7 +1702,6 @@ #include "code\modules\admin\secrets\fun_secrets\power_all_smes.dm" #include "code\modules\admin\secrets\fun_secrets\triple_ai_mode.dm" #include "code\modules\admin\secrets\fun_secrets\waddle.dm" -#include "code\modules\admin\secrets\investigation\admin_pms.dm" #include "code\modules\admin\secrets\investigation\attack_logs.dm" #include "code\modules\admin\verbs\adminhelp.dm" #include "code\modules\admin\verbs\adminjump.dm" @@ -2302,7 +2298,6 @@ #include "code\modules\emotes\definitions\tail.dm" #include "code\modules\emotes\definitions\visible.dm" #include "code\modules\error_handler\error_handler.dm" -#include "code\modules\error_handler\error_reporting.dm" #include "code\modules\error_handler\error_viewer.dm" #include "code\modules\events\ailments.dm" #include "code\modules\events\apc_damage.dm" @@ -2345,7 +2340,6 @@ #include "code\modules\events\trivial_news.dm" #include "code\modules\events\wallrot.dm" #include "code\modules\events\wormholes.dm" -#include "code\modules\ext_scripts\irc.dm" #include "code\modules\fabrication\__fabricator_defines.dm" #include "code\modules\fabrication\_fabricator.dm" #include "code\modules\fabrication\_fabricator_build_order.dm" diff --git a/~code/global_init.dm b/~code/global_init.dm index 00301af2ca2b..82950bb246da 100644 --- a/~code/global_init.dm +++ b/~code/global_init.dm @@ -15,7 +15,8 @@ var/global_init = new /datum/global_init() /datum/global_init/New() SSconfiguration.load_all_configuration() - callHook("global_init") + generate_game_id() + makeDatumRefLists() qdel(src) //we're done /datum/global_init/Destroy() From bb79c469c77163ec89f6a1c32d5abf386a2ecfb8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 5 Dec 2024 19:21:47 -0500 Subject: [PATCH 0062/1331] Remove redundant multitool extension Topic defines --- code/__defines/topic.dm | 4 +- .../datums/extensions/multitool/_multitool.dm | 3 - .../circuitboards/buildtype_select.dm | 2 +- .../circuitboards/shuttle_console.dm | 6 +- .../multitool/circuitboards/stationalert.dm | 4 +- .../extensions/multitool/items/cable.dm | 2 +- .../multitool/items/stock_parts_radio.dm | 58 +++++++++---------- code/datums/extensions/multitool/multitool.dm | 21 ++++--- .../airlock_controllers_dummy.dm | 6 +- .../embedded_controller_base.dm | 8 +-- nebula.dme | 1 - 11 files changed, 58 insertions(+), 57 deletions(-) delete mode 100644 code/datums/extensions/multitool/_multitool.dm diff --git a/code/__defines/topic.dm b/code/__defines/topic.dm index 74e2ca005f07..219abf3728e4 100644 --- a/code/__defines/topic.dm +++ b/code/__defines/topic.dm @@ -2,6 +2,8 @@ #define TOPIC_HANDLED BITFLAG(0) #define TOPIC_REFRESH BITFLAG(1) #define TOPIC_UPDATE_PREVIEW BITFLAG(2) - // use to force a browse() call, unblocking some rsc operations +/// Return this to force a browse() call, unblocking some rsc operations #define TOPIC_HARD_REFRESH BITFLAG(3) +/// Return this to indicate the window associated with this Topic() call should be closed. +#define TOPIC_CLOSE BITFLAG(4) #define TOPIC_REFRESH_UPDATE_PREVIEW (TOPIC_HARD_REFRESH|TOPIC_UPDATE_PREVIEW) diff --git a/code/datums/extensions/multitool/_multitool.dm b/code/datums/extensions/multitool/_multitool.dm deleted file mode 100644 index fd581a007f94..000000000000 --- a/code/datums/extensions/multitool/_multitool.dm +++ /dev/null @@ -1,3 +0,0 @@ -#define MT_NOACTION 0 -#define MT_REFRESH 1 -#define MT_CLOSE 2 diff --git a/code/datums/extensions/multitool/circuitboards/buildtype_select.dm b/code/datums/extensions/multitool/circuitboards/buildtype_select.dm index 703d9c4f3f6a..ae95b5cb13a2 100644 --- a/code/datums/extensions/multitool/circuitboards/buildtype_select.dm +++ b/code/datums/extensions/multitool/circuitboards/buildtype_select.dm @@ -25,5 +25,5 @@ board.build_path = path var/obj/thing = path board.SetName("circuitboard ([initial(thing.name)])") - return MT_REFRESH + return TOPIC_REFRESH return ..() \ No newline at end of file diff --git a/code/datums/extensions/multitool/circuitboards/shuttle_console.dm b/code/datums/extensions/multitool/circuitboards/shuttle_console.dm index 9e2679682461..77ad6c394f32 100644 --- a/code/datums/extensions/multitool/circuitboards/shuttle_console.dm +++ b/code/datums/extensions/multitool/circuitboards/shuttle_console.dm @@ -19,11 +19,11 @@ break if(!new_name) to_chat(user, SPAN_WARNING("No eligible shuttle could be located. Make sure the board is inside a shuttle and try again.")) - return MT_NOACTION + return TOPIC_NOACTION if(!board.is_valid_shuttle(SSshuttle.shuttles[new_name])) to_chat(user, SPAN_WARNING("The current shuttle does not support this console type. Try a different shuttle or circuit board.")) - return MT_NOACTION + return TOPIC_NOACTION board.shuttle_tag = new_name to_chat(user, SPAN_NOTICE("You set the shuttle name to '[new_name]'.")) - return MT_REFRESH + return TOPIC_REFRESH return ..() \ No newline at end of file diff --git a/code/datums/extensions/multitool/circuitboards/stationalert.dm b/code/datums/extensions/multitool/circuitboards/stationalert.dm index e3ec67a20247..209dfba6b736 100644 --- a/code/datums/extensions/multitool/circuitboards/stationalert.dm +++ b/code/datums/extensions/multitool/circuitboards/stationalert.dm @@ -18,12 +18,12 @@ var/datum/alarm_handler/AH = locate(href_list["add"]) in SSalarm.all_handlers if(AH) SA.alarm_handlers |= AH - return MT_REFRESH + return TOPIC_REFRESH if(href_list["remove"]) var/datum/alarm_handler/AH = locate(href_list["remove"]) in SSalarm.all_handlers if(AH) SA.alarm_handlers -= AH - return MT_REFRESH + return TOPIC_REFRESH return ..() diff --git a/code/datums/extensions/multitool/items/cable.dm b/code/datums/extensions/multitool/items/cable.dm index f58fc8232977..7f95e27e73e7 100644 --- a/code/datums/extensions/multitool/items/cable.dm +++ b/code/datums/extensions/multitool/items/cable.dm @@ -21,6 +21,6 @@ var/obj/item/stack/cable_coil/cable_coil = holder if(href_list["select_color"] && (href_list["select_color"] in get_global_cable_colors())) cable_coil.set_cable_color(href_list["select_color"], user) - return MT_REFRESH + return TOPIC_REFRESH return ..() diff --git a/code/datums/extensions/multitool/items/stock_parts_radio.dm b/code/datums/extensions/multitool/items/stock_parts_radio.dm index ec0b51dc4549..e35bf8649774 100644 --- a/code/datums/extensions/multitool/items/stock_parts_radio.dm +++ b/code/datums/extensions/multitool/items/stock_parts_radio.dm @@ -49,48 +49,48 @@ var/obj/item/stock_parts/radio/radio = holder if(href_list["unlink"]) machine = null - return MT_CLOSE + return TOPIC_CLOSE if(href_list["frequency"]) var/new_frequency = input(user, "Select a new frequency:", "Frequency Selection", radio.frequency) as null|num if(!new_frequency || (extension_status(user) != STATUS_INTERACTIVE)) - return MT_NOACTION + return TOPIC_NOACTION new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) if(new_frequency == radio.frequency) - return MT_NOACTION + return TOPIC_NOACTION radio.set_frequency(new_frequency, radio.filter) - return MT_REFRESH + return TOPIC_REFRESH if(href_list["id_tag"]) var/new_id_tag = input(user, "Select a new ID:", "ID Selection", radio.id_tag) as null|text if(!new_id_tag || (extension_status(user) != STATUS_INTERACTIVE)) - return MT_NOACTION + return TOPIC_NOACTION new_id_tag = sanitize(new_id_tag) if(new_id_tag == radio.id_tag) - return MT_NOACTION + return TOPIC_NOACTION radio.set_id_tag(new_id_tag) - return MT_REFRESH + return TOPIC_REFRESH if(href_list["filter"]) var/new_filter = input(user, "Select a new radio filter (usually signals are sent to listeners on your id_tag; this will override that behavior):", "Filter Selection", radio.filter) as null|anything in global.all_selectable_radio_filters if(!new_filter || (extension_status(user) != STATUS_INTERACTIVE)) - return MT_NOACTION + return TOPIC_NOACTION if(new_filter == radio.filter) - return MT_NOACTION + return TOPIC_NOACTION radio.set_frequency(radio.frequency, new_filter) - return MT_REFRESH + return TOPIC_REFRESH if(href_list["encryption"]) var/new_encryption = input(user, "Select a new encryption key:", "Encryption Key Selection", radio.encryption) as null|num if(!new_encryption || (extension_status(user) != STATUS_INTERACTIVE)) - return MT_NOACTION + return TOPIC_NOACTION new_encryption = sanitize_integer(new_encryption, 0, 999, radio.encryption) if(new_encryption == radio.encryption) - return MT_NOACTION + return TOPIC_NOACTION radio.encryption = new_encryption - return MT_REFRESH + return TOPIC_REFRESH if(href_list["stockreset"]) var/obj/machinery/actual_machine = machine && machine.resolve() if(!actual_machine) - return MT_CLOSE + return TOPIC_CLOSE actual_machine.apply_preset_to(radio) - return MT_REFRESH + return TOPIC_REFRESH // Helper. /datum/extension/interactive/multitool/radio/proc/event_list_to_selection_table(table_tag, list/selected_events) @@ -111,44 +111,44 @@ if(href_list["remove"]) var/thing = href_list["remove"] LAZYREMOVE(selected_events, thing) - return MT_REFRESH + return TOPIC_REFRESH if(href_list["rename"]) var/thing = href_list["rename"] if(selected_events && selected_events[thing]) var/new_name = input(user, "Select a new message key for this item:", "Key Select", thing) as null|text new_name = sanitize(new_name) if(!new_name || (extension_status(user) != STATUS_INTERACTIVE)) - return MT_REFRESH + return TOPIC_REFRESH if(!selected_events || !selected_events[thing]) - return MT_REFRESH + return TOPIC_REFRESH selected_events[new_name] = selected_events[thing] selected_events -= thing - return MT_REFRESH + return TOPIC_REFRESH if(href_list["new_val"]) var/thing = href_list["new_val"] var/decl/public_access/variable = selected_events && selected_events[thing] if(!variable || !LAZYLEN(valid_events)) - return MT_REFRESH + return TOPIC_REFRESH var/valid_variables = list() for(var/path in valid_events) valid_variables += valid_events[path] var/new_var = input(user, "Select a new action for this item:", "Action Select", thing) as null|anything in valid_variables if(!new_var || (extension_status(user) != STATUS_INTERACTIVE)) - return MT_REFRESH + return TOPIC_REFRESH if(!(selected_events && selected_events[thing] == variable)) - return MT_REFRESH + return TOPIC_REFRESH selected_events[thing] = new_var - return MT_REFRESH + return TOPIC_REFRESH if(href_list["add"]) if(!LAZYLEN(valid_events)) - return MT_REFRESH + return TOPIC_REFRESH LAZYSET(selected_events, copytext(md5(num2text(rand(0, 1))), 1, 11), valid_events[pick(valid_events)]) // random key - return MT_REFRESH + return TOPIC_REFRESH if(href_list["desc"]) var/decl/public_access/variable = locate(href_list["desc"]) if(istype(variable)) to_chat(user, variable.desc) - return MT_NOACTION + return TOPIC_NOACTION /datum/extension/interactive/multitool/radio/transmitter/aquire_target() var/obj/machinery/actual_machine = ..() @@ -178,7 +178,7 @@ return var/obj/machinery/actual_machine = machine.resolve() if(!actual_machine) - return MT_CLOSE + return TOPIC_CLOSE var/obj/item/stock_parts/radio/transmitter/basic/radio = holder if(href_list["on_change"]) return event_list_topic(radio.transmit_on_change, actual_machine.public_variables, user, href_list) @@ -216,7 +216,7 @@ return var/obj/machinery/actual_machine = machine.resolve() if(!actual_machine) - return MT_CLOSE + return TOPIC_CLOSE var/obj/item/stock_parts/radio/transmitter/on_event/radio = holder if(href_list["on_event"]) @@ -257,7 +257,7 @@ return var/obj/machinery/actual_machine = machine.resolve() if(!actual_machine) - return MT_CLOSE + return TOPIC_CLOSE var/obj/item/stock_parts/radio/receiver/radio = holder if(href_list["call"]) return event_list_topic(radio.receive_and_call, actual_machine.public_methods, user, href_list) diff --git a/code/datums/extensions/multitool/multitool.dm b/code/datums/extensions/multitool/multitool.dm index 08e98ebe7ebc..7327f919da97 100644 --- a/code/datums/extensions/multitool/multitool.dm +++ b/code/datums/extensions/multitool/multitool.dm @@ -45,26 +45,29 @@ . = send_buffer(M, buffer, user) else if(href_list["purge"]) M.set_buffer(null) - . = MT_REFRESH + . = TOPIC_REFRESH else . = on_topic(href, href_list, user) - switch(.) - if(MT_REFRESH) - interact(M, user) - if(MT_CLOSE) - close_window(user) - return MT_NOACTION ? FALSE : TRUE + if(. & TOPIC_CLOSE) + close_window(user) + return TOPIC_HANDLED // don't run any other Topic() behavior for this call + else if(. & TOPIC_REFRESH) + interact(M, user) + return TOPIC_HANDLED // don't return TOPIC_REFRESH to avoid any potential double-refreshes + else if(!.) + return TOPIC_NOACTION + return TOPIC_REFRESH /datum/extension/interactive/multitool/proc/on_topic(href, href_list, user) - return MT_NOACTION + return TOPIC_NOACTION /datum/extension/interactive/multitool/proc/send_buffer(var/obj/item/multitool/M, var/atom/buffer, var/mob/user) if(M.get_buffer() == buffer && buffer) receive_buffer(M, buffer, user) else if(!buffer) to_chat(user, "Unable to acquire data from the buffered object. Purging from memory.") - return MT_REFRESH + return TOPIC_REFRESH /datum/extension/interactive/multitool/proc/receive_buffer(var/obj/item/multitool/M, var/atom/buffer, var/mob/user) return \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm index f187d98f09c3..dabbb7680fbb 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm @@ -132,12 +132,12 @@ if(href_list["input_tag"]) var/new_tag = input(user, "Enter the tag of the controller to connect to.", "Tag Selection", terminal.id_tag) as text|null if(extension_status(user) != STATUS_INTERACTIVE) - return MT_NOACTION + return TOPIC_NOACTION new_tag = sanitize_name(new_tag, MAX_MESSAGE_LEN, TRUE, FALSE) if(new_tag) terminal.id_tag = new_tag terminal.setup_target_controller() - return MT_REFRESH + return TOPIC_REFRESH if(istext(href_list["set_tag"])) var/new_tag = href_list["set_tag"] @@ -145,6 +145,6 @@ if(new_tag) terminal.id_tag = new_tag terminal.setup_target_controller() - return MT_REFRESH + return TOPIC_REFRESH return ..() diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index bfdbc356c448..2f893501f6e8 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -174,20 +174,20 @@ if(href_list["set_tag"]) var/new_tag = input(user, "Enter a new tag to use. Warning: this will reset all tags used by this machine, not just the main one!", "Tag Selection", controller.id_tag) as text|null if(extension_status(user) != STATUS_INTERACTIVE) - return MT_NOACTION + return TOPIC_NOACTION new_tag = sanitize_name(new_tag, MAX_MESSAGE_LEN, TRUE, FALSE) if(new_tag) controller.reset_id_tags(new_tag) controller.set_frequency(controller.frequency) - return MT_REFRESH + return TOPIC_REFRESH if(href_list["set_freq"]) var/new_frequency = input(user, "Enter a new frequency to use.", "frequency Selection", controller.frequency) as num|null if(!new_frequency || (extension_status(user) != STATUS_INTERACTIVE)) - return MT_NOACTION + return TOPIC_NOACTION new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) controller.set_frequency(new_frequency) - return MT_REFRESH + return TOPIC_REFRESH /decl/stock_part_preset/radio/receiver/vent_pump/airlock frequency = EXTERNAL_AIR_FREQ diff --git a/nebula.dme b/nebula.dme index 5036830ed679..c3058b700c46 100644 --- a/nebula.dme +++ b/nebula.dme @@ -449,7 +449,6 @@ #include "code\datums\extensions\eye\landing.dm" #include "code\datums\extensions\holster\holster.dm" #include "code\datums\extensions\milkable\milkable.dm" -#include "code\datums\extensions\multitool\_multitool.dm" #include "code\datums\extensions\multitool\multitool.dm" #include "code\datums\extensions\multitool\store.dm" #include "code\datums\extensions\multitool\circuitboards\buildtype_select.dm" From 7635c33395e9f210636631147f84e5dcc24672fe Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 5 Dec 2024 20:30:33 -0500 Subject: [PATCH 0063/1331] Consolidate ghost mob files with only one proc each --- code/modules/mob/observer/ghost/ghost.dm | 18 ++++++++++++++++++ code/modules/mob/observer/ghost/login.dm | 6 ------ code/modules/mob/observer/ghost/logout.dm | 5 ----- code/modules/mob/observer/ghost/say.dm | 2 -- nebula.dme | 3 --- 5 files changed, 18 insertions(+), 16 deletions(-) delete mode 100644 code/modules/mob/observer/ghost/login.dm delete mode 100644 code/modules/mob/observer/ghost/logout.dm delete mode 100644 code/modules/mob/observer/ghost/say.dm diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm index acf4b0af582c..b6c2f5f18be9 100644 --- a/code/modules/mob/observer/ghost/ghost.dm +++ b/code/modules/mob/observer/ghost/ghost.dm @@ -626,3 +626,21 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ghost_all_access = new if(!is_type_in_list(ghost_all_access, exceptions)) LAZYDISTINCTADD(., ghost_all_access) + +/mob/observer/ghost/Login() + ..() + if (ghost_image) + ghost_image.appearance = src + ghost_image.appearance_flags = RESET_ALPHA + SSghost_images.queue_image_update(src) + +/mob/observer/ghost/proc/check_existence_failure() + if(!QDELETED(src) && !key) //we've transferred to another mob. This ghost should be deleted. + qdel(src) + +/mob/observer/ghost/Logout() + ..() + addtimer(CALLBACK(src, PROC_REF(check_existence_failure)), 0) + +/mob/observer/ghost/say(var/message) + sanitize_and_communicate(/decl/communication_channel/dsay, client, message) diff --git a/code/modules/mob/observer/ghost/login.dm b/code/modules/mob/observer/ghost/login.dm deleted file mode 100644 index f8be3dd46712..000000000000 --- a/code/modules/mob/observer/ghost/login.dm +++ /dev/null @@ -1,6 +0,0 @@ -/mob/observer/ghost/Login() - ..() - if (ghost_image) - ghost_image.appearance = src - ghost_image.appearance_flags = RESET_ALPHA - SSghost_images.queue_image_update(src) diff --git a/code/modules/mob/observer/ghost/logout.dm b/code/modules/mob/observer/ghost/logout.dm deleted file mode 100644 index 7c125cef367c..000000000000 --- a/code/modules/mob/observer/ghost/logout.dm +++ /dev/null @@ -1,5 +0,0 @@ -/mob/observer/ghost/Logout() - ..() - spawn(0) - if(src && !key) //we've transferred to another mob. This ghost should be deleted. - qdel(src) diff --git a/code/modules/mob/observer/ghost/say.dm b/code/modules/mob/observer/ghost/say.dm deleted file mode 100644 index f30f4a672c10..000000000000 --- a/code/modules/mob/observer/ghost/say.dm +++ /dev/null @@ -1,2 +0,0 @@ -/mob/observer/ghost/say(var/message) - sanitize_and_communicate(/decl/communication_channel/dsay, client, message) diff --git a/nebula.dme b/nebula.dme index c3058b700c46..a48e4415c232 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3089,9 +3089,6 @@ #include "code\modules\mob\observer\eye\freelook\ai\update_triggers.dm" #include "code\modules\mob\observer\ghost\follow.dm" #include "code\modules\mob\observer\ghost\ghost.dm" -#include "code\modules\mob\observer\ghost\login.dm" -#include "code\modules\mob\observer\ghost\logout.dm" -#include "code\modules\mob\observer\ghost\say.dm" #include "code\modules\mob\observer\virtual\_constants.dm" #include "code\modules\mob\observer\virtual\base.dm" #include "code\modules\mob\observer\virtual\helpers.dm" From da2fbfc1566d0bc7060b413a1309111efe8c5594 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 5 Dec 2024 22:01:24 -0500 Subject: [PATCH 0064/1331] Remove unused item worth define --- code/modules/item_worth/_helpers.dm | 3 --- nebula.dme | 1 - 2 files changed, 4 deletions(-) delete mode 100644 code/modules/item_worth/_helpers.dm diff --git a/code/modules/item_worth/_helpers.dm b/code/modules/item_worth/_helpers.dm deleted file mode 100644 index 8841debc67a8..000000000000 --- a/code/modules/item_worth/_helpers.dm +++ /dev/null @@ -1,3 +0,0 @@ -//Workaround by Ginja due to the fact initial(parent_type) does not work. - -#define PARENT(x) text2path(replacetext("[x]", regex("/\[^/\]+$"), "")) \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index a48e4415c232..2033d9acb32e 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2630,7 +2630,6 @@ #include "code\modules\item_effects\item_effect_charges.dm" #include "code\modules\item_effects\item_effect_debug.dm" #include "code\modules\item_effects\item_effect_item.dm" -#include "code\modules\item_worth\_helpers.dm" #include "code\modules\keybindings\_defines.dm" #include "code\modules\keybindings\_keybindings.dm" #include "code\modules\keybindings\admin.dm" From 0b9bea4cbe068eb6ffcddaf8a1c6ec7634f627e2 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 11 Dec 2024 01:51:16 -0500 Subject: [PATCH 0065/1331] Bump Del count for comments --- test/check-paths.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/check-paths.sh b/test/check-paths.sh index 77f46145433e..64ca05563de8 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -24,7 +24,7 @@ exactly() { # exactly N name search [mode] [filter] # With the potential exception of << if you increase any of these numbers you're probably doing it wrong # Additional exception August 2020: \b is a regex symbol as well as a BYOND macro. exactly 1 "escapes" '\\\\(red|blue|green|black|b|i[^mc])' -exactly 6 "Del()s" '\WDel\(' +exactly 8 "Del()s" '\WDel\(' exactly 2 "/atom text paths" '"/atom' exactly 2 "/area text paths" '"/area' exactly 2 "/datum text paths" '"/datum' From 2d69b12ab4403df901cc00b341a97a7da13c743e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 11 Dec 2024 01:51:31 -0500 Subject: [PATCH 0066/1331] Add guard for mobs destroyed in handle_environment --- code/modules/mob/living/life.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index e73ef41b016b..22631be20915 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -23,6 +23,8 @@ //Handle temperature/pressure differences between body and environment handle_environment(loc.return_air()) + if(QDELETED(src)) // Destroyed by fire or pressure damage in handle_environment() + return PROCESS_KILL handle_regular_status_updates() // Status & health update, are we dead or alive etc. handle_stasis() From 38aa9d3ed745366ed1caa444fe573a743107af52 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Dec 2024 17:27:11 -0500 Subject: [PATCH 0067/1331] Adjust counts in check-paths.sh --- test/check-paths.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/check-paths.sh b/test/check-paths.sh index 64ca05563de8..dcf902b9335e 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -32,12 +32,12 @@ exactly 2 "/mob text paths" '"/mob' exactly 6 "/obj text paths" '"/obj' exactly 10 "/turf text paths" '"/turf' exactly 1 "world<< uses" 'world<<|world[[:space:]]<<' -exactly 93 "'in world' uses" 'in world' +exactly 90 "'in world' uses" 'in world' exactly 1 "world.log<< uses" 'world.log<<|world.log[[:space:]]<<' exactly 18 "<< uses" '(?> uses" '>>(?!>)' -P exactly 0 "incorrect indentations" '^( {4,})' -P -exactly 24 "text2path uses" 'text2path' +exactly 22 "text2path uses" 'text2path' exactly 4 "update_icon() override" '/update_icon\((.*)\)' -P exactly 0 "goto uses" 'goto ' exactly 9 "atom/New uses" '^/(obj|atom|area|mob|turf).*/New\(' From c252f04be75508e97487b7225919972fc53f9450 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Fri, 13 Dec 2024 18:05:46 +1100 Subject: [PATCH 0068/1331] Reloacing/cleaning up some bodytype logic. --- .../_bodytype.dm} | 196 +++++++++++------- .../bodytype_abilities.dm} | 0 .../bodytype_crystalline.dm} | 0 .../bodytype_helpers.dm} | 7 +- .../bodytype_offsets.dm} | 0 .../bodytype_prosthetic.dm} | 44 ++-- .../bodytype_prosthetic_models.dm} | 0 .../bodytype_quadruped.dm} | 0 .../bodytype_random.dm} | 0 code/modules/mob/living/human/update_icons.dm | 2 +- code/modules/mob/living/life.dm | 2 +- .../organs/external/_external_icons.dm | 4 +- code/modules/organs/internal/eyes.dm | 4 +- .../_prosthetics.dm => organ_prosthetics.dm} | 0 code/modules/species/species_getters.dm | 3 - .../species/species_shapeshifter_bodytypes.dm | 5 +- .../species/station/human_bodytypes.dm | 2 +- .../unathi/datum/species_bodytypes.dm | 2 +- mods/species/drakes/species.dm | 3 - mods/species/drakes/species_bodytypes.dm | 1 + nebula.dme | 20 +- 21 files changed, 169 insertions(+), 126 deletions(-) rename code/modules/{species/species_bodytype.dm => bodytype/_bodytype.dm} (82%) rename code/modules/{species/species_bodytype_abilities.dm => bodytype/bodytype_abilities.dm} (100%) rename code/modules/{species/species_crystalline_bodytypes.dm => bodytype/bodytype_crystalline.dm} (100%) rename code/modules/{species/species_bodytype_helpers.dm => bodytype/bodytype_helpers.dm} (96%) rename code/modules/{species/species_bodytype_offsets.dm => bodytype/bodytype_offsets.dm} (100%) rename code/modules/{organs/prosthetics/prosthetics_manufacturer.dm => bodytype/bodytype_prosthetic.dm} (70%) rename code/modules/{organs/prosthetics/prosthetics_manufacturer_models.dm => bodytype/bodytype_prosthetic_models.dm} (100%) rename code/modules/{species/species_bodytype_quadruped.dm => bodytype/bodytype_quadruped.dm} (100%) rename code/modules/{species/species_bodytype_random.dm => bodytype/bodytype_random.dm} (100%) rename code/modules/organs/{prosthetics/_prosthetics.dm => organ_prosthetics.dm} (100%) diff --git a/code/modules/species/species_bodytype.dm b/code/modules/bodytype/_bodytype.dm similarity index 82% rename from code/modules/species/species_bodytype.dm rename to code/modules/bodytype/_bodytype.dm index 95394d64551c..ec0d6033d5f4 100644 --- a/code/modules/species/species_bodytype.dm +++ b/code/modules/bodytype/_bodytype.dm @@ -4,70 +4,89 @@ var/global/list/bodytypes_by_category = list() decl_flags = DECL_FLAG_MANDATORY_UID abstract_type = /decl/bodytype /// Name used in general. - var/name = "default" + var/name = "default" /// Name used in preference bodytype selection. Defaults to name. var/pref_name /// Seen when examining a prosthetic limb, if non-null. var/desc + /// The base 'standard' icon set for this bodytype's organs. var/icon_base + /// A variant on icon_base for used when a limb has the mutated status. var/icon_deformed + /// An icon used to draw from for cosmetic sprite accessories. var/cosmetics_icon + /// A set of overlays to draw from when a limb has been bandaged. TODO: merge with bandage markings. var/bandages_icon - var/bodytype_flag = BODY_EQUIP_FLAG_HUMANOID - var/bodytype_category = BODYTYPE_OTHER - var/limb_icon_intensity = 1.5 + /// A flag used on clothing to determine if this bodytype can wear a given clothing item. + var/bodytype_flag = BODY_EQUIP_FLAG_HUMANOID + /// A general label for bodytypes. + var/bodytype_category = BODYTYPE_OTHER + /// A general intensity value applied to limbs during apply_limb_colouration(). + var/limb_icon_intensity + /// A set of states to use when rendering blood over the top of worn clothing. TODO: move this to item icons. var/blood_overlays - var/vulnerable_location = BP_GROIN //organ tag that can be kicked for increased pain, previously `sexybits_location`. - var/limb_blend = ICON_ADD - var/damage_overlays = 'icons/mob/human_races/species/default_damage_overlays.dmi' - var/husk_icon = 'icons/mob/human_races/species/default_husk.dmi' - var/skeletal_icon = 'icons/mob/human_races/species/human/skeleton.dmi' - var/icon_template = 'icons/mob/human_races/species/template.dmi' // Used for mob icon generation for non-32x32 species. - var/ignited_icon = 'icons/mob/OnFire.dmi' + /// An organ tag that can be kicked for increased pain, previously `sexybits_location`. + var/vulnerable_location = BP_GROIN + /// What blend mode should be used when drawing this limb icon? + var/limb_blend = ICON_ADD + /// A set of base icons used to generate damaged states for brute/burn injuries to limbs. + var/damage_overlays = 'icons/mob/human_races/species/default_damage_overlays.dmi' + /// An overlay applied when a mob is 'husked' via burn damage. + var/husk_icon = 'icons/mob/human_races/species/default_husk.dmi' + /// An alternate icon set to use when a mob or organ has been skeletonised. + var/skeletal_icon = 'icons/mob/human_races/species/human/skeleton.dmi' + /// Used for mob icon generation for non-32x32 species. + var/icon_template = 'icons/mob/human_races/species/template.dmi' + /// Drawn over a mob when the mob is on fire. + var/ignited_icon = 'icons/mob/OnFire.dmi' + /// Drawn over mob bodyparts when they are surgically open/retracted. + var/surgery_overlay_icon = 'icons/mob/surgery.dmi' + /// Used to retrieve bodytypes by pronoun type in get_bodytype_by_pronouns() var/associated_gender - var/appearance_flags = 0 // Appearance/display related features. - - /// Used when filing your nails. + /// Appearance/display related features. + var/appearance_flags = 0 + /// What noun is used when filing your nails? var/nail_noun /// What tech levels should limbs of this type use/need? - var/limb_tech = @'{"biotech":2}' - var/icon_cache_uid + var/limb_tech = @'{"biotech":2}' /// Determines if eyes should render on heads using this bodytype. - var/has_eyes = TRUE + var/has_eyes = TRUE /// Prefixed to the initial name of the limb, if non-null. var/modifier_string /// Modifies min and max broken damage for the limb. - var/hardiness = 1 + var/hardiness = 1 /// Applies a slowdown value to this limb. - var/movement_slowdown = 0 + var/movement_slowdown = 0 /// Determines if this bodytype can be repaired by nanopaste, sparks when damaged, can malfunction, and can take EMP damage. - var/is_robotic = FALSE + var/is_robotic = FALSE /// For hands, determines the dexterity value passed to get_manual_dexterity(). If null, defers to species. - var/manual_dexterity = null + var/manual_dexterity = null /// Determines how the limb behaves with regards to manual attachment/detachment. - var/modular_limb_tier = MODULAR_BODYPART_INVALID - // Expected organ types per category, used only for stance checking at time of writing. - var/list/organs_by_category = list() - // Expected organ tags per category, used only for stance checking at time of writing. - var/list/organ_tags_by_category = list() - + var/modular_limb_tier = MODULAR_BODYPART_INVALID + /// Expected organ types per category, used only for stance checking at time of writing. + VAR_PRIVATE/list/_organs_by_category + /// Expected organ tags per category, used only for stance checking at time of writing. + VAR_PRIVATE/list/_organ_tags_by_category + /// A set of slot strings to modifier strings, used to modify clothing if the state is available in the icon. var/list/onmob_state_modifiers + /// An intensity value applied to limbs with this bodytype when creating the health status indicator HUD element. var/health_hud_intensity = 1 - - var/pixel_offset_x = 0 // Used for offsetting large icons. - var/pixel_offset_y = 0 // Used for offsetting large icons. - var/pixel_offset_z = 0 // Used for offsetting large icons. - - var/antaghud_offset_x = 0 // As above, but specifically for the antagHUD indicator. - var/antaghud_offset_y = 0 // As above, but specifically for the antagHUD indicator. - - var/eye_offset = 0 // Amount to shift eyes on the Y axis to correct for non-32px height. - - var/z_flags = 0 - - var/list/prone_overlay_offset = list(0, 0) // amount to shift overlays when lying - - // Per-bodytype per-zone message strings, see /mob/proc/get_hug_zone_messages + /// Used for offsetting large icons. + var/pixel_offset_x = 0 + /// Used for offsetting large icons. + var/pixel_offset_y = 0 + /// Used for offsetting large icons. + var/pixel_offset_z = 0 + /// Used to offset the antagHUD indicator for wide or tall mobs. + var/antaghud_offset_x = 0 + /// Used to offset the antagHUD indicator for wide or tall mobs. + var/antaghud_offset_y = 0 + /// Used to apply flags like WIDE_LOAD to nonstandard mobs. + var/z_flags = 0 + /// Amount to shift overlays when lying. TODO: check if this is still needed with KEEP_TOGETHER + var/list/prone_overlay_offset + + /// Per-bodytype per-zone message strings, see /mob/proc/get_hug_zone_messages var/list/default_hug_message var/list/hug_messages = list( BP_L_HAND = list( @@ -88,6 +107,7 @@ var/global/list/bodytypes_by_category = list() ) ) + /// For emotes that check bodytypes for sounds, this list will partially override the general emote_sounds list. var/list/override_emote_sounds = list( "cough" = list( 'sound/voice/emotes/f_cougha.ogg', @@ -97,6 +117,8 @@ var/global/list/bodytypes_by_category = list() 'sound/voice/emotes/f_sneeze.ogg' ) ) + + /// Provides bodytype-specific sounds to emote that need them. var/list/emote_sounds = list( "whistle" = list('sound/voice/emotes/longwhistle.ogg'), "qwhistle" = list('sound/voice/emotes/shortwhistle.ogg'), @@ -106,24 +128,26 @@ var/global/list/bodytypes_by_category = list() var/list/broadcast_emote_sounds = list( "swhistle" = list('sound/voice/emotes/summon_whistle.ogg') ) + + /// Sounds that play when a mob with this bodytype goes prone. var/list/bodyfall_sounds = list( 'sound/foley/meat1.ogg', 'sound/foley/meat2.ogg' ) // Used for initializing prefs/preview - var/base_color = COLOR_BLACK - var/base_eye_color = COLOR_BLACK + var/base_color = COLOR_BLACK + var/base_eye_color = COLOR_BLACK /// Used to initialize organ material var/organ_material = /decl/material/solid/organic/meat /// Used to initialize organ matter - var/list/matter = null + var/list/matter /// The reagent organs are filled with, which currently affects what mobs that eat the organ will receive. /// TODO: Remove this in a later matter edibility refactor. var/edible_reagent = /decl/material/solid/organic/meat /// A bitfield representing various bodytype-specific features. - var/body_flags = 0 + var/body_flags = 0 /// Used to modify the arterial_bleed_severity of organs. var/arterial_bleed_multiplier = 1 /// Associative list of organ_tag = "encased value". If set, sets the organ's encased var to the corresponding value; used in surgery. @@ -167,53 +191,66 @@ var/global/list/bodytypes_by_category = list() var/vision_organ /// If set, an organ with this tag is required for breathing var/breathing_organ - - var/list/override_organ_types // Used for species that only need to change one or two entries in has_organ. - + /// Used for species that only need to change one or two entries in has_organ. + var/list/override_organ_types + /// Used for comparing ages between mobs. var/age_descriptor = /datum/appearance_descriptor/age + /// Used for comparing various cosmetic properties between mobs. var/list/appearance_descriptors = list( /datum/appearance_descriptor/height = 1, /datum/appearance_descriptor/build = 1 ) - /// Losing an organ from this list will give a grace period of `vital_organ_failure_death_delay` then kill the mob. var/list/vital_organs = list(BP_BRAIN) /// The grace period before mob death when an organ in `vital_organs` is lost var/vital_organ_failure_death_delay = 25 SECONDS + /// The relative size of a mob. Consistent with ITEM_SIZE defines. var/mob_size = MOB_SIZE_MEDIUM - + /// A list of sprite accessories applied to this mob by default. var/list/default_sprite_accessories // Darksight handling /// Fractional multiplier (0 to 1) for the base alpha of the darkness overlay. A value of 1 means darkness is completely invisible. - var/eye_base_low_light_vision = 0 + var/eye_base_low_light_vision = 0 /// The lumcount (turf luminosity) threshold under which adaptive low light vision will begin processing. - var/eye_low_light_vision_threshold = 0.3 + var/eye_low_light_vision_threshold = 0.3 /// Fractional multiplier for the overall effectiveness of low light vision for this species. Caps the final alpha value of the darkness plane. - var/eye_low_light_vision_effectiveness = 0 + var/eye_low_light_vision_effectiveness = 0 /// The rate at which low light vision adjusts towards the final value, as a fractional multiplier of the difference between the current and target alphas. ie. set to 0.15 for a 15% shift towards the target value each tick. var/eye_low_light_vision_adjustment_speed = 0.15 + /// How many tiles can this mob see in the dark? Note that degree of visibility is determined by low light vision vars above, this is just the radius. + var/eye_darksight_range = 2 - // Other eye vars. - var/eye_contaminant_guard = 0 + /// Do the eyes of this bodytype protect against chlorine and such? + var/eye_contaminant_guard = 0 + /// Are the eyes of this bodytype resistant to flashes? var/eye_innate_flash_protection = FLASH_PROTECTION_NONE - var/eye_icon = 'icons/mob/human_races/species/default_eyes.dmi' - var/apply_eye_colour = TRUE - var/eye_darksight_range = 2 - var/eye_blend = ICON_ADD + /// Icon to draw eye overlays from. + var/eye_icon = 'icons/mob/human_races/species/default_eyes.dmi' + /// Do the eyes of this mob apply a pref colour like hair? + var/apply_eye_colour = TRUE + /// What blend mode is used to draw eyes onto the mob? + var/eye_blend = ICON_ADD /// Stun from blindness modifier. - var/eye_flash_mod = 1 + var/eye_flash_mod = 1 // Bodytype temperature damage thresholds. - var/cold_level_1 = 243 // Cold damage level 1 below this point. -30 Celsium degrees - var/cold_level_2 = 200 // Cold damage level 2 below this point. - var/cold_level_3 = 120 // Cold damage level 3 below this point. - var/heat_level_1 = 360 // Heat damage level 1 above this point. - var/heat_level_2 = 400 // Heat damage level 2 above this point. - var/heat_level_3 = 1000 // Heat damage level 3 above this point. - - // Temperature comfort levels and strings. + /// Cold damage level 1 below this point. -30 Celsium degrees + var/cold_level_1 = 243 + /// Cold damage level 2 below this point. + var/cold_level_2 = 200 + /// Cold damage level 3 below this point. + var/cold_level_3 = 120 + /// Heat damage level 1 above this point. + var/heat_level_1 = 360 + /// Heat damage level 2 above this point. + var/heat_level_2 = 400 + /// Heat damage level 3 above this point. + var/heat_level_3 = 1000 + + // Above this point, discomfort strings will be shown. var/heat_discomfort_level = 315 + // Below this point, discomfort strings will be shown. var/cold_discomfort_level = 285 /// Aesthetic messages about feeling warm. var/list/heat_discomfort_strings = list( @@ -372,15 +409,19 @@ var/global/list/bodytypes_by_category = list() organ_data["descriptor"] = initial(organ.name) var/organ_cat = initial(organ.organ_category) if(organ_cat) - LAZYADD(organs_by_category[organ_cat], organ) - LAZYADD(organ_tags_by_category[organ_cat], organ_tag) + LAZYINITLIST(_organs_by_category) + LAZYADD(_organs_by_category[organ_cat], organ) + LAZYINITLIST(_organ_tags_by_category) + LAZYADD(_organ_tags_by_category[organ_cat], organ_tag) for(var/organ_tag in has_organ) var/obj/item/organ/organ = has_organ[organ_tag] var/organ_cat = initial(organ.organ_category) if(organ_cat) - LAZYADD(organs_by_category[organ_cat], organ) - LAZYADD(organ_tags_by_category[organ_cat], organ_tag) + LAZYINITLIST(_organs_by_category) + LAZYADD(_organs_by_category[organ_cat], organ) + LAZYINITLIST(_organ_tags_by_category) + LAZYADD(_organ_tags_by_category[organ_cat], organ_tag) if(LAZYLEN(appearance_descriptors)) for(var/desctype in appearance_descriptors) @@ -394,13 +435,18 @@ var/global/list/bodytypes_by_category = list() appearance_descriptors.Insert(1, age.name) appearance_descriptors[age.name] = age +/decl/bodytype/proc/get_expected_organ_tags_for_category(var/category) + return LAZYACCESS(_organ_tags_by_category, category) + /decl/bodytype/proc/get_expected_organ_count_for_categories(var/list/categories) . = 0 for(var/category in categories) - if(category && (category in organs_by_category)) - . += length(organs_by_category[category]) + if(category && (category in _organs_by_category)) + . += length(_organs_by_category[category]) /decl/bodytype/proc/apply_limb_colouration(var/obj/item/organ/external/E, var/icon/applying) + if(!isnull(limb_icon_intensity)) + applying.SetIntensity(limb_icon_intensity) return applying /decl/bodytype/proc/check_dismember_type_override(var/disintegrate) diff --git a/code/modules/species/species_bodytype_abilities.dm b/code/modules/bodytype/bodytype_abilities.dm similarity index 100% rename from code/modules/species/species_bodytype_abilities.dm rename to code/modules/bodytype/bodytype_abilities.dm diff --git a/code/modules/species/species_crystalline_bodytypes.dm b/code/modules/bodytype/bodytype_crystalline.dm similarity index 100% rename from code/modules/species/species_crystalline_bodytypes.dm rename to code/modules/bodytype/bodytype_crystalline.dm diff --git a/code/modules/species/species_bodytype_helpers.dm b/code/modules/bodytype/bodytype_helpers.dm similarity index 96% rename from code/modules/species/species_bodytype_helpers.dm rename to code/modules/bodytype/bodytype_helpers.dm index bff05c2fa816..53e30984d13b 100644 --- a/code/modules/species/species_bodytype_helpers.dm +++ b/code/modules/bodytype/bodytype_helpers.dm @@ -5,9 +5,7 @@ return "Standing" /decl/bodytype/proc/get_icon_cache_uid(var/mob/H) - if(!icon_cache_uid) - icon_cache_uid = "[sequential_id(/decl/bodytype)]" - return icon_cache_uid + return uid /decl/bodytype/proc/get_bandages_icon(var/mob/living/human/H) return bandages_icon @@ -85,3 +83,6 @@ /decl/bodytype/proc/adjust_status(mob/living/target, condition, amount) return amount + +/decl/bodytype/proc/get_surgery_overlay_icon(var/mob/living/user) + return surgery_overlay_icon diff --git a/code/modules/species/species_bodytype_offsets.dm b/code/modules/bodytype/bodytype_offsets.dm similarity index 100% rename from code/modules/species/species_bodytype_offsets.dm rename to code/modules/bodytype/bodytype_offsets.dm diff --git a/code/modules/organs/prosthetics/prosthetics_manufacturer.dm b/code/modules/bodytype/bodytype_prosthetic.dm similarity index 70% rename from code/modules/organs/prosthetics/prosthetics_manufacturer.dm rename to code/modules/bodytype/bodytype_prosthetic.dm index 91915558ed67..c01812a75787 100644 --- a/code/modules/organs/prosthetics/prosthetics_manufacturer.dm +++ b/code/modules/bodytype/bodytype_prosthetic.dm @@ -1,19 +1,20 @@ /decl/bodytype/prosthetic - abstract_type = /decl/bodytype/prosthetic - icon_base = 'icons/mob/human_races/cyberlimbs/robotic.dmi' - desc = "A generic unbranded robotic prosthesis." - limb_tech = @'{"engineering":1,"materials":1,"magnets":1}' - modifier_string = "robotic" - is_robotic = TRUE - body_flags = BODY_FLAG_NO_DNA | BODY_FLAG_NO_DEFIB | BODY_FLAG_NO_STASIS | BODY_FLAG_NO_PAIN | BODY_FLAG_NO_EAT - organ_material = /decl/material/solid/metal/steel - appearance_flags = HAS_EYE_COLOR - eye_flash_mod = 1 - eye_darksight_range = 2 - associated_gender = null + abstract_type = /decl/bodytype/prosthetic + icon_base = 'icons/mob/human_races/cyberlimbs/robotic.dmi' + surgery_overlay_icon = null + desc = "A generic unbranded robotic prosthesis." + limb_tech = @'{"engineering":1,"materials":1,"magnets":1}' + modifier_string = "robotic" + is_robotic = TRUE + body_flags = BODY_FLAG_NO_DNA | BODY_FLAG_NO_DEFIB | BODY_FLAG_NO_STASIS | BODY_FLAG_NO_PAIN | BODY_FLAG_NO_EAT + organ_material = /decl/material/solid/metal/steel + appearance_flags = HAS_EYE_COLOR + eye_flash_mod = 1 + eye_darksight_range = 2 + associated_gender = null edible_reagent = null emote_sounds = list( - "whistle" = list('sound/voice/emotes/longwhistle_robot.ogg'), + "whistle" = list('sound/voice/emotes/longwhistle_robot.ogg'), "qwhistle" = list('sound/voice/emotes/shortwhistle_robot.ogg'), "swhistle" = list('sound/voice/emotes/summon_whistle_robot.ogg'), "wwhistle" = list('sound/voice/emotes/wolfwhistle_robot.ogg') @@ -38,17 +39,20 @@ BP_EYES = /obj/item/organ/internal/eyes, BP_CELL = /obj/item/organ/internal/cell ) - cold_level_1 = SYNTH_COLD_LEVEL_1 - cold_level_2 = SYNTH_COLD_LEVEL_2 - cold_level_3 = SYNTH_COLD_LEVEL_3 - heat_level_1 = SYNTH_HEAT_LEVEL_1 - heat_level_2 = SYNTH_HEAT_LEVEL_2 - heat_level_3 = SYNTH_HEAT_LEVEL_3 + cold_discomfort_strings = null - heat_discomfort_level = 373.15 + cold_level_1 = SYNTH_COLD_LEVEL_1 + cold_level_2 = SYNTH_COLD_LEVEL_2 + cold_level_3 = SYNTH_COLD_LEVEL_3 + + heat_level_1 = SYNTH_HEAT_LEVEL_1 + heat_level_2 = SYNTH_HEAT_LEVEL_2 + heat_level_3 = SYNTH_HEAT_LEVEL_3 + heat_discomfort_level = 373.15 heat_discomfort_strings = list( "You are dangerously close to overheating!" ) + /// Determines which bodyparts can use this limb. var/list/applies_to_part /// Prosthetics of this type are not available in chargen unless the map has the required tech level. diff --git a/code/modules/organs/prosthetics/prosthetics_manufacturer_models.dm b/code/modules/bodytype/bodytype_prosthetic_models.dm similarity index 100% rename from code/modules/organs/prosthetics/prosthetics_manufacturer_models.dm rename to code/modules/bodytype/bodytype_prosthetic_models.dm diff --git a/code/modules/species/species_bodytype_quadruped.dm b/code/modules/bodytype/bodytype_quadruped.dm similarity index 100% rename from code/modules/species/species_bodytype_quadruped.dm rename to code/modules/bodytype/bodytype_quadruped.dm diff --git a/code/modules/species/species_bodytype_random.dm b/code/modules/bodytype/bodytype_random.dm similarity index 100% rename from code/modules/species/species_bodytype_random.dm rename to code/modules/bodytype/bodytype_random.dm diff --git a/code/modules/mob/living/human/update_icons.dm b/code/modules/mob/living/human/update_icons.dm index d1c508dcd447..34fcb8ccc310 100644 --- a/code/modules/mob/living/human/update_icons.dm +++ b/code/modules/mob/living/human/update_icons.dm @@ -139,7 +139,7 @@ Please contact me on #coderbus IRC. ~Carn x return var/matrix/M = matrix() - if(current_posture?.prone && (root_bodytype.prone_overlay_offset[1] || root_bodytype.prone_overlay_offset[2])) + if(current_posture?.prone && !isnull(root_bodytype.prone_overlay_offset) && (root_bodytype.prone_overlay_offset[1] || root_bodytype.prone_overlay_offset[2])) M.Translate(root_bodytype.prone_overlay_offset[1], root_bodytype.prone_overlay_offset[2]) var/mangle_planes = FALSE diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index f81a0412fcce..87bd9c645275 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -631,7 +631,7 @@ // Calculate the expected and actual number of functioning legs we have. var/has_sufficient_working_legs = TRUE - var/list/root_limb_tags = root_bodytype.organ_tags_by_category[ORGAN_CATEGORY_STANCE_ROOT] + var/list/root_limb_tags = root_bodytype.get_expected_organ_tags_for_category(ORGAN_CATEGORY_STANCE_ROOT) var/minimum_working_legs = ceil(length(root_limb_tags) * 0.5) if(minimum_working_legs > 0) var/leg_count = 0 diff --git a/code/modules/organs/external/_external_icons.dm b/code/modules/organs/external/_external_icons.dm index ed9e91407341..7c7c5b2e24e8 100644 --- a/code/modules/organs/external/_external_icons.dm +++ b/code/modules/organs/external/_external_icons.dm @@ -21,9 +21,7 @@ var/global/list/limb_icon_cache = list() /obj/item/organ/external/proc/get_surgery_overlay_icon() if(limb_flags & ORGAN_FLAG_SKELETAL) return null - if(BP_IS_PROSTHETIC(src)) - return null - return species?.get_surgery_overlay_icon(owner) + return bodytype?.get_surgery_overlay_icon(owner) /obj/item/organ/external/proc/sync_colour_to_human(var/mob/living/human/human) _icon_cache_key = null diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index f193afaa4daa..9d887adb99aa 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -40,13 +40,11 @@ var/modifier = owner?.get_overlay_state_modifier() var/eye_state = modifier ? "eyes[modifier]" : "eyes" last_cached_eye_colour = eye_colour - last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[bodytype.eye_offset]-[eye_state]" + last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[eye_state]" if(!bodytype.eye_icon) return if(!global.eye_icon_cache[last_eye_cache_key]) var/icon/eyes_icon = icon(icon = bodytype.eye_icon, icon_state = eye_state) - if(bodytype.eye_offset) - eyes_icon.Shift(NORTH, bodytype.eye_offset) if(bodytype.apply_eye_colour) eyes_icon.Blend(last_cached_eye_colour, bodytype.eye_blend) global.eye_icon_cache[last_eye_cache_key] = eyes_icon diff --git a/code/modules/organs/prosthetics/_prosthetics.dm b/code/modules/organs/organ_prosthetics.dm similarity index 100% rename from code/modules/organs/prosthetics/_prosthetics.dm rename to code/modules/organs/organ_prosthetics.dm diff --git a/code/modules/species/species_getters.dm b/code/modules/species/species_getters.dm index 90b97759adc3..f2e3abce8458 100644 --- a/code/modules/species/species_getters.dm +++ b/code/modules/species/species_getters.dm @@ -19,9 +19,6 @@ /decl/species/proc/get_vision_flags(var/mob/living/human/H) return vision_flags -/decl/species/proc/get_surgery_overlay_icon(var/mob/living/human/H) - return 'icons/mob/surgery.dmi' - /decl/species/proc/get_footstep(var/mob/living/human/H, var/footstep_type) return diff --git a/code/modules/species/species_shapeshifter_bodytypes.dm b/code/modules/species/species_shapeshifter_bodytypes.dm index 9c3e4d00f154..a1ee2815579f 100644 --- a/code/modules/species/species_shapeshifter_bodytypes.dm +++ b/code/modules/species/species_shapeshifter_bodytypes.dm @@ -5,7 +5,7 @@ /decl/bodytype/shapeshifter/apply_limb_colouration(var/obj/item/organ/external/E, var/icon/applying) applying.MapColors("#4d4d4d","#969696","#1c1c1c", "#000000") - applying.SetIntensity(limb_icon_intensity) + applying = ..() applying += rgb(,,,180) // Makes the icon translucent, SO INTUITIVE TY BYOND return applying @@ -38,7 +38,8 @@ /decl/bodytype/shapeshifter/get_icon_cache_uid(var/mob/H) . = ..() if(H) - . = "[.]-[wrapped_species_by_ref["\ref[H]"]]" + var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) + if(S) return S.default_bodytype.get_icon_cache_uid(H) /decl/bodytype/shapeshifter/apply_bodytype_organ_modifications(obj/item/organ/org) ..() diff --git a/code/modules/species/station/human_bodytypes.dm b/code/modules/species/station/human_bodytypes.dm index be6879a20a49..b6c753b55143 100644 --- a/code/modules/species/station/human_bodytypes.dm +++ b/code/modules/species/station/human_bodytypes.dm @@ -8,7 +8,7 @@ bandages_icon = 'icons/mob/bandage.dmi' limb_icon_intensity = 0.7 associated_gender = FEMALE - onmob_state_modifiers = list(slot_w_uniform_str = "f") + onmob_state_modifiers = list((slot_w_uniform_str) = "f") appearance_flags = HAS_SKIN_TONE_NORMAL | HAS_UNDERWEAR | HAS_EYE_COLOR nail_noun = "nails" uid = "bodytype_human_fem" diff --git a/mods/species/bayliens/unathi/datum/species_bodytypes.dm b/mods/species/bayliens/unathi/datum/species_bodytypes.dm index 33fef3a1188c..31e9a0584382 100644 --- a/mods/species/bayliens/unathi/datum/species_bodytypes.dm +++ b/mods/species/bayliens/unathi/datum/species_bodytypes.dm @@ -10,7 +10,7 @@ limb_icon_intensity = 0.7 health_hud_intensity = 2 associated_gender = FEMALE - onmob_state_modifiers = list(slot_w_uniform_str = "f") + onmob_state_modifiers = list((slot_w_uniform_str) = "f") movement_slowdown = 0.5 base_color = "#066000" appearance_flags = HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR diff --git a/mods/species/drakes/species.dm b/mods/species/drakes/species.dm index 12b6b2109d0d..e752bcde2f49 100644 --- a/mods/species/drakes/species.dm +++ b/mods/species/drakes/species.dm @@ -69,9 +69,6 @@ pain_emotes_with_pain_level = adult_pain_emotes_with_pain_level return ..() -/decl/species/grafadreka/get_surgery_overlay_icon(var/mob/living/human/H) - return null // todo: 'mods/species/drakes/icons/surgery.dmi' - // Stub for muscle memory of the Sit verb on Polaris. /mob/living/human/proc/drake_sit() set name = "Sit" diff --git a/mods/species/drakes/species_bodytypes.dm b/mods/species/drakes/species_bodytypes.dm index daec4b0ea067..54f8feb36f05 100644 --- a/mods/species/drakes/species_bodytypes.dm +++ b/mods/species/drakes/species_bodytypes.dm @@ -28,6 +28,7 @@ eye_icon = 'mods/species/drakes/icons/eyes.dmi' icon_template = 'mods/species/drakes/icons/template.dmi' skeletal_icon = 'mods/species/drakes/icons/skeleton.dmi' + surgery_overlay_icon = null // todo: 'mods/species/drakes/icons/surgery.dmi' bodytype_category = BODYTYPE_GRAFADREKA eye_blend = ICON_MULTIPLY limb_blend = ICON_MULTIPLY diff --git a/nebula.dme b/nebula.dme index f083035f50c2..0077d792a408 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1834,6 +1834,15 @@ #include "code\modules\blood\blood.dm" #include "code\modules\blood\blood_types.dm" #include "code\modules\blood\blood_types_subtypes.dm" +#include "code\modules\bodytype\_bodytype.dm" +#include "code\modules\bodytype\bodytype_abilities.dm" +#include "code\modules\bodytype\bodytype_crystalline.dm" +#include "code\modules\bodytype\bodytype_helpers.dm" +#include "code\modules\bodytype\bodytype_offsets.dm" +#include "code\modules\bodytype\bodytype_prosthetic.dm" +#include "code\modules\bodytype\bodytype_prosthetic_models.dm" +#include "code\modules\bodytype\bodytype_quadruped.dm" +#include "code\modules\bodytype\bodytype_random.dm" #include "code\modules\brain_interface\_brain_interface.dm" #include "code\modules\brain_interface\interface_radio.dm" #include "code\modules\butchery\_butchery.dm" @@ -3280,6 +3289,7 @@ #include "code\modules\nano\modules\nano_module.dm" #include "code\modules\organs\_organ_setup.dm" #include "code\modules\organs\organ.dm" +#include "code\modules\organs\organ_prosthetics.dm" #include "code\modules\organs\pain.dm" #include "code\modules\organs\ailments\_ailment.dm" #include "code\modules\organs\ailments\ailment_codex.dm" @@ -3319,9 +3329,6 @@ #include "code\modules\organs\internal\stomach.dm" #include "code\modules\organs\internal\voice.dm" #include "code\modules\organs\internal\species\golem.dm" -#include "code\modules\organs\prosthetics\_prosthetics.dm" -#include "code\modules\organs\prosthetics\prosthetics_manufacturer.dm" -#include "code\modules\organs\prosthetics\prosthetics_manufacturer_models.dm" #include "code\modules\overmap\_defines.dm" #include "code\modules\overmap\_overmap.dm" #include "code\modules\overmap\overmap_object.dm" @@ -3757,13 +3764,6 @@ #include "code\modules\species\species.dm" #include "code\modules\species\species_allergies.dm" #include "code\modules\species\species_attack.dm" -#include "code\modules\species\species_bodytype.dm" -#include "code\modules\species\species_bodytype_abilities.dm" -#include "code\modules\species\species_bodytype_helpers.dm" -#include "code\modules\species\species_bodytype_offsets.dm" -#include "code\modules\species\species_bodytype_quadruped.dm" -#include "code\modules\species\species_bodytype_random.dm" -#include "code\modules\species\species_crystalline_bodytypes.dm" #include "code\modules\species\species_getters.dm" #include "code\modules\species\species_helpers.dm" #include "code\modules\species\species_hud.dm" From f7f02b87e35ad3b4f9cb77693ad0824088c423c4 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 21:19:44 -0500 Subject: [PATCH 0069/1331] Remove unnecessary uses of usr in onclick code --- code/_onclick/ai.dm | 46 ++++++++++++++++++------------------- code/_onclick/click.dm | 2 +- code/_onclick/cyborg.dm | 37 ++++++++++++++--------------- code/_onclick/hud/action.dm | 2 +- code/_onclick/hud/robot.dm | 4 ++-- 5 files changed, 44 insertions(+), 47 deletions(-) diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index e35ab0ceb7e0..56e4ee39cc83 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -60,7 +60,7 @@ if(silicon_camera.in_camera_mode) silicon_camera.camera_mode_off() - silicon_camera.captureimage(A, usr) + silicon_camera.captureimage(A, src) return A.add_hiddenprint(src) @@ -118,10 +118,10 @@ I have no idea why it was in atoms.dm instead of respective files. */ -/atom/proc/AICtrlAltClick() +/atom/proc/AICtrlAltClick(mob/living/silicon/user) -/obj/machinery/door/airlock/AICtrlAltClick() // Electrifies doors. - if(usr.incapacitated()) +/obj/machinery/door/airlock/AICtrlAltClick(mob/living/silicon/user) // Electrifies doors. + if(user.incapacitated()) return if(!electrified_until) // permanent shock @@ -131,14 +131,14 @@ Topic(src, list("command"="electrify_permanently", "activate" = "0")) return 1 -/atom/proc/AICtrlShiftClick() +/atom/proc/AICtrlShiftClick(mob/living/silicon/user) return -/atom/proc/AIShiftClick() +/atom/proc/AIShiftClick(mob/living/silicon/user) return -/obj/machinery/door/airlock/AIShiftClick() // Opens and closes doors! - if(usr.incapacitated()) +/obj/machinery/door/airlock/AIShiftClick(mob/living/silicon/user) // Opens and closes doors! + if(user.incapacitated()) return if(density) Topic(src, list("command"="open", "activate" = "1")) @@ -146,11 +146,11 @@ Topic(src, list("command"="open", "activate" = "0")) return 1 -/atom/proc/AICtrlClick() +/atom/proc/AICtrlClick(mob/living/silicon/user) return FALSE -/obj/machinery/door/airlock/AICtrlClick() // Bolts doors - if(usr.incapacitated()) +/obj/machinery/door/airlock/AICtrlClick(mob/living/silicon/user) // Bolts doors + if(user.incapacitated()) return FALSE if(locked) Topic(src, list("command"="bolts", "activate" = "0")) @@ -158,35 +158,35 @@ Topic(src, list("command"="bolts", "activate" = "1")) return TRUE -/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs. - if(usr.incapacitated()) +/obj/machinery/power/apc/AICtrlClick(mob/living/silicon/user) // turns off/on APCs. + if(user.incapacitated()) return FALSE Topic(src, list("breaker"="1")) return TRUE -/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets - if(usr.incapacitated()) +/obj/machinery/turretid/AICtrlClick(mob/living/silicon/user) //turns off/on Turrets + if(user.incapacitated()) return FALSE Topic(src, list("command"="enable", "value"="[!enabled]")) return TRUE -/atom/proc/AIAltClick(var/atom/A) - return AltClick(A) +/atom/proc/AIAltClick(mob/living/silicon/user) + return AltClick(user) -/obj/machinery/turretid/AIAltClick() //toggles lethal on turrets - if(usr.incapacitated()) +/obj/machinery/turretid/AIAltClick(mob/living/silicon/user) //toggles lethal on turrets + if(user.incapacitated()) return Topic(src, list("command"="lethal", "value"="[!lethal]")) return 1 -/obj/machinery/atmospherics/binary/pump/AIAltClick() - return AltClick() +/obj/machinery/atmospherics/binary/pump/AIAltClick(mob/living/silicon/user) + return AltClick(user) /atom/proc/AIMiddleClick(var/mob/living/silicon/user) return 0 -/obj/machinery/door/airlock/AIMiddleClick() // Toggles door bolt lights. - if(usr.incapacitated()) +/obj/machinery/door/airlock/AIMiddleClick(mob/living/silicon/user) // Toggles door bolt lights. + if(user.incapacitated()) return if(..()) return diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 5be9c8e94478..a2c8f683021e 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -108,7 +108,7 @@ if(holding == A) // Handle attack_self holding.attack_self(src) trigger_aiming(TARGET_CAN_CLICK) - usr.update_inhand_overlays(FALSE) + update_inhand_overlays(FALSE) return 1 //Atoms on your person diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 695fbe49f205..686a08b4cb21 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -42,9 +42,9 @@ if(silicon_camera.in_camera_mode) silicon_camera.camera_mode_off() if(is_component_functioning("camera")) - silicon_camera.captureimage(A, usr) + silicon_camera.captureimage(A, src) else - to_chat(src, "Your camera isn't functional.") + to_chat(src, SPAN_DANGER("Your camera isn't functional.")) return var/obj/item/holding = get_active_held_item() @@ -111,42 +111,39 @@ /atom/proc/BorgCtrlShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden CtrlShiftClick(user) -/obj/machinery/door/airlock/BorgCtrlShiftClick() - AICtrlShiftClick() +/obj/machinery/door/airlock/BorgCtrlShiftClick(mob/living/silicon/robot/user) + AICtrlShiftClick(user) /atom/proc/BorgShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden ShiftClick(user) -/obj/machinery/door/airlock/BorgShiftClick() // Opens and closes doors! Forwards to AI code. - AIShiftClick() +/obj/machinery/door/airlock/BorgShiftClick(mob/living/silicon/robot/user) // Opens and closes doors! Forwards to AI code. + AIShiftClick(user) /atom/proc/BorgCtrlClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden return CtrlClick(user) -/obj/machinery/door/airlock/BorgCtrlClick() // Bolts doors. Forwards to AI code. - return AICtrlClick() +/obj/machinery/door/airlock/BorgCtrlClick(mob/living/silicon/robot/user) // Bolts doors. Forwards to AI code. + return AICtrlClick(user) -/obj/machinery/power/apc/BorgCtrlClick() // turns off/on APCs. Forwards to AI code. - return AICtrlClick() +/obj/machinery/power/apc/BorgCtrlClick(mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. + return AICtrlClick(user) -/obj/machinery/turretid/BorgCtrlClick() //turret control on/off. Forwards to AI code. - return AICtrlClick() +/obj/machinery/turretid/BorgCtrlClick(mob/living/silicon/robot/user) //turret control on/off. Forwards to AI code. + return AICtrlClick(user) /atom/proc/BorgAltClick(var/mob/living/silicon/robot/user) AltClick(user) return -/obj/machinery/door/airlock/BorgAltClick() // Eletrifies doors. Forwards to AI code. - if (!usr.check_intent(I_FLAG_HELP)) - AICtrlAltClick() +/obj/machinery/door/airlock/BorgAltClick(mob/living/silicon/robot/user) // Eletrifies doors. Forwards to AI code. + if (!user.check_intent(I_FLAG_HELP)) + AICtrlAltClick(user) else ..() -/obj/machinery/turretid/BorgAltClick() //turret lethal on/off. Forwards to AI code. - AIAltClick() - -/obj/machinery/atmospherics/binary/pump/BorgAltClick() - return AltClick() +/obj/machinery/turretid/BorgAltClick(mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. + AIAltClick(user) /atom/proc/BorgCtrlAltClick(var/mob/living/silicon/robot/user) CtrlAltClick(user) diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm index dbd392509b6a..e122262cdb96 100644 --- a/code/_onclick/hud/action.dm +++ b/code/_onclick/hud/action.dm @@ -78,7 +78,7 @@ Deactivate() if(AB_GENERIC) if(target && procname) - call(target,procname)(usr) + call(target,procname)(owner) return /datum/action/proc/Activate() diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 6408ee430382..2243f9e49232 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -96,11 +96,11 @@ var/global/obj/screen/robot_inventory R.active_storage.close(R) //Closes the inventory ui. if(!R.module) - to_chat(usr, SPAN_WARNING("No module selected.")) + to_chat(R, SPAN_WARNING("No module selected.")) return if(!R.module.equipment) - to_chat(usr, SPAN_WARNING("Selected module has no equipment available.")) + to_chat(R, SPAN_WARNING("Selected module has no equipment available.")) return if(!R.robot_modules_background) From b7b6dabba1f405d216f12d6b889c3b0e64c8dfd1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 21:20:07 -0500 Subject: [PATCH 0070/1331] Remove unnecessary uses of usr in screenobj code --- code/_onclick/hud/screen/screen_ai_button.dm | 4 +- code/_onclick/hud/screen/screen_equip.dm | 4 +- code/_onclick/hud/screen/screen_exosuit.dm | 10 ++-- code/_onclick/hud/screen/screen_inventory.dm | 2 +- code/_onclick/hud/screen/screen_needs.dm | 56 ++++++++++---------- code/_onclick/hud/screen/screen_pai.dm | 2 +- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/code/_onclick/hud/screen/screen_ai_button.dm b/code/_onclick/hud/screen/screen_ai_button.dm index 03371223da7a..a7f2861002f3 100644 --- a/code/_onclick/hud/screen/screen_ai_button.dm +++ b/code/_onclick/hud/screen/screen_ai_button.dm @@ -8,9 +8,9 @@ var/image/template_undelay /obj/screen/ai_button/handle_click(mob/user, params) - if(!isAI(usr)) + if(!isAI(user)) return TRUE - var/mob/living/silicon/ai/A = usr + var/mob/living/silicon/ai/A = user if(!(ai_verb in A.verbs)) return TRUE diff --git a/code/_onclick/hud/screen/screen_equip.dm b/code/_onclick/hud/screen/screen_equip.dm index 997fe5683bc1..39ca9349ad81 100644 --- a/code/_onclick/hud/screen/screen_equip.dm +++ b/code/_onclick/hud/screen/screen_equip.dm @@ -3,6 +3,6 @@ icon_state = "act_equip" /obj/screen/equip/handle_click(mob/user, params) - if(ishuman(usr)) - var/mob/living/human/H = usr + if(ishuman(user)) + var/mob/living/human/H = user H.quick_equip() diff --git a/code/_onclick/hud/screen/screen_exosuit.dm b/code/_onclick/hud/screen/screen_exosuit.dm index 565de7e3644e..5677d2d93bcb 100644 --- a/code/_onclick/hud/screen/screen_exosuit.dm +++ b/code/_onclick/hud/screen/screen_exosuit.dm @@ -340,19 +340,19 @@ toggled = owner.head.active_sensors . = ..() -/obj/screen/exosuit/toggle/camera/toggled() +/obj/screen/exosuit/toggle/camera/toggled(mob/user) var/mob/living/exosuit/owner = get_owning_exosuit() if(!istype(owner) || !owner.head) - to_chat(usr, SPAN_WARNING("I/O Error: Camera systems not found.")) + to_chat(user, SPAN_WARNING("I/O Error: Camera systems not found.")) return if(!owner.head.vision_flags) - to_chat(usr, SPAN_WARNING("Alternative sensor configurations not found. Contact manufacturer for more details.")) + to_chat(user, SPAN_WARNING("Alternative sensor configurations not found. Contact manufacturer for more details.")) return if(!owner.get_cell()) - to_chat(usr, SPAN_WARNING("The augmented vision systems are offline.")) + to_chat(user, SPAN_WARNING("The augmented vision systems are offline.")) return owner.head.active_sensors = ..() - to_chat(usr, SPAN_NOTICE("[owner.head.name] advanced sensor mode is [owner.head.active_sensors ? "now" : "no longer" ] active.")) + to_chat(user, SPAN_NOTICE("[owner.head.name] advanced sensor mode is [owner.head.active_sensors ? "now" : "no longer" ] active.")) /obj/screen/exosuit/needle vis_flags = VIS_INHERIT_ID diff --git a/code/_onclick/hud/screen/screen_inventory.dm b/code/_onclick/hud/screen/screen_inventory.dm index 4033741be782..5bacceaeef04 100644 --- a/code/_onclick/hud/screen/screen_inventory.dm +++ b/code/_onclick/hud/screen/screen_inventory.dm @@ -55,7 +55,7 @@ // Mark anything we're potentially trying to equip. var/obj/item/mouse_over_atom = mouse_over_atom_ref?.resolve() - if(istype(mouse_over_atom) && !QDELETED(mouse_over_atom) && !usr.get_equipped_item(slot_id)) + if(istype(mouse_over_atom) && !QDELETED(mouse_over_atom) && !owner.get_equipped_item(slot_id)) var/mutable_appearance/MA = new /mutable_appearance(mouse_over_atom) MA.layer = HUD_ABOVE_ITEM_LAYER MA.plane = HUD_PLANE diff --git a/code/_onclick/hud/screen/screen_needs.dm b/code/_onclick/hud/screen/screen_needs.dm index eaa8d41ed3e8..8a4c66ca08b9 100644 --- a/code/_onclick/hud/screen/screen_needs.dm +++ b/code/_onclick/hud/screen/screen_needs.dm @@ -12,15 +12,15 @@ if(user.nutrition_icon == src) switch(icon_state) if("nutrition0") - to_chat(usr, SPAN_WARNING("You are completely stuffed.")) + to_chat(user, SPAN_WARNING("You are completely stuffed.")) if("nutrition1") - to_chat(usr, SPAN_NOTICE("You are not hungry.")) + to_chat(user, SPAN_NOTICE("You are not hungry.")) if("nutrition2") - to_chat(usr, SPAN_NOTICE("You are a bit peckish.")) + to_chat(user, SPAN_NOTICE("You are a bit peckish.")) if("nutrition3") - to_chat(usr, SPAN_WARNING("You are quite hungry.")) + to_chat(user, SPAN_WARNING("You are quite hungry.")) if("nutrition4") - to_chat(usr, SPAN_DANGER("You are starving!")) + to_chat(user, SPAN_DANGER("You are starving!")) /obj/screen/drink name = "hydration" @@ -32,15 +32,15 @@ if(user.hydration_icon == src) switch(icon_state) if("hydration0") - to_chat(usr, SPAN_WARNING("You are overhydrated.")) + to_chat(user, SPAN_WARNING("You are overhydrated.")) if("hydration1") - to_chat(usr, SPAN_NOTICE("You are not thirsty.")) + to_chat(user, SPAN_NOTICE("You are not thirsty.")) if("hydration2") - to_chat(usr, SPAN_NOTICE("You are a bit thirsty.")) + to_chat(user, SPAN_NOTICE("You are a bit thirsty.")) if("hydration3") - to_chat(usr, SPAN_WARNING("You are quite thirsty.")) + to_chat(user, SPAN_WARNING("You are quite thirsty.")) if("hydration4") - to_chat(usr, SPAN_DANGER("You are dying of thirst!")) + to_chat(user, SPAN_DANGER("You are dying of thirst!")) /obj/screen/bodytemp name = "body temperature" @@ -52,23 +52,23 @@ if(user.bodytemp == src) switch(icon_state) if("temp4") - to_chat(usr, SPAN_DANGER("You are being cooked alive!")) + to_chat(user, SPAN_DANGER("You are being cooked alive!")) if("temp3") - to_chat(usr, SPAN_DANGER("Your body is burning up!")) + to_chat(user, SPAN_DANGER("Your body is burning up!")) if("temp2") - to_chat(usr, SPAN_DANGER("You are overheating.")) + to_chat(user, SPAN_DANGER("You are overheating.")) if("temp1") - to_chat(usr, SPAN_WARNING("You are uncomfortably hot.")) + to_chat(user, SPAN_WARNING("You are uncomfortably hot.")) if("temp-4") - to_chat(usr, SPAN_DANGER("You are being frozen solid!")) + to_chat(user, SPAN_DANGER("You are being frozen solid!")) if("temp-3") - to_chat(usr, SPAN_DANGER("You are freezing cold!")) + to_chat(user, SPAN_DANGER("You are freezing cold!")) if("temp-2") - to_chat(usr, SPAN_WARNING("You are dangerously chilled!")) + to_chat(user, SPAN_WARNING("You are dangerously chilled!")) if("temp-1") - to_chat(usr, SPAN_NOTICE("You are uncomfortably cold.")) + to_chat(user, SPAN_NOTICE("You are uncomfortably cold.")) else - to_chat(usr, SPAN_NOTICE("Your body is at a comfortable temperature.")) + to_chat(user, SPAN_NOTICE("Your body is at a comfortable temperature.")) /obj/screen/pressure name = "pressure" @@ -80,15 +80,15 @@ if(user.pressure == src) switch(icon_state) if("pressure2") - to_chat(usr, SPAN_DANGER("The air pressure here is crushing!")) + to_chat(user, SPAN_DANGER("The air pressure here is crushing!")) if("pressure1") - to_chat(usr, SPAN_WARNING("The air pressure here is dangerously high.")) + to_chat(user, SPAN_WARNING("The air pressure here is dangerously high.")) if("pressure-1") - to_chat(usr, SPAN_WARNING("The air pressure here is dangerously low.")) + to_chat(user, SPAN_WARNING("The air pressure here is dangerously low.")) if("pressure-2") - to_chat(usr, SPAN_DANGER("There is nearly no air pressure here!")) + to_chat(user, SPAN_DANGER("There is nearly no air pressure here!")) else - to_chat(usr, SPAN_NOTICE("The local air pressure is comfortable.")) + to_chat(user, SPAN_NOTICE("The local air pressure is comfortable.")) /obj/screen/toxins name = "toxin" @@ -99,9 +99,9 @@ /obj/screen/toxins/handle_click(mob/user, params) if(user.toxin == src) if(icon_state == "tox0") - to_chat(usr, SPAN_NOTICE("The air is clear of toxins.")) + to_chat(user, SPAN_NOTICE("The air is clear of toxins.")) else - to_chat(usr, SPAN_DANGER("The air is eating away at your skin!")) + to_chat(user, SPAN_DANGER("The air is eating away at your skin!")) /obj/screen/oxygen name = "oxygen" @@ -112,6 +112,6 @@ /obj/screen/oxygen/handle_click(mob/user, params) if(user.oxygen == src) if(icon_state == "oxy0") - to_chat(usr, SPAN_NOTICE("You are breathing easy.")) + to_chat(user, SPAN_NOTICE("You are breathing easy.")) else - to_chat(usr, SPAN_DANGER("You cannot breathe!")) + to_chat(user, SPAN_DANGER("You cannot breathe!")) diff --git a/code/_onclick/hud/screen/screen_pai.dm b/code/_onclick/hud/screen/screen_pai.dm index 7059432c1a76..51ceb9d7c27e 100644 --- a/code/_onclick/hud/screen/screen_pai.dm +++ b/code/_onclick/hud/screen/screen_pai.dm @@ -68,7 +68,7 @@ /obj/screen/pai/subsystems/handle_click(mob/user, params) var/mob/living/silicon/pai/pai = user if(istype(pai)) - var/ss_name = input(usr, "Activates the given subsystem", "Subsystems", "") in pai.silicon_subsystems_by_name + var/ss_name = input(user, "Activates the given subsystem", "Subsystems", "") in pai.silicon_subsystems_by_name if (!ss_name) return var/stat_silicon_subsystem/SSS = pai.silicon_subsystems_by_name[ss_name] From 985f0c11d6d8c36f7a2b5382c36b189c879b84c2 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 21:20:33 -0500 Subject: [PATCH 0071/1331] Remove unnecessary uses of usr in machinery folder --- code/game/machinery/CableLayer.dm | 4 ++-- code/game/machinery/Sleeper.dm | 2 +- .../stock_parts/network_lock.dm | 2 +- code/game/machinery/atmo_control.dm | 8 +++---- code/game/machinery/biogenerator.dm | 6 ++--- code/game/machinery/camera/tracking.dm | 23 +++++++++---------- code/game/machinery/floorlayer.dm | 2 +- code/game/machinery/jukebox.dm | 2 +- code/game/machinery/newscaster.dm | 8 +++---- code/game/machinery/nuclear_bomb.dm | 10 ++++---- code/game/machinery/oxygen_pump.dm | 2 +- code/game/machinery/portable_turret.dm | 2 +- code/game/machinery/self_destruct.dm | 2 +- code/game/machinery/spaceheater.dm | 2 +- code/game/machinery/teleporter.dm | 6 ++--- code/game/machinery/turret_control.dm | 2 +- 16 files changed, 41 insertions(+), 42 deletions(-) diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm index 16353f9bf476..55054da3de1e 100644 --- a/code/game/machinery/CableLayer.dm +++ b/code/game/machinery/CableLayer.dm @@ -38,7 +38,7 @@ if(IS_WIRECUTTER(O)) if(cable && cable.amount) - var/m = round(input(usr,"Please specify the length of cable to cut","Cut cable",min(cable.amount,30)) as num, 1) + var/m = round(input(user,"Please specify the length of cable to cut","Cut cable",min(cable.amount,30)) as num, 1) m = min(m, cable.amount) m = min(m, 30) if(m) @@ -47,7 +47,7 @@ var/obj/item/stack/cable_coil/CC = new (get_turf(src)) CC.amount = m else - to_chat(usr, "There's no more cable on the reel.") + to_chat(user, "There's no more cable on the reel.") return TRUE return ..() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index dd697812746a..0efbfce6cd4d 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -248,7 +248,7 @@ /obj/machinery/sleeper/CanUseTopic(user) if(user == occupant) - to_chat(usr, SPAN_WARNING("You can't reach the controls from the inside.")) + to_chat(user, SPAN_WARNING("You can't reach the controls from the inside.")) return STATUS_CLOSE . = ..() diff --git a/code/game/machinery/_machines_base/stock_parts/network_lock.dm b/code/game/machinery/_machines_base/stock_parts/network_lock.dm index ca74dc094445..540984384085 100644 --- a/code/game/machinery/_machines_base/stock_parts/network_lock.dm +++ b/code/game/machinery/_machines_base/stock_parts/network_lock.dm @@ -183,7 +183,7 @@ if(href_list["add_pattern"]) if(length(groups) >= MAX_PATTERNS) - to_chat(usr, SPAN_WARNING("You cannot add more than [MAX_PATTERNS] patterns to \the [src]!")) + to_chat(user, SPAN_WARNING("You cannot add more than [MAX_PATTERNS] patterns to \the [src]!")) return TOPIC_HANDLED LAZYADD(groups, list(list())) return TOPIC_REFRESH diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index a08744dd2515..b9664324917c 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -221,7 +221,7 @@ return TOPIC_REFRESH if(href_list["set_input_tag"]) - var/t = sanitize_safe(input(usr, "Enter the input ID tag.", src.name, src.input_tag), MAX_NAME_LEN) + var/t = sanitize_safe(input(user, "Enter the input ID tag.", src.name, src.input_tag), MAX_NAME_LEN) t = sanitize_safe(t, MAX_NAME_LEN) if (t) src.input_tag = t @@ -229,7 +229,7 @@ return TOPIC_REFRESH if(href_list["set_output_tag"]) - var/t = sanitize_safe(input(usr, "Enter the output ID tag.", src.name, src.output_tag), MAX_NAME_LEN) + var/t = sanitize_safe(input(user, "Enter the output ID tag.", src.name, src.output_tag), MAX_NAME_LEN) t = sanitize_safe(t, MAX_NAME_LEN) if (t) src.output_tag = t @@ -237,7 +237,7 @@ return TOPIC_REFRESH if(href_list["set_sensor_tag"]) - var/t = sanitize_safe(input(usr, "Enter the sensor ID tag.", src.name, src.sensor_tag)) + var/t = sanitize_safe(input(user, "Enter the sensor ID tag.", src.name, src.sensor_tag)) t = sanitize_safe(t, MAX_NAME_LEN) if(t) src.sensor_tag = t @@ -245,7 +245,7 @@ return TOPIC_REFRESH if(href_list["set_sensor_name"]) - var/t = sanitize_safe(input(usr, "Enter the sensor name.", src.name, src.sensor_name)) + var/t = sanitize_safe(input(user, "Enter the sensor name.", src.name, src.sensor_name)) t = sanitize_safe(t, MAX_NAME_LEN) if(t) src.sensor_name = t diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index e6eb56094761..2260e4012d99 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -160,7 +160,7 @@ /obj/machinery/biogenerator/OnTopic(user, href_list) switch (href_list["action"]) if("activate") - activate() + activate(user) if("detach") if(beaker) beaker.dropInto(src.loc) @@ -186,8 +186,8 @@ ui_interact(user) return TRUE -/obj/machinery/biogenerator/proc/activate() - if (usr.stat) +/obj/machinery/biogenerator/proc/activate(mob/user) + if (user.incapacitated()) return if (stat) //NOPOWER etc return diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 0042bac3abbd..d1a10d81ea00 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -149,33 +149,32 @@ /mob/living/silicon/ai/proc/ai_actual_track(mob/living/target) if(!istype(target)) return - var/mob/living/silicon/ai/U = usr - if(target == U.cameraFollow) + if(target == cameraFollow) return - if(U.cameraFollow) - U.ai_cancel_tracking() - U.cameraFollow = target - to_chat(U, "Tracking target...") + if(cameraFollow) + ai_cancel_tracking() + cameraFollow = target + to_chat(src, "Tracking target...") target.tracking_initiated() spawn (0) - while (U.cameraFollow == target) - if (U.cameraFollow == null) + while (cameraFollow == target) + if (cameraFollow == null) return switch(target.tracking_status()) if(TRACKING_NO_COVERAGE) - to_chat(U, "Target is not near any active cameras.") + to_chat(src, "Target is not near any active cameras.") sleep(100) continue if(TRACKING_TERMINATE) - U.ai_cancel_tracking(1) + ai_cancel_tracking(1) return - if(U.eyeobj) - U.eyeobj.setLoc(get_turf(target), 0) + if(eyeobj) + eyeobj.setLoc(get_turf(target), 0) else view_core() return diff --git a/code/game/machinery/floorlayer.dm b/code/game/machinery/floorlayer.dm index 6d804caa374a..b8628b7e508a 100644 --- a/code/game/machinery/floorlayer.dm +++ b/code/game/machinery/floorlayer.dm @@ -41,7 +41,7 @@ var/m = input("Choose work mode", "Mode") as null|anything in mode mode[m] = !mode[m] var/O = mode[m] - user.visible_message("[usr] has set \the [src] [m] mode [!O?"off":"on"].", "You set \the [src] [m] mode [!O?"off":"on"].") + user.visible_message("[user] has set \the [src] [m] mode [!O?"off":"on"].", "You set \the [src] [m] mode [!O?"off":"on"].") return TRUE if(istype(W, /obj/item/stack/tile)) diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 6351cca16c77..1fb6b7666b72 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -110,7 +110,7 @@ if(emagged) emag_play() else if(!current_track) - to_chat(usr, "No track selected.") + to_chat(user, "No track selected.") else StartPlaying() return TOPIC_REFRESH diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 75221d302789..50cf312569bd 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -253,7 +253,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to dat+="Message Body: [src.msg]
    " dat+="Photo: " if(photo_data && photo_data.photo) - send_rsc(usr, photo_data.photo.img, "tmp_photo.png") + send_rsc(user, photo_data.photo.img, "tmp_photo.png") dat+="
    " dat+="
    Delete Photo
    " else @@ -325,7 +325,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to dat+="-[MESSAGE.body]
    " if(MESSAGE.img) var/resourc_name = "newscaster_photo_[sanitize(viewing_channel.channel_name)]_[i].png" - send_asset(usr.client, resourc_name) + send_asset(user.client, resourc_name) dat+="
    " if(MESSAGE.caption) dat+="[MESSAGE.caption]
    " @@ -397,7 +397,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to dat+="Description: [src.msg]
    " dat+="Photo: " if(photo_data && photo_data.photo) - send_rsc(usr, photo_data.photo.img, "tmp_photo.png") + send_rsc(user, photo_data.photo.img, "tmp_photo.png") dat+="
    " dat+="
    Delete Photo
    " else @@ -431,7 +431,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to dat+="Description: [news_network.wanted_issue.body]
    " dat+="Photo: " if(news_network.wanted_issue.img) - send_rsc(usr, news_network.wanted_issue.img, "tmp_photow.png") + send_rsc(user, news_network.wanted_issue.img, "tmp_photow.png") dat+="
    " else dat+="None" diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index 27e7d81ea038..dd3da2e44ae5 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -278,7 +278,7 @@ var/global/bomb_set to_chat(usr, "Nothing happens, something might be wrong with the wiring.") return 1 if(!timing && !safety) - start_bomb() + start_bomb(usr) else check_cutoff() if(href_list["safety"]) @@ -306,9 +306,9 @@ var/global/bomb_set to_chat(usr, "There is nothing to anchor to!") return 1 -/obj/machinery/nuclearbomb/proc/start_bomb() +/obj/machinery/nuclearbomb/proc/start_bomb(user) timing = 1 - log_and_message_admins("activated the detonation countdown of \the [src]") + log_and_message_admins("activated the detonation countdown of \the [src]", user) bomb_set++ //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) original_level = security_state.current_security_level @@ -479,11 +479,11 @@ var/global/bomb_set if(href_list["anchor"]) return -/obj/machinery/nuclearbomb/station/start_bomb() +/obj/machinery/nuclearbomb/station/start_bomb(mob/user) for(var/inserter in inserters) var/obj/machinery/self_destruct/sd = inserter if(!istype(sd) || !sd.armed) - to_chat(usr, "An inserter has not been armed or is damaged.") + to_chat(user, "An inserter has not been armed or is damaged.") return visible_message("Warning. The self-destruct sequence override will be disabled [self_destruct_cutoff] seconds before detonation.") ..() diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 27a13a93063a..aeb5ac90cac6 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -185,7 +185,7 @@ /obj/machinery/oxygen_pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] if(!tank) - to_chat(usr, SPAN_WARNING("It is missing a tank!")) + to_chat(user, SPAN_WARNING("It is missing a tank!")) data["tankPressure"] = 0 data["releasePressure"] = 0 data["defaultReleasePressure"] = 0 diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index e7f5b123b93c..0124b95786e2 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -193,7 +193,7 @@ var/global/list/turret_icons return STATUS_CLOSE if(!anchored) - to_chat(usr, "\The [src] has to be secured first!") + to_chat(user, "\The [src] has to be secured first!") return STATUS_CLOSE return ..() diff --git a/code/game/machinery/self_destruct.dm b/code/game/machinery/self_destruct.dm index ee842c32e23a..3cf9137e831f 100644 --- a/code/game/machinery/self_destruct.dm +++ b/code/game/machinery/self_destruct.dm @@ -14,7 +14,7 @@ if(!damaged) return FALSE user.visible_message("[user] begins to repair [src].", "You begin repairing [src].") - if(do_after(usr, 100, src)) + if(do_after(user, 100, src)) var/obj/item/weldingtool/w = W if(w.weld(10)) damaged = 0 diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index e59df75a663c..77dcbafee909 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -81,7 +81,7 @@ dat += " [set_temperature]K ([set_temperature-T0C]°C)" dat += "+
    " - var/datum/browser/popup = new(usr, "spaceheater", "Space Heater Control Panel") + var/datum/browser/popup = new(user, "spaceheater", "Space Heater Control Panel") popup.set_content(jointext(dat, null)) popup.open() diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index e6ce02af3f7b..f48d05b08d79 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -66,13 +66,13 @@ L = locate("landmark*[C.data]") // use old stype if(istype(L, /obj/abstract/landmark) && isturf(L.loc) && user.try_unequip(I)) - to_chat(usr, "You insert the coordinates into the machine.") - to_chat(usr, "A message flashes across the screen reminding the traveller that the nuclear authentication disk is to remain on the [station_name()] at all times.") + to_chat(user, "You insert the coordinates into the machine.") + to_chat(user, "A message flashes across the screen reminding the traveller that the nuclear authentication disk is to remain on the [station_name()] at all times.") qdel(I) audible_message(SPAN_NOTICE("Locked in.")) src.locked = L one_time_use = 1 - add_fingerprint(usr) + add_fingerprint(user) return TRUE /obj/machinery/computer/teleporter/interface_interact(var/mob/user) diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm index 141619bd14f7..c7c2924326f2 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -89,7 +89,7 @@ return FALSE if(istype(W, /obj/item/card/id)||istype(W, /obj/item/modular_computer)) - if(src.allowed(usr)) + if(src.allowed(user)) if(emagged) to_chat(user, "The turret control is unresponsive.") else From d93303df38dbd989a920f6bcdcddeed76cc2c5ea Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 21:20:54 -0500 Subject: [PATCH 0072/1331] Remove unnecessary uses of usr in objects folder --- code/game/objects/items/weapons/tanks/tanks.dm | 6 +++--- code/game/objects/items/weapons/tools/wirecutter.dm | 2 +- .../objects/structures/crates_lockers/closets/__closet.dm | 2 +- code/game/objects/structures/crematorium.dm | 2 +- code/game/objects/structures/fishtanks.dm | 6 +++--- code/game/objects/structures/girders.dm | 4 ++-- code/game/objects/structures/safe.dm | 2 +- .../objects/structures/stool_bed_chair_nest_sofa/bed.dm | 6 +++--- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 847e581f4460..35c99e1d7dce 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -147,12 +147,12 @@ var/global/list/global/tank_gauge_cache = list() var/obj/item/assembly_holder/assy = proxyassembly.assembly if(assy.a_left && assy.a_right) - assy.dropInto(usr.loc) + assy.dropInto(user.loc) assy.master = null proxyassembly.assembly = null else if(!proxyassembly.assembly.a_left) - assy.a_right.dropInto(usr.loc) + assy.a_right.dropInto(user.loc) assy.a_right.holder = null assy.a_right = null proxyassembly.assembly = null @@ -299,7 +299,7 @@ var/global/list/global/tank_gauge_cache = list() return TOPIC_REFRESH if (href_list["stat"]) - toggle_valve(usr) + toggle_valve(user) return TOPIC_REFRESH /obj/item/tank/proc/toggle_valve(var/mob/user) diff --git a/code/game/objects/items/weapons/tools/wirecutter.dm b/code/game/objects/items/weapons/tools/wirecutter.dm index 6cb93a254f17..2886de69bc3e 100644 --- a/code/game/objects/items/weapons/tools/wirecutter.dm +++ b/code/game/objects/items/weapons/tools/wirecutter.dm @@ -46,7 +46,7 @@ var/obj/item/handcuffs/cable/cuffs = target.get_equipped_item(slot_handcuffed_str) if(user.check_intent(I_FLAG_HELP) && istype(cuffs) && target.try_unequip(cuffs)) user.visible_message( - "\The [usr] cuts \the [target]'s restraints with \the [src]!", + "\The [user] cuts \the [target]'s restraints with \the [src]!", "You cut \the [target]'s restraints with \the [src]!", "You hear cable being cut." ) diff --git a/code/game/objects/structures/crates_lockers/closets/__closet.dm b/code/game/objects/structures/crates_lockers/closets/__closet.dm index 80a61cff4859..9e717d53f973 100644 --- a/code/game/objects/structures/crates_lockers/closets/__closet.dm +++ b/code/game/objects/structures/crates_lockers/closets/__closet.dm @@ -344,7 +344,7 @@ var/global/list/closets = list() if(!. && istype(AM) && opened && !istype(AM, /obj/structure/closet) && AM.simulated && !AM.anchored && (large || !ismob(AM))) step_towards(AM, loc) if(user != AM) - user.show_viewers(SPAN_DANGER("\The [user] stuffs \the [AM] into \the [src]!")) + user.visible_message(SPAN_DANGER("\The [user] stuffs \the [AM] into \the [src]!"), SPAN_DANGER("You stuff \the [AM] into \the [src]!")) return TRUE /obj/structure/closet/attack_ai(mob/living/silicon/ai/user) diff --git a/code/game/objects/structures/crematorium.dm b/code/game/objects/structures/crematorium.dm index 4b03414f3bd8..61b227552247 100644 --- a/code/game/objects/structures/crematorium.dm +++ b/code/game/objects/structures/crematorium.dm @@ -82,7 +82,7 @@ if(!user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) return ..() if(locked) - to_chat(usr, SPAN_WARNING("It's currently locked.")) + to_chat(user, SPAN_WARNING("It's currently locked.")) return TRUE if(open) close() diff --git a/code/game/objects/structures/fishtanks.dm b/code/game/objects/structures/fishtanks.dm index 3dafa9160f31..b6724f467752 100644 --- a/code/game/objects/structures/fishtanks.dm +++ b/code/game/objects/structures/fishtanks.dm @@ -191,13 +191,13 @@ var/global/list/global/aquarium_states_and_layers = list( return if(!Adjacent(target)) return - usr.visible_message(SPAN_WARNING("\The [user] starts climbing out of \the [src]!")) + user.visible_message(SPAN_WARNING("\The [user] starts climbing out of \the [src]!")) if(!do_after(user,50)) return if (!Adjacent(target)) return - usr.forceMove(target) - usr.visible_message(SPAN_WARNING("\The [user] climbs out of \the [src]!")) + user.forceMove(target) + user.visible_message(SPAN_WARNING("\The [user] climbs out of \the [src]!")) /obj/structure/glass_tank/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) . = locate(/obj/structure/glass_tank) in (target == loc) ? (mover && mover.loc) : target diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index d4aed89ecdcc..4a9c41b13ed0 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -145,7 +145,7 @@ to_chat(user, SPAN_WARNING("You will need a support made of sturdier material to hold up [S.material.solid_name] cladding.")) return FALSE - add_hiddenprint(usr) + add_hiddenprint(user) if(S.material.integrity < 50) to_chat(user, SPAN_WARNING("This material is too soft for use in wall construction.")) return 0 @@ -165,7 +165,7 @@ var/turf/wall/T = get_turf(src) T.set_turf_materials(S.material, reinf_material, null, material) T.can_open = prepped_for_fakewall - T.add_hiddenprint(usr) + T.add_hiddenprint(user) material = null reinf_material = null qdel(src) diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index 7b78fc8c944e..10db1cd1afb3 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -100,7 +100,7 @@ FLOOR SAFES to_chat(user, "You can't [open ? "close" : "open"] [src], the lock is engaged!") return - var/canhear = locate(/obj/item/clothing/neck/stethoscope) in usr.get_held_items() + var/canhear = locate(/obj/item/clothing/neck/stethoscope) in user.get_held_items() if(href_list["decrement"]) dial = decrement(dial) if(dial == tumbler_1_pos + 1 || dial == tumbler_1_pos - 71) diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm index 2090da1a2ccd..771e85942135 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm @@ -234,8 +234,8 @@ remove_beaker(user) return TRUE -/obj/structure/bed/roller/proc/collapse() - visible_message("[usr] collapses [src].") +/obj/structure/bed/roller/proc/collapse(mob/user) + visible_message("[user] collapses [src].") new item_form_type(get_turf(src)) qdel(src) @@ -300,7 +300,7 @@ remove_beaker(user) return TRUE if(!buckled_mob) - collapse() + collapse(user) return TRUE . = ..() From 68a223db6674a616004fab3d619d4c8f377b5593 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 21:21:19 -0500 Subject: [PATCH 0073/1331] Remove unnecessary uses of usr in datums folder --- code/datums/communication/aooc.dm | 2 +- code/datums/extensions/assembly/assembly_interaction.dm | 6 +++--- code/datums/extensions/eye/landing.dm | 4 ++-- code/datums/extensions/holster/holster.dm | 2 +- code/datums/extensions/multitool/multitool.dm | 4 ++-- code/datums/extensions/storage/subtypes_sheets.dm | 8 ++++---- code/datums/traits/_traits.dm | 4 +++- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/code/datums/communication/aooc.dm b/code/datums/communication/aooc.dm index 52ca16f41941..8c4a25349c3e 100644 --- a/code/datums/communication/aooc.dm +++ b/code/datums/communication/aooc.dm @@ -30,7 +30,7 @@ receive_communication(C, target, SPAN_AOOC("[get_options_bar(C, 0, 1, 1)]: [message]")) else if(target.mob?.mind?.assigned_special_role) var/display_name = C.key - var/player_display = holder ? "[display_name]([usr.client.holder.rank])" : display_name + var/player_display = holder ? "[display_name]([C.holder.rank])" : display_name receive_communication(C, target, SPAN_AOOC("[player_display]: [message]")) /decl/communication_channel/aooc/do_broadcast(message) diff --git a/code/datums/extensions/assembly/assembly_interaction.dm b/code/datums/extensions/assembly/assembly_interaction.dm index 9e250bb5d7bc..a4c4c8b2f920 100644 --- a/code/datums/extensions/assembly/assembly_interaction.dm +++ b/code/datums/extensions/assembly/assembly_interaction.dm @@ -20,7 +20,7 @@ return TRUE to_chat(user, "You begin repairing damage to \the [holder]...") - if(WT.weld(round(damage/75)) && do_after(usr, damage/10)) + if(WT.weld(round(damage/75)) && do_after(user, damage/10)) damage = 0 to_chat(user, "You repair \the [holder].") return TRUE @@ -33,11 +33,11 @@ for(var/obj/item/stock_parts/computer/H in parts) component_names.Add(H.name) - var/choice = input(usr, "Which component do you want to uninstall?", "[assembly_name] maintenance", null) as null|anything in component_names + var/choice = input(user, "Which component do you want to uninstall?", "[assembly_name] maintenance", null) as null|anything in component_names if(!choice) return TRUE var/atom/movable/HA = holder - if(!HA.Adjacent(usr)) + if(!HA.Adjacent(user)) return TRUE var/obj/item/stock_parts/H = find_component_by_name(choice) diff --git a/code/datums/extensions/eye/landing.dm b/code/datums/extensions/eye/landing.dm index b54a722cb4f9..deaa8dafc8d2 100644 --- a/code/datums/extensions/eye/landing.dm +++ b/code/datums/extensions/eye/landing.dm @@ -27,12 +27,12 @@ /datum/action/eye/landing/rotate_cw name = "Rotate clockwise" - procname ="turn_shuttle_cw" + procname = TYPE_PROC_REF(/mob/observer/eye/landing, turn_shuttle_cw) button_icon_state = "shuttle_rotate_cw" target_type = EYE_TARGET /datum/action/eye/landing/rotate_ccw name = "Rotate counterclockwise" - procname ="turn_shuttle_ccw" + procname = TYPE_PROC_REF(/mob/observer/eye/landing, turn_shuttle_ccw) button_icon_state = "shuttle_rotate_ccw" target_type = EYE_TARGET \ No newline at end of file diff --git a/code/datums/extensions/holster/holster.dm b/code/datums/extensions/holster/holster.dm index 55373db095d6..8792a616b5a6 100644 --- a/code/datums/extensions/holster/holster.dm +++ b/code/datums/extensions/holster/holster.dm @@ -82,7 +82,7 @@ G.check_accidents(user) if(G.safety() && !user.skill_fail_prob(SKILL_WEAPONS, 100, SKILL_EXPERT, 0.5)) //Experienced shooter will disable safety before shooting. G.toggle_safety(user) - usr.visible_message( + user.visible_message( "\The [user] draws \the [holstered], ready to go!", "You draw \the [holstered], ready to go!" ) diff --git a/code/datums/extensions/multitool/multitool.dm b/code/datums/extensions/multitool/multitool.dm index 08e98ebe7ebc..ce3c2ae09e79 100644 --- a/code/datums/extensions/multitool/multitool.dm +++ b/code/datums/extensions/multitool/multitool.dm @@ -13,7 +13,7 @@ popup.set_content(html) popup.open() else - close_window(usr) + close_window(user) /datum/extension/interactive/multitool/proc/get_interact_window(var/obj/item/multitool/M, var/mob/user) return @@ -36,7 +36,7 @@ /datum/extension/interactive/multitool/extension_act(href, href_list, var/mob/user) if(..()) - close_window(usr) + close_window(user) return TRUE var/obj/item/multitool/M = user.get_multitool() diff --git a/code/datums/extensions/storage/subtypes_sheets.dm b/code/datums/extensions/storage/subtypes_sheets.dm index 1db2042cb97d..a72c49ab856d 100644 --- a/code/datums/extensions/storage/subtypes_sheets.dm +++ b/code/datums/extensions/storage/subtypes_sheets.dm @@ -44,10 +44,10 @@ inserted = 1 break if(!inserted || !S.amount) - usr.drop_from_inventory(S, holder) + user.drop_from_inventory(S, holder) if(!S.amount) qdel(S) - prepare_ui(usr) + prepare_ui(user) if(isatom(holder)) var/atom/atom_holder = holder atom_holder.update_icon() @@ -64,8 +64,8 @@ if(!S.amount) qdel(S) // todo: there's probably something missing here prepare_ui() - if(usr.active_storage) - usr.active_storage.show_to(usr) + if(user.active_storage) + user.active_storage.show_to(user) if(isatom(holder)) var/atom/atom_holder = holder atom_holder.update_icon() diff --git a/code/datums/traits/_traits.dm b/code/datums/traits/_traits.dm index ad1041e32da2..42d3bdb1b751 100644 --- a/code/datums/traits/_traits.dm +++ b/code/datums/traits/_traits.dm @@ -236,6 +236,8 @@ result += trait.get_trait_selection_data(caller, ticked_traits, (recurse_level+1), ignore_children_if_unticked) return result +/// Shows `show_to` a browser window describing the character setup traits taken by `src`. +/// `show_to` must be non-null. /mob/proc/get_trait_data(var/mob/show_to) var/list/traits = get_traits() @@ -262,7 +264,7 @@ if(printed_cat) dat += "
    " - var/datum/browser/popup = new((show_to || usr), "trait_summary_\ref[src]", "Aspect Summary") + var/datum/browser/popup = new(show_to, "trait_summary_\ref[src]", "Aspect Summary") popup.set_content(jointext(dat, null)) popup.open() From c122b4e8c33cdf67d0646d4185d3bf73edbbbe43 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 21:21:53 -0500 Subject: [PATCH 0074/1331] Remove unnecessary uses of usr in preferences --- code/modules/client/preference_setup/general/05_flavor.dm | 8 ++++---- code/modules/client/preference_setup/global/03_pai.dm | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/modules/client/preference_setup/general/05_flavor.dm b/code/modules/client/preference_setup/general/05_flavor.dm index e34ae4c9dc39..803e65cee7cb 100644 --- a/code/modules/client/preference_setup/general/05_flavor.dm +++ b/code/modules/client/preference_setup/general/05_flavor.dm @@ -54,11 +54,11 @@ if("open") pass() if("general") - var/msg = sanitize(input(usr,"Give a general description of your character. This will be shown regardless of clothing. Do not include OOC information here.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0) + var/msg = sanitize(input(user,"Give a general description of your character. This will be shown regardless of clothing. Do not include OOC information here.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0) if(CanUseTopic(user)) pref.flavor_texts[href_list["flavor_text"]] = msg else - var/msg = sanitize(input(usr,"Set the flavor text for your [href_list["flavor_text"]].","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0) + var/msg = sanitize(input(user,"Set the flavor text for your [href_list["flavor_text"]].","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0) if(CanUseTopic(user)) pref.flavor_texts[href_list["flavor_text"]] = msg SetFlavorText(user) @@ -69,11 +69,11 @@ if("open") pass() if("Default") - var/msg = sanitize(input(usr,"Set the default flavour text for your robot. It will be used for any module without individual setting.","Flavour Text",html_decode(pref.flavour_texts_robot["Default"])) as message, extra = 0) + var/msg = sanitize(input(user,"Set the default flavour text for your robot. It will be used for any module without individual setting.","Flavour Text",html_decode(pref.flavour_texts_robot["Default"])) as message, extra = 0) if(CanUseTopic(user)) pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg else - var/msg = sanitize(input(usr,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this empty, default flavour text will be used for this module.","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]])) as message, extra = 0) + var/msg = sanitize(input(user,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this empty, default flavour text will be used for this module.","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]])) as message, extra = 0) if(CanUseTopic(user)) pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg SetFlavourTextRobot(user) diff --git a/code/modules/client/preference_setup/global/03_pai.dm b/code/modules/client/preference_setup/global/03_pai.dm index 61c829512864..aeef174d4ffd 100644 --- a/code/modules/client/preference_setup/global/03_pai.dm +++ b/code/modules/client/preference_setup/global/03_pai.dm @@ -67,13 +67,13 @@ if(!isnull(t) && CanUseTopic(user)) candidate.comments = sanitize(t) if("chassis") - t = input(usr,"What would you like to use for your mobile chassis icon?") as null|anything in global.possible_chassis + t = input(user,"What would you like to use for your mobile chassis icon?") as null|anything in global.possible_chassis if(!isnull(t) && CanUseTopic(user)) candidate.chassis = t update_pai_preview(user) . = TOPIC_HARD_REFRESH if("say") - t = input(usr,"What theme would you like to use for your speech verbs?") as null|anything in global.possible_say_verbs + t = input(user,"What theme would you like to use for your speech verbs?") as null|anything in global.possible_say_verbs if(!isnull(t) && CanUseTopic(user)) candidate.say_verb = t if("cyclebg") From 775e3458e289d14013629fc112a26a6383bd6d5c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:53:18 -0500 Subject: [PATCH 0075/1331] Remove unnecessary uses of usr in modpacks --- mods/content/matchmaking/matchmaker.dm | 4 ++-- mods/gamemodes/cult/spells/construct.dm | 4 ++-- mods/mobs/borers/mob/borer/borer_hud.dm | 2 +- mods/species/ascent/effects/razorweb.dm | 8 ++++---- mods/species/ascent/mobs/nymph/nymph_life.dm | 21 ++++++++++---------- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/mods/content/matchmaking/matchmaker.dm b/mods/content/matchmaking/matchmaker.dm index 0892ba4c827a..3634f91bd3fd 100644 --- a/mods/content/matchmaking/matchmaker.dm +++ b/mods/content/matchmaking/matchmaker.dm @@ -271,8 +271,8 @@ var/global/datum/matchmaker/matchmaker = new() var/list/relations = matchmaker.get_relationships(mind) for(var/datum/relation/R in relations) R.finalize() - show_browser(usr,null, "window=relations") + show_browser(src,null, "window=relations") else - show_browser(usr,null, "window=relations") + show_browser(src,null, "window=relations") return TOPIC_HANDLED return ..() \ No newline at end of file diff --git a/mods/gamemodes/cult/spells/construct.dm b/mods/gamemodes/cult/spells/construct.dm index a300bacacb2e..05c35c46785d 100644 --- a/mods/gamemodes/cult/spells/construct.dm +++ b/mods/gamemodes/cult/spells/construct.dm @@ -89,12 +89,12 @@ hud_state = "const_pylon" -/spell/aoe_turf/conjure/pylon/cast(list/targets) +/spell/aoe_turf/conjure/pylon/cast(list/targets, mob/user) ..() var/turf/spawn_place = pick(targets) for(var/obj/structure/cult/pylon/P in spawn_place.contents) if(P.isbroken) - P.repair(usr) + P.repair(user) continue return diff --git a/mods/mobs/borers/mob/borer/borer_hud.dm b/mods/mobs/borers/mob/borer/borer_hud.dm index 084bacb7a160..8ec7b333971a 100644 --- a/mods/mobs/borers/mob/borer/borer_hud.dm +++ b/mods/mobs/borers/mob/borer/borer_hud.dm @@ -54,7 +54,7 @@ /obj/screen/borer/handle_click(mob/user, params) if(!isborer(user)) return FALSE - var/mob/living/simple_animal/borer/worm = usr + var/mob/living/simple_animal/borer/worm = user if(!worm.host) return FALSE return TRUE diff --git a/mods/species/ascent/effects/razorweb.dm b/mods/species/ascent/effects/razorweb.dm index d67a55ee4988..c8b05c70deb4 100644 --- a/mods/species/ascent/effects/razorweb.dm +++ b/mods/species/ascent/effects/razorweb.dm @@ -13,7 +13,7 @@ web.buckle_mob(hit_atom) web.visible_message(SPAN_DANGER("\The [hit_atom] is tangled in \the [web]!")) web.entangle(hit_atom, TRUE) - playsound(usr, 'mods/species/ascent/sounds/razorweb_twang.ogg', 50) + playsound(src, 'mods/species/ascent/sounds/razorweb_twang.ogg', 50) qdel(src) // Hey, did you ever see The Cube (1997) directed by Vincenzo Natali? @@ -68,7 +68,7 @@ START_PROCESSING(SSobj, src) /obj/effect/razorweb/proc/decay() - playsound(usr, 'mods/species/ascent/sounds/razorweb_break.ogg', 50) + playsound(src, 'mods/species/ascent/sounds/razorweb_break.ogg', 50) qdel_self() /obj/effect/razorweb/attack_hand(mob/user) @@ -163,8 +163,8 @@ if(prob(break_chance)) visible_message(SPAN_DANGER("\The [src] breaks apart!")) - playsound(usr, 'mods/species/ascent/sounds/razorweb_break.ogg', 50) + playsound(src, 'mods/species/ascent/sounds/razorweb_break.ogg', 50) qdel(src) else - playsound(usr, 'mods/species/ascent/sounds/razorweb_twang.ogg', 50) + playsound(src, 'mods/species/ascent/sounds/razorweb_twang.ogg', 50) break_chance = min(break_chance+10, 100) \ No newline at end of file diff --git a/mods/species/ascent/mobs/nymph/nymph_life.dm b/mods/species/ascent/mobs/nymph/nymph_life.dm index 4c217a9aee0e..f8e5c45fd4a4 100644 --- a/mods/species/ascent/mobs/nymph/nymph_life.dm +++ b/mods/species/ascent/mobs/nymph/nymph_life.dm @@ -61,23 +61,22 @@ return molt = min(molt + 1, 5) - var/mob/living/simple_animal/alien/kharmaan/nymph = usr - nymph.visible_message("\icon[nymph] [nymph] begins to shimmy and shake out of its old skin.") + visible_message("\icon[src] [src] begins to shimmy and shake out of its old skin.") if(molt == 5) - if(do_after(nymph, 10 SECONDS, nymph, FALSE)) - var/mob/living/human/H = new(get_turf(usr), SPECIES_MANTID_ALATE) - H.set_gyne_lineage(nymph.get_gyne_lineage()) + if(do_after(src, 10 SECONDS, src, FALSE)) + var/mob/living/human/H = new(get_turf(src), SPECIES_MANTID_ALATE) + H.set_gyne_lineage(get_gyne_lineage()) H.real_name = "[random_id(/decl/species/mantid, 10000, 99999)] [H.get_gyne_name()]" - H.nutrition = nymph.nutrition * 0.25 // Homgry after molt. - nymph.mind.transfer_to(H) - qdel(nymph) + H.nutrition = nutrition * 0.25 // Homgry after molt. + mind.transfer_to(H) + qdel(src) H.visible_message("\icon[H] [H] emerges from its molt as a new alate.") new/obj/item/ascent_molt(get_turf(src)) else - nymph.visible_message("\icon[nymph] [nymph] abruptly stops molting.") + visible_message("\icon[src] [src] abruptly stops molting.") return - if(do_after(nymph, 5 SECONDS, nymph, FALSE)) + if(do_after(src, 5 SECONDS, src, FALSE)) var/matrix/M = matrix() M.Scale(1 + (molt / 10)) animate(src, transform = M, time = 2, easing = QUAD_EASING) @@ -88,4 +87,4 @@ new /obj/item/ascent_molt(get_turf(src)) else - nymph.visible_message("\icon[nymph] [nymph] abruptly stops molting.") \ No newline at end of file + visible_message("\icon[src] [src] abruptly stops molting.") \ No newline at end of file From ac7009d50ece5df310c8f758e35df7f0b52c9c2f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:53:45 -0500 Subject: [PATCH 0076/1331] Remove unnecessary uses of usr in mob code --- code/modules/mob/living/bot/bot.dm | 4 +-- code/modules/mob/living/human/human.dm | 16 ++++----- code/modules/mob/living/living.dm | 6 ++-- code/modules/mob/living/living_resist.dm | 14 ++++---- .../mob/living/silicon/robot/drone/drone.dm | 4 +-- .../mob/living/silicon/robot/robot_items.dm | 6 ++-- code/modules/mob/living/silicon/silicon.dm | 2 +- code/modules/mob/mob.dm | 35 ++++++------------- code/modules/mob/mob_movement.dm | 7 ++-- code/modules/mob/observer/ghost/ghost.dm | 3 -- code/modules/mob/stripping.dm | 2 +- 11 files changed, 41 insertions(+), 58 deletions(-) diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index cfe9577d41c3..ff453a99da4f 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -95,7 +95,7 @@ if(access_scanner.allowed(user) && !open) locked = !locked to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]") - Interact(usr) + Interact(user) else if(open) to_chat(user, "Please close the access panel before locking it.") else @@ -105,7 +105,7 @@ if(!locked) open = !open to_chat(user, "Maintenance panel is now [open ? "opened" : "closed"].") - Interact(usr) + Interact(user) else to_chat(user, "You need to unlock the controls first.") return TRUE diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 43c38c6f5532..dad5d4d85a8f 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -182,7 +182,7 @@ var/datum/computer_file/report/crew_record/R = network.get_crew_record_by_name(perpname) if(R) var/setcriminal = input(user, "Specify a new criminal status for this person.", "Security HUD", R.get_criminalStatus()) as null|anything in global.security_statuses - if(hasHUD(usr, HUD_SECURITY) && setcriminal) + if(hasHUD(user, HUD_SECURITY) && setcriminal) R.set_criminalStatus(setcriminal) modified = 1 @@ -196,11 +196,11 @@ U.handle_regular_hud_updates() if(!modified) - to_chat(usr, "Unable to locate a data core entry for this person.") + to_chat(user, "Unable to locate a data core entry for this person.") return TOPIC_HANDLED if (href_list["secrecord"]) - if(hasHUD(usr, HUD_SECURITY)) + if(hasHUD(user, HUD_SECURITY)) var/perpname = "wot" var/read = 0 @@ -274,11 +274,11 @@ var/datum/computer_file/report/crew_record/E = network.get_crew_record_by_name(perpname) if(E) if(hasHUD(user, HUD_MEDICAL)) - to_chat(usr, "Name: [E.get_name()]") - to_chat(usr, "Gender: [E.get_gender()]") - to_chat(usr, "Species: [E.get_species_name()]") - to_chat(usr, "Blood Type: [E.get_bloodtype()]") - to_chat(usr, "Details: [E.get_medical_record()]") + to_chat(user, "Name: [E.get_name()]") + to_chat(user, "Gender: [E.get_gender()]") + to_chat(user, "Species: [E.get_species_name()]") + to_chat(user, "Blood Type: [E.get_bloodtype()]") + to_chat(user, "Details: [E.get_medical_record()]") read = 1 if(!read) to_chat(user, "Unable to locate a data core entry for this person.") diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index a8af9c4657a9..7bab0e45da2d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -36,7 +36,7 @@ if(!..()) return 0 - usr.visible_message("[src] points to [A]") + visible_message("[src] points to [A]") return 1 /*one proc, four uses @@ -657,7 +657,7 @@ default behaviour is: if(length(selectable_postures) == 1) selected_posture = selectable_postures[1] else - selected_posture = input(usr, "Which posture do you wish to adopt?", "Change Posture", current_posture) as null|anything in selectable_postures + selected_posture = input(src, "Which posture do you wish to adopt?", "Change Posture", current_posture) as null|anything in selectable_postures if(!selected_posture || length(get_available_postures()) <= 1 || incapacitated(INCAPACITATION_KNOCKOUT) || !canClick()) return if(current_posture == selected_posture || !(selected_posture in get_selectable_postures())) @@ -1063,7 +1063,7 @@ default behaviour is: if(user.mob_size >= exosuit.body.min_pilot_size && user.mob_size <= exosuit.body.max_pilot_size) exosuit.enter(src) else - to_chat(usr, SPAN_WARNING("You cannot pilot a exosuit of this size.")) + to_chat(user, SPAN_WARNING("You cannot pilot a exosuit of this size.")) return TRUE . = ..() diff --git a/code/modules/mob/living/living_resist.dm b/code/modules/mob/living/living_resist.dm index 9228eda91a5c..c1339fbaefcb 100644 --- a/code/modules/mob/living/living_resist.dm +++ b/code/modules/mob/living/living_resist.dm @@ -13,37 +13,39 @@ if(buckled.can_buckle) buckled.user_unbuckle_mob(src) else - to_chat(usr, "You can't seem to escape from \the [buckled]!") + to_chat(src, "You can't seem to escape from \the [buckled]!") return setClickCooldown(100) unbuckle_time = max(0, (2 MINUTES) - get_special_resist_time()) + var/decl/pronouns/pronouns = get_pronouns() + visible_message( - "[src] attempts to unbuckle themself!", + "[src] attempts to unbuckle [pronouns.self]!", "You attempt to unbuckle yourself. (This will take around [unbuckle_time / (1 SECOND)] second\s and you need to stand still)", range = 2 ) if(unbuckle_time && buckled) var/stages = 2 for(var/i = 1 to stages) - if(!unbuckle_time || do_after(usr, unbuckle_time*0.5, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) + if(!unbuckle_time || do_after(src, unbuckle_time*0.5, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) if(!buckled) return visible_message( - SPAN_WARNING("\The [src] tries to unbuckle themself."), + SPAN_WARNING("\The [src] tries to unbuckle [pronouns.self]."), SPAN_WARNING("You try to unbuckle yourself ([i*100/stages]% done)."), range = 2 ) else if(!buckled) return visible_message( - SPAN_WARNING("\The [src] stops trying to unbuckle themself."), + SPAN_WARNING("\The [src] stops trying to unbuckle [pronouns.self]."), SPAN_WARNING("You stop trying to unbuckle yourself."), range = 2 ) return visible_message( - SPAN_DANGER("\The [src] manages to unbuckle themself!"), + SPAN_DANGER("\The [src] manages to unbuckle [pronouns.self]!"), SPAN_NOTICE("You successfully unbuckle yourself."), range = 2 ) buckled.user_unbuckle_mob(src) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index c80b26a83b5e..0563fce55217 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -178,7 +178,7 @@ if(!get_config_value(/decl/config/toggle/on/allow_drone_spawn) || emagged || should_be_dead()) //It's dead, Dave. to_chat(user, "The interface is fried, and a distressing burned smell wafts from the robot's interior. You're not rebooting this one.") return TRUE - if(!allowed(usr)) + if(!allowed(user)) to_chat(user, "Access denied.") return TRUE var/decl/pronouns/pronouns = user.get_pronouns() @@ -193,7 +193,7 @@ SPAN_DANGER("\The [user] swipes [pronouns.his] ID card through \the [src], attempting to shut it down."), \ SPAN_DANGER("You swipe your ID card through \the [src], attempting to shut it down.")) if(!emagged) - if(allowed(usr)) + if(allowed(user)) shut_down() else to_chat(user, SPAN_DANGER("Access denied.")) diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index eb0a535eec67..6c4ccab5c3a2 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -243,9 +243,9 @@ to_chat(user, "It has [stored_walls] wall segment\s and [stored_doors] door segment\s stored.") to_chat(user, "It is set to deploy [mode ? "doors" : "walls"]") -/obj/item/inflatable_dispenser/attack_self() +/obj/item/inflatable_dispenser/attack_self(mob/user) mode = !mode - to_chat(usr, "You set \the [src] to deploy [mode ? "doors" : "walls"].") + to_chat(user, "You set \the [src] to deploy [mode ? "doors" : "walls"].") /obj/item/inflatable_dispenser/afterattack(var/atom/A, var/mob/user) ..(A, user) @@ -301,7 +301,7 @@ if(istype(A, /obj/item/inflatable)) if(istype(A, /obj/item/inflatable/door)) if(stored_doors >= max_doors) - to_chat(usr, "\The [src] is full!") + to_chat(user, "\The [src] is full!") return stored_doors++ qdel(A) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 714102823d02..768dc1c0d513 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -368,7 +368,7 @@ if(istype(W) && user.try_unequip(W)) W.forceMove(src) stock_parts += W - to_chat(usr, "You install the [W.name].") + to_chat(user, "You install the [W.name].") return TRUE /mob/living/silicon/proc/try_stock_parts_removal(obj/item/W, mob/user) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 35df33c80fa8..5ed9d75db534 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -561,10 +561,10 @@ update_flavor_text(href_list["flavor_change"]) return TOPIC_HANDLED -// If usr != src, or if usr == src but the Topic call was not resolved, this is called next. /mob/proc/get_comments_record() return +// If usr != src, or if usr == src but the Topic call was not resolved, this is called next. /mob/OnTopic(mob/user, href_list, datum/topic_state/state) if(href_list["refresh"]) @@ -626,9 +626,6 @@ return TRUE . = ..() -/mob/proc/is_active() - return (0 >= usr.stat) - /mob/proc/can_touch(var/atom/touching) if(!touching.Adjacent(src) || incapacitated()) return FALSE @@ -639,16 +636,6 @@ to_chat(src, SPAN_WARNING("You are buckled down.")) return TRUE -/mob/proc/see(message) - if(!is_active()) - return 0 - to_chat(src, message) - return 1 - -/mob/proc/show_viewers(message) - for(var/mob/M in viewers()) - M.see(message) - /mob/Stat() ..() . = (is_client_active(10 MINUTES)) @@ -806,12 +793,10 @@ to_chat(usr, "You are restrained and cannot do that!") return - var/mob/S = src - var/mob/U = usr var/list/valid_objects = list() var/self = null - if(S == U) + if(src == usr) self = 1 // Removing object from yourself. valid_objects = get_visible_implants(0) @@ -819,16 +804,16 @@ if(self) to_chat(src, "You have nothing stuck in your body that is large enough to remove.") else - to_chat(U, "[src] has nothing stuck in their wounds that is large enough to remove.") + to_chat(usr, "[src] has nothing stuck in their wounds that is large enough to remove.") return var/obj/item/selection = input("What do you want to yank out?", "Embedded objects") in valid_objects if(self) to_chat(src, "You attempt to get a good grip on [selection] in your body.") else - to_chat(U, "You attempt to get a good grip on [selection] in [S]'s body.") - if(!do_mob(U, S, 30, incapacitation_flags = INCAPACITATION_DEFAULT & (~INCAPACITATION_FORCELYING))) //let people pinned to stuff yank it out, otherwise they're stuck... forever!!! + to_chat(usr, "You attempt to get a good grip on [selection] in [src]'s body.") + if(!do_mob(usr, src, 30, incapacitation_flags = INCAPACITATION_DEFAULT & (~INCAPACITATION_FORCELYING))) //let people pinned to stuff yank it out, otherwise they're stuck... forever!!! return - if(!selection || !S || !U) + if(QDELETED(selection) || QDELETED(src) || QDELETED(usr)) return if(self) @@ -837,10 +822,10 @@ visible_message("[usr] rips [selection] out of [src]'s body.","[usr] rips [selection] out of your body.") remove_implant(selection) selection.forceMove(get_turf(src)) - if(U.get_empty_hand_slot()) - U.put_in_hands(selection) - if(ishuman(U)) - var/mob/living/human/human_user = U + if(usr.get_empty_hand_slot()) + usr.put_in_hands(selection) + if(ishuman(usr)) + var/mob/living/human/human_user = usr human_user.bloody_hands(src) return 1 diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index c11e15b0580b..21c411010778 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -39,11 +39,10 @@ attack_self() return if(SOUTHWEST) - if(isliving(usr)) - var/mob/living/M = usr - M.toggle_throw_mode() + if(isliving(mob)) + mob.toggle_throw_mode() else - to_chat(usr, "This mob type cannot throw items.") + to_chat(src, "This mob type cannot throw items.") return if(NORTHWEST) mob.hotkey_drop() diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm index acf4b0af582c..e0d72bed61d2 100644 --- a/code/modules/mob/observer/ghost/ghost.dm +++ b/code/modules/mob/observer/ghost/ghost.dm @@ -185,9 +185,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ghost.timeofdeath = world.time // Because the living mob won't have a time of death and we want the respawn timer to work properly. announce_ghost_joinleave(ghost) -/mob/observer/ghost/is_active() - return FALSE - /mob/observer/ghost/Stat() . = ..() if(statpanel("Status") && SSevac.evacuation_controller) diff --git a/code/modules/mob/stripping.dm b/code/modules/mob/stripping.dm index 18b1ccb45e94..0856c8617870 100644 --- a/code/modules/mob/stripping.dm +++ b/code/modules/mob/stripping.dm @@ -47,7 +47,7 @@ visible_message(SPAN_NOTICE("\The [user] [sensor.get_sensors_locked() ? "" : "un"]locks \the [src]'s vitals sensor controls."), range = 2) return if("internals") - visible_message("\The [usr] is trying to set \the [src]'s internals!") + visible_message("\The [user] is trying to set \the [src]'s internals!") if(do_after(user, HUMAN_STRIP_DELAY, src, progress = 0)) toggle_internals(user) return From c750c274f81d12ef06fb36fecabdbb9654f4804a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:54:02 -0500 Subject: [PATCH 0077/1331] Remove unnecessary uses of usr in projectile code --- code/modules/projectiles/guns/launcher/rocket.dm | 2 +- .../projectiles/guns/projectile/dartgun.dm | 6 +++--- .../projectiles/targeting/targeting_client.dm | 15 +++++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/code/modules/projectiles/guns/launcher/rocket.dm b/code/modules/projectiles/guns/launcher/rocket.dm index ee2bc15639bf..d8d3d2e48bd8 100644 --- a/code/modules/projectiles/guns/launcher/rocket.dm +++ b/code/modules/projectiles/guns/launcher/rocket.dm @@ -32,7 +32,7 @@ to_chat(user, "[rockets.len] / [max_rockets] rockets.") return TRUE else - to_chat(usr, "\The [src] cannot hold more rockets.") + to_chat(user, "\The [src] cannot hold more rockets.") return TRUE return ..() diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm index a34c5d36ae99..6c00e6c25942 100644 --- a/code/modules/projectiles/guns/projectile/dartgun.dm +++ b/code/modules/projectiles/guns/projectile/dartgun.dm @@ -151,13 +151,13 @@ else if (href_list["eject"]) var/index = text2num(href_list["eject"]) if(beakers[index]) - remove_beaker(beakers[index], usr) + remove_beaker(beakers[index], user) . = TOPIC_REFRESH else if (href_list["eject_cart"]) - unload_ammo(usr) + unload_ammo(user) . = TOPIC_REFRESH - Interact(usr) + Interact(user) /obj/item/gun/projectile/dartgun/medical starting_chems = list(/decl/material/liquid/burn_meds,/decl/material/liquid/brute_meds,/decl/material/liquid/antitoxins) diff --git a/code/modules/projectiles/targeting/targeting_client.dm b/code/modules/projectiles/targeting/targeting_client.dm index eeaf61909deb..082471448a5c 100644 --- a/code/modules/projectiles/targeting/targeting_client.dm +++ b/code/modules/projectiles/targeting/targeting_client.dm @@ -1,12 +1,11 @@ //These are called by the on-screen buttons, adjusting what the victim can and cannot do. /client/proc/add_gun_icons() - if(!usr || !usr.item_use_icon) return 1 // This can runtime if someone manages to throw a gun out of their hand before the proc is called. - screen |= usr.item_use_icon - screen |= usr.gun_move_icon - screen |= usr.radio_use_icon + if(!mob.item_use_icon) return 1 // This can runtime if someone manages to throw a gun out of their hand before the proc is called. + screen |= mob.item_use_icon + screen |= mob.gun_move_icon + screen |= mob.radio_use_icon /client/proc/remove_gun_icons() - if(!usr) return 1 // Runtime prevention on N00k agents spawning with SMG - screen -= usr.item_use_icon - screen -= usr.gun_move_icon - screen -= usr.radio_use_icon + screen -= mob.item_use_icon + screen -= mob.gun_move_icon + screen -= mob.radio_use_icon From 525eddf776b04ae2ebf3af8cfeb2a1d9dafc2fc5 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:54:15 -0500 Subject: [PATCH 0078/1331] Remove unnecessary uses of usr in reagent code --- code/modules/reagents/Chemistry-Machinery.dm | 2 +- code/modules/reagents/dispenser/cartridge.dm | 9 +++++---- code/modules/reagents/reagent_containers.dm | 4 ++-- code/modules/reagents/reagent_containers/borghydro.dm | 4 ++-- code/modules/reagents/reagent_containers/food/eggs.dm | 4 ++-- code/modules/reagents/reagent_containers/glass.dm | 6 +++--- code/modules/reagents/reagent_containers/spray.dm | 6 +++--- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index eaac5570598d..0216e31ad13a 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -254,7 +254,7 @@ spawn() has_sprites += user.client for(var/i = 1 to MAX_PILL_SPRITE) - send_rsc(usr, icon('icons/obj/items/chem/pill.dmi', "pill" + num2text(i)), "pill[i].png") + send_rsc(user, icon('icons/obj/items/chem/pill.dmi', "pill" + num2text(i)), "pill[i].png") var/dat = list() dat += "[name]" dat += "[name] Menu:" diff --git a/code/modules/reagents/dispenser/cartridge.dm b/code/modules/reagents/dispenser/cartridge.dm index ab55f5dd9753..62f5d50a7e32 100644 --- a/code/modules/reagents/dispenser/cartridge.dm +++ b/code/modules/reagents/dispenser/cartridge.dm @@ -48,13 +48,14 @@ else if(user) to_chat(user, SPAN_NOTICE("You clear the label on \the [src].")) -/obj/item/chems/chem_disp_cartridge/attack_self() - ..() +/obj/item/chems/chem_disp_cartridge/attack_self(mob/user) + if((. = ..())) + return if (ATOM_IS_OPEN_CONTAINER(src)) - to_chat(usr, SPAN_NOTICE("You put the cap on \the [src].")) + to_chat(user, SPAN_NOTICE("You put the cap on \the [src].")) atom_flags ^= ATOM_FLAG_OPEN_CONTAINER else - to_chat(usr, SPAN_NOTICE("You take the cap off \the [src].")) + to_chat(user, SPAN_NOTICE("You take the cap off \the [src].")) atom_flags |= ATOM_FLAG_OPEN_CONTAINER /obj/item/chems/chem_disp_cartridge/afterattack(obj/target, mob/user, proximity_flag, click_parameters) diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 49d66cb0af70..a39da55a863c 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -43,10 +43,10 @@ /obj/item/chems/proc/cannot_interact(mob/user) if(!CanPhysicallyInteract(user)) - to_chat(usr, SPAN_WARNING("You're in no condition to do that!")) + to_chat(user, SPAN_WARNING("You're in no condition to do that!")) return TRUE if(ismob(loc) && loc != user) - to_chat(usr, SPAN_WARNING("You can't set transfer amounts while \the [src] is being held by someone else.")) + to_chat(user, SPAN_WARNING("You can't set transfer amounts while \the [src] is being held by someone else.")) return TRUE return FALSE diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index 6e69142d458c..06147c369b40 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -100,14 +100,14 @@ return -/obj/item/chems/borghypo/OnTopic(var/href, var/list/href_list) +/obj/item/chems/borghypo/OnTopic(mob/user, href_list, datum/topic_state/state) if(href_list["reagent_index"]) var/index = text2num(href_list["reagent_index"]) if(index > 0 && index <= reagent_ids.len) playsound(loc, 'sound/effects/pop.ogg', 50, 0) mode = index var/decl/material/R = reagent_ids[mode] - to_chat(usr, "Synthesizer is now producing '[initial(R.name)]'.") + to_chat(user, "Synthesizer is now producing '[initial(R.name)]'.") return TOPIC_REFRESH /obj/item/chems/borghypo/examine(mob/user, distance) diff --git a/code/modules/reagents/reagent_containers/food/eggs.dm b/code/modules/reagents/reagent_containers/food/eggs.dm index bf707313e556..ea7851b65edc 100644 --- a/code/modules/reagents/reagent_containers/food/eggs.dm +++ b/code/modules/reagents/reagent_containers/food/eggs.dm @@ -39,10 +39,10 @@ var/clr = W.get_tool_property(TOOL_PEN, TOOL_PROP_COLOR_NAME) if(!(clr in list("blue","green","mime","orange","purple","rainbow","red","yellow"))) - to_chat(usr, SPAN_WARNING("The egg refuses to take on this color!")) + to_chat(user, SPAN_WARNING("The egg refuses to take on this color!")) return TRUE - to_chat(usr, SPAN_NOTICE("You color \the [src] [clr]")) + to_chat(user, SPAN_NOTICE("You color \the [src] [clr]")) icon_state = "egg-[clr]" return TRUE return ..() diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index d120bce478c9..64e7fb66199b 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -56,14 +56,14 @@ /obj/item/chems/glass/proc/can_lid() return TRUE -/obj/item/chems/glass/attack_self() +/obj/item/chems/glass/attack_self(mob/user) . = ..() if(!. && can_lid()) if(ATOM_IS_OPEN_CONTAINER(src)) - to_chat(usr, SPAN_NOTICE("You put the lid on \the [src].")) + to_chat(user, SPAN_NOTICE("You put the lid on \the [src].")) atom_flags ^= ATOM_FLAG_OPEN_CONTAINER else - to_chat(usr, SPAN_NOTICE("You take the lid off \the [src].")) + to_chat(user, SPAN_NOTICE("You take the lid off \the [src].")) atom_flags |= ATOM_FLAG_OPEN_CONTAINER update_icon() diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 2df686595f83..711c448ebdd3 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -85,7 +85,7 @@ /obj/item/chems/spray/attack_self(var/mob/user) if(has_safety()) - toggle_safety() + toggle_safety(user) return TRUE else //If no safety, we just toggle the nozzle @@ -98,9 +98,9 @@ /obj/item/chems/spray/proc/has_safety() return FALSE -/obj/item/chems/spray/proc/toggle_safety() +/obj/item/chems/spray/proc/toggle_safety(mob/user) safety = !safety - to_chat(usr, SPAN_NOTICE("You switch the safety [safety ? "on" : "off"].")) + to_chat(user, SPAN_NOTICE("You switch the safety [safety ? "on" : "off"].")) /obj/item/chems/spray/examine(mob/user, distance) . = ..() From 0f78949e76df83a7f17fd490fd1aa0be37c3af94 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:54:33 -0500 Subject: [PATCH 0079/1331] Remove unnecessary uses of usr in modular computers --- .../file_system/programs/command/card.dm | 18 +++---- .../file_system/programs/command/comm.dm | 50 +++++++++---------- .../engineering/network_monitoring.dm | 4 +- .../file_system/programs/generic/records.dm | 2 +- .../file_system/programs/generic/reports.dm | 2 +- .../networking/machinery/acl.dm | 14 +++--- code/modules/modular_computers/os/ui.dm | 12 ++--- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index 68257d94e532..af9339b6a132 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -151,7 +151,7 @@ module.show_assignments = 1 if("print") if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) - to_chat(usr, SPAN_WARNING("Access denied.")) + to_chat(user, SPAN_WARNING("Access denied.")) return if(computer.has_component(PART_PRINTER)) //This option should never be called if there is no printer if(module.mod_mode) @@ -175,7 +175,7 @@ contents += " [get_access_desc(A)]" if(!computer.print_paper(contents,"access report")) - to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") + to_chat(user, "Hardware error: Printer was unable to print the file. It may be out of paper.") return else var/contents = {"

    Crew Manifest

    @@ -183,7 +183,7 @@ [html_crew_manifest()] "} if(!computer.print_paper(contents, "crew manifest ([stationtime2text()])")) - to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") + to_chat(user, "Hardware error: Printer was unable to print the file. It may be out of paper.") return if("eject") var/obj/item/stock_parts/computer/card_slot/card_slot = computer.get_component(PART_CARD) @@ -193,7 +193,7 @@ card_slot.insert_id(user.get_active_held_item(), user) if("terminate") if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) - to_chat(usr, SPAN_WARNING("Access denied.")) + to_chat(user, SPAN_WARNING("Access denied.")) return if(computer) id_card.assignment = "Terminated" @@ -201,7 +201,7 @@ RAISE_EVENT(/decl/observ/employee_id_terminated, id_card) if("edit") if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) - to_chat(usr, SPAN_WARNING("Access denied.")) + to_chat(user, SPAN_WARNING("Access denied.")) return if(computer) var/static/regex/hash_check = regex(@"^[0-9a-fA-F]{32}$") @@ -212,7 +212,7 @@ id_card.formal_name_suffix = initial(id_card.formal_name_suffix) id_card.formal_name_prefix = initial(id_card.formal_name_prefix) else - computer.show_error(usr, "Invalid name entered!") + computer.show_error(user, "Invalid name entered!") else if(href_list["account"]) var/account_num = text2num(input("Enter account number.", "Account", id_card.associated_account_number)) id_card.associated_account_number = account_num @@ -243,11 +243,11 @@ if(!isnull(sug_blood_type) && CanUseTopic(user)) id_card.blood_type = sug_blood_type else if(href_list["front_photo"]) - var/photo = get_photo(usr) + var/photo = get_photo(user) if(photo && CanUseTopic(user)) id_card.front = photo else if(href_list["side_photo"]) - var/photo = get_photo(usr) + var/photo = get_photo(user) if(photo && CanUseTopic(user)) id_card.side = photo else if(href_list["load_data"]) @@ -275,7 +275,7 @@ apply_access(id_card, access) if("assign") if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) - to_chat(usr, SPAN_WARNING("Access denied.")) + to_chat(user, SPAN_WARNING("Access denied.")) return if(computer && id_card) var/t1 = href_list["assign_target"] diff --git a/code/modules/modular_computers/file_system/programs/command/comm.dm b/code/modules/modular_computers/file_system/programs/command/comm.dm index 8d9a59feacfe..fbb4c292e2c5 100644 --- a/code/modules/modular_computers/file_system/programs/command/comm.dm +++ b/code/modules/modular_computers/file_system/programs/command/comm.dm @@ -62,7 +62,7 @@ data["message_line1"] = msg_line1 data["message_line2"] = msg_line2 data["state"] = current_status - data["isAI"] = issilicon(usr) + data["isAI"] = issilicon(user) data["authenticated"] = is_authenticated(user) data["boss_short"] = global.using_map.boss_short @@ -153,17 +153,17 @@ current_status = text2num(href_list["target"]) if("announce") . = 1 - if(is_authenticated(user) && !issilicon(usr) && ntn_comm) + if(is_authenticated(user) && !issilicon(user) && ntn_comm) if(user) var/obj/item/card/id/id_card = user.GetIdCard() crew_announcement.announcer = GetNameAndAssignmentFromId(id_card) else crew_announcement.announcer = "Unknown" if(announcment_cooldown) - to_chat(usr, "Please allow at least one minute to pass between announcements.") + to_chat(user, "Please allow at least one minute to pass between announcements.") return TRUE - var/input = input(usr, "Please write a message to announce to the [station_name()].", "Priority Announcement") as null|message - if(!input || !can_still_topic() || filter_block_message(usr, input)) + var/input = input(user, "Please write a message to announce to the [station_name()].", "Priority Announcement") as null|message + if(!input || !can_still_topic() || filter_block_message(user, input)) return 1 var/affected_zlevels = SSmapping.get_connected_levels(get_host_z()) crew_announcement.Announce(input, zlevels = affected_zlevels) @@ -174,35 +174,35 @@ . = 1 if(href_list["target"] == "emagged") if(program) - if(is_authenticated(user) && program.computer.emagged() && !issilicon(usr) && ntn_comm) + if(is_authenticated(user) && program.computer.emagged() && !issilicon(user) && ntn_comm) if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") + to_chat(user, "Arrays recycling. Please stand by.") SSnano.update_uis(src) return - var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text) - if(!input || !can_still_topic() || filter_block_message(usr, input)) + var/input = sanitize(input(user, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text) + if(!input || !can_still_topic() || filter_block_message(user, input)) return 1 - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_say("[key_name(usr)] has made an illegal announcement: [input]") + Syndicate_announce(input, user) + to_chat(user, "Message transmitted.") + log_say("[key_name(user)] has made an illegal announcement: [input]") centcomm_message_cooldown = 1 spawn(300)//30 second cooldown centcomm_message_cooldown = 0 else if(href_list["target"] == "regular") - if(is_authenticated(user) && !issilicon(usr) && ntn_comm) + if(is_authenticated(user) && !issilicon(user) && ntn_comm) if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") + to_chat(user, "Arrays recycling. Please stand by.") SSnano.update_uis(src) return if(!is_relay_online())//Contact Centcom has a check, Syndie doesn't to allow for Traitor funs. - to_chat(usr, "No emergency communication relay detected. Unable to transmit message.") + to_chat(user, "No emergency communication relay detected. Unable to transmit message.") return 1 var/input = sanitize(input("Please choose a message to transmit to [global.using_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text) - if(!input || !can_still_topic() || filter_block_message(usr, input)) + if(!input || !can_still_topic() || filter_block_message(user, input)) return 1 - Centcomm_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_say("[key_name(usr)] has made an IA [global.using_map.boss_short] announcement: [input]") + Centcomm_announce(input, user) + to_chat(user, "Message transmitted.") + log_say("[key_name(user)] has made an IA [global.using_map.boss_short] announcement: [input]") centcomm_message_cooldown = 1 spawn(300) //30 second cooldown centcomm_message_cooldown = 0 @@ -241,7 +241,7 @@ post_status(href_list["target"]) if("setalert") . = 1 - if(is_authenticated(user) && !issilicon(usr) && ntn_cont && ntn_comm) + if(is_authenticated(user) && !issilicon(user) && ntn_cont && ntn_comm) var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) var/decl/security_level/target_level = locate(href_list["target"]) in security_state.comm_console_security_levels if(target_level && security_state.can_switch_to(target_level)) @@ -250,7 +250,7 @@ if(security_state.set_security_level(target_level)) SSstatistics.add_field(target_level.type,1) else - to_chat(usr, "You press the button, but a red light flashes and nothing happens.") //This should never happen + to_chat(user, "You press the button, but a red light flashes and nothing happens.") //This should never happen current_status = STATE_DEFAULT if("viewmessage") @@ -270,13 +270,13 @@ . = 1 if(is_authenticated(user) && ntn_comm) if(!program.computer.print_paper(current_viewing_message["contents"],current_viewing_message["title"])) - to_chat(usr, "Hardware Error: Printer was unable to print the selected file.") + to_chat(user, "Hardware Error: Printer was unable to print the selected file.") if("unbolt_doors") global.using_map.unbolt_saferooms() - to_chat(usr, "The console beeps, confirming the signal was sent to have the saferooms unbolted.") + to_chat(user, "The console beeps, confirming the signal was sent to have the saferooms unbolted.") if("bolt_doors") global.using_map.bolt_saferooms() - to_chat(usr, "The console beeps, confirming the signal was sent to have the saferooms bolted.") + to_chat(user, "The console beeps, confirming the signal was sent to have the saferooms bolted.") #undef STATE_DEFAULT #undef STATE_MESSAGELIST @@ -365,7 +365,7 @@ var/global/last_message_id = 0 if(isnull(emergency)) emergency = 1 - if(!global.universe.OnShuttleCall(usr)) + if(!global.universe.OnShuttleCall(user)) to_chat(user, "Cannot establish a connection.") return diff --git a/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm b/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm index 0a0dd383145f..0b111c2306ae 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm @@ -119,7 +119,7 @@ return TOPIC_HANDLED var/new_roles = global.all_mainframe_roles - M.roles if(!length(new_roles)) - to_chat(usr, SPAN_WARNING("This server already has all possible roles enabled.")) + to_chat(user, SPAN_WARNING("This server already has all possible roles enabled.")) return TOPIC_HANDLED var/role = input(user,"What role to enable on this server?") as null|anything in new_roles if(role && CanUseTopic(user, state)) @@ -132,7 +132,7 @@ if(!istype(M)) return TOPIC_HANDLED if(!length(M.roles)) - to_chat(usr, SPAN_WARNING("This server has no enabled roles to remove.")) + to_chat(user, SPAN_WARNING("This server has no enabled roles to remove.")) return TOPIC_HANDLED var/role = input(user,"What role to disable on this server?") as null|anything in M.roles if(role && CanUseTopic(user, state)) diff --git a/code/modules/modular_computers/file_system/programs/generic/records.dm b/code/modules/modular_computers/file_system/programs/generic/records.dm index 4d79e385dd3a..0305f32e421a 100644 --- a/code/modules/modular_computers/file_system/programs/generic/records.dm +++ b/code/modules/modular_computers/file_system/programs/generic/records.dm @@ -163,7 +163,7 @@ var/obj/item/photo/photo = user.get_active_held_item() return photo.img if(issilicon(user)) - var/mob/living/silicon/tempAI = usr + var/mob/living/silicon/tempAI = user var/obj/item/photo/selection = tempAI.GetPicture() if (selection) return selection.img diff --git a/code/modules/modular_computers/file_system/programs/generic/reports.dm b/code/modules/modular_computers/file_system/programs/generic/reports.dm index 4897bd583853..a88753fdd88f 100644 --- a/code/modules/modular_computers/file_system/programs/generic/reports.dm +++ b/code/modules/modular_computers/file_system/programs/generic/reports.dm @@ -141,7 +141,7 @@ return 1 var/field_ID = text2num(href_list["ID"]) var/datum/report_field/field = selected_report.field_from_ID(field_ID) - if(!field || !(field.get_perms(get_access(usr), usr) & OS_WRITE_ACCESS)) + if(!field || !(field.get_perms(get_access(user), user) & OS_WRITE_ACCESS)) return 1 field.ask_value(user) //Handles the remaining IO. return 1 diff --git a/code/modules/modular_computers/networking/machinery/acl.dm b/code/modules/modular_computers/networking/machinery/acl.dm index 566d6e178162..7fe16ac83c03 100644 --- a/code/modules/modular_computers/networking/machinery/acl.dm +++ b/code/modules/modular_computers/networking/machinery/acl.dm @@ -32,14 +32,14 @@ if(href_list["back"]) current_group = null return TOPIC_REFRESH - + if(href_list["create_group"]) - var/group_name = sanitize_for_group(input(usr, "Enter the name of the new group. Maximum 15 characters, only alphanumeric characters, _ and - are allowed:", "Create Group")) + var/group_name = sanitize_for_group(input(user, "Enter the name of the new group. Maximum 15 characters, only alphanumeric characters, _ and - are allowed:", "Create Group")) if(!length(group_name)) return TOPIC_HANDLED if(!CanInteract(user, global.default_topic_state)) return TOPIC_REFRESH - + var/output = D.add_group(group_name, current_group) if(group_name in D.all_groups) to_chat(user, SPAN_NOTICE(output)) @@ -59,15 +59,15 @@ else to_chat(user, SPAN_NOTICE(output)) return TOPIC_REFRESH - + if(href_list["toggle_submanagement"]) D.toggle_submanagement() return TOPIC_REFRESH - + if(href_list["toggle_parent_account_creation"]) D.toggle_parent_account_creation() return TOPIC_REFRESH - + if(href_list["view_child_groups"]) var/parent_group = href_list["view_child_groups"] if(parent_group && (parent_group in D.group_dict)) @@ -75,7 +75,7 @@ else current_group = null return TOPIC_REFRESH - + if(href_list["info"]) switch(href_list["info"]) if("submanagement") diff --git a/code/modules/modular_computers/os/ui.dm b/code/modules/modular_computers/os/ui.dm index 59ec11f651eb..e619be1904ff 100644 --- a/code/modules/modular_computers/os/ui.dm +++ b/code/modules/modular_computers/os/ui.dm @@ -61,7 +61,7 @@ . = min(., extension_status(user)) // Handles user's GUI input -/datum/extension/interactive/os/extension_act(href, href_list, user) +/datum/extension/interactive/os/extension_act(href, href_list, mob/user) if( href_list["PC_exit"] ) kill_program(active_program) return TOPIC_HANDLED @@ -94,7 +94,7 @@ system_shutdown() return TOPIC_HANDLED if( href_list["PC_minimize"] ) - minimize_program(usr) + minimize_program(user) return TOPIC_HANDLED if( href_list["PC_killprogram"] ) @@ -105,7 +105,7 @@ kill_program(P) update_uis() - to_chat(usr, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.") + to_chat(user, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.") return TOPIC_HANDLED if( href_list["PC_runprogram"] ) @@ -117,15 +117,15 @@ return TOPIC_REFRESH if( href_list["PC_terminal"] ) - open_terminal(usr) + open_terminal(user) return TOPIC_HANDLED if( href_list["PC_login"]) - login_prompt(usr) + login_prompt(user) return TOPIC_REFRESH if( href_list["PC_logout"]) - logout_account(usr) + logout_account(user) return TOPIC_REFRESH /datum/extension/interactive/os/proc/regular_ui_update() From 23aa0f2c51dd21f15ede326c408aad3ab5e528d1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:55:32 -0500 Subject: [PATCH 0080/1331] Remove unnecessary uses of usr in paperwork code --- code/modules/paperwork/clipboard.dm | 2 +- code/modules/paperwork/folders.dm | 2 +- code/modules/paperwork/paper.dm | 2 +- code/modules/paperwork/paperbin.dm | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index ee1e92dd890d..d25c1d6cbe50 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -111,7 +111,7 @@ user.set_machine(src) show_browser(user, dat, "window=[initial(name)]") onclose(user, initial(name)) - add_fingerprint(usr) + add_fingerprint(user) return /obj/item/clipboard/proc/add_pen(var/obj/item/I, var/mob/user) diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index cdcbe7b3444e..a5008f8e2256 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -44,7 +44,7 @@ else if(IS_PEN(W)) updateUsrDialog() - var/n_name = sanitize_safe(input(usr, "What would you like to label the folder?", "Folder Labelling", null) as text, MAX_NAME_LEN) + var/n_name = sanitize_safe(input(user, "What would you like to label the folder?", "Folder Labelling", null) as text, MAX_NAME_LEN) if(!CanPhysicallyInteractWith(user, src)) to_chat(user, SPAN_WARNING("You must stay close to \the [src].")) return TRUE diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index b5beaeb8400d..92a37e75a1be 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -332,7 +332,7 @@ var/pen_flags = I.get_tool_property(TOOL_PEN, TOOL_PROP_PEN_FLAG) var/decl/tool_archetype/pen/parch = GET_DECL(TOOL_PEN) if(!(pen_flags & PEN_FLAG_ACTIVE)) - parch.toggle_active(usr, I) + parch.toggle_active(user, I) var/iscrayon = pen_flags & PEN_FLAG_CRAYON var/isfancy = pen_flags & PEN_FLAG_FANCY diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 5d900aab3575..70b9b4c453d3 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -129,7 +129,7 @@ //Dump all stored papers too for(var/i=1 to amount) var/obj/item/paper/P = new /obj/item/paper(forced_loc) - P.merge_with_existing(forced_loc, usr) + P.merge_with_existing(forced_loc, user) LAZYCLEARLIST(papers) /obj/item/paper_bin/proc/add_paper(var/obj/item/paper/P) From a5b3bb4d07c7a370629f3f02e306cb0971fa2606 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:56:08 -0500 Subject: [PATCH 0081/1331] Remove unnecessary uses of usr in power/machinery code --- code/modules/power/cable.dm | 4 ++-- code/modules/power/port_gen.dm | 4 ++-- code/modules/power/singularity/emitter.dm | 8 ++++---- code/modules/power/smes_construction.dm | 2 +- code/modules/power/turbine.dm | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 86418a32c3a1..dd74a49e3d3b 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -235,7 +235,7 @@ By design, d1 is the smallest direction and d2 is the highest if(c.d1 == UP || c.d2 == UP) qdel(c) - investigate_log("was cut by [key_name(usr, usr.client)] in [get_area_name(user)]","wires") + investigate_log("was cut by [key_name(user, user.client)] in [get_area_name(user)]","wires") qdel(src) @@ -245,7 +245,7 @@ By design, d1 is the smallest direction and d2 is the highest return 0 if (electrocute_mob(user, powernet, src, siemens_coeff)) spark_at(src, amount=5, cardinal_only = TRUE) - if(HAS_STATUS(usr, STAT_STUN)) + if(HAS_STATUS(user, STAT_STUN)) return 1 return 0 diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index ae7a1bc6e423..431439981132 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -76,9 +76,9 @@ if(distance > 1) return if(active) - to_chat(usr, "The generator is on.") + to_chat(user, "The generator is on.") else - to_chat(usr, "The generator is off.") + to_chat(user, "The generator is off.") /obj/machinery/port_gen/emp_act(severity) if(!active) return diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index d7ae1df1a1ad..233b66d985f9 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -67,8 +67,8 @@ if(active==1) active = 0 to_chat(user, "You turn off \the [src].") - log_and_message_admins("turned off \the [src]") - investigate_log("turned off by [key_name_admin(user || usr)]","singulo") + log_and_message_admins("turned off \the [src]", user) + investigate_log("turned off by [key_name_admin(user)]","singulo") else active = 1 if(user) @@ -77,8 +77,8 @@ to_chat(user, "You turn on \the [src].") shot_number = 0 fire_delay = get_initial_fire_delay() - log_and_message_admins("turned on \the [src]") - investigate_log("turned on by [key_name_admin(user || usr)]","singulo") + log_and_message_admins("turned on \the [src]", user) + investigate_log("turned on by [key_name_admin(user)]","singulo") update_icon() else to_chat(user, "The controls are locked!") diff --git a/code/modules/power/smes_construction.dm b/code/modules/power/smes_construction.dm index 3a956c49e00d..59665f270cb1 100644 --- a/code/modules/power/smes_construction.dm +++ b/code/modules/power/smes_construction.dm @@ -155,7 +155,7 @@ var/obj/item/clothing/gloves/G = h_user.get_equipped_item(slot_gloves_str) if(istype(G) && G.siemens_coefficient == 0) user_protected = 1 - log_and_message_admins("SMES FAILURE: [src.x]X [src.y]Y [src.z]Z User: [usr.ckey], Intensity: [intensity]/100 - JMP") + log_and_message_admins("SMES FAILURE: [src.x]X [src.y]Y [src.z]Z User: [user.ckey], Intensity: [intensity]/100 - JMP") switch (intensity) if (0 to 15) diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 8fb0064ca709..aa6c612f55b0 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -185,7 +185,7 @@ /obj/machinery/turbine/OnTopic(user, href_list) if(href_list["close"]) - close_browser(usr, "window=turbine") + close_browser(user, "window=turbine") return TOPIC_HANDLED if(href_list["str"]) @@ -244,9 +244,9 @@ -/obj/machinery/computer/turbine_computer/OnTopic(user, href_list) +/obj/machinery/computer/turbine_computer/OnTopic(mob/user, href_list) if( href_list["view"] ) - usr.client.eye = src.compressor + user.client.eye = src.compressor . = TOPIC_HANDLED else if( href_list["str"] ) src.compressor.starter = !src.compressor.starter From 92832d0e865905019b9216c4829979ac1ce08ac0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:56:22 -0500 Subject: [PATCH 0082/1331] Remove unnecessary uses of usr in vehicle code --- code/modules/vehicles/bike.dm | 2 +- code/modules/vehicles/cargo_train.dm | 2 +- code/modules/vehicles/vehicle.dm | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index 08318914e2e6..a5aa85daf86f 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -63,7 +63,7 @@ usr.visible_message("\The [usr] puts up \the [src]'s kickstand.") else if(isspaceturf(src.loc)) - to_chat(usr, " You don't think kickstands work in space...") + to_chat(usr, "You don't think kickstands work in space...") return usr.visible_message("\The [usr] puts down \the [src]'s kickstand.") diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm index 342d8dbba7ec..62606d2626bc 100644 --- a/code/modules/vehicles/cargo_train.dm +++ b/code/modules/vehicles/cargo_train.dm @@ -193,7 +193,7 @@ if(distance > 1) return - if(!ishuman(usr)) + if(!ishuman(user)) return to_chat(user, "The power light is [on ? "on" : "off"].\nThere are[key ? "" : " no"] keys in the ignition.") diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index fc8fbe1ee9d9..edfc8efaba4a 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -240,23 +240,23 @@ turn_on() return -/obj/vehicle/proc/insert_cell(var/obj/item/cell/C, var/mob/living/human/H) +/obj/vehicle/proc/insert_cell(var/obj/item/cell/C, var/mob/living/user) if(cell) return if(!istype(C)) return - if(!H.try_unequip(C, src)) + if(!user.try_unequip(C, src)) return cell = C powercheck() - to_chat(usr, "You install [C] in [src].") + to_chat(user, "You install [C] in [src].") -/obj/vehicle/proc/remove_cell(var/mob/living/human/H) +/obj/vehicle/proc/remove_cell(var/mob/living/user) if(!cell) return - to_chat(usr, "You remove [cell] from [src].") - H.put_in_hands(cell) + to_chat(user, "You remove [cell] from [src].") + user.put_in_hands(cell) cell = null powercheck() From 12e20b7f42fa003c1bc8fa9cbe4080bf77537aaa Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:56:32 -0500 Subject: [PATCH 0083/1331] Remove unnecessary uses of usr in shuttle code --- code/modules/shuttles/docking_beacon.dm | 16 ++++++++-------- code/modules/shuttles/shuttle_console.dm | 2 +- code/modules/shuttles/shuttle_console_multi.dm | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/code/modules/shuttles/docking_beacon.dm b/code/modules/shuttles/docking_beacon.dm index 8c0f64d87b1f..bac4e5fe16a1 100644 --- a/code/modules/shuttles/docking_beacon.dm +++ b/code/modules/shuttles/docking_beacon.dm @@ -94,7 +94,7 @@ if(href_list["edit_codes"]) var/newcode = sanitize(input("Input new docking codes:", "Docking codes", docking_codes) as text|null) - if(!CanInteract(usr,state)) + if(!CanInteract(user,state)) return TOPIC_NOACTION if(newcode) docking_codes = uppertext(newcode) @@ -103,7 +103,7 @@ if(href_list["edit_display_name"]) var/newname = sanitize(input("Input new display name:", "Display name", display_name) as text|null) - if(!CanInteract(usr,state)) + if(!CanInteract(user,state)) return TOPIC_NOACTION if(newname) display_name = newname @@ -114,7 +114,7 @@ if(href_list["edit_size"]) var/newwidth = input("Input new docking width for beacon:", "Docking size", docking_width) as num|null var/newheight = input("Input new docking height for beacon:", "Docking size", docking_height) as num|null - if(!CanInteract(usr,state)) + if(!CanInteract(user,state)) return TOPIC_NOACTION if(newwidth && newheight) docking_width = clamp(newwidth, 0, MAX_DOCKING_SIZE) @@ -134,7 +134,7 @@ return TOPIC_REFRESH if(href_list["edit_permitted_shuttles"]) - var/shuttle = sanitize(input(usr,"Enter the ID of the shuttle you wish to permit/unpermit for this beacon:", "Enter ID") as text|null) + var/shuttle = sanitize(input(user,"Enter the ID of the shuttle you wish to permit/unpermit for this beacon:", "Enter ID") as text|null) if(shuttle) if(shuttle in permitted_shuttles) permitted_shuttles -= shuttle @@ -166,7 +166,7 @@ if(href_list["change_color"]) var/new_color = input(user, "Choose a color.", "\the [src]", ship_color) as color|null - if(!CanInteract(usr,state)) + if(!CanInteract(user,state)) return TOPIC_NOACTION if(new_color && new_color != ship_color) ship_color = new_color @@ -175,7 +175,7 @@ if(href_list["change_ship_name"]) var/new_ship_name = sanitize(input(user, "Enter a new name for the ship:", "Change ship name.") as null|text) - if(!CanInteract(usr,state)) + if(!CanInteract(user,state)) return TOPIC_NOACTION if(!new_ship_name) return TOPIC_HANDLED @@ -195,7 +195,7 @@ if(!construction_mode) return TOPIC_HANDLED var/confirm = alert(user, "This will permanently finalize the ship, are you sure?", "Ship finalization", "No", "Yes") - if(!CanInteract(usr,state)) + if(!CanInteract(user,state)) return TOPIC_NOACTION if(confirm == "Yes") if(create_ship()) @@ -203,7 +203,7 @@ ship_name = "" LAZYCLEARLIST(errors) else - to_chat(usr, SPAN_WARNING("Could not finalize the construction of the ship!")) + to_chat(user, SPAN_WARNING("Could not finalize the construction of the ship!")) return TOPIC_REFRESH /obj/machinery/docking_beacon/proc/allow_projection() diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index 67337ab8fe25..422e33e3f0e1 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -89,7 +89,7 @@ if(href_list["set_codes"]) var/newcode = input("Input new docking codes", "Docking codes", shuttle.docking_codes) as text|null - if (newcode && CanInteract(usr, global.default_topic_state)) + if (newcode && CanInteract(user, global.default_topic_state)) shuttle.set_docking_codes(uppertext(newcode)) return TOPIC_REFRESH diff --git a/code/modules/shuttles/shuttle_console_multi.dm b/code/modules/shuttles/shuttle_console_multi.dm index c55dcc37721a..28f8805a36d4 100644 --- a/code/modules/shuttles/shuttle_console_multi.dm +++ b/code/modules/shuttles/shuttle_console_multi.dm @@ -9,14 +9,14 @@ "can_pick" = shuttle.moving_status == SHUTTLE_IDLE, ) -/obj/machinery/computer/shuttle_control/multi/handle_topic_href(var/datum/shuttle/autodock/multi/shuttle, var/list/href_list) +/obj/machinery/computer/shuttle_control/multi/handle_topic_href(var/datum/shuttle/autodock/multi/shuttle, var/list/href_list, var/user) if((. = ..()) != null) return if(href_list["pick"]) var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations() - if(dest_key && CanInteract(usr, global.default_topic_state)) - shuttle.set_destination(dest_key, usr) + if(dest_key && CanInteract(user, global.default_topic_state)) + shuttle.set_destination(dest_key, user) return TOPIC_REFRESH From 6362ba64548d507aa33263f58fb0c55ff00e8e79 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Dec 2024 23:56:46 -0500 Subject: [PATCH 0084/1331] Remove unnecessary uses of usr in miscellaneous code --- code/modules/clothing/neck/necklace/pendant_locket.dm | 4 ++-- code/modules/codex/codex_implant.dm | 2 +- code/modules/crafting/pottery/pottery_structures.dm | 2 +- .../detectivework/microscope/_forensic_machine.dm | 4 ++-- code/modules/economy/worth_cash.dm | 2 +- code/modules/fabrication/fabricator_intake.dm | 4 ++-- code/modules/games/boardgame.dm | 2 +- code/modules/games/cards.dm | 1 - code/modules/holodeck/HolodeckControl.dm | 2 +- code/modules/mechs/mech_interaction.dm | 4 ++-- code/modules/mining/ore_box.dm | 2 +- code/modules/mob_holder/_holder.dm | 4 ++-- code/modules/organs/external/_external.dm | 4 ++-- code/modules/overmap/internet/internet_uplink.dm | 4 ++-- code/modules/overmap/ships/computers/shuttle.dm | 10 +++++----- code/modules/shield_generators/handheld_diffuser.dm | 4 ++-- 16 files changed, 27 insertions(+), 28 deletions(-) diff --git a/code/modules/clothing/neck/necklace/pendant_locket.dm b/code/modules/clothing/neck/necklace/pendant_locket.dm index b0944c9c000d..930f7c927e38 100644 --- a/code/modules/clothing/neck/necklace/pendant_locket.dm +++ b/code/modules/clothing/neck/necklace/pendant_locket.dm @@ -42,9 +42,9 @@ return TRUE if(istype(used_item, /obj/item/paper) || istype(used_item, /obj/item/photo)) if(held) - to_chat(usr, SPAN_WARNING("\The [src] already has something inside it.")) + to_chat(user, SPAN_WARNING("\The [src] already has something inside it.")) else if(user.try_unequip(used_item, src)) - to_chat(usr, SPAN_NOTICE("You slip \the [used_item] into \the [src].")) + to_chat(user, SPAN_NOTICE("You slip \the [used_item] into \the [src].")) held = used_item return TRUE return ..() \ No newline at end of file diff --git a/code/modules/codex/codex_implant.dm b/code/modules/codex/codex_implant.dm index 6918ebd175ac..48d57b472edc 100644 --- a/code/modules/codex/codex_implant.dm +++ b/code/modules/codex/codex_implant.dm @@ -8,4 +8,4 @@ /obj/item/implant/codex/implanted(var/mob/source) . = ..(source) - to_chat(usr, "You feel the brief sensation of having an entire encyclopedia at the tip of your tongue as the codex implant meshes with your nervous system.") + to_chat(imp_in, SPAN_NOTICE("You feel the brief sensation of having an entire encyclopedia at the tip of your tongue as the codex implant meshes with your nervous system.")) diff --git a/code/modules/crafting/pottery/pottery_structures.dm b/code/modules/crafting/pottery/pottery_structures.dm index 34f021019fa0..2c9a369296b4 100644 --- a/code/modules/crafting/pottery/pottery_structures.dm +++ b/code/modules/crafting/pottery/pottery_structures.dm @@ -63,5 +63,5 @@ /decl/interaction_handler/open_firebox/invoked(atom/target, mob/user, obj/item/prop) var/obj/structure/fire_source/kiln/kiln = target kiln.firebox_open = !kiln.firebox_open - to_chat(usr, SPAN_NOTICE("You [kiln.firebox_open ? "open" : "close"] \the [kiln]'s firebox.")) + to_chat(user, SPAN_NOTICE("You [kiln.firebox_open ? "open" : "close"] \the [kiln]'s firebox.")) kiln.update_icon() diff --git a/code/modules/detectivework/microscope/_forensic_machine.dm b/code/modules/detectivework/microscope/_forensic_machine.dm index 9ba28f8bb138..5eb1834a2b55 100644 --- a/code/modules/detectivework/microscope/_forensic_machine.dm +++ b/code/modules/detectivework/microscope/_forensic_machine.dm @@ -111,7 +111,7 @@ /obj/machinery/forensic/handle_mouse_drop(atom/over, mob/user, params) if(user == over) - remove_sample(usr) + remove_sample(user) return TRUE . = ..() @@ -125,4 +125,4 @@ /decl/interaction_handler/forensics_remove_sample/invoked(atom/target, mob/user, obj/item/prop) var/obj/machinery/forensic/F = target - F.remove_sample(usr) + F.remove_sample(user) diff --git a/code/modules/economy/worth_cash.dm b/code/modules/economy/worth_cash.dm index fda0929fe7c4..2b0eec774a62 100644 --- a/code/modules/economy/worth_cash.dm +++ b/code/modules/economy/worth_cash.dm @@ -137,7 +137,7 @@ if(QDELETED(src) || get_worth() <= 1 || user.incapacitated() || loc != user) return TRUE - var/amount = input(usr, "How many [cur.name] do you want to take? (0 to [get_worth() - 1])", "Take Money", 20) as num + var/amount = input(user, "How many [cur.name] do you want to take? (0 to [get_worth() - 1])", "Take Money", 20) as num amount = round(clamp(amount, 0, floor(get_worth() - 1))) if(!amount || QDELETED(src) || get_worth() <= 1 || user.incapacitated() || loc != user) diff --git a/code/modules/fabrication/fabricator_intake.dm b/code/modules/fabrication/fabricator_intake.dm index 33c516ba6beb..c94d1681def8 100644 --- a/code/modules/fabrication/fabricator_intake.dm +++ b/code/modules/fabrication/fabricator_intake.dm @@ -115,10 +115,10 @@ if(istype(O, /obj/item/disk/design_disk)) var/obj/item/disk/design_disk/disk = O if(!disk.blueprint) - to_chat(usr, SPAN_WARNING("\The [O] is blank.")) + to_chat(user, SPAN_WARNING("\The [O] is blank.")) return TRUE if(disk.blueprint in installed_designs) - to_chat(usr, SPAN_WARNING("\The [src] is already loaded with the blueprint stored on \the [O].")) + to_chat(user, SPAN_WARNING("\The [src] is already loaded with the blueprint stored on \the [O].")) return TRUE installed_designs += disk.blueprint design_cache |= disk.blueprint diff --git a/code/modules/games/boardgame.dm b/code/modules/games/boardgame.dm index 6cd35f6b546f..5c7765027cb7 100644 --- a/code/modules/games/boardgame.dm +++ b/code/modules/games/boardgame.dm @@ -101,7 +101,7 @@ if(selected >= 0 && !isobserver(user)) dat += "
    Remove Selected Piece" show_browser(user, jointext(dat, null), "window=boardgame;size=430x500") // 50px * 8 squares + 30 margin - onclose(usr, "boardgame") + onclose(user, "boardgame") /obj/item/board/Topic(href, href_list) if(!usr.Adjacent(src)) diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index 25d30addecd4..c4351b213e2b 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -208,7 +208,6 @@ var/global/list/card_decks = list() for(var/mob/living/player in viewers(3)) if(!player.stat) players += player - //players -= usr var/mob/living/M = input("Who do you wish to deal a card?") as null|anything in players if(!usr || !src || !M) return diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index 3a497c97ce27..843d0ed4d48f 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -144,7 +144,7 @@ update_projections() to_chat(user, "You vastly increase projector power and override the safety and security protocols.") to_chat(user, "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call [global.using_map.company_name] maintenance and do not use the simulator.") - log_game("[key_name(usr)] emagged the Holodeck Control Computer") + log_game("[key_name(user)] emagged the Holodeck Control Computer") src.updateUsrDialog() return 1 else diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm index 77fc243c7057..bd5306b18bb6 100644 --- a/code/modules/mechs/mech_interaction.dm +++ b/code/modules/mechs/mech_interaction.dm @@ -314,7 +314,7 @@ if(!silent) to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked.")) return - hud_open.toggled() + hud_open.toggled(user) if(!silent) to_chat(user, SPAN_NOTICE("You open the hatch and climb out of \the [src].")) else @@ -506,7 +506,7 @@ to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked.")) return TRUE if(hud_open) - hud_open.toggled() + hud_open.toggled(user) return TRUE /mob/living/exosuit/default_hurt_interaction(var/mob/user) diff --git a/code/modules/mining/ore_box.dm b/code/modules/mining/ore_box.dm index b77e158dbe09..f61ac8e68862 100644 --- a/code/modules/mining/ore_box.dm +++ b/code/modules/mining/ore_box.dm @@ -31,7 +31,7 @@ return insert_ore(W, user) if(W.storage) var/added_ore = FALSE - W.storage.hide_from(usr) + W.storage.hide_from(user) for(var/obj/item/stack/material/ore/O in W.storage.get_contents()) if(total_ores >= maximum_ores) break diff --git a/code/modules/mob_holder/_holder.dm b/code/modules/mob_holder/_holder.dm index a462eff5ba09..ec0dcd39482f 100644 --- a/code/modules/mob_holder/_holder.dm +++ b/code/modules/mob_holder/_holder.dm @@ -115,9 +115,9 @@ if(length(cards)) LAZYDISTINCTADD(., cards) -/obj/item/holder/attack_self() +/obj/item/holder/attack_self(mob/user) for(var/mob/M in contents) - M.show_stripping_window(usr) + M.show_stripping_window(user) /obj/item/holder/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index 57bf6892044a..5712202d6aff 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -259,7 +259,7 @@ if(connecting_limb.organ_tag == parent_organ) if(length(connecting_limb.children)) - to_chat(usr, SPAN_WARNING("You cannot connect additional limbs to \the [connecting_limb].")) + to_chat(user, SPAN_WARNING("You cannot connect additional limbs to \the [connecting_limb].")) return TRUE var/mob/holder = loc @@ -282,7 +282,7 @@ else if(connecting_limb.parent_organ == organ_tag) if(LAZYLEN(children)) - to_chat(usr, SPAN_WARNING("You cannot connect additional limbs to \the [src].")) + to_chat(user, SPAN_WARNING("You cannot connect additional limbs to \the [src].")) return TRUE if(!user.try_unequip(connecting_limb, src)) diff --git a/code/modules/overmap/internet/internet_uplink.dm b/code/modules/overmap/internet/internet_uplink.dm index 337e9b075898..cd38e4509feb 100644 --- a/code/modules/overmap/internet/internet_uplink.dm +++ b/code/modules/overmap/internet/internet_uplink.dm @@ -181,7 +181,7 @@ var/global/list/internet_uplinks = list() return ..() -/obj/machinery/computer/internet_uplink/interface_interact(user) +/obj/machinery/computer/internet_uplink/interface_interact(mob/user) var/datum/extension/local_network_member/uplink_comp = get_extension(src, /datum/extension/local_network_member) var/datum/local_network/lan = uplink_comp.get_local_network() @@ -193,5 +193,5 @@ var/global/list/internet_uplinks = list() return FALSE var/datum/topic_state/remote/R = new(src, linked) - linked.ui_interact(usr, state = R) + linked.ui_interact(user, state = R) return TRUE \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm index ce08d9c7b59a..6543aebddfc0 100644 --- a/code/modules/overmap/ships/computers/shuttle.dm +++ b/code/modules/overmap/ships/computers/shuttle.dm @@ -42,8 +42,8 @@ if(length(port_choices)) port = input("Choose shuttle docking port:", "Shuttle Docking Port") as null|anything in port_choices else - to_chat(usr, SPAN_WARNING("No functional docking ports, defaulting to center-of-mass landing.")) - if(CanInteract(usr, global.default_topic_state) && (port in port_choices)) + to_chat(user, SPAN_WARNING("No functional docking ports, defaulting to center-of-mass landing.")) + if(CanInteract(user, global.default_topic_state) && (port in port_choices)) shuttle.set_port(port_choices[port]) if(href_list["pick"]) var/list/possible_d = shuttle.get_possible_destinations() @@ -51,9 +51,9 @@ if(possible_d.len) D = input("Choose shuttle destination", "Shuttle Destination") as null|anything in possible_d else - to_chat(usr, SPAN_WARNING("No valid landing sites in range.")) + to_chat(user, SPAN_WARNING("No valid landing sites in range.")) possible_d = shuttle.get_possible_destinations() - if(CanInteract(usr, global.default_topic_state) && (D in possible_d)) + if(CanInteract(user, global.default_topic_state) && (D in possible_d)) shuttle.set_destination(possible_d[D]) return TOPIC_REFRESH if(href_list["manual_landing"]) @@ -68,7 +68,7 @@ else start_landing(user, shuttle) return TOPIC_REFRESH - to_chat(usr, SPAN_WARNING("The manual controls look hopelessly complex to you!")) + to_chat(user, SPAN_WARNING("The manual controls look hopelessly complex to you!")) /obj/machinery/computer/shuttle_control/explore/proc/start_landing(var/mob/user, var/datum/shuttle/autodock/overmap/shuttle) var/obj/effect/overmap/visitable/current_sector = global.overmap_sectors[num2text(z)] diff --git a/code/modules/shield_generators/handheld_diffuser.dm b/code/modules/shield_generators/handheld_diffuser.dm index 250691aa2a62..4a546c0241f5 100644 --- a/code/modules/shield_generators/handheld_diffuser.dm +++ b/code/modules/shield_generators/handheld_diffuser.dm @@ -41,14 +41,14 @@ if(istype(S) && !S.diffused_for && !S.disabled_for && cell.checked_use(10 KILOWATTS * CELLRATE)) S.diffuse(20) -/obj/item/shield_diffuser/attack_self() +/obj/item/shield_diffuser/attack_self(mob/user) enabled = !enabled update_icon() if(enabled) START_PROCESSING(SSobj, src) else STOP_PROCESSING(SSobj, src) - to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].") + to_chat(user, "You turn \the [src] [enabled ? "on" : "off"].") /obj/item/shield_diffuser/examine(mob/user) . = ..() From 6189684deac3f11fad75d5e0cf82121f9bae99e6 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 11 Dec 2024 00:11:43 -0500 Subject: [PATCH 0085/1331] Remove unnecessary uses of usr in OnTopic overrides --- code/game/machinery/kitchen/microwave.dm | 8 ++++---- code/game/objects/items/devices/radio/radio.dm | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/code/game/machinery/kitchen/microwave.dm b/code/game/machinery/kitchen/microwave.dm index 632c1458349c..bda3e44c704c 100644 --- a/code/game/machinery/kitchen/microwave.dm +++ b/code/game/machinery/kitchen/microwave.dm @@ -415,20 +415,20 @@ ffuu.add_to_reagents(/decl/material/liquid/acrylamide, amount/10) return ffuu -/obj/machinery/microwave/OnTopic(href, href_list) +/obj/machinery/microwave/OnTopic(mob/user, href_list) switch(href_list["action"]) if ("cook") cook() return TOPIC_REFRESH if ("dispose") - dispose(usr) + dispose(user) return TOPIC_REFRESH if ("ejectitem") for(var/obj/O in get_contained_external_atoms()) if(strip_improper(O.name) == href_list["target"]) - eject_item(usr, O) + eject_item(user, O) break return TOPIC_REFRESH @@ -437,7 +437,7 @@ for(var/material_type in reagents.reagent_volumes) mat = GET_DECL(material_type) if(mat.name == href_list["target"]) - eject_reagent(usr, material_type) + eject_reagent(user, material_type) break return TOPIC_REFRESH diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index e4db86946c2a..2b4353629192 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -227,11 +227,11 @@ return STATUS_CLOSE return ..() -/obj/item/radio/OnTopic(href, href_list) +/obj/item/radio/OnTopic(mob/user, href_list) if((. = ..())) return - usr.set_machine(src) + user.set_machine(src) if(href_list["analog"]) if(can_use_analog) analog = text2num(href_list["analog"]) @@ -258,8 +258,8 @@ var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) set_frequency(new_frequency) if(hidden_uplink) - if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) - close_browser(usr, "window=radio") + if(hidden_uplink.check_trigger(user, frequency, traitor_frequency)) + close_browser(user, "window=radio") . = TOPIC_REFRESH else if (href_list["talk"]) toggle_broadcast() @@ -278,14 +278,14 @@ . = TOPIC_REFRESH else if(href_list["spec_freq"]) var freq = href_list["spec_freq"] - if(has_channel_access(usr, freq)) + if(has_channel_access(user, freq)) set_frequency(text2num(freq)) . = TOPIC_REFRESH if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk return TOPIC_HANDLED if(href_list["network_settings"]) var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) - D.ui_interact(usr) + D.ui_interact(user) . = TOPIC_HANDLED if(. & TOPIC_REFRESH) SSnano.update_uis(src) From 9c068f7c8d00e7d49beaaedad7b7e81453b92fe7 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Fri, 6 Dec 2024 18:34:54 +1100 Subject: [PATCH 0086/1331] Cleaning up some submap archetype code, allowing maps to override submap availability. --- .../subsystems/initialization/modpacks.dm | 2 +- code/controllers/subsystems/jobs.dm | 14 ++++++------- code/modules/mob/new_player/new_player.dm | 2 +- code/modules/submaps/_submap.dm | 6 +++--- code/modules/submaps/submap_archetype.dm | 17 +++++++++++----- code/modules/submaps/submap_job.dm | 8 ++++---- code/unit_tests/unique_tests.dm | 20 ------------------- maps/away/bearcat/bearcat.dm | 4 ++-- maps/away/liberia/liberia_jobs.dm | 2 +- maps/away/unishi/unishi.dm | 4 ++-- .../crashed_pod/crashed_pod.dm | 14 ++++++------- .../playablecolony/playablecolony.dm | 12 +++++------ maps/shaded_hills/shaded_hills_define.dm | 3 +++ maps/~mapsystem/maps.dm | 3 +++ 14 files changed, 52 insertions(+), 59 deletions(-) diff --git a/code/controllers/subsystems/initialization/modpacks.dm b/code/controllers/subsystems/initialization/modpacks.dm index 4e3819c4b560..5fad4ec549cb 100644 --- a/code/controllers/subsystems/initialization/modpacks.dm +++ b/code/controllers/subsystems/initialization/modpacks.dm @@ -40,7 +40,7 @@ SUBSYSTEM_DEF(modpacks) // Update compiled infolists and apply. default_submap_whitelisted_species |= global.using_map.default_species - for(var/decl/submap_archetype/submap in decls_repository.get_decls_of_type_unassociated(/decl/submap_archetype)) + for(var/decl/submap_archetype/submap in global.using_map.get_available_submap_archetypes()) if(islist(submap.whitelisted_species) && !length(submap.whitelisted_species)) submap.whitelisted_species |= SSmodpacks.default_submap_whitelisted_species if(islist(submap.blacklisted_species) && !length(submap.blacklisted_species)) diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index b900dcf8c32d..58e71e52d0d1 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -45,9 +45,9 @@ SUBSYSTEM_DEF(jobs) // Create abstract submap archetype jobs for use in prefs, etc. archetype_job_datums.Cut() - var/list/submap_archetypes = decls_repository.get_decls_of_subtype(/decl/submap_archetype) - for(var/atype in submap_archetypes) - var/decl/submap_archetype/arch = submap_archetypes[atype] + var/list/submap_archetypes = list() + for(var/decl/submap_archetype/arch as anything in global.using_map.get_available_submap_archetypes()) + submap_archetypes += arch for(var/jobtype in arch.crew_jobs) var/datum/job/job = get_by_path(jobtype) if(!job && ispath(jobtype, /datum/job/submap)) @@ -57,7 +57,8 @@ SUBSYSTEM_DEF(jobs) job = get_by_path(jobtype) if(job) archetype_job_datums |= job - submap_archetypes = sortTim(submap_archetypes, /proc/cmp_submap_archetype_asc, TRUE) + if(length(submap_archetypes)) + submap_archetypes = sortTim(submap_archetypes, /proc/cmp_submap_archetype_asc) // Load job configuration (is this even used anymore?) if(job_config_file && get_config_value(/decl/config/toggle/load_jobs_from_txt)) @@ -90,16 +91,15 @@ SUBSYSTEM_DEF(jobs) primary_job_datums = sortTim(primary_job_datums, /proc/cmp_job_desc) job_lists_by_map_name = list("[global.using_map.full_name]" = list("jobs" = primary_job_datums, "default_to_hidden" = FALSE)) - for(var/atype in submap_archetypes) + for(var/decl/submap_archetype/arch as anything in submap_archetypes) var/list/submap_job_datums - var/decl/submap_archetype/arch = submap_archetypes[atype] for(var/jobtype in arch.crew_jobs) var/datum/job/job = get_by_path(jobtype) if(job) LAZYADD(submap_job_datums, job) if(LAZYLEN(submap_job_datums)) submap_job_datums = sortTim(submap_job_datums, /proc/cmp_job_desc) - job_lists_by_map_name[arch.descriptor] = list("jobs" = submap_job_datums, "default_to_hidden" = arch.default_to_hidden) + job_lists_by_map_name[arch.name] = list("jobs" = submap_job_datums, "default_to_hidden" = arch.default_to_hidden) // Update global map blacklists and whitelists. for(var/mappath in global.all_maps) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index f03372203ada..e0d9757f4674 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -323,7 +323,7 @@ INITIALIZE_IMMEDIATE(/mob/new_player) ordered_submaps = sortTim(SSmapping.submaps.Copy(), /proc/cmp_submap_asc) for(var/datum/submap/submap as anything in ordered_submaps) if(submap?.available()) - dat += "[submap.name] ([submap.archetype.descriptor]):" + dat += "[submap.name] ([submap.archetype.name]):" job_summaries = list() for(var/otherthing in submap.jobs) var/datum/job/job = submap.jobs[otherthing] diff --git a/code/modules/submaps/_submap.dm b/code/modules/submaps/_submap.dm index 20404728d37b..c2fde5b895d8 100644 --- a/code/modules/submaps/_submap.dm +++ b/code/modules/submaps/_submap.dm @@ -26,7 +26,7 @@ archetype = _archetype if(!pref_name) - pref_name = archetype.descriptor + pref_name = archetype.name testing("Starting submap setup - '[name]', [archetype], [associated_z]z.") @@ -41,7 +41,7 @@ jobs[job.title] = job if(!associated_z) - testing( "Submap error - [name]/[archetype ? archetype.descriptor : "NO ARCHETYPE"] could not find an associated z-level for spawnpoint registration.") + testing( "Submap error - [name]/[archetype ? archetype.name : "NO ARCHETYPE"] could not find an associated z-level for spawnpoint registration.") qdel(src) return @@ -59,7 +59,7 @@ registered_spawnpoint = TRUE if(!registered_spawnpoint) - testing( "Submap error - [name]/[archetype ? archetype.descriptor : "NO ARCHETYPE"] has no job spawn points.") + testing( "Submap error - [name]/[archetype ? archetype.name : "NO ARCHETYPE"] has no job spawn points.") qdel(src) return diff --git a/code/modules/submaps/submap_archetype.dm b/code/modules/submaps/submap_archetype.dm index be118fa8202b..9597b64f1f6f 100644 --- a/code/modules/submaps/submap_archetype.dm +++ b/code/modules/submaps/submap_archetype.dm @@ -1,5 +1,6 @@ /decl/submap_archetype - var/descriptor = "generic ship archetype" + // TODO: use UID instead of name for pref saving. + var/name = "generic ship archetype" var/list/whitelisted_species = list() var/list/blacklisted_species = list() var/call_webhook @@ -13,12 +14,18 @@ /decl/submap_archetype/validate() . = ..() - if(!descriptor) - . += "no descriptor set" + if(name) + var/static/list/submaps_by_name = list( (global.using_map.name) = global.using_map.type) + if(submaps_by_name[name]) + . += "name '[name]' ([type]) collides with submap type '[submaps_by_name[name]]'" + else + submaps_by_name[name] = type + else + . += "no name set" // Generic ships to populate the list. /decl/submap_archetype/derelict - descriptor = "drifting wreck" + name = "drifting wreck" /decl/submap_archetype/abandoned_ship - descriptor = "abandoned ship" + name = "abandoned ship" diff --git a/code/modules/submaps/submap_job.dm b/code/modules/submaps/submap_job.dm index 49e6622328e8..9bc3687a1aed 100644 --- a/code/modules/submaps/submap_job.dm +++ b/code/modules/submaps/submap_job.dm @@ -78,17 +78,17 @@ to_chat(feedback, "Not old enough. Minimum character age is [minimum_character_age[S.get_root_species_name()]].") return TRUE if(LAZYLEN(whitelisted_species) && !(prefs.species in whitelisted_species)) - to_chat(feedback, "Your current species, [prefs.species], is not permitted as [title] on \a [owner.archetype.descriptor].") + to_chat(feedback, "Your current species, [prefs.species], is not permitted as [title] on \a [owner.archetype.name].") return TRUE if(prefs.species in blacklisted_species) - to_chat(feedback, "Your current species, [prefs.species], is not permitted as [title] on \a [owner.archetype.descriptor].") + to_chat(feedback, "Your current species, [prefs.species], is not permitted as [title] on \a [owner.archetype.name].") return TRUE if(owner && owner.archetype) if(LAZYLEN(owner.archetype.whitelisted_species) && !(prefs.species in owner.archetype.whitelisted_species)) - to_chat(feedback, "Your current species, [prefs.species], is not permitted on \a [owner.archetype.descriptor].") + to_chat(feedback, "Your current species, [prefs.species], is not permitted on \a [owner.archetype.name].") return TRUE if(prefs.species in owner.archetype.blacklisted_species) - to_chat(feedback, "Your current species, [prefs.species], is not permitted on \a [owner.archetype.descriptor].") + to_chat(feedback, "Your current species, [prefs.species], is not permitted on \a [owner.archetype.name].") return TRUE return FALSE diff --git a/code/unit_tests/unique_tests.dm b/code/unit_tests/unique_tests.dm index 2d23719ea302..b409acf6edc5 100644 --- a/code/unit_tests/unique_tests.dm +++ b/code/unit_tests/unique_tests.dm @@ -207,26 +207,6 @@ pass("All gas symbols are unique.") return TRUE -/datum/unit_test/submaps_shall_have_a_unique_descriptor - name = "UNIQUENESS: Archetypes shall have a valid, unique descriptor." - -/datum/unit_test/submaps_shall_have_a_unique_descriptor/start_test() - var/list/submaps_by_name = list() - - var/list/all_submaps = decls_repository.get_decls_of_subtype(/decl/submap_archetype) - for(var/submap_type in all_submaps) - var/decl/submap_archetype/submap = all_submaps[submap_type] - if(submap.descriptor) - group_by(submaps_by_name, submap.descriptor, submap_type) - - var/number_of_issues = number_of_issues(submaps_by_name, "Submap Archetype Descriptors") - if(length(number_of_issues)) - fail("Found [number_of_issues] submap archetype\s with duplicate descriptors.") - else - pass("All submap archetypes have unique descriptors.") - return 1 - - /datum/unit_test/proc/number_of_issues(var/list/entries, var/type, var/feedback = /decl/noi_feedback) var/issues = 0 for(var/key in entries) diff --git a/maps/away/bearcat/bearcat.dm b/maps/away/bearcat/bearcat.dm index e9f35248199f..37d26d36b557 100644 --- a/maps/away/bearcat/bearcat.dm +++ b/maps/away/bearcat/bearcat.dm @@ -3,11 +3,11 @@ #include "bearcat_access.dm" /obj/abstract/submap_landmark/joinable_submap/bearcat - name = "FTV Bearcat" + name = "FTV Bearcat" archetype = /decl/submap_archetype/derelict/bearcat /decl/submap_archetype/derelict/bearcat - descriptor = "derelict cargo vessel" + name = "derelict cargo vessel" crew_jobs = list( /datum/job/submap/bearcat_captain, /datum/job/submap/bearcat_crewman diff --git a/maps/away/liberia/liberia_jobs.dm b/maps/away/liberia/liberia_jobs.dm index 72d4d2ddbb2c..4d3b627143f2 100644 --- a/maps/away/liberia/liberia_jobs.dm +++ b/maps/away/liberia/liberia_jobs.dm @@ -1,6 +1,6 @@ // Submap datum and archetype. /decl/submap_archetype/liberia - descriptor = "merchant ship" + name = "merchant ship" crew_jobs = list( /datum/job/submap/merchant ) diff --git a/maps/away/unishi/unishi.dm b/maps/away/unishi/unishi.dm index 217009898388..a1670a3978c6 100644 --- a/maps/away/unishi/unishi.dm +++ b/maps/away/unishi/unishi.dm @@ -3,11 +3,11 @@ #include "../../../mods/content/xenobiology/_xenobiology.dme" /obj/abstract/submap_landmark/joinable_submap/unishi - name = "SRV Verne" + name = "SRV Verne" archetype = /decl/submap_archetype/derelict/unishi /decl/submap_archetype/derelict/unishi - descriptor = "derelict research vessel" + name = "derelict research vessel" crew_jobs = list( /datum/job/submap/unishi_crew, /datum/job/submap/unishi_researcher diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dm index 62b11f8ae724..7eb021cf0cbf 100644 --- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dm +++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dm @@ -1,17 +1,17 @@ /datum/map_template/ruin/exoplanet/crashed_pod - name = "crashed survival pod" - description = "A crashed survival pod from a destroyed ship." - suffixes = list("crashed_pod/crashed_pod.dmm") - cost = 2 + name = "crashed survival pod" + description = "A crashed survival pod from a destroyed ship." + suffixes = list("crashed_pod/crashed_pod.dmm") + cost = 2 template_flags = TEMPLATE_FLAG_CLEAR_CONTENTS | TEMPLATE_FLAG_NO_RUINS | TEMPLATE_FLAG_NO_RADS - template_tags = TEMPLATE_TAG_HUMAN|TEMPLATE_TAG_WRECK + template_tags = TEMPLATE_TAG_HUMAN|TEMPLATE_TAG_WRECK /area/map_template/crashed_pod - name = "\improper Crashed Survival Pod" + name = "\improper Crashed Survival Pod" icon_state = "blue" /decl/submap_archetype/crashed_pod - descriptor = "crashed survival pod" + name = "crashed survival pod" crew_jobs = list(/datum/job/submap/pod) /datum/submap/crashed_pod/sync_cell(var/obj/effect/overmap/visitable/cell) diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm b/maps/random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm index e27736fd2515..d0b72f2445f6 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm @@ -1,18 +1,18 @@ #include "../../../../mods/mobs/dionaea/_dionaea.dme" /datum/map_template/ruin/exoplanet/playablecolony - name = "established colony" - description = "a fully functional colony on the frontier of settled space" - suffixes = list("playablecolony/colony.dmm") - cost = 2 + name = "established colony" + description = "a fully functional colony on the frontier of settled space" + suffixes = list("playablecolony/colony.dmm") + cost = 2 template_flags = TEMPLATE_FLAG_CLEAR_CONTENTS | TEMPLATE_FLAG_NO_RUINS | TEMPLATE_FLAG_NO_RADS - template_tags = TEMPLATE_TAG_HUMAN|TEMPLATE_TAG_HABITAT + template_tags = TEMPLATE_TAG_HUMAN|TEMPLATE_TAG_HABITAT apc_test_exempt_areas = list( /area/map_template/colony/mineralprocessing = NO_SCRUBBER|NO_VENT ) /decl/submap_archetype/playablecolony - descriptor = "established colony" + name = "established colony" crew_jobs = list(/datum/job/submap/colonist) /datum/job/submap/colonist diff --git a/maps/shaded_hills/shaded_hills_define.dm b/maps/shaded_hills/shaded_hills_define.dm index bfb694d4ee6b..47684f0fc407 100644 --- a/maps/shaded_hills/shaded_hills_define.dm +++ b/maps/shaded_hills/shaded_hills_define.dm @@ -47,3 +47,6 @@ /datum/map/shaded_hills/get_map_info() return "You're in the [station_name] of the [system_name], nestled between the mountains and the river and bisected by the decaying Queens' Road. On all sides, you are surrounded by untamed wilds, with only a small ruined fort, rebuilt into an inn, to the east as a sign of civilisation. \ Far from the control of [boss_name], you are free to carve forward a path to survival for yourself and your comrades however you wish. Strike the earth!" + +/datum/map/shaded_hills/get_available_submap_archetypes() + return null // Return list of decl instances when relevant submaps exist. diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index fa0aaa499777..915cccfbaadd 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -527,3 +527,6 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable /datum/map/proc/finalize_map_generation() return + +/datum/map/proc/get_available_submap_archetypes() + return decls_repository.get_decls_of_subtype_unassociated(/decl/submap_archetype) From 6c5009eaf9e1ad53b3f2b72a7dae267c273ba500 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 16 Dec 2024 10:16:36 +1100 Subject: [PATCH 0087/1331] Readding eye_offset. --- code/modules/bodytype/_bodytype.dm | 2 ++ code/modules/organs/internal/eyes.dm | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/code/modules/bodytype/_bodytype.dm b/code/modules/bodytype/_bodytype.dm index ec0d6033d5f4..64d2e2f21b08 100644 --- a/code/modules/bodytype/_bodytype.dm +++ b/code/modules/bodytype/_bodytype.dm @@ -81,6 +81,8 @@ var/global/list/bodytypes_by_category = list() var/antaghud_offset_x = 0 /// Used to offset the antagHUD indicator for wide or tall mobs. var/antaghud_offset_y = 0 + /// Amount to shift eyes on the Y axis to correct for non-32px height. Used downstream, do not remove. + var/eye_offset = 0 /// Used to apply flags like WIDE_LOAD to nonstandard mobs. var/z_flags = 0 /// Amount to shift overlays when lying. TODO: check if this is still needed with KEEP_TOGETHER diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index 9d887adb99aa..f193afaa4daa 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -40,11 +40,13 @@ var/modifier = owner?.get_overlay_state_modifier() var/eye_state = modifier ? "eyes[modifier]" : "eyes" last_cached_eye_colour = eye_colour - last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[eye_state]" + last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[bodytype.eye_offset]-[eye_state]" if(!bodytype.eye_icon) return if(!global.eye_icon_cache[last_eye_cache_key]) var/icon/eyes_icon = icon(icon = bodytype.eye_icon, icon_state = eye_state) + if(bodytype.eye_offset) + eyes_icon.Shift(NORTH, bodytype.eye_offset) if(bodytype.apply_eye_colour) eyes_icon.Blend(last_cached_eye_colour, bodytype.eye_blend) global.eye_icon_cache[last_eye_cache_key] = eyes_icon From 694628ff2edfc3cbf9eea58cfb7d1a3263e2606c Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 15 Dec 2024 15:28:20 +1100 Subject: [PATCH 0088/1331] Moved various fire procs to /atom level as stubs, consistent name/setter/getter use. --- code/_helpers/washing.dm | 6 +- code/game/atoms_fires.dm | 21 ++++ code/game/machinery/igniter.dm | 18 ++-- code/game/objects/items/flame/_flame.dm | 32 +++---- .../items/flame/flame_fuelled_lighter.dm | 2 +- .../game/objects/items/flame/flame_matches.dm | 2 +- code/game/objects/items/flame/flame_torch.dm | 2 +- .../objects/items/weapons/flamethrower.dm | 2 +- .../objects/items/weapons/material/ashtray.dm | 2 +- .../game/objects/items/weapons/tanks/tanks.dm | 4 +- .../items/weapons/tools/weldingtool.dm | 4 +- code/game/objects/structures/wall_sconce.dm | 4 + code/modules/ZAS/Fire.dm | 2 +- code/modules/clothing/head/misc_special.dm | 12 +-- code/modules/clothing/masks/chewable.dm | 4 +- code/modules/clothing/masks/smokable.dm | 18 ++-- code/modules/detectivework/tools/rag.dm | 48 +++++----- code/modules/materials/_materials.dm | 2 +- .../liquids/materials_liquid_water.dm | 8 +- code/modules/mob/living/human/examine.dm | 7 +- .../mob/living/human/human_attackhand.dm | 2 +- code/modules/mob/living/human/life.dm | 4 +- code/modules/mob/living/living.dm | 36 +++---- code/modules/mob/living/living_attackhand.dm | 22 ++--- code/modules/mob/living/living_defense.dm | 91 +----------------- code/modules/mob/living/living_defines.dm | 4 +- code/modules/mob/living/living_fires.dm | 96 +++++++++++++++++++ code/modules/mob/living/silicon/robot/life.dm | 6 +- .../hostile/retaliate/exoplanet.dm | 4 +- .../organs/external/_external_damage.dm | 2 +- code/modules/projectiles/projectile/beams.dm | 6 +- .../modules/projectiles/projectile/special.dm | 4 +- .../reagent_containers/drinks/bottle.dm | 16 ++-- .../modules/spells/targeted/ethereal_jaunt.dm | 2 +- mods/gamemodes/cult/runes.dm | 7 +- mods/species/serpentid/datum/species.dm | 4 +- nebula.dme | 2 + 37 files changed, 268 insertions(+), 240 deletions(-) create mode 100644 code/game/atoms_fires.dm create mode 100644 code/modules/mob/living/living_fires.dm diff --git a/code/_helpers/washing.dm b/code/_helpers/washing.dm index 487afbd8700e..0bb0169a3d1a 100644 --- a/code/_helpers/washing.dm +++ b/code/_helpers/washing.dm @@ -2,8 +2,8 @@ if(!istype(washing)) return var/mob/living/L = washing - if(L.on_fire) + if(L.is_on_fire()) L.visible_message("A cloud of steam rises up as the water hits \the [L]!") - L.ExtinguishMob() - L.fire_stacks = -20 //Douse ourselves with water to avoid fire more easily + L.extinguish_fire() + L.adjust_fire_intensity(-20) //Douse ourselves with water to avoid fire more easily washing.clean() diff --git a/code/game/atoms_fires.dm b/code/game/atoms_fires.dm new file mode 100644 index 000000000000..c70cc007cb87 --- /dev/null +++ b/code/game/atoms_fires.dm @@ -0,0 +1,21 @@ +// Stubs for atom fire system, TODO. +/atom/proc/set_fire_intensity(amount) + return + +/atom/proc/get_fire_intensity() + return 0 + +/atom/proc/adjust_fire_intensity(amount) + return + +/atom/proc/can_ignite() + return FALSE + +/atom/proc/ignite_fire() + return + +/atom/proc/extinguish_fire(mob/user, no_message = FALSE) + return + +/atom/proc/is_on_fire() + return FALSE diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index f086a33332b3..47f7eab52117 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -40,7 +40,7 @@ /obj/machinery/igniter/interface_interact(mob/user) if(!CanInteract(user, DefaultTopicState())) return FALSE - ignite() + toggle_igniter() visible_message(SPAN_NOTICE("\The [user] toggles \the [src].")) return TRUE @@ -51,7 +51,7 @@ location.hotspot_expose(1000,500,1) return 1 -/obj/machinery/igniter/proc/ignite() +/obj/machinery/igniter/proc/toggle_igniter() use_power_oneoff(2000) on = !on if(on) @@ -73,7 +73,7 @@ /decl/public_access/public_method/igniter_toggle name = "igniter toggle" desc = "Toggle the igniter on or off." - call_proc = TYPE_PROC_REF(/obj/machinery/igniter, ignite) + call_proc = TYPE_PROC_REF(/obj/machinery/igniter, toggle_igniter) /decl/stock_part_preset/radio/receiver/igniter frequency = BUTTON_FREQ @@ -133,19 +133,15 @@ return ..() /obj/machinery/sparker/attack_ai() - if (anchored) - return ignite() - else - return + return anchored ? create_sparks() : null -/obj/machinery/sparker/proc/ignite() +/obj/machinery/sparker/proc/create_sparks() if (stat & NOPOWER) return if (disable || (last_spark && world.time < last_spark + 50)) return - flick("[base_state]-spark", src) spark_at(src, amount=2, cardinal_only = TRUE) src.last_spark = world.time @@ -159,13 +155,13 @@ if(stat & (BROKEN|NOPOWER)) ..(severity) return - ignite() + create_sparks() ..(severity) /decl/public_access/public_method/sparker_spark name = "spark" desc = "Creates sparks to ignite nearby gases." - call_proc = TYPE_PROC_REF(/obj/machinery/sparker, ignite) + call_proc = TYPE_PROC_REF(/obj/machinery/sparker, create_sparks) /decl/stock_part_preset/radio/receiver/sparker frequency = BUTTON_FREQ diff --git a/code/game/objects/items/flame/_flame.dm b/code/game/objects/items/flame/_flame.dm index 1078285da3b8..bee61f2c54c6 100644 --- a/code/game/objects/items/flame/_flame.dm +++ b/code/game/objects/items/flame/_flame.dm @@ -56,7 +56,7 @@ loc.update_icon() /obj/item/flame/Destroy() - extinguish(null, TRUE) + snuff_out(null, TRUE) return ..() /obj/item/flame/proc/get_available_scents() @@ -115,7 +115,7 @@ return ..() -/obj/item/flame/proc/extinguish(var/mob/user, var/no_message) +/obj/item/flame/proc/snuff_out(mob/user, no_message = FALSE) if(!lit) return FALSE lit = FALSE @@ -147,7 +147,7 @@ return TRUE if(lit && can_manually_extinguish) - extinguish(user) + snuff_out(user) return TRUE return ..() @@ -175,7 +175,7 @@ else check_depth = FLUID_SHALLOW if(fluids.total_volume >= check_depth) - extinguish(no_message = TRUE) + snuff_out(no_message = TRUE) /obj/item/flame/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) . = ..() @@ -206,31 +206,21 @@ other.light(user) /obj/item/flame/attackby(obj/item/used_item, mob/user) - if(!user.check_intent(I_FLAG_HARM) && !can_manually_light && (used_item.isflamesource() || used_item.get_heat() > T100C)) light(user) return TRUE - return ..() /obj/item/flame/Process() - if((!waterproof && submerged()) || !expend_fuel(_fuel_spend_amt)) - extinguish() + snuff_out() return PROCESS_KILL - update_icon() - if(istype(loc, /obj/structure/wall_sconce)) - loc.update_icon() - - // TODO: generalized ignition proc - if(isliving(loc)) - var/mob/living/M = loc - M.IgniteMob() - - var/turf/location = get_turf(src) - if(location) - location.hotspot_expose(get_heat(), w_class) + if(loc) + loc.ignite_fire() + var/turf/my_turf = get_turf(src) + if(my_turf) + my_turf.hotspot_expose(get_heat(), w_class) /obj/item/flame/dropped(var/mob/user) //If dropped, put ourselves out @@ -239,7 +229,7 @@ var/turf/location = loc if(istype(location)) location.hotspot_expose(700, 5) - extinguish() + snuff_out() return ..() /obj/item/flame/spark_act(obj/effect/sparks/sparks) diff --git a/code/game/objects/items/flame/flame_fuelled_lighter.dm b/code/game/objects/items/flame/flame_fuelled_lighter.dm index 7719c082a6fe..2e1e6903bc2a 100644 --- a/code/game/objects/items/flame/flame_fuelled_lighter.dm +++ b/code/game/objects/items/flame/flame_fuelled_lighter.dm @@ -33,7 +33,7 @@ user.visible_message(SPAN_NOTICE("After a few attempts, \the [user] manages to light \the [src], burning their finger in the process.")) playsound(src.loc, "light_bic", 100, 1, -4) -/obj/item/flame/fuelled/lighter/extinguish(var/mob/user, var/no_message) +/obj/item/flame/fuelled/lighter/snuff_out(mob/user, no_message = FALSE) if(!no_message && user) no_message = TRUE . = ..() diff --git a/code/game/objects/items/flame/flame_matches.dm b/code/game/objects/items/flame/flame_matches.dm index ebaead8790bb..41300c9d1e0b 100644 --- a/code/game/objects/items/flame/flame_matches.dm +++ b/code/game/objects/items/flame/flame_matches.dm @@ -23,7 +23,7 @@ /obj/item/flame/match/light(mob/user, no_message) . = !burnt && ..() -/obj/item/flame/match/extinguish(var/mob/user, var/no_message) +/obj/item/flame/match/snuff_out(mob/user, no_message = FALSE) . = ..() if(. && !burnt) _fuel = 0 diff --git a/code/game/objects/items/flame/flame_torch.dm b/code/game/objects/items/flame/flame_torch.dm index 66aac1847d65..26f616a4a47c 100644 --- a/code/game/objects/items/flame/flame_torch.dm +++ b/code/game/objects/items/flame/flame_torch.dm @@ -50,7 +50,7 @@ update_icon() -/obj/item/flame/torch/extinguish(var/mob/user, var/no_message) +/obj/item/flame/torch/snuff_out(mob/user, no_message = FALSE) . = ..() if(. && _fuel <= 0 && !burnt) burnt = TRUE diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 61ddcccfbaed..cc1018b16393 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -296,7 +296,7 @@ target.create_fire(tank.air_contents.temperature * 2 + 400) target.hotspot_expose(1000, 100) for(var/mob/living/M in target) - M.IgniteMob(1) + M.ignite_fire() // slightly weird looking initialize cuz it has to do some stuff first /obj/item/flamethrower/full/Initialize() diff --git a/code/game/objects/items/weapons/material/ashtray.dm b/code/game/objects/items/weapons/material/ashtray.dm index fe6ac6673c97..41b4cae00d92 100644 --- a/code/game/objects/items/weapons/material/ashtray.dm +++ b/code/game/objects/items/weapons/material/ashtray.dm @@ -34,7 +34,7 @@ var/obj/item/clothing/mask/smokable/cigarette/cig = W if (cig.lit == 1) visible_message(SPAN_NOTICE("\The [user] crushes \the [cig] in \the [src], putting it out.")) - W = cig.extinguish(no_message = 1) + W = cig.extinguish_fire(no_message = TRUE) else if (cig.lit == 0) to_chat(user, SPAN_NOTICE("You place \the [cig] in \the [src] without even smoking it. Why would you do that?")) else diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 35c99e1d7dce..68f46f69269a 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -553,7 +553,7 @@ var/global/list/global/tank_gauge_cache = list() return ..() /obj/item/tankassemblyproxy/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. - tank.ignite() //boom (or not boom if you made shijwtty mix) + tank.cause_explosion() //boom (or not boom if you made shijwtty mix) /obj/item/tank/proc/assemble_bomb(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb var/obj/item/assembly_holder/S = W @@ -573,7 +573,7 @@ var/global/list/global/tank_gauge_cache = list() update_icon(TRUE) -/obj/item/tank/proc/ignite() //This happens when a bomb is told to explode +/obj/item/tank/proc/cause_explosion() //This happens when a bomb is told to explode var/obj/item/assembly_holder/assy = proxyassembly.assembly var/ign = assy.a_right var/obj/item/other = assy.a_left diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index 4a95497c9aab..3b9c293256e1 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -192,7 +192,7 @@ var/turf/location = get_turf(user) if(isliving(O)) var/mob/living/L = O - L.IgniteMob() + L.ignite_fire() else if(isatom(O)) O.handle_external_heating(WELDING_TOOL_HOTSPOT_TEMP_ACTIVE, src, user) if (isturf(location)) @@ -243,7 +243,7 @@ var/mob/living/L = loc if(!(src in L.get_held_items())) fuel_usage = max(fuel_usage, 2) - L.IgniteMob() + L.ignite_fire() else if(isturf(loc)) var/turf/location = get_turf(src) location.hotspot_expose(WELDING_TOOL_HOTSPOT_TEMP_IDLE, 5) //a bit colder when idling diff --git a/code/game/objects/structures/wall_sconce.dm b/code/game/objects/structures/wall_sconce.dm index 5a3dd0a38a2f..c1211712b3c8 100644 --- a/code/game/objects/structures/wall_sconce.dm +++ b/code/game/objects/structures/wall_sconce.dm @@ -58,6 +58,10 @@ QDEL_NULL(light_source) return ..() +/obj/structure/wall_sconce/ignite_fire() + . = ..() + update_icon() + /obj/structure/wall_sconce/physically_destroyed() if(light_source) light_source.dropInto(loc) diff --git a/code/modules/ZAS/Fire.dm b/code/modules/ZAS/Fire.dm index c961aed56c7b..10ecd938f694 100644 --- a/code/modules/ZAS/Fire.dm +++ b/code/modules/ZAS/Fire.dm @@ -17,7 +17,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin return simulated /turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0) - if(locate(/obj/fire) in src) + if((locate(/obj/fire) in src) || !simulated) return 1 var/datum/gas_mixture/air_contents = return_air() diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 19e0a6c4aca2..01d01335e585 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -204,7 +204,7 @@ icon = 'icons/clothing/head/cakehat.dmi' body_parts_covered = SLOT_HEAD item_flags = null - var/is_on_fire = FALSE + VAR_PRIVATE/_on_fire = FALSE /obj/item/clothing/head/cakehat/equipped(mob/user, slot) . = ..() @@ -217,7 +217,7 @@ /obj/item/clothing/head/cakehat/on_update_icon(mob/user) . = ..() z_flags &= ~ZMM_MANGLE_PLANES - if(is_on_fire && check_state_in_icon("[icon_state]-flame", icon)) + if(is_on_fire() && check_state_in_icon("[icon_state]-flame", icon)) if(plane == HUD_PLANE) add_overlay("[icon_state]-flame") else @@ -230,7 +230,7 @@ return emissive_overlay(overlay.icon, "[overlay.icon_state]-flame") /obj/item/clothing/head/cakehat/apply_additional_mob_overlays(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) - if(overlay && is_on_fire) + if(overlay && is_on_fire()) var/image/I = get_mob_flame_overlay(overlay, bodytype) if(I) overlay.overlays += I @@ -241,7 +241,7 @@ return ..() /obj/item/clothing/head/cakehat/Process() - if(!is_on_fire) + if(!is_on_fire()) STOP_PROCESSING(SSobj, src) return var/turf/location = loc @@ -255,9 +255,9 @@ /obj/item/clothing/head/cakehat/attack_self(mob/user) . = ..() if(!.) - is_on_fire = !is_on_fire + _on_fire = !_on_fire update_icon() - if(is_on_fire) + if(is_on_fire()) atom_damage_type = BURN START_PROCESSING(SSobj, src) else diff --git a/code/modules/clothing/masks/chewable.dm b/code/modules/clothing/masks/chewable.dm index 594eb26b6aa1..84d490148fb9 100644 --- a/code/modules/clothing/masks/chewable.dm +++ b/code/modules/clothing/masks/chewable.dm @@ -51,7 +51,7 @@ /obj/item/clothing/mask/chewable/Process() chew(1) if(chewtime < 1) - extinguish() + extinguish_fire() /obj/item/clothing/mask/chewable/tobacco name = "wad" @@ -72,7 +72,7 @@ desc = "A disgusting spitwad." icon = 'icons/clothing/mask/chewables/chew_spit.dmi' -/obj/item/clothing/mask/chewable/proc/extinguish(var/mob/user, var/no_message) +/obj/item/clothing/mask/chewable/extinguish_fire(mob/user, no_message = FALSE) STOP_PROCESSING(SSobj, src) if(type_butt) var/obj/item/trash/cigbutt/butt = new type_butt(get_turf(src)) diff --git a/code/modules/clothing/masks/smokable.dm b/code/modules/clothing/masks/smokable.dm index e36a224c7873..9d5c9cf1a9f6 100644 --- a/code/modules/clothing/masks/smokable.dm +++ b/code/modules/clothing/masks/smokable.dm @@ -82,7 +82,7 @@ if (src == user.get_equipped_item(slot_wear_mask_str) && user.internal) environment = user.internal.return_air() if(environment.get_by_flag(XGM_GAS_OXIDIZER) < gas_consumption) - extinguish() + extinguish_fire() else environment.remove_by_flag(XGM_GAS_OXIDIZER, gas_consumption) environment.adjust_gas(/decl/material/gas/carbon_dioxide, 0.5*gas_consumption,0) @@ -91,7 +91,7 @@ /obj/item/clothing/mask/smokable/Process() var/turf/location = get_turf(src) if(submerged() || smoketime < 1) - extinguish() + extinguish_fire() return smoke(1) if(location) @@ -126,7 +126,7 @@ var/turf/location = get_turf(src) if(location) location.hotspot_expose(700, 5) - extinguish(no_message = TRUE) + extinguish_fire(no_message = TRUE) /obj/item/clothing/mask/smokable/proc/light(var/flavor_text = "[usr] lights \the [src].") if(QDELETED(src)) @@ -152,7 +152,7 @@ smoke_amount = reagents.total_volume / smoketime START_PROCESSING(SSobj, src) -/obj/item/clothing/mask/smokable/proc/extinguish(var/mob/user, var/no_message) +/obj/item/clothing/mask/smokable/extinguish_fire(mob/user, no_message = FALSE) lit = FALSE atom_damage_type = BRUTE STOP_PROCESSING(SSobj, src) @@ -182,7 +182,7 @@ return ..() /obj/item/clothing/mask/smokable/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - if(target.on_fire) + if(target.is_on_fire()) user.do_attack_animation(target) light(SPAN_NOTICE("\The [user] coldly lights the \the [src] with the burning body of \the [target].")) return TRUE @@ -221,7 +221,7 @@ if(is_processing) set_scent_by_reagents(src) -/obj/item/clothing/mask/smokable/extinguish(var/mob/user, var/no_message) +/obj/item/clothing/mask/smokable/extinguish_fire(mob/user, no_message = FALSE) ..() remove_extension(src, /datum/extension/scent) if (type_butt) @@ -384,7 +384,7 @@ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) return TRUE - if(!lit && target.on_fire) + if(!lit && target.is_on_fire()) user.do_attack_animation(target) light(target, user) return TRUE @@ -412,7 +412,7 @@ /obj/item/clothing/mask/smokable/cigarette/attack_self(var/mob/user) if(lit == 1) user.visible_message(SPAN_NOTICE("[user] calmly drops and treads on the lit [src], putting it out instantly.")) - extinguish(no_message = 1) + extinguish_fire(no_message = TRUE) return ..() //////////// @@ -530,7 +530,7 @@ set_scent_by_reagents(src) update_icon() -/obj/item/clothing/mask/smokable/pipe/extinguish(var/mob/user, var/no_message) +/obj/item/clothing/mask/smokable/pipe/extinguish_fire(mob/user, no_message) ..() new /obj/effect/decal/cleanable/ash(get_turf(src)) if(ismob(loc)) diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 5487388845dc..3ed5186e6b89 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -13,8 +13,8 @@ material = /decl/material/solid/organic/cloth material_alteration = MAT_FLAG_ALTERATION_NAME - var/on_fire = 0 - var/burn_time = 20 //if the rag burns for too long it turns to ashes + VAR_PRIVATE/_on_fire = 0 + VAR_PRIVATE/burn_time = 20 //if the rag burns for too long it turns to ashes /obj/item/chems/glass/rag/Initialize() . = ..() @@ -28,20 +28,23 @@ STOP_PROCESSING(SSobj, src) //so we don't continue turning to ash while gc'd . = ..() +/obj/item/chems/glass/rag/is_on_fire() + return _on_fire + /obj/item/chems/glass/rag/attack_self(mob/user) - if(on_fire && user.try_unequip(src)) + if(is_on_fire() && user.try_unequip(src)) user.visible_message(SPAN_NOTICE("\The [user] stamps out [src]."), SPAN_NOTICE("You stamp out [src].")) - extinguish() + extinguish_fire() else remove_contents(user) /obj/item/chems/glass/rag/attackby(obj/item/W, mob/user) if(W.isflamesource()) - if(on_fire) + if(is_on_fire()) to_chat(user, SPAN_WARNING("\The [src] is already blazing merrily!")) return TRUE - ignite() - if(on_fire) + ignite_fire() + if(is_on_fire()) visible_message(SPAN_DANGER("\The [user] lights \the [src] with \the [W].")) else to_chat(user, SPAN_WARNING("You attempt to light \the [src] with \the [W], but it doesn't seem to be flammable.")) @@ -50,7 +53,7 @@ return ..() /obj/item/chems/glass/rag/update_name() - if(on_fire) + if(is_on_fire()) name_prefix = "burning" else if(reagents && reagents.total_volume) name_prefix = "damp" @@ -60,7 +63,7 @@ /obj/item/chems/glass/rag/on_update_icon() . = ..() - icon_state = "rag[on_fire? "lit" : ""]" + icon_state = "rag[is_on_fire()? "lit" : ""]" var/obj/item/chems/drinks/bottle/B = loc if(istype(B)) B.update_icon() @@ -93,14 +96,14 @@ /obj/item/chems/glass/rag/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - if(on_fire) + if(is_on_fire()) user.visible_message( SPAN_DANGER("\The [user] hits \the [target] with \the [src]!"), SPAN_DANGER("You hit \the [target] with \the [src]!") ) user.do_attack_animation(target) admin_attack_log(user, target, "used \the [src] (ignited) to attack", "was attacked using \the [src] (ignited)", "attacked with \the [src] (ignited)") - target.IgniteMob() + target.ignite_fire() return TRUE if(reagents.total_volume) @@ -150,7 +153,7 @@ update_name() return - if(!on_fire && istype(A) && (src in user)) + if(!is_on_fire() && istype(A) && (src in user)) if(ATOM_IS_OPEN_CONTAINER(A) && !(A in user)) remove_contents(user, A) else if(!ismob(A)) //mobs are handled in use_on_mob() - this prevents us from wiping down people while smothering them. @@ -159,7 +162,7 @@ /obj/item/chems/glass/rag/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(exposed_temperature >= 50 + T0C) - ignite() + ignite_fire() if(exposed_temperature >= 900 + T0C) new /obj/effect/decal/cleanable/ash(get_turf(src)) qdel(src) @@ -168,7 +171,7 @@ //rag must have a minimum of 2 units welder fuel and at least 80% of the reagents must be welder fuel. //maybe generalize flammable reagents someday -/obj/item/chems/glass/rag/proc/can_ignite() +/obj/item/chems/glass/rag/can_ignite() var/total_fuel = 0 var/total_volume = 0 if(reagents) @@ -178,21 +181,21 @@ total_fuel += REAGENT_VOLUME(reagents, rtype) * R.accelerant_value . = (total_fuel >= 2 && total_fuel >= total_volume*0.5) -/obj/item/chems/glass/rag/proc/ignite() - if(on_fire) +/obj/item/chems/glass/rag/ignite_fire() + if(is_on_fire()) return if(!can_ignite()) return START_PROCESSING(SSobj, src) set_light(2, 1, "#e38f46") - on_fire = 1 + _on_fire = TRUE update_name() update_icon() -/obj/item/chems/glass/rag/proc/extinguish() +/obj/item/chems/glass/rag/extinguish_fire(mob/user, no_message = FALSE) STOP_PROCESSING(SSobj, src) set_light(0) - on_fire = 0 + _on_fire = FALSE //rags sitting around with 1 second of burn time left is dumb. //ensures players always have a few seconds of burn time left when they light their rag @@ -206,12 +209,11 @@ /obj/item/chems/glass/rag/Process() if(!can_ignite()) visible_message("\The [src] burns out.") - extinguish() + extinguish_fire() //copied from matches - if(isliving(loc)) - var/mob/living/M = loc - M.IgniteMob() + if(loc) + loc.ignite_fire() var/turf/location = get_turf(src) if(location) location.hotspot_expose(700, 5) diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 4b12ce62dee1..8a6a4d3b1ae1 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -746,7 +746,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) // This doesn't apply to skin contact - this is for, e.g. extinguishers and sprays. The difference is that reagent is not directly on the mob's skin - it might just be on their clothing. /decl/material/proc/touch_mob(var/mob/living/M, var/amount, var/datum/reagents/holder) if(accelerant_value != FUEL_VALUE_NONE && amount && istype(M)) - M.fire_stacks += floor((amount * accelerant_value)/FLAMMABLE_LIQUID_DIVISOR) + M.adjust_fire_intensity(floor((amount * accelerant_value)/FLAMMABLE_LIQUID_DIVISOR)) #undef FLAMMABLE_LIQUID_DIVISOR /decl/material/proc/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) // Cleaner cleaning, lube lubbing, etc, all go here diff --git a/code/modules/materials/definitions/liquids/materials_liquid_water.dm b/code/modules/materials/definitions/liquids/materials_liquid_water.dm index 76caa77b46fe..e9ca35c6c780 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_water.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_water.dm @@ -87,11 +87,11 @@ /decl/material/liquid/water/touch_mob(var/mob/living/M, var/amount, var/datum/reagents/holder) ..() if(istype(M)) - var/needed = M.fire_stacks * 10 + var/needed = M.get_fire_intensity() * 10 if(amount > needed) - M.fire_stacks = 0 - M.ExtinguishMob() + M.set_fire_intensity(0) + M.extinguish_fire() holder.remove_reagent(type, needed) else - M.adjust_fire_stacks(-(amount / 10)) + M.adjust_fire_intensity(-(amount / 10)) holder.remove_reagent(type, amount) diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index 389e209440cf..ddcaa27e09f9 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -83,12 +83,13 @@ else msg += "[use_He] [use_is] looking a bit damp.\n" - if(fire_stacks > 0) + var/fire_level = get_fire_intensity() + if(fire_level > 0) msg += "[use_He] [use_is] looking highly flammable!\n" - else if(fire_stacks < 0) + else if(fire_level < 0) msg += "[use_He] [use_is] looking rather damp.\n" - if(on_fire) + if(is_on_fire()) msg += "[use_He] [use_is] on fire!.\n" var/ssd_msg = species.get_ssd(src) diff --git a/code/modules/mob/living/human/human_attackhand.dm b/code/modules/mob/living/human/human_attackhand.dm index e88990677b46..83b036ada13d 100644 --- a/code/modules/mob/living/human/human_attackhand.dm +++ b/code/modules/mob/living/human/human_attackhand.dm @@ -99,7 +99,7 @@ if(user == src) check_self_injuries() return TRUE - if(ishuman(user) && (is_asystole() || (status_flags & FAKEDEATH) || failed_last_breath) && !on_fire && !(user.get_target_zone() == BP_R_ARM || user.get_target_zone() == BP_L_ARM)) + if(ishuman(user) && (is_asystole() || (status_flags & FAKEDEATH) || failed_last_breath) && !is_on_fire() && !(user.get_target_zone() == BP_R_ARM || user.get_target_zone() == BP_L_ARM)) if (performing_cpr) performing_cpr = FALSE else diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm index b6a065fc2f73..1bb84a587db9 100644 --- a/code/modules/mob/living/human/life.dm +++ b/code/modules/mob/living/human/life.dm @@ -258,7 +258,7 @@ return /mob/living/human/get_bodytemperature_difference() - if (on_fire) + if (is_on_fire()) return 0 //too busy for pesky metabolic regulation return ..() @@ -490,7 +490,7 @@ // Apply a fire overlay if we're burning. var/crit_markers = get_ui_icon(client?.prefs?.UI_style, UI_ICON_CRIT_MARKER) - if(on_fire) + if(is_on_fire()) health_images += image(crit_markers, "burning") // Show a general pain/crit indicator if needed. diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7bab0e45da2d..df4f9b1c97d3 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -312,8 +312,8 @@ default behaviour is: BITSET(hud_updateflag, HEALTH_HUD) BITSET(hud_updateflag, STATUS_HUD) BITSET(hud_updateflag, LIFE_HUD) - ExtinguishMob() - fire_stacks = 0 + extinguish_fire() + set_fire_intensity(0) var/obj/item/cuffs = get_equipped_item(slot_handcuffed_str) if (cuffs) try_unequip(cuffs, get_turf(src)) @@ -551,8 +551,8 @@ default behaviour is: spawn() escape_buckle() return TRUE //drop && roll - else if(on_fire) - fire_stacks = max(0, fire_stacks-1.2) + else if(is_on_fire()) + set_fire_intensity(max(0, get_fire_intensity()-1.2)) SET_STATUS_MAX(src, STAT_WEAK, 3) spin(32,2) var/decl/pronouns/pronouns = get_pronouns() @@ -561,12 +561,12 @@ default behaviour is: SPAN_NOTICE("You stop, drop, and roll!") ) sleep(3 SECONDS) - if(fire_stacks <= 0) + if(get_fire_intensity() <= 0) visible_message( SPAN_NOTICE("\The [src] successfully extinguishes [pronouns.him][pronouns.self]!"), SPAN_NOTICE("You extinguish yourself.") ) - ExtinguishMob() + extinguish_fire() return TRUE //Breaking out of a structure? @@ -1079,17 +1079,19 @@ default behaviour is: ADJ_STATUS(src, STAT_STUN, -3) ADJ_STATUS(src, STAT_WEAK, -3) - if(fire_stacks >= target.fire_stacks + 3) - target.fire_stacks += 1 - fire_stacks -= 1 - else if(target.fire_stacks >= fire_stacks + 3) - fire_stacks += 1 - target.fire_stacks -= 1 - - if(on_fire && !target.on_fire) - target.IgniteMob() - else if(!on_fire && target.on_fire) - IgniteMob() + var/fire_level = get_fire_intensity() + var/target_fire_level = target.get_fire_intensity() + if(fire_level >= target_fire_level + 3) + target.adjust_fire_intensity(1) + adjust_fire_intensity(-1) + else if(target_fire_level >= fire_level + 3) + adjust_fire_intensity(1) + target.adjust_fire_intensity(-1) + + if(is_on_fire() && !target.is_on_fire()) + target.ignite_fire() + else if(!is_on_fire() && target.is_on_fire()) + ignite_fire() /mob/living/proc/jump_layer_shift() jumping = TRUE diff --git a/code/modules/mob/living/living_attackhand.dm b/code/modules/mob/living/living_attackhand.dm index e7a7ae239f9a..055b82d84bfc 100644 --- a/code/modules/mob/living/living_attackhand.dm +++ b/code/modules/mob/living/living_attackhand.dm @@ -52,11 +52,11 @@ // Returns TRUE if further interactions should be halted, FALSE otherwise. /mob/living/proc/try_extinguish(mob/living/user) - if (!on_fire || !istype(user)) + if (!is_on_fire() || !istype(user)) return FALSE playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - if (user.on_fire) + if (user.is_on_fire()) user.visible_message( SPAN_WARNING("\The [user] tries to pat out \the [src]'s flames, but to no avail!"), SPAN_WARNING("You try to pat out [src]'s flames, but to no avail! Put yourself out first!") @@ -71,25 +71,25 @@ if(!do_mob(user, src, 15)) return TRUE - fire_stacks -= 0.5 - if (prob(10) && (user.fire_stacks <= 0)) - user.fire_stacks += 1 - user.IgniteMob() - if (user.on_fire) + adjust_fire_intensity(-0.5) + if (prob(10) && (user.get_fire_intensity() <= 0)) + user.adjust_fire_intensity(1) + user.ignite_fire() + if (user.is_on_fire()) user.visible_message( SPAN_DANGER("The fire spreads from \the [src] to \the [user]!"), SPAN_DANGER("The fire spreads to you as well!") ) return TRUE - fire_stacks -= 0.5 //Less effective than stop, drop, and roll - also accounting for the fact that it takes half as long. - if (fire_stacks <= 0) + adjust_fire_intensity(-0.5) //Less effective than stop, drop, and roll - also accounting for the fact that it takes half as long. + if (get_fire_intensity() <= 0) user.visible_message( SPAN_NOTICE("\The [user] successfully pats out \the [src]'s flames."), SPAN_NOTICE("You successfully pat out \the [src]'s flames.") ) - ExtinguishMob() - fire_stacks = 0 + extinguish_fire() + set_fire_intensity(0) return TRUE diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 97fe9281e2c3..a74526afdd40 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -291,91 +291,6 @@ user.do_attack_animation(src) return 1 -/mob/living/proc/can_ignite() - return fire_stacks > 0 && !on_fire - -/mob/living/proc/IgniteMob() - if(can_ignite()) - on_fire = TRUE - set_light(4, l_color = COLOR_ORANGE) - update_fire() - -/mob/living/proc/ExtinguishMob() - if(on_fire) - on_fire = FALSE - fire_stacks = 0 - set_light(0) - update_fire() - -/mob/living/proc/update_fire(var/update_icons=1) - if(on_fire) - var/decl/bodytype/mob_bodytype = get_bodytype() - var/image/standing = overlay_image(mob_bodytype?.get_ignited_icon(src) || 'icons/mob/OnFire.dmi', mob_bodytype?.get_ignited_icon_state(src) || "Generic_mob_burning", RESET_COLOR) - set_current_mob_overlay(HO_FIRE_LAYER, standing, update_icons) - else - set_current_mob_overlay(HO_FIRE_LAYER, null, update_icons) - -/mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person - fire_stacks = clamp(fire_stacks + add_fire_stacks, FIRE_MIN_STACKS, FIRE_MAX_STACKS) - -/mob/living/proc/handle_fire() - if(fire_stacks < 0) - fire_stacks = min(0, ++fire_stacks) //If we've doused ourselves in water to avoid fire, dry off slowly - - if(!on_fire) - return TRUE - else if(fire_stacks <= 0) - ExtinguishMob() //Fire's been put out. - return TRUE - - fire_stacks = max(0, fire_stacks - 0.2) //I guess the fire runs out of fuel eventually - - var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment - if(G.get_by_flag(XGM_GAS_OXIDIZER) < 1) - ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire - return TRUE - - var/turf/location = get_turf(src) - location.hotspot_expose(fire_burn_temperature(), 50, 1) - - var/burn_temperature = fire_burn_temperature() - var/thermal_protection = get_heat_protection(burn_temperature) - - if (thermal_protection < 1 && bodytemperature < burn_temperature) - bodytemperature += round(BODYTEMP_HEATING_MAX*(1-thermal_protection), 1) - - var/species_heat_mod = 1 - - var/protected_limbs = get_heat_protection_flags(burn_temperature) - - if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_2)) - species_heat_mod = 0.5 - else if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_3)) - species_heat_mod = 0.75 - - burn_temperature -= get_mob_temperature_threshold(HEAT_LEVEL_1) - - if(burn_temperature < 1) - return - - if(has_external_organs()) - for(var/obj/item/organ/external/E in get_external_organs()) - if(!(E.body_part & protected_limbs) && prob(20)) - E.take_external_damage(burn = round(species_heat_mod * log(10, (burn_temperature + 10)), 0.1), used_weapon = "fire") - else // fallback for simplemobs - take_damage(round(species_heat_mod * log(10, (burn_temperature + 10))), 0.1, BURN, DAM_DISPERSED) - -/mob/living/proc/increase_fire_stacks(exposed_temperature) - if(fire_stacks <= 4 || fire_burn_temperature() < exposed_temperature) - adjust_fire_stacks(2) - -/mob/living/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - //once our fire_burn_temperature has reached the temperature of the fire that's giving fire_stacks, stop adding them. - //allow fire_stacks to go up to 4 for fires cooler than 700 K, since are being immersed in flame after all. - increase_fire_stacks(exposed_temperature) - IgniteMob() - return ..() - /mob/living/proc/get_cold_protection() return 0 @@ -384,12 +299,12 @@ //Finds the effective temperature that the mob is burning at. /mob/living/proc/fire_burn_temperature() - if (fire_stacks <= 0) + var/fire_level = get_fire_intensity() + if (fire_level <= 0) return 0 - //Scale quadratically so that single digit numbers of fire stacks don't burn ridiculously hot. //lower limit of 700 K, same as matches and roughly the temperature of a cool flame. - return max(2.25*round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_stacks/FIRE_MAX_FIRESUIT_STACKS)**2), 700) + return max(2.25*round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_level/FIRE_MAX_FIRESUIT_STACKS)**2), 700) /mob/living/proc/reagent_permeability() return 1 diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 8d8270d08548..4d894ca4fa90 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -29,8 +29,8 @@ var/mob/living/cameraFollow = null var/list/datum/action/actions = list() - var/on_fire = 0 //The "Are we on fire?" var - var/fire_stacks + VAR_PRIVATE/_on_fire = 0 //The "Are we on fire?" var + VAR_PRIVATE/_fire_intensity var/ticks_since_last_successful_breath = 0 //if we failed to breathe last tick var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. diff --git a/code/modules/mob/living/living_fires.dm b/code/modules/mob/living/living_fires.dm new file mode 100644 index 000000000000..5711910fe86c --- /dev/null +++ b/code/modules/mob/living/living_fires.dm @@ -0,0 +1,96 @@ +/mob/living/is_on_fire() + return _on_fire + +/mob/living/set_fire_intensity(amount) + _fire_intensity = amount + +/mob/living/get_fire_intensity() + return _fire_intensity + +//Adjusting the amount of fire stacks we have on person +/mob/living/adjust_fire_intensity(amount) + _fire_intensity = clamp(_fire_intensity + amount, FIRE_MIN_STACKS, FIRE_MAX_STACKS) + +/mob/living/can_ignite() + return get_fire_intensity() > 0 && !is_on_fire() + +/mob/living/ignite_fire() + if(can_ignite()) + _on_fire = TRUE + set_light(4, l_color = COLOR_ORANGE) + update_fire() + +/mob/living/extinguish_fire(mob/user, no_message = FALSE) + if(is_on_fire()) + _on_fire = FALSE + set_fire_intensity(0) + set_light(0) + update_fire() + +/mob/living/proc/update_fire(var/update_icons=1) + if(is_on_fire()) + var/decl/bodytype/mob_bodytype = get_bodytype() + var/image/standing = overlay_image(mob_bodytype?.get_ignited_icon(src) || 'icons/mob/OnFire.dmi', mob_bodytype?.get_ignited_icon_state(src) || "Generic_mob_burning", RESET_COLOR) + set_current_mob_overlay(HO_FIRE_LAYER, standing, update_icons) + else + set_current_mob_overlay(HO_FIRE_LAYER, null, update_icons) + +/mob/living/proc/handle_fire() + var/fire_level = get_fire_intensity() + if(fire_level < 0) + set_fire_intensity(min(0, ++fire_level)) //If we've doused ourselves in water to avoid fire, dry off slowly + fire_level = get_fire_intensity() + + if(!is_on_fire()) + return TRUE + if(fire_level <= 0) + extinguish_fire() //Fire's been put out. + return TRUE + + set_fire_intensity(max(0, fire_level - 0.2)) //I guess the fire runs out of fuel eventually + + var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment + if(G.get_by_flag(XGM_GAS_OXIDIZER) < 1) + extinguish_fire() //If there's no oxygen in the tile we're on, put out the fire + return TRUE + + var/turf/location = get_turf(src) + location.hotspot_expose(fire_burn_temperature(), 50, 1) + + var/burn_temperature = fire_burn_temperature() + var/thermal_protection = get_heat_protection(burn_temperature) + + if (thermal_protection < 1 && bodytemperature < burn_temperature) + bodytemperature += round(BODYTEMP_HEATING_MAX*(1-thermal_protection), 1) + + var/species_heat_mod = 1 + + var/protected_limbs = get_heat_protection_flags(burn_temperature) + + if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_2)) + species_heat_mod = 0.5 + else if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_3)) + species_heat_mod = 0.75 + + burn_temperature -= get_mob_temperature_threshold(HEAT_LEVEL_1) + + if(burn_temperature < 1) + return + + if(has_external_organs()) + for(var/obj/item/organ/external/E in get_external_organs()) + if(!(E.body_part & protected_limbs) && prob(20)) + E.take_external_damage(burn = round(species_heat_mod * log(10, (burn_temperature + 10)), 0.1), used_weapon = "fire") + else // fallback for simplemobs + take_damage(round(species_heat_mod * log(10, (burn_temperature + 10))), 0.1, BURN, DAM_DISPERSED) + +/mob/living/proc/increase_fire_intensity(exposed_temperature) + if(get_fire_intensity() <= 4 || fire_burn_temperature() < exposed_temperature) + adjust_fire_intensity(2) + +/mob/living/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + //once our fire_burn_temperature has reached the temperature of the fire that's giving fire intensity, stop adding them. + //allow fire intensity to go up to 4 for fires cooler than 700 K, since are being immersed in flame after all. + increase_fire_intensity(exposed_temperature) + ignite_fire() + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index a8da2261da4e..4e2bc0fe3cf5 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -258,12 +258,12 @@ /mob/living/silicon/robot/update_fire() overlays -= image("icon"='icons/mob/OnFire.dmi', "icon_state"="Standing") - if(on_fire) + if(is_on_fire()) overlays += image("icon"='icons/mob/OnFire.dmi', "icon_state"="Standing") //Silicons don't gain stacks from hotspots, but hotspots can ignite them -/mob/living/silicon/increase_fire_stacks(exposed_temperature) +/mob/living/silicon/increase_fire_intensity(exposed_temperature) return /mob/living/silicon/can_ignite() - return !on_fire + return !is_on_fire() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm index 0541018c1de1..d8443751980f 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm @@ -188,8 +188,8 @@ /mob/living/simple_animal/hostile/beast/charbaby/apply_attack_effects(mob/living/target) . = ..() if(prob(10)) - target.adjust_fire_stacks(1) - target.IgniteMob() + target.adjust_fire_intensity(1) + target.ignite_fire() /mob/living/simple_animal/hostile/beast/shantak/lava desc = "A vaguely canine looking beast. It looks as though its fur is made of stone wool." diff --git a/code/modules/organs/external/_external_damage.dm b/code/modules/organs/external/_external_damage.dm index d1c38e68b5be..0d185bbe9ae9 100644 --- a/code/modules/organs/external/_external_damage.dm +++ b/code/modules/organs/external/_external_damage.dm @@ -89,7 +89,7 @@ if(laser) created_wound = createwound(LASER, burn) if(prob(40)) - owner.IgniteMob() + owner.ignite_fire() else created_wound = createwound(BURN, burn) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index d9cde61e7df9..dc643dcdc3b2 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -354,9 +354,9 @@ ..() if(isliving(target)) var/mob/living/L = target - L.adjust_fire_stacks(rand(2,4)) - if(L.fire_stacks >= 3) - L.IgniteMob() + L.adjust_fire_intensity(rand(2,4)) + if(L.get_fire_intensity() >= 3) + L.ignite_fire() /obj/item/projectile/beam/pop icon_state = "bluelaser" diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 014041095106..d648f6d70c3f 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -206,5 +206,5 @@ if(isliving(target)) var/mob/living/L = target to_chat(target, SPAN_WARNING("You feel a wave of heat wash over you!")) - L.adjust_fire_stacks(rand(5,8)) - L.IgniteMob() \ No newline at end of file + L.adjust_fire_intensity(rand(5,8)) + L.ignite_fire() \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/drinks/bottle.dm b/code/modules/reagents/reagent_containers/drinks/bottle.dm index 6c93e2c79a27..242cd0583bec 100644 --- a/code/modules/reagents/reagent_containers/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/drinks/bottle.dm @@ -41,7 +41,7 @@ /obj/item/chems/drinks/bottle/proc/smash_check(var/distance) if(!material?.is_brittle()) return 0 - if(rag && rag.on_fire) // Molotovs should be somewhat reliable, they're a pain to make. + if(rag?.is_on_fire()) // Molotovs should be somewhat reliable, they're a pain to make. return TRUE if(!smash_duration) return 0 @@ -68,13 +68,13 @@ if(rag) rag.dropInto(T) while(T) + if(!rag || QDELETED(src) || !HasBelow(T.z) || !T.is_open()) + break rag.forceMove(T) - if(rag.on_fire) + if(rag.is_on_fire()) T.hotspot_expose(700, 5) for(var/mob/living/M in T.contents) - M.IgniteMob() - if(!rag || QDELETED(src) || !HasBelow(T.z) || !T.is_open()) - break + M.ignite_fire() T = GetBelow(T) rag = null @@ -148,7 +148,7 @@ . = ..() underlays.Cut() if(rag) - var/underlay_image = image(icon='icons/obj/drinks.dmi', icon_state=rag.on_fire? "[rag_underlay]_lit" : rag_underlay) + var/underlay_image = image(icon='icons/obj/drinks.dmi', icon_state=rag.is_on_fire()? "[rag_underlay]_lit" : rag_underlay) underlays += underlay_image set_light(rag.light_range, 0.1, rag.light_color) else @@ -178,8 +178,8 @@ if(reagents) user.visible_message(SPAN_NOTICE("The contents of \the [src] splash all over [target]!")) reagents.splash(target, reagents.total_volume) - if(rag && rag.on_fire && istype(target)) - target.IgniteMob() + if(rag?.is_on_fire() && istype(target)) + target.ignite_fire() //Finally, smash the bottle. This kills (qdel) the bottle. var/obj/item/broken_bottle/B = smash(target.loc, target) diff --git a/code/modules/spells/targeted/ethereal_jaunt.dm b/code/modules/spells/targeted/ethereal_jaunt.dm index fdccd1ce64d1..55a0ed83359d 100644 --- a/code/modules/spells/targeted/ethereal_jaunt.dm +++ b/code/modules/spells/targeted/ethereal_jaunt.dm @@ -31,7 +31,7 @@ animation.anchored = TRUE animation.icon = 'icons/mob/mob.dmi' animation.layer = FLY_LAYER - target.ExtinguishMob() + target.extinguish_fire() if(target.buckled) target.buckled = null jaunt_disappear(animation, target) diff --git a/mods/gamemodes/cult/runes.dm b/mods/gamemodes/cult/runes.dm index 11fd2e328ed3..161d225a6481 100644 --- a/mods/gamemodes/cult/runes.dm +++ b/mods/gamemodes/cult/runes.dm @@ -481,8 +481,8 @@ if(casters.len < 3) break //T.turf_animation('icons/effects/effects.dmi', "rune_sac") - victim.fire_stacks = max(2, victim.fire_stacks) - victim.IgniteMob() + victim.set_fire_intensity(max(2, victim.get_fire_intensity())) + victim.ignite_fire() var/dam_amt = 2 + length(casters) victim.take_organ_damage(dam_amt, dam_amt) // This is to speed up the process and also damage mobs that don't take damage from being on fire, e.g. borgs if(ishuman(victim)) @@ -505,10 +505,9 @@ to_chat(victim, SPAN_OCCULT("The Geometer of Blood claims your body.")) victim.dust() if(victim) - victim.ExtinguishMob() // Technically allows them to put the fire out by sacrificing them and stopping immediately, but I don't think it'd have much effect + victim.extinguish_fire() // Technically allows them to put the fire out by sacrificing them and stopping immediately, but I don't think it'd have much effect victim = null - /obj/effect/rune/drain cultname = "blood drain" strokes = 3 diff --git a/mods/species/serpentid/datum/species.dm b/mods/species/serpentid/datum/species.dm index e58789252fc0..86d5ae09208e 100644 --- a/mods/species/serpentid/datum/species.dm +++ b/mods/species/serpentid/datum/species.dm @@ -84,8 +84,8 @@ #undef SERPENTID_FLIGHT_PRESSURE_THRESHOLD /decl/species/serpentid/handle_environment_special(var/mob/living/human/H) - if(!H.on_fire && H.fire_stacks < 2) - H.fire_stacks += 0.2 + if(!H.is_on_fire() && H.get_fire_intensity() < 2) + H.adjust_fire_intensity(0.2) return /decl/species/serpentid/handle_fall_special(var/mob/living/human/H, var/turf/landing) diff --git a/nebula.dme b/nebula.dme index 354c22a8b18c..95b6436de7ad 100644 --- a/nebula.dme +++ b/nebula.dme @@ -739,6 +739,7 @@ #include "code\game\atom_material.dm" #include "code\game\atoms.dm" #include "code\game\atoms_damage.dm" +#include "code\game\atoms_fires.dm" #include "code\game\atoms_fluids.dm" #include "code\game\atoms_init.dm" #include "code\game\atoms_layering.dm" @@ -2862,6 +2863,7 @@ #include "code\modules\mob\living\living_dreams.dm" #include "code\modules\mob\living\living_eating.dm" #include "code\modules\mob\living\living_electrocution.dm" +#include "code\modules\mob\living\living_fires.dm" #include "code\modules\mob\living\living_genetics.dm" #include "code\modules\mob\living\living_give.dm" #include "code\modules\mob\living\living_grabs.dm" From 4fb506777e73f6427c7502b263ff7e3299ac8d4a Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 17 Dec 2024 16:17:12 -0500 Subject: [PATCH 0089/1331] Add new rough-hewn wood plank turfs --- .../items/stacks/tiles/tile_types_wooden.dm | 10 ++++ code/game/turfs/flooring/flooring_wood.dm | 41 +++++++++++++++ code/game/turfs/floors/subtypes/floor_wood.dm | 48 ++++++++++-------- icons/turf/flooring/wood_alt.dmi | Bin 0 -> 1814 bytes 4 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 icons/turf/flooring/wood_alt.dmi diff --git a/code/game/objects/items/stacks/tiles/tile_types_wooden.dm b/code/game/objects/items/stacks/tiles/tile_types_wooden.dm index fc9ec6517d76..a184391251f5 100644 --- a/code/game/objects/items/stacks/tiles/tile_types_wooden.dm +++ b/code/game/objects/items/stacks/tiles/tile_types_wooden.dm @@ -28,6 +28,9 @@ /obj/item/stack/tile/wood/laminate abstract_type = /obj/item/stack/tile/wood/laminate +/obj/item/stack/tile/wood/rough + abstract_type = /obj/item/stack/tile/wood/rough + WOOD_TILE_SUBTYPE("oak", oak, oak) WOOD_TILE_SUBTYPE("mahogany", mahogany, mahogany) WOOD_TILE_SUBTYPE("maple", maple, maple) @@ -35,6 +38,13 @@ WOOD_TILE_SUBTYPE("ebony", ebony, ebony) WOOD_TILE_SUBTYPE("walnut", walnut, walnut) WOOD_TILE_SUBTYPE("bamboo", bamboo, bamboo) WOOD_TILE_SUBTYPE("yew", yew, yew) +WOOD_TILE_SUBTYPE("rough oak", rough/oak, oak) +WOOD_TILE_SUBTYPE("rough mahogany", rough/mahogany, mahogany) +WOOD_TILE_SUBTYPE("rough maple", rough/maple, maple) +WOOD_TILE_SUBTYPE("rough ebony", rough/ebony, ebony) +WOOD_TILE_SUBTYPE("rough walnut", rough/walnut, walnut) +WOOD_TILE_SUBTYPE("rough bamboo", rough/bamboo, bamboo) +WOOD_TILE_SUBTYPE("rough yew", rough/yew, yew) WOOD_TILE_SUBTYPE("oak laminate", laminate/oak, chipboard) WOOD_TILE_SUBTYPE("mahogany laminate", laminate/mahogany, chipboard/mahogany) WOOD_TILE_SUBTYPE("maple laminate", laminate/maple, chipboard/maple) diff --git a/code/game/turfs/flooring/flooring_wood.dm b/code/game/turfs/flooring/flooring_wood.dm index 9b2b75b0ffd2..b5fc086d42f4 100644 --- a/code/game/turfs/flooring/flooring_wood.dm +++ b/code/game/turfs/flooring/flooring_wood.dm @@ -53,6 +53,47 @@ build_type = /obj/item/stack/tile/wood/yew force_material = /decl/material/solid/organic/wood/yew +// Rough-hewn floors. +/decl/flooring/wood/rough + + name = "rough wooden floor" + desc = "A stretch of loosely-fitted, rough-hewn wooden planks." + icon = 'icons/turf/flooring/wood_alt.dmi' + icon_base = "wood_peasant" + has_base_range = 3 + build_type = /obj/item/stack/tile/wood/rough + broken_states = null + +/decl/flooring/wood/rough/mahogany + color = /decl/material/solid/organic/wood/mahogany::color + build_type = /obj/item/stack/tile/wood/rough/mahogany + force_material = /decl/material/solid/organic/wood/mahogany + +/decl/flooring/wood/rough/maple + color = /decl/material/solid/organic/wood/maple::color + build_type = /obj/item/stack/tile/wood/rough/maple + force_material = /decl/material/solid/organic/wood/maple + +/decl/flooring/wood/rough/ebony + color = /decl/material/solid/organic/wood/ebony::color + build_type = /obj/item/stack/tile/wood/rough/ebony + force_material = /decl/material/solid/organic/wood/ebony + +/decl/flooring/wood/rough/walnut + color = /decl/material/solid/organic/wood/walnut::color + build_type = /obj/item/stack/tile/wood/rough/walnut + force_material = /decl/material/solid/organic/wood/walnut + +/decl/flooring/wood/rough/bamboo + color = /decl/material/solid/organic/wood/bamboo::color + build_type = /obj/item/stack/tile/wood/rough/bamboo + force_material = /decl/material/solid/organic/wood/bamboo + +/decl/flooring/wood/rough/yew + color = /decl/material/solid/organic/wood/yew::color + build_type = /obj/item/stack/tile/wood/rough/yew + force_material = /decl/material/solid/organic/wood/yew + // Chipboard/wood laminate floors. Uses older icons. /decl/flooring/laminate name = "wooden laminate floor" diff --git a/code/game/turfs/floors/subtypes/floor_wood.dm b/code/game/turfs/floors/subtypes/floor_wood.dm index d5fa771be77a..eff98a2a1176 100644 --- a/code/game/turfs/floors/subtypes/floor_wood.dm +++ b/code/game/turfs/floors/subtypes/floor_wood.dm @@ -5,6 +5,12 @@ color = /decl/material/solid/organic/wood/oak::color _flooring = /decl/flooring/wood +#define WOOD_FLOOR_SUBTYPE(BASE, WOOD) \ +/turf/floor/##BASE/##WOOD { \ + color = /decl/material/solid/organic/wood/##WOOD::color; \ + _flooring = /decl/flooring/##BASE/##WOOD; \ +} + /turf/floor/wood/broken icon_state = "wood_broken0" _floor_broken = TRUE @@ -31,29 +37,27 @@ icon_state = "wood_broken4" _floor_broken = "broken4" -/turf/floor/wood/mahogany - color = /decl/material/solid/organic/wood/mahogany::color - _flooring = /decl/flooring/wood/mahogany - -/turf/floor/wood/maple - color = /decl/material/solid/organic/wood/maple::color - _flooring = /decl/flooring/wood/maple - -/turf/floor/wood/ebony - color = /decl/material/solid/organic/wood/ebony::color - _flooring = /decl/flooring/wood/ebony - -/turf/floor/wood/walnut - color = /decl/material/solid/organic/wood/walnut::color - _flooring = /decl/flooring/wood/walnut - -/turf/floor/wood/bamboo - color = /decl/material/solid/organic/wood/bamboo::color - _flooring = /decl/flooring/wood/bamboo +WOOD_FLOOR_SUBTYPE(wood, mahogany) +WOOD_FLOOR_SUBTYPE(wood, maple) +WOOD_FLOOR_SUBTYPE(wood, ebony) +WOOD_FLOOR_SUBTYPE(wood, walnut) +WOOD_FLOOR_SUBTYPE(wood, bamboo) +WOOD_FLOOR_SUBTYPE(wood, yew) + +// Rough wood floors; lower skill requirement, more wasteful to craft. +/turf/floor/wood/rough + name = "rough-hewn wooden floor" + icon = 'icons/turf/flooring/wood_alt.dmi' + icon_state = "wood_peasant0" + color = /decl/material/solid/organic/wood/oak::color + _flooring = /decl/flooring/wood -/turf/floor/wood/yew - color = /decl/material/solid/organic/wood/yew::color - _flooring = /decl/flooring/wood/yew +WOOD_FLOOR_SUBTYPE(wood/rough, mahogany) +WOOD_FLOOR_SUBTYPE(wood/rough, maple) +WOOD_FLOOR_SUBTYPE(wood/rough, ebony) +WOOD_FLOOR_SUBTYPE(wood/rough, walnut) +WOOD_FLOOR_SUBTYPE(wood/rough, bamboo) +WOOD_FLOOR_SUBTYPE(wood/rough, yew) // Laminate floor; basically identical to wood, but uses older smoother icons. /turf/floor/laminate diff --git a/icons/turf/flooring/wood_alt.dmi b/icons/turf/flooring/wood_alt.dmi new file mode 100644 index 0000000000000000000000000000000000000000..2f55a0fb6c4db73750af523aeaca370d05370a89 GIT binary patch literal 1814 zcmV+x2kH2UP)sOZ(?wKaD2nV zwaFvW=Kufz0d!JMQvg8b*k%9#0EK!~Sad{Xb7OL8aCB*JZU6vyoKseCa&`CgQ*iP1 zv)zWcDwhQix@LFpvshrCnx{R%(i_HMQvXg;vuK1|i==Wt9^=#<8 z0w}`VR1FOK4Q+riwP*H?`Ge*G_@JEhGS#*i|FI-1UGHsEt7ZAmP6aL3dpYph^zYjdD{+hebm>-cr*HUr4>Ecm{Kqz3Cpygr^V`j%eAYK?aqrmnt* zhvm{PV%!T9^Je#u$qCRHovo{Zt3`05_7K*;@xs|efEio_MVfmd(}qZS;Jxa%Yynx|gNJdN)&d89MWnQP#qREtD%AVF-}X8kErpixDO_#sTsY-%ttDU%al<0DTt>9aZ= zv*sOla*M>pwRxD%9;)prBUj@VIwZ20BzG!VyEw;5c-+XYr>|KZ1h?5~23;q_3L*$> zQ!@a-3F4fgUz@jCjn>U!@&HpXr znL5}L#GW$T*Nblgxsf>p@|x2YKr#g0Ms~Q)-c7V%?596jW#EC6@l|dB^R$H$IA|X7 z0C`%H<(9jdJ5}?BgE^tl&N=70Zx!wH0nl|@XqLGHI9)WW2cKm`2D_ih`vdNSw*2S^#o1sM(l%4A9UHIU z9cxJwtJv`Y=?|#o6hK#hEz`?ZQ(YkCDt1e&*Ni3=+~j3)&_?ipY4FkCQ*T6cImVom-LC(?8Xu%}h%~!7o)2>349M(i zob>;@P&<)Ihx<--bn@58uTy| zMoRTC5f7Oye<(+LE}{7~fcSxgHQHh-*R(C*Vd~%(StYc)XCjY1R##l=mX5vCm*Ju4 z;wZZW_~67^znuVvKwj5)>JlKM9tsPod0>c|%}U?T2bTbGu8J71hvnInAUaRa2iF>S zIqGq~BgX8lVmh1;++XQd9@xpTN^n{YUJFvgdb$Um4|qy4KlmBo5}=$8bKzS*BL@va zI=S9-a`aQ{q)X%Q+n6kJU=*$%Zl^C9jfpaymNtf`T*z& z*A)r50{EtGXfT9O_w62Fkh5?ZfT#CO4aTaN^1J44B4H8R$(4;(lwKeJ#Vv1cIN6^pwmSI>tN(Y*yj5d=e2)<01_1hd1yG23XoaGf zm9(y9l~E`zzXT8u3-jAr(&yl-t5Vl6nCvhv0fNbF{WacOeGZEONoW24rHQEO*Z%&p z|GzfT$EnU+Ts?U8CTlO3kZTpX1{i53cME6K>gY}QesN3@+}~<`$5(jK-^Z(xh?0A%=JPWXq&2><{907*qoM6N<$ Ef-fh0I{*Lx literal 0 HcmV?d00001 From 21854d52b6d5ad490b8a1742e46e2e5a6c80e063 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 17 Dec 2024 16:17:30 -0500 Subject: [PATCH 0090/1331] Add rough-hewn wood floors to Shaded Hills maps --- maps/shaded_hills/shaded_hills-grassland.dmm | 8 +- maps/shaded_hills/shaded_hills-inn.dmm | 239 ++++++++++-------- maps/shaded_hills/shaded_hills-swamp.dmm | 28 +- maps/shaded_hills/shaded_hills-woods.dmm | 26 +- .../woods/chemistry_shack/chemistry_shack.dmm | 24 +- .../submaps/woods/old_cabin/old_cabin.dmm | 30 +-- 6 files changed, 192 insertions(+), 163 deletions(-) diff --git a/maps/shaded_hills/shaded_hills-grassland.dmm b/maps/shaded_hills/shaded_hills-grassland.dmm index ac9e42b9c2df..b885f19cf3f4 100644 --- a/maps/shaded_hills/shaded_hills-grassland.dmm +++ b/maps/shaded_hills/shaded_hills-grassland.dmm @@ -110,7 +110,7 @@ "nl" = ( /obj/structure/door/walnut, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside) "oo" = ( /obj/item/stack/material/ore/handful/sand, @@ -144,7 +144,7 @@ /area/shaded_hills/caves/unexplored) "ul" = ( /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside) "vX" = ( /turf/floor/path/running_bond/basalt, @@ -159,7 +159,7 @@ /turf/floor/dirt, /area/shaded_hills/outside) "xC" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside) "yA" = ( /obj/abstract/landmark/latejoin/observer, @@ -188,7 +188,7 @@ /turf/floor/woven, /area/shaded_hills/outside) "EE" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/river) "EL" = ( /obj/abstract/exterior_marker/inside, diff --git a/maps/shaded_hills/shaded_hills-inn.dmm b/maps/shaded_hills/shaded_hills-inn.dmm index a2ee7dff5e43..62e2a71efb30 100644 --- a/maps/shaded_hills/shaded_hills-inn.dmm +++ b/maps/shaded_hills/shaded_hills-inn.dmm @@ -24,7 +24,7 @@ /area/shaded_hills/outside/downlands) "bu" = ( /obj/structure/table/desk/dresser, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/stable) "bv" = ( /obj/structure/bed/chair/rustic{ @@ -45,11 +45,11 @@ /obj/item/chems/glass/handmade/cup/wood, /obj/item/chems/glass/handmade/cup/wood, /obj/item/chems/glass/handmade/cup/wood, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "cl" = ( /obj/structure/table/wood/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "cq" = ( /obj/structure/table/wood/ebony, @@ -89,11 +89,11 @@ /area/shaded_hills/general_store) "dK" = ( /obj/structure/table/wood/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "dL" = ( /obj/structure/reagent_dispensers/barrel/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/storehouse) "dN" = ( /obj/structure/wall_sconce/lantern{ @@ -107,7 +107,7 @@ /area/shaded_hills/shrine) "eD" = ( /obj/structure/working/loom/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "eG" = ( /obj/structure/door/walnut, @@ -149,7 +149,7 @@ /turf/floor/path/herringbone/basalt, /area/shaded_hills/inn/kitchen) "fR" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/storehouse) "fS" = ( /obj/structure/table/marble, @@ -176,14 +176,14 @@ /turf/floor/path/basalt, /area/shaded_hills/slaughterhouse) "ge" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "gg" = ( /obj/structure/wall_sconce/lantern{ dir = 4 }, /obj/structure/table/wood/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "gn" = ( /obj/item/flame/candle/handmade, @@ -200,7 +200,7 @@ /obj/structure/wall_sconce/lantern{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/mahogany, /area/shaded_hills/shrine) "gA" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -212,7 +212,7 @@ /obj/structure/wall_sconce/lantern{ start_lit = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "gL" = ( /obj/structure/reagent_dispensers/barrel/ebony/oil, @@ -246,7 +246,7 @@ /obj/structure/bed/chair/bench/ebony{ dir = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse/porch) "hu" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -256,7 +256,7 @@ dir = 1; pixel_y = 10 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "hE" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -267,7 +267,7 @@ /area/shaded_hills/outside/downlands) "hJ" = ( /obj/item/stool/rustic, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "hU" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -280,7 +280,7 @@ dir = 1; pixel_y = 10 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "id" = ( /obj/structure/table/wood/ebony, @@ -313,6 +313,12 @@ /obj/abstract/landmark/lock_preset/shaded_hills/inn_interior, /turf/floor/path/herringbone/basalt, /area/shaded_hills/inn) +"iJ" = ( +/obj/structure/wall_sconce/lantern{ + dir = 4 + }, +/turf/floor/wood/rough/walnut, +/area/shaded_hills/shrine) "iK" = ( /turf/wall/brick/basalt{ unique_merge_identifier = "shrine wall" @@ -329,17 +335,17 @@ /obj/structure/wall_sconce/lantern{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/stable) "jk" = ( /obj/structure/bed/chair/bench/pew/mahogany{ dir = 1 }, /obj/abstract/landmark/start/shaded_hills/cleric, -/turf/floor/wood/walnut, +/turf/floor/wood/mahogany, /area/shaded_hills/shrine) "js" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/stable) "jx" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -357,7 +363,7 @@ "jA" = ( /obj/structure/bed/simple/ebony, /obj/item/bedsheet/furs, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/stable) "jI" = ( /obj/structure/wall_sconce/lantern{ @@ -381,11 +387,11 @@ /obj/structure/banner_frame/wall/ebony/green{ pixel_y = 32 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "kE" = ( /obj/structure/door/walnut, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/stable) "kI" = ( /obj/structure/bed/chair/bench/pew/ebony{ @@ -405,7 +411,7 @@ /obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "lE" = ( /obj/abstract/level_data_spawner/shaded_hills_downlands, @@ -419,7 +425,7 @@ dir = 1 }, /obj/abstract/landmark/start/shaded_hills/farmer, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse/porch) "me" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -438,13 +444,13 @@ "ml" = ( /obj/structure/table/wood/ebony, /obj/item/shears, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "mG" = ( /obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse/porch) "mJ" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -454,8 +460,14 @@ dir = 1; pixel_y = 10 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) +"mK" = ( +/obj/structure/door/walnut{ + dir = 4 + }, +/turf/floor/wood/rough/walnut, +/area/shaded_hills/farmhouse) "nn" = ( /turf/wall/log/walnut, /area/shaded_hills/stable) @@ -507,7 +519,7 @@ /obj/structure/bed/chair/bench/pew/mahogany{ dir = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/mahogany, /area/shaded_hills/shrine) "pi" = ( /obj/structure/table/wood/ebony, @@ -522,13 +534,13 @@ /obj/structure/bed/simple/ebony, /obj/abstract/landmark/start/shaded_hills/shrine_attendant, /obj/item/bedsheet/furs, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "qf" = ( /obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "qG" = ( /turf/wall/brick/basalt, @@ -556,7 +568,7 @@ /obj/structure/table/wood/ebony, /obj/item/chems/glass/mortar, /obj/item/rock/basalt, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "rc" = ( /obj/structure/railing/mapped/wooden/walnut, @@ -596,7 +608,7 @@ /obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "rC" = ( /obj/structure/table/marble, @@ -614,7 +626,7 @@ /obj/structure/wall_sconce/lantern{ start_lit = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "rY" = ( /obj/effect/floor_decal/spline/fancy/wood/corner/walnut, @@ -625,6 +637,10 @@ /obj/abstract/landmark/lock_preset/shaded_hills/inn_interior, /turf/floor/path/herringbone/basalt, /area/shaded_hills/inn/kitchen) +"sz" = ( +/obj/structure/door/walnut, +/turf/floor/wood/rough/walnut, +/area/shaded_hills/shrine) "sJ" = ( /obj/structure/closet/cabinet/wooden/ebony, /turf/floor/wood/walnut, @@ -672,7 +688,7 @@ /obj/structure/bed/chair/rustic{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "uk" = ( /obj/structure/bed/chair/bench/pew/ebony{ @@ -710,13 +726,13 @@ dir = 1; pixel_y = 10 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "vz" = ( /obj/structure/table/wood/ebony, /obj/item/chems/cooking_vessel/baking_dish/earthenware, /obj/item/chems/cooking_vessel/baking_dish/earthenware, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "vA" = ( /obj/structure/table/marble, @@ -764,7 +780,7 @@ /obj/item/chems/condiment/large/salt, /obj/item/chems/condiment/flour, /obj/item/chems/condiment/sugar, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "xk" = ( /obj/structure/flora/tree/dead/mahogany, @@ -789,7 +805,7 @@ /area/shaded_hills/outside/downlands) "ya" = ( /obj/structure/reagent_dispensers/barrel/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "yn" = ( /obj/structure/door/walnut, @@ -807,7 +823,7 @@ /obj/item/seeds/extracted/cabbage, /obj/item/seeds/extracted/cabbage, /obj/item/seeds/extracted/cabbage, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/storehouse) "yx" = ( /obj/structure/table/marble, @@ -822,11 +838,11 @@ dir = 1 }, /obj/abstract/landmark/start/shaded_hills/farmer, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "yN" = ( /obj/structure/closet/crate/chest/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "yY" = ( /turf/floor/grass, @@ -866,7 +882,7 @@ /turf/floor/wood/walnut, /area/shaded_hills/inn) "zQ" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/general_store/porch) "Ak" = ( /turf/floor/path/basalt, @@ -878,7 +894,8 @@ /area/shaded_hills/inn) "Aq" = ( /obj/structure/working/loom/ebony, -/turf/floor/wood/walnut, +/obj/structure/wall_sconce/lantern, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "Aw" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -907,7 +924,7 @@ "AG" = ( /obj/structure/table/wood/ebony, /obj/item/bag/sack, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "AW" = ( /obj/structure/railing/mapped/wooden/walnut, @@ -918,11 +935,11 @@ /obj/structure/wall_sconce/lantern{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/mahogany, /area/shaded_hills/shrine) "Bp" = ( /obj/structure/working/butter_churn/walnut, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "Br" = ( /turf/floor/path/basalt, @@ -952,7 +969,7 @@ /area/shaded_hills/general_store) "BX" = ( /obj/structure/wall_sconce/lantern, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse/porch) "Cj" = ( /obj/item/stack/material/log/mapped/walnut/twenty, @@ -964,10 +981,13 @@ /turf/floor/path/herringbone/basalt, /area/shaded_hills/shrine/kitchen) "CR" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/mahogany, +/area/shaded_hills/shrine) +"CV" = ( +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "Dl" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse/porch) "Dn" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -979,7 +999,7 @@ /obj/item/seeds/extracted/wheat, /obj/item/seeds/extracted/wheat, /obj/item/seeds/extracted/wheat, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/storehouse) "Dr" = ( /obj/structure/bed/simple/ebony/cloth, @@ -990,7 +1010,7 @@ /obj/structure/bed/simple/ebony/cloth, /obj/abstract/landmark/start/shaded_hills/shrine_keeper, /obj/item/bedsheet/furs, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "Ee" = ( /turf/wall/log/walnut, @@ -1045,7 +1065,7 @@ /obj/structure/wall_sconce/lantern{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "FD" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -1093,7 +1113,7 @@ /obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/general_store/porch) "Gu" = ( /obj/structure/table/marble, @@ -1153,7 +1173,7 @@ /area/shaded_hills/stable) "HF" = ( /obj/structure/working/spinning_wheel/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "HI" = ( /turf/floor/grass, @@ -1175,7 +1195,7 @@ /obj/item/flame/candle/handmade{ pixel_y = 12 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/stable) "Ic" = ( /turf/floor/path/running_bond/basalt, @@ -1184,7 +1204,7 @@ /obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "Im" = ( /obj/structure/wall_sconce/lantern{ @@ -1199,7 +1219,7 @@ "IB" = ( /obj/structure/door/walnut, /obj/abstract/landmark/lock_preset/shaded_hills/shrine, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "IC" = ( /obj/structure/bookcase/ebony, @@ -1209,7 +1229,7 @@ /obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/storehouse) "IQ" = ( /turf/wall/log/walnut/shutter/open, @@ -1246,11 +1266,11 @@ /area/shaded_hills/inn) "Jt" = ( /obj/structure/closet/cabinet/wooden/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "Jw" = ( /obj/structure/bed/chair/rustic, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "Jz" = ( /obj/structure/door/walnut{ @@ -1268,11 +1288,12 @@ /obj/structure/table/wood/ebony, /obj/item/ancient_surgery/cautery, /obj/item/ancient_surgery/scalpel, +/obj/structure/wall_sconce/lantern, /turf/floor/path/herringbone/basalt, /area/shaded_hills/shrine) "JU" = ( /obj/structure/railing/mapped/wooden/walnut, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "Ka" = ( /mob/living/simple_animal/cow, @@ -1317,7 +1338,7 @@ "LH" = ( /obj/structure/table/wood/ebony, /obj/item/chems/glass/bucket/wood, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse) "LK" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -1392,7 +1413,7 @@ "Ot" = ( /obj/structure/table/wood/ebony, /obj/item/chems/cooking_vessel/pot/iron, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "ON" = ( /obj/structure/table/marble, @@ -1424,7 +1445,7 @@ /area/shaded_hills/stable) "PG" = ( /obj/structure/door/walnut, -/turf/floor/wood/walnut, +/turf/floor/wood/mahogany, /area/shaded_hills/shrine) "PQ" = ( /obj/item/food/butchery/meat/beef, @@ -1436,7 +1457,7 @@ /area/shaded_hills/slaughterhouse) "PS" = ( /obj/structure/reagent_dispensers/barrel/ebony/oil, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/storehouse) "Qb" = ( /turf/floor/carpet, @@ -1464,7 +1485,7 @@ dir = 8; start_lit = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "QQ" = ( /turf/wall/log/walnut, @@ -1473,7 +1494,7 @@ /obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/general_store/porch) "QW" = ( /turf/floor/path/herringbone/basalt, @@ -1536,11 +1557,11 @@ /obj/structure/bed/chair/rustic{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "Sy" = ( /obj/structure/working/spinning_wheel/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "SB" = ( /obj/structure/door/walnut{ @@ -1614,7 +1635,7 @@ /obj/item/food/grown/carrot, /obj/item/food/grown/carrot, /obj/item/food/grown/cabbage, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "TR" = ( /turf/floor/dirt, @@ -1624,10 +1645,15 @@ /obj/abstract/landmark/lock_preset/shaded_hills/shrine, /turf/floor/path/herringbone/basalt, /area/shaded_hills/shrine) +"Ue" = ( +/obj/structure/door/walnut, +/obj/abstract/landmark/lock_preset/shaded_hills/farmhouse, +/turf/floor/wood/rough/walnut, +/area/shaded_hills/farmhouse) "UD" = ( /obj/item/flame/candle/handmade, /obj/structure/table/end/alt, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine) "US" = ( /obj/structure/bed/chair/rustic{ @@ -1649,7 +1675,7 @@ /area/shaded_hills/shrine) "VE" = ( /obj/structure/cask_rack/large/mapped, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "VM" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -1657,11 +1683,14 @@ }, /turf/floor/dirt, /area/shaded_hills/outside/shrine) +"VO" = ( +/turf/floor/wood/rough/walnut, +/area/shaded_hills/farmhouse) "VU" = ( /obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/farmhouse/porch) "VW" = ( /obj/structure/table/marble, @@ -1680,7 +1709,7 @@ /obj/structure/table/wood/ebony, /obj/item/chems/glass/mortar, /obj/item/rock/basalt, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) "Wk" = ( /obj/structure/railing/mapped/wooden/walnut{ @@ -1704,7 +1733,7 @@ /obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "WE" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -1762,7 +1791,7 @@ pixel_y = 10; start_lit = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/mahogany, /area/shaded_hills/shrine) "XM" = ( /turf/floor/carpet/red, @@ -1774,7 +1803,7 @@ /turf/wall/brick/basalt, /area/shaded_hills/shrine) "Yg" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/inn/porch) "Yj" = ( /obj/structure/meat_hook, @@ -13289,8 +13318,8 @@ EV TR LN cl -qP -qP +VO +VO yN LN TR @@ -13442,7 +13471,7 @@ TR IQ LH yJ -qP +VO yN LN TR @@ -13593,8 +13622,8 @@ EV TR LN ml -qP -qP +VO +VO gB LN TR @@ -13745,8 +13774,8 @@ EV TR LN AG -qP -qP +VO +VO ya LN zH @@ -13897,7 +13926,7 @@ VU VU LN LN -Ti +mK LN LN LN @@ -14048,8 +14077,8 @@ Ic Dl BX LN -qP -qP +VO +VO hJ Bp IQ @@ -14199,12 +14228,12 @@ EV EV Dl Dl -WO -qP -qP -qP -qP -WO +Ue +VO +VO +VO +VO +Ue TR Rl GW @@ -14353,9 +14382,9 @@ Dl lL LN hJ -qP -qP -qP +VO +VO +VO LN xW Rl @@ -18706,7 +18735,7 @@ By iK QB Dt -CR +CV QB QB QB @@ -18858,13 +18887,13 @@ By iK QB Jt -CR -PG -CR -CR -AX -CR -CR +CV +sz +CV +CV +iJ +CV +CV IB CR CR @@ -19164,10 +19193,10 @@ iK kv kv Kh -CR +CV rS QB -CR +CV rS QB CR diff --git a/maps/shaded_hills/shaded_hills-swamp.dmm b/maps/shaded_hills/shaded_hills-swamp.dmm index 0ac1cb7ad186..bbd09a8b2284 100644 --- a/maps/shaded_hills/shaded_hills-swamp.dmm +++ b/maps/shaded_hills/shaded_hills-swamp.dmm @@ -3,7 +3,7 @@ /turf/wall/log/walnut/shutter/open, /area/shaded_hills/witch_hut) "bg" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "cE" = ( /turf/floor/mud/water, @@ -12,7 +12,7 @@ /obj/structure/bed/chair/wood/ebony{ dir = 8 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "fG" = ( /obj/item/seeds/extracted/aloe, @@ -27,7 +27,7 @@ /obj/item/seeds/extracted/yarrow, /obj/item/seeds/extracted/yarrow, /obj/item/seeds/extracted/yarrow, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "hT" = ( /turf/floor/mud/water/deep, @@ -55,7 +55,7 @@ /obj/structure/closet/crate/chest/ebony, /obj/item/rock/hematite, /obj/item/rock/flint, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "oF" = ( /turf/unsimulated/dark_filler, @@ -65,7 +65,7 @@ /area/shaded_hills/caves/swamp) "pl" = ( /obj/structure/drying_rack/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "rP" = ( /obj/structure/reagent_dispensers/barrel/ebony/water, @@ -86,7 +86,7 @@ start_lit = 1 }, /obj/structure/table/end/alt/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "vZ" = ( /turf/floor/mud/water, @@ -110,7 +110,7 @@ /obj/structure/door/walnut{ dir = 4 }, -/turf/floor/path/basalt, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "BY" = ( /obj/machinery/portable_atmospherics/hydroponics/soil, @@ -118,7 +118,7 @@ /area/shaded_hills/outside/swamp) "Dh" = ( /obj/item/stool/rustic, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "Do" = ( /turf/unsimulated/dark_filler, @@ -135,14 +135,14 @@ /obj/item/chems/glass/handmade/cup/wood, /obj/item/chems/glass/handmade/cup/wood, /obj/item/chems/glass/bucket/wood, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "Ic" = ( /turf/floor/clay, /area/shaded_hills/outside/river/swamp) "KA" = ( /obj/abstract/landmark/start/shaded_hills/herbalist, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "LF" = ( /turf/wall/natural/basalt/shaded_hills, @@ -156,7 +156,7 @@ /area/shaded_hills/outside/swamp) "SE" = ( /obj/structure/door/walnut, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "Ty" = ( /obj/abstract/landmark/start/shaded_hills/traveller, @@ -172,7 +172,7 @@ /obj/structure/table/desk/ebony, /obj/item/chems/glass/mortar, /obj/item/rock/basalt, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "Vl" = ( /obj/structure/wall_sconce/lantern{ @@ -180,7 +180,7 @@ start_lit = 1 }, /obj/structure/closet/crate/chest/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) "VA" = ( /turf/wall/brick/basalt, @@ -198,7 +198,7 @@ /obj/structure/bed/simple/ebony, /obj/item/bedsheet/furs, /obj/abstract/landmark/start/shaded_hills/herbalist, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/witch_hut) (1,1,1) = {" diff --git a/maps/shaded_hills/shaded_hills-woods.dmm b/maps/shaded_hills/shaded_hills-woods.dmm index 569bfe9a2d1e..66168daa63f0 100644 --- a/maps/shaded_hills/shaded_hills-woods.dmm +++ b/maps/shaded_hills/shaded_hills-woods.dmm @@ -9,7 +9,7 @@ /area/shaded_hills/outside/woods) "cN" = ( /obj/item/bladed/knife, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "dp" = ( /turf/wall/log/walnut, @@ -41,7 +41,7 @@ /turf/wall/natural/basalt/shaded_hills, /area/shaded_hills/caves/river/woods) "lb" = ( -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "lC" = ( /turf/unsimulated/dark_filler, @@ -54,7 +54,7 @@ /area/shaded_hills/outside/river/lake) "mo" = ( /obj/structure/door/walnut, -/turf/floor/path/basalt, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "nl" = ( /turf/wall/natural/basalt/shaded_hills, @@ -80,7 +80,7 @@ "wD" = ( /obj/item/fishing_rod, /obj/structure/table/end/alt/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "xn" = ( /turf/unsimulated/mask, @@ -99,7 +99,7 @@ /area/shaded_hills/caves/unexplored/woods) "AN" = ( /obj/structure/railing/mapped/wooden/walnut, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/river/woods) "Co" = ( /obj/abstract/landmark/start/shaded_hills/traveller/learned, @@ -124,7 +124,7 @@ "Fz" = ( /obj/structure/fire_source/stove, /obj/item/stack/material/log/mapped/walnut/ten, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "GB" = ( /turf/floor/carpet/rustic, @@ -139,17 +139,17 @@ /obj/structure/bed/simple/ebony, /obj/item/bedsheet/furs, /obj/abstract/landmark/start/shaded_hills/forester, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "JI" = ( /obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/river/woods) "JJ" = ( /obj/structure/meat_hook, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "LJ" = ( /turf/floor/path/running_bond/basalt, @@ -159,7 +159,7 @@ /obj/item/stack/material/bundle/grass/dry{ amount = 5 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "My" = ( /turf/floor/grass, @@ -168,14 +168,14 @@ /obj/structure/wall_sconce/lantern{ start_lit = 1 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "MU" = ( /turf/wall/log/walnut/shutter/open, /area/shaded_hills/forester_hut) "Oi" = ( /obj/structure/closet/crate/chest/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "PO" = ( /turf/floor/mud/water/deep, @@ -185,7 +185,7 @@ /area/shaded_hills/forester_hut) "TP" = ( /obj/structure/table/desk/dresser/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "Uz" = ( /obj/effect/departure_signpost/north, diff --git a/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm b/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm index d5bf78ffb075..fa4f65d3fd71 100644 --- a/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm +++ b/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm @@ -4,7 +4,7 @@ /obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "h" = ( /obj/abstract/exterior_marker/inside, @@ -40,12 +40,12 @@ /obj/item/chems/cooking_vessel/pot/iron, /obj/item/chems/glass/handmade/teapot, /obj/abstract/landmark/organize/vertical, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "m" = ( /obj/abstract/exterior_marker/inside, /obj/structure/reagent_dispensers/barrel/ebony/oil, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "n" = ( /obj/structure/wall_sconce/lantern{ @@ -59,7 +59,7 @@ /obj/structure/table/wood/ebony, /obj/item/chems/glass/mortar, /obj/item/rock/basalt, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "q" = ( /obj/structure/drying_rack/ebony, @@ -80,7 +80,7 @@ "C" = ( /obj/abstract/exterior_marker/inside, /obj/structure/door/walnut, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "F" = ( /obj/abstract/exterior_marker/inside, @@ -93,17 +93,17 @@ "I" = ( /obj/abstract/exterior_marker/inside, /obj/structure/reagent_dispensers/barrel/ebony/water, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "J" = ( /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "K" = ( /obj/abstract/exterior_marker/inside, /obj/structure/bed/simple/ebony/cloth, /obj/item/bedsheet/furs, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "L" = ( /obj/abstract/exterior_marker/inside, @@ -114,12 +114,12 @@ /obj/item/food/grown/carrot, /obj/item/food/grown/carrot, /obj/item/kitchen/rollingpin, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "M" = ( /obj/abstract/exterior_marker/inside, /obj/structure/reagent_dispensers/barrel/ebony/wine, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "N" = ( /obj/abstract/exterior_marker/inside, @@ -129,7 +129,7 @@ /obj/abstract/exterior_marker/inside, /obj/structure/closet/crate/chest/ebony, /obj/random/jewelry, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) "U" = ( /obj/abstract/exterior_marker/inside, @@ -160,7 +160,7 @@ "Y" = ( /obj/abstract/exterior_marker/inside, /obj/structure/fire_source/stove, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/chemistry_shack) (1,1,1) = {" diff --git a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm b/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm index e6b329c0e900..84ac9a1af5e2 100644 --- a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm +++ b/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm @@ -2,12 +2,12 @@ "a" = ( /obj/structure/door/walnut, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "g" = ( /obj/abstract/exterior_marker/inside, /obj/structure/table/end, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "h" = ( /turf/template_noop, @@ -18,17 +18,17 @@ "k" = ( /obj/structure/closet/crate/chest/ebony, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "o" = ( /obj/structure/drying_rack/ebony, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "u" = ( /obj/structure/reagent_dispensers/barrel/ebony/beer, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "x" = ( /obj/structure/table/wood/ebony, @@ -37,7 +37,7 @@ /obj/item/rock/flint, /obj/item/rock/hematite, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "z" = ( /obj/effect/spider/spiderling/mundane, @@ -47,19 +47,19 @@ pixel_y = 24 }, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "B" = ( /obj/effect/decal/cleanable/blood, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "D" = ( /obj/effect/spider/spiderling/mundane, /obj/structure/table/wood/ebony, /obj/item/food/grown/carrot, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "G" = ( /obj/abstract/exterior_marker/inside, @@ -72,16 +72,16 @@ /obj/item/food/grown/potato, /obj/item/food/grown/carrot, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "I" = ( /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "L" = ( /obj/effect/spider/spiderling/mundane, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "M" = ( /obj/abstract/exterior_marker/inside, @@ -90,12 +90,12 @@ "N" = ( /obj/abstract/exterior_marker/inside, /obj/structure/coatrack/ebony, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "T" = ( /obj/abstract/exterior_marker/inside, /obj/structure/table/desk/dresser, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "W" = ( /obj/structure/wall_sconce/torch{ @@ -111,7 +111,7 @@ /obj/effect/decal/cleanable/blood, /obj/random/jewelry, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/walnut, +/turf/floor/wood/rough/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) (1,1,1) = {" From 39d4960b3d50459196803c21cc99ce2c181e7aca Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 18 Dec 2024 16:16:44 +1100 Subject: [PATCH 0091/1331] Moving standard SS13 jobs and departments into a modpack. --- maps/exodus/exodus.dm | 22 +-- maps/exodus/exodus_departments.dm | 98 -------------- .../{jobs/_goals.dm => exodus_goals.dm} | 0 maps/ministation/jobs/civilian.dm | 67 +-------- maps/ministation/jobs/command.dm | 128 +----------------- maps/ministation/jobs/engineering.dm | 55 +------- maps/ministation/jobs/medical.dm | 26 +--- maps/ministation/jobs/science.dm | 53 +------- maps/ministation/jobs/security.dm | 49 +------ maps/ministation/jobs/synthetics.dm | 79 ----------- maps/ministation/jobs/tradehouse.dm | 2 +- maps/ministation/ministation.dm | 2 +- maps/ministation/ministation_departments.dm | 74 +--------- maps/ministation/ministation_jobs.dm | 38 +++--- maps/tradeship/jobs/_goals.dm | 5 +- maps/tradeship/jobs/civilian.dm | 17 +-- maps/tradeship/jobs/command.dm | 25 +--- maps/tradeship/jobs/engineering.dm | 120 +--------------- maps/tradeship/jobs/medical.dm | 65 +-------- maps/tradeship/jobs/science.dm | 111 +-------------- maps/tradeship/jobs/synthetics.dm | 31 ----- maps/tradeship/tradeship.dm | 3 +- maps/tradeship/tradeship_jobs.dm | 22 +-- maps/tradeship/tradeship_spawnpoints.dm | 4 +- mods/content/standard_jobs/_standard_jobs.dm | 2 + mods/content/standard_jobs/_standard_jobs.dme | 30 ++++ .../standard_jobs/departments/civilian.dm | 4 + .../standard_jobs/departments/command.dm | 14 ++ .../standard_jobs/departments/engineering.dm | 12 ++ .../standard_jobs/departments/medical.dm | 13 ++ .../departments/miscellaneous.dm | 4 + .../standard_jobs/departments/science.dm | 18 +++ .../standard_jobs/departments/security.dm | 12 ++ .../standard_jobs/departments/service.dm | 6 + .../standard_jobs/departments/supply.dm | 8 ++ .../content/standard_jobs}/jobs/captain.dm | 0 .../content/standard_jobs}/jobs/civilian.dm | 5 + .../standard_jobs}/jobs/engineering.dm | 0 .../content/standard_jobs}/jobs/medical.dm | 3 +- .../content/standard_jobs}/jobs/science.dm | 0 .../content/standard_jobs}/jobs/security.dm | 0 .../content/standard_jobs}/jobs/synthetics.dm | 0 .../content/standard_jobs}/outfits/cargo.dm | 0 .../standard_jobs}/outfits/civilian.dm | 0 .../content/standard_jobs}/outfits/command.dm | 0 .../standard_jobs}/outfits/engineering.dm | 0 .../content/standard_jobs}/outfits/medical.dm | 0 .../content/standard_jobs/outfits/pda.dm | 0 .../content/standard_jobs}/outfits/science.dm | 0 .../standard_jobs}/outfits/security.dm | 0 50 files changed, 206 insertions(+), 1021 deletions(-) delete mode 100644 maps/exodus/exodus_departments.dm rename maps/exodus/{jobs/_goals.dm => exodus_goals.dm} (100%) delete mode 100644 maps/ministation/jobs/synthetics.dm delete mode 100644 maps/tradeship/jobs/synthetics.dm create mode 100644 mods/content/standard_jobs/_standard_jobs.dm create mode 100644 mods/content/standard_jobs/_standard_jobs.dme create mode 100644 mods/content/standard_jobs/departments/civilian.dm create mode 100644 mods/content/standard_jobs/departments/command.dm create mode 100644 mods/content/standard_jobs/departments/engineering.dm create mode 100644 mods/content/standard_jobs/departments/medical.dm create mode 100644 mods/content/standard_jobs/departments/miscellaneous.dm create mode 100644 mods/content/standard_jobs/departments/science.dm create mode 100644 mods/content/standard_jobs/departments/security.dm create mode 100644 mods/content/standard_jobs/departments/service.dm create mode 100644 mods/content/standard_jobs/departments/supply.dm rename {maps/exodus => mods/content/standard_jobs}/jobs/captain.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/jobs/civilian.dm (98%) rename {maps/exodus => mods/content/standard_jobs}/jobs/engineering.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/jobs/medical.dm (99%) rename {maps/exodus => mods/content/standard_jobs}/jobs/science.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/jobs/security.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/jobs/synthetics.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/outfits/cargo.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/outfits/civilian.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/outfits/command.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/outfits/engineering.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/outfits/medical.dm (100%) rename maps/exodus/outfits/_pda.dm => mods/content/standard_jobs/outfits/pda.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/outfits/science.dm (100%) rename {maps/exodus => mods/content/standard_jobs}/outfits/security.dm (100%) diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index a8aee1db817e..9372a9368a76 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -25,6 +25,9 @@ // Must come after borers for compatibility. #include "../../mods/content/psionics/_psionics.dme" + // Must come after psionics for counsellor compatibility. + #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/species/ascent/_ascent.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/species/serpentid/_serpentid.dme" @@ -49,30 +52,13 @@ #include "../away/unishi/unishi.dm" #include "../away/yacht/yacht.dm" - #include "jobs/_goals.dm" - #include "jobs/captain.dm" - #include "jobs/civilian.dm" - #include "jobs/engineering.dm" - #include "jobs/medical.dm" - #include "jobs/science.dm" - #include "jobs/security.dm" - #include "jobs/synthetics.dm" - - #include "outfits/_pda.dm" - #include "outfits/cargo.dm" - #include "outfits/civilian.dm" - #include "outfits/command.dm" - #include "outfits/engineering.dm" - #include "outfits/medical.dm" - #include "outfits/science.dm" - #include "outfits/security.dm" + #include "exodus_goals.dm" #include "exodus_announcements.dm" #include "exodus_antagonism.dm" #include "exodus_cameras.dm" #include "exodus_areas.dm" #include "exodus_elevator.dm" - #include "exodus_departments.dm" #include "exodus_jobs.dm" #include "exodus_loadout.dm" #include "exodus_overmap.dm" diff --git a/maps/exodus/exodus_departments.dm b/maps/exodus/exodus_departments.dm deleted file mode 100644 index 3a76c9b958ff..000000000000 --- a/maps/exodus/exodus_departments.dm +++ /dev/null @@ -1,98 +0,0 @@ -/decl/department/engineering - name = "Engineering" - announce_channel = "Engineering" - colour = "#ffa500" - display_priority = 4 - display_color = "#fff5cc" - -/obj/item/robot_module/engineering - associated_department = /decl/department/engineering - -/obj/machinery/network/pager/engineering - department = /decl/department/engineering - -/decl/department/security - name = "Security" - announce_channel = "Security" - colour = "#dd0000" - display_priority = 3 - display_color = "#ffddf0" - -/obj/item/robot_module/security - associated_department = /decl/department/security - -/obj/machinery/network/pager/security - department = /decl/department/security - -/decl/department/medical - name = "Medical" - goals = list(/datum/goal/department/medical_fatalities) - announce_channel = "Medical" - colour = "#008000" - display_priority = 2 - display_color = "#ffeef0" - -/obj/item/robot_module/medical - associated_department = /decl/department/medical - -/obj/machinery/network/pager/medical - department = /decl/department/medical - -/decl/department/science - name = "Science" - goals = list(/datum/goal/department/extract_slime_cores) - announce_channel = "Science" - colour = "#a65ba6" - display_color = "#e79fff" - -/obj/item/robot_module/research - associated_department = /decl/department/science - -/obj/machinery/network/pager/science - department = /decl/department/science - -/decl/department/civilian - name = "Civilian" - display_priority = 1 - display_color = "#dddddd" - -/decl/department/command - name = "Command" - colour = "#800080" - display_priority = 5 - display_color = "#ccccff" - -/obj/machinery/network/pager - department = /decl/department/command - -/decl/department/miscellaneous - name = "Misc" - display_priority = -1 - display_color = "#ccffcc" - -/decl/department/service - name = "Service" - announce_channel = "Service" - colour = "#88b764" - display_color = "#d0f0c0" - -/decl/department/supply - name = "Supply" - announce_channel = "Supply" - colour = "#bb9040" - display_color = "#f0e68c" - -/obj/machinery/network/pager/cargo - department = /decl/department/supply - -/decl/department/support - name = "Support" - announce_channel = "Command" - colour = "#800080" - display_color = "#87ceeb" - -/decl/department/exploration - name = "Exploration" - announce_channel = "Exploration" - colour = "#68099e" - display_color = "#b784a7" diff --git a/maps/exodus/jobs/_goals.dm b/maps/exodus/exodus_goals.dm similarity index 100% rename from maps/exodus/jobs/_goals.dm rename to maps/exodus/exodus_goals.dm diff --git a/maps/ministation/jobs/civilian.dm b/maps/ministation/jobs/civilian.dm index 432d3455d219..3d1601530d6b 100644 --- a/maps/ministation/jobs/civilian.dm +++ b/maps/ministation/jobs/civilian.dm @@ -1,26 +1,15 @@ -/datum/job/ministation/assistant +/datum/job/assistant/ministation title = "Recruit" - total_positions = -1 - spawn_positions = -1 supervisors = "absolutely everyone" - economic_power = 1 - access = list() - minimal_access = list() - hud_icon = "hudassistant" alt_titles = list("Technical Recruit","Medical Recruit","Research Recruit","Visitor") outfit_type = /decl/outfit/job/ministation_assistant - department_types = list(/decl/department/civilian) event_categories = list(ASSIGNMENT_GARDENER) - -/datum/job/ministation/assistant/get_access() - if(get_config_value(/decl/config/toggle/assistant_maint)) - return list(access_maint_tunnels) - return list() + hud_icon = "hudassistant" /decl/outfit/job/ministation_assistant name = "Job - Ministation Assistant" -/datum/job/ministation/bartender +/datum/job/bartender/ministation title = "Bartender" alt_titles = list("Cook","Barista") supervisors = "the Lieutenant and the Captain" @@ -30,16 +19,6 @@ department_types = list(/decl/department/service) selection_color = "#3fbe4a" economic_power = 5 - access = list( - access_hydroponics, - access_bar, - access_kitchen - ) - minimal_access = list( - access_hydroponics, - access_bar, - access_kitchen - ) min_skill = list( SKILL_COOKING = SKILL_ADEPT, SKILL_BOTANY = SKILL_BASIC, @@ -51,14 +30,13 @@ ) skill_points = 30 -/datum/job/ministation/cargo +/datum/job/cargo_tech/ministation title = "Cargo Technician" alt_titles = list("Shaft Miner","Drill Technician","Prospector") supervisors = "the Lieutenant and the Captain" total_positions = 3 spawn_positions = 1 outfit_type = /decl/outfit/job/ministation/cargo - department_types = list(/decl/department/service) selection_color = "#8a7c00" economic_power = 5 access = list( @@ -94,57 +72,24 @@ SKILL_FINANCE = SKILL_MAX ) skill_points = 30 - software_on_spawn = list( - /datum/computer_file/program/supply, - /datum/computer_file/program/deck_management, - /datum/computer_file/program/reports - ) -/datum/job/ministation/janitor - title = "Janitor" - event_categories = list(ASSIGNMENT_JANITOR) - department_types = list(/decl/department/service) +/datum/job/janitor/ministation total_positions = 2 - spawn_positions = 1 supervisors = "the Lieutenant and the Captain" economic_power = 3 selection_color = "#940088" - access = list( - access_janitor, - access_maint_tunnels, - access_engine, - access_research, - access_sec_doors, - access_medical - ) - minimal_access = list( - access_janitor, - access_maint_tunnels, - access_engine, - access_research, - access_sec_doors, - access_medical - ) - alt_titles = list( - "Custodian", - "Sanitation Technician" - ) outfit_type = /decl/outfit/job/ministation/janitor min_skill = list( SKILL_HAULING = SKILL_BASIC ) skill_points = 28 -/datum/job/ministation/librarian - title = "Librarian" - department_types = list(/decl/department/service) - total_positions = 1 +/datum/job/librarian/ministation spawn_positions = 2 supervisors = "the Lieutenant, the Captain, and the smell of old paper" economic_power = 5 selection_color = "#008800" access = list(access_library) - minimal_access = list(access_library) alt_titles = list( "Curator", "Archivist" diff --git a/maps/ministation/jobs/command.dm b/maps/ministation/jobs/command.dm index 7424f7724a5c..a823a716095c 100644 --- a/maps/ministation/jobs/command.dm +++ b/maps/ministation/jobs/command.dm @@ -1,5 +1,4 @@ -/datum/job/ministation/captain - title = "Captain" +/datum/job/captain/ministation supervisors = "your profit margin, your conscience, and the watchful eye of the Tradehouse Rep" outfit_type = /decl/outfit/job/ministation/captain min_skill = list( @@ -13,30 +12,12 @@ SKILL_WEAPONS = SKILL_MAX ) skill_points = 40 - head_position = 1 - department_types = list(/decl/department/command) - total_positions = 1 - spawn_positions = 1 - selection_color = "#1d1d4f" - hud_icon = "hudcaptain" - req_admin_notify = 1 - access = list() - minimal_access = list() - minimal_player_age = 14 - economic_power = 20 - ideal_character_age = 70 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 -/datum/job/ministation/captain/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) +/datum/job/captain/ministation/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) . = ..() if(H) H.verbs |= /mob/proc/freetradeunion_rename_company -/datum/job/ministation/captain/get_access() - return get_all_station_access() - /mob/proc/freetradeunion_rename_company() set name = "Defect from Tradehouse" set category = "Captain's Powers" @@ -52,113 +33,10 @@ command_announcement.Announce("Congratulations to all members of [capitalize(global.using_map.company_name)] on the new name. Their rebranding has changed the [global.using_map.company_short] market value by [0.01*rand(-10,10)]%.", "Trade Union Name Change") verbs -= /mob/proc/freetradeunion_rename_company -/datum/job/ministation/hop +/datum/job/hop/ministation title = "Lieutenant" - supervisors = "the Captain" outfit_type = /decl/outfit/job/ministation/hop - head_position = 1 - department_types = list( - /decl/department/command, - /decl/department/civilian - ) - total_positions = 1 - spawn_positions = 1 - selection_color = "#2f2f7f" hud_icon = "hudlieutenant" - req_admin_notify = 1 - minimal_player_age = 14 - economic_power = 10 - ideal_character_age = 50 - guestbanned = 1 - not_random_selectable = 1 - access = list( - access_security, - access_sec_doors, - access_brig, - access_forensics_lockers, - access_armory, - access_heads, - access_medical, - access_engine, - access_atmospherics, - access_change_ids, - access_ai_upload, - access_eva, - access_bridge, - access_all_personal_lockers, - access_maint_tunnels, - access_bar, - access_janitor, - access_construction, - access_morgue, - access_crematorium, - access_kitchen, - access_mining, - access_xenobiology, - access_robotics, - access_engine_equip, - access_cargo, - access_cargo_bot, - access_mailsorting, - access_qm, - access_hydroponics, - access_lawyer, - access_chapel_office, - access_library, - access_research, - access_mining, - access_heads_vault, - access_mining_station, - access_hop, - access_RC_announce, - access_keycard_auth, - access_gateway, - access_cameras - ) - minimal_access = list( - access_security, - access_sec_doors, - access_brig, - access_forensics_lockers, - access_armory, - access_heads, - access_medical, - access_engine, - access_atmospherics, - access_change_ids, - access_ai_upload, - access_eva, - access_bridge, - access_all_personal_lockers, - access_maint_tunnels, - access_bar, - access_janitor, - access_construction, - access_mining, - access_xenobiology, - access_robotics, - access_engine_equip, - access_morgue, - access_crematorium, - access_kitchen, - access_cargo, - access_cargo_bot, - access_mailsorting, - access_qm, - access_hydroponics, - access_lawyer, - access_chapel_office, - access_library, - access_research, - access_mining, - access_heads_vault, - access_mining_station, - access_hop, - access_RC_announce, - access_keycard_auth, - access_gateway, - access_cameras - ) min_skill = list( SKILL_LITERACY = SKILL_ADEPT, SKILL_WEAPONS = SKILL_BASIC, diff --git a/maps/ministation/jobs/engineering.dm b/maps/ministation/jobs/engineering.dm index b3d12fd6afa5..d780f2353d28 100644 --- a/maps/ministation/jobs/engineering.dm +++ b/maps/ministation/jobs/engineering.dm @@ -1,12 +1,9 @@ -/datum/job/ministation/engineer +/datum/job/engineer/ministation title = "Station Engineer" supervisors = "the Head Engineer" total_positions = 2 spawn_positions = 2 outfit_type = /decl/outfit/job/ministation/engineer - department_types = list(/decl/department/engineering) - selection_color = "#5b4d20" - economic_power = 5 minimal_player_age = 3 access = list( access_eva, @@ -32,41 +29,11 @@ access_emergency_storage, access_cameras ) - min_skill = list( - SKILL_LITERACY = SKILL_ADEPT, - SKILL_COMPUTER = SKILL_BASIC, - SKILL_EVA = SKILL_BASIC, - SKILL_CONSTRUCTION = SKILL_ADEPT, - SKILL_ELECTRICAL = SKILL_BASIC, - SKILL_ATMOS = SKILL_BASIC, - SKILL_ENGINES = SKILL_BASIC - ) - max_skill = list( - SKILL_CONSTRUCTION = SKILL_MAX, - SKILL_ELECTRICAL = SKILL_MAX, - SKILL_ATMOS = SKILL_MAX, - SKILL_ENGINES = SKILL_MAX - ) skill_points = 30 alt_titles = list("Atmospheric Technician", "Electrician", "Maintenance Technician") - event_categories = list(ASSIGNMENT_ENGINEER) -/datum/job/ministation/engineer/head +/datum/job/chief_engineer/ministation title = "Head Engineer" - head_position = 1 - department_types = list( - /decl/department/engineering, - /decl/department/command - ) - total_positions = 1 - spawn_positions = 1 - selection_color = "#7f6e2c" - req_admin_notify = 1 - economic_power = 10 - ideal_character_age = 50 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 hud_icon = "hudchiefengineer" access = list( access_engine, @@ -116,24 +83,6 @@ access_ai_upload, access_cameras ) - minimal_player_age = 14 - supervisors = "the Captain" outfit_type = /decl/outfit/job/ministation/chief_engineer - min_skill = list( - SKILL_LITERACY = SKILL_ADEPT, - SKILL_COMPUTER = SKILL_ADEPT, - SKILL_EVA = SKILL_ADEPT, - SKILL_CONSTRUCTION = SKILL_ADEPT, - SKILL_ELECTRICAL = SKILL_ADEPT, - SKILL_ATMOS = SKILL_ADEPT, - SKILL_ENGINES = SKILL_EXPERT - ) - max_skill = list( - SKILL_CONSTRUCTION = SKILL_MAX, - SKILL_ELECTRICAL = SKILL_MAX, - SKILL_ATMOS = SKILL_MAX, - SKILL_ENGINES = SKILL_MAX - ) skill_points = 40 alt_titles = list("Chief of Engineering") - event_categories = list(ASSIGNMENT_ENGINEER) \ No newline at end of file diff --git a/maps/ministation/jobs/medical.dm b/maps/ministation/jobs/medical.dm index 00c298a4c01d..0e91a2d945b4 100644 --- a/maps/ministation/jobs/medical.dm +++ b/maps/ministation/jobs/medical.dm @@ -1,7 +1,5 @@ -/datum/job/ministation/doctor +/datum/job/doctor/ministation title = "Medical Doctor" - department_types = list(/decl/department/medical) - head_position = 0 supervisors = "the Head Doctor" total_positions = 2 spawn_positions = 2 @@ -18,8 +16,6 @@ SKILL_ANATOMY = SKILL_MAX, SKILL_CHEMISTRY = SKILL_MAX ) - selection_color = "#013d3b" - economic_power = 7 access = list( access_medical, access_medical_equip, @@ -38,28 +34,13 @@ access_cameras ) outfit_type = /decl/outfit/job/ministation/doctor - minimal_player_age = 3 - event_categories = list(ASSIGNMENT_MEDICAL) -/datum/job/ministation/doctor/head +/datum/job/cmo/ministation title = "Head Doctor" - head_position = 1 - department_types = list( - /decl/department/medical, - /decl/department/command - ) supervisors = "the Captain and your own ethics" outfit_type = /decl/outfit/job/ministation/doctor/head alt_titles = list("Chief Medical Officer", "Head Surgeon") - total_positions = 1 - spawn_positions = 1 skill_points = 38 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 - selection_color = "#026865" - req_admin_notify = 1 - economic_power = 10 hud_icon = "hudheaddoctor" access = list( access_medical, @@ -113,6 +94,3 @@ access_external_airlocks, access_cameras ) - minimal_player_age = 14 - ideal_character_age = 50 - event_categories = list(ASSIGNMENT_MEDICAL) \ No newline at end of file diff --git a/maps/ministation/jobs/science.dm b/maps/ministation/jobs/science.dm index f9bcb575468d..f2d80cbe65f1 100644 --- a/maps/ministation/jobs/science.dm +++ b/maps/ministation/jobs/science.dm @@ -1,49 +1,15 @@ -/datum/job/ministation/scientist +/datum/job/scientist/ministation title = "Researcher" alt_titles = list("Scientist","Xenobiologist","Roboticist","Xenobotanist") supervisors = "the Head Researcher" spawn_positions = 1 total_positions = 2 - department_types = list(/decl/department/science) outfit_type = /decl/outfit/job/ministation/scientist hud_icon = "hudscientist" - min_skill = list( - SKILL_LITERACY = SKILL_ADEPT, - SKILL_COMPUTER = SKILL_BASIC, - SKILL_DEVICES = SKILL_BASIC, - SKILL_SCIENCE = SKILL_ADEPT - ) - max_skill = list( - SKILL_ANATOMY = SKILL_MAX, - SKILL_DEVICES = SKILL_MAX, - SKILL_SCIENCE = SKILL_MAX - ) skill_points = 34 - access = list( - access_robotics, - access_tox, - access_tox_storage, - access_research, - access_xenobiology, - access_xenoarch - ) - minimal_access = list( - access_robotics, - access_tox, - access_tox_storage, - access_research, - access_xenobiology, - access_xenoarch - ) - selection_color = "#633d63" - economic_power = 7 - event_categories = list(ASSIGNMENT_SCIENTIST) -/datum/job/ministation/scientist/head +/datum/job/rd/ministation title = "Research Director" - supervisors = "the Captain" - spawn_positions = 1 - total_positions = 1 alt_titles = list("Head Researcher", "Chief Researcher") outfit_type = /decl/outfit/job/ministation/scientist/head min_skill = list( @@ -61,14 +27,6 @@ SKILL_SCIENCE = SKILL_MAX ) skill_points = 40 - head_position = 1 - department_types = list( - /decl/department/science, - /decl/department/command - ) - selection_color = "#ad6bad" - req_admin_notify = 1 - economic_power = 15 hud_icon = "hudheadscientist" access = list( access_rd, @@ -124,10 +82,3 @@ access_network, access_cameras ) - minimal_player_age = 14 - ideal_character_age = 50 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 - event_categories = list(ASSIGNMENT_SCIENTIST) - diff --git a/maps/ministation/jobs/security.dm b/maps/ministation/jobs/security.dm index ac68182cff2b..55ea8e841e2f 100644 --- a/maps/ministation/jobs/security.dm +++ b/maps/ministation/jobs/security.dm @@ -1,14 +1,10 @@ -/datum/job/ministation/security +/datum/job/officer/ministation title = "Security Officer" alt_titles = list("Warden") - supervisors = "the Head of Security" spawn_positions = 1 total_positions = 2 outfit_type = /decl/outfit/job/ministation/security - department_types = list(/decl/department/security) - selection_color = "#990000" economic_power = 7 - minimal_player_age = 7 access = list( access_security, access_brig, @@ -24,28 +20,14 @@ access_brig, access_cameras ) - min_skill = list( - SKILL_LITERACY = SKILL_BASIC, - SKILL_COMPUTER = SKILL_BASIC, - SKILL_COMBAT = SKILL_BASIC, - SKILL_WEAPONS = SKILL_BASIC - ) - max_skill = list( - SKILL_COMBAT = SKILL_MAX, - SKILL_WEAPONS = SKILL_MAX - ) skill_points = 30 - event_categories = list(ASSIGNMENT_SECURITY) -/datum/job/ministation/detective - title = "Detective" +/datum/job/detective/ministation alt_titles = list("Inspector") supervisors = "Justice... and the Trademaster" spawn_positions = 1 total_positions = 1 outfit_type = /decl/outfit/job/ministation/detective - department_types = list(/decl/department/security) - selection_color = "#630000" economic_power = 7 minimal_player_age = 3 access = list( @@ -71,31 +53,10 @@ SKILL_WEAPONS = SKILL_BASIC, SKILL_FORENSICS = SKILL_ADEPT ) - max_skill = list( - SKILL_COMBAT = SKILL_MAX, - SKILL_WEAPONS = SKILL_MAX, - SKILL_FORENSICS = SKILL_MAX - ) skill_points = 34 -/datum/job/ministation/security/head - title = "Head of Security" - supervisors = "the Captain" +/datum/job/hos/ministation outfit_type = /decl/outfit/job/ministation/security/head - head_position = 1 - department_types = list( - /decl/department/security, - /decl/department/command - ) - total_positions = 1 - spawn_positions = 1 - selection_color = "#9d2300" - req_admin_notify = 1 - minimal_player_age = 14 - economic_power = 10 - ideal_character_age = 50 - guestbanned = 1 - not_random_selectable = 1 hud_icon = "hudhos" access = list( access_security, @@ -139,9 +100,5 @@ SKILL_COMBAT = SKILL_ADEPT, SKILL_WEAPONS = SKILL_ADEPT ) - max_skill = list( - SKILL_COMBAT = SKILL_MAX, - SKILL_WEAPONS = SKILL_MAX - ) skill_points = 40 alt_titles = list("Security Commander") diff --git a/maps/ministation/jobs/synthetics.dm b/maps/ministation/jobs/synthetics.dm deleted file mode 100644 index 1aea5738c902..000000000000 --- a/maps/ministation/jobs/synthetics.dm +++ /dev/null @@ -1,79 +0,0 @@ -/datum/job/ministation/robot - title = "Robot" - event_categories = list(ASSIGNMENT_ROBOT) - total_positions = 1 - spawn_positions = 1 - supervisors = "your laws, the Lieutenant, and the Captain" - selection_color = "#254c25" - minimal_player_age = 7 - account_allowed = 0 - economic_power = 0 - loadout_allowed = FALSE - outfit_type = /decl/outfit/job/silicon/cyborg - hud_icon = "hudblank" - skill_points = 0 - no_skill_buffs = TRUE - guestbanned = 1 - not_random_selectable = 1 - skip_loadout_preview = TRUE - department_types = list(/decl/department/miscellaneous) - -/datum/job/ministation/robot/handle_variant_join(var/mob/living/human/H, var/alt_title) - if(H) - return H.Robotize(SSrobots.get_mob_type_by_title(alt_title || title)) - -/datum/job/ministation/robot/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) - return !!H - -/datum/job/ministation/robot/New() - ..() - alt_titles = SSrobots.robot_alt_titles.Copy() - alt_titles -= title - -/datum/job/computer - title = "Computer" - event_categories = list(ASSIGNMENT_COMPUTER) - total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm - spawn_positions = 1 - selection_color = "#3f823f" - supervisors = "your laws" - req_admin_notify = 1 - minimal_player_age = 7 - account_allowed = 0 - economic_power = 0 - outfit_type = /decl/outfit/job/silicon/ai - loadout_allowed = FALSE - hud_icon = "hudblank" - skill_points = 0 - no_skill_buffs = TRUE - guestbanned = 1 - not_random_selectable = 1 - skip_loadout_preview = TRUE - department_types = list(/decl/department/miscellaneous) - -/datum/job/computer/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) - return !!H - -/datum/job/computer/is_position_available() - return (empty_playable_ai_cores.len != 0) - -/datum/job/computer/handle_variant_join(var/mob/living/human/H, var/alt_title) - return H - -/datum/job/computer/do_spawn_special(var/mob/living/character, var/mob/new_player/new_player_mob, var/latejoin) - character = character.AIize(move = FALSE) - - // is_available for AI checks that there is an empty core available in this list - var/obj/structure/aicore/deactivated/C = empty_playable_ai_cores[1] - empty_playable_ai_cores -= C - - character.forceMove(C.loc) - var/mob/living/silicon/ai/A = character - A.on_mob_init() - - if(latejoin) - new_player_mob.AnnounceCyborg(character, title, "has been downloaded to the empty core in \the [get_area_name(src)]") - SSticker.mode.handle_latejoin(character) - - qdel(C) - return TRUE diff --git a/maps/ministation/jobs/tradehouse.dm b/maps/ministation/jobs/tradehouse.dm index ed057b4fb202..1e167615cf89 100644 --- a/maps/ministation/jobs/tradehouse.dm +++ b/maps/ministation/jobs/tradehouse.dm @@ -1,4 +1,4 @@ -/datum/job/ministation/tradehouse/rep +/datum/job/tradehouse_rep title = "Tradehouse Representative" alt_titles = list("Narc") hud_icon = "hudnarc" diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index 84b2105a5018..2ecca91fa60e 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -25,6 +25,7 @@ Twice... #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/content/corporate/_corporate.dme" #include "../../mods/content/matchmaking/_matchmaking.dme" @@ -84,7 +85,6 @@ Twice... #include "jobs/security.dm" #include "jobs/science.dm" #include "jobs/tradehouse.dm" - #include "jobs/synthetics.dm" #include "outfits/_outfits.dm" #include "outfits/command.dm" diff --git a/maps/ministation/ministation_departments.dm b/maps/ministation/ministation_departments.dm index 06aa658ad794..206042093ee9 100644 --- a/maps/ministation/ministation_departments.dm +++ b/maps/ministation/ministation_departments.dm @@ -1,78 +1,6 @@ -/decl/department/service - name = "Service" - announce_channel = "Service" - colour = "#88b764" - display_color = "#d0f0c0" - -/decl/department/command - name = "Command" - colour = "#800080" - display_priority = 5 - display_color = "#ccccff" - -/obj/machinery/network/pager - department = /decl/department/command - -/decl/department/civilian - name = "Civilian" - display_priority = 1 - display_color = "#dddddd" - -/decl/department/engineering - name = "Engineering" - announce_channel = "Engineering" - colour = "#ffa500" - display_priority = 2 - display_color = "#fff5cc" - -/decl/department/medical - name = "Medical" - goals = list(/datum/goal/department/medical_fatalities) - announce_channel = "Medical" - colour = "#008000" - display_priority = 3 - display_color = "#ffeef0" - -/obj/item/robot_module/medical - associated_department = /decl/department/medical - -/obj/machinery/network/pager/medical - department = /decl/department/medical - -/decl/department/science - name = "Science" - goals = list(/datum/goal/department/extract_slime_cores) - announce_channel = "Science" - colour = "#a65ba6" - display_color = "#e79fff" - -/obj/item/robot_module/research - associated_department = /decl/department/science - -/obj/machinery/network/pager/science - department = /decl/department/science - -/decl/department/security - name = "Security" - announce_channel = "Security" - colour = "#dd0000" - display_priority = 4 - display_color = "#ffddf0" - -/obj/item/robot_module/security - associated_department = /decl/department/security - -/obj/machinery/network/pager/security - department = /decl/department/security - -/decl/department/miscellaneous - name = "Misc" - display_priority = -1 - display_color = "#ccffcc" - /decl/department/tradehouse name = "Tradehouse" announce_channel = "Tradehouse" colour = "#b98f03" display_priority = 4 - display_color = "#ffddf0" \ No newline at end of file + display_color = "#ffddf0" diff --git a/maps/ministation/ministation_jobs.dm b/maps/ministation/ministation_jobs.dm index b8db846a4358..92e28630c1f1 100644 --- a/maps/ministation/ministation_jobs.dm +++ b/maps/ministation/ministation_jobs.dm @@ -1,25 +1,25 @@ /datum/map/ministation - default_job_type = /datum/job/ministation/assistant + default_job_type = /datum/job/assistant/ministation default_department_type = /decl/department/civilian id_hud_icons = 'maps/ministation/hud.dmi' allowed_jobs = list( - /datum/job/ministation/assistant, - /datum/job/ministation/bartender, - /datum/job/ministation/captain, - /datum/job/ministation/cargo, - /datum/job/ministation/robot, + /datum/job/assistant/ministation, + /datum/job/bartender/ministation, + /datum/job/captain/ministation, + /datum/job/cargo_tech/ministation, + /datum/job/robot, /datum/job/computer, - /datum/job/ministation/detective, - /datum/job/ministation/doctor, - /datum/job/ministation/doctor/head, - /datum/job/ministation/engineer, - /datum/job/ministation/engineer/head, - /datum/job/ministation/hop, - /datum/job/ministation/janitor, - /datum/job/ministation/scientist, - /datum/job/ministation/scientist/head, - /datum/job/ministation/security, - /datum/job/ministation/security/head, - /datum/job/ministation/librarian, - /datum/job/ministation/tradehouse/rep + /datum/job/detective/ministation, + /datum/job/doctor/ministation, + /datum/job/cmo/ministation, + /datum/job/engineer/ministation, + /datum/job/chief_engineer/ministation, + /datum/job/hop/ministation, + /datum/job/janitor/ministation, + /datum/job/scientist/ministation, + /datum/job/rd/ministation, + /datum/job/officer/ministation, + /datum/job/hos/ministation, + /datum/job/librarian/ministation, + /datum/job/tradehouse_rep ) \ No newline at end of file diff --git a/maps/tradeship/jobs/_goals.dm b/maps/tradeship/jobs/_goals.dm index c0c13ad937df..e9d719bd2116 100644 --- a/maps/tradeship/jobs/_goals.dm +++ b/maps/tradeship/jobs/_goals.dm @@ -25,7 +25,10 @@ var/global/list/tradeship_paperwork_end_areas = list() /datum/goal/department/paperwork/tradeship paperwork_types = list(/obj/item/paperwork/tradeship) - signatory_job_list = list(/datum/job/tradeship_captain, /datum/job/tradeship_first_mate) + signatory_job_list = list( + /datum/job/captain/tradeship, + /datum/job/tradeship_first_mate + ) /datum/goal/department/paperwork/tradeship/get_paper_spawn_turfs() return global.tradeship_paperwork_spawn_turfs diff --git a/maps/tradeship/jobs/civilian.dm b/maps/tradeship/jobs/civilian.dm index c15bb5c5a770..f491bc953a3c 100644 --- a/maps/tradeship/jobs/civilian.dm +++ b/maps/tradeship/jobs/civilian.dm @@ -1,21 +1,10 @@ -/datum/job/tradeship_deckhand +/datum/job/assistant/tradeship title = "Deck Hand" - total_positions = -1 - spawn_positions = -1 supervisors = "literally everyone, you bottom feeder" outfit_type = /decl/outfit/job/tradeship/hand alt_titles = list( "Cook" = /decl/outfit/job/tradeship/hand/cook, "Cargo Hand", - "Passenger") - department_types = list(/decl/department/civilian) - economic_power = 1 - access = list() - minimal_access = list() + "Passenger" + ) event_categories = list(ASSIGNMENT_GARDENER, ASSIGNMENT_JANITOR) - -/datum/job/tradeship_deckhand/get_access() - if(get_config_value(/decl/config/toggle/assistant_maint)) - return list(access_maint_tunnels) - else - return list() diff --git a/maps/tradeship/jobs/command.dm b/maps/tradeship/jobs/command.dm index a6d37fe665e0..0d124a9841e0 100644 --- a/maps/tradeship/jobs/command.dm +++ b/maps/tradeship/jobs/command.dm @@ -1,4 +1,4 @@ -/datum/job/tradeship_captain +/datum/job/captain/tradeship title = "Captain" supervisors = "your profit margin, your conscience, and the Trademaster" outfit_type = /decl/outfit/job/tradeship/captain @@ -8,36 +8,15 @@ SKILL_SCIENCE = SKILL_ADEPT, SKILL_PILOT = SKILL_ADEPT ) - max_skill = list( - SKILL_PILOT = SKILL_MAX, - SKILL_WEAPONS = SKILL_MAX - ) - skill_points = 30 - head_position = 1 - department_types = list(/decl/department/command) - total_positions = 1 - spawn_positions = 1 - selection_color = "#1d1d4f" - req_admin_notify = 1 - access = list() - minimal_access = list() - minimal_player_age = 14 - economic_power = 20 ideal_character_age = 70 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 forced_spawnpoint = /decl/spawnpoint/cryo/captain -/datum/job/tradeship_captain/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) +/datum/job/captain/tradeship/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) . = ..() if(H) H.verbs |= /mob/proc/tradehouse_rename_ship H.verbs |= /mob/proc/tradehouse_rename_company -/datum/job/tradeship_captain/get_access() - return get_all_station_access() - /mob/proc/tradehouse_rename_ship() set name = "Rename Tradeship" set category = "Captain's Powers" diff --git a/maps/tradeship/jobs/engineering.dm b/maps/tradeship/jobs/engineering.dm index 1586944c1bf9..1938f034a310 100644 --- a/maps/tradeship/jobs/engineering.dm +++ b/maps/tradeship/jobs/engineering.dm @@ -1,126 +1,10 @@ -/datum/job/tradeship_engineer +/datum/job/engineer/tradeship title = "Junior Engineer" supervisors = "the Head Engineer" outfit_type = /decl/outfit/job/tradeship/hand/engine - department_types = list(/decl/department/engineering) - total_positions = 8 - spawn_positions = 7 - selection_color = "#5b4d20" - economic_power = 5 - minimal_player_age = 7 - access = list( - access_eva, - access_engine, - access_engine_equip, - access_tech_storage, - access_maint_tunnels, - access_external_airlocks, - access_construction, - access_atmospherics, - access_emergency_storage - ) - minimal_access = list( - access_eva, - access_engine, - access_engine_equip, - access_tech_storage, - access_maint_tunnels, - access_external_airlocks, - access_construction, - access_atmospherics, - access_emergency_storage - ) - min_skill = list( - SKILL_LITERACY = SKILL_ADEPT, - SKILL_COMPUTER = SKILL_BASIC, - SKILL_EVA = SKILL_BASIC, - SKILL_CONSTRUCTION = SKILL_ADEPT, - SKILL_ELECTRICAL = SKILL_BASIC, - SKILL_ATMOS = SKILL_BASIC, - SKILL_ENGINES = SKILL_BASIC - ) - max_skill = list( - SKILL_CONSTRUCTION = SKILL_MAX, - SKILL_ELECTRICAL = SKILL_MAX, - SKILL_ATMOS = SKILL_MAX, - SKILL_ENGINES = SKILL_MAX - ) - skill_points = 20 alt_titles = list() - event_categories = list(ASSIGNMENT_ENGINEER) -/datum/job/tradeship_engineer/head +/datum/job/chief_engineer/tradeship title = "Head Engineer" - head_position = 1 - department_types = list( - /decl/department/engineering, - /decl/department/command - ) - total_positions = 1 - spawn_positions = 1 - selection_color = "#7f6e2c" - req_admin_notify = 1 - economic_power = 10 - ideal_character_age = 50 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 - access = list( - access_engine, - access_engine_equip, - access_tech_storage, - access_maint_tunnels, - access_heads, - access_teleporter, - access_external_airlocks, - access_atmospherics, - access_emergency_storage, - access_eva, - access_bridge, - access_construction, access_sec_doors, - access_ce, - access_RC_announce, - access_keycard_auth, - access_tcomsat, - access_ai_upload - ) - minimal_access = list( - access_engine, - access_engine_equip, - access_tech_storage, - access_maint_tunnels, - access_heads, - access_teleporter, - access_external_airlocks, - access_atmospherics, - access_emergency_storage, - access_eva, - access_bridge, - access_construction, - access_sec_doors, - access_ce, access_RC_announce, - access_keycard_auth, - access_tcomsat, - access_ai_upload - ) - minimal_player_age = 14 - supervisors = "the Captain" outfit_type = /decl/outfit/job/tradeship/chief_engineer - min_skill = list( - SKILL_LITERACY = SKILL_ADEPT, - SKILL_COMPUTER = SKILL_ADEPT, - SKILL_EVA = SKILL_ADEPT, - SKILL_CONSTRUCTION = SKILL_ADEPT, - SKILL_ELECTRICAL = SKILL_ADEPT, - SKILL_ATMOS = SKILL_ADEPT, - SKILL_ENGINES = SKILL_EXPERT - ) - max_skill = list( - SKILL_CONSTRUCTION = SKILL_MAX, - SKILL_ELECTRICAL = SKILL_MAX, - SKILL_ATMOS = SKILL_MAX, - SKILL_ENGINES = SKILL_MAX - ) - skill_points = 30 alt_titles = list() - event_categories = list(ASSIGNMENT_ENGINEER) diff --git a/maps/tradeship/jobs/medical.dm b/maps/tradeship/jobs/medical.dm index 55ff9ea2f1ce..03b480321b82 100644 --- a/maps/tradeship/jobs/medical.dm +++ b/maps/tradeship/jobs/medical.dm @@ -1,9 +1,8 @@ -/datum/job/tradeship_doctor +/datum/job/doctor/tradeship title = "Junior Doctor" - department_types = list(/decl/department/medical) - head_position = 0 supervisors = "the Head Doctor and the Captain" alt_titles = list() + // Slightly beefier skills due to smaller crew. skill_points = 24 min_skill = list( SKILL_LITERACY = SKILL_ADEPT, @@ -16,11 +15,6 @@ SKILL_ANATOMY = SKILL_MAX, SKILL_CHEMISTRY = SKILL_MAX ) - minimal_player_age = 3 - total_positions = 5 - spawn_positions = 3 - selection_color = "#013d3b" - economic_power = 7 access = list( access_medical, access_medical_equip, @@ -39,61 +33,10 @@ outfit_type = /decl/outfit/job/tradeship/doc/junior event_categories = list(ASSIGNMENT_MEDICAL) -/datum/job/tradeship_doctor/head +/datum/job/cmo/tradeship title = "Head Doctor" - head_position = 1 - department_types = list( - /decl/department/medical, - /decl/department/command - ) supervisors = "the Captain and your own ethics" outfit_type = /decl/outfit/job/tradeship/doc alt_titles = list("Surgeon") - total_positions = 1 - spawn_positions = 1 + // Slightly beefier skills due to smaller crew. skill_points = 28 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 - selection_color = "#026865" - req_admin_notify = 1 - economic_power = 10 - access = list( - access_medical, - access_medical_equip, - access_morgue, - access_bridge, - access_heads, - access_chemistry, - access_virology, - access_cmo, - access_surgery, - access_RC_announce, - access_keycard_auth, - access_sec_doors, - access_psychiatrist, - access_eva, - access_maint_tunnels, - access_external_airlocks - ) - minimal_access = list( - access_medical, - access_medical_equip, - access_morgue, - access_bridge, - access_heads, - access_chemistry, - access_virology, - access_cmo, - access_surgery, - access_RC_announce, - access_keycard_auth, - access_sec_doors, - access_psychiatrist, - access_eva, - access_maint_tunnels, - access_external_airlocks - ) - minimal_player_age = 14 - ideal_character_age = 50 - event_categories = list(ASSIGNMENT_MEDICAL) diff --git a/maps/tradeship/jobs/science.dm b/maps/tradeship/jobs/science.dm index 4c6e5555d8e2..c14bb9e5b928 100644 --- a/maps/tradeship/jobs/science.dm +++ b/maps/tradeship/jobs/science.dm @@ -1,119 +1,14 @@ -/datum/job/tradeship_researcher +/datum/job/scientist/tradeship title = "Junior Researcher" supervisors = "the Head Researcher and the Captain" total_positions = 2 spawn_positions = 1 alt_titles = list() outfit_type = /decl/outfit/job/tradeship/hand/researcher/junior - min_skill = list( - SKILL_LITERACY = SKILL_ADEPT, - SKILL_COMPUTER = SKILL_BASIC, - SKILL_DEVICES = SKILL_BASIC, - SKILL_SCIENCE = SKILL_ADEPT - ) - max_skill = list( - SKILL_ANATOMY = SKILL_MAX, - SKILL_DEVICES = SKILL_MAX, - SKILL_SCIENCE = SKILL_MAX - ) + // Smaller crew, more points. skill_points = 24 - department_types = list(/decl/department/science) - selection_color = "#633d63" - economic_power = 7 - minimal_player_age = 7 - access = list( - access_robotics, - access_tox, - access_tox_storage, - access_research, - access_xenobiology, - access_xenoarch - ) - minimal_access = list( - access_robotics, - access_tox, - access_tox_storage, - access_research, - access_xenobiology, - access_xenoarch - ) - event_categories = list(ASSIGNMENT_SCIENTIST) -/datum/job/tradeship_researcher/head +/datum/job/rd/tradeship title = "Head Researcher" - supervisors = "the Captain" - spawn_positions = 1 - total_positions = 1 alt_titles = list() outfit_type = /decl/outfit/job/tradeship/hand/researcher - min_skill = list( - SKILL_LITERACY = SKILL_ADEPT, - SKILL_COMPUTER = SKILL_BASIC, - SKILL_FINANCE = SKILL_ADEPT, - SKILL_BOTANY = SKILL_BASIC, - SKILL_ANATOMY = SKILL_BASIC, - SKILL_DEVICES = SKILL_BASIC, - SKILL_SCIENCE = SKILL_ADEPT - ) - max_skill = list( - SKILL_ANATOMY = SKILL_MAX, - SKILL_DEVICES = SKILL_MAX, - SKILL_SCIENCE = SKILL_MAX - ) - skill_points = 30 - head_position = 1 - department_types = list( - /decl/department/science, - /decl/department/command - ) - selection_color = "#ad6bad" - req_admin_notify = 1 - economic_power = 15 - access = list( - access_rd, - access_bridge, - access_tox, - access_morgue, - access_tox_storage, - access_teleporter, - access_sec_doors, - access_heads, - access_research, - access_robotics, - access_xenobiology, - access_ai_upload, - access_tech_storage, - access_RC_announce, - access_keycard_auth, - access_tcomsat, - access_gateway, - access_xenoarch, - access_network - ) - minimal_access = list( - access_rd, - access_bridge, - access_tox, - access_morgue, - access_tox_storage, - access_teleporter, - access_sec_doors, - access_heads, - access_research, - access_robotics, - access_xenobiology, - access_ai_upload, - access_tech_storage, - access_RC_announce, - access_keycard_auth, - access_tcomsat, - access_gateway, - access_xenoarch, - access_network - ) - minimal_player_age = 14 - ideal_character_age = 50 - guestbanned = 1 - must_fill = 1 - not_random_selectable = 1 - event_categories = list(ASSIGNMENT_SCIENTIST) diff --git a/maps/tradeship/jobs/synthetics.dm b/maps/tradeship/jobs/synthetics.dm deleted file mode 100644 index e10101d48a2c..000000000000 --- a/maps/tradeship/jobs/synthetics.dm +++ /dev/null @@ -1,31 +0,0 @@ -/datum/job/tradeship_robot - title = "Robot" - event_categories = list(ASSIGNMENT_ROBOT) - total_positions = 1 - spawn_positions = 1 - supervisors = "your laws and the Captain" - selection_color = "#254c25" - minimal_player_age = 7 - account_allowed = 0 - economic_power = 0 - loadout_allowed = FALSE - outfit_type = /decl/outfit/job/silicon/cyborg - hud_icon = "hudblank" - skill_points = 0 - no_skill_buffs = TRUE - guestbanned = 1 - not_random_selectable = 1 - skip_loadout_preview = TRUE - department_types = list(/decl/department/miscellaneous) - -/datum/job/tradeship_robot/handle_variant_join(var/mob/living/human/H, var/alt_title) - if(H) - return H.Robotize(SSrobots.get_mob_type_by_title(alt_title || title)) - -/datum/job/tradeship_robot/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) - return !!H - -/datum/job/tradeship_robot/New() - ..() - alt_titles = SSrobots.robot_alt_titles.Copy() - alt_titles -= title diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 4ebf5e3219b7..32eeb975e2ca 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -17,6 +17,7 @@ #include "../../mods/content/government/away_sites/icarus/icarus.dm" #include "../../mods/content/corporate/away_sites/lar_maria/lar_maria.dm" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/mundane.dm" #include "../../mods/content/bigpharma/_bigpharma.dme" @@ -58,7 +59,6 @@ #include "tradeship_antagonists.dm" #include "tradeship_areas.dm" - #include "tradeship_departments.dm" #include "tradeship_documents.dm" #include "tradeship_jobs.dm" #include "tradeship_loadouts.dm" @@ -78,7 +78,6 @@ #include "jobs/engineering.dm" #include "jobs/medical.dm" #include "jobs/science.dm" - #include "jobs/synthetics.dm" #include "outfits/_outfits.dm" #include "outfits/command.dm" diff --git a/maps/tradeship/tradeship_jobs.dm b/maps/tradeship/tradeship_jobs.dm index 37acc9665636..61b526624e0d 100644 --- a/maps/tradeship/tradeship_jobs.dm +++ b/maps/tradeship/tradeship_jobs.dm @@ -1,19 +1,19 @@ /datum/map/tradeship - default_job_type = /datum/job/tradeship_deckhand + default_job_type = /datum/job/assistant/tradeship default_department_type = /decl/department/civilian default_law_type = /datum/ai_laws/corporate id_hud_icons = 'maps/tradeship/hud.dmi' allowed_jobs = list( - /datum/job/tradeship_deckhand, - /datum/job/tradeship_captain, + /datum/job/assistant/tradeship, + /datum/job/captain/tradeship, /datum/job/tradeship_first_mate, - /datum/job/tradeship_doctor/head, - /datum/job/tradeship_doctor, - /datum/job/tradeship_engineer/head, - /datum/job/tradeship_engineer, - /datum/job/tradeship_researcher/head, - /datum/job/tradeship_researcher, - /datum/job/tradeship_robot + /datum/job/cmo/tradeship, + /datum/job/doctor/tradeship, + /datum/job/chief_engineer/tradeship, + /datum/job/engineer/tradeship, + /datum/job/rd/tradeship, + /datum/job/scientist/tradeship, + /datum/job/robot ) /obj/machinery/suit_cycler/tradeship @@ -39,4 +39,4 @@ /obj/item/clothing/head/helmet/space/void/excavation, /obj/item/clothing/head/helmet/space/void/engineering/salvage )) - . = ..() \ No newline at end of file + . = ..() diff --git a/maps/tradeship/tradeship_spawnpoints.dm b/maps/tradeship/tradeship_spawnpoints.dm index 054216b46fea..c4be5d21c350 100644 --- a/maps/tradeship/tradeship_spawnpoints.dm +++ b/maps/tradeship/tradeship_spawnpoints.dm @@ -9,7 +9,7 @@ /decl/spawnpoint/cryo name = "Port Cryogenic Storage" spawn_announcement = "has completed revival in the port cryogenics bay" - disallow_job = list(/datum/job/tradeship_robot) + disallow_job = list(/datum/job/robot) /decl/spawnpoint/cryo/two name = "Starboard Cryogenic Storage" @@ -22,7 +22,7 @@ /decl/spawnpoint/cryo/captain name = "Captain Compartment" spawn_announcement = "has completed revival in the captain compartment" - restrict_job = list(/datum/job/tradeship_captain) + restrict_job = list(/datum/job/captain/tradeship) uid = "spawn_cryo_captain" /obj/abstract/landmark/latejoin/cryo_captain diff --git a/mods/content/standard_jobs/_standard_jobs.dm b/mods/content/standard_jobs/_standard_jobs.dm new file mode 100644 index 000000000000..f19759923d43 --- /dev/null +++ b/mods/content/standard_jobs/_standard_jobs.dm @@ -0,0 +1,2 @@ +/decl/modpack/standard_jobs + name = "Standard SS13 Jobs" diff --git a/mods/content/standard_jobs/_standard_jobs.dme b/mods/content/standard_jobs/_standard_jobs.dme new file mode 100644 index 000000000000..cbba09eacef5 --- /dev/null +++ b/mods/content/standard_jobs/_standard_jobs.dme @@ -0,0 +1,30 @@ +#ifndef MODPACK_STANDARD_JOBS +#define MODPACK_STANDARD_JOBS +// BEGIN_INCLUDE +#include "_standard_jobs.dm" +#include "departments\civilian.dm" +#include "departments\command.dm" +#include "departments\engineering.dm" +#include "departments\medical.dm" +#include "departments\miscellaneous.dm" +#include "departments\science.dm" +#include "departments\security.dm" +#include "departments\service.dm" +#include "departments\supply.dm" +#include "jobs\captain.dm" +#include "jobs\civilian.dm" +#include "jobs\engineering.dm" +#include "jobs\medical.dm" +#include "jobs\science.dm" +#include "jobs\security.dm" +#include "jobs\synthetics.dm" +#include "outfits\cargo.dm" +#include "outfits\civilian.dm" +#include "outfits\command.dm" +#include "outfits\engineering.dm" +#include "outfits\medical.dm" +#include "outfits\pda.dm" +#include "outfits\science.dm" +#include "outfits\security.dm" +// END_INCLUDE +#endif diff --git a/mods/content/standard_jobs/departments/civilian.dm b/mods/content/standard_jobs/departments/civilian.dm new file mode 100644 index 000000000000..3cda485f4bbe --- /dev/null +++ b/mods/content/standard_jobs/departments/civilian.dm @@ -0,0 +1,4 @@ +/decl/department/civilian + name = "Civilian" + display_priority = 1 + display_color = "#dddddd" diff --git a/mods/content/standard_jobs/departments/command.dm b/mods/content/standard_jobs/departments/command.dm new file mode 100644 index 000000000000..fa842fb881cb --- /dev/null +++ b/mods/content/standard_jobs/departments/command.dm @@ -0,0 +1,14 @@ +/decl/department/command + name = "Command" + colour = "#800080" + display_priority = 5 + display_color = "#ccccff" + +/obj/machinery/network/pager + department = /decl/department/command + +/decl/department/support + name = "Support" + announce_channel = "Command" + colour = "#800080" + display_color = "#87ceeb" diff --git a/mods/content/standard_jobs/departments/engineering.dm b/mods/content/standard_jobs/departments/engineering.dm new file mode 100644 index 000000000000..252b4de16a61 --- /dev/null +++ b/mods/content/standard_jobs/departments/engineering.dm @@ -0,0 +1,12 @@ +/decl/department/engineering + name = "Engineering" + announce_channel = "Engineering" + colour = "#ffa500" + display_priority = 4 + display_color = "#fff5cc" + +/obj/item/robot_module/engineering + associated_department = /decl/department/engineering + +/obj/machinery/network/pager/engineering + department = /decl/department/engineering diff --git a/mods/content/standard_jobs/departments/medical.dm b/mods/content/standard_jobs/departments/medical.dm new file mode 100644 index 000000000000..01506ddd5d47 --- /dev/null +++ b/mods/content/standard_jobs/departments/medical.dm @@ -0,0 +1,13 @@ +/decl/department/medical + name = "Medical" + goals = list(/datum/goal/department/medical_fatalities) + announce_channel = "Medical" + colour = "#008000" + display_priority = 2 + display_color = "#ffeef0" + +/obj/item/robot_module/medical + associated_department = /decl/department/medical + +/obj/machinery/network/pager/medical + department = /decl/department/medical diff --git a/mods/content/standard_jobs/departments/miscellaneous.dm b/mods/content/standard_jobs/departments/miscellaneous.dm new file mode 100644 index 000000000000..19149216fdbd --- /dev/null +++ b/mods/content/standard_jobs/departments/miscellaneous.dm @@ -0,0 +1,4 @@ +/decl/department/miscellaneous + name = "Misc" + display_priority = -1 + display_color = "#ccffcc" diff --git a/mods/content/standard_jobs/departments/science.dm b/mods/content/standard_jobs/departments/science.dm new file mode 100644 index 000000000000..47ebb9c21308 --- /dev/null +++ b/mods/content/standard_jobs/departments/science.dm @@ -0,0 +1,18 @@ +/decl/department/science + name = "Science" + goals = list(/datum/goal/department/extract_slime_cores) + announce_channel = "Science" + colour = "#a65ba6" + display_color = "#e79fff" + +/obj/item/robot_module/research + associated_department = /decl/department/science + +/obj/machinery/network/pager/science + department = /decl/department/science + +/decl/department/exploration + name = "Exploration" + announce_channel = "Exploration" + colour = "#68099e" + display_color = "#b784a7" diff --git a/mods/content/standard_jobs/departments/security.dm b/mods/content/standard_jobs/departments/security.dm new file mode 100644 index 000000000000..e758218721bd --- /dev/null +++ b/mods/content/standard_jobs/departments/security.dm @@ -0,0 +1,12 @@ +/decl/department/security + name = "Security" + announce_channel = "Security" + colour = "#dd0000" + display_priority = 3 + display_color = "#ffddf0" + +/obj/item/robot_module/security + associated_department = /decl/department/security + +/obj/machinery/network/pager/security + department = /decl/department/security diff --git a/mods/content/standard_jobs/departments/service.dm b/mods/content/standard_jobs/departments/service.dm new file mode 100644 index 000000000000..b82c1c6deaf2 --- /dev/null +++ b/mods/content/standard_jobs/departments/service.dm @@ -0,0 +1,6 @@ + +/decl/department/service + name = "Service" + announce_channel = "Service" + colour = "#88b764" + display_color = "#d0f0c0" diff --git a/mods/content/standard_jobs/departments/supply.dm b/mods/content/standard_jobs/departments/supply.dm new file mode 100644 index 000000000000..c71d4327554b --- /dev/null +++ b/mods/content/standard_jobs/departments/supply.dm @@ -0,0 +1,8 @@ +/decl/department/supply + name = "Supply" + announce_channel = "Supply" + colour = "#bb9040" + display_color = "#f0e68c" + +/obj/machinery/network/pager/cargo + department = /decl/department/supply diff --git a/maps/exodus/jobs/captain.dm b/mods/content/standard_jobs/jobs/captain.dm similarity index 100% rename from maps/exodus/jobs/captain.dm rename to mods/content/standard_jobs/jobs/captain.dm diff --git a/maps/exodus/jobs/civilian.dm b/mods/content/standard_jobs/jobs/civilian.dm similarity index 98% rename from maps/exodus/jobs/civilian.dm rename to mods/content/standard_jobs/jobs/civilian.dm index 90d800d0b6bd..af71d84e5369 100644 --- a/maps/exodus/jobs/civilian.dm +++ b/mods/content/standard_jobs/jobs/civilian.dm @@ -53,6 +53,11 @@ access_bar, access_kitchen ) + minimal_access = list( + access_hydroponics, + access_bar, + access_kitchen + ) minimal_access = list(access_bar) alt_titles = list("Barista") outfit_type = /decl/outfit/job/service/bartender diff --git a/maps/exodus/jobs/engineering.dm b/mods/content/standard_jobs/jobs/engineering.dm similarity index 100% rename from maps/exodus/jobs/engineering.dm rename to mods/content/standard_jobs/jobs/engineering.dm diff --git a/maps/exodus/jobs/medical.dm b/mods/content/standard_jobs/jobs/medical.dm similarity index 99% rename from maps/exodus/jobs/medical.dm rename to mods/content/standard_jobs/jobs/medical.dm index 4d37a277fa17..14e68446da0a 100644 --- a/maps/exodus/jobs/medical.dm +++ b/mods/content/standard_jobs/jobs/medical.dm @@ -124,7 +124,6 @@ /datum/computer_file/program/camera_monitor ) skill_points = 22 - title = "Paramedic" event_categories = list(ASSIGNMENT_MEDICAL) /datum/job/chemist @@ -200,12 +199,14 @@ ) give_psionic_implant_on_join = FALSE +#ifdef MODPACK_PSIONICS /datum/job/counselor/equip_job(var/mob/living/human/H) if(H.mind.role_alt_title == "Counselor") psi_faculties = list("[PSI_REDACTION]" = PSI_RANK_OPERANT) if(H.mind.role_alt_title == "Mentalist") psi_faculties = list("[PSI_COERCION]" = PSI_RANK_OPERANT) return ..() +#endif // Department-flavor IDs /obj/item/card/id/medical diff --git a/maps/exodus/jobs/science.dm b/mods/content/standard_jobs/jobs/science.dm similarity index 100% rename from maps/exodus/jobs/science.dm rename to mods/content/standard_jobs/jobs/science.dm diff --git a/maps/exodus/jobs/security.dm b/mods/content/standard_jobs/jobs/security.dm similarity index 100% rename from maps/exodus/jobs/security.dm rename to mods/content/standard_jobs/jobs/security.dm diff --git a/maps/exodus/jobs/synthetics.dm b/mods/content/standard_jobs/jobs/synthetics.dm similarity index 100% rename from maps/exodus/jobs/synthetics.dm rename to mods/content/standard_jobs/jobs/synthetics.dm diff --git a/maps/exodus/outfits/cargo.dm b/mods/content/standard_jobs/outfits/cargo.dm similarity index 100% rename from maps/exodus/outfits/cargo.dm rename to mods/content/standard_jobs/outfits/cargo.dm diff --git a/maps/exodus/outfits/civilian.dm b/mods/content/standard_jobs/outfits/civilian.dm similarity index 100% rename from maps/exodus/outfits/civilian.dm rename to mods/content/standard_jobs/outfits/civilian.dm diff --git a/maps/exodus/outfits/command.dm b/mods/content/standard_jobs/outfits/command.dm similarity index 100% rename from maps/exodus/outfits/command.dm rename to mods/content/standard_jobs/outfits/command.dm diff --git a/maps/exodus/outfits/engineering.dm b/mods/content/standard_jobs/outfits/engineering.dm similarity index 100% rename from maps/exodus/outfits/engineering.dm rename to mods/content/standard_jobs/outfits/engineering.dm diff --git a/maps/exodus/outfits/medical.dm b/mods/content/standard_jobs/outfits/medical.dm similarity index 100% rename from maps/exodus/outfits/medical.dm rename to mods/content/standard_jobs/outfits/medical.dm diff --git a/maps/exodus/outfits/_pda.dm b/mods/content/standard_jobs/outfits/pda.dm similarity index 100% rename from maps/exodus/outfits/_pda.dm rename to mods/content/standard_jobs/outfits/pda.dm diff --git a/maps/exodus/outfits/science.dm b/mods/content/standard_jobs/outfits/science.dm similarity index 100% rename from maps/exodus/outfits/science.dm rename to mods/content/standard_jobs/outfits/science.dm diff --git a/maps/exodus/outfits/security.dm b/mods/content/standard_jobs/outfits/security.dm similarity index 100% rename from maps/exodus/outfits/security.dm rename to mods/content/standard_jobs/outfits/security.dm From 81a65fea49289844582510a321af91fb58923a47 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Wed, 18 Dec 2024 10:48:22 -0600 Subject: [PATCH 0092/1331] Ignore .DS_Store --- .gitignore | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 52ee73c38355..b7e92fbae535 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ # ignore misc BYOND files -Thumbs.db -Thumbs.db:encryptable *.log *.int *.rsc @@ -22,6 +20,11 @@ atupdate config/* sql/test_db +# misc OS garbage +Thumbs.db +Thumbs.db:encryptable +.DS_Store + # vscode .vscode/* *.code-workspace From 40390355845535b96e778d3a5fdbc5931251f5ab Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 19 Dec 2024 10:38:25 +1100 Subject: [PATCH 0093/1331] Struts are now largely replaced by rods. --- code/game/machinery/constructable_frame.dm | 2 +- code/game/objects/items/stacks/rods.dm | 69 ++++++++----------- code/game/objects/random/subtypes/misc.dm | 4 +- .../crates_lockers/closets/syndicate.dm | 2 +- code/game/objects/structures/girders.dm | 10 +-- code/game/objects/structures/racks.dm | 2 +- code/game/objects/structures/railing.dm | 2 +- .../stool_bed_chair_nest_sofa/bed.dm | 2 +- code/game/objects/structures/tables.dm | 2 +- code/game/objects/structures/wall_frame.dm | 2 +- code/modules/butchery/butchery_data_misc.dm | 2 +- code/modules/butchery/butchery_hook.dm | 2 +- .../{recipes_struts.dm => recipes_rods.dm} | 36 ++++++---- .../designs/general/designs_general.dm | 8 +-- .../materials/material_sheets_mapping.dm | 5 ++ code/modules/materials/material_stack_misc.dm | 11 --- .../robot/flying/module_flying_repair.dm | 2 - .../robot/modules/module_engineering.dm | 2 - .../robot/modules/module_maintenance_drone.dm | 2 - maps/antag_spawn/mercenary/mercenary_base.dmm | 8 +-- maps/away/bearcat/bearcat-1.dmm | 2 +- maps/away/bearcat/bearcat-2.dmm | 14 ++-- maps/away/casino/casino.dmm | 2 +- maps/away/liberia/liberia.dmm | 8 +-- .../lost_supply_base/lost_supply_base.dmm | 6 +- maps/away/magshield/magshield.dmm | 12 ++-- maps/exodus/exodus-1.dmm | 2 +- maps/ministation/ministation-0.dmm | 10 +-- .../exoplanet_ruins/marooned/marooned.dmm | 4 +- .../exoplanet_ruins/oldpod/oldpod.dmm | 8 +-- .../exoplanet_ruins/playablecolony/colony.dmm | 8 +-- maps/tradeship/tradeship-1.dmm | 2 +- maps/tradeship/tradeship-3.dmm | 2 +- mods/content/dungeon_loot/subtypes/maint.dm | 2 +- .../government/away_sites/icarus/icarus-1.dmm | 14 ++-- mods/gamemodes/heist/heist_base.dmm | 2 +- mods/species/ascent/mobs/drone.dm | 2 - nebula.dme | 2 +- tools/map_migrations/4651_rods.txt | 2 + 39 files changed, 131 insertions(+), 148 deletions(-) rename code/modules/crafting/stack_recipes/{recipes_struts.dm => recipes_rods.dm} (72%) create mode 100644 tools/map_migrations/4651_rods.txt diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 11c41dca7caf..df5391cc06d8 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -19,7 +19,7 @@ update_icon() /obj/machinery/constructable_frame/dismantle() - SSmaterials.create_object(/decl/material/solid/metal/steel, loc, 5, object_type = /obj/item/stack/material/strut) + SSmaterials.create_object(/decl/material/solid/metal/steel, loc, 5, object_type = /obj/item/stack/material/rods) qdel(src) return TRUE diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index 86363f815e9c..7e244c18453f 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -1,45 +1,37 @@ /obj/item/stack/material/rods - name = "rod" - desc = "Some rods. Can be used for building, or something." - singular_name = "rod" - plural_name = "rods" - icon_state = "rod" - plural_icon_state = "rod-mult" - max_icon_state = "rod-max" - w_class = ITEM_SIZE_LARGE - attack_cooldown = 21 + name = "rod" + desc = "Some rods. Can be used for building, or something." + singular_name = "rod" + plural_name = "rods" + icon_state = "rod" + plural_icon_state = "rod-mult" + max_icon_state = "rod-max" + w_class = ITEM_SIZE_LARGE + attack_cooldown = 21 melee_accuracy_bonus = -20 - throw_speed = 5 - throw_range = 20 - max_amount = 100 - attack_verb = list("hit", "bludgeoned", "whacked") - lock_picking_level = 3 - matter_multiplier = 0.3 - material = /decl/material/solid/metal/steel - is_spawnable_type = TRUE - - pickup_sound = 'sound/foley/tooldrop3.ogg' - drop_sound = 'sound/foley/tooldrop2.ogg' + throw_speed = 5 + throw_range = 20 + max_amount = 100 + attack_verb = list("hit", "bludgeoned", "whacked") + lock_picking_level = 3 + matter_multiplier = 0.3 + material = /decl/material/solid/metal/steel + crafting_stack_type = /obj/item/stack/material/rods + pickup_sound = 'sound/foley/tooldrop3.ogg' + drop_sound = 'sound/foley/tooldrop2.ogg' /obj/item/stack/material/rods/get_autopsy_descriptors() . = ..() . += "narrow" -/obj/item/stack/material/rods/ten - amount = 10 - -/obj/item/stack/material/rods/fifty - amount = 50 - /obj/item/stack/material/rods/cyborg - name = "metal rod synthesizer" - desc = "A device that makes metal rods." - gender = NEUTER - matter = null - uses_charge = 1 - charge_costs = list(500) - max_health = ITEM_HEALTH_NO_DAMAGE - is_spawnable_type = FALSE + name = "metal rod synthesizer" + desc = "A device that makes metal rods." + gender = NEUTER + matter = null + uses_charge = 1 + charge_costs = list(500) + max_health = ITEM_HEALTH_NO_DAMAGE /obj/item/stack/material/rods/Initialize() . = ..() @@ -54,12 +46,14 @@ else icon_state = base_state +// TODO: slapcrafting recipes to replace this block. /obj/item/stack/material/rods/attackby(obj/item/W, mob/user) + if(IS_WELDER(W)) var/obj/item/weldingtool/WT = W if(!can_use(2)) - to_chat(user, "You need at least two rods to do this.") + to_chat(user, SPAN_WARNING("You need at least two rods to do this.")) return TRUE if(WT.weld(0,user)) @@ -87,8 +81,3 @@ src.use(1) return TRUE return ..() - -/obj/item/stack/material/rods/attack_self(mob/user) - add_fingerprint(user) - if(isturf(user.loc)) - place_grille(user, user.loc, src) diff --git a/code/game/objects/random/subtypes/misc.dm b/code/game/objects/random/subtypes/misc.dm index e0003d62ef89..0cf2714e8169 100644 --- a/code/game/objects/random/subtypes/misc.dm +++ b/code/game/objects/random/subtypes/misc.dm @@ -210,8 +210,8 @@ /obj/item/stack/material/plank/mapped/wood/fifty, /obj/item/stack/material/cardstock/mapped/cardboard/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, - /obj/item/stack/material/rods/ten, - /obj/item/stack/material/rods/fifty + /obj/item/stack/material/rods/mapped/steel/ten, + /obj/item/stack/material/rods/mapped/steel/fifty ) return spawnable_choices diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index 798d8f65c49f..6d3bec631ee9 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -128,7 +128,7 @@ /obj/item/stack/material/puck/mapped/uranium/fifty, /obj/item/stack/material/gemstone/mapped/diamond/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, - /obj/item/stack/material/rods/fifty + /obj/item/stack/material/rods/mapped/steel/fifty ) for(var/i = 0, i < 2, i++) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 4a9c41b13ed0..c508eb305f99 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -10,7 +10,7 @@ tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) max_health = 100 parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/cover = 50 var/prepped_for_fakewall @@ -30,9 +30,9 @@ if(reinf_material) playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) - visible_message(SPAN_NOTICE("\The [user] begins unscrewing \the [reinf_material.solid_name] struts from \the [src].")) + visible_message(SPAN_NOTICE("\The [user] begins unscrewing \the [reinf_material.solid_name] rods from \the [src].")) if(do_after(user, 5 SECONDS, src) || QDELETED(src) || !reinf_material) - visible_message(SPAN_NOTICE("\The [user] unscrews and removes \the [reinf_material.solid_name] struts from \the [src].")) + visible_message(SPAN_NOTICE("\The [user] unscrews and removes \the [reinf_material.solid_name] rods from \the [src].")) reinf_material.place_dismantled_product(get_turf(src)) reinf_material = null return TRUE @@ -85,7 +85,7 @@ /obj/structure/girder/can_unanchor(var/mob/user) if(anchored && reinf_material) - to_chat(user, SPAN_WARNING("You must remove the support struts before you can dislodge \the [src].")) + to_chat(user, SPAN_WARNING("You must remove the support rods before you can dislodge \the [src].")) return FALSE . = ..() @@ -182,7 +182,7 @@ if(!istype(M) || M.integrity < 50) to_chat(user, SPAN_WARNING("You cannot reinforce \the [src] with [M.solid_name]; it is too soft.")) return TRUE - visible_message(SPAN_NOTICE("\The [user] begins installing [M.solid_name] struts into \the [src].")) + visible_message(SPAN_NOTICE("\The [user] begins installing [M.solid_name] rods into \the [src].")) if (!do_after(user, 4 SECONDS, src) || !S.use(2)) return TRUE visible_message(SPAN_NOTICE("\The [user] finishes reinforcing \the [src] with [M.solid_name].")) diff --git a/code/game/objects/structures/racks.dm b/code/game/objects/structures/racks.dm index 7f1c77404ec0..3ade37d94fe8 100644 --- a/code/game/objects/structures/racks.dm +++ b/code/game/objects/structures/racks.dm @@ -10,7 +10,7 @@ atom_flags = ATOM_FLAG_CLIMBABLE throwpass = TRUE parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods density = TRUE anchored = TRUE structure_flags = STRUCTURE_FLAG_SURFACE diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index 93926bd3e51c..66f0afe17852 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -14,7 +14,7 @@ material_alteration = MAT_FLAG_ALTERATION_ALL max_health = 100 parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/broken = FALSE var/neighbor_status = 0 diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm index 771e85942135..f1ec03d9f7cf 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm @@ -22,7 +22,7 @@ material_alteration = MAT_FLAG_ALTERATION_ALL tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods user_comfort = 1 obj_flags = OBJ_FLAG_SUPPORT_MOB var/base_icon = "bed" diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index bec89bbb2ef8..8302b97bfec2 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -19,7 +19,7 @@ tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods structure_flags = STRUCTURE_FLAG_SURFACE can_support_butchery = TRUE diff --git a/code/game/objects/structures/wall_frame.dm b/code/game/objects/structures/wall_frame.dm index 850fc4d7c580..3aaf589d466e 100644 --- a/code/game/objects/structures/wall_frame.dm +++ b/code/game/objects/structures/wall_frame.dm @@ -18,7 +18,7 @@ tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) max_health = 40 parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/stripe_color var/list/connections var/list/other_connections diff --git a/code/modules/butchery/butchery_data_misc.dm b/code/modules/butchery/butchery_data_misc.dm index 60aa3f2e6ff9..54720ce3b742 100644 --- a/code/modules/butchery/butchery_data_misc.dm +++ b/code/modules/butchery/butchery_data_misc.dm @@ -4,7 +4,7 @@ meat_type = /obj/item/stack/material/rods bone_material = /decl/material/solid/metal/titanium - bone_type = /obj/item/stack/material/strut + bone_type = /obj/item/stack/material/rods skin_material = /decl/material/solid/metal/aluminium skin_type = /obj/item/stack/material/sheet/shiny diff --git a/code/modules/butchery/butchery_hook.dm b/code/modules/butchery/butchery_hook.dm index ae3ea54b8dde..0cc357e6f75a 100644 --- a/code/modules/butchery/butchery_hook.dm +++ b/code/modules/butchery/butchery_hook.dm @@ -19,7 +19,7 @@ ) tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/mob/living/occupant var/occupant_state = CARCASS_EMPTY diff --git a/code/modules/crafting/stack_recipes/recipes_struts.dm b/code/modules/crafting/stack_recipes/recipes_rods.dm similarity index 72% rename from code/modules/crafting/stack_recipes/recipes_struts.dm rename to code/modules/crafting/stack_recipes/recipes_rods.dm index fced999502f2..c010556a990d 100644 --- a/code/modules/crafting/stack_recipes/recipes_struts.dm +++ b/code/modules/crafting/stack_recipes/recipes_rods.dm @@ -1,6 +1,7 @@ -/decl/stack_recipe/strut - abstract_type = /decl/stack_recipe/strut +/decl/stack_recipe/rods + abstract_type = /decl/stack_recipe/rods craft_stack_types = list( + /obj/item/stack/material/rods, /obj/item/stack/material/strut, /obj/item/stack/material/bone ) @@ -10,64 +11,69 @@ available_to_map_tech_level = MAP_TECH_LEVEL_MEDIEVAL category = "structures" -/decl/stack_recipe/strut/stick +/decl/stack_recipe/rods/stick result_type = /obj/item/stick one_per_turf = FALSE on_floor = FALSE difficulty = MAT_VALUE_EASY_DIY category = "items" -/decl/stack_recipe/strut/stick/staff +/decl/stack_recipe/rods/stick/staff result_type = /obj/item/staff difficulty = MAT_VALUE_NORMAL_DIY -/decl/stack_recipe/strut/stick/cane +/decl/stack_recipe/rods/stick/cane result_type = /obj/item/cane difficulty = MAT_VALUE_NORMAL_DIY -/decl/stack_recipe/strut/railing +/decl/stack_recipe/rods/railing result_type = /obj/structure/railing -/decl/stack_recipe/strut/ladder +/decl/stack_recipe/rods/ladder result_type = /obj/structure/ladder on_floor = FALSE required_wall_support_value = 10 -/decl/stack_recipe/strut/girder +/decl/stack_recipe/rods/girder result_type = /obj/structure/girder required_wall_support_value = 10 req_amount = 5 * SHEET_MATERIAL_AMOUNT // Arbitrary value since girders return weird matter values. available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/strut/wall_frame +/decl/stack_recipe/rods/wall_frame result_type = /obj/structure/wall_frame available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/strut/table_frame +/decl/stack_recipe/rods/table_frame result_type = /obj/structure/table/frame category = "furniture" -/decl/stack_recipe/strut/rack +/decl/stack_recipe/rods/rack result_type = /obj/structure/rack category = "furniture" -/decl/stack_recipe/strut/butcher_hook +/decl/stack_recipe/rods/butcher_hook result_type = /obj/structure/meat_hook one_per_turf = TRUE difficulty = MAT_VALUE_NORMAL_DIY category = "furniture" -/decl/stack_recipe/strut/bed +/decl/stack_recipe/rods/bed result_type = /obj/structure/bed required_integrity = 50 required_min_hardness = MAT_VALUE_FLEXIBLE + 10 category = "furniture" -/decl/stack_recipe/strut/machine +/decl/stack_recipe/rods/machine result_type = /obj/machinery/constructable_frame/machine_frame req_amount = 5 * SHEET_MATERIAL_AMOUNT // Arbitrary value since machines don't handle matter properly yet. required_material = /decl/material/solid/metal/steel available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/strut/machine/spawn_result(mob/user, location, amount, decl/material/mat, decl/material/reinf_mat, paint_color, spent_type, spent_amount = 1) +/decl/stack_recipe/rods/machine/spawn_result(mob/user, location, amount, decl/material/mat, decl/material/reinf_mat, paint_color, spent_type, spent_amount = 1) return ..(user, location, amount, null, null, paint_color, spent_type, spent_amount) + +/decl/stack_recipe/rods/grille + result_type = /obj/structure/grille + one_per_turf = TRUE + difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index 64adcc09ac33..69b99bda0b9f 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -118,7 +118,7 @@ /datum/fabricator_recipe/struts name = "strut, steel" - path = /obj/item/stack/material/strut/mapped/steel + path = /obj/item/stack/material/rods/mapped/steel /datum/fabricator_recipe/struts/get_resources() resources = list( @@ -127,7 +127,7 @@ /datum/fabricator_recipe/struts/plastic name = "strut, plastic" - path = /obj/item/stack/material/strut/mapped/plastic + path = /obj/item/stack/material/rods/mapped/plastic /datum/fabricator_recipe/struts/plastic/get_resources() resources = list( @@ -136,7 +136,7 @@ /datum/fabricator_recipe/struts/aluminium name = "strut, aluminium" - path = /obj/item/stack/material/strut/mapped/aluminium + path = /obj/item/stack/material/rods/mapped/aluminium fabricator_types = list(FABRICATOR_CLASS_INDUSTRIAL) /datum/fabricator_recipe/struts/aluminium/get_resources() @@ -146,7 +146,7 @@ /datum/fabricator_recipe/struts/titanium name = "strut, titanium" - path = /obj/item/stack/material/strut/mapped/titanium + path = /obj/item/stack/material/rods/mapped/titanium fabricator_types = list(FABRICATOR_CLASS_INDUSTRIAL) /datum/fabricator_recipe/struts/titanium/get_resources() diff --git a/code/modules/materials/material_sheets_mapping.dm b/code/modules/materials/material_sheets_mapping.dm index 2f857da4e95f..1e17f9f3851f 100644 --- a/code/modules/materials/material_sheets_mapping.dm +++ b/code/modules/materials/material_sheets_mapping.dm @@ -114,6 +114,11 @@ STACK_SUBTYPES(plastic, "plastic", solid/organic/pl STACK_SUBTYPES(aluminium, "aluminium", solid/metal/aluminium, strut, null) STACK_SUBTYPES(titanium, "titanium", solid/metal/titanium, strut, null) +STACK_SUBTYPES(steel, "steel", solid/metal/steel, rods, null) +STACK_SUBTYPES(plastic, "plastic", solid/organic/plastic, rods, null) +STACK_SUBTYPES(aluminium, "aluminium", solid/metal/aluminium, rods, null) +STACK_SUBTYPES(titanium, "titanium", solid/metal/titanium, rods, null) + STACK_SUBTYPES(cotton, "cotton", solid/organic/cloth, thread, null) STACK_SUBTYPES(dried_gut, "dried gut", solid/organic/leather/gut, thread, null) diff --git a/code/modules/materials/material_stack_misc.dm b/code/modules/materials/material_stack_misc.dm index cf8ddd36474d..afabc40873b7 100644 --- a/code/modules/materials/material_stack_misc.dm +++ b/code/modules/materials/material_stack_misc.dm @@ -160,14 +160,3 @@ max_icon_state = "sheet-strut-max" stack_merge_type = /obj/item/stack/material/strut crafting_stack_type = /obj/item/stack/material/strut - -/obj/item/stack/material/strut/cyborg - name = "metal strut synthesizer" - desc = "A device that makes metal strut." - gender = NEUTER - matter = null - uses_charge = 1 - charge_costs = list(500) - material = /decl/material/solid/metal/steel - max_health = ITEM_HEALTH_NO_DAMAGE - is_spawnable_type = FALSE diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm index 2711d4981b8b..8a060e833c2f 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm @@ -34,7 +34,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -70,7 +69,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm index c334d6c8bb25..d79de772719c 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm @@ -47,7 +47,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -87,7 +86,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm index eeb10eae170c..fe58498a36e8 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm @@ -31,7 +31,6 @@ /obj/item/matter_decompiler, /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -84,7 +83,6 @@ for(var/thing in list( /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/maps/antag_spawn/mercenary/mercenary_base.dmm b/maps/antag_spawn/mercenary/mercenary_base.dmm index 86f6f092e57a..f75023f8eb7d 100644 --- a/maps/antag_spawn/mercenary/mercenary_base.dmm +++ b/maps/antag_spawn/mercenary/mercenary_base.dmm @@ -1209,7 +1209,7 @@ /obj/item/stack/material/sheet/reinforced/mapped/plasteel/ten, /obj/item/stack/material/ingot/mapped/copper/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/pane/mapped/rglass/fifty, /obj/item/stack/material/pane/mapped/rborosilicate/ten, /obj/structure/cable{ @@ -2751,9 +2751,9 @@ /obj/item/stack/material/sheet/mapped/steel/ten, /obj/item/stack/material/sheet/mapped/steel/ten, /obj/item/stack/material/sheet/mapped/steel/ten, -/obj/item/stack/material/rods/ten, -/obj/item/stack/material/rods/ten, -/obj/item/stack/material/rods/ten, +/obj/item/stack/material/rods/mapped/steel/ten, +/obj/item/stack/material/rods/mapped/steel/ten, +/obj/item/stack/material/rods/mapped/steel/ten, /obj/item/stack/material/pane/mapped/glass/ten, /obj/item/stack/material/pane/mapped/glass/ten, /obj/item/stack/material/pane/mapped/glass/ten, diff --git a/maps/away/bearcat/bearcat-1.dmm b/maps/away/bearcat/bearcat-1.dmm index 947b876eee6d..8de6dcd2a170 100644 --- a/maps/away/bearcat/bearcat-1.dmm +++ b/maps/away/bearcat/bearcat-1.dmm @@ -2384,7 +2384,7 @@ /obj/item/stack/tape_roll/duct_tape, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/panel/mapped/plastic/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 9fd790d88df5..2af993e9ca40 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -4058,7 +4058,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "hM" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/structure/lattice, /turf/space, /area/ship/scrap/maintenance/atmos) @@ -4542,13 +4542,13 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "iO" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /mob/living/simple_animal/hostile/carp, /turf/space, /area/ship/scrap/maintenance/atmos) "iP" = ( /obj/item/stack/material/sheet/mapped/steel, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/ship/scrap/maintenance/atmos) "iQ" = ( @@ -4686,7 +4686,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "jc" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/structure/lattice, /turf/space, /area/ship/scrap/maintenance/power) @@ -4861,7 +4861,7 @@ /turf/wall, /area/ship/scrap/maintenance/power) "jB" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "jC" = ( @@ -4939,7 +4939,7 @@ /area/ship/scrap/maintenance/engine/aft) "jN" = ( /obj/item/stack/material/sheet/mapped/steel, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/structure/lattice, /turf/space, /area/ship/scrap/maintenance/engine/aft) @@ -5003,7 +5003,7 @@ /turf/floor/plating/airless, /area/ship/scrap/maintenance/engine/aft) "jX" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/ship/scrap/maintenance/engine/aft) "jY" = ( diff --git a/maps/away/casino/casino.dmm b/maps/away/casino/casino.dmm index 44ea8318132f..f9fa8619ce6e 100644 --- a/maps/away/casino/casino.dmm +++ b/maps/away/casino/casino.dmm @@ -152,7 +152,7 @@ /area/casino/casino_bridge) "aw" = ( /obj/item/wirecutters, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "ax" = ( diff --git a/maps/away/liberia/liberia.dmm b/maps/away/liberia/liberia.dmm index e136fdc492c1..b3922665c297 100644 --- a/maps/away/liberia/liberia.dmm +++ b/maps/away/liberia/liberia.dmm @@ -2340,8 +2340,8 @@ /obj/item/stack/material/pane/mapped/glass/fifty, /obj/item/stack/material/pane/mapped/glass/fifty, /obj/item/stack/material/sheet/reinforced/mapped/fiberglass/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/forty, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, @@ -7523,8 +7523,8 @@ /obj/structure/closet/crate, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/panel/mapped/plastic/fifty, /obj/item/stack/material/sheet/reinforced/mapped/titanium/fifty, /obj/item/stack/material/sheet/reinforced/mapped/ocp/fifty, diff --git a/maps/away/lost_supply_base/lost_supply_base.dmm b/maps/away/lost_supply_base/lost_supply_base.dmm index 94c58cd1b3fb..aac57b9b881e 100644 --- a/maps/away/lost_supply_base/lost_supply_base.dmm +++ b/maps/away/lost_supply_base/lost_supply_base.dmm @@ -117,14 +117,14 @@ /turf/floor/plating/airless, /area/space) "aq" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "ar" = ( /obj/structure/cable/yellow{ icon_state = "4-8" }, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel, /turf/floor/plating/airless, /area/space) @@ -760,7 +760,7 @@ /turf/floor/plating/airless, /area/lost_supply_base) "cf" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/item/stack/material/sheet/mapped/steel, /turf/floor/plating/airless, /area/lost_supply_base) diff --git a/maps/away/magshield/magshield.dmm b/maps/away/magshield/magshield.dmm index 9148970f57f8..de625ae93469 100644 --- a/maps/away/magshield/magshield.dmm +++ b/maps/away/magshield/magshield.dmm @@ -106,7 +106,7 @@ /turf/floor/plating/airless, /area/space) "ao" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "ap" = ( @@ -529,7 +529,7 @@ /turf/space, /area/magshield/smes_storage) "bF" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/magshield/smes_storage) "bG" = ( @@ -1265,7 +1265,7 @@ /turf/floor/plating/airless, /area/magshield/north) "dF" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/floor/plating/airless, /area/magshield/north) "dG" = ( @@ -2750,11 +2750,11 @@ /turf/floor/carpet/blue, /area/magshield/west) "ic" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/floor/tiled/dark, /area/magshield/west) "id" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/magshield/west) "ie" = ( @@ -3769,7 +3769,7 @@ /turf/floor/tiled, /area/magshield/south) "lg" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/magshield/south) "lh" = ( diff --git a/maps/exodus/exodus-1.dmm b/maps/exodus/exodus-1.dmm index baed72f3aaea..df58cbab4de9 100644 --- a/maps/exodus/exodus-1.dmm +++ b/maps/exodus/exodus-1.dmm @@ -2424,7 +2424,7 @@ /turf/floor/plating, /area/exodus/maintenance/sub/central) "gJ" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "gK" = ( diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 5e3a75b879eb..ea0d6d4886c2 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -422,7 +422,7 @@ /turf/floor/tiled, /area/ministation/cargo) "cd" = ( -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /turf/floor/plating/airless, /area/space) "ce" = ( @@ -4268,8 +4268,8 @@ /obj/structure/closet/crate, /obj/item/stack/material/sheet/shiny/mapped/aluminium/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, /obj/item/stack/material/pane/mapped/glass/fifty, @@ -4755,7 +4755,7 @@ /turf/floor/tiled/monotile, /area/ministation/atmospherics) "va" = ( -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/pane/mapped/glass/fifty, /obj/item/stock_parts/circuitboard/airlock_electronics, /obj/item/stock_parts/circuitboard/airlock_electronics, @@ -8632,7 +8632,7 @@ /obj/structure/table, /obj/item/folder/yellow, /obj/item/clothing/head/earmuffs, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/cash/scavbucks, /turf/floor/tiled, /area/ministation/engine) diff --git a/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm b/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm index 29714944a166..13485f43f126 100644 --- a/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm +++ b/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm @@ -3,11 +3,11 @@ /turf/template_noop, /area/template_noop) "ac" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/template_noop, /area/template_noop) "ad" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/item/stack/material/sheet/reinforced/mapped/ocp, /turf/template_noop, /area/template_noop) diff --git a/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm b/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm index ed155d0788ad..ff7f8833e260 100644 --- a/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm +++ b/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm @@ -297,10 +297,10 @@ /obj/machinery/door/firedoor, /obj/structure/wall_frame/hull, /obj/structure/grille, -/obj/item/stack/material/rods, -/obj/item/stack/material/rods, -/obj/item/stack/material/rods, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, +/obj/item/stack/material/rods/mapped/steel, +/obj/item/stack/material/rods/mapped/steel, +/obj/item/stack/material/rods/mapped/steel, /obj/item/shard, /obj/item/shard, /obj/item/shard, diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index 808e62d51a2c..1d8a6189f4d1 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -7421,10 +7421,10 @@ /obj/structure/railing/mapped, /obj/effect/floor_decal/industrial/warning/dust, /obj/structure/rack, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/tool/pickaxe, /obj/item/tool/pickaxe, /obj/item/tool/shovel, diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index e72c0805f176..7b18e350e4b2 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -3279,7 +3279,7 @@ /obj/item/stack/tape_roll/duct_tape, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/panel/mapped/plastic/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, diff --git a/maps/tradeship/tradeship-3.dmm b/maps/tradeship/tradeship-3.dmm index eba7b7f15ab4..1a4f81e3fb31 100644 --- a/maps/tradeship/tradeship-3.dmm +++ b/maps/tradeship/tradeship-3.dmm @@ -870,7 +870,7 @@ /turf/floor/tiled/techfloor/grid, /area/ship/trade/comms) "yM" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/machinery/atmospherics/unary/vent_scrubber/on, /turf/floor/plating, /area/ship/trade/bridge_unused) diff --git a/mods/content/dungeon_loot/subtypes/maint.dm b/mods/content/dungeon_loot/subtypes/maint.dm index 1063d1d165f8..cea9732ac81c 100644 --- a/mods/content/dungeon_loot/subtypes/maint.dm +++ b/mods/content/dungeon_loot/subtypes/maint.dm @@ -146,7 +146,7 @@ /obj/item/food/old/hotdog, /obj/item/food/old/pizza, /obj/item/ammo_casing, - /obj/item/stack/material/rods/ten, + /obj/item/stack/material/rods/mapped/steel/ten, /obj/item/stack/material/sheet/mapped/steel/five, /obj/item/stack/material/cardstock/mapped/cardboard/five, /obj/item/poster, diff --git a/mods/content/government/away_sites/icarus/icarus-1.dmm b/mods/content/government/away_sites/icarus/icarus-1.dmm index 1be37c41159b..41b34e8350cf 100644 --- a/mods/content/government/away_sites/icarus/icarus-1.dmm +++ b/mods/content/government/away_sites/icarus/icarus-1.dmm @@ -1442,7 +1442,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "eR" = ( @@ -1492,7 +1492,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "eW" = ( @@ -1518,7 +1518,7 @@ /area/icarus/open) "eZ" = ( /obj/effect/decal/cleanable/dirt, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fa" = ( @@ -1589,7 +1589,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fh" = ( @@ -1604,13 +1604,13 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fj" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fk" = ( @@ -1669,7 +1669,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fq" = ( diff --git a/mods/gamemodes/heist/heist_base.dmm b/mods/gamemodes/heist/heist_base.dmm index 737f150b57eb..a98ec66d5293 100644 --- a/mods/gamemodes/heist/heist_base.dmm +++ b/mods/gamemodes/heist/heist_base.dmm @@ -1149,7 +1149,7 @@ "dB" = ( /obj/structure/rack, /obj/item/gun/launcher/bow/crossbow/powered, -/obj/item/stack/material/rods/ten, +/obj/item/stack/material/rods/mapped/steel/ten, /obj/machinery/light/small{ dir = 8 }, diff --git a/mods/species/ascent/mobs/drone.dm b/mods/species/ascent/mobs/drone.dm index f2f94d499dea..2294585ee151 100644 --- a/mods/species/ascent/mobs/drone.dm +++ b/mods/species/ascent/mobs/drone.dm @@ -35,7 +35,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -96,7 +95,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/nebula.dme b/nebula.dme index bff888b711e5..f1e76027c300 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2234,10 +2234,10 @@ #include "code\modules\crafting\stack_recipes\recipes_panels.dm" #include "code\modules\crafting\stack_recipes\recipes_planks.dm" #include "code\modules\crafting\stack_recipes\recipes_reinforced.dm" +#include "code\modules\crafting\stack_recipes\recipes_rods.dm" #include "code\modules\crafting\stack_recipes\recipes_soft.dm" #include "code\modules\crafting\stack_recipes\recipes_stacks.dm" #include "code\modules\crafting\stack_recipes\recipes_steel.dm" -#include "code\modules\crafting\stack_recipes\recipes_struts.dm" #include "code\modules\crafting\stack_recipes\recipes_textiles.dm" #include "code\modules\crafting\stack_recipes\recipes_turfs.dm" #include "code\modules\crafting\working\_working.dm" diff --git a/tools/map_migrations/4651_rods.txt b/tools/map_migrations/4651_rods.txt new file mode 100644 index 000000000000..e7d5bffba5e9 --- /dev/null +++ b/tools/map_migrations/4651_rods.txt @@ -0,0 +1,2 @@ +/obj/item/stack/material/rods/@SUBTYPES : /obj/item/stack/material/rods/mapped/steel/@SUBTYPES{@OLD} + From 75f6d6c55da8740ef93087bd56ba44af84d1ce11 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 19 Dec 2024 03:10:55 -0500 Subject: [PATCH 0094/1331] Add particle-based rain --- code/game/turfs/turf.dm | 1 + code/modules/weather/_weather.dm | 39 +++++++++++++++ code/modules/weather/weather_fsm_states.dm | 53 +++++++++++++++++++-- code/modules/weather/weather_init.dm | 4 ++ code/modules/weather/weather_wind.dm | 5 ++ icons/effects/weather.dmi | Bin 11805 -> 12238 bytes 6 files changed, 99 insertions(+), 3 deletions(-) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index a034ac913ee2..18bd63adae10 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -637,6 +637,7 @@ LAZYDISTINCTADD(., air_graphic) if(length(weather?.vis_contents_additions)) LAZYADD(., weather.vis_contents_additions) + . += pick(weather.particle_sources) // we know . is never null here if(flooded) var/flood_object = get_flood_overlay(flooded) if(flood_object) diff --git a/code/modules/weather/_weather.dm b/code/modules/weather/_weather.dm index dd78d0d269b2..9c1a6493a533 100644 --- a/code/modules/weather/_weather.dm +++ b/code/modules/weather/_weather.dm @@ -43,6 +43,13 @@ var/obj/abstract/lightning_overlay/lightning_overlay // A visible atom used for animated lighting effects. var/tmp/list/vis_contents_additions // Holder for a list used to add required atoms to turf vis_contents. + /// A list of particle sources to randomize particle-based effects per-turf. + var/list/obj/abstract/weather_particles/particle_sources = newlist( + /obj/abstract/weather_particles, + /obj/abstract/weather_particles, + /obj/abstract/weather_particles, + /obj/abstract/weather_particles + ) // Main heartbeat proc, called by SSweather. /obj/abstract/weather_system/proc/tick() @@ -106,3 +113,35 @@ invisibility = INVISIBILITY_NONE is_spawnable_type = FALSE appearance_flags = RESET_COLOR | KEEP_APART + +// Dummy object for weather particles. +/obj/abstract/weather_particles + // plane = EMISSIVE_PLANE + // layer = ABOVE_LIGHTING_LAYER + icon = null + invisibility = INVISIBILITY_NONE + is_spawnable_type = FALSE + appearance_flags = RESET_COLOR | KEEP_APART + layer = ABOVE_HUMAN_LAYER + +/obj/abstract/weather_particles/proc/update_particle_system(obj/abstract/weather_system/holder) + if(!istype(particles, /particles/weather)) + return + var/particles/weather/weather_particles = particles + weather_particles.color = holder.color // sync color + alpha = holder.alpha // sync alpha + // reset rotation and velocity + weather_particles.rotation = 0 + weather_particles.velocity = generator("vector", weather_particles.base_velocity[1], weather_particles.base_velocity[2], NORMAL_RAND) + if(holder.wind_direction != 0 && holder.wind_strength != 0) // direction is set + // rain always falls down, but if the wind is east or west + // then it also gets a little bit of side momentum + // based on the horizontal component of the direction + var/wind_angle = 90 - dir2angle(holder.wind_direction) // byond's coordinate axis is fucky + var/x_wind_vel = cos(wind_angle) * holder.wind_strength + var/z_wind_vel = sin(wind_angle) * holder.wind_strength // experimental! + // tilt to an angle that makes sense for our min/max velocity + // 0 is south, but if our velocity is pure south we get -90, so add 90 + // and then invert it, because byond uses counter-clockwise and we want clockwise + weather_particles.rotation = generator("num", 90 - arctan(x_wind_vel * 0.50, weather_particles.base_velocity[1][2]), 90 - arctan(x_wind_vel, weather_particles.base_velocity[2][2]), NORMAL_RAND) + weather_particles.velocity += generator("vector", list(0, 0, 0), list(x_wind_vel, 0, z_wind_vel), NORMAL_RAND) \ No newline at end of file diff --git a/code/modules/weather/weather_fsm_states.dm b/code/modules/weather/weather_fsm_states.dm index 6c80309d3574..e44f43efa8e9 100644 --- a/code/modules/weather/weather_fsm_states.dm +++ b/code/modules/weather/weather_fsm_states.dm @@ -12,8 +12,9 @@ var/icon = 'icons/effects/weather.dmi' var/icon_state + var/particles/weather/particle_system - var/alpha = 210 + var/alpha = 170 var/minimum_time = 2 MINUTES var/maximum_time = 10 MINUTES var/is_liquid = FALSE @@ -22,6 +23,30 @@ var/list/ambient_sounds var/list/ambient_indoors_sounds +/particles/weather + width = 32 + height = 32 + bound1 = list(-16, -16, -20) + bound2 = list(20, 20, 20) + count = 100 + spawning = 2 + lifespan = 2 SECONDS // they'll hopefully hit the bounds long before this runs out + // basic 3d projection matrix + // 16px in the z axis = 1 in the y axis, because perspective memes i guess? + transform = list( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 1/16, 0, 0, + 0, 0, 0, 1, + ) + fadein = 1 + position = generator("box", list(-16, 16, -16), list(20, 20, 20)) // start at the top in the Y axis + /// How much does (east/west) wind affect the horizontal component of the particles? + var/wind_intensity = 2 + /// What is the non-wind-affected velocity component of the particles? + /// A list of two lists (minimum and maximum velocities) passed to a generator. + var/list/base_velocity = list(list(0, -6, 0), list(0, -10, 0)) + /decl/state/weather/entered_state(datum/holder) . = ..() @@ -41,6 +66,15 @@ else weather.color = COLOR_WHITE + if(ispath(particle_system)) + for(var/obj/abstract/weather_particles/particle_source in weather.particle_sources) + particle_source.particles = new particle_system // separate datums so that you could make some turfs have special effects in the future + weather.update_particle_system() // sync wind, etc. + else + for(var/obj/abstract/weather_particles/particle_source in weather.particle_sources) + if(particle_source.particles) + QDEL_NULL(particle_source.particles) + /decl/state/weather/proc/tick(var/obj/abstract/weather_system/weather) return @@ -148,7 +182,8 @@ /decl/state/weather/rain name = "Light Rain" - icon_state = "rain" + icon_state = null//"rain" + particle_system = /particles/weather/rain descriptor = "It is raining gently." cosmetic_span_class = "notice" is_liquid = TRUE @@ -162,13 +197,19 @@ protected_messages = list("Raindrops patter against $ITEM$.") var/list/roof_messages = list("Rain patters against the roof.") +/particles/weather/rain + icon = 'icons/effects/weather.dmi' + icon_state = "rain_particle" // animated particles don't seem to work... + wind_intensity = 1 + /decl/state/weather/rain/handle_roofed_effects(var/mob/living/M, var/obj/abstract/weather_system/weather) if(length(roof_messages) && prob(cosmetic_message_chance)) to_chat(M, "[pick(roof_messages)]") /decl/state/weather/rain/storm name = "Heavy Rain" - icon_state = "storm" + icon_state = null // "storm" + particle_system = /particles/weather/rain/storm descriptor = "It is raining heavily." cosmetic_span_class = "warning" transitions = list( @@ -180,6 +221,11 @@ roof_messages = list("Torrential rain thunders against the roof.") ambient_sounds = list('sound/effects/weather/rain_heavy.ogg') +/particles/weather/rain/storm + wind_intensity = 3 + spawning = 5 + count = 200 + /decl/state/weather/rain/storm/tick(var/obj/abstract/weather_system/weather) ..() if(prob(0.5)) @@ -188,6 +234,7 @@ /decl/state/weather/rain/hail name = "Hail" icon_state = "hail" + particle_system = null descriptor = "It is hailing." cosmetic_span_class = "danger" is_liquid = FALSE diff --git a/code/modules/weather/weather_init.dm b/code/modules/weather/weather_init.dm index 276b6ce614a1..48b8e16f3cd2 100644 --- a/code/modules/weather/weather_init.dm +++ b/code/modules/weather/weather_init.dm @@ -6,6 +6,10 @@ INITIALIZE_IMMEDIATE(/obj/abstract/weather_system) set_invisibility(INVISIBILITY_NONE) + if(prob(20)) // arbitrary chance to already have some degree of wind when the weather system starts + wind_direction = pick(global.alldirs) + wind_strength = rand(1,5) + banned_weather_conditions = banned // Bookkeeping/rightclick guards. diff --git a/code/modules/weather/weather_wind.dm b/code/modules/weather/weather_wind.dm index c363f20e36e9..dc95fdd4f731 100644 --- a/code/modules/weather/weather_wind.dm +++ b/code/modules/weather/weather_wind.dm @@ -23,6 +23,11 @@ wind_direction = turn(wind_direction, 180) if(old_strength != wind_strength) mob_shown_wind.Cut() + update_particle_system() + +/obj/abstract/weather_system/proc/update_particle_system() + for(var/obj/abstract/weather_particles/particle_source in particle_sources) + particle_source.update_particle_system(src) /obj/abstract/weather_system/proc/show_wind(var/mob/M, var/force = FALSE) var/mob_ref = weakref(M) diff --git a/icons/effects/weather.dmi b/icons/effects/weather.dmi index 01aa9d456f4208a31f3f3eabcace8c2072008c79..aa4438a638dc8cac216050ccc45eb43e06c5fa59 100644 GIT binary patch literal 12238 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 literal 11805 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 From 5c034727c7feda95e170b2d163cdc6c6eb99fb65 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 10 Dec 2024 00:21:39 -0500 Subject: [PATCH 0095/1331] Fix strings that misspell separate as seperate --- code/game/turfs/walls/_wall.dm | 2 +- code/modules/clothing/spacesuits/rig/modules/utility.dm | 2 +- code/modules/codex/entries/atmospherics.dm | 2 +- code/modules/reagents/reactions/reaction_compounds.dm | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 084fb135e175..9689cf629aee 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -18,7 +18,7 @@ var/global/list/wall_fullblend_objects = list( /turf/wall name = "wall" - desc = "A huge chunk of metal used to seperate rooms." + desc = "A huge chunk of metal used to separate rooms." icon = 'icons/turf/walls/_previews.dmi' icon_state = "solid" opacity = TRUE diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index b3d1533d3b63..7626ac00c32e 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -368,7 +368,7 @@ deactivate_string = "Deactivate Thrusters" interface_name = "maneuvering jets" - interface_desc = "An inbuilt EVA maneuvering system that runs off a seperate gas supply." + interface_desc = "An inbuilt EVA maneuvering system that runs off a separate gas supply." origin_tech = @'{"materials":6,"engineering":7}' material = /decl/material/solid/metal/steel matter = list( diff --git a/code/modules/codex/entries/atmospherics.dm b/code/modules/codex/entries/atmospherics.dm index ccc05138738d..5b0b67fb8fbf 100644 --- a/code/modules/codex/entries/atmospherics.dm +++ b/code/modules/codex/entries/atmospherics.dm @@ -19,7 +19,7 @@ //Normal valves /datum/codex_entry/atmos_valve associated_paths = list(/obj/machinery/atmospherics/valve) - mechanics_text = "Click this to turn the valve. If red, the pipes on each end are seperated. Otherwise, they are connected." + mechanics_text = "Click this to turn the valve. If red, the pipes on each end are separated. Otherwise, they are connected." disambiguator = "atmospherics" available_to_map_tech_level = MAP_TECH_LEVEL_SPACE diff --git a/code/modules/reagents/reactions/reaction_compounds.dm b/code/modules/reagents/reactions/reaction_compounds.dm index a2126b561d77..528b85dbcbce 100644 --- a/code/modules/reagents/reactions/reaction_compounds.dm +++ b/code/modules/reagents/reactions/reaction_compounds.dm @@ -151,7 +151,7 @@ name = "Condensed Capsaicin" minimum_temperature = 100 CELSIUS maximum_temperature = 200 CELSIUS // To avoid cooking chili creating condensed capsaicin. - mix_message = "darkens and thickens as it seperates from its water content" + mix_message = "darkens and thickens as it separates from its water content" required_reagents = list(/decl/material/liquid/capsaicin = 2) result = list(/decl/material/liquid/capsaicin/condensed = 1) From bf0700f25c1b81fa11e572f54e6176dee3cc21b6 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 10 Dec 2024 00:24:17 -0500 Subject: [PATCH 0096/1331] Update cabinet sprites --- icons/obj/closets/bases/cabinet.dmi | Bin 1904 -> 2350 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/obj/closets/bases/cabinet.dmi b/icons/obj/closets/bases/cabinet.dmi index 236b164ca6efcaf049aac254340983f4fb938b26..603874dd3c1891d1178073dd336c580505df5289 100644 GIT binary patch delta 2157 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 From ecbae5b87ab171d17d7fb1e0c9aea397ee086535 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 10 Dec 2024 00:24:05 -0500 Subject: [PATCH 0097/1331] Add wattle, daubed, plastered, and timber-framed walls --- code/game/turfs/walls/_wall.dm | 2 + code/game/turfs/walls/wall_icon.dm | 27 ++- code/game/turfs/walls/wall_wattle.dm | 158 ++++++++++++++++++ code/modules/materials/_material_stack.dm | 5 + code/modules/materials/_materials.dm | 17 +- .../solids/materials_solid_wood.dm | 8 + code/unit_tests/turf_icons.dm | 13 +- icons/turf/walls/_previews.dmi | Bin 4162 -> 6424 bytes icons/turf/walls/plaster.dmi | Bin 0 -> 1911 bytes icons/turf/walls/reinforced_timber.dmi | Bin 0 -> 1512 bytes icons/turf/walls/reinforced_timber_alt_1.dmi | Bin 0 -> 1668 bytes icons/turf/walls/reinforced_timber_alt_2.dmi | Bin 0 -> 1643 bytes icons/turf/walls/reinforced_timber_alt_3.dmi | Bin 0 -> 1645 bytes icons/turf/walls/reinforced_timber_alt_4.dmi | Bin 0 -> 1655 bytes icons/turf/walls/square_shutter.dmi | Bin 0 -> 951 bytes icons/turf/walls/wattle.dmi | Bin 0 -> 2996 bytes icons/turf/walls/wattledaub.dmi | Bin 0 -> 4213 bytes nebula.dme | 1 + 18 files changed, 214 insertions(+), 17 deletions(-) create mode 100644 code/game/turfs/walls/wall_wattle.dm create mode 100644 icons/turf/walls/plaster.dmi create mode 100644 icons/turf/walls/reinforced_timber.dmi create mode 100644 icons/turf/walls/reinforced_timber_alt_1.dmi create mode 100644 icons/turf/walls/reinforced_timber_alt_2.dmi create mode 100644 icons/turf/walls/reinforced_timber_alt_3.dmi create mode 100644 icons/turf/walls/reinforced_timber_alt_4.dmi create mode 100644 icons/turf/walls/square_shutter.dmi create mode 100644 icons/turf/walls/wattle.dmi create mode 100644 icons/turf/walls/wattledaub.dmi diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 9689cf629aee..973d7105635d 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -51,6 +51,8 @@ var/global/list/wall_fullblend_objects = list( var/handle_structure_blending = TRUE var/min_dismantle_amount = 2 var/max_dismantle_amount = 2 + /// The reinforcement icon to use. Set in update_material() based on reinf_material. + var/reinf_icon /// Icon to use if shutter state is non-null. var/shutter_icon = 'icons/turf/walls/shutter.dmi' diff --git a/code/game/turfs/walls/wall_icon.dm b/code/game/turfs/walls/wall_icon.dm index c682d4f266fe..08732bbecaf9 100644 --- a/code/game/turfs/walls/wall_icon.dm +++ b/code/game/turfs/walls/wall_icon.dm @@ -12,8 +12,12 @@ if(material) explosion_resistance = material.explosion_resistance hitsound = material.hitsound - if(reinf_material && reinf_material.explosion_resistance > explosion_resistance) - explosion_resistance = reinf_material.explosion_resistance + if(reinf_material) + reinf_icon = islist(reinf_material.icon_reinf) ? pick(reinf_material.icon_reinf) : reinf_material.icon_reinf + if(reinf_material.explosion_resistance > explosion_resistance) + explosion_resistance = reinf_material.explosion_resistance + else + reinf_icon = null update_strings() refresh_opacity() SSradiation.resistance_cache.Remove(src) @@ -52,7 +56,16 @@ . = (istype(material) && material.icon_base) || 'icons/turf/walls/solid.dmi' /turf/wall/proc/apply_reinf_overlay() - . = istype(reinf_material) + . = istype(reinf_material) && reinf_icon + +/// Gets the base wall colour for icon rendering. Can be overridden on wall subtypes. Not equivalent to get_color(). +/// Should only be used in places where material is known to be set, e.g. update_wall_icon(). +/turf/wall/proc/get_base_color() + return material.color + +/// Gets the reinforcement colour. Can be overridden so that some wall types don't apply paint colour to their reinforcements. +/turf/wall/proc/get_reinf_color() + return paint_color || reinf_material?.color /turf/wall/proc/refresh_connections() if(wall_connections && other_connections) @@ -100,7 +113,7 @@ /turf/wall/proc/update_wall_icon() var/material_icon_base = get_wall_icon() - var/base_color = material.color + var/base_color = get_base_color() var/new_icon var/new_icon_state @@ -124,17 +137,17 @@ if(apply_reinf_overlay()) var/image/I - var/reinf_color = paint_color ? paint_color : reinf_material.color + var/reinf_color = get_reinf_color() if(construction_stage != null && construction_stage < 6) I = image('icons/turf/walls/_construction_overlays.dmi', "[construction_stage]") I.color = reinf_color add_overlay(I) else if(reinf_material.use_reinf_state) - I = image(reinf_material.icon_reinf, reinf_material.use_reinf_state) + I = image(reinf_icon, reinf_material.use_reinf_state) I.color = reinf_color else - I = image(_get_wall_subicon(reinf_material.icon_reinf, wall_connections, reinf_color)) + I = image(_get_wall_subicon(reinf_icon, wall_connections, reinf_color)) add_overlay(I) // Update icon on ambient light change, for shutter overlays. diff --git a/code/game/turfs/walls/wall_wattle.dm b/code/game/turfs/walls/wall_wattle.dm new file mode 100644 index 000000000000..42a93e90fc08 --- /dev/null +++ b/code/game/turfs/walls/wall_wattle.dm @@ -0,0 +1,158 @@ +/turf/wall/wattle + icon_state = "wattle" + material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color + girder_material = null + floor_type = /turf/floor/dirt + min_dismantle_amount = 3 + max_dismantle_amount = 5 + shutter_icon = 'icons/turf/walls/square_shutter.dmi' + var/decl/skill/daubing_skill = SKILL_CONSTRUCTION + var/decl/material/daubing_material // todo: daubing as a material made from clay/soil and plant matter? + var/const/matter_to_daub = MATTER_AMOUNT_REINFORCEMENT + // Currently, plastering is done via painting... undecided if that should change in the future. + +/turf/wall/wattle/Initialize(ml, materialtype, rmaterialtype) + if(ispath(daubing_material)) + daubing_material = GET_DECL(daubing_material) + return ..() + +/turf/wall/wattle/get_turf_validation_corner_states() + return list("", "paint") // paint should always be available because of plastering! + +// Daubing with clay or soil +/turf/wall/wattle/attackby(obj/item/W, mob/user, click_params) + if(isnull(daubing_material)) + var/static/list/daub_materials = list( // Does not include subtypes. + /decl/material/solid/soil = TRUE, + /decl/material/solid/clay = TRUE + ) + if(istype(W, /obj/item/stack/material) && daub_materials[W.material?.type]) + if(!user.check_dexterity(DEXTERITY_WIELD_ITEM)) + return TRUE + var/obj/item/stack/material/stack = W + var/sheets_to_use = stack.matter_units_to_sheets(matter_to_daub) + if(stack.can_use(sheets_to_use) && user.do_skilled(1 SECOND, daubing_skill, target = src) && stack.can_use(sheets_to_use)) + to_chat(user, SPAN_NOTICE("You daub \the [src] with \the [stack].")) + daubing_material = stack.material + stack.use(sheets_to_use) + else if(stack.can_use(sheets_to_use)) // failed the do_skilled + to_chat(user, SPAN_WARNING("You have to stay still to daub \the [src] with \the [stack].")) + else + to_chat(user, SPAN_WARNING("You need [stack.get_string_for_amount(sheets_to_use)] to daub \the [src].")) + return TRUE + return ..() + +/turf/wall/wattle/get_dismantle_stack_type() + return /obj/item/stack/material/log // temp? + +// daubed walls have the color of their daubing +/turf/wall/wattle/get_base_color() + if(daubing_material) + return "#ae9f70" // daubing_material.color // sorry, but using the daubing material color looks bad + return ..() + +// don't plaster over our damn reinforcements +/turf/wall/wattle/get_reinf_color() + return reinf_material?.color + +/turf/wall/wattle/get_wall_icon() + if(isnull(daubing_material)) + return 'icons/turf/walls/wattle.dmi' + else + return 'icons/turf/walls/wattledaub.dmi' + +/turf/wall/wattle/get_dismantle_sound() + return 'sound/foley/wooden_drop.ogg' + +/turf/wall/wattle/update_strings() + if(isnull(daubing_material)) + if(reinf_material) + SetName("[reinf_material.solid_name]-framed [material.adjective_name] wattle wall") + desc = "A wattle wall made of [material.adjective_name] strips and framed with [reinf_material.solid_name]." + else + SetName("[material.solid_name] wattle wall") + desc = "A wattle wall made of [material.adjective_name] strips." + else if(paint_color) + if(reinf_material) + SetName("[reinf_material.solid_name]-framed plastered wall") + else + SetName("plastered wall") + else + if(reinf_material) + SetName("[reinf_material.solid_name]-framed [material.adjective_name] wattle and daub wall") + desc = "A daubed wattle wall made of [material.adjective_name] strips and framed with [reinf_material.solid_name]." + else + SetName("[material.solid_name] wattle and daub wall") + desc = "A daubed wattle wall made of [material.adjective_name] strips." + +/turf/wall/wattle/daubed + icon_state = "wattledaub" + daubing_material = /decl/material/solid/clay + // the daub is lost when destroyed/deconstructed, since it's dried anyway + +/turf/wall/wattle/daubed/plastered + icon_state = "plaster" + paint_color = "#c2b8a1" // this is what applies the plaster... icky + color = "#c2b8a1" // preview color for plaster + +/turf/wall/wattle/daubed/plastered/framed + icon_state = "framed" + reinf_material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color // preview, still painted + +// Subtypes. +#define WATTLE_WALL_SUBTYPE(material_name) \ +/turf/wall/wattle/##material_name { \ + material = /decl/material/solid/organic/wood/##material_name; \ + color = /decl/material/solid/organic/wood/##material_name::color; \ +}; \ +/turf/wall/wattle/##material_name/shutter { \ + shutter_state = FALSE; \ + icon_state = "wattle_shutter"; \ +}; \ +/turf/wall/wattle/##material_name/shutter/open { \ + shutter_state = TRUE; \ +}; \ +/turf/wall/wattle/daubed/##material_name { \ + material = /decl/material/solid/organic/wood/##material_name; \ + color = /decl/material/solid/organic/wood/##material_name::color; \ +}; \ +/turf/wall/wattle/daubed/##material_name/shutter { \ + shutter_state = FALSE; \ + icon_state = "wattle_shutter"; \ +}; \ +/turf/wall/wattle/daubed/##material_name/shutter/open { \ + shutter_state = TRUE; \ +}; \ +/turf/wall/wattle/daubed/plastered/##material_name { \ + material = /decl/material/solid/organic/wood/##material_name; \ +}; \ +/turf/wall/wattle/daubed/plastered/##material_name/shutter { \ + shutter_state = FALSE; \ + icon_state = "wattle_shutter"; \ +}; \ +/turf/wall/wattle/daubed/plastered/##material_name/shutter/open { \ + shutter_state = TRUE; \ +}; \ +/turf/wall/wattle/daubed/plastered/framed/##material_name { \ + material = /decl/material/solid/organic/wood/##material_name; \ + reinf_material = /decl/material/solid/organic/wood/##material_name; \ + color = /decl/material/solid/organic/wood/##material_name::color; \ +}; \ +/turf/wall/wattle/daubed/plastered/framed/##material_name/shutter { \ + shutter_state = FALSE; \ + icon_state = "wattle_shutter"; \ +}; \ +/turf/wall/wattle/daubed/plastered/framed/##material_name/shutter/open { \ + shutter_state = TRUE; \ +} +WATTLE_WALL_SUBTYPE(fungal) +WATTLE_WALL_SUBTYPE(ebony) +WATTLE_WALL_SUBTYPE(walnut) +WATTLE_WALL_SUBTYPE(maple) +WATTLE_WALL_SUBTYPE(mahogany) +WATTLE_WALL_SUBTYPE(bamboo) +WATTLE_WALL_SUBTYPE(yew) + +#undef WATTLE_WALL_SUBTYPE \ No newline at end of file diff --git a/code/modules/materials/_material_stack.dm b/code/modules/materials/_material_stack.dm index ac7fc8ae4492..f5286e91141f 100644 --- a/code/modules/materials/_material_stack.dm +++ b/code/modules/materials/_material_stack.dm @@ -219,3 +219,8 @@ . += " [singular_name]" return indefinite_article ? "[indefinite_article] [.]" : ADD_ARTICLE(.) return "[amount] [.] [plural_name]" + +/obj/item/stack/material/proc/matter_units_to_sheets(used) + if(!material || get_reinforced_material()) + return 0 + return ceil(used / matter_per_piece[material.type]) \ No newline at end of file diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 8a6a4d3b1ae1..f4a057dd4115 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -125,6 +125,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) // Icons var/icon_base = 'icons/turf/walls/solid.dmi' var/icon_base_natural = 'icons/turf/walls/natural.dmi' + /// Either the icon used for reinforcement, or a list of icons to pick from. var/icon_reinf = 'icons/turf/walls/reinforced_metal.dmi' var/wall_flags = 0 var/list/wall_blend_icons = list() // Which wall icon types walls of this material type will consider blending with. Assoc list (icon path = TRUE/FALSE) @@ -563,13 +564,15 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) . += "'[icon_base_natural]' - missing natural shine icon state 'shine[i]'" if(icon_reinf) - if(use_reinf_state) - if(!check_state_in_icon(use_reinf_state, icon_reinf)) - . += "'[icon_reinf]' - missing reinf icon state '[use_reinf_state]'" - else - for(var/i = 0 to 7) - if(!check_state_in_icon("[i]", icon_reinf)) - . += "'[icon_reinf]' - missing directional reinf icon state '[i]'" + var/list/all_reinf_icons = islist(icon_reinf) ? icon_reinf : list(icon_reinf) + for(var/sub_icon in all_reinf_icons) + if(use_reinf_state) + if(!check_state_in_icon(use_reinf_state, sub_icon)) + . += "'[sub_icon]' - missing reinf icon state '[use_reinf_state]'" + else + for(var/i = 0 to 7) + if(!check_state_in_icon(num2text(i), sub_icon)) + . += "'[sub_icon]' - missing directional reinf icon state '[i]'" if(length(color) != 7) . += "invalid color (not #RRGGBB)" diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index ac5c1372920a..e4cb6575b1c3 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -12,6 +12,14 @@ 'icons/turf/walls/log.dmi' = TRUE, 'icons/turf/walls/metal.dmi' = TRUE ) + icon_reinf = list( + 'icons/turf/walls/reinforced_timber.dmi', + 'icons/turf/walls/reinforced_timber_alt_1.dmi', + 'icons/turf/walls/reinforced_timber_alt_2.dmi', + 'icons/turf/walls/reinforced_timber_alt_3.dmi', + 'icons/turf/walls/reinforced_timber_alt_4.dmi' + ) + use_reinf_state = null table_icon_base = "wood" bench_icon = 'icons/obj/structures/wood_benches.dmi' pew_icon = 'icons/obj/structures/wood_pews.dmi' diff --git a/code/unit_tests/turf_icons.dm b/code/unit_tests/turf_icons.dm index e0b6a7e17ae1..5cf5cb9dce72 100644 --- a/code/unit_tests/turf_icons.dm +++ b/code/unit_tests/turf_icons.dm @@ -127,15 +127,22 @@ ) /turf/wall/proc/get_turf_validation_corner_states() - . = list("", "other") - if(paint_color) + . = list("") + if(!material) + CRASH("[type] lacks a material!") + if(material?.wall_flags & WALL_HAS_EDGES) + . |= "other" + if(paint_color || (material?.wall_flags & PAINT_PAINTABLE)) . |= "paint" - if(stripe_color) + if(stripe_color || (material?.wall_flags & PAINT_STRIPABLE)) . |= "stripe" /turf/wall/natural/get_turf_validation_corner_states() return list("", "shine") +/turf/wall/log/get_turf_validation_corner_states() + return list("", "other") + /turf/wall/validate_turf() // Walls generate their own icons, icon/icon_state are largely irrelevant other than map previews. diff --git a/icons/turf/walls/_previews.dmi b/icons/turf/walls/_previews.dmi index 02e2637c0c77f5c1655d2d83394cb327e5628dd6..59e03e52b4d6c9868d9761dc09c80c5bbbf79b65 100644 GIT binary patch literal 6424 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 msFdsLULF4R34G(__5TA*OVnnEj99?{0000 literal 4162 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 literal 0 HcmV?d00001 diff --git a/icons/turf/walls/reinforced_timber.dmi b/icons/turf/walls/reinforced_timber.dmi new file mode 100644 index 0000000000000000000000000000000000000000..46e537f04e0847544f60e45c76a0edd0ad641cf6 GIT binary patch literal 1512 zcmX|Bdo+}J82)`T!(eD!Yh*N|6pf|Ps3ezfWC|Xgc zvLw0IOono4sNABqjub1ai)37yjM>?>Xa9KL=e+Oxoag!DIp2#rSGjQ=T>N_l*t>^gE7 z^Ib-UV%(hF0X+3GNH$zN@8KO|7Z~jy6&eu}8h!?VxMFv&h^W{lk$c44!X)KqW6HC> z=k9f$)B)P~`=6dWZGQryGB355)7)r7G)Hpr`|QWr9%T0?aC!!9>%B?>H!*`&V`#cZ z>+x>8N~(Tk$3>2ke-gz>;Wl+^Wk)7QDLRRg9i={BuXbD6)nmI+i?|g@)&^;DtY-9G zZV~{PcMf)RkGSH`MeWY7VbxUQvWOj8{Lvo!R##MwIeKq}qF1iYp_>iG)gwZj$6jnf z$lIo~^#sk?KATrQhdtu5igXjkbmN0p9n={|7rP2$E_^tL4Gy&^4t%v3m-jT=jDO)W zDA*;h%6K$;@73b+>BW8No0ao@&ZbkMYyErf0mji}Y9 zA0DY82>8SZS8RT(>3nNha}OcfMCDs$KF{i*Z`WO)uEsgxpxI{!h<~#FQCppwZgNMe z!A^l+hi~#cpU`u%zjXlqzDWy|F=X(8%wliXARBZqiBC1Hu%^C3KKT#uE7<*W8ME$pR4;qnlqF3s470g z=%z(W`ziO^SY2hf5z`>1D}6lUsUDG zb4C4-ju=W

    eE5DpecT32=}em#gb)PTe9od{wo7S;@~5m^Dk2$&a*nad7lejEv7q zeiJ85S(B|f*ZsdZpD|eM^jF%U$UeYbu(?HYgh#1I_2qcR83ubeD=A*^;QbIGTt z*Sv4JOsAm8gZNtsU65f36HKW~nBsfD(LfVPx3kGv#S=LPMQ}%5QfYh~FJ*a$w{=`vVqQLq*? zhDhvCgt7D3RuKc?wcyrYi85|{pyT~(#VG!_(qkXwEBZb{-cXsw2qLleYZv8VnHKF$ zVdJ5vE}nG>%yF&*-eWt|kU>Q^I1@NIn-oo$lQ>i6qhqs$)~oUXGb8w1S4lz@8~G>3 z>oZtoa)S2e>Q6k#O#&e9x^uC171xNWr-nh7%P-L<=xjNpE7@r#jfr%*Z<8@Q|5H+EWN_J6 zz{}OPU7pTCvQ+J5wjoI)M+8P-g~r5%K*k)o24sCd>IjSrk^j0OwH8xA!sa>39x!lV LINR0QvXcG=eE63pfMJZ# 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 B3Z(!5 literal 0 HcmV?d00001 diff --git a/icons/turf/walls/square_shutter.dmi b/icons/turf/walls/square_shutter.dmi new file mode 100644 index 0000000000000000000000000000000000000000..d0729a887bebae9227a27c9013cf36cac5b3475b GIT binary patch literal 951 zcmeAS@N?(olHy`uVBq!ia0vp^4M3d0!3HF+R#kZdsfwzQh!U67;^d;tf|AVqJfO&& z)>C`A8Wcp>F5LVrBEQYO{&nmjj`pf{2k(XQ0V}WPXJm98V^(**{k5t^%4DvYOZ}S! zlS;=&pVTZ3UUju>t}A@$=o7pt;e^v913MQ3DWT#SX3_^W?g#$sOSN6Me#$~=(JKpH zvISXaXI)(OOPqm$>6fRAV@SoEw|6%B-FA>U{;^)GUnfO9S&cuWWb-G6)Dz58j6N!S zah>siQ(}&((5|&BcqZ<$u8uHrTNJ!1NOjx!b2nq^?w?QM{Jli@SKa7j^!V5MGyGLzc*N0Q!KBc~(4h=s?pPt^XgCvS=Dy6DP-uXU~pJbN%>EBFRUZ#X*KqAiSF2;PmJ1yS`>dirDn^wtQDBFq%1K zUVT+nmW^b|d25EpA9sAQVeIHGXQ;b#Z`!=~zs%U)R%7eFE^t=2exr9lR@oPxwpPJ)KGjF9?RR=OLWo}~nwlS+n zT;ZHH!|6pUbQn9h*SB^*jzsnd&`)}^raS$#bC9^ma-ioubBlwrhfFm?A=u6C3`Yc^ zOz}`<4YeA12bufq4Yqc6w>D|qlXdRNYz7C&54#47_r3|CzO($6_o_F)3uHP{xt?ME z`SZ?am~B7u9auko`lZi*$_rvLSXj5*<AUyw7m76Ie_U^=hNQ| zcUU_<|Fd%l<;>XiWug2YF}*$^c$h z!n5K|0r4fh3SkY0yYv_=bj%u2BMoc-%aqic-(!zE_h&Y{1t{F!_BCzuPQOV{{>^`U zFRXO3VeUpIPz>*x&-TgS*^Ua`yb!4?^8>@^(l`fv~<~=V*XsUmb^;V1?OTNPOKB<>lad3 m=gGN1u*9W8LIuNTznNv5Dqq^%IK&9di42~uelF{r5}E))x}6{Z literal 0 HcmV?d00001 diff --git a/icons/turf/walls/wattle.dmi b/icons/turf/walls/wattle.dmi new file mode 100644 index 0000000000000000000000000000000000000000..01ee0aadc3e9d7fefb10f22df131f4ae0807614e GIT binary patch literal 2996 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 literal 0 HcmV?d00001 diff --git a/icons/turf/walls/wattledaub.dmi b/icons/turf/walls/wattledaub.dmi new file mode 100644 index 0000000000000000000000000000000000000000..126f7829c7e9e37a06963820543d121c0aef7e31 GIT binary patch literal 4213 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 H3z7c^+&WX` literal 0 HcmV?d00001 diff --git a/nebula.dme b/nebula.dme index ba7a029e6eb0..1dcdcdb621dd 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1634,6 +1634,7 @@ #include "code\game\turfs\walls\wall_natural_subtypes.dm" #include "code\game\turfs\walls\wall_natural_xenoarch.dm" #include "code\game\turfs\walls\wall_types.dm" +#include "code\game\turfs\walls\wall_wattle.dm" #include "code\game\verbs\byond_membership.dm" #include "code\game\verbs\ignore.dm" #include "code\game\verbs\ooc.dm" From 053047b855c7e2771a5be1d21f26f1276a072800 Mon Sep 17 00:00:00 2001 From: HeyBanditoz Date: Thu, 19 Dec 2024 18:59:01 -0700 Subject: [PATCH 0098/1331] Initial 516 support Game compiles, runs, and UIs are working with my limited testing --- code/_helpers/unsorted.dm | 2 ++ code/datums/mind/mind.dm | 12 ++++++------ code/modules/admin/view_variables/topic.dm | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 657d9e500352..45dc05bd2c83 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -169,8 +169,10 @@ Turf and target are seperate in case you want to teleport some distance from a t return 1 return 0 +#if DM_VERSION < 516 /proc/sign(x) return x!=0?x/abs(x):0 +#endif /proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm var/px=M.x //starting x diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 377c2dced75d..34bad84cb65d 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -151,8 +151,8 @@ if(href_list["add_goal"]) - var/mob/caller = locate(href_list["add_goal_caller"]) - if(caller && caller == current) can_modify = TRUE + var/mob/calling_proc = locate(href_list["add_goal_caller"]) + if(calling_proc && calling_proc == current) can_modify = TRUE if(can_modify) if(is_admin) @@ -170,8 +170,8 @@ if(href_list["abandon_goal"]) var/datum/goal/goal = get_goal_from_href(href_list["abandon_goal"]) - var/mob/caller = locate(href_list["abandon_goal_caller"]) - if(caller && caller == current) can_modify = TRUE + var/mob/calling_proc = locate(href_list["abandon_goal_caller"]) + if(calling_proc && calling_proc == current) can_modify = TRUE if(goal && can_modify) if(usr == current) @@ -185,8 +185,8 @@ if(href_list["reroll_goal"]) var/datum/goal/goal = get_goal_from_href(href_list["reroll_goal"]) - var/mob/caller = locate(href_list["reroll_goal_caller"]) - if(caller && caller == current) can_modify = TRUE + var/mob/calling_proc = locate(href_list["reroll_goal_caller"]) + if(calling_proc && calling_proc == current) can_modify = TRUE if(goal && (goal in goals) && can_modify) qdel(goal) diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 9cec7322e10b..f1118e682c96 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -599,9 +599,9 @@ href_list["datumrefresh"] = href_list["mobToDamage"] else if(href_list["call_proc"]) - var/datum/callee = locate(href_list["call_proc"]) - if(istype(callee) || istype(callee, /client)) // can call on clients too, not just datums - callproc_targetpicked(1, callee) + var/datum/called_proc = locate(href_list["call_proc"]) + if(istype(called_proc) || istype(called_proc, /client)) // can call on clients too, not just datums + callproc_targetpicked(1, called_proc) else if(href_list["addaura"]) if(!check_rights(R_DEBUG|R_ADMIN|R_FUN)) return var/mob/living/victim = locate(href_list["addaura"]) From 95d62d8e754bb4bc32714c8466f36820df0ed1f4 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 20 Dec 2024 22:49:21 +1100 Subject: [PATCH 0099/1331] Automatic changelog generation for PR #4654 [ci skip] --- html/changelogs/AutoChangeLog-pr-4654.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4654.yml diff --git a/html/changelogs/AutoChangeLog-pr-4654.yml b/html/changelogs/AutoChangeLog-pr-4654.yml new file mode 100644 index 000000000000..05d15519407e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4654.yml @@ -0,0 +1,4 @@ +author: Penelope Haze +changes: + - {tweak: Makes weather effects slightly more transparent.} +delete-after: true From 44b370dc6b35641426cf1cb332744fa94c76235f Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 17 Dec 2024 16:18:21 -0500 Subject: [PATCH 0100/1331] Add new mud turf sprite and dirt color --- code/game/turfs/flooring/_flooring.dm | 2 +- code/game/turfs/flooring/flooring_mud.dm | 3 ++- .../turfs/floors/subtypes/floor_natural.dm | 3 ++- icons/turf/flooring/mud.dmi | Bin 1839 -> 2654 bytes 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index 9d3ce2f33cfd..a02efe3cb841 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -78,7 +78,7 @@ var/global/list/flooring_cache = list() var/render_trenches = TRUE var/floor_layer = TURF_LAYER var/holographic = FALSE - var/dirt_color = "#7c5e42" + var/dirt_color = /decl/material/solid/soil::color var/list/burned_states var/list/broken_states diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index 59be34fdfad7..fd4866144b90 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -3,6 +3,7 @@ desc = "A stretch of thick, waterlogged mud." icon = 'icons/turf/flooring/mud.dmi' icon_base = "mud" + color = null // autoset from material icon_edge_layer = FLOOR_EDGE_MUD footstep_type = /decl/footsteps/mud turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID @@ -45,7 +46,7 @@ icon = 'icons/turf/flooring/dirt.dmi' icon_base = "dirt" icon_edge_layer = FLOOR_EDGE_DIRT - color = "#41311b" + color = null // autoset from material footstep_type = /decl/footsteps/asteroid turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/soil diff --git a/code/game/turfs/floors/subtypes/floor_natural.dm b/code/game/turfs/floors/subtypes/floor_natural.dm index 4b2ddd092f9a..09fbbcc07d27 100644 --- a/code/game/turfs/floors/subtypes/floor_natural.dm +++ b/code/game/turfs/floors/subtypes/floor_natural.dm @@ -8,7 +8,7 @@ name = "dirt" icon = 'icons/turf/flooring/dirt.dmi' icon_state = "dirt" - color = "#41311b" + color = /decl/material/solid/soil::color // preview color _base_flooring = /decl/flooring/dirt /turf/floor/chlorine_sand @@ -72,6 +72,7 @@ name = "mud" icon = 'icons/turf/flooring/mud.dmi' icon_state = "mud" + color = /decl/material/solid/soil::color // preview color _base_flooring = /decl/flooring/mud /turf/floor/mud/water diff --git a/icons/turf/flooring/mud.dmi b/icons/turf/flooring/mud.dmi index 738ae2dd4ef287787827d846b22df139c8df4f26..b73fcd5c76fb0a6a3f18208eac7011e49859c9b2 100644 GIT binary patch delta 2482 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 delta 1661 zcmV-@27>wC6t51DB!4VWOjJd{z`#a1C`mgmL^dKkFC0KKA3rl6LNp;kG$2PgB}O?U zNIE4&HzPtdAwe@BIWZr2u zG9E=YB1=3deQQ&5VMAJFdj@jDMBf!Iwnm&DrQzcR75X5FdaE78ALZFQA9OQKr3-#QC(3( zTTM1pMKE<_O*dpzJy}XLM>-{9Q#^2AM_WuaU{E@JZB&79R#HMOZe2xOPBuC&8&yUy zZCpiZSV6$Rz!)!=9g|Q2rhf$*Nkl5SFP9Pm^yprH8WWbb-!Q$<@^=s=0#gH5nW*7jAjR=f9%K`Gu{D1n5#O5jBk4Yi# zw{ZqjU;&q|uN5u;5P@;11}6GuZ7_z&ZP{&nS!wyD3Gd9_+$r&UhQhXojS#<%H_iH$ zq!R$+HL!&edGmbrT0sB+8!5d#_Ymf{UfsP7P%K_cU_3sPlV4rtzcnBl7awq!Vuvrx$1Nbl7v zx=+IaG}`yqsI>`$Kmak+u=nQ8x88n-ynt*mqyVqP7Qq0v*nj9}=|`|QCwFdZlI3uo zCRGOSZsvpw3b0B5oJ5~%)xcMZPQUJipCOa78C6i$J zi`>Hp?=R-KSE$Axr0Jv5i%uSuLo~w9fd&A>wSX1jyf@&jsKr5A=TN7QpNu~oKY9H0 zqwHe_@X3gr!GC@l>u7*CEYc+zvH}3(oPfvY{QuD6vvhi_0H8^He04qyjeieCUyNw?bFgF727W%rknr{S zi!T)bG&U!za{)TD1NlHsWv7XIfEy>(xlHiY*WmzI=j`>)J6@9x7(hYAhX}uB#h|=@ zr^YI@@&LX`GywQ6#56JB+X4h6#b};=o08Oc2*)!jjx#QX_~fM2a6*=Cq$GOvb5Ry2 z#B^63y?^+QBQW0PZLeAjSdA!P(ar|}M+8M=+Y^$U#qRegsu*Ae^xiP@J%Ls=6wULU zD zfBXr%f|&vUBcUhR$oRtCcEzITf--?5qMC}stbevYzx-t%TO<_#J`uI~n3cewoNG+Z zj?Y@3|0;#yXCK`gDBAN0DFvO*786;B?K+oZTF)6r zd4ENjEkB88{$_;_m}-_hY)QTVE%9Eq`Dfg*#!s{$KuE05*tQp(hLjFb)S0RL?Z#127F>)|d~3 zRdj{eDWKx00D=l|}^8uI}>|95BB zm=ECjKY&}I7KDYeLKv1I@-^lIL3_}c4`5>%z^pJI!1I3q0EPJg9d$Z0Ys?4e{NI^b zV?Gdc#@DEZc>eFstT7)*!vKWO2Lf9%jbaqStZzxmdg`gCo_gx(e^37aEo*5sNIp!O00000NkvXX Hu0mjf+`rD^ From debc2e8634b5a945a22343f9b4b1de603b359727 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 17 Dec 2024 16:18:47 -0500 Subject: [PATCH 0101/1331] Adjust wood floor color ramp --- icons/turf/flooring/wood.dmi | Bin 2723 -> 2723 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/turf/flooring/wood.dmi b/icons/turf/flooring/wood.dmi index a93de0fefca62d4bc74c33f7ab8ee31f21eaa3a4..9e17d5cbeb4095a698d99cba5288022eae738b33 100644 GIT binary patch delta 48 zcmV-00MGxU6{8i9G977Qa=*H&r=y>#q@i0|Tb7oVa(RQOsHmHpo6gRWPc8()kJQ1j Gfg%b^x)_20 delta 48 zcmV-00MGxU6{8i9G96)Oa=)#rr=y>#q@i0|Tb7oVa(RQOsHmHpo6f|MPc8)cpFah$ Gfg%b+{TE*V From d4020b99553c372460a5a2353b77610f145da485 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 17 Dec 2024 16:19:43 -0500 Subject: [PATCH 0102/1331] Add temperature requirements to cooking recipes --- code/modules/food/cooking/recipes/recipe_baked.dm | 3 +++ code/modules/food/cooking/recipes/recipe_boiled.dm | 2 +- code/modules/food/cooking/recipes/recipe_fried.dm | 3 +++ code/modules/food/cooking/recipes/recipe_grilled.dm | 3 +++ code/modules/food/cooking/recipes/recipe_steamed.dm | 3 +++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/code/modules/food/cooking/recipes/recipe_baked.dm b/code/modules/food/cooking/recipes/recipe_baked.dm index 90bb8ad590d9..a549e1b3f8a8 100644 --- a/code/modules/food/cooking/recipes/recipe_baked.dm +++ b/code/modules/food/cooking/recipes/recipe_baked.dm @@ -4,6 +4,9 @@ RECIPE_CATEGORY_MICROWAVE, RECIPE_CATEGORY_BAKING_DISH ) + // some arbitrary value to make sure it doesn't cook in open air, but will when microwaved + // todo: rework futurecooking so that microwaves aren't the only appliance for everything (modern stove, oven, fryer, etc) + minimum_temperature = 80 CELSIUS //cooking_heat_type = COOKING_HEAT_INDIRECT /decl/recipe/baked/pizzamargherita diff --git a/code/modules/food/cooking/recipes/recipe_boiled.dm b/code/modules/food/cooking/recipes/recipe_boiled.dm index d1f8f74207fc..031bd5a164ab 100644 --- a/code/modules/food/cooking/recipes/recipe_boiled.dm +++ b/code/modules/food/cooking/recipes/recipe_boiled.dm @@ -1,6 +1,6 @@ /decl/recipe/boiled abstract_type = /decl/recipe/boiled - //minimum_temperature = T100C + minimum_temperature = 80 CELSIUS // increase to /decl/material/liquid/water::boiling_point once microwaves are reworked //cooking_heat_type = COOKING_HEAT_INDIRECT //cooking_medium_type = /decl/material/liquid/water //cooking_medium_amount = 20 diff --git a/code/modules/food/cooking/recipes/recipe_fried.dm b/code/modules/food/cooking/recipes/recipe_fried.dm index 784b619335ed..3d4af90fc98d 100644 --- a/code/modules/food/cooking/recipes/recipe_fried.dm +++ b/code/modules/food/cooking/recipes/recipe_fried.dm @@ -1,5 +1,8 @@ /decl/recipe/fried abstract_type = /decl/recipe/fried + // some arbitrary value to make sure it doesn't cook in open air, but will when microwaved + // todo: rework futurecooking so that microwaves aren't the only appliance for everything (modern stove, oven, fryer, etc) + minimum_temperature = 80 CELSIUS //cooking_heat_type = COOKING_HEAT_DIRECT //cooking_medium_type = /decl/material/liquid/oil container_categories = list( diff --git a/code/modules/food/cooking/recipes/recipe_grilled.dm b/code/modules/food/cooking/recipes/recipe_grilled.dm index d2e4e2340902..cf6896a827c3 100644 --- a/code/modules/food/cooking/recipes/recipe_grilled.dm +++ b/code/modules/food/cooking/recipes/recipe_grilled.dm @@ -5,6 +5,9 @@ RECIPE_CATEGORY_MICROWAVE, RECIPE_CATEGORY_SKILLET ) + // some arbitrary value to make sure it doesn't cook in open air, but will when microwaved + // todo: rework futurecooking so that microwaves aren't the only appliance for everything (modern stove, oven, fryer, etc) + minimum_temperature = 80 CELSIUS completion_message = "The meat sizzles as it is cooked through." /decl/recipe/grilled/plainsteak diff --git a/code/modules/food/cooking/recipes/recipe_steamed.dm b/code/modules/food/cooking/recipes/recipe_steamed.dm index dacac34bed15..746439f6ac28 100644 --- a/code/modules/food/cooking/recipes/recipe_steamed.dm +++ b/code/modules/food/cooking/recipes/recipe_steamed.dm @@ -1,5 +1,8 @@ /decl/recipe/steamed abstract_type = /decl/recipe/steamed + // some arbitrary value to make sure it doesn't cook in open air, but will when microwaved + // todo: rework futurecooking so that microwaves aren't the only appliance for everything (modern stove, oven, fryer, etc) + minimum_temperature = 80 CELSIUS /decl/recipe/steamed/chawanmushi fruit = list("mushroom" = 1) From 2ac7a44f1ced45e587b81d4fd2c4ac11d8afd22b Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 17 Dec 2024 16:20:55 -0500 Subject: [PATCH 0103/1331] Add doc comments to lighting_turf.dm --- code/modules/lighting/lighting_turf.dm | 28 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index de8134d5e519..d4008f536a20 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,16 +1,22 @@ /turf var/dynamic_lighting = TRUE - var/ambient_light // If non-null, a hex RGB light color that should be applied to this turf. - var/ambient_light_multiplier = 0.3 // The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. + /// If non-null, a hex RGB light color that should be applied to this turf. + var/ambient_light + /// The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. + var/ambient_light_multiplier = 0.3 luminosity = 1 var/tmp/lighting_corners_initialised = FALSE - var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf. - var/tmp/atom/movable/lighting_overlay/lighting_overlay // Our lighting overlay. + /// List of light sources affecting this turf. + var/tmp/list/datum/light_source/affecting_lights + /// Our lighting overlay, used to apply multiplicative lighting to the tile and its contents. + var/tmp/atom/movable/lighting_overlay/lighting_overlay var/tmp/list/datum/lighting_corner/corners - var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. - var/tmp/ambient_has_indirect = FALSE // If this is TRUE, an above turf's ambient light is affecting this turf. + /// Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. + var/tmp/has_opaque_atom = FALSE + /// If this is TRUE, an above turf's ambient light is affecting this turf. + var/tmp/ambient_has_indirect = FALSE // Record-keeping, do not touch -- that means you, admins. var/tmp/ambient_light_old @@ -92,14 +98,14 @@ ambient_light_old = ambient_light -// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. +/// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() var/datum/light_source/L for (var/thing in affecting_lights) L = thing L.vis_update() -// Forces a lighting update. Reconsider lights is preferred when possible. +/// Forces a lighting update. Reconsider lights is preferred when possible. /turf/proc/force_update_lights() var/datum/light_source/L for (var/thing in affecting_lights) @@ -137,7 +143,7 @@ C.active = TRUE -// Returns the average color of this tile. Roughly corresponds to the color of a single old-style lighting overlay. +/// Returns the average color of this tile. Roughly corresponds to the color of a single old-style lighting overlay. /turf/proc/get_avg_color() if (!lighting_overlay) return null @@ -159,7 +165,7 @@ #define SCALE(targ,min,max) (targ - min) / (max - min) -// Used to get a scaled lumcount. +/// Returns a lumcount (average intensity of color channels) scaled between minlum and maxlum. /turf/proc/get_lumcount(minlum = 0, maxlum = 1) if (!lighting_overlay) return 0.5 @@ -176,7 +182,7 @@ #undef SCALE -// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. +/// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. /turf/proc/recalc_atom_opacity() #ifdef AO_USE_LIGHTING_OPACITY var/old = has_opaque_atom From cc76d55872dcb2ab23aaf4e456932dd67a1dc27e Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Wed, 18 Dec 2024 19:28:27 -0500 Subject: [PATCH 0104/1331] Add Doe's new chest sprites --- .../structures/crates_lockers/crates.dm | 23 ++++++++++++++++++ icons/obj/closets/bases/chest.dmi | Bin 1415 -> 1872 bytes 2 files changed, 23 insertions(+) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index f9a8de10bf38..6df0fd7eb1cb 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -324,6 +324,29 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC material = /decl/material/solid/organic/wood/oak color = /decl/material/solid/organic/wood/oak::color + var/icon/overlay_icon = 'icons/obj/closets/bases/chest.dmi' + // TODO: Rework chest crafting so that this can use reinf_material instead. + /// The material used for the opacity and color of the trim overlay. + var/decl/material/overlay_material = /decl/material/solid/metal/iron + +/obj/structure/closet/crate/chest/Initialize() + . = ..() + if(ispath(overlay_material)) + overlay_material = GET_DECL(overlay_material) + // icon update is already queued in parent because of closet appearance + +/obj/structure/closet/crate/chest/update_material_desc(override_desc) + ..() + if(overlay_material) + desc = "[desc] It has a trim made of [overlay_material.solid_name]." + +/obj/structure/closet/crate/chest/on_update_icon() + . = ..() + if(istype(overlay_material)) + var/overlay_state = opened ? "open-overlay" : "base-overlay" + var/image/trim = overlay_image(overlay_icon, overlay_state, overlay_material.color, RESET_COLOR|RESET_ALPHA) + trim.alpha = clamp((50 + overlay_material.opacity * 255), 0, 255) + add_overlay(trim) /obj/structure/closet/crate/chest/ebony material = /decl/material/solid/organic/wood/ebony diff --git a/icons/obj/closets/bases/chest.dmi b/icons/obj/closets/bases/chest.dmi index 491dcbd62c0485d89f2339333860f3722b42ccbe..bef40525584deb2103b95053b0fbaabd9801ca3b 100644 GIT binary patch delta 1796 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 R()0iT002ovPDHLkV1m>hruYB= From 9c958bf5831ce9a29e3566c914bb7d2b8ac7963e Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Thu, 19 Dec 2024 01:09:45 -0500 Subject: [PATCH 0105/1331] Add recipes for rough wooden tiles --- .../crafting/stack_recipes/recipes_stacks.dm | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/code/modules/crafting/stack_recipes/recipes_stacks.dm b/code/modules/crafting/stack_recipes/recipes_stacks.dm index c78160e09fc0..06c19604fbfc 100644 --- a/code/modules/crafting/stack_recipes/recipes_stacks.dm +++ b/code/modules/crafting/stack_recipes/recipes_stacks.dm @@ -26,6 +26,26 @@ result_type = /obj/item/stack/tile/wood/walnut required_material = /decl/material/solid/organic/wood/walnut +/decl/stack_recipe/tile/wood/mahogany/rough + crafting_extra_cost_factor = 2 // wasteful but easy + difficulty = MAT_VALUE_EASY_DIY + result_type = /obj/item/stack/tile/wood/rough/mahogany + +/decl/stack_recipe/tile/wood/maple/rough + crafting_extra_cost_factor = 2 + difficulty = MAT_VALUE_EASY_DIY + result_type = /obj/item/stack/tile/wood/rough/maple + +/decl/stack_recipe/tile/wood/ebony/rough + crafting_extra_cost_factor = 2 + difficulty = MAT_VALUE_HARD_DIY + result_type = /obj/item/stack/tile/wood/rough/ebony + +/decl/stack_recipe/tile/wood/walnut/rough + crafting_extra_cost_factor = 2 + difficulty = MAT_VALUE_EASY_DIY + result_type = /obj/item/stack/tile/wood/rough/walnut + /decl/stack_recipe/tile/wood/oak_laminate result_type = /obj/item/stack/tile/wood/laminate/oak required_material = /decl/material/solid/organic/wood/chipboard From d6cdb600bf3b271c56aafc4a92ba7234ed14b98f Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 21 Dec 2024 00:49:02 +0000 Subject: [PATCH 0106/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-4654.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4654.yml diff --git a/html/changelog.html b/html/changelog.html index 00a4fec35a09..d152fc178400 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->

    +

    21 December 2024

    +

    Penelope Haze updated:

    +
      +
    • Makes weather effects slightly more transparent.
    • +
    +

    07 December 2024

    MistakeNot4892 updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 3906fe0882f2..09a36ad22649 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14933,3 +14933,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. MistakeNot4892: - tweak: Most wooden floors and tables on space maps are now chipboard laminate instead. +2024-12-21: + Penelope Haze: + - tweak: Makes weather effects slightly more transparent. diff --git a/html/changelogs/AutoChangeLog-pr-4654.yml b/html/changelogs/AutoChangeLog-pr-4654.yml deleted file mode 100644 index 05d15519407e..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4654.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Penelope Haze -changes: - - {tweak: Makes weather effects slightly more transparent.} -delete-after: true From ce03e41897c03892818414c61e60f7bc88a83371 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 30 May 2024 18:13:22 -0400 Subject: [PATCH 0107/1331] Add heights to mob inventory slots --- code/datums/inventory_slots/_inventory_slot.dm | 2 ++ code/datums/inventory_slots/slots/slot_back.dm | 1 + code/datums/inventory_slots/slots/slot_ears.dm | 1 + code/datums/inventory_slots/slots/slot_glasses.dm | 1 + code/datums/inventory_slots/slots/slot_head.dm | 1 + code/datums/inventory_slots/slots/slot_id.dm | 1 + code/datums/inventory_slots/slots/slot_mask.dm | 1 + code/datums/inventory_slots/slots/slot_shoes.dm | 1 + code/datums/inventory_slots/slots/slot_suit_storage.dm | 1 + 9 files changed, 10 insertions(+) diff --git a/code/datums/inventory_slots/_inventory_slot.dm b/code/datums/inventory_slots/_inventory_slot.dm index 204284e6f2cc..cc8aa6ad420c 100644 --- a/code/datums/inventory_slots/_inventory_slot.dm +++ b/code/datums/inventory_slots/_inventory_slot.dm @@ -19,6 +19,8 @@ var/requires_slot_flags var/requires_organ_tag var/quick_equip_priority = 0 // Higher priority means it will be checked first. If null, will not be considered for quick equip. + /// What depth of fluid is necessary for an item in this slot to be considered submerged? + var/fluid_height = FLUID_SHALLOW var/mob_overlay_layer var/alt_mob_overlay_layer diff --git a/code/datums/inventory_slots/slots/slot_back.dm b/code/datums/inventory_slots/slots/slot_back.dm index cc65742561b3..7c2496867edf 100644 --- a/code/datums/inventory_slots/slots/slot_back.dm +++ b/code/datums/inventory_slots/slots/slot_back.dm @@ -7,6 +7,7 @@ requires_slot_flags = SLOT_BACK mob_overlay_layer = HO_BACK_LAYER quick_equip_priority = 14 + fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level /datum/inventory_slot/back/simple requires_organ_tag = null diff --git a/code/datums/inventory_slots/slots/slot_ears.dm b/code/datums/inventory_slots/slots/slot_ears.dm index 295c24a5a89d..1e905433cfc3 100644 --- a/code/datums/inventory_slots/slots/slot_ears.dm +++ b/code/datums/inventory_slots/slots/slot_ears.dm @@ -10,6 +10,7 @@ requires_slot_flags = SLOT_EARS mob_overlay_layer = HO_L_EAR_LAYER quick_equip_priority = 7 + fluid_height = (FLUID_SHALLOW * 0.25 + FLUID_OVER_MOB_HEAD * 0.75) // 3/4 of the way between waist-level and the top of your head /datum/inventory_slot/ear/update_mob_equipment_overlay(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE) for(var/slot in global.airtight_slots) diff --git a/code/datums/inventory_slots/slots/slot_glasses.dm b/code/datums/inventory_slots/slots/slot_glasses.dm index 0068fac4a1ed..d686ac93edb9 100644 --- a/code/datums/inventory_slots/slots/slot_glasses.dm +++ b/code/datums/inventory_slots/slots/slot_glasses.dm @@ -11,6 +11,7 @@ mob_overlay_layer = HO_GLASSES_LAYER alt_mob_overlay_layer = HO_GOGGLES_LAYER quick_equip_priority = 5 + fluid_height = (FLUID_SHALLOW * 0.25 + FLUID_OVER_MOB_HEAD * 0.75) // 3/4 of the way between waist-level and the top of your head /datum/inventory_slot/glasses/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDEEYES)) diff --git a/code/datums/inventory_slots/slots/slot_head.dm b/code/datums/inventory_slots/slots/slot_head.dm index dce1c5e4ab64..6c31459f016e 100644 --- a/code/datums/inventory_slots/slots/slot_head.dm +++ b/code/datums/inventory_slots/slots/slot_head.dm @@ -9,6 +9,7 @@ requires_slot_flags = SLOT_HEAD mob_overlay_layer = HO_HEAD_LAYER quick_equip_priority = 9 + fluid_height = FLUID_OVER_MOB_HEAD /datum/inventory_slot/head/simple requires_organ_tag = null diff --git a/code/datums/inventory_slots/slots/slot_id.dm b/code/datums/inventory_slots/slots/slot_id.dm index 35d80c2c5a47..67b759c682ca 100644 --- a/code/datums/inventory_slots/slots/slot_id.dm +++ b/code/datums/inventory_slots/slots/slot_id.dm @@ -6,6 +6,7 @@ requires_slot_flags = SLOT_ID mob_overlay_layer = HO_ID_LAYER quick_equip_priority = 13 + fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level /datum/inventory_slot/id/update_mob_equipment_overlay(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE) var/obj/item/clothing/clothes = user.get_equipped_item(slot_w_uniform_str) diff --git a/code/datums/inventory_slots/slots/slot_mask.dm b/code/datums/inventory_slots/slots/slot_mask.dm index 33f44fa90444..53fa82f64213 100644 --- a/code/datums/inventory_slots/slots/slot_mask.dm +++ b/code/datums/inventory_slots/slots/slot_mask.dm @@ -10,6 +10,7 @@ can_be_hidden = TRUE mob_overlay_layer = HO_FACEMASK_LAYER quick_equip_priority = 10 + fluid_height = (FLUID_SHALLOW * 0.25 + FLUID_OVER_MOB_HEAD * 0.75) // 3/4 of the way between waist-level and the top of your head /datum/inventory_slot/mask/update_mob_equipment_overlay(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE) if(prop?.flags_inv & BLOCK_ALL_HAIR) diff --git a/code/datums/inventory_slots/slots/slot_shoes.dm b/code/datums/inventory_slots/slots/slot_shoes.dm index 02c47d26787c..6fc4889be0bd 100644 --- a/code/datums/inventory_slots/slots/slot_shoes.dm +++ b/code/datums/inventory_slots/slots/slot_shoes.dm @@ -10,6 +10,7 @@ ) requires_slot_flags = SLOT_FEET quick_equip_priority = 3 + fluid_height = 3 /datum/inventory_slot/shoes/update_mob_equipment_overlay(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE) var/obj/item/suit = user.get_equipped_item(slot_wear_suit_str) diff --git a/code/datums/inventory_slots/slots/slot_suit_storage.dm b/code/datums/inventory_slots/slots/slot_suit_storage.dm index 91a03c4610d8..832a72563294 100644 --- a/code/datums/inventory_slots/slots/slot_suit_storage.dm +++ b/code/datums/inventory_slots/slots/slot_suit_storage.dm @@ -5,6 +5,7 @@ slot_id = slot_s_store_str requires_organ_tag = BP_CHEST mob_overlay_layer = HO_SUIT_STORE_LAYER + fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level /datum/inventory_slot/suit_storage/can_equip_to_slot(var/mob/user, var/obj/item/prop, var/disable_warning, var/ignore_equipped) . = ..() From 14ba304d8d7d468fd1de5aa778d8a8b98097f08f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 30 May 2024 18:13:34 -0400 Subject: [PATCH 0108/1331] Add more slot helpers to items --- code/modules/mob/inventory.dm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 3dddd78acedb..89f590dcecda 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -271,6 +271,9 @@ return 1 //already unequipped, so success return I.mob_can_unequip(src, slot) +/obj/item/proc/get_any_equipped_slot() + return get_equipped_slot() || get_held_slot() + /obj/item/proc/get_equipped_slot() if(!ismob(loc)) return null @@ -285,6 +288,12 @@ if(get_equipped_item(slot_str) == I) // slots[slot]._holding == I return slot_str +/obj/item/proc/get_held_slot() + if(!ismob(loc)) + return null + var/mob/mob = loc + return mob.get_held_slot_for_item(src) + /mob/proc/get_held_slot_for_item(obj/item/I) var/list/slots = get_held_item_slots() if(!length(slots)) From dfa2dcbd1bdbe0173f842c1e2546dae52da91405 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 21 Dec 2024 00:48:03 -0500 Subject: [PATCH 0109/1331] Make fluids check tables, floating/throwing, and turf height --- .../datums/inventory_slots/_inventory_slot.dm | 2 +- code/game/atoms_fluids.dm | 48 ++++++++++++++++++- code/game/turfs/turf_fluids.dm | 5 ++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/code/datums/inventory_slots/_inventory_slot.dm b/code/datums/inventory_slots/_inventory_slot.dm index cc8aa6ad420c..6f23b1b51a10 100644 --- a/code/datums/inventory_slots/_inventory_slot.dm +++ b/code/datums/inventory_slots/_inventory_slot.dm @@ -20,7 +20,7 @@ var/requires_organ_tag var/quick_equip_priority = 0 // Higher priority means it will be checked first. If null, will not be considered for quick equip. /// What depth of fluid is necessary for an item in this slot to be considered submerged? - var/fluid_height = FLUID_SHALLOW + var/fluid_height = FLUID_SHALLOW // we're treating FLUID_SHALLOW as waist level, basically var/mob_overlay_layer var/alt_mob_overlay_layer diff --git a/code/game/atoms_fluids.dm b/code/game/atoms_fluids.dm index e69ed3bbfc30..d25bb595df6d 100644 --- a/code/game/atoms_fluids.dm +++ b/code/game/atoms_fluids.dm @@ -23,19 +23,63 @@ var/turf/T = get_turf(src) return T?.is_flooded(lying_mob, absolute) -/atom/proc/submerged(depth) +/atom/proc/submerged(depth, above_turf) if(isnull(depth)) var/turf/T = get_turf(src) if(!istype(T)) return FALSE depth = T.get_fluid_depth() + var/turf_height = T.get_physical_height() + // If we're not on the surface of the turf (floating, leaping, or other sources) + // then we add the turf height to the depth, so you can jump over a water-filled pit + // or throw something over it + if(turf_height < 0 && above_turf) + depth += turf_height if(ismob(loc)) return depth >= FLUID_SHALLOW if(isturf(loc)) + if(locate(/obj/structure/table)) + return depth >= FLUID_SHALLOW return depth >= 3 return depth >= FLUID_OVER_MOB_HEAD -/turf/submerged(depth) +// This override exists purely because throwing is movable-level and not atom-level, +// for obvious reasons (that being that non-movable atoms cannot move). +/atom/movable/submerged(depth, above_turf) + above_turf ||= !!throwing + return ..() + +/obj/item/submerged(depth, above_turf) + var/datum/inventory_slot/slot = get_any_equipped_slot() + // we're in a mob and have a slot, so we bail early + if(istype(slot)) + var/mob/owner = loc // get_any_equipped_slot checks istype already + if(owner.current_posture.prone) + return ..() // treat us like an atom sitting on the ground (or table), really + if(isnull(depth)) // copied from base proc, since we aren't calling parent in this block + var/turf/T = get_turf(src) + if(!istype(T)) + return FALSE + depth = T.get_fluid_depth() + return depth >= slot.fluid_height + return ..() + +/mob/submerged(depth, above_turf) + above_turf ||= is_floating || !!throwing // check throwing here because of the table check coming before parent call + var/obj/structure/table/standing_on = locate(/obj/structure/table) in loc + // can't stand on a table if we're floating + if(!above_turf && standing_on && standing_on.mob_offset > 0) // standing atop a table that is a meaningful amount above the ground (not a bench) + if(isnull(depth)) // duplicated from atom because we don't call parent in this block + var/turf/T = get_turf(src) + if(!istype(T)) + return FALSE + depth = T.get_fluid_depth() + // assuming default tables are at waist height, this is a simple adjustment to scale it for taller/shorter ones + return depth >= floor(FLUID_SHALLOW * (standing_on.mob_offset / /obj/structure/table::mob_offset)) + return ..() + +// above_turf is nonsensical for turfs but I don't want the linter to complain +/turf/submerged(depth, above_turf) if(isnull(depth)) depth = get_fluid_depth() return depth >= FLUID_OVER_MOB_HEAD diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index e377ec4b32fa..42220adc739b 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -117,7 +117,12 @@ ..() if(!QDELETED(src) && fluids?.total_volume) fluids.touch_turf(src) + // technically, fluids might not be our own reagent holder + // so we factor in height ourselves + var/fluid_height = fluids.total_volume + get_physical_height() for(var/atom/movable/AM as anything in get_contained_external_atoms()) + if(!AM.submerged(fluid_height)) + continue AM.fluid_act(fluids) /turf/proc/remove_fluids(var/amount, var/defer_update) From 3efa978cd3a304fcbc82f051fe1dffeba2b37d9f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 21 Dec 2024 02:12:03 -0500 Subject: [PATCH 0110/1331] Fix items being affected by fluids when they shouldn't be --- .../inventory_slots/inventory_gripper.dm | 1 + code/game/atoms_fluids.dm | 5 ++- code/game/atoms_movable.dm | 2 +- code/game/objects/items/flame/_flame.dm | 9 +---- code/game/turfs/turf_fluids.dm | 5 +-- code/modules/mob/inventory.dm | 38 ++++++++++++++++++- code/modules/mob/living/living.dm | 5 +++ 7 files changed, 49 insertions(+), 16 deletions(-) diff --git a/code/datums/inventory_slots/inventory_gripper.dm b/code/datums/inventory_slots/inventory_gripper.dm index c11a09eed720..334be51e15c7 100644 --- a/code/datums/inventory_slots/inventory_gripper.dm +++ b/code/datums/inventory_slots/inventory_gripper.dm @@ -5,6 +5,7 @@ /// If set, use this icon_state for the hand slot overlay; otherwise, use slot_id. var/hand_overlay quick_equip_priority = null // you quick-equip stuff by holding it in a gripper, so this ought to be skipped + fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level, reasoning that you can just hold it up out of the water // For reference, grippers do not use ui_loc, they have it set dynamically during /datum/hud/proc/rebuild_hands() diff --git a/code/game/atoms_fluids.dm b/code/game/atoms_fluids.dm index d25bb595df6d..2984d5393f27 100644 --- a/code/game/atoms_fluids.dm +++ b/code/game/atoms_fluids.dm @@ -24,11 +24,12 @@ return T?.is_flooded(lying_mob, absolute) /atom/proc/submerged(depth, above_turf) + var/turf/T = get_turf(src) if(isnull(depth)) - var/turf/T = get_turf(src) if(!istype(T)) return FALSE depth = T.get_fluid_depth() + if(istype(T)) var/turf_height = T.get_physical_height() // If we're not on the surface of the turf (floating, leaping, or other sources) // then we add the turf height to the depth, so you can jump over a water-filled pit @@ -50,7 +51,7 @@ return ..() /obj/item/submerged(depth, above_turf) - var/datum/inventory_slot/slot = get_any_equipped_slot() + var/datum/inventory_slot/slot = get_any_equipped_slot_datum() // we're in a mob and have a slot, so we bail early if(istype(slot)) var/mob/owner = loc // get_any_equipped_slot checks istype already diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 477e9d25c5df..4b0049e92f2c 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -272,7 +272,7 @@ if(isturf(loc)) var/turf/T = loc - if(T.reagents) + if(T.reagents?.total_volume && submerged()) fluid_act(T.reagents) for(var/mob/viewer in storage?.storage_ui?.is_seeing) diff --git a/code/game/objects/items/flame/_flame.dm b/code/game/objects/items/flame/_flame.dm index bee61f2c54c6..645e04d1c719 100644 --- a/code/game/objects/items/flame/_flame.dm +++ b/code/game/objects/items/flame/_flame.dm @@ -167,14 +167,7 @@ if(waterproof) return - var/check_depth = FLUID_PUDDLE - if(ismob(loc)) - var/mob/holder = loc - if(!holder.current_posture?.prone) - check_depth = FLUID_OVER_MOB_HEAD - else - check_depth = FLUID_SHALLOW - if(fluids.total_volume >= check_depth) + if(fluids.total_volume >= FLUID_PUDDLE) snuff_out(no_message = TRUE) /obj/item/flame/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index 42220adc739b..0ea31b04c6f3 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -117,11 +117,8 @@ ..() if(!QDELETED(src) && fluids?.total_volume) fluids.touch_turf(src) - // technically, fluids might not be our own reagent holder - // so we factor in height ourselves - var/fluid_height = fluids.total_volume + get_physical_height() for(var/atom/movable/AM as anything in get_contained_external_atoms()) - if(!AM.submerged(fluid_height)) + if(!AM.submerged()) continue AM.fluid_act(fluids) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 89f590dcecda..62329f27692f 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -271,15 +271,49 @@ return 1 //already unequipped, so success return I.mob_can_unequip(src, slot) +/// Gets the inventory slot string ID for the mob whose contents we're in, if any. +/// Checks both equipped and held item slots. /obj/item/proc/get_any_equipped_slot() - return get_equipped_slot() || get_held_slot() + if(!ismob(loc)) + return null + var/mob/mob = loc + return mob.get_any_equipped_slot_for_item(src) + +/// Gets the inventory slot string ID for an item that may be in our inventory. +/// Checks both equipped and held item slots. +/mob/proc/get_any_equipped_slot_for_item(obj/item/I) + var/list/slots = get_inventory_slots() + get_held_item_slots() + if(!length(slots)) + return + for(var/slot_str in slots) + if(get_equipped_item(slot_str) == I) // slots[slot]._holding == I + return slot_str +/// A counterpart to get_any_equipped_slot_for_item that returns the slot datum rather than the slot name. +/// Checks both equipped and held item slots. +/obj/item/proc/get_any_equipped_slot_datum() + if(!ismob(loc)) + return null + var/mob/mob = loc + return mob.get_inventory_slot_datum(mob.get_any_equipped_slot_for_item(src)) + +/// Gets the equipment (worn) slot string ID for the mob whose contents we're in, if any. Does not include held slots. /obj/item/proc/get_equipped_slot() if(!ismob(loc)) return null var/mob/mob = loc return mob.get_equipped_slot_for_item(src) +/// A helper that returns the slot datum rather than the slot name. +/// Does not include held slots. +/// Saves unnecessary duplicate ismob checks and loc casts. +/obj/item/proc/get_equipped_slot_datum() + if(!ismob(loc)) + return null + var/mob/mob = loc + return mob.get_inventory_slot_datum(mob.get_equipped_slot_for_item(src)) + +/// Gets the equipment (worn) slot string ID for an item we may be wearing. Does not include held slots. /mob/proc/get_equipped_slot_for_item(obj/item/I) var/list/slots = get_inventory_slots() if(!length(slots)) @@ -288,12 +322,14 @@ if(get_equipped_item(slot_str) == I) // slots[slot]._holding == I return slot_str +/// Gets the held item slot string ID for the mob whose contents we're in, if any. Does not include worn slots. /obj/item/proc/get_held_slot() if(!ismob(loc)) return null var/mob/mob = loc return mob.get_held_slot_for_item(src) +/// Gets the held item slot string ID for an item we may be holding. Does not include worn slots. /mob/proc/get_held_slot_for_item(obj/item/I) var/list/slots = get_held_item_slots() if(!length(slots)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index df4f9b1c97d3..e76e323abea1 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -853,9 +853,14 @@ default behaviour is: fluids.touch_mob(src) if(QDELETED(src) || !fluids.total_volume) return + var/on_turf = fluids.my_atom == get_turf(src) for(var/atom/movable/A as anything in get_equipped_items(TRUE)) if(!A.simulated) continue + // if we're being affected by reagent fluids, items check if they're submerged + // todo: i don't like how this works, it feels hacky. maybe separate coating and submersion somehow and make this only checked for submersion + if(on_turf && !A.submerged()) + continue A.fluid_act(fluids) if(QDELETED(src) || !fluids.total_volume) return From af964e47137865e9c62ae43bce45ce9233fcafae Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 22 Dec 2024 15:33:40 +1100 Subject: [PATCH 0111/1331] Automatic changelog generation for PR #4653 [ci skip] --- html/changelogs/AutoChangeLog-pr-4653.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4653.yml diff --git a/html/changelogs/AutoChangeLog-pr-4653.yml b/html/changelogs/AutoChangeLog-pr-4653.yml new file mode 100644 index 000000000000..2fd019eea5a2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4653.yml @@ -0,0 +1,6 @@ +author: ophelia v0.8 +changes: + - {imageadd: added new dirt and mud tile sprites} + - {imageadd: 'added new wooden chest sprites, by Doe'} + - {tweak: mud and soil plots are now properly greyscaled to soil material color} +delete-after: true From 2c4a22e592d30e07508673a4a351fd1568378551 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 23 Dec 2024 00:51:49 +0000 Subject: [PATCH 0112/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 8 ++++++++ html/changelogs/.all_changelog.yml | 5 +++++ html/changelogs/AutoChangeLog-pr-4653.yml | 6 ------ 3 files changed, 13 insertions(+), 6 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4653.yml diff --git a/html/changelog.html b/html/changelog.html index d152fc178400..7ec0db885cb9 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,14 @@ -->
      +

      23 December 2024

      +

      ophelia v0.8 updated:

      +
        +
      • added new dirt and mud tile sprites
      • +
      • added new wooden chest sprites, by Doe
      • +
      • mud and soil plots are now properly greyscaled to soil material color
      • +
      +

      21 December 2024

      Penelope Haze updated:

        diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 09a36ad22649..9e2102536fb4 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14936,3 +14936,8 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2024-12-21: Penelope Haze: - tweak: Makes weather effects slightly more transparent. +2024-12-23: + ophelia v0.8: + - imageadd: added new dirt and mud tile sprites + - imageadd: added new wooden chest sprites, by Doe + - tweak: mud and soil plots are now properly greyscaled to soil material color diff --git a/html/changelogs/AutoChangeLog-pr-4653.yml b/html/changelogs/AutoChangeLog-pr-4653.yml deleted file mode 100644 index 2fd019eea5a2..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4653.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: ophelia v0.8 -changes: - - {imageadd: added new dirt and mud tile sprites} - - {imageadd: 'added new wooden chest sprites, by Doe'} - - {tweak: mud and soil plots are now properly greyscaled to soil material color} -delete-after: true From 4c1de0d3e19ec8cf8a3b42dc05970b13778b87c0 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Wed, 25 Dec 2024 00:49:05 +0000 Subject: [PATCH 0113/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 7 ------- 1 file changed, 7 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 7ec0db885cb9..b64a9c790dac 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -106,13 +106,6 @@

        MistakeNot4892 updated:

        • Curries, soups and stews have been rewritten, please refer to the codex for recipes.
        - -

        23 October 2024

        -

        MistakeNot4892 updated:

        -
          -
        • Dough now requires a 60C temperature and does not use egg.
        • -
        • Flatbread now uses unleavened dough, made without yeast, while pies use pie crust. Check the codex for updated recipes.
        • -
      From 85b234d9517377e45c7edf2bcbfa89254907538d Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 26 Dec 2024 18:23:56 +1100 Subject: [PATCH 0114/1331] Milking cows/goats will also give cream, which can be skimmed off the top of the milk with an empty container. --- code/datums/extensions/milkable/milkable.dm | 19 +++++++++++--- .../crafting/metalwork/metalwork_items.dm | 26 +++++++++---------- code/modules/materials/_materials.dm | 2 ++ code/modules/reagents/Chemistry-Holder.dm | 8 +++++- code/modules/reagents/chems/chems_drinks.dm | 1 + code/modules/reagents/reagent_containers.dm | 15 +++++++++++ 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/code/datums/extensions/milkable/milkable.dm b/code/datums/extensions/milkable/milkable.dm index debc42f48536..523a37e3e533 100644 --- a/code/datums/extensions/milkable/milkable.dm +++ b/code/datums/extensions/milkable/milkable.dm @@ -8,6 +8,10 @@ var/milk_min = 5 var/milk_max = 10 + var/cream_type = /decl/material/liquid/drink/milk/cream + var/cream_min = 2 + var/cream_max = 5 + var/impatience = 0 var/decl/skill/milking_skill = SKILL_BOTANY var/milking_skill_req = SKILL_BASIC @@ -42,9 +46,16 @@ create_milk() /datum/extension/milkable/proc/create_milk() - var/create_milk = min(rand(milk_min, milk_max), REAGENTS_FREE_SPACE(udder)) - if(create_milk > 0) - udder.add_reagent(milk_type, create_milk, get_milk_data()) + + var/create_milk = min(rand(milk_min, milk_max), REAGENTS_FREE_SPACE(udder)) + var/create_cream = min(rand(cream_min, cream_max), REAGENTS_FREE_SPACE(udder) - create_milk) + + if(create_milk <= 0 && create_cream <= 0) + return + + var/list/milk_data = get_milk_data() + udder.add_reagent(milk_type, create_milk, milk_data) + udder.add_reagent(cream_type, create_cream, milk_data) /datum/extension/milkable/proc/get_milk_data() var/static/list/milk_data = list( @@ -113,7 +124,7 @@ SPAN_NOTICE("\The [user] milks \the [critter] into \the [container]."), SPAN_NOTICE("You milk \the [critter] into \the [container].") ) - udder.trans_type_to(container, milk_type, min(REAGENTS_FREE_SPACE(container.reagents), rand(15, 20))) + udder.trans_to(container, min(REAGENTS_FREE_SPACE(container.reagents), rand(15, 20))) return TRUE /datum/extension/milkable/proc/handle_milking_failure(mob/user, mob/living/critter) diff --git a/code/modules/crafting/metalwork/metalwork_items.dm b/code/modules/crafting/metalwork/metalwork_items.dm index 5815340c135a..5278aaf343ec 100644 --- a/code/modules/crafting/metalwork/metalwork_items.dm +++ b/code/modules/crafting/metalwork/metalwork_items.dm @@ -19,37 +19,37 @@ obj_flags = OBJ_FLAG_NO_STORAGE var/max_held = 10 -/obj/item/chems/crucible/attackby(obj/item/W, mob/user) +/obj/item/chems/crucible/attackby(obj/item/used_item, mob/user) // Fill a mould. - if(istype(W, /obj/item/chems/mould)) - if(W.material?.hardness <= MAT_VALUE_MALLEABLE) - to_chat(user, SPAN_WARNING("\The [W] is currently too soft to be used as a mould.")) + if(istype(used_item, /obj/item/chems/mould)) + if(used_item.material?.hardness <= MAT_VALUE_MALLEABLE) + to_chat(user, SPAN_WARNING("\The [used_item] is currently too soft to be used as a mould.")) return TRUE - if(standard_pour_into(user, W)) + if(standard_pour_into(user, used_item)) return TRUE // Skim off any slag. - if(istype(W, /obj/item/chems) && ATOM_IS_OPEN_CONTAINER(W) && W.reagents) + if(istype(used_item, /obj/item/chems) && ATOM_IS_OPEN_CONTAINER(used_item) && used_item.reagents) // Pour contents into the crucible. - if(W.reagents.total_volume) - var/obj/item/chems/pouring = W + if(used_item.reagents.total_volume) + var/obj/item/chems/pouring = used_item if(pouring.standard_pour_into(user, src)) return TRUE // Attempting to skim off slag. // TODO: check for appropriate vessel material? Check melting point against temperature of crucible? if(reagents?.total_volume && length(reagents.reagent_volumes) > 1) - var/removing = min(amount_per_transfer_from_this, REAGENTS_FREE_SPACE(W.reagents)) + var/removing = min(amount_per_transfer_from_this, REAGENTS_FREE_SPACE(used_item.reagents)) if(removing < length(reagents.reagent_volumes)-1) - to_chat(user, SPAN_WARNING("\The [W] is full.")) + to_chat(user, SPAN_WARNING("\The [used_item] is full.")) return TRUE // Remove a portion, excepting the primary reagent. - var/old_amt = W.reagents.total_volume + var/old_amt = used_item.reagents.total_volume var/decl/material/primary_mat = reagents.get_primary_reagent_decl() - reagents.trans_to_holder(W.reagents, removing, skip_reagents = list(primary_mat.type)) - to_chat(user, SPAN_NOTICE("You skim [W.reagents.total_volume-old_amt] unit\s of slag from the top of \the [primary_mat].")) + reagents.trans_to_holder(used_item.reagents, removing, skip_reagents = list(primary_mat.type)) + to_chat(user, SPAN_NOTICE("You skim [used_item.reagents.total_volume-old_amt] unit\s of slag from the top of \the [primary_mat].")) return TRUE return ..() diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index f4a057dd4115..5a88f901d296 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -141,6 +141,8 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) var/list/stack_origin_tech = @'{"materials":1}' // Research level for stacks. // Attributes + /// Does this material float to the top of liquids, allowing it to be skimmed off? Specific to cream at time of writing. + var/skimmable = FALSE /// How rare is this material in exoplanet xenoflora? var/exoplanet_rarity_plant = MAT_RARITY_MUNDANE /// How rare is this material in exoplanet atmospheres? diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index d35715d884ad..e67584ef67c1 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -907,6 +907,12 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /* Atom reagent creation - use it all the time */ +/datum/reagents/proc/get_skimmable_reagents() + for(var/mat in reagent_volumes) + var/decl/material/reagent = GET_DECL(mat) + if(reagent.skimmable) + LAZYADD(., mat) + /atom/proc/create_reagents(var/max_vol) if(reagents) log_debug("Attempted to create a new reagents holder when already referencing one: [log_info_line(src)]") @@ -926,4 +932,4 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new var/decl/material/newreagent = GET_DECL(reagent_type) if(!istype(newreagent)) return FALSE - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/reagents/chems/chems_drinks.dm b/code/modules/reagents/chems/chems_drinks.dm index ba069e72a232..c8f1650d7f5a 100644 --- a/code/modules/reagents/chems/chems_drinks.dm +++ b/code/modules/reagents/chems/chems_drinks.dm @@ -308,6 +308,7 @@ taste_description = "creamy milk" color = "#dfd7af" uid = "chem_drink_cream" + skimmable = TRUE glass_name = "cream" glass_desc = "Ewwww..." diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index a39da55a863c..1d82ec12a025 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -100,6 +100,21 @@ return /obj/item/chems/attackby(obj/item/used_item, mob/user) + + // Skimming off cream, repurposed from crucibles. + // TODO: potentially make this an alt interaction and unify with slag skimming. + if(istype(used_item, /obj/item/chems) && ATOM_IS_OPEN_CONTAINER(used_item) && used_item.reagents?.maximum_volume && reagents?.total_volume && length(reagents.reagent_volumes) > 1) + var/list/skimmable_reagents = reagents.get_skimmable_reagents() + if(length(skimmable_reagents)) + var/removing = min(amount_per_transfer_from_this, REAGENTS_FREE_SPACE(used_item.reagents)) + if(removing <= 0) + to_chat(user, SPAN_WARNING("\The [used_item] is full.")) + else + var/old_amt = used_item.reagents.total_volume + reagents.trans_to_holder(used_item.reagents, removing, skip_reagents = (reagents.reagent_volumes - skimmable_reagents)) + to_chat(user, SPAN_NOTICE("You skim [used_item.reagents.total_volume-old_amt] unit\s of [used_item.reagents.get_primary_reagent_name()] from the top of \the [reagents.get_primary_reagent_name()].")) + return TRUE + if(used_item.user_can_attack_with(user, silent = TRUE)) if(IS_PEN(used_item)) var/tmp_label = sanitize_safe(input(user, "Enter a label for [name]", "Label", label_text), MAX_NAME_LEN) From 14cd7d0f3d229f9c4fcc5f961fed3ef8f0690926 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 22 Dec 2024 19:44:07 -0500 Subject: [PATCH 0115/1331] Add covered wells --- code/game/objects/structures/well.dm | 7 +++++++ icons/obj/structures/well.dmi | Bin 1279 -> 2021 bytes 2 files changed, 7 insertions(+) diff --git a/code/game/objects/structures/well.dm b/code/game/objects/structures/well.dm index 10c34059fea6..1b2039aa77e0 100644 --- a/code/game/objects/structures/well.dm +++ b/code/game/objects/structures/well.dm @@ -32,6 +32,10 @@ . = ..() if(reagents?.total_volume) add_overlay(overlay_image(icon, "[icon_state]-fluid", reagents.get_color(), (RESET_COLOR | RESET_ALPHA))) + if(istype(reinf_material)) // reinf_material -> roof and posts, at this point in time + var/image/roof_image = overlay_image(icon, "[icon_state]-roof", reinf_material.color, RESET_COLOR | RESET_ALPHA | KEEP_APART) + roof_image.pixel_y = 16 // we have to use 32x32 sprites but want this to be, effectively, 48x32 + add_overlay(roof_image) /obj/structure/reagent_dispensers/well/on_reagent_change() if(!(. = ..())) @@ -64,6 +68,9 @@ /obj/structure/reagent_dispensers/well/mapped auto_refill = /decl/material/liquid/water +/obj/structure/reagent_dispensers/well/mapped/covered + reinf_material = /decl/material/solid/organic/wood/walnut + /obj/structure/reagent_dispensers/well/wall_fountain name = "wall fountain" desc = "An intricately-constructed fountain set into a wall." diff --git a/icons/obj/structures/well.dmi b/icons/obj/structures/well.dmi index 33a27086c184cfaa2fdc1555d80edb09aada4c59..9a6402de82652553c919d3ce3359b4ca0239161c 100644 GIT binary patch delta 2020 zcmVV=-0C=2JR&a84 z_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex7wuvIWN;^NFm%}mcIfpCgT5=&AQY!#Hs z^NVs)l(;xkGK-28Y!wW-IMa#}b5kK~R8_iZIi;Dzsx8PbE`KQ|Qf*Owei|;N$_lQ2 zE($Kbo?HNCc0muY+L+b=00$aLL_t(|ob6gsNE}BP{!V;|)rSx{F76&xPL8|8JV~N? zv!0NclRT7yd1$Rk8!CO02of4XVxSl#5b&v7Nhvn;MaYwQHUUr5zC;u5B{_3)^vH(0 z4aZ@*2gN+J*?&H4W}KaySv}3U+a@0@w>vvKGv7D=|Ia_8p2fvQyw9j{dH_!2g8)ts zz-heq0@POe!SZs}kiF~Z2zd53oc+hP0`P?U`y*&>ZZ4~*X&R=d6PCLF1wlNrWsndT z5!N)#QpP`%vgzpr0Cx*fCV+@IOYoboFH0Ny@u!Kh`hRX0!Xm)(a@H6fy+EHeO~bcC zSK(I`S{4k4kx8c!42NkMdCsKMv@Dm`@k8u;i)Gxp^|R|lpsT?0a@OeYk3iEj06?^N z0OtoU!LKR^hQk)Z04Q0OGyq!HuPQh{cnQ(o0RX0${rwT!AnF7xL6FQ=w0D3K=Lr(9 zmLRJW?SDIqOgfEd?*QVz-F+tm?&|7#B767n;ltyVw&z(~T(p>ew08jAJrN-(maIqs znHa%v7z^_WDncc$ARvdrPlkPF%Sj9nSpsQ4lYdU5qa$ETo`@wP`*E{R_$w2otE=mY zOoZBrn|BO0%6IofurQyXF@PtS%j?2HlQu-6EzBp--4n6YpSXF)xcuEkp&R`1`?8dz zjO+2gdQUk!kd;+<=4!^2cNN>L05H=(dhLm;pL_k)U^>y+0EZ!KK$6soN zCx4ZYu@mEsu_<){&zaL-7=Ss0!y|)eYdZ?S3{-$Agp9Nu>JHS?StkKDm0H2%?I|jZ z%mP@{c>O_C#VI?SzldCni7osl*c?K^9E9v!^^P1BG{tpETe zzxjFtbqDHqMZ!*yoe<_>$peT805CiR>jI!{u(Q4!cd908`GJvDfcWp^?I{}3qtQ+PR+21IsTE3^ z&kv$~XQ}(RNeF5I#3$pkGYGZ@%Lo8lGg=8U=`{SRg81yrj{SZ=Q3F_)wqR=zxqrNF zNoF$Xw3*YtwSbeIZHPuY>2E+$JH9op8M`s;A#tGYKs|!3LBwZggsJTC$RHjqrEsXR z0f!nJ0D#^Rb~I`V+csW1Ti`b*q$d?gZu2k!!(e;IWhX%@Bu@PFV15y9<* zc~m68{}CVlHV6Q-4;~wRr%rhI`*#UhALHZWjvbRefkst zaQN`yvif|Ps1A$z`g%+I2Y(M9r0=KzEv-t`McA&7Y&Hu3l)RqH?YU3I@5jc5eDh_o zfRc=ZZ;E(gW$ z2e2h@Ah2~LZ&?MLa)WF(i{e{9E;9b?YicSZO|Z7MX4WqjEf%hzMS<7hbQ|LYjX^E`nmQNbB3d6?uTjGy&6uS%_xY4*d5bRQx|e)j+9K z0s!2fn?o=ZLS}Upir)`Cmy?np<=c@Y?T~ia{s5P)0;kWMfvT!FckUcwu^6siy9Qmi zACsb(F2&X(WdJPg1b+en+@G7XeD8`pfQ6zclnhHMpU*>8Rpj&eZNKG3({&w+q6oir zl?AZTxmYOB0jsJCRaNOdTh2n)^)iyAoqRrTX`jFqF@P*lrBVsWWD>Yq4Du?<47n(pS?TS%I+4d?>}E%wVnq!iRW(<6#jlc;J5_<<_S@# z#XO3yRveP`;a!To0N~t4%ryd#C33&}aE$V=-0C=2r$~g*v zFcd}6I)4=ptre#>jYK4c?mZwx_t{P3%h&aq-eWdk5GfA%lg2_4v8i&NZf3q zgG61CaF!Pr-wAPw781#Q_|+sN4Eq3ylEmz%&A$Cr*}R_oYkyO8qkf z%m4rcLPu`aiCyKz@Sq}a4CxE#gLQG(u-xez1YInH4CDK4U!;-_9Ww+vOR47WM(w8v79#A zCV3!MGyC`Nyni?U|CyawyWPgSjHyWvVA6Omz@!H-X}t3S#K(Q0(>e8Z_ZAng#a?eX zZyv7{Kqg!&74p&Yqwe3^E`a}&G$$p6MwAFFimt_7lvuFK5BPem-Y4f z1N`{#2O%;ZJoq_sBCu7U(>e7^r93>(0{~dXGHzAwAeBtOFin9l07jN44S?CEk_p_Z z+<{dr0|>+{mGYrQbP^DXAkCIlEHmOVK>{BMIvZ5)b?09ha5kGg*4;Ze zIJiEpzkgV}-4;UMDwdHe|Vi=~0{k;|wVVqP@5vCd=Uu9fG5EcOx`tra5 z2stnJ$@M_);{{Q@LkA+RBKy>P1^^>h*^YdK2@!ue$kFAFpIMm<-{$+N2B;!|{C8a! zi;LGn$&*+rvLDWWpuFlt$!4?1IuYWV8{0l#<$rU9JofimtOm#gd;NiO(BwlU+WuY( zxk6sB-`v>t?|%1{G7WzJt)nHWBYP1$r2zD2I6PyO8UXh9TIlr$NF@^j>GS7jN=GVT zs4rK@gI1W~Q2jiQ7ykM6r{1&Uama*;@xnjXra%KrpMl{s6VDI;yJ`0Qx<@-E3%` zS<5Rzdu{DDJkNvUoB#kLfByUoGt;w|A`vFYMF`6f_5maU0902iaGVpZ5Gu&KNS-+P ze_dRLWEcbiC=zmoJZ$?20AN`eMr3YoHh=VM5CF3Uzkov@OxJaTJ^eosu#`!|vNG(qlt}{!B7|(|Im0vqe+veCM^t7X0Dod1 zXK(s)NXpfm-}aU(pK6`$)SoD~$AGwc(EHZ!S10~yJhgtm8lyTeGd&Aq!GPUtD7UiJ z)d~)e99+3Nhbvd-0D%2JJaX6)%A@PLEUoArQ6Ud-2O{hDYCh#C6+qRuKBWNw{PpLM z2>}E(LiG#~Y^VT4iaa2l2yJd``*HFrz+)+s#!mf-G7YHz@W>gtNB-<)BU0o6#hKvx zy_!!2OU`2;D0K47Q(Lu4u!8tUH*(~GkW*m*_~o%J9ABvfks+tRXJ1&6Bh4dJ02#6% iDny;IjIjiG8}T Date: Thu, 26 Dec 2024 12:21:14 -0500 Subject: [PATCH 0116/1331] Replace Shaded Hills well with covered well --- maps/shaded_hills/shaded_hills-inn.dmm | 39 ++++++++++---------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/maps/shaded_hills/shaded_hills-inn.dmm b/maps/shaded_hills/shaded_hills-inn.dmm index 62e2a71efb30..7dc097ab79b6 100644 --- a/maps/shaded_hills/shaded_hills-inn.dmm +++ b/maps/shaded_hills/shaded_hills-inn.dmm @@ -771,10 +771,6 @@ /obj/structure/closet/crate/chest/ebony, /turf/floor/path/basalt, /area/shaded_hills/general_store) -"wA" = ( -/obj/abstract/exterior_marker/inside, -/turf/floor/dirt, -/area/shaded_hills/outside/downlands) "xh" = ( /obj/structure/table/wood/ebony, /obj/item/chems/condiment/large/salt, @@ -1212,10 +1208,6 @@ }, /turf/floor/wood/walnut, /area/shaded_hills/inn) -"Iz" = ( -/obj/abstract/exterior_marker/inside, -/turf/wall/brick/basalt, -/area/shaded_hills/outside/downlands) "IB" = ( /obj/structure/door/walnut, /obj/abstract/landmark/lock_preset/shaded_hills/shrine, @@ -1324,8 +1316,7 @@ /turf/floor/path/basalt, /area/shaded_hills/outside/downlands) "Lj" = ( -/obj/structure/reagent_dispensers/well/mapped, -/obj/abstract/exterior_marker/inside, +/obj/structure/reagent_dispensers/well/mapped/covered, /turf/floor/dirt, /area/shaded_hills/outside/downlands) "Ly" = ( @@ -7835,9 +7826,9 @@ HI HI HI TR -Iz -Iz -Iz +TR +TR +TR TR TR EV @@ -7987,9 +7978,9 @@ HI HI TR TR -wA -wA -wA +TR +TR +TR TR TR EV @@ -8139,9 +8130,9 @@ HI HI TR TR -wA +TR Lj -wA +TR TR TR EV @@ -8291,9 +8282,9 @@ HI HI TR TR -wA -wA -wA +TR +TR +TR TR TR TR @@ -8443,9 +8434,9 @@ HI HI HI TR -Iz -Iz -Iz +TR +TR +TR TR TR EV From b485b3ec68ebc62deefd48e8c95a8d4481d5c128 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 26 Dec 2024 22:17:59 -0500 Subject: [PATCH 0117/1331] Combine unnecessary duplicate proc overrides in core code --- code/_helpers/unsorted.dm | 5 +-- code/datums/datum.dm | 7 ---- .../machinery/_machines_base/machinery.dm | 7 +--- code/game/machinery/doors/airlock.dm | 3 -- code/game/machinery/hologram.dm | 10 ++--- code/game/machinery/vending/_vending.dm | 5 --- code/game/objects/items/__item.dm | 5 +-- code/game/objects/items/devices/tvcamera.dm | 1 + code/game/objects/items/flame/flame_torch.dm | 5 +-- code/game/objects/items/trash.dm | 7 +--- code/game/objects/items/weapons/RCD.dm | 5 +-- .../objects/items/weapons/material/shards.dm | 7 +--- .../items/weapons/melee/energy_sword.dm | 3 -- .../objects/items/weapons/storage/backpack.dm | 7 +--- code/game/objects/random/subtypes/clothing.dm | 16 -------- code/game/objects/random/subtypes/misc.dm | 10 ----- .../game/objects/random/subtypes/paperwork.dm | 3 +- code/game/objects/structures/fires.dm | 5 +-- code/game/objects/structures/girders.dm | 11 ++---- code/game/objects/structures/target_stake.dm | 7 +--- code/game/objects/structures/watercloset.dm | 38 +++++++++---------- code/modules/butchery/butchery_products.dm | 6 --- .../clothing/spacesuits/rig/modules/combat.dm | 4 -- code/modules/events/meteors.dm | 7 +--- .../designs/general/designs_general.dm | 11 +----- code/modules/games/cards.dm | 5 +-- code/modules/mechs/components/body.dm | 33 ++++++++-------- code/modules/mechs/premade/heavy.dm | 4 -- code/modules/mechs/premade/misc.dm | 4 -- code/modules/mob/living/brain/brain.dm | 11 ++---- .../modules/mob/living/human/human_species.dm | 3 -- code/modules/mob/living/living.dm | 17 ++++----- .../robot/modules/module_maintenance_drone.dm | 7 +--- code/modules/mob/living/silicon/silicon.dm | 3 -- code/modules/mob/mob.dm | 5 --- code/modules/organs/internal/_internal.dm | 14 +++---- .../overmap/contacts/contact_sensors.dm | 9 ++--- code/modules/persistence/persistence_datum.dm | 6 +-- code/modules/projectiles/gun.dm | 7 +--- code/modules/projectiles/projectile.dm | 5 +-- code/modules/reagents/reagent_containers.dm | 7 +--- .../reagents/reagent_containers/borghydro.dm | 3 -- .../drinkingglass/drinkingglass.dm | 25 ++++-------- .../reagents/reagent_containers/hypospray.dm | 5 +-- .../tools/ano_device_battery.dm | 5 +-- 45 files changed, 99 insertions(+), 274 deletions(-) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 45dc05bd2c83..32eac707ce72 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -719,11 +719,8 @@ Turf and target are seperate in case you want to teleport some distance from a t /obj/item/weldingtool/can_puncture() return 1 -/obj/item/screwdriver/can_puncture() - return 1 - /obj/item/clothing/mask/smokable/cigarette/can_puncture() - return src.lit + return ..() || lit // in case someone has a sharp cigarette for some reason /* Checks if that loc and dir has a item on the wall diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 0fbfeaf0232b..a0aeed07808d 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -111,10 +111,3 @@ */ /datum/proc/PopulateClone(var/datum/clone) return clone - -///////////////////////////////////////////////////////////// -//Common implementations -///////////////////////////////////////////////////////////// - -/image/GetCloneArgs() - return list(icon, loc, icon_state, layer, dir) \ No newline at end of file diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index 6736ad221bbe..d6fafc99f1e5 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -234,6 +234,8 @@ Class Procs: /obj/machinery/CouldUseTopic(var/mob/user) ..() user.set_machine(src) + if(clicksound && isliving(user)) + playsound(src, clicksound, clickvol) /obj/machinery/CouldNotUseTopic(var/mob/user) user.unset_machine() @@ -391,11 +393,6 @@ Class Procs: /datum/proc/remove_visual(mob/M) return -/obj/machinery/CouldUseTopic(var/mob/user) - ..() - if(clicksound && isliving(user)) - playsound(src, clicksound, clickvol) - /obj/machinery/proc/display_parts(mob/user) to_chat(user, "Following parts detected in the machine:") for(var/obj/item/C in component_parts) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index c9149697a668..aa341461f1c8 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -141,9 +141,6 @@ About the new airlock wires panel: return ..(user) -/obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user) - ..(user) - /obj/machinery/door/airlock/proc/isElectrified() if(src.electrified_until != 0) return 1 diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 9d871d3e2b56..ce3c79b45d88 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -80,6 +80,9 @@ var/global/list/holopads = list() /obj/machinery/hologram/holopad/Destroy() global.listening_objects -= src + global.holopads -= src + for (var/mob/living/master in masters) + clear_holo(master) return ..() /obj/machinery/hologram/holopad/interface_interact(var/mob/living/human/user) //Carn: Hologram requests. @@ -387,13 +390,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ idle_power_usage = 5 active_power_usage = 100 -//Destruction procs. -/obj/machinery/hologram/holopad/Destroy() - global.holopads -= src - for (var/mob/living/master in masters) - clear_holo(master) - return ..() - /* * Other Stuff: Is this even used? */ diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index 2bc56a9c1051..866062f2a54b 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -195,11 +195,6 @@ . = ..() SSnano.update_uis(src) -/obj/machinery/vending/receive_mouse_drop(atom/dropping, mob/user, params) - . = ..() - if(!. && dropping.loc == user && attempt_to_stock(dropping, user)) - return TRUE - /obj/machinery/vending/proc/attempt_to_stock(var/obj/item/I, var/mob/user) for(var/datum/stored_items/vending_products/R in product_records) if(I.type == R.item_path) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 5081ba6b45d8..e2e71c3653ec 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -378,7 +378,7 @@ return ..(user, distance, "", jointext(desc_comp, "
      ")) /obj/item/check_mousedrop_adjacency(var/atom/over, var/mob/user) - . = (loc == user && istype(over, /obj/screen/inventory)) || ..() + . = (loc == user && istype(over, /obj/screen)) || ..() /obj/item/handle_mouse_drop(atom/over, mob/user, params) @@ -1115,9 +1115,6 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. step_towards(src,S) else ..() -/obj/item/check_mousedrop_adjacency(var/atom/over, var/mob/user) - . = (loc == user && istype(over, /obj/screen)) || ..() - // Supplied during loadout gear tweaking. /obj/item/proc/set_custom_name(var/new_name) base_name = new_name diff --git a/code/game/objects/items/devices/tvcamera.dm b/code/game/objects/items/devices/tvcamera.dm index bbcbc43e4889..272de8433723 100644 --- a/code/game/objects/items/devices/tvcamera.dm +++ b/code/game/objects/items/devices/tvcamera.dm @@ -96,6 +96,7 @@ update_held_icon() /* Assembly by a roboticist */ +// TODO: Make this slapcrafting or remove tvcamera/tvassembly entirely /obj/item/robot_parts/head/attackby(var/obj/item/assembly/S, mob/user) if (!istype(S, /obj/item/assembly/infra)) return ..() diff --git a/code/game/objects/items/flame/flame_torch.dm b/code/game/objects/items/flame/flame_torch.dm index 26f616a4a47c..7f0edbe8dd3e 100644 --- a/code/game/objects/items/flame/flame_torch.dm +++ b/code/game/objects/items/flame/flame_torch.dm @@ -13,10 +13,6 @@ var/head_material = /decl/material/solid/organic/cloth var/burnt = FALSE -/obj/item/flame/torch/Initialize() - . = ..() - set_color(null) // clear our scent color - /obj/item/flame/torch/get_available_scents() var/static/list/available_scents = list( /decl/scent_type/woodsmoke @@ -34,6 +30,7 @@ /obj/item/flame/torch/Initialize(var/ml, var/material_key, var/_head_material) . = ..() + set_color(null) // clear our scent color. TODO: allow flame items to disable scent color setting in the first place if(_head_material) head_material = _head_material diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index bf4bfab6f1be..8c88b8edc0df 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -10,15 +10,12 @@ var/age = 0 /obj/item/trash/Initialize(mapload, var/_age) + if(!mapload) + SSpersistence.track_value(src, /decl/persistence_handler/filth/trash) . = ..(mapload) if(!isnull(_age)) age = _age -/obj/item/trash/Initialize(var/ml) - if(!ml) - SSpersistence.track_value(src, /decl/persistence_handler/filth/trash) - . = ..() - /obj/item/trash/Destroy() SSpersistence.forget_value(src, /decl/persistence_handler/filth/trash) . = ..() diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 8e40d1f38f80..f48b28762d0e 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -32,6 +32,7 @@ var/decl/hierarchy/h = GET_DECL(/decl/hierarchy/rcd_mode) work_modes = h.children work_mode = work_modes[1] + update_icon() //Initializes the ammo counter /obj/item/rcd/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) return FALSE @@ -45,10 +46,6 @@ to_chat(user, "The current mode is '[work_mode]'.") to_chat(user, "It currently holds [stored_matter]/[max_stored_matter] matter-units.") -/obj/item/rcd/Initialize() - . = ..() - update_icon() //Initializes the ammo counter - /obj/item/rcd/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/rcd_ammo)) var/obj/item/rcd_ammo/cartridge = W diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index dd248df7bb77..7ff92bf32ad2 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -53,6 +53,8 @@ . = ..() // 1-(1-x)^2, so that glass shards with 0.3 opacity end up somewhat visible at 0.51 opacity alpha = 255 * (material ? (1 - (1 - material.opacity)**2) : 1) + if(has_handle) + add_overlay(overlay_image(icon, "handle", has_handle, RESET_COLOR)) /obj/item/shard/attackby(obj/item/W, mob/user) if(IS_WELDER(W) && material.shard_can_repair) @@ -80,11 +82,6 @@ return TRUE return ..() -/obj/item/shard/on_update_icon() - . = ..() - if(has_handle) - add_overlay(overlay_image(icon, "handle", has_handle, RESET_COLOR)) - /obj/item/shard/Crossed(atom/movable/AM) ..() if(!isliving(AM)) diff --git a/code/game/objects/items/weapons/melee/energy_sword.dm b/code/game/objects/items/weapons/melee/energy_sword.dm index 3d20dd638d3e..045040acb762 100644 --- a/code/game/objects/items/weapons/melee/energy_sword.dm +++ b/code/game/objects/items/weapons/melee/energy_sword.dm @@ -20,9 +20,6 @@ if(!lighting_color) lighting_color = blade_color . = ..() - -/obj/item/energy_blade/sword/Initialize() - . = ..() set_extension(src, /datum/extension/demolisher/energy) /obj/item/energy_blade/sword/is_special_cutting_tool(var/high_power) diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 14a20b4125c1..fb4d2bb67566 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -17,17 +17,14 @@ /obj/item/backpack/can_contaminate() return FALSE -/obj/item/backpack/equipped() - if(!has_extension(src, /datum/extension/appearance)) - set_extension(src, /datum/extension/appearance/cardborg) - ..() - /obj/item/backpack/attackby(obj/item/W, mob/user) if (storage?.use_sound) playsound(src.loc, storage.use_sound, 50, 1, -5) return ..() /obj/item/backpack/equipped(var/mob/user, var/slot) + if(!has_extension(src, /datum/extension/appearance)) + set_extension(src, /datum/extension/appearance/cardborg) if (slot == slot_back_str && storage?.use_sound) playsound(loc, storage.use_sound, 50, 1, -5) return ..(user, slot) diff --git a/code/game/objects/random/subtypes/clothing.dm b/code/game/objects/random/subtypes/clothing.dm index f9bac2fd1c41..8524aff474c2 100644 --- a/code/game/objects/random/subtypes/clothing.dm +++ b/code/game/objects/random/subtypes/clothing.dm @@ -238,19 +238,3 @@ /obj/item/clothing/suit/space/void/medical/alt ) return spawnable_choices - -/obj/random/hardsuit - name = "Random Hardsuit" - desc = "This is a random hardsuit control module." - icon = 'icons/obj/rig_modules.dmi' - icon_state = "generic" - -/obj/random/hardsuit/spawn_choices() - var/static/list/spawnable_choices = list( - /obj/item/rig/industrial, - /obj/item/rig/eva, - /obj/item/rig/light/hacker, - /obj/item/rig/light/stealth, - /obj/item/rig/light - ) - return spawnable_choices diff --git a/code/game/objects/random/subtypes/misc.dm b/code/game/objects/random/subtypes/misc.dm index b5206b950d30..4ccde91270d2 100644 --- a/code/game/objects/random/subtypes/misc.dm +++ b/code/game/objects/random/subtypes/misc.dm @@ -465,16 +465,6 @@ ) return spawnable_choices -/obj/random/crayon - name = "random crayon" - desc = "This is a random crayon." - icon = 'icons/obj/items/crayons.dmi' - icon_state = "crayonred" - -/obj/random/crayon/spawn_choices() - var/static/list/spawnable_choices = subtypesof(/obj/item/pen/crayon) - return spawnable_choices - /obj/random/umbrella name = "Random Umbrella" desc = "This is a random umbrella." diff --git a/code/game/objects/random/subtypes/paperwork.dm b/code/game/objects/random/subtypes/paperwork.dm index 061b4944609a..a073a9d95067 100644 --- a/code/game/objects/random/subtypes/paperwork.dm +++ b/code/game/objects/random/subtypes/paperwork.dm @@ -12,7 +12,8 @@ icon_state = "crayonred" /obj/random/crayon/spawn_choices() - return subtypesof(/obj/item/pen/crayon) + var/static/list/spawnable_choices = subtypesof(/obj/item/pen/crayon) + return spawnable_choices /obj/random/clipboard name = "random clipboard" diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index cdb416489e39..56dca65f9b5a 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -244,9 +244,6 @@ /obj/structure/fire_source/isflamesource() return (lit == FIRE_LIT) -/obj/structure/fire_source/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - return ..() || (istype(mover) && mover.checkpass(PASS_FLAG_TABLE)) - /obj/structure/fire_source/proc/burn_material(var/decl/material/mat, var/amount) var/effective_burn_temperature = get_effective_burn_temperature() . = mat.get_burn_products(amount, effective_burn_temperature) @@ -492,7 +489,7 @@ try_light(1000) /obj/structure/fire_source/CanPass(atom/movable/mover, turf/target, height, air_group) - . = ..() + . = ..() || mover?.checkpass(PASS_FLAG_TABLE) if(. && lit && ismob(mover)) var/mob/M = mover if(!MOVING_QUICKLY(M)) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index c508eb305f99..a82c4c7733da 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -19,13 +19,6 @@ set_extension(src, /datum/extension/penetration/simple, 100) . = ..() -/obj/structure/girder/can_unanchor(var/mob/user) - . = ..() - var/turf/T = loc - if(!anchored && . && (!istype(T) || T.is_open())) - to_chat(user, SPAN_WARNING("You can only secure \the [src] to solid ground.")) - return FALSE - /obj/structure/girder/handle_default_screwdriver_attackby(var/mob/user, var/obj/item/screwdriver) if(reinf_material) @@ -88,6 +81,10 @@ to_chat(user, SPAN_WARNING("You must remove the support rods before you can dislodge \the [src].")) return FALSE . = ..() + var/turf/T = loc + if(!anchored && . && (!istype(T) || T.is_open())) + to_chat(user, SPAN_WARNING("You can only secure \the [src] to solid ground.")) + return FALSE /obj/structure/girder/can_dismantle(var/mob/user) if(reinf_material) diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index 0dc8afefce6a..38feb74fd25d 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -15,11 +15,6 @@ dummy = null . = ..() -/obj/structure/target_stake/attackby(obj/item/used_item, mob/user) - if(dummy?.repair_target_dummy(used_item, user)) - return TRUE - return ..() - /obj/structure/target_stake/take_damage(damage, damage_type, damage_flags, inflicter, armor_pen, silent, do_update_health) if(dummy) . = dummy.take_damage(damage, damage_type, damage_flags, inflicter, armor_pen, silent, do_update_health) @@ -42,6 +37,8 @@ return ..() /obj/structure/target_stake/attackby(obj/item/used_item, mob/user) + if(dummy?.repair_target_dummy(used_item, user)) + return TRUE if(istype(used_item, /obj/item/training_dummy) && can_hold_dummy(user, used_item)) if(dummy) to_chat(user, SPAN_WARNING("\The [src] is already holding \the [dummy].")) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 454e6e9b6b53..549d7d913abd 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -49,6 +49,23 @@ var/global/list/hygiene_props = list() if(clogged <= 0) unclog() return TRUE + //toilet paper interaction for clogging toilets and other facilities + if (istype(thing, /obj/item/stack/tape_roll/barricade_tape/toilet)) + if (clogged == -1) + to_chat(user, SPAN_WARNING("Try as you might, you can not clog \the [src] with \the [thing].")) + return TRUE + if (clogged) + to_chat(user, SPAN_WARNING("\The [src] is already clogged.")) + return TRUE + if (!do_after(user, 3 SECONDS, src)) + to_chat(user, SPAN_WARNING("You must stay still to clog \the [src].")) + return TRUE + if (clogged || QDELETED(thing) || !user.try_unequip(thing)) + return TRUE + to_chat(user, SPAN_NOTICE("You unceremoniously jam \the [src] with \the [thing]. What a rebel.")) + clog(1) + qdel(thing) + return TRUE . = ..() /obj/structure/hygiene/examine(mob/user) @@ -474,27 +491,6 @@ var/global/list/hygiene_props = list() . = ..() icon_state = "puddle" -//toilet paper interaction for clogging toilets and other facilities - -/obj/structure/hygiene/attackby(obj/item/I, mob/user) - if (!istype(I, /obj/item/stack/tape_roll/barricade_tape/toilet)) - return ..() - if (clogged == -1) - to_chat(user, SPAN_WARNING("Try as you might, you can not clog \the [src] with \the [I].")) - return TRUE - if (clogged) - to_chat(user, SPAN_WARNING("\The [src] is already clogged.")) - return TRUE - if (!do_after(user, 3 SECONDS, src)) - to_chat(user, SPAN_WARNING("You must stay still to clog \the [src].")) - return TRUE - if (clogged || QDELETED(I) || !user.try_unequip(I)) - return TRUE - to_chat(user, SPAN_NOTICE("You unceremoniously jam \the [src] with \the [I]. What a rebel.")) - clog(1) - qdel(I) - return TRUE - //////////////////////////////////////////////////// // Toilet Paper Roll //////////////////////////////////////////////////// diff --git a/code/modules/butchery/butchery_products.dm b/code/modules/butchery/butchery_products.dm index 23f9dc4858c9..075345d3e337 100644 --- a/code/modules/butchery/butchery_products.dm +++ b/code/modules/butchery/butchery_products.dm @@ -63,12 +63,6 @@ else return ..() -/obj/item/food/butchery/get_dried_product() - . = ..() - if(meat_name && istype(., /obj/item/food/jerky)) - var/obj/item/food/jerky/jerk = . - jerk.set_meat_name(meat_name) - /obj/item/food/butchery/get_drying_state(var/obj/rack) return "meat" diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 5f16eca89002..0386948c6020 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -215,10 +215,6 @@ var/obj/item/gun/gun -/obj/item/rig_module/mounted/Destroy() - QDEL_NULL(gun) - . = ..() - /obj/item/rig_module/mounted/Initialize() . = ..() if(ispath(gun)) diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm index f291cda35f54..b39f3c9da047 100644 --- a/code/modules/events/meteors.dm +++ b/code/modules/events/meteors.dm @@ -243,6 +243,8 @@ var/global/list/meteors_major = list( . = ..() z_original = z global.meteor_list += src + if(!ismissile) + SpinAnimation() /obj/effect/meteor/Move() . = ..() //process movement... @@ -259,11 +261,6 @@ var/global/list/meteors_major = list( global.meteor_list -= src . = ..() -/obj/effect/meteor/Initialize() - . = ..() - if(!ismissile) - SpinAnimation() - /obj/effect/meteor/Bump(atom/A) ..() if(A && !QDELETED(src)) // Prevents explosions and other effects when we were deleted by whatever we Bumped() - currently used by shields. diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index 69b99bda0b9f..fda6ce15bad0 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -188,7 +188,7 @@ /datum/fabricator_recipe/fishing_line_high_quality path = /obj/item/fishing_line/high_quality -/datum/fabricator_recipe/chipboard +/datum/fabricator_recipe/chipboard // base type is for oak path = /obj/item/stack/material/sheet/mapped/chipboard_oak category = "Textiles" fabricator_types = list( @@ -202,15 +202,6 @@ /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) ) -/datum/fabricator_recipe/chipboard - path = /obj/item/stack/material/sheet/mapped/chipboard_oak - -/datum/fabricator_recipe/chipboard/get_resources() - resources = list( - /decl/material/solid/organic/wood/oak = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2), - /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) - ) - /datum/fabricator_recipe/chipboard/maple path = /obj/item/stack/material/sheet/mapped/chipboard_maple diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index c4351b213e2b..8084a66d09df 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -27,6 +27,7 @@ var/global/list/card_decks = list() /obj/item/deck/Initialize() . = ..() global.card_decks += src + generate_cards() /obj/item/deck/Destroy() . = ..() @@ -70,10 +71,6 @@ var/global/list/card_decks = list() desc = "A simple deck of playing cards." icon_state = "deck" -/obj/item/deck/Initialize() - . = ..() - generate_cards() - /obj/item/deck/proc/generate_cards() return diff --git a/code/modules/mechs/components/body.dm b/code/modules/mechs/components/body.dm index 4d8016e71384..e60ba0724037 100644 --- a/code/modules/mechs/components/body.dm +++ b/code/modules/mechs/components/body.dm @@ -50,6 +50,21 @@ "[WEST]" = list("x" = 8, "y" = 0) ) ) + if(pilot_coverage >= 100) //Open cockpits dont get to have air + cockpit = new + cockpit.volume = 200 + if(loc) + var/datum/gas_mixture/air = loc.return_air() + if(air) + //Essentially at this point its like we created a vacuum, but realistically making a bottle doesnt actually increase volume of a room and neither should a mech + for(var/g in air.gas) + cockpit.gas[g] = (air.gas[g] / air.volume) * cockpit.volume + + cockpit.temperature = air.temperature + cockpit.update_values() + + air_supply = new /obj/machinery/portable_atmospherics/canister/air(src) + storage_compartment = new(src) /obj/item/mech_component/chassis/Destroy() QDEL_NULL(cell) @@ -74,24 +89,6 @@ if(!m_armour) to_chat(user, SPAN_WARNING("It is missing exosuit armour plating.")) -/obj/item/mech_component/chassis/Initialize() - . = ..() - if(pilot_coverage >= 100) //Open cockpits dont get to have air - cockpit = new - cockpit.volume = 200 - if(loc) - var/datum/gas_mixture/air = loc.return_air() - if(air) - //Essentially at this point its like we created a vacuum, but realistically making a bottle doesnt actually increase volume of a room and neither should a mech - for(var/g in air.gas) - cockpit.gas[g] = (air.gas[g] / air.volume) * cockpit.volume - - cockpit.temperature = air.temperature - cockpit.update_values() - - air_supply = new /obj/machinery/portable_atmospherics/canister/air(src) - storage_compartment = new(src) - /obj/item/mech_component/chassis/proc/update_air(var/take_from_supply) var/changed diff --git a/code/modules/mechs/premade/heavy.dm b/code/modules/mechs/premade/heavy.dm index a81ce89533f6..e1dfd62d77f5 100644 --- a/code/modules/mechs/premade/heavy.dm +++ b/code/modules/mechs/premade/heavy.dm @@ -90,10 +90,6 @@ "[WEST]" = list("x" = 12, "y" = 8) ) ) - - . = ..() - -/obj/item/mech_component/chassis/heavy/prebuild() . = ..() m_armour = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src) diff --git a/code/modules/mechs/premade/misc.dm b/code/modules/mechs/premade/misc.dm index 4d150377c327..dafb3bc44d58 100644 --- a/code/modules/mechs/premade/misc.dm +++ b/code/modules/mechs/premade/misc.dm @@ -53,7 +53,3 @@ ) ) . = ..() - -/obj/item/mech_component/chassis/pod/prebuild() - . = ..() - m_armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src) \ No newline at end of file diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index 35b97e4880b8..b80dfb8f85db 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -26,11 +26,6 @@ ) return default_emotes -/mob/living/brain/handle_regular_status_updates() - . = ..() - if(emp_damage || stat == DEAD || !is_in_interface()) - SET_STATUS_MAX(src, STAT_SILENCE, 2) - /mob/living/brain/is_deaf() return emp_damage || stat == DEAD || !is_in_interface() @@ -99,8 +94,10 @@ emp_damage += rand(0,10) emp_damage = clamp(emp_damage, 0, max_emp_damage) -/mob/living/brain/handle_regular_status_updates() // Status & health update, are we dead or alive etc. +/mob/living/brain/handle_regular_status_updates() // Status & health update, are we dead or alive, can we communicate, etc. . = ..() + if(emp_damage || stat == DEAD || !is_in_interface()) + SET_STATUS_MAX(src, STAT_SILENCE, 2) if(stat == DEAD || !isSynthetic()) emp_damage = 0 return @@ -114,4 +111,4 @@ if(emp_damage <= 0) last_emp_message = 0 emp_damage = 0 - to_chat(src, SPAN_NOTICE("All systems restored.")) + to_chat(src, SPAN_NOTICE("All systems restored.")) \ No newline at end of file diff --git a/code/modules/mob/living/human/human_species.dm b/code/modules/mob/living/human/human_species.dm index 6988a940bed6..c499bbb2da2e 100644 --- a/code/modules/mob/living/human/human_species.dm +++ b/code/modules/mob/living/human/human_species.dm @@ -37,9 +37,6 @@ corpse.equip_corpse_outfit(src) return INITIALIZE_HINT_LATELOAD -/mob/living/human/corpse/get_death_message(gibbed) - return SKIP_DEATH_MESSAGE - /mob/living/human/corpse/LateInitialize() ..() var/current_max_health = get_max_health() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index e76e323abea1..1ac5dbe15c4b 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1524,6 +1524,13 @@ default behaviour is: qdel(grab) if(istype(ai)) ai.on_buckled(M) + reset_layer() + update_icon() + +/mob/living/unbuckle_mob() + . = ..() + reset_layer() + update_icon() /mob/living/try_make_grab(mob/living/user, defer_hand = FALSE) . = ..() @@ -1891,16 +1898,6 @@ default behaviour is: return TRUE return FALSE -/mob/living/buckle_mob(mob/living/M) - . = ..() - reset_layer() - update_icon() - -/mob/living/unbuckle_mob() - . = ..() - reset_layer() - update_icon() - /mob/living/proc/flee(atom/target, upset = FALSE) var/static/datum/automove_metadata/_flee_automove_metadata = new( _move_delay = null, diff --git a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm index fe58498a36e8..b9456bc2050c 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm @@ -115,6 +115,8 @@ ..() var/obj/item/chems/spray/cleaner/drone/SC = locate() in equipment SC.add_to_reagents(/decl/material/liquid/cleaner, 8 * amount) + var/obj/item/lightreplacer/LR = locate() in equipment + LR.Charge(R, amount) /obj/item/robot_module/drone/construction name = "construction drone module" @@ -126,8 +128,3 @@ /obj/item/robot_module/drone/construction/Initialize() equipment += /obj/item/rcd/borg . = ..() - -/obj/item/robot_module/drone/respawn_consumable(var/mob/living/silicon/robot/R, var/amount) - var/obj/item/lightreplacer/LR = locate() in equipment - LR.Charge(R, amount) - ..() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 768dc1c0d513..52457ceb078c 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -68,9 +68,6 @@ QDEL_NULL_LIST(stock_parts) return ..() -/mob/living/silicon/get_dexterity(silent) - return dexterity - /mob/living/silicon/fully_replace_character_name(new_name) ..() create_or_update_account(new_name) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 5ed9d75db534..dfb7852c5288 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1297,11 +1297,6 @@ /mob/proc/get_blood_type() return -// Gets the ID card of a mob, but will not check types in the exceptions list -/mob/GetIdCard(exceptions = null) - RETURN_TYPE(/obj/item/card/id) - return LAZYACCESS(GetIdCards(exceptions), 1) - /mob/get_overhead_text_x_offset() return offset_overhead_text_x diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index ac00fcf30e58..d891023e600d 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -56,6 +56,12 @@ affected.cavity_max_w_class = max(affected.cavity_max_w_class, w_class) affected.update_internal_organs_cost() + // This might need revisiting to stop people successfully implanting brains in groins and transferring minds. + if(transfer_brainmob_with_organ && istype(owner)) + var/mob/living/brainmob = get_brainmob(create_if_missing = FALSE) + if(brainmob?.key) + transfer_key_from_mob_to_mob(brainmob, owner) + /obj/item/organ/internal/do_uninstall(in_place, detach, ignore_children, update_icon) var/mob/living/victim = owner // cleared in parent proc @@ -289,11 +295,3 @@ /obj/item/organ/internal/preserve_in_cryopod(var/obj/machinery/cryopod/pod) var/mob/living/brainmob = get_brainmob() return brainmob?.key - -// This might need revisiting to stop people successfully implanting brains in groins and transferring minds. -/obj/item/organ/internal/do_install(mob/living/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) - . = ..() - if(transfer_brainmob_with_organ && istype(owner)) - var/mob/living/brainmob = get_brainmob(create_if_missing = FALSE) - if(brainmob?.key) - transfer_key_from_mob_to_mob(brainmob, owner) diff --git a/code/modules/overmap/contacts/contact_sensors.dm b/code/modules/overmap/contacts/contact_sensors.dm index f6dee900ce47..fcdd349fb077 100644 --- a/code/modules/overmap/contacts/contact_sensors.dm +++ b/code/modules/overmap/contacts/contact_sensors.dm @@ -16,6 +16,9 @@ var/obj/effect/overmap/overmap_location = loc if(overmap_location.requires_contact) new /datum/overmap_contact(src, overmap_location) + if(!contact_datums[linked]) + var/datum/overmap_contact/record = new(src, linked) + record.marker.alpha = 255 /obj/machinery/computer/ship/sensors/Destroy() objects_in_view.Cut() @@ -23,12 +26,6 @@ trackers.Cut() . = ..() -/obj/machinery/computer/ship/sensors/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) - . = ..() - if(. && linked && !contact_datums[linked]) - var/datum/overmap_contact/record = new(src, linked) - record.marker.alpha = 255 - /obj/machinery/computer/ship/sensors/proc/reveal_contacts(var/mob/user) if(user && user.client) for(var/key in contact_datums) diff --git a/code/modules/persistence/persistence_datum.dm b/code/modules/persistence/persistence_datum.dm index 0c9e0aedca6b..557ae252b88a 100644 --- a/code/modules/persistence/persistence_datum.dm +++ b/code/modules/persistence/persistence_datum.dm @@ -12,10 +12,6 @@ var/ignore_area_flags = FALSE // Set to TRUE to skip area flag checks such as nonpersistent areas. var/ignore_invalid_loc = FALSE // Set to TRUE to skip checking for a non-null station turf for the entry. -/decl/persistence_handler/Initialize() - SetFilename() - . = ..() - /decl/persistence_handler/proc/SetFilename() if(name) filename = "data/persistent/[ckey(global.using_map.name)]-[ckey(name)].json" @@ -95,6 +91,8 @@ /decl/persistence_handler/Initialize() + SetFilename() + . = ..() if(!fexists(filename)) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 83ee026551b2..4de22aa06166 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -259,7 +259,8 @@ /obj/item/gun/dropped(var/mob/living/user) check_accidents(user) update_icon() - return ..() + . = ..() + clear_autofire() /obj/item/gun/proc/Fire(atom/target, atom/movable/firer, clickparams, pointblank = FALSE, reflex = FALSE, set_click_cooldown = TRUE, target_zone = BP_CHEST) if(!firer || !target) @@ -708,10 +709,6 @@ afterattack(shoot_to,target) return 1 -/obj/item/gun/dropped(mob/living/user) - . = ..() - clear_autofire() - /obj/item/gun/proc/can_autofire() return (autofire_enabled && world.time >= next_fire_time) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index fbf7adf743f3..07ad3c6c6865 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -97,7 +97,7 @@ else animate_movement = NO_STEPS . = ..() -/obj/item/projectile/CanPass() +/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) return TRUE /obj/item/projectile/damage_flags() @@ -315,9 +315,6 @@ SHOULD_CALL_PARENT(FALSE) return -/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - return 1 - /obj/item/projectile/proc/before_move() return diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index a39da55a863c..615e68c3be99 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -30,6 +30,8 @@ var/image/contents_overlay = get_reagents_overlay(use_single_icon ? icon_state : null) if(contents_overlay) add_overlay(contents_overlay) + if(detail_state) + add_overlay(overlay_image(icon, "[initial(icon_state)][detail_state]", detail_color || COLOR_WHITE, RESET_COLOR)) /obj/item/chems/apply_additional_mob_overlays(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing) var/image/reagents_overlay = get_reagents_overlay(overlay.icon_state) @@ -50,11 +52,6 @@ return TRUE return FALSE -/obj/item/chems/on_update_icon() - . = ..() - if(detail_state) - add_overlay(overlay_image(icon, "[initial(icon_state)][detail_state]", detail_color || COLOR_WHITE, RESET_COLOR)) - /obj/item/chems/update_name() . = ..() // handles material, etc var/newname = name diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index 06147c369b40..0098dd2a9b81 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -37,9 +37,6 @@ reagent_volumes[T] = volume var/decl/material/R = T reagent_names += initial(R.name) - -/obj/item/chems/borghypo/Initialize() - . = ..() START_PROCESSING(SSobj, src) /obj/item/chems/borghypo/Destroy() diff --git a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm index 1dc986fb81be..386ad49b09a5 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm @@ -39,23 +39,6 @@ var/global/const/DRINK_ICON_NOISY = "noise" if(obj_flags & OBJ_FLAG_HOLLOW) . /= HOLLOW_OBJECT_MATTER_MULTIPLIER -/obj/item/chems/drinks/glass2/examine(mob/M) - . = ..() - - for(var/I in extras) - if(istype(I, /obj/item/glass_extra)) - to_chat(M, "There is \a [I] in \the [src].") - else if(istype(I, /obj/item/food/processed_grown/slice)) - to_chat(M, "There is \a [I] on the rim.") - else - to_chat(M, "There is \a [I] somewhere on the glass. Somehow.") - - if(has_ice()) - to_chat(M, "There is some ice floating in the drink.") - - if(has_fizz()) - to_chat(M, "It is fizzing slightly.") - /obj/item/chems/drinks/glass2/proc/has_ice() if(LAZYLEN(reagents.reagent_volumes)) var/decl/material/R = reagents.get_primary_reagent_decl() @@ -113,7 +96,7 @@ var/global/const/DRINK_ICON_NOISY = "noise" /obj/item/chems/drinks/glass2/examine(mob/user, distance) . = ..() - if(!istype(user) || distance > 1) + if(!istype(user)) return var/list/extra_text for(var/extra in extras) @@ -122,8 +105,14 @@ var/global/const/DRINK_ICON_NOISY = "noise" LAZYADD(extra_text, GE.glass_desc) else if(istype(extra, /obj/item/food/processed_grown/slice)) LAZYADD(extra_text, "There is \a [extra] on the rim.") + else + to_chat(user, "There is \a [extra] somewhere on the glass. Somehow.") if(length(extra_text)) to_chat(user, SPAN_NOTICE(jointext(extra_text," "))) + if(has_ice()) + to_chat(user, "There is some ice floating in the drink.") + if(has_fizz()) + to_chat(user, "It is fizzing slightly.") /obj/item/chems/drinks/glass2/proc/get_filling_overlay(amount, overlay) diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index af8367645f71..be5659ebba4e 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -185,6 +185,7 @@ . = ..() if(label_text) update_name() + update_icon() /obj/item/chems/hypospray/autoinjector/populate_reagents() SHOULD_CALL_PARENT(TRUE) @@ -192,10 +193,6 @@ if(reagents?.total_volume > 0 && autolabel && !label_text) // don't override preset labels label_text = "[reagents.get_primary_reagent_name()], [reagents.total_volume]u" -/obj/item/chems/hypospray/autoinjector/Initialize() - . = ..() - update_icon() - /obj/item/chems/hypospray/autoinjector/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) . = ..() if(.) diff --git a/code/modules/xenoarcheaology/tools/ano_device_battery.dm b/code/modules/xenoarcheaology/tools/ano_device_battery.dm index 0ac71ec22783..86f037fd0f88 100644 --- a/code/modules/xenoarcheaology/tools/ano_device_battery.dm +++ b/code/modules/xenoarcheaology/tools/ano_device_battery.dm @@ -57,6 +57,7 @@ /obj/item/anodevice/Destroy() inserted_battery = null + STOP_PROCESSING(SSobj, src) . = ..() /obj/item/anodevice/attackby(var/obj/I, var/mob/user) @@ -188,10 +189,6 @@ else icon_state = "anodev_empty" -/obj/item/anodevice/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - /obj/item/anodevice/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) if (!istype(target)) return ..() From 762711c04c4404b7364c19ecab15be8ec91c5428 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 26 Dec 2024 22:41:53 -0500 Subject: [PATCH 0118/1331] Condense assembly and timer side-overrides into definitions --- code/modules/assembly/assembly.dm | 70 ++++++++++-------------------- code/modules/assembly/holder.dm | 52 ++++++++++------------ code/modules/assembly/proximity.dm | 35 +++++++-------- code/modules/assembly/timer.dm | 5 +-- 4 files changed, 61 insertions(+), 101 deletions(-) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index e583f5664cec..a4cf742c410f 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -35,53 +35,49 @@ /// What the device does when turned on /obj/item/assembly/proc/activate() - return - -/// Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs -/obj/item/assembly/proc/pulsed(var/radio = 0) - return - -/// Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct -/obj/item/assembly/proc/pulse_device(var/radio = 0) - return + if(!secured || (cooldown > 0)) return 0 + cooldown = 2 + spawn(10) + process_cooldown() + return 1 /// Code that has to happen when the assembly is un\secured goes here /obj/item/assembly/proc/toggle_secure() - return + secured = !secured + update_icon() + return secured /// Called when an assembly is attacked by another -/obj/item/assembly/proc/attach_assembly(var/obj/A, var/mob/user) - return - -/// Called via spawn(10) to have it count down the cooldown var -/obj/item/assembly/proc/process_cooldown() - return +/obj/item/assembly/proc/attach_assembly(var/obj/item/A, var/mob/user) + holder = new/obj/item/assembly_holder(get_turf(src)) + if(holder.attach(A,src,user)) + to_chat(user, "You attach \the [A] to \the [src]!") + return 1 + return 0 /// Called when the holder is moved /obj/item/assembly/proc/holder_movement() return -/// Called when attack_self is called -/obj/item/assembly/interact(mob/user) - return - -/obj/item/assembly/process_cooldown() +/// Called via spawn(10) to have it count down the cooldown var +/// This is really bad. Please just make it process... +/obj/item/assembly/proc/process_cooldown() cooldown-- if(cooldown <= 0) return 0 spawn(10) process_cooldown() return 1 - -/obj/item/assembly/pulsed(var/radio = 0) +/// Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs +/obj/item/assembly/proc/pulsed(var/radio = 0) if(holder && (wires & WIRE_RECEIVE)) activate() if(radio && (wires & WIRE_RADIO_RECEIVE)) activate() return 1 - -/obj/item/assembly/pulse_device(var/radio = 0) +/// Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct +/obj/item/assembly/proc/pulse_device(var/radio = 0) if(holder && (wires & WIRE_PULSE)) holder.process_activation(src, 1, 0) if(holder && (wires & WIRE_PULSE_SPECIAL)) @@ -90,29 +86,6 @@ //Not sure what goes here quite yet send signal? return 1 - -/obj/item/assembly/activate() - if(!secured || (cooldown > 0)) return 0 - cooldown = 2 - spawn(10) - process_cooldown() - return 1 - - -/obj/item/assembly/toggle_secure() - secured = !secured - update_icon() - return secured - - -/obj/item/assembly/attach_assembly(var/obj/item/assembly/A, var/mob/user) - holder = new/obj/item/assembly_holder(get_turf(src)) - if(holder.attach(A,src,user)) - to_chat(user, "You attach \the [A] to \the [src]!") - return 1 - return 0 - - /obj/item/assembly/attackby(obj/item/component, mob/user) if(!user_can_attack_with(user) || !component.user_can_attack_with(user)) return TRUE @@ -152,6 +125,7 @@ interact(user) return TRUE +/// Called when attack_self is called /obj/item/assembly/interact(mob/user) return //HTML MENU FOR WIRES GOES HERE diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index c4ef0fcdbfa7..dba32a26eb25 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -36,20 +36,11 @@ special_assembly = null return ..() -/obj/item/assembly_holder/proc/attach(var/obj/item/left, var/obj/item/right, var/mob/user) - return - -/obj/item/assembly_holder/proc/process_activation(var/atom/activator) - return - -/obj/item/assembly_holder/proc/detached() - return - -/obj/item/assembly_holder/attach(var/obj/item/assembly/left, var/obj/item/assembly/right, var/mob/user) - if((!left)||(!right)) - return 0 - if((!istype(left))||(!istype(right))) +/obj/item/assembly_holder/proc/attach(var/obj/item/left_item, var/obj/item/right_item, var/mob/user) + if(!istype(left_item) || !istype(right_item)) return 0 + var/obj/item/assembly/left = left_item + var/obj/item/assembly/right = right_item if((left.secured)||(right.secured)) return 0 if(user) @@ -67,6 +58,24 @@ return 1 +/obj/item/assembly_holder/proc/process_activation(var/atom/activator, var/normal = 1, var/special = 1) + if(!activator) return 0 + if(!secured) + visible_message("[html_icon(src)] *beep* *beep*", "*beep* *beep*") + if((normal) && (a_right) && (a_left)) + if(a_right != activator) + a_right.pulsed(0) + if(a_left != activator) + a_left.pulsed(0) + if(master) + master.receive_signal() +// if(special && special_assembly) +// if(!special_assembly == activator) +// special_assembly.dothings() + return 1 + +/obj/item/assembly_holder/proc/detached() + return /obj/item/assembly_holder/HasProximity(atom/movable/AM) . = ..() @@ -154,23 +163,6 @@ qdel(src) return - -/obj/item/assembly_holder/process_activation(var/atom/activator, var/normal = 1, var/special = 1) - if(!activator) return 0 - if(!secured) - visible_message("[html_icon(src)] *beep* *beep*", "*beep* *beep*") - if((normal) && (a_right) && (a_left)) - if(a_right != activator) - a_right.pulsed(0) - if(a_left != activator) - a_left.pulsed(0) - if(master) - master.receive_signal() -// if(special && special_assembly) -// if(!special_assembly == activator) -// special_assembly.dothings() - return 1 - /obj/item/assembly_holder/hear_talk(mob/living/M, msg, verb, decl/language/speaking) if(a_right) a_right.hear_talk(M,msg,verb,speaking) diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index aee7624c0f7a..a451bcffec1e 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -20,7 +20,23 @@ var/range = 2 /obj/item/assembly/prox_sensor/proc/toggle_scan() + if(!secured) return 0 + scanning = !scanning + update_icon() + return + /obj/item/assembly/prox_sensor/proc/sense() + var/turf/mainloc = get_turf(src) +// if(scanning && cooldown <= 0) +// mainloc.visible_message("[html_icon(src)] *boop* *boop*", "*boop* *boop*") + if((!holder && !secured)||(!scanning)||(cooldown > 0)) return 0 + pulse_device(0) + if(!holder) + mainloc.visible_message("[html_icon(src)] *beep* *beep*", "*beep* *beep*") + cooldown = 2 + spawn(10) + process_cooldown() + return /obj/item/assembly/prox_sensor/activate() @@ -46,19 +62,6 @@ if(. && !istype(AM, /obj/effect/ir_beam) && AM.move_speed < 12) sense() -/obj/item/assembly/prox_sensor/sense() - var/turf/mainloc = get_turf(src) -// if(scanning && cooldown <= 0) -// mainloc.visible_message("[html_icon(src)] *boop* *boop*", "*boop* *boop*") - if((!holder && !secured)||(!scanning)||(cooldown > 0)) return 0 - pulse_device(0) - if(!holder) - mainloc.visible_message("[html_icon(src)] *beep* *beep*", "*beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - return - /obj/item/assembly/prox_sensor/Process() if(scanning) @@ -80,12 +83,6 @@ . = ..() addtimer(CALLBACK(src, PROC_REF(sense)), 0) -/obj/item/assembly/prox_sensor/toggle_scan() - if(!secured) return 0 - scanning = !scanning - update_icon() - return - /obj/item/assembly/prox_sensor/on_update_icon() . = ..() diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index aeb57facd843..bac9d7ec0796 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -16,9 +16,6 @@ var/timing = 0 var/time = 10 -/obj/item/assembly/timer/proc/timer_end() - - /obj/item/assembly/timer/activate() if(!..()) return 0//Cooldown check @@ -39,7 +36,7 @@ return secured -/obj/item/assembly/timer/timer_end() +/obj/item/assembly/timer/proc/timer_end() if(!secured) return 0 pulse_device(0) if(!holder) From 4f98b9ced282a8ecff11227e5538b8321d9f2bfa Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 26 Dec 2024 22:42:23 -0500 Subject: [PATCH 0119/1331] Make smokable extinguishing code more consistent --- code/modules/clothing/masks/smokable.dm | 45 +++++++++---------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/code/modules/clothing/masks/smokable.dm b/code/modules/clothing/masks/smokable.dm index 9d5c9cf1a9f6..3612df6f7183 100644 --- a/code/modules/clothing/masks/smokable.dm +++ b/code/modules/clothing/masks/smokable.dm @@ -158,6 +158,19 @@ STOP_PROCESSING(SSobj, src) set_light(0) update_icon() + remove_extension(src, /datum/extension/scent) + if (type_butt) + var/obj/item/trash/cigbutt/butt = new type_butt(get_turf(src)) + transfer_fingerprints_to(butt) + if(istype(butt) && butt.use_color) + butt.color = color + if(brand) + butt.desc += " This one is a [brand]." + if(ismob(loc)) + var/mob/living/M = loc + if (!no_message) + to_chat(M, SPAN_NOTICE("Your [name] goes out.")) + qdel(src) /obj/item/clothing/mask/smokable/attackby(var/obj/item/W, var/mob/user) if(W.isflamesource() || W.get_heat() >= T100C) @@ -221,22 +234,6 @@ if(is_processing) set_scent_by_reagents(src) -/obj/item/clothing/mask/smokable/extinguish_fire(mob/user, no_message = FALSE) - ..() - remove_extension(src, /datum/extension/scent) - if (type_butt) - var/obj/item/trash/cigbutt/butt = new type_butt(get_turf(src)) - transfer_fingerprints_to(butt) - if(istype(butt) && butt.use_color) - butt.color = color - if(brand) - butt.desc += " This one is a [brand]." - if(ismob(loc)) - var/mob/living/M = loc - if (!no_message) - to_chat(M, SPAN_NOTICE("Your [name] goes out.")) - qdel(src) - /obj/item/clothing/mask/smokable/cigarette/menthol name = "menthol cigarette" desc = "A cigarette with a little minty kick. Well, minty in theory." @@ -530,22 +527,10 @@ set_scent_by_reagents(src) update_icon() -/obj/item/clothing/mask/smokable/pipe/extinguish_fire(mob/user, no_message) - ..() - new /obj/effect/decal/cleanable/ash(get_turf(src)) - if(ismob(loc)) - var/mob/living/M = loc - if (!no_message) - to_chat(M, SPAN_NOTICE("Your [name] goes out, and you empty the ash.")) - remove_extension(src, /datum/extension/scent) - /obj/item/clothing/mask/smokable/pipe/attack_self(var/mob/user) - if(lit == 1) + if(lit) user.visible_message(SPAN_NOTICE("[user] puts out [src]."), SPAN_NOTICE("You put out [src].")) - lit = FALSE - update_icon() - STOP_PROCESSING(SSobj, src) - remove_extension(src, /datum/extension/scent) + extinguish_fire(user, no_message = TRUE) else if (smoketime) var/turf/location = get_turf(user) user.visible_message(SPAN_NOTICE("[user] empties out [src]."), SPAN_NOTICE("You empty out [src].")) From 3a5aaf766f43cf7128d9499799a5245f9cc881d8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 26 Dec 2024 22:43:51 -0500 Subject: [PATCH 0120/1331] Condense clothing OnDisguise overrides --- code/modules/clothing/_clothing_accessories.dm | 4 +++- code/modules/clothing/chameleon.dm | 11 ----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/code/modules/clothing/_clothing_accessories.dm b/code/modules/clothing/_clothing_accessories.dm index 81e9d41a80aa..0469f605d014 100644 --- a/code/modules/clothing/_clothing_accessories.dm +++ b/code/modules/clothing/_clothing_accessories.dm @@ -216,7 +216,8 @@ accessory_visibility = clothes.accessory_visibility accessory_slowdown = clothes.accessory_slowdown mimicking_state_modifiers = TRUE - clothing_state_modifiers = clothes.clothing_state_modifiers?.Copy() + clothing_state_modifiers = clothes.clothing_state_modifiers?.Copy() + bodytype_equip_flags = clothes.bodytype_equip_flags else accessory_hide_on_states = get_initial_accessory_hide_on_states() accessory_slot = initial(accessory_slot) @@ -225,5 +226,6 @@ accessory_slowdown = initial(accessory_slowdown) mimicking_state_modifiers = FALSE clothing_state_modifiers = null + bodytype_equip_flags = initial(bodytype_equip_flags) update_clothing_state_toggles() diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 5132c8e2bc9f..4a85cb47cef1 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -22,17 +22,6 @@ flags_inv = copy.flags_inv set_gender(copy.gender) -/obj/item/clothing/OnDisguise(obj/item/copy, mob/user) - . = ..() - if(. && istype(copy, /obj/item/clothing)) - var/obj/item/clothing/clothing_copy = copy - bodytype_equip_flags = clothing_copy.bodytype_equip_flags - accessory_slot = clothing_copy.accessory_slot - accessory_removable = clothing_copy.accessory_removable - accessory_visibility = clothing_copy.accessory_visibility - accessory_slowdown = clothing_copy.accessory_slowdown - accessory_hide_on_states = clothing_copy.accessory_hide_on_states?.Copy() - /proc/generate_chameleon_choices(var/basetype) . = list() From 6e9683efb6dd3cfe74fdd4a0f86ce63fde1225eb Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 26 Dec 2024 22:44:18 -0500 Subject: [PATCH 0121/1331] Fix non-chest lung organ subtypes not showing ruptures on scan --- code/modules/organs/external/diagnostics.dm | 3 +++ code/modules/organs/external/standard.dm | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/code/modules/organs/external/diagnostics.dm b/code/modules/organs/external/diagnostics.dm index 2bc96bfa3fc2..973c506b9fcd 100644 --- a/code/modules/organs/external/diagnostics.dm +++ b/code/modules/organs/external/diagnostics.dm @@ -110,6 +110,9 @@ for(var/obj/item/organ/internal/augment/aug in internal_organs) if(istype(aug) && aug.known) . += "[capitalize(aug.name)] implanted" + var/obj/item/organ/internal/lungs/L = locate() in src + if( L && L.is_bruised()) + . += "Lung ruptured" /obj/item/organ/external/proc/inspect(mob/user) diff --git a/code/modules/organs/external/standard.dm b/code/modules/organs/external/standard.dm index d3ec8d68e7f4..2beef64083b1 100644 --- a/code/modules/organs/external/standard.dm +++ b/code/modules/organs/external/standard.dm @@ -23,12 +23,6 @@ /obj/item/organ/external/chest/proc/get_current_skin() return -/obj/item/organ/external/get_scan_results() - . = ..() - var/obj/item/organ/internal/lungs/L = locate() in src - if( L && L.is_bruised()) - . += "Lung ruptured" - /obj/item/organ/external/chest/die() //Special handling for synthetics if(BP_IS_PROSTHETIC(src) || BP_IS_CRYSTAL(src)) From 4bdf373dfd479c00a828933ff5e510bb1d14ff2e Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Fri, 27 Dec 2024 11:39:14 +1100 Subject: [PATCH 0122/1331] Adding lantern fish and cave fish. --- .../game/objects/structures/barrels/barrel.dm | 2 +- code/game/objects/structures/barrels/cask.dm | 2 +- code/modules/butchery/butchery_data_fish.dm | 3 ++ .../butchery/butchery_products_meat_fish.dm | 11 ++++++++ .../hydroponics/plant_types/seeds_misc.dm | 12 ++++---- code/modules/hydroponics/seed_packets.dm | 2 +- .../simple_animal/aquatic/aquatic_fish.dm | 14 ++++++++++ code/modules/organs/internal/_internal.dm | 2 +- code/modules/organs/internal/brain.dm | 4 +-- .../modules/reagents/chems/chems_nutriment.dm | 13 --------- code/modules/reagents/chems/chems_oil.dm | 28 +++++++++++++++++++ .../reactions/reaction_recipe_food.dm | 2 +- .../reagents/reactions/reaction_synthesis.dm | 2 +- maps/shaded_hills/areas/grassland.dm | 10 +++++++ maps/shaded_hills/shaded_hills_map.dm | 2 +- nebula.dme | 1 + 16 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 code/modules/reagents/chems/chems_oil.dm diff --git a/code/game/objects/structures/barrels/barrel.dm b/code/game/objects/structures/barrels/barrel.dm index 2f92b82e1636..4b7f0d2e87c4 100644 --- a/code/game/objects/structures/barrels/barrel.dm +++ b/code/game/objects/structures/barrels/barrel.dm @@ -110,4 +110,4 @@ /obj/structure/reagent_dispensers/barrel/ebony/oil/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/nutriment/plant_oil, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/nutriment/oil/plant, reagents.maximum_volume) diff --git a/code/game/objects/structures/barrels/cask.dm b/code/game/objects/structures/barrels/cask.dm index 20acd0490234..8a704ad55cf7 100644 --- a/code/game/objects/structures/barrels/cask.dm +++ b/code/game/objects/structures/barrels/cask.dm @@ -39,4 +39,4 @@ /obj/structure/reagent_dispensers/barrel/cask/ebony/oil/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/nutriment/plant_oil, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/nutriment/oil/plant, reagents.maximum_volume) diff --git a/code/modules/butchery/butchery_data_fish.dm b/code/modules/butchery/butchery_data_fish.dm index 1facebe1a93a..f7f37c31878f 100644 --- a/code/modules/butchery/butchery_data_fish.dm +++ b/code/modules/butchery/butchery_data_fish.dm @@ -11,6 +11,9 @@ gut_type = /obj/item/food/butchery/offal/small meat_flags = ALLERGEN_FISH +/decl/butchery_data/animal/fish/oily + meat_type = /obj/item/food/butchery/meat/fish/oily + /decl/butchery_data/animal/fish/small bone_amount = 1 skin_amount = 2 diff --git a/code/modules/butchery/butchery_products_meat_fish.dm b/code/modules/butchery/butchery_products_meat_fish.dm index f1e06223d09a..8361c4af74cf 100644 --- a/code/modules/butchery/butchery_products_meat_fish.dm +++ b/code/modules/butchery/butchery_products_meat_fish.dm @@ -16,6 +16,17 @@ slice_num = 3 butchery_data = /decl/butchery_data/animal/fish allergen_flags = ALLERGEN_FISH + var/oil_type = /decl/material/liquid/nutriment/oil/fish + var/oil_amount = 2 + +/obj/item/food/butchery/meat/fish/oily + nutriment_amt = 4 + oil_amount = 4 + +/obj/item/food/butchery/meat/fish/populate_reagents() + . = ..() + if(oil_type && oil_amount > 0) + add_to_reagents(oil_type, oil_amount) /obj/item/food/butchery/meat/fish/get_meat_icons() var/static/list/meat_icons = list( diff --git a/code/modules/hydroponics/plant_types/seeds_misc.dm b/code/modules/hydroponics/plant_types/seeds_misc.dm index b44099d34122..db5d4739d6d9 100644 --- a/code/modules/hydroponics/plant_types/seeds_misc.dm +++ b/code/modules/hydroponics/plant_types/seeds_misc.dm @@ -4,7 +4,7 @@ display_name = "cotton patch" product_material = /decl/material/solid/organic/plantmatter/pith/husk chems = list( - /decl/material/liquid/nutriment/plant_oil = list(3,10), + /decl/material/liquid/nutriment/oil/plant = list(3,10), /decl/material/solid/organic/cloth = list(10,1) ) slice_product = null @@ -28,7 +28,7 @@ display_name = "flax patch" // Do we want linseed oil at some point? chems = list( - /decl/material/liquid/nutriment/plant_oil = list(5,12), + /decl/material/liquid/nutriment/oil/plant = list(5,12), /decl/material/solid/organic/cloth/linen = list(8,1) ) @@ -400,7 +400,7 @@ display_name = "hemp patch" mutants = null chems = list( - /decl/material/liquid/nutriment/plant_oil = list(3,10), + /decl/material/liquid/nutriment/oil/plant = list(3,10), /decl/material/solid/organic/cloth/hemp = list(8,1), /decl/material/liquid/nutriment = list(1) ) @@ -725,7 +725,7 @@ product_name = "sunflower" display_name = "sunflower patch" chems = list( - /decl/material/liquid/nutriment/plant_oil = list(10,10) + /decl/material/liquid/nutriment/oil/plant = list(10,10) ) /datum/seed/flower/sunflower/New() @@ -800,7 +800,7 @@ display_name = "peanut vine" chems = list( /decl/material/liquid/nutriment = list(1,10), - /decl/material/liquid/nutriment/plant_oil = list(1,10) + /decl/material/liquid/nutriment/oil/plant = list(1,10) ) slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -987,7 +987,7 @@ display_name = "soybean patch" chems = list( /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/nutriment/plant_oil = list(3,20), + /decl/material/liquid/nutriment/oil/plant = list(3,20), /decl/material/liquid/drink/milk/soymilk = list(7,20) ) grown_tag = "soybeans" diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index aca1014088d5..d9033c86f84a 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -26,7 +26,7 @@ /obj/item/seeds/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/nutriment/plant_oil, 3) + add_to_reagents(/decl/material/liquid/nutriment/oil/plant, 3) /obj/item/seeds/get_single_monetary_worth() . = seed ? seed.get_monetary_value() : ..() diff --git a/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm b/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm index 7b8f5b5f1782..7de69b839449 100644 --- a/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm +++ b/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm @@ -16,6 +16,20 @@ name = "small fish" icon = 'icons/mob/simple_animal/fish_grump.dmi' +/mob/living/simple_animal/aquatic/fish/lantern + name = "lantern-fish" + desc = "An oily, glowing fish sometimes caught in cave rivers, rumoured to have cousins in the deep ocean." + // TODO: icon = 'icons/mob/simple_animal/fish_lantern.dmi' + butchery_data = /decl/butchery_data/animal/fish/oily + +/mob/living/simple_animal/aquatic/fish/cave + name = "blind cave fish" + desc = "A pale, blobby fish that lives its entire life in the cold darkness of cave rivers, and hence has no need for eyes." + // TODO: icon = 'icons/mob/simple_animal/fish_cave.dmi' + +/mob/living/simple_animal/aquatic/fish/cave/is_blind() + return TRUE + /mob/living/simple_animal/aquatic/fish/large name = "smallmouth bass" icon = 'icons/mob/simple_animal/fish_judge.dmi' diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index ac00fcf30e58..e8e076a057c6 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -171,7 +171,7 @@ // We clamp/round here so that we don't accidentally heal past the threshold and // cheat our way into a full second threshold of healing. - damage = clamp(damage-get_organ_heal_amount(), min_heal_val, absolute_max_damage) + damage = clamp(damage - max(0, get_organ_heal_amount() * GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN)), min_heal_val, absolute_max_damage) // If we're within 1 damage of the nearest threshold (such as 0), round us down. // This should be removed when float-aware modulo comes in in 515, but for now is needed diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 32a4b6e1c2de..a26e424aba86 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -99,10 +99,10 @@ alert(owner, "You have taken massive brain damage! You will not be able to remember the events leading up to your injury.", "Brain Damaged") /obj/item/organ/internal/brain/organ_can_heal() - return (damage && owner && GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN)) || ..() + return (damage && owner && GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN) > 0) || ..() /obj/item/organ/internal/brain/has_limited_healing() - return (!owner || !GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN)) && ..() + return (!owner || GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN) <= 0) && ..() /obj/item/organ/internal/brain/get_organ_heal_amount() return 1 diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index 3ac879172e79..3b5567d3f362 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -62,19 +62,6 @@ uid = "chem_nutriment_plant" allergen_flags = ALLERGEN_VEGETABLE -/decl/material/liquid/nutriment/plant_oil - name = "plant oil" - lore_text = "A thin yellow oil pressed from vegetables or nuts. Useful as fuel, or in cooking." - uid = "chem_nutriment_plant_oil" - melting_point = 273 - boiling_point = 373 - taste_description = "oily blandness" - burn_product = /decl/material/gas/carbon_monoxide - ignition_point = T0C+150 - accelerant_value = FUEL_VALUE_ACCELERANT - gas_flags = XGM_GAS_FUEL - allergen_flags = ALLERGEN_VEGETABLE - /decl/material/liquid/nutriment/honey name = "honey" lore_text = "A golden yellow syrup, loaded with sugary sweetness." diff --git a/code/modules/reagents/chems/chems_oil.dm b/code/modules/reagents/chems/chems_oil.dm new file mode 100644 index 000000000000..5395daacc11c --- /dev/null +++ b/code/modules/reagents/chems/chems_oil.dm @@ -0,0 +1,28 @@ +/decl/material/liquid/nutriment/oil + abstract_type = /decl/material/liquid/nutriment/oil + burn_product = /decl/material/gas/carbon_monoxide + ignition_point = T0C+150 + accelerant_value = FUEL_VALUE_ACCELERANT + gas_flags = XGM_GAS_FUEL + melting_point = 273 + boiling_point = 373 + +/decl/material/liquid/nutriment/oil/plant + name = "plant oil" + lore_text = "A thin yellow oil pressed from vegetables or nuts. Useful as fuel, or in cooking." + uid = "chem_nutriment_plant_oil" + taste_description = "oily blandness" + allergen_flags = ALLERGEN_VEGETABLE + +/decl/material/liquid/nutriment/oil/fish + name = "fish oil" + lore_text = "A pungent yellow oil pressed from fish meat and fish skin. Useful as fuel, or in cooking, or for encouraging recovery after brain injuries." + uid = "chem_nutriment_fish_oil" + taste_description = "pungent, oily fish" + allergen_flags = ALLERGEN_FISH + +// Copied from neuroannealer; yes, it's silly, but we need a way to treat brain damage on the medieval map. +// Should possibly be an ingredient rather than the be-all end-all medication. +/decl/material/liquid/nutriment/oil/fish/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) + . = ..() + M.add_chemical_effect(CE_BRAIN_REGEN, 0.5) // Half as effective as neuroannealer, without the side-effects. diff --git a/code/modules/reagents/reactions/reaction_recipe_food.dm b/code/modules/reagents/reactions/reaction_recipe_food.dm index 0900c6d5c2e6..ef99755711d6 100644 --- a/code/modules/reagents/reactions/reaction_recipe_food.dm +++ b/code/modules/reagents/reactions/reaction_recipe_food.dm @@ -58,7 +58,7 @@ name = "Enzyme Margarine" required_reagents = list( /decl/material/solid/sodiumchloride = 1, - /decl/material/liquid/nutriment/plant_oil = 20 + /decl/material/liquid/nutriment/oil/plant = 20 ) catalysts = list(/decl/material/liquid/enzyme = 5) mix_message = "The solution thickens and curdles into a pale yellow solid." diff --git a/code/modules/reagents/reactions/reaction_synthesis.dm b/code/modules/reagents/reactions/reaction_synthesis.dm index c70444faff81..d12783c8f7ad 100644 --- a/code/modules/reagents/reactions/reaction_synthesis.dm +++ b/code/modules/reagents/reactions/reaction_synthesis.dm @@ -130,7 +130,7 @@ required_reagents = list( /decl/material/solid/carbon/ashes = 5, /decl/material/liquid/water = 5, - /decl/material/liquid/nutriment/plant_oil = 10 + /decl/material/liquid/nutriment/oil/plant = 10 ) result_amount = 1 mix_message = "The solution thickens and solidifies." diff --git a/maps/shaded_hills/areas/grassland.dm b/maps/shaded_hills/areas/grassland.dm index 46c253326445..3fa264b43926 100644 --- a/maps/shaded_hills/areas/grassland.dm +++ b/maps/shaded_hills/areas/grassland.dm @@ -53,6 +53,16 @@ area_blurb_category = /area/shaded_hills/caves sound_env = CAVE area_flags = AREA_FLAG_IS_BACKGROUND + fishing_results = list( + /mob/living/simple_animal/aquatic/fish/cave = 13 + /mob/living/simple_animal/aquatic/fish/lantern = 7 + /obj/item/mollusc = 5, + /obj/item/mollusc/barnacle/fished = 5, + /obj/item/mollusc/clam/fished/pearl = 3, + /obj/item/trash/mollusc_shell/clam = 1, + /obj/item/trash/mollusc_shell/barnacle = 1, + /obj/item/trash/mollusc_shell = 1 + ) /area/shaded_hills/caves/deep name = "\improper Deep Caverns" diff --git a/maps/shaded_hills/shaded_hills_map.dm b/maps/shaded_hills/shaded_hills_map.dm index 5e3cda3b8bc6..0b545c07a61c 100644 --- a/maps/shaded_hills/shaded_hills_map.dm +++ b/maps/shaded_hills/shaded_hills_map.dm @@ -1,5 +1,5 @@ /datum/map/shaded_hills - default_liquid_fuel_type = /decl/material/liquid/nutriment/plant_oil + default_liquid_fuel_type = /decl/material/liquid/nutriment/oil/plant default_species = SPECIES_KOBALOI loadout_categories = list( /decl/loadout_category/fantasy/clothing, diff --git a/nebula.dme b/nebula.dme index c08cae0b5a94..dcafae477c0b 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3605,6 +3605,7 @@ #include "code\modules\reagents\chems\chems_herbal.dm" #include "code\modules\reagents\chems\chems_medicines.dm" #include "code\modules\reagents\chems\chems_nutriment.dm" +#include "code\modules\reagents\chems\chems_oil.dm" #include "code\modules\reagents\chems\chems_painkillers.dm" #include "code\modules\reagents\chems\chems_pigments.dm" #include "code\modules\reagents\chems\chems_poisons.dm" From f0923877b5f2cd99277e5d0b6c2237a555693e2c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 27 Dec 2024 18:17:30 -0500 Subject: [PATCH 0123/1331] Remove mind.original --- code/datums/communication/dsay.dm | 6 +----- code/datums/mind/mind.dm | 8 -------- code/game/antagonist/antagonist_update.dm | 1 - code/game/gamemodes/game_mode.dm | 2 +- code/game/machinery/computer/message.dm | 4 ++-- code/game/machinery/computer/robot.dm | 8 ++++---- code/game/objects/items/robot/robot_frame.dm | 1 + code/modules/mob/living/silicon/robot/laws.dm | 7 +++++-- code/modules/mob/mob_helpers.dm | 2 -- code/modules/mob/new_player/new_player.dm | 1 - code/modules/mob/transform_procs.dm | 2 -- mods/gamemodes/traitor/overrides.dm | 10 ++-------- mods/species/vox/datum/heist_compatibility.dm | 2 +- 13 files changed, 17 insertions(+), 37 deletions(-) diff --git a/code/datums/communication/dsay.dm b/code/datums/communication/dsay.dm index c21705a672eb..273f17a36b74 100644 --- a/code/datums/communication/dsay.dm +++ b/code/datums/communication/dsay.dm @@ -61,12 +61,8 @@ keyname = C.key if(C.mob) //Most of the time this is the dead/observer mob; we can totally use him if there is no better name - var/mindname + var/mindname = C.mob.mind?.name // the mind's "original name" var/realname = C.mob.real_name - if(C.mob.mind) - mindname = C.mob.mind.name - if(C.mob.mind.original && C.mob.mind.original.real_name) - realname = C.mob.mind.original.real_name if(mindname && mindname != realname) name = "[realname] died as [mindname]" else diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 34bad84cb65d..cb9ad6c9539e 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -33,7 +33,6 @@ var/key var/name //replaces mob/var/original_name var/mob/living/current - var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. var/active = 0 var/gen_relations_info @@ -71,18 +70,12 @@ if(current?.mind == src) current.mind = null current = null - if(original?.mind == src) - original.mind = null - original = null . = ..() /datum/mind/proc/handle_mob_deletion(mob/living/deleted_mob) if (current == deleted_mob) current = null - if (original == deleted_mob) - original = null - /datum/mind/proc/transfer_to(mob/living/new_character) if(!istype(new_character)) to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") @@ -505,7 +498,6 @@ mind.key = key else mind = new /datum/mind(key) - mind.original = src SSticker.minds += mind if(!mind.name) mind.name = real_name mind.current = src diff --git a/code/game/antagonist/antagonist_update.dm b/code/game/antagonist/antagonist_update.dm index be03efcadfd8..642141255657 100644 --- a/code/game/antagonist/antagonist_update.dm +++ b/code/game/antagonist/antagonist_update.dm @@ -13,7 +13,6 @@ player.current = new mob_path(get_turf(player.current)) player.transfer_to(player.current) if(holder) qdel(holder) - player.original = player.current if(!preserve_appearance && (flags & ANTAG_SET_APPEARANCE)) spawn(3) var/mob/living/human/H = player.current diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 9bfdd9791429..be38f8871efc 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -550,7 +550,7 @@ var/global/list/additional_antag_types = list() continue //Happy connected client for(var/mob/observer/ghost/D in SSmobs.mob_list) - if(D.mind && (D.mind.original == L || D.mind.current == L)) + if(D.mind && D.mind.current == L) if(L.stat == DEAD) msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" continue //Dead mob, ghost abandoned diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index 16503b09fe95..0c145faa9de9 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -120,7 +120,7 @@ dat += "
      [++i]. Set Custom Key
      " else dat += "

      Please authenticate with the server in order to show additional options." - if((isAI(user) || isrobot(user)) && (user.mind.assigned_special_role && user.mind.original == user)) + if((isAI(user) || isrobot(user)) && player_is_antag(user.mind)) //Malf/Traitor AIs can bruteforce into the system to gain the Key. dat += "
      *&@#. Bruteforce Key
      " @@ -283,7 +283,7 @@ //Hack the Console to get the password if (href_list["hack"]) - if((isAI(usr) || isrobot(usr)) && usr.mind.assigned_special_role && usr.mind.original == usr) + if((isAI(usr) || isrobot(usr)) && player_is_antag(usr.mind)) src.hacking = 1 src.screen = 2 update_icon() diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 524f6a358899..4b1d38034e52 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -68,7 +68,7 @@ return TOPIC_HANDLED // Antag AI checks - if(!isAI(user) || !(user.mind.assigned_special_role && user.mind.original == user)) + if(!isAI(user) || !player_is_antag(user.mind)) to_chat(user, "Access Denied") return TOPIC_HANDLED @@ -103,9 +103,9 @@ . = TOPIC_REFRESH // Proc: get_cyborgs() -// Parameters: 1 (operator - mob which is operating the console.) +// Parameters: 1 (user - mob which is operating the console.) // Description: Returns NanoUI-friendly list of accessible cyborgs. -/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) +/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/user) var/list/robots = list() for(var/mob/living/silicon/robot/R in global.silicon_mob_list) @@ -145,7 +145,7 @@ robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" robot["hackable"] = 0 // Antag AIs know whether linked cyborgs are hacked or not. - if(operator && isAI(operator) && (R.connected_ai == operator) && (operator.mind.assigned_special_role && operator.mind.original == operator)) + if(user && isAI(user) && (R.connected_ai == user) && player_is_antag(user.mind)) robot["hacked"] = R.emagged ? 1 : 0 robot["hackable"] = R.emagged? 0 : 1 robots.Add(list(robot)) diff --git a/code/game/objects/items/robot/robot_frame.dm b/code/game/objects/items/robot/robot_frame.dm index bd8f71787b83..24f7474aa6e1 100644 --- a/code/game/objects/items/robot/robot_frame.dm +++ b/code/game/objects/items/robot/robot_frame.dm @@ -111,6 +111,7 @@ O.custom_name = created_name O.updatename("Default") + clear_antag_roles(brainmob.mind, implanted = TRUE) // some antag roles persist brainmob.mind.transfer_to(O) if(O.mind && O.mind.assigned_role) O.job = O.mind.assigned_role diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index 22ecbf50f91f..991631baa850 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -42,8 +42,11 @@ var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null if (master) master.sync(src) - ..() - return + . = ..() + // if we aren't malfunctioning and we have a law 0, it's presumably shared + // if we are malfunctioning and we don't have a law 0, we don't need to worry about this + if(connected_ai && is_malfunctioning() && has_zeroth_law()) + to_chat(src, SPAN_BOLD("Remember, your AI does NOT share or know about your law 0.")) /mob/living/silicon/robot/proc/robot_checklaws() set category = "Silicon Commands" diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 6751c0ad7f2e..67e363adcb0c 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -294,8 +294,6 @@ var/global/list/global/organ_rel_size = list( var/datum/mind/M = O if(M.current && M.current.client) C = M.current.client - else if(M.original && M.original.client) - C = M.original.client if(C) if(C.get_preference_value(/datum/client_preference/anon_say) == PREF_YES) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index caa16d854fb7..d81eaccd530a 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -392,7 +392,6 @@ INITIALIZE_IMMEDIATE(/mob/new_player) if(mind) mind.active = 0 //we wish to transfer the key manually - mind.original = new_character var/memory = client.prefs.records[PREF_MEM_RECORD] if(memory) mind.StoreMemory(memory) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index e0c1d4468761..866c96a62c6e 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -70,7 +70,6 @@ O.aiRestorePowerRoutine = 0 if(mind) mind.transfer_to(O) - O.mind.original = O else O.key = key @@ -128,7 +127,6 @@ mind.active = TRUE mind.transfer_to(O) if(O.mind && O.mind.assigned_role == ASSIGNMENT_ROBOT) - O.mind.original = O var/mmi_type = SSrobots.get_brain_type_by_title(O.mind.role_alt_title ? O.mind.role_alt_title : O.mind.assigned_role) if(mmi_type) O.central_processor = new mmi_type(O) diff --git a/mods/gamemodes/traitor/overrides.dm b/mods/gamemodes/traitor/overrides.dm index 122c6cf5daa3..0f8142a16352 100644 --- a/mods/gamemodes/traitor/overrides.dm +++ b/mods/gamemodes/traitor/overrides.dm @@ -3,18 +3,12 @@ return mind && traitors.is_antagonist(mind) /mob/living/silicon/robot/show_master(mob/who) - // TODO: Update to new antagonist system. - if (mind?.assigned_special_role == /decl/special_role/traitor && mind.original == src && connected_ai) + var/decl/special_role/traitor/traitor_role = IMPLIED_DECL + if(traitor_role.is_antagonist(mind) && connected_ai) to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") return return ..() -/mob/living/silicon/robot/lawsync() - . = ..() - // TODO: Update to new antagonist system. - if(mind?.assigned_special_role == /decl/special_role/traitor && mind.original == src) - to_chat(src, SPAN_BOLD("Remember, your AI does NOT share or know about your law 0.")) - /mob/living/silicon/robot/handle_regular_hud_updates() . = ..() if(!.) diff --git a/mods/species/vox/datum/heist_compatibility.dm b/mods/species/vox/datum/heist_compatibility.dm index a34c7d79244a..776057948c06 100644 --- a/mods/species/vox/datum/heist_compatibility.dm +++ b/mods/species/vox/datum/heist_compatibility.dm @@ -16,7 +16,7 @@ var/decl/species/my_species = user?.get_species() var/decl/special_role/raider/raiders = GET_DECL(/decl/special_role/raider) - if(!istype(user) || !user.mind || !user.mind.assigned_special_role != raiders || !my_species || my_species.name == SPECIES_VOX || !is_alien_whitelisted(user, SPECIES_VOX)) + if(!istype(user) || !user.mind || !raiders.is_antagonist(user.mind) || !my_species || my_species.name == SPECIES_VOX || !is_alien_whitelisted(user, SPECIES_VOX)) return ..() var/choice = input("Do you wish to become a vox of the Shoal? This is not reversible.") as null|anything in list("No","Yes") From 7c0d3eaadc3fe1408f3a254eb6fec2de3c12daa9 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 16 Feb 2023 09:39:22 +1100 Subject: [PATCH 0124/1331] Compiled tweaks from old codex dump branch. --- code/game/objects/items/weapons/policetape.dm | 11 ++++++----- code/modules/mob/new_player/new_player.dm | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/code/game/objects/items/weapons/policetape.dm b/code/game/objects/items/weapons/policetape.dm index 7800b0131cd6..81ae83daba05 100644 --- a/code/game/objects/items/weapons/policetape.dm +++ b/code/game/objects/items/weapons/policetape.dm @@ -250,7 +250,7 @@ var/global/list/image/hazard_overlays //Cached hazard floor overlays for the bar /obj/structure/tape_barricade/proc/update_neighbors(var/location = loc) for (var/look_dir in global.cardinal) var/obj/structure/tape_barricade/B = locate(/obj/structure/tape_barricade, get_step(location, look_dir)) - if(B && !QDELETED(B)) + if(!QDELETED(B)) B.update_icon() if(!QDELETED(src)) @@ -261,10 +261,11 @@ var/global/list/image/hazard_overlays //Cached hazard floor overlays for the bar neighbors = 0 for (var/look_dir in global.cardinal) var/turf/target_turf = get_step(src, look_dir) - var/obj/structure/tape_barricade/B = locate(/obj/structure/tape_barricade, target_turf) - //We connect to walls and other tape_barricades - if((B && !QDELETED(B)) || (!B && target_turf?.is_wall())) - neighbors |= look_dir + if(target_turf) + var/obj/structure/tape_barricade/B = locate(/obj/structure/tape_barricade) in target_turf + //We connect to walls and other tape_barricades + if((B && !QDELETED(B)) || (!B && target_turf.is_wall())) + neighbors |= look_dir /**Allow sutypes to override with their own forced icon state name.*/ /obj/structure/tape_barricade/proc/icon_name_override() diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index caa16d854fb7..c0e255949e21 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -2,6 +2,7 @@ universal_speak = TRUE mob_sort_value = 10 invisibility = INVISIBILITY_ABSTRACT + is_spawnable_type = FALSE simulated = FALSE density = FALSE stat = DEAD From d6aef39e573ebf74b9dcec9ce4075ec37b999137 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 12 Dec 2024 18:36:07 +1100 Subject: [PATCH 0125/1331] Cleaning up equip_adjust list access/definition. --- code/game/objects/item_mob_overlay.dm | 2 +- code/modules/bodytype/bodytype_offsets.dm | 14 +-- code/modules/mob/living/human/update_icons.dm | 38 ++++---- .../mob/living/silicon/robot/drone/drone.dm | 12 +-- .../mob/living/simple_animal/friendly/cat.dm | 16 ++-- .../living/simple_animal/friendly/corgi.dm | 16 ++-- .../mob/living/simple_animal/friendly/crab.dm | 8 +- .../mob/living/simple_animal/passive/deer.dm | 8 +- .../mob/living/simple_animal/passive/fox.dm | 8 +- .../species/station/monkey_bodytypes.dm | 12 +-- mods/content/fantasy/datum/hnoll/bodytypes.dm | 10 +-- .../fantasy/datum/kobaloi/bodytypes.dm | 32 +++---- mods/mobs/dionaea/mob/_nymph.dm | 8 +- .../species/ascent/datum/species_bodytypes.dm | 4 +- .../adherent/datum/species_bodytypes.dm | 26 +++--- .../tajaran/datum/species_bodytypes.dm | 23 ++++- mods/species/drakes/species_bodytypes.dm | 70 +++++++-------- .../neoavians/datum/species_bodytypes.dm | 86 ++++++++++++++++--- .../serpentid/datum/species_bodytypes.dm | 58 +++++++++++-- .../utility_frames/species_bodytypes.dm | 8 +- mods/species/vox/datum/species_bodytypes.dm | 22 ++--- 21 files changed, 293 insertions(+), 188 deletions(-) diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm index 5827dbba4786..be29ad618ca9 100644 --- a/code/game/objects/item_mob_overlay.dm +++ b/code/game/objects/item_mob_overlay.dm @@ -70,7 +70,7 @@ var/global/list/icon_state_cache = list() var/mob_icon = global.default_onmob_icons[slot] var/decl/bodytype/root_bodytype = user_mob?.get_bodytype() if(istype(root_bodytype)) - var/use_slot = (bodypart in root_bodytype.equip_adjust) ? bodypart : slot + var/use_slot = (bodypart in root_bodytype.get_equip_adjustments(user_mob)) ? bodypart : slot return root_bodytype.get_offset_overlay_image(user_mob, mob_icon, mob_state, color, use_slot) return overlay_image(mob_icon, mob_state, color, RESET_COLOR) diff --git a/code/modules/bodytype/bodytype_offsets.dm b/code/modules/bodytype/bodytype_offsets.dm index e00901891127..279b43e99183 100644 --- a/code/modules/bodytype/bodytype_offsets.dm +++ b/code/modules/bodytype/bodytype_offsets.dm @@ -4,8 +4,8 @@ each one can be in the NORTH, SOUTH, EAST, and WEST direction. Specify the x and y amounts to shift the thing for a given direction. example: - equip_adjust = list( - slot_back_str = list("[NORTH]" = list(-12, 7), "[EAST]" = list(-2, -12)) + _equip_adjust = list( + (slot_back_str) = list("[NORTH]" = list(-12, 7), "[EAST]" = list(-2, -12)) ) This would shift back items (backpacks, axes, etc.) when the mob @@ -18,15 +18,15 @@ The slots that you can use are found in items_clothing.dm and are the inventory */ /decl/bodytype - var/list/equip_adjust - var/list/equip_overlays = list() + VAR_PRIVATE/list/_equip_adjust + VAR_PRIVATE/list/equip_overlays = list() -/decl/bodytype/proc/get_equip_adjust(mob/mob) - return equip_adjust +/decl/bodytype/proc/get_equip_adjustments(mob/mob) + return _equip_adjust /decl/bodytype/proc/get_offset_overlay_image(mob/mob, mob_icon, mob_state, color, slot) // If we don't actually need to offset this, don't bother with any of the generation/caching. - var/list/use_equip_adjust = get_equip_adjust(mob) + var/list/use_equip_adjust = get_equip_adjustments(mob) if(length(use_equip_adjust) && use_equip_adjust[slot] && length(use_equip_adjust[slot])) // Check the cache for previously made icons. diff --git a/code/modules/mob/living/human/update_icons.dm b/code/modules/mob/living/human/update_icons.dm index 34fcb8ccc310..eafc3556942a 100644 --- a/code/modules/mob/living/human/update_icons.dm +++ b/code/modules/mob/living/human/update_icons.dm @@ -320,27 +320,23 @@ Please contact me on #coderbus IRC. ~Carn x //UNDERWEAR OVERLAY /mob/living/human/proc/update_underwear(var/update_icons=1) - var/list/undies = list() - for(var/entry in worn_underwear) - - var/obj/item/underwear/UW = entry - if (!UW?.icon) // Avoid runtimes for nude underwear types - continue - - var/decl/bodytype/root_bodytype = get_bodytype() - if(!root_bodytype) - continue // Avoid runtimes for dummy mobs with no bodytype set - - var/image/I - if(UW.slot_offset_str && LAZYACCESS(root_bodytype.equip_adjust, UW.slot_offset_str)) - I = root_bodytype.get_offset_overlay_image(src, UW.icon, UW.icon_state, UW.color, UW.slot_offset_str) - else - I = image(icon = UW.icon, icon_state = UW.icon_state) - I.color = UW.color - if(I) // get_offset_overlay_image() may potentially return null - I.appearance_flags |= RESET_COLOR - undies += I - set_current_mob_overlay(HO_UNDERWEAR_LAYER, undies, update_icons) + var/list/undies_overlays = list() + var/decl/bodytype/root_bodytype = get_bodytype() + if(root_bodytype) + var/list/adjustments = root_bodytype.get_equip_adjustments(src) + for(var/obj/item/underwear/undies as anything in worn_underwear) + if (!undies?.icon) // Avoid runtimes for nude underwear types + continue + var/image/undies_overlay + if(undies.slot_offset_str && (undies.slot_offset_str in adjustments)) + undies_overlay = root_bodytype.get_offset_overlay_image(src, undies.icon, undies.icon_state, undies.color, undies.slot_offset_str) + else + undies_overlay = image(icon = undies.icon, icon_state = undies.icon_state) + undies_overlay.color = undies.color + if(undies_overlay) // get_offset_overlay_image() may potentially return null + undies_overlay.appearance_flags |= RESET_COLOR + undies_overlays += undies_overlay + set_current_mob_overlay(HO_UNDERWEAR_LAYER, undies_overlays, update_icons) /mob/living/human/update_hair(var/update_icons=1) var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD, /obj/item/organ/external/head) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 0563fce55217..90516b063816 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -117,7 +117,7 @@ uid = "bodytype_drone_construction" /decl/bodytype/drone/construction/Initialize() - equip_adjust = list( + _equip_adjust = list( slot_head_str = list( "[NORTH]" = list(1, -12), "[SOUTH]" = list(1, -12), @@ -355,13 +355,13 @@ uid = "bodytype_drone" /decl/bodytype/drone/Initialize() - if(!length(equip_adjust)) - equip_adjust = list( - slot_head_str = list( + if(!length(_equip_adjust)) + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list(0, -13), "[SOUTH]" = list(0, -13), - "[EAST]" = list(0, -13), - "[WEST]" = list(0, -13) + "[EAST]" = list(0, -13), + "[WEST]" = list(0, -13) ) ) . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index f4b20f9b75b5..a9b8e6891b80 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -70,12 +70,12 @@ uid = "bodytype_animal_cat" /decl/bodytype/quadruped/animal/cat/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 1, -9), "[SOUTH]" = list( 1, -12), - "[EAST]" = list( 7, -10), - "[WEST]" = list(-7, -10) + "[EAST]" = list( 7, -10), + "[WEST]" = list(-7, -10) ) ) . = ..() @@ -210,12 +210,12 @@ uid = "bodytype_animal_kitten" /decl/bodytype/quadruped/animal/kitten/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 1, -14), "[SOUTH]" = list( 1, -14), - "[EAST]" = list( 5, -14), - "[WEST]" = list(-5, -14) + "[EAST]" = list( 5, -14), + "[WEST]" = list(-5, -14) ) ) . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm index c8ee4417ebff..64ea0d6d25f3 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm @@ -42,12 +42,12 @@ uid = "bodytype_animal_corgi" /decl/bodytype/quadruped/animal/corgi/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 1, -8), "[SOUTH]" = list( 1, -8), - "[EAST]" = list( 7, -8), - "[WEST]" = list(-7, -8) + "[EAST]" = list( 7, -8), + "[WEST]" = list(-7, -8) ) ) . = ..() @@ -151,12 +151,12 @@ uid = "bodytype_animal_puppy" /decl/bodytype/quadruped/animal/puppy/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 0, -12), "[SOUTH]" = list( 0, -12), - "[EAST]" = list( 5, -14), - "[WEST]" = list(-5, -14) + "[EAST]" = list( 5, -14), + "[WEST]" = list(-5, -14) ) ) . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 992ee0b67a9d..bcd84ddc3dfa 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -42,12 +42,12 @@ uid = "bodytype_animal_crab" /decl/bodytype/hexapod/animal/crab/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list(-1, -10), "[SOUTH]" = list(-1, -10), - "[EAST]" = list(-1, -10), - "[WEST]" = list(-1, -10) + "[EAST]" = list(-1, -10), + "[WEST]" = list(-1, -10) ) ) . = ..() diff --git a/code/modules/mob/living/simple_animal/passive/deer.dm b/code/modules/mob/living/simple_animal/passive/deer.dm index 03acf0a006df..da09424bce87 100644 --- a/code/modules/mob/living/simple_animal/passive/deer.dm +++ b/code/modules/mob/living/simple_animal/passive/deer.dm @@ -36,12 +36,12 @@ uid = "bodytype_animal_deer" /decl/bodytype/quadruped/animal/deer/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 1, -4), "[SOUTH]" = list( 1, -4), - "[EAST]" = list( 9, -4), - "[WEST]" = list(-9, -4) + "[EAST]" = list( 9, -4), + "[WEST]" = list(-9, -4) ) ) return ..() diff --git a/code/modules/mob/living/simple_animal/passive/fox.dm b/code/modules/mob/living/simple_animal/passive/fox.dm index cfa2421fcd38..c2db673e6e85 100644 --- a/code/modules/mob/living/simple_animal/passive/fox.dm +++ b/code/modules/mob/living/simple_animal/passive/fox.dm @@ -32,12 +32,12 @@ uid = "bodytype_animal_fox" /decl/bodytype/quadruped/animal/fox/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 1, -9), "[SOUTH]" = list( 1, -8), - "[EAST]" = list( 11, -9), - "[WEST]" = list(-11, -9) + "[EAST]" = list( 11, -9), + "[WEST]" = list(-11, -9) ) ) return ..() diff --git a/code/modules/species/station/monkey_bodytypes.dm b/code/modules/species/station/monkey_bodytypes.dm index 804d6d0add1c..b1b22de545cd 100644 --- a/code/modules/species/station/monkey_bodytypes.dm +++ b/code/modules/species/station/monkey_bodytypes.dm @@ -19,12 +19,12 @@ uid = "bodytype_monkey" /decl/bodytype/monkey/Initialize() - equip_adjust = list( - BP_L_HAND = list("[NORTH]" = list( 1, 3), "[EAST]" = list(-3, 2), "[SOUTH]" = list(-1, 3), "[WEST]" = list( 3, 2)), - BP_R_HAND = list("[NORTH]" = list(-1, 3), "[EAST]" = list( 3, 2), "[SOUTH]" = list( 1, 3), "[WEST]" = list(-3, 2)), - slot_shoes_str = list("[NORTH]" = list( 0, 7), "[EAST]" = list(-1, 7), "[SOUTH]" = list( 0, 7), "[WEST]" = list( 1, 7)), - slot_head_str = list("[NORTH]" = list( 0, 0), "[EAST]" = list(-2, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 2, 0)), - slot_wear_mask_str = list("[NORTH]" = list( 0, 0), "[EAST]" = list(-1, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 1, 0)) + _equip_adjust = list( + (BP_L_HAND) = list("[NORTH]" = list( 1, 3), "[EAST]" = list(-3, 2), "[SOUTH]" = list(-1, 3), "[WEST]" = list( 3, 2)), + (BP_R_HAND) = list("[NORTH]" = list(-1, 3), "[EAST]" = list( 3, 2), "[SOUTH]" = list( 1, 3), "[WEST]" = list(-3, 2)), + (slot_shoes_str) = list("[NORTH]" = list( 0, 7), "[EAST]" = list(-1, 7), "[SOUTH]" = list( 0, 7), "[WEST]" = list( 1, 7)), + (slot_head_str) = list("[NORTH]" = list( 0, 0), "[EAST]" = list(-2, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 2, 0)), + (slot_wear_mask_str) = list("[NORTH]" = list( 0, 0), "[EAST]" = list(-1, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 1, 0)) ) . = ..() diff --git a/mods/content/fantasy/datum/hnoll/bodytypes.dm b/mods/content/fantasy/datum/hnoll/bodytypes.dm index b5da3ed52b55..218068362ced 100644 --- a/mods/content/fantasy/datum/hnoll/bodytypes.dm +++ b/mods/content/fantasy/datum/hnoll/bodytypes.dm @@ -55,21 +55,21 @@ ) /decl/bodytype/hnoll/Initialize() - if(!equip_adjust) - equip_adjust = list( - slot_glasses_str = list( + if(!_equip_adjust) + _equip_adjust = list( + (slot_glasses_str) = list( "[NORTH]" = list( 0, 2), "[EAST]" = list( 0, 2), "[SOUTH]" = list( 0, 2), "[WEST]" = list( 0, 2) ), - slot_wear_mask_str = list( + (slot_wear_mask_str) = list( "[NORTH]" = list( 0, 2), "[EAST]" = list( 2, 2), "[SOUTH]" = list( 0, 2), "[WEST]" = list(-2, 2) ), - slot_head_str = list( + (slot_head_str) = list( "[NORTH]" = list( 0, 2), "[EAST]" = list( 0, 2), "[SOUTH]" = list( 0, 2), diff --git a/mods/content/fantasy/datum/kobaloi/bodytypes.dm b/mods/content/fantasy/datum/kobaloi/bodytypes.dm index 3e87eafcf54c..72740539a495 100644 --- a/mods/content/fantasy/datum/kobaloi/bodytypes.dm +++ b/mods/content/fantasy/datum/kobaloi/bodytypes.dm @@ -32,87 +32,87 @@ uid = "bodytype_kobaloi" /decl/bodytype/kobaloi/Initialize() - if(!equip_adjust) - equip_adjust = list( - BP_R_HAND = list( + if(!_equip_adjust) + _equip_adjust = list( + (BP_R_HAND) = list( "[NORTH]" = list( 1, -4), "[EAST]" = list( 0, -4), "[SOUTH]" = list(-1, -4), "[WEST]" = list(-1, -4) ), - BP_L_HAND = list( + (BP_L_HAND) = list( "[NORTH]" = list(-1, -4), "[EAST]" = list( 1, -4), "[SOUTH]" = list( 1, -4), "[WEST]" = list( 0, -4) ), - slot_w_uniform_str = list( + (slot_w_uniform_str) = list( "[NORTH]" = list( 0, -6), "[EAST]" = list( 1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-1, -6) ), - slot_belt_str = list( + (slot_belt_str) = list( "[NORTH]" = list( 0, -6), "[EAST]" = list( 1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-1, -6) ), - slot_handcuffed_str = list( + (slot_handcuffed_str) = list( "[NORTH]" = list(-1, -4), "[EAST]" = list( 1, -4), "[SOUTH]" = list( 1, -4), "[WEST]" = list( 0, -4) ), - slot_wear_id_str = list( + (slot_wear_id_str) = list( "[NORTH]" = list( 0, -6), "[EAST]" = list( 1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-1, -6) ), - slot_gloves_str = list( + (slot_gloves_str) = list( "[NORTH]" = list(-1, -4), "[EAST]" = list( 1, -4), "[SOUTH]" = list( 1, -4), "[WEST]" = list( 0, -4) ), - slot_wear_suit_str = list( + (slot_wear_suit_str) = list( "[NORTH]" = list( 0, -6), "[EAST]" = list( 1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-1, -6) ), - slot_back_str = list( + (slot_back_str) = list( "[NORTH]" = list( 0, -5), "[EAST]" = list( 1, -5), "[SOUTH]" = list( 0, -5), "[WEST]" = list(-1, -5) ), - slot_glasses_str = list( + (slot_glasses_str) = list( "[NORTH]" = list( 0, -6), "[EAST]" = list( 3, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-3, -6) ), - slot_wear_mask_str = list( + (slot_wear_mask_str) = list( "[NORTH]" = list( 0, -7), "[EAST]" = list( 5, -7), "[SOUTH]" = list( 0, -7), "[WEST]" = list(-5, -7) ), - slot_head_str = list( + (slot_head_str) = list( "[NORTH]" = list( 0, -5), "[EAST]" = list( 3, -5), "[SOUTH]" = list( 0, -5), "[WEST]" = list(-3, -5) ), - slot_l_ear_str = list( + (slot_l_ear_str) = list( "[NORTH]" = list( 0, -5), "[EAST]" = list( 3, -5), "[SOUTH]" = list( 0, -5), "[WEST]" = list(-3, -5) ), - slot_r_ear_str = list( + (slot_r_ear_str) = list( "[NORTH]" = list( 0, -5), "[EAST]" = list( 3, -5), "[SOUTH]" = list( 0, -5), diff --git a/mods/mobs/dionaea/mob/_nymph.dm b/mods/mobs/dionaea/mob/_nymph.dm index 6ec480cda3be..a1c8efe44862 100644 --- a/mods/mobs/dionaea/mob/_nymph.dm +++ b/mods/mobs/dionaea/mob/_nymph.dm @@ -95,12 +95,12 @@ uid = "bodytype_diona" /decl/bodytype/diona/Initialize() - equip_adjust = list( - slot_head_str = list( + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list(0, -8), "[SOUTH]" = list(0, -8), - "[EAST]" = list(0, -8), - "[WEST]" = list(0, -8) + "[EAST]" = list(0, -8), + "[WEST]" = list(0, -8) ) ) . = ..() diff --git a/mods/species/ascent/datum/species_bodytypes.dm b/mods/species/ascent/datum/species_bodytypes.dm index 05ca4ed987f4..9136d47e269a 100644 --- a/mods/species/ascent/datum/species_bodytypes.dm +++ b/mods/species/ascent/datum/species_bodytypes.dm @@ -85,8 +85,8 @@ uid = "bodytype_crystalline_gyne" /decl/bodytype/crystalline/mantid/gyne/Initialize() - equip_adjust = list( - BP_L_HAND = list( + _equip_adjust = list( + (BP_L_HAND) = list( "[NORTH]" = list(-4, 12), "[EAST]" = list(-4, 12), "[SOUTH]" = list(-4, 12), diff --git a/mods/species/bayliens/adherent/datum/species_bodytypes.dm b/mods/species/bayliens/adherent/datum/species_bodytypes.dm index 8e6c7ab2ee9e..bbf275eb8558 100644 --- a/mods/species/bayliens/adherent/datum/species_bodytypes.dm +++ b/mods/species/bayliens/adherent/datum/species_bodytypes.dm @@ -53,54 +53,48 @@ uid = "bodytype_crystalline_adherent_turquoise" /decl/bodytype/crystalline/adherent/Initialize() - equip_adjust = list( - "[BP_L_HAND]" = list( + _equip_adjust = list( + (BP_L_HAND) = list( "[NORTH]" = list(0, 14), "[EAST]" = list(0, 14), "[SOUTH]" = list(0, 14), "[WEST]" = list(0, 14) ), - - "[BP_R_HAND]" = list( + (BP_R_HAND) = list( "[NORTH]" = list(0, 14), "[EAST]" = list(0, 14), "[SOUTH]" = list(0, 14), "[WEST]" = list(0, 14) ), - - "[slot_back_str]" = list( + (slot_back_str) = list( "[NORTH]" = list(0, 14), "[EAST]" = list(0, 14), "[SOUTH]" = list(0, 14), "[WEST]" = list(0, 14) ), - - "[slot_belt_str]" = list( + (slot_belt_str) = list( "[NORTH]" = list(0, 14), "[EAST]" = list(0, 14), "[SOUTH]" = list(0, 14), "[WEST]" = list(0, 14) ), - - "[slot_head_str]" = list( + (slot_head_str) = list( "[NORTH]" = list( 0, 14), "[EAST]" = list( 3, 14), "[SOUTH]" = list( 0, 14), "[WEST]" = list(-3, 14) ), - - "[slot_l_ear_str]" = list( + (slot_l_ear_str) = list( "[NORTH]" = list(0, 14), "[EAST]" = list(0, 14), "[SOUTH]" = list(0, 14), - "[WEST]" = list(0, 14) + "[WEST]" = list(0, 14) ), - - "[slot_r_ear_str]" = list( + (slot_r_ear_str) = list( "[NORTH]" = list(0, 14), "[EAST]" = list(0, 14), "[SOUTH]" = list(0, 14), - "[WEST]" = list(0, 14) + "[WEST]" = list(0, 14) ) ) . = ..() diff --git a/mods/species/bayliens/tajaran/datum/species_bodytypes.dm b/mods/species/bayliens/tajaran/datum/species_bodytypes.dm index 9dbd49a90fec..225bf5bc86e7 100644 --- a/mods/species/bayliens/tajaran/datum/species_bodytypes.dm +++ b/mods/species/bayliens/tajaran/datum/species_bodytypes.dm @@ -52,10 +52,25 @@ ) /decl/bodytype/feline/Initialize() - equip_adjust = list( - slot_glasses_str = list("[NORTH]" = list(0, 2), "[EAST]" = list(0, 2), "[SOUTH]" = list( 0, 2), "[WEST]" = list(0, 2)), - slot_wear_mask_str = list("[NORTH]" = list(0, 2), "[EAST]" = list(0, 2), "[SOUTH]" = list( 0, 2), "[WEST]" = list(0, 2)), - slot_head_str = list("[NORTH]" = list(0, 2), "[EAST]" = list(0, 2), "[SOUTH]" = list( 0, 2), "[WEST]" = list(0, 2)) + _equip_adjust = list( + (slot_glasses_str) = list( + "[NORTH]" = list(0, 2), + "[EAST]" = list(0, 2), + "[SOUTH]" = list( 0, 2), + "[WEST]" = list(0, 2) + ), + (slot_wear_mask_str) = list( + "[NORTH]" = list(0, 2), + "[EAST]" = list(0, 2), + "[SOUTH]" = list( 0, 2), + "[WEST]" = list(0, 2) + ), + (slot_head_str) = list( + "[NORTH]" = list(0, 2), + "[EAST]" = list(0, 2), + "[SOUTH]" = list( 0, 2), + "[WEST]" = list(0, 2) + ) ) . = ..() diff --git a/mods/species/drakes/species_bodytypes.dm b/mods/species/drakes/species_bodytypes.dm index 3d16db257628..dc5afea4ea45 100644 --- a/mods/species/drakes/species_bodytypes.dm +++ b/mods/species/drakes/species_bodytypes.dm @@ -134,48 +134,48 @@ ARMOR_BOMB = ARMOR_BOMB_PADDED ) - var/list/sitting_equip_adjust - var/list/lying_equip_adjust + VAR_PRIVATE/list/_sitting_equip_adjust + VAR_PRIVATE/list/_lying_equip_adjust /decl/bodytype/quadruped/grafadreka/Initialize() - if(!length(equip_adjust)) - equip_adjust = list( - slot_head_str = list( + if(!length(_equip_adjust)) + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list(16, -8), "[SOUTH]" = list(16, -12), - "[EAST]" = list(38, -8), - "[WEST]" = list(-6, -8) + "[EAST]" = list(38, -8), + "[WEST]" = list(-6, -8) ) ) - if(!length(sitting_equip_adjust)) - sitting_equip_adjust = list( - slot_head_str = list( + if(!length(_sitting_equip_adjust)) + _sitting_equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list(16, -2), "[SOUTH]" = list(16, -2), - "[EAST]" = list(22, -2), - "[WEST]" = list(12, -2) + "[EAST]" = list(22, -2), + "[WEST]" = list(12, -2) ) ) - if(!length(lying_equip_adjust)) - lying_equip_adjust = list( - slot_head_str = list( + if(!length(_lying_equip_adjust)) + _lying_equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 24, -24), "[SOUTH]" = list( 24, -24), - "[EAST]" = list( 24, -24), - "[WEST]" = list(-10, -24) + "[EAST]" = list( 24, -24), + "[WEST]" = list(-10, -24) ) ) return ..() -/decl/bodytype/quadruped/grafadreka/get_equip_adjust(mob/mob) +/decl/bodytype/quadruped/grafadreka/get_equip_adjustments(mob/mob) switch(mob.current_posture?.name) if("lying", "resting") - return lying_equip_adjust + return _lying_equip_adjust if("sitting") - return sitting_equip_adjust + return _sitting_equip_adjust return ..() /decl/bodytype/quadruped/grafadreka/hatchling @@ -206,31 +206,31 @@ uid = "bodytype_drake_hatchling" /decl/bodytype/quadruped/grafadreka/hatchling/Initialize() - if(!length(equip_adjust)) - equip_adjust = list( - slot_head_str = list( + if(!length(_equip_adjust)) + _equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 0, -18), "[SOUTH]" = list( 0, -18), - "[EAST]" = list( 8, -18), - "[WEST]" = list(-8, -18) + "[EAST]" = list( 8, -18), + "[WEST]" = list(-8, -18) ) ) - if(!length(sitting_equip_adjust)) - sitting_equip_adjust = list( - slot_head_str = list( + if(!length(_sitting_equip_adjust)) + _sitting_equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 0, -14), "[SOUTH]" = list( 0, -14), - "[EAST]" = list( 4, -14), - "[WEST]" = list(-4, -14) + "[EAST]" = list( 4, -14), + "[WEST]" = list(-4, -14) ) ) - if(!length(lying_equip_adjust)) - lying_equip_adjust = list( - slot_head_str = list( + if(!length(_lying_equip_adjust)) + _lying_equip_adjust = list( + (slot_head_str) = list( "[NORTH]" = list( 0, -24), "[SOUTH]" = list( 0, -24), - "[EAST]" = list( 0, -24), - "[WEST]" = list( 0, -24) + "[EAST]" = list( 0, -24), + "[WEST]" = list( 0, -24) ) ) return ..() diff --git a/mods/species/neoavians/datum/species_bodytypes.dm b/mods/species/neoavians/datum/species_bodytypes.dm index 5843792a090c..35eb54d9a7ff 100644 --- a/mods/species/neoavians/datum/species_bodytypes.dm +++ b/mods/species/neoavians/datum/species_bodytypes.dm @@ -94,19 +94,79 @@ uid = "bodytype_avian_additive_raptor" /decl/bodytype/avian/Initialize() - equip_adjust = list( - slot_l_ear_str = list("[NORTH]" = list( 1, -5), "[EAST]" = list(-2, -5), "[SOUTH]" = list(-1, -5), "[WEST]" = list( 0, -5)), - slot_r_ear_str = list("[NORTH]" = list( 1, -5), "[EAST]" = list( 0, -5), "[SOUTH]" = list(-1, -5), "[WEST]" = list( 2, -5)), - BP_L_HAND = list("[NORTH]" = list( 3, -3), "[EAST]" = list( 1, -3), "[SOUTH]" = list(-3, -3), "[WEST]" = list(-5, -3)), - BP_R_HAND = list("[NORTH]" = list(-3, -3), "[EAST]" = list( 5, -3), "[SOUTH]" = list( 3, -3), "[WEST]" = list(-1, -3)), - slot_head_str = list("[NORTH]" = list( 0, -5), "[EAST]" = list( 1, -5), "[SOUTH]" = list( 0, -5), "[WEST]" = list(-1, -5)), - slot_wear_mask_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list( 2, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-2, -6)), - slot_glasses_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list( 1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-1, -6)), - slot_back_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list( 3, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list(-3, -6)), - slot_w_uniform_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list( 1, -6)), - slot_wear_id_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list( 1, -6)), - slot_wear_suit_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list( 1, -6)), - slot_belt_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6), "[WEST]" = list( 1, -6)) + _equip_adjust = list( + (slot_l_ear_str) = list( + "[NORTH]" = list( 1, -5), + "[EAST]" = list(-2, -5), + "[SOUTH]" = list(-1, -5), + "[WEST]" = list( 0, -5) + ), + (slot_r_ear_str) = list( + "[NORTH]" = list( 1, -5), + "[EAST]" = list( 0, -5), + "[SOUTH]" = list(-1, -5), + "[WEST]" = list( 2, -5) + ), + (BP_L_HAND) = list( + "[NORTH]" = list( 3, -3), + "[EAST]" = list( 1, -3), + "[SOUTH]" = list(-3, -3), + "[WEST]" = list(-5, -3) + ), + (BP_R_HAND) = list( + "[NORTH]" = list(-3, -3), + "[EAST]" = list( 5, -3), + "[SOUTH]" = list( 3, -3), + "[WEST]" = list(-1, -3) + ), + (slot_head_str) = list( + "[NORTH]" = list( 0, -5), + "[EAST]" = list( 1, -5), + "[SOUTH]" = list( 0, -5), + "[WEST]" = list(-1, -5) + ), + (slot_wear_mask_str) = list( + "[NORTH]" = list( 0, -6), + "[EAST]" = list( 2, -6), + "[SOUTH]" = list( 0, -6), + "[WEST]" = list(-2, -6) + ), + (slot_glasses_str) = list( + "[NORTH]" = list( 0, -6), + "[EAST]" = list( 1, -6), + "[SOUTH]" = list( 0, -6), + "[WEST]" = list(-1, -6) + ), + (slot_back_str) = list( + "[NORTH]" = list( 0, -6), + "[EAST]" = list( 3, -6), + "[SOUTH]" = list( 0, -6), + "[WEST]" = list(-3, -6) + ), + (slot_w_uniform_str) = list( + "[NORTH]" = list( 0, -6), + "[EAST]" = list(-1, -6), + "[SOUTH]" = list( 0, -6), + "[WEST]" = list( 1, -6) + ), + (slot_wear_id_str) = list( + "[NORTH]" = list( 0, -6), + "[EAST]" = list(-1, -6), + "[SOUTH]" = list( 0, -6), + "[WEST]" = list( 1, -6) + ), + (slot_wear_suit_str) = list( + "[NORTH]" = list( 0, -6), + "[EAST]" = list(-1, -6), + "[SOUTH]" = list( 0, -6), + "[WEST]" = list( 1, -6) + ), + (slot_belt_str) = list( + "[NORTH]" = list( 0, -6), + "[EAST]" = list(-1, -6), + "[SOUTH]" = list( 0, -6), + "[WEST]" = list( 1, -6) + ) ) . = ..() diff --git a/mods/species/serpentid/datum/species_bodytypes.dm b/mods/species/serpentid/datum/species_bodytypes.dm index de0c77e659f4..6c4e562b4510 100644 --- a/mods/species/serpentid/datum/species_bodytypes.dm +++ b/mods/species/serpentid/datum/species_bodytypes.dm @@ -65,15 +65,55 @@ ) /decl/bodytype/serpentid/Initialize() - equip_adjust = list( - BP_L_HAND_UPPER = list("[NORTH]" = list( 0, 8), "[EAST]" = list(0, 8), "[SOUTH]" = list(-0, 8), "[WEST]" = list( 0, 8)), - BP_R_HAND_UPPER = list("[NORTH]" = list( 0, 8), "[EAST]" = list(0, 8), "[SOUTH]" = list( 0, 8), "[WEST]" = list( 0, 8)), - BP_L_HAND = list("[NORTH]" = list( 4, 0), "[EAST]" = list(0, 0), "[SOUTH]" = list(-4, 0), "[WEST]" = list( 0, 0)), - BP_R_HAND = list("[NORTH]" = list(-4, 0), "[EAST]" = list(0, 0), "[SOUTH]" = list( 4, 0), "[WEST]" = list( 0, 0)), - slot_head_str = list("[NORTH]" = list( 0, 7), "[EAST]" = list(0, 8), "[SOUTH]" = list( 0, 8), "[WEST]" = list( 0, 8)), - slot_back_str = list("[NORTH]" = list( 0, 7), "[EAST]" = list(0, 8), "[SOUTH]" = list( 0, 8), "[WEST]" = list( 0, 8)), - slot_belt_str = list("[NORTH]" = list( 0, 0), "[EAST]" = list(8, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list(-8, 0)), - slot_glasses_str = list("[NORTH]" = list( 0, 10), "[EAST]" = list(0, 11), "[SOUTH]" = list( 0, 11), "[WEST]" = list( 0, 11)) + _equip_adjust = list( + (BP_L_HAND_UPPER) = list( + "[NORTH]" = list( 0, 8), + "[EAST]" = list( 0, 8), + "[SOUTH]" = list(-0, 8), + "[WEST]" = list( 0, 8) + ), + (BP_R_HAND_UPPER) = list( + "[NORTH]" = list( 0, 8), + "[EAST]" = list( 0, 8), + "[SOUTH]" = list( 0, 8), + "[WEST]" = list( 0, 8) + ), + (BP_L_HAND) = list( + "[NORTH]" = list( 4, 0), + "[EAST]" = list( 0, 0), + "[SOUTH]" = list(-4, 0), + "[WEST]" = list( 0, 0) + ), + (BP_R_HAND) = list( + "[NORTH]" = list(-4, 0), + "[EAST]" = list( 0, 0), + "[SOUTH]" = list( 4, 0), + "[WEST]" = list( 0, 0) + ), + (slot_head_str) = list( + "[NORTH]" = list( 0, 7), + "[EAST]" = list( 0, 8), + "[SOUTH]" = list( 0, 8), + "[WEST]" = list( 0, 8) + ), + (slot_back_str) = list( + "[NORTH]" = list( 0, 7), + "[EAST]" = list( 0, 8), + "[SOUTH]" = list( 0, 8), + "[WEST]" = list( 0, 8) + ), + (slot_belt_str) = list( + "[NORTH]" = list( 0, 0), + "[EAST]" = list( 8, 0), + "[SOUTH]" = list( 0, 0), + "[WEST]" = list(-8, 0) + ), + (slot_glasses_str) = list( + "[NORTH]" = list( 0, 10), + "[EAST]" = list( 0, 11), + "[SOUTH]" = list( 0, 11), + "[WEST]" = list( 0, 11) + ) ) . = ..() diff --git a/mods/species/utility_frames/species_bodytypes.dm b/mods/species/utility_frames/species_bodytypes.dm index 378c5d86f08b..59b71e3abd40 100644 --- a/mods/species/utility_frames/species_bodytypes.dm +++ b/mods/species/utility_frames/species_bodytypes.dm @@ -32,14 +32,14 @@ uid = "bodytype_prosthetic_utility_frame" /decl/bodytype/prosthetic/utility_frame/Initialize() - equip_adjust = list( - "[slot_l_ear_str]" = list( + _equip_adjust = list( + (slot_l_ear_str) = list( "[NORTH]" = list( 2, 0), "[EAST]" = list( 0, 0), "[SOUTH]" = list(-2, 0), "[WEST]" = list( 0, 0) ), - "[slot_r_ear_str]" = list( + (slot_r_ear_str) = list( "[NORTH]" = list(-2, 0), "[EAST]" = list( 0, 0), "[SOUTH]" = list( 2, 0), @@ -48,4 +48,4 @@ ) . = ..() -DEFINE_ROBOLIMB_DESIGNS(/decl/bodytype/prosthetic/utility_frame, utility_frame) \ No newline at end of file +DEFINE_ROBOLIMB_DESIGNS(/decl/bodytype/prosthetic/utility_frame, utility_frame) diff --git a/mods/species/vox/datum/species_bodytypes.dm b/mods/species/vox/datum/species_bodytypes.dm index 25f4e64c1804..ee3c6ba7f399 100644 --- a/mods/species/vox/datum/species_bodytypes.dm +++ b/mods/species/vox/datum/species_bodytypes.dm @@ -60,17 +60,17 @@ var/icon/vox_marking_icon = 'mods/species/vox/icons/body/soldier/markings.dmi' /decl/bodytype/vox/Initialize() - if(!length(equip_adjust)) - equip_adjust = list( - BP_L_HAND = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list( 0, -2)), - BP_R_HAND = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list( 0, -2)), - slot_head_str = list("[NORTH]" = list(0, -2), "[EAST]" = list(3, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list(-3, -2)), - slot_wear_mask_str = list("[NORTH]" = list(0, 0), "[EAST]" = list(4, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list(-4, 0)), - slot_wear_suit_str = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - slot_w_uniform_str = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - slot_underpants_str = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - slot_undershirt_str = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - slot_back_str = list("[NORTH]" = list(0, 0), "[EAST]" = list(3, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list(-3, 0)) + if(!length(_equip_adjust)) + _equip_adjust = list( + (BP_L_HAND) = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list( 0, -2)), + (BP_R_HAND) = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list( 0, -2)), + (slot_head_str) = list("[NORTH]" = list(0, -2), "[EAST]" = list(3, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list(-3, -2)), + (slot_wear_mask_str) = list("[NORTH]" = list(0, 0), "[EAST]" = list(4, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list(-4, 0)), + (slot_wear_suit_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), + (slot_w_uniform_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), + (slot_underpants_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), + (slot_undershirt_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), + (slot_back_str) = list("[NORTH]" = list(0, 0), "[EAST]" = list(3, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list(-3, 0)) ) return ..() From 996e803786df59487a026098fa6cfd27ade070c8 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 12 Dec 2024 18:44:10 +1100 Subject: [PATCH 0126/1331] Allows overriding organ appearance bodytype for future work. --- .../organs/external/_external_icons.dm | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/code/modules/organs/external/_external_icons.dm b/code/modules/organs/external/_external_icons.dm index 7c7c5b2e24e8..887bdb0f6c6c 100644 --- a/code/modules/organs/external/_external_icons.dm +++ b/code/modules/organs/external/_external_icons.dm @@ -27,11 +27,14 @@ var/global/list/limb_icon_cache = list() _icon_cache_key = null skin_tone = null skin_colour = null + var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype() + if(!icon_bodytype) + return // This used to do a bodytype set but that was *really really bad.* Things that need that should do it themselves. - skin_blend = bodytype.limb_blend - if(!isnull(human.skin_tone) && bodytype?.appearance_flags & HAS_A_SKIN_TONE) + skin_blend = icon_bodytype.limb_blend + if(!isnull(human.skin_tone) && (icon_bodytype.appearance_flags & HAS_A_SKIN_TONE)) skin_tone = human.skin_tone - if(bodytype.appearance_flags & HAS_SKIN_COLOR) + if(icon_bodytype.appearance_flags & HAS_SKIN_COLOR) skin_colour = human.get_skin_colour() /obj/item/organ/external/head/sync_colour_to_human(var/mob/living/human/human) @@ -46,21 +49,28 @@ var/global/list/limb_icon_cache = list() addtimer(CALLBACK(last_owner, TYPE_PROC_REF(/mob, update_hair)), 1, TIMER_UNIQUE) return ..() +// For overriding on shapeshifters/changelings in the future. +/obj/item/organ/external/proc/get_organ_appearance_bodytype() + RETURN_TYPE(/decl/bodytype) + return bodytype + /obj/item/organ/external/proc/update_limb_icon_file() - if(!bodytype) // This should not happen. + var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype() + if(!icon_bodytype) // This should not happen. icon = initial(icon) else if(limb_flags & ORGAN_FLAG_SKELETAL) - icon = bodytype.get_skeletal_icon(owner) + icon = icon_bodytype.get_skeletal_icon(owner) else if(!BP_IS_PROSTHETIC(src) && (status & ORGAN_MUTATED)) - icon = bodytype.get_base_icon(owner, get_deform = TRUE) + icon = icon_bodytype.get_base_icon(owner, get_deform = TRUE) else - icon = bodytype.get_base_icon(owner) + icon = icon_bodytype.get_base_icon(owner) var/global/list/organ_icon_cache = list() /obj/item/organ/external/proc/generate_mob_icon() // Generate base icon with colour and tone. - var/icon/ret = bodytype.apply_limb_colouration(src, new /icon(icon, icon_state)) + var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype() + var/icon/ret = icon_bodytype.apply_limb_colouration(src, new /icon(icon, icon_state)) if(limb_flags & ORGAN_FLAG_SKELETAL) global.organ_icon_cache[_icon_cache_key] = ret return ret @@ -75,7 +85,7 @@ var/global/list/organ_icon_cache = list() else ret.Blend(rgb(-skin_tone, -skin_tone, -skin_tone), ICON_SUBTRACT) - if((bodytype.appearance_flags & HAS_SKIN_COLOR) && skin_colour) + if((icon_bodytype.appearance_flags & HAS_SKIN_COLOR) && skin_colour) ret.Blend(skin_colour, skin_blend) // Body markings, hair, lips, etc. @@ -111,7 +121,7 @@ var/global/list/organ_icon_cache = list() /obj/item/organ/external/proc/get_icon_cache_key_components() - . = list("[icon_state]_[species.name]_[bodytype?.name || "BAD_BODYTYPE"]_[render_alpha]_[icon]") + . = list("[icon_state]_[species.name]_[get_organ_appearance_bodytype()?.name || "BAD_BODYTYPE"]_[render_alpha]_[icon]") // Skeletons don't care about most icon appearance stuff. if(limb_flags & ORGAN_FLAG_SKELETAL) @@ -191,7 +201,7 @@ var/global/list/organ_icon_cache = list() var/list/refresh_accessories if(accessory_metadata) - if(!accessory_decl.accessory_is_available(owner, species, bodytype, (owner ? owner.get_traits() : FALSE))) + if(!accessory_decl.accessory_is_available(owner, species, get_organ_appearance_bodytype(), (owner ? owner.get_traits() : FALSE))) return FALSE var/list/existing_metadata = LAZYACCESS(accessories, accessory_type) if(same_entries(existing_metadata, accessory_metadata)) @@ -324,9 +334,10 @@ var/global/list/robot_hud_colours = list("#ffffff","#cccccc","#aaaaaa","#888888" var/image/temp = image(limb_icon_cache[cache_key]) if(species) // Calculate the required colour matrix. - var/r = 0.30 * bodytype.health_hud_intensity - var/g = 0.59 * bodytype.health_hud_intensity - var/b = 0.11 * bodytype.health_hud_intensity + var/hud_intensity = get_organ_appearance_bodytype()?.health_hud_intensity || 1 + var/r = 0.30 * hud_intensity + var/g = 0.59 * hud_intensity + var/b = 0.11 * hud_intensity temp.color = list(r, r, r, g, g, g, b, b, b) temp.pixel_x = owner.default_pixel_x temp.pixel_y = owner.default_pixel_y @@ -361,7 +372,7 @@ var/global/list/robot_hud_colours = list("#ffffff","#cccccc","#aaaaaa","#888888" if(ispath(accessory_style)) accessory_style = GET_DECL(accessory_style) // Check if this style is permitted for this species, period. - if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, bodytype, (owner ? owner.get_traits() : FALSE))) + if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, get_organ_appearance_bodytype(), (owner ? owner.get_traits() : FALSE))) return null // Check if we are concealed (long hair under a hat for example). if(accessory_style.is_hidden(src)) @@ -372,7 +383,7 @@ var/global/list/robot_hud_colours = list("#ffffff","#cccccc","#aaaaaa","#888888" for(var/acc_cat in _sprite_accessories) for(var/accessory in _sprite_accessories[acc_cat]) var/decl/sprite_accessory/accessory_style = GET_DECL(accessory) - if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, bodytype, (owner ? owner.get_traits() : FALSE))) + if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, get_organ_appearance_bodytype(), (owner ? owner.get_traits() : FALSE))) _sprite_accessories[acc_cat] -= accessory . = TRUE if(.) From f466649d1f41217b345990adba47c1850261f299 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 12 Dec 2024 19:12:17 +1100 Subject: [PATCH 0127/1331] Adding some supporting code for shapeshifters. --- code/game/objects/item_mob_overlay.dm | 4 ++-- code/modules/bodytype/bodytype_offsets.dm | 4 ++++ code/modules/mob/living/living_organs.dm | 12 ----------- code/modules/mob/mob.dm | 10 ++++++++- code/modules/mob/mob_grabs.dm | 9 -------- code/modules/mob/mob_organs.dm | 21 +++++++++++++++++++ .../organs/external/_external_icons.dm | 16 +++++++++----- code/modules/organs/internal/eyes.dm | 20 +++++++++++------- code/modules/organs/organ.dm | 14 +++++++++++++ nebula.dme | 1 + 10 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 code/modules/mob/mob_organs.dm diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm index be29ad618ca9..d6881129c230 100644 --- a/code/game/objects/item_mob_overlay.dm +++ b/code/game/objects/item_mob_overlay.dm @@ -68,7 +68,7 @@ var/global/list/icon_state_cache = list() if(!use_single_icon) var/mob_state = "[item_state || icon_state][state_modifier]" var/mob_icon = global.default_onmob_icons[slot] - var/decl/bodytype/root_bodytype = user_mob?.get_bodytype() + var/decl/bodytype/root_bodytype = user_mob?.get_equipment_bodytype(slot, bodypart) if(istype(root_bodytype)) var/use_slot = (bodypart in root_bodytype.get_equip_adjustments(user_mob)) ? bodypart : slot return root_bodytype.get_offset_overlay_image(user_mob, mob_icon, mob_state, color, use_slot) @@ -153,7 +153,7 @@ var/global/list/icon_state_cache = list() overlay.icon_state = wielded_state apply_additional_mob_overlays(user_mob, bodytype, overlay, slot, bodypart, use_fallback_if_icon_missing) - var/decl/bodytype/root_bodytype = user_mob?.get_bodytype() + var/decl/bodytype/root_bodytype = user_mob?.get_equipment_bodytype(slot, bodypart) if(root_bodytype && root_bodytype.bodytype_category != bodytype) var/list/overlays_to_offset = overlay.overlays overlay = root_bodytype.get_offset_overlay_image(user_mob, overlay.icon, overlay.icon_state, color, (bodypart || slot)) diff --git a/code/modules/bodytype/bodytype_offsets.dm b/code/modules/bodytype/bodytype_offsets.dm index 279b43e99183..0a149de8192f 100644 --- a/code/modules/bodytype/bodytype_offsets.dm +++ b/code/modules/bodytype/bodytype_offsets.dm @@ -21,6 +21,10 @@ The slots that you can use are found in items_clothing.dm and are the inventory VAR_PRIVATE/list/_equip_adjust VAR_PRIVATE/list/equip_overlays = list() +// Will be used by changelings/shapeshifters in the future +/decl/bodytype/proc/resolve_to_equipment_bodytype(mob/living/user) + return src + /decl/bodytype/proc/get_equip_adjustments(mob/mob) return _equip_adjust diff --git a/code/modules/mob/living/living_organs.dm b/code/modules/mob/living/living_organs.dm index 9fefc64c8949..dce22c9a9933 100644 --- a/code/modules/mob/living/living_organs.dm +++ b/code/modules/mob/living/living_organs.dm @@ -1,15 +1,3 @@ -/mob/living/get_organs() - for(var/organ in get_external_organs()) - LAZYADD(., organ) - for(var/organ in get_internal_organs()) - LAZYADD(., organ) - -/mob/living/proc/get_external_organs() - return - -/mob/living/proc/get_internal_organs() - return - /mob/living/proc/get_organs_by_categories(var/category) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 449c6a76a2a3..03827667a287 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1110,10 +1110,18 @@ /mob/proc/get_bodytype() RETURN_TYPE(/decl/bodytype) +// Bit of a stub for now, but should return the bodytype specific +// to the slot and organ being checked in the future instead of +// always using the mob root bodytype. +/mob/proc/get_equipment_bodytype(slot, bodypart) + RETURN_TYPE(/decl/bodytype) + var/decl/bodytype/root_bodytype = get_bodytype() + return root_bodytype?.resolve_to_equipment_bodytype(src) + /mob/proc/has_body_flag(flag, default = FALSE) var/decl/bodytype/root_bodytype = get_bodytype() if(istype(root_bodytype)) - return root_bodytype.body_flags & flag + return (root_bodytype.body_flags & flag) return default /// Update the mouse pointer of the attached client in this mob. diff --git a/code/modules/mob/mob_grabs.dm b/code/modules/mob/mob_grabs.dm index e02bed4b3dc9..0cea8113ed7e 100644 --- a/code/modules/mob/mob_grabs.dm +++ b/code/modules/mob/mob_grabs.dm @@ -7,15 +7,6 @@ return /mob/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0) return -/mob/proc/has_organ(organ_tag) - return !!get_organ(organ_tag, /obj/item/organ) -/mob/proc/get_organ(var/organ_tag, var/expected_type) - RETURN_TYPE(/obj/item/organ) - return -/mob/proc/get_injured_organs() - return -/mob/proc/get_organs() - return // End grab casting stubs. /mob/can_be_grabbed(var/mob/grabber, var/target_zone) diff --git a/code/modules/mob/mob_organs.dm b/code/modules/mob/mob_organs.dm new file mode 100644 index 000000000000..eb5b763c95fd --- /dev/null +++ b/code/modules/mob/mob_organs.dm @@ -0,0 +1,21 @@ +/mob/proc/has_organ(organ_tag) + return !!get_organ(organ_tag, /obj/item/organ) + +/mob/proc/get_organ(var/organ_tag, var/expected_type) + RETURN_TYPE(/obj/item/organ) + return + +/mob/proc/get_injured_organs() + return + +/mob/proc/get_external_organs() + return + +/mob/proc/get_internal_organs() + return + +/mob/proc/get_organs() + for(var/organ in get_external_organs()) + LAZYADD(., organ) + for(var/organ in get_internal_organs()) + LAZYADD(., organ) diff --git a/code/modules/organs/external/_external_icons.dm b/code/modules/organs/external/_external_icons.dm index 887bdb0f6c6c..8f9b601a8c55 100644 --- a/code/modules/organs/external/_external_icons.dm +++ b/code/modules/organs/external/_external_icons.dm @@ -49,10 +49,16 @@ var/global/list/limb_icon_cache = list() addtimer(CALLBACK(last_owner, TYPE_PROC_REF(/mob, update_hair)), 1, TIMER_UNIQUE) return ..() -// For overriding on shapeshifters/changelings in the future. -/obj/item/organ/external/proc/get_organ_appearance_bodytype() - RETURN_TYPE(/decl/bodytype) - return bodytype +/obj/item/organ/external/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE) + . = ..() + if(.) + if(update_sprite_accessories) + sanitize_sprite_accessories(skip_update = TRUE) + _icon_cache_key = null + get_icon_for_bodytype() + update_icon() + if(owner && !skip_owner_update) + owner.update_body() /obj/item/organ/external/proc/update_limb_icon_file() var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype() @@ -121,7 +127,7 @@ var/global/list/organ_icon_cache = list() /obj/item/organ/external/proc/get_icon_cache_key_components() - . = list("[icon_state]_[species.name]_[get_organ_appearance_bodytype()?.name || "BAD_BODYTYPE"]_[render_alpha]_[icon]") + . = list("[icon_state]_[species.name]_[get_organ_appearance_bodytype()?.uid || "BAD_BODYTYPE"]_[render_alpha]_[icon]") // Skeletons don't care about most icon appearance stuff. if(limb_flags & ORGAN_FLAG_SKELETAL) diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index f193afaa4daa..7dbc75053e6a 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -36,19 +36,25 @@ verbs |= /obj/item/organ/internal/eyes/proc/change_eye_color_verb verbs |= /obj/item/organ/internal/eyes/proc/toggle_eye_glow +/obj/item/organ/external/eyes/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE) + . = ..() + if(. && owner && !skip_owner_update) + owner.update_eyes() + /obj/item/organ/internal/eyes/proc/get_onhead_icon() + var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype() var/modifier = owner?.get_overlay_state_modifier() var/eye_state = modifier ? "eyes[modifier]" : "eyes" last_cached_eye_colour = eye_colour - last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[bodytype.eye_offset]-[eye_state]" - if(!bodytype.eye_icon) + last_eye_cache_key = "[type]-[icon_bodytype.eye_icon]-[last_cached_eye_colour]-[icon_bodytype.eye_offset]-[eye_state]" + if(!icon_bodytype.eye_icon) return if(!global.eye_icon_cache[last_eye_cache_key]) - var/icon/eyes_icon = icon(icon = bodytype.eye_icon, icon_state = eye_state) - if(bodytype.eye_offset) - eyes_icon.Shift(NORTH, bodytype.eye_offset) - if(bodytype.apply_eye_colour) - eyes_icon.Blend(last_cached_eye_colour, bodytype.eye_blend) + var/icon/eyes_icon = icon(icon = icon_bodytype.eye_icon, icon_state = eye_state) + if(icon_bodytype.eye_offset) + eyes_icon.Shift(NORTH, icon_bodytype.eye_offset) + if(icon_bodytype.apply_eye_colour) + eyes_icon.Blend(last_cached_eye_colour, icon_bodytype.eye_blend) global.eye_icon_cache[last_eye_cache_key] = eyes_icon return global.eye_icon_cache[last_eye_cache_key] diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 8de64cc22727..92bcc2d8ed77 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -22,6 +22,7 @@ var/mob/living/human/owner // Current mob owning the organ. var/decl/species/species // Original species. var/decl/bodytype/bodytype // Original bodytype. + var/decl/bodytype/appearance_bodytype // A bodytype used only for icons, marking validation and equipment offsets. var/list/ailments // Current active ailments if any. var/meat_name // Taken from first owner. @@ -674,3 +675,16 @@ var/global/list/ailment_reference_cache = list() new /obj/effect/decal/cleanable/ash(loc) if(!QDELETED(src)) qdel(src) + +// For overriding on shapeshifters/changelings in the future. +/obj/item/organ/proc/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE) + if(ispath(new_bodytype, /decl/bodytype)) + new_bodytype = GET_DECL(new_bodytype) + if((new_bodytype && !istype(new_bodytype)) || appearance_bodytype == new_bodytype || bodytype == new_bodytype) + return FALSE + appearance_bodytype = new_bodytype + return TRUE + +/obj/item/organ/proc/get_organ_appearance_bodytype() + RETURN_TYPE(/decl/bodytype) + return appearance_bodytype || bodytype diff --git a/nebula.dme b/nebula.dme index c08cae0b5a94..a0c5a4609a25 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2827,6 +2827,7 @@ #include "code\modules\mob\mob_intent.dm" #include "code\modules\mob\mob_layering.dm" #include "code\modules\mob\mob_movement.dm" +#include "code\modules\mob\mob_organs.dm" #include "code\modules\mob\mob_snapshot.dm" #include "code\modules\mob\mob_status.dm" #include "code\modules\mob\mob_temperature.dm" From ee0259599969a82d60d248fc389855b4375dc13f Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 12 Dec 2024 19:46:50 +1100 Subject: [PATCH 0128/1331] Removing old shapeshifter species/bodytype pending rewrite. --- code/modules/species/species_getters.dm | 3 - code/modules/species/species_shapeshifter.dm | 129 ------------------ .../species/species_shapeshifter_bodytypes.dm | 48 ------- mods/species/vox/datum/species_bodytypes.dm | 63 +++++++-- nebula.dme | 2 - 5 files changed, 54 insertions(+), 191 deletions(-) delete mode 100644 code/modules/species/species_shapeshifter.dm delete mode 100644 code/modules/species/species_shapeshifter_bodytypes.dm diff --git a/code/modules/species/species_getters.dm b/code/modules/species/species_getters.dm index f2e3abce8458..4bf80fd91e74 100644 --- a/code/modules/species/species_getters.dm +++ b/code/modules/species/species_getters.dm @@ -1,6 +1,3 @@ -/decl/species/proc/get_valid_shapeshifter_forms(var/mob/living/human/H) - return list() - /decl/species/proc/get_additional_examine_text(var/mob/living/human/H) return diff --git a/code/modules/species/species_shapeshifter.dm b/code/modules/species/species_shapeshifter.dm deleted file mode 100644 index f49e307387ea..000000000000 --- a/code/modules/species/species_shapeshifter.dm +++ /dev/null @@ -1,129 +0,0 @@ -// This is something of an intermediary species used for species that -// need to emulate the appearance of another race. Currently it is only -// used for slimes but it may be useful for other species later. -var/global/list/wrapped_species_by_ref = list() - -/decl/species/shapeshifter - available_bodytypes = list(/decl/bodytype/shapeshifter) - inherent_verbs = list( - /mob/living/human/proc/shapeshifter_select_shape, - /mob/living/human/proc/shapeshifter_select_hair, - /mob/living/human/proc/shapeshifter_select_gender, - /mob/living/human/proc/shapeshifter_select_colour - ) - hidden_from_codex = TRUE - var/list/valid_transform_species = list() - var/monochromatic - var/default_form - -/decl/species/shapeshifter/Initialize() - default_form = global.using_map.default_species - valid_transform_species |= default_form - . = ..() - -/decl/species/shapeshifter/get_valid_shapeshifter_forms(var/mob/living/human/H) - return valid_transform_species - -/decl/species/shapeshifter/get_root_species_name(var/mob/living/human/H) - if(!H) return ..() - var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) - return S.get_root_species_name(H) - -/decl/species/shapeshifter/handle_pre_spawn(var/mob/living/human/H) - ..() - wrapped_species_by_ref["\ref[H]"] = default_form - -/decl/species/shapeshifter/handle_post_spawn(var/mob/living/human/H) - if(monochromatic) - var/skin_colour = H.get_skin_colour() - SET_HAIR_COLOR(H, skin_colour, TRUE) - SET_FACIAL_HAIR_COLOR(H, skin_colour, TRUE) - ..() - -/decl/species/shapeshifter/get_pain_emote(var/mob/living/human/H, var/pain_power) - var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) - return S.get_pain_emote(H, pain_power) - -// Verbs follow. -/mob/living/human/proc/shapeshifter_select_hair() - - set name = "Select Hair" - set category = "Abilities" - - if(stat || is_on_special_ability_cooldown()) - return - - set_special_ability_cooldown(1 SECOND) - - visible_message("\The [src]'s form contorts subtly.") - var/decl/bodytype/root_bodytype = get_bodytype() - var/list/hairstyles = species.get_available_accessory_types(root_bodytype, SAC_HAIR) - if(length(hairstyles)) - var/decl/sprite_accessory/new_hair = input("Select a hairstyle.", "Shapeshifter Hair") as null|anything in hairstyles - SET_HAIR_STYLE(src, (new_hair ? new_hair.type : /decl/sprite_accessory/hair/bald), FALSE) - - var/list/beardstyles = species.get_available_accessory_types(root_bodytype, SAC_FACIAL_HAIR) - if(length(beardstyles)) - var/decl/sprite_accessory/new_hair = input("Select a facial hair style.", "Shapeshifter Hair") as null|anything in beardstyles - SET_FACIAL_HAIR_STYLE(src, (new_hair ? new_hair.type : /decl/sprite_accessory/facial_hair/shaved), FALSE) - -/mob/living/human/proc/shapeshifter_select_gender() - - set name = "Select Gender" - set category = "Abilities" - - if(stat || is_on_special_ability_cooldown()) - return - - set_special_ability_cooldown(5 SECONDS) - - var/new_gender = input("Please select a gender.", "Shapeshifter Gender") as null|anything in list(FEMALE, MALE, NEUTER, PLURAL) - if(!new_gender) - return - - visible_message("\The [src]'s form contorts subtly.") - set_gender(new_gender, TRUE) - -/mob/living/human/proc/shapeshifter_select_shape() - - set name = "Select Body Shape" - set category = "Abilities" - - if(stat ||is_on_special_ability_cooldown()) - return - - set_special_ability_cooldown(5 SECONDS) - - var/new_species = input("Please select a species to emulate.", "Shapeshifter Body") as null|anything in species.get_valid_shapeshifter_forms(src) - if(!new_species || !get_species_by_key(new_species) || wrapped_species_by_ref["\ref[src]"] == new_species) - return - - wrapped_species_by_ref["\ref[src]"] = new_species - visible_message("\The [src] shifts and contorts, taking the form of \a ["\improper [new_species]"]!") - try_refresh_visible_overlays() - -/mob/living/human/proc/shapeshifter_select_colour() - - set name = "Select Body Colour" - set category = "Abilities" - - if(stat || is_on_special_ability_cooldown()) - return - - set_special_ability_cooldown(5 SECONDS) - - var/new_skin = input("Please select a new body color.", "Shapeshifter Colour") as color - if(!new_skin) - return - shapeshifter_set_colour(new_skin) - -/mob/living/human/proc/shapeshifter_set_colour(var/new_skin) - set_skin_colour(new_skin, skip_update = TRUE) - var/decl/species/shapeshifter/S = species - if(S.monochromatic) - var/skin_colour = get_skin_colour() - SET_HAIR_COLOR(src, skin_colour, TRUE) - SET_FACIAL_HAIR_COLOR(src, skin_colour, TRUE) - for(var/obj/item/organ/external/E in get_external_organs()) - E.sync_colour_to_human(src) - try_refresh_visible_overlays() diff --git a/code/modules/species/species_shapeshifter_bodytypes.dm b/code/modules/species/species_shapeshifter_bodytypes.dm deleted file mode 100644 index a1ee2815579f..000000000000 --- a/code/modules/species/species_shapeshifter_bodytypes.dm +++ /dev/null @@ -1,48 +0,0 @@ -/decl/bodytype/shapeshifter - name = "protean form" - bodytype_category = BODYTYPE_HUMANOID - uid = "bodytype_shapeshifter" - -/decl/bodytype/shapeshifter/apply_limb_colouration(var/obj/item/organ/external/E, var/icon/applying) - applying.MapColors("#4d4d4d","#969696","#1c1c1c", "#000000") - applying = ..() - applying += rgb(,,,180) // Makes the icon translucent, SO INTUITIVE TY BYOND - return applying - -/decl/bodytype/shapeshifter/check_dismember_type_override(var/disintegrate) - if(disintegrate == DISMEMBER_METHOD_EDGE) - return DISMEMBER_METHOD_BLUNT - return ..() - -/decl/bodytype/shapeshifter/get_base_icon(var/mob/living/human/H, var/get_deform) - if(!H) return ..(null, get_deform) - var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) - return S.default_bodytype.get_base_icon(H, get_deform) - -/decl/bodytype/shapeshifter/get_blood_overlays(var/mob/living/human/H) - if(!H) return ..() - var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) - return S.default_bodytype.get_blood_overlays(H) - -/decl/bodytype/shapeshifter/get_damage_overlays(var/mob/living/human/H) - if(!H) return ..() - var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) - return S.default_bodytype.get_damage_overlays(H) - -/decl/bodytype/shapeshifter/get_husk_icon(var/mob/living/human/H) - if(H) - var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) - if(S) return S.default_bodytype.get_husk_icon(H) - return ..() - -/decl/bodytype/shapeshifter/get_icon_cache_uid(var/mob/H) - . = ..() - if(H) - var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"]) - if(S) return S.default_bodytype.get_icon_cache_uid(H) - -/decl/bodytype/shapeshifter/apply_bodytype_organ_modifications(obj/item/organ/org) - ..() - var/obj/item/organ/external/E = org - if(istype(E) && E.owner) - E.sync_colour_to_human(E.owner) \ No newline at end of file diff --git a/mods/species/vox/datum/species_bodytypes.dm b/mods/species/vox/datum/species_bodytypes.dm index ee3c6ba7f399..cff74b6141d9 100644 --- a/mods/species/vox/datum/species_bodytypes.dm +++ b/mods/species/vox/datum/species_bodytypes.dm @@ -62,15 +62,60 @@ /decl/bodytype/vox/Initialize() if(!length(_equip_adjust)) _equip_adjust = list( - (BP_L_HAND) = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list( 0, -2)), - (BP_R_HAND) = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list( 0, -2)), - (slot_head_str) = list("[NORTH]" = list(0, -2), "[EAST]" = list(3, -2), "[SOUTH]" = list( 0, -2), "[WEST]" = list(-3, -2)), - (slot_wear_mask_str) = list("[NORTH]" = list(0, 0), "[EAST]" = list(4, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list(-4, 0)), - (slot_wear_suit_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - (slot_w_uniform_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - (slot_underpants_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - (slot_undershirt_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1), "[WEST]" = list( 0, -1)), - (slot_back_str) = list("[NORTH]" = list(0, 0), "[EAST]" = list(3, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list(-3, 0)) + (BP_L_HAND) = list( + "[NORTH]" = list(0, -2), + "[EAST]" = list(0, -2), + "[SOUTH]" = list( 0, -2), + "[WEST]" = list( 0, -2) + ), + (BP_R_HAND) = list( + "[NORTH]" = list(0, -2), + "[EAST]" = list(0, -2), + "[SOUTH]" = list( 0, -2), + "[WEST]" = list( 0, -2) + ), + (slot_head_str) = list( + "[NORTH]" = list(0, -2), + "[EAST]" = list(3, -2), + "[SOUTH]" = list( 0, -2), + "[WEST]" = list(-3, -2) + ), + (slot_wear_mask_str) = list( + "[NORTH]" = list(0, 0), + "[EAST]" = list(4, 0), + "[SOUTH]" = list( 0, 0), + "[WEST]" = list(-4, 0) + ), + (slot_wear_suit_str) = list( + "[NORTH]" = list(0, -1), + "[EAST]" = list(0, -1), + "[SOUTH]" = list( 0, -1), + "[WEST]" = list( 0, -1) + ), + (slot_w_uniform_str) = list( + "[NORTH]" = list(0, -1), + "[EAST]" = list(0, -1), + "[SOUTH]" = list( 0, -1), + "[WEST]" = list( 0, -1) + ), + (slot_underpants_str) = list( + "[NORTH]" = list(0, -1), + "[EAST]" = list(0, -1), + "[SOUTH]" = list( 0, -1), + "[WEST]" = list( 0, -1) + ), + (slot_undershirt_str) = list( + "[NORTH]" = list(0, -1), + "[EAST]" = list(0, -1), + "[SOUTH]" = list( 0, -1), + "[WEST]" = list( 0, -1) + ), + (slot_back_str) = list( + "[NORTH]" = list(0, 0), + "[EAST]" = list(3, 0), + "[SOUTH]" = list( 0, 0), + "[WEST]" = list(-3, 0) + ) ) return ..() diff --git a/nebula.dme b/nebula.dme index a0c5a4609a25..f7c82a45cffb 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3771,8 +3771,6 @@ #include "code\modules\species\species_getters.dm" #include "code\modules\species\species_helpers.dm" #include "code\modules\species\species_hud.dm" -#include "code\modules\species\species_shapeshifter.dm" -#include "code\modules\species\species_shapeshifter_bodytypes.dm" #include "code\modules\species\outsider\random.dm" #include "code\modules\species\station\golem.dm" #include "code\modules\species\station\human.dm" From 07fabaa68293217a8f46130b2c63cf624a99004d Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 21 Dec 2024 11:08:30 +1100 Subject: [PATCH 0129/1331] Ported chicken nuggets from Polaris. --- code/datums/supplypacks/galley.dm | 6 ++ .../objects/items/weapons/storage/nuggets.dm | 63 ++++++++++++++++++ code/modules/food/nuggets.dm | 24 +++++++ icons/obj/food/nuggets/nugget.dmi | Bin 0 -> 313 bytes icons/obj/food/nuggets/nugget_corgi.dmi | Bin 0 -> 318 bytes icons/obj/food/nuggets/nugget_lizard.dmi | Bin 0 -> 319 bytes icons/obj/food/nuggets/nugget_star.dmi | Bin 0 -> 317 bytes icons/obj/items/storage/nugget_box.dmi | Bin 0 -> 818 bytes nebula.dme | 2 + 9 files changed, 95 insertions(+) create mode 100644 code/game/objects/items/weapons/storage/nuggets.dm create mode 100644 code/modules/food/nuggets.dm create mode 100644 icons/obj/food/nuggets/nugget.dmi create mode 100644 icons/obj/food/nuggets/nugget_corgi.dmi create mode 100644 icons/obj/food/nuggets/nugget_lizard.dmi create mode 100644 icons/obj/food/nuggets/nugget_star.dmi create mode 100644 icons/obj/items/storage/nugget_box.dmi diff --git a/code/datums/supplypacks/galley.dm b/code/datums/supplypacks/galley.dm index 5eb7cc9f071e..45c2c05ea161 100644 --- a/code/datums/supplypacks/galley.dm +++ b/code/datums/supplypacks/galley.dm @@ -72,6 +72,12 @@ containername = "pizza crate" supply_method = /decl/supply_method/randomized +/decl/hierarchy/supply_pack/galley/nuggets + name = "Emergency - Nugget crate" + contains = list(/obj/item/box/nuggets = 2) + containertype = /obj/structure/closet/crate/freezer + containername = "nugget crate" + /decl/hierarchy/supply_pack/galley/rations num_contained = 6 name = "Emergency - MREs" diff --git a/code/game/objects/items/weapons/storage/nuggets.dm b/code/game/objects/items/weapons/storage/nuggets.dm new file mode 100644 index 000000000000..3ecc72ce2270 --- /dev/null +++ b/code/game/objects/items/weapons/storage/nuggets.dm @@ -0,0 +1,63 @@ +/datum/storage/box/nuggets + can_hold = list(/obj/item/food/nugget) + var/expected_nugget_count = 10 + +/datum/storage/box/nuggets/New() + max_storage_space = /obj/item/food/nugget::w_class * expected_nugget_count + ..() + +/datum/storage/box/nuggets/twenty + expected_nugget_count = 20 + +/datum/storage/box/nuggets/forty + expected_nugget_count = 40 + +/obj/item/box/nuggets + name = "10-piece nuggets box" + icon = 'icons/obj/items/storage/nugget_box.dmi' + icon_state = "nuggetbox_ten" + desc = "A share pack of golden chicken nuggets in various fun shapes. Rumours of the rare and deadly 'fifth nugget shape' remain unsubstantiated." + storage = /datum/storage/box/nuggets + center_of_mass = @'{"x":16,"y":9}' + +/obj/item/box/nuggets/Initialize(ml, material_key) + . = ..() + update_icon() + +/obj/item/box/nuggets/WillContain() + . = list() + if(istype(storage, /datum/storage/box/nuggets)) + var/datum/storage/box/nuggets/nugget_box = storage + for(var/i = 1 to nugget_box.expected_nugget_count) + . += /obj/item/food/nugget + +/obj/item/box/nuggets/on_update_icon() + var/datum/storage/box/nuggets/nugget_box = storage + if(length(contents) == 0 || !istype(nugget_box)) + icon_state = "[initial(icon_state)]_empty" + else if(length(contents) >= nugget_box.expected_nugget_count) + icon_state = "[initial(icon_state)]_full" + else + icon_state = initial(icon_state) + +// Subtypes below. +/obj/item/box/nuggets/twenty + name = "20-piece nuggets box" + icon_state = "nuggetbox_twenty" + storage = /datum/storage/box/nuggets/twenty + +/obj/item/box/nuggets/twenty/WillContain() + . = list() + for(var/i = 1 to 20) + . += /obj/item/food/nugget + +/obj/item/box/nuggets/twenty/empty/WillContain() + return + +/obj/item/box/nuggets/forty + name = "40-piece nuggets box" + icon_state = "nuggetbox_forty" + storage = /datum/storage/box/nuggets/forty + +/obj/item/box/nuggets/forty/empty/WillContain() + return diff --git a/code/modules/food/nuggets.dm b/code/modules/food/nuggets.dm new file mode 100644 index 000000000000..c2171198fa13 --- /dev/null +++ b/code/modules/food/nuggets.dm @@ -0,0 +1,24 @@ +/obj/item/food/nugget + name = "chicken nugget" + icon = 'icons/obj/food/nuggets/nugget.dmi' + icon_state = ICON_STATE_WORLD + nutriment_desc = "mild battered chicken" + nutriment_amt = 6 + nutriment_type = /decl/material/solid/organic/meat/chicken + material = /decl/material/solid/organic/meat/chicken + bitesize = 3 + var/shape = null + var/static/list/nugget_icons = list( + "lump" = 'icons/obj/food/nuggets/nugget.dmi', + "star" = 'icons/obj/food/nuggets/nugget_star.dmi', + "lizard" = 'icons/obj/food/nuggets/nugget_lizard.dmi', + "corgi" = 'icons/obj/food/nuggets/nugget_corgi.dmi' + ) + +/obj/item/food/nugget/Initialize() + . = ..() + if(isnull(shape)) + shape = pick(nugget_icons) + set_icon(nugget_icons[shape]) + desc = "A chicken nugget vaguely shaped like a [shape]." + add_allergen_flags(ALLERGEN_GLUTEN) // flour diff --git a/icons/obj/food/nuggets/nugget.dmi b/icons/obj/food/nuggets/nugget.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6022a38314e4f5b4f265c566cd2c8a7bb453ad5e GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvi2$Dv*8>L*JZNKhIfLQlWTlVu z7%o>bJf5v|FpnYL=-ynQ8pe_!zhDN3XE)M-oXo0_h!U67;^d;tf|AVqJO+k}Il*BC zMWx@r1Q&e#`b5iHSL@uF^T8WJ4K5l#c%*aQNAn~@QBUs<3*#W;%f?;`a~@4PGATqM zc%{vL4RzPfZe!KWDsKY8S%?RCbtb;DHAn7=UxjxjMmLC@hd@G@Nnucss0)Y0&K(x4S}T=U4#muAsG7qi zkhX-Ox1i9O!(hX7ZY|BuMHWi}0t`g1X1;mK%Hu4L*JZNKhIfLQjJcfgL z3@;}uoy%eYN-{8(1o;IsI6S+N2IOQ`g+!FNq!uR^WfqiV=I1dmRLltuD<~@c{w28J zc*Y}^>MK*4FYfX9&=91B|l0}O5i+>E%$ zsP{-ige#;;MPbE~B_cU3i=12o0yq=dE(k1GqOd7CreVg>#zYTBhV8q!?T=hP>mdKI;Vst045H0+yDRo literal 0 HcmV?d00001 diff --git a/icons/obj/food/nuggets/nugget_lizard.dmi b/icons/obj/food/nuggets/nugget_lizard.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7eb06bd525ed141241b0779e7e7facefd7e9775f GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*JZNKhIfLQjJcfgL z3`MQJ+ksMyB|(0{3=Yq3qyagZRUr{2E~&-IMVSR9nfZAP3>9;N!wQN@zkdlX`1tjS zmbb3fxija3H-s8oG=A_%=e&>RNrs}H-W?XkLB^Mjy%gp=nsj7Rh(hp6oBbNL*T&`qz(8h2ukKyHH zrI#}pKF(vflo4tSRKQpgeukTAW;zSx}OhpU1#ZF(){zps4iw zm*9erU!Q1s>uQ}lb3S-OsKG_!2aj~l`)HnIDC+6mVPPC(eA(DbVa}sTM<#_R1h2H& zuc7YR*=?-4d4wXA0N{oI+S=Ox|NpCE0EtcjemH(+GiEcH0RPMYmUGIX00001 zbW%=J06^y0W&i*Hn|f4ObVOxyV{&P5bZKvH004NLQ&wU+WT!!Nk?Oj%{36oTlj=5z zCcI8lR&e!m0Y^Uow*R9KN8+mg0006BNklu!TE425x%KnT#i|J%;2>*m`Q zWk!`I`OxyCCw3q}`<+fxC=_~`KH%IK2kggszvJtUOJFtd`BOfQ_3aH`xHB6#eLMnsljO_&367)Uc6?sN z%YFHnT<(WLp-?FF5=rhG{Z;DM3;G@=_gU)@xA_(_x;y3_WC?OSPh&$&kt4ur_b|)ahrR6ksox1 z@cPz+eyJY{g+ihKAPf6OzR~;j27C|kd%xab;~{?U)|&$c_`O@-TSc#rN6^jfy%FNN{9-Jkeoe>#Ck2e>uo1dQLIiFB|m8|81aKlQEt zlpR$%5CH*cT#5U}LqI1`bPpWxy}qV||U>Hf+0yn)l#bgz!k=f7Zr w9j}tOP&A@=eIuXnh~o8s_%6K84~2Ys2i96Vq}~T{D*ylh07*qoM6N<$g38g1nE(I) literal 0 HcmV?d00001 diff --git a/nebula.dme b/nebula.dme index 1c258d2fd94e..611d89830580 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1369,6 +1369,7 @@ #include "code\game\objects\items\weapons\storage\med_pouch.dm" #include "code\game\objects\items\weapons\storage\misc.dm" #include "code\game\objects\items\weapons\storage\mre.dm" +#include "code\game\objects\items\weapons\storage\nuggets.dm" #include "code\game\objects\items\weapons\storage\parachute.dm" #include "code\game\objects\items\weapons\storage\picnic_basket.dm" #include "code\game\objects\items\weapons\storage\secure.dm" @@ -2439,6 +2440,7 @@ #include "code\modules\fluids\fluid_flood.dm" #include "code\modules\fluids\fluid_mapped.dm" #include "code\modules\food\assembled.dm" +#include "code\modules\food\nuggets.dm" #include "code\modules\food\cooking\_recipe.dm" #include "code\modules\food\cooking\cooking_vessels\_cooking_vessel.dm" #include "code\modules\food\cooking\cooking_vessels\baking_dish.dm" From 5d7c35fa699e9c0fc61359546188eea80d491fb9 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Fri, 27 Dec 2024 11:53:33 +1100 Subject: [PATCH 0130/1331] Reworking oil subtypes and lantern fuel. --- code/_helpers/emissive.dm | 3 +- .../game/objects/structures/barrels/barrel.dm | 2 +- code/game/objects/structures/barrels/cask.dm | 2 +- .../butchery/butchery_products_meat_fish.dm | 2 +- .../hydroponics/plant_types/seeds_misc.dm | 14 ++-- code/modules/hydroponics/seed_packets.dm | 2 +- .../living/simple_animal/_simple_animal.dm | 9 ++- .../simple_animal/aquatic/aquatic_fish.dm | 22 +++--- .../aquatic/aquatic_fish_lantern.dm | 26 +++++++ code/modules/mob_holder/_holder.dm | 7 ++ code/modules/organs/internal/_internal.dm | 2 +- code/modules/organs/internal/brain.dm | 4 +- .../modules/reagents/chems/chems_nutriment.dm | 13 ---- code/modules/reagents/chems/chems_oil.dm | 66 ++++++++++++------ .../reagents/reactions/reaction_alcohol.dm | 2 +- .../reagents/reactions/reaction_compounds.dm | 24 +++++++ .../reagents/reactions/reaction_recipe.dm | 2 +- .../reactions/reaction_recipe_food.dm | 2 +- .../reagents/reactions/reaction_synthesis.dm | 16 +++-- .../condiments/condiment_appearance.dm | 2 +- .../real_instruments/Violin/violin.dm | 2 +- icons/mob/simple_animal/fish_cave.dmi | Bin 0 -> 4028 bytes icons/mob/simple_animal/fish_lantern.dmi | Bin 0 -> 5654 bytes maps/shaded_hills/areas/grassland.dm | 4 +- maps/shaded_hills/shaded_hills_map.dm | 2 +- nebula.dme | 1 + 26 files changed, 156 insertions(+), 75 deletions(-) create mode 100644 code/modules/mob/living/simple_animal/aquatic/aquatic_fish_lantern.dm create mode 100644 icons/mob/simple_animal/fish_cave.dmi create mode 100644 icons/mob/simple_animal/fish_lantern.dmi diff --git a/code/_helpers/emissive.dm b/code/_helpers/emissive.dm index 4ea0ca7e7cf3..98cde2aadb31 100644 --- a/code/_helpers/emissive.dm +++ b/code/_helpers/emissive.dm @@ -1,5 +1,6 @@ -/proc/emissive_overlay(var/icon, var/icon_state, var/loc, var/dir, var/color) +/proc/emissive_overlay(icon, icon_state, loc, dir, color, flags) var/image/I = image(icon, loc, icon_state, EMISSIVE_LAYER, dir) I.plane = EMISSIVE_PLANE I.color = color + I.appearance_flags |= flags return I diff --git a/code/game/objects/structures/barrels/barrel.dm b/code/game/objects/structures/barrels/barrel.dm index 4b7f0d2e87c4..b28986d55923 100644 --- a/code/game/objects/structures/barrels/barrel.dm +++ b/code/game/objects/structures/barrels/barrel.dm @@ -110,4 +110,4 @@ /obj/structure/reagent_dispensers/barrel/ebony/oil/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/nutriment/oil/plant, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/oil, reagents.maximum_volume) diff --git a/code/game/objects/structures/barrels/cask.dm b/code/game/objects/structures/barrels/cask.dm index 8a704ad55cf7..d3755dfe55e6 100644 --- a/code/game/objects/structures/barrels/cask.dm +++ b/code/game/objects/structures/barrels/cask.dm @@ -39,4 +39,4 @@ /obj/structure/reagent_dispensers/barrel/cask/ebony/oil/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/nutriment/oil/plant, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/oil, reagents.maximum_volume) diff --git a/code/modules/butchery/butchery_products_meat_fish.dm b/code/modules/butchery/butchery_products_meat_fish.dm index 8361c4af74cf..2ea9678087d0 100644 --- a/code/modules/butchery/butchery_products_meat_fish.dm +++ b/code/modules/butchery/butchery_products_meat_fish.dm @@ -16,7 +16,7 @@ slice_num = 3 butchery_data = /decl/butchery_data/animal/fish allergen_flags = ALLERGEN_FISH - var/oil_type = /decl/material/liquid/nutriment/oil/fish + var/oil_type = /decl/material/liquid/oil/fish var/oil_amount = 2 /obj/item/food/butchery/meat/fish/oily diff --git a/code/modules/hydroponics/plant_types/seeds_misc.dm b/code/modules/hydroponics/plant_types/seeds_misc.dm index db5d4739d6d9..e98328e4b399 100644 --- a/code/modules/hydroponics/plant_types/seeds_misc.dm +++ b/code/modules/hydroponics/plant_types/seeds_misc.dm @@ -4,7 +4,7 @@ display_name = "cotton patch" product_material = /decl/material/solid/organic/plantmatter/pith/husk chems = list( - /decl/material/liquid/nutriment/oil/plant = list(3,10), + /decl/material/liquid/oil/plant = list(3,10), /decl/material/solid/organic/cloth = list(10,1) ) slice_product = null @@ -28,7 +28,7 @@ display_name = "flax patch" // Do we want linseed oil at some point? chems = list( - /decl/material/liquid/nutriment/oil/plant = list(5,12), + /decl/material/liquid/oil/plant = list(5,12), /decl/material/solid/organic/cloth/linen = list(8,1) ) @@ -400,7 +400,7 @@ display_name = "hemp patch" mutants = null chems = list( - /decl/material/liquid/nutriment/oil/plant = list(3,10), + /decl/material/liquid/oil/plant = list(3,10), /decl/material/solid/organic/cloth/hemp = list(8,1), /decl/material/liquid/nutriment = list(1) ) @@ -725,7 +725,7 @@ product_name = "sunflower" display_name = "sunflower patch" chems = list( - /decl/material/liquid/nutriment/oil/plant = list(10,10) + /decl/material/liquid/oil/plant = list(10,10) ) /datum/seed/flower/sunflower/New() @@ -800,7 +800,7 @@ display_name = "peanut vine" chems = list( /decl/material/liquid/nutriment = list(1,10), - /decl/material/liquid/nutriment/oil/plant = list(1,10) + /decl/material/liquid/oil/plant = list(1,10) ) slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -890,7 +890,7 @@ name = "corn" product_name = "corn" display_name = "ears of corn" - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/nutriment/cornoil = list(1,10)) + chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/oil/plant/corn = list(1,10)) grown_tag = "corn" trash_type = /obj/item/corncob backyard_grilling_product = /obj/item/food/popcorn @@ -987,7 +987,7 @@ display_name = "soybean patch" chems = list( /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/nutriment/oil/plant = list(3,20), + /decl/material/liquid/oil/plant = list(3,20), /decl/material/liquid/drink/milk/soymilk = list(7,20) ) grown_tag = "soybeans" diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index d9033c86f84a..21efe8deead6 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -26,7 +26,7 @@ /obj/item/seeds/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/nutriment/oil/plant, 3) + add_to_reagents(/decl/material/liquid/oil/plant, 3) /obj/item/seeds/get_single_monetary_worth() . = seed ? seed.get_monetary_value() : ..() diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index 2e1b6d8e3b46..77c479915f82 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -164,16 +164,23 @@ var/global/list/simplemob_icon_bitflag_cache = list() mob_icon_state_flags |= MOB_ICON_HAS_PARALYZED_STATE global.simplemob_icon_bitflag_cache[type] = mob_icon_state_flags +/mob/living/simple_animal/proc/add_additional_visible_overlays(list/accumulator) + return + /mob/living/simple_animal/refresh_visible_overlays() + var/list/add_overlays = list() if(length(draw_visible_overlays)) - var/list/add_overlays = list() for(var/overlay_state in draw_visible_overlays) var/overlay_color = draw_visible_overlays[overlay_state] if(overlay_state == "base") add_overlays += overlay_image(icon, icon_state, overlay_color, RESET_COLOR) else add_overlays += overlay_image(icon, "[icon_state]-[overlay_state]", overlay_color, RESET_COLOR) + + add_additional_visible_overlays(add_overlays) + + if(length(add_overlays)) set_current_mob_overlay(HO_SKIN_LAYER, add_overlays) else set_current_mob_overlay(HO_SKIN_LAYER, null) diff --git a/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm b/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm index 7de69b839449..bbd6b916d820 100644 --- a/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm +++ b/code/modules/mob/living/simple_animal/aquatic/aquatic_fish.dm @@ -16,26 +16,20 @@ name = "small fish" icon = 'icons/mob/simple_animal/fish_grump.dmi' -/mob/living/simple_animal/aquatic/fish/lantern - name = "lantern-fish" - desc = "An oily, glowing fish sometimes caught in cave rivers, rumoured to have cousins in the deep ocean." - // TODO: icon = 'icons/mob/simple_animal/fish_lantern.dmi' - butchery_data = /decl/butchery_data/animal/fish/oily - -/mob/living/simple_animal/aquatic/fish/cave - name = "blind cave fish" - desc = "A pale, blobby fish that lives its entire life in the cold darkness of cave rivers, and hence has no need for eyes." - // TODO: icon = 'icons/mob/simple_animal/fish_cave.dmi' - -/mob/living/simple_animal/aquatic/fish/cave/is_blind() - return TRUE - /mob/living/simple_animal/aquatic/fish/large name = "smallmouth bass" icon = 'icons/mob/simple_animal/fish_judge.dmi' butchery_data = /decl/butchery_data/animal/fish/medium mob_size = MOB_SIZE_SMALL +/mob/living/simple_animal/aquatic/fish/large/cave + name = "blind cave fish" + desc = "A pale, blobby fish that lives its entire life in the cold darkness of cave rivers, and hence has no need for eyes." + icon = 'icons/mob/simple_animal/fish_cave.dmi' + +/mob/living/simple_animal/aquatic/fish/large/cave/is_blind() + return TRUE + /mob/living/simple_animal/aquatic/fish/large/bass name = "largemouth bass" icon = 'icons/mob/simple_animal/fish_bass.dmi' diff --git a/code/modules/mob/living/simple_animal/aquatic/aquatic_fish_lantern.dm b/code/modules/mob/living/simple_animal/aquatic/aquatic_fish_lantern.dm new file mode 100644 index 000000000000..179605c12c9d --- /dev/null +++ b/code/modules/mob/living/simple_animal/aquatic/aquatic_fish_lantern.dm @@ -0,0 +1,26 @@ +/mob/living/simple_animal/aquatic/fish/large/lantern + name = "lantern fish" + desc = "An oily, glowing fish. They are sometimes caught in cave rivers, and are rumoured to have cousins in the deep ocean." + icon = 'icons/mob/simple_animal/fish_lantern.dmi' + butchery_data = /decl/butchery_data/animal/fish/oily + holder_type = /obj/item/holder/lanternfish + var/glow_color = COLOR_LIME + var/glow_power = 0.5 + var/glow_range = 2 + +/mob/living/simple_animal/aquatic/fish/large/lantern/Initialize() + . = ..() + set_light(glow_range, glow_power, glow_color) + refresh_visible_overlays() + +/mob/living/simple_animal/aquatic/fish/large/lantern/add_additional_visible_overlays(list/accumulator) + var/glow_state = "[icon_state]-glow" + if(check_state_in_icon(glow_state, icon)) + accumulator += emissive_overlay(icon, glow_state, color = light_color, flags = RESET_COLOR) + +/obj/item/holder/lanternfish/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing) + if(overlay && (slot in global.all_hand_slots)) + var/glow_state = "[overlay.icon_state]-glow" + if(check_state_in_icon(glow_state, overlay.icon)) + overlay.overlays += emissive_overlay(overlay.icon, glow_state, color = light_color, flags = RESET_COLOR) + return ..() diff --git a/code/modules/mob_holder/_holder.dm b/code/modules/mob_holder/_holder.dm index ec0dcd39482f..002814c0c09a 100644 --- a/code/modules/mob_holder/_holder.dm +++ b/code/modules/mob_holder/_holder.dm @@ -132,8 +132,15 @@ return ..() /obj/item/holder/proc/sync(var/mob/living/M) + SetName(M.name) desc = M.desc + + if(QDELETED(src) || QDELETED(M) || !istype(M)) + set_light(0) + else + set_light(M.light_range, M.light_power, M.light_color) + var/mob/living/human/H = loc if(istype(H)) last_holder = H diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index e8e076a057c6..a62006b938d3 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -171,7 +171,7 @@ // We clamp/round here so that we don't accidentally heal past the threshold and // cheat our way into a full second threshold of healing. - damage = clamp(damage - max(0, get_organ_heal_amount() * GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN)), min_heal_val, absolute_max_damage) + damage = clamp(damage - max(0, get_organ_heal_amount()), min_heal_val, absolute_max_damage) // If we're within 1 damage of the nearest threshold (such as 0), round us down. // This should be removed when float-aware modulo comes in in 515, but for now is needed diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index a26e424aba86..c4980c3a3034 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -105,7 +105,9 @@ return (!owner || GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN) <= 0) && ..() /obj/item/organ/internal/brain/get_organ_heal_amount() - return 1 + if(!has_limited_healing()) + . = 1 // We have full healing, so we always heal at least 1 unit of damage. + . += (owner ? GET_CHEMICAL_EFFECT(owner, CE_BRAIN_REGEN) : 0) /obj/item/organ/internal/brain/Process() if(owner) diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index 3b5567d3f362..d96d2b840d70 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -296,19 +296,6 @@ boiling_point = 373 allergen_flags = ALLERGEN_FRUIT -/decl/material/liquid/nutriment/cornoil - name = "corn oil" - lore_text = "An oil derived from various types of corn." - taste_description = "slime" - taste_mult = 0.1 - nutriment_factor = 20 - color = "#302000" - slipperiness = 8 - uid = "chem_nutriment_cornoil" - melting_point = 273 - boiling_point = 373 - allergen_flags = ALLERGEN_VEGETABLE - /decl/material/liquid/nutriment/sprinkles name = "sprinkles" lore_text = "Multi-colored little bits of sugar, commonly found on donuts. Loved by cops." diff --git a/code/modules/reagents/chems/chems_oil.dm b/code/modules/reagents/chems/chems_oil.dm index 5395daacc11c..ad359dd61ca4 100644 --- a/code/modules/reagents/chems/chems_oil.dm +++ b/code/modules/reagents/chems/chems_oil.dm @@ -1,28 +1,52 @@ -/decl/material/liquid/nutriment/oil - abstract_type = /decl/material/liquid/nutriment/oil - burn_product = /decl/material/gas/carbon_monoxide - ignition_point = T0C+150 - accelerant_value = FUEL_VALUE_ACCELERANT - gas_flags = XGM_GAS_FUEL - melting_point = 273 - boiling_point = 373 +/decl/material/liquid/oil + name = "fuel oil" // paraffin etc + lore_text = "Clarified fuel oil, perfect for fuelling a lantern." + burn_product = /decl/material/gas/carbon_monoxide + ignition_point = T0C+150 + accelerant_value = FUEL_VALUE_ACCELERANT + gas_flags = XGM_GAS_FUEL + melting_point = 273 + boiling_point = 373 + uid = "chem_oil_lamp" + color = "#664330" + value = 1.5 + fishing_bait_value = 0 + taste_mult = 4 + metabolism = REM * 4 + exoplanet_rarity_gas = MAT_RARITY_NOWHERE + affect_blood_on_ingest = 0 + affect_blood_on_inhale = 0 + slipperiness = 8 -/decl/material/liquid/nutriment/oil/plant - name = "plant oil" - lore_text = "A thin yellow oil pressed from vegetables or nuts. Useful as fuel, or in cooking." - uid = "chem_nutriment_plant_oil" - taste_description = "oily blandness" - allergen_flags = ALLERGEN_VEGETABLE +/decl/material/liquid/oil/plant + name = "plant oil" + lore_text = "A thin yellow oil pressed from vegetables or nuts. Useful as fuel, or in cooking." + uid = "chem_oil_plant" + taste_description = "oily blandness" + allergen_flags = ALLERGEN_VEGETABLE + compost_value = 1 + nutriment_factor = 8 -/decl/material/liquid/nutriment/oil/fish - name = "fish oil" - lore_text = "A pungent yellow oil pressed from fish meat and fish skin. Useful as fuel, or in cooking, or for encouraging recovery after brain injuries." - uid = "chem_nutriment_fish_oil" - taste_description = "pungent, oily fish" - allergen_flags = ALLERGEN_FISH +/decl/material/liquid/oil/plant/corn + name = "corn oil" + lore_text = "An oil derived from various types of corn." + taste_description = "slime" + nutriment_factor = 20 + color = "#302000" + uid = "chem_oil_corn" + taste_mult = 0.1 + +/decl/material/liquid/oil/fish + name = "fish oil" + lore_text = "A pungent yellow oil pressed from fish meat and fish skin. Useful as fuel, or in cooking, or for encouraging recovery after brain injuries." + uid = "chem_oil_fish" + taste_description = "pungent, oily fish" + allergen_flags = ALLERGEN_FISH + compost_value = 1 + nutriment_factor = 6 // Copied from neuroannealer; yes, it's silly, but we need a way to treat brain damage on the medieval map. // Should possibly be an ingredient rather than the be-all end-all medication. -/decl/material/liquid/nutriment/oil/fish/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) +/decl/material/liquid/oil/fish/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() M.add_chemical_effect(CE_BRAIN_REGEN, 0.5) // Half as effective as neuroannealer, without the side-effects. diff --git a/code/modules/reagents/reactions/reaction_alcohol.dm b/code/modules/reagents/reactions/reaction_alcohol.dm index 121c4a9bf2f3..4d96bdb5d128 100644 --- a/code/modules/reagents/reactions/reaction_alcohol.dm +++ b/code/modules/reagents/reactions/reaction_alcohol.dm @@ -62,7 +62,7 @@ /decl/chemical_reaction/recipe/beer name = "Plain Beer" result = /decl/material/liquid/alcohol/beer - required_reagents = list(/decl/material/liquid/nutriment/cornoil = 10) + required_reagents = list(/decl/material/liquid/oil/plant/corn = 10) catalysts = list(/decl/material/liquid/enzyme = 5) result_amount = 10 mix_message = "The solution roils as it rapidly ferments into a foaming amber liquid." diff --git a/code/modules/reagents/reactions/reaction_compounds.dm b/code/modules/reagents/reactions/reaction_compounds.dm index ea0b51142156..8e530dacaa58 100644 --- a/code/modules/reagents/reactions/reaction_compounds.dm +++ b/code/modules/reagents/reactions/reaction_compounds.dm @@ -158,3 +158,27 @@ /decl/chemical_reaction/compound/condensed_capsaicin/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) . = ..() holder?.add_reagent(/decl/material/liquid/water, created_volume) + +// This is a bit silly, but we need a way to unify oil types until someone rewrites lanterns. +/decl/chemical_reaction/compound/fuel_oil + name = "Plant Fuel Oil" + result = /decl/material/liquid/oil + result_amount = 3 + required_reagents = list( + /decl/material/liquid/oil/plant = 2, + /decl/material/solid/graphite = 1 + ) + +/decl/chemical_reaction/compound/fuel_oil/corn + name = "Corn Fuel Oil" + required_reagents = list( + /decl/material/liquid/oil/plant/corn = 2, + /decl/material/solid/graphite = 1 + ) + +/decl/chemical_reaction/compound/fuel_oil/fish + name = "Fish Fuel Oil" + required_reagents = list( + /decl/material/liquid/oil/fish = 2, + /decl/material/solid/graphite = 1 + ) diff --git a/code/modules/reagents/reactions/reaction_recipe.dm b/code/modules/reagents/reactions/reaction_recipe.dm index bcd93c0e8af8..a113d821cc58 100644 --- a/code/modules/reagents/reactions/reaction_recipe.dm +++ b/code/modules/reagents/reactions/reaction_recipe.dm @@ -37,7 +37,7 @@ /decl/chemical_reaction/recipe/garlicsauce name = "Garlic Sauce" result = /decl/material/liquid/nutriment/garlicsauce - required_reagents = list(/decl/material/liquid/drink/juice/garlic = 1, /decl/material/liquid/nutriment/cornoil = 1) + required_reagents = list(/decl/material/liquid/drink/juice/garlic = 1, /decl/material/liquid/oil/plant/corn = 1) result_amount = 2 mix_message = "The solution thickens into a creamy white oil." diff --git a/code/modules/reagents/reactions/reaction_recipe_food.dm b/code/modules/reagents/reactions/reaction_recipe_food.dm index ef99755711d6..bc052960e913 100644 --- a/code/modules/reagents/reactions/reaction_recipe_food.dm +++ b/code/modules/reagents/reactions/reaction_recipe_food.dm @@ -58,7 +58,7 @@ name = "Enzyme Margarine" required_reagents = list( /decl/material/solid/sodiumchloride = 1, - /decl/material/liquid/nutriment/oil/plant = 20 + /decl/material/liquid/oil/plant = 20 ) catalysts = list(/decl/material/liquid/enzyme = 5) mix_message = "The solution thickens and curdles into a pale yellow solid." diff --git a/code/modules/reagents/reactions/reaction_synthesis.dm b/code/modules/reagents/reactions/reaction_synthesis.dm index d12783c8f7ad..5c4f5b710f4c 100644 --- a/code/modules/reagents/reactions/reaction_synthesis.dm +++ b/code/modules/reagents/reactions/reaction_synthesis.dm @@ -126,11 +126,11 @@ new /obj/item/stack/medical/resin/crafted(T, create_stacks) /decl/chemical_reaction/synthesis/soap - name = "Handmade Soap" + name = "Handmade Plant Soap" required_reagents = list( - /decl/material/solid/carbon/ashes = 5, - /decl/material/liquid/water = 5, - /decl/material/liquid/nutriment/oil/plant = 10 + /decl/material/solid/carbon/ashes = 5, + /decl/material/liquid/water = 5, + /decl/material/liquid/oil/plant = 10 ) result_amount = 1 mix_message = "The solution thickens and solidifies." @@ -147,6 +147,14 @@ for(var/i = 1 to create_soap) new /obj/item/soap/crafted(T) +/decl/chemical_reaction/synthesis/soap/corn + name = "Handmade Corn Soap" + required_reagents = list( + /decl/material/solid/carbon/ashes = 5, + /decl/material/liquid/water = 5, + /decl/material/liquid/oil/plant/corn = 10 + ) + // Making chipboard out of wood scraps/recycled wood. /decl/chemical_reaction/synthesis/chipboard name = "Oak Chipboard" diff --git a/code/modules/reagents/reagent_containers/condiments/condiment_appearance.dm b/code/modules/reagents/reagent_containers/condiments/condiment_appearance.dm index b534f6bc812c..4236c1867544 100644 --- a/code/modules/reagents/reagent_containers/condiments/condiment_appearance.dm +++ b/code/modules/reagents/reagent_containers/condiments/condiment_appearance.dm @@ -75,7 +75,7 @@ condiment_center_of_mass = @'{"x":16,"y":8}' /decl/condiment_appearance/cornoil - condiment_type = /decl/material/liquid/nutriment/cornoil + condiment_type = /decl/material/liquid/oil/plant/corn condiment_name = "corn oil" condiment_desc = "A delicious oil used in cooking. Made from corn." condiment_icon = 'icons/obj/food/condiments/cornoil.dmi' diff --git a/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm b/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm index f3baab9aa07c..0e08f5bfd164 100644 --- a/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm +++ b/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm @@ -12,4 +12,4 @@ matter = list(/decl/material/solid/metal/steel = MATTER_AMOUNT_TRACE) /obj/structure/synthesized_instrument/synthesizer/shouldStopPlaying(mob/user) - return !(src && in_range(src, user)) \ No newline at end of file + return !(src && in_range(src, user)) diff --git a/icons/mob/simple_animal/fish_cave.dmi b/icons/mob/simple_animal/fish_cave.dmi new file mode 100644 index 0000000000000000000000000000000000000000..40e1f162f86382302d12e99368da340644ca121f GIT binary patch literal 4028 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 a3CU{Ct)kTBgn`#_kcR40NV$q-*uMZdFNsJ1 literal 0 HcmV?d00001 diff --git a/icons/mob/simple_animal/fish_lantern.dmi b/icons/mob/simple_animal/fish_lantern.dmi new file mode 100644 index 0000000000000000000000000000000000000000..eea9923c6d45211bedfbf06a955ac40695f270a2 GIT binary patch literal 5654 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 B+V21W literal 0 HcmV?d00001 diff --git a/maps/shaded_hills/areas/grassland.dm b/maps/shaded_hills/areas/grassland.dm index 3fa264b43926..9b7de6659e1b 100644 --- a/maps/shaded_hills/areas/grassland.dm +++ b/maps/shaded_hills/areas/grassland.dm @@ -54,8 +54,8 @@ sound_env = CAVE area_flags = AREA_FLAG_IS_BACKGROUND fishing_results = list( - /mob/living/simple_animal/aquatic/fish/cave = 13 - /mob/living/simple_animal/aquatic/fish/lantern = 7 + /mob/living/simple_animal/aquatic/fish/large/cave = 13, + /mob/living/simple_animal/aquatic/fish/large/lantern = 7, /obj/item/mollusc = 5, /obj/item/mollusc/barnacle/fished = 5, /obj/item/mollusc/clam/fished/pearl = 3, diff --git a/maps/shaded_hills/shaded_hills_map.dm b/maps/shaded_hills/shaded_hills_map.dm index 0b545c07a61c..09bf988942e0 100644 --- a/maps/shaded_hills/shaded_hills_map.dm +++ b/maps/shaded_hills/shaded_hills_map.dm @@ -1,5 +1,5 @@ /datum/map/shaded_hills - default_liquid_fuel_type = /decl/material/liquid/nutriment/oil/plant + default_liquid_fuel_type = /decl/material/liquid/oil default_species = SPECIES_KOBALOI loadout_categories = list( /decl/loadout_category/fantasy/clothing, diff --git a/nebula.dme b/nebula.dme index dcafae477c0b..d2e41d9119a2 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3024,6 +3024,7 @@ #include "code\modules\mob\living\simple_animal\aquatic\_aquatic_retaliate.dm" #include "code\modules\mob\living\simple_animal\aquatic\aquatic_carp.dm" #include "code\modules\mob\living\simple_animal\aquatic\aquatic_fish.dm" +#include "code\modules\mob\living\simple_animal\aquatic\aquatic_fish_lantern.dm" #include "code\modules\mob\living\simple_animal\aquatic\aquatic_sharks.dm" #include "code\modules\mob\living\simple_animal\crow\crow.dm" #include "code\modules\mob\living\simple_animal\friendly\cat.dm" From 37dcd2ca5760bf9beb9502cb37986c923342d422 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 31 Dec 2024 15:28:57 -0500 Subject: [PATCH 0131/1331] Rename code files with spaces in their name --- .../uplink/{devices and tools.dm => devices_and_tools.dm} | 0 .../ZAS/{Variable Settings.dm => VariableSettings.dm} | 0 .../keycard_authentication.dm | 0 nebula.dme | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename code/datums/uplink/{devices and tools.dm => devices_and_tools.dm} (100%) rename code/modules/ZAS/{Variable Settings.dm => VariableSettings.dm} (100%) rename code/modules/{security levels => security_levels}/keycard_authentication.dm (100%) diff --git a/code/datums/uplink/devices and tools.dm b/code/datums/uplink/devices_and_tools.dm similarity index 100% rename from code/datums/uplink/devices and tools.dm rename to code/datums/uplink/devices_and_tools.dm diff --git a/code/modules/ZAS/Variable Settings.dm b/code/modules/ZAS/VariableSettings.dm similarity index 100% rename from code/modules/ZAS/Variable Settings.dm rename to code/modules/ZAS/VariableSettings.dm diff --git a/code/modules/security levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm similarity index 100% rename from code/modules/security levels/keycard_authentication.dm rename to code/modules/security_levels/keycard_authentication.dm diff --git a/nebula.dme b/nebula.dme index c08cae0b5a94..2c7c6835f1d0 100644 --- a/nebula.dme +++ b/nebula.dme @@ -693,7 +693,7 @@ #include "code\datums\underwear\underwear.dm" #include "code\datums\uplink\ammunition.dm" #include "code\datums\uplink\badassery.dm" -#include "code\datums\uplink\devices and tools.dm" +#include "code\datums\uplink\devices_and_tools.dm" #include "code\datums\uplink\grenades.dm" #include "code\datums\uplink\hardsuit_modules.dm" #include "code\datums\uplink\highly_visible_and_dangerous_weapons.dm" @@ -3738,7 +3738,7 @@ #include "code\modules\sealant_gun\sealant_injector.dm" #include "code\modules\sealant_gun\sealant_rack.dm" #include "code\modules\sealant_gun\sealant_tank.dm" -#include "code\modules\security levels\keycard_authentication.dm" +#include "code\modules\security_levels\keycard_authentication.dm" #include "code\modules\shield_generators\floor_diffuser.dm" #include "code\modules\shield_generators\handheld_diffuser.dm" #include "code\modules\shield_generators\modes.dm" @@ -4002,7 +4002,7 @@ #include "code\modules\ZAS\Diagnostic.dm" #include "code\modules\ZAS\Fire.dm" #include "code\modules\ZAS\Turf.dm" -#include "code\modules\ZAS\Variable Settings.dm" +#include "code\modules\ZAS\VariableSettings.dm" #include "code\modules\ZAS\Zone.dm" #include "code\procs\announce.dm" #include "code\procs\dbcore.dm" From 75945001df749d15895e349117e5269a09f477e6 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 31 Dec 2024 15:31:08 -0500 Subject: [PATCH 0132/1331] Rename asset files with spaces in their path --- code/datums/music_tracks/europa.dm | 2 +- .../{Martian Cowboy.ogg => MartianCowboy.ogg} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename sound/music/europa/{Martian Cowboy.ogg => MartianCowboy.ogg} (100%) diff --git a/code/datums/music_tracks/europa.dm b/code/datums/music_tracks/europa.dm index ccbc948eda00..b1b050118ba0 100644 --- a/code/datums/music_tracks/europa.dm +++ b/code/datums/music_tracks/europa.dm @@ -8,7 +8,7 @@ /decl/music_track/martiancowboy artist = "Kevin MacLeod" title = "Martian Cowboy" - song = 'sound/music/europa/Martian Cowboy.ogg' + song = 'sound/music/europa/MartianCowboy.ogg' license = /decl/license/cc_by_3_0 url = "https://incompetech.com/music/royalty-free/index.html?isrc=usuan1100349" diff --git a/sound/music/europa/Martian Cowboy.ogg b/sound/music/europa/MartianCowboy.ogg similarity index 100% rename from sound/music/europa/Martian Cowboy.ogg rename to sound/music/europa/MartianCowboy.ogg From 570908975268471779955f45a8d8df346af19042 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 31 Dec 2024 15:32:26 -0500 Subject: [PATCH 0133/1331] Move security levels into security levels module folder --- code/datums/security_state.dm | 335 ------------------ .../security_levels/_security_level.dm | 48 +++ .../security_levels/alarm_appearance.dm | 61 ++++ .../security_levels/security_levels.dm | 93 +++++ .../modules/security_levels/security_state.dm | 134 +++++++ nebula.dme | 5 +- 6 files changed, 340 insertions(+), 336 deletions(-) delete mode 100644 code/datums/security_state.dm create mode 100644 code/modules/security_levels/_security_level.dm create mode 100644 code/modules/security_levels/alarm_appearance.dm create mode 100644 code/modules/security_levels/security_levels.dm create mode 100644 code/modules/security_levels/security_state.dm diff --git a/code/datums/security_state.dm b/code/datums/security_state.dm deleted file mode 100644 index 1863d4ae8601..000000000000 --- a/code/datums/security_state.dm +++ /dev/null @@ -1,335 +0,0 @@ -/decl/security_state - abstract_type = /decl/security_state - // When defining any of these values type paths should be used, not instances. Instances will be acquired in /New() - - var/decl/security_level/severe_security_level // At which security level (and higher) the use of nuclear fission devices and other extreme measures are allowed. Defaults to the last entry in all_security_levels if unset. - var/decl/security_level/high_security_level // At which security level (and higher) transfer votes are disabled, ERT may be requested, and other similar high alert implications. Defaults to the second to last entry in all_security_levels if unset. - // All security levels within the above convention: Low, Guarded, Elevated, High, Severe - - - // Under normal conditions the crew may not raise the current security level higher than the highest_standard_security_level - // The crew may also not adjust the security level once it is above the highest_standard_security_level. - // Defaults to the second to last entry in all_security_levels if unset/null. - // Set to FALSE/0 if there should be no restrictions. - var/decl/security_level/highest_standard_security_level - - var/decl/security_level/current_security_level // The current security level. Defaults to the first entry in all_security_levels if unset. - var/decl/security_level/stored_security_level // The security level that we are escalating to high security from - we will return to this level once we choose to revert. - var/list/all_security_levels // List of all available security levels - var/list/standard_security_levels // List of all normally selectable security levels - var/list/comm_console_security_levels // List of all selectable security levels for the command and communication console - basically standard_security_levels - 1 - -/decl/security_state/Initialize() - - . = ..() - - // Setup the severe security level - if(!(severe_security_level in all_security_levels)) - severe_security_level = all_security_levels[all_security_levels.len] - severe_security_level = GET_DECL(severe_security_level) - - // Setup the high security level - if(!(high_security_level in all_security_levels)) - high_security_level = all_security_levels[all_security_levels.len - 1] - high_security_level = GET_DECL(high_security_level) - - // Setup the highest standard security level - if(highest_standard_security_level || isnull(highest_standard_security_level)) - if(!(highest_standard_security_level in all_security_levels)) - highest_standard_security_level = all_security_levels[all_security_levels.len - 1] - highest_standard_security_level = GET_DECL(highest_standard_security_level) - else - highest_standard_security_level = null - - // Setup the current security level - if(current_security_level in all_security_levels) - current_security_level = GET_DECL(current_security_level) - else - current_security_level = GET_DECL(all_security_levels[1]) - - // Setup the full list of available security levels now that we no longer need to use "x in all_security_levels" - var/list/security_level_instances = list() - for(var/security_level_type in all_security_levels) - security_level_instances += GET_DECL(security_level_type) - all_security_levels = security_level_instances - - standard_security_levels = list() - // Setup the list of normally selectable security levels - for(var/security_level in all_security_levels) - standard_security_levels += security_level - if(security_level == highest_standard_security_level) - break - - comm_console_security_levels = list() - // Setup the list of selectable security levels available in the comm. console - for(var/security_level in all_security_levels) - if(security_level == highest_standard_security_level) - break - comm_console_security_levels += security_level - - // Now we ensure the high security level is not above the severe one (but we allow them to be equal) - var/severe_index = all_security_levels.Find(severe_security_level) - var/high_index = all_security_levels.Find(high_security_level) - if(high_index > severe_index) - high_security_level = severe_security_level - - // Finally switch up to the default starting security level. - current_security_level.switching_up_to() - -/decl/security_state/proc/can_change_security_level() - return current_security_level in standard_security_levels - -/decl/security_state/proc/can_switch_to(var/given_security_level) - if(!can_change_security_level()) - return FALSE - return given_security_level in standard_security_levels - -/decl/security_state/proc/current_security_level_is_lower_than(var/given_security_level) - var/current_index = all_security_levels.Find(current_security_level) - var/given_index = all_security_levels.Find(given_security_level) - - return given_index && current_index < given_index - -/decl/security_state/proc/current_security_level_is_same_or_higher_than(var/given_security_level) - var/current_index = all_security_levels.Find(current_security_level) - var/given_index = all_security_levels.Find(given_security_level) - - return given_index && current_index >= given_index - -/decl/security_state/proc/current_security_level_is_higher_than(var/given_security_level) - var/current_index = all_security_levels.Find(current_security_level) - var/given_index = all_security_levels.Find(given_security_level) - - return given_index && current_index > given_index - -/decl/security_state/proc/set_security_level(var/decl/security_level/new_security_level, var/force_change = FALSE) - if(new_security_level == current_security_level) - return FALSE - if(!(new_security_level in all_security_levels)) - return FALSE - if(!force_change && !can_switch_to(new_security_level)) - return FALSE - - var/decl/security_level/previous_security_level = current_security_level - current_security_level = new_security_level - - var/previous_index = all_security_levels.Find(previous_security_level) - var/new_index = all_security_levels.Find(new_security_level) - - if(new_index > previous_index) - previous_security_level.switching_up_from() - new_security_level.switching_up_to() - else - previous_security_level.switching_down_from() - new_security_level.switching_down_to() - - log_and_message_admins("has changed the security level from [previous_security_level.name] to [new_security_level.name].") - return TRUE - -// This proc decreases the current security level, if possible -/decl/security_state/proc/decrease_security_level(var/force_change = FALSE) - var/current_index = all_security_levels.Find(current_security_level) - if(current_index == 1) - return FALSE - return set_security_level(all_security_levels[current_index - 1], force_change) - -/decl/security_level - var/icon - var/name - - // These values are primarily for station alarms and status displays, and which light colors and overlays to use - var/light_range - var/light_power - var/light_color_alarm - var/light_color_class - var/light_color_status_display - - var/up_description - var/down_description - - var/datum/alarm_appearance/alarm_appearance - - abstract_type = /decl/security_level - -/decl/security_level/Initialize() - . = ..() - if(ispath(alarm_appearance, /datum/alarm_appearance)) - alarm_appearance = new alarm_appearance - -/decl/security_level/validate() - . = ..() - var/initial_appearance = initial(alarm_appearance) - if(!initial_appearance) - . += "alarm_appearance was not set" - else if(!ispath(initial_appearance)) - . += "alarm_appearance was not set to a /datum/alarm_appearance subpath" - else if(!istype(alarm_appearance, /datum/alarm_appearance)) - . += "alarm_appearance creation failed (check runtimes?)" - -// Called when we're switching from a lower security level to this one. -/decl/security_level/proc/switching_up_to() - return - -// Called when we're switching from a higher security level to this one. -/decl/security_level/proc/switching_down_to() - return - -// Called when we're switching from this security level to a higher one. -/decl/security_level/proc/switching_up_from() - return - -// Called when we're switching from this security level to a lower one. -/decl/security_level/proc/switching_down_from() - return - -/* -* The default security state and levels setup -*/ -/decl/security_state/default - all_security_levels = list(/decl/security_level/default/code_green, /decl/security_level/default/code_blue, /decl/security_level/default/code_red, /decl/security_level/default/code_delta) - -/decl/security_level/default - abstract_type = /decl/security_level/default - icon = 'icons/misc/security_state.dmi' - - var/static/datum/announcement/priority/security/security_announcement_up = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/misc/notice1.ogg')) - var/static/datum/announcement/priority/security/security_announcement_down = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/misc/notice1.ogg')) - -/decl/security_level/default/switching_up_to() - if(up_description) - security_announcement_up.Announce(up_description, "Attention! Alert level elevated to [name]!") - notify_station() - -/decl/security_level/default/switching_down_to() - if(down_description) - security_announcement_down.Announce(down_description, "Attention! Alert level changed to [name]!") - notify_station() - -/decl/security_level/default/proc/notify_station() - for(var/obj/machinery/firealarm/FA in SSmachines.machinery) - if(isContactLevel(FA.z)) - FA.update_icon() - post_status("alert") - -/decl/security_level/default/code_green - name = "code green" - - light_range = 2 - light_power = 1 - - light_color_alarm = COLOR_GREEN - light_color_class = "font_green" - light_color_status_display = COLOR_GREEN - - - alarm_appearance = /datum/alarm_appearance/green - - down_description = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." - -/decl/security_level/default/code_blue - name = "code blue" - - light_range = 2 - light_power = 1 - light_color_alarm = COLOR_BLUE - light_color_class = "font_blue" - light_color_status_display = COLOR_BLUE - - alarm_appearance = /datum/alarm_appearance/blue - - up_description = "The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible, random searches are permitted." - down_description = "The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed." - -/decl/security_level/default/code_red - name = "code red" - - light_range = 4 - light_power = 2 - light_color_alarm = COLOR_RED - light_color_class = "font_red" - light_color_status_display = COLOR_RED - - alarm_appearance = /datum/alarm_appearance/red - - up_description = "There is an immediate serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised." - down_description = "The self-destruct mechanism has been deactivated, there is still however an immediate serious threat to the station. Security may have weapons unholstered at all times, random searches are allowed and advised." - -/decl/security_level/default/code_delta - name = "code delta" - - light_range = 4 - light_power = 2 - light_color_alarm = COLOR_RED - light_color_class = "font_red" - light_color_status_display = COLOR_RED - - alarm_appearance = /datum/alarm_appearance/delta - - - var/static/datum/announcement/priority/security/security_announcement_delta = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/effects/siren.ogg')) - -/decl/security_level/default/code_delta/switching_up_to() - security_announcement_delta.Announce("The self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.", "Attention! Delta security level reached!") - notify_station() - -/datum/alarm_appearance - var/display_icon //The icon_state for the displays. Normally only one is used, unless uses_twotone_displays is TRUE. - var/display_icon_color //The color for the display icon. - - var/display_icon_twotone //Used for two-tone displays. - var/display_icon_twotone_color //The color for the display icon. - - var/display_emblem //The icon_state for the emblem, i.e for delta, a radstorm, alerts. - var/display_emblem_color //The color for the emblem. - - var/alarm_icon //The icon_state for the alarms - var/alarm_icon_color //the color for the icon_state - - var/alarm_icon_twotone //Used for two-tone alarms (i.e delta). - var/alarm_icon_twotone_color //The color for the secondary tone icon. - -/datum/alarm_appearance/green - display_icon = "status_display_lines" - display_icon_color = PIPE_COLOR_GREEN - - display_emblem = "status_display_alert" - display_emblem_color = COLOR_WHITE - - alarm_icon = "alarm_normal" - alarm_icon_color = PIPE_COLOR_GREEN - -/datum/alarm_appearance/blue - display_icon = "status_display_lines" - display_icon_color = COLOR_BLUE - - display_emblem = "status_display_alert" - display_emblem_color = COLOR_WHITE - - alarm_icon = "alarm_normal" - alarm_icon_color = COLOR_BLUE - -/datum/alarm_appearance/red - display_icon = "status_display_lines" - display_icon_color = COLOR_RED - - display_emblem = "status_display_alert" - display_emblem_color = COLOR_WHITE - - alarm_icon = "alarm_blinking" - alarm_icon_color = COLOR_RED - -/datum/alarm_appearance/delta - display_icon = "status_display_twotone1" - display_icon_color = COLOR_RED - - display_icon_twotone = "status_display_twotone2" - display_icon_twotone_color = COLOR_YELLOW - - display_emblem = "delta" - display_emblem_color = COLOR_WHITE - - alarm_icon = "alarm_blinking_twotone1" - alarm_icon_color = COLOR_RED - - alarm_icon_twotone = "alarm_blinking_twotone2" - alarm_icon_twotone_color = PIPE_COLOR_YELLOW \ No newline at end of file diff --git a/code/modules/security_levels/_security_level.dm b/code/modules/security_levels/_security_level.dm new file mode 100644 index 000000000000..ea9d88b442a4 --- /dev/null +++ b/code/modules/security_levels/_security_level.dm @@ -0,0 +1,48 @@ +/decl/security_level + var/icon + var/name + + // These values are primarily for station alarms and status displays, and which light colors and overlays to use + var/light_range + var/light_power + var/light_color_alarm + var/light_color_class + var/light_color_status_display + + var/up_description + var/down_description + + var/datum/alarm_appearance/alarm_appearance + + abstract_type = /decl/security_level + +/decl/security_level/Initialize() + . = ..() + if(ispath(alarm_appearance, /datum/alarm_appearance)) + alarm_appearance = new alarm_appearance + +/decl/security_level/validate() + . = ..() + var/initial_appearance = initial(alarm_appearance) + if(!initial_appearance) + . += "alarm_appearance was not set" + else if(!ispath(initial_appearance)) + . += "alarm_appearance was not set to a /datum/alarm_appearance subpath" + else if(!istype(alarm_appearance, /datum/alarm_appearance)) + . += "alarm_appearance creation failed (check runtimes?)" + +// Called when we're switching from a lower security level to this one. +/decl/security_level/proc/switching_up_to() + return + +// Called when we're switching from a higher security level to this one. +/decl/security_level/proc/switching_down_to() + return + +// Called when we're switching from this security level to a higher one. +/decl/security_level/proc/switching_up_from() + return + +// Called when we're switching from this security level to a lower one. +/decl/security_level/proc/switching_down_from() + return \ No newline at end of file diff --git a/code/modules/security_levels/alarm_appearance.dm b/code/modules/security_levels/alarm_appearance.dm new file mode 100644 index 000000000000..494158a60393 --- /dev/null +++ b/code/modules/security_levels/alarm_appearance.dm @@ -0,0 +1,61 @@ +/datum/alarm_appearance + var/display_icon //The icon_state for the displays. Normally only one is used, unless uses_twotone_displays is TRUE. + var/display_icon_color //The color for the display icon. + + var/display_icon_twotone //Used for two-tone displays. + var/display_icon_twotone_color //The color for the display icon. + + var/display_emblem //The icon_state for the emblem, i.e for delta, a radstorm, alerts. + var/display_emblem_color //The color for the emblem. + + var/alarm_icon //The icon_state for the alarms + var/alarm_icon_color //the color for the icon_state + + var/alarm_icon_twotone //Used for two-tone alarms (i.e delta). + var/alarm_icon_twotone_color //The color for the secondary tone icon. + +/datum/alarm_appearance/green + display_icon = "status_display_lines" + display_icon_color = PIPE_COLOR_GREEN + + display_emblem = "status_display_alert" + display_emblem_color = COLOR_WHITE + + alarm_icon = "alarm_normal" + alarm_icon_color = PIPE_COLOR_GREEN + +/datum/alarm_appearance/blue + display_icon = "status_display_lines" + display_icon_color = COLOR_BLUE + + display_emblem = "status_display_alert" + display_emblem_color = COLOR_WHITE + + alarm_icon = "alarm_normal" + alarm_icon_color = COLOR_BLUE + +/datum/alarm_appearance/red + display_icon = "status_display_lines" + display_icon_color = COLOR_RED + + display_emblem = "status_display_alert" + display_emblem_color = COLOR_WHITE + + alarm_icon = "alarm_blinking" + alarm_icon_color = COLOR_RED + +/datum/alarm_appearance/delta + display_icon = "status_display_twotone1" + display_icon_color = COLOR_RED + + display_icon_twotone = "status_display_twotone2" + display_icon_twotone_color = COLOR_YELLOW + + display_emblem = "delta" + display_emblem_color = COLOR_WHITE + + alarm_icon = "alarm_blinking_twotone1" + alarm_icon_color = COLOR_RED + + alarm_icon_twotone = "alarm_blinking_twotone2" + alarm_icon_twotone_color = PIPE_COLOR_YELLOW \ No newline at end of file diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm new file mode 100644 index 000000000000..a1825bd9be85 --- /dev/null +++ b/code/modules/security_levels/security_levels.dm @@ -0,0 +1,93 @@ +/// The default security state used on most space maps. +/decl/security_state/default + all_security_levels = list( + /decl/security_level/default/code_green, + /decl/security_level/default/code_blue, + /decl/security_level/default/code_red, + /decl/security_level/default/code_delta + ) + +/// An abstract security level type that supports announcements on level change. +/decl/security_level/default + abstract_type = /decl/security_level/default + icon = 'icons/misc/security_state.dmi' + + var/static/datum/announcement/priority/security/security_announcement_up = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/misc/notice1.ogg')) + var/static/datum/announcement/priority/security/security_announcement_down = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/misc/notice1.ogg')) + +/decl/security_level/default/switching_up_to() + if(up_description) + security_announcement_up.Announce(up_description, "Attention! Alert level elevated to [name]!") + notify_station() + +/decl/security_level/default/switching_down_to() + if(down_description) + security_announcement_down.Announce(down_description, "Attention! Alert level changed to [name]!") + notify_station() + +/decl/security_level/default/proc/notify_station() + for(var/obj/machinery/firealarm/FA in SSmachines.machinery) + if(isContactLevel(FA.z)) + FA.update_icon() + post_status("alert") + +/decl/security_level/default/code_green + name = "code green" + + light_range = 2 + light_power = 1 + + light_color_alarm = COLOR_GREEN + light_color_class = "font_green" + light_color_status_display = COLOR_GREEN + + + alarm_appearance = /datum/alarm_appearance/green + + down_description = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." + +/decl/security_level/default/code_blue + name = "code blue" + + light_range = 2 + light_power = 1 + light_color_alarm = COLOR_BLUE + light_color_class = "font_blue" + light_color_status_display = COLOR_BLUE + + alarm_appearance = /datum/alarm_appearance/blue + + up_description = "The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible, random searches are permitted." + down_description = "The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed." + +/decl/security_level/default/code_red + name = "code red" + + light_range = 4 + light_power = 2 + light_color_alarm = COLOR_RED + light_color_class = "font_red" + light_color_status_display = COLOR_RED + + alarm_appearance = /datum/alarm_appearance/red + + up_description = "There is an immediate serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised." + down_description = "The self-destruct mechanism has been deactivated, there is still however an immediate serious threat to the station. Security may have weapons unholstered at all times, random searches are allowed and advised." + +/decl/security_level/default/code_delta + name = "code delta" + + light_range = 4 + light_power = 2 + light_color_alarm = COLOR_RED + light_color_class = "font_red" + light_color_status_display = COLOR_RED + + alarm_appearance = /datum/alarm_appearance/delta + + + var/static/datum/announcement/priority/security/security_announcement_delta = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/effects/siren.ogg')) + +/decl/security_level/default/code_delta/switching_up_to() + security_announcement_delta.Announce("The self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.", "Attention! Delta security level reached!") + notify_station() \ No newline at end of file diff --git a/code/modules/security_levels/security_state.dm b/code/modules/security_levels/security_state.dm new file mode 100644 index 000000000000..0f115f8c57b6 --- /dev/null +++ b/code/modules/security_levels/security_state.dm @@ -0,0 +1,134 @@ +/decl/security_state + abstract_type = /decl/security_state + // When defining any of these values type paths should be used, not instances. Instances will be acquired in /New() + + var/decl/security_level/severe_security_level // At which security level (and higher) the use of nuclear fission devices and other extreme measures are allowed. Defaults to the last entry in all_security_levels if unset. + var/decl/security_level/high_security_level // At which security level (and higher) transfer votes are disabled, ERT may be requested, and other similar high alert implications. Defaults to the second to last entry in all_security_levels if unset. + // All security levels within the above convention: Low, Guarded, Elevated, High, Severe + + + // Under normal conditions the crew may not raise the current security level higher than the highest_standard_security_level + // The crew may also not adjust the security level once it is above the highest_standard_security_level. + // Defaults to the second to last entry in all_security_levels if unset/null. + // Set to FALSE/0 if there should be no restrictions. + var/decl/security_level/highest_standard_security_level + + var/decl/security_level/current_security_level // The current security level. Defaults to the first entry in all_security_levels if unset. + var/decl/security_level/stored_security_level // The security level that we are escalating to high security from - we will return to this level once we choose to revert. + var/list/all_security_levels // List of all available security levels + var/list/standard_security_levels // List of all normally selectable security levels + var/list/comm_console_security_levels // List of all selectable security levels for the command and communication console - basically standard_security_levels - 1 + +/decl/security_state/Initialize() + + . = ..() + + // Setup the severe security level + if(!(severe_security_level in all_security_levels)) + severe_security_level = all_security_levels[all_security_levels.len] + severe_security_level = GET_DECL(severe_security_level) + + // Setup the high security level + if(!(high_security_level in all_security_levels)) + high_security_level = all_security_levels[all_security_levels.len - 1] + high_security_level = GET_DECL(high_security_level) + + // Setup the highest standard security level + if(highest_standard_security_level || isnull(highest_standard_security_level)) + if(!(highest_standard_security_level in all_security_levels)) + highest_standard_security_level = all_security_levels[all_security_levels.len - 1] + highest_standard_security_level = GET_DECL(highest_standard_security_level) + else + highest_standard_security_level = null + + // Setup the current security level + if(current_security_level in all_security_levels) + current_security_level = GET_DECL(current_security_level) + else + current_security_level = GET_DECL(all_security_levels[1]) + + // Setup the full list of available security levels now that we no longer need to use "x in all_security_levels" + var/list/security_level_instances = list() + for(var/security_level_type in all_security_levels) + security_level_instances += GET_DECL(security_level_type) + all_security_levels = security_level_instances + + standard_security_levels = list() + // Setup the list of normally selectable security levels + for(var/security_level in all_security_levels) + standard_security_levels += security_level + if(security_level == highest_standard_security_level) + break + + comm_console_security_levels = list() + // Setup the list of selectable security levels available in the comm. console + for(var/security_level in all_security_levels) + if(security_level == highest_standard_security_level) + break + comm_console_security_levels += security_level + + // Now we ensure the high security level is not above the severe one (but we allow them to be equal) + var/severe_index = all_security_levels.Find(severe_security_level) + var/high_index = all_security_levels.Find(high_security_level) + if(high_index > severe_index) + high_security_level = severe_security_level + + // Finally switch up to the default starting security level. + current_security_level.switching_up_to() + +/decl/security_state/proc/can_change_security_level() + return current_security_level in standard_security_levels + +/decl/security_state/proc/can_switch_to(var/given_security_level) + if(!can_change_security_level()) + return FALSE + return given_security_level in standard_security_levels + +/decl/security_state/proc/current_security_level_is_lower_than(var/given_security_level) + var/current_index = all_security_levels.Find(current_security_level) + var/given_index = all_security_levels.Find(given_security_level) + + return given_index && current_index < given_index + +/decl/security_state/proc/current_security_level_is_same_or_higher_than(var/given_security_level) + var/current_index = all_security_levels.Find(current_security_level) + var/given_index = all_security_levels.Find(given_security_level) + + return given_index && current_index >= given_index + +/decl/security_state/proc/current_security_level_is_higher_than(var/given_security_level) + var/current_index = all_security_levels.Find(current_security_level) + var/given_index = all_security_levels.Find(given_security_level) + + return given_index && current_index > given_index + +/decl/security_state/proc/set_security_level(var/decl/security_level/new_security_level, var/force_change = FALSE) + if(new_security_level == current_security_level) + return FALSE + if(!(new_security_level in all_security_levels)) + return FALSE + if(!force_change && !can_switch_to(new_security_level)) + return FALSE + + var/decl/security_level/previous_security_level = current_security_level + current_security_level = new_security_level + + var/previous_index = all_security_levels.Find(previous_security_level) + var/new_index = all_security_levels.Find(new_security_level) + + if(new_index > previous_index) + previous_security_level.switching_up_from() + new_security_level.switching_up_to() + else + previous_security_level.switching_down_from() + new_security_level.switching_down_to() + + log_and_message_admins("has changed the security level from [previous_security_level.name] to [new_security_level.name].") + return TRUE + +// This proc decreases the current security level, if possible +/decl/security_state/proc/decrease_security_level(var/force_change = FALSE) + var/current_index = all_security_levels.Find(current_security_level) + if(current_index == 1) + return FALSE + return set_security_level(all_security_levels[current_index - 1], force_change) diff --git a/nebula.dme b/nebula.dme index 2c7c6835f1d0..324850fd28f7 100644 --- a/nebula.dme +++ b/nebula.dme @@ -349,7 +349,6 @@ #include "code\datums\mutable_appearance.dm" #include "code\datums\position_point_vector.dm" #include "code\datums\progressbar.dm" -#include "code\datums\security_state.dm" #include "code\datums\sound_player.dm" #include "code\datums\suit_sensor_jammer_method.dm" #include "code\datums\sun.dm" @@ -3738,7 +3737,11 @@ #include "code\modules\sealant_gun\sealant_injector.dm" #include "code\modules\sealant_gun\sealant_rack.dm" #include "code\modules\sealant_gun\sealant_tank.dm" +#include "code\modules\security_levels\_security_level.dm" +#include "code\modules\security_levels\alarm_appearance.dm" #include "code\modules\security_levels\keycard_authentication.dm" +#include "code\modules\security_levels\security_levels.dm" +#include "code\modules\security_levels\security_state.dm" #include "code\modules\shield_generators\floor_diffuser.dm" #include "code\modules\shield_generators\handheld_diffuser.dm" #include "code\modules\shield_generators\modes.dm" From 62365c50efa42f0ea827451b37984ca6ac6228de Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 31 Dec 2024 19:22:27 -0500 Subject: [PATCH 0134/1331] Disable security state on Shaded Hills --- code/modules/mob/new_player/login.dm | 11 +++++------ code/modules/security_levels/_security_level.dm | 2 +- code/modules/security_levels/security_levels.dm | 16 ++++++++++++++-- code/modules/security_levels/security_state.dm | 2 ++ maps/shaded_hills/shaded_hills_define.dm | 1 + 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index 6951ec383e30..6c3a0d04adaf 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -36,12 +36,11 @@ show_lobby_menu(TRUE) var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) - var/decl/security_level/SL = security_state.current_security_level - var/alert_desc = "" - if(SL.up_description) - alert_desc = SL.up_description - - to_chat(src, SPAN_NOTICE("The alert level on the [station_name()] is currently: [SL.name]. [alert_desc]")) + if(security_state?.show_on_login) + var/decl/security_level/SL = security_state.current_security_level + // todo: allow maps to oevrride this string for things like the fantasy map being on high alert? + // eg "The alert level *in* Karzerfeste Keep is currently high alert." or "Karzerfeste Keep is currently on high alert." + to_chat(src, SPAN_NOTICE("The alert level on the [station_name()] is currently: [SL.name]. [SL?.up_description]")) // bolds the changelog button on the interface so we know there are updates. if(client.prefs?.lastchangelog != global.changelog_hash) diff --git a/code/modules/security_levels/_security_level.dm b/code/modules/security_levels/_security_level.dm index ea9d88b442a4..ca4ba811c82f 100644 --- a/code/modules/security_levels/_security_level.dm +++ b/code/modules/security_levels/_security_level.dm @@ -1,5 +1,5 @@ /decl/security_level - var/icon + var/icon = 'icons/misc/security_state.dmi' var/name // These values are primarily for station alarms and status displays, and which light colors and overlays to use diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm index a1825bd9be85..186fd37fbbe7 100644 --- a/code/modules/security_levels/security_levels.dm +++ b/code/modules/security_levels/security_levels.dm @@ -10,7 +10,6 @@ /// An abstract security level type that supports announcements on level change. /decl/security_level/default abstract_type = /decl/security_level/default - icon = 'icons/misc/security_state.dmi' var/static/datum/announcement/priority/security/security_announcement_up = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/misc/notice1.ogg')) var/static/datum/announcement/priority/security/security_announcement_down = new(do_log = 0, do_newscast = 1, new_sound = sound('sound/misc/notice1.ogg')) @@ -90,4 +89,17 @@ /decl/security_level/default/code_delta/switching_up_to() security_announcement_delta.Announce("The self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.", "Attention! Delta security level reached!") - notify_station() \ No newline at end of file + notify_station() + +// The following are dummy states and levels to soft-disable security levels on some maps. +/// A security state used for maps that don't have security levels exposed to players. +/decl/security_state/none + all_security_levels = list( + /decl/security_level/none + ) + +/// A dummy security level with no effects. +/decl/security_level/none + name = "none" + // Since currently we're required to have an alarm_appearance, we just use a blank one. + alarm_appearance = /datum/alarm_appearance \ No newline at end of file diff --git a/code/modules/security_levels/security_state.dm b/code/modules/security_levels/security_state.dm index 0f115f8c57b6..dfc0c78a94c2 100644 --- a/code/modules/security_levels/security_state.dm +++ b/code/modules/security_levels/security_state.dm @@ -1,5 +1,7 @@ /decl/security_state abstract_type = /decl/security_state + /// Whether or not security level information should be shown to new players on login. + var/show_on_login = TRUE // When defining any of these values type paths should be used, not instances. Instances will be acquired in /New() var/decl/security_level/severe_security_level // At which security level (and higher) the use of nuclear fission devices and other extreme measures are allowed. Defaults to the last entry in all_security_levels if unset. diff --git a/maps/shaded_hills/shaded_hills_define.dm b/maps/shaded_hills/shaded_hills_define.dm index 757f99488262..6266ab2a09e4 100644 --- a/maps/shaded_hills/shaded_hills_define.dm +++ b/maps/shaded_hills/shaded_hills_define.dm @@ -31,6 +31,7 @@ 'sound/music/Miris-Magic-Dance.ogg' ) game_year = -914 // in 2024, the year should be 1110, roughly a century after the fall of the Imperial Aegis + security_state = /decl/security_state/none char_preview_bgstate_options = list( "000", From 8be56cccfa49f3810e4571435ccd64ddeeea0d49 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 31 Dec 2024 19:44:04 -0500 Subject: [PATCH 0135/1331] Move inertial dampener into a modpack --- code/datums/supplypacks/engineering.dm | 7 --- code/modules/events/event_container.dm | 1 - .../imprinter/designs_misc_circuits.dm | 3 -- .../overmap/ships/computers/engine_control.dm | 23 ++++++--- code/modules/overmap/ships/ship.dm | 19 -------- maps/modpack_testing/modpack_testing.dm | 1 + mods/content/inertia/_inertia.dm | 3 ++ mods/content/inertia/_inertia.dme | 13 ++++++ .../content/inertia/fabrication.dm | 5 +- .../inertia/icons}/inertial_damper.dmi | Bin mods/content/inertia/inertia_controller.dm | 24 ++++++++++ .../content/inertia/inertia_failure.dm | 11 +++++ .../content/inertia}/inertial_damper.dm | 35 ++++---------- mods/content/inertia/ship_inertia.dm | 44 ++++++++++++++++++ mods/content/inertia/supplies.dm | 6 +++ .../content/inertia/wires.dm | 9 ++-- nebula.dme | 4 -- 17 files changed, 135 insertions(+), 73 deletions(-) create mode 100644 mods/content/inertia/_inertia.dm create mode 100644 mods/content/inertia/_inertia.dme rename code/game/objects/items/weapons/circuitboards/machinery/inertial_damper.dm => mods/content/inertia/fabrication.dm (80%) rename {icons/obj/machines => mods/content/inertia/icons}/inertial_damper.dmi (100%) create mode 100644 mods/content/inertia/inertia_controller.dm rename code/modules/events/inertial_damper.dm => mods/content/inertia/inertia_failure.dm (78%) rename {code/modules/inertial_damper => mods/content/inertia}/inertial_damper.dm (88%) create mode 100644 mods/content/inertia/ship_inertia.dm create mode 100644 mods/content/inertia/supplies.dm rename code/datums/wires/inertial_damper.dm => mods/content/inertia/wires.dm (71%) diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm index 91b8006a1945..3c771acd1e42 100644 --- a/code/datums/supplypacks/engineering.dm +++ b/code/datums/supplypacks/engineering.dm @@ -155,13 +155,6 @@ containername = "shield generator construction kit crate" access = access_engine -/decl/hierarchy/supply_pack/engineering/inertial_damper - name = "Equipment - inertial damper construction kit" - contains = list(/obj/item/stock_parts/circuitboard/inertial_damper, /obj/item/stock_parts/capacitor, /obj/item/stock_parts/micro_laser, /obj/item/stock_parts/console_screen) - containertype = /obj/structure/closet/crate/secure - containername = "inertial damper construction kit crate" - access = access_engine - /decl/hierarchy/supply_pack/engineering/smbig name = "Power - Supermatter core" contains = list(/obj/machinery/power/supermatter) diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index 97dc8f97f3ff..2e0256a4706b 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -157,7 +157,6 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT new /datum/event_meta/no_overmap(EVENT_LEVEL_MODERATE, "Electrical Storm", /datum/event/electrical_storm, 10, list(ASSIGNMENT_ENGINEER = 15, ASSIGNMENT_JANITOR = 10)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Gravity Failure", /datum/event/gravity, 75, list(ASSIGNMENT_ENGINEER = 25)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Grid Check", /datum/event/grid_check, 200, list(ASSIGNMENT_ENGINEER = 10)), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Inertial Damper Recalibration", /datum/event/inertial_damper, 75, list(ASSIGNMENT_ENGINEER = 25)), new /datum/event_meta/no_overmap(EVENT_LEVEL_MODERATE, "Ion Storm", /datum/event/ionstorm, 0, list(ASSIGNMENT_COMPUTER = 50, ASSIGNMENT_ROBOT = 50, ASSIGNMENT_ENGINEER = 15, ASSIGNMENT_SCIENTIST = 5)), new /datum/event_meta/no_overmap(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 20)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Prison Break", /datum/event/prison_break, 0, list(ASSIGNMENT_SECURITY = 100)), diff --git a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm index c817352197fe..44f481a550c0 100644 --- a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm +++ b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm @@ -467,9 +467,6 @@ /datum/fabricator_recipe/imprinter/circuit/long_range_relay path = /obj/item/stock_parts/circuitboard/relay/long_range -/datum/fabricator_recipe/imprinter/circuit/inertial_damper - path = /obj/item/stock_parts/circuitboard/inertial_damper - /datum/fabricator_recipe/imprinter/circuit/docking_beacon path = /obj/item/stock_parts/circuitboard/docking_beacon diff --git a/code/modules/overmap/ships/computers/engine_control.dm b/code/modules/overmap/ships/computers/engine_control.dm index a62b7258705a..f7f37fcb3467 100644 --- a/code/modules/overmap/ships/computers/engine_control.dm +++ b/code/modules/overmap/ships/computers/engine_control.dm @@ -6,6 +6,21 @@ icon_screen = "engines" var/display_state = "status" +/// A stub used for modpacks to modify per-ship readout information. +/// This mutates the UI data list, so it can be used to mask or remove features +/// as well as to add entirely new ones. +/// It does not return a value. +/// While it can access per-engine data, it should be dispreferred for that compared to modify_engine_ui_data(). +/obj/machinery/computer/ship/engines/proc/modify_ship_ui_data(list/ui_data) + return + +/// A stub used for modpacks to modify per-engine readout information. +/// This mutates the engine's data list, so it can be used to mask or remove features +/// as well as to add entirely new ones. +/// It does not return a value. +/obj/machinery/computer/ship/engines/proc/modify_engine_ui_data(datum/extension/ship_engine/engine, list/engine_data) + return + /obj/machinery/computer/ship/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!linked) display_reconnect_dialog(user, "ship control systems") @@ -29,17 +44,13 @@ var/thrust = E.get_exhaust_velocity() total_thrust += thrust rdata["eng_thrust"] = "[thrust] m/s" + modify_engine_ui_data(E, rdata) enginfo.Add(list(rdata)) data["engines_info"] = enginfo data["total_thrust"] = "[total_thrust] m/s" - var/damping_strength = 0 - for(var/datum/ship_inertial_damper/I in linked.inertial_dampers) - var/obj/machinery/inertial_damper/ID = I.holder - damping_strength += ID.get_damping_strength(FALSE) - data["damping_strength"] = damping_strength - data["needs_dampers"] = linked.needs_dampers + modify_ship_ui_data(data) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) diff --git a/code/modules/overmap/ships/ship.dm b/code/modules/overmap/ships/ship.dm index f4f1a734c884..133c506ffa63 100644 --- a/code/modules/overmap/ships/ship.dm +++ b/code/modules/overmap/ships/ship.dm @@ -19,9 +19,6 @@ var/global/const/OVERMAP_SPEED_CONSTANT = (1 SECOND) var/skill_needed = SKILL_ADEPT //piloting skill needed to steer it without going in random dir var/operator_skill - var/needs_dampers = FALSE - var/list/inertial_dampers = list() - var/damping_strength = null var/vessel_size = SHIP_SIZE_LARGE // arbitrary number, affects how likely are we to evade meteors var/list/navigation_viewers // list of weakrefs to people viewing the overmap via this ship @@ -85,15 +82,6 @@ var/global/const/OVERMAP_SPEED_CONSTANT = (1 SECOND) /obj/effect/overmap/visitable/ship/adjust_speed(n_x, n_y) . = ..() - var/magnitude = norm(n_x, n_y) - var/inertia_dir = magnitude >= 0 ? turn(fore_dir, 180) : fore_dir - var/inertia_strength = magnitude * 1e3 - if(needs_dampers && damping_strength < inertia_strength) - var/list/areas_by_name = area_repository.get_areas_by_z_level() - for(var/area_name in areas_by_name) - var/area/A = areas_by_name[area_name] - if(area_belongs_to_zlevels(A, map_z)) - A.throw_unbuckled_occupants(inertia_strength+2, inertia_strength, inertia_dir) for(var/zz in map_z) if(is_still()) toggle_move_stars(zz) @@ -119,10 +107,6 @@ var/global/const/OVERMAP_SPEED_CONSTANT = (1 SECOND) return round(num_burns / burns_per_grid) /obj/effect/overmap/visitable/ship/Process(wait, tick) - damping_strength = 0 - for(var/datum/ship_inertial_damper/I in inertial_dampers) - var/obj/machinery/inertial_damper/ID = I.holder - damping_strength += ID.get_damping_strength(TRUE) sensor_visibility = min(round(base_sensor_visibility + get_speed_sensor_increase(), 1), 100) /obj/effect/overmap/visitable/ship/on_update_icon() @@ -181,9 +165,6 @@ var/global/const/OVERMAP_SPEED_CONSTANT = (1 SECOND) for(var/datum/extension/ship_engine/E in global.ship_engines) if(check_ownership(E.holder)) engines |= E - for(var/datum/ship_inertial_damper/I in global.ship_inertial_dampers) - if(check_ownership(I.holder)) - inertial_dampers |= I var/v_mass = recalculate_vessel_mass() if(v_mass) vessel_mass = v_mass diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index de68e49bc7bd..f94fbbc844c8 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -29,6 +29,7 @@ #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/species/drakes/_drakes.dme" // include before _fantasy.dme so overrides work #include "../../mods/content/fantasy/_fantasy.dme" + #include "../../mods/content/inertia/_inertia.dme" #include "../../mods/mobs/dionaea/_dionaea.dme" #include "../../mods/mobs/borers/_borers.dme" diff --git a/mods/content/inertia/_inertia.dm b/mods/content/inertia/_inertia.dm new file mode 100644 index 000000000000..97bee4de9a0f --- /dev/null +++ b/mods/content/inertia/_inertia.dm @@ -0,0 +1,3 @@ +/decl/modpack/inertia + name = "Ship Inertia Content" + desc = "This modpack adds support for inertia (throwing unsecured mobs) when ship thrusters fire, and adds an inertial dampener machine to counteract this." \ No newline at end of file diff --git a/mods/content/inertia/_inertia.dme b/mods/content/inertia/_inertia.dme new file mode 100644 index 000000000000..2407851eca30 --- /dev/null +++ b/mods/content/inertia/_inertia.dme @@ -0,0 +1,13 @@ +#ifndef MODPACK_SHIP_INERTIA +#define MODPACK_SHIP_INERTIA +// BEGIN_INCLUDE +#include "_inertia.dm" +#include "fabrication.dm" +#include "inertia_controller.dm" +#include "inertia_failure.dm" +#include "inertial_damper.dm" +#include "ship_inertia.dm" +#include "supplies.dm" +#include "wires.dm" +// END_INCLUDE +#endif diff --git a/code/game/objects/items/weapons/circuitboards/machinery/inertial_damper.dm b/mods/content/inertia/fabrication.dm similarity index 80% rename from code/game/objects/items/weapons/circuitboards/machinery/inertial_damper.dm rename to mods/content/inertia/fabrication.dm index 3bf3f65219e2..72a1837b5137 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/inertial_damper.dm +++ b/mods/content/inertia/fabrication.dm @@ -10,4 +10,7 @@ /obj/item/stock_parts/console_screen = 1, /obj/item/stock_parts/keyboard = 1, /obj/item/stock_parts/power/apc/buildable = 1 - ) \ No newline at end of file + ) + +/datum/fabricator_recipe/imprinter/circuit/inertial_damper + path = /obj/item/stock_parts/circuitboard/inertial_damper diff --git a/icons/obj/machines/inertial_damper.dmi b/mods/content/inertia/icons/inertial_damper.dmi similarity index 100% rename from icons/obj/machines/inertial_damper.dmi rename to mods/content/inertia/icons/inertial_damper.dmi diff --git a/mods/content/inertia/inertia_controller.dm b/mods/content/inertia/inertia_controller.dm new file mode 100644 index 000000000000..6b020a67b60c --- /dev/null +++ b/mods/content/inertia/inertia_controller.dm @@ -0,0 +1,24 @@ +/// A list of all inertial dampers in existence. This is only used for assigning them to ships at roundstart. +var/global/list/ship_inertial_dampers = list() + +/datum/ship_inertial_damper + var/name = "ship inertial damper" + var/obj/machinery/holder + +/datum/ship_inertial_damper/proc/get_damping_strength(var/reliable) + return 0 + +/datum/ship_inertial_damper/New(var/obj/machinery/_holder) + ..() + holder = _holder + global.ship_inertial_dampers += src + +/datum/ship_inertial_damper/Destroy() + global.ship_inertial_dampers -= src + // This may need some future optimization for servers with lots of ships. + // Just track what ship we're assigned to somehow, and then use that here. + // You'd also have to register it to clear that ref if/when the ship object is destroyed. + for(var/obj/effect/overmap/visitable/ship/S in SSshuttle.ships) + S.inertial_dampers -= src + holder = null + . = ..() \ No newline at end of file diff --git a/code/modules/events/inertial_damper.dm b/mods/content/inertia/inertia_failure.dm similarity index 78% rename from code/modules/events/inertial_damper.dm rename to mods/content/inertia/inertia_failure.dm index e8d145859674..ce7d147f7709 100644 --- a/code/modules/events/inertial_damper.dm +++ b/mods/content/inertia/inertia_failure.dm @@ -1,7 +1,18 @@ +// TODO: This should either be removed, or reworked to announce to specifically only the affected ship or its associated map. /datum/event/inertial_damper announceWhen = 5 check_proc = /proc/inertial_dampener_event_can_fire +/datum/event_container/moderate/New() + ..() + available_events += new /datum/event_meta( + EVENT_LEVEL_MODERATE, + "Inertial Damper Recalibration", + /datum/event/inertial_damper, + 75, + list(ASSIGNMENT_ENGINEER = 25) + ) + /datum/event/inertial_damper/setup() endWhen = rand(45, 120) diff --git a/code/modules/inertial_damper/inertial_damper.dm b/mods/content/inertia/inertial_damper.dm similarity index 88% rename from code/modules/inertial_damper/inertial_damper.dm rename to mods/content/inertia/inertial_damper.dm index 60eea5160441..4cb9905a95ce 100644 --- a/code/modules/inertial_damper/inertial_damper.dm +++ b/mods/content/inertia/inertial_damper.dm @@ -1,28 +1,6 @@ -#define WARNING_DELAY 80 //seconds between warnings. -var/global/list/ship_inertial_dampers = list() - -/datum/ship_inertial_damper - var/name = "ship inertial damper" - var/obj/machinery/holder - -/datum/ship_inertial_damper/proc/get_damping_strength(var/reliable) - return 0 - -/datum/ship_inertial_damper/New(var/obj/machinery/_holder) - ..() - holder = _holder - global.ship_inertial_dampers += src - -/datum/ship_inertial_damper/Destroy() - global.ship_inertial_dampers -= src - for(var/obj/effect/overmap/visitable/ship/S in SSshuttle.ships) - S.inertial_dampers -= src - holder = null - . = ..() - /obj/machinery/inertial_damper name = "inertial damper" - icon = 'icons/obj/machines/inertial_damper.dmi' + icon = 'mods/content/inertia/icons/inertial_damper.dmi' desc = "An inertial damper, a very large machine that balances against engine thrust to prevent harm to the crew." density = TRUE icon_state = "damper_on" @@ -67,6 +45,9 @@ var/global/list/ship_inertial_dampers = list() var/width = 3 var/height = 2 + /// The cooldown between announcements that the inertial damping system is off. + var/const/WARNING_DELAY = 8 SECONDS + /obj/machinery/inertial_damper/Initialize() . = ..() SetBounds() @@ -117,8 +98,10 @@ var/global/list/ship_inertial_dampers = list() /obj/machinery/inertial_damper/proc/is_on() return active -/obj/machinery/inertial_damper/proc/get_damping_strength(var/reliable) - if(hacked && !reliable) +/// Returns either the true damping strength including modifiers (include_modifiers == TRUE), +/// or just the value the damper is set to (include_modifiers == FALSE). +/obj/machinery/inertial_damper/proc/get_damping_strength(var/include_modifiers) + if(hacked && !include_modifiers) return initial(damping_strength) return damping_strength + damping_modifier @@ -203,5 +186,3 @@ var/global/list/ship_inertial_dampers = list() /obj/machinery/inertial_damper/dismantle() if((. = ..())) update_nearby_tiles(locs) - -#undef WARNING_DELAY \ No newline at end of file diff --git a/mods/content/inertia/ship_inertia.dm b/mods/content/inertia/ship_inertia.dm new file mode 100644 index 000000000000..d903ef02aa14 --- /dev/null +++ b/mods/content/inertia/ship_inertia.dm @@ -0,0 +1,44 @@ +/obj/effect/overmap/visitable/ship + /// Whether or not this ship throws mobs on acceleration if dampers are inactive. + var/needs_dampers = FALSE + /// A list of inertial damping controller datums associated with this ship. + var/list/datum/ship_inertial_damper/inertial_dampers = list() + /// The current damping strength from all inertial dampers, recalculated every tick in the ship's Process(). + var/tmp/damping_strength = null + +/obj/effect/overmap/visitable/ship/populate_sector_objects() + ..() + for(var/datum/ship_inertial_damper/I in global.ship_inertial_dampers) + if(check_ownership(I.holder)) + inertial_dampers |= I + +// Theoretically there's no need to recalculate this every tick, +// instead it should be recalculated any time damping strength changes +// based only on the damper that changed. +/obj/effect/overmap/visitable/ship/Process(wait, tick) + . = ..() + damping_strength = 0 + for(var/datum/ship_inertial_damper/I in inertial_dampers) + var/obj/machinery/inertial_damper/ID = I.holder + damping_strength += ID.get_damping_strength(TRUE) + +/obj/effect/overmap/visitable/ship/adjust_speed(n_x, n_y) + . = ..() + var/magnitude = norm(n_x, n_y) + var/inertia_dir = magnitude >= 0 ? turn(fore_dir, 180) : fore_dir + var/inertia_strength = magnitude * 1e3 + if(needs_dampers && damping_strength < inertia_strength) + var/list/areas_by_name = area_repository.get_areas_by_z_level() + for(var/area_name in areas_by_name) + var/area/A = areas_by_name[area_name] + if(area_belongs_to_zlevels(A, map_z)) + A.throw_unbuckled_occupants(inertia_strength+2, inertia_strength, inertia_dir) + +// Add additional data to the engine console. +/obj/machinery/computer/ship/engines/modify_ship_ui_data(list/ui_data) + var/damping_strength = 0 + for(var/datum/ship_inertial_damper/inertia_controller in linked.inertial_dampers) + var/obj/machinery/inertial_damper/damper = inertia_controller.holder + damping_strength += damper.get_damping_strength(FALSE) // get only the level it's set to, not the actual level + ui_data["damping_strength"] = damping_strength + ui_data["needs_dampers"] = linked.needs_dampers \ No newline at end of file diff --git a/mods/content/inertia/supplies.dm b/mods/content/inertia/supplies.dm new file mode 100644 index 000000000000..fbe975ab7cb9 --- /dev/null +++ b/mods/content/inertia/supplies.dm @@ -0,0 +1,6 @@ +/decl/hierarchy/supply_pack/engineering/inertial_damper + name = "Equipment - inertial damper construction kit" + contains = list(/obj/item/stock_parts/circuitboard/inertial_damper, /obj/item/stock_parts/capacitor, /obj/item/stock_parts/micro_laser, /obj/item/stock_parts/console_screen) + containertype = /obj/structure/closet/crate/secure + containername = "inertial damper construction kit crate" + access = access_engine \ No newline at end of file diff --git a/code/datums/wires/inertial_damper.dm b/mods/content/inertia/wires.dm similarity index 71% rename from code/datums/wires/inertial_damper.dm rename to mods/content/inertia/wires.dm index 4ea708f73bec..4cc74f8bc941 100644 --- a/code/datums/wires/inertial_damper.dm +++ b/mods/content/inertia/wires.dm @@ -7,11 +7,10 @@ new /datum/wire_description(DAMPER_WIRE_CONTROL, "This wire connects to the main control panel."), new /datum/wire_description(DAMPER_WIRE_AICONTROL, "This wire connects to automated control systems.") ) - -var/global/const/DAMPER_WIRE_POWER = 1 // Cut to disable power input into the generator. Pulse does nothing. Mend to restore. -var/global/const/DAMPER_WIRE_HACK = 2 // Pulse to hack the dampener, causing false display on engine consoles. Cut to unhack. Mend does nothing. -var/global/const/DAMPER_WIRE_CONTROL = 4 // Cut to lock controls. Mend to unlock them. Pulse does nothing. -var/global/const/DAMPER_WIRE_AICONTROL = 8 // Cut to disable AI control. Mend to restore. + var/const/DAMPER_WIRE_POWER = 1 // Cut to disable power input into the generator. Pulse does nothing. Mend to restore. + var/const/DAMPER_WIRE_HACK = 2 // Pulse to hack the dampener, causing false display on engine consoles. Cut to unhack. Mend does nothing. + var/const/DAMPER_WIRE_CONTROL = 4 // Cut to lock controls. Mend to unlock them. Pulse does nothing. + var/const/DAMPER_WIRE_AICONTROL = 8 // Cut to disable AI control. Mend to restore. /datum/wires/inertial_damper/CanUse() var/obj/machinery/inertial_damper/I = holder diff --git a/nebula.dme b/nebula.dme index 324850fd28f7..5272f7674a01 100644 --- a/nebula.dme +++ b/nebula.dme @@ -720,7 +720,6 @@ #include "code\datums\wires\camera.dm" #include "code\datums\wires\explosive.dm" #include "code\datums\wires\fabricator.dm" -#include "code\datums\wires\inertial_damper.dm" #include "code\datums\wires\nuclearbomb.dm" #include "code\datums\wires\particle_accelerator.dm" #include "code\datums\wires\radio.dm" @@ -1286,7 +1285,6 @@ #include "code\game\objects\items\weapons\circuitboards\machinery\forensic.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\holomap.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\household.dm" -#include "code\game\objects\items\weapons\circuitboards\machinery\inertial_damper.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\mech_recharger.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\medical.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\mining.dm" @@ -2336,7 +2334,6 @@ #include "code\modules\events\event_dynamic.dm" #include "code\modules\events\gravity.dm" #include "code\modules\events\grid_check.dm" -#include "code\modules\events\inertial_damper.dm" #include "code\modules\events\infestation.dm" #include "code\modules\events\ion_storm.dm" #include "code\modules\events\location_event.dm" @@ -2599,7 +2596,6 @@ #include "code\modules\hydroponics\trays\tray_soil.dm" #include "code\modules\hydroponics\trays\tray_tools.dm" #include "code\modules\hydroponics\trays\tray_update_icons.dm" -#include "code\modules\inertial_damper\inertial_damper.dm" #include "code\modules\integrated_electronics\_defines.dm" #include "code\modules\integrated_electronics\core\_electronics.dm" #include "code\modules\integrated_electronics\core\analyzer.dm" From 20f95d633b5bd10464bbff91b270828746d79395 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 31 Dec 2024 19:46:00 -0500 Subject: [PATCH 0136/1331] Allow modpacks to include NanoUI template folders --- .../subsystems/initialization/modpacks.dm | 1 + code/modules/client/asset_cache.dm | 41 +++++++++++++------ mods/_modpack.dm | 11 +++++ mods/content/inertia/_inertia.dm | 3 +- .../nano_templates}/inertial_damper.tmpl | 0 nano/templates/engines_control.tmpl | 2 + scripts/server.sh | 2 +- 7 files changed, 46 insertions(+), 14 deletions(-) rename {nano/templates => mods/content/inertia/nano_templates}/inertial_damper.tmpl (100%) diff --git a/code/controllers/subsystems/initialization/modpacks.dm b/code/controllers/subsystems/initialization/modpacks.dm index 5fad4ec549cb..f2f6c65a2e69 100644 --- a/code/controllers/subsystems/initialization/modpacks.dm +++ b/code/controllers/subsystems/initialization/modpacks.dm @@ -7,6 +7,7 @@ SUBSYSTEM_DEF(modpacks) // Compiled modpack information. var/list/default_submap_whitelisted_species = list() var/list/default_submap_blacklisted_species = list(SPECIES_ALIEN, SPECIES_GOLEM) + var/list/modpack_nanoui_directories = list() /datum/controller/subsystem/modpacks/Initialize() var/list/all_modpacks = decls_repository.get_decls_of_subtype(/decl/modpack) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 8e8c90860c46..d582783e0912 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -185,7 +185,7 @@ var/global/template_file_name = "all_templates.json" if(fexists(path + filename)) register_asset(filename, fcopy_rsc(path + filename)) - merge_and_register_templates() + merge_and_register_all_templates() var/list/mapnames = list() for(var/z in SSmapping.map_levels) @@ -199,13 +199,18 @@ var/global/template_file_name = "all_templates.json" common[filename] = fcopy_rsc(file_path) register_asset(filename, common[filename]) -/datum/asset/nanoui/proc/merge_and_register_templates() - var/list/templates = flist(template_dir) - for(var/filename in templates) - if(copytext(filename, length(filename)) != "/") - templates[filename] = replacetext(replacetext(file2text(template_dir + filename), "\n", ""), "\t", "") - else - templates -= filename +/datum/asset/nanoui/proc/merge_and_register_all_templates() + . = merge_templates(template_dir) + . += merge_modpack_templates() + register_templates(.) + +/datum/asset/nanoui/proc/merge_modpack_templates() + PRIVATE_PROC(TRUE) + . = list() + for(var/mod_template_dir in SSmodpacks.modpack_nanoui_directories) + . += merge_templates(mod_template_dir) + +/datum/asset/nanoui/proc/register_templates(templates) var/full_file_name = template_temp_dir + global.template_file_name if(fexists(full_file_name)) fdel(file(full_file_name)) @@ -213,6 +218,17 @@ var/global/template_file_name = "all_templates.json" to_file(template_file, json_encode(templates)) register_asset(global.template_file_name, fcopy_rsc(template_file)) +/// Handles adding a directory's templates to the compiled templates list. +/datum/asset/nanoui/proc/merge_templates(use_dir) + PRIVATE_PROC(TRUE) + var/list/templates = flist(use_dir) + for(var/filename in templates) + if(copytext(filename, length(filename)) != "/") + templates[filename] = replacetext(replacetext(file2text(use_dir + filename), "\n", ""), "\t", "") + else + templates -= filename + return templates + /datum/asset/nanoui/send(client, uncommon) if(!islist(uncommon)) uncommon = list(uncommon) @@ -223,11 +239,12 @@ var/global/template_file_name = "all_templates.json" // Note: this is intended for dev work, and is unsafe. Do not use outside of that. /datum/asset/nanoui/proc/recompute_and_resend_templates() - merge_and_register_templates() + merge_and_register_all_templates() for(var/client/C in clients) - if(C) // there are sleeps here, potentially - send_asset(C, global.template_file_name, FALSE, FALSE) - to_chat(C, SPAN_WARNING("Nanoui templates have been updated. Please close and reopen any browser windows.")) + spawn() // there are sleeps here, potentially + if(C) + send_asset(C, global.template_file_name, FALSE, FALSE) + to_chat(C, SPAN_WARNING("Nanoui templates have been updated. Please close and reopen any browser windows.")) /client/proc/resend_nanoui_templates() set category = "Debug" diff --git a/mods/_modpack.dm b/mods/_modpack.dm index 1e8accdf3061..81c16e69f7a0 100644 --- a/mods/_modpack.dm +++ b/mods/_modpack.dm @@ -6,6 +6,8 @@ /// A string with authors of this modpack. var/author var/secrets_directory + /// The folder to load additional NanoUI templates from. Must be relative to the DME's location (root game folder). + var/nanoui_directory var/list/dreams //! A list of strings to be added to the random dream proc. var/list/credits_other //! A list of strings that are used by the end of round credits roll. @@ -32,6 +34,15 @@ if(!fexists(secrets_directory)) return "Modpack secrets_directory does not exist." SSsecrets.load_directories |= secrets_directory + if(nanoui_directory) + nanoui_directory = trim(lowertext(nanoui_directory)) + if(!length(nanoui_directory)) + return "Modpack nanoui_directory is zero length after trim." + if(copytext(nanoui_directory, -1) != "/") + nanoui_directory = "[nanoui_directory]/" + if(!fexists(nanoui_directory)) + return "Modpack nanoui_directory does not exist." + SSmodpacks.modpack_nanoui_directories |= nanoui_directory /decl/modpack/proc/initialize() return diff --git a/mods/content/inertia/_inertia.dm b/mods/content/inertia/_inertia.dm index 97bee4de9a0f..585525241f16 100644 --- a/mods/content/inertia/_inertia.dm +++ b/mods/content/inertia/_inertia.dm @@ -1,3 +1,4 @@ /decl/modpack/inertia name = "Ship Inertia Content" - desc = "This modpack adds support for inertia (throwing unsecured mobs) when ship thrusters fire, and adds an inertial dampener machine to counteract this." \ No newline at end of file + desc = "This modpack adds support for inertia (throwing unsecured mobs) when ship thrusters fire, and adds an inertial dampener machine to counteract this." + nanoui_directory = "mods/content/inertia/nano_templates/" \ No newline at end of file diff --git a/nano/templates/inertial_damper.tmpl b/mods/content/inertia/nano_templates/inertial_damper.tmpl similarity index 100% rename from nano/templates/inertial_damper.tmpl rename to mods/content/inertia/nano_templates/inertial_damper.tmpl diff --git a/nano/templates/engines_control.tmpl b/nano/templates/engines_control.tmpl index 4df88e86014b..c9ec27c77c73 100644 --- a/nano/templates/engines_control.tmpl +++ b/nano/templates/engines_control.tmpl @@ -29,6 +29,8 @@ {{:data.total_thrust}}
    + + {{if data.needs_dampers}}
    diff --git a/scripts/server.sh b/scripts/server.sh index b65b6b33d166..bc89d45b7c41 100755 --- a/scripts/server.sh +++ b/scripts/server.sh @@ -63,7 +63,7 @@ while [[ ! -e stopserver ]]; do cp "$GITDIR/$DME.rsc" . cp -r "$GITDIR/nano" . # Necessary for NanoUI cp -r "$GITDIR/maps" . # Necessary for runtime submap loading - cp -r "$GITDIR/mods" . # Also necessary for runtime submap loading. TODO: a better solution + cp -r "$GITDIR/mods" . # Also necessary for runtime submap and NanoUI loading. TODO: a better solution? [[ ! -e btime.so && -e "$GITDIR/btime.so" ]] && cp "$GITDIR/btime.so" . [[ ! -e .git/logs ]] && mkdir -p .git/logs cp "$GITDIR/.git/HEAD" ./.git/HEAD From ae35726b6f8356bffad45908c7026bdccbc0466b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 1 Jan 2025 00:51:08 -0500 Subject: [PATCH 0137/1331] Fix runtime from security states with fewer than two levels --- code/modules/security_levels/security_state.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/security_levels/security_state.dm b/code/modules/security_levels/security_state.dm index dfc0c78a94c2..2e997845d411 100644 --- a/code/modules/security_levels/security_state.dm +++ b/code/modules/security_levels/security_state.dm @@ -32,13 +32,13 @@ // Setup the high security level if(!(high_security_level in all_security_levels)) - high_security_level = all_security_levels[all_security_levels.len - 1] + high_security_level = all_security_levels[max(1, all_security_levels.len - 1)] high_security_level = GET_DECL(high_security_level) // Setup the highest standard security level if(highest_standard_security_level || isnull(highest_standard_security_level)) if(!(highest_standard_security_level in all_security_levels)) - highest_standard_security_level = all_security_levels[all_security_levels.len - 1] + highest_standard_security_level = all_security_levels[max(1, all_security_levels.len - 1)] highest_standard_security_level = GET_DECL(highest_standard_security_level) else highest_standard_security_level = null From 15239692021c8a7430db9d49235e0ec3cd48d055 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 2 Jan 2025 12:33:14 +1100 Subject: [PATCH 0138/1331] Automatic changelog generation for PR #4677 [ci skip] --- html/changelogs/AutoChangeLog-pr-4677.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4677.yml diff --git a/html/changelogs/AutoChangeLog-pr-4677.yml b/html/changelogs/AutoChangeLog-pr-4677.yml new file mode 100644 index 000000000000..81f376b535af --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4677.yml @@ -0,0 +1,5 @@ +author: MistakeNot4892 +changes: + - {tweak: Raw plant oil no longer works as lantern fuel; it must be mixed with powdered + graphite first.} +delete-after: true From 8dec28a7db45109a2088e19d8577ae91a1c479e5 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 31 May 2024 19:47:54 -0400 Subject: [PATCH 0139/1331] Add a hack for interior ambience and multiz daycycle --- code/controllers/subsystems/ambience.dm | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/code/controllers/subsystems/ambience.dm b/code/controllers/subsystems/ambience.dm index ad9d2a1c52b4..757f846c6402 100644 --- a/code/controllers/subsystems/ambience.dm +++ b/code/controllers/subsystems/ambience.dm @@ -63,10 +63,23 @@ SUBSYSTEM_DEF(ambience) // Grab what we need to set ambient light from our level handler. var/datum/level_data/level_data = SSmapping.levels_by_z[z] + var/daycycle_id = level_data.daycycle_id + // if we don't have a daycycle ourselves, and we're indoors because of a turf blocking us + // find the first daycycle above us to use + if(!outsideness && !daycycle_id && HasAbove(z)) + var/turf/above = src + var/datum/level_data/above_level_data + while ((above = GetAbove(above))) + if((above.z_flags & ZM_TERMINATOR) || !HasAbove(above.z)) + break + above_level_data = SSmapping.levels_by_z[above.z] + if(above_level_data.daycycle_id) + daycycle_id = above_level_data.daycycle_id + break // Check for daycycle ambience. - if(level_data.daycycle_id) - var/datum/daycycle/daycycle = SSdaycycle.get_daycycle(level_data.daycycle_id) + if(daycycle_id) + var/datum/daycycle/daycycle = SSdaycycle.get_daycycle(daycycle_id) var/new_power = daycycle?.current_period?.power if(!isnull(new_power)) if(new_power > 0) From 7e1d5459952c5747e7264a1c644d0c917ba2133c Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 3 Jan 2025 00:50:18 +0000 Subject: [PATCH 0140/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 12 ++++++------ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-4677.yml | 5 ----- 3 files changed, 10 insertions(+), 11 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4677.yml diff --git a/html/changelog.html b/html/changelog.html index b64a9c790dac..e3ca34984821 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    03 January 2025

    +

    MistakeNot4892 updated:

    +
      +
    • Raw plant oil no longer works as lantern fuel; it must be mixed with powdered graphite first.
    • +
    +

    23 December 2024

    ophelia v0.8 updated:

      @@ -100,12 +106,6 @@

      Neerti updated:

    • Microlasers added to mining drills no longer multiply ore out of the ground, but make the drill mine faster, proportionally increasing the energy usage.
    • Capacitors added to mining drills are less powerful.
    - -

    01 November 2024

    -

    MistakeNot4892 updated:

    -
      -
    • Curries, soups and stews have been rewritten, please refer to the codex for recipes.
    • -
    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 9e2102536fb4..894f308ccdbc 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14941,3 +14941,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - imageadd: added new dirt and mud tile sprites - imageadd: added new wooden chest sprites, by Doe - tweak: mud and soil plots are now properly greyscaled to soil material color +2025-01-03: + MistakeNot4892: + - tweak: Raw plant oil no longer works as lantern fuel; it must be mixed with powdered + graphite first. diff --git a/html/changelogs/AutoChangeLog-pr-4677.yml b/html/changelogs/AutoChangeLog-pr-4677.yml deleted file mode 100644 index 81f376b535af..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4677.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: MistakeNot4892 -changes: - - {tweak: Raw plant oil no longer works as lantern fuel; it must be mixed with powdered - graphite first.} -delete-after: true From e4e2ff429ec3bfe92d4eb71fdb34705dc5aba340 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 2 Jan 2025 22:28:36 -0500 Subject: [PATCH 0141/1331] Simplify kharmaan nymph contains_crystals check --- mods/species/ascent/mobs/nymph/nymph_inventory.dm | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/mods/species/ascent/mobs/nymph/nymph_inventory.dm b/mods/species/ascent/mobs/nymph/nymph_inventory.dm index 12396419807c..e79adb9931d0 100644 --- a/mods/species/ascent/mobs/nymph/nymph_inventory.dm +++ b/mods/species/ascent/mobs/nymph/nymph_inventory.dm @@ -1,13 +1,8 @@ -/mob/living/simple_animal/alien/kharmaan/proc/contains_crystals(var/obj/item/W) - for(var/mat in W.matter) - if(mat == /decl/material/solid/sand) - . += W.matter[mat] - else if(mat == /decl/material/solid/gemstone/crystal) - . += W.matter[mat] - else if(mat == /decl/material/solid/quartz) - . += W.matter[mat] - else if(mat == /decl/material/solid/glass) - . += W.matter[mat] +/mob/living/simple_animal/alien/kharmaan/proc/contains_crystals(var/obj/item/prop) + . += prop.matter[/decl/material/solid/sand] + . += prop.matter[/decl/material/solid/gemstone/crystal] + . += prop.matter[/decl/material/solid/quartz] + . += prop.matter[/decl/material/solid/glass] /datum/inventory_slot/gripper/mouth/nymph/ascent/equipped(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE, var/delete_old_item = TRUE) var/mob/living/simple_animal/alien/kharmaan/nimp = user From 3202ca28a1f12fca306ac462a1b5db345536ab37 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 2 Jan 2025 22:07:56 -0500 Subject: [PATCH 0142/1331] Rename supermatter material presets to exotic matter --- code/game/objects/items/blades/spear_improvised.dm | 2 +- code/modules/clothing/suits/armor/craftable.dm | 2 +- code/modules/power/fuel_assembly/fuel_assembly.dm | 2 +- code/modules/power/fusion/fusion_reactions.dm | 11 +++++------ 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/code/game/objects/items/blades/spear_improvised.dm b/code/game/objects/items/blades/spear_improvised.dm index d9846e8a1a05..9f1a1da24257 100644 --- a/code/game/objects/items/blades/spear_improvised.dm +++ b/code/game/objects/items/blades/spear_improvised.dm @@ -34,7 +34,7 @@ hilt_material = /decl/material/solid/organic/wood/oak force_binding_color = COLOR_GREEN -/obj/item/bladed/polearm/spear/improvised/supermatter +/obj/item/bladed/polearm/spear/improvised/exotic_matter material = /decl/material/solid/exotic_matter hilt_material = /decl/material/solid/organic/wood/ebony force_binding_color = COLOR_INDIGO diff --git a/code/modules/clothing/suits/armor/craftable.dm b/code/modules/clothing/suits/armor/craftable.dm index c73e38510fcb..824a7c5b553b 100644 --- a/code/modules/clothing/suits/armor/craftable.dm +++ b/code/modules/clothing/suits/armor/craftable.dm @@ -33,5 +33,5 @@ material = /decl/material/solid/gemstone/diamond /obj/item/clothing/suit/armor/crafted/gold material = /decl/material/solid/metal/gold -/obj/item/clothing/suit/armor/crafted/supermatter +/obj/item/clothing/suit/armor/crafted/exotic_matter material = /decl/material/solid/exotic_matter diff --git a/code/modules/power/fuel_assembly/fuel_assembly.dm b/code/modules/power/fuel_assembly/fuel_assembly.dm index 6958f2431d2c..0527ee149418 100644 --- a/code/modules/power/fuel_assembly/fuel_assembly.dm +++ b/code/modules/power/fuel_assembly/fuel_assembly.dm @@ -87,7 +87,7 @@ /obj/item/fuel_assembly/tritium material = /decl/material/gas/hydrogen/tritium -/obj/item/fuel_assembly/supermatter +/obj/item/fuel_assembly/exotic_matter material = /decl/material/solid/exotic_matter /obj/item/fuel_assembly/hydrogen diff --git a/code/modules/power/fusion/fusion_reactions.dm b/code/modules/power/fusion/fusion_reactions.dm index 73cffcc1529d..5ff951b477b0 100644 --- a/code/modules/power/fusion/fusion_reactions.dm +++ b/code/modules/power/fusion/fusion_reactions.dm @@ -92,7 +92,7 @@ minimum_reaction_temperature = 10000 // VERY UNIDEAL REACTIONS. -/decl/fusion_reaction/helium_supermatter +/decl/fusion_reaction/helium_exotic_matter p_react = /decl/material/solid/exotic_matter s_react = /decl/material/gas/helium energy_consumption = 0 @@ -101,7 +101,7 @@ instability = 20 * FUSION_PROCESSING_TIME_MULT hidden_from_codex = TRUE -/decl/fusion_reaction/helium_supermatter/handle_reaction_special(var/obj/effect/fusion_em_field/holder) +/decl/fusion_reaction/helium_exotic_matter/handle_reaction_special(var/obj/effect/fusion_em_field/holder) set waitfor = FALSE . = 1 var/datum/event/wormholes/WM = new /datum/event/wormholes(new /datum/event_meta(EVENT_LEVEL_MAJOR)) @@ -112,8 +112,7 @@ qdel(holder) var/radiation_level = rand(100, 200) - // Copied from the SM for proof of concept. //Not any more --Cirra //Use the whole z proc --Leshana - SSradiation.z_radiate(locate(1, 1, holder.z), radiation_level, 1) + SSradiation.z_radiate(origin, radiation_level, respect_maint = TRUE) for(var/mob/living/human/H in global.living_mob_list_) var/turf/T = get_turf(H) @@ -121,10 +120,10 @@ H.set_hallucination(rand(100,150), 51) for(var/obj/machinery/fusion_fuel_injector/I in range(world.view, origin)) - if(I.cur_assembly && I.cur_assembly.material && I.cur_assembly.material.type == /decl/material/solid/exotic_matter) + if(I.cur_assembly && I.cur_assembly.material && I.cur_assembly.material.type == p_react) explosion(get_turf(I), 1, 2, 3) if(!QDELETED(I)) - QDEL_IN(I, 5) + addtimer(CALLBACK(I, TYPE_PROC_REF(/atom, physically_destroyed)), 0.5 SECONDS) sleep(5) explosion(origin, 1, 2, 5) From 070c898c79548cecc678956fa38f9eefb086c89d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 2 Jan 2025 22:20:36 -0500 Subject: [PATCH 0143/1331] Improve modpack modularity for niche edge cases --- code/game/gamemodes/endgame/endgame.dm | 1 + .../endgame/supermatter_cascade/universe.dm | 3 -- code/game/gamemodes/game_mode.dm | 16 +++--- .../objects/items/weapons/secrets_disk.dm | 42 +++++++++++---- code/modules/admin/verbs/grief_fixers.dm | 54 ++++++++++++++----- 5 files changed, 83 insertions(+), 33 deletions(-) diff --git a/code/game/gamemodes/endgame/endgame.dm b/code/game/gamemodes/endgame/endgame.dm index 6d81b42d639f..c1c03164c55a 100644 --- a/code/game/gamemodes/endgame/endgame.dm +++ b/code/game/gamemodes/endgame/endgame.dm @@ -1,3 +1,4 @@ +var/global/universe_has_ended = 0 /********************** * ENDGAME STUFF **********************/ diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index e9b2d2e40fd4..507097a121eb 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -1,6 +1,3 @@ -var/global/universe_has_ended = 0 - - /datum/universal_state/supermatter_cascade name = "Supermatter Cascade" desc = "Unknown harmonance affecting universal substructure, converting nearby matter to supermatter." diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index be38f8871efc..c8cc812c4861 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -283,11 +283,10 @@ var/global/list/additional_antag_types = list() for(var/decl/special_role/antag in antag_templates) antag.reset_antag_selection() -/decl/game_mode/proc/announce_ert_disabled() - if(!ert_disabled) - return - - var/list/reasons = list( +/// Gets a list of default reasons for the ERT to be disabled. +/decl/game_mode/proc/possible_ert_disabled_reasons() + // This uses a static var so that modpacks can add default reasons, e.g. "supermatter dust". + var/static/list/reasons = list( "political instability", "quantum fluctuations", "hostile raiders", @@ -319,7 +318,12 @@ var/global/list/additional_antag_types = list() "classified security operations", "a gargantuan glowing goat" ) - command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") + return reasons + +/decl/game_mode/proc/announce_ert_disabled() + if(!ert_disabled) + return + command_announcement.Announce("The presence of [pick(possible_ert_disabled_reasons())] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") /decl/game_mode/proc/check_finished() if(SSevac.evacuation_controller?.round_over() || station_was_nuked) diff --git a/code/game/objects/items/weapons/secrets_disk.dm b/code/game/objects/items/weapons/secrets_disk.dm index ee152b86151a..c5bbf89afb1b 100644 --- a/code/game/objects/items/weapons/secrets_disk.dm +++ b/code/game/objects/items/weapons/secrets_disk.dm @@ -13,19 +13,39 @@ This one has a lengthy legal label on it denoting it the private, copyrighted property of the Expeditionary Corps Organisation." req_access = list(access_rd) +/obj/item/disk/secret_project/proc/get_secret_project_codenames() + var/static/list/codenames = list( + "gamma", "delta", "epsilon", "zeta", "theta", "lambda", "omicron", "sigma", "tau", + "upsilon", "omega", "echelon", "prism", "calypso", "bernoulli", "harmony", "nyx", "fresnel" + ) + +/obj/item/disk/secret_project/proc/get_secret_project_types() + var/static/list/types = list( + "an experimental design for", + "a blueprint to build", + "a long set of theoretical formulas detailing the functioning of" + ) + return types + +/obj/item/disk/secret_project/proc/get_secret_project_nouns() + var/static/list/nouns = list( + "a superluminal artillery cannon", "a supermatter engine", "a fusion engine", "an atmospheric scrubber",\ + "a human cloning pod", "a microwave oven", "a wormhole generator", "a laser carbine", "an energy pistol",\ + "a wormhole", "a teleporter", "a huge mining drill", "a strange spacecraft", "a space station",\ + "a sleek-looking fighter spacecraft", "a ballistic rifle", "an energy sword", "an inanimate carbon rod" + ) + return nouns + +/obj/item/disk/secret_project/proc/get_secret_project_descriptors() + var/static/list/descriptors = list( + "that is extremely powerful", "which is highly efficient", "which is incredibly compact", "created by aliens", + "that runs off of an exotic form of matter", "that runs off of hydrogen gas", "that just looks really cool" + ) + /obj/item/disk/secret_project/Initialize() . = ..() - var/codename = pick("gamma", "delta", "epsilon", "zeta", "theta", "lambda", "omicron", "sigma", "tau",\ - "upsilon", "omega", "echelon", "prism", "calypso", "bernoulli", "harmony", "nyx", "fresnel") - name = "'[codename]' project data disk" - subject = pick("an experimental design for", "a blueprint to build",\ - "a long set of theoretical formulas detailing the functioning of") - subject += " " + pick("a superluminal artillery cannon", "a supermatter engine", "a fusion engine", "an atmospheric scrubber",\ - "a human cloning pod", "a microwave oven", "a wormhole generator", "a laser carbine", "an energy pistol",\ - "a wormhole", "a teleporter", "a huge mining drill", "a strange spacecraft", "a space station",\ - "a sleek-looking fighter spacecraft", "a ballistic rifle", "an energy sword", "an inanimate carbon rod") - subject += " " + pick("that is extremely powerful", "which is highly efficient", "which is incredibly compact",\ - "that runs off of an exotic form of matter", "that runs off of hydrogen gas", "created by aliens", "that just looks really cool") + name = "'[pick(get_secret_project_codenames())]' project data disk" + subject = "[pick(get_secret_project_types())] [pick(get_secret_project_nouns())] [pick(get_secret_project_descriptors())]" /obj/item/disk/secret_project/examine(mob/user) . = ..() diff --git a/code/modules/admin/verbs/grief_fixers.dm b/code/modules/admin/verbs/grief_fixers.dm index 2394d2a8197f..03b4f40f77bd 100644 --- a/code/modules/admin/verbs/grief_fixers.dm +++ b/code/modules/admin/verbs/grief_fixers.dm @@ -4,35 +4,64 @@ if(!check_rights(R_ADMIN|R_DEBUG)) return - if(alert("WARNING: Executing this command will perform a full reset of atmosphere. All pipelines will lose any gas that may be in them, and all zones will be reset to contain air mix as on roundstart. The supermatter engine will also be stopped (to prevent overheat due to removal of coolant). Do not use unless the map is suffering serious atmospheric issues due to grief or bug.", "Full Atmosphere Reboot", "No", "Yes") == "No") + if(alert("WARNING: Executing this command will perform a full reset of atmosphere. All pipelines will lose any gas that may be in them, and all zones will be reset to contain air mix as on roundstart. This may require any atmos-based generators to shut down. Do not use unless the map is suffering serious atmospheric issues due to grief or bug.", "Full Atmosphere Reboot", "No", "Yes") == "No") return SSstatistics.add_field_details("admin_verb","FA") log_and_message_admins("Full atmosphere reset initiated by [usr].") - to_world("Initiating restart of atmosphere. The server may lag a bit.") + to_world(SPAN_DANGER("Initiating restart of atmosphere. The server may lag a bit.")) sleep(10) var/current_time = world.timeofday + var/list/steps = sortTim(decls_repository.get_decls_of_subtype_unassociated(/decl/atmos_grief_fix_step), /proc/cmp_decl_sort_value_asc) + var/step_count = length(steps) + for(var/step_index in 1 to length(step_count)) + var/decl/atmos_grief_fix_step/fix_step = steps[step_index] + to_chat(usr, "\[[step_index]/[step_count]\] - [fix_step.name].") + fix_step.act() + to_world(SPAN_DANGER("Atmosphere restart completed in [(world.timeofday - current_time)/(1 SECOND)] seconds.")) + +/decl/atmos_grief_fix_step + abstract_type = /decl/atmos_grief_fix_step + var/name + +/decl/atmos_grief_fix_step/proc/act() + return + +/decl/atmos_grief_fix_step/supermatter + name = "Supermatter depowered" + sort_order = 0 + +/decl/atmos_grief_fix_step/supermatter/act() // Depower the supermatter, as it would quickly blow up once we remove all gases from the pipes. for(var/obj/machinery/power/supermatter/S in SSmachines.machinery) S.power = 0 - to_chat(usr, "\[1/5\] - Supermatter depowered") +/decl/atmos_grief_fix_step/purge_pipenets + name = "All pipenets purged of gas" + sort_order = 1 + +/decl/atmos_grief_fix_step/purge_pipenets/act() // Remove all gases from all pipenets - for(var/net in SSmachines.pipenets) - var/datum/pipe_network/PN = net + for(var/datum/pipe_network/PN as anything in SSmachines.pipenets) for(var/datum/gas_mixture/G in PN.gases) - G.gas = list() + G.gas.Cut() G.update_values() - to_chat(usr, "\[2/5\] - All pipenets purged of gas.") +/decl/atmos_grief_fix_step/delete_zones + name = "All ZAS Zones removed" + sort_order = 2 +/decl/atmos_grief_fix_step/delete_zones/act() // Delete all zones. for(var/zone/Z in SSair.zones) Z.c_invalidate() - to_chat(usr, "\[3/5\] - All ZAS Zones removed.") +/decl/atmos_grief_fix_step/reset_turfs + name = "All turfs reset to roundstart values" + sort_order = 3 +/decl/atmos_grief_fix_step/reset_turfs/act() var/list/unsorted_overlays = list() var/list/all_gasses = decls_repository.get_decls_of_subtype(/decl/material/gas) for(var/id in all_gasses) @@ -41,12 +70,11 @@ for(var/turf/T in world) T.air = null - T.overlays.Remove(unsorted_overlays) T.zone = null - to_chat(usr, "\[4/5\] - All turfs reset to roundstart values.") +/decl/atmos_grief_fix_step/reboot_zas + name = "ZAS Rebooted" + sort_order = 4 +/decl/atmos_grief_fix_step/reboot_zas/act() SSair.reboot() - - to_chat(usr, "\[5/5\] - ZAS Rebooted") - to_world("Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds.") From 1bb2ab4ded41e6e412210da115421685200e52a3 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 2 Jan 2025 22:25:34 -0500 Subject: [PATCH 0144/1331] Fix hardcoded stage checks in singularity code --- code/modules/power/singularity/singularity.dm | 11 ++++----- .../power/singularity/singularity_events.dm | 11 ++++----- .../power/singularity/singularity_stages.dm | 23 +++++++++++++++++++ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 6482521407ea..c1c8ea8295ec 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -13,7 +13,7 @@ var/global/list/singularities = list() /// Category used for investigation entries relating to this atom. var/const/investigation_label = "singulo" - /// A list of events. Toxins is in here twice to double the chance of proccing. + /// A weighted list of events. var/static/list/singularity_events = list( /decl/singularity_event/empulse = 1, /decl/singularity_event/toxins = 2, @@ -63,7 +63,7 @@ var/global/list/singularities = list() /obj/effect/singularity/explosion_act(severity) SHOULD_CALL_PARENT(FALSE) - if(current_stage.stage_size == STAGE_SUPER)//IT'S UNSTOPPABLE + if(!current_stage.explosion_vulnerable)//IT'S UNSTOPPABLE return if(severity == 1) if(prob(25)) @@ -153,16 +153,13 @@ var/global/list/singularities = list() // Handle random events. if(prob(current_stage.event_chance)) - if(current_stage.stage_size >= STAGE_SUPER) - var/decl/singularity_event/wave_event = GET_DECL(/decl/singularity_event/supermatter_wave) - wave_event.handle_event(src) - var/decl/singularity_event/singularity_event = pickweight(singularity_events) + var/decl/singularity_event/singularity_event = current_stage.forced_event || pickweight(singularity_events) singularity_event = GET_DECL(singularity_event) singularity_event.handle_event(src) /obj/effect/singularity/proc/try_move(var/movement_dir, var/vertical_move) set waitfor = FALSE - if(current_stage.stage_size >= STAGE_FIVE)//The superlarge one does not care about things in its way + if(current_stage.ignore_obstacles)//The superlarge one does not care about things in its way step(src, movement_dir) if(!vertical_move) sleep(1) diff --git a/code/modules/power/singularity/singularity_events.dm b/code/modules/power/singularity/singularity_events.dm index e48a040231be..083fe4595737 100644 --- a/code/modules/power/singularity/singularity_events.dm +++ b/code/modules/power/singularity/singularity_events.dm @@ -7,10 +7,7 @@ /decl/singularity_event/nothing // Nothing happens. /decl/singularity_event/empulse/handle_event(obj/effect/singularity/source) - if(source.current_stage.stage_size != STAGE_SUPER) - empulse(source, 8, 10) - else - empulse(source, 12, 16) + empulse(source, source.current_stage.em_heavy_range, source.current_stage.em_light_range) /decl/singularity_event/toxins var/toxrange = 10 @@ -35,11 +32,11 @@ if(ishuman(M)) var/mob/living/human/H = M if(istype(H.get_equipped_item(slot_glasses_str), /obj/item/clothing/glasses/meson)) - if(source.current_stage.stage_size != STAGE_SUPER) + if(!source.current_stage.the_goggles_do_nothing) to_chat(H, SPAN_WARNING("You look directly into \the [source]. Good thing you had your protective eyewear on!")) continue to_chat(H, SPAN_WARNING("Your eyewear does absolutely nothing to protect you from \the [source]")) - to_chat(M, SPAN_DANGER("You look directly into \the [source] and feel [source.current_stage.stage_size == STAGE_SUPER ? "helpless" : "weak"].")) + to_chat(M, SPAN_DANGER("You look directly into \the [source] and feel [source.current_stage.mesmerize_text].")) M.apply_effect(3, STUN) M.visible_message(SPAN_DANGER("\The [M] stares blankly at \the [source]!")) @@ -51,4 +48,4 @@ else to_chat(M, SPAN_DANGER("You don't even have a moment to react as you are reduced to ashes by the intense radiation.")) M.dust() - SSradiation.radiate(source, rand(source.energy)) + SSradiation.radiate(source, rand(source.energy)) \ No newline at end of file diff --git a/code/modules/power/singularity/singularity_stages.dm b/code/modules/power/singularity/singularity_stages.dm index 831cacd48c5f..b95595f4839c 100644 --- a/code/modules/power/singularity/singularity_stages.dm +++ b/code/modules/power/singularity/singularity_stages.dm @@ -32,8 +32,22 @@ var/dissipation_energy_loss = 1 /// What is the percent chance of an event each tick? var/event_chance + /// Do we force a specific event when we proc events? + var/decl/singularity_event/forced_event = null /// Will we wander around? var/wander + /// Can explosions destroy the singularity? + var/explosion_vulnerable + /// What is the heavy range for the EM pulse event in this stage? + var/em_heavy_range = 8 + /// What is the light range for the EM pulse event in this stage? + var/em_light_range = 10 + /// What do characters feel when they're mesmerized during this stage? + var/mesmerize_text = "weak" + /// Do we ignore PPE for mesmerizing in this stage? + var/the_goggles_do_nothing = FALSE + /// Do we ignore obstacles in our way? + var/ignore_obstacles = FALSE /decl/singularity_stage/validate() . = ..() @@ -171,6 +185,7 @@ dissipates_over_time = FALSE //It cant go smaller due to e loss. wander = TRUE event_chance = 20 + ignore_obstacles = TRUE /decl/singularity_stage/stage_five/grow_to(obj/effect/singularity/source) source.visible_message(SPAN_DANGER("\The [source] has grown out of control!")) @@ -178,6 +193,7 @@ /decl/singularity_stage/stage_five/shrink_to(obj/effect/singularity/source) source.visible_message(SPAN_WARNING("\The [source] miraculously reduces in size and loses its supermatter properties.")) +/// A singularity that has the mass of a supermatter crystal. /decl/singularity_stage/stage_super name = "super gravitational singularity" desc = "A gravitational singularity with the properties of supermatter. It has the power to destroy worlds." @@ -193,7 +209,14 @@ consume_range = 5 dissipates_over_time = 0 //It cant go smaller due to e loss event_chance = 25 //Events will fire off more often. + forced_event = /decl/singularity_event/supermatter_wave wander = TRUE + explosion_vulnerable = FALSE + em_heavy_range = 12 + em_light_range = 16 + mesmerize_text = "helpless" + the_goggles_do_nothing = TRUE + ignore_obstacles = TRUE /decl/singularity_stage/stage_super/grow_to(obj/effect/singularity/source) source.visible_message(SPAN_SINISTER("You witness the creation of a destructive force that cannot possibly be stopped by human hands.")) From 767934966009a76523c111d9bf94e40b2eed7ba3 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 5 Jan 2025 12:41:25 +1100 Subject: [PATCH 0145/1331] Vomit and mud can now be tracked around the floor. --- code/_helpers/global_lists.dm | 11 +++- .../effects/decals/Cleanable/humans.dm | 26 +++------ .../objects/effects/decals/Cleanable/misc.dm | 13 ++++- code/game/objects/items/__item.dm | 45 ++++++++-------- code/game/objects/items/_item_materials.dm | 4 +- .../objects/items/weapons/swords_axes_etc.dm | 4 +- code/game/turfs/flooring/_flooring.dm | 6 +++ code/game/turfs/flooring/flooring_mud.dm | 9 ++++ code/game/turfs/floors/_floor.dm | 8 +++ code/game/turfs/turf.dm | 3 ++ code/modules/detectivework/tools/uvlight.dm | 7 +-- code/modules/mob/living/human/human.dm | 34 ------------ code/modules/mob/living/life.dm | 5 +- code/modules/mob/living/living.dm | 54 +++++++++++++++++-- maps/away/mining/mining-signal.dmm | 4 +- maps/away/smugglers/smugglers.dmm | 2 +- maps/away/unishi/unishi-2.dmm | 4 +- .../crashed_pod/crashed_pod.dmm | 2 +- .../deserted_lab/deserted_lab.dmm | 2 +- .../exoplanet_ruins/playablecolony/colony.dmm | 2 +- .../spider_nest/spider_nest.dmm | 4 +- maps/tradeship/tradeship-1.dmm | 2 +- 22 files changed, 150 insertions(+), 101 deletions(-) diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 84463762e677..7b98b5f58af5 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -107,4 +107,13 @@ var/global/list/bodytype_species_pairs = list() // A list of bodytypes -> specie . = global.playable_species /proc/get_bodytype_species_pairs() build_species_lists() - . = global.bodytype_species_pairs \ No newline at end of file + . = global.bodytype_species_pairs + +// Used to avoid constantly generating new lists during movement. +var/global/list/all_stance_limbs = list( + ORGAN_CATEGORY_STANCE, + ORGAN_CATEGORY_STANCE_ROOT +) +var/global/list/child_stance_limbs = list( + ORGAN_CATEGORY_STANCE +) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 3cc2dd4bf5e7..cbd3f166ddbb 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -91,26 +91,12 @@ /obj/effect/decal/cleanable/blood/Crossed(atom/movable/AM) if(!isliving(AM) || amount < 1) return - - var/mob/living/M = AM - var/obj/item/organ/external/l_foot = GET_EXTERNAL_ORGAN(M, BP_L_FOOT) - var/obj/item/organ/external/r_foot = GET_EXTERNAL_ORGAN(M, BP_R_FOOT) - var/hasfeet = l_foot && r_foot - - var/transferred_data = blood_data ? blood_data[pick(blood_data)] : null - var/obj/item/clothing/shoes/shoes = M.get_equipped_item(slot_shoes_str) - if(istype(shoes) && !M.buckled)//Adding blood to shoes - shoes.add_coating(chemical, amount, transferred_data) - else if (hasfeet)//Or feet - if(l_foot) - l_foot.add_coating(chemical, amount, transferred_data) - if(r_foot) - r_foot.add_coating(chemical, amount, transferred_data) - else if (M.buckled && istype(M.buckled, /obj/structure/bed/chair/wheelchair)) - var/obj/structure/bed/chair/wheelchair/W = M.buckled - W.bloodiness = 4 - - M.update_equipment_overlay(slot_shoes_str) + var/mob/living/walker = AM + if(istype(walker.buckled, /obj/structure/bed/chair/wheelchair)) + var/obj/structure/bed/chair/wheelchair/wheelchair = walker.buckled + wheelchair.bloodiness = 4 + else + walker.add_walking_contaminant(chemical, amount, (blood_data ? blood_data[pick(blood_data)] : null)) amount-- /obj/effect/decal/cleanable/blood/proc/dry() diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 2a22a171886e..6fe6ea7441a4 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -90,9 +90,20 @@ if(prob(75)) set_rotation(pick(90, 180, 270)) +/obj/effect/decal/cleanable/vomit/mapped/Initialize(ml, _age) + . = ..() + add_to_reagents(/decl/material/liquid/acid/stomach, rand(3,5)) + add_to_reagents(/decl/material/liquid/nutriment, rand(5,8)) + /obj/effect/decal/cleanable/vomit/on_update_icon() . = ..() - color = reagents.get_color() + color = reagents?.get_color() + +/obj/effect/decal/cleanable/vomit/Crossed(atom/movable/AM) + . = ..() + if(!QDELETED(src) && reagents?.total_volume >= 1 && isliving(AM)) + var/mob/living/walker = AM + walker.add_walking_contaminant(reagents, rand(2, 3)) /obj/effect/decal/cleanable/tomato_smudge name = "tomato smudge" diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index b02237a6b2f3..20c732782fa0 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -12,7 +12,8 @@ /// Set to false to skip state checking and never draw an icon on the mob (except when held) var/draw_on_mob_when_equipped = TRUE - var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite + /// this saves our blood splatter/coating overlay, which will be processed not to go over the edges of the sprite. + var/image/coating_overlay var/randpixel = 6 var/material_health_multiplier = 0.2 var/hitsound @@ -245,7 +246,7 @@ /obj/item/PopulateClone(obj/item/clone) clone = ..() clone.contaminated = contaminated - clone.blood_overlay = image(blood_overlay) + clone.coating_overlay = image(coating_overlay) clone.current_health = current_health //#TODO: once item damage in, check health! @@ -790,7 +791,7 @@ if(was_bloodied && !fluorescent) fluorescent = FLUORESCENT_GLOWS blood_color = COLOR_LUMINOL - blood_overlay.color = COLOR_LUMINOL + coating_overlay.color = COLOR_LUMINOL update_icon() /obj/item/add_blood(mob/living/M, amount = 2, list/blood_data) @@ -814,21 +815,21 @@ LAZYSET(blood_DNA, unique_enzymes, blood_type) return TRUE -var/global/list/_blood_overlay_cache = list() -var/global/icon/_item_blood_mask = icon('icons/effects/blood.dmi', "itemblood") -/obj/item/proc/generate_blood_overlay(force = FALSE) - if(blood_overlay && !force) +var/global/list/_coating_overlay_cache = list() +var/global/icon/_item_coating_mask = icon('icons/effects/blood.dmi', "itemblood") +/obj/item/proc/generate_coating_overlay(force = FALSE) + if(coating_overlay && !force) return var/cache_key = "[icon]-[icon_state]" - if(global._blood_overlay_cache[cache_key]) - blood_overlay = global._blood_overlay_cache[cache_key] + if(global._coating_overlay_cache[cache_key]) + coating_overlay = global._coating_overlay_cache[cache_key] return var/icon/I = new /icon(icon, icon_state) I.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) // Sets the icon RGB channel to pure white. - I.Blend(global._item_blood_mask, ICON_MULTIPLY) // Masks the blood overlay against the generated mask. - blood_overlay = image(I) - blood_overlay.appearance_flags |= NO_CLIENT_COLOR|RESET_COLOR - global._blood_overlay_cache[cache_key] = blood_overlay + I.Blend(global._item_coating_mask, ICON_MULTIPLY) // Masks the coating overlay against the generated mask. + coating_overlay = image(I) + coating_overlay.appearance_flags |= NO_CLIENT_COLOR|RESET_COLOR + global._coating_overlay_cache[cache_key] = coating_overlay /obj/item/proc/showoff(mob/user) for(var/mob/M in view(user)) @@ -1018,13 +1019,15 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /obj/item/proc/add_coating(reagent_type, amount, data) if(!coating) - coating = new/datum/reagents(10, src) - coating.add_reagent(reagent_type, amount, data) - - if(!blood_overlay) - generate_blood_overlay() - blood_overlay.color = coating.get_color() - + coating = new /datum/reagents(10, src) + if(ispath(reagent_type)) + coating.add_reagent(reagent_type, amount, data) + else if(istype(reagent_type, /datum/reagents)) + var/datum/reagents/source = reagent_type + source.trans_to_holder(coating, amount) + if(!coating_overlay) + generate_coating_overlay() + coating_overlay.color = coating.get_color() update_icon() /obj/item/proc/remove_coating(amount) @@ -1037,7 +1040,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /obj/item/clean(clean_forensics=TRUE) . = ..() QDEL_NULL(coating) - blood_overlay = null + coating_overlay = null if(clean_forensics) var/datum/extension/forensic_evidence/forensics = get_extension(src, /datum/extension/forensic_evidence) if(forensics) diff --git a/code/game/objects/items/_item_materials.dm b/code/game/objects/items/_item_materials.dm index 261a8c5f5d0d..ecf48b7029e8 100644 --- a/code/game/objects/items/_item_materials.dm +++ b/code/game/objects/items/_item_materials.dm @@ -5,8 +5,8 @@ if((material_alteration & MAT_FLAG_ALTERATION_COLOR) && material) alpha = 100 + material.opacity * 255 color = get_color() // avoiding set_color() here as that will set it on paint_color - if(blood_overlay) - add_overlay(blood_overlay) + if(coating_overlay) + add_overlay(coating_overlay) if(global.contamination_overlay && contaminated) add_overlay(global.contamination_overlay) diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index 124bfb02526c..25ecca6f8563 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -66,8 +66,8 @@ update_held_icon() /obj/item/telebaton/on_update_icon() - if(length(blood_DNA)) - generate_blood_overlay(TRUE) // Force recheck. + if(coating?.total_volume || blood_DNA) + generate_coating_overlay(TRUE) // Force recheck. . = ..() if(on) icon = 'icons/obj/items/weapon/telebaton_extended.dmi' diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index a02efe3cb841..8150d0abd34b 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -350,3 +350,9 @@ var/global/list/flooring_cache = list() /decl/flooring/proc/handle_turf_digging(turf/floor/target) return TRUE + +/decl/flooring/proc/turf_crossed(atom/movable/crosser) + return + +/decl/flooring/proc/can_show_footsteps(turf/target) + return TRUE diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index fd4866144b90..2b5fa2c5063d 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -19,6 +19,15 @@ return return ..() +/decl/flooring/mud/turf_crossed(atom/movable/crosser) + if(!isliving(crosser)) + return + var/mob/living/walker = crosser + walker.add_walking_contaminant(/decl/material/solid/soil, rand(2,3)) + +/decl/flooring/mud/can_show_footsteps(turf/target) + return FALSE // So we don't end up covered in a million footsteps that we provided. + /decl/flooring/dry_mud name = "dry mud" desc = "This was once mud, but forgot to keep hydrated." diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 8a5562d03ad1..e8ede4d119e8 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -297,3 +297,11 @@ /turf/floor/get_plant_growth_rate() var/decl/flooring/flooring = get_topmost_flooring() return flooring ? flooring.growth_value : ..() + +/turf/floor/Crossed(atom/movable/AM) + var/decl/flooring/flooring = get_topmost_flooring() + flooring?.turf_crossed(AM) + return ..() + +/turf/floor/can_show_footsteps() + return ..() && get_topmost_flooring()?.can_show_footsteps(src) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index bdd356e53223..cf4d6f67920d 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -822,6 +822,9 @@ if(IS_HOE(held) && can_dig_farm(held.material?.hardness)) LAZYDISTINCTADD(., /decl/interaction_handler/dig/farm) +/turf/proc/can_show_footsteps() + return simulated + /decl/interaction_handler/show_turf_contents name = "Show Turf Contents" expected_user_type = /mob diff --git a/code/modules/detectivework/tools/uvlight.dm b/code/modules/detectivework/tools/uvlight.dm index 1b5c3a86b71f..90152aa387fa 100644 --- a/code/modules/detectivework/tools/uvlight.dm +++ b/code/modules/detectivework/tools/uvlight.dm @@ -47,6 +47,7 @@ add_overlay(emissive_overlay(icon, "[icon_state]-on")) z_flags |= ZMM_MANGLE_PLANES +// TODO: does this even work with SSoverlays? /obj/item/uv_light/proc/clear_last_scan() if(scanned.len) for(var/atom/O in scanned) @@ -62,7 +63,7 @@ stored_alpha.Cut() if(reset_objects.len) for(var/obj/item/I in reset_objects) - I.overlays -= I.blood_overlay + I.overlays -= I.coating_overlay if(I.fluorescent == FLUORESCENT_GLOWING) I.fluorescent = FLUORESCENT_GLOWS reset_objects.Cut() @@ -86,6 +87,6 @@ A.alpha = use_alpha if(istype(A, /obj/item)) var/obj/item/O = A - if(O.was_bloodied && !(O.blood_overlay in O.overlays)) - O.overlays |= O.blood_overlay + if(O.was_bloodied && !(O.coating_overlay in O.overlays)) + O.overlays |= O.coating_overlay reset_objects |= O \ No newline at end of file diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 884babdc9164..38e6f1d6c10e 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -1071,40 +1071,6 @@ var/datum/appearance_descriptor/age = LAZYACCESS(bodytype.appearance_descriptors, "age") LAZYSET(appearance_descriptors, "age", (age ? age.sanitize_value(val) : 30)) -/mob/living/human/HandleBloodTrail(turf/T, old_loc) - // Tracking blood - var/obj/item/source - var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str) - if(istype(shoes)) - shoes.handle_movement(src, MOVING_QUICKLY(src)) - if(shoes.coating && shoes.coating.total_volume > 1) - source = shoes - else - for(var/foot_tag in list(BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/stomper = GET_EXTERNAL_ORGAN(src, foot_tag) - if(stomper && stomper.coating && stomper.coating.total_volume > 1) - source = stomper - if(!source) - species.handle_trail(src, T, old_loc) - return - - var/list/bloodDNA - var/bloodcolor - var/list/blood_data = REAGENT_DATA(source.coating, /decl/material/liquid/blood) - if(blood_data) - bloodDNA = list(blood_data[DATA_BLOOD_DNA] = blood_data[DATA_BLOOD_TYPE]) - else - bloodDNA = list() - bloodcolor = source.coating.get_color() - source.remove_coating(1) - update_equipment_overlay(slot_shoes_str) - - if(species.get_move_trail(src)) - T.AddTracks(species.get_move_trail(src),bloodDNA, dir, 0, bloodcolor) // Coming - if(isturf(old_loc)) - var/turf/old_turf = old_loc - old_turf.AddTracks(species.get_move_trail(src), bloodDNA, 0, dir, bloodcolor) // Going - /mob/living/human/remove_implant(obj/item/implant, surgical_removal = FALSE, obj/item/organ/external/affected) if((. = ..()) && !surgical_removal) shock_stage += 20 diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index fd868a66a6ce..446bbe39a059 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -585,8 +585,7 @@ if(!root_bodytype) return - var/static/list/all_stance_limbs = list(ORGAN_CATEGORY_STANCE, ORGAN_CATEGORY_STANCE_ROOT) - var/expected_limbs_for_bodytype = root_bodytype.get_expected_organ_count_for_categories(all_stance_limbs) + var/expected_limbs_for_bodytype = root_bodytype.get_expected_organ_count_for_categories(global.all_stance_limbs) if(expected_limbs_for_bodytype <= 0) return // we don't care about stance for whatever reason. @@ -598,7 +597,7 @@ var/found_limbs = 0 var/had_limb_pain = FALSE - for(var/obj/item/organ/external/limb in get_organs_by_categories(all_stance_limbs)) + for(var/obj/item/organ/external/limb in get_organs_by_categories(global.all_stance_limbs)) found_limbs++ var/add_stance_damage = 0 if(limb.is_malfunctioning()) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index fbb42a1db1a2..7685c69fda5d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1552,7 +1552,7 @@ default behaviour is: /mob/living/OnSimulatedTurfEntered(turf/T, old_loc) T.add_dirt(0.5) - HandleBloodTrail(T, old_loc) + handle_walking_tracks(T, old_loc) if(current_posture.prone) return @@ -1583,8 +1583,46 @@ default behaviour is: step(src, dir) sleep(1) -/mob/living/proc/HandleBloodTrail(turf/T, old_loc) - return +/mob/living/proc/handle_walking_tracks(turf/T, old_loc) + + if(!T.can_show_footsteps()) + return + + // Tracking blood or other contaminants + var/obj/item/source + var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str) + if(istype(shoes)) + shoes.handle_movement(src, MOVING_QUICKLY(src)) + if(shoes.coating && shoes.coating.total_volume > 1) + source = shoes + else + for(var/obj/item/organ/external/stomper in get_organs_by_categories(global.child_stance_limbs)) + if(stomper.coating?.total_volume > 1) + source = stomper + break + + var/decl/species/my_species = get_species() + if(!source) + my_species?.handle_trail(src, T, old_loc) + return + + var/list/bloodDNA + var/bloodcolor + var/list/blood_data = REAGENT_DATA(source.coating, /decl/material/liquid/blood) + if(blood_data) + bloodDNA = list(blood_data[DATA_BLOOD_DNA] = blood_data[DATA_BLOOD_TYPE]) + else + bloodDNA = list() + bloodcolor = source.coating.get_color() + source.remove_coating(1) + update_equipment_overlay(slot_shoes_str) + + var/use_move_trail = my_species?.get_move_trail(src) + if(use_move_trail) + T.AddTracks(use_move_trail, bloodDNA, dir, 0, bloodcolor) // Coming + if(isturf(old_loc)) + var/turf/old_turf = old_loc + old_turf.AddTracks(use_move_trail, bloodDNA, 0, dir, bloodcolor) // Going /mob/living/proc/handle_general_grooming(user, obj/item/grooming/tool) if(tool.grooming_flags & (GROOMABLE_BRUSH|GROOMABLE_COMB)) @@ -1944,3 +1982,13 @@ default behaviour is: /mob/living/proc/get_age() . = LAZYACCESS(appearance_descriptors, "age") || 30 + +/mob/living/proc/add_walking_contaminant(material_type, amount, data) + var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str) + if(istype(shoes)) + if(!buckled) + shoes.add_coating(material_type, amount, data) + else + for(var/obj/item/organ/external/limb in get_organs_by_categories(global.child_stance_limbs)) + limb.add_coating(material_type, amount, data) + update_equipment_overlay(slot_shoes_str) diff --git a/maps/away/mining/mining-signal.dmm b/maps/away/mining/mining-signal.dmm index 68863f236667..867c49b56143 100644 --- a/maps/away/mining/mining-signal.dmm +++ b/maps/away/mining/mining-signal.dmm @@ -1561,7 +1561,7 @@ /area/outpost/abandoned) "fs" = ( /obj/effect/decal/cleanable/dirt/visible, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /turf/floor/tiled/airless, /area/outpost/abandoned) "ft" = ( @@ -2428,7 +2428,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/red{ dir = 4 }, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, diff --git a/maps/away/smugglers/smugglers.dmm b/maps/away/smugglers/smugglers.dmm index 911447bf6122..1881db19743e 100644 --- a/maps/away/smugglers/smugglers.dmm +++ b/maps/away/smugglers/smugglers.dmm @@ -854,7 +854,7 @@ /turf/floor, /area/smugglers/dorms) "cp" = ( -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/random/medical/lite, /turf/floor/plating/airless, /area/smugglers/dorms) diff --git a/maps/away/unishi/unishi-2.dmm b/maps/away/unishi/unishi-2.dmm index 19aad746cee2..f31f2097fe4d 100644 --- a/maps/away/unishi/unishi-2.dmm +++ b/maps/away/unishi/unishi-2.dmm @@ -2283,7 +2283,7 @@ /turf/space, /area/unishi/smresearch) "go" = ( -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/item/defibrillator, /turf/floor/tiled/techfloor, /area/unishi/smresearch) @@ -2397,7 +2397,7 @@ /obj/machinery/atmospherics/pipe/simple/visible/black{ dir = 4 }, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm index b944f2498005..c7ef24bae56b 100644 --- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm +++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm @@ -119,7 +119,7 @@ "ap" = ( /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/effect/decal/cleanable/filth, /obj/structure/curtain/open/shower/engineering, /obj/structure/hygiene/toilet{ diff --git a/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm b/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm index f88a3a7043aa..1552d243610f 100644 --- a/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm +++ b/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm @@ -184,7 +184,7 @@ /turf/floor/tiled/white, /area/template_noop) "aM" = ( -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/item/chems/glass/paint/random, /obj/structure/closet/medical_wall/filled{ pixel_y = 32 diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index 6d33bc94adb7..c210e95884c6 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -4708,7 +4708,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 5 }, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /turf/floor/tiled/white/monotile, /area/map_template/colony/bathroom) "jl" = ( diff --git a/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm b/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm index dd4f13ac1092..c3c28c3bdc98 100644 --- a/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm +++ b/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm @@ -83,7 +83,7 @@ /obj/effect/decal/cleanable/blood, /obj/item/shard, /obj/effect/decal/cleanable/blood/drip, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /turf/floor/tiled/techfloor, /area/template_noop) "q" = ( @@ -242,7 +242,7 @@ /obj/effect/spider/stickyweb, /obj/random/voidsuit, /obj/random/loot, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/structure/extinguisher_cabinet{ pixel_x = -29; dir = 4 diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index 8c98a303b76a..8a068ace688f 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -3161,7 +3161,7 @@ dir = 1; pixel_y = -21 }, -/obj/effect/decal/cleanable/vomit, +/obj/effect/decal/cleanable/vomit/mapped, /obj/structure/hygiene/toilet{ dir = 1 }, From b98168e5c9f826b2395562d4e592b064be2c3485 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 13:23:45 -0500 Subject: [PATCH 0146/1331] Optimize ship mass calculation --- code/modules/overmap/ships/ship_physics.dm | 51 ++++++++++++++-------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/code/modules/overmap/ships/ship_physics.dm b/code/modules/overmap/ships/ship_physics.dm index 66e85a3f0b97..28e71cf1f5b1 100644 --- a/code/modules/overmap/ships/ship_physics.dm +++ b/code/modules/overmap/ships/ship_physics.dm @@ -36,31 +36,43 @@ /obj/effect/overmap/visitable/ship/proc/recalculate_vessel_mass() var/list/zones = list() + // for(var/turf/tile in area) is an implied in-world loop + // an in-world loop per area is very bad, so instead + // we do one in-world loop and check area + var/list/areas = list() + // create an associative list of area -> TRUE so that lookup is faster for(var/area/A in get_areas()) - - // Do not include space please - if(istype(A, world.area)) + if(istype(A, world.area)) // exclude the base area continue + areas[A] = TRUE + var/start_z = min(map_z) + var/end_z = max(map_z) + if(!start_z || !end_z) + return initial(vessel_mass) // This shouldn't happen ideally so just go with the initial vessel mass + for(var/z_level in start_z to end_z) + var/datum/level_data/z_data = SSmapping.levels_by_z[z_level] + for(var/turf/tile in block(z_data.level_inner_min_x, z_data.level_inner_min_y, z_level, z_data.level_inner_max_x, z_data.level_inner_max_y)) + var/area/tile_area = tile.loc + if(!tile_area || !areas[tile_area]) + continue - for(var/turf/T in A) - - if(!T.simulated || T.is_open()) + if(!tile.simulated || tile.is_open()) continue . += DEFAULT_TURF_MASS - if(istype(T, /turf/wall)) - var/turf/wall/W = T - if(W.material) - . += W.material.weight * 5 - if(W.reinf_material) - . += W.reinf_material.weight * 5 - if(W.girder_material) - . += W.girder_material.weight * 5 + if(istype(tile, /turf/wall)) + var/turf/wall/wall_tile = tile + if(wall_tile.material) + . += wall_tile.material.weight * 5 + if(wall_tile.reinf_material) + . += wall_tile.reinf_material.weight * 5 + if(wall_tile.girder_material) + . += wall_tile.girder_material.weight * 5 - if(T.zone) - zones |= T.zone + if(tile.zone) + zones[tile.zone] = TRUE // assoc list for fast deduplication - for(var/atom/movable/C in T) + for(var/atom/movable/C as anything in tile) // as anything is safe here since only movables can be in turf contents if(!C.simulated) continue . += C.get_mass() @@ -69,8 +81,9 @@ continue . += C2.get_mass() - for(var/zone/Z in zones) - . += Z.air.get_mass() + // loop over keys of all zones in the list + for(var/zone/zone as anything in zones) + . += zone.air.get_mass() // Convert kilograms to metric tonnes. . = . / 1000 \ No newline at end of file From 5deaa895f1c5dd5ec827c523a77b14ecc1e15631 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:05:26 -0500 Subject: [PATCH 0147/1331] Port TG's ref()less garbage collector --- code/__defines/qdel.dm | 6 ++ code/controllers/subsystems/garbage.dm | 104 ++++++++++++++++++------- code/datums/datum.dm | 9 ++- 3 files changed, 87 insertions(+), 32 deletions(-) diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index 48a9f0e09738..2c81f12ad7d1 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -15,6 +15,12 @@ #define GC_QUEUE_HARDDELETE 3 #define GC_QUEUE_COUNT 3 //increase this when adding more steps. +// Defines for the ssgarbage queue items +#define GC_QUEUE_ITEM_QUEUE_TIME 1 //! Time this item entered the queue +#define GC_QUEUE_ITEM_REF 2 //! Ref to the item +#define GC_QUEUE_ITEM_GCD_DESTROYED 3 //! Item's gc_destroyed var value. Used to detect ref reuse. +#define GC_QUEUE_ITEM_INDEX_COUNT 3 //! Number of item indexes, used for allocating the nested lists. Don't forget to increase this if you add a new queue item index + #define GC_QUEUED_FOR_HARD_DEL -1 #define GC_CURRENTLY_BEING_QDELETED -2 diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index 47c1be9e2452..c27fcb3238eb 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -130,30 +130,33 @@ SUBSYSTEM_DEF(garbage) lastlevel = level +// 1 from the hard reference in the queue, and 1 from `D` in the code below +#define REFS_WE_EXPECT 2 + //We do this rather then for(var/refID in queue) because that sort of for loop copies the whole list. //Normally this isn't expensive, but the gc queue can grow to 40k items, and that gets costly/causes overrun. - for (var/refidx in 1 to length(queue)) - var/refID = queue[refidx] - if (isnull(refID)) + for (var/i in 1 to length(queue)) + var/list/L = queue[i] + if (length(L) < GC_QUEUE_ITEM_INDEX_COUNT) count++ if (MC_TICK_CHECK) return continue - var/GCd_at_time = queue[refID] - if(GCd_at_time > cut_off_time) + var/queued_at_time = L[GC_QUEUE_ITEM_QUEUE_TIME] + if(queued_at_time > cut_off_time) break // Everything else is newer, skip them count++ - var/datum/D - D = locate(refID) + var/datum/D = L[GC_QUEUE_ITEM_REF] - if (isnull(D) || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake + // If that's all we've got, send er off + if (refcount(D) == REFS_WE_EXPECT) ++gcedlasttick ++totalgcs pass_counts[level]++ #ifdef TESTING - reference_find_on_fail -= refID //It's deleted we don't care anymore. + reference_find_on_fail -= ref(D) //It's deleted we don't care anymore. #endif if (MC_TICK_CHECK) return @@ -161,14 +164,18 @@ SUBSYSTEM_DEF(garbage) // Something's still referring to the qdel'd object. fail_counts[level]++ + switch (level) if (GC_QUEUE_CHECK) #ifdef TESTING + // Decides how many refs to look for (potentially) + // Based off the remaining and the ones we can account for + var/remaining_refs = refcount(D) - REFS_WE_EXPECT if(reference_find_on_fail[refID]) - D.find_references() + D.find_references(remaining_refs) #ifdef GC_FAILURE_HARD_LOOKUP else - D.find_references() + D.find_references(remaining_refs) #endif reference_find_on_fail -= refID #endif @@ -201,12 +208,14 @@ SUBSYSTEM_DEF(garbage) if (level > GC_QUEUE_COUNT) HardDelete(D) return - var/gctime = world.time - var/refid = "\ref[D]" + var/queue_time = world.time - D.gc_destroyed = gctime + if(D.gc_destroyed <= 0) // hasn't been queued yet, or is queued for harddel/actively being qdeleted + D.gc_destroyed = queue_time var/list/queue = queues[level] - queue[refid] = gctime + // not += for byond reasons + // we include D.gc_destroyed to skip things under the cutoff + queue[++queue.len] = list(queue_time, D, D.gc_destroyed) //this is mainly to separate things profile wise. /datum/controller/subsystem/garbage/proc/HardDelete(datum/D) @@ -378,7 +387,7 @@ SUBSYSTEM_DEF(garbage) return find_references() -/datum/proc/find_references() +/datum/proc/find_references(references_to_clear = INFINITY) running_find_references = type if(usr && usr.client) if(usr.client.running_find_references) @@ -406,13 +415,19 @@ SUBSYSTEM_DEF(garbage) normal_globals[global_var] = global.vars[global_var] DoSearchVar(normal_globals, "(global) -> ") //globals testing("Finished searching globals") + if(src.references_to_clear == 0) // Found all expected references! + return for(var/atom/atom_thing) //atoms DoSearchVar(atom_thing, "World -> [atom_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching atoms") for (var/datum/datum_thing) //datums DoSearchVar(datum_thing, "World -> [datum_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching datums") #ifndef FIND_REF_SKIP_CLIENTS @@ -420,6 +435,8 @@ SUBSYSTEM_DEF(garbage) // IT WILL CRASH!!! for (var/client/client_thing) //clients DoSearchVar(client_thing, "World -> [client_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching clients") #endif @@ -455,37 +472,47 @@ SUBSYSTEM_DEF(garbage) #define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref)-6) ) ) #define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) -/datum/proc/DoSearchVar(X, Xname, recursive_limit = 128) +/datum/proc/DoSearchVar(X, container_name, recursive_limit = 128) if(usr && usr.client && !usr.client.running_find_references) return if (!recursive_limit) return + if(references_to_clear == 0) + return #ifndef FIND_REF_NO_CHECK_TICK CHECK_TICK #endif if(istype(X, /datum)) - var/datum/D = X - if(D.last_find_references == last_find_references) + var/datum/datum_container = X + if(datum_container.last_find_references == last_find_references) return - D.last_find_references = last_find_references - var/list/L = D.vars + datum_container.last_find_references = last_find_references + var/list/vars_list = datum_container.vars - for(var/varname in L) + var/is_atom = FALSE + var/is_area = FALSE + if(isatom(datum_container)) + is_atom = TRUE + if(isarea(datum_container)) + is_area = TRUE + for(var/varname in vars_list) #ifndef FIND_REF_NO_CHECK_TICK CHECK_TICK #endif - if (varname == "vars") + //Fun fact, vis_locs don't count for references + if(varname == "vars" || (is_atom && (varname == "vis_locs" || varname == "overlays" || varname == "underlays" || varname == "filters" || varname == "verbs" || (is_area && varname == "contents")))) continue - var/variable = L[varname] + var/variable = vars_list[varname] if(variable == src) - testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]") + testing("Found [src.type] \ref[src] in [datum_container.type]'s [varname] var. [container_name]") + references_to_clear -= 1 else if(islist(variable)) - DoSearchVar(variable, "[Xname] -> [varname] (list)", recursive_limit-1) + DoSearchVar(variable, "[container_name] -> [varname] (list)", recursive_limit-1) else if(islist(X)) var/normal = IS_NORMAL_LIST(X) @@ -494,16 +521,33 @@ SUBSYSTEM_DEF(garbage) CHECK_TICK #endif if (I == src) - testing("Found [src.type] \ref[src] in list [Xname].") + testing("Found [src.type] \ref[src] in list [container_name].") + + // This is dumb as hell I'm sorry + // I don't want the garbage subsystem to count as a ref for the purposes of this number + // If we find all other refs before it I want to early exit, and if we don't I want to keep searching past it + var/ignore_ref = FALSE + var/list/queues = SSgarbage.queues + for(var/list/queue in queues) + if(potential_cache in queue) + ignore_ref = TRUE + break + if(ignore_ref) + testing("[container_name] does not count as a ref for our count") + else + references_to_clear -= 1 + if(references_to_clear == 0) + testing("All references to [type] \ref[src] found, exiting.") + return else if (I && !isnum(I) && normal) if(X[I] == src) - testing("Found [src.type] \ref[src] in list [Xname]\[[I]\]") + testing("Found [src.type] \ref[src] in list [container_name]\[[I]\]") else if(islist(X[I])) - DoSearchVar(X[I], "[Xname]\[[I]\]", recursive_limit-1) + DoSearchVar(X[I], "[container_name]\[[I]\]", recursive_limit-1) else if (islist(I)) var/list/Xlist = X - DoSearchVar(I, "[Xname]\[[Xlist.Find(I)]\] -> list", recursive_limit-1) + DoSearchVar(I, "[container_name]\[[Xlist.Find(I)]\] -> list", recursive_limit-1) #endif diff --git a/code/datums/datum.dm b/code/datums/datum.dm index a0aeed07808d..617802037037 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -12,7 +12,12 @@ #ifdef TESTING var/tmp/running_find_references + /// When was this datum last touched by a reftracker? + /// If this value doesn't match with the start of the search + /// We know this datum has never been seen before, and we should check it var/tmp/last_find_references = 0 + /// How many references we're trying to find when searching + var/tmp/references_to_clear = 0 #endif // Default implementation of clean-up code. @@ -54,11 +59,11 @@ cleanup_events(src) if(has_state_machine) - var/list/machines = global.state_machines["\ref[src]"] + var/list/machines = global.state_machines[src] if(length(machines)) for(var/base_type in machines) qdel(machines[base_type]) - global.state_machines -= "\ref[src]" + global.state_machines -= src return QDEL_HINT_QUEUE From 0c15e2dcc2f422c2cc58b0698f8284ad7db1876d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:05:46 -0500 Subject: [PATCH 0148/1331] Remove ID var from seed piles --- code/game/machinery/vending/_vending.dm | 6 +-- code/modules/hydroponics/seed_storage.dm | 69 +++++++++--------------- 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index 866062f2a54b..b3036e173176 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -309,10 +309,8 @@ if (href_list["vend"] && !currently_vending) var/key = text2num(href_list["vend"]) - if(!is_valid_index(key, product_records)) - return TOPIC_REFRESH - var/datum/stored_items/vending_products/R = product_records[key] - if(!istype(R)) + var/datum/stored_items/vending_products/R = LAZYACCESS(product_records, key) + if(!R) return TOPIC_REFRESH // This should not happen unless the request from NanoUI was bad diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 2d609cf629e3..840d453ce501 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -3,14 +3,12 @@ var/amount var/datum/seed/seed_type // Keeps track of what our seed is var/list/obj/item/seeds/seeds = list() // Tracks actual objects contained in the pile - var/ID -/datum/seed_pile/New(var/obj/item/seeds/O, var/ID) +/datum/seed_pile/New(var/obj/item/seeds/O) name = O.name amount = 1 seed_type = O.seed seeds += O - src.ID = ID /datum/seed_pile/proc/matches(var/obj/item/seeds/O) if (O.seed == seed_type) @@ -195,7 +193,8 @@ if ("soil" in scanner) dat += "NutriWater" dat += "NotesAmount" - for (var/datum/seed_pile/S in piles) + for (var/key in 1 to length(piles)) + var/datum/seed_pile/S = piles[key] var/datum/seed/seed = S.seed_type if(!seed) continue @@ -281,7 +280,7 @@ dat += "LUM " dat += "" dat += "[S.amount]" - dat += "Vend Purge" + dat += "Vend Purge" dat += "" dat += "" @@ -292,29 +291,26 @@ if (..()) return var/task = href_list["task"] - var/ID = text2num(href_list["id"]) + var/id = text2num(href_list["id"]) + var/datum/seed_pile/our_pile = LAZYACCESS(piles, id) - for (var/datum/seed_pile/N in piles) - if (N.ID == ID) - if (task == "vend") - var/obj/O = pick(N.seeds) - if (O) - --N.amount - N.seeds -= O - if (N.amount <= 0 || N.seeds.len <= 0) - piles -= N - qdel(N) - flick("[initial(icon_state)]-vend", src) - O.dropInto(loc) - else - piles -= N - qdel(N) - else if (task == "purge") - for (var/obj/O in N.seeds) - qdel(O) - piles -= N - qdel(N) - break + switch(task) + if ("vend") + var/obj/O = pick(our_pile.seeds) + if (O) + --our_pile.amount + our_pile.seeds -= O + if (our_pile.amount <= 0 || our_pile.seeds.len <= 0) + piles -= our_pile + qdel(our_pile) + flick("[initial(icon_state)]-vend", src) + O.dropInto(loc) + if ("purge") + QDEL_LIST(our_pile.seeds) + our_pile.seeds.Cut() + if(!length(our_pile.seeds)) + piles -= our_pile + QDEL_NULL(our_pile) updateUsrDialog() /obj/machinery/seed_storage/attackby(var/obj/item/O, var/mob/user) @@ -334,7 +330,7 @@ if (loaded) user.visible_message(SPAN_NOTICE("\The [user] puts the seeds from \the [O] into \the [src].")) else - to_chat(user, SPAN_WARNING("There are no seeds in \the [O.name].")) + to_chat(user, SPAN_WARNING("There are no seeds in \the [O].")) return TRUE return ..() @@ -349,28 +345,13 @@ O.loc?.storage?.remove_from_storage(null, O, src) O.forceMove(src) - var/newID = 0 for (var/datum/seed_pile/N in piles) if (N.matches(O)) ++N.amount N.seeds += (O) return - else if(N.ID >= newID) - newID = N.ID + 1 - piles += new /datum/seed_pile(O, newID) + piles += new /datum/seed_pile(O) flick("[initial(icon_state)]-vend", src) return - -/obj/machinery/seed_storage/cannot_transition_to(state_path, mob/user) - if(state_path == /decl/machine_construction/default/deconstructed) - var/alert = alert(user, "Are you certain you wish to deconstruct this? It will destroy all seeds stored inside!", "Deconstruct Warning", "Yes", "No") - if(alert != "Yes" || !CanPhysicallyInteract(user)) - return MCS_BLOCK - return ..() - -/obj/machinery/seed_storage/dismantle() - for(var/obj/item/seeds/seed in src) - qdel(seed) // ..() would dump them; this would cause lots of client lag. We did warn them above... - return ..() \ No newline at end of file From 6e2e8d7c0b6ddd61aaa5fc566e563c056c12edd7 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:06:20 -0500 Subject: [PATCH 0149/1331] Fix unnecessary UI updates on storage object destroy --- code/datums/extensions/storage/_storage.dm | 2 ++ code/datums/extensions/storage/_storage_ui.dm | 3 ++- code/game/atoms_init.dm | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/code/datums/extensions/storage/_storage.dm b/code/datums/extensions/storage/_storage.dm index c79e91a7357b..4ff70967e1c6 100644 --- a/code/datums/extensions/storage/_storage.dm +++ b/code/datums/extensions/storage/_storage.dm @@ -252,6 +252,8 @@ var/global/list/_test_storage_items = list() storage_ui?.on_insertion() /datum/storage/proc/update_ui_after_item_removal(obj/item/removed) + if(QDELETED(holder)) + return prepare_ui() storage_ui?.on_post_remove() diff --git a/code/datums/extensions/storage/_storage_ui.dm b/code/datums/extensions/storage/_storage_ui.dm index 6f4db924f328..a3e1f23bfcb5 100644 --- a/code/datums/extensions/storage/_storage_ui.dm +++ b/code/datums/extensions/storage/_storage_ui.dm @@ -216,7 +216,8 @@ closer.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST + cols + 1]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" /datum/storage_ui/default/proc/space_orient_objs() - + if(QDELETED(_storage?.holder)) // don't bother if we've been deleted + return var/baseline_max_storage_space = DEFAULT_BOX_STORAGE //storage size corresponding to 224 pixels var/storage_cap_width = 2 //length of sprite for start and end of the box representing total storage space var/stored_cap_width = 4 //length of sprite for start and end of the box representing the stored item diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm index 3a2f3b619c1d..d1cd533c7967 100644 --- a/code/game/atoms_init.dm +++ b/code/game/atoms_init.dm @@ -80,7 +80,7 @@ /atom/Destroy() // must be done before deletion // TODO: ADD PRE_DELETION OBSERVATION - if(isatom(loc) && loc.storage) + if(isatom(loc) && loc.storage && !QDELETED(loc.storage)) loc.storage.on_item_pre_deletion(src) UNQUEUE_TEMPERATURE_ATOM(src) QDEL_NULL(reagents) @@ -94,7 +94,7 @@ QDEL_NULL(atom_codex_ref) var/atom/oldloc = loc . = ..() - if(isatom(oldloc) && oldloc.storage) + if(isatom(oldloc) && oldloc.storage && !QDELETED(loc.storage)) oldloc.storage.on_item_post_deletion(src) // must be done after deletion // This might need to be moved onto a Del() override at some point. QDEL_NULL(storage) From 2dbbf4d3edb84f4359d0d1b3e76d2889fa9c9bc9 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:10:36 -0500 Subject: [PATCH 0150/1331] Optimize icon state checks via more caching --- code/game/objects/item_mob_overlay.dm | 28 +++++++++++++++---- code/modules/client/ui_styles/_ui_style.dm | 2 +- code/modules/economy/worth_currency.dm | 4 +-- code/modules/persistence/graffiti.dm | 6 ++-- code/modules/projectiles/ammunition.dm | 3 +- .../metadata/accessory_metadata_gradient.dm | 3 +- code/unit_tests/alt_appearances_test.dm | 3 +- code/unit_tests/closets.dm | 8 +++--- code/unit_tests/clothing.dm | 2 +- code/unit_tests/job_tests.dm | 8 +++--- code/unit_tests/mob_tests.dm | 6 ++-- 11 files changed, 44 insertions(+), 29 deletions(-) diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm index d6881129c230..fe253ea6f0bb 100644 --- a/code/game/objects/item_mob_overlay.dm +++ b/code/game/objects/item_mob_overlay.dm @@ -8,13 +8,14 @@ var/global/list/bodypart_to_slot_lookup_table = list( ) /obj/item/proc/reconsider_single_icon(var/update_icon) - use_single_icon = check_state_in_icon(ICON_STATE_INV, icon) || check_state_in_icon(ICON_STATE_WORLD, icon) + var/list/icon_states = get_states_in_icon_cached(icon) // pre-cache to make our up-to-three checks faster + // except now we only do two because i rewrote it + has_inventory_icon = use_single_icon = icon_states[ICON_STATE_INV] + if(!has_inventory_icon) + use_single_icon = icon_states[ICON_STATE_WORLD] if(use_single_icon) - has_inventory_icon = check_state_in_icon(ICON_STATE_INV, icon) icon_state = get_world_inventory_state() . = TRUE - else - has_inventory_icon = FALSE if(. || update_icon) update_icon() @@ -27,6 +28,23 @@ var/global/list/icon_state_cache = list() // isicon() is apparently quite expensive so short-circuit out early if we can. if(!istext(checkstate) || isnull(checkicon) || !(isfile(checkicon) || isicon(checkicon))) return FALSE + var/list/check = _fetch_icon_state_cache_entry(checkicon) // should never return null once we reach this point + return check[checkstate] + +/// A proc for getting an associative list of icon states in an icon. +/// Uses the same cache as check_state_in_icon. +/// Does not copy, MUST NOT BE MUTATED. +/proc/get_states_in_icon_cached(checkicon) /* as OD_MAP(text, OD_BOOL) */ + return _fetch_icon_state_cache_entry(checkicon) || list() + +/// get_states_in_icon_cached but it does a copy, so the return value can be mutated. +/proc/get_states_in_icon(checkicon) /* as OD_MAP(text, OD_BOOL) */ + var/list/out = get_states_in_icon_cached(checkicon) + return out.Copy() + +/proc/_fetch_icon_state_cache_entry(checkicon) + if(isnull(checkicon) || !(isfile(checkicon) || isicon(checkicon))) + return null var/checkkey = "\ref[checkicon]" var/list/check = global.icon_state_cache[checkkey] if(!check) @@ -34,7 +52,7 @@ var/global/list/icon_state_cache = list() for(var/istate in icon_states(checkicon)) check[istate] = TRUE global.icon_state_cache[checkkey] = check - . = check[checkstate] + return check /obj/item/proc/update_world_inventory_state() if(use_single_icon && has_inventory_icon) diff --git a/code/modules/client/ui_styles/_ui_style.dm b/code/modules/client/ui_styles/_ui_style.dm index 5e0a5ba34c41..1a5885cfac67 100644 --- a/code/modules/client/ui_styles/_ui_style.dm +++ b/code/modules/client/ui_styles/_ui_style.dm @@ -46,7 +46,7 @@ var/check_icon = icons[ui_key] var/list/missing_states = list() var/list/checking_states = states_to_check[ui_key] - var/list/remaining_states = icon_states(check_icon) + var/list/remaining_states = get_states_in_icon(check_icon) for(var/check_state in checking_states) remaining_states -= check_state if(!check_state_in_icon(check_state, check_icon)) diff --git a/code/modules/economy/worth_currency.dm b/code/modules/economy/worth_currency.dm index ed12b938e92c..9e5de940b128 100644 --- a/code/modules/economy/worth_currency.dm +++ b/code/modules/economy/worth_currency.dm @@ -70,13 +70,13 @@ if(!name_singular) . += "No singular name set." - var/list/coinage_states = icon_states(icon) + var/list/coinage_states = get_states_in_icon_cached(icon) // cache this to avoid excessive ref() usage for(var/datum/denomination/denomination in denominations) if(!istext(denomination.name)) . += "Non-text name found for '[denomination.type]'." else if(!(denomination.state in coinage_states)) . += "State '[denomination.state]' not found in icon file for '[denomination.type]'." - else if(denomination.mark && !(denomination.mark in coinage_states)) + else if(denomination.mark && !coinage_states[denomination.mark]) . += "Mark state '[denomination.mark]' not found in icon file for '[denomination.type]'." else if(!isnum(denomination.marked_value)) . += "Non-numerical denomination marked value found for '[denomination]'." diff --git a/code/modules/persistence/graffiti.dm b/code/modules/persistence/graffiti.dm index e9afbbad8d87..1ed8a188417e 100644 --- a/code/modules/persistence/graffiti.dm +++ b/code/modules/persistence/graffiti.dm @@ -14,10 +14,10 @@ var/author = "unknown" /obj/effect/decal/writing/Initialize(mapload, var/_age, var/_message, var/_author) - var/list/random_icon_states = icon_states(icon) + var/list/random_icon_states = get_states_in_icon(icon) for(var/obj/effect/decal/writing/W in loc) - random_icon_states.Remove(W.icon_state) - if(random_icon_states.len) + random_icon_states -= W.icon_state + if(length(random_icon_states)) icon_state = pick(random_icon_states) SSpersistence.track_value(src, /decl/persistence_handler/graffiti) . = ..(mapload) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index f163d0f64c76..a32f29099972 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -270,10 +270,9 @@ var/global/list/magazine_icondata_states = list() /proc/magazine_icondata_cache_add(var/obj/item/ammo_magazine/M) var/list/icon_keys = list() var/list/ammo_states = list() - var/list/states = icon_states(M.icon) for(var/i = 0, i <= M.max_ammo, i++) var/ammo_state = "[M.icon_state]-[i]" - if(ammo_state in states) + if(check_state_in_icon(ammo_state, M.icon)) icon_keys += i ammo_states += ammo_state diff --git a/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm b/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm index 8b5d7b48734c..d134b6782f83 100644 --- a/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm +++ b/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm @@ -19,8 +19,7 @@ var/list/selectable_labels_to_states = list() /decl/sprite_accessory_metadata/gradient/Initialize() - var/list/selectable = icon_states(icon) - for(var/state in selectable) + for(var/state in get_states_in_icon_cached(icon)) if(!selectable_states_to_labels[state]) selectable_states_to_labels[state] = capitalize(state) for(var/state in selectable_states_to_labels) diff --git a/code/unit_tests/alt_appearances_test.dm b/code/unit_tests/alt_appearances_test.dm index 1bfd0ed325e3..d1c4b22a8e80 100644 --- a/code/unit_tests/alt_appearances_test.dm +++ b/code/unit_tests/alt_appearances_test.dm @@ -20,9 +20,8 @@ for(var/ca_type in decls_repository.get_decl_paths_of_subtype(/decl/cardborg_appearance)) var/decl/cardborg_appearance/ca = ca_type - var/list/existing_icon_states = icon_states(initial(ca.icon)) var/icon_state = initial(ca.icon_state) - if(!(icon_state in existing_icon_states)) + if(!check_state_in_icon(icon_state, initial(ca.icon))) log_unit_test("Icon state [icon_state] is missing.") failed = TRUE if(failed) diff --git a/code/unit_tests/closets.dm b/code/unit_tests/closets.dm index 9bc236f8ff92..10688fd72317 100644 --- a/code/unit_tests/closets.dm +++ b/code/unit_tests/closets.dm @@ -29,18 +29,18 @@ if(!closet.base_icon) LAZYADD(bad_base_icon, "[closet.type]") else - var/list/base_states = icon_states(closet.base_icon) + var/list/base_states = get_states_in_icon_cached(closet.base_icon) for(var/thing in check_base_states) - if(!(thing in base_states)) + if(!base_states[thing]) LAZYADD(bad_base_state, "[closet.type] - [thing] - [closet.base_icon]") if(LAZYLEN(closet.decals) && !closet.decal_icon) LAZYADD(bad_decal_icon, "[closet.type]") else - var/list/decal_states = icon_states(closet.decal_icon) + var/list/decal_states = get_states_in_icon_cached(closet.decal_icon) for(var/thing in closet.decals) if(isnull(closet.decals[thing])) LAZYADD(bad_decal_colour, "[check_appearance] - [thing]") - if(!(thing in decal_states)) + if(!decal_states[thing]) LAZYADD(bad_decal_state, "[check_appearance] - [thing] - [closet.decal_icon]") if( \ diff --git a/code/unit_tests/clothing.dm b/code/unit_tests/clothing.dm index 2b3889695c33..8219e6d04188 100644 --- a/code/unit_tests/clothing.dm +++ b/code/unit_tests/clothing.dm @@ -99,7 +99,7 @@ generated_tokens += "[token][clothes.markings_state_modifier]" // Keep track of which states we've looked for or otherwise evaluated for later state checking. - var/list/check_states = icon_states(clothes.icon) + var/list/check_states = get_states_in_icon(clothes.icon) // Validate against the list of generated tokens. for(var/gen_token in generated_tokens) diff --git a/code/unit_tests/job_tests.dm b/code/unit_tests/job_tests.dm index b12951657343..858063c9bad5 100644 --- a/code/unit_tests/job_tests.dm +++ b/code/unit_tests/job_tests.dm @@ -42,20 +42,20 @@ var/failed_jobs = 0 var/failed_sanity_checks = 0 - var/job_huds = icon_states(global.using_map.id_hud_icons) + var/list/job_huds = get_states_in_icon_cached(global.using_map.id_hud_icons) - if(!("" in job_huds)) + if(!job_huds[""]) log_bad("Sanity Check - Missing default/unnamed HUD icon") failed_sanity_checks++ - if(!("hudunknown" in job_huds)) + if(!job_huds["hudunknown"]) log_bad("Sanity Check - Missing HUD icon: hudunknown") failed_sanity_checks++ for(var/job_name in SSjobs.titles_to_datums) var/datum/job/J = SSjobs.titles_to_datums[job_name] var/hud_icon_state = J.hud_icon - if(!(hud_icon_state in job_huds)) + if(!job_huds[hud_icon_state]) log_bad("[J.title] - Missing HUD icon: [hud_icon_state]") failed_jobs++ diff --git a/code/unit_tests/mob_tests.dm b/code/unit_tests/mob_tests.dm index 5867dd2ca904..0228ea803411 100644 --- a/code/unit_tests/mob_tests.dm +++ b/code/unit_tests/mob_tests.dm @@ -260,15 +260,15 @@ var/global/default_mobloc = null fail("[icon_file] is not a valid icon file.") return 1 - var/list/valid_states = icon_states(icon_file) + var/list/valid_states = get_states_in_icon_cached(icon_file) - if(!valid_states.len) + if(!length(valid_states)) return 1 for(var/i=1, i<=SSrobots.all_module_names.len, i++) var/modname = lowertext(SSrobots.all_module_names[i]) var/bad_msg = "[ascii_red]--------------- [modname]" - if(!(modname in valid_states)) + if(!valid_states[modname]) log_unit_test("[bad_msg] does not contain a valid icon state in [icon_file][ascii_reset]") failed=1 From 07cf2d127f2e6a06471002fd3229894ecda9a350 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:11:32 -0500 Subject: [PATCH 0151/1331] Fix bookcase icon state check --- code/game/objects/structures/bookcase.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/structures/bookcase.dm b/code/game/objects/structures/bookcase.dm index 06d58e357aab..944bcd3fe0fb 100644 --- a/code/game/objects/structures/bookcase.dm +++ b/code/game/objects/structures/bookcase.dm @@ -168,7 +168,7 @@ var/global/list/station_bookcases = list() add_overlay(book_overlay) var/page_state = "[book_overlay.icon_state]-pages" - if(check_state_in_icon(book_overlay.icon, page_state)) + if(check_state_in_icon(page_state, book_overlay.icon)) var/image/page_overlay = overlay_image(book_overlay.icon, page_state, COLOR_WHITE, RESET_COLOR) page_overlay.pixel_x = book_overlay.pixel_x page_overlay.pixel_y = book_overlay.pixel_y From a05b48bfd240f9eb9ad412be1f47ea92d56a681b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:12:40 -0500 Subject: [PATCH 0152/1331] Make state machine list use hardrefs --- code/datums/extensions/state_machine.dm | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/code/datums/extensions/state_machine.dm b/code/datums/extensions/state_machine.dm index 63e58f0dd671..edba84c7965d 100644 --- a/code/datums/extensions/state_machine.dm +++ b/code/datums/extensions/state_machine.dm @@ -3,16 +3,15 @@ var/global/list/state_machines = list() /proc/get_state_machine(var/datum/holder, var/base_type) if(istype(holder) && base_type && holder.has_state_machine) - var/list/machines = global.state_machines["\ref[holder]"] + var/list/machines = global.state_machines[holder] return islist(machines) && machines[base_type] /proc/add_state_machine(var/datum/holder, var/datum/state_machine/fsm_type) if(istype(holder) && fsm_type) - var/holder_ref = "\ref[holder]" - var/list/machines = global.state_machines[holder_ref] + var/list/machines = global.state_machines[holder] if(!islist(machines)) machines = list() - global.state_machines[holder_ref] = machines + global.state_machines[holder] = machines var/base_type = fsm_type::base_type if(!machines[base_type]) var/datum/state_machine/machine = new fsm_type(holder) @@ -22,12 +21,11 @@ var/global/list/state_machines = list() /proc/remove_state_machine(var/datum/holder, var/base_type) if(istype(holder) && base_type && holder.has_state_machine) - var/holder_ref = "\ref[holder]" - var/list/machines = global.state_machines[holder_ref] + var/list/machines = global.state_machines[holder] if(length(machines)) machines -= base_type if(!length(machines)) - global.state_machines -= holder_ref + global.state_machines -= holder holder.has_state_machine = FALSE return TRUE return FALSE From 7b614a7de91a275ee9c0d9435ce847ae4ae4a8ce Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:13:52 -0500 Subject: [PATCH 0153/1331] Fix unnecessary .name usage in text strings --- code/game/objects/items/blueprints.dm | 2 +- code/game/objects/items/devices/lightreplacer.dm | 2 +- code/game/objects/items/devices/paint_sprayer.dm | 2 +- code/game/objects/items/weapons/soap.dm | 8 ++++---- .../structures/stool_bed_chair_nest_sofa/wheelchair.dm | 2 +- code/modules/mechs/mech_wreckage.dm | 2 +- code/modules/mob/living/living_defense.dm | 2 +- code/modules/multiz/movement.dm | 2 +- code/modules/overmap/ships/landable.dm | 6 +++--- code/modules/paperwork/paper_bundle.dm | 2 +- code/modules/surgery/necrotic.dm | 6 +++--- code/modules/surgery/organs_internal.dm | 6 +++--- code/modules/surgery/robotics.dm | 4 ++-- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index c9c67e53fc72..5a082bb9180c 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -59,7 +59,7 @@ return FALSE name += " - [S.name]" - desc = "Blueprints of \the [S.name]. There is a \"Classified\" stamp and several coffee stains on it." + desc = "Blueprints of \the [S]. There is a \"Classified\" stamp and several coffee stains on it." valid_z_levels += S.map_z area_prefix = S.name return TRUE diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 3565259a08e1..34115f49b63e 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -110,7 +110,7 @@ if(!user.try_unequip(L)) return TRUE AddUses(1) - to_chat(user, "You insert \the [L.name] into \the [src]. You have [uses] light\s remaining.") + to_chat(user, "You insert \the [L] into \the [src]. You have [uses] light\s remaining.") qdel(L) return TRUE else diff --git a/code/game/objects/items/devices/paint_sprayer.dm b/code/game/objects/items/devices/paint_sprayer.dm index a9dbd8a0878b..88c1e1195d73 100644 --- a/code/game/objects/items/devices/paint_sprayer.dm +++ b/code/game/objects/items/devices/paint_sprayer.dm @@ -223,7 +223,7 @@ return FALSE if(!flooring.can_paint || F.is_floor_damaged()) - to_chat(user, SPAN_WARNING("\The [src] cannot paint \the [F.name].")) + to_chat(user, SPAN_WARNING("\The [src] cannot paint \the [F].")) return FALSE var/list/decal_data = decals[decal] diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm index c14047c71f62..7667753288a4 100644 --- a/code/game/objects/items/weapons/soap.dm +++ b/code/game/objects/items/weapons/soap.dm @@ -68,13 +68,13 @@ //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn var/cleaned = FALSE if(user.client && (target in user.client.screen)) - to_chat(user, SPAN_NOTICE("You need to take that [target.name] off before cleaning it.")) + to_chat(user, SPAN_NOTICE("You need to take \the [target] off before cleaning it.")) else if(istype(target,/obj/effect/decal/cleanable/blood)) - to_chat(user, SPAN_NOTICE("You scrub \the [target.name] out.")) + to_chat(user, SPAN_NOTICE("You scrub \the [target] out.")) target.clean() //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. cleaned = TRUE else if(istype(target,/obj/effect/decal/cleanable)) - to_chat(user, SPAN_NOTICE("You scrub \the [target.name] out.")) + to_chat(user, SPAN_NOTICE("You scrub \the [target] out.")) qdel(target) cleaned = TRUE else if(isturf(target) || istype(target, /obj/structure/catwalk)) @@ -90,7 +90,7 @@ to_chat(user, SPAN_NOTICE("You wet \the [src] in the sink.")) wet() else - to_chat(user, SPAN_NOTICE("You clean \the [target.name].")) + to_chat(user, SPAN_NOTICE("You clean \the [target].")) target.clean() //Clean bloodied atoms. Blood decals themselves need to be handled above. cleaned = TRUE diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm index caa3e14df932..67640a583fdf 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm @@ -141,7 +141,7 @@ user.visible_message("[user] starts to lay out \the [src].") if(do_after(user, 4 SECONDS, src)) var/obj/structure/bed/chair/wheelchair/W = new structure_form_type(get_turf(user)) - user.visible_message(SPAN_NOTICE("[user] lays out \the [W.name].")) + user.visible_message(SPAN_NOTICE("[user] lays out \the [W].")) W.add_fingerprint(user) qdel(src) diff --git a/code/modules/mechs/mech_wreckage.dm b/code/modules/mechs/mech_wreckage.dm index b22b39231075..a78f4c7596c2 100644 --- a/code/modules/mechs/mech_wreckage.dm +++ b/code/modules/mechs/mech_wreckage.dm @@ -12,7 +12,7 @@ /obj/structure/mech_wreckage/Initialize(mapload, var/mob/living/exosuit/exosuit, var/gibbed) . = ..(mapload) if(exosuit) - name = "wreckage of \the [exosuit.name]" + name = "wreckage of \the [exosuit]" loot_pool = list() if(!gibbed) for(var/obj/item/thing in list(exosuit.arms, exosuit.legs, exosuit.head, exosuit.body)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index a74526afdd40..4cacbc889af7 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -189,7 +189,7 @@ visible_message(SPAN_NOTICE("\The [O] misses \the [src] narrowly!")) return FALSE - visible_message(SPAN_DANGER("\The [src] is hit [affecting ? "in \the [affecting.name] " : ""]by \the [O]!")) + visible_message(SPAN_DANGER("\The [src] is hit [affecting ? "in \the [affecting] " : ""]by \the [O]!")) if(TT?.thrower?.client) admin_attack_log(TT.thrower, src, "Threw \an [O] at the victim.", "Had \an [O] thrown at them.", "threw \an [O] at") try_embed_in_mob(TT.thrower, O, zone, throw_damage, dtype, null, affecting, direction = TT.init_dir) diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index a30099212492..585f57d0d7e3 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -225,7 +225,7 @@ for(var/mob/living/M in landing.contents) if(M == src) continue - visible_message("\The [src] hits \the [M.name]!") + visible_message("\The [src] hits \the [M]!") M.take_overall_damage(fall_damage) return TRUE return FALSE diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm index 375e4253ec21..24f4d999c0ac 100644 --- a/code/modules/overmap/ships/landable.dm +++ b/code/modules/overmap/ships/landable.dm @@ -231,14 +231,14 @@ if(SHIP_STATUS_LANDED) var/obj/effect/overmap/visitable/location = loc if(istype(loc, /obj/effect/overmap/visitable/sector)) - return "Landed on \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Landed on \the [location]. Use secondary thrust to get clear before activating primary engines." if(istype(loc, /obj/effect/overmap/visitable/ship)) - return "Docked with \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Docked with \the [location]. Use secondary thrust to get clear before activating primary engines." return "Docked with an unknown object." if(SHIP_STATUS_ENCOUNTER) var/datum/shuttle/autodock/overmap/child_shuttle = SSshuttle.shuttles[shuttle] var/obj/effect/overmap/visitable/location = global.overmap_sectors[num2text(child_shuttle.current_location.z)] - return "Maneuvering nearby \the [location.name]." + return "Maneuvering nearby \the [location]." if(SHIP_STATUS_TRANSIT) return "Maneuvering under secondary thrust." if(SHIP_STATUS_OVERMAP) diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index bf75bc608465..b01ab3044261 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -40,7 +40,7 @@ // merging bundles else if(istype(W, /obj/item/paper_bundle) && merge(W, user, cur_page)) - to_chat(user, SPAN_NOTICE("You add \the [W.name] to \the [name].")) + to_chat(user, SPAN_NOTICE("You add \the [W] to \the [name].")) return TRUE // burning diff --git a/code/modules/surgery/necrotic.dm b/code/modules/surgery/necrotic.dm index 14b39b1f7395..6f41080ae0d7 100644 --- a/code/modules/surgery/necrotic.dm +++ b/code/modules/surgery/necrotic.dm @@ -133,15 +133,15 @@ var/list/dead_organs if(E.status & ORGAN_DEAD) var/image/radial_button = image(icon = E.icon, icon_state = E.icon_state) - radial_button.name = "Regenerate \the [E.name]" + radial_button.name = "Regenerate \the [E]" LAZYSET(dead_organs, E.organ_tag, radial_button) for(var/obj/item/organ/I in target.get_internal_organs()) if(I && (I.status & ORGAN_DEAD) && I.parent_organ == target_zone) if(!I.can_recover()) - to_chat(user, SPAN_WARNING("\The [I.name] is beyond saving.")) + to_chat(user, SPAN_WARNING("\The [I] is beyond saving.")) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Regenerate \the [I.name]" + radial_button.name = "Regenerate \the [I]" LAZYSET(dead_organs, I.organ_tag, radial_button) if(!LAZYLEN(dead_organs)) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index f6b9bffb28e3..916db17c1d2b 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -104,7 +104,7 @@ for(var/obj/item/organ/I in target.get_internal_organs()) if(I && !(I.status & ORGAN_CUT_AWAY) && I.parent_organ == target_zone) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Detach \the [I.name]" + radial_button.name = "Detach \the [I]" LAZYSET(attached_organs, I.organ_tag, radial_button) if(!LAZYLEN(attached_organs)) to_chat(user, SPAN_WARNING("You can't find any organs to separate.")) @@ -158,7 +158,7 @@ for(var/obj/item/organ/internal/I in affected.implants) if(I.status & ORGAN_CUT_AWAY) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Remove \the [I.name]" + radial_button.name = "Remove \the [I]" LAZYSET(removable_organs, I, radial_button) if(!LAZYLEN(removable_organs)) to_chat(user, SPAN_WARNING("You can't find any removable organs.")) @@ -336,7 +336,7 @@ for(var/obj/item/organ/I in (affected.implants|affected.internal_organs)) if(I.status & ORGAN_CUT_AWAY) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Attach \the [I.name]" + radial_button.name = "Attach \the [I]" LAZYSET(attachable_organs, I, radial_button) if(!LAZYLEN(attachable_organs)) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 44020605deee..8695ab984993 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -416,7 +416,7 @@ for(var/obj/item/organ/I in target.get_internal_organs()) if(I && !(I.status & ORGAN_CUT_AWAY) && !BP_IS_CRYSTAL(I) && I.parent_organ == target_zone) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Detach \the [I.name]" + radial_button.name = "Detach \the [I]" LAZYSET(attached_organs, I.organ_tag, radial_button) if(!LAZYLEN(attached_organs)) to_chat(user, SPAN_WARNING("There are no appropriate internal components to decouple.")) @@ -461,7 +461,7 @@ for(var/obj/item/organ/I in affected.implants) if ((I.status & ORGAN_CUT_AWAY) && BP_IS_PROSTHETIC(I) && !BP_IS_CRYSTAL(I) && (I.parent_organ == target_zone)) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Reattach \the [I.name]" + radial_button.name = "Reattach \the [I]" LAZYSET(removable_organs, I.organ_tag, radial_button) var/organ_to_replace = show_radial_menu(user, tool, removable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) if(!organ_to_replace) From 2d3c37fff214de57a6a36248996bc85fb3d46b40 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 16:19:54 -0500 Subject: [PATCH 0154/1331] Make NanoUI use hardrefs for src_object --- .../controllers/subsystems/processing/nano.dm | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/code/controllers/subsystems/processing/nano.dm b/code/controllers/subsystems/processing/nano.dm index dfaae9e7999e..dd358012c92a 100644 --- a/code/controllers/subsystems/processing/nano.dm +++ b/code/controllers/subsystems/processing/nano.dm @@ -37,11 +37,10 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return /nanoui Returns the found ui, or null if none exists */ /datum/controller/subsystem/processing/nano/proc/get_open_ui(mob/user, src_object, ui_key) - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key] || !open_uis[src_object_key][ui_key]) + if (!open_uis[src_object] || !open_uis[src_object][ui_key]) return - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) if (ui.user == user) return ui @@ -54,12 +53,11 @@ PROCESSING_SUBSYSTEM_DEF(nano) */ /datum/controller/subsystem/processing/nano/proc/update_uis(src_object) . = 0 - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key]) + if (!open_uis[src_object]) return - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/ui_key in open_uis[src_object]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) if(ui.src_object && ui.user && ui.src_object.nano_host()) ui.try_update(1) .++ @@ -78,12 +76,11 @@ PROCESSING_SUBSYSTEM_DEF(nano) if (!length(open_uis)) return - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key]) + if (!open_uis[src_object]) return - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/ui_key in open_uis[src_object]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) ui.close() // If it's missing src_object or user, we want to close it even more. .++ @@ -134,9 +131,9 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return nothing */ /datum/controller/subsystem/processing/nano/proc/ui_opened(datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - LAZYINITLIST(open_uis[src_object_key]) - LAZYDISTINCTADD(open_uis[src_object_key][ui.ui_key], ui) + var/src_object = ui.src_object + LAZYINITLIST(open_uis[src_object]) + LAZYDISTINCTADD(open_uis[src_object][ui.ui_key], ui) LAZYDISTINCTADD(ui.user.open_uis, ui) START_PROCESSING(SSnano, ui) @@ -149,18 +146,18 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return int 0 if no ui was removed, 1 if removed successfully */ /datum/controller/subsystem/processing/nano/proc/ui_closed(var/datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - if (!open_uis[src_object_key] || !open_uis[src_object_key][ui.ui_key]) + var/src_object = ui.src_object + if (!open_uis[src_object] || !open_uis[src_object][ui.ui_key]) return 0 // wasn't open STOP_PROCESSING(SSnano, ui) if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface) LAZYREMOVE(ui.user.open_uis, ui) - open_uis[src_object_key][ui.ui_key] -= ui - if(!length(open_uis[src_object_key][ui.ui_key])) - open_uis[src_object_key] -= ui.ui_key - if(!length(open_uis[src_object_key])) - open_uis -= src_object_key + open_uis[src_object][ui.ui_key] -= ui + if(!length(open_uis[src_object][ui.ui_key])) + open_uis[src_object] -= ui.ui_key + if(!length(open_uis[src_object])) + open_uis -= src_object return 1 /** From 3cbff133ab22ef1077133bd5df06b2891f3e16ad Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 17:37:06 -0500 Subject: [PATCH 0155/1331] Fix del the world with new SSgarbage queues --- code/unit_tests/del_the_world.dm | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/code/unit_tests/del_the_world.dm b/code/unit_tests/del_the_world.dm index 3a9c575e6991..9d3f151e81d7 100644 --- a/code/unit_tests/del_the_world.dm +++ b/code/unit_tests/del_the_world.dm @@ -64,8 +64,9 @@ if(!length(filter_queue)) filter_queue_finished = TRUE break - var/oldest_item = filter_queue[1] - var/qdel_time = filter_queue[oldest_item] + var/list/oldest_packet = filter_queue[1] + //Pull out the time we inserted at + var/qdel_time = oldest_packet[GC_QUEUE_ITEM_GCD_DESTROYED] if(qdel_time > start_time) // Everything is in the check queue now! filter_queue_finished = TRUE break @@ -89,9 +90,9 @@ garbage_queue_processed = TRUE break - var/oldest_packet = check_queue[1] - //Pull out the time we deld at - var/qdeld_at = check_queue[oldest_packet] + var/list/oldest_packet = filter_queue[1] + //Pull out the time we inserted at + var/qdeld_at = oldest_packet[GC_QUEUE_ITEM_GCD_DESTROYED] //If we've found a packet that got del'd later then we finished, then all our shit has been processed if(qdeld_at > start_time) garbage_queue_processed = TRUE From 0fa9e1fe424fde5993d1d2375db344e6534971ad Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 18:03:44 -0500 Subject: [PATCH 0156/1331] Fix harddel issues found by improved GC --- .../objects/items/devices/transfer_valve.dm | 21 +++++++++++++++++-- code/game/objects/items/weapons/ecigs.dm | 4 ++++ code/modules/assembly/assembly.dm | 7 ++----- code/modules/clothing/masks/voice.dm | 4 ++++ code/modules/mechs/equipment/_equipment.dm | 4 ---- code/modules/mechs/equipment/medical.dm | 2 +- code/modules/mechs/mech.dm | 5 ++++- code/modules/projectiles/ammunition.dm | 4 ++++ 8 files changed, 38 insertions(+), 13 deletions(-) diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index b83f35bca481..2f2506b6aa05 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -7,11 +7,27 @@ var/obj/item/tank/tank_one var/obj/item/tank/tank_two var/obj/item/assembly/attached_device - var/mob/attacher = null + var/weakref/attacher_ref = null var/valve_open = 0 var/toggle = 1 movable_flags = MOVABLE_FLAG_PROXMOVE +/obj/item/transfer_valve/Destroy() + if(!QDELETED(tank_one)) + QDEL_NULL(tank_one) + else + tank_one = null + if(!QDELETED(tank_two)) + QDEL_NULL(tank_two) + else + tank_two = null + if(!QDELETED(attached_device)) + QDEL_NULL(attached_device) + else + attached_device = null + attacher_ref = null + return ..() + /obj/item/transfer_valve/attackby(obj/item/item, mob/user) var/turf/location = get_turf(src) // For admin logs if(istype(item, /obj/item/tank)) @@ -56,7 +72,7 @@ global.bombers += "[key_name(user)] attached a [item] to a transfer valve." message_admins("[key_name_admin(user)] attached a [item] to a transfer valve. (JMP)") log_game("[key_name_admin(user)] attached a [item] to a transfer valve.") - attacher = user + attacher_ref = weakref(user) . = TRUE if(.) update_icon() @@ -189,6 +205,7 @@ var/area/A = get_area(bombturf) var/attacher_name = "" + var/mob/attacher = attacher_ref.resolve() if(!attacher) attacher_name = "Unknown" else diff --git a/code/game/objects/items/weapons/ecigs.dm b/code/game/objects/items/weapons/ecigs.dm index dc05320c0908..0a55a70a9509 100644 --- a/code/game/objects/items/weapons/ecigs.dm +++ b/code/game/objects/items/weapons/ecigs.dm @@ -22,6 +22,10 @@ ec_cartridge = new cartridge_type(src) . = ..() +/obj/item/clothing/mask/smokable/ecig/Destroy() + QDEL_NULL(ec_cartridge) + return ..() + /obj/item/clothing/mask/smokable/ecig/setup_power_supply(loaded_cell_type, accepted_cell_type, power_supply_extension_type, charge_value) loaded_cell_type = loaded_cell_type || /obj/item/cell/device/standard accepted_cell_type = accepted_cell_type || /obj/item/cell/device diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index a4cf742c410f..47dcc913a450 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -12,7 +12,7 @@ var/secured = 1 var/list/attached_overlays = null - var/obj/item/assembly_holder/holder = null + var/obj/item/assembly_holder/holder = null // currently can be a TTV or assemblyholder, todo make ttv use assemblyholder var/cooldown = 0//To prevent spam var/wires = WIRE_RECEIVE | WIRE_PULSE @@ -24,10 +24,7 @@ /obj/item/assembly/Destroy() if(!QDELETED(holder)) - if(holder.a_left == src) - holder.a_left = null - if(holder.a_right == src) - holder.a_right = null + // the holder has the responsibility to clear its associated vars on destroy QDEL_NULL(holder) else holder = null diff --git a/code/modules/clothing/masks/voice.dm b/code/modules/clothing/masks/voice.dm index edee4acd8eb8..8ecc47d727ce 100644 --- a/code/modules/clothing/masks/voice.dm +++ b/code/modules/clothing/masks/voice.dm @@ -30,3 +30,7 @@ /obj/item/clothing/mask/chameleon/voice/Initialize() . = ..() changer = new(src) + +/obj/item/clothing/mask/chameleon/voice/Destroy() + QDEL_NULL(changer) + return ..() diff --git a/code/modules/mechs/equipment/_equipment.dm b/code/modules/mechs/equipment/_equipment.dm index 03ba4a1ac813..9df6c6915979 100644 --- a/code/modules/mechs/equipment/_equipment.dm +++ b/code/modules/mechs/equipment/_equipment.dm @@ -71,10 +71,6 @@ owner = null canremove = TRUE -/obj/item/mech_equipment/Destroy() - owner = null - . = ..() - /obj/item/mech_equipment/proc/get_effective_obj() return src diff --git a/code/modules/mechs/equipment/medical.dm b/code/modules/mechs/equipment/medical.dm index 7c39947f122b..4aadea64370d 100644 --- a/code/modules/mechs/equipment/medical.dm +++ b/code/modules/mechs/equipment/medical.dm @@ -22,7 +22,7 @@ /obj/item/mech_equipment/sleeper/uninstalled() . = ..() - sleeper.go_out() + sleeper?.go_out() /obj/item/mech_equipment/sleeper/attack_self(var/mob/user) . = ..() diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/mech.dm index 034695077605..962f42e67771 100644 --- a/code/modules/mechs/mech.dm +++ b/code/modules/mechs/mech.dm @@ -157,10 +157,13 @@ hud_elements.Cut() for(var/hardpoint in hardpoints) - qdel(hardpoints[hardpoint]) + var/obj/item/equipment = hardpoints[hardpoint] // cache before removal + remove_system(hardpoint, null, force = TRUE) + QDEL_NULL(equipment) hardpoints.Cut() QDEL_NULL(access_card) + QDEL_NULL(radio) QDEL_NULL(arms) QDEL_NULL(legs) QDEL_NULL(head) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index a32f29099972..9230d872fd56 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -39,6 +39,10 @@ pixel_y = rand(-randpixel, randpixel) . = ..() +/obj/item/ammo_casing/Destroy() + QDEL_NULL(BB) + return ..() + //removes the projectile from the ammo casing /obj/item/ammo_casing/proc/expend() . = BB From e1c1aa06b0b89e9dc283a55152c2579a8f9ee63c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 18:05:54 -0500 Subject: [PATCH 0157/1331] Enable reftracking in CI --- code/__defines/_compile_options.dm | 13 +++++++++++++ code/__defines/qdel.dm | 4 ++-- code/controllers/subsystems/garbage.dm | 27 ++++++++++++++------------ code/datums/datum.dm | 2 +- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index d2f61e13dca5..0c60ba0a5ddb 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -1,3 +1,16 @@ // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. // 1 will enable set background. 0 will disable set background. #define BACKGROUND_ENABLED 0 + +// If REFTRACK_IN_CI is defined, the reftracker will run in CI. +#define REFTRACK_IN_CI +#if defined(REFTRACK_IN_CI) && defined(UNIT_TEST) && !defined(SPACEMAN_DMM) +#define REFTRACKING_ENABLED +#define GC_FAILURE_HARD_LOOKUP +#define FIND_REF_NO_CHECK_TICK +#endif + +// parity with previous behavior where TESTING enabled reftracking +#ifdef TESTING +#define REFTRACKING_ENABLED +#endif \ No newline at end of file diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index 2c81f12ad7d1..fa9cf0a8debe 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -5,8 +5,8 @@ #define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it. #define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference. #define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste. -#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if TESTING is not enabled in _compiler_options.dm. - //if TESTING is enabled, qdel will call this object's find_references() verb. +#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if REFTRACKING_ENABLED is not enabled in _compiler_options.dm. + //if REFTRACKING_ENABLED is enabled, qdel will call this object's find_references() verb. #define QDEL_HINT_IFFAIL_FINDREFERENCE 6 //Above but only if gc fails. //defines for the gc_destroyed var diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index c27fcb3238eb..d44e54106465 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(garbage) //Queue var/list/queues - #ifdef TESTING + #ifdef REFTRACKING_ENABLED var/list/reference_find_on_fail = list() #endif @@ -155,7 +155,7 @@ SUBSYSTEM_DEF(garbage) ++gcedlasttick ++totalgcs pass_counts[level]++ - #ifdef TESTING + #ifdef REFTRACKING_ENABLED reference_find_on_fail -= ref(D) //It's deleted we don't care anymore. #endif if (MC_TICK_CHECK) @@ -167,10 +167,11 @@ SUBSYSTEM_DEF(garbage) switch (level) if (GC_QUEUE_CHECK) - #ifdef TESTING + #ifdef REFTRACKING_ENABLED // Decides how many refs to look for (potentially) // Based off the remaining and the ones we can account for var/remaining_refs = refcount(D) - REFS_WE_EXPECT + var/refID = ref(D) if(reference_find_on_fail[refID]) D.find_references(remaining_refs) #ifdef GC_FAILURE_HARD_LOOKUP @@ -279,7 +280,7 @@ SUBSYSTEM_DEF(garbage) /datum/qdel_item/New(mytype) name = "[mytype]" -#ifdef TESTING +#ifdef REFTRACKING_ENABLED /proc/qdel_and_find_ref_if_fail(datum/D, force = FALSE) SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE qdel(D, force) @@ -337,7 +338,7 @@ SUBSYSTEM_DEF(garbage) return // Returning LETMELIVE after being told to force destroy // indicates the objects Destroy() does not respect force - #ifdef TESTING + #ifdef REFTRACKING_ENABLED if(!I.no_respect_force) PRINT_STACK_TRACE("WARNING: [D.type] has been force deleted, but is \ returning an immortal QDEL_HINT, indicating it does \ @@ -353,18 +354,19 @@ SUBSYSTEM_DEF(garbage) SSgarbage.HardQueue(D) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. SSgarbage.HardDelete(D) - if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. + if (QDEL_HINT_FINDREFERENCE)//qdel will, if REFTRACKING_ENABLED is enabled, display all references to this object, then queue the object for deletion. SSgarbage.Queue(D) - #ifdef TESTING - D.find_references() + #ifdef REFTRACKING_ENABLED + var/remaining_refs = refcount(D) - REFS_WE_EXPECT + D.find_references(remaining_refs) #endif if (QDEL_HINT_IFFAIL_FINDREFERENCE) SSgarbage.Queue(D) - #ifdef TESTING + #ifdef REFTRACKING_ENABLED SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE #endif else - #ifdef TESTING + #ifdef REFTRACKING_ENABLED if(!I.no_hint) PRINT_STACK_TRACE("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") #endif @@ -373,7 +375,7 @@ SUBSYSTEM_DEF(garbage) else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") -#ifdef TESTING +#ifdef REFTRACKING_ENABLED /datum/verb/find_refs() set category = "Debug" @@ -389,6 +391,7 @@ SUBSYSTEM_DEF(garbage) /datum/proc/find_references(references_to_clear = INFINITY) running_find_references = type + src.references_to_clear = references_to_clear if(usr && usr.client) if(usr.client.running_find_references) testing("CANCELLED search for references to a [usr.client.running_find_references].") @@ -529,7 +532,7 @@ SUBSYSTEM_DEF(garbage) var/ignore_ref = FALSE var/list/queues = SSgarbage.queues for(var/list/queue in queues) - if(potential_cache in queue) + if(X in queue) ignore_ref = TRUE break if(ignore_ref) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 617802037037..aa855c0b1e3b 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -10,7 +10,7 @@ /// Used to avoid unnecessary refstring creation in Destroy(). var/tmp/has_state_machine = FALSE -#ifdef TESTING +#ifdef REFTRACKING_ENABLED var/tmp/running_find_references /// When was this datum last touched by a reftracker? /// If this value doesn't match with the start of the search From 46e7debf87de4f2348dc979a7ac4fb3494c82053 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 20:12:09 -0500 Subject: [PATCH 0158/1331] Fix terminal GC fail from geothermal generator --- code/modules/power/geothermal/_geothermal.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/power/geothermal/_geothermal.dm b/code/modules/power/geothermal/_geothermal.dm index 5f76fa6dd0ff..3425a37cd2cd 100644 --- a/code/modules/power/geothermal/_geothermal.dm +++ b/code/modules/power/geothermal/_geothermal.dm @@ -148,6 +148,7 @@ var/global/const/MAX_GEOTHERMAL_PRESSURE = 12000 /obj/machinery/geothermal/Destroy() var/atom/last_loc = loc unset_vent() + connector = null . = ..() if(istype(last_loc)) propagate_refresh_neighbors(last_loc) From e509f48b75756aac2b0dc8b1fc6cdf561cf0fea9 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 20:12:32 -0500 Subject: [PATCH 0159/1331] Add additional safeguards to graphs --- code/datums/graph/graph.dm | 2 ++ code/unit_tests/graph_tests.dm | 15 ++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/code/datums/graph/graph.dm b/code/datums/graph/graph.dm index 249f513e419e..736a33f6a4e7 100644 --- a/code/datums/graph/graph.dm +++ b/code/datums/graph/graph.dm @@ -35,6 +35,8 @@ /datum/graph/proc/Connect(var/datum/node/node, var/list/neighbours, var/queue = TRUE) SHOULD_NOT_SLEEP(TRUE) SHOULD_NOT_OVERRIDE(TRUE) + if(QDELETED(src)) + CRASH("Attempted to connect node [node] to a qdeleted graph!") if(!istype(neighbours)) neighbours = list(neighbours) if(!length(neighbours)) diff --git a/code/unit_tests/graph_tests.dm b/code/unit_tests/graph_tests.dm index 66a939e3a8f5..1d1cd69b6219 100644 --- a/code/unit_tests/graph_tests.dm +++ b/code/unit_tests/graph_tests.dm @@ -480,7 +480,7 @@ /atom/movable/graph_test is_spawnable_type = FALSE var/datum/node/physical/node - var/list/neighoursByDirection = list() + var/list/neighboursByDirection = list() /atom/movable/graph_test/Initialize() . = ..() @@ -497,20 +497,21 @@ /atom/movable/graph_test/proc/Connect(atom/movable/graph_test/neighbour) var/direction = get_dir(src, neighbour) - neighoursByDirection[num2text(direction)] = neighbour - neighbour.neighoursByDirection[num2text(global.flip_dir[direction])] = src + neighboursByDirection[num2text(direction)] = neighbour + neighbour.neighboursByDirection[num2text(global.flip_dir[direction])] = src node.Connect(neighbour.node) /atom/movable/graph_test/CheckNodeNeighbours() // This is a lazy setup for ease of debugging // In a practical setup you'd preferably gather a list of neighbours to be disconnected and pass them in a single Disconnect-call // You'd possibly also verify the dir of this and neighbour nodes, to ensure that they're still facing each other properly - for(var/direction in neighoursByDirection) - var/atom/movable/graph_test/neighbour = neighoursByDirection[direction] + for(var/direction in neighboursByDirection) + var/atom/movable/graph_test/neighbour = neighboursByDirection[direction] var/turf/expected_loc = get_step(src, text2num(direction)) - if(neighbour.loc != expected_loc) + // can't connect in nullspace + if(isnull(neighbour.loc) || neighbour.loc != expected_loc) node.Disconnect(neighbour.node) - neighoursByDirection -= direction + neighboursByDirection -= direction return TRUE /datum/graph/testing From cf96ecaba700d43e9916216fffb0a2a13165d688 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 20:27:52 -0500 Subject: [PATCH 0160/1331] Fix GC fails from ballistic weapons --- code/modules/projectiles/guns/projectile.dm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 1466e138166a..8534b7ddc7af 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -49,6 +49,12 @@ ammo_magazine = new magazine_type(src) update_icon() +/obj/item/gun/projectile/Destroy() + chambered = null + loaded.Cut() + ammo_magazine = null + return ..() + /obj/item/gun/projectile/consume_next_projectile() if(!is_jammed && prob(jam_chance)) src.visible_message("\The [src] jams!") From b79e75145d3c944eb3951375de47d241abd45bc9 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 5 Jan 2025 20:30:44 -0500 Subject: [PATCH 0161/1331] Remove GC exceptions list --- code/unit_tests/del_the_world.dm | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/code/unit_tests/del_the_world.dm b/code/unit_tests/del_the_world.dm index 9d3f151e81d7..0ceff60ef7dd 100644 --- a/code/unit_tests/del_the_world.dm +++ b/code/unit_tests/del_the_world.dm @@ -7,15 +7,6 @@ var/turf/spawn_loc = get_safe_turf() var/list/cached_contents = spawn_loc.contents.Copy() - /// Types to except from GC checking tests. - var/list/gc_exceptions = list( - // I hate doing this, but until the graph tests are fixed by someone who actually understands them, - // this is the best I can do without breaking other stuff. - /datum/node/physical, - // Randomly fails to GC during CI, cause unclear. Remove this if the root cause is identified. - /obj/item/organ/external/chest - ) - var/list/ignore = typesof( // will error if the area already has one /obj/machinery/power/apc, @@ -110,8 +101,6 @@ //Alright, time to see if anything messed up var/list/cache_for_sonic_speed = SSgarbage.items for(var/path in cache_for_sonic_speed) - if(path in gc_exceptions) - continue var/datum/qdel_item/item = cache_for_sonic_speed[path] if(item.failures) failures += "[item.name] hard deleted [item.failures] times out of a total del count of [item.qdels]" From e8c033f8b0a56416f99cc8a47c3780a233d6b624 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 6 Jan 2025 00:13:17 -0500 Subject: [PATCH 0162/1331] Fix miscellaneous GC issues --- code/game/alpha_masks.dm | 2 +- code/game/atoms.dm | 4 +++- code/game/objects/auras/aura.dm | 1 + code/modules/mechs/mech.dm | 5 +++-- .../mob/living/simple_animal/hostile/retaliate/drone.dm | 4 ++++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/code/game/alpha_masks.dm b/code/game/alpha_masks.dm index 05b23f45eaa2..9c8a341a4823 100644 --- a/code/game/alpha_masks.dm +++ b/code/game/alpha_masks.dm @@ -57,7 +57,7 @@ var/global/list/_alpha_masks = list() // Proc called by /turf/Entered() to update a mob's mask overlay. /atom/movable/proc/update_turf_alpha_mask() set waitfor = FALSE - if(!simulated || updating_turf_alpha_mask) + if(!simulated || QDELETED(src) || updating_turf_alpha_mask) return updating_turf_alpha_mask = TRUE sleep(0) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 5dd985c6064c..d13760956e3b 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -145,10 +145,12 @@ /atom/proc/try_on_reagent_change() SHOULD_NOT_OVERRIDE(TRUE) set waitfor = FALSE - if(_reagent_update_started >= world.time) + if(QDELETED(src) ||_reagent_update_started >= world.time) return FALSE _reagent_update_started = world.time sleep(0) // Defer to end of tick so we don't drop subsequent reagent updates. + if(QDELETED(src)) + return return on_reagent_change() /atom/proc/on_reagent_change() diff --git a/code/game/objects/auras/aura.dm b/code/game/objects/auras/aura.dm index 8d1d35a0680c..5de18f3adf62 100644 --- a/code/game/objects/auras/aura.dm +++ b/code/game/objects/auras/aura.dm @@ -15,6 +15,7 @@ They should also be used for when you want to effect the ENTIRE mob, like having /obj/aura/Destroy() if(user) user.remove_aura(src) + removed() return ..() /obj/aura/proc/added_to(var/mob/living/target) diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/mech.dm index 962f42e67771..809f205d2bdc 100644 --- a/code/modules/mechs/mech.dm +++ b/code/modules/mechs/mech.dm @@ -157,8 +157,9 @@ hud_elements.Cut() for(var/hardpoint in hardpoints) - var/obj/item/equipment = hardpoints[hardpoint] // cache before removal - remove_system(hardpoint, null, force = TRUE) + var/obj/item/mech_equipment/equipment = hardpoints[hardpoint] + if(istype(equipment)) + equipment.uninstalled() QDEL_NULL(equipment) hardpoints.Cut() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index e3f5b4fe9ff0..c8de00b79d20 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -82,6 +82,10 @@ ion_trail.set_up(src) ion_trail.start() +/mob/living/simple_animal/hostile/malf_drone/Destroy() + QDEL_NULL(ion_trail) + return ..() + /mob/living/simple_animal/hostile/malf_drone/Process_Spacemove() return 1 From 75eac099ce1c2bea532fc9377dd047cad9cca48d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 6 Jan 2025 00:13:31 -0500 Subject: [PATCH 0163/1331] Fix graph GC --- code/datums/graph/graph.dm | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/code/datums/graph/graph.dm b/code/datums/graph/graph.dm index 736a33f6a4e7..1610a6b4397a 100644 --- a/code/datums/graph/graph.dm +++ b/code/datums/graph/graph.dm @@ -92,8 +92,7 @@ if(neighbours_to_disconnect) neighbours |= neighbours_to_disconnect - if(neighbours.len) - LAZYSET(pending_disconnections, node, neighbours) + LAZYSET(pending_disconnections, node, neighbours) if(queue) SSgraphs_update.Queue(src) @@ -195,8 +194,6 @@ neighbour_edges |= N LAZYCLEARLIST(pending_connections) - if(!LAZYLEN(pending_disconnections)) - return for(var/pending_node_disconnect in pending_disconnections) var/pending_edge_disconnects = pending_disconnections[pending_node_disconnect] @@ -213,7 +210,8 @@ if(!length(other_pending_edge_disconnects)) pending_disconnections -= connected_node - edges[pending_node_disconnect] -= pending_edge_disconnects + if(edges[pending_node_disconnect]) + edges[pending_node_disconnect] -= pending_edge_disconnects if(!length(edges[pending_node_disconnect])) edges -= pending_node_disconnect @@ -227,12 +225,15 @@ var/checked_nodes = list() var/list/nodes_to_traverse = list(root_node) while(length(nodes_to_traverse)) - var/node_to_check = nodes_to_traverse[nodes_to_traverse.len] + var/datum/node/node_to_check = nodes_to_traverse[nodes_to_traverse.len] nodes_to_traverse.len-- + if(QDELETED(node_to_check)) + continue checked_nodes += node_to_check nodes_to_traverse |= ((edges[node_to_check] || list()) - checked_nodes) - all_nodes -= checked_nodes - subgraphs[++subgraphs.len] = checked_nodes + if(length(checked_nodes)) + all_nodes -= checked_nodes + subgraphs[++subgraphs.len] = checked_nodes if(length(subgraphs) == 1) if(!length(nodes)) From 6ef7d8717779d2a7c08b3dc8bf1c6f428a1fdd9a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 6 Jan 2025 00:32:31 -0500 Subject: [PATCH 0164/1331] Fix hard-queued objects causing runtimes --- code/controllers/subsystems/garbage.dm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index d44e54106465..18f925703957 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -250,11 +250,6 @@ SUBSYSTEM_DEF(garbage) message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).") postpone(time) -/datum/controller/subsystem/garbage/proc/HardQueue(datum/D) - if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) - queues[GC_QUEUE_FILTER] += D - D.gc_destroyed = GC_QUEUED_FOR_HARD_DEL - /datum/controller/subsystem/garbage/Recover() if (istype(SSgarbage.queues)) for (var/i in 1 to SSgarbage.queues.len) @@ -351,7 +346,7 @@ SUBSYSTEM_DEF(garbage) SSgarbage.Queue(D) if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate() GC_CHECK_AM_NULLSPACE(D, "QDEL_HINT_HARDDEL") - SSgarbage.HardQueue(D) + SSgarbage.Queue(D, GC_QUEUE_HARDDELETE) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. SSgarbage.HardDelete(D) if (QDEL_HINT_FINDREFERENCE)//qdel will, if REFTRACKING_ENABLED is enabled, display all references to this object, then queue the object for deletion. From 8be553419f302977b75f38e72c011f759060a71d Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 7 Jan 2025 00:50:48 +0000 Subject: [PATCH 0165/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index e3ca34984821..357f4dcea1ff 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -98,14 +98,6 @@

    Penelope Haze updated:

  • Crayons are now slowly used up while writing on paper, at a rate of one charge per 25 characters. (Crayons have a default of 30 charges.)
  • Added new furniture to the Shaded Hills inn.
  • - -

    05 November 2024

    -

    Neerti updated:

    -
      -
    • Mining drill braces are now crafted from steel sheets directly.
    • -
    • Microlasers added to mining drills no longer multiply ore out of the ground, but make the drill mine faster, proportionally increasing the energy usage.
    • -
    • Capacitors added to mining drills are less powerful.
    • -
    From e00cd6f6c016cc8eb598ea922356bc1b301585c3 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 7 Jan 2025 18:14:07 +1100 Subject: [PATCH 0166/1331] Automatic changelog generation for PR #4695 [ci skip] --- html/changelogs/AutoChangeLog-pr-4695.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4695.yml diff --git a/html/changelogs/AutoChangeLog-pr-4695.yml b/html/changelogs/AutoChangeLog-pr-4695.yml new file mode 100644 index 000000000000..84dde3ff37c0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4695.yml @@ -0,0 +1,4 @@ +author: MistakeNot4892 +changes: + - {tweak: Mud and blood can now leave footprints.} +delete-after: true From 7ef6054f59dfdaf36f652ceb79af520f57a363c5 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 2 Jan 2025 22:06:54 -0500 Subject: [PATCH 0167/1331] Add a modpack compatibility patch system --- maps/exodus/exodus.dm | 35 ++++++++-------- maps/ministation/ministation.dm | 40 +++++++++---------- maps/modpack_testing/modpack_testing.dm | 38 +++++++++--------- maps/tradeship/tradeship.dm | 37 +++++++++-------- mods/content/fantasy/datum/overrides.dm | 22 ---------- mods/content/psionics/_psionics.dme | 2 - mods/content/standard_jobs/jobs/medical.dm | 9 ----- mods/gamemodes/cult/overrides.dm | 1 + mods/gamemodes/heist/_heist.dme | 8 ---- mods/gamemodes/mixed.dm | 7 ++++ mods/gamemodes/mixed/_mixed.dm | 2 - mods/gamemodes/mixed/_mixed.dme | 15 ------- mods/mobs/borers/_borers.dme | 5 --- mods/species/drakes/_drakes.dme | 5 --- mods/species/vox/_vox.dme | 1 - mods/~compatibility/patches/fantasy.dm | 4 ++ .../patches/fantasy/drake_fantasy.dm | 21 ++++++++++ .../patches/heist_vox.dm} | 2 - .../~compatibility/patches/mixed_gamemodes.dm | 12 ++++++ .../patches/mixed_gamemodes}/crossfire.dm | 0 .../patches/mixed_gamemodes}/siege.dm | 0 .../patches/mixed_gamemodes}/uprising.dm | 0 mods/~compatibility/patches/psionics.dm | 13 ++++++ .../patches/psionics/borer_psi.dm} | 4 +- .../patches/psionics/cult_psi.dm} | 7 ++-- .../patches/psionics/psi_jobs.dm | 6 +++ mods/~compatibility/readme.md | 22 ++++++++++ mods/~compatibility/~compatibility.dm | 16 ++++++++ nebula.dme | 1 + 29 files changed, 179 insertions(+), 156 deletions(-) create mode 100644 mods/gamemodes/mixed.dm delete mode 100644 mods/gamemodes/mixed/_mixed.dm delete mode 100644 mods/gamemodes/mixed/_mixed.dme create mode 100644 mods/~compatibility/patches/fantasy.dm create mode 100644 mods/~compatibility/patches/fantasy/drake_fantasy.dm rename mods/{species/vox/datum/heist_compatibility.dm => ~compatibility/patches/heist_vox.dm} (98%) create mode 100644 mods/~compatibility/patches/mixed_gamemodes.dm rename mods/{gamemodes/mixed => ~compatibility/patches/mixed_gamemodes}/crossfire.dm (100%) rename mods/{gamemodes/mixed => ~compatibility/patches/mixed_gamemodes}/siege.dm (100%) rename mods/{gamemodes/mixed => ~compatibility/patches/mixed_gamemodes}/uprising.dm (100%) create mode 100644 mods/~compatibility/patches/psionics.dm rename mods/{content/psionics/system/psionics/mob/borer_power.dm => ~compatibility/patches/psionics/borer_psi.dm} (97%) rename mods/{content/psionics/items/soulstone.dm => ~compatibility/patches/psionics/cult_psi.dm} (61%) create mode 100644 mods/~compatibility/patches/psionics/psi_jobs.dm create mode 100644 mods/~compatibility/readme.md create mode 100644 mods/~compatibility/~compatibility.dm diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 9372a9368a76..09ef9d18d9dd 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -1,38 +1,35 @@ #if !defined(USING_MAP_DATUM) - #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/heist/_heist.dme" - #include "../../mods/gamemodes/meteor/_meteor.dme" - #include "../../mods/gamemodes/ninja/_ninja.dme" - #include "../../mods/gamemodes/revolution/_revolution.dme" - #include "../../mods/gamemodes/traitor/_traitor.dme" - #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" - #include "../../mods/content/mundane.dm" + #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/corporate/_corporate.dme" #include "../../mods/content/government/_government.dme" #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" - #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/mobs/borers/_borers.dme" - - // Must come after borers for compatibility. - #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/gamemodes/cult/_cult.dme" + #include "../../mods/gamemodes/heist/_heist.dme" + #include "../../mods/gamemodes/meteor/_meteor.dme" + #include "../../mods/gamemodes/ninja/_ninja.dme" + #include "../../mods/gamemodes/revolution/_revolution.dme" + #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" + #include "../../mods/gamemodes/traitor/_traitor.dme" + #include "../../mods/gamemodes/mixed.dm" - // Must come after psionics for counsellor compatibility. - #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/mobs/dionaea/_dionaea.dme" #include "../../mods/species/ascent/_ascent.dme" - #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/serpentid/_serpentid.dme" #include "../../mods/species/utility_frames/_utility_frames.dme" - #include "../../mods/species/bayliens/_bayliens.dme" #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index 2ecca91fa60e..2f82716a80ce 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -15,31 +15,37 @@ Twice... #include "../../code/unit_tests/offset_tests.dm" #endif + #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" + + #include "../../mods/content/mundane.dm" + #include "../../mods/content/scaling_descriptors.dm" + + #include "../../mods/content/bigpharma/_bigpharma.dme" + #include "../../mods/content/corporate/_corporate.dme" + #include "../../mods/content/government/_government.dme" + #include "../../mods/content/matchmaking/_matchmaking.dme" + #include "../../mods/content/modern_earth/_modern_earth.dme" + #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" + #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/content/xenobiology/_xenobiology.dme" + #include "../../mods/gamemodes/cult/_cult.dme" #include "../../mods/gamemodes/heist/_heist.dme" #include "../../mods/gamemodes/ninja/_ninja.dme" #include "../../mods/gamemodes/revolution/_revolution.dme" #include "../../mods/gamemodes/traitor/_traitor.dme" #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" + #include "../../mods/gamemodes/mixed.dm" - #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" + #include "../../mods/mobs/dionaea/_dionaea.dme" + #include "../../mods/mobs/borers/_borers.dme" - #include "../../mods/content/standard_jobs/_standard_jobs.dme" - #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/content/corporate/_corporate.dme" - #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/species/ascent/_ascent.dme" + #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/neoavians/_neoavians.dme" - #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/species/serpentid/_serpentid.dme" - #include "../../mods/species/bayliens/_bayliens.dme" - #include "../../mods/content/mundane.dm" - #include "../../mods/content/bigpharma/_bigpharma.dme" - #include "../../mods/content/government/_government.dme" - #include "../../mods/content/modern_earth/_modern_earth.dme" - #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" - #include "../../mods/content/scaling_descriptors.dm" #include "ministation_antagonists.dm" #include "ministation_areas.dm" @@ -70,12 +76,6 @@ Twice... #include "../away/yacht/yacht.dm" #include "../away/liberia/liberia.dm" - #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/mobs/borers/_borers.dme" - - // Must come after borers for compatibility. - #include "../../mods/content/psionics/_psionics.dme" - #include "ministation_overmap.dm" #include "jobs/command.dm" diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index f94fbbc844c8..d7843b96d6b5 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -3,45 +3,43 @@ #include "modpack_testing_lobby.dm" #include "blank.dmm" - #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/heist/_heist.dme" - #include "../../mods/gamemodes/meteor/_meteor.dme" - #include "../../mods/gamemodes/ninja/_ninja.dme" - #include "../../mods/gamemodes/revolution/_revolution.dme" - #include "../../mods/gamemodes/traitor/_traitor.dme" - #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" - #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" - #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/byond_membership/_byond_membership.dm" #include "../../mods/content/corporate/_corporate.dme" + #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" + #include "../../mods/content/fantasy/_fantasy.dme" #include "../../mods/content/generic_shuttles/_generic_shuttles.dme" #include "../../mods/content/government/_government.dme" + #include "../../mods/content/inertia/_inertia.dme" #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" + #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/psionics/_psionics.dme" #include "../../mods/content/shackles/_shackles.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/content/pheromones/_pheromones.dme" - #include "../../mods/species/drakes/_drakes.dme" // include before _fantasy.dme so overrides work - #include "../../mods/content/fantasy/_fantasy.dme" - #include "../../mods/content/inertia/_inertia.dme" - #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/gamemodes/cult/_cult.dme" + #include "../../mods/gamemodes/heist/_heist.dme" + #include "../../mods/gamemodes/meteor/_meteor.dme" + #include "../../mods/gamemodes/ninja/_ninja.dme" + #include "../../mods/gamemodes/revolution/_revolution.dme" + #include "../../mods/gamemodes/traitor/_traitor.dme" + #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" + #include "../../mods/gamemodes/mixed.dm" - // Must come after borers for compatibility. - #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/species/serpentid/_serpentid.dme" #include "../../mods/species/ascent/_ascent.dme" + #include "../../mods/species/bayliens/_bayliens.dme" + #include "../../mods/species/drakes/_drakes.dme" #include "../../mods/species/neoavians/_neoavians.dme" + #include "../../mods/species/serpentid/_serpentid.dme" #include "../../mods/species/utility_frames/_utility_frames.dme" - #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/vox/_vox.dme" #define USING_MAP_DATUM /datum/map/modpack_testing diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 32eeb975e2ca..e13776a4276f 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -4,44 +4,43 @@ #include "../../code/unit_tests/offset_tests.dm" #endif - #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/heist/_heist.dme" - #include "../../mods/gamemodes/ninja/_ninja.dme" - #include "../../mods/gamemodes/revolution/_revolution.dme" - #include "../../mods/gamemodes/traitor/_traitor.dme" - #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" - #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" #include "../../mods/content/government/away_sites/icarus/icarus.dm" #include "../../mods/content/corporate/away_sites/lar_maria/lar_maria.dm" - #include "../../mods/content/standard_jobs/_standard_jobs.dme" - #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/mundane.dm" + #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/corporate/_corporate.dme" + #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/government/_government.dme" + #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" - #include "../../mods/content/scaling_descriptors.dm" - #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/gamemodes/cult/_cult.dme" + #include "../../mods/gamemodes/heist/_heist.dme" + #include "../../mods/gamemodes/ninja/_ninja.dme" + #include "../../mods/gamemodes/revolution/_revolution.dme" + #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" + #include "../../mods/gamemodes/traitor/_traitor.dme" + #include "../../mods/gamemodes/mixed.dm" - // Must come after borers for compatibility. - #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/mobs/dionaea/_dionaea.dme" #include "../../mods/species/ascent/_ascent.dme" + #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/drakes/_drakes.dme" + #include "../../mods/species/neoavians/_neoavians.dme" #include "../../mods/species/serpentid/_serpentid.dme" #include "../../mods/species/utility_frames/_utility_frames.dme" - #include "../../mods/species/neoavians/_neoavians.dme" - #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/vox/_vox.dme" #include "../away/bearcat/bearcat.dm" diff --git a/mods/content/fantasy/datum/overrides.dm b/mods/content/fantasy/datum/overrides.dm index 0726866427dc..6183c93b33c1 100644 --- a/mods/content/fantasy/datum/overrides.dm +++ b/mods/content/fantasy/datum/overrides.dm @@ -31,28 +31,6 @@ /decl/background_category/religion = /decl/background_detail/religion/other ) -// Rename grafadreka -/decl/species/grafadreka - name = "Meredrake" - name_plural = "Meredrakes" - description = "Meredrakes, sometimes called mire-drakes, are large reptillian pack predators, widely assumed to be cousins to true dragons. \ - They are commonly found living in caves or burrows bordering grassland or forest, and while they prefer to hunt deer or rabbits, they will sometimes attack travellers if pickings are slim enough. \ - While they are not domesticated, they can be habituated and trained as working animals if captured young enough." - -/decl/sprite_accessory/marking/grafadreka - species_allowed = list("Meredrake") - -/decl/language/grafadreka - desc = "Hiss hiss, feed me rabbits." - -/decl/material/liquid/sifsap - name = "drake spittle" - lore_text = "A complex chemical slurry brewed up in the gullet of meredrakes." - -/obj/aura/sifsap_salve - name = "Drakespittle Salve" - descriptor = "glowing spittle" - // Rename wooden prostheses /decl/bodytype/prosthetic/wooden name = "carved wooden" // weird to call it 'crude' when it's cutting-edge for the setting diff --git a/mods/content/psionics/_psionics.dme b/mods/content/psionics/_psionics.dme index df5544cffe15..b978b8a0c7b4 100644 --- a/mods/content/psionics/_psionics.dme +++ b/mods/content/psionics/_psionics.dme @@ -27,7 +27,6 @@ #include "items\literature.dm" #include "items\null_ammo.dm" #include "items\nullrod.dm" -#include "items\soulstone.dm" #include "machines\psimeter.dm" #include "machines\psimonitor.dm" #include "system\subsystem_psi.dm" @@ -54,7 +53,6 @@ #include "system\psionics\interface\ui.dm" #include "system\psionics\interface\ui_hub.dm" #include "system\psionics\interface\ui_toggles.dm" -#include "system\psionics\mob\borer_power.dm" #include "system\psionics\mob\mob.dm" #include "system\psionics\mob\mob_assay.dm" #include "system\psionics\mob\mob_interactions.dm" diff --git a/mods/content/standard_jobs/jobs/medical.dm b/mods/content/standard_jobs/jobs/medical.dm index 14e68446da0a..f2c11cb05d12 100644 --- a/mods/content/standard_jobs/jobs/medical.dm +++ b/mods/content/standard_jobs/jobs/medical.dm @@ -199,15 +199,6 @@ ) give_psionic_implant_on_join = FALSE -#ifdef MODPACK_PSIONICS -/datum/job/counselor/equip_job(var/mob/living/human/H) - if(H.mind.role_alt_title == "Counselor") - psi_faculties = list("[PSI_REDACTION]" = PSI_RANK_OPERANT) - if(H.mind.role_alt_title == "Mentalist") - psi_faculties = list("[PSI_COERCION]" = PSI_RANK_OPERANT) - return ..() -#endif - // Department-flavor IDs /obj/item/card/id/medical name = "identification card" diff --git a/mods/gamemodes/cult/overrides.dm b/mods/gamemodes/cult/overrides.dm index 2077c9f0b1e1..f536e4eabe68 100644 --- a/mods/gamemodes/cult/overrides.dm +++ b/mods/gamemodes/cult/overrides.dm @@ -15,6 +15,7 @@ playsound(src, 'sound/effects/ghost2.ogg', 10, 5) /datum/trader/ship/clothingshop/hatglovesaccessories/New() + ..() possible_trading_items[/obj/item/clothing/head/culthood] = TRADER_BLACKLIST_ALL /mob/living/silicon/ai diff --git a/mods/gamemodes/heist/_heist.dme b/mods/gamemodes/heist/_heist.dme index 57bf5c58b426..de2a67d69d3c 100644 --- a/mods/gamemodes/heist/_heist.dme +++ b/mods/gamemodes/heist/_heist.dme @@ -1,13 +1,5 @@ #ifndef GAMEMODE_PACK_HEIST #define GAMEMODE_PACK_HEIST - -#ifdef MODPACK_VOX -#warn Vox modpack loaded before Heist modpack, compatibility features will be missing. -#endif -#ifdef GAMEMODE_PACK_MIXED -#warn Mixed gamemodes modpack loaded before Heist modpack, Heist combination modes will be missing. -#endif - // BEGIN_INCLUDE #include "_heist.dm" #include "areas.dm" diff --git a/mods/gamemodes/mixed.dm b/mods/gamemodes/mixed.dm new file mode 100644 index 000000000000..f89baef2aa8f --- /dev/null +++ b/mods/gamemodes/mixed.dm @@ -0,0 +1,7 @@ +#ifndef GAMEMODE_PACK_MIXED +#define GAMEMODE_PACK_MIXED +#endif + +// This modpack doesn't actually have anything here, and instead it uses the compatibility patch system to make load order not matter. +/decl/modpack/mixed_modes + name = "Mixed Gamemodes" \ No newline at end of file diff --git a/mods/gamemodes/mixed/_mixed.dm b/mods/gamemodes/mixed/_mixed.dm deleted file mode 100644 index c1c9167d414e..000000000000 --- a/mods/gamemodes/mixed/_mixed.dm +++ /dev/null @@ -1,2 +0,0 @@ -/decl/modpack/mixed_modes - name = "Mixed Gamemodes" \ No newline at end of file diff --git a/mods/gamemodes/mixed/_mixed.dme b/mods/gamemodes/mixed/_mixed.dme deleted file mode 100644 index 6db33d077069..000000000000 --- a/mods/gamemodes/mixed/_mixed.dme +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef GAMEMODE_PACK_MIXED -#define GAMEMODE_PACK_MIXED -// BEGIN_INCLUDE -#include "_mixed.dm" -#if defined(GAMEMODE_PACK_HEIST) // TODO: && defined(GAMEMODE_PACK_MERCENARY) -#include "crossfire.dm" -#endif -#if defined(GAMEMODE_PACK_REVOLUTIONARY) -#include "siege.dm" -#endif -#if defined(GAMEMODE_PACK_REVOLUTIONARY) && defined(GAMEMODE_PACK_CULT) -#include "uprising.dm" -#endif -// END_INCLUDE -#endif \ No newline at end of file diff --git a/mods/mobs/borers/_borers.dme b/mods/mobs/borers/_borers.dme index 380c95f4e87f..7f230fc95363 100644 --- a/mods/mobs/borers/_borers.dme +++ b/mods/mobs/borers/_borers.dme @@ -1,10 +1,5 @@ #ifndef CONTENT_PACK_BORERS #define CONTENT_PACK_BORERS - -#ifdef MODPACK_PSIONICS -#warn Psionics modpack loaded before Borers modpack, compatibility features will be missing. -#endif - // BEGIN_INCLUDE #include "borer.dm" #include "datum\antagonist.dm" diff --git a/mods/species/drakes/_drakes.dme b/mods/species/drakes/_drakes.dme index 1143d6a510ee..025f1c54c518 100644 --- a/mods/species/drakes/_drakes.dme +++ b/mods/species/drakes/_drakes.dme @@ -1,10 +1,5 @@ #ifndef MODPACK_DRAKES #define MODPACK_DRAKES - -#ifdef MODPACK_FANTASY -#warn Fantasy modpack loaded before Drakes modpack, compatibility features will be missing. -#endif - // BEGIN_INCLUDE #include "_drakes.dm" #include "_overrides.dm" diff --git a/mods/species/vox/_vox.dme b/mods/species/vox/_vox.dme index 83d61f50f51f..0e0e635d99b4 100644 --- a/mods/species/vox/_vox.dme +++ b/mods/species/vox/_vox.dme @@ -8,7 +8,6 @@ #include "datum\cultures_vox.dm" #include "datum\descriptors_vox.dm" #include "datum\factions_vox.dm" -#include "datum\heist_compatibility.dm" #include "datum\language.dm" #include "datum\locations_vox.dm" #include "datum\outfits.dm" diff --git a/mods/~compatibility/patches/fantasy.dm b/mods/~compatibility/patches/fantasy.dm new file mode 100644 index 000000000000..c176360672c6 --- /dev/null +++ b/mods/~compatibility/patches/fantasy.dm @@ -0,0 +1,4 @@ +// Override drake lore and names for the fantasy modpack. +#ifdef MODPACK_DRAKES +#include "fantasy/drake_fantasy.dm" +#endif \ No newline at end of file diff --git a/mods/~compatibility/patches/fantasy/drake_fantasy.dm b/mods/~compatibility/patches/fantasy/drake_fantasy.dm new file mode 100644 index 000000000000..70ad5e628e7d --- /dev/null +++ b/mods/~compatibility/patches/fantasy/drake_fantasy.dm @@ -0,0 +1,21 @@ +// Rename grafadreka +/decl/species/grafadreka + name = "Meredrake" + name_plural = "Meredrakes" + description = "Meredrakes, sometimes called mire-drakes, are large reptillian pack predators, widely assumed to be cousins to true dragons. \ + They are commonly found living in caves or burrows bordering grassland or forest, and while they prefer to hunt deer or rabbits, they will sometimes attack travellers if pickings are slim enough. \ + While they are not domesticated, they can be habituated and trained as working animals if captured young enough." + +/decl/sprite_accessory/marking/grafadreka + species_allowed = list("Meredrake") + +/decl/language/grafadreka + desc = "Hiss hiss, feed me rabbits." + +/decl/material/liquid/sifsap + name = "drake spittle" + lore_text = "A complex chemical slurry brewed up in the gullet of meredrakes." + +/obj/aura/sifsap_salve + name = "Drakespittle Salve" + descriptor = "glowing spittle" diff --git a/mods/species/vox/datum/heist_compatibility.dm b/mods/~compatibility/patches/heist_vox.dm similarity index 98% rename from mods/species/vox/datum/heist_compatibility.dm rename to mods/~compatibility/patches/heist_vox.dm index 776057948c06..e37a3416faa6 100644 --- a/mods/species/vox/datum/heist_compatibility.dm +++ b/mods/~compatibility/patches/heist_vox.dm @@ -1,4 +1,3 @@ -#ifdef GAMEMODE_PACK_HEIST /decl/special_role/raider/Initialize() . = ..() LAZYSET(outfits_per_species, SPECIES_VOX, /decl/outfit/vox_raider) @@ -41,4 +40,3 @@ vox.SetName(vox.real_name) var/decl/special_role/raider/raiders = GET_DECL(/decl/special_role/raider) raiders.update_access(vox) -#endif \ No newline at end of file diff --git a/mods/~compatibility/patches/mixed_gamemodes.dm b/mods/~compatibility/patches/mixed_gamemodes.dm new file mode 100644 index 000000000000..c554702eff52 --- /dev/null +++ b/mods/~compatibility/patches/mixed_gamemodes.dm @@ -0,0 +1,12 @@ +// TODO: #ifdef GAMEMODE_PACK_MERCENARY +#if defined(GAMEMODE_PACK_HEIST) +#include "mixed_gamemodes/crossfire.dm" +#endif +#if defined(GAMEMODE_PACK_REVOLUTIONARY) +#include "mixed_gamemodes/siege.dm" +#endif +// #endif + +#if defined(GAMEMODE_PACK_REVOLUTIONARY) && defined(GAMEMODE_PACK_CULT) +#include "mixed_gamemodes/uprising.dm" +#endif \ No newline at end of file diff --git a/mods/gamemodes/mixed/crossfire.dm b/mods/~compatibility/patches/mixed_gamemodes/crossfire.dm similarity index 100% rename from mods/gamemodes/mixed/crossfire.dm rename to mods/~compatibility/patches/mixed_gamemodes/crossfire.dm diff --git a/mods/gamemodes/mixed/siege.dm b/mods/~compatibility/patches/mixed_gamemodes/siege.dm similarity index 100% rename from mods/gamemodes/mixed/siege.dm rename to mods/~compatibility/patches/mixed_gamemodes/siege.dm diff --git a/mods/gamemodes/mixed/uprising.dm b/mods/~compatibility/patches/mixed_gamemodes/uprising.dm similarity index 100% rename from mods/gamemodes/mixed/uprising.dm rename to mods/~compatibility/patches/mixed_gamemodes/uprising.dm diff --git a/mods/~compatibility/patches/psionics.dm b/mods/~compatibility/patches/psionics.dm new file mode 100644 index 000000000000..93473b9271f8 --- /dev/null +++ b/mods/~compatibility/patches/psionics.dm @@ -0,0 +1,13 @@ +// Give borers a paramount rank psi aura, and gives them a ranged psychic attack. +#ifdef CONTENT_PACK_BORERS +#include "psionics/borer_psi.dm" +#endif +// Allows psion blood to be used to create soulstones, +// and lets full soulstones nullify psi and shatter into nullglass. +#ifdef GAMEMODE_PACK_CULT +#include "psionics/cult_psi.dm" +#endif +// Adds psi abilities to the counselor. +#ifdef MODPACK_STANDARD_JOBS +#include "psionics/psi_jobs.dm" +#endif \ No newline at end of file diff --git a/mods/content/psionics/system/psionics/mob/borer_power.dm b/mods/~compatibility/patches/psionics/borer_psi.dm similarity index 97% rename from mods/content/psionics/system/psionics/mob/borer_power.dm rename to mods/~compatibility/patches/psionics/borer_psi.dm index 48f62c8adab6..68c70ec95f78 100644 --- a/mods/content/psionics/system/psionics/mob/borer_power.dm +++ b/mods/~compatibility/patches/psionics/borer_psi.dm @@ -1,4 +1,3 @@ -#ifdef CONTENT_PACK_BORERS /mob/living/simple_animal/borer var/image/aura_image @@ -52,5 +51,4 @@ SET_STATUS_MAX(M, STAT_WEAK, 10) set_ability_cooldown(15 SECONDS) - return TRUE -#endif \ No newline at end of file + return TRUE \ No newline at end of file diff --git a/mods/content/psionics/items/soulstone.dm b/mods/~compatibility/patches/psionics/cult_psi.dm similarity index 61% rename from mods/content/psionics/items/soulstone.dm rename to mods/~compatibility/patches/psionics/cult_psi.dm index 3d984a0711c7..85c203913624 100644 --- a/mods/content/psionics/items/soulstone.dm +++ b/mods/~compatibility/patches/psionics/cult_psi.dm @@ -1,15 +1,14 @@ -#ifdef GAMEMODE_PACK_CULT +// Make soulstones interact with psionics. /obj/item/soulstone/disrupts_psionics() . = !full ? src : FALSE /obj/item/soulstone/shatter() for(var/i=1 to rand(2,5)) - new /obj/item/shard(get_turf(src), /decl/material/nullglass) + new /obj/item/shard(get_turf(src), full ? /decl/material/nullglass : /decl/material/solid/gemstone/crystal) . = ..() /obj/item/soulstone/withstand_psi_stress(var/stress, var/atom/source) . = ..(stress, source) if(. > 0) . = max(0, . - rand(2,5)) - shatter() -#endif \ No newline at end of file + shatter() \ No newline at end of file diff --git a/mods/~compatibility/patches/psionics/psi_jobs.dm b/mods/~compatibility/patches/psionics/psi_jobs.dm new file mode 100644 index 000000000000..f4579e9d7c05 --- /dev/null +++ b/mods/~compatibility/patches/psionics/psi_jobs.dm @@ -0,0 +1,6 @@ +/datum/job/counselor/equip_job(var/mob/living/human/H) + if(H.mind.role_alt_title == "Counselor") + psi_faculties = list("[PSI_REDACTION]" = PSI_RANK_OPERANT) + if(H.mind.role_alt_title == "Mentalist") + psi_faculties = list("[PSI_COERCION]" = PSI_RANK_OPERANT) + return ..() \ No newline at end of file diff --git a/mods/~compatibility/readme.md b/mods/~compatibility/readme.md new file mode 100644 index 000000000000..d422273e7bb4 --- /dev/null +++ b/mods/~compatibility/readme.md @@ -0,0 +1,22 @@ +# Modpack Compatibility System +This folder exists as a way to work around the fact that the previous system for modpack cross-compatibility, define-gating, is sensitive to include order. This resulted in a lot of boilerplate, like having to emit warnings if modpacks were included in the wrong order. This meant that you could also introduce cyclical dependencies, where no matter what it would emit a warning and content would be missing. + +To avoid this issue, we instead include all compatibility patches last, so it is load order agnostic. + +## FAQ +### Why aren't the compatibility files in the modpacks themselves? +I didn't want to edit the modpack include validation script to exclude the compatibility patches from all DMEs. + +### Why is it organised using subfolders? +I didn't like using `#if defined(FOO) && defined(BAR)` and nested `#ifdef`s were hard to follow, so instead I group them by modpack. + +### Is there a general rule for which modpacks get their own folder? +Not really. I just grouped them in roughly the way that would result in the largest existing groupings, and then chose groupings that would make the most sense to expand in the future (fantasy and standard jobs). + +### Do all patches need to be in a subfolder? +No, it's totally fine to just put something in the base patches directory if there's only one patch for either of the mods in that pairing. That said, sometimes it can make sense to add a folder with just one patch if you can foresee future development requiring additional patches in the same category. + +### How do I decide which folder a patch goes in if both modpacks have folders? +I tend to personally go based on whatever it's mostly about; a hypothetical patch renaming and respriting psionics for the fantasy modpack would go in the fantasy folder. Alternatively, you could think of it as going for whichever one is more specific. + +That said, if one has a lot more patches than the other, or if one modpack (take Standard Jobs, for example) is patched by several modpacks that already have folders, it's fine to just go with whatever produces the largest patch subfolders (or gets rid of small/redundant ones). \ No newline at end of file diff --git a/mods/~compatibility/~compatibility.dm b/mods/~compatibility/~compatibility.dm new file mode 100644 index 000000000000..f59ac86c052c --- /dev/null +++ b/mods/~compatibility/~compatibility.dm @@ -0,0 +1,16 @@ +// Add Vox-specific content for the Heist gamemode (Vox raider outfit, mirror to transform into a Vox as a raider) +#if defined(GAMEMODE_PACK_HEIST) && defined(MODPACK_VOX) +#include "patches/heist_vox.dm" +#endif + +#ifdef MODPACK_PSIONICS +#include "patches/psionics.dm" +#endif + +#ifdef GAMEMODE_PACK_MIXED +#include "patches/mixed_gamemodes.dm" +#endif + +#ifdef MODPACK_FANTASY_SPECIES +#include "patches/fantasy.dm" +#endif \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index db8ca0135486..5d10b384d1c5 100644 --- a/nebula.dme +++ b/nebula.dme @@ -4101,5 +4101,6 @@ #include "maps\~mapsystem\maps_unit_testing.dm" #include "maps\~unit_tests\unit_testing.dm" #include "mods\_modpack.dm" +#include "mods\~compatibility\~compatibility.dm" #include "~code\global_init.dm" // END_INCLUDE From 54b94a4948d9efbad9a9ddcae55485910427840a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 2 Jan 2025 22:22:56 -0500 Subject: [PATCH 0168/1331] Rewrite soulstone reaction handling --- code/modules/reagents/reactions/_reaction.dm | 6 ++--- .../reagents/reactions/reaction_compounds.dm | 2 +- .../reactions/reaction_grenade_reaction.dm | 16 +++++------ .../reagents/reactions/reaction_herbal.dm | 2 +- .../reagents/reactions/reaction_other.dm | 2 +- .../reactions/reaction_recipe_food.dm | 2 +- .../reagents/reactions/reaction_synthesis.dm | 14 +++++----- mods/content/psionics/datum/chems.dm | 27 ------------------- .../cult/mobs/constructs/soulstone.dm | 27 +++++++++++++++++++ .../patches/psionics/cult_psi.dm | 4 +++ 10 files changed, 52 insertions(+), 50 deletions(-) diff --git a/code/modules/reagents/reactions/_reaction.dm b/code/modules/reagents/reactions/_reaction.dm index e743ecf0ab2b..fbc2eaa85d68 100644 --- a/code/modules/reagents/reactions/_reaction.dm +++ b/code/modules/reagents/reactions/_reaction.dm @@ -38,7 +38,7 @@ return 1 -/decl/chemical_reaction/proc/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/proc/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(thermal_product && location && ATOM_SHOULD_TEMPERATURE_ENQUEUE(location)) ADJUST_ATOM_TEMPERATURE(location, location.temperature + (location.get_thermal_mass_coefficient() * thermal_product)) @@ -61,8 +61,6 @@ if(reaction_volume > A) reaction_volume = A - var/alt_reaction_indicator = get_alternate_reaction_indicator(holder) - for(var/reactant in required_reagents) holder.remove_reagent(reactant, reaction_volume * required_reagents[reactant], safety = 1) @@ -71,7 +69,7 @@ if(result) holder.add_reagent(result, amt_produced, data, safety = 1) - on_reaction(holder, amt_produced, alt_reaction_indicator, data) + on_reaction(holder, amt_produced, data) //called after processing reactions, if they occurred /decl/chemical_reaction/proc/post_reaction(var/datum/reagents/holder) diff --git a/code/modules/reagents/reactions/reaction_compounds.dm b/code/modules/reagents/reactions/reaction_compounds.dm index 8e530dacaa58..d76c4b9630c7 100644 --- a/code/modules/reagents/reactions/reaction_compounds.dm +++ b/code/modules/reagents/reactions/reaction_compounds.dm @@ -155,7 +155,7 @@ required_reagents = list(/decl/material/liquid/capsaicin = 2) result = list(/decl/material/liquid/capsaicin/condensed = 1) -/decl/chemical_reaction/compound/condensed_capsaicin/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/compound/condensed_capsaicin/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) . = ..() holder?.add_reagent(/decl/material/liquid/water, created_volume) diff --git a/code/modules/reagents/reactions/reaction_grenade_reaction.dm b/code/modules/reagents/reactions/reaction_grenade_reaction.dm index ec529bef717f..7827c6ac0548 100644 --- a/code/modules/reagents/reactions/reaction_grenade_reaction.dm +++ b/code/modules/reagents/reactions/reaction_grenade_reaction.dm @@ -11,7 +11,7 @@ required_reagents = list(/decl/material/liquid/water = 1, /decl/material/solid/potassium = 1) mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/explosion_potassium/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/explosion_potassium/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) @@ -32,7 +32,7 @@ result_amount = null mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/flash_powder/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/flash_powder/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -58,7 +58,7 @@ mix_message = "The solution bubbles vigorously!" maximum_temperature = T100C -/decl/chemical_reaction/grenade_reaction/emp_pulse/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/emp_pulse/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) @@ -79,7 +79,7 @@ reaction_sound = 'sound/items/Welder.ogg' mix_message = "The solution suddenly ignites!" -/decl/chemical_reaction/grenade_reaction/flash_fire/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/flash_fire/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(istype(location)) @@ -93,7 +93,7 @@ result_amount = 0.4 mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/chemsmoke/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/chemsmoke/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -112,7 +112,7 @@ result_amount = 2 mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/foam/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/foam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -129,7 +129,7 @@ result_amount = 5 mix_message = "The solution foams up violently!" -/decl/chemical_reaction/grenade_reaction/metalfoam/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/metalfoam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) @@ -151,7 +151,7 @@ result_amount = 5 mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/ironfoam/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/ironfoam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) diff --git a/code/modules/reagents/reactions/reaction_herbal.dm b/code/modules/reagents/reactions/reaction_herbal.dm index b8a02ca07fec..62950dd1d64c 100644 --- a/code/modules/reagents/reactions/reaction_herbal.dm +++ b/code/modules/reagents/reactions/reaction_herbal.dm @@ -3,7 +3,7 @@ result_amount = 2 minimum_temperature = 100 CELSIUS -/decl/chemical_reaction/drug/herbal/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/drug/herbal/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) . = ..() // Add plant matter to represent the herbs that the medicine has been leached out of. holder?.add_reagent(/decl/material/solid/organic/plantmatter, created_volume) diff --git a/code/modules/reagents/reactions/reaction_other.dm b/code/modules/reagents/reactions/reaction_other.dm index 19c5613ea7ec..dca52f041f62 100644 --- a/code/modules/reagents/reactions/reaction_other.dm +++ b/code/modules/reagents/reactions/reaction_other.dm @@ -10,7 +10,7 @@ return ..() return 0 -/decl/chemical_reaction/soap_key/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/soap_key/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/obj/item/soap/S = holder.get_reaction_loc(chemical_reaction_flags) if(istype(S) && S.key_data) new /obj/item/key/temporary(get_turf(S), /decl/material/liquid/cleaner, S.key_data, strength) diff --git a/code/modules/reagents/reactions/reaction_recipe_food.dm b/code/modules/reagents/reactions/reaction_recipe_food.dm index bc052960e913..0ece0daefd2a 100644 --- a/code/modules/reagents/reactions/reaction_recipe_food.dm +++ b/code/modules/reagents/reactions/reaction_recipe_food.dm @@ -4,7 +4,7 @@ abstract_type = /decl/chemical_reaction/recipe/food var/obj_result -/decl/chemical_reaction/recipe/food/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/recipe/food/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(obj_result && isturf(location)) diff --git a/code/modules/reagents/reactions/reaction_synthesis.dm b/code/modules/reagents/reactions/reaction_synthesis.dm index 5c4f5b710f4c..2cf1dfce05f3 100644 --- a/code/modules/reagents/reactions/reaction_synthesis.dm +++ b/code/modules/reagents/reactions/reaction_synthesis.dm @@ -19,7 +19,7 @@ ) . = ..() -/decl/chemical_reaction/synthesis/fiberglass/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/fiberglass/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -47,7 +47,7 @@ if(rtype != /decl/material/liquid/crystal_agent && REAGENT_VOLUME(holder, rtype) >= REAGENT_UNITS_PER_MATERIAL_SHEET) return TRUE -/decl/chemical_reaction/synthesis/crystalization/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/crystalization/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) var/list/removing_reagents = list() @@ -81,7 +81,7 @@ continue return TRUE -/decl/chemical_reaction/synthesis/aerogel/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/aerogel/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) var/list/removing_reagents = list() @@ -100,7 +100,7 @@ required_reagents = list(/decl/material/liquid/acid = 1, /decl/material/liquid/plasticide = 2) mix_message = "The solution solidifies into a grey-white mass." -/decl/chemical_reaction/synthesis/plastication/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/plastication/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -115,7 +115,7 @@ result_amount = 3 mix_message = "The solution hardens and begins to crystallize." -/decl/chemical_reaction/synthesis/resin_pack/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/resin_pack/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/T = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(!istype(T)) @@ -136,7 +136,7 @@ mix_message = "The solution thickens and solidifies." minimum_temperature = 100 CELSIUS -/decl/chemical_reaction/synthesis/soap/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/soap/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/T = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(!istype(T)) @@ -167,7 +167,7 @@ minimum_temperature = 100 CELSIUS var/chipboard_type = /decl/material/solid/organic/wood/chipboard -/decl/chemical_reaction/synthesis/chipboard/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/chipboard/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/T = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(!istype(T)) diff --git a/mods/content/psionics/datum/chems.dm b/mods/content/psionics/datum/chems.dm index 1bc713de0eb3..945334faa79a 100644 --- a/mods/content/psionics/datum/chems.dm +++ b/mods/content/psionics/datum/chems.dm @@ -5,30 +5,3 @@ ..() var/datum/ability_handler/psionics/psi = M.get_ability_handler(/datum/ability_handler/psionics) psi?.check_latency_trigger(30, "a [name] overdose") - -/decl/chemical_reaction/synthesis/nullglass - name = "Soulstone" - result = null - required_reagents = list(/decl/material/liquid/blood = 15, /decl/material/liquid/crystal_agent = 1) - result_amount = 1 - -#ifdef GAMEMODE_PACK_CULT -/decl/chemical_reaction/synthesis/nullglass/get_alternate_reaction_indicator(var/datum/reagents/holder) - var/list/blood_data = REAGENT_DATA(holder, /decl/material/liquid/blood) - var/weakref/donor_ref = LAZYACCESS(blood_data, DATA_BLOOD_DONOR) - var/mob/living/donor = donor_ref?.resolve() - . = istype(donor) && !!donor.get_ability_handler(/datum/ability_handler/psionics) -#endif - -/decl/chemical_reaction/synthesis/nullglass/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) - var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) - if(reaction_flags) - #ifdef GAMEMODE_PACK_CULT - for(var/i = 1, i <= created_volume, i++) - new /obj/item/soulstone(location) - #else - CRASH("Nullglass alternate reaction triggered in [holder.my_atom] without cult modpack loaded!") - #endif - else - for(var/i = 1, i <= created_volume*2, i++) - new /obj/item/shard(location, /decl/material/solid/gemstone/crystal) \ No newline at end of file diff --git a/mods/gamemodes/cult/mobs/constructs/soulstone.dm b/mods/gamemodes/cult/mobs/constructs/soulstone.dm index a6e8d693c3b9..b5a8f6f09485 100644 --- a/mods/gamemodes/cult/mobs/constructs/soulstone.dm +++ b/mods/gamemodes/cult/mobs/constructs/soulstone.dm @@ -107,6 +107,33 @@ full = f update_icon() +// Soulstone synthesis recipe. +/decl/chemical_reaction/synthesis/soulstone + name = "Soulstone" + result = null + required_reagents = list(/decl/material/liquid/blood = 15, /decl/material/liquid/crystal_agent = 1) + result_amount = 1 + hidden_from_codex = TRUE // This shouldn't show up in search. Maybe it should be linked in a 'guide to cult' or something? + +/decl/chemical_reaction/synthesis/soulstone/send_data(datum/reagents/holder, reaction_limit) + return REAGENT_DATA(holder, /decl/material/liquid/blood) // allow on_reaction to get donor data + +/// Whether or not the reaction should produce a soulstone or a normal crystal. +/// The donor mob parameter may either be /mob/living or null. +/decl/chemical_reaction/synthesis/soulstone/proc/donor_is_magic(mob/living/donor) + return FALSE // By default, no one is magic! This is for modpacks to override. + +/decl/chemical_reaction/synthesis/soulstone/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) + var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) + var/weakref/donor_ref = LAZYACCESS(reaction_data, DATA_BLOOD_DONOR) + if(donor_is_magic(donor_ref?.resolve())) + for(var/i = 1, i <= created_volume, i++) + new /obj/item/soulstone(location) + else // waste it and produce useless crystal shards + for(var/i = 1, i <= created_volume*2, i++) + new /obj/item/shard(location, /decl/material/solid/gemstone/crystal) + +// Construct shells. These accept soulstones. /obj/structure/constructshell name = "empty shell" icon = 'icons/obj/structures/construct.dmi' diff --git a/mods/~compatibility/patches/psionics/cult_psi.dm b/mods/~compatibility/patches/psionics/cult_psi.dm index 85c203913624..0fa46de32d68 100644 --- a/mods/~compatibility/patches/psionics/cult_psi.dm +++ b/mods/~compatibility/patches/psionics/cult_psi.dm @@ -1,3 +1,7 @@ +// Make psion blood usable for soulstone synthesis. +/decl/chemical_reaction/synthesis/soulstone/donor_is_magic(mob/living/donor) + return ..() || !!donor?.get_ability_handler(/datum/ability_handler/psionics) + // Make soulstones interact with psionics. /obj/item/soulstone/disrupts_psionics() . = !full ? src : FALSE From efe0c557c83b0e7c860eec1a5777524b1506f670 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Wed, 8 Jan 2025 00:50:37 +0000 Subject: [PATCH 0169/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-4695.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4695.yml diff --git a/html/changelog.html b/html/changelog.html index 357f4dcea1ff..daa00b874bd0 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    08 January 2025

    +

    MistakeNot4892 updated:

    +
      +
    • Mud and blood can now leave footprints.
    • +
    +

    03 January 2025

    MistakeNot4892 updated:

    From 833e97d9c08dfcf6a47b8dc354ea918db16ce98d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 23 Mar 2025 18:46:56 -0400 Subject: [PATCH 0564/1331] Rewrite breath code for better modularity --- code/game/atoms.dm | 2 +- .../gasses/material_gas_mundane.dm | 4 +- .../liquids/materials_liquid_toxins.dm | 4 +- code/modules/mob/grab/normal/norm_kill.dm | 2 +- code/modules/mob/living/human/examine.dm | 4 +- code/modules/mob/living/human/human.dm | 2 +- .../mob/living/human/human_attackhand.dm | 2 +- code/modules/mob/living/living_breath.dm | 65 +++++++++++-------- code/modules/mob/living/living_defines.dm | 9 ++- code/modules/organs/internal/lungs.dm | 7 +- 10 files changed, 56 insertions(+), 45 deletions(-) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 09b5980c5523..4d964f5c4677 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -102,7 +102,7 @@ return null /** - Merge an exhaled air volume into air contents.. + Merge an exhaled air volume into air contents. */ /atom/proc/merge_exhaled_volume(datum/gas_mixture/exhaled) var/datum/gas_mixture/environment = return_air() diff --git a/code/modules/materials/definitions/gasses/material_gas_mundane.dm b/code/modules/materials/definitions/gasses/material_gas_mundane.dm index 4d21232b7ff2..cff373e83716 100644 --- a/code/modules/materials/definitions/gasses/material_gas_mundane.dm +++ b/code/modules/materials/definitions/gasses/material_gas_mundane.dm @@ -81,8 +81,8 @@ else if(dosage >= 0.25) warning_message = pick("a little dizzy","short of breath") warning_prob = 10 - if(istype(H) && dosage > 1 && H.ticks_since_last_successful_breath < 15) - H.ticks_since_last_successful_breath++ + if(istype(H) && dosage > 1 && H.suffocation_counter < 15) + H.suffocation_counter++ if(warning_message && prob(warning_prob)) to_chat(M, SPAN_WARNING("You feel [warning_message].")) diff --git a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm index d28aeeb98edc..348abd21f1c3 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm @@ -131,8 +131,8 @@ /decl/material/liquid/heartstopper/affect_overdose(mob/living/victim, total_dose) ..() if(victim.stat != UNCONSCIOUS) - if(victim.ticks_since_last_successful_breath >= 10) - victim.ticks_since_last_successful_breath = max(10, victim.ticks_since_last_successful_breath-10) + if(victim.suffocation_counter >= 10) + victim.suffocation_counter = max(10, victim.suffocation_counter-10) victim.take_damage(2, OXY) SET_STATUS_MAX(victim, STAT_WEAK, 10) victim.add_chemical_effect(CE_NOPULSE, 1) diff --git a/code/modules/mob/grab/normal/norm_kill.dm b/code/modules/mob/grab/normal/norm_kill.dm index 28762d0cbc8a..e716be668640 100644 --- a/code/modules/mob/grab/normal/norm_kill.dm +++ b/code/modules/mob/grab/normal/norm_kill.dm @@ -27,4 +27,4 @@ SET_STATUS_MAX(affecting, STAT_WEAK, 5) //Should keep you down unless you get help. if(isliving(affecting)) var/mob/living/M = affecting - M.ticks_since_last_successful_breath = max(M.ticks_since_last_successful_breath + 2, 3) + M.suffocation_counter = max(M.suffocation_counter + 2, 3) diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index 2c36562f57b7..da4bb651a8d5 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -60,9 +60,9 @@ if(limb && limb.splinted && limb.splinted.loc == limb) . += "[use_He] [use_has] \a [limb.splinted] on [use_his] [limb.name]!" - if (src.stat) + if (stat) . += "[use_He] [use_is]n't responding to anything around [use_him] and seems to be unconscious." - if((stat == DEAD || is_asystole() || src.ticks_since_last_successful_breath) && distance <= 3) + if((stat == DEAD || is_asystole() || suffocation_counter) && distance <= 3) . += "[use_He] [use_does] not appear to be breathing." var/datum/reagents/touching_reagents = get_contact_reagents() diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 5f1afb8faea7..85abc9b909fe 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -389,7 +389,7 @@ brain.mind.transfer_to(src) qdel(brain.loc) break - ticks_since_last_successful_breath = 0 + suffocation_counter = 0 ..() /mob/living/add_blood(mob/living/M, amount = 2, list/blood_data) diff --git a/code/modules/mob/living/human/human_attackhand.dm b/code/modules/mob/living/human/human_attackhand.dm index 93d436e3a882..39c6d931df58 100644 --- a/code/modules/mob/living/human/human_attackhand.dm +++ b/code/modules/mob/living/human/human_attackhand.dm @@ -340,7 +340,7 @@ if(!lungs.handle_owner_breath(H.get_breath_from_environment(), 1)) if(!lungs.is_bruised()) - ticks_since_last_successful_breath = 0 + suffocation_counter = 0 to_chat(src, SPAN_NOTICE("You feel a breath of fresh air enter your lungs. It feels good.")) // Again. diff --git a/code/modules/mob/living/living_breath.dm b/code/modules/mob/living/living_breath.dm index 164a8503215e..69241dac54ae 100644 --- a/code/modules/mob/living/living_breath.dm +++ b/code/modules/mob/living/living_breath.dm @@ -15,39 +15,49 @@ /mob/living/proc/should_breathe() return ((life_tick % 2) == 0 || failed_last_breath || is_asystole()) -/mob/living/proc/try_breathe() +// These procs all have a lung organ as an argument to avoid having to do repeated get_bodytype and get_organ calls. +// It's a small optimization but it adds up if you have a lot of mobs breathing. + +/// This proc contains all of the logic for returning a gasmix datum to be used in other breath-related code. +/// Does not necessarily involve inhaling at all; that's obtain_new_breath()'s job. +/mob/living/proc/get_breath(obj/item/organ/internal/lungs/lungs) + // Handle optional pre-breath logic. If it returns TRUE, skip the breath entirely. + if(handle_pre_breath(lungs)) + return null + return obtain_new_breath(lungs) + +/// Return a gasmix representing newly-inhaled air. +/mob/living/proc/obtain_new_breath(obj/item/organ/internal/lungs/lungs) + var/static/datum/gas_mixture/vacuum = new //avoid having to create a new gas mixture for each breath in space + //Okay, we can breathe, now check if we can get air + var/volume_needed = get_breath_volume() + //First check for air from internals, then the local environment, then use vacuum + return get_breath_from_internal(volume_needed) || get_breath_from_environment(volume_needed) || vacuum + +/// Handles pre-breath checks. If this proc returns TRUE, breathing is skipped that tick. +/mob/living/proc/handle_pre_breath(obj/item/organ/internal/lungs/lungs) + //Check if we can breathe at all + if(handle_drowning() || (is_asystole() && !GET_CHEMICAL_EFFECT(src, CE_STABLE) && lungs.active_breathing)) //crit aka circulatory shock + . = TRUE + else if(suffocation_counter > 0) + // We aren't drowning or in asystole, but something else is suffocating us, so lower the counter and don't take a breath + suffocation_counter-- + . = TRUE + if(.) + // Gasp on average every 10 ticks + if (prob(10) && !is_asystole() && lungs.active_breathing) + INVOKE_ASYNC(src, PROC_REF(emote), /decl/emote/audible/gasp) + return TRUE + return FALSE // not handled, proceed with breathing +/mob/living/proc/try_breathe() var/decl/bodytype/root_bodytype = get_bodytype() if(!root_bodytype?.breathing_organ) return - - var/active_breathe = FALSE var/obj/item/organ/internal/lungs/lungs = get_organ(root_bodytype.breathing_organ, /obj/item/organ/internal/lungs) - if(lungs) - active_breathe = lungs.active_breathing - - var/datum/gas_mixture/breath = null - //First, check if we can breathe at all - if(handle_drowning() || (is_asystole() && !GET_CHEMICAL_EFFECT(src, CE_STABLE) && active_breathe)) //crit aka circulatory shock - ticks_since_last_successful_breath = max(2, ticks_since_last_successful_breath + 1) - - if(ticks_since_last_successful_breath>0) //Suffocating so do not take a breath - ticks_since_last_successful_breath-- - if (prob(10) && !is_asystole() && active_breathe) //Gasp per 10 ticks? Sounds about right. - INVOKE_ASYNC(src, PROC_REF(emote), "gasp") - else - //Okay, we can breathe, now check if we can get air - var/volume_needed = get_breath_volume() - breath = get_breath_from_internal(volume_needed) //First, check for air from internals - if(!breath) - breath = get_breath_from_environment(volume_needed) //No breath from internals so let's try to get air from our location - if(!breath) - var/static/datum/gas_mixture/vacuum //avoid having to create a new gas mixture for each breath in space - if(!vacuum) vacuum = new - breath = vacuum //still nothing? must be vacuum - + var/datum/gas_mixture/breath = get_breath(lungs) //if breath is null or vacuum, the lungs will handle it for us - failed_last_breath = (!lungs || nervous_system_failure()) ? 1 : lungs.handle_owner_breath(breath) + failed_last_breath = (!lungs || nervous_system_failure()) ? TRUE : lungs.handle_owner_breath(breath) handle_post_breath(breath) /mob/living/proc/get_breath_from_environment(var/volume_needed=STD_BREATH_VOLUME, var/atom/location = src.loc) @@ -135,7 +145,6 @@ . *= (!BP_IS_PROSTHETIC(heart)) ? get_pulse()/PULSE_NORM : 1.5 /mob/living/proc/handle_post_breath(datum/gas_mixture/breath) - if(!breath) return diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index aadc7cf7edf0..aed17db45348 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -33,9 +33,12 @@ VAR_PRIVATE/_on_fire = FALSE VAR_PRIVATE/_fire_intensity - var/ticks_since_last_successful_breath = 0 //if we failed to breathe last tick - var/failed_last_breath = FALSE //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. - var/possession_candidate = FALSE // Can be possessed by ghosts if unplayed. + /// A suffocation counter representing the number of ticks we should fail to breathe. + var/suffocation_counter = 0 + /// This is used to determine if the mob failed a breath. If they did fail a breath, they will attempt to breathe each tick, otherwise just once per 4 ticks. + var/failed_last_breath = FALSE + /// Can be possessed by ghosts if unplayed. + var/possession_candidate = FALSE var/job = null//Living diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 9c6e8070cb68..6fdca0adbeb1 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -11,7 +11,7 @@ max_damage = 70 relative_size = 60 - var/active_breathing = 1 + var/active_breathing = TRUE var/has_gills = FALSE var/breath_type var/exhale_type @@ -86,14 +86,13 @@ poison_types = list(/decl/material/gas/chlorine = TRUE) exhale_type = /decl/material/gas/carbon_dioxide - /obj/item/organ/internal/lungs/Process() ..() if(!owner) return if(owner.vital_organ_missing_time) - owner.ticks_since_last_successful_breath = max(10, owner.ticks_since_last_successful_breath) + owner.suffocation_counter = max(10, owner.suffocation_counter) return if (germ_level > INFECTION_LEVEL_ONE && active_breathing) @@ -125,7 +124,7 @@ else to_chat(owner, "You're having trouble getting enough [breath_type]!") - owner.ticks_since_last_successful_breath = max(3, owner.ticks_since_last_successful_breath) + owner.suffocation_counter = max(3, owner.suffocation_counter) /obj/item/organ/internal/lungs/proc/rupture() var/obj/item/organ/external/parent = GET_EXTERNAL_ORGAN(owner, parent_organ) From 61f3cb396a822e248262fc554d4480190d241f1c Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 25 Mar 2025 03:57:48 +1100 Subject: [PATCH 0565/1331] Automatic changelog generation for PR #4968 [ci skip] --- html/changelogs/AutoChangeLog-pr-4968.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4968.yml diff --git a/html/changelogs/AutoChangeLog-pr-4968.yml b/html/changelogs/AutoChangeLog-pr-4968.yml new file mode 100644 index 000000000000..3149ada779ee --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4968.yml @@ -0,0 +1,8 @@ +author: Penelope Haze +changes: + - {balance: 'Adjusts how suffocation works, meaning characters who were drowning + or in asystole start breathing as soon as the issue is resolved, rather than + counting down a timer instead. This might make brief bouts of drowning/cardiac + arrest more survivable. Please report any weirdness with breathing/suffocation + on the issue tracker.'} +delete-after: true From 6efa7ff2e86f8dfcc835f050b817b61019e3721e Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 25 Mar 2025 00:54:21 +0000 Subject: [PATCH 0566/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 12 ++++++------ html/changelogs/.all_changelog.yml | 7 +++++++ html/changelogs/AutoChangeLog-pr-4968.yml | 8 -------- 3 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4968.yml diff --git a/html/changelog.html b/html/changelog.html index 4fac4c564df6..be4c9f5af53c 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    25 March 2025

    +

    Penelope Haze updated:

    +
      +
    • Adjusts how suffocation works, meaning characters who were drowning or in asystole start breathing as soon as the issue is resolved, rather than counting down a timer instead. This might make brief bouts of drowning/cardiac arrest more survivable. Please report any weirdness with breathing/suffocation on the issue tracker.
    • +
    +

    14 March 2025

    Typhin updated:

      @@ -112,12 +118,6 @@

      MistakeNot4892 updated:

    • Swords on Shaded Hills can be sharpened using whetstones or grindstones.
    • Guns can now be fired from rigs regardless of dexterity.
    - -

    21 January 2025

    -

    Penelope Haze updated:

    -
      -
    • Mud can now receive footprints with any reagent other than mud. No more conspicuously missing bloody footprints!
    • -
    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 55fad9100e4a..e0248f4635e5 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15003,3 +15003,10 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2025-03-14: Typhin: - bugfix: Allows parts/screwdrivers to be used on biogenerators +2025-03-25: + Penelope Haze: + - balance: Adjusts how suffocation works, meaning characters who were drowning or + in asystole start breathing as soon as the issue is resolved, rather than counting + down a timer instead. This might make brief bouts of drowning/cardiac arrest + more survivable. Please report any weirdness with breathing/suffocation on the + issue tracker. diff --git a/html/changelogs/AutoChangeLog-pr-4968.yml b/html/changelogs/AutoChangeLog-pr-4968.yml deleted file mode 100644 index 3149ada779ee..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4968.yml +++ /dev/null @@ -1,8 +0,0 @@ -author: Penelope Haze -changes: - - {balance: 'Adjusts how suffocation works, meaning characters who were drowning - or in asystole start breathing as soon as the issue is resolved, rather than - counting down a timer instead. This might make brief bouts of drowning/cardiac - arrest more survivable. Please report any weirdness with breathing/suffocation - on the issue tracker.'} -delete-after: true From 78fe8f6f913e1838f0378a7f88d36d4bd940f451 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Wed, 26 Mar 2025 00:53:50 +0000 Subject: [PATCH 0567/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 7 ------- 1 file changed, 7 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index be4c9f5af53c..eafddc8a7f18 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -111,13 +111,6 @@

    MistakeNot4892 updated:

    • Various map changes to Bearcat, crashed pod, and several others. Please report issues!
    - -

    22 January 2025

    -

    MistakeNot4892 updated:

    -
      -
    • Swords on Shaded Hills can be sharpened using whetstones or grindstones.
    • -
    • Guns can now be fired from rigs regardless of dexterity.
    • -
    From 633715330bc6465b99c31e2a1ac311a3a0529289 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 25 Mar 2025 21:30:38 -0400 Subject: [PATCH 0568/1331] fix underpressure hardcoded 1atm --- code/modules/mob/living/human/life.dm | 14 ++++++++------ code/modules/species/species.dm | 3 +++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm index 8514c5a6d80f..0c4e6d4846eb 100644 --- a/code/modules/mob/living/human/life.dm +++ b/code/modules/mob/living/human/life.dm @@ -88,13 +88,15 @@ // Calculate how much of the environment pressure-difference affects the human. /mob/living/human/calculate_affecting_pressure(var/pressure) var/pressure_difference + var/species_safe_pressure = species.get_safe_pressure(src) + pressure_difference = abs(pressure - species_safe_pressure) // First get the absolute pressure difference. - if(pressure < ONE_ATMOSPHERE) // We are in an underpressure. - pressure_difference = ONE_ATMOSPHERE - pressure + /*if(pressure < species_safe_pressure) // We are in an underpressure. + pressure_difference = species_safe_pressure - pressure else //We are in an overpressure or standard atmosphere. - pressure_difference = pressure - ONE_ATMOSPHERE + pressure_difference = pressure - species_safe_pressure*/ if(pressure_difference < 5) // If the difference is small, don't bother calculating the fraction. pressure_difference = 0 @@ -107,10 +109,10 @@ // The difference is always positive to avoid extra calculations. // Apply the relative difference on a standard atmosphere to get the final result. // The return value will be the adjusted_pressure of the human that is the basis of pressure warnings and damage. - if(pressure < ONE_ATMOSPHERE) - return ONE_ATMOSPHERE - pressure_difference + if(pressure < species_safe_pressure) + return species_safe_pressure - pressure_difference else - return ONE_ATMOSPHERE + pressure_difference + return species_safe_pressure + pressure_difference /mob/living/human/handle_impaired_vision() diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index e2720ffd1596..2b8e4a628fca 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -742,3 +742,6 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/decl/background_category/background_cat = GET_DECL(cat_type) if(background_cat.background_flags & background_flag) return GET_DECL(default_background_info[cat_type]) + +/decl/species/proc/get_safe_pressure() + return (warning_high_pressure + warning_low_pressure)/2 \ No newline at end of file From f39c0f9f4d941e3bf474e089f5ff5121c9f7d01d Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 25 Mar 2025 22:00:50 -0400 Subject: [PATCH 0569/1331] clean up commented code --- code/modules/mob/living/human/life.dm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm index 0c4e6d4846eb..b6b9c2961886 100644 --- a/code/modules/mob/living/human/life.dm +++ b/code/modules/mob/living/human/life.dm @@ -90,13 +90,8 @@ var/pressure_difference var/species_safe_pressure = species.get_safe_pressure(src) - pressure_difference = abs(pressure - species_safe_pressure) // First get the absolute pressure difference. - /*if(pressure < species_safe_pressure) // We are in an underpressure. - pressure_difference = species_safe_pressure - pressure - - else //We are in an overpressure or standard atmosphere. - pressure_difference = pressure - species_safe_pressure*/ + pressure_difference = abs(pressure - species_safe_pressure) if(pressure_difference < 5) // If the difference is small, don't bother calculating the fraction. pressure_difference = 0 From 56a4127dd71a0948e8415196ab140d1229b438ae Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 27 Mar 2025 03:49:20 +1100 Subject: [PATCH 0570/1331] Automatic changelog generation for PR #4970 [ci skip] --- html/changelogs/AutoChangeLog-pr-4970.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4970.yml diff --git a/html/changelogs/AutoChangeLog-pr-4970.yml b/html/changelogs/AutoChangeLog-pr-4970.yml new file mode 100644 index 000000000000..1da7fafd1caa --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4970.yml @@ -0,0 +1,4 @@ +author: Martin Rivard +changes: + - {tweak: removed calculate_affecting_pressure() hardcoding} +delete-after: true From e788ca1fca56435369378855054f2188e3e076df Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 26 Mar 2025 13:48:42 -0400 Subject: [PATCH 0571/1331] centralize flash behavior, add flash burn support --- code/game/machinery/flasher.dm | 25 +------------------------ code/modules/bodytype/_bodytype.dm | 2 ++ code/modules/mechs/equipment/combat.dm | 18 +----------------- code/modules/mob/living/living.dm | 12 ++++++++++++ code/modules/organs/internal/eyes.dm | 3 +++ 5 files changed, 19 insertions(+), 41 deletions(-) diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 375bc0653cd4..1fefcc5ed634 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -71,30 +71,7 @@ continue var/flash_time = strength - if(isliving(viewer)) - if(viewer.eyecheck() > FLASH_PROTECTION_NONE) - continue - if(ishuman(viewer)) - var/mob/living/human/H = viewer - flash_time = round(H.get_flash_mod() * flash_time) - if(flash_time <= 0) - return - var/vision_organ_tag = H.get_vision_organ_tag() - if(vision_organ_tag) - var/obj/item/organ/internal/organ = GET_INTERNAL_ORGAN(H, vision_organ_tag) - if(organ && organ.is_bruised() && prob(organ.get_organ_damage() + 50)) - H.flash_eyes() - organ.adjust_organ_damage(rand(1, 5)) - - if(!viewer.is_blind()) - do_flash(viewer, flash_time) - -/obj/machinery/flasher/proc/do_flash(var/mob/living/victim, var/flash_time) - victim.flash_eyes() - ADJ_STATUS(victim, STAT_BLURRY, flash_time) - ADJ_STATUS(victim, STAT_CONFUSE, flash_time + 2) - SET_STATUS_MAX(victim, STAT_STUN, flash_time / 2) - SET_STATUS_MAX(victim, STAT_WEAK, 3) + viewer.handle_flashed(flash_time) /obj/machinery/flasher/emp_act(severity) if(stat & (BROKEN|NOPOWER)) diff --git a/code/modules/bodytype/_bodytype.dm b/code/modules/bodytype/_bodytype.dm index a805373f1b1b..a60007dda2e4 100644 --- a/code/modules/bodytype/_bodytype.dm +++ b/code/modules/bodytype/_bodytype.dm @@ -239,6 +239,8 @@ var/global/list/bodytypes_by_category = list() var/eye_blend = ICON_ADD /// Stun from blindness modifier. var/eye_flash_mod = 1 + //how much damage to take from being flashed (if any) + var/eye_flash_burn = 0 // Bodytype temperature damage thresholds. /// Cold damage level 1 below this point. -30 Celsium degrees diff --git a/code/modules/mechs/equipment/combat.dm b/code/modules/mechs/equipment/combat.dm index 6ab5b456ccbc..9ef1fedf828e 100644 --- a/code/modules/mechs/equipment/combat.dm +++ b/code/modules/mechs/equipment/combat.dm @@ -317,23 +317,7 @@ for (var/mob/living/O in oviewers(flash_range, owner)) if(istype(O)) - var/protection = O.eyecheck() - if(protection >= FLASH_PROTECTION_MODERATE) - return - - if(protection >= FLASH_PROTECTION_MINOR) - flash_time /= 2 - - if(ishuman(O)) - var/mob/living/human/H = O - flash_time = round(H.get_flash_mod() * flash_time) - if(flash_time <= 0) - return - - if(!O.is_blind()) - O.flash_eyes(FLASH_PROTECTION_MODERATE - protection) - SET_STATUS_MAX(O, STAT_BLURRY, flash_time) - SET_STATUS_MAX(O, STAT_CONFUSE, (flash_time + 2)) + O.handle_flashed(flash_time) /obj/item/mech_equipment/flash/attack_self(mob/user) . = ..() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index fbce10dfb123..b85cdfcb404c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -869,6 +869,14 @@ default behaviour is: return I.get_flash_mod() return get_bodytype()?.eye_flash_mod +/mob/living/proc/get_flash_burn() + var/vision_organ_tag = get_vision_organ_tag() + if(vision_organ_tag) + var/obj/item/organ/internal/eyes/I = get_organ(vision_organ_tag, /obj/item/organ/internal/eyes) + if(I) + return I.get_flash_burn() + return get_bodytype()?.eye_flash_burn + /mob/living/proc/eyecheck() var/total_protection = flash_protection if(should_have_organ(BP_EYES)) @@ -1692,6 +1700,7 @@ default behaviour is: /mob/living/handle_flashed(var/flash_strength) var/safety = eyecheck() + var/flash_burn = get_flash_burn() if(safety >= FLASH_PROTECTION_MODERATE || flash_strength <= 0) // May be modified by human proc. return FALSE @@ -1699,6 +1708,9 @@ default behaviour is: SET_STATUS_MAX(src, STAT_STUN, (flash_strength / 2)) SET_STATUS_MAX(src, STAT_BLURRY, flash_strength) SET_STATUS_MAX(src, STAT_CONFUSE, (flash_strength + 2)) + + if(flash_burn > 0) + apply_damage(flash_strength * flash_burn/5, BURN, BP_HEAD, used_weapon = "Photon burns") if(flash_strength > 3) drop_held_items() if(flash_strength > 5) diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index 94e7e27418e3..27d1be46f409 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -22,6 +22,9 @@ /obj/item/organ/internal/eyes/proc/get_flash_mod() return bodytype.eye_flash_mod +/obj/item/organ/internal/eyes/proc/get_flash_burn() + return bodytype.eye_flash_burn + /obj/item/organ/internal/eyes/proc/get_darksight_range() return bodytype.eye_darksight_range From 324fcddbe2b41ea41dcd719c88689e081c54b6c9 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 26 Mar 2025 15:46:45 -0400 Subject: [PATCH 0572/1331] fix portable flasher offset --- code/game/machinery/flasher.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 1fefcc5ed634..edd7f125381e 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -86,6 +86,7 @@ desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." icon_state = "pflash1" icon = 'icons/obj/machines/flash_portable.dmi' + directional_offset = @'{"NORTH":{"y":0}, "SOUTH":{"y":0}, "EAST":{"x":0}, "WEST":{"x":0}}' strength = 8 anchored = FALSE base_state = "pflash" From f9ff7bbab4078fc04fff784e1885a1366d3ac3ed Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 26 Mar 2025 15:50:43 -0400 Subject: [PATCH 0573/1331] mech flash afterattack --- code/modules/mechs/equipment/combat.dm | 28 +------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/code/modules/mechs/equipment/combat.dm b/code/modules/mechs/equipment/combat.dm index 9ef1fedf828e..b2518d69aaeb 100644 --- a/code/modules/mechs/equipment/combat.dm +++ b/code/modules/mechs/equipment/combat.dm @@ -347,30 +347,4 @@ var/obj/item/cell/cell = owner.get_cell() cell.use(active_power_use * CELLRATE) - var/protection = O.eyecheck() - if(protection >= FLASH_PROTECTION_MAJOR) - return - - if(protection >= FLASH_PROTECTION_MODERATE) - flash_time /= 2 - - if(ishuman(O)) - var/mob/living/human/H = O - flash_time = round(H.get_flash_mod() * flash_time) - if(flash_time <= 0) - return - - if(!O.is_blind()) - O.flash_eyes(FLASH_PROTECTION_MAJOR - protection) - SET_STATUS_MAX(O, STAT_BLURRY, flash_time) - SET_STATUS_MAX(O, STAT_CONFUSE, (flash_time + 2)) - - if(isanimal(O)) //Hit animals a bit harder - SET_STATUS_MAX(O, STAT_STUN, flash_time) - else - SET_STATUS_MAX(O, STAT_STUN, (flash_time / 2)) - - if(flash_time > 3) - O.drop_held_items() - if(flash_time > 5) - SET_STATUS_MAX(O, STAT_WEAK, 2) + O.handle_flashed(flash_time) From a6f5dcc53d4c227aaffc64e98dd78199ed170fc1 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 26 Mar 2025 15:51:14 -0400 Subject: [PATCH 0574/1331] add support for non-stunning flashes --- code/modules/mechs/equipment/combat.dm | 2 +- code/modules/mob/living/living.dm | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/code/modules/mechs/equipment/combat.dm b/code/modules/mechs/equipment/combat.dm index b2518d69aaeb..750d69df49de 100644 --- a/code/modules/mechs/equipment/combat.dm +++ b/code/modules/mechs/equipment/combat.dm @@ -317,7 +317,7 @@ for (var/mob/living/O in oviewers(flash_range, owner)) if(istype(O)) - O.handle_flashed(flash_time) + O.handle_flashed(flash_time, do_stun = FALSE) /obj/item/mech_equipment/flash/attack_self(mob/user) . = ..() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index b85cdfcb404c..043a2bc69b54 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1697,15 +1697,18 @@ default behaviour is: return range * range - 0.333 return range -/mob/living/handle_flashed(var/flash_strength) +/mob/living/handle_flashed(var/flash_strength, do_stun = TRUE) var/safety = eyecheck() var/flash_burn = get_flash_burn() - if(safety >= FLASH_PROTECTION_MODERATE || flash_strength <= 0) // May be modified by human proc. + var/flash_mod = get_flash_mod() + + if(safety >= FLASH_PROTECTION_MODERATE || flash_strength <= 0 || flash_mod <= 0) // May be modified by human proc. return FALSE flash_eyes(FLASH_PROTECTION_MODERATE - safety) - SET_STATUS_MAX(src, STAT_STUN, (flash_strength / 2)) + if(do_stun) + SET_STATUS_MAX(src, STAT_STUN, (flash_strength / 2)) SET_STATUS_MAX(src, STAT_BLURRY, flash_strength) SET_STATUS_MAX(src, STAT_CONFUSE, (flash_strength + 2)) @@ -1715,6 +1718,7 @@ default behaviour is: drop_held_items() if(flash_strength > 5) SET_STATUS_MAX(src, STAT_WEAK, 2) + return TRUE /mob/living/verb/showoff() set name = "Show Held Item" From 30b0405dd10ce08fdd00cade21ef8096a0f4c207 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 26 Mar 2025 16:03:53 -0400 Subject: [PATCH 0575/1331] do_stun fix --- code/modules/mob/living/human/human.dm | 2 +- code/modules/mob/living/silicon/silicon.dm | 2 +- code/modules/mob/mob.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 85abc9b909fe..f2a5565b03e8 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -1019,7 +1019,7 @@ /mob/living/human/proc/post_setup(species_uid, datum/mob_snapshot/supplied_appearance) try_refresh_visible_overlays() //Do this exactly once per setup -/mob/living/human/handle_flashed(var/flash_strength) +/mob/living/human/handle_flashed(var/flash_strength, do_stun = FALSE) var/safety = eyecheck() if(safety < FLASH_PROTECTION_MODERATE) flash_strength = round(get_flash_mod() * flash_strength) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 8d293f584fa5..5e738a6f7c98 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -415,7 +415,7 @@ if(os) os.Process() -/mob/living/silicon/handle_flashed(var/flash_strength) +/mob/living/silicon/handle_flashed(var/flash_strength, do_stun = FALSE) SET_STATUS_MAX(src, STAT_PARA, flash_strength) SET_STATUS_MAX(src, STAT_WEAK, flash_strength) return TRUE diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dca36aa57a61..b53f19cc41e3 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1156,7 +1156,7 @@ return FALSE -/mob/proc/handle_flashed(var/flash_strength) +/mob/proc/handle_flashed(var/flash_strength, do_stun = FALSE) return FALSE /mob/proc/do_flash_animation() From 11979bab238a398e220d6fcf2c3abe828067b841 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 27 Mar 2025 00:53:41 +0000 Subject: [PATCH 0576/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-4970.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4970.yml diff --git a/html/changelog.html b/html/changelog.html index eafddc8a7f18..51d24fb53a78 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    27 March 2025

    +

    Martin Rivard updated:

    +
      +
    • removed calculate_affecting_pressure() hardcoding
    • +
    +

    25 March 2025

    Penelope Haze updated:

    From 670f619dcb0158f0ee7e987999894fccf8433291 Mon Sep 17 00:00:00 2001 From: Cerebulon Date: Sun, 6 Apr 2025 20:59:54 +0100 Subject: [PATCH 0607/1331] Fix tajaran clothing overrides --- .../tajaran/icons/clothing/atmos/suit.dmi | Bin 901 -> 903 bytes .../tajaran/icons/clothing/atmos_alt/suit.dmi | Bin 824 -> 826 bytes .../icons/clothing/deathsquad/suit.dmi | Bin 1086 -> 1088 bytes .../icons/clothing/engineering/suit.dmi | Bin 2635 -> 2453 bytes .../icons/clothing/engineering_alt/suit.dmi | Bin 824 -> 826 bytes .../icons/clothing/excavation/suit.dmi | Bin 936 -> 938 bytes .../tajaran/icons/clothing/medical/suit.dmi | Bin 2217 -> 2218 bytes .../icons/clothing/medical_alt/suit.dmi | Bin 863 -> 865 bytes .../tajaran/icons/clothing/merc/suit.dmi | Bin 956 -> 958 bytes .../tajaran/icons/clothing/mining/suit.dmi | Bin 779 -> 781 bytes .../icons/clothing/mining_alt/suit.dmi | Bin 1143 -> 1145 bytes .../tajaran/icons/clothing/nasa/suit.dmi | Bin 1195 -> 1197 bytes .../tajaran/icons/clothing/pilot/suit.dmi | Bin 813 -> 815 bytes .../tajaran/icons/clothing/salvage/suit.dmi | Bin 1142 -> 1139 bytes .../tajaran/icons/clothing/sec/suit.dmi | Bin 905 -> 907 bytes .../tajaran/icons/clothing/sec_alt/suit.dmi | Bin 941 -> 943 bytes .../tajaran/icons/clothing/wizard/suit.dmi | Bin 969 -> 971 bytes 17 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mods/species/tajaran/icons/clothing/atmos/suit.dmi b/mods/species/tajaran/icons/clothing/atmos/suit.dmi index 856901b8e11ee415dd21a4439074284deba956a2..a31a324c51b546cd7c7269cbea6eb9f4013edc2e 100644 GIT binary patch delta 51 zcmV-30L=e|2ZslcZ~=UgaZUq2GewcW7aR;1XIfEWZYqdv$fc~{>gNKs2mr89Avu{@ Jf$XvKk^0WC41SWll6zAt^tlQnxrKza+l6G_ypBi!&v&s2F4j7iU^gVs0vk aZOEmp;OgfBwgv!T(I8?fTKG+|=}`imJ{&0k diff --git a/mods/species/tajaran/icons/clothing/deathsquad/suit.dmi b/mods/species/tajaran/icons/clothing/deathsquad/suit.dmi index e55e927d5ec975a0838b2ec2a07da40eee6343a9..54641a3200ef76676f7490f84e3a1a930cdd1d37 100644 GIT binary patch delta 71 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 Nz{tQ1`&5q*{Xb`|$gNKs2mr89Avu{@ Jf$Xv2R|4(L6s70UIBWMUrqs4k+2sW2p4BsQDSZ?h;7KFtl;YB0=5PKV9_9ADq8qW HvEx?))gTlB diff --git a/mods/species/tajaran/icons/clothing/excavation/suit.dmi b/mods/species/tajaran/icons/clothing/excavation/suit.dmi index dc261cdcbfccf5bfa220e785b876a3ab37abb749..4cd8145d824899a79998ebaf1af1138548cfc6e1 100644 GIT binary patch delta 51 zcmV-30L=fW2dW2gNKs2mr89Avu{@ Jf$XvKwF2>n6)pe( delta 68 zcmV-K0K5OH2dD>-Z~=OeaZWTouz6V{3J#-Um$R()?kB;12r#CA+&eWnG1HusL Q2e42fIddwDK(Va-2cRz(s{jB1 delta 57 zcmV-90LK5S5vdW7BmsYsC0;33#3=-NYJlNvgu5BRALS%?gh*wdluyFhu$R PV9_9A46vn1v90|F=|>ve diff --git a/mods/species/tajaran/icons/clothing/medical_alt/suit.dmi b/mods/species/tajaran/icons/clothing/medical_alt/suit.dmi index 7b9f801e353411fb7faa90dcf669c94fdc65c1f8..ec3911d4b0ecb9dcdb319f888bea8d8b87d9ce56 100644 GIT binary patch delta 70 zcmV-M0J;C)2H^&fY5{zaYfd&lGesdOKc!N)I48d(zPL2AM2U+tC9|j)WDFN)T2W$d cDu`{!rL5rU=K{6}0I*OYIhk33?6K`~0@jZmQ2+n{ delta 68 zcmV-K0K5O;2HysdY5{tYYfdy(At^tlQnxrKza+l6G_ypBi!&v&s2F4j7iU^gVs0vk aZOEmp;OgfBwgv!T(I8?fTKG+|?{WgP*c_?= diff --git a/mods/species/tajaran/icons/clothing/merc/suit.dmi b/mods/species/tajaran/icons/clothing/merc/suit.dmi index 37bef8119e7a20e6be66694eb6eadfdcb884fcde..142f157f9e10b55448ad54cf70892735a6fa6907 100644 GIT binary patch delta 71 zcmV-N0J#6W2fhc8g#mn#hE6s=GesdOKc!N)I48d(zPL2AM2U+tC9|j)WDFN)T2W$d dDu`{!rL5rU=K{6}0I*OYIhk33?6U{~v;yl-9HRgL delta 69 zcmV-L0J{Ia2fPQ6g#mhzhE6n9At^tlQnxrKza+l6G_ypBi!&v&s2F4j7iU^gVs0vk bZOEmp;OgfBwgv!T(I8?fTKG+~3IVhN$qgI) diff --git a/mods/species/tajaran/icons/clothing/mining/suit.dmi b/mods/species/tajaran/icons/clothing/mining/suit.dmi index 0b6cc99dbfcabbb3cbf9083f7665a8db1e656f02..807bbbe1006766721d8f26ff85d0dea4fc14f341 100644 GIT binary patch delta 70 zcmV-M0J;B*28{-gTLFBLTuwGWGesdOKc!N)I48d(zPL2AM2U+tC9|j)WDFN)T2W$d cDu`{!rL5rU=K{6}0I*OYIhk33?6KZ00--J(Hvj+t delta 49 zcmV-10M7r728#xeTLF5JTuuR1k*yaT2p4BsQDSZ?h;7KFtl;YB0=5PKV9_9ADq8qW HvEVHN!hRER diff --git a/mods/species/tajaran/icons/clothing/mining_alt/suit.dmi b/mods/species/tajaran/icons/clothing/mining_alt/suit.dmi index 6c6a7e8b724d3be521c3398ee103feb7c39feb45..e0875569f4af2cc7b48285a61232d0ad1b101dfc 100644 GIT binary patch delta 71 zcmV-N0J#762>A$*!~uMf#ZERqGesdOKc!N)I48d(zPL2AM2U+tC9|j)WDFN)T2W$d dDu`{!rL5rU=K{6}0I*OYIhk33?6XG!Fas;09aR7T delta 69 zcmV-L0J{JA2=@q(!~uGd#ZEL;At^tlQnxrKza+l6G_ypBi!&v&s2F4j7iU^gVs0vk bZOEmp;OgfBwgv!T(I8?fTKG+~NdYhe2Xh>* diff --git a/mods/species/tajaran/icons/clothing/nasa/suit.dmi b/mods/species/tajaran/icons/clothing/nasa/suit.dmi index a641b49608b5c0631ed857a54ba0a056df4e0d74..1106524e22b37eb12b5bc93cd27aaa49ddbbc782 100644 GIT binary patch delta 59 zcmV-B0L1^R39Si`!~uno#ZDVPGesdOKc!N)I48d(zPL2AM2U+tC9|j)WDJum0U8Qs R01~TLYQU6=MJZ delta 57 zcmV-90LK5V39AW^!~uhm#ZDPjAt^tlQnxrKza+l6G_ypBi!&v&s2F4jlP&=o3SI!S Pr6f=dyn@xUQUO~7gI^U+ diff --git a/mods/species/tajaran/icons/clothing/pilot/suit.dmi b/mods/species/tajaran/icons/clothing/pilot/suit.dmi index b11b61d8d69911d19de93cab69388f41745d958b..eeafe5468dd23c687fae9cc9962d9b2315465a91 100644 GIT binary patch delta 51 zcmV-30L=fb2CoK?UIBcOUrqx*GewcE7aR;1XIfEWZYqdv$fc~{>gNKs2mr89Avu{@ Jf$Xv2Oakd^6omi) delta 49 zcmV-10M7rf2CW8=UIBWMUrqs4k+2sW2p4BsQDSZ?h;7KFtl;YB0=5PKV9_9ADq8qW HvExhv(CQQ5 diff --git a/mods/species/tajaran/icons/clothing/salvage/suit.dmi b/mods/species/tajaran/icons/clothing/salvage/suit.dmi index df273abc4f408163149609581ea9631e4d2146d3..de86bbe11a8b1793400df1c1ddeca14b8d43485c 100644 GIT binary patch delta 928 zcmV;R17H012=fS#7ZpGV0{{R3owtGP0001TP)t-s0001yEN=v^gtDrUpHLDYoZ^zi zl2jm5DJ?Z8GcP|gMUw#m7k>;FXIfEWZYqdv$fc~{>gNKs2mr89Avu{@f$RVP11CvD zK~z|U?N?oM+%^mp0f}G;NV3S-Qc7MDC(i%>i(Osa>C__Khj#j8%-oIU7F;eweDH?h z!#?bPH+NbeCU)aEI;I}(#%Y>H*TcPpPuSp76yL!ehY;9xaJ6|^;eW6$vuO)g;`L#E zSedkmd)ZQ^g>u@&Up+L*O7rK(&oix(8Vt%$yVvZq`u*Sr0YzHoFTZ`67d$m^pIQ7e zZ}Z}X^F@Msj`FHNB3~t{V^;#nDijKyF9dpMl)D0nc)k)a6ckqoA1JuQfZ%G5kT6rs z6@gwu;3{FWWLuDWm468QwtwYs`#1i&-&!Gj_bD7(^>sT!@{JJO4M#jb%sA?syM|DG z(&M>i%lv$3^7RdV%RU}N;)LjkKHuIFU<)Wrrk0?wktw$yz}1bcz<*rV!wx0ODI4k0 zf`@~Bb&jj&HjZ4jMZGT8%;ft)sGC?DNAM>>qEZT_9)W2AL4VN>KCr3;7etlYxEV5x zSF<)gj41Uo78L#1#V3wO4-4CJ<2et$r~u?2c<2M=F{bLcn*sW&H#GVITIN8Itv1IR#V; z^E(#^lq4KR7k_#L0Tken-g3ceEDs%mSqg=496w)PURpSQ{&^fBP?5Pw@K}77xJ$~~ zCeWN@kCJ_PY%3_3j~hCBwl;yGuP+-0XXz*qe&T(xTAS;^P^&FFoTQ^bXss*BUfvGI zSF2@zb!jX3C7CS(&OL&R_0c%XueJhQlO$4-+&M=8iGQLzv9=Wqg?*~e+n!?^k9pso zYw}#$IQnJww$G*Z6x1^BhC}KRRE#~MRXYM8L02NPweyY!SBYwk+S>qADb~}cS1zGi z#eDDapBhitYOk1Fh_tSK0jGp@r5Jh&K#*#!?-2k_T>^(n#pS>{H=xrFo9<3+1{8W( z;ydr~{$Up5QbJn-rhmX2viCzc@i0yo2HYOP7?4W7TDKt~m8^QC5nSK=cS2OyAD0x0 zXIJU2M=yzl`^l-{?=K?!(df?6ul?zN2hINShy4!AoH9B??sB960000 delta 931 zcmV;U16=&`2=)k&7Zpqh0{{R3>UBYv0001TP)t-sz`($fEN=v&NM!?&pHLD&91E`E zlEji!AX6zVH77GKRg(b$7k>yBXIfEWZYqdv$fc~{>gNKs1^{ARAZT3gycPfe11d>G zK~z|U?N`xq+c*pq0hwS4NQ%fNQcJpR+VuZ_u`4$>Hy4q)hkLskGxlh_z;Z!?5IhWb zyW8FVNOOnvVPZFqqhspfZoFJBqwC>b!UrtyA&T$djzb9SI=I?2FMn`Y=E<~$EAjF$ zJuFPx#J#L3(@Z(7;?EwMWTENfNeu?&r_F2jS^c(igMcE<)2Gj$rWsES+-DX) zPwO;$;e3*yo};`fkjQ6=>e!V)vIvEO=M#bM8s(-yBA(9#33BP2`| zb48-(6u3%QE!i5Ro_{3*ziwan>-LquY}Zx_-+T)DT76yjkbENrcf}D;4-=01>dq-t zpLBn&*)l)xntXkOU$c)pkvJjRqtDm31XuzJlc^6ug-Dx+{RJMHmld!nw5Mz2z3|M#u5BQkf@YGsYhU1Kz~rQgAc4K!39y}Hg1Lr zC%+X^tON~9#YbAOHi5=D7pZ7UcG`&6ITEyp$< z^R_ppq-U65nVm*9% z*CkY|n6i5OhsFc8+Ph3HL|T@=*QwGCI7%LXH3c002ovPDHLk FV1j)vuDt*N diff --git a/mods/species/tajaran/icons/clothing/sec/suit.dmi b/mods/species/tajaran/icons/clothing/sec/suit.dmi index 1672208128ea4a2983560edc6a541257a5fe74af..ab3e3755309d16730bb5cb2c316164025f78cce9 100644 GIT binary patch delta 70 zcmV-M0J;B(2a5-gY5{zaYfd&lGesdOKc!N)I48d(zPL2AM2U+tC9|j)WDFN)T2W$d cDu`{!rL5rU=K{6}0I*OYIhk33?6K{f0;vxi(EtDd delta 68 zcmV-K0K5N-2Z;xeY5{tYYfdy(At^tlQnxrKza+l6G_ypBi!&v&s2F4j7iU^gVs0vk aZOEmp;OgfBwgv!T(I8?fTKG+|@0t delta 69 zcmV-L0J{IL2dxK?f&qGwgHAM6At^tlQnxrKza+l6G_ypBi!&v&s2F4j7iU^gVs0vk bZOEmp;OgfBwgv!T(I8?fTKG+~2LY%8zkM6Y diff --git a/mods/species/tajaran/icons/clothing/wizard/suit.dmi b/mods/species/tajaran/icons/clothing/wizard/suit.dmi index b8b7ed3ebd0b2ad97efee68680b224ce11a1e4f7..c27a511cc66270ee2ad8d716a36de45122188e40 100644 GIT binary patch delta 71 zcmV-N0J#6j2g?VLg#mn#hE6s=GesdOKc!N)I48d(zPL2AM2U+tC9|j)WDFN)T2W$d dDu`{!rL5rU=K{6}0I*OYIhk33?6U{~!2 Date: Sun, 13 Apr 2025 00:21:54 +1000 Subject: [PATCH 0608/1331] Automatic changelog generation for PR #4985 [ci skip] --- html/changelogs/AutoChangeLog-pr-4985.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4985.yml diff --git a/html/changelogs/AutoChangeLog-pr-4985.yml b/html/changelogs/AutoChangeLog-pr-4985.yml new file mode 100644 index 000000000000..080d86fbb9c5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4985.yml @@ -0,0 +1,4 @@ +author: Cerebulon +changes: + - {bugfix: Fixed tajaran sprite sheet overrides.} +delete-after: true From 71a8725605ff112220fd21c57b2d96c4a226a3e0 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 13 Apr 2025 02:24:40 +0000 Subject: [PATCH 0609/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-4985.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4985.yml diff --git a/html/changelog.html b/html/changelog.html index 2403d9ab6b9a..37e63ea08cd9 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    13 April 2025

    +

    Cerebulon updated:

    +
      +
    • Fixed tajaran sprite sheet overrides.
    • +
    +

    06 April 2025

    Elizabeth updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index cc4fe1742e68..aa659926f105 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15024,3 +15024,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: many of the buildings in Shaded Hills now use timber-framed, plastered, and/or wattle-and-daub walls instead of just log walls - soundadd: 'added a new lobby track to Shaded Hills: Adventure by Alexander Nakarada.' +2025-04-13: + Cerebulon: + - bugfix: Fixed tajaran sprite sheet overrides. diff --git a/html/changelogs/AutoChangeLog-pr-4985.yml b/html/changelogs/AutoChangeLog-pr-4985.yml deleted file mode 100644 index 080d86fbb9c5..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4985.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Cerebulon -changes: - - {bugfix: Fixed tajaran sprite sheet overrides.} -delete-after: true From f900b7613d9352e72a8f3a338538472ee6f43c91 Mon Sep 17 00:00:00 2001 From: Zandario Date: Fri, 11 Apr 2025 20:35:21 -0400 Subject: [PATCH 0610/1331] Add toggle browser inspect admin verb for debugging Web UIs --- code/modules/admin/admin_verbs.dm | 1 + code/modules/admin/verbs/debug.dm | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 9c203d83c176..52587d2a21d2 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -218,6 +218,7 @@ var/global/list/admin_verbs_debug = list( /datum/admins/proc/force_weather_state, /datum/admins/proc/force_kill_weather, /client/proc/force_reload_theme_css, + /client/proc/toggle_browser_inspect, ) var/global/list/admin_verbs_paranoid_debug = list( diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index edc6df1696af..6b3cd461505b 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -512,3 +512,26 @@ . += "" show_browser(usr, JOINTEXT(.), "window=dellog") + +/client/proc/toggle_browser_inspect() + set category = "Debug" + set name = "Toggle Browser Inspect" + + #if DM_VERSION >= 516 + + var/list/browser_options = winget(src, null, "browser-options") + + if(findtext(browser_options, "devtools") == TRUE) + // Disable the dev tools. + winset(src, null, list("browser-options" = "-devtools")) + message_admins("[key_name_admin(usr)] has disabled Browser Inspection.") + else + // Enable the dev tools. + winset(src, null, list("browser-options" = "+devtools")) + message_admins("[key_name_admin(usr)] has enabled Browser Inspection.") + + #else + + alert("Browser Inspection is not supported in this version of BYOND, please update to 516 or later.") + + #endif \ No newline at end of file From 0a6500edc7bd671635a521a023afd7e9b5cd0f75 Mon Sep 17 00:00:00 2001 From: Zandario Date: Fri, 11 Apr 2025 20:43:56 -0400 Subject: [PATCH 0611/1331] Fixed wrong variable type --- code/modules/admin/verbs/debug.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 6b3cd461505b..de5d9bb426d5 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -519,7 +519,7 @@ #if DM_VERSION >= 516 - var/list/browser_options = winget(src, null, "browser-options") + var/browser_options = winget(src, null, "browser-options") if(findtext(browser_options, "devtools") == TRUE) // Disable the dev tools. From e54c2400170bf3eb78bf6a37ab8be590eb8c715d Mon Sep 17 00:00:00 2001 From: Zandario Date: Fri, 11 Apr 2025 20:45:04 -0400 Subject: [PATCH 0612/1331] Removed uneccessary condition check --- code/modules/admin/verbs/debug.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index de5d9bb426d5..c22ef2c4ab04 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -521,7 +521,7 @@ var/browser_options = winget(src, null, "browser-options") - if(findtext(browser_options, "devtools") == TRUE) + if(findtext(browser_options, "devtools")) // Disable the dev tools. winset(src, null, list("browser-options" = "-devtools")) message_admins("[key_name_admin(usr)] has disabled Browser Inspection.") From 1624050093fffd73611b67b3fc7977db02d9f13d Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 13 Apr 2025 23:27:11 +1000 Subject: [PATCH 0613/1331] Automatic changelog generation for PR #4987 [ci skip] --- html/changelogs/AutoChangeLog-pr-4987.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4987.yml diff --git a/html/changelogs/AutoChangeLog-pr-4987.yml b/html/changelogs/AutoChangeLog-pr-4987.yml new file mode 100644 index 000000000000..875e999fdbbd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4987.yml @@ -0,0 +1,4 @@ +author: Zandario +changes: + - {admin: Added a "Toggle Browser Inspect" debug verb for debugging UIs} +delete-after: true From b24b7966e5d3253c8201b1dfea4d81a065d20e77 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 14 Apr 2025 00:57:55 +0000 Subject: [PATCH 0614/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-4987.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4987.yml diff --git a/html/changelog.html b/html/changelog.html index 37e63ea08cd9..b466000035d1 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
      +

      14 April 2025

      +

      Zandario updated:

      +
        +
      • Added a "Toggle Browser Inspect" debug verb for debugging UIs
      • +
      +

      13 April 2025

      Cerebulon updated:

        diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index aa659926f105..6358b713724c 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15027,3 +15027,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2025-04-13: Cerebulon: - bugfix: Fixed tajaran sprite sheet overrides. +2025-04-14: + Zandario: + - admin: Added a "Toggle Browser Inspect" debug verb for debugging UIs diff --git a/html/changelogs/AutoChangeLog-pr-4987.yml b/html/changelogs/AutoChangeLog-pr-4987.yml deleted file mode 100644 index 875e999fdbbd..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4987.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Zandario -changes: - - {admin: Added a "Toggle Browser Inspect" debug verb for debugging UIs} -delete-after: true From 85c57f34b0de15a1a0cd27462ab3219aeff8b9ec Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 22 Apr 2025 00:55:51 +0000 Subject: [PATCH 0615/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index b466000035d1..67af07b8f140 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -120,12 +120,6 @@

        Penelope Haze updated:

        • Human and unathi sprites should be slightly brighter now.
        - -

        18 February 2025

        -

        MistakeNot4892 updated:

        -
          -
        • Modifiers like regeneration or personal shielding will now show at the top of the screen.
        • -
      From 6797e6742373432c906911a7d19f232ba0eb1230 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 28 Apr 2025 00:58:04 +0000 Subject: [PATCH 0616/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 67af07b8f140..13b7880bf540 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -114,12 +114,6 @@

      MistakeNot4892 updated:

      • Old browser-based module selection for robots has been removed, use the INV button to open storage like you would a belt/backpack.
      - -

      24 February 2025

      -

      Penelope Haze updated:

      -
        -
      • Human and unathi sprites should be slightly brighter now.
      • -
    From 5a98a6fe0d1ab993214153ebf49564324f80ce76 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Tue, 18 Mar 2025 01:52:13 -0400 Subject: [PATCH 0617/1331] Improve wooden chest overlays --- icons/obj/closets/bases/chest.dmi | Bin 1872 -> 1914 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/obj/closets/bases/chest.dmi b/icons/obj/closets/bases/chest.dmi index bef40525584deb2103b95053b0fbaabd9801ca3b..b43edd86460bcea5fb6d538226e14ed9105a5ae6 100644 GIT binary patch delta 1531 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 From 22406221f63f96a4bb26e480fbdcb757581cc71c Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Tue, 18 Mar 2025 02:24:41 -0400 Subject: [PATCH 0618/1331] Fix sharpened weapon damage calculation --- mods/content/item_sharpening/effect_sharpen.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/content/item_sharpening/effect_sharpen.dm b/mods/content/item_sharpening/effect_sharpen.dm index 5847ac347b36..b5703b653f4b 100644 --- a/mods/content/item_sharpening/effect_sharpen.dm +++ b/mods/content/item_sharpening/effect_sharpen.dm @@ -2,7 +2,7 @@ var/uses = LAZYACCESS(parameters, IE_PAR_USES) if(uses <= 0) return base_damage - . = (1 + ((uses / max(1, LAZYACCESS(parameters, IE_PAR_MAX_USES))) * LAZYACCESS(parameters, IE_PAR_SHARP_DAM_MULT))) + . = base_damage * (1 + ((uses / max(1, LAZYACCESS(parameters, IE_PAR_MAX_USES))) * LAZYACCESS(parameters, IE_PAR_SHARP_DAM_MULT))) /decl/item_effect/sharpened/expend_attack_use(obj/item/used_item, mob/user, list/parameters) var/uses = LAZYACCESS(parameters, IE_PAR_USES) From ec667ef7f6097c02b77a280c07daf8c9c3d203c7 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 20:53:05 -0400 Subject: [PATCH 0619/1331] Fix missing or broken icon states --- .../objects/structures/beds/simple_bed.dm | 4 ++-- code/game/objects/structures/benches/bench.dm | 2 +- .../clothing/neck/necklace/_pendant.dm | 1 + code/modules/gemstones/_gemstone.dm | 1 + icons/obj/structures/furniture/bench.dmi | Bin 1836 -> 1862 bytes icons/obj/structures/furniture/bench_wood.dmi | Bin 5423 -> 5535 bytes icons/obj/structures/furniture/lounge.dmi | Bin 3902 -> 3970 bytes icons/obj/structures/furniture/pew_wood.dmi | Bin 19787 -> 18361 bytes .../obj/structures/furniture/sofa_middle.dmi | Bin 2975 -> 2858 bytes 9 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/game/objects/structures/beds/simple_bed.dm b/code/game/objects/structures/beds/simple_bed.dm index 3092fc63a717..31a656cfa0a8 100644 --- a/code/game/objects/structures/beds/simple_bed.dm +++ b/code/game/objects/structures/beds/simple_bed.dm @@ -1,7 +1,7 @@ /obj/structure/bed/simple desc = "A slatted wooden bed." icon = 'icons/obj/structures/furniture/bed_simple.dmi' - icon_state = "bed_padded_preview" // For map editor preview purposes + icon_state = "world_padded_preview" // For map editor preview purposes parts_type = /obj/item/stack/material/plank material = /decl/material/solid/organic/wood/oak initial_padding_material = /decl/material/solid/organic/plantmatter/grass/dry @@ -48,7 +48,7 @@ /obj/structure/bed/simple/crafted initial_padding_material = null - icon_state = "bed" + icon_state = ICON_STATE_WORLD color = /decl/material/solid/organic/wood/oak::color /obj/item/bedsheet/furs diff --git a/code/game/objects/structures/benches/bench.dm b/code/game/objects/structures/benches/bench.dm index 30cf502c7213..16ba3b36eceb 100644 --- a/code/game/objects/structures/benches/bench.dm +++ b/code/game/objects/structures/benches/bench.dm @@ -3,7 +3,7 @@ name = "bench" desc = "A simple slatted bench." icon = 'icons/obj/structures/furniture/bench.dmi' - icon_state = ICON_STATE_WORLD + "_standing" + icon_state = ICON_STATE_WORLD + "_preview" color = WOOD_COLOR_GENERIC initial_padding_material = null material = /decl/material/solid/organic/wood/oak diff --git a/code/modules/clothing/neck/necklace/_pendant.dm b/code/modules/clothing/neck/necklace/_pendant.dm index d29a9e6af27f..8b87b69213df 100644 --- a/code/modules/clothing/neck/necklace/_pendant.dm +++ b/code/modules/clothing/neck/necklace/_pendant.dm @@ -2,6 +2,7 @@ name = "pendant" desc = "A simple pendant." icon = 'icons/clothing/accessories/jewelry/pendants/square.dmi' + icon_state = ICON_STATE_WORLD abstract_type = /obj/item/pendant material = /decl/material/solid/metal/silver material_alteration = MAT_FLAG_ALTERATION_COLOR // We do manual name/desc handling for the gem. diff --git a/code/modules/gemstones/_gemstone.dm b/code/modules/gemstones/_gemstone.dm index d4e51499dd1f..54e970892efa 100644 --- a/code/modules/gemstones/_gemstone.dm +++ b/code/modules/gemstones/_gemstone.dm @@ -12,6 +12,7 @@ var/global/list/_available_gemstone_cuts name = "uncut gemstone" desc = "A hunk of uncut gemstone." icon = 'icons/obj/items/gemstones/uncut.dmi' + icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_TINY material = /decl/material/solid/gemstone/diamond material_alteration = MAT_FLAG_ALTERATION_COLOR // Name and desc are handled manually. diff --git a/icons/obj/structures/furniture/bench.dmi b/icons/obj/structures/furniture/bench.dmi index 80fda7a9a165a69e6635837aa98592dd9dcb20bf..0bf99b3e39271b8c2931f746611fe11bbcdd0b9d 100644 GIT binary patch delta 1721 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 a+9?3>E69-XKig|YGz6# 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 NGSD;8eW&dd@n5lrufhNT diff --git a/icons/obj/structures/furniture/lounge.dmi b/icons/obj/structures/furniture/lounge.dmi index 1323daaccd69af48d5dc7c5d40122d4f6550e01a..f377c4d7421beb8049c1837c6e9bf5ccea4c41b7 100644 GIT binary patch literal 3970 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 BN~{0? diff --git a/icons/obj/structures/furniture/pew_wood.dmi b/icons/obj/structures/furniture/pew_wood.dmi index cfc0b9d578e991d3d30badaf5f7bf702dd7a6925..7084548224ba10cc80d1f05c9ed60bf273aa4659 100644 GIT binary patch literal 18361 zcmX_o1y~%x*6iX0cXx;2?gWS6k_4Ba!9BRUOK=VD?(U1byCk@~^CtKH?|$F1i_K0? zPxm=>s_JZ*vZ6FH0s#UD1VWaTkx&I*xBh*>!vfC)OcvX~OP#yAhLePeqmjc8JEtGE zHXx8|Mq-=*LLV!dgykVtzHyK6>B{YE^nUtd z9>e8<#hi8CrcA*A=9yw?I0!@rl9doucg;BMu(TsQbUT0ChbNmt<~*ZPFUnp(5%ZimI?OMJ(dT5WJb-6h}G zmYsN7!t7=Fj7!%Ex_ChXH!0~Ra{4f6SBPnI5VBL=vT`+P>t?d;rrmD|NP^p{aLL+f zbDa(EjnQl2$!k)N>v5iKqYK*2x3BwQ1=QfnNuSHa8uIqc=RIi=!9~OOu7>tV+N30s zl}X1bq`X%sIOpw<#NA0Dx%X%U$jXkNCZXqhZ-ydD3<5zT3wLOn;|GpQr?za}ixb{e zd8MU)0}FGvx?XJG+x$Fy>#DqqX4SDkpUgdp{CqD}^`A!0BdyK_;)XTeY=_vft~orh z=&-`Kpf^HcKnEbhR8?CvveRY~jCZQkNUtU_0ph@skt#Hp0IJqO`?lbp^fLJ(kt_Mdx zJ6d}YJJ!`{w0YoezK(tq5ctau%1d6NPqwdGAFQmL-JckrI1@7SBCasS9F~W@U$Hix z`C(;a?S~56)|pyyYHeItnA4~Q9KaSgY;Zb}>E%`3yGIjW!U-aS%_%vhnCgpsG(fX` zIgMciLuN*Um-Av6Ws@cyE#2^2oK~)144dAo@ui-%kyuG0whu08+4%Uv)b?sjD}v>j zLv|;Hp+I!WlEw~vpzgIb#+g*4#->JcS#0--lU}y+&Gw*#A`xT|e2^H(S5CyO>^OZ` zQ9;zEAy%FWctkF4n#H`VsnMdD*)%%wN2Sr-d1iGaZ>)JYFUtS9o;$kDf@K9J1O}z; zl^D@~WTDaIH%ZKZozj<@4!s<(ldiPfEbxQ<@sUsdERsNz?FPZYcyVdCTJ|eau0M#J z0tDLdeszBN6V-KmVTkfs?v#&q-;KmG1qvkid>Nxn;_-aD*d~1c)8`#^(EFy(F%wuZ z5rpSHpJLtzvR{ADl0@(onyEH?7C^Yu=+FbJfn#QkZa>3-E`nIRTB=GoHAN(M>;uKg zInxxztJ zup?t^Uo5vDXR{qLR%1<`eyX!ZCgL#};H|=mJZtJ#tww2ephzypDbX;Iu!EXCABDg* zswIJ9ze8IJB;Ct(Sos5Oy?2i@t6rx`e{%h2ty5AZprtH+O#GaZk>Qk4R=>*Yc4ZX3 zyJ)laiJ1gWUi?Fd7+vCLFRu;5#&`vUKgFpo)yyipj(r;!l5T9Bo#410}UB-XD5s4bl zpNa136GJLe%cF;6ktvoCfa8~R4lC-k1 zH8R{zDNsX`ME^KCDx}{=6W)B$IDh@ML_PL%0i&97iPnH(%gKdB{H_uslW0=@^OXgO z%X17u$nqNslum~SuA`9=uwQ?36Kq+1O^*csK0OoT+;7%Or zgG#<0uR^&vy5UE#;&g>G1~f7GJF2OH$|H7_U90XD1~6~fB$EysC>lvxae-A&x;JIh zFcoZF|JN;o?bk<-O?1-jxb!stTsmben0CI`&qcG%SJxZqt%;@3hDc5;v=Vt5AF*

    4*L+61hfTdZL6G5b*PZt=VY@SFt3 z41>l9gD#zC=v7lQkBKs*NH|>0l;al9=Cg<7Qjth^lb_exq8NnJ(%=pLT;Tt5gY=4# zZ-Jf3MwXuGuU*W4#^1j~_MqgXpDv!$STV%q%np8@=MO>%=}RJH zAVSf0CtaXSl&2z-!w_;2N$+ka`(#T{Rc667R2%8>exsyJfNYf>4I91t$sfM4i5vYx zlz0-==+pIm=hJ+ann5j!Wz*_X>AZ5XiOf7h^u&Z#20jGHB5(VtW#MdN;F_hqx@wXi z2zrtzwXZ^pzCb29X#FeaYzbfcV7dqc-3D`S#OSHOx0u4oc(OKOXU#+lDOa%*mk4}Jx8@7kjli2Piu!C$fwemQ$e^=R=_;6Sj zd_NkNTV}Zy<=v5UBZO4)DjgSK<)Z{ufA4a)s~6$eG~h~;c-96-)d7EL>|*)J<)*Ih zTF^^DcX4{H$UZzb)S@AHxrCmM37b~<4ssoG8a&8&W(s+nHDOW&0kXYY3aIDcN|&B_ zIgw7r5o#hovAF=^>lS{yL5%asVD*a69Ph!M&)9L$sXpuE^_JVl4C0E$(B^UQg}aUlR$mm#c}Jw9HBA!L2U725BvCXn zSqv&!e^YO%D)gyw-!``LJ5nx`)>qw*oI`iw+~uWy#6d@*TTh5j{E4~mb=Ug6PTUbu z-_e0Tf(~xoFYB`Pd)8js`w)5JhPQBb2Nec)=^`kip&^^i2v*Q(W_&0TdbDcbp##jh z)9I{jZh=+#D=FMvT#A|#rH4CUa^FlRd)Gzh#0YgN0tmT4FtYeHY>WXsa9MODXpA_^ zbLoI&C_{r`zF<+gcnJQ$0o>|;BV>YoSnu*|xcl>Qe05zCKqd;3J#+ogim$X?=ALMhJu%*{m9rmP?6(h8;7u9n+F~Tv zS~zA0WA)aSx`Di<-Rt*9Du5q4-(udO!j<&w!RK|~XCpN;trtie_>J7*H-%|K#-McE zDs=`?tdANRoP}ihW>j%#2?+`2L+k{#kvc8}iD^G^#CT|EX;tX3h{4pdy)QLx7lJX$ zbkGX3IX?)1t(!|l?sHRYvhs8H6}1~KKzG#uYsDu80Ax(CGB!3i)ch2hJ%}#^GZP`;qvJ-w0?``M546HuqOe}f zW6bz=%>nY2-*{mD>iq`snOT3McZY%^Qad%%W@&kC#jYO-#a}xnO`b-DA<;a?amXn9 zdSKE!_^qOTwQ?U#s^t|mz_7k+0CcJ+(L?=ajkcsrCmZixxeGGl7nf@D#yM-pFB_6F z8j6AqG{@3zAb4#fUCY1%0%G)T&T6Eavk44nDi&3EBJQTx#b&Kpwh2-LDr5LBqavM{ zu*fVIrT70`@cox~3I=!~+&mRg-^$~Z%=A34am~zMeAv=biYe(qC{QcJXQZ*Spa@pQ z0B^p_o7rLx%VIgu0s@RQ$@-r^RLuI`wg&ry-qW1phGg5y@IS5#HTAW{*;Dh6*CX-S z`Ue>*D=I`{?;T=KMXUgPbZ_GO>go^NCj(Yc!SU1CEBR}+>KVRzUScm zgMJqaSM0xB()C=+q1IZwa4{1wm*`JHpuPQl``1C|{8{=CN3!f}Kf136MgoL{NzPy+ zp#`#TJw^kxRu>|L>s_rl(>*Df1DJDdy;P_OA|HoB_J3m1x9Y7m_!p@Wrhwy5H6 zmv%5lu;W_i!rC^iKTDU!#IaJlh;E{uentGoqL)k9Ir{OVhF1Ln?_42hW2q;XjL4*Ejc8^yc(Vp!9 z^pc?pFBs^!a-j-;E3$pZ@_A_Rng1qC2J(EFl_L2Qv-#j5_}+V+rC5u?z+YHX7)4r11nrDvFG33pvrB1;}n__ zqAIWV=bIYu&tjm#!h#vq67k?0Dn0zm{=Pqr+mieQ51aT+~~Ner2dJ-DrD#tm|qheBOw?KT#6G-l;kS>!;+CS$agOj{pwLriwW7$ zG5GhI>pK*%?;S`Bu%!xKL&gmofeqC%r8fv7-OCB-O(KcBaknS8IfOVSA?y-)C_5MX zQIgbNtfyXzv2)0YC0WC3yflY`bC;6-dwsGsV2$-$r`=bn<0h=5U0hPMY`)i}1p4Ic ztONJjQLn=Vc|3e}JLjgxC7wFW9d-Czdvuf&FJ+*D3X)I1NVunVNTWM?yBruxIPx%E5WyEjn(#9+X!zu`bp;UAvVh_VlB z>Ipn|pn&bC(h**F?j4D%oDl_-&Rr&Qs=yeL7oXHb8?^d3Xv;}hRIqAv8SpJt3zJ!_ zu$VsdLQAStx2@`%y0%cLQvT^90;)m5&T4bx@99;2LA_gIhWV+4`aeoiQnLn+j;w*I z1prD0zJfz4R_}%mc&F!h5R?C)>hd@JQ%)}AmJ<58HY|tJ!4-Fi2st?o9*))L;UK7T z{ADYgTb{1VTNJa^S<9Fx2qUA1p5=4rMmcKpwl-x#))uS z18uz?FJ1s8T;#c!rJ*)EcNDhf;DQvopIu6&pQWk1Da${+)enT+eXN^QOxo7$semwf zf%U&Z}e1nJm*O7!K}NthWTDkdgJ|u5$u9;}Nxzujn6ws!beQ%jYeY=&;b(q;c=O%GWz0J{1|!hbZG4 zFdR0$Z#TRzi5@{cJvjx4GjLQfRZVTEm3~D(aANcb{D*@|RD0yIUBjeyc#M}F1KwV= zMFwndio+FnpVzyN7=@gP06-E?wH<(3xxWedLWR{X+Bs8Vw%0STtVcd@w4#vL?so1WYcbxBqZS zd+7Abc&n~ToSj`pH_d=drmP_DUWe{RanJdCM>{5*Idba1YHgOh*py&4j zG0UiYHF^NtTXDX5Ggj-0Y+_W+CGoF~Nm29&{ zxW^-tIuASFFc{X5#t%~=1VO4dNFs;Hb1g5DZ*&^7AIiW{&LdF7!4G=mhg)0Oke8MM z6w;46G#h&_;?0-rH}FITJ~YZnLqjRSW$zyn4Q+j%oe(~h-rlC6QB;dg$6-;v* zmxXu*juWm!zZl<+n?qr@r&&`Y99F|5J_GV6NAy);jS}_Vl41QEq0184qJ-{FVaPk1 zi@wkK#8@6MO|R;Fb^9f<@%6S|!Jfh~O&d@2?5XvZu7re^a#G#HVMtnOu;c^Ma^8tjI%Tg+iFzLvS2uBtSwbz(bGi7%1~L9NY2EX zaHfan1;R{TLnWI2aodQ$?X0##^7U9xHD;HsyTz9DTQkpD@7ynzn2O=YG=R<-je_mK zO$+yzJI`oZgS%_Iv+!tyMdk}3+I1sIPWl$S0aU1hR$lzMSw=kU6dnT$?uSL~fgQ<} z^A<_zPu}c%s8DyWambsQ4yiWcXqT$w6k9Kk*BV+n{%gNVYB*QB0cE)^v(sjWDV>ztpsc6C)Q8^+`&`6iSh5M zux7vo^?^bPl)#GE>+Y7ii&5^hP=D^@j++&_WLCzCx06=HsFoSQ4t#4Zj%Jf&P26bM z(B+loQ|^i7=qRnoqO64t$Y6qu?&wG$I>gC9?@XWwHIf>U*O4~u=2O-b2`+yLC&9@e zxC|**YO1Rf7gC4bDWKY5-KlB!x@QKtE`%CtYU?+-3Pa>+aip%;ZC`kODHFC*uLv?2 z72u3CM-zNsLtI*0%g@VQN( zxuARMk`C*Jxt2dO)Ub}knDBY`E^V8|PCFu4{I^$?3MpsSlP|C)_qi~F#DDwURi2J#SwIN%rS@P^KL}{s5>AZP+ zHm*xm(yMK}#*h2ZTDHJE`4)=Yjac*#e8@5v%6|-z1Mp5l%09YY-W+!7sf9{0ts>^X zF2_}Augtz4U^Z$IoK@;evd8%XJk(hKd1abU+dvTHMCl>kvQcO92BcZ1<1aDr0nA_Mj5q#o))Ch9{T zY1VPZ%MCpNVf#zZmnV&~0>d7pCxC0KM_%4~)2fgvXRda9iWP8)mz!u>rSNaxlamk`e}+fk(UBqfyghM-^|c z9`Q%#l3&)X12(*ca=OrC+uwooFrGNyxw`uHJ~tLw=0iplML`mix5xIGlp9Jx^i&Yu ztJUm%dDw50)1X|o-vR$n-jrgVfX9@Bk5VrBRg^tiEQS~;VCJAThY*FncASuo}A78di=KD*=Mi&ns-e||ETx88m2v5iS%Ty+By`4hYfH-PLZeip^kj) zHRntmCDo^j3d<^v-e(w*E`o=2ml&f_8Jcw}VBRJhKligG+Hl9hH>5;q(Td?8(Vibq zU(AH<=;+8xMUWh|?QTYGY;0uX;gN1Y_7n0F-ytS-dG^?Yg7kQDvS^E<&2n4$ zt+d}LI(al(Ltt4;H64G+$kKU3q33xh3F?kI=3y1e>>C`UDJ*gvGIdn<{)iK^J4~CM zGZ{gaba;0U9Ml55zBrrd1A_OadGkAHwL3P@WKqS zaly^IVHQ~A$)npq26l3yu(GkC$mAa3i%kteLx&BAG09~0t)U&Y5rvS_?9XUfpv_aG z!xAIIlTfHM+A#6qS?D`XNl)uNoGoG#5QtDKQrvDh_aW7PxYaMR{(yH13o`RQM6z%| zzSTmeRm$oI#{DV;jDI+R7TrjUFd~Qo+EL_ou0&0JUDkR-|8(|wz|c3OLh1f|@-_CY zf)1flw*?Xuy$K!f0*Oc9a#u~s^1vFB`0hIHa555v=ye`N%zXk6kN2Ep<-7+V*v0+7 z4tzRY_UjlQC?%jr9iQD!ooE7LA3ZTrdtr6KyTNSB9h)4pvr8*>ij;B%7~{ql6renX z$`tH&%UqfH)XJq&_tkee-}qEXeTS0r?yCu2w}7WK-&fqsD$TJo{L48O zeJTrG$sk5SQL)wiJ2G~T?vthr93QY|9HF7NrZ8W6tKQu=&)f8uo{j%VecG}E&UXL) zlHRd+6?$qEv%E=&_$;Wp=TFSGjyHfFtH4LG0uBA$^RA?b__@_i1dVcH-^`pIY3Jh7 z;ig`TiIFI?sWBm!i~(NVbS>iZm)>92`qK*87v7R0%g?6&cLHILJx9tfICfY7;n*R2 z1+e|@rv!oY$M?-GEkA~zp^*A?bv0Ygt>D=a5~4q0eRO@}qwI)G>SE3O)M7PprS7E zFRp;~n+LmIo5>0{qWBwwdbT3zs&?{inbN+|iE}*RV)WWCavl@TM4Gvz@bL=dr`cE& zIR!k2%C%o`Bf-j8L3)$dLTS`&AYLTh*`HC0sgIg^nrFuiT{nrF_^b30=1>|;Si)EL zQTxJMz+EHxGBiH^NP_(fts#W@l)ZC)6r4N&X8S^*{n!wTC zCB`GSM9)|$WD*q7&Yqk&k_{{RFD8OG0T*Z%h-Yw0P(+3QjuwCn?laVy5$SfNg&TIk zbE3@R%2F+X7Ve#f$L2<}SYGtp#4jW*^k2EIV+NX((jQ6MOYVNL;uAsviUo%wg+&y^aVYh4EI_*5tMyQ3zwe_IWW{?|p&JGyn`HdOG&Ge}C&Tufm3h zMtWq*BGqi-U#6qg@(I8a6{eFpj0k4*;7?papHd)GU%ZWbV6@>ysy2~osvC$0*i?84 zfR5FMwDFM4K5t^be$Amnz%Uiu0;0?1gup)P?Jblas8>G6otJ?4M@L(x13o^D84D50 zE^I=gFw#;e;f^mwFyQuRGa0J>3%AYyk3?w3NS7c630f_{5Zm3YkENFtaeKL>*6DoF z+hnOH^&5H~s8o`vU(gH{{;g3mHNF`p4?Av(85bI7J;fP;R>x%`ql?iv5!%7PL5h@H z-GMBMc2~)jE5v5dzS-pSf?A&!1>SAu37qjN=T`%&#IrMICO1f5guoTd0WO8S^Gpdb zbbB)0MSEk3@XJ~!FXN!iB zHeXgpOwcb~$7V5QvwYspeIoO~F-A5vN#3@$?RPw=J4VFtxm21*`Sk=*S@aTce&4U} zY73NF(Gh~Gbs4txo=HG`@Wa0BrS7p_&L3GZvByS z1f>xc0)*RTgUHh^+m4@(Huu69py$7@Hg(RsIs6#L$PlHlTpJ+jslyrY?yS@vO42mVriHVh%3Q!P4{j@kKbP#BZ&>h zraz9|R1jJT?_BO@NtUQPFoBTp8a|(ZWmn`}+=ICV>76f3vk80js!h%y@;rt8Le($; zxnat=b&n96$zQKJ`lIleC~8m-A|ya*C00;oi1=*SM-quRA7Q<|2LSd4klo$tLj?@5~tD$1aqD0P6z0qoD=&xl1JsH%0)LaHGLTJ`!x`KSi0d zsyv0JrWawe!A`6noS)L)?`>lz_u103``iu3WzKLw_CW zw?Ejh>j!ir1dT)mi<|k+^ZX)2h99PC440JHvESv3=d2}BhD?2n8!B?;kD{!>%X)nP z$H@~CQ_7xe>FPQM=0SAFQA(RbGL6n#E)s!TMeklK^}9p^dOigFq8Vmw4bKaN%Zv9RoA`#50t)Zk%qc@ znuD3nZ2>E~a=ys^rbdKe+i`IdW&K{PcoM95L}4SN0b)|!TpWMwPVcM%&QvA2;iB!^ zMqe~Oxn!q!)_zT^JK4VK{uH8YEprzC+poW_EW35mJHI|s{3-O@B)$;cI)b78 z(lRuD_*H(mj3N1z7d`rfhq6sIF=ur!^lLJi9=9_Bs2N_V%lwNaIUXC5#Si-r$!~oE z(fd`E8!kkn3JRk0T7a0&n5D3w%^x?MW34EQ2xG*QB3nd0Z{gJUtz6wsX%&WcG%MCZMZGURy?5_o5 z?9+Ec_a8&dpp8AO8|l!}8|2%7}%qS(7beTLuC8`7a~m85gd+ z1cOLz6|6N~{ea2_G&y|H{`E)^EEGN>K4Lr%0|_-7iUd(k3{kmRbCqwkg^19Q^%Cuk zZ4t(T(P3d9PEJnnw6R;Gi%aZzd1f~`JyVuUrUQ*J$_iN-JWwXDo3GxUD-~JeV-@^w zAo7sw*WWx5seI5_#U|DYTc}#sI=LBbtjMs4ekT4r<{{i=yyy`*mZ@NpOfCm3bn309V5XGXESeG_WdSSf z0c%#WX*}<8+|O7pP#d>#RLnem(RnhP@&cZ-u5`pQo_C5tQLHgxxOIZq^b&#HM`GK> zs6TEbYR`DDK;b>PqjnG}`lB?u!~~Fo^S(NcxhH)~Qn3|;j$?HJq+O}tY90>!+mj$e z(z#$-LS!HrUzT-Ul`XBURJZGu*GI{39Q|pze1oezWG(ynv3M>c6I8|p?tR-F$2TD* zK8;Q;%mww3_5Pj`ufy(%m!~o(gRNdYCb4txLP^nR;}~shZD+8NSQQM6Q!zMgowW)#5>-4 ziaG|}Vfho*M(zy4D!`krHZH5jhR5i|+b%Mv-t6h=O^qgR0!W^;PNGB_2I9OW)D0;O z%|c%%;eB~GA7m|_bk7~*mKEL?YY8C*-dcd#&^oL78b;VJp2i8)Ihq@)yEiCbq^fL; z@2nY&M$6uoo086P)Khz}#bZxEfkG*V@oIQW|Ljc};%fpOIq==}lB3IjZ+k{*BUgxw zus{(nX(>!Rl`A!{@CRW1k)ciCB*+qaC*6EQjXstiA9Fn#UyiBq8I1}y$s86=%1FuS zGT~5&fG&%17h_f7r`goaTdwbiOfc4sAd5KlxGp81WjV1r+&?EwoF=SwhW$$&hkP=e z2l3Twq_s3!$1cza;j=sYogB5AGCLQQ(Ddywk9jPkWvHIMeAf+!F14isw`9;1ci#51 zpL_{uU#nJof2QgwT4>2*uI8HrJTxR77Q&E{0=ap#6&3MNS>{Jom}_u!tMCHqU24Q? z<5yO^emGK{f~Q~5Ez_Uu9TQ-q>sEpQGHpg4kr0~^wpopNSHxrCsH64I6_tZgY3Y%B z!bX*Zg9!3gBkYG3D|Dn@vb@LQUu(Q{p%N}~Uw-6391{VCIkbUrN;!z;ca#(-$PU$5 zhZdbh6Dc*7i-+A9N-Nm7AqS|DP-+QnBxP$(#t-GtRcWj?uK0S}px)|s0)8~!9gEPA z?cheGX;N;jG}GnxjFc4CJ39el=`511jUP3{d)|RuZGST{00qHjvooNjMN^K*V)Sd@ z4XNJy`_v3){i?N+*NEW`!^C&9(+OfgMn>Fn%i0U05BKlQ6xbT?U}{TKD(l4crjf(g z0i-_Jr`WY$H60BK$Wc)cPRAgbN1>KwC-Wsq7>Kk8vGc#9=Peea%TaC0bUS~*iZd2d z*Vor{r&emxpcdF?9Gd*}U&K^nFy~|a>+U)=UO5@44SzY&DxH}BrC$eAn67RJ<0yP!-&}WSC_}_L zpEUP`+5VngkrH;U&#TYNd1gKSk@S@)V$k=^#~e_Oj&}?Sl0-H}e4xn7N*6(kI_sj> zr!Qa7m9e0E4ab<6a!Zbf_OEZG$`>tMPiiR9p#8#)G3eQHQDv@f0EPs4V`DNa{Tp5U z;$`aLkM_12!$bQv`os3yr_I|}J-}Wh&{QtO{J4qXI)>NP!8~4!n=$XazS9hfeBw#d z#05v_Ihekh_5l-JtYX5BZ_JKA`r3a5sP;rpzy3Ukye4diQa=4UT{wXG4yDVz zsR;#KjO7*OTbTw^?2CJSM3oT~Gl%t*U;Y9{4+E6@B%L-dUYYh^2I-$?e`}&)BT+9Y z+pQTo>GdoHlHfYtHmC06a$9G>Eq!jSZ+iZ@U=9A5|lbt-X1LJ9;=AJzVfl*yvx!Y zd3>~(fo-Sjm^R%kVwKY``nrD5@lCZ7$FknHQ25H8C7UP5j)VDhkAcK1L6Iv;vUF4g zC)8Y8KRlQ1iGLcFe^1VUT6=b>s~Bf^W+w5sef$z9VGbdME`5w-k4YS1dWpPlc$!^E z64;c_ROn_1M}T9q4QJEPFzWhjCG9UFt&~O?Y8Hx^)Ti1risSJ4L~JYs$ut~E3|>S+ z$^P$<*!IQ)MC$Vc^jOoJ=L z)MCo$-Kp|9v1%+b3Ge{;GX~Xx+Vi<_GytqA zNb}2t^5Em0y5~IqUbfQ6Fffi;7`=d%9Wpgz(3^A=IO86{C{hJ-LS#hJcC|g8K>-8A ze1~BNK-*KpFRjpNb=q0~#0_VhT}dxp$c?E=!!DNXB*8^E*Cw4}`oox>(bUeXj(b*_S%KA~l7^a-YC`K)M2~JACci zmOsxS$8OX(%}DiHD*F0=qaN(;I=bGBh2CFY^6`2?-CW{Rpz0@*Wrx7J2>6-I-HV}O zZi2i}BDFdc??peL9=nW*ZWHEA`q^8Z(S2@UAko*zzwGskD;;9@guJ8RN>yL^-Lw@f z1R^|OSqoiPQrBYmN36X$8Heh)KU=T7xd_35qCuWG+!}qzEezX^?>pa^*jjD7-kzY` z_eqfb9k=Zyut<{6GGqZIBx=8`n4AC>&~io6`D_11pxo)@Vo05 zdi>)jvJUFuB)Hc7t(tb%ryG#%;=ZWSqVkEPh?|$y>Y~KH{IFRJh)wuv;EtV$U7{H{4CVB2NlK;Fd9;WD<*G$= z0#tGyuAuQ#>!-GjSiq;VRjIj3g%e>|q%)Y2T2r2u3KsZVE2)iYhYE8o-MxJ^F@P9t zl%FX>xN;8DO>jO+LV1s1V9vr&i=>Cfx&ZMtDcOE6M`TJ|3Ixgm49TD6B<+o$G}Dt@ z%*Ljsjw2xu5y6oq7Lrc=cX2bee}4W)3Yr=F0~n}KzkOrnur9~6Y|z?lMG%IAo91vb zGIb|N{0yVR8~H1Bz6MaOE!0H$Go=jkLIGQ|dIbzKRxsetmFb_x=kPBO-$tYOZBLo- z8*1#9-0Q6v{UGwR;!q>c;W?e@UNN31$b2q7ajaJ_0FTMi^*OQo&dm;uSeyq-@;5Pq z_H}RsUT$%TI>O)ZP&;0ENreyjrvb!Me{8;9PlL%mb*g*lA`6rgw(6iW4bFaCcKF2+ zPwMRM?B8(~@y~%ZZ+Bxa#Ut=jnx4eQ^IkXKEi(1vK_%fpM2YJOU97x6Xo(54#}W`PHU4ZrqbUkN-8X1E!}n%ch#L_FX|)^dfnFWLd1~wRK4<4AF!*lzTRKd1H2`9(lp! zVF^g!;a11#%8BtiI>Dt)VbV;xMx7xDC=wKjL{LD*U(0yyUw8o#7_uj)uS8rAY}PDT zQ!laFTcIFx2zSLBw@gC~CF=nfAbO-=QU!J->WJ~ofX?M^p=pHpBV{z1{T^9WD~=A?l&$@a`WHNQ2PU`qiwfg&2Maz>q+R{kY@b zW3c!u?^HF6fZ$OfGPVMIAlX8JLZ$iFk+u4VnV#s~iO_cNq7XFRM22q*(WN~G8Q0wh zt9BZ`i9y)-_n#vsSS!{Mi&#t7o`q`k0H=x$4=c1QX3GTdA7;WotFxzyJ_7E%Ppf-? zZL_`}2k@r?^FL?b8`M|))l!CPXNlVx-^esm?ODk1UE7+oyM%#;9pB20PD^M6Iux8h z2NZsW3gzn3(Njul?KRy7$d<+Yodvq?{ zjv9+sufxRN9S#mo^zY^xKMh8yb0~^_qpoinAw={0P@c4rh!EW=t98fRZQmmMA!^gl zw(XYK=c`$Ub}yURkh!Yl{Huo@Jy{M83^Xkw`P3#tS-MGNl=yYInU_ZOFIefskpn>0 zJQmA_U1NIDc(r8ry{MAz4|a$m#!*pG7T*nbgJoG8&+Qy($>e4}31NcmqgA&81aZ4N zv(K-$md}f+PEYWEERrNGvP0p*SeH&coia;XOC7c>jhvajQHbnY4+y9dX@US?R5;Gy+UL+aHjl zVvH;tFuL{#_mv<2Iro~C@c`F?`3vpphF-Ypouka^W~)8ad*Ay@*A(~bza*KOo()!P zK@!FzlA*j(ApY8& z(+X3;NvmgC99*U%CL*EFldr|H^+NET&Qzq?_2-6jL;}FA$rPXd#NA(qZcVLE*=7U>eadEuizy_^)>oKvU_om7N(2`HwcKzC`I*ku%6r>S z*F)rC6-}5DYB&ujM1~Y6eE}?{B$C2KR;DczKPnC30}Z^Ru#PT_KESOyU^IREW^ZNK zqZ(4ESLN~bG+GiSBAC3V(o%*KN!rl1kLOc%97Xo%Gy(gg4$9*peqf(rDT|wv4GM@c zv5~1E+?5329SvIw>B9e!nW4F%^?bR7q*FugUH`7J){aQ}cy>wBu)_kOC2@Kve^v@# zUTrbT{Y^}Q2qN| zQI0UBWQ88MknTIszOb_1wk2Wo#2Ys}MIe5tJ~BmU#r?aO;p>;gBL$2OIS(enDt?}9 zkvXOPex6UkB9C`X{4Gud`A4|hc|=hn7WJ$5=G3<>srbOaF`NXKm2+BL}oD8#&%4MF4SkZLr5kL^eaqyU17qreHKpkzFI)roX(TxH%Y2N zg`TqFj^m698?8u_9wqa0Y`15t?g{wh+a$j zYx?W@g0q#Cwcjr9*x&0V=0tP0gU4mQHJ#0-hTTR^)LPl5nhj(*(gBVyKT_T79w`sPJ>(yRZnob7TY z9Rs;Ei(`DtAd6JI4ng^)3~&txuc&uRW~hzs4C;}PJ?n^&|Aba)!1-sp2BoIr8=IQ4 ziip_qnR$5;tEs7_WM@k&R6>B_fAj_1pB4OM3Qip*Xm$Tqr7Q63{++cisfv~Z{X6UW zg4M7@Z;GmW%Iw$;=R6k?wI`HQPl`JvF#Hycet^(dX4b-#lvL^8yW>tPq~iI<@$1CM zm7^6ZDV@~h0LPavcrD)v)mir15G3_=^K>&8Yaw1qp@;Qn`O=q&*Je8yx#ydAA@fhZ4ekTW1gc|GNqTi%gbrOMHdCzI9@^zwl7Yf z{5GGo8&RQN)(KUJ3JjBR^JQ>R;6oi{Ms{W&a3%G+Mhend*Z)5Gc>sy?cQW4n%b5RISUHTS6Dg8f<>i^6*C(Mb7k!6UJXsL$a_EnhIa(%lgBHEAFQ38b`1lQCO1OZiw zyMC?3zxQNaDhkt~la~}PX;0TNf`e zer&GUnG=j9DqMRD(|_%>V4UOwuAy7a7xXD`$TrZXf&&4N0pf8c7tC4#tfaJoy&iu* zs$D{R$;X+vSA}ctPRtHA3oM+eI3Mo2oEq&>$I55WPF}y0E&=2?|pF; zZHIVjJS3F0o`1N+mscqiY6oD!vNW_DcW6&0Y#$wo&>>wilbmz zcVL?V6aU`eV0Td8l*`YWmiDzTF|k2J9uD>&c)leu>8Z~fF#s4Q21XS>W8ZCz2|qU< zAWwk~b4$LeW%Y~U6kk9B0r?jG0DQfFYvH{dF?Glcc&X);av5bGR!CnjhulRzkovsA z)I->zleCuZ03Un+w2bb_A6}_5nAmrNaa1>kB7o6SoOH_Ew2REA@6Fj-4+jQsueoS z;V)axoL$op_*&&{QSwgDlI52(*BIPnS=@EKq{#m1%lhx%|1Wr63{vYlP36R-mZ-gb zAv%0}S1!1A=ktruRiPfDg4@Ht^>j_S9~~K2Huc4|FBfOG@i|YQQ0p5nf8qJWXHTcc z%e{L2`=$V>@Hck=HqajdQzo!d0EQR5K@J{z0u`-1f}mm!R#y4gCt zvz9r&UX=ZBgHhM7mVX>CoCBkEpZE$29a^fmcRKsQ!wdXhNH5@D8oBym@BY6RH{3tQ z{5bJ~IWKDQbj+4H>a#@W-+k4zulOU=RbG|rEWgU1*?9eGsf%=y zXV0HweVx;-0=TjNV~G^7yZJQ!U+VG7=jZ1iUm3jo#s8vUx5arDmWwuONNwU_2+4i< zsFnN2-t~1`R}1cXDIB-&kDTAUKQo^00ZvkGzFBZp{mz#Q>cHugw%BPG7nLlxfAlm5+v|rma7JR$fVQmN%Gb=C-C>e{Z(o8Q>HKL&4WuTj#C+ zyW*uVsDwW-qe`kt`)QIOuo33GGh)je^f}c4v03`&%{fyzh6L=Yf~izkFw`wK?f`-hrbPvv-?*;syRc{xhe4(M;djD0v2W77v4`tDnm{r-UW|SH72! literal 19787 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

      !_kdkS|*~-z5|ouR!#B#K@WG+5P}00Bp$n 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 diff --git a/icons/obj/structures/furniture/sofa_middle.dmi b/icons/obj/structures/furniture/sofa_middle.dmi index 92a69a0146e3381d25119608a7721fd54a09fdf6..67794edf8386cfba104c68395594205a616fc5e9 100644 GIT binary patch delta 2629 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 delta 2747 zcma)4X*84#0~LdrjK-Fo8B1eQ)~wNtv6S5?o#VAD6+*UT$vixhRFjN&ZP{L1Ude08 z9z#mDq=?Bny~x9n$Oq^av(Tk$uB)YYLXg~u z`ceGJAj(Diq6|m}B#6?WJ;%HaL`jVzti?re{vBWfJ^d8a9XaMSA$6~|jJp_7B_MKt zi-y;(^ieV@RDw;|y`V`rQR<$w&Wz&#{$|E?p-0<>L%8dV?ws!>RzK9+7hgnZWUjr@?oU)gV2+PRk*beP072 z*FTho`rIj_0yn=U8WhH%VSmL=5N^-rwOSJk?>YOxp}M_j^zji^+WuYR!u8@cKf!cC zLpVG+){gUDrx9_Pb;?7#kp5tB-$&x-;sK1k1ueD|If=8?e#a*_pLR)P*Vbc58LIhS z0P2F?8W6E zo5J1UA-ObTr0RCjfWau7WFsEQSF|`n4_8ka)~Hc)AaIn(sA--)$*CUOjm-q^+Z={F z8v4Pafo8=CqU36Zv6T9qDK@o%f506>~5&i(nc_(wyjb zr~F|k9D-GQCyLLXVwRH2p%A#vToIyis}>VX*Cj}s;NC2LsR|zOaME0|7K<-_D;vG+ z6Zh*dB7U2x5qDu+@Jc@X#|LIt`b#_KMW?fFVWwWdKlI!APx_C!xJZiIS64a%0XJ*a zY&KGLKP%!PYf^+3q4lpn1kJ>!>VvF>$E4zN|2*jF-?O{B$HQ#cIK$vQi_g^^qgrFxx4m-IY_=c3*$C%UM{=@O?ve*_$nxf~!dj zJKYla(Usyd&ROsY*y+|Zq_S34H5JydjaJ@}Nma`M2p-|EW2Um5fp2TB(t+&wKZ3ARwL z5@h*-k8-V-K0wP{NsdGYJzre9?^!?(zrfq(pTyr_b6tEw?`_VY*-tk zX%4-&)(j(>I8HpgtVP5*GuXUs0W?f+xwngOxf^j|&pTYTHsIXF z1A2;FVWOYOe8lxLW4`_)FNPS7cirw*rc#F|yIWVz2UEx7^5c$5*cT8bWj2 zm~2VxIe5FQ3ywxj?)pt4fuu!-<;sI5Bsh~hG`fyZkXf+FO)IMVCU%h<^re22Qu63oelF-=$t@6AOhGIBcpS4{BU+w+#8ed!5BdcD~k{7rAg zRey}W$t!`FaE~y_pm8E3e1H}mWM$|${Ny$WN?8>e-Tr+oqdKShFKN)nA+%c^W5R(NU6mbRBo zk)%gUP;<~+4e+NK#dl^l=YaG3_))%nV(63XDZW<{Wj4sx_SOz!rECoY4sa-L{-_nP2zfDAp7OH zEIxrmy)IsSkzO9T($RK9&NC8C`(~@HuU?*YMhXgz$`V`U)a)i<72@BJ*%Tq&a8L>& zGs!!0xfBo>c)o&*57i4fCgnE|(SqteqXTBx&|-?XZH6~0Cv}h6euM#st@dmGP10-? zk-z#n?U{SW_SxjHyHR^L!JDc^@K-$ZQ1wfL(O{t4=X%H-7BXUab-SUAIN3Hj6)UdL zB>n0TEX(D`QhTZ&W9MPAO}YPKRq^!&9X(dTz+sJNNVt>JuzkG9@%#Nma=7ydDGB-n zWg71w!BHt~TR`sZmlXS7ULr*gH0)!0hmAZJMW2ppY<03zRcQ3GW_vGAGWI}uunW<(Aq8Urc0bod9*C}?w7v-Em zd(Y=;xA+__z62^8Eq z&w%C}f^AI%pAKffYUX;oX5~{h3qv*gJXohxnGs&!;grnkxtZE<A#iwm4#+e=Ret6HBhb$Hj z85uUDo|fFon+y4GW>2U@qIKFTZq=?{jlfvFrvq!!1 Date: Wed, 26 Mar 2025 20:46:53 -0400 Subject: [PATCH 0620/1331] Add mapping preview colours to various types --- code/game/objects/items/artifice/chain.dm | 1 + code/game/objects/items/artifice/hook.dm | 1 + code/game/objects/items/horseshoe.dm | 1 + code/game/objects/items/tools/shears.dm | 1 + .../objects/items/weapons/material/kitchen.dm | 5 ++ .../objects/items/weapons/material/stick.dm | 5 ++ .../items/weapons/shields/shield_crafting.dm | 2 + .../items/weapons/surgery_tools_ancient.dm | 3 +- code/game/objects/structures/drying_rack.dm | 2 + .../objects/structures/grandfather_clock.dm | 1 + code/game/objects/structures/hay.dm | 1 + code/game/objects/structures/pit.dm | 4 ++ code/game/objects/structures/travois.dm | 1 + code/modules/butchery/butchery_hook.dm | 1 + code/modules/butchery/butchery_products.dm | 1 + .../butchery/butchery_products_meat.dm | 4 +- .../butchery/butchery_products_meat_fish.dm | 1 + .../clothing/suits/armor/forged/_forged.dm | 1 + .../clothing/suits/armor/forged/plate.dm | 3 + code/modules/crafting/handmade_items.dm | 1 + code/modules/fishing/fishing_rod.dm | 2 + code/modules/gemstones/_gemstone.dm | 57 +++++++------------ .../mob/living/simple_animal/passive/horse.dm | 2 + .../reagents/reagent_containers/mortar.dm | 1 + code/modules/tools/subtypes/hammers.dm | 1 + mods/content/blacksmithy/anvil.dm | 1 + mods/content/blacksmithy/billet.dm | 1 + mods/content/blacksmithy/tongs.dm | 1 + mods/content/item_sharpening/whetstone.dm | 1 + 29 files changed, 70 insertions(+), 37 deletions(-) diff --git a/code/game/objects/items/artifice/chain.dm b/code/game/objects/items/artifice/chain.dm index 3a48abad4759..2e44bf586f62 100644 --- a/code/game/objects/items/artifice/chain.dm +++ b/code/game/objects/items/artifice/chain.dm @@ -6,4 +6,5 @@ icon_state = ICON_STATE_WORLD icon = 'icons/obj/items/chain.dmi' material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color material_alteration = MAT_FLAG_ALTERATION_ALL diff --git a/code/game/objects/items/artifice/hook.dm b/code/game/objects/items/artifice/hook.dm index c20c77b14801..c04401d873fb 100644 --- a/code/game/objects/items/artifice/hook.dm +++ b/code/game/objects/items/artifice/hook.dm @@ -5,4 +5,5 @@ icon_state = ICON_STATE_WORLD icon = 'icons/obj/items/hook.dmi' material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color material_alteration = MAT_FLAG_ALTERATION_ALL diff --git a/code/game/objects/items/horseshoe.dm b/code/game/objects/items/horseshoe.dm index 51746bbc206c..622dfaf99721 100644 --- a/code/game/objects/items/horseshoe.dm +++ b/code/game/objects/items/horseshoe.dm @@ -5,6 +5,7 @@ icon_state = ICON_STATE_WORLD icon = 'icons/obj/items/horseshoe.dmi' material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color material_alteration = MAT_FLAG_ALTERATION_ALL /// A horseshoe hung above a door. diff --git a/code/game/objects/items/tools/shears.dm b/code/game/objects/items/tools/shears.dm index b5958ab00254..47ff4cf48b7a 100644 --- a/code/game/objects/items/tools/shears.dm +++ b/code/game/objects/items/tools/shears.dm @@ -6,6 +6,7 @@ w_class = ITEM_SIZE_SMALL origin_tech = @'{"materials":1,"engineering":1}' material = /decl/material/solid/metal/steel + color = /decl/material/solid/metal/steel::color center_of_mass = @'{"x":18,"y":10}' attack_verb = list("sheared", "cut") sharp = TRUE diff --git a/code/game/objects/items/weapons/material/kitchen.dm b/code/game/objects/items/weapons/material/kitchen.dm index 750d92c2401f..344eb317abbf 100644 --- a/code/game/objects/items/weapons/material/kitchen.dm +++ b/code/game/objects/items/weapons/material/kitchen.dm @@ -10,6 +10,7 @@ attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") material = /decl/material/solid/organic/wood/oak material_alteration = MAT_FLAG_ALTERATION_ALL + color = /decl/material/solid/organic/wood/oak::color /obj/item/rollingpin/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) if (user.has_genetic_condition(GENE_COND_CLUMSY) && prob(50) && user.try_unequip(src)) @@ -18,3 +19,7 @@ SET_STATUS_MAX(user, STAT_PARA, 2) return TRUE return ..() + +/obj/item/kitchen/rollingpin/walnut + material = /decl/material/solid/organic/wood/walnut + color = /decl/material/solid/organic/wood/walnut::color \ No newline at end of file diff --git a/code/game/objects/items/weapons/material/stick.dm b/code/game/objects/items/weapons/material/stick.dm index 8473904228e8..92faaca3265f 100644 --- a/code/game/objects/items/weapons/material/stick.dm +++ b/code/game/objects/items/weapons/material/stick.dm @@ -5,6 +5,7 @@ icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_NORMAL material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color attack_verb = list("poked", "jabbed") material_alteration = MAT_FLAG_ALTERATION_ALL lock_picking_level = 3 @@ -74,3 +75,7 @@ user.do_attack_animation(target) return TRUE return ..() + +/obj/item/stick/walnut + material = /decl/material/solid/organic/wood/walnut + color = /decl/material/solid/organic/wood/walnut::color \ No newline at end of file diff --git a/code/game/objects/items/weapons/shields/shield_crafting.dm b/code/game/objects/items/weapons/shields/shield_crafting.dm index 307db9bce88b..783be5a4e6e2 100644 --- a/code/game/objects/items/weapons/shields/shield_crafting.dm +++ b/code/game/objects/items/weapons/shields/shield_crafting.dm @@ -5,6 +5,7 @@ icon_state = ICON_STATE_WORLD icon = 'icons/obj/items/shield_fasteners.dmi' material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color material_alteration = MAT_FLAG_ALTERATION_ALL // TODO: single-step slapcrafting @@ -14,6 +15,7 @@ icon_state = ICON_STATE_WORLD abstract_type = /obj/item/shield_base material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color material_alteration = MAT_FLAG_ALTERATION_ALL var/wooden_icon var/fittings_type = /obj/item/shield_fasteners diff --git a/code/game/objects/items/weapons/surgery_tools_ancient.dm b/code/game/objects/items/weapons/surgery_tools_ancient.dm index f6f31166bfac..d9c4f6d27433 100644 --- a/code/game/objects/items/weapons/surgery_tools_ancient.dm +++ b/code/game/objects/items/weapons/surgery_tools_ancient.dm @@ -2,12 +2,13 @@ abstract_type = /obj/item/ancient_surgery icon_state = ICON_STATE_WORLD material = /decl/material/solid/metal/bronze + color = /decl/material/solid/metal/bronze::color + material_alteration = MAT_FLAG_ALTERATION_ALL matter = null obj_flags = OBJ_FLAG_CONDUCTIBLE w_class = ITEM_SIZE_SMALL origin_tech = @'{"materials":1,"biotech":1}' drop_sound = 'sound/foley/knifedrop3.ogg' - material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC /obj/item/ancient_surgery/proc/get_tool_properties() return diff --git a/code/game/objects/structures/drying_rack.dm b/code/game/objects/structures/drying_rack.dm index 40f44e2c3fc6..3feffa51de95 100644 --- a/code/game/objects/structures/drying_rack.dm +++ b/code/game/objects/structures/drying_rack.dm @@ -4,11 +4,13 @@ icon = 'icons/obj/drying_rack.dmi' icon_state = ICON_STATE_WORLD material = /decl/material/solid/metal/steel + color = /decl/material/solid/metal/steel::color material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC var/obj/item/drying /obj/structure/drying_rack/ebony material = /decl/material/solid/organic/wood/ebony + color = /decl/material/solid/organic/wood/ebony::color /obj/structure/drying_rack/Destroy() QDEL_NULL(drying) diff --git a/code/game/objects/structures/grandfather_clock.dm b/code/game/objects/structures/grandfather_clock.dm index 24882cb59e6c..49f4b63c22b7 100644 --- a/code/game/objects/structures/grandfather_clock.dm +++ b/code/game/objects/structures/grandfather_clock.dm @@ -8,6 +8,7 @@ density = TRUE material = /decl/material/solid/organic/wood/mahogany material_alteration = MAT_FLAG_ALTERATION_ALL + color = /decl/material/solid/organic/wood/mahogany::color var/face_color = "#f0edc7" var/last_time var/decl/material/clockwork_mat = /decl/material/solid/metal/brass diff --git a/code/game/objects/structures/hay.dm b/code/game/objects/structures/hay.dm index 47fa6ac32056..71f6c73a0647 100644 --- a/code/game/objects/structures/hay.dm +++ b/code/game/objects/structures/hay.dm @@ -44,6 +44,7 @@ icon = 'icons/obj/structures/haystack.dmi' icon_state = ICON_STATE_WORLD material = /decl/material/solid/organic/plantmatter/grass/dry + color = /decl/material/solid/organic/plantmatter/grass/dry::color storage = /datum/storage/haystack material_alteration = MAT_FLAG_ALTERATION_COLOR atom_flags = ATOM_FLAG_CLIMBABLE diff --git a/code/game/objects/structures/pit.dm b/code/game/objects/structures/pit.dm index 3e906526c8db..245e55bce956 100644 --- a/code/game/objects/structures/pit.dm +++ b/code/game/objects/structures/pit.dm @@ -148,6 +148,7 @@ pixel_y = 8 anchored = TRUE material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color w_class = ITEM_SIZE_NORMAL material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC | MAT_FLAG_ALTERATION_COLOR var/message = "Unknown." @@ -214,6 +215,7 @@ destruction_start_message = "smashing" destruction_finish_message = "smashing" material = /decl/material/solid/stone/granite + color = /decl/material/solid/stone/granite::color // Gravemarker items. // TODO: unify with signs somehow? some of this behaviour is similar... @@ -223,6 +225,7 @@ icon = 'icons/obj/structures/gravestone.dmi' icon_state = "wood" material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color w_class = ITEM_SIZE_NORMAL material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC | MAT_FLAG_ALTERATION_COLOR var/gravemarker_type = /obj/structure/gravemarker @@ -232,6 +235,7 @@ name = "gravestone" icon_state = "stone" material = /decl/material/solid/stone/granite + color = /decl/material/solid/stone/granite::color gravemarker_type = /obj/structure/gravemarker/gravestone /obj/item/gravemarker/get_examine_strings(mob/user, distance, infix, suffix) diff --git a/code/game/objects/structures/travois.dm b/code/game/objects/structures/travois.dm index 781dc5b6487a..324972fbba5f 100644 --- a/code/game/objects/structures/travois.dm +++ b/code/game/objects/structures/travois.dm @@ -20,6 +20,7 @@ parts_type = /obj/item/stack/material/log material_alteration = MAT_FLAG_ALTERATION_ALL material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color /obj/structure/travois/walnut material = /decl/material/solid/organic/wood/walnut diff --git a/code/modules/butchery/butchery_hook.dm b/code/modules/butchery/butchery_hook.dm index c69c4f8ce2cd..1453bc7f051f 100644 --- a/code/modules/butchery/butchery_hook.dm +++ b/code/modules/butchery/butchery_hook.dm @@ -13,6 +13,7 @@ icon = 'icons/obj/structures/butchery.dmi' icon_state = "spike" material = /decl/material/solid/metal/steel + color = /decl/material/solid/metal/steel::color material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME matter = list( DEFAULT_FURNITURE_MATERIAL = MATTER_AMOUNT_PRIMARY diff --git a/code/modules/butchery/butchery_products.dm b/code/modules/butchery/butchery_products.dm index edfdfa65796c..659416ea06c3 100644 --- a/code/modules/butchery/butchery_products.dm +++ b/code/modules/butchery/butchery_products.dm @@ -3,6 +3,7 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR icon_state = ICON_STATE_WORLD material = /decl/material/solid/organic/meat + color = /decl/material/solid/organic/meat::color w_class = ITEM_SIZE_NORMAL volume = 20 nutriment_type = /decl/material/solid/organic/meat diff --git a/code/modules/butchery/butchery_products_meat.dm b/code/modules/butchery/butchery_products_meat.dm index 9907f21994ce..c9d2ca23778b 100644 --- a/code/modules/butchery/butchery_products_meat.dm +++ b/code/modules/butchery/butchery_products_meat.dm @@ -53,6 +53,7 @@ name = "piece" desc = "It tastes like you'd expect." material = /decl/material/solid/organic/meat/chicken + color = /decl/material/solid/organic/meat/chicken::color butchery_data = /decl/butchery_data/animal/small/fowl/chicken /obj/item/food/butchery/meat/chicken/game @@ -66,7 +67,8 @@ /obj/item/food/butchery/meat/xeno desc = "A slab of green meat. Smells like acid." icon_state = "xenomeat" - color = "#43de18" + color = "#43de18" // todo: add xenomeat material and use material alteration + material_alteration = MAT_FLAG_ALTERATION_NONE center_of_mass = @'{"x":16,"y":10}' bitesize = 6 butchery_data = /decl/butchery_data/xeno diff --git a/code/modules/butchery/butchery_products_meat_fish.dm b/code/modules/butchery/butchery_products_meat_fish.dm index 90040989b1ff..f0171f3ef6a3 100644 --- a/code/modules/butchery/butchery_products_meat_fish.dm +++ b/code/modules/butchery/butchery_products_meat_fish.dm @@ -8,6 +8,7 @@ nutriment_amt = 6 nutriment_type = /decl/material/solid/organic/meat/fish material = /decl/material/solid/organic/meat/fish + color = /decl/material/solid/organic/meat/fish::color drying_wetness = 60 dried_type = /obj/item/food/jerky/fish backyard_grilling_product = /obj/item/food/butchery/meat/fish/grilled diff --git a/code/modules/clothing/suits/armor/forged/_forged.dm b/code/modules/clothing/suits/armor/forged/_forged.dm index 8a9b2aa3db4d..d04b1a7c84e6 100644 --- a/code/modules/clothing/suits/armor/forged/_forged.dm +++ b/code/modules/clothing/suits/armor/forged/_forged.dm @@ -2,6 +2,7 @@ abstract_type = /obj/item/clothing/suit/armor/forged icon_state = ICON_STATE_WORLD material = /decl/material/solid/metal/steel + color = /decl/material/solid/metal/steel::color material_alteration = MAT_FLAG_ALTERATION_ALL armor_degradation_speed = 1 armor_type = /datum/extension/armor/ablative diff --git a/code/modules/clothing/suits/armor/forged/plate.dm b/code/modules/clothing/suits/armor/forged/plate.dm index dbd83bd806db..362908c31c1a 100644 --- a/code/modules/clothing/suits/armor/forged/plate.dm +++ b/code/modules/clothing/suits/armor/forged/plate.dm @@ -11,6 +11,7 @@ desc = "A form of segmented armor that covers the hands and arms, typically worn as part of plate mail." icon = 'icons/clothing/plate_armour/vambrace.dmi' material = /decl/material/solid/metal/steel + color = /decl/material/solid/metal/steel::color material_alteration = MAT_FLAG_ALTERATION_ALL body_parts_covered = SLOT_HANDS|SLOT_ARMS accessory_slot = ACCESSORY_SLOT_GREAVES @@ -20,6 +21,7 @@ desc = "Segmented armour that protects the feet and legs, typically worn as part of plate mail." icon = 'icons/clothing/plate_armour/sabatons.dmi' material = /decl/material/solid/metal/steel + color = /decl/material/solid/metal/steel::color material_alteration = MAT_FLAG_ALTERATION_ALL body_parts_covered = SLOT_FEET|SLOT_LOWER_BODY|SLOT_LEGS accessory_slot = ACCESSORY_SLOT_GAUNTLETS @@ -29,6 +31,7 @@ desc = "A visored helmet that covers the entire face and skull." icon = 'icons/clothing/plate_armour/helm.dmi' material = /decl/material/solid/metal/steel + color = /decl/material/solid/metal/steel::color material_alteration = MAT_FLAG_ALTERATION_ALL markings_color = COLOR_PURPLE markings_state_modifier = "-plume" diff --git a/code/modules/crafting/handmade_items.dm b/code/modules/crafting/handmade_items.dm index 93ae530b1134..75a8ac9650f2 100644 --- a/code/modules/crafting/handmade_items.dm +++ b/code/modules/crafting/handmade_items.dm @@ -2,6 +2,7 @@ abstract_type = /obj/item/chems/glass/handmade icon_state = ICON_STATE_WORLD material = /decl/material/solid/stone/pottery + color = /decl/material/solid/stone/pottery::color material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME presentation_flags = PRESENTATION_FLAG_NAME diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index a2935a323cad..ae8342c0f677 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -6,6 +6,7 @@ name = "fishing rod" desc = "A simple fishing rod with eyelets for stringing a line." material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color matter = null material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC icon = 'icons/obj/fishing_rod.dmi' @@ -341,6 +342,7 @@ name = "advanced fishing rod" desc = "It's a fishing rod, an enhanced fiberglass Telescope Ultralight 47; the latest model." material = /decl/material/solid/fiberglass + color = /decl/material/solid/fiberglass::color matter = list( /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT ) diff --git a/code/modules/gemstones/_gemstone.dm b/code/modules/gemstones/_gemstone.dm index 54e970892efa..b95cb57181d3 100644 --- a/code/modules/gemstones/_gemstone.dm +++ b/code/modules/gemstones/_gemstone.dm @@ -16,6 +16,7 @@ var/global/list/_available_gemstone_cuts w_class = ITEM_SIZE_TINY material = /decl/material/solid/gemstone/diamond material_alteration = MAT_FLAG_ALTERATION_COLOR // Name and desc are handled manually. + color = /decl/material/solid/gemstone/diamond::color var/decl/gemstone_cut/cut = /decl/gemstone_cut/uncut var/work_skill = SKILL_CONSTRUCTION @@ -91,38 +92,24 @@ var/global/list/_available_gemstone_cuts // Material subtypes. -/obj/item/gemstone/baguette/topaz - material = /decl/material/solid/gemstone/topaz - -/obj/item/gemstone/baguette/sapphire - material = /decl/material/solid/gemstone/sapphire - -/obj/item/gemstone/baguette/ruby - material = /decl/material/solid/gemstone/ruby - -/obj/item/gemstone/hexagon/topaz - material = /decl/material/solid/gemstone/topaz - -/obj/item/gemstone/hexagon/sapphire - material = /decl/material/solid/gemstone/sapphire - -/obj/item/gemstone/hexagon/ruby - material = /decl/material/solid/gemstone/ruby - -/obj/item/gemstone/octagon/topaz - material = /decl/material/solid/gemstone/topaz - -/obj/item/gemstone/octagon/sapphire - material = /decl/material/solid/gemstone/sapphire - -/obj/item/gemstone/octagon/ruby - material = /decl/material/solid/gemstone/ruby - -/obj/item/gemstone/round/topaz - material = /decl/material/solid/gemstone/topaz - -/obj/item/gemstone/round/sapphire - material = /decl/material/solid/gemstone/sapphire - -/obj/item/gemstone/round/ruby - material = /decl/material/solid/gemstone/ruby +#define MATERIAL_CUT_GEMSTONES(MAT)\ +/obj/item/gemstone/baguette/##MAT{\ + material = /decl/material/solid/gemstone/##MAT;\ + color = /decl/material/solid/gemstone/##MAT::color;\ +}\ +/obj/item/gemstone/hexagon/##MAT{\ + material = /decl/material/solid/gemstone/##MAT;\ + color = /decl/material/solid/gemstone/##MAT::color;\ +}\ +/obj/item/gemstone/octagon/##MAT{\ + material = /decl/material/solid/gemstone/##MAT;\ + color = /decl/material/solid/gemstone/##MAT::color;\ +}\ +/obj/item/gemstone/round/##MAT{\ + material = /decl/material/solid/gemstone/##MAT;\ + color = /decl/material/solid/gemstone/##MAT::color;\ +} + +MATERIAL_CUT_GEMSTONES(topaz) +MATERIAL_CUT_GEMSTONES(sapphire) +MATERIAL_CUT_GEMSTONES(ruby) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/passive/horse.dm b/code/modules/mob/living/simple_animal/passive/horse.dm index 04d58aa4ede2..f90f16747a10 100644 --- a/code/modules/mob/living/simple_animal/passive/horse.dm +++ b/code/modules/mob/living/simple_animal/passive/horse.dm @@ -14,6 +14,7 @@ can_have_rider = TRUE max_rider_size = MOB_SIZE_MEDIUM ai = /datum/mob_controller/passive/horse + color = "#ccc496" // preview color draw_visible_overlays = list( "base" = "#ccc496" ) @@ -30,6 +31,7 @@ /mob/living/simple_animal/passive/horse/Initialize() . = ..() + color = null // clear preview color add_inventory_slot(new /datum/inventory_slot/back/horse) equip_to_slot_or_del(new /obj/item/saddle(src), slot_back_str) if(!LAZYACCESS(draw_visible_overlays, "base")) diff --git a/code/modules/reagents/reagent_containers/mortar.dm b/code/modules/reagents/reagent_containers/mortar.dm index 5df6aa94e4c8..76cb59c9a98e 100644 --- a/code/modules/reagents/reagent_containers/mortar.dm +++ b/code/modules/reagents/reagent_containers/mortar.dm @@ -5,6 +5,7 @@ icon_state = ICON_STATE_WORLD volume = 40 material = /decl/material/solid/stone/basalt + color = /decl/material/solid/stone/basalt::color material_alteration = MAT_FLAG_ALTERATION_ALL storage = /datum/storage/hopper/mortar var/grinding = FALSE diff --git a/code/modules/tools/subtypes/hammers.dm b/code/modules/tools/subtypes/hammers.dm index acd258ef5c08..cd6871ce2546 100644 --- a/code/modules/tools/subtypes/hammers.dm +++ b/code/modules/tools/subtypes/hammers.dm @@ -86,6 +86,7 @@ /obj/item/tool/hammer/forge/iron material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color handle_material = /decl/material/solid/organic/wood/mahogany // Forging hammers are not great at general hammer tasks (too heavy I guess), diff --git a/mods/content/blacksmithy/anvil.dm b/mods/content/blacksmithy/anvil.dm index 0968a7806ce5..8c886cadf2c1 100644 --- a/mods/content/blacksmithy/anvil.dm +++ b/mods/content/blacksmithy/anvil.dm @@ -36,6 +36,7 @@ atom_flags = ATOM_FLAG_CLIMBABLE w_class = ITEM_SIZE_STRUCTURE //_LARGE material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color max_health = 1000 structure_flags = STRUCTURE_FLAG_SURFACE material_alteration = MAT_FLAG_ALTERATION_ALL diff --git a/mods/content/blacksmithy/billet.dm b/mods/content/blacksmithy/billet.dm index da40b1fcd00d..3a18225c09a6 100644 --- a/mods/content/blacksmithy/billet.dm +++ b/mods/content/blacksmithy/billet.dm @@ -26,6 +26,7 @@ icon = 'mods/content/blacksmithy/icons/billet.dmi' icon_state = ICON_STATE_WORLD material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color material_alteration = MAT_FLAG_ALTERATION_ALL var/decl/forging_step/current_forging_step = /decl/forging_step/billet diff --git a/mods/content/blacksmithy/tongs.dm b/mods/content/blacksmithy/tongs.dm index 624e790dc53a..173ccef04688 100644 --- a/mods/content/blacksmithy/tongs.dm +++ b/mods/content/blacksmithy/tongs.dm @@ -4,6 +4,7 @@ icon = 'mods/content/blacksmithy/icons/tongs.dmi' icon_state = ICON_STATE_WORLD material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color obj_flags = OBJ_FLAG_INSULATED_HANDLE material_alteration = MAT_FLAG_ALTERATION_ALL var/obj/item/holding_bar diff --git a/mods/content/item_sharpening/whetstone.dm b/mods/content/item_sharpening/whetstone.dm index cdfbb09eea51..09d3cd9de50a 100644 --- a/mods/content/item_sharpening/whetstone.dm +++ b/mods/content/item_sharpening/whetstone.dm @@ -5,6 +5,7 @@ w_class = ITEM_SIZE_TINY material_alteration = MAT_FLAG_ALTERATION_ALL material = /decl/material/solid/quartz + color = /decl/material/solid/quartz::color /obj/item/attackby(obj/item/used_item, mob/user) if(istype(used_item, /obj/item/whetstone)) From f85a7eaceedf82131433f9fbe506c1c753432a37 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 20:47:25 -0400 Subject: [PATCH 0621/1331] Make wood tiles take colour from material --- code/game/turfs/flooring/flooring_wood.dm | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/code/game/turfs/flooring/flooring_wood.dm b/code/game/turfs/flooring/flooring_wood.dm index b5fc086d42f4..c68b599d5554 100644 --- a/code/game/turfs/flooring/flooring_wood.dm +++ b/code/game/turfs/flooring/flooring_wood.dm @@ -9,7 +9,7 @@ build_type = /obj/item/stack/tile/wood flooring_flags = TURF_IS_FRAGILE | TURF_REMOVE_SCREWDRIVER footstep_type = /decl/footsteps/wood - color = /decl/material/solid/organic/wood/oak::color + color = null force_material = /decl/material/solid/organic/wood/oak constructed = TRUE gender = NEUTER @@ -24,32 +24,26 @@ ) /decl/flooring/wood/mahogany - color = /decl/material/solid/organic/wood/mahogany::color build_type = /obj/item/stack/tile/wood/mahogany force_material = /decl/material/solid/organic/wood/mahogany /decl/flooring/wood/maple - color = /decl/material/solid/organic/wood/maple::color build_type = /obj/item/stack/tile/wood/maple force_material = /decl/material/solid/organic/wood/maple /decl/flooring/wood/ebony - color = /decl/material/solid/organic/wood/ebony::color build_type = /obj/item/stack/tile/wood/ebony force_material = /decl/material/solid/organic/wood/ebony /decl/flooring/wood/walnut - color = /decl/material/solid/organic/wood/walnut::color build_type = /obj/item/stack/tile/wood/walnut force_material = /decl/material/solid/organic/wood/walnut /decl/flooring/wood/bamboo - color = /decl/material/solid/organic/wood/bamboo::color build_type = /obj/item/stack/tile/wood/bamboo force_material = /decl/material/solid/organic/wood/bamboo /decl/flooring/wood/yew - color = /decl/material/solid/organic/wood/yew::color build_type = /obj/item/stack/tile/wood/yew force_material = /decl/material/solid/organic/wood/yew @@ -65,32 +59,26 @@ broken_states = null /decl/flooring/wood/rough/mahogany - color = /decl/material/solid/organic/wood/mahogany::color build_type = /obj/item/stack/tile/wood/rough/mahogany force_material = /decl/material/solid/organic/wood/mahogany /decl/flooring/wood/rough/maple - color = /decl/material/solid/organic/wood/maple::color build_type = /obj/item/stack/tile/wood/rough/maple force_material = /decl/material/solid/organic/wood/maple /decl/flooring/wood/rough/ebony - color = /decl/material/solid/organic/wood/ebony::color build_type = /obj/item/stack/tile/wood/rough/ebony force_material = /decl/material/solid/organic/wood/ebony /decl/flooring/wood/rough/walnut - color = /decl/material/solid/organic/wood/walnut::color build_type = /obj/item/stack/tile/wood/rough/walnut force_material = /decl/material/solid/organic/wood/walnut /decl/flooring/wood/rough/bamboo - color = /decl/material/solid/organic/wood/bamboo::color build_type = /obj/item/stack/tile/wood/rough/bamboo force_material = /decl/material/solid/organic/wood/bamboo /decl/flooring/wood/rough/yew - color = /decl/material/solid/organic/wood/yew::color build_type = /obj/item/stack/tile/wood/rough/yew force_material = /decl/material/solid/organic/wood/yew @@ -105,7 +93,7 @@ build_type = /obj/item/stack/tile/wood/laminate/oak flooring_flags = TURF_IS_FRAGILE | TURF_REMOVE_SCREWDRIVER footstep_type = /decl/footsteps/wood - color = /decl/material/solid/organic/wood/chipboard::color + color = null force_material = /decl/material/solid/organic/wood/chipboard constructed = TRUE gender = NEUTER @@ -120,26 +108,21 @@ ) /decl/flooring/laminate/mahogany - color = /decl/material/solid/organic/wood/chipboard/mahogany::color build_type = /obj/item/stack/tile/wood/laminate/mahogany force_material = /decl/material/solid/organic/wood/chipboard/mahogany /decl/flooring/laminate/maple - color = /decl/material/solid/organic/wood/chipboard/maple::color build_type = /obj/item/stack/tile/wood/laminate/maple force_material = /decl/material/solid/organic/wood/chipboard/maple /decl/flooring/laminate/ebony - color = /decl/material/solid/organic/wood/chipboard/ebony::color build_type = /obj/item/stack/tile/wood/laminate/ebony force_material = /decl/material/solid/organic/wood/chipboard/ebony /decl/flooring/laminate/walnut - color = /decl/material/solid/organic/wood/chipboard/walnut::color build_type = /obj/item/stack/tile/wood/laminate/walnut force_material = /decl/material/solid/organic/wood/chipboard/yew /decl/flooring/laminate/yew - color = /decl/material/solid/organic/wood/chipboard/yew::color build_type = /obj/item/stack/tile/wood/laminate/yew force_material = /decl/material/solid/organic/wood/chipboard/yew From 3d062a9c1c1b0c3d867d4198afcbc2f00d2a86ee Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 21:10:04 -0400 Subject: [PATCH 0622/1331] Make grass flooring use material colour --- code/game/turfs/flooring/flooring_grass.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/turfs/flooring/flooring_grass.dm b/code/game/turfs/flooring/flooring_grass.dm index e557193db38d..bc340140b5d5 100644 --- a/code/game/turfs/flooring/flooring_grass.dm +++ b/code/game/turfs/flooring/flooring_grass.dm @@ -6,7 +6,7 @@ has_base_range = 3 footstep_type = /decl/footsteps/grass icon_edge_layer = FLOOR_EDGE_GRASS - color = "#5e7a3b" + color = null // color from material turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID can_engrave = FALSE damage_temperature = T0C+80 @@ -50,5 +50,6 @@ desc = "Do they smoke grass out in space, Bowie? Or do they smoke AstroTurf?" icon = 'icons/turf/flooring/fakegrass.dmi' has_base_range = 3 + color = "#5e7a3b" build_type = /obj/item/stack/tile/grass force_material = /decl/material/solid/organic/plastic From 971aba201ec0df12da5a6c7eebc5e97dfca0d977 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 20:54:26 -0400 Subject: [PATCH 0623/1331] Fix mob modifier and stamina bar screen objects --- code/_onclick/hud/screen/screen_mob_modifier.dm | 3 +++ code/_onclick/hud/screen/screen_stamina.dm | 3 +++ 2 files changed, 6 insertions(+) diff --git a/code/_onclick/hud/screen/screen_mob_modifier.dm b/code/_onclick/hud/screen/screen_mob_modifier.dm index da38d783020e..d8d32bd3c586 100644 --- a/code/_onclick/hud/screen/screen_mob_modifier.dm +++ b/code/_onclick/hud/screen/screen_mob_modifier.dm @@ -102,6 +102,9 @@ maptext_x = -8 maptext_y = -3 icon_state = "modifier_base" + // these must be enabled + use_supplied_ui_alpha = TRUE + use_supplied_ui_color = TRUE var/decl/mob_modifier/archetype var/obj/screen/mob_modifiers/holder diff --git a/code/_onclick/hud/screen/screen_stamina.dm b/code/_onclick/hud/screen/screen_stamina.dm index b70d9e743618..5d4f34b32da9 100644 --- a/code/_onclick/hud/screen/screen_stamina.dm +++ b/code/_onclick/hud/screen/screen_stamina.dm @@ -4,6 +4,9 @@ icon_state = "prog_bar_100" invisibility = INVISIBILITY_MAXIMUM screen_loc = ui_stamina + use_supplied_ui_color = FALSE + use_supplied_ui_alpha = FALSE + use_supplied_ui_icon = FALSE requires_ui_style = FALSE layer = HUD_BASE_LAYER + 0.1 // needs to layer over the movement intent element From 2775fc98eaef759acff1ede37984d67e7d1f2d97 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 21:14:11 -0400 Subject: [PATCH 0624/1331] Fix not being able to put whetstones in storage --- mods/content/item_sharpening/whetstone.dm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mods/content/item_sharpening/whetstone.dm b/mods/content/item_sharpening/whetstone.dm index 09d3cd9de50a..4ff1dc020e3f 100644 --- a/mods/content/item_sharpening/whetstone.dm +++ b/mods/content/item_sharpening/whetstone.dm @@ -2,14 +2,15 @@ name = "whetstone" desc = "A worn-down lozenge used to sharpen blades." icon = 'icons/obj/items/striker.dmi' // TODO unique icon? + icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_TINY material_alteration = MAT_FLAG_ALTERATION_ALL material = /decl/material/solid/quartz color = /decl/material/solid/quartz::color /obj/item/attackby(obj/item/used_item, mob/user) - if(istype(used_item, /obj/item/whetstone)) - return try_sharpen_with(user, used_item) + if(istype(used_item, /obj/item/whetstone) && try_sharpen_with(user, used_item)) + return TRUE return ..() /decl/loadout_option/utility/whetstone From c5fdef9716a9e71a5a8b4781b1aed7ca088859b6 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 21:18:04 -0400 Subject: [PATCH 0625/1331] Fix premapped doormats having random offsets --- code/game/objects/items/weapons/towels.dm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/weapons/towels.dm b/code/game/objects/items/weapons/towels.dm index 5d4a9525fac3..f65ff6962d76 100644 --- a/code/game/objects/items/weapons/towels.dm +++ b/code/game/objects/items/weapons/towels.dm @@ -241,4 +241,8 @@ /// A mapping subtype for a doormat that's already been laid out. /obj/item/towel/doormat/flat laid_out = TRUE - icon_state = ICON_STATE_WORLD + "-flat" \ No newline at end of file + icon_state = ICON_STATE_WORLD + "-flat" + +/obj/item/towel/doormat/flat/Initialize() + . = ..() + reset_offsets() // we don't want to overwrite randpixel but we don't want it to have a random offset either \ No newline at end of file From 0f4473c76fdf512826f710251717df630b35c31d Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 21:18:26 -0400 Subject: [PATCH 0626/1331] Fix damage-category item effects causing weapons to deal zero damage --- code/game/objects/items/_item_force.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/_item_force.dm b/code/game/objects/items/_item_force.dm index 790d894e5962..500bec99069c 100644 --- a/code/game/objects/items/_item_force.dm +++ b/code/game/objects/items/_item_force.dm @@ -22,7 +22,7 @@ var/list/item_effects = get_item_effects(IE_CAT_DAMAGE) if(length(item_effects)) for(var/decl/item_effect/damage_effect as anything in item_effects) - . = damage_effect.expend_attack_use(src, user, item_effects[damage_effect]) + damage_effect.expend_attack_use(src, user, item_effects[damage_effect]) /obj/item/proc/get_attack_force(mob/living/user) if(_base_attack_force <= 0 || (item_flags & ITEM_FLAG_NO_BLUDGEON)) From e8317dd301f25ac9bf2d281744cec6ce40c87770 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 21:20:45 -0400 Subject: [PATCH 0627/1331] Unset TILE_BOUND for multitile objects --- code/game/objects/structures/tables.dm | 2 ++ code/modules/multiz/stairs.dm | 1 + mods/content/inertia/inertial_damper.dm | 2 ++ 3 files changed, 5 insertions(+) diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index 85874e57021d..024c006fd386 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -904,6 +904,7 @@ reinf_material = /decl/material/solid/organic/wood/walnut storage = /datum/storage/structure/desk bound_width = 64 + appearance_flags = /obj/structure/table::appearance_flags & ~TILE_BOUND material_alteration = MAT_FLAG_ALTERATION_ALL can_flip = FALSE top_surface_noun = "desktop" @@ -979,6 +980,7 @@ icon = 'icons/obj/structures/dresser.dmi' icon_state = "dresser" bound_width = 32 + appearance_flags = /obj/structure/table::appearance_flags top_surface_noun = "surface" tabletop_height = 15 mob_offset = 18 diff --git a/code/modules/multiz/stairs.dm b/code/modules/multiz/stairs.dm index e78d6e90ffa6..c93c6f50229b 100644 --- a/code/modules/multiz/stairs.dm +++ b/code/modules/multiz/stairs.dm @@ -63,6 +63,7 @@ /obj/structure/stairs/long icon = 'icons/obj/stairs_64.dmi' bound_height = 64 + appearance_flags = /obj/structure/stairs::appearance_flags & ~TILE_BOUND /obj/structure/stairs/long/north dir = NORTH diff --git a/mods/content/inertia/inertial_damper.dm b/mods/content/inertia/inertial_damper.dm index 4cb9905a95ce..6e1f636d5971 100644 --- a/mods/content/inertia/inertial_damper.dm +++ b/mods/content/inertia/inertial_damper.dm @@ -136,6 +136,8 @@ /obj/machinery/inertial_damper/proc/SetBounds() bound_width = width * world.icon_size bound_height = height * world.icon_size + if(bound_height != world.icon_size || bound_width != world.icon_size) + appearance_flags = /obj/machinery::appearance_flags & ~TILE_BOUND /obj/machinery/inertial_damper/interface_interact(var/mob/user) ui_interact(user) From 5c4902f6303111bd6d97675ccc873f32225d2610 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 21:21:38 -0400 Subject: [PATCH 0628/1331] Allow wood and stone to smooth with wattle and daub walls --- .../materials/definitions/solids/materials_solid_stone.dm | 2 ++ .../materials/definitions/solids/materials_solid_wood.dm | 2 ++ 2 files changed, 4 insertions(+) diff --git a/code/modules/materials/definitions/solids/materials_solid_stone.dm b/code/modules/materials/definitions/solids/materials_solid_stone.dm index 99c4a2bd31c3..dc692ddb16f7 100644 --- a/code/modules/materials/definitions/solids/materials_solid_stone.dm +++ b/code/modules/materials/definitions/solids/materials_solid_stone.dm @@ -15,6 +15,8 @@ 'icons/turf/walls/wood.dmi' = TRUE, 'icons/turf/walls/brick.dmi' = TRUE, 'icons/turf/walls/log.dmi' = TRUE, + 'icons/turf/walls/wattle.dmi' = TRUE, + 'icons/turf/walls/wattledaub.dmi' = TRUE, 'icons/turf/walls/metal.dmi' = TRUE ) dissolves_into = list( diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index 6402e6aa627f..b059c599ebc2 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -10,6 +10,8 @@ 'icons/turf/walls/stone.dmi' = TRUE, 'icons/turf/walls/brick.dmi' = TRUE, 'icons/turf/walls/log.dmi' = TRUE, + 'icons/turf/walls/wattle.dmi' = TRUE, + 'icons/turf/walls/wattledaub.dmi' = TRUE, 'icons/turf/walls/metal.dmi' = TRUE ) icon_reinf = list( From fd6b54751c78e8bb72a31a9f20b669622c212548 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Wed, 26 Mar 2025 21:23:16 -0400 Subject: [PATCH 0629/1331] Prevent forging dirt bars or making dirt billets --- mods/content/blacksmithy/anvil.dm | 2 +- mods/content/blacksmithy/billet.dm | 8 +++++++- mods/content/blacksmithy/forge_fire.dm | 7 ++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/mods/content/blacksmithy/anvil.dm b/mods/content/blacksmithy/anvil.dm index 8c886cadf2c1..9f88267f22a8 100644 --- a/mods/content/blacksmithy/anvil.dm +++ b/mods/content/blacksmithy/anvil.dm @@ -83,7 +83,7 @@ // Flow through into procs below. // Put the bar onto the anvil (need to do this to avoid repairs in ..()) - if(istype(used_item, /obj/item/stack/material/bar)) + if(istype(used_item, /obj/item/stack/material/bar) && used_item.is_forgable()) var/obj/item/stack/material/bar/bar = used_item if(used_item.material != material || current_health >= get_max_health()) if(bar.get_amount() > 1) diff --git a/mods/content/blacksmithy/billet.dm b/mods/content/blacksmithy/billet.dm index 3a18225c09a6..b269d10ae0c1 100644 --- a/mods/content/blacksmithy/billet.dm +++ b/mods/content/blacksmithy/billet.dm @@ -14,9 +14,15 @@ return TRUE . = ..() -/obj/item/proc/hot_enough_to_forge(melting_point_percent = 0.25) +/// Whether or not this item is considered forgable (e.g. there is a temperature at which it can be forged) +/obj/item/proc/is_forgable() if(!istype(material) || isnull(material.melting_point) || !material.forgable) return FALSE + return TRUE + +/obj/item/proc/hot_enough_to_forge(melting_point_percent = 0.25) + if(!is_forgable()) + return FALSE // Defaults to >25% of the way to melting to be considered 'forgable' return temperature >= ((material.melting_point - T20C) * melting_point_percent) + T20C diff --git a/mods/content/blacksmithy/forge_fire.dm b/mods/content/blacksmithy/forge_fire.dm index 437c9cd1c1c8..1c32a6e8d912 100644 --- a/mods/content/blacksmithy/forge_fire.dm +++ b/mods/content/blacksmithy/forge_fire.dm @@ -20,13 +20,14 @@ /obj/structure/fire_source/forge/proc/get_forgable_contents() . = list() for(var/obj/item/thing in get_stored_inventory()) - if(thing.material?.forgable && (istype(thing, /obj/item/billet) || istype(thing, /obj/item/stack/material/bar))) + if(thing.is_forgable() && (istype(thing, /obj/item/billet) || istype(thing, /obj/item/stack/material/bar))) . += thing /obj/structure/fire_source/forge/attackby(obj/item/used_item, mob/user) + var/item_is_forgable = used_item.is_forgable() // Raw materials. - if(istype(used_item, /obj/item/stack/material/bar)) + if(istype(used_item, /obj/item/stack/material/bar) && item_is_forgable) var/obj/item/stack/material/bar/bar = used_item if(used_item.material != material || current_health >= get_max_health()) if(bar.get_amount() > 1) @@ -41,7 +42,7 @@ // Flows through to below. // Partially worked billets. - if(istype(used_item, /obj/item/billet)) + if(istype(used_item, /obj/item/billet) && item_is_forgable) if(used_item.loc == user) user.try_unequip(used_item, loc) else From 2128a4d3365b82d3b6ba8b9a3b47cfc46cbff032 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Thu, 27 Mar 2025 21:00:40 -0400 Subject: [PATCH 0630/1331] Fix new hand slots not updating until you swap hands --- code/_onclick/hud/hud_types/_hud.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/_onclick/hud/hud_types/_hud.dm b/code/_onclick/hud/hud_types/_hud.dm index 103c016a6d97..c14dcf7c378e 100644 --- a/code/_onclick/hud/hud_types/_hud.dm +++ b/code/_onclick/hud/hud_types/_hud.dm @@ -403,6 +403,8 @@ if(mymob.client) mymob.client.screen |= swap_elem + update_hand_elements() + return TRUE /datum/hud/proc/build_inventory_ui() From 273e3df8635ce3533ba22f59f7bc336ba1b2bcac Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Thu, 27 Mar 2025 21:31:21 -0400 Subject: [PATCH 0631/1331] Fix horses all being the same colour --- code/modules/mob/living/simple_animal/passive/horse.dm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/simple_animal/passive/horse.dm b/code/modules/mob/living/simple_animal/passive/horse.dm index f90f16747a10..0e2c0d0c023e 100644 --- a/code/modules/mob/living/simple_animal/passive/horse.dm +++ b/code/modules/mob/living/simple_animal/passive/horse.dm @@ -14,10 +14,8 @@ can_have_rider = TRUE max_rider_size = MOB_SIZE_MEDIUM ai = /datum/mob_controller/passive/horse - color = "#ccc496" // preview color - draw_visible_overlays = list( - "base" = "#ccc496" - ) + color = "#806146" // preview color + draw_visible_overlays = null // e.g. list("base" = "#806146") /datum/mob_controller/passive/horse emote_speech = list("Neigh!","NEIGH!","Neigh?") From 0c53d90e085c671379270250f734e05bcb879d1c Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Thu, 27 Mar 2025 21:33:27 -0400 Subject: [PATCH 0632/1331] Add support for buckle shifting based on atom offset --- code/game/atoms_movable.dm | 3 ++- code/game/objects/structures/chairs/rustic_chairs.dm | 1 + code/modules/mob/mob_layering.dm | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 069199090b81..3554eeb7fc3b 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -10,7 +10,8 @@ var/buckle_layer_above = FALSE var/buckle_dir = 0 var/buckle_lying = -1 // bed-like behavior, forces mob to lie or stand if buckle_lying != -1 - var/buckle_pixel_shift // ex. @'{"x":0,"y":0,"z":0}' //where the buckled mob should be pixel shifted to, or null for no pixel shift control + /// A list or JSON-encoded list of pixel offsets to use on a mob buckled to this atom. TRUE to use this atom's pixel shifts, null for no pixel shift control. + var/buckle_pixel_shift // ex. @'{"x":0,"y":0,"z":0}' var/buckle_require_restraints = 0 // require people to be cuffed before being able to buckle. eg: pipes var/buckle_require_same_tile = FALSE var/buckle_sound diff --git a/code/game/objects/structures/chairs/rustic_chairs.dm b/code/game/objects/structures/chairs/rustic_chairs.dm index 840678cb077e..a61ca9b1a171 100644 --- a/code/game/objects/structures/chairs/rustic_chairs.dm +++ b/code/game/objects/structures/chairs/rustic_chairs.dm @@ -5,6 +5,7 @@ material = /decl/material/solid/organic/wood/walnut color = /decl/material/solid/organic/wood/walnut::color user_comfort = -0.5 + buckle_pixel_shift = TRUE // use chair offset /obj/structure/chair/rustic_fancy name_prefix = "fancy" diff --git a/code/modules/mob/mob_layering.dm b/code/modules/mob/mob_layering.dm index e4ecb2b50071..ad458692d2d3 100644 --- a/code/modules/mob/mob_layering.dm +++ b/code/modules/mob/mob_layering.dm @@ -124,6 +124,10 @@ new_pixel_x += pixel_shift["x"] || 0 new_pixel_y += pixel_shift["y"] || 0 new_pixel_z += pixel_shift["z"] || 0 + if(pixel_shift == TRUE) // TRUE -> use object's offset + new_pixel_x = buckled.pixel_x + new_pixel_y = buckled.pixel_y + new_pixel_z = buckled.pixel_z if(last_pixel_x != new_pixel_x || last_pixel_y != new_pixel_y || last_pixel_z != new_pixel_z) if(anim_time > 0) From b6e1054b508b38b8739444489b05d756ec8b0b73 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Thu, 27 Mar 2025 21:33:44 -0400 Subject: [PATCH 0633/1331] Adjust large cask rack naming code --- code/game/objects/structures/barrels/cask_rack.dm | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/code/game/objects/structures/barrels/cask_rack.dm b/code/game/objects/structures/barrels/cask_rack.dm index c8e5cebc6184..2dfbdc2d8e09 100644 --- a/code/game/objects/structures/barrels/cask_rack.dm +++ b/code/game/objects/structures/barrels/cask_rack.dm @@ -143,16 +143,12 @@ // A larger stack, used to arrange up to three casks. /obj/structure/cask_rack/large + name_prefix = "large" desc = "A flat rack used to stop casks from rolling around." max_stack = 3 w_class = ITEM_SIZE_LARGE_STRUCTURE icon = 'icons/obj/structures/barrels/cask_rack_large.dmi' -// We want 'large wooden cask rack' not 'wooden large cask rack' -/obj/structure/cask_rack/large/update_material_name(override_name) - . = ..() - SetName("large [name]") - /obj/structure/cask_rack/large/adjust_barrel_offsets(atom/movable/barrel, barrel_position) ..() switch(barrel_position) From 9c982b5362c88d56ef57ad7a1dc8dddfffda18c7 Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Thu, 27 Mar 2025 21:38:52 -0400 Subject: [PATCH 0634/1331] Fix descriptions for plastered wattle walls --- code/game/turfs/walls/wall_wattle.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/game/turfs/walls/wall_wattle.dm b/code/game/turfs/walls/wall_wattle.dm index b1ce9e571847..4cdca1b6df0c 100644 --- a/code/game/turfs/walls/wall_wattle.dm +++ b/code/game/turfs/walls/wall_wattle.dm @@ -76,8 +76,10 @@ else if(paint_color) if(reinf_material) SetName("[reinf_material.solid_name]-framed plastered wall") + desc = "A plastered wall framed with [reinf_material.solid_name]." else SetName("plastered wall") + desc = "A plastered wall." else if(reinf_material) SetName("[reinf_material.solid_name]-framed [material.adjective_name] wattle and daub wall") From 2c2683cf665fa3c485ab8166cabaaa49ba8123de Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Thu, 27 Mar 2025 22:13:05 -0400 Subject: [PATCH 0635/1331] Fix reverb in foresters' hut --- maps/shaded_hills/areas/woods.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/maps/shaded_hills/areas/woods.dm b/maps/shaded_hills/areas/woods.dm index 73c262300a6f..5a5a662ed04c 100644 --- a/maps/shaded_hills/areas/woods.dm +++ b/maps/shaded_hills/areas/woods.dm @@ -33,5 +33,6 @@ /area/shaded_hills/forester_hut name = "\improper Foresters' Hut" + sound_env = STANDARD_STATION fishing_failure_prob = 100 fishing_results = list() From 0eb1ab93e3eec29bf161e47ea29e4f427caa90bd Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 4 May 2025 01:02:06 +0000 Subject: [PATCH 0636/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 13b7880bf540..6aef7ebd7b31 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -108,12 +108,6 @@

      Cerebulon updated:

      • New icons for a variety of objects, mostly machines and grenades.
      - -

      02 March 2025

      -

      MistakeNot4892 updated:

      -
        -
      • Old browser-based module selection for robots has been removed, use the INV button to open storage like you would a belt/backpack.
      • -
    From da4185d8c4f8ccbf201ebea8fdaf697224105043 Mon Sep 17 00:00:00 2001 From: Neerti Date: Mon, 5 May 2025 16:43:42 -0400 Subject: [PATCH 0637/1331] Fixes overmap trading hubs being uninteractable --- code/modules/merchant/merchant_programs.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/merchant/merchant_programs.dm b/code/modules/merchant/merchant_programs.dm index bd3c35a5a1e1..9eaf55156e0a 100644 --- a/code/modules/merchant/merchant_programs.dm +++ b/code/modules/merchant/merchant_programs.dm @@ -39,7 +39,7 @@ /datum/computer_file/program/merchant/proc/get_available_hubs() . = list() - var/turf/T = get_turf(holder) + var/turf/T = get_turf(holder.resolve()) for(var/datum/trade_hub/hub in SStrade.trade_hubs) if(hub.is_accessible_from(T)) . |= hub From 593bed0cc29cdb3b2a438faa36df6eb6dec9e107 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 11 May 2025 01:01:11 +0000 Subject: [PATCH 0638/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 6aef7ebd7b31..8985c1af8281 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -102,12 +102,6 @@

    Cerebulon updated:

    • Changed modpack Tajara to use old-style humanoid sprites instead of a unique feline body shape which has since been used by Hnolls.
    - -

    09 March 2025

    -

    Cerebulon updated:

    -
      -
    • New icons for a variety of objects, mostly machines and grenades.
    • -
    From ceb3f06438d6f297732c7ff684f9dfd02b974fa2 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 9 May 2025 07:58:24 -0400 Subject: [PATCH 0639/1331] Fix dev branch issues with matter --- code/game/atoms.dm | 2 +- code/game/objects/items/stacks/medical/medical_bandage.dm | 2 +- code/game/objects/items/weapons/soap.dm | 2 +- code/game/turfs/turf_fluids.dm | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 4d964f5c4677..a121490e2aea 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -439,7 +439,7 @@ if(length(reagents?.reagent_volumes)) LAZYINITLIST(.) for(var/decl/material/reagent as anything in reagents.reagent_volumes) - .[reagent] += floor(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_MATERIAL_UNIT) + .[reagent.type] += floor(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_MATERIAL_UNIT) for(var/atom/contained_obj as anything in get_contained_external_atoms()) // machines handle component parts separately . = MERGE_ASSOCS_WITH_NUM_VALUES(., contained_obj.get_contained_matter()) diff --git a/code/game/objects/items/stacks/medical/medical_bandage.dm b/code/game/objects/items/stacks/medical/medical_bandage.dm index 5a5727b290bd..cd0ca1efdc10 100644 --- a/code/game/objects/items/stacks/medical/medical_bandage.dm +++ b/code/game/objects/items/stacks/medical/medical_bandage.dm @@ -75,7 +75,7 @@ /obj/item/stack/medical/bandage/proc/bandage_wound(mob/user, mob/target, obj/item/organ/external/affecting, datum/wound/wound) user.visible_message( SPAN_NOTICE("\The [user] bandages \a [wound.desc] on \the [target]'s [affecting.name]."), - SPAN_NOTICE("You bandage \a [wound.desc] on \the [target]'s [affecting.name].") + SPAN_NOTICE("You bandage \a [wound.desc] on \the [target]'s [affecting.name].") ) wound.bandage() diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm index bd0956bad5b2..ebb10a60b96d 100644 --- a/code/game/objects/items/weapons/soap.dm +++ b/code/game/objects/items/weapons/soap.dm @@ -54,7 +54,7 @@ update_icon() /obj/item/soap/proc/wet() - add_to_reagents(/decl/material/liquid/cleaner/soap, SOAP_CLEANER_ON_WET) + add_to_reagents(/decl/material/liquid/cleaner/soap, SOAP_CLEANER_ON_WET, phase = MAT_PHASE_LIQUID) /obj/item/soap/Crossed(atom/movable/AM) var/mob/living/victim = AM diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index b7b1f4fe95e7..48c6e74fc863 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -218,7 +218,7 @@ var/list/matter_list = list() for(var/decl/material/reagent as anything in solids.solid_volumes) var/reagent_amount = solids.solid_volumes[reagent] - matter_list[reagent] = round(reagent_amount/REAGENT_UNITS_PER_MATERIAL_UNIT) + matter_list[reagent.type] = round(reagent_amount/REAGENT_UNITS_PER_MATERIAL_UNIT) solids.remove_reagent(reagent, reagent_amount, defer_update = TRUE, removed_phases = MAT_PHASE_SOLID) var/obj/item/debris/scraps/chemical/scraps = locate() in contents From d3449d74364adf97448559b230e7abc29616c83d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 9 May 2025 08:37:24 -0400 Subject: [PATCH 0640/1331] Fix NanoUI map runtime --- code/modules/nano/nanoui.dm | 7 ++++--- nano/templates/crew_monitor_map_header.tmpl | 2 +- nano/templates/layout_default.tmpl | 2 +- nano/templates/sec_camera_map_header.tmpl | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index 9fc00cd5a649..e5310e15826d 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -514,13 +514,14 @@ nanoui is used to open and update nano browser uis set_show_map(text2num(href_list["showMap"])) map_update = 1 - if(href_list["mapZLevel"]) - var/map_z = text2num(href_list["mapZLevel"]) + if(href_list["switchMapZLevel"]) + var/map_z = text2num(href_list["switchMapZLevel"]) if(isMapLevel(map_z)) set_map_z_level(map_z) map_update = 1 - if (src_object && (src_object.Topic(href, href_list, state) || map_update)) + // src_object.Topic() might null out src_object by deleting us. + if (src_object && (src_object.Topic(href, href_list, state) || (!QDELETED(src) && src_object && map_update))) SSnano.update_uis(src_object) // update all UIs attached to src_object /** diff --git a/nano/templates/crew_monitor_map_header.tmpl b/nano/templates/crew_monitor_map_header.tmpl index 24398dec5b01..a86abc85d837 100644 --- a/nano/templates/crew_monitor_map_header.tmpl +++ b/nano/templates/crew_monitor_map_header.tmpl @@ -7,7 +7,7 @@ Used In File(s): code\modules\modular_computers\file_system\programs\medical\sui
    Z Level:  {{for config.mapZLevels :zValue:zIndex}} - {{:helper.link(zValue, 'close', {'mapZLevel' : zValue}, null, config.mapZLevel == zValue ? 'selected' : null)}} + {{:helper.link(zValue, 'close', {'switchMapZLevel' : zValue}, null, config.mapZLevel == zValue ? 'selected' : null)}} {{/for}}
    diff --git a/nano/templates/layout_default.tmpl b/nano/templates/layout_default.tmpl index 8dae1468bfb1..a80f025bce3e 100644 --- a/nano/templates/layout_default.tmpl +++ b/nano/templates/layout_default.tmpl @@ -8,7 +8,7 @@
    Z Level:  {{for config.mapZLevels :zValue:zIndex}} - {{:helper.link(zValue, 'close', {'mapZLevel' : zValue}, null, config.mapZLevel == zValue ? 'selected' : null)}} + {{:helper.link(zValue, 'close', {'switchMapZLevel' : zValue}, null, config.mapZLevel == zValue ? 'selected' : null)}} {{/for}}
    diff --git a/nano/templates/sec_camera_map_header.tmpl b/nano/templates/sec_camera_map_header.tmpl index 7871b1ec11a4..0ed4cbdccc55 100644 --- a/nano/templates/sec_camera_map_header.tmpl +++ b/nano/templates/sec_camera_map_header.tmpl @@ -18,7 +18,7 @@
    {{for config.mapZLevels :zValue:zIndex}} - {{:helper.link(zValue, 'close', {'mapZLevel' : zValue}, null, config.mapZLevel == zValue ? 'selected' : null)}} + {{:helper.link(zValue, 'close', {'switchMapZLevel' : zValue}, null, config.mapZLevel == zValue ? 'selected' : null)}} {{/for}}
    From 0d307d15fcd86a90d0e7d3aa7af8f1658805f1e8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 9 May 2025 08:52:39 -0400 Subject: [PATCH 0641/1331] Fix bug with walnut laminate material --- code/game/turfs/flooring/flooring_wood.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/turfs/flooring/flooring_wood.dm b/code/game/turfs/flooring/flooring_wood.dm index c68b599d5554..d7352fb03ae2 100644 --- a/code/game/turfs/flooring/flooring_wood.dm +++ b/code/game/turfs/flooring/flooring_wood.dm @@ -121,7 +121,7 @@ /decl/flooring/laminate/walnut build_type = /obj/item/stack/tile/wood/laminate/walnut - force_material = /decl/material/solid/organic/wood/chipboard/yew + force_material = /decl/material/solid/organic/wood/chipboard/walnut /decl/flooring/laminate/yew build_type = /obj/item/stack/tile/wood/laminate/yew From 33c49f44d2a946baf41ffddc7032926232c2d4d5 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Wed, 14 May 2025 00:57:37 +0000 Subject: [PATCH 0642/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 8985c1af8281..b65f492394dc 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -96,12 +96,6 @@

    Typhin updated:

    • Allows parts/screwdrivers to be used on biogenerators
    - -

    12 March 2025

    -

    Cerebulon updated:

    -
      -
    • Changed modpack Tajara to use old-style humanoid sprites instead of a unique feline body shape which has since been used by Hnolls.
    • -
    From 7d78dfb6e3a7e7c647c8f434ea0c2add56b5806e Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sat, 4 Jan 2025 22:15:37 -0600 Subject: [PATCH 0643/1331] lighting: Update ambience impl, general updates --- code/__defines/lighting.dm | 7 +- code/controllers/subsystems/lighting.dm | 51 +++++++-- code/modules/lighting/lighting_corner.dm | 41 ++++---- code/modules/lighting/lighting_overlay.dm | 2 +- code/modules/lighting/lighting_turf.dm | 121 +++++++++++++++------- 5 files changed, 153 insertions(+), 69 deletions(-) diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index c02fc243ba77..d9c934a0fb5f 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -10,7 +10,8 @@ #define LIGHTING_DARKNESS_ICON_STATE "black" // icon_state used for lighting overlays with no luminosity. #define LIGHTING_TRANSPARENT_ICON_STATE "blank" -#define LIGHTING_SOFT_THRESHOLD 0.001 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. +// This is purely used as a threshold for 'is this turf probably dark' to avoid floating point nonsense. +#define LIGHTING_SOFT_THRESHOLD 0.001 #define LIGHTING_BLOCKED_FACTOR 0.5 // How much the range of a directional light will be reduced while facing a wall. // If defined, instant updates will be used whenever server load permits. Otherwise queued updates are always used. @@ -20,6 +21,10 @@ #define TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) ((T:dynamic_lighting && T:loc:dynamic_lighting)) #define TURF_IS_DYNAMICALLY_LIT(T) (isturf(T) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) +// Note: this does not imply the above, a turf can have ambient light without being dynamically lit. +#define TURF_IS_AMBIENT_LIT_UNSAFE(T) (T:ambient_active) +#define TURF_IS_AMBIENT_LIT(T) (isturf(T) && TURF_IS_AMBIENT_LIT_UNSAFE(T)) + // If I were you I'd leave this alone. #define LIGHTING_BASE_MATRIX \ list \ diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm index 068353abbe0c..43e79dea97ef 100644 --- a/code/controllers/subsystems/lighting.dm +++ b/code/controllers/subsystems/lighting.dm @@ -8,13 +8,16 @@ SUBSYSTEM_DEF(lighting) var/total_lighting_overlays = 0 var/total_lighting_sources = 0 var/total_ambient_turfs = 0 - var/list/lighting_corners = list() // List of all lighting corners in the world. + var/total_lighting_corners = 0 - var/list/light_queue = list() // lighting sources queued for update. + /// lighting sources queued for update. + var/list/light_queue = list() var/lq_idex = 1 - var/list/corner_queue = list() // lighting corners queued for update. + /// lighting corners queued for update. + var/list/corner_queue = list() var/cq_idex = 1 - var/list/overlay_queue = list() // lighting overlays queued for update. + /// lighting overlays queued for update. + var/list/overlay_queue = list() var/oq_idex = 1 var/tmp/processed_lights = 0 @@ -25,18 +28,20 @@ SUBSYSTEM_DEF(lighting) var/total_instant_updates = 0 #ifdef USE_INTELLIGENT_LIGHTING_UPDATES + var/instant_ctr = 0 var/force_queued = TRUE - var/force_override = FALSE // For admins. + /// For admins. + var/force_override = FALSE #endif /datum/controller/subsystem/lighting/stat_entry() var/list/out = list( #ifdef USE_INTELLIGENT_LIGHTING_UPDATES - "IUR: [total_ss_updates ? round(total_instant_updates/(total_instant_updates+total_ss_updates)*100, 0.1) : "NaN"]%\n", + "IUR: [total_ss_updates ? round(total_instant_updates/(total_instant_updates+total_ss_updates)*100, 0.1) : "NaN"]% Instant: [force_queued ? "Disabled" : "Allowed"]\n", #endif - "\tT:{L:[total_lighting_sources] C:[lighting_corners.len] O:[total_lighting_overlays] A:[total_ambient_turfs]}\n", - "\tP:{L:[light_queue.len - (lq_idex - 1)]|C:[corner_queue.len - (cq_idex - 1)]|O:[overlay_queue.len - (oq_idex - 1)]}\n", - "\tL:{L:[processed_lights]|C:[processed_corners]|O:[processed_overlays]}\n" + "\tT: { L: [total_lighting_sources] C: [total_lighting_corners] O:[total_lighting_overlays] A: [total_ambient_turfs] }\n", + "\tP: { L: [light_queue.len - (lq_idex - 1)] C: [corner_queue.len - (cq_idex - 1)] O: [overlay_queue.len - (oq_idex - 1)] }\n", + "\tL: { L: [processed_lights] C: [processed_corners] O: [processed_overlays]}\n" ) ..(out.Join()) @@ -46,6 +51,32 @@ SUBSYSTEM_DEF(lighting) force_queued = FALSE total_ss_updates = 0 total_instant_updates = 0 + +/// Disable instant updates, relying entirely on the (slower, but less laggy) queued pathway. Use if changing a *lot* of lights. +/datum/controller/subsystem/lighting/proc/pause_instant() + if (force_override) + return + + instant_ctr += 1 + if (instant_ctr == 1) + force_queued = TRUE + +/// Resume instant updates. +/datum/controller/subsystem/lighting/proc/resume_instant() + if (force_override) + return + + instant_ctr = max(instant_ctr - 1, 0) + + if (!instant_ctr) + force_queued = FALSE + +#else + +/datum/controller/subsystem/lighting/proc/pause_instant() + +/datum/controller/subsystem/lighting/proc/resume_instant() + #endif /datum/controller/subsystem/lighting/Initialize(timeofday) @@ -157,7 +188,7 @@ SUBSYSTEM_DEF(lighting) oq_idex = 1 /datum/controller/subsystem/lighting/Recover() - lighting_corners = SSlighting.lighting_corners + total_lighting_corners = SSlighting.total_lighting_corners total_lighting_overlays = SSlighting.total_lighting_overlays total_lighting_sources = SSlighting.total_lighting_sources diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index b1e8d111ed79..3170ed85d090 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -56,13 +56,16 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/needs_update = FALSE - var/cache_r = LIGHTING_SOFT_THRESHOLD - var/cache_g = LIGHTING_SOFT_THRESHOLD - var/cache_b = LIGHTING_SOFT_THRESHOLD + var/cache_r = 0 + var/cache_g = 0 + var/cache_b = 0 var/cache_mx = 0 + /// Used for planet lighting. Probably needs a better system to prevent over-updating when not needed at some point. + var/update_gen = 0 + /datum/lighting_corner/New(turf/new_turf, diagonal, oi) - SSlighting.lighting_corners += src + SSlighting.total_lighting_corners += 1 var/has_ambience = FALSE @@ -70,7 +73,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, z = t1.z t1i = oi - if (t1.ambient_light) + if (TURF_IS_AMBIENT_LIT_UNSAFE(new_turf)) has_ambience = TRUE var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction. @@ -96,7 +99,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, t2 = T t2i = REVERSE_LIGHTING_CORNER_DIAGONAL[diagonal] T.corners[t2i] = src - if (T.ambient_light) + if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) has_ambience = TRUE // Now the horizontal one. @@ -109,7 +112,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, t3 = T t3i = REVERSE_LIGHTING_CORNER_DIAGONAL[((Tc > x) ? EAST : WEST) | ((t1.y > y) ? NORTH : SOUTH)] // Get the dir based on coordinates. T.corners[t3i] = src - if (T.ambient_light) + if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) has_ambience = TRUE // And finally the vertical one. @@ -122,7 +125,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, t4 = T t4i = REVERSE_LIGHTING_CORNER_DIAGONAL[((t1.x > x) ? EAST : WEST) | ((Tc > y) ? NORTH : SOUTH)] // Get the dir based on coordinates. T.corners[t4i] = src - if (T.ambient_light) + if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) has_ambience = TRUE update_active() @@ -161,9 +164,11 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, if (!T || !T.ambient_light) continue - sum_r += (HEX_RED(T.ambient_light) / 255) * T.ambient_light_multiplier - sum_g += (HEX_GREEN(T.ambient_light) / 255) * T.ambient_light_multiplier - sum_b += (HEX_BLUE(T.ambient_light) / 255) * T.ambient_light_multiplier + var/list/parts = rgb2num(T.ambient_light) + + sum_r += (parts[1] / 255) * T.ambient_light_multiplier + sum_g += (parts[2] / 255) * T.ambient_light_multiplier + sum_b += (parts[3] / 255) * T.ambient_light_multiplier sum_r /= 4 sum_g /= 4 @@ -314,15 +319,9 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, if (mx > 1) . = 1 / mx - else if (mx < LIGHTING_SOFT_THRESHOLD) - . = 0 // 0 means soft lighting. - - if (.) - cache_r = round(lr * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD - cache_g = round(lg * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD - cache_b = round(lb * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD - else - cache_r = cache_g = cache_b = LIGHTING_SOFT_THRESHOLD + cache_r = round(lr * ., LIGHTING_ROUND_VALUE) + cache_g = round(lg * ., LIGHTING_ROUND_VALUE) + cache_b = round(lb * ., LIGHTING_ROUND_VALUE) cache_mx = round(mx, LIGHTING_ROUND_VALUE) @@ -348,7 +347,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, if (!force) return QDEL_HINT_LETMELIVE - SSlighting.lighting_corners -= src + SSlighting.total_lighting_corners -= 1 return ..() /datum/lighting_corner/dummy/New() diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index c7fae9674340..c4900999b891 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -77,7 +77,7 @@ ca = corners[1] || dummy_lighting_corner var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx) - luminosity = max > LIGHTING_SOFT_THRESHOLD + luminosity = max > 0 var/rr = cr.cache_r var/rg = cr.cache_g diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 10f19c3ae5eb..9af3682e4e85 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -4,7 +4,7 @@ var/ambient_light /// The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. var/ambient_light_multiplier = 0.3 - luminosity = 1 + luminosity = 1 var/tmp/lighting_corners_initialised = FALSE @@ -19,7 +19,7 @@ var/tmp/ambient_has_indirect = FALSE // Record-keeping, do not touch -- that means you, admins. - var/tmp/ambient_light_old + var/tmp/ambient_active = FALSE //! Do we have non-zero ambient light? Use [TURF_IS_AMBIENT_LIT] instead of reading this directly. var/tmp/ambient_light_old_r = 0 var/tmp/ambient_light_old_g = 0 var/tmp/ambient_light_old_b = 0 @@ -33,35 +33,66 @@ update_ambient_light() -/turf/proc/clear_ambient_light() - if (ambient_light == null) +/turf/proc/replace_ambient_light(old_color, new_color, old_multiplier, new_multiplier = 0) + if (!TURF_IS_AMBIENT_LIT_UNSAFE(src)) + add_ambient_light(new_color, new_multiplier) return - ambient_light = null - update_ambient_light() + ASSERT(!isnull(old_multiplier)) // omitting new_multiplier is allowed for removing light nondestructively -/turf/proc/update_ambient_light(no_corner_update = FALSE) - // These are deltas. - var/ambient_r = 0 - var/ambient_g = 0 - var/ambient_b = 0 + old_color ||= COLOR_WHITE + new_color ||= COLOR_WHITE - if (ambient_light && ambient_light_multiplier) // If either of these are false-y we can use the simplier path and avoid calculations - ambient_r = round(((HEX_RED(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_r, LIGHTING_ROUND_VALUE) - ambient_g = round(((HEX_GREEN(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_g, LIGHTING_ROUND_VALUE) - ambient_b = round(((HEX_BLUE(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_b, LIGHTING_ROUND_VALUE) - else - ambient_r = -ambient_light_old_r - ambient_g = -ambient_light_old_g - ambient_b = -ambient_light_old_b + var/list/old_parts = rgb2num(old_color) + var/list/new_parts = rgb2num(new_color) + + var/dr = (new_parts[1] / 255) * new_multiplier - (old_parts[1] / 255) * old_multiplier + var/dg = (new_parts[2] / 255) * new_multiplier - (old_parts[2] / 255) * old_multiplier + var/db = (new_parts[3] / 255) * new_multiplier - (old_parts[3] / 255) * old_multiplier + + if (!dr && !dg && !db) + return - ambient_light_old_r += ambient_r - ambient_light_old_g += ambient_g - ambient_light_old_b += ambient_b + add_ambient_light_raw(dr, dg, db) - if (abs(ambient_r + ambient_g + ambient_b) == 0) +/turf/proc/add_ambient_light(color, multiplier, update = TRUE) + if (!color) return + multiplier ||= ambient_light_multiplier + + var/list/ambient_parts = rgb2num(color) + + var/ambient_r = (ambient_parts[1] / 255) * multiplier + var/ambient_g = (ambient_parts[2] / 255) * multiplier + var/ambient_b = (ambient_parts[3] / 255) * multiplier + + add_ambient_light_raw(ambient_r, ambient_g, ambient_b, update) + +/turf/proc/add_ambient_light_raw(lr, lg, lb, update = TRUE) + if (!lr && !lg && !lb) + if (!ambient_light_old_r || !ambient_light_old_g || !ambient_light_old_b) + ambient_active = FALSE + SSlighting.total_ambient_turfs -= 1 + return + + if (!ambient_active) + SSlighting.total_ambient_turfs += 1 + ambient_active = TRUE + + // There are four corners per (lit) turf, we don't want to apply our light 4 times -- compensate by dividing by 4. + lr /= 4 + lg /= 4 + lb /= 4 + + lr = round(lr, LIGHTING_ROUND_VALUE) + lg = round(lg, LIGHTING_ROUND_VALUE) + lb = round(lb, LIGHTING_ROUND_VALUE) + + ambient_light_old_r += lr + ambient_light_old_g += lg + ambient_light_old_b += lb + if (!corners || !lighting_corners_initialised) if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) generate_missing_corners() @@ -70,23 +101,41 @@ // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. for (var/datum/lighting_corner/C in corners) - C.update_ambient_lumcount(ambient_r, ambient_g, ambient_b, no_corner_update) + C.update_ambient_lumcount(lr, lg, lb, !update) - if (ambient_light_old == null && ambient_light != ambient_light_old) - SSlighting.total_ambient_turfs += 1 - else if (ambient_light_old != null && ambient_light == null) - SSlighting.total_ambient_turfs -= 1 +/turf/proc/clear_ambient_light() + if (ambient_light == null) + return + + ambient_light = null + update_ambient_light() + +/turf/proc/update_ambient_light(no_corner_update = FALSE) + // These are deltas. + var/ambient_r = 0 + var/ambient_g = 0 + var/ambient_b = 0 + + if (ambient_light) + var/list/parts = rgb2num(ambient_light) + ambient_r = ((parts[1] / 255) * ambient_light_multiplier) - ambient_light_old_r + ambient_g = ((parts[2] / 255) * ambient_light_multiplier) - ambient_light_old_g + ambient_b = ((parts[3] / 255) * ambient_light_multiplier) - ambient_light_old_b + else + ambient_r = -ambient_light_old_r + ambient_g = -ambient_light_old_g + ambient_b = -ambient_light_old_b - ambient_light_old = ambient_light + add_ambient_light_raw(ambient_r, ambient_g, ambient_b, !no_corner_update) -/// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. +// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() var/datum/light_source/L for (var/thing in affecting_lights) L = thing L.vis_update() -/// Forces a lighting update. Reconsider lights is preferred when possible. +// Forces a lighting update. Reconsider lights is preferred when possible. /turf/proc/force_update_lights() var/datum/light_source/L for (var/thing in affecting_lights) @@ -137,15 +186,15 @@ lum_g += L.apparent_g lum_b += L.apparent_b - lum_r = CLAMP01(lum_r / length(corners)) * 255 - lum_g = CLAMP01(lum_g / length(corners)) * 255 - lum_b = CLAMP01(lum_b / length(corners)) * 255 + lum_r = CLAMP01(lum_r / 4) * 255 + lum_g = CLAMP01(lum_g / 4) * 255 + lum_b = CLAMP01(lum_b / 4) * 255 return rgb(lum_r, lum_g, lum_b) #define SCALE(targ,min,max) (targ - min) / (max - min) -/// Returns a lumcount (average intensity of color channels) scaled between minlum and maxlum. +// Used to get a scaled lumcount. /turf/proc/get_lumcount(minlum = 0, maxlum = 1) if (!lighting_overlay) return 0.5 @@ -162,7 +211,7 @@ #undef SCALE -/// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. +// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. /turf/proc/recalc_atom_opacity() #ifdef AO_USE_LIGHTING_OPACITY var/old = has_opaque_atom From 9c61b045b1af873d8090b354f33c6d6bc4990edf Mon Sep 17 00:00:00 2001 From: Lohikar Date: Thu, 8 May 2025 15:56:36 -0500 Subject: [PATCH 0644/1331] cascade: Commit crimes against performance to fix compile --- mods/content/supermatter/endgame_cascade/universe.dm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mods/content/supermatter/endgame_cascade/universe.dm b/mods/content/supermatter/endgame_cascade/universe.dm index ccca16707e43..46eb1efd5bf4 100644 --- a/mods/content/supermatter/endgame_cascade/universe.dm +++ b/mods/content/supermatter/endgame_cascade/universe.dm @@ -70,14 +70,18 @@ // TODO: Should this be changed to use the actual ambient lights system...? /datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() spawn(0) - for(var/datum/lighting_corner/L in SSlighting.lighting_corners) + // TODO: dear god anything but this + for(var/datum/lighting_corner/L) if(isAdminLevel(L.z)) L.update_lumcount(1,1,1) else L.update_lumcount(0.0, 0.4, 1) + CHECK_TICK + for(var/turf/space/T) OnTurfChange(T) + CHECK_TICK /datum/universal_state/supermatter_cascade/proc/MiscSet() for (var/obj/machinery/firealarm/alm in SSmachines.machinery) From bb010eda07d3f99cf4947a4d3fa24e7fed64a782 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Thu, 8 May 2025 16:03:24 -0500 Subject: [PATCH 0645/1331] lighting: Move ambience into own file --- code/modules/lighting/ambient_turf.dm | 118 +++++++++++++++++++++++++ code/modules/lighting/lighting_turf.dm | 116 ------------------------ nebula.dme | 1 + 3 files changed, 119 insertions(+), 116 deletions(-) create mode 100644 code/modules/lighting/ambient_turf.dm diff --git a/code/modules/lighting/ambient_turf.dm b/code/modules/lighting/ambient_turf.dm new file mode 100644 index 000000000000..5cce40eec333 --- /dev/null +++ b/code/modules/lighting/ambient_turf.dm @@ -0,0 +1,118 @@ +/turf + /// If non-null, a hex RGB light color that should be applied to this turf. + var/ambient_light + /// The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. + var/ambient_light_multiplier = 0.3 + + /// If this is TRUE, an above turf's ambient light is affecting this turf. + var/tmp/ambient_has_indirect = FALSE + + // Record-keeping, do not touch -- that means you, admins. + var/tmp/ambient_active = FALSE //! Do we have non-zero ambient light? Use [TURF_IS_AMBIENT_LIT] instead of reading this directly. + var/tmp/ambient_light_old_r = 0 + var/tmp/ambient_light_old_g = 0 + var/tmp/ambient_light_old_b = 0 + +/turf/proc/set_ambient_light(color, multiplier) + if (color == ambient_light && multiplier == ambient_light_multiplier) + return + + ambient_light = isnull(color) ? ambient_light : color + ambient_light_multiplier = isnull(multiplier) ? ambient_light_multiplier : multiplier + + update_ambient_light() + +/turf/proc/replace_ambient_light(old_color, new_color, old_multiplier, new_multiplier = 0) + if (!TURF_IS_AMBIENT_LIT_UNSAFE(src)) + add_ambient_light(new_color, new_multiplier) + return + + ASSERT(!isnull(old_multiplier)) // omitting new_multiplier is allowed for removing light nondestructively + + old_color ||= COLOR_WHITE + new_color ||= COLOR_WHITE + + var/list/old_parts = rgb2num(old_color) + var/list/new_parts = rgb2num(new_color) + + var/dr = (new_parts[1] / 255) * new_multiplier - (old_parts[1] / 255) * old_multiplier + var/dg = (new_parts[2] / 255) * new_multiplier - (old_parts[2] / 255) * old_multiplier + var/db = (new_parts[3] / 255) * new_multiplier - (old_parts[3] / 255) * old_multiplier + + if (!dr && !dg && !db) + return + + add_ambient_light_raw(dr, dg, db) + +/turf/proc/add_ambient_light(color, multiplier, update = TRUE) + if (!color) + return + + multiplier ||= ambient_light_multiplier + + var/list/ambient_parts = rgb2num(color) + + var/ambient_r = (ambient_parts[1] / 255) * multiplier + var/ambient_g = (ambient_parts[2] / 255) * multiplier + var/ambient_b = (ambient_parts[3] / 255) * multiplier + + add_ambient_light_raw(ambient_r, ambient_g, ambient_b, update) + +/turf/proc/add_ambient_light_raw(lr, lg, lb, update = TRUE) + if (!lr && !lg && !lb) + if (!ambient_light_old_r || !ambient_light_old_g || !ambient_light_old_b) + ambient_active = FALSE + SSlighting.total_ambient_turfs -= 1 + return + + if (!ambient_active) + SSlighting.total_ambient_turfs += 1 + ambient_active = TRUE + + // There are four corners per (lit) turf, we don't want to apply our light 4 times -- compensate by dividing by 4. + lr /= 4 + lg /= 4 + lb /= 4 + + lr = round(lr, LIGHTING_ROUND_VALUE) + lg = round(lg, LIGHTING_ROUND_VALUE) + lb = round(lb, LIGHTING_ROUND_VALUE) + + ambient_light_old_r += lr + ambient_light_old_g += lg + ambient_light_old_b += lb + + if (!corners || !lighting_corners_initialised) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) + generate_missing_corners() + else + return + + // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. + for (var/datum/lighting_corner/C in corners) + C.update_ambient_lumcount(lr, lg, lb, !update) + +/turf/proc/clear_ambient_light() + if (ambient_light == null) + return + + ambient_light = null + update_ambient_light() + +/turf/proc/update_ambient_light(no_corner_update = FALSE) + // These are deltas. + var/ambient_r = 0 + var/ambient_g = 0 + var/ambient_b = 0 + + if (ambient_light) + var/list/parts = rgb2num(ambient_light) + ambient_r = ((parts[1] / 255) * ambient_light_multiplier) - ambient_light_old_r + ambient_g = ((parts[2] / 255) * ambient_light_multiplier) - ambient_light_old_g + ambient_b = ((parts[3] / 255) * ambient_light_multiplier) - ambient_light_old_b + else + ambient_r = -ambient_light_old_r + ambient_g = -ambient_light_old_g + ambient_b = -ambient_light_old_b + + add_ambient_light_raw(ambient_r, ambient_g, ambient_b, !no_corner_update) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 9af3682e4e85..e35022b38f89 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,9 +1,5 @@ /turf var/dynamic_lighting = TRUE - /// If non-null, a hex RGB light color that should be applied to this turf. - var/ambient_light - /// The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. - var/ambient_light_multiplier = 0.3 luminosity = 1 var/tmp/lighting_corners_initialised = FALSE @@ -15,118 +11,6 @@ var/tmp/list/datum/lighting_corner/corners /// Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. var/tmp/has_opaque_atom = FALSE - /// If this is TRUE, an above turf's ambient light is affecting this turf. - var/tmp/ambient_has_indirect = FALSE - - // Record-keeping, do not touch -- that means you, admins. - var/tmp/ambient_active = FALSE //! Do we have non-zero ambient light? Use [TURF_IS_AMBIENT_LIT] instead of reading this directly. - var/tmp/ambient_light_old_r = 0 - var/tmp/ambient_light_old_g = 0 - var/tmp/ambient_light_old_b = 0 - -/turf/proc/set_ambient_light(color, multiplier) - if (color == ambient_light && multiplier == ambient_light_multiplier) - return - - ambient_light = isnull(color) ? ambient_light : color - ambient_light_multiplier = isnull(multiplier) ? ambient_light_multiplier : multiplier - - update_ambient_light() - -/turf/proc/replace_ambient_light(old_color, new_color, old_multiplier, new_multiplier = 0) - if (!TURF_IS_AMBIENT_LIT_UNSAFE(src)) - add_ambient_light(new_color, new_multiplier) - return - - ASSERT(!isnull(old_multiplier)) // omitting new_multiplier is allowed for removing light nondestructively - - old_color ||= COLOR_WHITE - new_color ||= COLOR_WHITE - - var/list/old_parts = rgb2num(old_color) - var/list/new_parts = rgb2num(new_color) - - var/dr = (new_parts[1] / 255) * new_multiplier - (old_parts[1] / 255) * old_multiplier - var/dg = (new_parts[2] / 255) * new_multiplier - (old_parts[2] / 255) * old_multiplier - var/db = (new_parts[3] / 255) * new_multiplier - (old_parts[3] / 255) * old_multiplier - - if (!dr && !dg && !db) - return - - add_ambient_light_raw(dr, dg, db) - -/turf/proc/add_ambient_light(color, multiplier, update = TRUE) - if (!color) - return - - multiplier ||= ambient_light_multiplier - - var/list/ambient_parts = rgb2num(color) - - var/ambient_r = (ambient_parts[1] / 255) * multiplier - var/ambient_g = (ambient_parts[2] / 255) * multiplier - var/ambient_b = (ambient_parts[3] / 255) * multiplier - - add_ambient_light_raw(ambient_r, ambient_g, ambient_b, update) - -/turf/proc/add_ambient_light_raw(lr, lg, lb, update = TRUE) - if (!lr && !lg && !lb) - if (!ambient_light_old_r || !ambient_light_old_g || !ambient_light_old_b) - ambient_active = FALSE - SSlighting.total_ambient_turfs -= 1 - return - - if (!ambient_active) - SSlighting.total_ambient_turfs += 1 - ambient_active = TRUE - - // There are four corners per (lit) turf, we don't want to apply our light 4 times -- compensate by dividing by 4. - lr /= 4 - lg /= 4 - lb /= 4 - - lr = round(lr, LIGHTING_ROUND_VALUE) - lg = round(lg, LIGHTING_ROUND_VALUE) - lb = round(lb, LIGHTING_ROUND_VALUE) - - ambient_light_old_r += lr - ambient_light_old_g += lg - ambient_light_old_b += lb - - if (!corners || !lighting_corners_initialised) - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) - generate_missing_corners() - else - return - - // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. - for (var/datum/lighting_corner/C in corners) - C.update_ambient_lumcount(lr, lg, lb, !update) - -/turf/proc/clear_ambient_light() - if (ambient_light == null) - return - - ambient_light = null - update_ambient_light() - -/turf/proc/update_ambient_light(no_corner_update = FALSE) - // These are deltas. - var/ambient_r = 0 - var/ambient_g = 0 - var/ambient_b = 0 - - if (ambient_light) - var/list/parts = rgb2num(ambient_light) - ambient_r = ((parts[1] / 255) * ambient_light_multiplier) - ambient_light_old_r - ambient_g = ((parts[2] / 255) * ambient_light_multiplier) - ambient_light_old_g - ambient_b = ((parts[3] / 255) * ambient_light_multiplier) - ambient_light_old_b - else - ambient_r = -ambient_light_old_r - ambient_g = -ambient_light_old_g - ambient_b = -ambient_light_old_b - - add_ambient_light_raw(ambient_r, ambient_g, ambient_b, !no_corner_update) // Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() diff --git a/nebula.dme b/nebula.dme index 25a61fa32422..59beea9b2c7b 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2680,6 +2680,7 @@ #include "code\modules\keybindings\movement.dm" #include "code\modules\keybindings\setup.dm" #include "code\modules\lighting\_lighting_defs.dm" +#include "code\modules\lighting\ambient_turf.dm" #include "code\modules\lighting\lighting_area.dm" #include "code\modules\lighting\lighting_atom.dm" #include "code\modules\lighting\lighting_corner.dm" From 58753c56043ae2d06c979b4dd478e5690c019374 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Thu, 8 May 2025 16:05:06 -0500 Subject: [PATCH 0646/1331] lighting: Ambience groups --- code/modules/lighting/ambient_group.dm | 121 +++++++++++++++++++++++++ nebula.dme | 1 + 2 files changed, 122 insertions(+) create mode 100644 code/modules/lighting/ambient_group.dm diff --git a/code/modules/lighting/ambient_group.dm b/code/modules/lighting/ambient_group.dm new file mode 100644 index 000000000000..b7a88dfa9b12 --- /dev/null +++ b/code/modules/lighting/ambient_group.dm @@ -0,0 +1,121 @@ +#define BITWISE_MAX_BITS 24 + +/// A bitmap of free ambience group indexes. +var/ambience_group_free_bitmap = ~0 +var/ambience_group_map[BITWISE_MAX_BITS] + +/datum/ambience_group + var/global_index + var/list/member_turfs_by_z = list() + var/apparent_r + var/apparent_g + var/apparent_b + var/invalid = FALSE + var/busy = FALSE + +/datum/ambience_group/New() + global_index = allocate_index() + if (!global_index) + invalid = TRUE + return + + ambience_group_map[global_index] = src + +/datum/ambience_group/Destroy() + if (!invalid) + ambience_group_map[global_index] = null + ambience_group_free_bitmap |= (1 << global_index) + return ..() + +/datum/ambience_group/proc/allocate_index() + if (ambience_group_free_bitmap == 0) + CRASH("Failed to allocate ambience_group: index bitmap is exhausted") + + // Find the first free index in the bitmap. + var/index = 1 + while (!(ambience_group_free_bitmap & (1 << index)) && index < BITWISE_MAX_BITS) + index += 1 + + ambience_group_free_bitmap &= ~(1 << index) + + return index + +/datum/ambience_group/proc/add_turf(turf/T) + set waitfor = FALSE + + UNTIL(!busy) + if (T.z > member_turfs_by_z) + member_turfs_by_z.len = T.z + + LAZYADD(member_turfs_by_z[T.z], T) + T.add_ambient_light_raw(apparent_r, apparent_g, apparent_b) + T.ambience_active_groups += 1 + +/datum/ambience_group/proc/remove_turf(turf/T) + set waitfor = FALSE + + UNTIL(!busy) + if (T.z > member_turfs_by_z.len) + CRASH("Attempt to remove member turf with Z greater than local max -- this turf is not a member") + T.add_ambient_light_raw(-apparent_r, -apparent_g, -apparent_b) + member_turfs_by_z[T.z] -= T + T.ambience_active_groups -= 1 + +/datum/ambience_group/proc/set_ambient_light(color, multiplier) + var/list/new_parts = rgb2num(color) + + var/dr = (new_parts[1] / 255) * multiplier - apparent_r + var/dg = (new_parts[2] / 255) * multiplier - apparent_g + var/db = (new_parts[3] / 255) * multiplier - apparent_b + + if (round(dr/4, LIGHTING_ROUND_VALUE) == 0 && round(dg/4, LIGHTING_ROUND_VALUE) == 0 && round(db/4, LIGHTING_ROUND_VALUE) == 0) + // no-op + return + + busy = TRUE + + // Doing it ordered by zlev should ensure that it looks vaguely coherent mid-update regardless of turf insertion order. + for (var/zlev in 1 to member_turfs_by_z.len) + for (var/turf/T as anything in member_turfs_by_z[zlev]) + T.add_ambient_light_raw(dr, dg, db) + CHECK_TICK + + apparent_r += dr + apparent_g += dg + apparent_b += db + + busy = FALSE + +/turf + /// A bitfield of which global ambience groups are affecting this turf. + var/tmp/ambience_affecting_bitmap = 0 + /// A counter of how many ambience groups are affecting this turf, used to avoid pointlessly iterating the entire 24-bit bitfield. + var/tmp/ambience_active_groups = 0 + +/turf/Destroy() + if (ambience_affecting_bitmap) + var/remaining_groups = ambience_active_groups + for (var/i in 1 to BITWISE_MAX_BITS) + if (ambience_affecting_bitmap & (1 << i)) + var/datum/ambience_group/group = ambience_group_map[i] + add_ambient_light_raw(-group.apparent_r, -group.apparent_g, -group.apparent_b) + + remaining_groups -= 1 + + if (!remaining_groups) + break + + return ..() + +/turf/Initialize() + . = ..() + if (ambience_affecting_bitmap) + var/remaining_groups = ambience_active_groups + for (var/i in 1 to BITWISE_MAX_BITS) + if (ambience_affecting_bitmap & (1 << i)) + var/datum/ambience_group/group = ambience_group_map[i] + add_ambient_light_raw(group.apparent_r, group.apparent_g, group.apparent_b) + remaining_groups -= 1 + + if (!remaining_groups) + break diff --git a/nebula.dme b/nebula.dme index 59beea9b2c7b..8a77c79025d6 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2680,6 +2680,7 @@ #include "code\modules\keybindings\movement.dm" #include "code\modules\keybindings\setup.dm" #include "code\modules\lighting\_lighting_defs.dm" +#include "code\modules\lighting\ambient_group.dm" #include "code\modules\lighting\ambient_turf.dm" #include "code\modules\lighting\lighting_area.dm" #include "code\modules\lighting\lighting_atom.dm" From 445569388d9dce0623d6f2f82f3be72e8da6ddec Mon Sep 17 00:00:00 2001 From: Lohikar Date: Thu, 8 May 2025 16:19:01 -0500 Subject: [PATCH 0647/1331] lighting: Fix some accidentally reverted doc comments --- code/modules/lighting/lighting_turf.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index e35022b38f89..724b2d46182e 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -12,14 +12,14 @@ /// Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. var/tmp/has_opaque_atom = FALSE -// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. +/// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() var/datum/light_source/L for (var/thing in affecting_lights) L = thing L.vis_update() -// Forces a lighting update. Reconsider lights is preferred when possible. +/// Forces a lighting update. Reconsider lights is preferred when possible. /turf/proc/force_update_lights() var/datum/light_source/L for (var/thing in affecting_lights) @@ -78,7 +78,7 @@ #define SCALE(targ,min,max) (targ - min) / (max - min) -// Used to get a scaled lumcount. +/// Returns a lumcount (average intensity of color channels) scaled between minlum and maxlum. /turf/proc/get_lumcount(minlum = 0, maxlum = 1) if (!lighting_overlay) return 0.5 @@ -95,7 +95,7 @@ #undef SCALE -// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. +/// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. /turf/proc/recalc_atom_opacity() #ifdef AO_USE_LIGHTING_OPACITY var/old = has_opaque_atom From 0e2014fd145742be67eefbf73b2109b972709f7a Mon Sep 17 00:00:00 2001 From: Lohikar Date: Thu, 8 May 2025 16:19:32 -0500 Subject: [PATCH 0648/1331] ambience: Docs, fix logic error in clear_ambient_light() --- code/modules/lighting/ambient_turf.dm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/modules/lighting/ambient_turf.dm b/code/modules/lighting/ambient_turf.dm index 5cce40eec333..5b9ba5a03d9a 100644 --- a/code/modules/lighting/ambient_turf.dm +++ b/code/modules/lighting/ambient_turf.dm @@ -13,6 +13,7 @@ var/tmp/ambient_light_old_g = 0 var/tmp/ambient_light_old_b = 0 +/// Set the turf's self-ambience channel. This can only contain one value at a time, so it should generally only be used by the turf itself. /turf/proc/set_ambient_light(color, multiplier) if (color == ambient_light && multiplier == ambient_light_multiplier) return @@ -22,6 +23,7 @@ update_ambient_light() +/// Replace one ambient light with another. This is effectively a delta update, but it can be used to pretend that our one channel is doing color blending. /turf/proc/replace_ambient_light(old_color, new_color, old_multiplier, new_multiplier = 0) if (!TURF_IS_AMBIENT_LIT_UNSAFE(src)) add_ambient_light(new_color, new_multiplier) @@ -44,6 +46,7 @@ add_ambient_light_raw(dr, dg, db) +/// Add an ambient light to the turf's self-channel. This is a delta update, retain your applied color if you want to be able to remove it later. /turf/proc/add_ambient_light(color, multiplier, update = TRUE) if (!color) return @@ -58,6 +61,7 @@ add_ambient_light_raw(ambient_r, ambient_g, ambient_b, update) +/// Directly manipulate the state of the self-ambience channel. Don't use unless you know what you're doing and how ambience works internally. /turf/proc/add_ambient_light_raw(lr, lg, lb, update = TRUE) if (!lr && !lg && !lb) if (!ambient_light_old_r || !ambient_light_old_g || !ambient_light_old_b) @@ -92,12 +96,14 @@ for (var/datum/lighting_corner/C in corners) C.update_ambient_lumcount(lr, lg, lb, !update) +/// Wipe the entire self-ambience channel. This will preserve ambience from ambience groups. /turf/proc/clear_ambient_light() if (ambient_light == null) return + replace_ambient_light(ambient_light, COLOR_WHITE, ambient_light_multiplier, 0) ambient_light = null - update_ambient_light() + ambient_light_multiplier = initial(ambient_light_multiplier) /turf/proc/update_ambient_light(no_corner_update = FALSE) // These are deltas. From c03a275d3fb90ff65e2cc9014fd4c8acc1c3ddb9 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Thu, 8 May 2025 16:48:39 -0500 Subject: [PATCH 0649/1331] Jiggle bitshift count --- test/check-paths.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/check-paths.sh b/test/check-paths.sh index 8984b116442e..5643ae1afa53 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -34,7 +34,7 @@ exactly 10 "/turf text paths" '"/turf' exactly 1 "world<< uses" 'world\s*<<' exactly 75 "'in world' uses" '\s+\bin world\b(?=\s*$|\s*//|\s*\))' -P exactly 1 "world.log<< uses" 'world.log\s*<<' -exactly 18 "<< uses" '(?> uses" '(?\\])>>(?!>)' -P exactly 0 "incorrect indentations" '^( {4,})' -P From 104e2a780860ee58a808adf5c3cce6d6f2226ee7 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Thu, 8 May 2025 16:57:41 -0500 Subject: [PATCH 0650/1331] ambience: Properly mark globals --- code/modules/lighting/ambient_group.dm | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/code/modules/lighting/ambient_group.dm b/code/modules/lighting/ambient_group.dm index b7a88dfa9b12..009565a16fdf 100644 --- a/code/modules/lighting/ambient_group.dm +++ b/code/modules/lighting/ambient_group.dm @@ -1,8 +1,8 @@ #define BITWISE_MAX_BITS 24 /// A bitmap of free ambience group indexes. -var/ambience_group_free_bitmap = ~0 -var/ambience_group_map[BITWISE_MAX_BITS] +var/global/ambience_group_free_bitmap = ~0 +var/global/ambience_group_map[BITWISE_MAX_BITS] /datum/ambience_group var/global_index @@ -19,24 +19,24 @@ var/ambience_group_map[BITWISE_MAX_BITS] invalid = TRUE return - ambience_group_map[global_index] = src + global.ambience_group_map[global_index] = src /datum/ambience_group/Destroy() if (!invalid) - ambience_group_map[global_index] = null - ambience_group_free_bitmap |= (1 << global_index) + global.ambience_group_map[global_index] = null + global.ambience_group_free_bitmap |= (1 << global_index) return ..() /datum/ambience_group/proc/allocate_index() - if (ambience_group_free_bitmap == 0) + if (global.ambience_group_free_bitmap == 0) CRASH("Failed to allocate ambience_group: index bitmap is exhausted") // Find the first free index in the bitmap. var/index = 1 - while (!(ambience_group_free_bitmap & (1 << index)) && index < BITWISE_MAX_BITS) + while (!(global.ambience_group_free_bitmap & (1 << index)) && index < BITWISE_MAX_BITS) index += 1 - ambience_group_free_bitmap &= ~(1 << index) + global.ambience_group_free_bitmap &= ~(1 << index) return index @@ -97,7 +97,7 @@ var/ambience_group_map[BITWISE_MAX_BITS] var/remaining_groups = ambience_active_groups for (var/i in 1 to BITWISE_MAX_BITS) if (ambience_affecting_bitmap & (1 << i)) - var/datum/ambience_group/group = ambience_group_map[i] + var/datum/ambience_group/group = global.ambience_group_map[i] add_ambient_light_raw(-group.apparent_r, -group.apparent_g, -group.apparent_b) remaining_groups -= 1 @@ -113,7 +113,7 @@ var/ambience_group_map[BITWISE_MAX_BITS] var/remaining_groups = ambience_active_groups for (var/i in 1 to BITWISE_MAX_BITS) if (ambience_affecting_bitmap & (1 << i)) - var/datum/ambience_group/group = ambience_group_map[i] + var/datum/ambience_group/group = global.ambience_group_map[i] add_ambient_light_raw(group.apparent_r, group.apparent_g, group.apparent_b) remaining_groups -= 1 From 88745c8f1e54a05914aa867960f94ed971b3cf77 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 16 May 2025 00:58:28 +0000 Subject: [PATCH 0651/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index b65f492394dc..fa25dd37d04d 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -90,12 +90,6 @@

    Penelope Haze updated:

    • Adjusts how suffocation works, meaning characters who were drowning or in asystole start breathing as soon as the issue is resolved, rather than counting down a timer instead. This might make brief bouts of drowning/cardiac arrest more survivable. Please report any weirdness with breathing/suffocation on the issue tracker.
    - -

    14 March 2025

    -

    Typhin updated:

    -
      -
    • Allows parts/screwdrivers to be used on biogenerators
    • -
    From 1f7637f6697c56d92d38ab6f6b53f9675b336f5d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 15 May 2025 00:30:44 -0400 Subject: [PATCH 0652/1331] Fix being unable to D-notice newscaster channels --- code/game/machinery/newscaster.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 50533238ce2a..1152b6d5dc03 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -711,7 +711,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to else if(href_list["pick_censor_channel"]) var/datum/feed_channel/FC = locate(href_list["pick_censor_channel"]) viewing_channel = FC - screen = SCREEN_PICK_CENSOR_CHANNEL + screen = SCREEN_PICK_CENSOR_STORY . = TOPIC_REFRESH else if(href_list["refresh"]) From 9be21462cd60c78260ff960ee9b1ee4459aa36dd Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 13 May 2025 19:28:02 -0400 Subject: [PATCH 0653/1331] Move and do a quality pass on tape recorder code --- .../devices/tape_recorder/magnetic_tape.dm | 176 ++++++ .../devices/tape_recorder/tape_recorder.dm | 360 ++++++++++++ .../tape_recorder/taperecorder_wires.dm} | 3 +- .../objects/items/devices/taperecorder.dm | 540 ------------------ nebula.dme | 5 +- 5 files changed, 540 insertions(+), 544 deletions(-) create mode 100644 code/game/objects/items/devices/tape_recorder/magnetic_tape.dm create mode 100644 code/game/objects/items/devices/tape_recorder/tape_recorder.dm rename code/{datums/wires/taperecorder.dm => game/objects/items/devices/tape_recorder/taperecorder_wires.dm} (90%) delete mode 100644 code/game/objects/items/devices/taperecorder.dm diff --git a/code/game/objects/items/devices/tape_recorder/magnetic_tape.dm b/code/game/objects/items/devices/tape_recorder/magnetic_tape.dm new file mode 100644 index 000000000000..26d5cdf79b36 --- /dev/null +++ b/code/game/objects/items/devices/tape_recorder/magnetic_tape.dm @@ -0,0 +1,176 @@ +/obj/item/magnetic_tape + name = "tape" + desc = "A magnetic tape that can hold up to ten minutes of content." + icon = 'icons/obj/items/device/tape_recorder/tape_casette_white.dmi' + icon_state = ICON_STATE_WORLD + w_class = ITEM_SIZE_SMALL + material = /decl/material/solid/organic/plastic + matter = list( + /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/fiberglass = MATTER_AMOUNT_TRACE + ) + _base_attack_force = 1 + var/max_capacity = 600 + var/used_capacity = 0 + var/list/storedinfo = new/list() + var/list/timestamp = new/list() + var/ruined = FALSE + var/doctored = FALSE + /// Whether we draw the ruined ribbon overlay when ruined. + var/draw_ribbon_if_ruined = TRUE + +/obj/item/magnetic_tape/on_update_icon() + . = ..() + icon_state = get_world_inventory_state() + if(draw_ribbon_if_ruined && ruined && max_capacity) + add_overlay(overlay_image(icon, "[icon_state]_ribbonoverlay", flags = RESET_COLOR)) + +/obj/item/magnetic_tape/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ruin() + return ..() + +/obj/item/magnetic_tape/attack_self(mob/user) + if(!ruined) + to_chat(user, SPAN_NOTICE("You pull out all the tape!")) + get_loose_tape(user, length(storedinfo)) + ruin() + + +/obj/item/magnetic_tape/proc/ruin() + ruined = TRUE + update_icon() + + +/obj/item/magnetic_tape/proc/fix() + ruined = FALSE + update_icon() + + +/obj/item/magnetic_tape/proc/record_speech(text) + timestamp += used_capacity + storedinfo += "\[[time2text(used_capacity SECONDS,"mm:ss")]\] [text]" + + +//shows up on the printed transcript as (Unrecognized sound) +/obj/item/magnetic_tape/proc/record_noise(text) + timestamp += used_capacity + storedinfo += "*\[[time2text(used_capacity SECONDS,"mm:ss")]\] [text]" + + +/obj/item/magnetic_tape/attackby(obj/item/used_item, mob/user, params) + if(user.incapacitated()) // TODO: this may not be necessary since OnClick checks before starting the attack chain + return TRUE + if(ruined && IS_SCREWDRIVER(used_item)) + if(!max_capacity) + to_chat(user, SPAN_NOTICE("There is no tape left inside.")) + return TRUE + to_chat(user, SPAN_NOTICE("You start winding the tape back in...")) + if(do_after(user, 12 SECONDS, target = src)) + to_chat(user, SPAN_NOTICE("You wound the tape back in.")) + fix() + return TRUE + else if(IS_PEN(used_item)) + if(loc == user) + var/new_name = input(user, "What would you like to label the tape?", "Tape labeling") as null|text + if(isnull(new_name)) return TRUE + new_name = sanitize_safe(new_name) + if(new_name) + SetName("tape - '[new_name]'") + to_chat(user, SPAN_NOTICE("You label the tape '[new_name]'.")) + else + SetName("tape") + to_chat(user, SPAN_NOTICE("You scratch off the label.")) + return TRUE + else if(IS_WIRECUTTER(used_item)) + cut(user) + return TRUE + else if(istype(used_item, /obj/item/magnetic_tape/loose)) + join(user, used_item) + return TRUE + return ..() + +/obj/item/magnetic_tape/proc/cut(mob/user) + if(!LAZYLEN(timestamp)) + to_chat(user, SPAN_NOTICE("There's nothing on this tape!")) + return + var/list/output = list("
    " + + var/datum/browser/popup = new(user, "tape_cutting", "Cutting tape", 170, 600) + popup.set_content(jointext(output,null)) + popup.open() + +/obj/item/magnetic_tape/proc/join(mob/user, obj/item/magnetic_tape/other) + if(max_capacity + other.max_capacity > initial(max_capacity)) + to_chat(user, SPAN_NOTICE("You can't fit this much tape in!")) + return + if(user.try_unequip(other)) + to_chat(user, SPAN_NOTICE("You join ends of the tape together.")) + max_capacity += other.max_capacity + used_capacity = min(used_capacity + other.used_capacity, max_capacity) + timestamp += other.timestamp + storedinfo += other.storedinfo + doctored = TRUE + ruin() + update_icon() + qdel(other) + +/obj/item/magnetic_tape/OnTopic(var/mob/user, var/list/href_list) + if(href_list["cut_after"]) + var/index = text2num(href_list["cut_after"]) + if(index >= length(timestamp)) + return + + to_chat(user, SPAN_NOTICE("You remove part of the tape.")) + get_loose_tape(user, index) + cut(user) + return TOPIC_REFRESH + +//Spawns new loose tape item, with data starting from [index] entry +/obj/item/magnetic_tape/proc/get_loose_tape(var/mob/user, var/index) + var/obj/item/magnetic_tape/loose/newtape = new() + newtape.timestamp = timestamp.Copy(index+1) + newtape.storedinfo = storedinfo.Copy(index+1) + newtape.max_capacity = max_capacity - index + newtape.used_capacity = max(0, used_capacity - max_capacity) + newtape.doctored = doctored + user.put_in_hands(newtape) + + timestamp.Cut(index+1) + storedinfo.Cut(index+1) + max_capacity = index + used_capacity = min(used_capacity,index) + +//Random colour tapes +/obj/item/magnetic_tape/random/Initialize() + icon = pick(list( + 'icons/obj/items/device/tape_recorder/tape_casette_white.dmi', + 'icons/obj/items/device/tape_recorder/tape_casette_blue.dmi', + 'icons/obj/items/device/tape_recorder/tape_casette_red.dmi', + 'icons/obj/items/device/tape_recorder/tape_casette_yellow.dmi', + 'icons/obj/items/device/tape_recorder/tape_casette_purple.dmi' + )) + . = ..() + +/obj/item/magnetic_tape/loose + name = "magnetic tape" + desc = "Quantum-enriched self-repairing nanotape, used for magnetic storage of information." + icon = 'icons/obj/items/device/tape_recorder/tape_casette_loose.dmi' + ruined = TRUE + draw_ribbon_if_ruined = FALSE + +/obj/item/magnetic_tape/loose/fix() + return + +/obj/item/magnetic_tape/loose/get_loose_tape() + return + +/obj/item/magnetic_tape/loose/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(distance <= 1) + . += SPAN_NOTICE("It looks long enough to hold [max_capacity] seconds worth of recording.") + if(doctored && user.skill_check(SKILL_FORENSICS, SKILL_PROF)) + . += SPAN_WARNING("It has been tampered with...") diff --git a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm new file mode 100644 index 000000000000..7677f30a1dc0 --- /dev/null +++ b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm @@ -0,0 +1,360 @@ +/obj/item/taperecorder + name = "universal recorder" + desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." + icon = 'icons/obj/items/device/tape_recorder/tape_recorder.dmi' + icon_state = ICON_STATE_WORLD + w_class = ITEM_SIZE_SMALL + material = /decl/material/solid/metal/aluminium + matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) + obj_flags = OBJ_FLAG_CONDUCTIBLE + slot_flags = SLOT_LOWER_BODY + throw_speed = 4 + throw_range = 20 + + var/emagged = FALSE + var/recording = FALSE + var/playing = FALSE + var/playsleepseconds = 0 + var/obj/item/magnetic_tape/mytape = /obj/item/magnetic_tape/random + var/const/TRANSCRIPT_PRINT_COOLDOWN = 5 MINUTES + /// (FLOAT) The minimum world.time before a transcript can be printed again. + var/next_print_time = 0 + var/datum/wires/taperecorder/wires = null // Wires datum + /// (BOOL) If TRUE, wire datum is accessible for interactions. + var/wires_accessible = FALSE + +/obj/item/taperecorder/Initialize() + . = ..() + wires = new(src) + if(ispath(mytape)) + mytape = new mytape(src) + global.listening_objects += src + update_icon() + +/obj/item/taperecorder/empty + mytape = null + +/obj/item/taperecorder/Destroy() + QDEL_NULL(wires) + QDEL_NULL(mytape) + return ..() + +/obj/item/taperecorder/attackby(obj/item/used_item, mob/user, params) + if(IS_SCREWDRIVER(used_item)) + wires_accessible = !wires_accessible + to_chat(user, SPAN_NOTICE("You [wires_accessible ? "open" : "secure"] the lid.")) + return TRUE + if(istype(used_item, /obj/item/magnetic_tape)) + if(mytape) + to_chat(user, SPAN_NOTICE("There's already a tape inside.")) + return TRUE + if(!user.try_unequip(used_item)) + return TRUE + used_item.forceMove(src) + mytape = used_item + to_chat(user, SPAN_NOTICE("You insert [used_item] into [src].")) + update_icon() + return TRUE + return ..() + + +/obj/item/taperecorder/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(mytape) + mytape.ruin() //Fires destroy the tape + return ..() + + +/obj/item/taperecorder/attack_hand(mob/user) + if(user.is_holding_offhand(src) && mytape && user.check_dexterity(DEXTERITY_SIMPLE_MACHINES)) + eject() + return TRUE + return ..() + + +/obj/item/taperecorder/verb/eject() + set name = "Eject Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, SPAN_NOTICE("There's no tape in \the [src].")) + return + if(emagged) + to_chat(usr, SPAN_NOTICE("The tape seems to be stuck inside.")) + return + + if(playing || recording) + stop() + to_chat(usr, SPAN_NOTICE("You remove [mytape] from [src].")) + usr.put_in_hands(mytape) + mytape = null + update_icon() + +/obj/item/taperecorder/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(distance <= 1 && wires_accessible) + . += SPAN_NOTICE("The wires are exposed.") + +/obj/item/taperecorder/hear_talk(mob/living/M, msg, var/verb="says", decl/language/speaking=null) + if(mytape && recording) + + if(speaking) + if(!speaking.machine_understands) + msg = speaking.scramble(M, msg) + mytape.record_speech("[M.name] [speaking.format_message_plain(msg, verb)]") + else + mytape.record_speech("[M.name] [verb], \"[msg]\"") + +/obj/item/taperecorder/show_message(msg, type, alt, alt_type) + var/recordedtext + if (msg && type == AUDIBLE_MESSAGE) //must be hearable + recordedtext = msg + else if (alt && alt_type == AUDIBLE_MESSAGE) + recordedtext = alt + else + return + if(mytape && recording) + mytape.record_noise("[strip_html_properly(recordedtext)]") + +/obj/item/taperecorder/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + emagged = TRUE + recording = FALSE + to_chat(user, SPAN_WARNING("PZZTTPFFFT")) + update_icon() + return 1 + else + to_chat(user, SPAN_WARNING("It is already emagged!")) + +/obj/item/taperecorder/proc/explode() + var/turf/T = get_turf(loc) + if(ismob(loc)) + var/mob/M = loc + to_chat(M, SPAN_DANGER("\The [src] explodes!")) + if(T) + T.hotspot_expose(700,125) + explosion(T, -1, -1, 0, 4) + qdel(src) + return + +/obj/item/taperecorder/verb/record() + set name = "Start Recording" + set category = "Object" + + if(usr.incapacitated()) + return + playsound(src, 'sound/machines/click.ogg', 10, 1) + if(!mytape) + to_chat(usr, SPAN_NOTICE("There's no tape!")) + return + if(mytape.ruined || emagged) + audible_message(SPAN_WARNING("The tape recorder makes a scratchy noise.")) + return + if(recording) + to_chat(usr, SPAN_NOTICE("You're already recording!")) + return + if(playing) + to_chat(usr, SPAN_NOTICE("You can't record when playing!")) + return + if(mytape.used_capacity < mytape.max_capacity) + to_chat(usr, SPAN_NOTICE("Recording started.")) + recording = TRUE + update_icon() + + mytape.record_speech("Recording started.") + + //count seconds until full, or recording is stopped + while(mytape && recording && mytape.used_capacity < mytape.max_capacity) + sleep(1 SECOND) + mytape.used_capacity++ + if(mytape.used_capacity >= mytape.max_capacity) + to_chat(usr, SPAN_NOTICE("The tape is full.")) + stop_recording() + + + update_icon() + return + else + to_chat(usr, SPAN_NOTICE("The tape is full.")) + + +/obj/item/taperecorder/proc/stop_recording() + //Sanity checks skipped, should not be called unless actually recording + recording = FALSE + update_icon() + mytape.record_speech("Recording stopped.") + if(ismob(loc)) + var/mob/M = loc + to_chat(M, SPAN_NOTICE("Recording stopped.")) + + +/obj/item/taperecorder/verb/stop() + set name = "Stop" + set category = "Object" + + if(usr.incapacitated()) + return + playsound(src, 'sound/machines/click.ogg', 10, 1) + if(recording) + stop_recording() + return + else if(playing) + playing = FALSE + update_icon() + to_chat(usr, SPAN_NOTICE("Playback stopped.")) + return + else + to_chat(usr, SPAN_NOTICE("Stop what?")) + + +/obj/item/taperecorder/verb/wipe_tape() + set name = "Wipe Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + return + if(emagged || mytape.ruined) + audible_message(SPAN_WARNING("The tape recorder makes a scratchy noise.")) + return + if(recording || playing) + to_chat(usr, SPAN_NOTICE("You can't wipe the tape while playing or recording!")) + return + else + if(mytape.storedinfo) mytape.storedinfo.Cut() + if(mytape.timestamp) mytape.timestamp.Cut() + mytape.used_capacity = 0 + to_chat(usr, SPAN_NOTICE("You wipe the tape.")) + return + + +/obj/item/taperecorder/verb/playback_memory() + set name = "Playback Tape" + set category = "Object" + + if(usr.incapacitated()) + return + play(usr) + +/obj/item/taperecorder/proc/play(mob/user) + if(!mytape) + to_chat(user, SPAN_NOTICE("There's no tape!")) + return + if(mytape.ruined) + audible_message(SPAN_WARNING("The tape recorder makes a scratchy noise.")) + return + if(recording) + to_chat(user, SPAN_NOTICE("You can't playback when recording!")) + return + if(playing) + to_chat(user, SPAN_NOTICE("\The [src] is already playing its recording!")) + return + playing = TRUE + update_icon() + to_chat(user, SPAN_NOTICE("Audio playback started.")) + playsound(src, 'sound/machines/click.ogg', 10, 1) + for(var/i=1 , i < mytape.max_capacity , i++) + if(!mytape || !playing) + break + if(length(mytape.storedinfo) < i) + break + + var/turf/T = get_turf(src) + var/playedmessage = mytape.storedinfo[i] + if (findtextEx(playedmessage,"*",1,2)) //remove marker for action sounds + playedmessage = copytext(playedmessage,2) + T.audible_message(SPAN_MAROON("Tape Recorder: [playedmessage]")) + + if(length(mytape.storedinfo) < i+1) + playsleepseconds = 1 + sleep(10) + T = get_turf(src) + T.audible_message(SPAN_MAROON("Tape Recorder: End of recording.")) + playsound(src, 'sound/machines/click.ogg', 10, 1) + break + else + playsleepseconds = mytape.timestamp[i+1] - mytape.timestamp[i] + + if(playsleepseconds > 14) + sleep(10) + T = get_turf(src) + T.audible_message(SPAN_MAROON("Tape Recorder: Skipping [playsleepseconds] seconds of silence")) + playsleepseconds = 1 + sleep(playsleepseconds SECONDS) + + + playing = FALSE + update_icon() + + if(emagged) + var/turf/T = get_turf(src) + T.audible_message(SPAN_MAROON("Tape Recorder: This tape recorder will self-destruct in... Five.")) + sleep(10) + T = get_turf(src) + T.audible_message(SPAN_MAROON("Tape Recorder: Four.")) + sleep(10) + T = get_turf(src) + T.audible_message(SPAN_MAROON("Tape Recorder: Three.")) + sleep(10) + T = get_turf(src) + T.audible_message(SPAN_MAROON("Tape Recorder: Two.")) + sleep(10) + T = get_turf(src) + T.audible_message(SPAN_MAROON("Tape Recorder: One.")) + sleep(10) + explode() + + +/obj/item/taperecorder/verb/print_transcript() + set name = "Print Transcript" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, SPAN_NOTICE("There's no tape!")) + return + if(mytape.ruined || emagged) + audible_message(SPAN_WARNING("The tape recorder makes a scratchy noise.")) + return + if(next_print_time < world.time) + to_chat(usr, SPAN_NOTICE("The recorder can't print that fast!")) + return + if(recording || playing) + to_chat(usr, SPAN_NOTICE("You can't print the transcript while playing or recording!")) + return + + to_chat(usr, SPAN_NOTICE("Transcript printed.")) + var/obj/item/paper/P = new /obj/item/paper(get_turf(src)) + var/t1 = "Transcript:

    " + for(var/i in 1 to length(mytape.storedinfo)) + var/printedmessage = mytape.storedinfo[i] + if (findtextEx(printedmessage,"*",1,2)) //replace action sounds + printedmessage = "\[[time2text(mytape.timestamp[i]*10,"mm:ss")]\] (Unrecognized sound)" + t1 += "[printedmessage]
    " + P.info = t1 + P.SetName("Transcript") + next_print_time = world.time + TRANSCRIPT_PRINT_COOLDOWN + +/obj/item/taperecorder/attack_self(mob/user) + if(wires_accessible) + wires.Interact(user) + return + if(recording || playing) + stop() + else + record() + +/obj/item/taperecorder/on_update_icon() + . = ..() + icon_state = get_world_inventory_state() + if(!mytape) + icon_state = "[icon_state]_empty" + else if(recording) + icon_state = "[icon_state]_recording" + else if(playing) + icon_state = "[icon_state]_playing" + else + icon_state = "[icon_state]_idle" diff --git a/code/datums/wires/taperecorder.dm b/code/game/objects/items/devices/tape_recorder/taperecorder_wires.dm similarity index 90% rename from code/datums/wires/taperecorder.dm rename to code/game/objects/items/devices/tape_recorder/taperecorder_wires.dm index 784cd1a17df1..20e4bd7f63fa 100644 --- a/code/datums/wires/taperecorder.dm +++ b/code/game/objects/items/devices/tape_recorder/taperecorder_wires.dm @@ -4,8 +4,7 @@ descriptions = list( new /datum/wire_description(TAPE_WIRE_TOGGLE, "This wire runs to the play/stop toggle.", SKILL_ADEPT) ) - -var/global/const/TAPE_WIRE_TOGGLE = 1 + var/const/TAPE_WIRE_TOGGLE = 1 /datum/wires/taperecorder/UpdatePulsed(var/index) var/obj/item/taperecorder/T = holder diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm deleted file mode 100644 index 32287bf5e738..000000000000 --- a/code/game/objects/items/devices/taperecorder.dm +++ /dev/null @@ -1,540 +0,0 @@ -/obj/item/taperecorder - name = "universal recorder" - desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." - icon = 'icons/obj/items/device/tape_recorder/tape_recorder.dmi' - icon_state = ICON_STATE_WORLD - w_class = ITEM_SIZE_SMALL - material = /decl/material/solid/metal/aluminium - matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) - obj_flags = OBJ_FLAG_CONDUCTIBLE - slot_flags = SLOT_LOWER_BODY - throw_speed = 4 - throw_range = 20 - - var/emagged = 0.0 - var/recording = 0.0 - var/playing = 0.0 - var/playsleepseconds = 0.0 - var/obj/item/magnetic_tape/mytape = /obj/item/magnetic_tape/random - var/canprint = 1 - var/datum/wires/taperecorder/wires = null // Wires datum - var/maintenance = 0 - -/obj/item/taperecorder/Initialize() - . = ..() - wires = new(src) - if(ispath(mytape)) - mytape = new mytape(src) - global.listening_objects += src - update_icon() - -/obj/item/taperecorder/empty - mytape = null - -/obj/item/taperecorder/Destroy() - QDEL_NULL(wires) - if(mytape) - qdel(mytape) - mytape = null - return ..() - -/obj/item/taperecorder/attackby(obj/item/used_item, mob/user, params) - if(IS_SCREWDRIVER(used_item)) - maintenance = !maintenance - to_chat(user, "You [maintenance ? "open" : "secure"] the lid.") - return TRUE - if(istype(used_item, /obj/item/magnetic_tape)) - if(mytape) - to_chat(user, "There's already a tape inside.") - return TRUE - if(!user.try_unequip(used_item)) - return TRUE - used_item.forceMove(src) - mytape = used_item - to_chat(user, "You insert [used_item] into [src].") - update_icon() - return TRUE - return ..() - - -/obj/item/taperecorder/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(mytape) - mytape.ruin() //Fires destroy the tape - return ..() - - -/obj/item/taperecorder/attack_hand(mob/user) - if(user.is_holding_offhand(src) && mytape && user.check_dexterity(DEXTERITY_SIMPLE_MACHINES)) - eject() - return TRUE - return ..() - - -/obj/item/taperecorder/verb/eject() - set name = "Eject Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape in \the [src].") - return - if(emagged) - to_chat(usr, "The tape seems to be stuck inside.") - return - - if(playing || recording) - stop() - to_chat(usr, "You remove [mytape] from [src].") - usr.put_in_hands(mytape) - mytape = null - update_icon() - -/obj/item/taperecorder/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(distance <= 1 && maintenance) - . += SPAN_NOTICE("The wires are exposed.") - -/obj/item/taperecorder/hear_talk(mob/living/M, msg, var/verb="says", decl/language/speaking=null) - if(mytape && recording) - - if(speaking) - if(!speaking.machine_understands) - msg = speaking.scramble(M, msg) - mytape.record_speech("[M.name] [speaking.format_message_plain(msg, verb)]") - else - mytape.record_speech("[M.name] [verb], \"[msg]\"") - -/obj/item/taperecorder/show_message(msg, type, alt, alt_type) - var/recordedtext - if (msg && type == AUDIBLE_MESSAGE) //must be hearable - recordedtext = msg - else if (alt && alt_type == AUDIBLE_MESSAGE) - recordedtext = alt - else - return - if(mytape && recording) - mytape.record_noise("[strip_html_properly(recordedtext)]") - -/obj/item/taperecorder/emag_act(var/remaining_charges, var/mob/user) - if(emagged == 0) - emagged = 1 - recording = 0 - to_chat(user, "PZZTTPFFFT") - update_icon() - return 1 - else - to_chat(user, "It is already emagged!") - -/obj/item/taperecorder/proc/explode() - var/turf/T = get_turf(loc) - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "\The [src] explodes!") - if(T) - T.hotspot_expose(700,125) - explosion(T, -1, -1, 0, 4) - qdel(src) - return - -/obj/item/taperecorder/verb/record() - set name = "Start Recording" - set category = "Object" - - if(usr.incapacitated()) - return - playsound(src, 'sound/machines/click.ogg', 10, 1) - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined || emagged) - audible_message("The tape recorder makes a scratchy noise.") - return - if(recording) - to_chat(usr, "You're already recording!") - return - if(playing) - to_chat(usr, "You can't record when playing!") - return - if(mytape.used_capacity < mytape.max_capacity) - to_chat(usr, "Recording started.") - recording = 1 - update_icon() - - mytape.record_speech("Recording started.") - - //count seconds until full, or recording is stopped - while(mytape && recording && mytape.used_capacity < mytape.max_capacity) - sleep(10) - mytape.used_capacity++ - if(mytape.used_capacity >= mytape.max_capacity) - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "The tape is full.") - stop_recording() - - - update_icon() - return - else - to_chat(usr, "The tape is full.") - - -/obj/item/taperecorder/proc/stop_recording() - //Sanity checks skipped, should not be called unless actually recording - recording = 0 - update_icon() - mytape.record_speech("Recording stopped.") - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "Recording stopped.") - - -/obj/item/taperecorder/verb/stop() - set name = "Stop" - set category = "Object" - - if(usr.incapacitated()) - return - playsound(src, 'sound/machines/click.ogg', 10, 1) - if(recording) - stop_recording() - return - else if(playing) - playing = 0 - update_icon() - to_chat(usr, "Playback stopped.") - return - else - to_chat(usr, "Stop what?") - - -/obj/item/taperecorder/verb/wipe_tape() - set name = "Wipe Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - return - if(emagged || mytape.ruined) - audible_message("The tape recorder makes a scratchy noise.") - return - if(recording || playing) - to_chat(usr, "You can't wipe the tape while playing or recording!") - return - else - if(mytape.storedinfo) mytape.storedinfo.Cut() - if(mytape.timestamp) mytape.timestamp.Cut() - mytape.used_capacity = 0 - to_chat(usr, "You wipe the tape.") - return - - -/obj/item/taperecorder/verb/playback_memory() - set name = "Playback Tape" - set category = "Object" - - if(usr.incapacitated()) - return - play(usr) - -/obj/item/taperecorder/proc/play(mob/user) - if(!mytape) - to_chat(user, "There's no tape!") - return - if(mytape.ruined) - audible_message("The tape recorder makes a scratchy noise.") - return - if(recording) - to_chat(user, "You can't playback when recording!") - return - if(playing) - to_chat(user, "You're already playing!") - return - playing = 1 - update_icon() - to_chat(user, "Audio playback started.") - playsound(src, 'sound/machines/click.ogg', 10, 1) - for(var/i=1 , i < mytape.max_capacity , i++) - if(!mytape || !playing) - break - if(mytape.storedinfo.len < i) - break - - var/turf/T = get_turf(src) - var/playedmessage = mytape.storedinfo[i] - if (findtextEx(playedmessage,"*",1,2)) //remove marker for action sounds - playedmessage = copytext(playedmessage,2) - T.audible_message(SPAN_MAROON("Tape Recorder: [playedmessage]")) - - if(mytape.storedinfo.len < i+1) - playsleepseconds = 1 - sleep(10) - T = get_turf(src) - T.audible_message(SPAN_MAROON("Tape Recorder: End of recording.")) - playsound(src, 'sound/machines/click.ogg', 10, 1) - break - else - playsleepseconds = mytape.timestamp[i+1] - mytape.timestamp[i] - - if(playsleepseconds > 14) - sleep(10) - T = get_turf(src) - T.audible_message(SPAN_MAROON("Tape Recorder: Skipping [playsleepseconds] seconds of silence")) - playsleepseconds = 1 - sleep(10 * playsleepseconds) - - - playing = 0 - update_icon() - - if(emagged) - var/turf/T = get_turf(src) - T.audible_message(SPAN_MAROON("Tape Recorder: This tape recorder will self-destruct in... Five.")) - sleep(10) - T = get_turf(src) - T.audible_message(SPAN_MAROON("Tape Recorder: Four.")) - sleep(10) - T = get_turf(src) - T.audible_message(SPAN_MAROON("Tape Recorder: Three.")) - sleep(10) - T = get_turf(src) - T.audible_message(SPAN_MAROON("Tape Recorder: Two.")) - sleep(10) - T = get_turf(src) - T.audible_message(SPAN_MAROON("Tape Recorder: One.")) - sleep(10) - explode() - - -/obj/item/taperecorder/verb/print_transcript() - set name = "Print Transcript" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined || emagged) - audible_message("The tape recorder makes a scratchy noise.") - return - if(!canprint) - to_chat(usr, "The recorder can't print that fast!") - return - if(recording || playing) - to_chat(usr, "You can't print the transcript while playing or recording!") - return - - to_chat(usr, "Transcript printed.") - var/obj/item/paper/P = new /obj/item/paper(get_turf(src)) - var/t1 = "Transcript:

    " - for(var/i=1,mytape.storedinfo.len >= i,i++) - var/printedmessage = mytape.storedinfo[i] - if (findtextEx(printedmessage,"*",1,2)) //replace action sounds - printedmessage = "\[[time2text(mytape.timestamp[i]*10,"mm:ss")]\] (Unrecognized sound)" - t1 += "[printedmessage]
    " - P.info = t1 - P.SetName("Transcript") - canprint = 0 - sleep(300) - canprint = 1 - -/obj/item/taperecorder/attack_self(mob/user) - if(maintenance) - wires.Interact(user) - return - if(recording || playing) - stop() - else - record() - -/obj/item/taperecorder/on_update_icon() - . = ..() - icon_state = get_world_inventory_state() - if(!mytape) - icon_state = "[icon_state]_empty" - else if(recording) - icon_state = "[icon_state]_recording" - else if(playing) - icon_state = "[icon_state]_playing" - else - icon_state = "[icon_state]_idle" - -/obj/item/magnetic_tape - name = "tape" - desc = "A magnetic tape that can hold up to ten minutes of content." - icon = 'icons/obj/items/device/tape_recorder/tape_casette_white.dmi' - icon_state = ICON_STATE_WORLD - w_class = ITEM_SIZE_SMALL - material = /decl/material/solid/organic/plastic - matter = list( - /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/fiberglass = MATTER_AMOUNT_TRACE - ) - _base_attack_force = 1 - var/max_capacity = 600 - var/used_capacity = 0 - var/list/storedinfo = new/list() - var/list/timestamp = new/list() - var/ruined = 0 - var/doctored = 0 - /// Whether we draw the ruined ribbon overlay when ruined. - var/draw_ribbon_if_ruined = TRUE - -/obj/item/magnetic_tape/on_update_icon() - . = ..() - icon_state = get_world_inventory_state() - if(draw_ribbon_if_ruined && ruined && max_capacity) - add_overlay(overlay_image(icon, "[icon_state]_ribbonoverlay", flags = RESET_COLOR)) - -/obj/item/magnetic_tape/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ruin() - return ..() - -/obj/item/magnetic_tape/attack_self(mob/user) - if(!ruined) - to_chat(user, "You pull out all the tape!") - get_loose_tape(user, storedinfo.len) - ruin() - - -/obj/item/magnetic_tape/proc/ruin() - ruined = TRUE - update_icon() - - -/obj/item/magnetic_tape/proc/fix() - ruined = FALSE - update_icon() - - -/obj/item/magnetic_tape/proc/record_speech(text) - timestamp += used_capacity - storedinfo += "\[[time2text(used_capacity*10,"mm:ss")]\] [text]" - - -//shows up on the printed transcript as (Unrecognized sound) -/obj/item/magnetic_tape/proc/record_noise(text) - timestamp += used_capacity - storedinfo += "*\[[time2text(used_capacity*10,"mm:ss")]\] [text]" - - -/obj/item/magnetic_tape/attackby(obj/item/used_item, mob/user, params) - if(user.incapacitated()) // TODO: this may not be necessary since OnClick checks before starting the attack chain - return TRUE - if(ruined && IS_SCREWDRIVER(used_item)) - if(!max_capacity) - to_chat(user, "There is no tape left inside.") - return TRUE - to_chat(user, "You start winding the tape back in...") - if(do_after(user, 120, target = src)) - to_chat(user, "You wound the tape back in.") - fix() - return TRUE - else if(IS_PEN(used_item)) - if(loc == user) - var/new_name = input(user, "What would you like to label the tape?", "Tape labeling") as null|text - if(isnull(new_name)) return TRUE - new_name = sanitize_safe(new_name) - if(new_name) - SetName("tape - '[new_name]'") - to_chat(user, "You label the tape '[new_name]'.") - else - SetName("tape") - to_chat(user, "You scratch off the label.") - return TRUE - else if(IS_WIRECUTTER(used_item)) - cut(user) - return TRUE - else if(istype(used_item, /obj/item/magnetic_tape/loose)) - join(user, used_item) - return TRUE - return ..() - -/obj/item/magnetic_tape/proc/cut(mob/user) - if(!LAZYLEN(timestamp)) - to_chat(user, "There's nothing on this tape!") - return - var/list/output = list("
    ") - for(var/i=1, i < timestamp.len, i++) - var/time = "\[[time2text(timestamp[i]*10,"mm:ss")]\]" - output += "[time]
    -----CUT------
    " - output += "
    " - - var/datum/browser/popup = new(user, "tape_cutting", "Cutting tape", 170, 600) - popup.set_content(jointext(output,null)) - popup.open() - -/obj/item/magnetic_tape/proc/join(mob/user, obj/item/magnetic_tape/other) - if(max_capacity + other.max_capacity > initial(max_capacity)) - to_chat(user, "You can't fit this much tape in!") - return - if(user.try_unequip(other)) - to_chat(user, "You join ends of the tape together.") - max_capacity += other.max_capacity - used_capacity = min(used_capacity + other.used_capacity, max_capacity) - timestamp += other.timestamp - storedinfo += other.storedinfo - doctored = 1 - ruin() - update_icon() - qdel(other) - -/obj/item/magnetic_tape/OnTopic(var/mob/user, var/list/href_list) - if(href_list["cut_after"]) - var/index = text2num(href_list["cut_after"]) - if(index >= timestamp.len) - return - - to_chat(user, "You remove part of the tape off.") - get_loose_tape(user, index) - cut(user) - return TOPIC_REFRESH - -//Spawns new loose tape item, with data starting from [index] entry -/obj/item/magnetic_tape/proc/get_loose_tape(var/mob/user, var/index) - var/obj/item/magnetic_tape/loose/newtape = new() - newtape.timestamp = timestamp.Copy(index+1) - newtape.storedinfo = storedinfo.Copy(index+1) - newtape.max_capacity = max_capacity - index - newtape.used_capacity = max(0, used_capacity - max_capacity) - newtape.doctored = doctored - user.put_in_hands(newtape) - - timestamp.Cut(index+1) - storedinfo.Cut(index+1) - max_capacity = index - used_capacity = min(used_capacity,index) - -//Random colour tapes -/obj/item/magnetic_tape/random/Initialize() - icon = pick(list( - 'icons/obj/items/device/tape_recorder/tape_casette_white.dmi', - 'icons/obj/items/device/tape_recorder/tape_casette_blue.dmi', - 'icons/obj/items/device/tape_recorder/tape_casette_red.dmi', - 'icons/obj/items/device/tape_recorder/tape_casette_yellow.dmi', - 'icons/obj/items/device/tape_recorder/tape_casette_purple.dmi' - )) - . = ..() - -/obj/item/magnetic_tape/loose - name = "magnetic tape" - desc = "Quantum-enriched self-repairing nanotape, used for magnetic storage of information." - icon = 'icons/obj/items/device/tape_recorder/tape_casette_loose.dmi' - ruined = TRUE - draw_ribbon_if_ruined = FALSE - -/obj/item/magnetic_tape/loose/fix() - return - -/obj/item/magnetic_tape/loose/get_loose_tape() - return - -/obj/item/magnetic_tape/loose/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(distance <= 1) - . += SPAN_NOTICE("It looks long enough to hold [max_capacity] seconds worth of recording.") - if(doctored && user.skill_check(SKILL_FORENSICS, SKILL_PROF)) - . += SPAN_WARNING("It has been tampered with...") diff --git a/nebula.dme b/nebula.dme index 8a77c79025d6..0c5c058dc7e4 100644 --- a/nebula.dme +++ b/nebula.dme @@ -744,7 +744,6 @@ #include "code\datums\wires\smartfridge.dm" #include "code\datums\wires\smes.dm" #include "code\datums\wires\suit_cycler.dm" -#include "code\datums\wires\taperecorder.dm" #include "code\datums\wires\vending.dm" #include "code\datums\wires\wire_description.dm" #include "code\datums\wires\wires.dm" @@ -1206,7 +1205,6 @@ #include "code\game\objects\items\devices\suit_cooling.dm" #include "code\game\objects\items\devices\suit_sensor_jammer.dm" #include "code\game\objects\items\devices\t_scanner.dm" -#include "code\game\objects\items\devices\taperecorder.dm" #include "code\game\objects\items\devices\traitordevices.dm" #include "code\game\objects\items\devices\transfer_valve.dm" #include "code\game\objects\items\devices\tvcamera.dm" @@ -1223,6 +1221,9 @@ #include "code\game\objects\items\devices\radio\radio_borg.dm" #include "code\game\objects\items\devices\radio\radio_exosuit.dm" #include "code\game\objects\items\devices\radio\radio_misc.dm" +#include "code\game\objects\items\devices\tape_recorder\magnetic_tape.dm" +#include "code\game\objects\items\devices\tape_recorder\tape_recorder.dm" +#include "code\game\objects\items\devices\tape_recorder\taperecorder_wires.dm" #include "code\game\objects\items\flame\_flame.dm" #include "code\game\objects\items\flame\flame_candle.dm" #include "code\game\objects\items\flame\flame_fuelled.dm" From 2b189054c641ecf13cfede74d7c9c55a1b9d9818 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 13 May 2025 20:18:34 -0400 Subject: [PATCH 0654/1331] Refactor stimulants/antidepressants to reduce code duplication --- code/modules/clothing/masks/chewable.dm | 4 +- .../spacesuits/rig/modules/utility.dm | 10 +- .../hydroponics/plant_types/seeds_misc.dm | 4 +- .../modules/reagents/chems/chems_medicines.dm | 64 ------------- .../reagents/chems/chems_psychiatric.dm | 91 +++++++++++++++++++ .../reagents/reactions/reaction_drugs.dm | 4 +- .../reagent_containers/food/donkpocket.dm | 2 +- .../reagents/reagent_containers/pill.dm | 16 ++-- nebula.dme | 1 + 9 files changed, 112 insertions(+), 84 deletions(-) create mode 100644 code/modules/reagents/chems/chems_psychiatric.dm diff --git a/code/modules/clothing/masks/chewable.dm b/code/modules/clothing/masks/chewable.dm index 682c68bc51fd..1df74f5bc5fa 100644 --- a/code/modules/clothing/masks/chewable.dm +++ b/code/modules/clothing/masks/chewable.dm @@ -210,8 +210,8 @@ /decl/material/liquid/regenerator, /decl/material/liquid/amphetamines, /decl/material/liquid/antirads, - /decl/material/liquid/stimulants, - /decl/material/liquid/antidepressants, + /decl/material/liquid/accumulated/stimulants, + /decl/material/liquid/accumulated/antidepressants, /decl/material/liquid/antitoxins, /decl/material/liquid/brute_meds, /decl/material/liquid/burn_meds, diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index f46dd0447280..b024ab15dae1 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -265,11 +265,11 @@ desc = "A complex web of tubing and needles suitable for hardsuit use." charges = list( - list("antidepressants", "antidepressants", /decl/material/liquid/antidepressants, 30), - list("stimulants", "stimulants", /decl/material/liquid/stimulants, 30), - list("amphetamines", "amphetamines", /decl/material/liquid/amphetamines, 30), - list("painkillers", "painkillers", /decl/material/liquid/painkillers/strong, 30), - list("glucose", "glucose", /decl/material/liquid/nutriment/glucose, 80) + list("antidepressants", "antidepressants", /decl/material/liquid/accumulated/antidepressants, 30), + list("stimulants", "stimulants", /decl/material/liquid/accumulated/stimulants, 30), + list("amphetamines", "amphetamines", /decl/material/liquid/amphetamines, 30), + list("painkillers", "painkillers", /decl/material/liquid/painkillers/strong, 30), + list("glucose", "glucose", /decl/material/liquid/nutriment/glucose, 80) ) interface_name = "combat chem dispenser" diff --git a/code/modules/hydroponics/plant_types/seeds_misc.dm b/code/modules/hydroponics/plant_types/seeds_misc.dm index 8ee10be8c9dd..ea29e6249413 100644 --- a/code/modules/hydroponics/plant_types/seeds_misc.dm +++ b/code/modules/hydroponics/plant_types/seeds_misc.dm @@ -414,8 +414,8 @@ chems = list( /decl/material/liquid/nutriment = list(1), /decl/material/liquid/brute_meds = list(1,8), - /decl/material/liquid/antidepressants = list(1,8,1), - /decl/material/liquid/stimulants = list(1,8,1), + /decl/material/liquid/accumulated/antidepressants = list(1,8,1), + /decl/material/liquid/accumulated/stimulants = list(1,8,1), /decl/material/liquid/amphetamines = list(1,10,1), /decl/material/liquid/psychoactives = list(1,10) ) diff --git a/code/modules/reagents/chems/chems_medicines.dm b/code/modules/reagents/chems/chems_medicines.dm index 02e3484df2c4..9c22dfc135ef 100644 --- a/code/modules/reagents/chems/chems_medicines.dm +++ b/code/modules/reagents/chems/chems_medicines.dm @@ -168,70 +168,6 @@ victim.add_chemical_effect(CE_TOXIN, 1) victim.adjust_immunity(-0.5) -/decl/material/liquid/stimulants - name = "stimulants" - lore_text = "Improves the ability to concentrate." - taste_description = "sourness" - color = "#bf80bf" - scannable = 1 - metabolism = 0.01 - value = 1.5 - exoplanet_rarity_gas = MAT_RARITY_EXOTIC - uid = "chem_stimulants" - allergen_flags = ALLERGEN_STIMULANT - -/decl/material/liquid/stimulants/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) - var/volume = REAGENT_VOLUME(holder, src) - . = ..() - var/update_data = FALSE - var/list/data = REAGENT_DATA(holder, src) - if(volume <= 0.1 && CHEM_DOSE(M, src) >= 0.5 && world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 5 MINUTES) - update_data = TRUE - to_chat(M, "You lose focus...") - else - ADJ_STATUS(M, STAT_DROWSY, -5) - ADJ_STATUS(M, STAT_PARA, -1) - ADJ_STATUS(M, STAT_STUN, -1) - ADJ_STATUS(M, STAT_WEAK, -1) - if(world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 5 MINUTES) - update_data = TRUE - to_chat(M, "Your mind feels focused and undivided.") - - if(update_data) - LAZYSET(data, DATA_COOLDOWN_TIME, world.time) - LAZYSET(holder.reagent_data, type, data) - -/decl/material/liquid/antidepressants - name = "antidepressants" - lore_text = "Stabilizes the mind a little." - taste_description = "bitterness" - color = "#ff80ff" - scannable = 1 - metabolism = 0.01 - value = 1.5 - exoplanet_rarity_gas = MAT_RARITY_EXOTIC - uid = "chem_antidepressants" - -/decl/material/liquid/antidepressants/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) - var/volume = REAGENT_VOLUME(holder, src) - . = ..() - - var/update_data = FALSE - var/list/data = REAGENT_DATA(holder, src) - if(volume <= 0.1 && CHEM_DOSE(M, src) >= 0.5 && world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 5 MINUTES) - update_data = TRUE - to_chat(M, "Your mind feels a little less stable...") - else - M.add_chemical_effect(CE_MIND, 1) - M.adjust_hallucination(-10) - if(world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 5 MINUTES) - update_data = TRUE - to_chat(M, "Your mind feels stable... a little stable.") - - if(update_data) - LAZYSET(data, DATA_COOLDOWN_TIME, world.time) - LAZYSET(holder.reagent_data, type, data) - /decl/material/liquid/antibiotics name = "antibiotics" lore_text = "An all-purpose antibiotic agent." diff --git a/code/modules/reagents/chems/chems_psychiatric.dm b/code/modules/reagents/chems/chems_psychiatric.dm new file mode 100644 index 000000000000..2bc88925516f --- /dev/null +++ b/code/modules/reagents/chems/chems_psychiatric.dm @@ -0,0 +1,91 @@ +/// This material type has mob effects based on accumulated dose. +/// If the drug has been in your system enough to accumulate a dose, +/// and the level of it in your bloodstream drops below a certain volume, +/// you will get discontinuation effects. +/// If it is above that volume or hasn't been in your system long enough to accumulate, +/// you will get the positive effects. +/decl/material/liquid/accumulated + abstract_type = /decl/material/liquid/accumulated + /// (FLOAT) The dose under which you will receive effects from discontinuation. + var/required_dose = 0.5 + /// (FLOAT) The minimum volume to get the positive effect of this reagent. + var/required_volume = 0.1 + /// (FLOAT) The cooldown between effect triggers. + var/effect_cooldown = 5 MINUTES + +/decl/material/liquid/accumulated/affect_blood(mob/living/victim, removed, datum/reagents/holder) + var/volume = REAGENT_VOLUME(holder, src) + . = ..() + + var/update_data = FALSE + var/list/data = REAGENT_DATA(holder, src) + var/is_off_cooldown = world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + effect_cooldown + if(volume <= required_volume && CHEM_DOSE(victim, src) >= required_dose) + update_data = discontinuation_effect(victim, removed, holder, is_off_cooldown) + else + update_data = positive_effect(victim, removed, holder, is_off_cooldown) + + if(update_data) + LAZYSET(data, DATA_COOLDOWN_TIME, world.time) + LAZYSET(holder.reagent_data, type, data) + +/// Returns TRUE to signal that the effect should go on cooldown. +/decl/material/liquid/accumulated/proc/positive_effect(mob/living/victim, removed, datum/reagents/holder, is_off_cooldown) + return FALSE + +/// Returns TRUE to signal that the effect should go on cooldown. +/decl/material/liquid/accumulated/proc/discontinuation_effect(mob/living/victim, removed, datum/reagents/holder, is_off_cooldown) + return FALSE + +/decl/material/liquid/accumulated/stimulants + name = "stimulants" + lore_text = "Improves the ability to concentrate." + taste_description = "sourness" + color = "#bf80bf" + scannable = 1 + metabolism = 0.01 + value = 1.5 + exoplanet_rarity_gas = MAT_RARITY_EXOTIC + uid = "chem_stimulants" + allergen_flags = ALLERGEN_STIMULANT + +/decl/material/liquid/accumulated/stimulants/positive_effect(mob/living/victim, removed, datum/reagents/holder, is_off_cooldown) + ADJ_STATUS(victim, STAT_DROWSY, -5) + ADJ_STATUS(victim, STAT_PARA, -1) + ADJ_STATUS(victim, STAT_STUN, -1) + ADJ_STATUS(victim, STAT_WEAK, -1) + if(is_off_cooldown) + to_chat(victim, SPAN_NOTICE("Your mind feels focused and undivided.")) + return TRUE + return FALSE + +/decl/material/liquid/accumulated/stimulants/discontinuation_effect(mob/living/victim, removed, datum/reagents/holder, is_off_cooldown) + if(is_off_cooldown) + to_chat(victim, SPAN_WARNING("You lose focus...")) + return TRUE + return FALSE + +/decl/material/liquid/accumulated/antidepressants + name = "antidepressants" + lore_text = "Stabilizes the mind a little." + taste_description = "bitterness" + color = "#ff80ff" + scannable = 1 + metabolism = 0.01 + value = 1.5 + exoplanet_rarity_gas = MAT_RARITY_EXOTIC + uid = "chem_antidepressants" + +/decl/material/liquid/accumulated/antidepressants/positive_effect(mob/living/victim, removed, datum/reagents/holder, is_off_cooldown) + victim.add_chemical_effect(CE_MIND, 1) + victim.adjust_hallucination(-10) + if(is_off_cooldown) + to_chat(victim, SPAN_NOTICE("Your mind feels stable... a little stable.")) + return TRUE + return FALSE + +/decl/material/liquid/accumulated/antidepressants/discontinuation_effect(mob/living/victim, removed, datum/reagents/holder, is_off_cooldown) + if(is_off_cooldown) + to_chat(victim, SPAN_WARNING("Your mind feels a little less stable...")) + return TRUE + return FALSE diff --git a/code/modules/reagents/reactions/reaction_drugs.dm b/code/modules/reagents/reactions/reaction_drugs.dm index a4f85cb3fa73..8f0c839b3b0f 100644 --- a/code/modules/reagents/reactions/reaction_drugs.dm +++ b/code/modules/reagents/reactions/reaction_drugs.dm @@ -159,13 +159,13 @@ /decl/chemical_reaction/drug/stimulants name = "Stimulants" - result = /decl/material/liquid/stimulants + result = /decl/material/liquid/accumulated/stimulants required_reagents = list(/decl/material/liquid/hallucinogenics = 1, /decl/material/solid/lithium = 1) result_amount = 3 /decl/chemical_reaction/drug/antidepressants name = "Antidepressants" - result = /decl/material/liquid/antidepressants + result = /decl/material/liquid/accumulated/antidepressants required_reagents = list(/decl/material/liquid/hallucinogenics = 1, /decl/material/solid/carbon = 1) result_amount = 3 diff --git a/code/modules/reagents/reagent_containers/food/donkpocket.dm b/code/modules/reagents/reagent_containers/food/donkpocket.dm index b261f8781a84..55e9e809efee 100644 --- a/code/modules/reagents/reagent_containers/food/donkpocket.dm +++ b/code/modules/reagents/reagent_containers/food/donkpocket.dm @@ -57,7 +57,7 @@ heated_reagents = list( /decl/material/liquid/regenerator = 5, /decl/material/liquid/amphetamines = 0.75, - /decl/material/liquid/stimulants = 0.25 + /decl/material/liquid/accumulated/stimulants = 0.25 ) var/has_been_heated = 0 // Unlike the warm var, this checks if the one-time self-heating operation has been used. diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index 14286f7debe5..e58ada9128be 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -184,10 +184,10 @@ autolabel = FALSE /obj/item/chems/pill/zoom/populate_reagents() - add_to_reagents(/decl/material/liquid/narcotics, 5) - add_to_reagents(/decl/material/liquid/antidepressants, 5) - add_to_reagents(/decl/material/liquid/stimulants, 5) - add_to_reagents(/decl/material/liquid/amphetamines, 5) + add_to_reagents(/decl/material/liquid/narcotics, 5) + add_to_reagents(/decl/material/liquid/accumulated/antidepressants, 5) + add_to_reagents(/decl/material/liquid/accumulated/stimulants, 5) + add_to_reagents(/decl/material/liquid/amphetamines, 5) . = ..() /obj/item/chems/pill/gleam @@ -210,19 +210,19 @@ //Psychiatry pills. /obj/item/chems/pill/stimulants - desc = "Improves the ability to concentrate." + desc = "Improves the ability to concentrate. Abrupt discontinuation may result in side-effects." icon_state = "pill2" /obj/item/chems/pill/stimulants/populate_reagents() - add_to_reagents(/decl/material/liquid/stimulants, 15) + add_to_reagents(/decl/material/liquid/accumulated/stimulants, 15) . = ..() /obj/item/chems/pill/antidepressants - desc = "Mild anti-depressant." + desc = "Mild anti-depressant. Abrupt discontinuation may result in side-effects." icon_state = "pill4" /obj/item/chems/pill/antidepressants/populate_reagents() - add_to_reagents(/decl/material/liquid/antidepressants, 15) + add_to_reagents(/decl/material/liquid/accumulated/antidepressants, 15) . = ..() /obj/item/chems/pill/antirads diff --git a/nebula.dme b/nebula.dme index 8a77c79025d6..8dd681b5c38e 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3648,6 +3648,7 @@ #include "code\modules\reagents\chems\chems_painkillers.dm" #include "code\modules\reagents\chems\chems_pigments.dm" #include "code\modules\reagents\chems\chems_poisons.dm" +#include "code\modules\reagents\chems\chems_psychiatric.dm" #include "code\modules\reagents\chems\random\chems_random.dm" #include "code\modules\reagents\chems\random\random_effects.dm" #include "code\modules\reagents\dispenser\_defines.dm" From db76f0681311deaa1ae6f028fce911122342971d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 13 May 2025 20:22:52 -0400 Subject: [PATCH 0655/1331] Refactor syndie cigarettes to reduce code duplication --- .../items/weapons/storage/fancy/cigarettes.dm | 83 +++++++------------ .../items/weapons/storage/uplink_kits.dm | 8 +- maps/ministation/ministation-1.dmm | 2 +- tools/map_migrations/9999_cigarettes.txt | 4 + 4 files changed, 41 insertions(+), 56 deletions(-) create mode 100644 tools/map_migrations/9999_cigarettes.txt diff --git a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm index ef4dcddd236f..379d275b5f8f 100644 --- a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm +++ b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm @@ -167,76 +167,57 @@ //////////////////////////////////////////////////////////////////////////////// // Syndie Cigs //////////////////////////////////////////////////////////////////////////////// +/obj/item/box/fancy/cigarettes/covert + abstract_type = /obj/item/box/fancy/cigarettes/covert + /// (TYPEPATH) Used to reset the name and description of covert cigarette packs on init. + var/obj/item/box/fancy/cigarettes/disguised_as = /obj/item/box/fancy/cigarettes + /// (STRING) Part of a string appended to the description on init. + var/scribble = null + +/obj/item/box/fancy/cigarettes/covert/Initialize(ml, material_key) + . = ..() + if(ispath(disguised_as, /obj/item/box/fancy/cigarettes)) + //Reset the name to the default cig pack. Done for codex reasons, since it indexes things by initial names + if(name == initial(name)) // allow mapped names to override it + SetName(disguised_as::name) + if(desc == initial(desc) && istext(scribble)) // ditto for mapped descs + desc = "[disguised_as::desc] '[scribble]' has been scribbled on it." // Flash Powder Pack -/obj/item/box/fancy/cigarettes/flash_powder +/obj/item/box/fancy/cigarettes/covert/flash_powder name = "pack of flash powder laced Trans-Stellar Duty-frees" + disguised_as = /obj/item/box/fancy/cigarettes + scribble = "F" -/obj/item/box/fancy/cigarettes/flash_powder/Initialize(ml, material_key) - . = ..() - //Reset the name to the default cig pack. Done for codex reasons, since it indexes things by initial names - var/obj/item/box/fancy/cigarettes/C = /obj/item/box/fancy/cigarettes - if(name == initial(name)) - SetName(initial(C.name)) - if(desc == initial(desc)) - desc = "[initial(desc)] 'F' has been scribbled on it." - -/obj/item/box/fancy/cigarettes/flash_powder/populate_reagents() +/obj/item/box/fancy/cigarettes/covert/flash_powder/populate_reagents() var/max_storage_space = max(1, storage?.max_storage_space) add_to_reagents(/decl/material/solid/metal/aluminium, max_storage_space) add_to_reagents(/decl/material/solid/potassium, max_storage_space) add_to_reagents(/decl/material/solid/sulfur, max_storage_space) //Chemsmoke Pack -/obj/item/box/fancy/cigarettes/chemsmoke +/obj/item/box/fancy/cigarettes/covert/chemsmoke name = "pack of smoke powder laced Trans-Stellar Duty-frees" + scribble = "S" -/obj/item/box/fancy/cigarettes/chemsmoke/Initialize(ml, material_key) - . = ..() - //Reset the name to the default cig pack. Done for codex reasons, since it indexes things by initial names - var/obj/item/box/fancy/cigarettes/C = /obj/item/box/fancy/cigarettes - if(name == initial(name)) - SetName(initial(C.name)) - if(desc == initial(desc)) - desc = "[initial(desc)] 'S' has been scribbled on it." - -/obj/item/box/fancy/cigarettes/chemsmoke/populate_reagents() +/obj/item/box/fancy/cigarettes/covert/chemsmoke/populate_reagents() var/max_storage_space = max(1, storage?.max_storage_space) add_to_reagents(/decl/material/solid/potassium, max_storage_space) add_to_reagents(/decl/material/liquid/nutriment/sugar, max_storage_space) add_to_reagents(/decl/material/solid/phosphorus, max_storage_space) //Mindbreak Pack (now called /decl/chemical_reaction/hallucinogenics) -/obj/item/box/fancy/cigarettes/mindbreak - name = "pack of mindbreak toxin laced Trans-Stellar Duty-frees" //#TODO: maybe fix the lore for that? +/obj/item/box/fancy/cigarettes/covert/mindbreak + name = "pack of hallucinogen-laced Trans-Stellar Duty-frees" //#TODO: maybe fix the lore for that? + scribble = "H" -/obj/item/box/fancy/cigarettes/mindbreak/Initialize(ml, material_key) - . = ..() - //Reset the name to the default cig pack. Done for codex reasons, since it indexes things by initial names - var/obj/item/box/fancy/cigarettes/C = /obj/item/box/fancy/cigarettes - if(name == initial(name)) - SetName(initial(C.name)) - if(desc == initial(desc)) - desc = "[initial(desc)] 'MB' has been scribbled on it." //#TODO: maybe fix the lore for that? - -/obj/item/box/fancy/cigarettes/mindbreak/populate_reagents() - var/max_storage_space = max(1, storage?.max_storage_space) - add_to_reagents(/decl/material/solid/silicon, max_storage_space) - add_to_reagents(/decl/material/liquid/fuel/hydrazine, max_storage_space) - add_to_reagents(/decl/material/liquid/antitoxins, max_storage_space) +/obj/item/box/fancy/cigarettes/covert/mindbreak/populate_reagents() + add_to_reagents(/decl/material/liquid/hallucinogenics, (3 * max(1, storage?.max_storage_space))) -//Tricord pack (now called /decl/material/liquid/regenerator) -/obj/item/box/fancy/cigarettes/tricord - name = "pack of tricordazine laced Trans-Stellar Duty-frees" //#TODO: maybe fix the lore for that? +//Tricord pack (now called 'regenerative serum') +/obj/item/box/fancy/cigarettes/covert/tricord + name = "pack of regenerative serum-laced Trans-Stellar Duty-frees" //#TODO: maybe fix the lore for that? + scribble = "R" -/obj/item/box/fancy/cigarettes/tricord/Initialize(ml, material_key) - . = ..() - //Reset the name to the default cig pack. Done for codex reasons, since it indexes things by initial names - var/obj/item/box/fancy/cigarettes/C = /obj/item/box/fancy/cigarettes - if(name == initial(name)) - SetName(initial(C.name)) - if(desc == initial(desc)) - desc = "[initial(desc)] 'T' has been scribbled on it." //#TODO: maybe fix the lore for that? - -/obj/item/box/fancy/cigarettes/tricord/populate_reagents() +/obj/item/box/fancy/cigarettes/covert/tricord/populate_reagents() add_to_reagents(/decl/material/liquid/regenerator, (4 * max(1, storage?.max_storage_space))) diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index 7612a6e897b0..a6eb745f50c6 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -126,10 +126,10 @@ /obj/item/box/syndie_kit/cigarette/WillContain() return list( - /obj/item/box/fancy/cigarettes/flash_powder = 2, - /obj/item/box/fancy/cigarettes/chemsmoke = 2, - /obj/item/box/fancy/cigarettes/mindbreak, - /obj/item/box/fancy/cigarettes/tricord, + /obj/item/box/fancy/cigarettes/covert/flash_powder = 2, + /obj/item/box/fancy/cigarettes/covert/chemsmoke = 2, + /obj/item/box/fancy/cigarettes/covert/mindbreak, + /obj/item/box/fancy/cigarettes/covert/tricord, /obj/item/flame/fuelled/lighter/zippo/random, ) diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 34871f8e955d..272d29948511 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -65,7 +65,7 @@ pixel_y = 24 }, /obj/item/flashlight/lamp/green, -/obj/item/box/fancy/cigarettes/tricord, +/obj/item/box/fancy/cigarettes/covert/tricord, /turf/floor/carpet/blue, /area/ministation/medical) "at" = ( diff --git a/tools/map_migrations/9999_cigarettes.txt b/tools/map_migrations/9999_cigarettes.txt new file mode 100644 index 000000000000..875e6404ad48 --- /dev/null +++ b/tools/map_migrations/9999_cigarettes.txt @@ -0,0 +1,4 @@ +/obj/item/box/fancy/cigarettes/flash_powder : /obj/item/box/fancy/cigarettes/covert/flash_powder{@OLD} +/obj/item/box/fancy/cigarettes/chemsmoke : /obj/item/box/fancy/cigarettes/covert/flash_powder{@OLD} +/obj/item/box/fancy/cigarettes/mindbreak : /obj/item/box/fancy/cigarettes/covert/flash_powder{@OLD} +/obj/item/box/fancy/cigarettes/tricord : /obj/item/box/fancy/cigarettes/covert/flash_powder{@OLD} \ No newline at end of file From abd37d7a5e8ef767ee0bac141bae412a4fd3db49 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 14 May 2025 20:47:25 -0400 Subject: [PATCH 0656/1331] Fix get_other_examine_strings being expensive due to string ops --- code/modules/mob/examine.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/examine.dm b/code/modules/mob/examine.dm index 164b4d8b96b4..d5c9f02bb7b0 100644 --- a/code/modules/mob/examine.dm +++ b/code/modules/mob/examine.dm @@ -28,7 +28,7 @@ . += SPAN_WARNING("[pronouns.He] [pronouns.is] [html_icon(buckled)] buckled to [buckled]!") /mob/proc/get_other_examine_strings(mob/user, distance, infix, suffix, hideflags, decl/pronouns/pronouns) - return + return list() // We add a default parameter here for hidden inventory flags. /mob/get_examine_header(mob/user, distance, infix, suffix, hideflags) From acb56c5e96f4c33e8fa67c2dc948cf81018ae51c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 13 May 2025 20:41:33 -0400 Subject: [PATCH 0657/1331] Make chameleon disguise verbs use a define to avoid code duplication --- .../objects/items/weapons/storage/backpack.dm | 4 + code/modules/clothing/chameleon.dm | 258 +++++------------- 2 files changed, 71 insertions(+), 191 deletions(-) diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 3ba1614419cd..2b3e166a2d24 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -13,6 +13,10 @@ storage = /datum/storage/backpack material = /decl/material/solid/organic/leather/synth +/obj/item/backpack/get_associated_equipment_slots() + . = ..() + LAZYDISTINCTADD(., slot_back_str) + //Cannot be washed :( /obj/item/backpack/can_contaminate() return FALSE diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 39ea1c743a97..50adae6297ca 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -2,7 +2,19 @@ //**Cham Jumpsuit** //***************** +#define CHAMELEON_VERB(TYPE, VERB_NAME)\ +##TYPE/verb/change(picked in disguise_choices){ \ + set name = VERB_NAME; \ + set category = "Chameleon Items"; \ + set src in usr; \ + if(usr.incapacitated()) return; \ + if(!ispath(disguise_choices[picked])) return; \ + disguise(disguise_choices[picked], usr); \ + update_clothing_icon(); \ +} + /obj/item/proc/disguise(var/newtype, var/mob/user) + SHOULD_NOT_OVERRIDE(TRUE) if(user && CanPhysicallyInteract(user)) return OnDisguise(atom_info_repository.get_instance_of(newtype), user) return FALSE @@ -20,7 +32,7 @@ body_parts_covered = copy.body_parts_covered sprite_sheets = copy.sprite_sheets?.Copy() flags_inv = copy.flags_inv - set_gender(copy.gender) + set_gender(copy.gender) // mostly for plural/neuter, e.g. "some prayer beads" versus "a necklace" /proc/generate_chameleon_choices(var/basetype) . = list() @@ -44,26 +56,18 @@ icon = 'icons/clothing/pants/slacks.dmi' origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/pants/chameleon/Initialize() . = ..() - if(!clothing_choices) + if(!disguise_choices) var/static/list/clothing_types = list( /obj/item/clothing/pants, /obj/item/clothing/skirt ) - clothing_choices = generate_chameleon_choices(clothing_types) - -/obj/item/clothing/pants/chameleon/verb/change(picked in clothing_choices) - set name = "Change Pants Appearance" - set category = "Chameleon Items" - set src in usr - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - disguise(clothing_choices[picked], usr) - update_clothing_icon() + disguise_choices = generate_chameleon_choices(clothing_types) + +CHAMELEON_VERB(/obj/item/clothing/pants/chameleon, "Change Pants Appearance") /obj/item/clothing/shirt/chameleon name = "dress shirt" @@ -71,25 +75,17 @@ icon = 'icons/clothing/shirts/button_up.dmi' origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/shirt/chameleon/Initialize() . = ..() - if(!clothing_choices) + if(!disguise_choices) var/static/list/clothing_types = list( /obj/item/clothing/shirt ) - clothing_choices = generate_chameleon_choices(clothing_types) - -/obj/item/clothing/shirt/chameleon/verb/change(picked in clothing_choices) - set name = "Change Shirt Appearance" - set category = "Chameleon Items" - set src in usr - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - disguise(clothing_choices[picked], usr) - update_clothing_icon() + disguise_choices = generate_chameleon_choices(clothing_types) + +CHAMELEON_VERB(/obj/item/clothing/shirt/chameleon, "Change Shirt Appearance") //starts off as a jumpsuit /obj/item/clothing/jumpsuit/chameleon @@ -99,29 +95,19 @@ origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON bodytype_equip_flags = null - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/jumpsuit/chameleon/Initialize() . = ..() - if(!clothing_choices) + if(!disguise_choices) var/static/list/clothing_types = list( /obj/item/clothing/jumpsuit, /obj/item/clothing/dress, /obj/item/clothing/costume ) - clothing_choices = generate_chameleon_choices(clothing_types) - -/obj/item/clothing/jumpsuit/chameleon/verb/change(picked in clothing_choices) - set name = "Change Jumpsuit Appearance" - set category = "Chameleon Items" - set src in usr + disguise_choices = generate_chameleon_choices(clothing_types) - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/jumpsuit/chameleon, "Change Jumpsuit Appearance") //***************** //**Chameleon Hat** @@ -135,24 +121,14 @@ body_parts_covered = 0 item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON bodytype_equip_flags = null - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/head/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/clothing/head) - -/obj/item/clothing/head/chameleon/verb/change(picked in clothing_choices) - set name = "Change Hat/Helmet Appearance" - set category = "Chameleon Items" - set src in usr - - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/clothing/head) - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/head/chameleon, "Change Hat/Helmet Appearance") //****************** //**Chameleon Suit** @@ -165,24 +141,14 @@ origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON bodytype_equip_flags = null - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/suit/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/clothing/suit) + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/clothing/suit) -/obj/item/clothing/suit/chameleon/verb/change(picked in clothing_choices) - set name = "Change Oversuit Appearance" - set category = "Chameleon Items" - set src in usr - - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/suit/chameleon, "Change Oversuit Appearance") //******************* //**Chameleon Shoes** @@ -194,24 +160,14 @@ origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON bodytype_equip_flags = null - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/shoes/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/clothing/shoes) - -/obj/item/clothing/shoes/chameleon/verb/change(picked in clothing_choices) - set name = "Change Footwear Appearance" - set category = "Chameleon Items" - set src in usr + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/clothing/shoes) - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/shoes/chameleon, "Change Footwear Appearance") //********************** //**Chameleon Backpack** @@ -222,28 +178,14 @@ origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON icon = 'icons/obj/items/storage/backpack/backpack.dmi' - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/backpack/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/backpack) - -/obj/item/backpack/chameleon/verb/change(picked in clothing_choices) - set name = "Change Backpack Appearance" - set category = "Chameleon Items" - set src in usr - - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/backpack) - disguise(clothing_choices[picked], usr) - - //so our overlays update. - if (ismob(src.loc)) - var/mob/M = src.loc - M.update_equipment_overlay(slot_back_str) +CHAMELEON_VERB(/obj/item/backpack/chameleon, "Change Backpack Appearance") //******************** //**Chameleon Gloves** @@ -258,24 +200,14 @@ origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON bodytype_equip_flags = null - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/gloves/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/clothing/gloves) - -/obj/item/clothing/gloves/chameleon/verb/change(picked in clothing_choices) - set name = "Change Gloves Appearance" - set category = "Chameleon Items" - set src in usr + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/clothing/gloves) - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/gloves/chameleon, "Change Gloves Appearance") //****************** //**Chameleon Mask** @@ -288,24 +220,14 @@ origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON bodytype_equip_flags = null - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/mask/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/clothing/mask) - -/obj/item/clothing/mask/chameleon/verb/change(picked in clothing_choices) - set name = "Change Mask Appearance" - set category = "Chameleon Items" - set src in usr + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/clothing/mask) - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/mask/chameleon, "Change Mask Appearance") //********************* //**Chameleon Glasses** @@ -318,24 +240,14 @@ origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON bodytype_equip_flags = null - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/clothing/glasses/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/clothing/glasses) - -/obj/item/clothing/glasses/chameleon/verb/change(picked in clothing_choices) - set name = "Change Glasses Appearance" - set category = "Chameleon Items" - set src in usr - - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/clothing/glasses) - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/glasses/chameleon, "Change Glasses Appearance") //********************* //**Chameleon Headset** @@ -347,27 +259,14 @@ desc = "An updated, modular intercom that fits over the head. This one seems to have a small dial on it." origin_tech = @'{"esoteric":3}' item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - var/static/list/clothing_choices + var/static/list/disguise_choices /obj/item/radio/headset/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(/obj/item/radio/headset) + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/radio/headset) -/obj/item/radio/headset/chameleon/verb/change(picked in clothing_choices) - set name = "Change Headset Appearance" - set category = "Chameleon Items" - set src in usr - - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - - disguise(clothing_choices[picked], usr) - if (ismob(src.loc)) - var/mob/M = src.loc - M.update_equipment_overlay(slot_l_ear_str, FALSE) - M.update_equipment_overlay(slot_r_ear_str) +CHAMELEON_VERB(/obj/item/radio/headset/chameleon, "Change Headset Appearance") //*********************** //**Chameleon Accessory** @@ -381,7 +280,7 @@ item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON w_class = ITEM_SIZE_SMALL fallback_slot = slot_wear_mask_str - var/static/list/clothing_choices + var/static/list/disguise_choices var/static/list/decor_types = list( /obj/item/clothing/neck, /obj/item/clothing/badge, @@ -394,19 +293,10 @@ /obj/item/clothing/chameleon/Initialize() . = ..() - if(!clothing_choices) - clothing_choices = generate_chameleon_choices(get_non_abstract_types(decor_types)) - -/obj/item/clothing/chameleon/verb/change(picked in clothing_choices) - set name = "Change Accessory Appearance" - set category = "Chameleon Items" - set src in usr + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(get_non_abstract_types(decor_types)) - if (!(usr.incapacitated())) - if(!ispath(clothing_choices[picked])) - return - disguise(clothing_choices[picked], usr) - update_clothing_icon() //so our overlays update. +CHAMELEON_VERB(/obj/item/clothing/chameleon, "Change Accessory Appearance") //***************** //**Chameleon Gun** @@ -428,12 +318,12 @@ max_shots = 50 var/obj/item/projectile/copy_projectile - var/static/list/gun_choices + var/static/list/disguise_choices /obj/item/gun/energy/chameleon/Initialize() . = ..() - if(!gun_choices) - gun_choices = generate_chameleon_choices(/obj/item/gun) + if(!disguise_choices) + disguise_choices = generate_chameleon_choices(/obj/item/gun) /obj/item/gun/energy/chameleon/consume_next_projectile() var/obj/item/projectile/P = ..() @@ -466,18 +356,4 @@ copy_projectile = null //charge_meter = 0 -/obj/item/gun/energy/chameleon/verb/change(picked in gun_choices) - set name = "Change Gun Appearance" - set category = "Chameleon Items" - set src in usr - - if (!(usr.incapacitated())) - if(!ispath(gun_choices[picked])) - return - - disguise(gun_choices[picked], usr) - - //so our overlays update. - if (ismob(src.loc)) - var/mob/M = src.loc - M.update_inhand_overlays() \ No newline at end of file +CHAMELEON_VERB(/obj/item/gun/energy/chameleon, "Change Gun Appearance") \ No newline at end of file From feff427b3e4903572370e33bcdfa6b9f213048fe Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 14 May 2025 23:53:39 -0400 Subject: [PATCH 0658/1331] Split human get_other_examine_strings() into separate handlers --- code/__defines/mobs.dm | 3 + code/_helpers/cmp.dm | 3 + code/_macros.dm | 1 + code/game/objects/structures/fountain.dm | 16 + code/modules/mob/examine.dm | 7 +- code/modules/mob/living/human/examine.dm | 395 ++++++++++-------- .../mob/living/human/human_attackhand.dm | 1 + .../modules/mob/living/human/human_defines.dm | 3 - .../mob/living/human/human_examine_decl.dm | 7 +- .../pronouns/pronouns_second_person.dm | 13 + mods/content/matchmaking/matchmaker.dm | 7 +- nebula.dme | 1 + 12 files changed, 274 insertions(+), 183 deletions(-) create mode 100644 code/modules/pronouns/pronouns_second_person.dm diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index c05a1c545c0a..e95fac6faa6e 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -346,7 +346,10 @@ var/global/list/dexterity_levels = list( #define MOB_ICON_HAS_GIB_STATE BITFLAG(5) #define MOB_ICON_HAS_DUST_STATE BITFLAG(6) #define MOB_ICON_HAS_PARALYZED_STATE BITFLAG(7) + +// Additional pronoun sets. #define NEUTER_ANIMATE "animate singular neutral" +#define SECOND_PERSON_SINGULAR "second person singular" // Equipment Overlays Indices // #define HO_CONDITION_LAYER 1 diff --git a/code/_helpers/cmp.dm b/code/_helpers/cmp.dm index 902bc95518be..70bb6dc061fa 100644 --- a/code/_helpers/cmp.dm +++ b/code/_helpers/cmp.dm @@ -86,6 +86,9 @@ /proc/cmp_fusion_reaction_des(var/decl/fusion_reaction/A, var/decl/fusion_reaction/B) return B.priority - A.priority +/proc/cmp_human_examine_priority(decl/human_examination/a, decl/human_examination/b) + return a.priority - b.priority + /proc/cmp_program(var/datum/computer_file/program/A, var/datum/computer_file/program/B) return cmp_text_asc(A.filedesc, B.filedesc) diff --git a/code/_macros.dm b/code/_macros.dm index fba1b139fae3..62f7629af378 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -174,6 +174,7 @@ #define SPAN_PALEPINK(X) SPAN_CLASS("font_palepink", X) #define SPAN_SINISTER(X) SPAN_CLASS("sinister", X) #define SPAN_MODERATE(X) SPAN_CLASS("moderate", X) +#define SPAN_DEADSAY(X) SPAN_CLASS("deadsay", X) // placeholders #define SPAN_GOOD(X) SPAN_GREEN(X) #define SPAN_NEUTRAL(X) SPAN_BLUE(X) diff --git a/code/game/objects/structures/fountain.dm b/code/game/objects/structures/fountain.dm index c92c9d360b1f..f596d169c6e0 100644 --- a/code/game/objects/structures/fountain.dm +++ b/code/game/objects/structures/fountain.dm @@ -85,6 +85,22 @@ used = TRUE desc = "The water flows beautifully from the spout, but the water in the pool does not ripple." +/mob/living/human + /// Used by the Fountain of Youth point of interest for on-examine messages. + var/became_older + /// Used by the Fountain of Youth point of interest for on-examine messages. + var/became_younger + +/decl/human_examination/fountain + priority = /decl/human_examination/graffiti::priority + 0.5 // just squeeze it in there + +/decl/human_examination/fountain/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + . = list() + if(source.became_younger) + . += "[pronouns.He] look[pronouns.s] a lot younger than you remember." + if(source.became_older) + . += "[pronouns.He] look[pronouns.s] a lot older than you remember." + /obj/structure/fountain/mundane name = "fountain" desc = "A beautifully constructed fountain." diff --git a/code/modules/mob/examine.dm b/code/modules/mob/examine.dm index 164b4d8b96b4..e501bd418632 100644 --- a/code/modules/mob/examine.dm +++ b/code/modules/mob/examine.dm @@ -4,6 +4,11 @@ return GET_DECL(/decl/pronouns) return get_pronouns() +/mob/proc/get_visible_pronouns_for_viewer(mob/viewer, hideflags) + if(viewer == src) + return GET_DECL(/decl/pronouns/self) + return get_visible_pronouns(hideflags) + /mob/proc/get_equipment_visibility() . = 0 for(var/obj/item/thing in get_equipped_items(include_carried = FALSE)) @@ -46,7 +51,7 @@ hideflags |= HIDEEARS // Show our equipment, held items, desc, etc. - var/decl/pronouns/pronouns = get_visible_pronouns(hideflags) + var/decl/pronouns/pronouns = get_visible_pronouns_for_viewer(user, hideflags) // handles second-person if src == user var/list/examine_items = get_examined_worn_held_items(user, distance, infix, suffix, hideflags, pronouns) if(length(examine_items)) . += examine_items diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index da4bb651a8d5..d33940b0c301 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -1,158 +1,166 @@ /mob/living/human/get_examine_header(mob/user, distance, infix, suffix, hideflags) SHOULD_CALL_PARENT(FALSE) - . = list("*---------*
    [user == src ? "You are" : "This is"] [name]") + . = list(SPAN_INFO("*---------*")) + var/list/mob_intro = "[user == src ? "You are" : "This is"] [name]" if(!(hideflags & HIDEJUMPSUIT) || !(hideflags & HIDEFACE)) var/species_name = "\improper " if(isSynthetic() && species.cyborg_noun) species_name += "[species.cyborg_noun] [species.name]" else species_name += "[species.name]" - . += ", \a [species_name]![(user.can_use_codex() && SScodex.get_codex_entry(get_codex_value(user))) ? SPAN_NOTICE(" \[?\]") : ""]" + mob_intro += ", \a [species_name]![(user.can_use_codex() && SScodex.get_codex_entry(get_codex_value(user))) ? SPAN_NOTICE(" \[?\]") : ""]" + . += SPAN_INFO(JOINTEXT(mob_intro)) var/extra_species_text = species.get_additional_examine_text(src) if(extra_species_text) - . += "
    [extra_species_text]" + . += "[extra_species_text]" var/show_descs = show_descriptors_to(user) if(show_descs) - . += "
    [jointext(show_descs, "
    ")]
    " + . += SPAN_INFO(jointext(show_descs, "
    ")) var/print_flavour = print_flavor_text() if(print_flavour) - . += "
    *---------*" - . += "
    [print_flavour]" - . += "
    *---------*" - . = list(jointext(., null)) + . += SPAN_INFO("*---------*") + . += SPAN_INFO("[print_flavour]") + . += SPAN_INFO("*---------*") + . = list(jointext(., "
    ")) + +/decl/human_examination/jitters/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + var/jitteriness = GET_STATUS(source, STAT_JITTER) + switch(jitteriness) + if(300 to INFINITY) + return SPAN_DANGER("[pronouns.He] [pronouns.is] convulsing violently!") + if(200 to 300) + return SPAN_WARNING("[pronouns.He] [pronouns.is] extremely jittery.") + if(100 to 200) + return SPAN_WARNING("[pronouns.He] [pronouns.is] twitching ever so slightly.") + else + pass() -/mob/living/human/get_other_examine_strings(mob/user, distance, infix, suffix, hideflags, decl/pronouns/pronouns) +/decl/human_examination/disfigured + priority = /decl/human_examination/jitters::priority + 1 - . = ..() +/decl/human_examination/disfigured/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + //Disfigured face + if(hideflags & HIDEFACE) //Disfigurement only matters for the head currently. + return + var/obj/item/organ/external/limb = GET_EXTERNAL_ORGAN(source, BP_HEAD) + if(!limb || !(limb.status & ORGAN_DISFIGURED)) //Check to see if we even have a head and if the head's disfigured. + return + if(limb.species) //Check to make sure we have a species + return limb.species.disfigure_msg(source) + else //Just in case they lack a species for whatever reason. + return SPAN_WARNING("[pronouns.His] face is horribly mangled!") - var/self_examine = (user == src) - var/use_He = self_examine ? "You" : pronouns.He - var/use_he = self_examine ? "you" : pronouns.he - var/use_His = self_examine ? "Your" : pronouns.His - var/use_his = self_examine ? "your" : pronouns.his - var/use_is = self_examine ? "are" : pronouns.is - var/use_does = self_examine ? "do" : pronouns.does - var/use_has = self_examine ? "have" : pronouns.has - var/use_him = self_examine ? "you" : pronouns.him - var/use_looks = self_examine ? "look" : "looks" - - //Jitters - var/jitteriness = GET_STATUS(src, STAT_JITTER) - if(jitteriness >= 300) - . += "[use_He] [use_is] convulsing violently!" - else if(jitteriness >= 200) - . += "[use_He] [use_is] extremely jittery." - else if(jitteriness >= 100) - . += "[use_He] [use_is] twitching ever so slightly." +/decl/human_examination/stat + priority = /decl/human_examination/disfigured::priority + 1 - //Disfigured face - if(!(hideflags & HIDEFACE)) //Disfigurement only matters for the head currently. - var/obj/item/organ/external/limb = GET_EXTERNAL_ORGAN(src, BP_HEAD) - if(limb && (limb.status & ORGAN_DISFIGURED)) //Check to see if we even have a head and if the head's disfigured. - if(limb.species) //Check to make sure we have a species - . += limb.species.disfigure_msg(src) - else //Just in case they lack a species for whatever reason. - . += "[use_His] face is horribly mangled!" - - //splints - for(var/organ in list(BP_L_LEG, BP_R_LEG, BP_L_ARM, BP_R_ARM)) - var/obj/item/organ/external/limb = GET_EXTERNAL_ORGAN(src, organ) - if(limb && limb.splinted && limb.splinted.loc == limb) - . += "[use_He] [use_has] \a [limb.splinted] on [use_his] [limb.name]!" - - if (stat) - . += "[use_He] [use_is]n't responding to anything around [use_him] and seems to be unconscious." - if((stat == DEAD || is_asystole() || suffocation_counter) && distance <= 3) - . += "[use_He] [use_does] not appear to be breathing." - - var/datum/reagents/touching_reagents = get_contact_reagents() +/decl/human_examination/stat/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + if (source.stat) + . = list(SPAN_WARNING("[pronouns.He] [pronouns.is]n't responding to anything around [pronouns.him] and seems to be unconscious.")) + if((source.stat == DEAD || source.is_asystole() || source.suffocation_counter) && distance <= 3) + . += SPAN_WARNING("[pronouns.He] [pronouns.does] not appear to be breathing.") + +/decl/human_examination/contact_reagents + priority = /decl/human_examination/stat::priority + 1 + +/decl/human_examination/contact_reagents/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + var/datum/reagents/touching_reagents = source.get_contact_reagents() if(touching_reagents?.total_volume >= 1) var/saturation = touching_reagents.total_volume / touching_reagents.maximum_volume if(saturation > 0.9) - . += "[use_He] [use_is] completely saturated." + . += "[pronouns.He] [pronouns.is] completely saturated." else if(saturation > 0.6) - . += "[use_He] [use_is] looking half-drowned." + . += "[pronouns.He] [pronouns.is] looking half-drowned." else if(saturation > 0.3) - . += "[use_He] [use_is] looking notably soggy." + . += "[pronouns.He] [pronouns.is] looking notably soggy." else - . += "[use_He] [use_is] looking a bit soggy." + . += "[pronouns.He] [pronouns.is] looking a bit soggy." + +/decl/human_examination/fire + priority = /decl/human_examination/contact_reagents::priority + 1 - var/fire_level = get_fire_intensity() +/decl/human_examination/fire/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + . = list() + var/fire_level = source.get_fire_intensity() if(fire_level > 0) - . += "[use_He] [use_is] looking highly flammable!" + . += "[pronouns.He] [pronouns.is] looking highly flammable!" else if(fire_level < 0) - . += "[use_He] [use_is] looking rather incombustible." + . += "[pronouns.He] [pronouns.is] looking rather incombustible." - if(is_on_fire()) - . += "[use_He] [use_is] on fire!" + if(source.is_on_fire()) + . += SPAN_WARNING("[pronouns.He] [pronouns.is] on fire!") - var/ssd_msg = species.get_ssd(src) - if(ssd_msg && (!should_have_organ(BP_BRAIN) || has_brain()) && stat != DEAD) - if(!key) - . += "[use_He] [use_is] [ssd_msg]. It doesn't look like [use_he] [use_is] waking up anytime soon." - else if(!client) - . += "[use_He] [use_is] [ssd_msg]." +/decl/human_examination/ssd + priority = /decl/human_examination/fire::priority + 1 - var/obj/item/organ/external/head/H = get_organ(BP_HEAD, /obj/item/organ/external/head) +/decl/human_examination/ssd/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + var/ssd_msg = source.species.get_ssd(source) + if(ssd_msg && (!source.should_have_organ(BP_BRAIN) || source.has_brain()) && source.stat != DEAD) + if(!source.key) + . += SPAN_DEADSAY("[pronouns.He] [pronouns.is] [ssd_msg]. It doesn't look like [pronouns.he] [pronouns.is] waking up anytime soon.") + else if(!source.client) + . += SPAN_DEADSAY("[pronouns.He] [pronouns.is] [ssd_msg].") + +// TODO: generalize and fold this into limb examine +/decl/human_examination/graffiti + priority = /decl/human_examination/ssd::priority + 1 + +/decl/human_examination/graffiti/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + var/obj/item/organ/external/head/H = source.get_organ(BP_HEAD, /obj/item/organ/external/head) if(istype(H) && H.forehead_graffiti && H.graffiti_style) - . += "[use_He] [use_has] \"[H.forehead_graffiti]\" written on [use_his] [H.name] in [H.graffiti_style]!" + . += SPAN_NOTICE("[pronouns.He] [pronouns.has] \"[H.forehead_graffiti]\" written on [pronouns.his] [H.name] in [H.graffiti_style]!") - if(became_younger) - . += "[use_He] [use_looks] a lot younger than you remember." - if(became_older) - . += "[use_He] [use_looks] a lot older than you remember." +/decl/human_examination/limb_examine + priority = /decl/human_examination/graffiti::priority + 1 +// This is still kind of a mess. TODO: /decl/organ_examination? lol +/decl/human_examination/limb_examine/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + . = list() var/list/wound_flavor_text = list() var/applying_pressure = "" var/list/shown_objects = list() var/list/hidden_bleeders = list() - var/decl/bodytype/root_bodytype = get_bodytype() + var/decl/bodytype/root_bodytype = source.get_bodytype() for(var/organ_tag in root_bodytype.has_limbs) - var/list/organ_data = root_bodytype.has_limbs[organ_tag] - var/organ_descriptor = organ_data["descriptor"] - var/obj/item/organ/external/limb = GET_EXTERNAL_ORGAN(src, organ_tag) + var/obj/item/organ/external/limb = GET_EXTERNAL_ORGAN(source, organ_tag) if(!limb) - wound_flavor_text[organ_descriptor] = "[use_He] [use_is] missing [use_his] [organ_descriptor]." + wound_flavor_text[organ_tag] = list(SPAN_DANGER("[pronouns.He] [pronouns.is] missing [pronouns.his] [organ_data["descriptor"]].")) continue - wound_flavor_text[limb.name] = "" + wound_flavor_text[limb.organ_tag] = list() - if(limb.applied_pressure == src) - applying_pressure = "[use_He] [use_is] applying pressure to [use_his] [limb.name]." + if(limb.applied_pressure == source) + applying_pressure = SPAN_INFO("[pronouns.He] [pronouns.is] applying pressure to [pronouns.his] [limb.name].") - var/obj/item/clothing/hidden - for(var/slot in global.standard_clothing_slots) - var/obj/item/clothing/C = get_equipped_item(slot) - if(istype(C) && (C.body_parts_covered & limb.body_part)) - hidden = C - break + var/obj/item/clothing/hidden = source.get_covering_equipped_item(limb.body_part) - if(hidden && user != src) + if(hidden && user != source) if(limb.status & ORGAN_BLEEDING && !(hidden.item_flags & ITEM_FLAG_THICKMATERIAL)) //not through a spacesuit if(!hidden_bleeders[hidden]) hidden_bleeders[hidden] = list() - hidden_bleeders[hidden] += limb.name + hidden_bleeders[hidden] += limb.organ_tag else - if(!isSynthetic() && BP_IS_PROSTHETIC(limb) && (limb.parent && !BP_IS_PROSTHETIC(limb.parent))) - wound_flavor_text[limb.name] = "[use_He] [use_has] a [limb.name].\n" + // TODO: Make this just report if the bodytype is different than root and parent? + // That way a robotic right arm would show up but the hand attached to it wouldn't. + if(!source.isSynthetic() && BP_IS_PROSTHETIC(limb) && (limb.parent && !BP_IS_PROSTHETIC(limb.parent))) + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.He] [pronouns.has] a [limb.name].") var/wounddesc = limb.get_wounds_desc() if(wounddesc != "nothing") - wound_flavor_text[limb.name] += "[use_He] [use_has] [wounddesc] on [use_his] [limb.name]." + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.He] [pronouns.has] [wounddesc] on [pronouns.his] [limb.name].") if(!hidden || distance <=1) if(limb.is_dislocated()) - wound_flavor_text[limb.name] += "[use_His] [limb.joint] is dislocated!
    " + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.His] [limb.joint] is dislocated!") if(((limb.status & ORGAN_BROKEN) && limb.brute_dam > limb.min_broken_damage) || (limb.status & ORGAN_MUTATED)) - wound_flavor_text[limb.name] += "[use_His] [limb.name] is dented and swollen!
    " + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.His] [limb.name] is dented and swollen!") if(limb.status & ORGAN_DEAD) if(BP_IS_PROSTHETIC(limb) || BP_IS_CRYSTAL(limb)) - wound_flavor_text[limb.name] += "[use_His] [limb.name] is irrecoverably damaged!" + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.His] [limb.name] is irrecoverably damaged!") else - wound_flavor_text[limb.name] += "[use_His] [limb.name] is grey and necrotic!
    " + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.His] [limb.name] is grey and necrotic!") else if((limb.brute_dam + limb.burn_dam) >= limb.max_damage && limb.germ_level >= INFECTION_LEVEL_TWO) - wound_flavor_text[limb.name] += "[use_His] [limb.name] is likely beyond saving, and has begun to decay!" + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.His] [limb.name] is likely beyond saving, and has begun to decay!") for(var/datum/wound/wound in limb.wounds) var/list/embedlist = wound.embedded_objects @@ -165,105 +173,138 @@ else if(!parsedembed.Find("multiple [embedded.name]")) parsedembed.Remove(embedded.name) parsedembed.Add("multiple "+embedded.name) - wound_flavor_text["[limb.name]"] += "The [wound.desc] on [use_his] [limb.name] has \a [english_list(parsedembed, and_text = " and a ", comma_text = ", a ")] sticking out of it!" + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("The [wound.desc] on [pronouns.his] [limb.name] has \a [english_list(parsedembed, and_text = " and a ", comma_text = ", a ")] sticking out of it!") + + if(limb.splinted && limb.splinted.loc == limb) + wound_flavor_text[limb.organ_tag] += SPAN_WARNING("[pronouns.He] [pronouns.has] \a [limb.splinted] on [pronouns.his] [limb.name]!") + for(var/hidden in hidden_bleeders) - wound_flavor_text[hidden] = "[use_He] [use_has] blood soaking through [hidden] around [use_his] [english_list(hidden_bleeders[hidden])]!" + wound_flavor_text[hidden] = SPAN_WARNING("[pronouns.He] [pronouns.has] blood soaking through [hidden] around [pronouns.his] [english_list(hidden_bleeders[hidden])]!") - . += "" - for(var/limb in wound_flavor_text) - . += wound_flavor_text[limb] - . += "" + for(var/section in wound_flavor_text) // originally named limb, but can also include clothes + if(length(wound_flavor_text[section])) + . += jointext(wound_flavor_text[section], "
    ") - for(var/obj/implant in get_visible_implants(0)) + if(applying_pressure) + . += applying_pressure + + // TODO: move this into the limb for loop? + for(var/obj/implant in source.get_visible_implants(0)) if(implant in shown_objects) continue - . += "[src] [use_has] \a [implant.name] sticking out of [use_his] flesh!" - if(digitalcamo) - . += "[use_He] [use_is] repulsively uncanny!" + . += SPAN_DANGER("[source] [pronouns.has] \a [implant.name] sticking out of [pronouns.his] flesh!") - if(hasHUD(user, HUD_SECURITY)) - var/perpname = "wot" - var/criminal = "None" +/decl/human_examination/digicamo + priority = /decl/human_examination/limb_examine::priority + 1 - var/obj/item/card/id/check_id = GetIdCard() - if(istype(check_id)) - perpname = check_id.registered_name - else - perpname = src.name +/decl/human_examination/digicamo/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + if(source.digitalcamo) + return SPAN_WARNING("[pronouns.He] [pronouns.is] repulsively uncanny!") - if(perpname) - var/datum/computer_network/network = user.getHUDnetwork(HUD_SECURITY) - if(network) - var/datum/computer_file/report/crew_record/R = network.get_crew_record_by_name(perpname) - if(R) - criminal = R.get_criminalStatus() +/decl/human_examination/hud + priority = /decl/human_examination/digicamo::priority + 1 - . += "Criminal status: \[[criminal]\]" - . += "Security records: \[View\]" - - if(hasHUD(user, HUD_MEDICAL)) - var/perpname = "wot" - var/medical = "None" +/decl/human_examination/hud/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + var/perpname = source.get_authentification_name(source.get_face_name()) + if(!perpname) + return - var/obj/item/card/id/check_id = GetIdCard() - if(istype(check_id)) - perpname = check_id.registered_name - else - perpname = src.name + if(hasHUD(user, HUD_SECURITY)) + var/datum/computer_network/network = user.getHUDnetwork(HUD_SECURITY) + if(network) + var/datum/computer_file/report/crew_record/R = network.get_crew_record_by_name(perpname) + LAZYINITLIST(.) + . += "Criminal status: \[[R?.get_criminalStatus() || "None"]\]" + . += "Security records: \[View\]" + if(hasHUD(user, HUD_MEDICAL)) var/datum/computer_network/network = user.getHUDnetwork(HUD_MEDICAL) if(network) var/datum/computer_file/report/crew_record/R = network.get_crew_record_by_name(perpname) - if(R) - medical = R.get_status() + LAZYINITLIST(.) + . += "Physical status: \[[R?.get_status() || "None"]\]" + . += "Medical records: \[View\]" - . += "Physical status: \[[medical]\]" - . += "Medical records: \[View\]" +/decl/human_examination/pose + priority = /decl/human_examination/hud::priority + 1 + section_prefix = "*---------*" +/decl/human_examination/pose/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + if (!source.pose) + return + var/treated_pose = trim(source.handle_autopunctuation(source.pose)) + // if the pose starts with is, are, do, does, or doesn't, apply basic verb correction + if(starts_with(treated_pose, "is ")) // if the pose starts with is + treated_pose = "[pronouns.is] [copytext(treated_pose, 1, 4)]" + if(starts_with(treated_pose, "are ")) // if the pose starts with are + treated_pose = "[pronouns.is] [copytext(treated_pose, 1, 5)]" + else if(starts_with(treated_pose, "does ")) + treated_pose = "[pronouns.does] [copytext(treated_pose, 1, 6)]" + else if(starts_with(treated_pose, "do ")) + treated_pose = "[pronouns.does] [copytext(treated_pose, 1, 4)]" + else if(starts_with(treated_pose, "doesn't ")) + treated_pose = "[pronouns.does]n't [copytext(treated_pose, 1, 9)]" + else if(starts_with(treated_pose, "don't ")) + treated_pose = "[pronouns.does]n't [copytext(treated_pose, 1, 7)]" + return "[pronouns.He] [source.handle_autopunctuation(source.pose)]" + +/decl/human_examination/comments + priority = /decl/human_examination/pose::priority + 99 // OOC info should show up pretty late. + section_prefix = "*---------*" + section_postfix = "*---------*" // this only applies if there is something after it + +/decl/human_examination/comments/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) // Show IC/OOC info if available. - if(comments_record_id) - var/datum/character_information/comments = SScharacter_info.get_record(comments_record_id) - if(comments?.show_info_on_examine && (comments.ic_info || comments.ooc_info)) - . += "*---------*" - if(comments.ic_info) - if(length(comments.ic_info) <= 40) - . += "IC Info:" - . += "    [comments.ic_info]" - else - . += "IC Info:" - . += "    [copytext_preserve_html(comments.ic_info,1,37)]... More..." - if(comments.ooc_info) - if(length(comments.ooc_info) <= 40) - . += "OOC Info:" - . += "    [comments.ooc_info]" - else - . += "OOC Info:" - . += "    [copytext_preserve_html(comments.ooc_info,1,37)]... More..." - - . += "*---------*
    " - . += applying_pressure - - if (pose) - if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) - pose = addtext(pose,".") //Makes sure all emotes end with a period. - . += "[use_He] [pose]" - - var/list/human_examines = decls_repository.get_decls_of_subtype(/decl/human_examination) - for(var/exam in human_examines) - var/decl/human_examination/examiner = human_examines[exam] - var/adding_text = examiner.do_examine(user, distance, src) - if(adding_text) - . += adding_text - - // Foul, todo fix this - if(stat && ishuman(user) && !user.incapacitated() && Adjacent(user)) - spawn(0) - user.visible_message("\The [user] checks \the [src]'s pulse.", "You check \the [src]'s pulse.") - if(do_after(user, 15, src)) - if(get_pulse() == PULSE_NONE) - to_chat(user, "[use_He] [use_has] no pulse.") - else - to_chat(user, "[use_He] [use_has] a pulse!") + if(!source.comments_record_id) + return + var/datum/character_information/comments = SScharacter_info.get_record(source.comments_record_id) + if(comments?.show_info_on_examine && (comments.ic_info || comments.ooc_info)) + if(comments.ic_info) + if(length(comments.ic_info) <= 40) + . += "IC Info:" + . += "    [comments.ic_info]" + else + . += "IC Info:" + . += "    [copytext_preserve_html(comments.ic_info,1,37)]... More..." + if(comments.ooc_info) + if(length(comments.ooc_info) <= 40) + . += "OOC Info:" + . += "    [comments.ooc_info]" + else + . += "OOC Info:" + . += "    [copytext_preserve_html(comments.ooc_info,1,37)]... More..." + +/mob/living/human/get_other_examine_strings(mob/user, distance, infix, suffix, hideflags, decl/pronouns/pronouns) + . = ..() + var/static/list/priority_examine_decls = sortTim(decls_repository.get_decls_of_subtype_unassociated(/decl/human_examination), /proc/cmp_human_examine_priority) + var/last_divider = null + for(var/decl/human_examination/examiner in priority_examine_decls) + var/adding_text = examiner.do_examine(user, distance, src, hideflags, pronouns) + if(!LAZYLEN(adding_text)) + continue + if(last_divider) // insert the divider from the last entry + . += last_divider + else if(length(.)) // we already have prior entries, insert our prefix + . += examiner.section_prefix + . += adding_text + last_divider = examiner.section_postfix + +/mob/living/human/examined_by(mob/user, distance, infix, suffix) + . = ..() + if(!stat || !ishuman(user) || user.incapacitated() || !Adjacent(user)) + return + INVOKE_ASYNC(src, PROC_REF(check_heartbeat), user) + +/mob/living/human/proc/check_heartbeat(mob/living/human/user) + if(!stat || !ishuman(user) || user.incapacitated() || !Adjacent(user)) + return + user.visible_message("\The [user] checks \the [src]'s pulse.", "You check \the [src]'s pulse.") + if(do_after(user, 1.5 SECONDS, src)) + var/decl/pronouns/seen_pronouns = get_pronouns() + if(get_pulse() == PULSE_NONE) + to_chat(user, SPAN_DEADSAY("[seen_pronouns.He] [seen_pronouns.has] no pulse.")) + else + to_chat(user, SPAN_DEADSAY("[seen_pronouns.He] [seen_pronouns.has] a pulse!")) //Helper procedure. Called by /mob/living/human/examined_by() and /mob/living/human/Topic() to determine HUD access to security and medical records. /proc/hasHUD(mob/M, hudtype) diff --git a/code/modules/mob/living/human/human_attackhand.dm b/code/modules/mob/living/human/human_attackhand.dm index 18e93dc7dea6..dfcc31cb4120 100644 --- a/code/modules/mob/living/human/human_attackhand.dm +++ b/code/modules/mob/living/human/human_attackhand.dm @@ -382,6 +382,7 @@ user.visible_message( \ SPAN_NOTICE("\The [user] starts applying pressure to \the [src]'s [organ.name]!"), \ SPAN_NOTICE("You start applying pressure to \the [src]'s [organ.name]!")) + // TODO: refactor applying pressure to use grabs instead? would probably require making grabs locked to the zone they were started on spawn(0) organ.applied_pressure = user diff --git a/code/modules/mob/living/human/human_defines.dm b/code/modules/mob/living/human/human_defines.dm index 8108d6b8d3ee..9983f7eb7a4b 100644 --- a/code/modules/mob/living/human/human_defines.dm +++ b/code/modules/mob/living/human/human_defines.dm @@ -49,9 +49,6 @@ var/obj/machinery/machine_visual var/shock_stage var/rounded_shock_stage - /// vars for fountain of youth examine lines - var/became_older - var/became_younger /// var for caching last pain calc to avoid looping through organs over and over and over again var/last_pain var/vital_organ_missing_time diff --git a/code/modules/mob/living/human/human_examine_decl.dm b/code/modules/mob/living/human/human_examine_decl.dm index b791442cea3b..bad406e8b985 100644 --- a/code/modules/mob/living/human/human_examine_decl.dm +++ b/code/modules/mob/living/human/human_examine_decl.dm @@ -1,4 +1,9 @@ /decl/human_examination //This is essentially a stub-method for modpacks to be able to add onto the human examination stuff due to ... messy stuff. + var/priority = 0 + /// If non-null, this is inserted before this entry if there is not already a postfix before it. + var/section_prefix = null + /// If non-null, this is inserted after this entry if there exist entries after it. + var/section_postfix = null -/decl/human_examination/proc/do_examine(var/user, var/distance, var/source) //These can either return text, or should return nothing at all if you're doing to_chat() +/decl/human_examination/proc/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) //These can either return text, or should return nothing at all if you're doing to_chat() return diff --git a/code/modules/pronouns/pronouns_second_person.dm b/code/modules/pronouns/pronouns_second_person.dm new file mode 100644 index 000000000000..4860a1afaaa5 --- /dev/null +++ b/code/modules/pronouns/pronouns_second_person.dm @@ -0,0 +1,13 @@ +/decl/pronouns/self + name = SECOND_PERSON_SINGULAR + He = "You" + he = "you" + His = "Your" + his = "your" + him = "you" // yourself? you? + has = "have" + is = "are" + does = "do" + self = "yourself" + s = "" + es = "" \ No newline at end of file diff --git a/mods/content/matchmaking/matchmaker.dm b/mods/content/matchmaking/matchmaker.dm index 8f6bb706cb5d..70e424a3dfde 100644 --- a/mods/content/matchmaking/matchmaker.dm +++ b/mods/content/matchmaking/matchmaker.dm @@ -61,7 +61,12 @@ if(relation.holder == holder && relation.other && relation.other.holder == target && (relation.finalized || !finalized_only)) . += relation -/decl/human_examination/matchmaking/do_examine(var/mob/living/user, var/distance, var/mob/living/source) //These can either return text, or should return nothing at all if you're doing to_chat() +/decl/human_examination/matchmaking + // Show up after pose. + priority = /decl/human_examination/pose::priority + 1 + +// These should return null, text, or a list of text strings. +/decl/human_examination/matchmaking/do_examine(var/mob/living/user, var/distance, var/mob/living/human/source, hideflags, decl/pronouns/pronouns) if(!istype(source) || !istype(user)) return if(!source.mind || !user.mind || source.name != source.real_name) diff --git a/nebula.dme b/nebula.dme index 8a77c79025d6..b01e21e58889 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3594,6 +3594,7 @@ #include "code\modules\pronouns\pronouns_female.dm" #include "code\modules\pronouns\pronouns_male.dm" #include "code\modules\pronouns\pronouns_neuter.dm" +#include "code\modules\pronouns\pronouns_second_person.dm" #include "code\modules\radiation\radiation.dm" #include "code\modules\random_map\_random_map_setup.dm" #include "code\modules\random_map\random_map.dm" From aedd1547d67561440ff69b7c89bdec89b1d63991 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 13 May 2025 17:32:19 -0400 Subject: [PATCH 0659/1331] Add laser interaction to xenoarch crystal --- .../artifacts/standalone/crystal.dm | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/code/modules/xenoarcheaology/artifacts/standalone/crystal.dm b/code/modules/xenoarcheaology/artifacts/standalone/crystal.dm index dd1fa65634f1..1fe1e576b2a7 100644 --- a/code/modules/xenoarcheaology/artifacts/standalone/crystal.dm +++ b/code/modules/xenoarcheaology/artifacts/standalone/crystal.dm @@ -3,11 +3,13 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "ano70" density = TRUE + var/base_state = null + var/energized = FALSE /obj/structure/crystal/Initialize() . = ..() - icon_state = pick("ano70","ano80") + base_state = pick("ano7","ano8") desc = pick( "It shines faintly as it catches the light.", @@ -16,6 +18,10 @@ "Something twinkles faintly as you look at it.", "It's mesmerizing to behold.") +/obj/structure/crystal/on_update_icon() + . = ..() + icon_state = "[base_state][energized ? 0 : 1]" + /obj/structure/crystal/Destroy() src.visible_message("[src] shatters!") if(prob(75)) @@ -33,6 +39,15 @@ return ..() /obj/structure/crystal/get_artifact_scan_data() - return "Crystal formation - pseudo-organic crystalline matrix, unlikely to have formed naturally. No known technology exists to synthesize this exact composition." + . = "Crystal formation - pseudo-organic crystalline matrix, unlikely to have formed naturally. No known technology exists to synthesize this exact composition." + if(energized) + . += " Stimulation of the inner crystal lattice has caused it to enter a metastable energy level, indicating potential uses in power storage and energy manipulation." -//todo: laser_act +// Placeholder functionality so that these do something +/obj/structure/crystal/bullet_act(obj/item/projectile/the_bullet) + var/proj_damage = the_bullet.get_structure_damage() + if(!(the_bullet.damage_flags & DAM_LASER) || (proj_damage < 10) || energized) + return ..() + visible_message(SPAN_WARNING("\The [src] begins glowing...")) + energized = TRUE + update_icon() \ No newline at end of file From 59bcb86138b47af89c4ca7164ba41570ff345cb9 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 16 May 2025 23:42:38 -0400 Subject: [PATCH 0660/1331] Make the recipe material unit test more robust --- code/unit_tests/materials.dm | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/code/unit_tests/materials.dm b/code/unit_tests/materials.dm index f55ade48d342..c5d23c40310c 100644 --- a/code/unit_tests/materials.dm +++ b/code/unit_tests/materials.dm @@ -62,17 +62,19 @@ else if(!istype(product, recipe.expected_product_type)) failed += "unexpected product type returned ([product.type])" else if(isobj(product)) - var/obj/product_obj = product - LAZYINITLIST(product_obj.matter) // For the purposes of the following tests not runtiming. + var/list/product_matter = list() + for(var/obj/product_obj in results) + product_matter = MERGE_ASSOCS_WITH_NUM_VALUES(product_matter, product_obj.get_contained_matter()) + LAZYINITLIST(product_matter) // For the purposes of the following tests not runtiming. if(!material && !reinforced) - if(length(product_obj.matter)) + if(length(product_matter)) failed += "unsupplied material types" - else if(material && (product_obj.matter[material.type]) > recipe.req_amount) - failed += "excessive base material ([recipe.req_amount]/[ceil(product_obj.matter[material.type])])" - else if(reinforced && (product_obj.matter[reinforced.type]) > recipe.req_amount) - failed += "excessive reinf material ([recipe.req_amount]/[ceil(product_obj.matter[reinforced.type])])" + else if(material && (product_matter[material.type]) > recipe.req_amount) + failed += "excessive base material ([recipe.req_amount]/[ceil(product_matter[material.type])])" + else if(reinforced && (product_matter[reinforced.type]) > recipe.req_amount) + failed += "excessive reinf material ([recipe.req_amount]/[ceil(product_matter[reinforced.type])])" else - for(var/mat in product_obj.matter) + for(var/mat in product_matter) if(mat != material?.type && mat != reinforced?.type) failed += "extra material type ([mat])" From c090d859a303847232fc5773d87a7f5e17a4040d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 16 May 2025 23:43:00 -0400 Subject: [PATCH 0661/1331] Remove unnecessary amount override for haystack recipe --- code/modules/crafting/stack_recipes/recipes_fodder.dm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/modules/crafting/stack_recipes/recipes_fodder.dm b/code/modules/crafting/stack_recipes/recipes_fodder.dm index e256692b6547..f6002a657768 100644 --- a/code/modules/crafting/stack_recipes/recipes_fodder.dm +++ b/code/modules/crafting/stack_recipes/recipes_fodder.dm @@ -7,9 +7,7 @@ on_floor = TRUE difficulty = MAT_VALUE_EASY_DIY recipe_skill = SKILL_BOTANY - -/decl/stack_recipe/fodder/get_required_stack_amount(obj/item/stack/stack, product_amount = 1) - return 30 * product_amount // Arbitrary amount to make 20 food items. + req_amount = 30 * SHEET_MATERIAL_AMOUNT // Arbitrary amount to make 20 food items. /decl/stack_recipe/fodder/bale result_type = /obj/structure/haystack/bale From 7260ed93cceca18796e01f34500d96c95496041f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 16 May 2025 23:45:52 -0400 Subject: [PATCH 0662/1331] Fix tank dispenser recipe producing a full tank dispenser --- .../crafting/stack_recipes/recipes_hardness_integrity.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm index bf877bfbab75..57fc74fddf57 100644 --- a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm +++ b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm @@ -63,7 +63,7 @@ result_type = /obj/structure/closet /decl/stack_recipe/hardness/integrity/furniture/tank_dispenser - result_type = /obj/structure/tank_rack + result_type = /obj/structure/tank_rack/empty available_to_map_tech_level = MAP_TECH_LEVEL_SPACE /decl/stack_recipe/hardness/integrity/furniture/coffin From 2a74a73f0d83b2c5fd431bd8e0a978fc1c4bcd67 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:31:37 -0400 Subject: [PATCH 0663/1331] Implement weather particles wind_intensity var --- code/modules/weather/_weather.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/weather/_weather.dm b/code/modules/weather/_weather.dm index 00e996e256d8..9a8fb5341015 100644 --- a/code/modules/weather/_weather.dm +++ b/code/modules/weather/_weather.dm @@ -144,4 +144,4 @@ // 0 is south, but if our velocity is pure south we get -90, so add 90 // and then invert it, because byond uses counter-clockwise and we want clockwise weather_particles.rotation = generator("num", 90 - arctan(x_wind_vel * 0.50, weather_particles.base_velocity[1][2]), 90 - arctan(x_wind_vel, weather_particles.base_velocity[2][2]), NORMAL_RAND) - weather_particles.velocity += generator("vector", list(0, 0, 0), list(x_wind_vel, 0, z_wind_vel), NORMAL_RAND) \ No newline at end of file + weather_particles.velocity += generator("vector", list(0, 0, 0), list(x_wind_vel * weather_particles.wind_intensity, 0, z_wind_vel), NORMAL_RAND) \ No newline at end of file From 1cee5ccab2f40be2d55b36d5ce731dc23e75cedd Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 17 May 2025 21:55:35 -0400 Subject: [PATCH 0664/1331] Improve stack recipe material validation --- code/datums/repositories/decls.dm | 14 ++++ code/datums/vending/stored_item.dm | 4 +- code/game/atoms.dm | 6 +- code/game/machinery/Sleeper.dm | 6 +- .../machinery/_machines_base/machinery.dm | 4 +- .../stock_parts/building_material.dm | 4 +- code/game/machinery/vending_deconstruction.dm | 4 +- code/game/machinery/wall_frames.dm | 2 +- code/game/objects/__objs.dm | 2 +- code/modules/augment/active/cyberbrain.dm | 4 +- .../modules/crafting/stack_recipes/_recipe.dm | 72 ++++++++++++------- .../crafting/stack_recipes/recipes_bricks.dm | 4 +- .../stack_recipes/recipes_cardstock.dm | 1 + .../crafting/stack_recipes/recipes_grass.dm | 3 + .../stack_recipes/recipes_hardness.dm | 16 +---- .../crafting/stack_recipes/recipes_items.dm | 20 +++++- .../crafting/stack_recipes/recipes_logs.dm | 2 + .../crafting/stack_recipes/recipes_opacity.dm | 1 + .../crafting/stack_recipes/recipes_panels.dm | 1 + .../crafting/stack_recipes/recipes_planks.dm | 1 + .../stack_recipes/recipes_reinforced.dm | 9 +-- .../crafting/stack_recipes/recipes_rods.dm | 1 + .../crafting/stack_recipes/recipes_soft.dm | 5 +- .../crafting/stack_recipes/recipes_stacks.dm | 1 + .../stack_recipes/recipes_textiles.dm | 4 ++ code/modules/fabrication/_fabricator.dm | 2 +- code/modules/mob/living/living_organs.dm | 4 +- .../computers/modular_computer/core.dm | 4 +- code/modules/projectiles/ammunition.dm | 2 +- code/modules/tools/components/recipes.dm | 2 + code/unit_tests/materials.dm | 43 +++++++---- 31 files changed, 161 insertions(+), 87 deletions(-) diff --git a/code/datums/repositories/decls.dm b/code/datums/repositories/decls.dm index 73a9f14fb610..28dea46d5cd1 100644 --- a/code/datums/repositories/decls.dm +++ b/code/datums/repositories/decls.dm @@ -144,6 +144,20 @@ var/global/repository/decls/decls_repository = new . = get_decls(subtypesof(decl_prototype)) fetched_decl_subtypes[decl_prototype] = . +/// Gets the path of any concrete (non-abstract) decl of the provided type. +/// If decl_prototype is not abstract it will return that type. +/// Otherwise, it returns the first (in compile order) non-abstract child of this type, +/// or null otherwise. +/// This doesn't respect DECL_FLAG_ALLOW_ABSTRACT_INIT, but that flag should probably be deprecated someday +/// and replaced with a better solution to avoid instantiating abstract decls. +/// This is mostly used for recipe validation in unit tests and such. +/repository/decls/proc/get_first_concrete_decl_path_of_type(decl_prototype) + RETURN_TYPE(/decl) + . = fetched_decl_paths_by_type[decl_prototype] + if(!.) + . = get_decl_paths_of_type(decl_prototype) + return LAZYACCESS(., 1) // gets the first key (type) if it exists, else null if index is out of range + /decl abstract_type = /decl var/uid diff --git a/code/datums/vending/stored_item.dm b/code/datums/vending/stored_item.dm index 8eda6287cacb..3246f711603d 100644 --- a/code/datums/vending/stored_item.dm +++ b/code/datums/vending/stored_item.dm @@ -82,7 +82,7 @@ for(var/atom/movable/thing in instances) thing.forceMove(new_storing_obj) -/datum/stored_items/proc/get_combined_matter(include_instances = TRUE) +/datum/stored_items/proc/get_combined_matter(include_instances = TRUE, include_reagents = TRUE) var/virtual_amount = amount - length(instances) if(virtual_amount) . = atom_info_repository.get_matter_for(item_path)?.Copy() @@ -92,4 +92,4 @@ . = list() if(include_instances) for(var/atom/instance in instances) - . = MERGE_ASSOCS_WITH_NUM_VALUES(., instance.get_contained_matter()) \ No newline at end of file + . = MERGE_ASSOCS_WITH_NUM_VALUES(., instance.get_contained_matter(include_reagents)) \ No newline at end of file diff --git a/code/game/atoms.dm b/code/game/atoms.dm index a121490e2aea..5e466056ddd6 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -435,13 +435,13 @@ * Obj adds matter contents. Other overrides may add extra handling for things like material storage. * Most useful for calculating worth or deconstructing something along with its contents. */ -/atom/proc/get_contained_matter() - if(length(reagents?.reagent_volumes)) +/atom/proc/get_contained_matter(include_reagents = TRUE) + if(include_reagents && length(reagents?.reagent_volumes)) LAZYINITLIST(.) for(var/decl/material/reagent as anything in reagents.reagent_volumes) .[reagent.type] += floor(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_MATERIAL_UNIT) for(var/atom/contained_obj as anything in get_contained_external_atoms()) // machines handle component parts separately - . = MERGE_ASSOCS_WITH_NUM_VALUES(., contained_obj.get_contained_matter()) + . = MERGE_ASSOCS_WITH_NUM_VALUES(., contained_obj.get_contained_matter(include_reagents)) /// Return a list of all simulated atoms inside this one. /atom/proc/get_contained_external_atoms() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 47bc17acc0dd..eecfec8852bb 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -96,11 +96,11 @@ LAZYREMOVE(., loaded_canisters) LAZYREMOVE(., beaker) -/obj/machinery/sleeper/get_contained_matter() +/obj/machinery/sleeper/get_contained_matter(include_reagents = TRUE) . = ..() - . = MERGE_ASSOCS_WITH_NUM_VALUES(., beaker.get_contained_matter()) + . = MERGE_ASSOCS_WITH_NUM_VALUES(., beaker.get_contained_matter(include_reagents)) for(var/obj/canister in loaded_canisters) - . = MERGE_ASSOCS_WITH_NUM_VALUES(., canister.get_contained_matter()) + . = MERGE_ASSOCS_WITH_NUM_VALUES(., canister.get_contained_matter(include_reagents)) /obj/machinery/sleeper/Initialize(mapload, d = 0, populate_parts = TRUE) . = ..() diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index c57624ee3c9e..5f03a9d4ba72 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -506,12 +506,12 @@ Class Procs: LAZYREMOVE(., component_parts) // This only includes external atoms by default, so we need to add components back. -/obj/machinery/get_contained_matter() +/obj/machinery/get_contained_matter(include_reagents = TRUE) . = ..() var/list/component_types = types_of_component(/obj/item/stock_parts) for(var/path in component_types) for(var/obj/item/stock_parts/part in get_all_components_of_type(path)) - var/list/part_costs = part.get_contained_matter() + var/list/part_costs = part.get_contained_matter(include_reagents) for(var/key in part_costs) .[key] += part_costs[key] * component_types[path] diff --git a/code/game/machinery/_machines_base/stock_parts/building_material.dm b/code/game/machinery/_machines_base/stock_parts/building_material.dm index 615a39b9a852..2ad4e83360f9 100644 --- a/code/game/machinery/_machines_base/stock_parts/building_material.dm +++ b/code/game/machinery/_machines_base/stock_parts/building_material.dm @@ -67,9 +67,9 @@ materials = null ..() -/obj/item/stock_parts/building_material/get_contained_matter() +/obj/item/stock_parts/building_material/get_contained_matter(include_reagents = TRUE) . = ..() for(var/obj/item/thing in materials) - var/list/costs = thing.get_contained_matter() + var/list/costs = thing.get_contained_matter(include_reagents) for(var/key in costs) .[key] += costs[key] diff --git a/code/game/machinery/vending_deconstruction.dm b/code/game/machinery/vending_deconstruction.dm index 2577f8fc61b7..57ab1eba0302 100644 --- a/code/game/machinery/vending_deconstruction.dm +++ b/code/game/machinery/vending_deconstruction.dm @@ -38,12 +38,12 @@ return TRUE . = ..() -/obj/structure/vending_refill/get_contained_matter() +/obj/structure/vending_refill/get_contained_matter(include_reagents = TRUE) . = ..() for(var/datum/stored_items/vending_products/record in product_records) . = MERGE_ASSOCS_WITH_NUM_VALUES(., record.get_combined_matter(include_instances = FALSE)) -/obj/machinery/vending/get_contained_matter() +/obj/machinery/vending/get_contained_matter(include_reagents = TRUE) . = ..() for(var/datum/stored_items/vending_products/record in product_records) . = MERGE_ASSOCS_WITH_NUM_VALUES(., record.get_combined_matter(include_instances = FALSE)) diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm index 19d63aeebfab..56e7c788416d 100644 --- a/code/game/machinery/wall_frames.dm +++ b/code/game/machinery/wall_frames.dm @@ -9,7 +9,7 @@ var/reverse = 0 //if resulting object faces opposite its dir (like light fixtures) var/fully_construct = FALSE // Results in a machine with all parts auto-installed and ready to go if TRUE; if FALSE, the machine will spawn without removable expected parts -/obj/item/frame/get_contained_matter() +/obj/item/frame/get_contained_matter(include_reagents = TRUE) . = ..() if(fully_construct) var/list/cost = atom_info_repository.get_matter_for(build_machine_type) diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 3f7e83ffaae0..62566afe1a1c 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -293,7 +293,7 @@ /obj/proc/WillContain() return -/obj/get_contained_matter() +/obj/get_contained_matter(include_reagents = TRUE) . = ..() if(length(matter)) . = MERGE_ASSOCS_WITH_NUM_VALUES(., matter.Copy()) diff --git a/code/modules/augment/active/cyberbrain.dm b/code/modules/augment/active/cyberbrain.dm index c508f4cfcedd..d5972c12333d 100644 --- a/code/modules/augment/active/cyberbrain.dm +++ b/code/modules/augment/active/cyberbrain.dm @@ -79,11 +79,11 @@ if(os) os.Process() -/obj/item/organ/internal/augment/active/cyberbrain/get_contained_matter() +/obj/item/organ/internal/augment/active/cyberbrain/get_contained_matter(include_reagents = TRUE) . = ..() var/datum/extension/assembly/assembly = get_extension(src, /datum/extension/assembly) for(var/obj/part in assembly?.parts) - . = MERGE_ASSOCS_WITH_NUM_VALUES(., part.get_contained_matter()) + . = MERGE_ASSOCS_WITH_NUM_VALUES(., part.get_contained_matter(include_reagents)) /* * diff --git a/code/modules/crafting/stack_recipes/_recipe.dm b/code/modules/crafting/stack_recipes/_recipe.dm index 640bd41adf39..205948de85e2 100644 --- a/code/modules/crafting/stack_recipes/_recipe.dm +++ b/code/modules/crafting/stack_recipes/_recipe.dm @@ -23,9 +23,6 @@ var/gender /// Object path to the desired product. var/result_type - /// Object path to use in unit testing; leave null to use result_type instead. - /// Useful for items that require a material to Initialize() correctly as testing tries to use a null material. - var/test_result_type /// Amount of matter units needed for this recipe. If null, generates from result matter. var/req_amount /// Time it takes for this recipe to be crafted (not including skill and tool modifiers). If null, generates from product w_class and difficulty. @@ -78,6 +75,10 @@ /// Can this recipe use a reinforced material? Set to type for a specific material. var/required_reinforce_material = MATERIAL_FORBIDDEN + /// In cases where required_material is MATERIAL_REQUIRED and not a typepath, + /// this is the material type used for setup, validation, and tests. + var/validation_material + /// Minimum material wall support value. var/required_wall_support_value /// Minimum material integrity value. @@ -131,17 +132,29 @@ else if(!ispath(result_type, expected_product_type)) . += "result type [result_type || "NULL"] is not subtype of expected product type [expected_product_type || "NULL"]" - if(isnull(required_material) || required_material == MATERIAL_FORBIDDEN) - if(!isnull(required_wall_support_value)) - . += "null required material but non-null wall support value" - if(!isnull(required_integrity)) - . += "null required material but non-null integrity value" - if(!isnull(required_max_opacity)) - . += "null required material but non-null max opacity value" - if(!isnull(required_min_hardness)) - . += "null required material but non-null min hardness value" - if(!isnull(required_max_hardness)) - . += "null required material but non-null max hardness value" + switch(required_material) + if(null) + . += "null required material value is deprecated, use MATERIAL_FORBIDDEN" + if(MATERIAL_FORBIDDEN) + if(!isnull(validation_material)) + . += "material is forbidden but non-null validation material" + if(!isnull(required_wall_support_value)) + . += "material is forbidden but non-null wall support value" + if(!isnull(required_integrity)) + . += "material is forbidden but non-null integrity value" + if(!isnull(required_max_opacity)) + . += "material is forbidden but non-null max opacity value" + if(!isnull(required_min_hardness)) + . += "material is forbidden but non-null min hardness value" + if(!isnull(required_max_hardness)) + . += "material is forbidden but non-null max hardness value" + if(MATERIAL_REQUIRED) + if(isnull(validation_material)) + . += "material is required but validation material was null" + else + var/list/material_failures = get_missing_material_properties(RESOLVE_TO_DECL(validation_material)) + if(LAZYLEN(material_failures)) + . += "validation material is [english_list(material_failures)]" // 'material is too soft, too opaque, and unable to support enough weight' if(recipe_skill && !isnull(difficulty)) var/decl/skill/used_skill = GET_DECL(recipe_skill) @@ -235,6 +248,21 @@ . = JOINTEXT(.) +/// Returns a list of descriptive error strings e.g. "too soft/hard/opaque" if material properties are not satisfied. +/decl/stack_recipe/proc/get_missing_material_properties(decl/material/mat) + ASSERT(mat) + // Check if the material has the appropriate properties. + if(!isnull(required_wall_support_value) && mat.wall_support_value < required_wall_support_value) + LAZYADD(., "unable to support enough weight") + if(!isnull(required_integrity) && mat.integrity < required_integrity) + LAZYADD(., "too weak") + if(!isnull(required_min_hardness) && mat.hardness < required_min_hardness) + LAZYADD(., "too soft") + if(!isnull(required_max_hardness) && mat.hardness > required_max_hardness) + LAZYADD(., "too hard") + if(!isnull(required_max_opacity) && mat.opacity > required_max_opacity) + LAZYADD(., "too opaque") + /decl/stack_recipe/proc/can_be_made_from(stack_type, tool_type, decl/material/mat, decl/material/reinf_mat) // Early checks to avoid letting recipes make themselves. @@ -259,17 +287,8 @@ return FALSE // Check if the material has the appropriate properties. - if(mat) - if(!isnull(required_wall_support_value) && mat.wall_support_value < required_wall_support_value) - return FALSE - if(!isnull(required_integrity) && mat.integrity < required_integrity) - return FALSE - if(!isnull(required_min_hardness) && mat.hardness < required_min_hardness) - return FALSE - if(!isnull(required_max_hardness) && mat.hardness > required_max_hardness) - return FALSE - if(!isnull(required_max_opacity) && mat.opacity > required_max_opacity) - return FALSE + if(mat && length(get_missing_material_properties(mat))) + return FALSE // Check if they're using the right tool. if(required_tool) @@ -325,7 +344,8 @@ if(result_type && isnull(req_amount)) req_amount = 0 var/list/materials - materials = atom_info_repository.get_matter_for((test_result_type || result_type), (ispath(required_material) ? required_material : null)) + var/decl/material/validation_material_path = ispath(required_material) ? decls_repository.get_first_concrete_decl_path_of_type(required_material) : validation_material + materials = atom_info_repository.get_matter_for(result_type, validation_material_path) for(var/mat in materials) req_amount += round(materials[mat]) req_amount = ceil(req_amount*crafting_extra_cost_factor) diff --git a/code/modules/crafting/stack_recipes/recipes_bricks.dm b/code/modules/crafting/stack_recipes/recipes_bricks.dm index 8be3160ba95c..0c9670fbcd9a 100644 --- a/code/modules/crafting/stack_recipes/recipes_bricks.dm +++ b/code/modules/crafting/stack_recipes/recipes_bricks.dm @@ -9,6 +9,7 @@ /obj/item/stack/material/log, /obj/item/stack/material/lump ) + validation_material = /decl/material/solid/stone/basalt category = "structures" /decl/stack_recipe/bricks/cup @@ -121,6 +122,7 @@ result_type = /turf/wall/brick craft_stack_types = /obj/item/stack/material/brick difficulty = MAT_VALUE_HARD_DIY + validation_material = /decl/material/solid/stone/basalt /decl/stack_recipe/turfs/wall/brick/shutter name = "shuttered brick wall" @@ -129,8 +131,8 @@ /decl/stack_recipe/turfs/floor/brick name = "cobblestone path" result_type = /turf/floor/path - expected_product_type = /turf/floor/path craft_stack_types = /obj/item/stack/material/brick + validation_material = /decl/material/solid/stone/basalt /decl/stack_recipe/turfs/floor/brick/herringbone name = "herringbone path" diff --git a/code/modules/crafting/stack_recipes/recipes_cardstock.dm b/code/modules/crafting/stack_recipes/recipes_cardstock.dm index 30e9e6f5e3a4..12ef76c3ecd8 100644 --- a/code/modules/crafting/stack_recipes/recipes_cardstock.dm +++ b/code/modules/crafting/stack_recipes/recipes_cardstock.dm @@ -1,6 +1,7 @@ /decl/stack_recipe/cardstock abstract_type = /decl/stack_recipe/cardstock craft_stack_types = /obj/item/stack/material/cardstock + validation_material = /decl/material/solid/organic/cardboard available_to_map_tech_level = MAP_TECH_LEVEL_SPACE // not exactly high-tech, but donuts etc are not medieval /decl/stack_recipe/cardstock/box diff --git a/code/modules/crafting/stack_recipes/recipes_grass.dm b/code/modules/crafting/stack_recipes/recipes_grass.dm index 17caba7e0c49..776dfb49589e 100644 --- a/code/modules/crafting/stack_recipes/recipes_grass.dm +++ b/code/modules/crafting/stack_recipes/recipes_grass.dm @@ -3,7 +3,10 @@ craft_stack_types = /obj/item/stack/material/bundle category = "woven items" forbidden_craft_stack_types = null + validation_material = /decl/material/solid/organic/plantmatter/grass/dry +// TODO: make this check a material property. tensile strength? dryness? +// literally anything but a direct type equality check please /decl/stack_recipe/woven/can_be_made_from(stack_type, tool_type, decl/material/mat, decl/material/reinf_mat) if((istype(mat) ? mat.type : mat) == /decl/material/solid/organic/plantmatter/grass) return FALSE diff --git a/code/modules/crafting/stack_recipes/recipes_hardness.dm b/code/modules/crafting/stack_recipes/recipes_hardness.dm index 903769244d6e..5f8f43e2a4b7 100644 --- a/code/modules/crafting/stack_recipes/recipes_hardness.dm +++ b/code/modules/crafting/stack_recipes/recipes_hardness.dm @@ -1,6 +1,7 @@ /decl/stack_recipe/hardness abstract_type = /decl/stack_recipe/hardness required_min_hardness = MAT_VALUE_FLEXIBLE + 10 + validation_material = DEFAULT_FURNITURE_MATERIAL /decl/stack_recipe/hardness/improvised_armour result_type = /obj/item/clothing/suit/armor/crafted @@ -74,21 +75,6 @@ /decl/stack_recipe/hardness/mortar result_type = /obj/item/chems/glass/mortar -/decl/stack_recipe/ring - result_type = /obj/item/clothing/gloves/ring - -/decl/stack_recipe/ring_thin - name = "ring, thin" - result_type = /obj/item/clothing/gloves/ring/thin - -/decl/stack_recipe/ring_thick - name = "ring, thick" - result_type = /obj/item/clothing/gloves/ring/thick - -/decl/stack_recipe/ring_split - name = "ring, split" - result_type = /obj/item/clothing/gloves/ring/split - /decl/stack_recipe/hardness/clipboard result_type = /obj/item/clipboard available_to_map_tech_level = MAP_TECH_LEVEL_SPACE diff --git a/code/modules/crafting/stack_recipes/recipes_items.dm b/code/modules/crafting/stack_recipes/recipes_items.dm index d98f860c3f3f..e09b5b933cf9 100644 --- a/code/modules/crafting/stack_recipes/recipes_items.dm +++ b/code/modules/crafting/stack_recipes/recipes_items.dm @@ -37,4 +37,22 @@ bundle = new(location) bundles += bundle bundle.merge(paper) - return bundles \ No newline at end of file + return bundles + +// These don't check hardness so that you can make them out of clay and fire them, I guess? +// They used to be in the hardness-based recipes file but aren't now. +/decl/stack_recipe/ring + result_type = /obj/item/clothing/gloves/ring + validation_material = /decl/material/solid/metal/silver + +/decl/stack_recipe/ring/thin + name = "ring, thin" + result_type = /obj/item/clothing/gloves/ring/thin + +/decl/stack_recipe/ring/thick + name = "ring, thick" + result_type = /obj/item/clothing/gloves/ring/thick + +/decl/stack_recipe/ring/split + name = "ring, split" + result_type = /obj/item/clothing/gloves/ring/split \ No newline at end of file diff --git a/code/modules/crafting/stack_recipes/recipes_logs.dm b/code/modules/crafting/stack_recipes/recipes_logs.dm index 150f36cd1021..02010e3a9252 100644 --- a/code/modules/crafting/stack_recipes/recipes_logs.dm +++ b/code/modules/crafting/stack_recipes/recipes_logs.dm @@ -2,6 +2,7 @@ abstract_type = /decl/stack_recipe/logs craft_stack_types = /obj/item/stack/material/log forbidden_craft_stack_types = /obj/item/stack/material/ore + validation_material = /decl/material/solid/organic/wood/oak /decl/stack_recipe/logs/travois result_type = /obj/structure/travois @@ -12,6 +13,7 @@ result_type = /turf/wall/log craft_stack_types = /obj/item/stack/material/log forbidden_craft_stack_types = /obj/item/stack/material/ore + validation_material = /decl/material/solid/organic/wood/oak difficulty = MAT_VALUE_HARD_DIY /decl/stack_recipe/turfs/wall/logs/shutter diff --git a/code/modules/crafting/stack_recipes/recipes_opacity.dm b/code/modules/crafting/stack_recipes/recipes_opacity.dm index dccf4e889e6d..c712344f2464 100644 --- a/code/modules/crafting/stack_recipes/recipes_opacity.dm +++ b/code/modules/crafting/stack_recipes/recipes_opacity.dm @@ -9,6 +9,7 @@ /obj/item/stack/material/sheet, /obj/item/stack/material/panel ) + validation_material = /decl/material/solid/glass /decl/stack_recipe/opacity/fullwindow name = "full-tile window" diff --git a/code/modules/crafting/stack_recipes/recipes_panels.dm b/code/modules/crafting/stack_recipes/recipes_panels.dm index a78196e388b4..dc21533bd6a7 100644 --- a/code/modules/crafting/stack_recipes/recipes_panels.dm +++ b/code/modules/crafting/stack_recipes/recipes_panels.dm @@ -2,6 +2,7 @@ abstract_type = /decl/stack_recipe/panels craft_stack_types = /obj/item/stack/material/panel available_to_map_tech_level = MAP_TECH_LEVEL_SPACE + validation_material = /decl/material/solid/organic/plastic /decl/stack_recipe/panels/bag result_type = /obj/item/bag/flimsy diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index d643a2e51b8f..03464a086a4e 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -1,6 +1,7 @@ /decl/stack_recipe/planks abstract_type = /decl/stack_recipe/planks craft_stack_types = /obj/item/stack/material/plank + validation_material = /decl/material/solid/organic/wood/oak /decl/stack_recipe/planks/sandals result_type = /obj/item/clothing/shoes/sandal diff --git a/code/modules/crafting/stack_recipes/recipes_reinforced.dm b/code/modules/crafting/stack_recipes/recipes_reinforced.dm index c5335e7132f0..6a918cac0ef8 100644 --- a/code/modules/crafting/stack_recipes/recipes_reinforced.dm +++ b/code/modules/crafting/stack_recipes/recipes_reinforced.dm @@ -1,9 +1,10 @@ /decl/stack_recipe/reinforced - abstract_type = /decl/stack_recipe/reinforced - craft_stack_types = /obj/item/stack/material/sheet/reinforced - one_per_turf = TRUE - difficulty = MAT_VALUE_HARD_DIY + abstract_type = /decl/stack_recipe/reinforced + craft_stack_types = /obj/item/stack/material/sheet/reinforced + one_per_turf = TRUE + difficulty = MAT_VALUE_HARD_DIY available_to_map_tech_level = MAP_TECH_LEVEL_SPACE + validation_material = /decl/material/solid/metal/plasteel /decl/stack_recipe/reinforced/ai_core result_type = /obj/structure/aicore diff --git a/code/modules/crafting/stack_recipes/recipes_rods.dm b/code/modules/crafting/stack_recipes/recipes_rods.dm index c010556a990d..8483c2346746 100644 --- a/code/modules/crafting/stack_recipes/recipes_rods.dm +++ b/code/modules/crafting/stack_recipes/recipes_rods.dm @@ -10,6 +10,7 @@ difficulty = MAT_VALUE_HARD_DIY available_to_map_tech_level = MAP_TECH_LEVEL_MEDIEVAL category = "structures" + validation_material = /decl/material/solid/metal/steel /decl/stack_recipe/rods/stick result_type = /obj/item/stick diff --git a/code/modules/crafting/stack_recipes/recipes_soft.dm b/code/modules/crafting/stack_recipes/recipes_soft.dm index 27f9c6328ceb..e7e6d12d924f 100644 --- a/code/modules/crafting/stack_recipes/recipes_soft.dm +++ b/code/modules/crafting/stack_recipes/recipes_soft.dm @@ -9,6 +9,7 @@ ) required_min_hardness = 0 required_max_hardness = MAT_VALUE_SOFT + validation_material = /decl/material/solid/clay crafting_extra_cost_factor = 1 // No wastage for just resculpting materials. /decl/stack_recipe/soft/teapot @@ -42,13 +43,11 @@ name = "brick" name_plural = "bricks" result_type = /obj/item/stack/material/brick - test_result_type = /obj/item/stack/material/brick/clay /decl/stack_recipe/soft/bar name = "bar" name_plural = "bars" result_type = /obj/item/stack/material/bar - test_result_type = /obj/item/stack/material/bar/wax /decl/stack_recipe/soft/stack/spawn_result(mob/user, location, amount, decl/material/mat, decl/material/reinf_mat, paint_color, spent_type, spent_amount = 1) var/obj/item/stack/S = ..() @@ -68,13 +67,11 @@ name = "large lump" name_plural = "large lumps" result_type = /obj/item/stack/material/lump/large - test_result_type = /obj/item/stack/material/lump/large/clay /decl/stack_recipe/soft/stack/small_lump name = "small lump" name_plural = "small lumps" result_type = /obj/item/stack/material/lump - test_result_type = /obj/item/stack/material/lump/clay /decl/stack_recipe/soft/crucible result_type = /obj/item/chems/crucible diff --git a/code/modules/crafting/stack_recipes/recipes_stacks.dm b/code/modules/crafting/stack_recipes/recipes_stacks.dm index 06c19604fbfc..5b45b2c90f42 100644 --- a/code/modules/crafting/stack_recipes/recipes_stacks.dm +++ b/code/modules/crafting/stack_recipes/recipes_stacks.dm @@ -112,6 +112,7 @@ abstract_type = /decl/stack_recipe/tile/panels craft_stack_types = /obj/item/stack/material/panel available_to_map_tech_level = MAP_TECH_LEVEL_SPACE + validation_material = /decl/material/solid/organic/plastic /decl/stack_recipe/tile/panels/floor result_type = /obj/item/stack/tile/floor_white diff --git a/code/modules/crafting/stack_recipes/recipes_textiles.dm b/code/modules/crafting/stack_recipes/recipes_textiles.dm index 4b49616eef76..0e0a5e20271a 100644 --- a/code/modules/crafting/stack_recipes/recipes_textiles.dm +++ b/code/modules/crafting/stack_recipes/recipes_textiles.dm @@ -6,6 +6,7 @@ difficulty = null // Autoset from material difficulty crafting_extra_cost_factor = 1.5 // measure twice, cut once; material is lost. todo: produce scraps? abstract_type = /decl/stack_recipe/textiles + validation_material = /decl/material/solid/organic/cloth /decl/stack_recipe/textiles/rug result_type = /obj/structure/rug/crafted @@ -62,6 +63,7 @@ /decl/stack_recipe/textiles/leather abstract_type = /decl/stack_recipe/textiles/leather + validation_material = /decl/material/solid/organic/leather craft_stack_types = /obj/item/stack/material/skin category = "clothing" @@ -105,6 +107,7 @@ /decl/stack_recipe/textiles/cloth abstract_type = /decl/stack_recipe/textiles/cloth + validation_material = /decl/material/solid/organic/cloth craft_stack_types = /obj/item/stack/material/bolt category = "clothing" @@ -141,6 +144,7 @@ /decl/stack_recipe/textiles/fur abstract_type = /decl/stack_recipe/textiles/fur craft_stack_types = /obj/item/stack/material/skin/pelt + validation_material = /decl/material/solid/organic/skin/fur /decl/stack_recipe/textiles/fur/bedding difficulty = MAT_VALUE_EASY_DIY diff --git a/code/modules/fabrication/_fabricator.dm b/code/modules/fabrication/_fabricator.dm index b4c227c101a6..d10d5064bc4f 100644 --- a/code/modules/fabrication/_fabricator.dm +++ b/code/modules/fabrication/_fabricator.dm @@ -230,6 +230,6 @@ return pipe_colors //override with null for hex color selections // Our stored_material is just the right format to be added to the matter list. -/obj/machinery/fabricator/get_contained_matter() +/obj/machinery/fabricator/get_contained_matter(include_reagents = TRUE) . = ..() . = MERGE_ASSOCS_WITH_NUM_VALUES(., stored_material) \ No newline at end of file diff --git a/code/modules/mob/living/living_organs.dm b/code/modules/mob/living/living_organs.dm index dce22c9a9933..8617ed077091 100644 --- a/code/modules/mob/living/living_organs.dm +++ b/code/modules/mob/living/living_organs.dm @@ -11,10 +11,10 @@ /mob/living/proc/has_internal_organs() return LAZYLEN(get_internal_organs()) > 0 -/mob/living/get_contained_matter() +/mob/living/get_contained_matter(include_reagents = TRUE) . = ..() for(var/obj/item/organ in get_organs()) - . = MERGE_ASSOCS_WITH_NUM_VALUES(., organ.get_contained_matter()) + . = MERGE_ASSOCS_WITH_NUM_VALUES(., organ.get_contained_matter(include_reagents)) //Can be called when we want to add an organ in a detached state or an attached state. /mob/living/proc/add_organ(var/obj/item/organ/O, var/obj/item/organ/external/affected = null, var/in_place = FALSE, var/update_icon = TRUE, var/detached = FALSE, var/skip_health_update = FALSE) diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm index 521a4a6b01a4..0869c4431739 100644 --- a/code/modules/modular_computers/computers/modular_computer/core.dm +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -4,11 +4,11 @@ if(assembly) LAZYREMOVE(., assembly.parts) -/obj/item/modular_computer/get_contained_matter() +/obj/item/modular_computer/get_contained_matter(include_reagents = TRUE) . = ..() var/datum/extension/assembly/assembly = get_extension(src, /datum/extension/assembly) for(var/obj/part in assembly?.parts) - . = MERGE_ASSOCS_WITH_NUM_VALUES(., part.get_contained_matter()) + . = MERGE_ASSOCS_WITH_NUM_VALUES(., part.get_contained_matter(include_reagents)) /obj/item/modular_computer/Process() var/datum/extension/assembly/assembly = get_extension(src, /datum/extension/assembly) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 67c0341696df..83c79b11ce9a 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -176,7 +176,7 @@ stored_ammo += new ammo_type(src) contents_initialized = TRUE -/obj/item/ammo_magazine/get_contained_matter() +/obj/item/ammo_magazine/get_contained_matter(include_reagents = TRUE) . = ..() if(!lazyload_contents || contents_initialized || !ammo_type || !initial_ammo) return diff --git a/code/modules/tools/components/recipes.dm b/code/modules/tools/components/recipes.dm index 9ab5516ceb8e..38b76cff8959 100644 --- a/code/modules/tools/components/recipes.dm +++ b/code/modules/tools/components/recipes.dm @@ -7,6 +7,7 @@ /obj/item/stack/material/lump, /obj/item/stack/material/plank ) + validation_material = /decl/material/solid/stone/basalt /decl/stack_recipe/tool/handle abstract_type = /decl/stack_recipe/tool/handle @@ -16,6 +17,7 @@ /obj/item/stack/material/rods, /obj/item/stack/material/bone ) + validation_material = /decl/material/solid/organic/bone /decl/stack_recipe/tool/handle/long result_type = /obj/item/tool_component/handle/long diff --git a/code/unit_tests/materials.dm b/code/unit_tests/materials.dm index c5d23c40310c..8ad6424f271c 100644 --- a/code/unit_tests/materials.dm +++ b/code/unit_tests/materials.dm @@ -12,32 +12,51 @@ var/list/stack_types = list(null) var/list/tool_types = list(null) - var/list/all_recipes = decls_repository.get_decls_of_subtype(/decl/stack_recipe) - for(var/recipe_type in all_recipes) - var/decl/stack_recipe/recipe = all_recipes[recipe_type] + var/list/all_recipes = decls_repository.get_decls_of_subtype_unassociated(/decl/stack_recipe) + for(var/decl/stack_recipe/recipe in all_recipes) if(recipe.required_tool) tool_types |= recipe.required_tool if(recipe.craft_stack_types) stack_types |= recipe.craft_stack_types // Force config to be the most precise recipes possible. + // This first one doesn't actually work because it's applied on decl init... var/decl/config/config = GET_DECL(/decl/config/toggle/on/stack_crafting_uses_types) config.set_value(TRUE) config = GET_DECL(/decl/config/toggle/stack_crafting_uses_tools) config.set_value(TRUE) var/list/test_materials = list( - GET_DECL(/decl/material/solid/organic/wood), + GET_DECL(/decl/material/solid/organic/wood/oak), GET_DECL(/decl/material/solid/organic/plastic), GET_DECL(/decl/material/solid/organic/meat), + GET_DECL(/decl/material/solid/organic/cloth/linen), + GET_DECL(/decl/material/solid/organic/leather), + GET_DECL(/decl/material/solid/organic/plantmatter/grass/dry), + GET_DECL(/decl/material/solid/organic/paper), GET_DECL(/decl/material/solid/metal/steel), GET_DECL(/decl/material/solid/metal/plasteel), GET_DECL(/decl/material/solid/metal/gold), + GET_DECL(/decl/material/solid/metal/aluminium), + GET_DECL(/decl/material/solid/organic/wax), GET_DECL(/decl/material/solid/glass), - GET_DECL(/decl/material/solid/stone/sandstone), - GET_DECL(/decl/material/solid/clay) + GET_DECL(/decl/material/solid/stone/basalt), + GET_DECL(/decl/material/solid/clay), ) + // Add validation materials if not already present + // (this is especially useful for modpacked recipes/materials, like nullglass) + for(var/decl/stack_recipe/the_recipe in all_recipes) + if(ispath(the_recipe.required_material)) + var/decl/material/the_validation_material = the_recipe.required_material + if(TYPE_IS_ABSTRACT(the_validation_material)) + the_validation_material = decls_repository.get_first_concrete_decl_path_of_type(the_recipe.required_material) + test_materials |= GET_DECL(the_validation_material) + continue + if(the_recipe.validation_material) + test_materials |= GET_DECL(the_recipe.validation_material) + continue + // This is obscene, but completeness requires it. for(var/stack_type in stack_types) for(var/tool_type in tool_types) @@ -51,21 +70,19 @@ // Handle the actual validation. for(var/decl/stack_recipe/recipe as anything in recipes) - var/test_type = recipe.test_result_type || recipe.result_type - if(!test_type || ispath(test_type, /turf)) // Cannot exist without a loc and doesn't have matter, cannot assess here. + if(!recipe.result_type || ispath(recipe.result_type, /turf)) // Cannot exist without a loc and doesn't have matter, cannot assess here. continue var/list/results = recipe.spawn_result(null, null, 1, material, reinforced, null) var/atom/product = LAZYACCESS(results, 1) var/list/failed = list() if(!product) failed += "no product returned" - else if(!istype(product, recipe.expected_product_type)) - failed += "unexpected product type returned ([product.type])" + else if(!istype(product, recipe.result_type)) + failed += "unexpected product type, got '[product.type]' (expected '[recipe.result_type]')" else if(isobj(product)) var/list/product_matter = list() for(var/obj/product_obj in results) - product_matter = MERGE_ASSOCS_WITH_NUM_VALUES(product_matter, product_obj.get_contained_matter()) - LAZYINITLIST(product_matter) // For the purposes of the following tests not runtiming. + product_matter = MERGE_ASSOCS_WITH_NUM_VALUES(product_matter, product_obj.get_contained_matter(include_reagents = FALSE)) if(!material && !reinforced) if(length(product_matter)) failed += "unsupplied material types" @@ -87,6 +104,8 @@ passed_designs += recipe if(!QDELETED(product)) qdel(product) + CHECK_TICK + CHECK_TICK if(failed_count) fail("[failed_count] crafting recipes had inconsistent output materials: [jointext(failed_designs, "\n")].") From 1ff5cc8fe8fdf4acda90375d850522024bba7978 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 23 May 2025 15:15:37 -0400 Subject: [PATCH 0665/1331] Lower FileAlreadyIncluded pragma to disabled --- code/___opendream_linting.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/___opendream_linting.dm b/code/___opendream_linting.dm index 446ad58ad57b..e9fe9369d681 100644 --- a/code/___opendream_linting.dm +++ b/code/___opendream_linting.dm @@ -1,6 +1,7 @@ #ifdef OPENDREAM //1000-1999 -#pragma FileAlreadyIncluded error +// FileAlreadyIncluded trips up on modpack DMEs +#pragma FileAlreadyIncluded disabled #pragma MissingIncludedFile error #pragma MisplacedDirective error #pragma UndefineMissingDirective error From 1e062a68ac49b24d8b991a19969f50cb5a455527 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 26 May 2025 00:24:33 -0400 Subject: [PATCH 0666/1331] Fix locked machines failing panel opening unit test --- .../machinery/_machines_base/machine_construction/default.dm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/game/machinery/_machines_base/machine_construction/default.dm b/code/game/machinery/_machines_base/machine_construction/default.dm index a3056c056a52..d573df31e478 100644 --- a/code/game/machinery/_machines_base/machine_construction/default.dm +++ b/code/game/machinery/_machines_base/machine_construction/default.dm @@ -21,6 +21,9 @@ /decl/machine_construction/default/panel_closed/proc/fail_test_state_transfer(obj/machinery/machine, mob/user) var/static/obj/item/screwdriver/screwdriver = new + // Prevent access locks on machines interfering with our interactions. + for(var/obj/item/stock_parts/access_lock/lock in machine.get_all_components_of_type(/obj/item/stock_parts/access_lock)) + lock.locked = FALSE if(!machine.attackby(screwdriver, user)) return "Machine [log_info_line(machine)] did not respond to attackby with screwdriver." if(machine.construct_state.type != down_state) From 5b433a365a94f3dea2ce90db22f1ef3900d994d6 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 26 May 2025 00:25:17 -0400 Subject: [PATCH 0667/1331] Copy default panel-closed test to wall frame construction state --- .../machine_construction/wall_frame.dm | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/code/game/machinery/_machines_base/machine_construction/wall_frame.dm b/code/game/machinery/_machines_base/machine_construction/wall_frame.dm index 5c38cf0cd7c2..a81bd4d52312 100644 --- a/code/game/machinery/_machines_base/machine_construction/wall_frame.dm +++ b/code/game/machinery/_machines_base/machine_construction/wall_frame.dm @@ -55,6 +55,23 @@ machine.panel_open = TRUE machine.queue_icon_update() +/decl/machine_construction/wall_frame/panel_closed/fail_unit_test(obj/machinery/machine) + if((. = ..())) + return + var/static/mob/living/human/user = new + return fail_test_state_transfer(machine, user) + +/decl/machine_construction/wall_frame/panel_closed/proc/fail_test_state_transfer(obj/machinery/machine, mob/user) + var/static/obj/item/screwdriver/screwdriver = new + // Prevent access locks on machines interfering with our interactions. + for(var/obj/item/stock_parts/access_lock/lock in machine.get_all_components_of_type(/obj/item/stock_parts/access_lock)) + lock.locked = FALSE + if(!machine.attackby(screwdriver, user)) + return "Machine [log_info_line(machine)] did not respond to attackby with screwdriver." + if(machine.construct_state.type != open_state) + return "Machine [log_info_line(machine)] had a construct_state of type [machine.construct_state.type] after screwdriver interaction (expected [open_state])." + + // Open panel /decl/machine_construction/wall_frame/panel_open/state_is_valid(obj/machinery/machine) From 3e4b040369ab5055760e6b80e8657e1c9bb78643 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 26 May 2025 00:45:13 -0400 Subject: [PATCH 0668/1331] Add hacky workaround for self destruct storage --- code/game/machinery/self_destruct_storage.dm | 10 ++++++++++ code/unit_tests/machine_tests.dm | 11 ++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/code/game/machinery/self_destruct_storage.dm b/code/game/machinery/self_destruct_storage.dm index 0d97e56de5a7..df7512e5da09 100644 --- a/code/game/machinery/self_destruct_storage.dm +++ b/code/game/machinery/self_destruct_storage.dm @@ -51,6 +51,16 @@ to_chat(user, SPAN_WARNING("The card fails to do anything. It seems this device has an advanced encryption system.")) return NO_EMAG_ACT +// I hate this but can't think of a better way aside from skipping the testing entirely for this type, +// which is worse. Basically, we just need to ensure we pass the cannot_transition_to check. +/obj/machinery/nuclear_cylinder_storage/fail_construct_state_unit_test() + locked = FALSE + open = TRUE + var/list/old_cylinders = cylinders // This is evil. + cylinders = list() + . = ..() + cylinders = old_cylinders + /obj/machinery/nuclear_cylinder_storage/components_are_accessible(path) return !locked && open && ..() diff --git a/code/unit_tests/machine_tests.dm b/code/unit_tests/machine_tests.dm index 3c9258787e65..7b14762cb32d 100644 --- a/code/unit_tests/machine_tests.dm +++ b/code/unit_tests/machine_tests.dm @@ -48,15 +48,20 @@ /datum/unit_test/machine_construct_states_shall_be_valid name = "MACHINE: All mapped machines with construct states shall meet state requirements." +/// Returns a failure string if the unit test should fail, FALSE if it should succeed. +/// Mostly just a wrapper to handle things like unlocking prior to testing, or skipping the testing entirely. +/obj/machinery/proc/fail_construct_state_unit_test() + if(!construct_state) + return FALSE + return construct_state.fail_unit_test(src) + /datum/unit_test/machine_construct_states_shall_be_valid/start_test() var/failed = list() for(var/thing in SSmachines.machinery) var/obj/machinery/machine = thing if(failed[machine.type]) continue - if(!machine.construct_state) - continue - var/fail = machine.construct_state.fail_unit_test(machine) + var/fail = machine.fail_construct_state_unit_test() if(fail) failed[machine.type] = TRUE log_bad(fail) From 575723fd4ab823ec1b3403032be59389606cc5c5 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 26 May 2025 00:58:09 -0400 Subject: [PATCH 0669/1331] Exempt light fixtures from panel opening unit test --- code/modules/power/lighting.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 9564d1220823..bc4d4bdba08c 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -250,6 +250,10 @@ update_light_status(TRUE) update_icon() +// Just... skip the entire test. We don't need to remove the bulbs from every single light just to test this. +/obj/machinery/light/fail_construct_state_unit_test() + return FALSE + /obj/machinery/light/cannot_transition_to(state_path, mob/user) if(lightbulb && !ispath(state_path, /decl/machine_construction/wall_frame/panel_closed)) return SPAN_WARNING("You must first remove the lightbulb!") From 818d55007524c801d9bba8a8620b9f7cbbedb781 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 26 May 2025 01:09:18 -0400 Subject: [PATCH 0670/1331] Expand wall frame attackby validation for hackables --- .../machine_construction/airlock.dm | 2 +- .../wall_frame_hackable.dm | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/code/game/machinery/_machines_base/machine_construction/airlock.dm b/code/game/machinery/_machines_base/machine_construction/airlock.dm index 0c686e0b227a..48de7420a1b7 100644 --- a/code/game/machinery/_machines_base/machine_construction/airlock.dm +++ b/code/game/machinery/_machines_base/machine_construction/airlock.dm @@ -19,7 +19,7 @@ if(machine.construct_state != src) return "Machine [log_info_line(machine)] had a construct_state of type [machine.construct_state.type] after screwdriver interaction (expected [type])." // Now test the down state - var/obj/item/wrench/wrench = new + var/static/obj/item/wrench/wrench = new if(!machine.attackby(wrench, user)) return "Machine [log_info_line(machine)] did not respond to attackby with wrench." if(machine.construct_state.type != down_state) diff --git a/code/game/machinery/_machines_base/machine_construction/wall_frame_hackable.dm b/code/game/machinery/_machines_base/machine_construction/wall_frame_hackable.dm index 2afb4460f3fa..7c1d98873bda 100644 --- a/code/game/machinery/_machines_base/machine_construction/wall_frame_hackable.dm +++ b/code/game/machinery/_machines_base/machine_construction/wall_frame_hackable.dm @@ -64,6 +64,29 @@ . += "Use a screwdriver close the hatch and tuck the exposed wires back in." . += "Use a parts replacer to view installed parts." +/decl/machine_construction/wall_frame/panel_closed/hackable/fail_test_state_transfer(obj/machinery/machine, mob/user) + var/static/obj/item/screwdriver/screwdriver = new + // Prevent access locks on doors from interfering with our interactions. + for(var/obj/item/stock_parts/access_lock/lock in machine.get_all_components_of_type(/obj/item/stock_parts/access_lock)) + lock.locked = FALSE + // Test hacking state + if(!machine.attackby(screwdriver, user)) + return "Machine [log_info_line(machine)] did not respond to attackby with screwdriver." + var/const/hacking_state = /decl/machine_construction/wall_frame/panel_closed/hackable/hacking // TODO: un-hardcode this + if(machine.construct_state.type != hacking_state) + return "Machine [log_info_line(machine)] had a construct_state of type [machine.construct_state.type] after screwdriver interaction (expected [hacking_state])." + // Do it again to reverse that state change. + if(!machine.attackby(screwdriver, user)) + return "Machine [log_info_line(machine)] did not respond to attackby with screwdriver on a second try." + if(machine.construct_state != src) + return "Machine [log_info_line(machine)] had a construct_state of type [machine.construct_state.type] after screwdriver interaction (expected [type])." + // Now test the open state + var/static/obj/item/crowbar/crowbar = new + if(!machine.attackby(crowbar, user)) + return "Machine [log_info_line(machine)] did not respond to attackby with crowbar." + if(machine.construct_state.type != open_state) + return "Machine [log_info_line(machine)] had a construct_state of type [machine.construct_state.type] after screwdriver interaction (expected [open_state])." + /decl/machine_construction/wall_frame/panel_open/hackable/up_interaction(obj/item/I, mob/user, obj/machinery/machine) if(IS_CROWBAR(I)) TRANSFER_STATE(active_state) From 0405f491a2cfd289cf64305049fb143bf92b1b87 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 26 May 2025 01:15:11 -0400 Subject: [PATCH 0671/1331] Make disconnecting a wall-mounted igniter use wirecutters --- code/game/machinery/igniter.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 9cbd9367bf7e..176f286e06bb 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -120,13 +120,13 @@ icon_state = "[base_state]-p" /obj/machinery/sparker/attackby(obj/item/used_item, mob/user) - if(IS_SCREWDRIVER(used_item)) + if(panel_open && IS_WIRECUTTER(used_item)) add_fingerprint(user) disable = !disable if(disable) - user.visible_message("[user] has disabled \the [src]!", "You disable the connection to \the [src].") - else if(!disable) - user.visible_message("[user] has reconnected \the [src]!", "You fix the connection to \the [src].") + user.visible_message(SPAN_WARNING("[user] has disabled \the [src]!"), SPAN_WARNING("You disable the connection to \the [src].")) + else + user.visible_message(SPAN_WARNING("[user] has reconnected \the [src]!"), SPAN_WARNING("You fix the connection to \the [src].")) update_icon() return TRUE else From 62fd33e66c64a2eec0f4c9e8403f6c5091dabd91 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 27 May 2025 00:57:06 +0000 Subject: [PATCH 0672/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index fa25dd37d04d..e7f339e388a8 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -84,12 +84,6 @@

    Martin Rivard updated:

    • removed calculate_affecting_pressure() hardcoding
    - -

    25 March 2025

    -

    Penelope Haze updated:

    -
      -
    • Adjusts how suffocation works, meaning characters who were drowning or in asystole start breathing as soon as the issue is resolved, rather than counting down a timer instead. This might make brief bouts of drowning/cardiac arrest more survivable. Please report any weirdness with breathing/suffocation on the issue tracker.
    • -
    From 88ebe188b219cfb5fbac173fe5748591e7d61fa9 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 29 May 2025 00:58:26 +0000 Subject: [PATCH 0673/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index e7f339e388a8..f16e5be1a304 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -78,12 +78,6 @@

    Martin Rivard updated:

    • fixed portable flasher sprite offset
    - -

    27 March 2025

    -

    Martin Rivard updated:

    -
      -
    • removed calculate_affecting_pressure() hardcoding
    • -
    From 9924aa1a96ceee873d96680731960c5bb0d52b54 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 31 May 2025 00:57:06 +0000 Subject: [PATCH 0674/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index f16e5be1a304..9a98da8995fa 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -72,12 +72,6 @@

    Elizabeth updated:

  • many of the buildings in Shaded Hills now use timber-framed, plastered, and/or wattle-and-daub walls instead of just log walls
  • added a new lobby track to Shaded Hills: Adventure by Alexander Nakarada.
  • - -

    29 March 2025

    -

    Martin Rivard updated:

    -
      -
    • fixed portable flasher sprite offset
    • -
    From 368ea6477091a6ec8d27a420a86a845f11c65b12 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 6 Jun 2025 17:29:18 -0400 Subject: [PATCH 0675/1331] Add a basic modpack system readme --- mods/README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 mods/README.md diff --git a/mods/README.md b/mods/README.md new file mode 100644 index 000000000000..3ad3e5f818b8 --- /dev/null +++ b/mods/README.md @@ -0,0 +1,56 @@ +# Modpacks + +## What exactly are modpacks? + +Modpacks are, effectively, self-contained (modular) feature bundles. They can contain new objects, new species, new UIs, overrides for existing code, and even more. If you want a feature to be optional on a per-map or per-server basis, while ensuring it's always checked for correctness in unit tests, it should be placed in a modpack. + +## Wait, but I've heard that modular code is bad! + +This is a common sentiment, but it's sort of a misconception. There's nothing *inherently* wrong with modular code; after all, it's just code that'd designed for easy extension and reusability, which is always desirable in a project like SS13. However, the way other codebases tend to handle modularity by just adding new DM files in a separate folder, directly included in the DME, creates issues. Some of the common objections are: + +### Load Order + +With the typical way "modular" code is done, load order is difficult to manage and often results in folder names like `upstreamname`, `downstreamname`, `zzzz_servername`, etc., plus it requires editing the main DME. Modpacks have a defined, user-controlled load order, and cross-modpack compatibility is always handled after all modpacks are loaded. + +### Organization and Navigation + +Similarly, that file-structure makes it very difficult to organize and navigate the codebase as a whole. The original modularization standards were fairly straightforward, in that the modular folder was treated as a straight-forward layer on top of the original codebase, with files being treated as though they're present in the original codebase structure. a trend that ended up being set by other servers going for modularization was adding subdirectories of "modules", with almost every unique piece of content being given it's own individual module folder. The problem with this is that modules don't really have any defined structure or handling, and tend to just amount to subdirectories and nothing more. With every piece of content having its own module, even if they just override or overwrite base-game definitions and nothing more, this leads to the codebase being incredibly hard to navigate even if you already know roughly where what you want to modify is. This even applies to the base game, because some code is separated by type (obj, datum, etc.) and others are separated into conceptual modules. + +Nebula's modpacks are similar to modules in that they're separated by concept, but modpacks are more rigorous, with scripts and tests for validation. Each modpack is tested as a unit (along with the base game) as well as in integration with other modpacks on the various included maps. + +### Proc Overrides + +Modular code relies heavily on overrides and even side-overrides (overrides defined after, but on the same type as, an existing proc override or definition), which can obfuscate control flow and lead to code breaking when assumptions made about the calling proc change. This is a valid concern! In a lot of cases, though, it's avoidable by making modular behaviour use explicit interfaces intended for change in modpacks, like specifying datum (often decl) handlers that receive one override each. `/decl/human_examination` is a good example of this. + +### Merge Conflicts + +The issue with "modularity" as done elsewhere is that it just masks conflicts; upstream files are kept intact, but they might be unincluded or the methods they define/call might be completely replaced later on. This swaps out annoying but easily-resolvable merge conflicts for the far-worse issue of silent breakage. + +### Upstream Conflicts + +More broadly, modular code on a downstream with an upstream that does frequent refactors and rewrites is inevitably going to break when the upstream codebase does anything. Names change, so do assumptions, and even design directions might diverge so far that reconciling them will be hard or even impossible. There's not really getting around that, but we can at least mitigate it by designing stable interfaces and documenting changes. When upstream code is written with modularity in mind, downstreams have a much easier time adding content. + +### Implicit Dependencies + +The idea of modular code as independent falls apart when code modules begin to require each other to function, without explicitly noting that dependency. Worse yet, sometimes there are non-modular edits to base-game code that require a particular piece of modular code to be included. Since Nebula runs unit tests on an example map with no modpacks and no map-specific code, the core game will always function without any modpacks included. Similarly, modpacks are tested one-at-a-time as well as all together, to ensure there aren't any implicit dependencies. If one modpack requires another to function, there's three methods to resolve this. First, you could have one modpack include the other, making the dependency explicit. Second, you could merge the modpacks so they aren't separate at all. Third, you could use a compatibility patch to gate the content that requires both to be loaded. + +## Okay, but how do I make a modpack? + +TODO: Write this section. Explain things like creating a DME, where to include the DME, etc. Also explain some common organizational conventions like `foo_overrides.dm` files and `overrides` folders to better organize overrides of base-game procs and functionality. + +### How do I modpack... + +- A subsystem? + - Check the psionics modpack for an example. +- Something small? + - Check the mundane or scaling descriptor modpacks as examples. +- NanoUI templates? + - Check the inertial dampener or supermatter modpacks as examples. (Don't forget to set the nanoui_directory variable on the modpack decl!) + +## How do I write upstream/core code with extension via modpacks in mind? + +TODO: Write this section. Give examples like `/decl/atmos_grief_fix_step`, `/decl/human_examination`, the cocktails system, etc. Iterating over subtypes of a base type makes it easy for modpacks to add new code. Also maybe address some footguns like trying to make something modular before trying to make it actually work? Could also discuss the open-closed principle I guess, e.g. write code that gets *extended* rather than *modified* (so avoiding side-overrides, etc.). + +# Contribution + +Please contribute to this README/guide. It's currently unfinished and doesn't cover a lot of important things. Thanks. \ No newline at end of file From b1e66a202838de42b4ba2f45e4aec98cc6b768fe Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 8 Jun 2025 01:04:32 +0000 Subject: [PATCH 0676/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 9 --------- 1 file changed, 9 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 9a98da8995fa..928bd534bf95 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -63,15 +63,6 @@

    Cerebulon updated:

    • Fixed tajaran sprite sheet overrides.
    - -

    06 April 2025

    -

    Elizabeth updated:

    -
      -
    • tweaked the Forester's Hut on Shaded Hills to be more compact and have timber-framed walls
    • -
    • tweaked the inn backroom on Shaded Hills
    • -
    • many of the buildings in Shaded Hills now use timber-framed, plastered, and/or wattle-and-daub walls instead of just log walls
    • -
    • added a new lobby track to Shaded Hills: Adventure by Alexander Nakarada.
    • -
    From 951484b4d3d3c55b57053c5724bfe36288019cbd Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 24 Aug 2024 20:16:16 -0400 Subject: [PATCH 0677/1331] Fix errors discovered by unit tests --- .../species/bayliens/skrell/datum/language.dm | 35 +++++++++++++++++++ .../neoavians/datum/species_bodytypes.dm | 2 ++ 2 files changed, 37 insertions(+) create mode 100644 mods/species/bayliens/skrell/datum/language.dm diff --git a/mods/species/bayliens/skrell/datum/language.dm b/mods/species/bayliens/skrell/datum/language.dm new file mode 100644 index 000000000000..649ee1649e6c --- /dev/null +++ b/mods/species/bayliens/skrell/datum/language.dm @@ -0,0 +1,35 @@ +var/global/list/first_name_skrell = file2list("mods/species/bayliens/skrell/names/first_name_skrell.txt") +var/global/list/last_name_skrell = file2list("mods/species/bayliens/skrell/names/last_name_skrell.txt") + +/decl/language/skrell + name = "Common Skrellian" + desc = "A melodic and complex language spoken by the Skrell of the Oligarchy. Some of the notes are inaudible to humans." + speech_verb = "warbles" + ask_verb = "trills" + exclaim_verb = "croaks" + colour = "selenian" + key = "k" + flags = LANG_FLAG_WHITELISTED + syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","'","!") + shorthand = "SK" + partial_understanding = list( + /decl/language/skrell/casteless = 90 + ) + +/decl/language/skrell/casteless + name = "Ue-Katish Cant" + desc = "A flat and emotionless language spoken mainly by Ue-Katish pirates, laden with slurs and insults." + speech_verb = "croaks" + ask_verb = "trills" + exclaim_verb = "chirps" + colour = "selenian" + key = "p" + flags = LANG_FLAG_WHITELISTED + syllables = list("qra","xa","vilz*","s!zaao","qu!*m","girx","vol","xrim","za-r*oo","qu-xx","qix","qa'taz","zix","tuep","!","*") + shorthand = "UK" + partial_understanding = list( + /decl/language/skrell = 90 + ) + +/decl/language/skrell/get_random_name(var/gender) + return capitalize(pick(global.first_name_skrell)) + capitalize(pick(global.last_name_skrell)) \ No newline at end of file diff --git a/mods/species/neoavians/datum/species_bodytypes.dm b/mods/species/neoavians/datum/species_bodytypes.dm index a83f74e9b60c..03e8a9e5657e 100644 --- a/mods/species/neoavians/datum/species_bodytypes.dm +++ b/mods/species/neoavians/datum/species_bodytypes.dm @@ -26,6 +26,7 @@ uid = "bodytype_prosthetic_raptor" /decl/bodytype/avian + abstract_type = /decl/bodytype/avian name = "avian" bodytype_category = BODYTYPE_AVIAN icon_base = 'mods/species/neoavians/icons/body.dmi' @@ -84,6 +85,7 @@ uid = "bodytype_avian_raptor" /decl/bodytype/avian/additive + abstract_type = /decl/bodytype/avian/additive name = "avian, additive" icon_base = 'mods/species/neoavians/icons/body_add.dmi' health_hud_intensity = 3 From 7361804850e6e0e1082252c44e143a7217d3f167 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 27 Aug 2024 17:04:51 -0400 Subject: [PATCH 0678/1331] Add support for migration directories to update_paths --- tools/mapmerge2/update_paths.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tools/mapmerge2/update_paths.py b/tools/mapmerge2/update_paths.py index b9e095b5ee32..f0b6af46072b 100644 --- a/tools/mapmerge2/update_paths.py +++ b/tools/mapmerge2/update_paths.py @@ -190,9 +190,20 @@ def main(args): print("Using replacement:", args.update_source) updates = [args.update_source] else: - with open(args.update_source) as f: - updates = [line for line in f if line and not line.startswith("#") and not line.isspace()] - print(f"Using {len(updates)} replacements from file:", args.update_source) + # optional support for passing a directory + if os.path.isdir(args.update_source): + updates = [] + for root, _, files in os.walk(args.update_source): + for filepath in files: + if filepath.endswith(".txt"): + path = os.path.join(root, filepath) + with open(path) as f: + updates.extend(line for line in f if line and not line.startswith("#") and not line.isspace()) + print(f"Using {len(updates)} replacements from directory:", args.update_source) + else: + with open(args.update_source) as f: + updates = [line for line in f if line and not line.startswith("#") and not line.isspace()] + print(f"Using {len(updates)} replacements from file:", args.update_source) if args.map: update_map(args.map, updates, verbose=args.verbose) From c3a2bba560527978b810ba8b0c981dc2ad85148d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 27 Aug 2024 18:47:04 -0400 Subject: [PATCH 0679/1331] Add missing oldtile floor decals --- code/game/turfs/flooring/_flooring_decals.dm | 110 +++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/code/game/turfs/flooring/_flooring_decals.dm b/code/game/turfs/flooring/_flooring_decals.dm index 57b678005ee5..50ca2d36ab71 100644 --- a/code/game/turfs/flooring/_flooring_decals.dm +++ b/code/game/turfs/flooring/_flooring_decals.dm @@ -1525,3 +1525,113 @@ var/global/list/floor_decals = list() /obj/effect/floor_decal/arrows name = "floor arrows" icon_state = "arrows" + +//Old tile, taken from Polaris +//TODO: Change these colors to use color defines? +/obj/effect/floor_decal/corner_oldtile + name = "corner oldtile" + icon_state = "corner_oldtile" + +/obj/effect/floor_decal/corner_oldtile/white + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#d9d9d9" + +/obj/effect/floor_decal/corner_oldtile/white/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/white/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" + +/obj/effect/floor_decal/corner_oldtile/blue + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#8ba7ad" + +/obj/effect/floor_decal/corner_oldtile/blue/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/blue/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" + +/obj/effect/floor_decal/corner_oldtile/yellow + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#8c6d46" + +/obj/effect/floor_decal/corner_oldtile/yellow/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/yellow/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" + +/obj/effect/floor_decal/corner_oldtile/gray + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#687172" + +/obj/effect/floor_decal/corner_oldtile/gray/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/gray/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" + +/obj/effect/floor_decal/corner_oldtile/beige + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#385e60" + +/obj/effect/floor_decal/corner_oldtile/beige/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/beige/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" + +/obj/effect/floor_decal/corner_oldtile/red + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#964e51" + +/obj/effect/floor_decal/corner_oldtile/red/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/red/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" + +/obj/effect/floor_decal/corner_oldtile/purple + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#906987" + +/obj/effect/floor_decal/corner_oldtile/purple/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/purple/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" + +/obj/effect/floor_decal/corner_oldtile/green + name = "corner oldtile" + icon_state = "corner_oldtile" + color = "#46725c" + +/obj/effect/floor_decal/corner_oldtile/green/diagonal + name = "corner oldtile diagonal" + icon_state = "corner_oldtile_diagonal" + +/obj/effect/floor_decal/corner_oldtile/green/full + name = "corner oldtile full" + icon_state = "corner_oldtile_full" \ No newline at end of file From 8cbe69ad46c2b6f2ec866cb9de63f2ae94d67d93 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 27 Aug 2024 19:15:34 -0400 Subject: [PATCH 0680/1331] Fix incorrect pipe dispenser migrations --- tools/map_migrations/0000_legacy.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/map_migrations/0000_legacy.txt b/tools/map_migrations/0000_legacy.txt index 91f8fa256685..d2bd25806eab 100644 --- a/tools/map_migrations/0000_legacy.txt +++ b/tools/map_migrations/0000_legacy.txt @@ -242,10 +242,10 @@ # FABRICATORS /obj/machinery/autolathe{hacked = 1} : /obj/machinery/fabricator/hacked{@OLD; hacked = @SKIP} /obj/machinery/autolathe : /obj/machinery/fabricator{@OLD} -/obj/machinery/pipedispenser : /obj/machinery/fabricator/pipe{@OLD} -/obj/machinery/pipedispenser/orderable : /obj/machinery/fabricator/pipe{@OLD; anchored = 1} -/obj/machinery/pipedispenser/disposal : /obj/machinery/fabricator/pipe/disposal{@OLD} -/obj/machinery/pipedispenser/disposal/orderable : /obj/machinery/fabricator/pipe/disposal{@OLD; anchored = 1} +/obj/machinery/pipedispenser : /obj/machinery/fabricator/pipe{@OLD; anchored = 1} +/obj/machinery/pipedispenser/orderable : /obj/machinery/fabricator/pipe{@OLD} +/obj/machinery/pipedispenser/disposal : /obj/machinery/fabricator/pipe/disposal{@OLD; anchored = 1} +/obj/machinery/pipedispenser/disposal/orderable : /obj/machinery/fabricator/pipe/disposal{@OLD} /obj/machinery/r_n_d/circuit_imprinter : /obj/machinery/fabricator/imprinter{@OLD} /obj/machinery/pros_fabricator : /obj/machinery/fabricator/robotics{@OLD} /obj/machinery/mecha_part_fabricator : /obj/machinery/fabricator/industrial{@OLD} From 91140f49538afd6120e933333718d70398bf23ff Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 30 Aug 2024 07:00:20 -0400 Subject: [PATCH 0681/1331] Fix mis-named yellow dress --- code/modules/clothing/dresses/misc.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/clothing/dresses/misc.dm b/code/modules/clothing/dresses/misc.dm index 49c74faa645e..b6c623159b25 100644 --- a/code/modules/clothing/dresses/misc.dm +++ b/code/modules/clothing/dresses/misc.dm @@ -4,7 +4,7 @@ icon = 'icons/clothing/dresses/dress_green.dmi' /obj/item/clothing/dress/yellow - name = "green dress" + name = "yellow dress" desc = "A simple yellow dress." icon = 'icons/clothing/dresses/dress_yellow.dmi' @@ -20,7 +20,7 @@ /obj/item/clothing/dress/purple name = "purple dress" - desc= "A simple, tight-fitting purple dress." + desc = "A simple, tight-fitting purple dress." icon = 'icons/clothing/dresses/dress_purple.dmi' /obj/item/clothing/dress/saloon From c8ee7b5763645c1c9dc61e76dd72f135d70f20e0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 30 Aug 2024 07:03:36 -0400 Subject: [PATCH 0682/1331] Add a handful of legacy migrations (mostly clothing) --- tools/map_migrations/0000_legacy.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/map_migrations/0000_legacy.txt b/tools/map_migrations/0000_legacy.txt index d2bd25806eab..6ddafebd24f3 100644 --- a/tools/map_migrations/0000_legacy.txt +++ b/tools/map_migrations/0000_legacy.txt @@ -144,6 +144,7 @@ # CLOTHING /obj/item/clothing/head/cone : /obj/item/caution/cone{@OLD} +/obj/item/clothing/belt/security/tactical : /obj/item/belt/holster/security/tactical{@OLD} # Item remains /obj/effect/decal/remains/@SUBTYPES : /obj/item/remains/@SUBTYPES{@OLD} @@ -330,6 +331,10 @@ /obj/machinery/account_database : /obj/machinery/computer/account_database{@OLD} /obj/machinery/shield_gen/@SUBTYPES : /obj/machinery/shield_generator +/obj/structure/morgue/crematorium : /obj/structure/crematorium{@OLD} + +/obj/item/robot_parts/frame : /obj/item/robot_parts/robot_suit{@OLD} + /obj/structure/closet/secure_closet/psych : /obj/structure/closet/secure_closet/psychiatry{@OLD} /obj/structure/closet/firecloset/full : /obj/structure/closet/firecloset{@OLD} /obj/structure/closet/firecloset/full/double : /obj/structure/closet/firecloset/chief{@OLD} @@ -388,6 +393,24 @@ /obj/item/banner/nt : /obj/item/banner/nanotrasen{@OLD} +/obj/item/box/syndie_kit/clerical : /obj/item/backpack/satchel/syndie_kit/clerical{@OLD} + +/obj/item/clothing/under/costume/rainbow : /obj/item/clothing/jumpsuit/rainbow{@OLD} +/obj/item/clothing/under/dress/dress_orange : /obj/item/clothing/dress/orange{@OLD} +/obj/item/clothing/head/collectable/xenos : /obj/item/clothing/head/xenos{@OLD} +/obj/item/clothing/head/collectable/bearpelt : /obj/item/clothing/head/bearpelt{@OLD} +/obj/item/clothing/head/collectable/philosopher_wig : /obj/item/clothing/head/philosopher_wig{@OLD} +/obj/item/clothing/head/collectable/plaguedoctorhat : /obj/item/clothing/head/plaguedoctorhat{@OLD} +/obj/item/clothing/head/helmet/space/void/security/riot : /obj/item/clothing/head/helmet/space/void/security/alt{@OLD} +/obj/item/clothing/suit/space/void/security/riot : /obj/item/clothing/suit/space/void/security/alt{@OLD} +/obj/item/clothing/head/helmet/space/vox/pressure : /obj/item/clothing/head/helmet/space/vox{@OLD} +/obj/item/clothing/under/vox/vox_utility : /obj/item/clothing/pants/vox{@OLD} +/obj/item/clothing/suit/space/pirate : /obj/item/clothing/suit/pirate{@OLD} +/obj/item/clothing/suit/costume : /obj/item/clothing/suit/pirate{@OLD} +/obj/item/clothing/suit/costume/hgpirate : /obj/item/clothing/suit/hgpirate{@OLD} +/obj/item/clothing/suit/storage/vest/heavy/merc : /obj/item/clothing/suit/armor/pcarrier/merc{@OLD} +/obj/item/clothing/suit/storage/vest/@SUBTYPES : /obj/item/clothing/suit/armor/vest/@SUBTYPES{@OLD} + ############ # VAREDITS # ############ From 95f53f3a7dda57902345d6ec1a25f934818a3d6e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 1 Sep 2024 05:39:57 -0400 Subject: [PATCH 0683/1331] Add partially-constructed light fixture presets --- code/modules/power/lighting.dm | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index bc4d4bdba08c..39184fecc631 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -655,3 +655,18 @@ if(lightbulb) remove_bulb() return TRUE + +// Partially-constructed presets for mapping +/obj/machinery/light/fixture + icon_state = "tube-construct-stage1" + +/obj/machinery/light/fixture/Initialize(mapload, d, populate_parts) + . = ..(mapload, d, populate_parts = FALSE) + construct_state.post_construct(src) + +/obj/machinery/light/small/fixture + icon_state = "bulb-construct-stage1" + +/obj/machinery/light/small/fixture/Initialize(mapload, d, populate_parts) + . = ..(mapload, d, populate_parts = FALSE) + construct_state.post_construct(src) \ No newline at end of file From 7f93e2e9fb0875b3248f288c142dab4f62fa2a45 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 1 Sep 2024 05:40:14 -0400 Subject: [PATCH 0684/1331] Add red back-mounted oxygen tank --- code/game/objects/items/weapons/tanks/tank_types.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index f5b1580e8a67..643125e14e14 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -22,6 +22,10 @@ desc = "A tank of oxygen. This one is yellow." icon = 'icons/obj/items/tanks/tank_yellow.dmi' +/obj/item/tank/oxygen/red + desc = "A tank of oxygen. This one is red." + icon = 'icons/obj/items/tanks/tank_red.dmi' + /obj/item/tank/oxygen/empty starting_pressure = list() From 076f9033c15d951df9039381ea25a15464c61dfa Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 6 Sep 2024 01:39:48 -0400 Subject: [PATCH 0685/1331] Add mobile oxygen pumps --- code/game/machinery/oxygen_pump.dm | 44 +++++++++++++++++++++++++++++ icons/obj/machines/medpump.dmi | Bin 0 -> 1107 bytes 2 files changed, 44 insertions(+) create mode 100644 icons/obj/machines/medpump.dmi diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 103628176c11..70671c1d7e45 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -233,3 +233,47 @@ tank.distribute_pressure += cp tank.distribute_pressure = min(max(round(tank.distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE) . = TOPIC_REFRESH // Refreshing is handled in machinery/Topic + +/obj/machinery/oxygen_pump/mobile + name = "portable oxygen pump" + icon = 'icons/obj/machines/medpump.dmi' + desc = "A portable oxygen pump with a retractable mask that you can pull over your face in case of emergencies." + icon_state = "medpump" + icon_state_open = "medpump_open" + icon_state_closed = "medpump" + icon_state_active = "medpump_active" + + anchored = FALSE + density = TRUE + + +/obj/machinery/oxygen_pump/mobile/stabilizer + name = "portable patient stabilizer" + desc = "A portable oxygen pump with a retractable mask used for stabilizing patients in the field." + icon_state = "patient_stabilizer" + icon_state_closed = "patient_stabilizer" + icon_state_open = "patient_stabilizer_open" + icon_state_active = "patient_stabilizer_active" + +/obj/machinery/oxygen_pump/mobile/stabilizer/process() + . = ..() + if(!breather) // Safety. + return + if(breather.isSynthetic()) + return + +/* TODO: port modifiers or something similar + breather.add_modifier(breather.stat == DEAD ? /datum/modifier/bloodpump/corpse : /datum/modifier/bloodpump, 6 SECONDS) +*/ + + var/obj/item/organ/internal/lungs/lungs = breather.get_organ(BP_LUNGS, /obj/item/organ/internal/lungs) + if(!lungs) + return + if(lungs.status & ORGAN_DEAD) + breather.adjustOxyLoss(-(rand(1,8))) + else + breather.adjustOxyLoss(-(rand(10,15))) + if(lungs.is_bruised() && prob(30)) + lungs.heal_damage(1) + else + breather.ticks_since_last_successful_breath = max(breather.ticks_since_last_successful_breath - rand(1,5), 0) \ No newline at end of file diff --git a/icons/obj/machines/medpump.dmi b/icons/obj/machines/medpump.dmi new file mode 100644 index 0000000000000000000000000000000000000000..01f5a1dc4b9aeacdcca8b62c80f87a7c5cdbfac5 GIT binary patch literal 1107 zcmV-Z1g!gsP)004jp0{{R3yS%*-fU#JkzPmzS3u92^f14?aUhmx~#;syW`# zS&);M6%`d-U1TF7CO$ns)YQ@^Cnq>GG%zhKmK7Zq5faT9BUDpRG@np2Gcy`UCLUie zThVp@00001bW%=J06^y0W&i*Hzj{TiQFu?N-Y7|BmOhqD5Nl zRG6MK&A0x`{%=-dAA3MV@8%wJgDV3eAE;n#7B`Vha9~32OJA?Yb#&>s(oeSJr?KA z9eiEa{t({<^1Ol}&|<(>PPz;<(*7^)2Ty&7YvCVwsB0r|(*LnI6!;G1 zy%PmK!vdr~1Wx)t7N6n=|H6~tr2k`aasQ`&aBh7kUO-yE@%`Ye{!rZiJ;D!8IH>32 z;{H$lp!z}egX#xGxw!vRKd8}YG~Xfl`@?W&$hbmxf&vBoAic{2l2J zhfC{6+^!6~NpNJonc(k8e`wtq-r(%)8Y_Uu%~XGf`N6?1o{<8ka-_qC>FFAy#IeA0^q2^| zX&a1p1@^qbj~Uh%5P@DU7rX<3Hw+jg}ul5mhGjsdBB){56 z>|V_Eck|9K7Aw-vrFh!!vHjtS^mn&Bx<_vOUc5uo{UNgciu7|Sp7whQKbZ21eF3sI^elTvgQht%|?;gP^zsUA?51Ws6aAv>9_Jec#J@td~T-^VuA5=f6evrj8 Z%|GtHvJSlBd-MPR002ovPDHLkV1j6y7mfe` literal 0 HcmV?d00001 From 28da263a19352b41733041013fb907b48ec5220d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 28 Aug 2024 00:47:30 -0400 Subject: [PATCH 0686/1331] Add additional legacy migrations for mech parts --- tools/map_migrations/0000_legacy.txt | 987 ++++++++++++++------------- 1 file changed, 503 insertions(+), 484 deletions(-) diff --git a/tools/map_migrations/0000_legacy.txt b/tools/map_migrations/0000_legacy.txt index 6ddafebd24f3..721a4edf9f27 100644 --- a/tools/map_migrations/0000_legacy.txt +++ b/tools/map_migrations/0000_legacy.txt @@ -1,485 +1,504 @@ -/obj/item/reagent_containers/@SUBTYPES : /obj/item/chems/@SUBTYPES{@OLD} -/obj/item/material/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/weapon/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/melee/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/packageWrap/@SUBTYPES : /obj/item/stack/package_wrap/@SUBTYPES{@OLD} -/obj/structure/table/rack/@SUBTYPES : /obj/structure/rack/@SUBTYPES{@OLD} -/obj/structure/table/woodentable/holotable : /obj/structure/table/holo_woodentable{@OLD} -/obj/item/chems/food/drinks/@SUBTYPES : /obj/item/chems/drinks/@SUBTYPES{@OLD} -/obj/item/chems/food/snacks/@SUBTYPES : /obj/item/food/@SUBTYPES{@OLD} -/obj/item/chems/food/condiment/@SUBTYPES : /obj/item/chems/condiment/@SUBTYPES{@OLD} -/obj/item/chems/condiment/peppermill/@SUBTYPES : /obj/item/chems/condiment/small/peppermill/@SUBTYPES{@OLD} -/obj/item/chems/condiment/saltshaker/@SUBTYPES : /obj/item/chems/condiment/small/saltshaker/@SUBTYPES{@OLD} -/obj/item/analyzer : /obj/item/scanner/gas{@OLD} -/obj/item/analyzer/plant_analyzer/@SUBTYPES : /obj/item/scanner/plant/@SUBTYPES{@OLD} -/obj/item/tape_roll/@SUBTYPES : /obj/item/stack/tape_roll/@SUBTYPES{@OLD} -/obj/structure/table/standard/@SUBTYPES : /obj/structure/table/@SUBTYPES{@OLD} -/obj/machinery/bluespacerelay/@SUBTYPES : /obj/machinery/commsrelay/@SUBTYPES{@OLD} -/obj/machinery/computer/helm/@SUBTYPES : /obj/machinery/computer/ship/helm/@SUBTYPES{@OLD} -/obj/machinery/computer/engines/@SUBTYPES : /obj/machinery/computer/ship/engines/@SUBTYPES{@OLD} -/obj/machinery/computer/navigation/@SUBTYPES : /obj/machinery/computer/ship/navigation/@SUBTYPES{@OLD} -/obj/machinery/requests_console/@SUBTYPES : /obj/machinery/network/requests_console/@SUBTYPES{@OLD; departmentType = @SKIP} -/obj/item/healthanalyzer/@SUBTYPES : /obj/item/scanner/health/@SUBTYPES{@OLD} -/obj/machinery/iv_drip/@SUBTYPES : /obj/structure/iv_drip/@SUBTYPES{@OLD} -/obj/structure/device/piano/@SUBTYPES : /obj/structure/synthesized_instrument/synthesizer/piano/@SUBTYPES{@OLD} -/obj/item/folder/white/@SUBTYPES : /obj/item/folder/@SUBTYPES{@OLD} -/obj/machinery/dnaforensics/@SUBTYPES : /obj/machinery/forensic/dnascanner/@SUBTYPES{@OLD} -/obj/machinery/microscope/@SUBTYPES : /obj/machinery/forensic/microscope/@SUBTYPES{@OLD} -/obj/item/stack/material/steel/@SUBTYPES : /obj/item/stack/material/sheet/mapped/steel/@SUBTYPES{@OLD} -/obj/item/stack/material/plasteel/@SUBTYPES : /obj/item/stack/material/sheet/reinforced/mapped/plasteel/@SUBTYPES{@OLD} -/obj/item/stack/material/glass/reinforced/@SUBTYPES : /obj/item/stack/material/pane/mapped/rglass/@SUBTYPES{@OLD} -/obj/item/stack/material/glass/@SUBTYPES : /obj/item/stack/material/pane/mapped/glass/@SUBTYPES{@OLD} -/obj/item/stack/material/wood/@SUBTYPES : /obj/item/stack/material/plank/mapped/wood/@SUBTYPES{@OLD} -/obj/item/stack/material/deuterium/@SUBTYPES : /obj/item/stack/material/aerogel/mapped/deuterium/@SUBTYPES{@OLD} -/obj/item/stack/material/uranium/@SUBTYPES : /obj/item/stack/material/puck/mapped/uranium/@SUBTYPES{@OLD} -/obj/machinery/autolathe/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD} -/obj/machinery/message_server/@SUBTYPES : /obj/machinery/network/message_server/@SUBTYPES{@OLD} -/obj/item/floor_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} -/obj/machinery/pipedispenser/@SUBTYPES : /obj/machinery/fabricator/pipe/@SUBTYPES{@OLD} -/obj/machinery/power/emitter/@SUBTYPES : /obj/machinery/emitter/@SUBTYPES{@OLD} -/obj/machinery/power/fusion_core/@SUBTYPES : /obj/machinery/fusion_core/@SUBTYPES{@OLD} -/obj/item/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} -/obj/machinery/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} -/obj/machinery/computer/modular/preset/command/@SUBTYPES : /obj/machinery/computer/modular/preset/cardslot/command/@SUBTYPES{@OLD} -/obj/machinery/computer/fusion_core_control/@SUBTYPES : /obj/machinery/computer/fusion/core_control/@SUBTYPES{@OLD} -/obj/machinery/computer/fusion_fuel_control/@SUBTYPES : /obj/machinery/computer/fusion/fuel_control/@SUBTYPES{@OLD} -/obj/machinery/computer/gyrotron_control/@SUBTYPES : /obj/machinery/computer/fusion/gyrotron/@SUBTYPES{@OLD} -/obj/machinery/computer/power_monitor/@SUBTYPES : /obj/machinery/computer/modular/preset/engineering/power/@SUBTYPES{@OLD} -/obj/item/module/power_control/@SUBTYPES : /obj/item/stock_parts/circuitboard/apc/@SUBTYPES{@OLD} -/obj/machinery/power/port_gen/@SUBTYPES : /obj/machinery/port_gen/@SUBTYPES{@OLD} -/obj/item/pipe_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} -/obj/item/airlock_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/airlock_electronics/@SUBTYPES{@OLD} -/obj/item/airalarm_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/air_alarm/@SUBTYPES{@OLD} -/obj/item/autopsy_scanner/@SUBTYPES : /obj/item/scanner/autopsy/@SUBTYPES{@OLD} -/obj/machinery/fusion_fuel_compressor/@SUBTYPES : /obj/machinery/fuel_compressor/@SUBTYPES{@OLD} -/obj/item/extinguisher/micro/@SUBTYPES : /obj/item/chems/spray/extinguisher/mini/@SUBTYPES{@OLD} -/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} -/obj/item/stack/material/plastic/@SUBTYPES : /obj/item/stack/material/panel/mapped/plastic/@SUBTYPES{@OLD} -/obj/machinery/computer/general_air_control/@SUBTYPES : /obj/machinery/computer/air_control/@SUBTYPES{@OLD} -/obj/machinery/computer/air_control/large_tank_control : /obj/machinery/computer/air_control{@OLD} -/turf/simulated/floor/tiled/steel/@SUBTYPES : /turf/simulated/floor/tiled/@SUBTYPES{@OLD} -/obj/machinery/power/engine/@SUBTYPES : /obj/machinery/ion_thruster/@SUBTYPES{@OLD} -/obj/machinery/computer/aifixer/@SUBTYPES : /obj/machinery/computer/modular/preset/aislot/sysadmin/@SUBTYPES{@OLD} -/obj/machinery/computer/aiupload/@SUBTYPES : /obj/machinery/computer/upload/ai/@SUBTYPES{@OLD} -/obj/machinery/computer/borgupload/@SUBTYPES : /obj/machinery/computer/upload/robot/@SUBTYPES{@OLD} -/obj/machinery/computer/supplycomp/control/@SUBTYPES : /obj/machinery/computer/modular/preset/dock/@SUBTYPES{@OLD} -/obj/machinery/computer/supplycomp/@SUBTYPES : /obj/machinery/computer/modular/preset/supply_public/@SUBTYPES{@OLD} -/obj/item/closet_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} -/turf/simulated/shuttle/plating/@SUBTYPES : /turf/floor/shuttle -/turf/simulated/shuttle/floor/@SUBTYPES : /turf/floor/shuttle/@SUBTYPES - -# REMOVE BAD VAREDITS -/@SUBTYPES {layer = @SET} : @OLD {@OLD; layer = @SKIP} -/@SUBTYPES {plane = @SET} : @OLD {@OLD; plane = @SKIP} -/obj/structure/cable/@SUBTYPES : @OLD {@OLD; d1 = @SKIP;d2 = @SKIP} -# unacidable removed -/@SUBTYPES {unacidable = @SET} : @OLD{@OLD; unacidable = @SKIP} -# Simple pipes shall not face north or west -/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} -/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} -/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} -/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} -# Do not change air alarm frequency on base type -/obj/machinery/alarm {frequency = @SET} : @OLD {@OLD; frequency = @SKIP} -# we use a completely different access format -/@SUBTYPES {req_access = @SET}: @OLD {@OLD; req_access = @SKIP;} -/@SUBTYPES {req_one_access = @SET}: @OLD {@OLD; req_one_access = @SKIP;} -/obj/machinery/door/blast/shutters{icon_state = "shutter0"} : /obj/machinery/door/blast/shutters{@OLD; icon_state = @SKIP} -/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1; density = 0, opacity = 0} : /obj/machinery/door/airlock/external/glass/bolted_open{@OLD; icon_state = @SKIP; locked = @SKIP, density = @SKIP; opacity = @SKIP} -/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1} : /obj/machinery/door/airlock/external/glass/bolted{@OLD; icon_state = @SKIP; locked = @SKIP} - -# BUTTONS -/obj/machinery/access_button/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} -/obj/machinery/access_button/airlock_interior : /obj/machinery/button/access/interior{@OLD} -/obj/machinery/airlock_sensor/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} -/obj/machinery/airlock_sensor/airlock_interior : /obj/machinery/button/access/interior{@OLD} -/obj/machinery/access_button : /obj/machinery/button/access{@OLD} -/obj/machinery/button/remote/airlock{specialfunctions = 4} : /obj/machinery/button/alternate/door/bolts{@OLD; specialfunctions = @SKIP; desiredstate = @SKIP} -/obj/machinery/button/remote/airlock : /obj/machinery/button/alternate/door{@OLD} -/obj/machinery/button/remote/driver : /obj/machinery/button/mass_driver{@OLD} -/obj/machinery/button/remote/blast_door : /obj/machinery/button/blast_door{@OLD} -/obj/machinery/button/remote : /obj/machinery/button/alternate{@OLD} - -# Medical -/obj/item/pill_bottle/tramadol : /obj/item/pill_bottle/strong_painkillers{@OLD} -/obj/item/chems/pill/tramadol : /obj/item/chems/pill/strong_painkillers{@OLD} -/obj/item/chems/glass/bottle/stoxin : /obj/item/chems/glass/bottle/sedatives{@OLD} -/obj/item/chems/glass/bottle/inaprovaline : /obj/item/chems/glass/bottle/stabilizer{@OLD} -/obj/item/chems/syringe/inaprovaline : /obj/item/chems/syringe/stabilizer{@OLD} -/obj/item/chems/blood/empty : /obj/item/chems/ivbag{@OLD} -/obj/item/chems/blood/@SUBTYPES : /obj/item/chems/ivbag/blood/@SUBTYPES{@OLD} -/obj/item/defib_kit/@SUBTYPES : /obj/item/defibrillator/@SUBTYPES{@OLD} - -# Drinking glasses and cups -/obj/item/chems/drinks/cup : /obj/item/chems/drinks/glass2/coffeecup{@OLD} -/obj/item/chems/drinks/drinkingglass : /obj/item/chems/drinks/glass2/square{@OLD} - -# Airtight flaps -/obj/structure/plasticflaps/mining : /obj/structure/flaps/airtight{@OLD} - -# Bushes -/obj/structure/flora/ausbushes/@SUBTYPES : /obj/structure/flora/bush/@SUBTYPES - -# Cassette tapes -> magnetic tapes -/obj/item/cassette_tape/@SUBTYPES : /obj/item/magnetic_tape/@SUBTYPES{@OLD} -/obj/item/tape_recorder : /obj/item/taperecorder{@OLD} - -# FIFTYSPAWNER -> MAPPED SHEETS -/obj/fiftyspawner/wood : /obj/item/stack/material/plank/mapped/wood/fifty -/obj/fiftyspawner/steel : /obj/item/stack/material/sheet/mapped/steel/fifty -/obj/fiftyspawner/glass : /obj/item/stack/material/pane/mapped/glass/fifty -/obj/fiftyspawner/deuterium : /obj/item/stack/material/aerogel/mapped/deuterium/fifty -/obj/fiftyspawner/tritium : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/osmium : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/phoron : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/rods : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/grass : /obj/item/stack/tile/grass{amount = 50} -/obj/fiftyspawner/plasteel : /obj/item/stack/material/aerogel/mapped/tritium/fifty - -# STOCK PARTS AND SMES COILS -/obj/item/stock_parts/subspace/sub_filter : /obj/item/stock_parts/subspace/filter -/obj/item/smes_coil/@SUBTYPES : /obj/item/stock_parts/smes_coil/@SUBTYPES{@OLD} -/obj/item/circuitboard/@SUBTYPES : /obj/item/stock_parts/circuitboard/@SUBTYPES{@OLD} -/obj/item/airlock_electronics : /obj/item/stock_parts/circuitboard/airlock_electronics{@OLD} - -# CLOTHING -/obj/item/clothing/head/cone : /obj/item/caution/cone{@OLD} -/obj/item/clothing/belt/security/tactical : /obj/item/belt/holster/security/tactical{@OLD} - -# Item remains -/obj/effect/decal/remains/@SUBTYPES : /obj/item/remains/@SUBTYPES{@OLD} - -# Circuit prefabs -/obj/item/hand_tele : /obj/prefab/hand_teleporter{@OLD} - -# Filing cabinets -/obj/structure/filingcabinet : /obj/structure/filing_cabinet{@OLD} -/obj/structure/filingcabinet/chestdrawer : /obj/structure/filing_cabinet/chestdrawer{@OLD} -/obj/structure/filingcabinet/filingcabinet : /obj/structure/filing_cabinet/tall{@OLD} -/obj/structure/filingcabinet/medical : /obj/structure/filing_cabinet/records/medical{@OLD} -/obj/structure/filingcabinet/security : /obj/structure/filing_cabinet/records{@OLD} - -# Machine frames -/obj/structure/frame: /obj/machinery/constructable_frame/machine_frame{@OLD} -/obj/machinery/constructable_frame/machine_frame{anchored = 1} : /obj/machinery/constructable_frame/machine_frame/deconstruct{anchored = @SKIP} -/obj/structure/frame/computer : /obj/machinery/constructable_frame/computerframe{@OLD} -/obj/machinery/constructable_frame/computerframe{anchored = 1} : /obj/machinery/constructable_frame/computerframe/deconstruct{anchored = @SKIP} - -# Hygiene/bathroom objects -/obj/structure/toilet : /obj/structure/hygiene/toilet{@OLD} -/obj/structure/sink/@SUBTYPES : /obj/structure/hygiene/sink/@SUBTYPES{@OLD} -/obj/machinery/shower : /obj/structure/hygiene/shower{@OLD} - -# Turbolift spawners -/obj/turbolift_map_holder/@SUBTYPES : /obj/abstract/turbolift_spawner/@SUBTYPES{@OLD} - -# Borosilicate windows -/obj/structure/window/phoronreinforced/@SUBTYPES : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD} - -# Tools; see 3763_pickaxe_hammer and 3959_hatchets for other migrations -/obj/item/knife/machete : /obj/item/tool/machete{@OLD} -/obj/item/knife/machete/hatchet : /obj/item/tool/axe/hatchet{@OLD} -/obj/item/tool/pickaxe/hand : /obj/item/tool/pickaxe/xeno/hand{@OLD} - -# RANDOM SPAWNERS -/obj/random/cigarettes : /obj/random/smokes{@OLD} -/obj/item/lipstick/random : /obj/random/lipstick -/obj/random/slimecore : /obj/item/slime_extract/random{@OLD} - -# MINING EQUIPMENT -/obj/machinery/mineral/processing_unit : /obj/machinery/material_processing/smeltery -/obj/machinery/mineral/stacking_machine : /obj/machinery/material_processing/stacker -/obj/machinery/mineral/unloading_machine : /obj/machinery/material_processing/unloader -/obj/machinery/mineral/output : @DELETE -/obj/machinery/mineral/input : @DELETE -/obj/machinery/mineral/mint : @DELETE -/obj/machinery/mineral/processing_unit_console : @DELETE - -# NEW PDAS (NO CARTRIDGES) -/obj/item/pda/@SUBTYPES : /obj/item/modular_computer/pda/@SUBTYPES{@OLD} -/obj/item/modular_computer/pda/captain : /obj/item/modular_computer/pda/heads/captain -/obj/item/storage/box/seccarts : @DELETE -/obj/item/cartridge/@SUBTYPES : @DELETE - -# MODULAR COMPUTER PRESETS -/obj/machinery/computer/card : /obj/machinery/computer/modular/preset/cardslot/personnel{@OLD} -/obj/machinery/computer/secure_data : /obj/machinery/computer/modular/preset/civilian{@OLD} -/obj/machinery/computer/skills : /obj/machinery/computer/modular/preset/civilian{@OLD} -/obj/machinery/computer/security/telescreen : /obj/machinery/computer/modular/telescreen/preset/security{@OLD} -/obj/machinery/computer/security/telescreen/entertainment : /obj/machinery/computer/modular/telescreen/preset/entertainment{@OLD} -/obj/machinery/computer/security/mining : /obj/machinery/computer/modular/preset/security{@OLD} -/obj/machinery/computer/security/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES -/obj/machinery/computer/crew : /obj/machinery/computer/modular/preset/medical{@OLD} -/obj/machinery/computer/rcon : /obj/machinery/computer/modular/preset/engineering/rcon{@OLD} -/obj/machinery/computer/communications : /obj/machinery/computer/modular/preset/cardslot/command{@OLD} -/obj/machinery/computer/med_data : /obj/machinery/computer/modular/preset/civilian{@OLD} -/obj/machinery/computer/med_data/laptop : /obj/item/modular_computer/laptop/preset/records{@OLD} - -# ATMOS REPATHS -/obj/machinery/atmospherics/pipe/tank/@SUBTYPES : /obj/machinery/atmospherics/unary/tank/@SUBTYPES -/obj/machinery/portable_atmospherics/canister/nitrous_oxide : /obj/machinery/portable_atmospherics/canister/sleeping_agent{@OLD} - -# MISC. ITEMS -/obj/item/moneybag/vault : /obj/item/bag/cash/filled{@OLD} -/obj/item/moneybag : /obj/item/bag/cash{@OLD} -/obj/item/chems/bucket : /obj/item/chems/glass/bucket{@OLD} -/obj/item/card/id/gold/captain/spare : /obj/item/card/id/captains_spare{@OLD} -/obj/item/chainofcommand : /obj/item/whip/chainofcommand{@OLD} -/obj/item/chems/drinks/bottle/specialwhiskey : /obj/item/chems/drinks/bottle/agedwhiskey{@OLD} - -# Tape is weird -/obj/item/taperoll/@SUBTYPES : /obj/item/stack/tape_roll/barricade_tape/@SUBTYPES{@OLD} -/obj/item/tape/@SUBTYPES : /obj/structure/tape_barricade/@SUBTYPES{@OLD} - -# Radio -/obj/item/radio/headset/headset_sec/alt : /obj/item/radio/headset/headset_sec/bowman{@OLD} -/obj/item/radio/headset/syndicate : /obj/item/radio/headset/hacked{@OLD} -/obj/item/radio/headset/syndicate/alt : /obj/item/radio/headset/hacked{@OLD} -/obj/item/phone : /obj/item/radio/phone{@OLD} - -# Singulo -/obj/machinery/the_singularitygen : /obj/machinery/singularity_generator{@OLD} - -# FABRICATORS -/obj/machinery/autolathe{hacked = 1} : /obj/machinery/fabricator/hacked{@OLD; hacked = @SKIP} -/obj/machinery/autolathe : /obj/machinery/fabricator{@OLD} -/obj/machinery/pipedispenser : /obj/machinery/fabricator/pipe{@OLD; anchored = 1} -/obj/machinery/pipedispenser/orderable : /obj/machinery/fabricator/pipe{@OLD} -/obj/machinery/pipedispenser/disposal : /obj/machinery/fabricator/pipe/disposal{@OLD; anchored = 1} -/obj/machinery/pipedispenser/disposal/orderable : /obj/machinery/fabricator/pipe/disposal{@OLD} -/obj/machinery/r_n_d/circuit_imprinter : /obj/machinery/fabricator/imprinter{@OLD} -/obj/machinery/pros_fabricator : /obj/machinery/fabricator/robotics{@OLD} -/obj/machinery/mecha_part_fabricator : /obj/machinery/fabricator/industrial{@OLD} -/obj/machinery/r_n_d/protolathe : /obj/machinery/fabricator/protolathe{@OLD} -/obj/machinery/bookbinder : /obj/machinery/fabricator/book{@OLD} -/obj/machinery/organ_printer/flesh : /obj/machinery/fabricator/bioprinter{@OLD} -/obj/machinery/vending/food : /obj/machinery/fabricator/replicator - -/obj/machinery/fabricator/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD; res_max_amount = @SKIP; speed = @SKIP} - -# MMIs and posibrains -/obj/item/mmi/digital/@SUBTYPES : /obj/item/organ/internal/brain/robotic{@OLD} -/obj/item/mmi/@SUBTYPES : /obj/item/organ/internal/brain_interface/@SUBTYPES{@OLD} -/obj/item/organ/internal/posibrain : /obj/item/organ/internal/brain/robotic/positronic{@OLD} - -# Energy blades -/obj/item/energy/@SUBTYPES : /obj/item/energy_blade/@SUBTYPES{@OLD} - -# Medals and other accessories; see 3902_ties.txt for further migrations -/obj/item/clothing/accessory/medal/bronze_heart : /obj/item/clothing/accessory/medal/nanotrasen/bronze{@OLD} -/obj/item/clothing/accessory/medal/nobel_science : /obj/item/clothing/medal/nanotrasen/silver{@OLD} -/obj/item/clothing/accessory/scarf/white : /obj/item/clothing/accessory/scarf{@OLD} -/obj/item/clothing/accessory/storage/webbing : /obj/item/clothing/accessory/webbing/vest - -# MATERIALS -/obj/item/stack/material/plastic : /obj/item/stack/material/panel/mapped/plastic{@OLD} -/obj/item/stack/material/sandstone : /obj/item/stack/material/brick/mapped/sandstone{@OLD} -/obj/item/stack/material/mhydrogen : /obj/item/stack/material/segment/mapped/mhydrogen{@OLD} -/obj/item/stack/material/diamond : /obj/item/stack/material/gemstone/mapped/diamond{@OLD} -/obj/item/stack/material/wood : /obj/item/stack/material/plank/mapped/wood{@OLD} -/obj/item/stack/material/steel : /obj/item/stack/material/sheet/mapped/steel{@OLD} -/obj/item/stack/material/glass : /obj/item/stack/material/pane/mapped/glass{@OLD} -/obj/item/stack/material/phoron : /obj/item/stack/material/crystal/mapped/phoron{@OLD} -/obj/item/stack/material/algae : /obj/item/stack/material/puck/mapped/algae{@OLD} -/obj/item/stack/material/gold : /obj/item/stack/material/ingot/mapped/gold{@OLD} -/obj/item/stack/material/plasteel : /obj/item/stack/material/reinforced/mapped/plasteel{@OLD} -/obj/item/stack/material/glass/reinforced : /obj/item/stack/material/pane/mapped/rglass{@OLD} -/obj/item/stack/material/glass/phoronglass : /obj/item/stack/material/pane/mapped/borosilicate{@OLD} -/obj/item/stack/material/glass/phoronrglass : /obj/item/stack/material/pane/mapped/rborosilicate{@OLD} -/obj/item/stack/rods : /obj/item/stack/material/rods{@OLD} -/obj/item/packageWrap{amount = @UNSET} : /obj/item/stack/package_wrap/twenty_five{@OLD; amount = @SKIP} -/obj/item/packageWrap : /obj/item/stack/package_wrap{@OLD} -/obj/item/wrapping_paper{amount = @UNSET} : /obj/item/stack/package_wrap/gift/twenty_five{@OLD; amount = @SKIP} -/obj/item/wrapping_paper : /obj/item/stack/package_wrap{@OLD} - -/obj/structure/dispenser{phorontanks = 0} : /obj/structure/tank_rack/oxygen{@OLD; phorontanks = @SKIP} -/obj/structure/dispenser/oxygen{oxygentanks = 0} : /obj/structure/tank_rack/empty{@OLD; oxygentanks = @SKIP} -/obj/structure/dispenser/oxygen : /obj/structure/tank_rack/oxygen -/obj/structure/dispenser : /obj/structure/tank_rack - -# TELECOMMS REMOVAL -/obj/item/stock_parts/circuitboard/telecomms/@SUBTYPES : @DELETE -/obj/machinery/telecomms/@SUBTYPES : @DELETE -/obj/machinery/computer/telecomms/@SUBTYPES : @DELETE - -/obj/machinery/r_n_d/server/@SUBTYPES : /obj/machinery/design_database -/obj/machinery/r_n_d/destructive_analyzer : /obj/machinery/destructive_analyzer - -# SMES bullshit -/obj/machinery/power/smes/buildable/engine : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 1)} -/obj/machinery/power/smes/buildable/main : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 4)} -/obj/machinery/power/smes/buildable/point_of_interest : /obj/machinery/power/smes/buildable/preset/hidden - -/obj/item/surgical/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/FixOVein : /obj/item/sutures{@OLD} - -/obj/structure/flora/skeleton : /obj/structure/skele_stand{@OLD} - -/obj/mecha/working/ripley/firefighter : /mob/living/exosuit/premade/firefighter -/obj/mecha/working/ripley/mining : /mob/living/exosuit/premade/powerloader - -/obj/structure/AIcore/@SUBTYPES : /obj/structure/aicore/@SUBTYPES -/obj/item/tank/vox : /obj/item/tank/nitrogen{@OLD} - -/obj/machinery/shuttle_sensor : /obj/machinery/airlock_sensor/shuttle{@OLD} - -/obj/machinery/computer/rdconsole/@SUBTYPES : /obj/machinery/computer/design_console{@OLD} -/obj/machinery/computer/rdservercontrol : /obj/machinery/computer/design_console{@OLD; name = @SKIP; badmin = @SKIP} -/obj/item/stock_parts/circuitboard/rdconsole : /obj/item/stock_parts/circuitboard/design_console{@OLD} -/obj/item/stock_parts/circuitboard/rdserver : /obj/item/stock_parts/circuitboard/design_database{@OLD} - -/obj/machinery/account_database : /obj/machinery/computer/account_database{@OLD} -/obj/machinery/shield_gen/@SUBTYPES : /obj/machinery/shield_generator - -/obj/structure/morgue/crematorium : /obj/structure/crematorium{@OLD} - -/obj/item/robot_parts/frame : /obj/item/robot_parts/robot_suit{@OLD} - -/obj/structure/closet/secure_closet/psych : /obj/structure/closet/secure_closet/psychiatry{@OLD} -/obj/structure/closet/firecloset/full : /obj/structure/closet/firecloset{@OLD} -/obj/structure/closet/firecloset/full/double : /obj/structure/closet/firecloset/chief{@OLD} -/obj/structure/closet/l3closet/scientist/double : /obj/structure/closet/l3closet/scientist/multi{@OLD} -/obj/structure/closet/secure_closet/hos2 : /obj/structure/closet/secure_closet/hos{@OLD} -/obj/structure/closet/secure_closet/medical_wall/synth_anesthetics : /obj/structure/closet/secure_closet/medical_wall/anesthetics/robotics{@OLD} -/obj/structure/closet/secure_closet/medical2 : /obj/structure/closet/secure_closet/anesthetics{@OLD} -/obj/structure/closet/walllocker/emerglocker/@SUBTYPES : /obj/structure/emergency_dispenser/@SUBTYPES{@OLD} - -/obj/item/firstaid_arm_assembly : /obj/item/firstaid/empty, /obj/item/robot_parts/l_arm -/obj/item/bucket_sensor : /obj/item/chems/glass/bucket, /obj/item/assembly/prox_sensor -/obj/item/broken_device : @DELETE - -/obj/item/spacecash/@SUBTYPES : /obj/item/cash/@SUBTYPES{@OLD} - -/obj/item/kit/paint/ripley : /obj/item/kit/paint/camouflage -/obj/item/kit/paint/ripley/death : /obj/item/kit/paint/camouflage/forest -/obj/item/kit/paint/ripley/flames_blue : /obj/item/kit/paint/flames_blue -/obj/item/kit/paint/ripley/flames_red : /obj/item/kit/paint/flames_red - -/obj/machinery/photocopier/faxmachine : /obj/machinery/faxmachine{@OLD; init_network_tag = @OLD:department; department = @SKIP} -/obj/item/tvcamera : /obj/item/camera/tvcamera{@OLD} -/obj/machinery/station_map : /obj/machinery/holomap{@OLD} - -/obj/item/ducttape : /obj/item/duct_tape{@OLD} -/obj/item/toy/katana : /obj/item/sword/katana/toy{@OLD} -/obj/item/toy/cultsword : /obj/item/sword/cult_toy{@OLD} -/obj/item/toy/sword : /obj/item/energy_blade/sword/toy{@OLD} -/obj/item/lipstick/jade : /obj/item/cosmetics/lipstick/green{@OLD} -/obj/item/rig_module/device/plasmacutter : /obj/item/rig_module/mounted/plasmacutter{@OLD} -/obj/item/storage/box/syndie_kit/ewar_voice : /obj/item/backpack/satchel/syndie_kit/ewar_voice -/obj/item/tray : /obj/item/plate/tray{@OLD} -/obj/item/weldingtool/weldpack : /obj/item/chems/weldpack{@OLD} -/obj/machinery/crystal : /obj/structure/crystal{@OLD} -/obj/item/organ/internal/stack : /obj/item/organ/internal/voxstack{@OLD} -/obj/item/radio_jammer : /obj/item/suit_sensor_jammer - -# AMMO BOXES -/obj/item/storage/box/beanbags/@SUBTYPES : /obj/item/box/ammo/beanbags/@SUBTYPES{@OLD} -/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} -/obj/item/storage/box/blanks/@SUBTYPES : /obj/item/box/ammo/blanks/@SUBTYPES{@OLD} -/obj/item/storage/box/empshells/@SUBTYPES : /obj/item/box/ammo/empshells/@SUBTYPES{@OLD} -/obj/item/storage/box/flashshells/@SUBTYPES : /obj/item/box/ammo/flashshells/@SUBTYPES{@OLD} -/obj/item/storage/box/shotgunammo/@SUBTYPES : /obj/item/box/ammo/shotgunammo/@SUBTYPES{@OLD} -/obj/item/storage/box/shotgunshells/@SUBTYPES : /obj/item/box/ammo/shotgunshells/@SUBTYPES{@OLD} -/obj/item/storage/box/sniperammo/@SUBTYPES : /obj/item/box/ammo/sniperammo/@SUBTYPES{@OLD} -/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} - -/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} -/obj/item/camera_assembly : /obj/item/frame/camera/kit -/obj/structure/ladder/up : /obj/structure/ladder -/obj/random/mob/mouse : /obj/random/mouse - -/obj/structure/closet/grave/dirthole : /obj/structure/pit{@OLD} -/obj/structure/closet/grave/@SUBTYPES : /obj/structure/pit/closed/grave{@OLD} - -/obj/item/banner/nt : /obj/item/banner/nanotrasen{@OLD} - -/obj/item/box/syndie_kit/clerical : /obj/item/backpack/satchel/syndie_kit/clerical{@OLD} - -/obj/item/clothing/under/costume/rainbow : /obj/item/clothing/jumpsuit/rainbow{@OLD} -/obj/item/clothing/under/dress/dress_orange : /obj/item/clothing/dress/orange{@OLD} -/obj/item/clothing/head/collectable/xenos : /obj/item/clothing/head/xenos{@OLD} -/obj/item/clothing/head/collectable/bearpelt : /obj/item/clothing/head/bearpelt{@OLD} -/obj/item/clothing/head/collectable/philosopher_wig : /obj/item/clothing/head/philosopher_wig{@OLD} -/obj/item/clothing/head/collectable/plaguedoctorhat : /obj/item/clothing/head/plaguedoctorhat{@OLD} -/obj/item/clothing/head/helmet/space/void/security/riot : /obj/item/clothing/head/helmet/space/void/security/alt{@OLD} -/obj/item/clothing/suit/space/void/security/riot : /obj/item/clothing/suit/space/void/security/alt{@OLD} -/obj/item/clothing/head/helmet/space/vox/pressure : /obj/item/clothing/head/helmet/space/vox{@OLD} -/obj/item/clothing/under/vox/vox_utility : /obj/item/clothing/pants/vox{@OLD} -/obj/item/clothing/suit/space/pirate : /obj/item/clothing/suit/pirate{@OLD} -/obj/item/clothing/suit/costume : /obj/item/clothing/suit/pirate{@OLD} -/obj/item/clothing/suit/costume/hgpirate : /obj/item/clothing/suit/hgpirate{@OLD} -/obj/item/clothing/suit/storage/vest/heavy/merc : /obj/item/clothing/suit/armor/pcarrier/merc{@OLD} -/obj/item/clothing/suit/storage/vest/@SUBTYPES : /obj/item/clothing/suit/armor/vest/@SUBTYPES{@OLD} - -############ -# VAREDITS # -############ -# Atmos varedits -/obj/machinery/computer/air_control/@SUBTYPES{sensors = @SET} : /obj/machinery/computer/air_control/@SUBTYPES{@OLD; sensors = @SKIP} -# move these to the external air subtype so frequencies work -/obj/machinery/atmospherics/unary/vent_pump/high_volume{frequency = 1379} : /obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{@OLD; frequency = @SKIP} -/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrub_id = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} -/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrubbing_gas = list("phoron")} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrubbing_gas = list(/decl/material/solid/phoron)} -/obj/machinery/computer/area_atmos/tag/@SUBTYPES{scrub_id = @SET} : /obj/machinery/computer/area_atmos/tag/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} -# for the rest of this, frequency was moved to component presets and i'm too lazy to set that up -/obj/machinery/door/airlock/@SUBTYPES{frequency = @SET} : /obj/machinery/door/airlock/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/portable_atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/airlock_sensor/@SUBTYPES{frequency = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/air_sensor/@SUBTYPES : /obj/machinery/air_sensor/@SUBTYPES{@OLD; frequency = @SKIP; output = @SKIP} -/obj/machinery/embedded_controller/radio/@SUBTYPES{frequency = @SET} : /obj/machinery/embedded_controller/radio/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/button/@SUBTYPES{frequency = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/meter/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; frequency = @SKIP} - -# id -> id_tag -/obj/machinery/door_timer/@SUBTYPES{id = @SET} : /obj/machinery/door_timer/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/conveyor/@SUBTYPES{id = @SET} : /obj/machinery/conveyor/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/conveyor_switch/@SUBTYPES{id = @SET} : /obj/machinery/conveyor_switch/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/door/@SUBTYPES{id = @SET} : /obj/machinery/door/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/button/@SUBTYPES{id = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/atmospherics/@SUBTYPES{id = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/holosign/bar{id = @SET} : /obj/machinery/holosign/bar{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/meter/@SUBTYPES{id = @SET} : /obj/machinery/meter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/flasher/@SUBTYPES{id = @SET} : /obj/machinery/flasher/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/sparker/@SUBTYPES{id = @SET} : /obj/machinery/sparker/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/mass_driver/@SUBTYPES{id = @SET} : /obj/machinery/mass_driver/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/emitter/@SUBTYPES{id = @SET} : /obj/machinery/emitter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} - -# Camera preset_channels -> network -/obj/machinery/camera/network/@SUBTYPES{network = @SET} : /obj/machinery/camera/network/@SUBTYPES{@OLD; network = @SKIP; preset_channels = @OLD:network} - -# Remove button/sensor master_tag -/obj/machinery/airlock_sensor/@SUBTYPES{master_tag = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; master_tag = @SKIP} -/obj/machinery/button/access/@SUBTYPES{master_tag = @SET} : /obj/machinery/button/access/@SUBTYPES{@OLD; master_tag = @SKIP} - -# Skip power sensor name_tag -/obj/machinery/power/sensor{name_tag = @SET} : /obj/machinery/power/sensor{@OLD; name_tag = @SKIP} - -# join_group -> unique_merge_identifier -/turf/wall/shuttle/@SUBTYPES{join_group = @SET} : /turf/wall/shuttle/@SUBTYPES{@OLD; unique_merge_identifier = @OLD:join_group} - -# Remove set icon states/density on windows, doors, shutters -/obj/machinery/door/blast/regular{icon_state = @SET} : /obj/machinery/door/blast/regular{@OLD; icon_state = @SKIP} -/obj/structure/window/borosilicate_reinforced/@SUBTYPES{icon_state = @SET} : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD; icon_state = @SKIP} -/obj/machinery/door/blast/regular{p_open = 1} : /obj/machinery/door/blast/regular/open{@OLD; p_open = @SKIP} -/obj/machinery/door/blast/regular{p_open = 0} : /obj/machinery/door/blast/regular{@OLD; p_open = @SKIP} -/obj/machinery/door/blast/regular{density = 0} : /obj/machinery/door/blast/regular/open{@OLD; density = @SKIP; opacity = @SKIP} -/obj/structure/window/@SUBTYPES{icon_state = "fwindow"} : /obj/structure/window/@SUBTYPES{@OLD; icon_state = @SKIP} -/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} -/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} - -# Prices -> markup -/obj/machinery/vending/@SUBTYPES{prices = list()} : /obj/machinery/vending/@SUBTYPES{@OLD; markup = 0; prices=@SKIP} -/obj/machinery/vending/@SUBTYPES{prices = @SET} : /obj/machinery/vending/@SUBTYPES{@OLD; prices = @SKIP} - -# Radio varedits -/obj/item/radio/@SUBTYPES{subspace_transmission = @SET} : @OLD {@OLD; subspace_transmission = @SKIP} -/obj/item/radio/@SUBTYPES{syndie = @SET} : @OLD {@OLD; decrypt_all_messages = 1; syndie = @SKIP} -/obj/item/radio/@SUBTYPES{adhoc_fallback = @SET} : @OLD {@OLD; can_use_analog = @SKIP} - -# Empty first-aid kits -/obj/item/storage/firstaid/regular{empty = 1} : /obj/item/firstaid/empty{@OLD; empty = @SKIP; name = @SKIP} - -/obj/machinery/floor_light/prebuilt{on = 1} : /obj/machinery/floor_light/prebuilt{@OLD; use_power = 2; on = @SKIP} - +/obj/item/reagent_containers/@SUBTYPES : /obj/item/chems/@SUBTYPES{@OLD} +/obj/item/material/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/weapon/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/melee/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/packageWrap/@SUBTYPES : /obj/item/stack/package_wrap/@SUBTYPES{@OLD} +/obj/structure/table/rack/@SUBTYPES : /obj/structure/rack/@SUBTYPES{@OLD} +/obj/structure/table/woodentable/holotable : /obj/structure/table/holo_woodentable{@OLD} +/obj/item/chems/food/drinks/@SUBTYPES : /obj/item/chems/drinks/@SUBTYPES{@OLD} +/obj/item/chems/food/snacks/@SUBTYPES : /obj/item/food/@SUBTYPES{@OLD} +/obj/item/chems/food/condiment/@SUBTYPES : /obj/item/chems/condiment/@SUBTYPES{@OLD} +/obj/item/chems/condiment/peppermill/@SUBTYPES : /obj/item/chems/condiment/small/peppermill/@SUBTYPES{@OLD} +/obj/item/chems/condiment/saltshaker/@SUBTYPES : /obj/item/chems/condiment/small/saltshaker/@SUBTYPES{@OLD} +/obj/item/analyzer : /obj/item/scanner/gas{@OLD} +/obj/item/analyzer/plant_analyzer/@SUBTYPES : /obj/item/scanner/plant/@SUBTYPES{@OLD} +/obj/item/tape_roll/@SUBTYPES : /obj/item/stack/tape_roll/@SUBTYPES{@OLD} +/obj/structure/table/standard/@SUBTYPES : /obj/structure/table/@SUBTYPES{@OLD} +/obj/machinery/bluespacerelay/@SUBTYPES : /obj/machinery/commsrelay/@SUBTYPES{@OLD} +/obj/machinery/computer/helm/@SUBTYPES : /obj/machinery/computer/ship/helm/@SUBTYPES{@OLD} +/obj/machinery/computer/engines/@SUBTYPES : /obj/machinery/computer/ship/engines/@SUBTYPES{@OLD} +/obj/machinery/computer/navigation/@SUBTYPES : /obj/machinery/computer/ship/navigation/@SUBTYPES{@OLD} +/obj/machinery/requests_console/@SUBTYPES : /obj/machinery/network/requests_console/@SUBTYPES{@OLD; departmentType = @SKIP} +/obj/item/healthanalyzer/@SUBTYPES : /obj/item/scanner/health/@SUBTYPES{@OLD} +/obj/machinery/iv_drip/@SUBTYPES : /obj/structure/iv_drip/@SUBTYPES{@OLD} +/obj/structure/device/piano/@SUBTYPES : /obj/structure/synthesized_instrument/synthesizer/piano/@SUBTYPES{@OLD} +/obj/item/folder/white/@SUBTYPES : /obj/item/folder/@SUBTYPES{@OLD} +/obj/machinery/dnaforensics/@SUBTYPES : /obj/machinery/forensic/dnascanner/@SUBTYPES{@OLD} +/obj/machinery/microscope/@SUBTYPES : /obj/machinery/forensic/microscope/@SUBTYPES{@OLD} +/obj/item/stack/material/steel/@SUBTYPES : /obj/item/stack/material/sheet/mapped/steel/@SUBTYPES{@OLD} +/obj/item/stack/material/plasteel/@SUBTYPES : /obj/item/stack/material/sheet/reinforced/mapped/plasteel/@SUBTYPES{@OLD} +/obj/item/stack/material/glass/reinforced/@SUBTYPES : /obj/item/stack/material/pane/mapped/rglass/@SUBTYPES{@OLD} +/obj/item/stack/material/glass/@SUBTYPES : /obj/item/stack/material/pane/mapped/glass/@SUBTYPES{@OLD} +/obj/item/stack/material/wood/@SUBTYPES : /obj/item/stack/material/plank/mapped/wood/@SUBTYPES{@OLD} +/obj/item/stack/material/deuterium/@SUBTYPES : /obj/item/stack/material/aerogel/mapped/deuterium/@SUBTYPES{@OLD} +/obj/item/stack/material/uranium/@SUBTYPES : /obj/item/stack/material/puck/mapped/uranium/@SUBTYPES{@OLD} +/obj/machinery/autolathe/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD} +/obj/machinery/message_server/@SUBTYPES : /obj/machinery/network/message_server/@SUBTYPES{@OLD} +/obj/item/floor_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} +/obj/machinery/pipedispenser/@SUBTYPES : /obj/machinery/fabricator/pipe/@SUBTYPES{@OLD} +/obj/machinery/power/emitter/@SUBTYPES : /obj/machinery/emitter/@SUBTYPES{@OLD} +/obj/machinery/power/fusion_core/@SUBTYPES : /obj/machinery/fusion_core/@SUBTYPES{@OLD} +/obj/item/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} +/obj/machinery/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} +/obj/machinery/computer/modular/preset/command/@SUBTYPES : /obj/machinery/computer/modular/preset/cardslot/command/@SUBTYPES{@OLD} +/obj/machinery/computer/fusion_core_control/@SUBTYPES : /obj/machinery/computer/fusion/core_control/@SUBTYPES{@OLD} +/obj/machinery/computer/fusion_fuel_control/@SUBTYPES : /obj/machinery/computer/fusion/fuel_control/@SUBTYPES{@OLD} +/obj/machinery/computer/gyrotron_control/@SUBTYPES : /obj/machinery/computer/fusion/gyrotron/@SUBTYPES{@OLD} +/obj/machinery/computer/power_monitor/@SUBTYPES : /obj/machinery/computer/modular/preset/engineering/power/@SUBTYPES{@OLD} +/obj/item/module/power_control/@SUBTYPES : /obj/item/stock_parts/circuitboard/apc/@SUBTYPES{@OLD} +/obj/machinery/power/port_gen/@SUBTYPES : /obj/machinery/port_gen/@SUBTYPES{@OLD} +/obj/item/pipe_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} +/obj/item/airlock_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/airlock_electronics/@SUBTYPES{@OLD} +/obj/item/airalarm_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/air_alarm/@SUBTYPES{@OLD} +/obj/item/autopsy_scanner/@SUBTYPES : /obj/item/scanner/autopsy/@SUBTYPES{@OLD} +/obj/machinery/fusion_fuel_compressor/@SUBTYPES : /obj/machinery/fuel_compressor/@SUBTYPES{@OLD} +/obj/item/extinguisher/micro/@SUBTYPES : /obj/item/chems/spray/extinguisher/mini/@SUBTYPES{@OLD} +/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} +/obj/item/stack/material/plastic/@SUBTYPES : /obj/item/stack/material/panel/mapped/plastic/@SUBTYPES{@OLD} +/obj/machinery/computer/general_air_control/@SUBTYPES : /obj/machinery/computer/air_control/@SUBTYPES{@OLD} +/obj/machinery/computer/air_control/large_tank_control : /obj/machinery/computer/air_control{@OLD} +/turf/simulated/floor/tiled/steel/@SUBTYPES : /turf/simulated/floor/tiled/@SUBTYPES{@OLD} +/obj/machinery/power/engine/@SUBTYPES : /obj/machinery/ion_thruster/@SUBTYPES{@OLD} +/obj/machinery/computer/aifixer/@SUBTYPES : /obj/machinery/computer/modular/preset/aislot/sysadmin/@SUBTYPES{@OLD} +/obj/machinery/computer/aiupload/@SUBTYPES : /obj/machinery/computer/upload/ai/@SUBTYPES{@OLD} +/obj/machinery/computer/borgupload/@SUBTYPES : /obj/machinery/computer/upload/robot/@SUBTYPES{@OLD} +/obj/machinery/computer/supplycomp/control/@SUBTYPES : /obj/machinery/computer/modular/preset/dock/@SUBTYPES{@OLD} +/obj/machinery/computer/supplycomp/@SUBTYPES : /obj/machinery/computer/modular/preset/supply_public/@SUBTYPES{@OLD} +/obj/item/closet_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} +/turf/simulated/shuttle/plating/@SUBTYPES : /turf/floor/shuttle +/turf/simulated/shuttle/floor/@SUBTYPES : /turf/floor/shuttle/@SUBTYPES + +# REMOVE BAD VAREDITS +/@SUBTYPES {layer = @SET} : @OLD {@OLD; layer = @SKIP} +/@SUBTYPES {plane = @SET} : @OLD {@OLD; plane = @SKIP} +/obj/structure/cable/@SUBTYPES : @OLD {@OLD; d1 = @SKIP;d2 = @SKIP} +# unacidable removed +/@SUBTYPES {unacidable = @SET} : @OLD{@OLD; unacidable = @SKIP} +# Simple pipes shall not face north or west +/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} +/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} +/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} +/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} +# Do not change air alarm frequency on base type +/obj/machinery/alarm {frequency = @SET} : @OLD {@OLD; frequency = @SKIP} +# we use a completely different access format +/@SUBTYPES {req_access = @SET}: @OLD {@OLD; req_access = @SKIP;} +/@SUBTYPES {req_one_access = @SET}: @OLD {@OLD; req_one_access = @SKIP;} +/obj/machinery/door/blast/shutters{icon_state = "shutter0"} : /obj/machinery/door/blast/shutters{@OLD; icon_state = @SKIP} +/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1; density = 0, opacity = 0} : /obj/machinery/door/airlock/external/glass/bolted_open{@OLD; icon_state = @SKIP; locked = @SKIP, density = @SKIP; opacity = @SKIP} +/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1} : /obj/machinery/door/airlock/external/glass/bolted{@OLD; icon_state = @SKIP; locked = @SKIP} + +# BUTTONS +/obj/machinery/access_button/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} +/obj/machinery/access_button/airlock_interior : /obj/machinery/button/access/interior{@OLD} +/obj/machinery/airlock_sensor/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} +/obj/machinery/airlock_sensor/airlock_interior : /obj/machinery/button/access/interior{@OLD} +/obj/machinery/access_button : /obj/machinery/button/access{@OLD} +/obj/machinery/button/remote/airlock{specialfunctions = 4} : /obj/machinery/button/alternate/door/bolts{@OLD; specialfunctions = @SKIP; desiredstate = @SKIP} +/obj/machinery/button/remote/airlock : /obj/machinery/button/alternate/door{@OLD} +/obj/machinery/button/remote/driver : /obj/machinery/button/mass_driver{@OLD} +/obj/machinery/button/remote/blast_door : /obj/machinery/button/blast_door{@OLD} +/obj/machinery/button/remote : /obj/machinery/button/alternate{@OLD} + +# Medical +/obj/item/pill_bottle/tramadol : /obj/item/pill_bottle/strong_painkillers{@OLD} +/obj/item/chems/pill/tramadol : /obj/item/chems/pill/strong_painkillers{@OLD} +/obj/item/chems/glass/bottle/stoxin : /obj/item/chems/glass/bottle/sedatives{@OLD} +/obj/item/chems/glass/bottle/inaprovaline : /obj/item/chems/glass/bottle/stabilizer{@OLD} +/obj/item/chems/syringe/inaprovaline : /obj/item/chems/syringe/stabilizer{@OLD} +/obj/item/chems/blood/empty : /obj/item/chems/ivbag{@OLD} +/obj/item/chems/blood/@SUBTYPES : /obj/item/chems/ivbag/blood/@SUBTYPES{@OLD} +/obj/item/defib_kit/@SUBTYPES : /obj/item/defibrillator/@SUBTYPES{@OLD} +/obj/item/chems/spray/sterilizine : /obj/item/chems/spray/antiseptic{@OLD} + +# Drinking glasses and cups +/obj/item/chems/drinks/cup : /obj/item/chems/drinks/glass2/coffeecup{@OLD} +/obj/item/chems/drinks/drinkingglass : /obj/item/chems/drinks/glass2/square{@OLD} + +# Airtight flaps +/obj/structure/plasticflaps/mining : /obj/structure/flaps/airtight{@OLD} + +# Bushes +/obj/structure/flora/ausbushes/@SUBTYPES : /obj/structure/flora/bush/@SUBTYPES + +# Cassette tapes -> magnetic tapes +/obj/item/cassette_tape/@SUBTYPES : /obj/item/magnetic_tape/@SUBTYPES{@OLD} +/obj/item/tape_recorder : /obj/item/taperecorder{@OLD} + +# FIFTYSPAWNER -> MAPPED SHEETS +/obj/fiftyspawner/wood : /obj/item/stack/material/plank/mapped/wood/fifty +/obj/fiftyspawner/steel : /obj/item/stack/material/sheet/mapped/steel/fifty +/obj/fiftyspawner/glass : /obj/item/stack/material/pane/mapped/glass/fifty +/obj/fiftyspawner/deuterium : /obj/item/stack/material/aerogel/mapped/deuterium/fifty +/obj/fiftyspawner/tritium : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/osmium : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/phoron : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/rods : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/grass : /obj/item/stack/tile/grass{amount = 50} +/obj/fiftyspawner/plasteel : /obj/item/stack/material/aerogel/mapped/tritium/fifty + +# STOCK PARTS AND SMES COILS +/obj/item/stock_parts/subspace/sub_filter : /obj/item/stock_parts/subspace/filter +/obj/item/smes_coil/@SUBTYPES : /obj/item/stock_parts/smes_coil/@SUBTYPES{@OLD} +/obj/item/circuitboard/@SUBTYPES : /obj/item/stock_parts/circuitboard/@SUBTYPES{@OLD} +/obj/item/airlock_electronics : /obj/item/stock_parts/circuitboard/airlock_electronics{@OLD} + +# CLOTHING +/obj/item/clothing/head/cone : /obj/item/caution/cone{@OLD} +/obj/item/clothing/belt/security/tactical : /obj/item/belt/holster/security/tactical{@OLD} + +# Item remains +/obj/effect/decal/remains/@SUBTYPES : /obj/item/remains/@SUBTYPES{@OLD} + +# Circuit prefabs +/obj/item/hand_tele : /obj/prefab/hand_teleporter{@OLD} + +# Filing cabinets +/obj/structure/filingcabinet : /obj/structure/filing_cabinet{@OLD} +/obj/structure/filingcabinet/chestdrawer : /obj/structure/filing_cabinet/chestdrawer{@OLD} +/obj/structure/filingcabinet/filingcabinet : /obj/structure/filing_cabinet/tall{@OLD} +/obj/structure/filingcabinet/medical : /obj/structure/filing_cabinet/records/medical{@OLD} +/obj/structure/filingcabinet/security : /obj/structure/filing_cabinet/records{@OLD} + +# Machine frames +/obj/structure/frame: /obj/machinery/constructable_frame/machine_frame{@OLD} +/obj/machinery/constructable_frame/machine_frame{anchored = 1} : /obj/machinery/constructable_frame/machine_frame/deconstruct{anchored = @SKIP} +/obj/structure/frame/computer : /obj/machinery/constructable_frame/computerframe{@OLD} +/obj/machinery/constructable_frame/computerframe{anchored = 1} : /obj/machinery/constructable_frame/computerframe/deconstruct{anchored = @SKIP} + +# Hygiene/bathroom objects +/obj/structure/toilet : /obj/structure/hygiene/toilet{@OLD} +/obj/structure/sink/@SUBTYPES : /obj/structure/hygiene/sink/@SUBTYPES{@OLD} +/obj/machinery/shower : /obj/structure/hygiene/shower{@OLD} + +# Turbolift spawners +/obj/turbolift_map_holder/@SUBTYPES : /obj/abstract/turbolift_spawner/@SUBTYPES{@OLD} + +# Borosilicate windows +/obj/structure/window/phoronreinforced/@SUBTYPES : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD} + +# Tools; see 3763_pickaxe_hammer and 3959_hatchets for other migrations +/obj/item/knife/machete : /obj/item/tool/machete{@OLD} +/obj/item/knife/machete/hatchet : /obj/item/tool/axe/hatchet{@OLD} +/obj/item/tool/pickaxe/hand : /obj/item/tool/pickaxe/xeno/hand{@OLD} + +# RANDOM SPAWNERS +/obj/random/cigarettes : /obj/random/smokes{@OLD} +/obj/item/lipstick/random : /obj/random/lipstick +/obj/random/slimecore : /obj/item/slime_extract/random{@OLD} + +# MINING EQUIPMENT +/obj/machinery/mineral/processing_unit : /obj/machinery/material_processing/smeltery +/obj/machinery/mineral/stacking_machine : /obj/machinery/material_processing/stacker +/obj/machinery/mineral/unloading_machine : /obj/machinery/material_processing/unloader +/obj/machinery/mineral/output : @DELETE +/obj/machinery/mineral/input : @DELETE +/obj/machinery/mineral/mint : @DELETE +/obj/machinery/mineral/processing_unit_console : @DELETE + +# NEW PDAS (NO CARTRIDGES) +/obj/item/pda/@SUBTYPES : /obj/item/modular_computer/pda/@SUBTYPES{@OLD} +/obj/item/modular_computer/pda/captain : /obj/item/modular_computer/pda/heads/captain +/obj/item/storage/box/seccarts : @DELETE +/obj/item/cartridge/@SUBTYPES : @DELETE + +# MODULAR COMPUTER PRESETS +/obj/machinery/computer/card : /obj/machinery/computer/modular/preset/cardslot/personnel{@OLD} +/obj/machinery/computer/secure_data : /obj/machinery/computer/modular/preset/civilian{@OLD} +/obj/machinery/computer/skills : /obj/machinery/computer/modular/preset/civilian{@OLD} +/obj/machinery/computer/security/telescreen : /obj/machinery/computer/modular/telescreen/preset/security{@OLD} +/obj/machinery/computer/security/telescreen/entertainment : /obj/machinery/computer/modular/telescreen/preset/entertainment{@OLD} +/obj/machinery/computer/security/mining : /obj/machinery/computer/modular/preset/security{@OLD} +/obj/machinery/computer/security/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES +/obj/machinery/computer/crew : /obj/machinery/computer/modular/preset/medical{@OLD} +/obj/machinery/computer/rcon : /obj/machinery/computer/modular/preset/engineering/rcon{@OLD} +/obj/machinery/computer/communications : /obj/machinery/computer/modular/preset/cardslot/command{@OLD} +/obj/machinery/computer/med_data : /obj/machinery/computer/modular/preset/civilian{@OLD} +/obj/machinery/computer/med_data/laptop : /obj/item/modular_computer/laptop/preset/records{@OLD} + +# ATMOS REPATHS +/obj/machinery/atmospherics/pipe/tank/@SUBTYPES : /obj/machinery/atmospherics/unary/tank/@SUBTYPES +/obj/machinery/portable_atmospherics/canister/nitrous_oxide : /obj/machinery/portable_atmospherics/canister/sleeping_agent{@OLD} + +# MISC. ITEMS +/obj/item/moneybag/vault : /obj/item/bag/cash/filled{@OLD} +/obj/item/moneybag : /obj/item/bag/cash{@OLD} +/obj/item/chems/bucket : /obj/item/chems/glass/bucket{@OLD} +/obj/item/card/id/gold/captain/spare : /obj/item/card/id/captains_spare{@OLD} +/obj/item/chainofcommand : /obj/item/whip/chainofcommand{@OLD} +/obj/item/chems/drinks/bottle/specialwhiskey : /obj/item/chems/drinks/bottle/agedwhiskey{@OLD} + +# Tape is weird +/obj/item/taperoll/@SUBTYPES : /obj/item/stack/tape_roll/barricade_tape/@SUBTYPES{@OLD} +/obj/item/tape/@SUBTYPES : /obj/structure/tape_barricade/@SUBTYPES{@OLD} + +# Radio +/obj/item/radio/headset/headset_sec/alt : /obj/item/radio/headset/headset_sec/bowman{@OLD} +/obj/item/radio/headset/syndicate : /obj/item/radio/headset/hacked{@OLD} +/obj/item/radio/headset/syndicate/alt : /obj/item/radio/headset/hacked{@OLD} +/obj/item/phone : /obj/item/radio/phone{@OLD} + +# Singulo +/obj/machinery/the_singularitygen : /obj/machinery/singularity_generator{@OLD} + +# FABRICATORS +/obj/machinery/autolathe{hacked = 1} : /obj/machinery/fabricator/hacked{@OLD; hacked = @SKIP} +/obj/machinery/autolathe : /obj/machinery/fabricator{@OLD} +/obj/machinery/pipedispenser : /obj/machinery/fabricator/pipe{@OLD; anchored = 1} +/obj/machinery/pipedispenser/orderable : /obj/machinery/fabricator/pipe{@OLD} +/obj/machinery/pipedispenser/disposal : /obj/machinery/fabricator/pipe/disposal{@OLD; anchored = 1} +/obj/machinery/pipedispenser/disposal/orderable : /obj/machinery/fabricator/pipe/disposal{@OLD} +/obj/machinery/r_n_d/circuit_imprinter : /obj/machinery/fabricator/imprinter{@OLD} +/obj/machinery/pros_fabricator : /obj/machinery/fabricator/robotics{@OLD} +/obj/machinery/mecha_part_fabricator : /obj/machinery/fabricator/industrial{@OLD} +/obj/machinery/r_n_d/protolathe : /obj/machinery/fabricator/protolathe{@OLD} +/obj/machinery/bookbinder : /obj/machinery/fabricator/book{@OLD} +/obj/machinery/organ_printer/flesh : /obj/machinery/fabricator/bioprinter{@OLD} +/obj/machinery/vending/food : /obj/machinery/fabricator/replicator + +/obj/machinery/fabricator/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD; res_max_amount = @SKIP; speed = @SKIP} + +# MMIs and posibrains +/obj/item/mmi/digital/@SUBTYPES : /obj/item/organ/internal/brain/robotic{@OLD} +/obj/item/mmi/@SUBTYPES : /obj/item/organ/internal/brain_interface/@SUBTYPES{@OLD} +/obj/item/organ/internal/posibrain : /obj/item/organ/internal/brain/robotic/positronic{@OLD} + +# Energy blades +/obj/item/energy/@SUBTYPES : /obj/item/energy_blade/@SUBTYPES{@OLD} + +# Medals and other accessories; see 3902_ties.txt for further migrations +/obj/item/clothing/accessory/medal/bronze_heart : /obj/item/clothing/accessory/medal/nanotrasen/bronze{@OLD} +/obj/item/clothing/accessory/medal/nobel_science : /obj/item/clothing/medal/nanotrasen/silver{@OLD} +/obj/item/clothing/accessory/scarf/white : /obj/item/clothing/accessory/scarf{@OLD} +/obj/item/clothing/accessory/storage/webbing : /obj/item/clothing/accessory/webbing/vest + +# MATERIALS +/obj/item/stack/material/plastic : /obj/item/stack/material/panel/mapped/plastic{@OLD} +/obj/item/stack/material/sandstone : /obj/item/stack/material/brick/mapped/sandstone{@OLD} +/obj/item/stack/material/mhydrogen : /obj/item/stack/material/segment/mapped/mhydrogen{@OLD} +/obj/item/stack/material/diamond : /obj/item/stack/material/gemstone/mapped/diamond{@OLD} +/obj/item/stack/material/wood : /obj/item/stack/material/plank/mapped/wood{@OLD} +/obj/item/stack/material/steel : /obj/item/stack/material/sheet/mapped/steel{@OLD} +/obj/item/stack/material/glass : /obj/item/stack/material/pane/mapped/glass{@OLD} +/obj/item/stack/material/phoron : /obj/item/stack/material/crystal/mapped/phoron{@OLD} +/obj/item/stack/material/algae : /obj/item/stack/material/puck/mapped/algae{@OLD} +/obj/item/stack/material/gold : /obj/item/stack/material/ingot/mapped/gold{@OLD} +/obj/item/stack/material/plasteel : /obj/item/stack/material/reinforced/mapped/plasteel{@OLD} +/obj/item/stack/material/glass/reinforced : /obj/item/stack/material/pane/mapped/rglass{@OLD} +/obj/item/stack/material/glass/phoronglass : /obj/item/stack/material/pane/mapped/borosilicate{@OLD} +/obj/item/stack/material/glass/phoronrglass : /obj/item/stack/material/pane/mapped/rborosilicate{@OLD} +/obj/item/stack/rods : /obj/item/stack/material/rods{@OLD} +/obj/item/packageWrap{amount = @UNSET} : /obj/item/stack/package_wrap/twenty_five{@OLD; amount = @SKIP} +/obj/item/packageWrap : /obj/item/stack/package_wrap{@OLD} +/obj/item/wrapping_paper{amount = @UNSET} : /obj/item/stack/package_wrap/gift/twenty_five{@OLD; amount = @SKIP} +/obj/item/wrapping_paper : /obj/item/stack/package_wrap{@OLD} + +/obj/structure/dispenser{phorontanks = 0} : /obj/structure/tank_rack/oxygen{@OLD; phorontanks = @SKIP} +/obj/structure/dispenser/oxygen{oxygentanks = 0} : /obj/structure/tank_rack/empty{@OLD; oxygentanks = @SKIP} +/obj/structure/dispenser/oxygen : /obj/structure/tank_rack/oxygen +/obj/structure/dispenser : /obj/structure/tank_rack + +# TELECOMMS REMOVAL +/obj/item/stock_parts/circuitboard/telecomms/@SUBTYPES : @DELETE +/obj/machinery/telecomms/@SUBTYPES : @DELETE +/obj/machinery/computer/telecomms/@SUBTYPES : @DELETE + +/obj/machinery/r_n_d/server/@SUBTYPES : /obj/machinery/design_database +/obj/machinery/r_n_d/destructive_analyzer : /obj/machinery/destructive_analyzer + +# SMES bullshit +/obj/machinery/power/smes/buildable/engine : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 1)} +/obj/machinery/power/smes/buildable/main : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 4)} +/obj/machinery/power/smes/buildable/point_of_interest : /obj/machinery/power/smes/buildable/preset/hidden + +/obj/item/surgical/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/FixOVein : /obj/item/sutures{@OLD} + +/obj/structure/flora/skeleton : /obj/structure/skele_stand{@OLD} + +/obj/mecha/working/ripley/firefighter : /mob/living/exosuit/premade/firefighter +/obj/mecha/working/ripley/mining : /mob/living/exosuit/premade/powerloader + +/obj/structure/AIcore/@SUBTYPES : /obj/structure/aicore/@SUBTYPES +/obj/item/tank/vox : /obj/item/tank/nitrogen{@OLD} + +/obj/machinery/shuttle_sensor : /obj/machinery/airlock_sensor/shuttle{@OLD} + +/obj/machinery/computer/rdconsole/@SUBTYPES : /obj/machinery/computer/design_console{@OLD} +/obj/machinery/computer/rdservercontrol : /obj/machinery/computer/design_console{@OLD; name = @SKIP; badmin = @SKIP} +/obj/item/stock_parts/circuitboard/rdconsole : /obj/item/stock_parts/circuitboard/design_console{@OLD} +/obj/item/stock_parts/circuitboard/rdserver : /obj/item/stock_parts/circuitboard/design_database{@OLD} + +/obj/machinery/account_database : /obj/machinery/computer/account_database{@OLD} +/obj/machinery/shield_gen/@SUBTYPES : /obj/machinery/shield_generator + +/obj/structure/morgue/crematorium : /obj/structure/crematorium{@OLD} + +/obj/item/robot_parts/frame : /obj/item/robot_parts/robot_suit{@OLD} + +/obj/structure/closet/secure_closet/psych : /obj/structure/closet/secure_closet/psychiatry{@OLD} +/obj/structure/closet/firecloset/full : /obj/structure/closet/firecloset{@OLD} +/obj/structure/closet/firecloset/full/double : /obj/structure/closet/firecloset/chief{@OLD} +/obj/structure/closet/l3closet/scientist/double : /obj/structure/closet/l3closet/scientist/multi{@OLD} +/obj/structure/closet/secure_closet/hos2 : /obj/structure/closet/secure_closet/hos{@OLD} +/obj/structure/closet/secure_closet/medical_wall/synth_anesthetics : /obj/structure/closet/secure_closet/medical_wall/anesthetics/robotics{@OLD} +/obj/structure/closet/secure_closet/medical2 : /obj/structure/closet/secure_closet/anesthetics{@OLD} +/obj/structure/closet/walllocker/emerglocker/@SUBTYPES : /obj/structure/emergency_dispenser/@SUBTYPES{@OLD} + +/obj/item/firstaid_arm_assembly : /obj/item/firstaid/empty, /obj/item/robot_parts/l_arm +/obj/item/bucket_sensor : /obj/item/chems/glass/bucket, /obj/item/assembly/prox_sensor +/obj/item/broken_device : @DELETE + +/obj/item/spacecash/@SUBTYPES : /obj/item/cash/@SUBTYPES{@OLD} + +/obj/item/kit/paint/ripley : /obj/item/kit/paint/camouflage +/obj/item/kit/paint/ripley/death : /obj/item/kit/paint/camouflage/forest +/obj/item/kit/paint/ripley/flames_blue : /obj/item/kit/paint/flames_blue +/obj/item/kit/paint/ripley/flames_red : /obj/item/kit/paint/flames_red + +/obj/machinery/photocopier/faxmachine : /obj/machinery/faxmachine{@OLD; init_network_tag = @OLD:department; department = @SKIP} +/obj/item/tvcamera : /obj/item/camera/tvcamera{@OLD} +/obj/machinery/station_map : /obj/machinery/holomap{@OLD} + +/obj/item/ducttape : /obj/item/duct_tape{@OLD} +/obj/item/toy/katana : /obj/item/sword/katana/toy{@OLD} +/obj/item/toy/cultsword : /obj/item/sword/cult_toy{@OLD} +/obj/item/toy/sword : /obj/item/energy_blade/sword/toy{@OLD} +/obj/item/lipstick/jade : /obj/item/cosmetics/lipstick/green{@OLD} +/obj/item/rig_module/device/plasmacutter : /obj/item/rig_module/mounted/plasmacutter{@OLD} +/obj/item/storage/box/syndie_kit/ewar_voice : /obj/item/backpack/satchel/syndie_kit/ewar_voice +/obj/item/tray : /obj/item/plate/tray{@OLD} +/obj/item/weldingtool/weldpack : /obj/item/chems/weldpack{@OLD} +/obj/machinery/crystal : /obj/structure/crystal{@OLD} +/obj/item/organ/internal/stack : /obj/item/organ/internal/voxstack{@OLD} +/obj/item/radio_jammer : /obj/item/suit_sensor_jammer + +# AMMO BOXES +/obj/item/storage/box/beanbags/@SUBTYPES : /obj/item/box/ammo/beanbags/@SUBTYPES{@OLD} +/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} +/obj/item/storage/box/blanks/@SUBTYPES : /obj/item/box/ammo/blanks/@SUBTYPES{@OLD} +/obj/item/storage/box/empshells/@SUBTYPES : /obj/item/box/ammo/empshells/@SUBTYPES{@OLD} +/obj/item/storage/box/flashshells/@SUBTYPES : /obj/item/box/ammo/flashshells/@SUBTYPES{@OLD} +/obj/item/storage/box/shotgunammo/@SUBTYPES : /obj/item/box/ammo/shotgunammo/@SUBTYPES{@OLD} +/obj/item/storage/box/shotgunshells/@SUBTYPES : /obj/item/box/ammo/shotgunshells/@SUBTYPES{@OLD} +/obj/item/storage/box/sniperammo/@SUBTYPES : /obj/item/box/ammo/sniperammo/@SUBTYPES{@OLD} +/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} + +/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} +/obj/item/camera_assembly : /obj/item/frame/camera/kit +/obj/structure/ladder/up : /obj/structure/ladder +/obj/random/mob/mouse : /obj/random/mouse + +/obj/structure/closet/grave/dirthole : /obj/structure/pit{@OLD} +/obj/structure/closet/grave/@SUBTYPES : /obj/structure/pit/closed/grave{@OLD} + +/obj/item/banner/nt : /obj/item/banner/nanotrasen{@OLD} + +/obj/item/box/syndie_kit/clerical : /obj/item/backpack/satchel/syndie_kit/clerical{@OLD} + +/obj/item/clothing/under/costume/rainbow : /obj/item/clothing/jumpsuit/rainbow{@OLD} +/obj/item/clothing/under/dress/dress_orange : /obj/item/clothing/dress/orange{@OLD} +/obj/item/clothing/head/collectable/xenos : /obj/item/clothing/head/xenos{@OLD} +/obj/item/clothing/head/collectable/bearpelt : /obj/item/clothing/head/bearpelt{@OLD} +/obj/item/clothing/head/collectable/philosopher_wig : /obj/item/clothing/head/philosopher_wig{@OLD} +/obj/item/clothing/head/collectable/plaguedoctorhat : /obj/item/clothing/head/plaguedoctorhat{@OLD} +/obj/item/clothing/head/helmet/space/void/security/riot : /obj/item/clothing/head/helmet/space/void/security/alt{@OLD} +/obj/item/clothing/suit/space/void/security/riot : /obj/item/clothing/suit/space/void/security/alt{@OLD} +/obj/item/clothing/head/helmet/space/vox/pressure : /obj/item/clothing/head/helmet/space/vox{@OLD} +/obj/item/clothing/under/vox/vox_utility : /obj/item/clothing/pants/vox{@OLD} +/obj/item/clothing/suit/space/pirate : /obj/item/clothing/suit/pirate{@OLD} +/obj/item/clothing/suit/costume : /obj/item/clothing/suit/pirate{@OLD} +/obj/item/clothing/suit/costume/hgpirate : /obj/item/clothing/suit/hgpirate{@OLD} +/obj/item/clothing/suit/storage/vest/heavy/merc : /obj/item/clothing/suit/armor/pcarrier/merc{@OLD} +/obj/item/clothing/suit/storage/vest/@SUBTYPES : /obj/item/clothing/suit/armor/vest/@SUBTYPES{@OLD} + +# Migrate/delete old mech parts +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster : @DELETE +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster : @DELETE +/obj/item/mecha_parts/mecha_equipment/repair_droid : @DELETE +/obj/item/mecha_parts/mecha_equipment/teleporter : @DELETE +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay : @DELETE +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer : @DELETE +/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill : /obj/item/mech_equipment/drill/diamond{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/extinguisher : /obj/item/mech_equipment/mounted_system/extinguisher{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp : /obj/item/mech_equipment/clamp{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/passenger : @DELETE +/obj/item/mecha_parts/mecha_equipment/tool/rcd : /obj/item/mech_equipment/mounted_system/rcd{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/sleeper : /obj/item/mech_equipment/sleeper{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun : @DELETE +/obj/item/mecha_parts/mecha_equipment/weapon/energy/ion : /obj/item/mech_equipment/mounted_system/taser/ion{@OLD} +/obj/item/mecha_parts/mecha_equipment/weapon/energy/taser : /obj/item/mech_equipment/mounted_system/taser{@OLD} +/obj/item/mecha_parts/mecha_tracking : @DELETE + +############ +# VAREDITS # +############ +# Atmos varedits +/obj/machinery/computer/air_control/@SUBTYPES{sensors = @SET} : /obj/machinery/computer/air_control/@SUBTYPES{@OLD; sensors = @SKIP} +# move these to the external air subtype so frequencies work +/obj/machinery/atmospherics/unary/vent_pump/high_volume{frequency = 1379} : /obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{@OLD; frequency = @SKIP} +/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrub_id = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} +/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrubbing_gas = list("phoron")} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrubbing_gas = list(/decl/material/solid/phoron)} +/obj/machinery/computer/area_atmos/tag/@SUBTYPES{scrub_id = @SET} : /obj/machinery/computer/area_atmos/tag/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} +# for the rest of this, frequency was moved to component presets and i'm too lazy to set that up +/obj/machinery/door/airlock/@SUBTYPES{frequency = @SET} : /obj/machinery/door/airlock/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/portable_atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/airlock_sensor/@SUBTYPES{frequency = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/air_sensor/@SUBTYPES : /obj/machinery/air_sensor/@SUBTYPES{@OLD; frequency = @SKIP; output = @SKIP} +/obj/machinery/embedded_controller/radio/@SUBTYPES{frequency = @SET} : /obj/machinery/embedded_controller/radio/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/button/@SUBTYPES{frequency = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/meter/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; frequency = @SKIP} + +# id -> id_tag +/obj/machinery/door_timer/@SUBTYPES{id = @SET} : /obj/machinery/door_timer/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/conveyor/@SUBTYPES{id = @SET} : /obj/machinery/conveyor/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/conveyor_switch/@SUBTYPES{id = @SET} : /obj/machinery/conveyor_switch/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/door/@SUBTYPES{id = @SET} : /obj/machinery/door/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/button/@SUBTYPES{id = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/atmospherics/@SUBTYPES{id = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/holosign/bar{id = @SET} : /obj/machinery/holosign/bar{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/meter/@SUBTYPES{id = @SET} : /obj/machinery/meter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/flasher/@SUBTYPES{id = @SET} : /obj/machinery/flasher/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/sparker/@SUBTYPES{id = @SET} : /obj/machinery/sparker/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/mass_driver/@SUBTYPES{id = @SET} : /obj/machinery/mass_driver/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/emitter/@SUBTYPES{id = @SET} : /obj/machinery/emitter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} + +# Camera preset_channels -> network +/obj/machinery/camera/network/@SUBTYPES{network = @SET} : /obj/machinery/camera/network/@SUBTYPES{@OLD; network = @SKIP; preset_channels = @OLD:network} + +# Remove button/sensor master_tag +/obj/machinery/airlock_sensor/@SUBTYPES{master_tag = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; master_tag = @SKIP} +/obj/machinery/button/access/@SUBTYPES{master_tag = @SET} : /obj/machinery/button/access/@SUBTYPES{@OLD; master_tag = @SKIP} + +# Skip power sensor name_tag +/obj/machinery/power/sensor{name_tag = @SET} : /obj/machinery/power/sensor{@OLD; name_tag = @SKIP} + +# join_group -> unique_merge_identifier +/turf/wall/shuttle/@SUBTYPES{join_group = @SET} : /turf/wall/shuttle/@SUBTYPES{@OLD; unique_merge_identifier = @OLD:join_group} + +# Remove set icon states/density on windows, doors, shutters +/obj/machinery/door/blast/regular{icon_state = @SET} : /obj/machinery/door/blast/regular{@OLD; icon_state = @SKIP} +/obj/structure/window/borosilicate_reinforced/@SUBTYPES{icon_state = @SET} : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD; icon_state = @SKIP} +/obj/machinery/door/blast/regular{p_open = 1} : /obj/machinery/door/blast/regular/open{@OLD; p_open = @SKIP} +/obj/machinery/door/blast/regular{p_open = 0} : /obj/machinery/door/blast/regular{@OLD; p_open = @SKIP} +/obj/machinery/door/blast/regular{density = 0} : /obj/machinery/door/blast/regular/open{@OLD; density = @SKIP; opacity = @SKIP} +/obj/structure/window/@SUBTYPES{icon_state = "fwindow"} : /obj/structure/window/@SUBTYPES{@OLD; icon_state = @SKIP} +/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} +/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} + +# Prices -> markup +/obj/machinery/vending/@SUBTYPES{prices = list()} : /obj/machinery/vending/@SUBTYPES{@OLD; markup = 0; prices=@SKIP} +/obj/machinery/vending/@SUBTYPES{prices = @SET} : /obj/machinery/vending/@SUBTYPES{@OLD; prices = @SKIP} + +# Radio varedits +/obj/item/radio/@SUBTYPES{subspace_transmission = @SET} : @OLD {@OLD; subspace_transmission = @SKIP} +/obj/item/radio/@SUBTYPES{syndie = @SET} : @OLD {@OLD; decrypt_all_messages = 1; syndie = @SKIP} +/obj/item/radio/@SUBTYPES{adhoc_fallback = @SET} : @OLD {@OLD; can_use_analog = @SKIP} + +# Empty first-aid kits +/obj/item/storage/firstaid/regular{empty = 1} : /obj/item/firstaid/empty{@OLD; empty = @SKIP; name = @SKIP} + +/obj/machinery/floor_light/prebuilt{on = 1} : /obj/machinery/floor_light/prebuilt{@OLD; use_power = 2; on = @SKIP} + /obj/structure/disposalpipe/sortjunction/@SUBTYPES{sortType = @SET} : /obj/structure/disposalpipe/sortjunction/@SUBTYPES{@OLD; sort_type = @OLD:sortType; sortType = @SKIP} \ No newline at end of file From 89c4dd0db2fafbe97c48576bacd0a32e49fefb75 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 6 Sep 2024 03:50:48 -0400 Subject: [PATCH 0687/1331] Fix article capitalization in emote strings --- code/modules/emotes/emote_define.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/emotes/emote_define.dm b/code/modules/emotes/emote_define.dm index 0223e3f3185b..b76c74f5c98a 100644 --- a/code/modules/emotes/emote_define.dm +++ b/code/modules/emotes/emote_define.dm @@ -7,7 +7,7 @@ . = replacetext(., "$TARGET_THEM$", target_gender.him) . = replacetext(., "$TARGET_THEIR$", target_gender.his) . = replacetext(., "$TARGET_SELF$", target_gender.self) - . = replacetext(., "$TARGET$", "\the [target]") + . = replacetext(., "$TARGET$", "[target]") /proc/emote_replace_user_tokens(var/msg, var/atom/user) . = msg @@ -18,7 +18,7 @@ . = replacetext(., "$USER_THEM$", user_gender.him) . = replacetext(., "$USER_THEIR$", user_gender.his) . = replacetext(., "$USER_SELF$", user_gender.self) - . = replacetext(., "$USER$", "\the [user]") + . = replacetext(., "$USER$", "[user]") // Note about emote messages: // - $USER$ / $TARGET$ will be replaced with the relevant name, in bold. From 205acef4175a492cfb53e9b2a76462a084d6afd1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 6 Sep 2024 04:08:01 -0400 Subject: [PATCH 0688/1331] Fix incorrect usage of bool for material opacity --- code/modules/materials/_materials.dm | 2 +- test/check-paths.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index dba95990092c..16aec1e852bb 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -182,7 +182,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /// General-use HP value for products. var/integrity = 150 /// Is the material transparent? 0.5< makes transparent walls/doors. - var/opacity = TRUE + var/opacity = 1.0 /// Only used by walls currently. var/explosion_resistance = 5 /// Objects with this var add CONDUCTS to flags on spawn. diff --git a/test/check-paths.sh b/test/check-paths.sh index 5643ae1afa53..d514d5d29a41 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -51,9 +51,9 @@ exactly 1 "direct usage of decls_repository.get_decl()" 'decls_repository\.get_d exactly 19 "direct loc set" '[^ ,(/]\bloc\s*=(?!=)' -P exactly 6 "pronoun macro use" '\\(he|she|him|her|his|hers|himself|herself|He|She|Him|Her|His|Hers|Himself|Herself)\b' -P exactly 0 "magic number mouse opacity set" 'mouse_opacity\s*=\s*[0-2]' -P -exactly 0 "magic number density set" '\bdensity\s*=\s*[01]\b' -P +exactly 1 "magic number density set" '\bdensity\s*=\s*[01]\b' -P exactly 0 "magic number anchored set" '\banchored\s*=\s*[01]\b' -P -exactly 0 "magic number opacity set" '\bopacity\s*=\s*[01](?!\.)' -P +exactly 0 "magic number opacity set" '\bopacity\s*=\s*[01]\b(?!\.)' -P # With the potential exception of << if you increase any of these numbers you're probably doing it wrong From e939102a6f97a007d1b1402b9277c2cb98ad3519 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 6 Sep 2024 04:10:06 -0400 Subject: [PATCH 0689/1331] Add/expand cryopod visible messages --- code/game/machinery/cryopod.dm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 438184b0cd55..75795405175f 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -157,7 +157,9 @@ var/occupied_icon_state = "body_scanner_1" var/on_store_message = "has entered long-term storage." var/on_store_name = "Cryogenic Oversight" + var/on_store_visible_message = "$TARGET$ hums and hisses as it moves $USER$ into storage." //! A visible message to display when the user is despawned. $USER$ will be replaced with the user's name, $TARGET$ will be replaced with the pod's name. var/on_enter_occupant_message = "You feel cool air surround you. You go numb as your senses turn inward." + var/on_enter_visible_message = "$USER$ starts climbing into $TARGET$." //! A visible message to display when the user enters the pod. $USER$ will be replaced with the user's name. var/allow_occupant_types = list(/mob/living/human) var/disallow_occupant_types = list() @@ -359,6 +361,7 @@ control_computer._admin_logs += "[key_name(occupant)] ([role_alt_title]) at [stationtime2text()]" log_and_message_admins("[key_name(occupant)] ([role_alt_title]) entered cryostorage.") + visible_message(SPAN_NOTICE(emote_replace_user_tokens(emote_replace_target_tokens(on_store_visible_message, src), occupant))) do_telecomms_announcement(src, "[occupant.real_name], [role_alt_title], [on_store_message]", "[on_store_name]") despawn_character(occupant) set_occupant(null) @@ -369,7 +372,10 @@ if(alert(target,"Would you like to enter long-term storage?",,"Yes","No") != "Yes") return if(!user.incapacitated() && !user.anchored && user.Adjacent(src) && user.Adjacent(target)) - visible_message("[user] starts putting [target] into \the [src].", range = 3) + if(M == user) + visible_message(SPAN_NOTICE(emote_replace_user_tokens(emote_replace_target_tokens(on_enter_visible_message, src), usr)), range = 3) + else + visible_message("[user] starts putting [target] into \the [src].", range = 3) if(!do_after(user, 20, src)|| QDELETED(target)) return set_occupant(target) @@ -444,7 +450,7 @@ if(!usr.can_enter_cryopod(usr)) return - visible_message("\The [usr] starts climbing into \the [src].", range = 3) + visible_message(emote_replace_user_tokens(emote_replace_target_tokens(on_enter_visible_message, src), usr), range = 3) if(do_after(usr, 20, src)) @@ -487,7 +493,7 @@ if(occupant.client) if(!silent) - to_chat(occupant, SPAN_NOTICE("[on_enter_occupant_message]")) + occupant.visible_message("\The [src] [emote_replace_target_tokens(on_enter_visible_message)]", SPAN_NOTICE(on_enter_occupant_message)) to_chat(occupant, SPAN_NOTICE("If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.")) occupant.client.perspective = EYE_PERSPECTIVE occupant.client.eye = src From f9c5da0d529252bbd346060259e3ad599296e756 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 6 Sep 2024 04:11:06 -0400 Subject: [PATCH 0690/1331] Add security newscaster preset --- code/game/machinery/newscaster.dm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 1152b6d5dc03..ae2af31628d8 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -943,3 +943,8 @@ var/global/list/allCasters = list() //Global list that will contain reference to else audible_message("[src.name] beeps, \"Attention! Wanted issue distributed!\"") playsound(src.loc, 'sound/machines/warning-buzzer.ogg', 75, 1) + +// security newscaster preset +/obj/machinery/newscaster/security_unit + base_type = /obj/machinery/newscaster + securityCaster = TRUE \ No newline at end of file From 62ce9a055ce7a3381286adc7d8f8c4e76d189299 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 6 Sep 2024 04:11:23 -0400 Subject: [PATCH 0691/1331] Add hidden SMES preset for use in submaps --- code/modules/power/smes_presets.dm | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/code/modules/power/smes_presets.dm b/code/modules/power/smes_presets.dm index 0775d5ca164c..39d638616c0c 100644 --- a/code/modules/power/smes_presets.dm +++ b/code/modules/power/smes_presets.dm @@ -14,4 +14,12 @@ input_attempt = _input_on output_attempt = _output_on if(_fully_charged) - charge = capacity \ No newline at end of file + charge = capacity + +// Pre-installed and pre-charged SMES hidden from the station, for use in submaps. +/obj/machinery/power/smes/buildable/preset/hidden + _fully_charged = TRUE + _input_on = TRUE + _input_maxed = TRUE + _output_maxed = TRUE + RCon = FALSE \ No newline at end of file From a4e093c832dc1a8512abe0d8c9be9cede0e84b2a Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 8 Jun 2025 20:25:01 +1000 Subject: [PATCH 0692/1331] Piecemeal corrections to cherrypicks/merges. --- code/game/machinery/cryopod.dm | 2 +- code/game/machinery/oxygen_pump.dm | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 75795405175f..1cbb8ba53cc8 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -372,7 +372,7 @@ if(alert(target,"Would you like to enter long-term storage?",,"Yes","No") != "Yes") return if(!user.incapacitated() && !user.anchored && user.Adjacent(src) && user.Adjacent(target)) - if(M == user) + if(target == user) visible_message(SPAN_NOTICE(emote_replace_user_tokens(emote_replace_target_tokens(on_enter_visible_message, src), usr)), range = 3) else visible_message("[user] starts putting [target] into \the [src].", range = 3) diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 70671c1d7e45..51f5f1d33cec 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -17,6 +17,7 @@ var/mask_type = /obj/item/clothing/mask/breath/emergency var/icon_state_open = "emerg_open" var/icon_state_closed = "emerg" + var/icon_state_active // TODO implement power_channel = ENVIRON idle_power_usage = 10 @@ -242,11 +243,9 @@ icon_state_open = "medpump_open" icon_state_closed = "medpump" icon_state_active = "medpump_active" - anchored = FALSE density = TRUE - /obj/machinery/oxygen_pump/mobile/stabilizer name = "portable patient stabilizer" desc = "A portable oxygen pump with a retractable mask used for stabilizing patients in the field." @@ -255,7 +254,7 @@ icon_state_open = "patient_stabilizer_open" icon_state_active = "patient_stabilizer_active" -/obj/machinery/oxygen_pump/mobile/stabilizer/process() +/obj/machinery/oxygen_pump/mobile/stabilizer/Process() . = ..() if(!breather) // Safety. return @@ -276,4 +275,4 @@ if(lungs.is_bruised() && prob(30)) lungs.heal_damage(1) else - breather.ticks_since_last_successful_breath = max(breather.ticks_since_last_successful_breath - rand(1,5), 0) \ No newline at end of file + breather.suffocation_counter = max(breather.suffocation_counter - rand(1,5), 0) \ No newline at end of file From bce09a08fe018759bccf50cc754bb165f1a6c410 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 8 Jun 2025 20:36:44 +1000 Subject: [PATCH 0693/1331] Several string tweaks from Polaris. --- code/modules/codex/categories/category_cultures.dm | 2 +- code/modules/codex/entries/atmospherics.dm | 2 +- code/modules/mob/language/generic.dm | 3 ++- code/modules/power/port_gen.dm | 2 +- code/modules/random_map/noise/ore.dm | 4 ++-- code/modules/species/species.dm | 2 +- .../artifacts/standalone/gigadrill.dm | 10 +++++++--- .../xenoarcheaology/finds/find_types/material.dm | 13 +++++++++++-- 8 files changed, 26 insertions(+), 12 deletions(-) diff --git a/code/modules/codex/categories/category_cultures.dm b/code/modules/codex/categories/category_cultures.dm index 3c51bbf65f42..0aef22c0c0cd 100644 --- a/code/modules/codex/categories/category_cultures.dm +++ b/code/modules/codex/categories/category_cultures.dm @@ -1,5 +1,5 @@ /decl/codex_category/cultures - name = "Factions and Culture" + name = "Places, Factions and Culture" desc = "Prominent planets, cultures, factions and religions of known space." /decl/codex_category/cultures/Populate() diff --git a/code/modules/codex/entries/atmospherics.dm b/code/modules/codex/entries/atmospherics.dm index 5b0b67fb8fbf..4c7e8993f078 100644 --- a/code/modules/codex/entries/atmospherics.dm +++ b/code/modules/codex/entries/atmospherics.dm @@ -147,7 +147,7 @@ /datum/codex_entry/transfer_valve associated_paths = list(/obj/item/transfer_valve) mechanics_text = "This machine is used to merge the contents of two different gas tanks. Plug the tanks into the transfer, then open the valve to mix them together. You can also attach various assembly devices to trigger this process." - antag_text = "With a tank of hot hydrogen and cold oxygen, this benign little atmospheric device becomes an incredibly deadly bomb. You don't want to be anywhere near it when it goes off." + antag_text = "With a tank of hot accelerant and cold oxidiser, this benign little atmospheric device becomes an incredibly deadly bomb. You don't want to be anywhere near it when it goes off." disambiguator = "component" available_to_map_tech_level = MAP_TECH_LEVEL_SPACE diff --git a/code/modules/mob/language/generic.dm b/code/modules/mob/language/generic.dm index 78301a7ca87a..1665c0b215c2 100644 --- a/code/modules/mob/language/generic.dm +++ b/code/modules/mob/language/generic.dm @@ -21,7 +21,8 @@ /decl/language/sign name = "Sign Language" - desc = "A sign language commonly used for those who are deaf or mute." + desc = "In an age of commonplace extra-vehicular activity and habitation of airless worlds, \ + sign language is often an essential communication tool for large portions of the population." signlang_verb = list("gestures") colour = "say_quote" key = "s" diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 058d9aef597a..526fefb07ed4 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -113,7 +113,7 @@ /* These values were chosen so that the generator can run safely up to 80 kW - A full 50 deuterium sheet stack should last 20 minutes at power_output = 4 + A full 50 graphite sheet stack should last 20 minutes at power_output = 4 temperature_gain and max_temperature are set so that the max safe power level is 4. Setting to 5 or higher can only be done temporarily before the generator overheats. */ diff --git a/code/modules/random_map/noise/ore.dm b/code/modules/random_map/noise/ore.dm index f23e1c2cd01c..474ccc3687ab 100644 --- a/code/modules/random_map/noise/ore.dm +++ b/code/modules/random_map/noise/ore.dm @@ -153,7 +153,7 @@ /decl/material/solid/metal/silver = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX), /decl/material/solid/metal/uranium = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX), /decl/material/solid/metal/osmium = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX), - /decl/material/solid/rutile = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX) + /decl/material/solid/rutile = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX) ) deep_metals = list( /decl/material/solid/ice/aspium = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX), @@ -165,5 +165,5 @@ /decl/material/solid/gemstone/diamond = list(RESOURCE_LOW_MIN, RESOURCE_LOW_MAX), /decl/material/solid/metal/osmium = list(RESOURCE_HIGH_MIN, RESOURCE_HIGH_MAX), /decl/material/solid/metallic_hydrogen = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX), - /decl/material/solid/rutile = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX) + /decl/material/solid/rutile = list(RESOURCE_MID_MIN, RESOURCE_MID_MAX) ) \ No newline at end of file diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index 2b8e4a628fca..799a656f84dc 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -362,7 +362,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 else if(!LAZYLEN(available_background_info[cat_type])) var/list/map_systems = global.using_map.available_background_info[cat_type] - available_background_info[cat_type] = map_systems.Copy() + available_background_info[cat_type] = islist(map_systems) ? map_systems.Copy() : list() if(LAZYLEN(available_background_info[cat_type]) && !default_background_info[cat_type]) var/list/avail_systems = available_background_info[cat_type] diff --git a/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm b/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm index 22e68e9011e4..c56e354022c8 100644 --- a/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm +++ b/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm @@ -3,11 +3,15 @@ desc = "A giant, alien drill mounted on long treads." icon = 'icons/obj/machines/gigadrill.dmi' icon_state = "gigadrill" + density = TRUE + layer = ABOVE_OBJ_LAYER //to go over ores + matter = list( + /decl/material/solid/metal/plasteel/ocp = MATTER_AMOUNT_PRIMARY, + /decl/material/solid/gemstone/diamond = MATTER_AMOUNT_REINFORCEMENT + ) var/active = 0 var/drill_time = 10 var/turf/drilling_turf - density = TRUE - layer = ABOVE_OBJ_LAYER //to go over ores /obj/machinery/giga_drill/physical_attack_hand(mob/user) if(active) @@ -35,4 +39,4 @@ anchored = FALSE /obj/machinery/giga_drill/get_artifact_scan_data() - return "Automated mining drill - structure composed of titanium-carbide alloy, with tip and drill lines edged in a complex lattice of diamond." + return "Automated mining drill - structure composed of osmium-carbide alloy, with tip and drill lines edged in a complex lattice of diamond." diff --git a/code/modules/xenoarcheaology/finds/find_types/material.dm b/code/modules/xenoarcheaology/finds/find_types/material.dm index f4bfc53c61f0..d870cb60ef4e 100644 --- a/code/modules/xenoarcheaology/finds/find_types/material.dm +++ b/code/modules/xenoarcheaology/finds/find_types/material.dm @@ -2,14 +2,23 @@ /decl/archaeological_find/material item_type = "material lump" modification_flags = XENOFIND_APPLY_PREFIX - var/list/possible_materials = list(/decl/material/solid/metal/steel, /decl/material/solid/metal/plasteel, /decl/material/solid/metal/titanium, /decl/material/solid/glass) + var/list/possible_materials = list( + /decl/material/solid/metal/steel, + /decl/material/solid/metal/plasteel, + /decl/material/solid/metal/titanium, + /decl/material/solid/glass + ) /decl/archaeological_find/material/spawn_item(atom/loc) return SSmaterials.create_object(pick(possible_materials), loc, rand(5,45))?[1] /decl/archaeological_find/material/exotic item_type = "rare material lump" - possible_materials = list(/decl/material/solid/metal/aliumium, /decl/material/solid/metallic_hydrogen, /decl/material/solid/glass/borosilicate) + possible_materials = list( + /decl/material/solid/metal/aliumium, + /decl/material/solid/metallic_hydrogen, + /decl/material/solid/glass/borosilicate + ) //Machinery parts /decl/archaeological_find/parts From ff0bbe4907dc3765c209ab8b04446510f6c1084c Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 8 Jun 2025 20:45:34 +1000 Subject: [PATCH 0694/1331] Updating test. --- test/check-paths.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/check-paths.sh b/test/check-paths.sh index d514d5d29a41..f59aa02ce4c7 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -51,7 +51,7 @@ exactly 1 "direct usage of decls_repository.get_decl()" 'decls_repository\.get_d exactly 19 "direct loc set" '[^ ,(/]\bloc\s*=(?!=)' -P exactly 6 "pronoun macro use" '\\(he|she|him|her|his|hers|himself|herself|He|She|Him|Her|His|Hers|Himself|Herself)\b' -P exactly 0 "magic number mouse opacity set" 'mouse_opacity\s*=\s*[0-2]' -P -exactly 1 "magic number density set" '\bdensity\s*=\s*[01]\b' -P +exactly 0 "magic number density set" '\bdensity\s*=\s*[01]\b' -P exactly 0 "magic number anchored set" '\banchored\s*=\s*[01]\b' -P exactly 0 "magic number opacity set" '\bopacity\s*=\s*[01]\b(?!\.)' -P From 4b26ddcb23e52963bf9c5050a9549526496997d1 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 8 Jun 2025 20:46:46 +1000 Subject: [PATCH 0695/1331] Fixing runtime in undies code. --- code/modules/mob/living/human/human.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index f2a5565b03e8..f08ad9c21a3a 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -559,7 +559,8 @@ //Drop anything that cannot be worn by the current species of the mob /mob/living/human/proc/apply_species_inventory_restrictions() - if(!(get_bodytype().appearance_flags & HAS_UNDERWEAR)) + var/decl/bodytype/check_bodytype = get_bodytype() + if(!istype(check_bodytype) || !(check_bodytype.appearance_flags & HAS_UNDERWEAR)) QDEL_NULL_LIST(worn_underwear) var/list/new_slots From 04dbb0753bd13e9bc1e483ca7c9c487688d4ed65 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 8 Jun 2025 20:54:21 +1000 Subject: [PATCH 0696/1331] Fix CAMERA_CAMERA_ --- code/__defines/machinery.dm | 2 +- code/datums/supplypacks/science.dm | 11 ++++++++++- code/game/machinery/camera/presets.dm | 2 +- .../silicon/robot/flying/module_flying_repair.dm | 2 +- .../silicon/robot/modules/module_engineering.dm | 2 +- .../silicon/robot/modules/module_maintenance_drone.dm | 2 +- maps/exodus/exodus_cameras.dm | 2 +- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index b7018659403b..c45a497ba5f3 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -44,7 +44,7 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called // Camera channels // Station channels #define CAMERA_CHANNEL_PUBLIC "Public" -#define CAMERA_CAMERA_CHANNEL_ENGINEERING "Engineering" +#define CAMERA_CHANNEL_ENGINEERING "Engineering" #define CAMERA_CHANNEL_MEDICAL "Medical" #define CAMERA_CHANNEL_RESEARCH "Research" #define CAMERA_CHANNEL_SECURITY "Security" diff --git a/code/datums/supplypacks/science.dm b/code/datums/supplypacks/science.dm index 8458268f05a5..5505d65f4647 100644 --- a/code/datums/supplypacks/science.dm +++ b/code/datums/supplypacks/science.dm @@ -65,4 +65,13 @@ /decl/hierarchy/supply_pack/science/illuminate name = "Gear - Illumination grenades" contains = list(/obj/item/grenade/light = 8) - containername = "illumination grenade crate" \ No newline at end of file + containername = "illumination grenade crate" + +/decl/hierarchy/supply_pack/science/stasis_cages + name = "Stasis Cage" + contains = list( + /obj/structure/stasis_cage = 1 + ) + containertype = /obj/structure/closet/crate/large + containername = "stasis cage crate" + access = access_research diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 12d24b214fad..0fe1d526de06 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -1,5 +1,5 @@ /obj/machinery/camera/network/engineering - preset_channels = list(CAMERA_CAMERA_CHANNEL_ENGINEERING) + preset_channels = list(CAMERA_CHANNEL_ENGINEERING) req_access = list(access_engine) /obj/machinery/camera/network/ert diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm index 83f08c2c46d1..3c6f36ea20c2 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm @@ -2,7 +2,7 @@ name = "repair drone module" display_name = "Repair" channels = list ("Engineering" = TRUE) - camera_channels = list(CAMERA_CAMERA_CHANNEL_ENGINEERING) + camera_channels = list(CAMERA_CHANNEL_ENGINEERING) software = list( /datum/computer_file/program/power_monitor ) diff --git a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm index bab3a53ce0bf..65bd61bbd014 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm @@ -5,7 +5,7 @@ "Engineering" = 1 ) camera_channels = list( - CAMERA_CAMERA_CHANNEL_ENGINEERING + CAMERA_CHANNEL_ENGINEERING ) software = list( /datum/computer_file/program/power_monitor diff --git a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm index d23e9445afc4..1635721d1c87 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm @@ -4,7 +4,7 @@ has_nonslip_feet = TRUE has_magnetic_feet = TRUE camera_channels = list( - CAMERA_CAMERA_CHANNEL_ENGINEERING + CAMERA_CHANNEL_ENGINEERING ) languages = list( /decl/language/human/common = FALSE diff --git a/maps/exodus/exodus_cameras.dm b/maps/exodus/exodus_cameras.dm index 1f5d4b51ee31..0c55ca3a3824 100644 --- a/maps/exodus/exodus_cameras.dm +++ b/maps/exodus/exodus_cameras.dm @@ -37,7 +37,7 @@ var/global/const/CAMERA_CHANNEL_ENGINEERING_OUTPOST = "Engineering Outpost" req_access = list(access_heads) /obj/machinery/camera/network/maintenance - preset_channels = list(CAMERA_CAMERA_CHANNEL_ENGINEERING) + preset_channels = list(CAMERA_CHANNEL_ENGINEERING) req_access = list(access_engine) /obj/machinery/camera/xray/security From fa56557ee698dda453eb8e67096c7f6280d1f2e6 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 8 Jun 2025 21:04:59 +1000 Subject: [PATCH 0697/1331] Porting robot cryopods from Lighthouse. --- code/game/machinery/cryopod.dm | 25 +++++++++++++++++++++++++ icons/obj/machines/tramdoors.dmi | Bin 0 -> 2661 bytes 2 files changed, 25 insertions(+) create mode 100644 icons/obj/machines/tramdoors.dmi diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 1cbb8ba53cc8..7a0bf2fccd41 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -190,6 +190,31 @@ disallow_occupant_types = list(/mob/living/silicon/robot/drone) applies_stasis = 0 +// Cryo +/obj/machinery/cryopod/robot/door + //This inherits from the robot cryo, so synths can be properly cryo'd. If a non-synth enters and is cryo'd, ..() is called and it'll still work. + abstract_type = /obj/machinery/cryopod/robot/door + name = "Airlock of Wonders" + desc = "An airlock that isn't an airlock, and shouldn't exist. Yell at a coder/mapper." + icon = 'icons/obj/machines/tramdoors.dmi' + icon_state = "door_closed" + base_icon_state = "door_closed" + occupied_icon_state = "door_locked" + on_enter_visible_message = "$USER$ steps into $TARGET$." + + time_till_despawn = 1 MINUTE //We want to be much faster then normal cryo, since waiting in an elevator for half an hour is a special kind of hell. + + allow_occupant_types = list(/mob/living/silicon/robot,/mob/living/human) + disallow_occupant_types = list(/mob/living/silicon/robot/drone) + +/obj/machinery/cryopod/robot/door/dorms + name = "Residential District Elevator" + desc = "A small elevator that goes down to the deeper section of the colony." + on_store_message = "has departed for the residential district." + on_store_name = "Residential Oversight" + on_enter_occupant_message = "The elevator door closes slowly, ready to bring you down to the residential district." + on_store_visible_message = "$TARGET$ makes a ding as it moves $USER$ to the residential district." + /obj/machinery/cryopod/lifepod name = "life pod" desc = "A man-sized pod for entering suspended animation. Dubbed 'cryocoffin' by more cynical spacers, it is pretty barebone, counting on stasis system to keep the victim alive rather than packing extended supply of food or air. Can be ordered with symbols of common religious denominations to be used in space funerals too." diff --git a/icons/obj/machines/tramdoors.dmi b/icons/obj/machines/tramdoors.dmi new file mode 100644 index 0000000000000000000000000000000000000000..3b40ac65a8ef96c31f1163c079610e6d7543aaef GIT binary patch literal 2661 zcmV-r3YztaP)C0003gP)t-sz`(#- z5CCTs09X$IF$DmC8vu7003972kzNChTmx8GSd3c(0000lFE3(ZVw+|Jp=t!5Xak*S z10Eh8qiO@3XanGe2r4QnZf1Da(7nPdc+ zV+5ye1dUw-DMD8;Qfo3)ZZuVIFiK#gW&}VZ9a1J8K_ecSTm<5Q3C|}0<|+X9EdbCb z0HS0CzJmo`K|F0iIfOzqnnEy7K|g*Q1d1O9Y$_dQ6asP=0+S&GE+8F=9s*Gg1VIS` zfg1uZARam_DU2QjVG{s3k3A9(U*Z=?k0d!JMQvg8b*k%9# z0EK!~Sad{Xb7OL8aCB*JZU6vyoKseCa&`CgQ*iP1=Hw@5<5sJz;OgfB_8tHwGeb>##8hqo00>!0L_t(|ob8wk zchfc$#}zq=?PBBfMN87X*U_zP>$Z+H8zcqFdvO~ti34q+^!tB^T}hTLIeKKrIl#_| z(x$e4pueQ6$GsP!PodN_WnR84AGu&melc>eP=i_RJiK}bNNc1`M;0L8^KgXf7(2AW z$^Ni+2iND}wY)VvD^Rm@D!>}f+GStlPu>9&hejGuL;=S^nA8z*D<=Zlg681RNCO@l z@(8o4nw>L&ro+bMIQc_U?0|vO*5KdB0`Q(`+Bg+xI+<1J3}=>Z79B8gIFCR8)rCa{ z>z&jz2(xk`(4;Lk8+mCGF;GBraLfgYZB__iJI@80qH(htGzYI?)*M+{e2N`bgV`D| zb1slicsA+f4rn>%0*)^c!#S|Adc9bSib0UBm?4)gGg}pTp?|_kG zE`Sv_#&%8xvWEFT;IDZHOnf1`KyEmH%}rq9#YtD5;_^wT=2SqM+G48n4wyJL9ktlb zsQ|2M3?KT<{;+p62QMxg$;OJyNX?l5E&Hz&xDYEV8db{Z7##F>$`x zrEP|1-Etl03Z(h$i#`bVpN&^9CYN*`zAROW>-j6LrmWoXT;QYxLG~j(5*}v7y?K-W zKX}S(Z5+8#%hkpIZYy$u`#SCe_g2@S$@O*5YqcUDej`6>wLEWq9jez>JsU@^@9QSO zb#DN(@Quw|Tids{x1xv$kZ&RoMYQtP=7x=wvGEcVg)Ti#g1>|s2#QR z0cn?-w(CL_uDoO0xmrsJJ3hy$gASv94; zolaqq!Fs1oC!BDBje`jyef!*mxIISN<5)yMz4--QCdlNW&pm2eg=Q7-zIMb-_DB2EAIj#(3FMqSKfi9ES$d*?iVLrd5XzN=i4~zfG+;$9We0X zq*EQW;z_9HbPZ_Z|7;DIcu-t6j^eTjY#chUt&jh=Ne9fFO)ozxmo};$A zrH}u&+_sI=`<^)*;uJH7gZl#pj`n~2Ngx03{@k{4GR1nAi%=;HsK-CyB4-t6dj2YBVcGknk&VA-6PQ}*7F7B2gnHJ_#Y&N;8f4W{J@`o@sBz`pyVv_AtRJ+43a`*q~}6@ z;C>-+UzHzFa~A)XMu@W!=J^3VFY*K3?ve1B%@1fen_{Jn1rzA`g7O1&l}qtIvoX&P z==p;31M&#TBBLYB^8i<1tQ_PG$8 z>iL5519XIr`6N{H1?302v+;jd%n#`KGC#1)5Bx9k14jNK@&iWxA@T!8zRVAN0{MXl zy}%=~*8teH*Wy9~i{FQ)oXPh}Z2P28Gie>wuA0 z&kqdaUQEhBJnILvsuvG^8?T-pNQS+Cpd3CeM^6?njk1sDCAG@R^--rTCv5G%PqZ zaXJIn*$8>um5uP4&wz=;6pz?RSC0S5QAauJWn z%&X@I3a3*Rm~~2(yn22hce};-U*(podG-82)xe~BexPD#Ry{vZH8}mq`GJ1{D#Us) T6>@Bg00000NkvXXu0mjfGxOcP literal 0 HcmV?d00001 From b5959b28da9f1696e4116d88dfbfc4bfe16a7c2c Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 8 Jun 2025 21:10:23 +1000 Subject: [PATCH 0698/1331] Porting permits from Polaris/Lighthouse. --- code/modules/clothing/permits/_permit.dm | 86 +++++++++++++++++++++++ icons/clothing/accessories/permits.dmi | Bin 0 -> 781 bytes nebula.dme | 1 + 3 files changed, 87 insertions(+) create mode 100644 code/modules/clothing/permits/_permit.dm create mode 100644 icons/clothing/accessories/permits.dmi diff --git a/code/modules/clothing/permits/_permit.dm b/code/modules/clothing/permits/_permit.dm new file mode 100644 index 000000000000..1f42fce9e5cf --- /dev/null +++ b/code/modules/clothing/permits/_permit.dm @@ -0,0 +1,86 @@ +// Permits (drone identification, gun permit, etc) +/obj/item/clothing/permit + name = "permit" + desc = "A permit for something." + icon = 'icons/clothing/accessories/permits.dmi' + w_class = ITEM_SIZE_TINY + accessory_slot = ACCESSORY_SLOT_MEDAL + /// If set, the accents on the ID are set to this color using an overlay. + var/detail_color = COLOR_GUNMETAL + /// Each string in this list is an overlay applied to the permit icon. Example: list("goldstripe") + var/list/extra_details + /// Set to TRUE when someone names the permit by using it in hand rather than using a labeler. Prevents doing so again. + var/hand_name_used = FALSE + /// String. When set, owner is set to this on initialize. + var/default_owner = null + +/obj/item/clothing/permit/Initialize() + . = ..() + get_or_create_extension(src, /datum/extension/labels/single) + if(istext(default_owner)) + set_owner(default_owner, force = TRUE) + +/obj/item/clothing/permit/adjust_mob_overlay(var/mob/living/user_mob, var/bodytype, var/image/overlay, var/slot, var/bodypart) + if(overlay && detail_color) + overlay.overlays += overlay_image(overlay.icon, "[overlay.icon_state]-colors", detail_color, RESET_COLOR) + . = ..() + +/obj/item/clothing/permit/on_update_icon() + . = ..() + if(detail_color) + add_overlay(overlay_image(icon, "[icon_state]-colors", detail_color, RESET_COLOR)) + for(var/detail in extra_details) + add_overlay(overlay_image(icon, "[icon_state]-[detail]", flags = RESET_COLOR)) + +/obj/item/clothing/permit/attack_self(mob/living/user) + var/datum/extension/labels/label_ext = get_extension(src, /datum/extension/labels) + if(length(label_ext.labels)) + to_chat(user, SPAN_WARNING("This permit's integrated labeler can't function if a label is already present. Please remove any existing labels or blockages and try again.")) + return + if(hand_name_used) + to_chat(user, SPAN_WARNING("This permit's integrated ink cartridge has already been used. New labels must be applied manually using a hand labeler.")) + return + var/name_to_use = user.get_authentification_name(if_no_id = null) + if(!name_to_use) + to_chat(user, SPAN_WARNING("Unable to link permit with ID card. Please ensure your ID card is in range of the permit and try again.")) + return + if(alert(user, "Claim this [name] for [name_to_use]? Once you claim it, the name can only be replaced using a hand labeler.", "Claim \the [src]?", "Yes", "No") != "Yes") + return + hand_name_used = TRUE + to_chat(user, SPAN_NOTICE("You claim \the [src] for [name_to_use].")) // must be here so that the label isn't included already + set_owner(name_to_use) + +/obj/item/clothing/permit/proc/set_owner(owner_name, force = FALSE) + var/datum/extension/labels/label_ext = get_extension(src, /datum/extension/labels) + if(force) + label_ext.RemoveAllLabels() + if(!length(label_ext.labels)) + label_ext.AttachLabel(null, owner_name) + +/obj/item/clothing/permit/gun + name = "weapon permit" + desc = "A card indicating that the owner is allowed to carry a weapon." + detail_color = COLOR_NT_RED + +/obj/item/clothing/permit/gun/bar + name = "bar shotgun permit" + desc = "A card indicating that the owner is allowed to carry a shotgun in the bar." + +/obj/item/clothing/permit/gun/planetside + name = "planetside weapon permit" + desc = "A card indicating that the owner is allowed to carry a weapon while on the surface." + detail_color = COLOR_PALE_PINK + +/obj/item/clothing/permit/gun/paramedic + name = "paramedic weapon permit" + desc = "A card indicating that the owner is allowed to carry a weapon while on EVA retrieval missions." + detail_color = COLOR_SKY_BLUE + +/obj/item/clothing/permit/chaplain + name = "holy weapon permit" + desc = "A card indicating that the owner is allowed to carry a weapon for religious rites and purposes." + detail_color = COLOR_GRAY15 + +/obj/item/clothing/permit/gun/planetside/exploration + name = "explorer weapon permit" + desc = "A card indicating that the owner is allowed to carry weaponry during active exploration missions." \ No newline at end of file diff --git a/icons/clothing/accessories/permits.dmi b/icons/clothing/accessories/permits.dmi new file mode 100644 index 0000000000000000000000000000000000000000..25ee8e450845e67e4ec01a5a31c8508d26f65e00 GIT binary patch literal 781 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRS`>R4CN?cNllZ!G7N;32F7#J$% zOr3O^tJy%n_4>(hme}ZD1-E@yiim#|KVo)~=ZMctm!tdFn{aC`U9o#-!TGuD!rk6w zr@0IFKHn4mgkh~kcggnWISenqs`$B|+tTFrTVc-nJ+n?_I2ai{@?qNPDQ~fQWmf2l z{jn#7c_&4iG{2qt)#Gv1^II8rw@!7P@TjrKXO3e$Uuyb7x2$xFyL|t7KBy5PgC3DK!4evg+R4rj(VB++2aSW-L^Y*S`-z^6b zh5+`bLLJ&q7(x^-Do9DS9O1P%G)cOEHLt`FBtW`>DL`x948^>$P;!&(Bx0Ov8J=|Ni^(arxbQ z|E}Ap|JUJbKkPf-TCRWR?R$CKcb@+^?~Uf!;FBiIF~+jb=6*VLr%q6+_gL)l$v?Fp z?0so)WJ>t8Z{poYXFv8^E`0X+=M^_|++KgRs(f3s|Mpw9+2f1ghxOOH&1U<4@Uv+$dD&gGeu;jKl+K%ep%YL@jfFcIAFkT@_T*# zo&UBynQ2p{W27Jb>ih5Y%=6BsEsgtDl7C)++xsv~C6tzPdS?3e^wUqJzBRk=)}1i@ zxUhp2MF}g%u6eotjlHg&3f2M!%Z#)?*9JsD}BW8pJ9g@= Date: Sun, 8 Jun 2025 21:12:23 +1000 Subject: [PATCH 0699/1331] Fixes erroneous setting of abstract_type on avian bodytype. --- mods/species/neoavians/datum/species_bodytypes.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/mods/species/neoavians/datum/species_bodytypes.dm b/mods/species/neoavians/datum/species_bodytypes.dm index 03e8a9e5657e..45719a3e2b68 100644 --- a/mods/species/neoavians/datum/species_bodytypes.dm +++ b/mods/species/neoavians/datum/species_bodytypes.dm @@ -26,7 +26,6 @@ uid = "bodytype_prosthetic_raptor" /decl/bodytype/avian - abstract_type = /decl/bodytype/avian name = "avian" bodytype_category = BODYTYPE_AVIAN icon_base = 'mods/species/neoavians/icons/body.dmi' From 23fd03e0c07506eb2a25a5b51510663ee43472b5 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 9 Jun 2025 13:59:46 +1000 Subject: [PATCH 0700/1331] Removed erroneous language inclusion. --- .../species/bayliens/skrell/datum/language.dm | 35 ------------------- .../neoavians/datum/species_bodytypes.dm | 1 - 2 files changed, 36 deletions(-) delete mode 100644 mods/species/bayliens/skrell/datum/language.dm diff --git a/mods/species/bayliens/skrell/datum/language.dm b/mods/species/bayliens/skrell/datum/language.dm deleted file mode 100644 index 649ee1649e6c..000000000000 --- a/mods/species/bayliens/skrell/datum/language.dm +++ /dev/null @@ -1,35 +0,0 @@ -var/global/list/first_name_skrell = file2list("mods/species/bayliens/skrell/names/first_name_skrell.txt") -var/global/list/last_name_skrell = file2list("mods/species/bayliens/skrell/names/last_name_skrell.txt") - -/decl/language/skrell - name = "Common Skrellian" - desc = "A melodic and complex language spoken by the Skrell of the Oligarchy. Some of the notes are inaudible to humans." - speech_verb = "warbles" - ask_verb = "trills" - exclaim_verb = "croaks" - colour = "selenian" - key = "k" - flags = LANG_FLAG_WHITELISTED - syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","'","!") - shorthand = "SK" - partial_understanding = list( - /decl/language/skrell/casteless = 90 - ) - -/decl/language/skrell/casteless - name = "Ue-Katish Cant" - desc = "A flat and emotionless language spoken mainly by Ue-Katish pirates, laden with slurs and insults." - speech_verb = "croaks" - ask_verb = "trills" - exclaim_verb = "chirps" - colour = "selenian" - key = "p" - flags = LANG_FLAG_WHITELISTED - syllables = list("qra","xa","vilz*","s!zaao","qu!*m","girx","vol","xrim","za-r*oo","qu-xx","qix","qa'taz","zix","tuep","!","*") - shorthand = "UK" - partial_understanding = list( - /decl/language/skrell = 90 - ) - -/decl/language/skrell/get_random_name(var/gender) - return capitalize(pick(global.first_name_skrell)) + capitalize(pick(global.last_name_skrell)) \ No newline at end of file diff --git a/mods/species/neoavians/datum/species_bodytypes.dm b/mods/species/neoavians/datum/species_bodytypes.dm index 45719a3e2b68..a83f74e9b60c 100644 --- a/mods/species/neoavians/datum/species_bodytypes.dm +++ b/mods/species/neoavians/datum/species_bodytypes.dm @@ -84,7 +84,6 @@ uid = "bodytype_avian_raptor" /decl/bodytype/avian/additive - abstract_type = /decl/bodytype/avian/additive name = "avian, additive" icon_base = 'mods/species/neoavians/icons/body_add.dmi' health_hud_intensity = 3 From 4f9efee272b9d75f2f959aa3a9f2940aa814cd9e Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 11 Jun 2025 18:43:13 +1000 Subject: [PATCH 0701/1331] Added CRLF rules for .txt and .md --- .editorconfig | 5 + .gitattributes | 2 + tools/map_migrations/0000_legacy.txt | 1008 +++++++++++++------------- 3 files changed, 511 insertions(+), 504 deletions(-) diff --git a/.editorconfig b/.editorconfig index ad578927262d..2643d9b63442 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,3 +20,8 @@ indent_size = 4 indent_style = space indent_size = 2 +[*.txt] +end_of_line = crlf + +[*.md] +end_of_line = crlf diff --git a/.gitattributes b/.gitattributes index add01d675f96..e9d52dc929b5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,3 +12,5 @@ html/changelog.html merge=union # Declare files that will always have CRLF line endings on checkout. *.dm text eol=crlf *.dmm text eol=crlf +*.txt text eol=crlf +*.md text eol=crlf \ No newline at end of file diff --git a/tools/map_migrations/0000_legacy.txt b/tools/map_migrations/0000_legacy.txt index 721a4edf9f27..ccac8405c68a 100644 --- a/tools/map_migrations/0000_legacy.txt +++ b/tools/map_migrations/0000_legacy.txt @@ -1,504 +1,504 @@ -/obj/item/reagent_containers/@SUBTYPES : /obj/item/chems/@SUBTYPES{@OLD} -/obj/item/material/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/weapon/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/melee/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/packageWrap/@SUBTYPES : /obj/item/stack/package_wrap/@SUBTYPES{@OLD} -/obj/structure/table/rack/@SUBTYPES : /obj/structure/rack/@SUBTYPES{@OLD} -/obj/structure/table/woodentable/holotable : /obj/structure/table/holo_woodentable{@OLD} -/obj/item/chems/food/drinks/@SUBTYPES : /obj/item/chems/drinks/@SUBTYPES{@OLD} -/obj/item/chems/food/snacks/@SUBTYPES : /obj/item/food/@SUBTYPES{@OLD} -/obj/item/chems/food/condiment/@SUBTYPES : /obj/item/chems/condiment/@SUBTYPES{@OLD} -/obj/item/chems/condiment/peppermill/@SUBTYPES : /obj/item/chems/condiment/small/peppermill/@SUBTYPES{@OLD} -/obj/item/chems/condiment/saltshaker/@SUBTYPES : /obj/item/chems/condiment/small/saltshaker/@SUBTYPES{@OLD} -/obj/item/analyzer : /obj/item/scanner/gas{@OLD} -/obj/item/analyzer/plant_analyzer/@SUBTYPES : /obj/item/scanner/plant/@SUBTYPES{@OLD} -/obj/item/tape_roll/@SUBTYPES : /obj/item/stack/tape_roll/@SUBTYPES{@OLD} -/obj/structure/table/standard/@SUBTYPES : /obj/structure/table/@SUBTYPES{@OLD} -/obj/machinery/bluespacerelay/@SUBTYPES : /obj/machinery/commsrelay/@SUBTYPES{@OLD} -/obj/machinery/computer/helm/@SUBTYPES : /obj/machinery/computer/ship/helm/@SUBTYPES{@OLD} -/obj/machinery/computer/engines/@SUBTYPES : /obj/machinery/computer/ship/engines/@SUBTYPES{@OLD} -/obj/machinery/computer/navigation/@SUBTYPES : /obj/machinery/computer/ship/navigation/@SUBTYPES{@OLD} -/obj/machinery/requests_console/@SUBTYPES : /obj/machinery/network/requests_console/@SUBTYPES{@OLD; departmentType = @SKIP} -/obj/item/healthanalyzer/@SUBTYPES : /obj/item/scanner/health/@SUBTYPES{@OLD} -/obj/machinery/iv_drip/@SUBTYPES : /obj/structure/iv_drip/@SUBTYPES{@OLD} -/obj/structure/device/piano/@SUBTYPES : /obj/structure/synthesized_instrument/synthesizer/piano/@SUBTYPES{@OLD} -/obj/item/folder/white/@SUBTYPES : /obj/item/folder/@SUBTYPES{@OLD} -/obj/machinery/dnaforensics/@SUBTYPES : /obj/machinery/forensic/dnascanner/@SUBTYPES{@OLD} -/obj/machinery/microscope/@SUBTYPES : /obj/machinery/forensic/microscope/@SUBTYPES{@OLD} -/obj/item/stack/material/steel/@SUBTYPES : /obj/item/stack/material/sheet/mapped/steel/@SUBTYPES{@OLD} -/obj/item/stack/material/plasteel/@SUBTYPES : /obj/item/stack/material/sheet/reinforced/mapped/plasteel/@SUBTYPES{@OLD} -/obj/item/stack/material/glass/reinforced/@SUBTYPES : /obj/item/stack/material/pane/mapped/rglass/@SUBTYPES{@OLD} -/obj/item/stack/material/glass/@SUBTYPES : /obj/item/stack/material/pane/mapped/glass/@SUBTYPES{@OLD} -/obj/item/stack/material/wood/@SUBTYPES : /obj/item/stack/material/plank/mapped/wood/@SUBTYPES{@OLD} -/obj/item/stack/material/deuterium/@SUBTYPES : /obj/item/stack/material/aerogel/mapped/deuterium/@SUBTYPES{@OLD} -/obj/item/stack/material/uranium/@SUBTYPES : /obj/item/stack/material/puck/mapped/uranium/@SUBTYPES{@OLD} -/obj/machinery/autolathe/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD} -/obj/machinery/message_server/@SUBTYPES : /obj/machinery/network/message_server/@SUBTYPES{@OLD} -/obj/item/floor_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} -/obj/machinery/pipedispenser/@SUBTYPES : /obj/machinery/fabricator/pipe/@SUBTYPES{@OLD} -/obj/machinery/power/emitter/@SUBTYPES : /obj/machinery/emitter/@SUBTYPES{@OLD} -/obj/machinery/power/fusion_core/@SUBTYPES : /obj/machinery/fusion_core/@SUBTYPES{@OLD} -/obj/item/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} -/obj/machinery/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} -/obj/machinery/computer/modular/preset/command/@SUBTYPES : /obj/machinery/computer/modular/preset/cardslot/command/@SUBTYPES{@OLD} -/obj/machinery/computer/fusion_core_control/@SUBTYPES : /obj/machinery/computer/fusion/core_control/@SUBTYPES{@OLD} -/obj/machinery/computer/fusion_fuel_control/@SUBTYPES : /obj/machinery/computer/fusion/fuel_control/@SUBTYPES{@OLD} -/obj/machinery/computer/gyrotron_control/@SUBTYPES : /obj/machinery/computer/fusion/gyrotron/@SUBTYPES{@OLD} -/obj/machinery/computer/power_monitor/@SUBTYPES : /obj/machinery/computer/modular/preset/engineering/power/@SUBTYPES{@OLD} -/obj/item/module/power_control/@SUBTYPES : /obj/item/stock_parts/circuitboard/apc/@SUBTYPES{@OLD} -/obj/machinery/power/port_gen/@SUBTYPES : /obj/machinery/port_gen/@SUBTYPES{@OLD} -/obj/item/pipe_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} -/obj/item/airlock_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/airlock_electronics/@SUBTYPES{@OLD} -/obj/item/airalarm_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/air_alarm/@SUBTYPES{@OLD} -/obj/item/autopsy_scanner/@SUBTYPES : /obj/item/scanner/autopsy/@SUBTYPES{@OLD} -/obj/machinery/fusion_fuel_compressor/@SUBTYPES : /obj/machinery/fuel_compressor/@SUBTYPES{@OLD} -/obj/item/extinguisher/micro/@SUBTYPES : /obj/item/chems/spray/extinguisher/mini/@SUBTYPES{@OLD} -/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} -/obj/item/stack/material/plastic/@SUBTYPES : /obj/item/stack/material/panel/mapped/plastic/@SUBTYPES{@OLD} -/obj/machinery/computer/general_air_control/@SUBTYPES : /obj/machinery/computer/air_control/@SUBTYPES{@OLD} -/obj/machinery/computer/air_control/large_tank_control : /obj/machinery/computer/air_control{@OLD} -/turf/simulated/floor/tiled/steel/@SUBTYPES : /turf/simulated/floor/tiled/@SUBTYPES{@OLD} -/obj/machinery/power/engine/@SUBTYPES : /obj/machinery/ion_thruster/@SUBTYPES{@OLD} -/obj/machinery/computer/aifixer/@SUBTYPES : /obj/machinery/computer/modular/preset/aislot/sysadmin/@SUBTYPES{@OLD} -/obj/machinery/computer/aiupload/@SUBTYPES : /obj/machinery/computer/upload/ai/@SUBTYPES{@OLD} -/obj/machinery/computer/borgupload/@SUBTYPES : /obj/machinery/computer/upload/robot/@SUBTYPES{@OLD} -/obj/machinery/computer/supplycomp/control/@SUBTYPES : /obj/machinery/computer/modular/preset/dock/@SUBTYPES{@OLD} -/obj/machinery/computer/supplycomp/@SUBTYPES : /obj/machinery/computer/modular/preset/supply_public/@SUBTYPES{@OLD} -/obj/item/closet_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} -/turf/simulated/shuttle/plating/@SUBTYPES : /turf/floor/shuttle -/turf/simulated/shuttle/floor/@SUBTYPES : /turf/floor/shuttle/@SUBTYPES - -# REMOVE BAD VAREDITS -/@SUBTYPES {layer = @SET} : @OLD {@OLD; layer = @SKIP} -/@SUBTYPES {plane = @SET} : @OLD {@OLD; plane = @SKIP} -/obj/structure/cable/@SUBTYPES : @OLD {@OLD; d1 = @SKIP;d2 = @SKIP} -# unacidable removed -/@SUBTYPES {unacidable = @SET} : @OLD{@OLD; unacidable = @SKIP} -# Simple pipes shall not face north or west -/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} -/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} -/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} -/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} -# Do not change air alarm frequency on base type -/obj/machinery/alarm {frequency = @SET} : @OLD {@OLD; frequency = @SKIP} -# we use a completely different access format -/@SUBTYPES {req_access = @SET}: @OLD {@OLD; req_access = @SKIP;} -/@SUBTYPES {req_one_access = @SET}: @OLD {@OLD; req_one_access = @SKIP;} -/obj/machinery/door/blast/shutters{icon_state = "shutter0"} : /obj/machinery/door/blast/shutters{@OLD; icon_state = @SKIP} -/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1; density = 0, opacity = 0} : /obj/machinery/door/airlock/external/glass/bolted_open{@OLD; icon_state = @SKIP; locked = @SKIP, density = @SKIP; opacity = @SKIP} -/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1} : /obj/machinery/door/airlock/external/glass/bolted{@OLD; icon_state = @SKIP; locked = @SKIP} - -# BUTTONS -/obj/machinery/access_button/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} -/obj/machinery/access_button/airlock_interior : /obj/machinery/button/access/interior{@OLD} -/obj/machinery/airlock_sensor/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} -/obj/machinery/airlock_sensor/airlock_interior : /obj/machinery/button/access/interior{@OLD} -/obj/machinery/access_button : /obj/machinery/button/access{@OLD} -/obj/machinery/button/remote/airlock{specialfunctions = 4} : /obj/machinery/button/alternate/door/bolts{@OLD; specialfunctions = @SKIP; desiredstate = @SKIP} -/obj/machinery/button/remote/airlock : /obj/machinery/button/alternate/door{@OLD} -/obj/machinery/button/remote/driver : /obj/machinery/button/mass_driver{@OLD} -/obj/machinery/button/remote/blast_door : /obj/machinery/button/blast_door{@OLD} -/obj/machinery/button/remote : /obj/machinery/button/alternate{@OLD} - -# Medical -/obj/item/pill_bottle/tramadol : /obj/item/pill_bottle/strong_painkillers{@OLD} -/obj/item/chems/pill/tramadol : /obj/item/chems/pill/strong_painkillers{@OLD} -/obj/item/chems/glass/bottle/stoxin : /obj/item/chems/glass/bottle/sedatives{@OLD} -/obj/item/chems/glass/bottle/inaprovaline : /obj/item/chems/glass/bottle/stabilizer{@OLD} -/obj/item/chems/syringe/inaprovaline : /obj/item/chems/syringe/stabilizer{@OLD} -/obj/item/chems/blood/empty : /obj/item/chems/ivbag{@OLD} -/obj/item/chems/blood/@SUBTYPES : /obj/item/chems/ivbag/blood/@SUBTYPES{@OLD} -/obj/item/defib_kit/@SUBTYPES : /obj/item/defibrillator/@SUBTYPES{@OLD} -/obj/item/chems/spray/sterilizine : /obj/item/chems/spray/antiseptic{@OLD} - -# Drinking glasses and cups -/obj/item/chems/drinks/cup : /obj/item/chems/drinks/glass2/coffeecup{@OLD} -/obj/item/chems/drinks/drinkingglass : /obj/item/chems/drinks/glass2/square{@OLD} - -# Airtight flaps -/obj/structure/plasticflaps/mining : /obj/structure/flaps/airtight{@OLD} - -# Bushes -/obj/structure/flora/ausbushes/@SUBTYPES : /obj/structure/flora/bush/@SUBTYPES - -# Cassette tapes -> magnetic tapes -/obj/item/cassette_tape/@SUBTYPES : /obj/item/magnetic_tape/@SUBTYPES{@OLD} -/obj/item/tape_recorder : /obj/item/taperecorder{@OLD} - -# FIFTYSPAWNER -> MAPPED SHEETS -/obj/fiftyspawner/wood : /obj/item/stack/material/plank/mapped/wood/fifty -/obj/fiftyspawner/steel : /obj/item/stack/material/sheet/mapped/steel/fifty -/obj/fiftyspawner/glass : /obj/item/stack/material/pane/mapped/glass/fifty -/obj/fiftyspawner/deuterium : /obj/item/stack/material/aerogel/mapped/deuterium/fifty -/obj/fiftyspawner/tritium : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/osmium : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/phoron : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/rods : /obj/item/stack/material/aerogel/mapped/tritium/fifty -/obj/fiftyspawner/grass : /obj/item/stack/tile/grass{amount = 50} -/obj/fiftyspawner/plasteel : /obj/item/stack/material/aerogel/mapped/tritium/fifty - -# STOCK PARTS AND SMES COILS -/obj/item/stock_parts/subspace/sub_filter : /obj/item/stock_parts/subspace/filter -/obj/item/smes_coil/@SUBTYPES : /obj/item/stock_parts/smes_coil/@SUBTYPES{@OLD} -/obj/item/circuitboard/@SUBTYPES : /obj/item/stock_parts/circuitboard/@SUBTYPES{@OLD} -/obj/item/airlock_electronics : /obj/item/stock_parts/circuitboard/airlock_electronics{@OLD} - -# CLOTHING -/obj/item/clothing/head/cone : /obj/item/caution/cone{@OLD} -/obj/item/clothing/belt/security/tactical : /obj/item/belt/holster/security/tactical{@OLD} - -# Item remains -/obj/effect/decal/remains/@SUBTYPES : /obj/item/remains/@SUBTYPES{@OLD} - -# Circuit prefabs -/obj/item/hand_tele : /obj/prefab/hand_teleporter{@OLD} - -# Filing cabinets -/obj/structure/filingcabinet : /obj/structure/filing_cabinet{@OLD} -/obj/structure/filingcabinet/chestdrawer : /obj/structure/filing_cabinet/chestdrawer{@OLD} -/obj/structure/filingcabinet/filingcabinet : /obj/structure/filing_cabinet/tall{@OLD} -/obj/structure/filingcabinet/medical : /obj/structure/filing_cabinet/records/medical{@OLD} -/obj/structure/filingcabinet/security : /obj/structure/filing_cabinet/records{@OLD} - -# Machine frames -/obj/structure/frame: /obj/machinery/constructable_frame/machine_frame{@OLD} -/obj/machinery/constructable_frame/machine_frame{anchored = 1} : /obj/machinery/constructable_frame/machine_frame/deconstruct{anchored = @SKIP} -/obj/structure/frame/computer : /obj/machinery/constructable_frame/computerframe{@OLD} -/obj/machinery/constructable_frame/computerframe{anchored = 1} : /obj/machinery/constructable_frame/computerframe/deconstruct{anchored = @SKIP} - -# Hygiene/bathroom objects -/obj/structure/toilet : /obj/structure/hygiene/toilet{@OLD} -/obj/structure/sink/@SUBTYPES : /obj/structure/hygiene/sink/@SUBTYPES{@OLD} -/obj/machinery/shower : /obj/structure/hygiene/shower{@OLD} - -# Turbolift spawners -/obj/turbolift_map_holder/@SUBTYPES : /obj/abstract/turbolift_spawner/@SUBTYPES{@OLD} - -# Borosilicate windows -/obj/structure/window/phoronreinforced/@SUBTYPES : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD} - -# Tools; see 3763_pickaxe_hammer and 3959_hatchets for other migrations -/obj/item/knife/machete : /obj/item/tool/machete{@OLD} -/obj/item/knife/machete/hatchet : /obj/item/tool/axe/hatchet{@OLD} -/obj/item/tool/pickaxe/hand : /obj/item/tool/pickaxe/xeno/hand{@OLD} - -# RANDOM SPAWNERS -/obj/random/cigarettes : /obj/random/smokes{@OLD} -/obj/item/lipstick/random : /obj/random/lipstick -/obj/random/slimecore : /obj/item/slime_extract/random{@OLD} - -# MINING EQUIPMENT -/obj/machinery/mineral/processing_unit : /obj/machinery/material_processing/smeltery -/obj/machinery/mineral/stacking_machine : /obj/machinery/material_processing/stacker -/obj/machinery/mineral/unloading_machine : /obj/machinery/material_processing/unloader -/obj/machinery/mineral/output : @DELETE -/obj/machinery/mineral/input : @DELETE -/obj/machinery/mineral/mint : @DELETE -/obj/machinery/mineral/processing_unit_console : @DELETE - -# NEW PDAS (NO CARTRIDGES) -/obj/item/pda/@SUBTYPES : /obj/item/modular_computer/pda/@SUBTYPES{@OLD} -/obj/item/modular_computer/pda/captain : /obj/item/modular_computer/pda/heads/captain -/obj/item/storage/box/seccarts : @DELETE -/obj/item/cartridge/@SUBTYPES : @DELETE - -# MODULAR COMPUTER PRESETS -/obj/machinery/computer/card : /obj/machinery/computer/modular/preset/cardslot/personnel{@OLD} -/obj/machinery/computer/secure_data : /obj/machinery/computer/modular/preset/civilian{@OLD} -/obj/machinery/computer/skills : /obj/machinery/computer/modular/preset/civilian{@OLD} -/obj/machinery/computer/security/telescreen : /obj/machinery/computer/modular/telescreen/preset/security{@OLD} -/obj/machinery/computer/security/telescreen/entertainment : /obj/machinery/computer/modular/telescreen/preset/entertainment{@OLD} -/obj/machinery/computer/security/mining : /obj/machinery/computer/modular/preset/security{@OLD} -/obj/machinery/computer/security/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES -/obj/machinery/computer/crew : /obj/machinery/computer/modular/preset/medical{@OLD} -/obj/machinery/computer/rcon : /obj/machinery/computer/modular/preset/engineering/rcon{@OLD} -/obj/machinery/computer/communications : /obj/machinery/computer/modular/preset/cardslot/command{@OLD} -/obj/machinery/computer/med_data : /obj/machinery/computer/modular/preset/civilian{@OLD} -/obj/machinery/computer/med_data/laptop : /obj/item/modular_computer/laptop/preset/records{@OLD} - -# ATMOS REPATHS -/obj/machinery/atmospherics/pipe/tank/@SUBTYPES : /obj/machinery/atmospherics/unary/tank/@SUBTYPES -/obj/machinery/portable_atmospherics/canister/nitrous_oxide : /obj/machinery/portable_atmospherics/canister/sleeping_agent{@OLD} - -# MISC. ITEMS -/obj/item/moneybag/vault : /obj/item/bag/cash/filled{@OLD} -/obj/item/moneybag : /obj/item/bag/cash{@OLD} -/obj/item/chems/bucket : /obj/item/chems/glass/bucket{@OLD} -/obj/item/card/id/gold/captain/spare : /obj/item/card/id/captains_spare{@OLD} -/obj/item/chainofcommand : /obj/item/whip/chainofcommand{@OLD} -/obj/item/chems/drinks/bottle/specialwhiskey : /obj/item/chems/drinks/bottle/agedwhiskey{@OLD} - -# Tape is weird -/obj/item/taperoll/@SUBTYPES : /obj/item/stack/tape_roll/barricade_tape/@SUBTYPES{@OLD} -/obj/item/tape/@SUBTYPES : /obj/structure/tape_barricade/@SUBTYPES{@OLD} - -# Radio -/obj/item/radio/headset/headset_sec/alt : /obj/item/radio/headset/headset_sec/bowman{@OLD} -/obj/item/radio/headset/syndicate : /obj/item/radio/headset/hacked{@OLD} -/obj/item/radio/headset/syndicate/alt : /obj/item/radio/headset/hacked{@OLD} -/obj/item/phone : /obj/item/radio/phone{@OLD} - -# Singulo -/obj/machinery/the_singularitygen : /obj/machinery/singularity_generator{@OLD} - -# FABRICATORS -/obj/machinery/autolathe{hacked = 1} : /obj/machinery/fabricator/hacked{@OLD; hacked = @SKIP} -/obj/machinery/autolathe : /obj/machinery/fabricator{@OLD} -/obj/machinery/pipedispenser : /obj/machinery/fabricator/pipe{@OLD; anchored = 1} -/obj/machinery/pipedispenser/orderable : /obj/machinery/fabricator/pipe{@OLD} -/obj/machinery/pipedispenser/disposal : /obj/machinery/fabricator/pipe/disposal{@OLD; anchored = 1} -/obj/machinery/pipedispenser/disposal/orderable : /obj/machinery/fabricator/pipe/disposal{@OLD} -/obj/machinery/r_n_d/circuit_imprinter : /obj/machinery/fabricator/imprinter{@OLD} -/obj/machinery/pros_fabricator : /obj/machinery/fabricator/robotics{@OLD} -/obj/machinery/mecha_part_fabricator : /obj/machinery/fabricator/industrial{@OLD} -/obj/machinery/r_n_d/protolathe : /obj/machinery/fabricator/protolathe{@OLD} -/obj/machinery/bookbinder : /obj/machinery/fabricator/book{@OLD} -/obj/machinery/organ_printer/flesh : /obj/machinery/fabricator/bioprinter{@OLD} -/obj/machinery/vending/food : /obj/machinery/fabricator/replicator - -/obj/machinery/fabricator/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD; res_max_amount = @SKIP; speed = @SKIP} - -# MMIs and posibrains -/obj/item/mmi/digital/@SUBTYPES : /obj/item/organ/internal/brain/robotic{@OLD} -/obj/item/mmi/@SUBTYPES : /obj/item/organ/internal/brain_interface/@SUBTYPES{@OLD} -/obj/item/organ/internal/posibrain : /obj/item/organ/internal/brain/robotic/positronic{@OLD} - -# Energy blades -/obj/item/energy/@SUBTYPES : /obj/item/energy_blade/@SUBTYPES{@OLD} - -# Medals and other accessories; see 3902_ties.txt for further migrations -/obj/item/clothing/accessory/medal/bronze_heart : /obj/item/clothing/accessory/medal/nanotrasen/bronze{@OLD} -/obj/item/clothing/accessory/medal/nobel_science : /obj/item/clothing/medal/nanotrasen/silver{@OLD} -/obj/item/clothing/accessory/scarf/white : /obj/item/clothing/accessory/scarf{@OLD} -/obj/item/clothing/accessory/storage/webbing : /obj/item/clothing/accessory/webbing/vest - -# MATERIALS -/obj/item/stack/material/plastic : /obj/item/stack/material/panel/mapped/plastic{@OLD} -/obj/item/stack/material/sandstone : /obj/item/stack/material/brick/mapped/sandstone{@OLD} -/obj/item/stack/material/mhydrogen : /obj/item/stack/material/segment/mapped/mhydrogen{@OLD} -/obj/item/stack/material/diamond : /obj/item/stack/material/gemstone/mapped/diamond{@OLD} -/obj/item/stack/material/wood : /obj/item/stack/material/plank/mapped/wood{@OLD} -/obj/item/stack/material/steel : /obj/item/stack/material/sheet/mapped/steel{@OLD} -/obj/item/stack/material/glass : /obj/item/stack/material/pane/mapped/glass{@OLD} -/obj/item/stack/material/phoron : /obj/item/stack/material/crystal/mapped/phoron{@OLD} -/obj/item/stack/material/algae : /obj/item/stack/material/puck/mapped/algae{@OLD} -/obj/item/stack/material/gold : /obj/item/stack/material/ingot/mapped/gold{@OLD} -/obj/item/stack/material/plasteel : /obj/item/stack/material/reinforced/mapped/plasteel{@OLD} -/obj/item/stack/material/glass/reinforced : /obj/item/stack/material/pane/mapped/rglass{@OLD} -/obj/item/stack/material/glass/phoronglass : /obj/item/stack/material/pane/mapped/borosilicate{@OLD} -/obj/item/stack/material/glass/phoronrglass : /obj/item/stack/material/pane/mapped/rborosilicate{@OLD} -/obj/item/stack/rods : /obj/item/stack/material/rods{@OLD} -/obj/item/packageWrap{amount = @UNSET} : /obj/item/stack/package_wrap/twenty_five{@OLD; amount = @SKIP} -/obj/item/packageWrap : /obj/item/stack/package_wrap{@OLD} -/obj/item/wrapping_paper{amount = @UNSET} : /obj/item/stack/package_wrap/gift/twenty_five{@OLD; amount = @SKIP} -/obj/item/wrapping_paper : /obj/item/stack/package_wrap{@OLD} - -/obj/structure/dispenser{phorontanks = 0} : /obj/structure/tank_rack/oxygen{@OLD; phorontanks = @SKIP} -/obj/structure/dispenser/oxygen{oxygentanks = 0} : /obj/structure/tank_rack/empty{@OLD; oxygentanks = @SKIP} -/obj/structure/dispenser/oxygen : /obj/structure/tank_rack/oxygen -/obj/structure/dispenser : /obj/structure/tank_rack - -# TELECOMMS REMOVAL -/obj/item/stock_parts/circuitboard/telecomms/@SUBTYPES : @DELETE -/obj/machinery/telecomms/@SUBTYPES : @DELETE -/obj/machinery/computer/telecomms/@SUBTYPES : @DELETE - -/obj/machinery/r_n_d/server/@SUBTYPES : /obj/machinery/design_database -/obj/machinery/r_n_d/destructive_analyzer : /obj/machinery/destructive_analyzer - -# SMES bullshit -/obj/machinery/power/smes/buildable/engine : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 1)} -/obj/machinery/power/smes/buildable/main : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 4)} -/obj/machinery/power/smes/buildable/point_of_interest : /obj/machinery/power/smes/buildable/preset/hidden - -/obj/item/surgical/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} -/obj/item/FixOVein : /obj/item/sutures{@OLD} - -/obj/structure/flora/skeleton : /obj/structure/skele_stand{@OLD} - -/obj/mecha/working/ripley/firefighter : /mob/living/exosuit/premade/firefighter -/obj/mecha/working/ripley/mining : /mob/living/exosuit/premade/powerloader - -/obj/structure/AIcore/@SUBTYPES : /obj/structure/aicore/@SUBTYPES -/obj/item/tank/vox : /obj/item/tank/nitrogen{@OLD} - -/obj/machinery/shuttle_sensor : /obj/machinery/airlock_sensor/shuttle{@OLD} - -/obj/machinery/computer/rdconsole/@SUBTYPES : /obj/machinery/computer/design_console{@OLD} -/obj/machinery/computer/rdservercontrol : /obj/machinery/computer/design_console{@OLD; name = @SKIP; badmin = @SKIP} -/obj/item/stock_parts/circuitboard/rdconsole : /obj/item/stock_parts/circuitboard/design_console{@OLD} -/obj/item/stock_parts/circuitboard/rdserver : /obj/item/stock_parts/circuitboard/design_database{@OLD} - -/obj/machinery/account_database : /obj/machinery/computer/account_database{@OLD} -/obj/machinery/shield_gen/@SUBTYPES : /obj/machinery/shield_generator - -/obj/structure/morgue/crematorium : /obj/structure/crematorium{@OLD} - -/obj/item/robot_parts/frame : /obj/item/robot_parts/robot_suit{@OLD} - -/obj/structure/closet/secure_closet/psych : /obj/structure/closet/secure_closet/psychiatry{@OLD} -/obj/structure/closet/firecloset/full : /obj/structure/closet/firecloset{@OLD} -/obj/structure/closet/firecloset/full/double : /obj/structure/closet/firecloset/chief{@OLD} -/obj/structure/closet/l3closet/scientist/double : /obj/structure/closet/l3closet/scientist/multi{@OLD} -/obj/structure/closet/secure_closet/hos2 : /obj/structure/closet/secure_closet/hos{@OLD} -/obj/structure/closet/secure_closet/medical_wall/synth_anesthetics : /obj/structure/closet/secure_closet/medical_wall/anesthetics/robotics{@OLD} -/obj/structure/closet/secure_closet/medical2 : /obj/structure/closet/secure_closet/anesthetics{@OLD} -/obj/structure/closet/walllocker/emerglocker/@SUBTYPES : /obj/structure/emergency_dispenser/@SUBTYPES{@OLD} - -/obj/item/firstaid_arm_assembly : /obj/item/firstaid/empty, /obj/item/robot_parts/l_arm -/obj/item/bucket_sensor : /obj/item/chems/glass/bucket, /obj/item/assembly/prox_sensor -/obj/item/broken_device : @DELETE - -/obj/item/spacecash/@SUBTYPES : /obj/item/cash/@SUBTYPES{@OLD} - -/obj/item/kit/paint/ripley : /obj/item/kit/paint/camouflage -/obj/item/kit/paint/ripley/death : /obj/item/kit/paint/camouflage/forest -/obj/item/kit/paint/ripley/flames_blue : /obj/item/kit/paint/flames_blue -/obj/item/kit/paint/ripley/flames_red : /obj/item/kit/paint/flames_red - -/obj/machinery/photocopier/faxmachine : /obj/machinery/faxmachine{@OLD; init_network_tag = @OLD:department; department = @SKIP} -/obj/item/tvcamera : /obj/item/camera/tvcamera{@OLD} -/obj/machinery/station_map : /obj/machinery/holomap{@OLD} - -/obj/item/ducttape : /obj/item/duct_tape{@OLD} -/obj/item/toy/katana : /obj/item/sword/katana/toy{@OLD} -/obj/item/toy/cultsword : /obj/item/sword/cult_toy{@OLD} -/obj/item/toy/sword : /obj/item/energy_blade/sword/toy{@OLD} -/obj/item/lipstick/jade : /obj/item/cosmetics/lipstick/green{@OLD} -/obj/item/rig_module/device/plasmacutter : /obj/item/rig_module/mounted/plasmacutter{@OLD} -/obj/item/storage/box/syndie_kit/ewar_voice : /obj/item/backpack/satchel/syndie_kit/ewar_voice -/obj/item/tray : /obj/item/plate/tray{@OLD} -/obj/item/weldingtool/weldpack : /obj/item/chems/weldpack{@OLD} -/obj/machinery/crystal : /obj/structure/crystal{@OLD} -/obj/item/organ/internal/stack : /obj/item/organ/internal/voxstack{@OLD} -/obj/item/radio_jammer : /obj/item/suit_sensor_jammer - -# AMMO BOXES -/obj/item/storage/box/beanbags/@SUBTYPES : /obj/item/box/ammo/beanbags/@SUBTYPES{@OLD} -/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} -/obj/item/storage/box/blanks/@SUBTYPES : /obj/item/box/ammo/blanks/@SUBTYPES{@OLD} -/obj/item/storage/box/empshells/@SUBTYPES : /obj/item/box/ammo/empshells/@SUBTYPES{@OLD} -/obj/item/storage/box/flashshells/@SUBTYPES : /obj/item/box/ammo/flashshells/@SUBTYPES{@OLD} -/obj/item/storage/box/shotgunammo/@SUBTYPES : /obj/item/box/ammo/shotgunammo/@SUBTYPES{@OLD} -/obj/item/storage/box/shotgunshells/@SUBTYPES : /obj/item/box/ammo/shotgunshells/@SUBTYPES{@OLD} -/obj/item/storage/box/sniperammo/@SUBTYPES : /obj/item/box/ammo/sniperammo/@SUBTYPES{@OLD} -/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} - -/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} -/obj/item/camera_assembly : /obj/item/frame/camera/kit -/obj/structure/ladder/up : /obj/structure/ladder -/obj/random/mob/mouse : /obj/random/mouse - -/obj/structure/closet/grave/dirthole : /obj/structure/pit{@OLD} -/obj/structure/closet/grave/@SUBTYPES : /obj/structure/pit/closed/grave{@OLD} - -/obj/item/banner/nt : /obj/item/banner/nanotrasen{@OLD} - -/obj/item/box/syndie_kit/clerical : /obj/item/backpack/satchel/syndie_kit/clerical{@OLD} - -/obj/item/clothing/under/costume/rainbow : /obj/item/clothing/jumpsuit/rainbow{@OLD} -/obj/item/clothing/under/dress/dress_orange : /obj/item/clothing/dress/orange{@OLD} -/obj/item/clothing/head/collectable/xenos : /obj/item/clothing/head/xenos{@OLD} -/obj/item/clothing/head/collectable/bearpelt : /obj/item/clothing/head/bearpelt{@OLD} -/obj/item/clothing/head/collectable/philosopher_wig : /obj/item/clothing/head/philosopher_wig{@OLD} -/obj/item/clothing/head/collectable/plaguedoctorhat : /obj/item/clothing/head/plaguedoctorhat{@OLD} -/obj/item/clothing/head/helmet/space/void/security/riot : /obj/item/clothing/head/helmet/space/void/security/alt{@OLD} -/obj/item/clothing/suit/space/void/security/riot : /obj/item/clothing/suit/space/void/security/alt{@OLD} -/obj/item/clothing/head/helmet/space/vox/pressure : /obj/item/clothing/head/helmet/space/vox{@OLD} -/obj/item/clothing/under/vox/vox_utility : /obj/item/clothing/pants/vox{@OLD} -/obj/item/clothing/suit/space/pirate : /obj/item/clothing/suit/pirate{@OLD} -/obj/item/clothing/suit/costume : /obj/item/clothing/suit/pirate{@OLD} -/obj/item/clothing/suit/costume/hgpirate : /obj/item/clothing/suit/hgpirate{@OLD} -/obj/item/clothing/suit/storage/vest/heavy/merc : /obj/item/clothing/suit/armor/pcarrier/merc{@OLD} -/obj/item/clothing/suit/storage/vest/@SUBTYPES : /obj/item/clothing/suit/armor/vest/@SUBTYPES{@OLD} - -# Migrate/delete old mech parts -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster : @DELETE -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster : @DELETE -/obj/item/mecha_parts/mecha_equipment/repair_droid : @DELETE -/obj/item/mecha_parts/mecha_equipment/teleporter : @DELETE -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay : @DELETE -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer : @DELETE -/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill : /obj/item/mech_equipment/drill/diamond{@OLD} -/obj/item/mecha_parts/mecha_equipment/tool/extinguisher : /obj/item/mech_equipment/mounted_system/extinguisher{@OLD} -/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp : /obj/item/mech_equipment/clamp{@OLD} -/obj/item/mecha_parts/mecha_equipment/tool/passenger : @DELETE -/obj/item/mecha_parts/mecha_equipment/tool/rcd : /obj/item/mech_equipment/mounted_system/rcd{@OLD} -/obj/item/mecha_parts/mecha_equipment/tool/sleeper : /obj/item/mech_equipment/sleeper{@OLD} -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun : @DELETE -/obj/item/mecha_parts/mecha_equipment/weapon/energy/ion : /obj/item/mech_equipment/mounted_system/taser/ion{@OLD} -/obj/item/mecha_parts/mecha_equipment/weapon/energy/taser : /obj/item/mech_equipment/mounted_system/taser{@OLD} -/obj/item/mecha_parts/mecha_tracking : @DELETE - -############ -# VAREDITS # -############ -# Atmos varedits -/obj/machinery/computer/air_control/@SUBTYPES{sensors = @SET} : /obj/machinery/computer/air_control/@SUBTYPES{@OLD; sensors = @SKIP} -# move these to the external air subtype so frequencies work -/obj/machinery/atmospherics/unary/vent_pump/high_volume{frequency = 1379} : /obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{@OLD; frequency = @SKIP} -/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrub_id = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} -/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrubbing_gas = list("phoron")} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrubbing_gas = list(/decl/material/solid/phoron)} -/obj/machinery/computer/area_atmos/tag/@SUBTYPES{scrub_id = @SET} : /obj/machinery/computer/area_atmos/tag/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} -# for the rest of this, frequency was moved to component presets and i'm too lazy to set that up -/obj/machinery/door/airlock/@SUBTYPES{frequency = @SET} : /obj/machinery/door/airlock/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/portable_atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/airlock_sensor/@SUBTYPES{frequency = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/air_sensor/@SUBTYPES : /obj/machinery/air_sensor/@SUBTYPES{@OLD; frequency = @SKIP; output = @SKIP} -/obj/machinery/embedded_controller/radio/@SUBTYPES{frequency = @SET} : /obj/machinery/embedded_controller/radio/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/button/@SUBTYPES{frequency = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; frequency = @SKIP} -/obj/machinery/meter/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; frequency = @SKIP} - -# id -> id_tag -/obj/machinery/door_timer/@SUBTYPES{id = @SET} : /obj/machinery/door_timer/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/conveyor/@SUBTYPES{id = @SET} : /obj/machinery/conveyor/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/conveyor_switch/@SUBTYPES{id = @SET} : /obj/machinery/conveyor_switch/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/door/@SUBTYPES{id = @SET} : /obj/machinery/door/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/button/@SUBTYPES{id = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/atmospherics/@SUBTYPES{id = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/holosign/bar{id = @SET} : /obj/machinery/holosign/bar{@OLD; id_tag = @OLD:id; id = @SKIP} -/obj/machinery/meter/@SUBTYPES{id = @SET} : /obj/machinery/meter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/flasher/@SUBTYPES{id = @SET} : /obj/machinery/flasher/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/sparker/@SUBTYPES{id = @SET} : /obj/machinery/sparker/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/mass_driver/@SUBTYPES{id = @SET} : /obj/machinery/mass_driver/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} -/obj/machinery/emitter/@SUBTYPES{id = @SET} : /obj/machinery/emitter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} - -# Camera preset_channels -> network -/obj/machinery/camera/network/@SUBTYPES{network = @SET} : /obj/machinery/camera/network/@SUBTYPES{@OLD; network = @SKIP; preset_channels = @OLD:network} - -# Remove button/sensor master_tag -/obj/machinery/airlock_sensor/@SUBTYPES{master_tag = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; master_tag = @SKIP} -/obj/machinery/button/access/@SUBTYPES{master_tag = @SET} : /obj/machinery/button/access/@SUBTYPES{@OLD; master_tag = @SKIP} - -# Skip power sensor name_tag -/obj/machinery/power/sensor{name_tag = @SET} : /obj/machinery/power/sensor{@OLD; name_tag = @SKIP} - -# join_group -> unique_merge_identifier -/turf/wall/shuttle/@SUBTYPES{join_group = @SET} : /turf/wall/shuttle/@SUBTYPES{@OLD; unique_merge_identifier = @OLD:join_group} - -# Remove set icon states/density on windows, doors, shutters -/obj/machinery/door/blast/regular{icon_state = @SET} : /obj/machinery/door/blast/regular{@OLD; icon_state = @SKIP} -/obj/structure/window/borosilicate_reinforced/@SUBTYPES{icon_state = @SET} : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD; icon_state = @SKIP} -/obj/machinery/door/blast/regular{p_open = 1} : /obj/machinery/door/blast/regular/open{@OLD; p_open = @SKIP} -/obj/machinery/door/blast/regular{p_open = 0} : /obj/machinery/door/blast/regular{@OLD; p_open = @SKIP} -/obj/machinery/door/blast/regular{density = 0} : /obj/machinery/door/blast/regular/open{@OLD; density = @SKIP; opacity = @SKIP} -/obj/structure/window/@SUBTYPES{icon_state = "fwindow"} : /obj/structure/window/@SUBTYPES{@OLD; icon_state = @SKIP} -/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} -/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} - -# Prices -> markup -/obj/machinery/vending/@SUBTYPES{prices = list()} : /obj/machinery/vending/@SUBTYPES{@OLD; markup = 0; prices=@SKIP} -/obj/machinery/vending/@SUBTYPES{prices = @SET} : /obj/machinery/vending/@SUBTYPES{@OLD; prices = @SKIP} - -# Radio varedits -/obj/item/radio/@SUBTYPES{subspace_transmission = @SET} : @OLD {@OLD; subspace_transmission = @SKIP} -/obj/item/radio/@SUBTYPES{syndie = @SET} : @OLD {@OLD; decrypt_all_messages = 1; syndie = @SKIP} -/obj/item/radio/@SUBTYPES{adhoc_fallback = @SET} : @OLD {@OLD; can_use_analog = @SKIP} - -# Empty first-aid kits -/obj/item/storage/firstaid/regular{empty = 1} : /obj/item/firstaid/empty{@OLD; empty = @SKIP; name = @SKIP} - -/obj/machinery/floor_light/prebuilt{on = 1} : /obj/machinery/floor_light/prebuilt{@OLD; use_power = 2; on = @SKIP} - -/obj/structure/disposalpipe/sortjunction/@SUBTYPES{sortType = @SET} : /obj/structure/disposalpipe/sortjunction/@SUBTYPES{@OLD; sort_type = @OLD:sortType; sortType = @SKIP} \ No newline at end of file +/obj/item/reagent_containers/@SUBTYPES : /obj/item/chems/@SUBTYPES{@OLD} +/obj/item/material/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/weapon/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/melee/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/packageWrap/@SUBTYPES : /obj/item/stack/package_wrap/@SUBTYPES{@OLD} +/obj/structure/table/rack/@SUBTYPES : /obj/structure/rack/@SUBTYPES{@OLD} +/obj/structure/table/woodentable/holotable : /obj/structure/table/holo_woodentable{@OLD} +/obj/item/chems/food/drinks/@SUBTYPES : /obj/item/chems/drinks/@SUBTYPES{@OLD} +/obj/item/chems/food/snacks/@SUBTYPES : /obj/item/food/@SUBTYPES{@OLD} +/obj/item/chems/food/condiment/@SUBTYPES : /obj/item/chems/condiment/@SUBTYPES{@OLD} +/obj/item/chems/condiment/peppermill/@SUBTYPES : /obj/item/chems/condiment/small/peppermill/@SUBTYPES{@OLD} +/obj/item/chems/condiment/saltshaker/@SUBTYPES : /obj/item/chems/condiment/small/saltshaker/@SUBTYPES{@OLD} +/obj/item/analyzer : /obj/item/scanner/gas{@OLD} +/obj/item/analyzer/plant_analyzer/@SUBTYPES : /obj/item/scanner/plant/@SUBTYPES{@OLD} +/obj/item/tape_roll/@SUBTYPES : /obj/item/stack/tape_roll/@SUBTYPES{@OLD} +/obj/structure/table/standard/@SUBTYPES : /obj/structure/table/@SUBTYPES{@OLD} +/obj/machinery/bluespacerelay/@SUBTYPES : /obj/machinery/commsrelay/@SUBTYPES{@OLD} +/obj/machinery/computer/helm/@SUBTYPES : /obj/machinery/computer/ship/helm/@SUBTYPES{@OLD} +/obj/machinery/computer/engines/@SUBTYPES : /obj/machinery/computer/ship/engines/@SUBTYPES{@OLD} +/obj/machinery/computer/navigation/@SUBTYPES : /obj/machinery/computer/ship/navigation/@SUBTYPES{@OLD} +/obj/machinery/requests_console/@SUBTYPES : /obj/machinery/network/requests_console/@SUBTYPES{@OLD; departmentType = @SKIP} +/obj/item/healthanalyzer/@SUBTYPES : /obj/item/scanner/health/@SUBTYPES{@OLD} +/obj/machinery/iv_drip/@SUBTYPES : /obj/structure/iv_drip/@SUBTYPES{@OLD} +/obj/structure/device/piano/@SUBTYPES : /obj/structure/synthesized_instrument/synthesizer/piano/@SUBTYPES{@OLD} +/obj/item/folder/white/@SUBTYPES : /obj/item/folder/@SUBTYPES{@OLD} +/obj/machinery/dnaforensics/@SUBTYPES : /obj/machinery/forensic/dnascanner/@SUBTYPES{@OLD} +/obj/machinery/microscope/@SUBTYPES : /obj/machinery/forensic/microscope/@SUBTYPES{@OLD} +/obj/item/stack/material/steel/@SUBTYPES : /obj/item/stack/material/sheet/mapped/steel/@SUBTYPES{@OLD} +/obj/item/stack/material/plasteel/@SUBTYPES : /obj/item/stack/material/sheet/reinforced/mapped/plasteel/@SUBTYPES{@OLD} +/obj/item/stack/material/glass/reinforced/@SUBTYPES : /obj/item/stack/material/pane/mapped/rglass/@SUBTYPES{@OLD} +/obj/item/stack/material/glass/@SUBTYPES : /obj/item/stack/material/pane/mapped/glass/@SUBTYPES{@OLD} +/obj/item/stack/material/wood/@SUBTYPES : /obj/item/stack/material/plank/mapped/wood/@SUBTYPES{@OLD} +/obj/item/stack/material/deuterium/@SUBTYPES : /obj/item/stack/material/aerogel/mapped/deuterium/@SUBTYPES{@OLD} +/obj/item/stack/material/uranium/@SUBTYPES : /obj/item/stack/material/puck/mapped/uranium/@SUBTYPES{@OLD} +/obj/machinery/autolathe/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD} +/obj/machinery/message_server/@SUBTYPES : /obj/machinery/network/message_server/@SUBTYPES{@OLD} +/obj/item/floor_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} +/obj/machinery/pipedispenser/@SUBTYPES : /obj/machinery/fabricator/pipe/@SUBTYPES{@OLD} +/obj/machinery/power/emitter/@SUBTYPES : /obj/machinery/emitter/@SUBTYPES{@OLD} +/obj/machinery/power/fusion_core/@SUBTYPES : /obj/machinery/fusion_core/@SUBTYPES{@OLD} +/obj/item/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} +/obj/machinery/modular_computer/console/preset/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES{@OLD} +/obj/machinery/computer/modular/preset/command/@SUBTYPES : /obj/machinery/computer/modular/preset/cardslot/command/@SUBTYPES{@OLD} +/obj/machinery/computer/fusion_core_control/@SUBTYPES : /obj/machinery/computer/fusion/core_control/@SUBTYPES{@OLD} +/obj/machinery/computer/fusion_fuel_control/@SUBTYPES : /obj/machinery/computer/fusion/fuel_control/@SUBTYPES{@OLD} +/obj/machinery/computer/gyrotron_control/@SUBTYPES : /obj/machinery/computer/fusion/gyrotron/@SUBTYPES{@OLD} +/obj/machinery/computer/power_monitor/@SUBTYPES : /obj/machinery/computer/modular/preset/engineering/power/@SUBTYPES{@OLD} +/obj/item/module/power_control/@SUBTYPES : /obj/item/stock_parts/circuitboard/apc/@SUBTYPES{@OLD} +/obj/machinery/power/port_gen/@SUBTYPES : /obj/machinery/port_gen/@SUBTYPES{@OLD} +/obj/item/pipe_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} +/obj/item/airlock_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/airlock_electronics/@SUBTYPES{@OLD} +/obj/item/airalarm_electronics/@SUBTYPES : /obj/item/stock_parts/circuitboard/air_alarm/@SUBTYPES{@OLD} +/obj/item/autopsy_scanner/@SUBTYPES : /obj/item/scanner/autopsy/@SUBTYPES{@OLD} +/obj/machinery/fusion_fuel_compressor/@SUBTYPES : /obj/machinery/fuel_compressor/@SUBTYPES{@OLD} +/obj/item/extinguisher/micro/@SUBTYPES : /obj/item/chems/spray/extinguisher/mini/@SUBTYPES{@OLD} +/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} +/obj/item/stack/material/plastic/@SUBTYPES : /obj/item/stack/material/panel/mapped/plastic/@SUBTYPES{@OLD} +/obj/machinery/computer/general_air_control/@SUBTYPES : /obj/machinery/computer/air_control/@SUBTYPES{@OLD} +/obj/machinery/computer/air_control/large_tank_control : /obj/machinery/computer/air_control{@OLD} +/turf/simulated/floor/tiled/steel/@SUBTYPES : /turf/simulated/floor/tiled/@SUBTYPES{@OLD} +/obj/machinery/power/engine/@SUBTYPES : /obj/machinery/ion_thruster/@SUBTYPES{@OLD} +/obj/machinery/computer/aifixer/@SUBTYPES : /obj/machinery/computer/modular/preset/aislot/sysadmin/@SUBTYPES{@OLD} +/obj/machinery/computer/aiupload/@SUBTYPES : /obj/machinery/computer/upload/ai/@SUBTYPES{@OLD} +/obj/machinery/computer/borgupload/@SUBTYPES : /obj/machinery/computer/upload/robot/@SUBTYPES{@OLD} +/obj/machinery/computer/supplycomp/control/@SUBTYPES : /obj/machinery/computer/modular/preset/dock/@SUBTYPES{@OLD} +/obj/machinery/computer/supplycomp/@SUBTYPES : /obj/machinery/computer/modular/preset/supply_public/@SUBTYPES{@OLD} +/obj/item/closet_painter/@SUBTYPES : /obj/item/paint_sprayer/@SUBTYPES{@OLD} +/turf/simulated/shuttle/plating/@SUBTYPES : /turf/floor/shuttle +/turf/simulated/shuttle/floor/@SUBTYPES : /turf/floor/shuttle/@SUBTYPES + +# REMOVE BAD VAREDITS +/@SUBTYPES {layer = @SET} : @OLD {@OLD; layer = @SKIP} +/@SUBTYPES {plane = @SET} : @OLD {@OLD; plane = @SKIP} +/obj/structure/cable/@SUBTYPES : @OLD {@OLD; d1 = @SKIP;d2 = @SKIP} +# unacidable removed +/@SUBTYPES {unacidable = @SET} : @OLD{@OLD; unacidable = @SKIP} +# Simple pipes shall not face north or west +/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} +/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 1} : @OLD {@OLD; dir = 2} +/obj/machinery/atmospherics/pipe/simple/hidden@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} +/obj/machinery/atmospherics/pipe/simple/visible@SUBTYPES {dir = 8} : @OLD {@OLD; dir = 4} +# Do not change air alarm frequency on base type +/obj/machinery/alarm {frequency = @SET} : @OLD {@OLD; frequency = @SKIP} +# we use a completely different access format +/@SUBTYPES {req_access = @SET}: @OLD {@OLD; req_access = @SKIP;} +/@SUBTYPES {req_one_access = @SET}: @OLD {@OLD; req_one_access = @SKIP;} +/obj/machinery/door/blast/shutters{icon_state = "shutter0"} : /obj/machinery/door/blast/shutters{@OLD; icon_state = @SKIP} +/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1; density = 0, opacity = 0} : /obj/machinery/door/airlock/external/glass/bolted_open{@OLD; icon_state = @SKIP; locked = @SKIP, density = @SKIP; opacity = @SKIP} +/obj/machinery/door/airlock/external/glass{icon_state = @SET; locked = 1} : /obj/machinery/door/airlock/external/glass/bolted{@OLD; icon_state = @SKIP; locked = @SKIP} + +# BUTTONS +/obj/machinery/access_button/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} +/obj/machinery/access_button/airlock_interior : /obj/machinery/button/access/interior{@OLD} +/obj/machinery/airlock_sensor/airlock_exterior : /obj/machinery/button/access/exterior{@OLD} +/obj/machinery/airlock_sensor/airlock_interior : /obj/machinery/button/access/interior{@OLD} +/obj/machinery/access_button : /obj/machinery/button/access{@OLD} +/obj/machinery/button/remote/airlock{specialfunctions = 4} : /obj/machinery/button/alternate/door/bolts{@OLD; specialfunctions = @SKIP; desiredstate = @SKIP} +/obj/machinery/button/remote/airlock : /obj/machinery/button/alternate/door{@OLD} +/obj/machinery/button/remote/driver : /obj/machinery/button/mass_driver{@OLD} +/obj/machinery/button/remote/blast_door : /obj/machinery/button/blast_door{@OLD} +/obj/machinery/button/remote : /obj/machinery/button/alternate{@OLD} + +# Medical +/obj/item/pill_bottle/tramadol : /obj/item/pill_bottle/strong_painkillers{@OLD} +/obj/item/chems/pill/tramadol : /obj/item/chems/pill/strong_painkillers{@OLD} +/obj/item/chems/glass/bottle/stoxin : /obj/item/chems/glass/bottle/sedatives{@OLD} +/obj/item/chems/glass/bottle/inaprovaline : /obj/item/chems/glass/bottle/stabilizer{@OLD} +/obj/item/chems/syringe/inaprovaline : /obj/item/chems/syringe/stabilizer{@OLD} +/obj/item/chems/blood/empty : /obj/item/chems/ivbag{@OLD} +/obj/item/chems/blood/@SUBTYPES : /obj/item/chems/ivbag/blood/@SUBTYPES{@OLD} +/obj/item/defib_kit/@SUBTYPES : /obj/item/defibrillator/@SUBTYPES{@OLD} +/obj/item/chems/spray/sterilizine : /obj/item/chems/spray/antiseptic{@OLD} + +# Drinking glasses and cups +/obj/item/chems/drinks/cup : /obj/item/chems/drinks/glass2/coffeecup{@OLD} +/obj/item/chems/drinks/drinkingglass : /obj/item/chems/drinks/glass2/square{@OLD} + +# Airtight flaps +/obj/structure/plasticflaps/mining : /obj/structure/flaps/airtight{@OLD} + +# Bushes +/obj/structure/flora/ausbushes/@SUBTYPES : /obj/structure/flora/bush/@SUBTYPES + +# Cassette tapes -> magnetic tapes +/obj/item/cassette_tape/@SUBTYPES : /obj/item/magnetic_tape/@SUBTYPES{@OLD} +/obj/item/tape_recorder : /obj/item/taperecorder{@OLD} + +# FIFTYSPAWNER -> MAPPED SHEETS +/obj/fiftyspawner/wood : /obj/item/stack/material/plank/mapped/wood/fifty +/obj/fiftyspawner/steel : /obj/item/stack/material/sheet/mapped/steel/fifty +/obj/fiftyspawner/glass : /obj/item/stack/material/pane/mapped/glass/fifty +/obj/fiftyspawner/deuterium : /obj/item/stack/material/aerogel/mapped/deuterium/fifty +/obj/fiftyspawner/tritium : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/osmium : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/phoron : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/rods : /obj/item/stack/material/aerogel/mapped/tritium/fifty +/obj/fiftyspawner/grass : /obj/item/stack/tile/grass{amount = 50} +/obj/fiftyspawner/plasteel : /obj/item/stack/material/aerogel/mapped/tritium/fifty + +# STOCK PARTS AND SMES COILS +/obj/item/stock_parts/subspace/sub_filter : /obj/item/stock_parts/subspace/filter +/obj/item/smes_coil/@SUBTYPES : /obj/item/stock_parts/smes_coil/@SUBTYPES{@OLD} +/obj/item/circuitboard/@SUBTYPES : /obj/item/stock_parts/circuitboard/@SUBTYPES{@OLD} +/obj/item/airlock_electronics : /obj/item/stock_parts/circuitboard/airlock_electronics{@OLD} + +# CLOTHING +/obj/item/clothing/head/cone : /obj/item/caution/cone{@OLD} +/obj/item/clothing/belt/security/tactical : /obj/item/belt/holster/security/tactical{@OLD} + +# Item remains +/obj/effect/decal/remains/@SUBTYPES : /obj/item/remains/@SUBTYPES{@OLD} + +# Circuit prefabs +/obj/item/hand_tele : /obj/prefab/hand_teleporter{@OLD} + +# Filing cabinets +/obj/structure/filingcabinet : /obj/structure/filing_cabinet{@OLD} +/obj/structure/filingcabinet/chestdrawer : /obj/structure/filing_cabinet/chestdrawer{@OLD} +/obj/structure/filingcabinet/filingcabinet : /obj/structure/filing_cabinet/tall{@OLD} +/obj/structure/filingcabinet/medical : /obj/structure/filing_cabinet/records/medical{@OLD} +/obj/structure/filingcabinet/security : /obj/structure/filing_cabinet/records{@OLD} + +# Machine frames +/obj/structure/frame: /obj/machinery/constructable_frame/machine_frame{@OLD} +/obj/machinery/constructable_frame/machine_frame{anchored = 1} : /obj/machinery/constructable_frame/machine_frame/deconstruct{anchored = @SKIP} +/obj/structure/frame/computer : /obj/machinery/constructable_frame/computerframe{@OLD} +/obj/machinery/constructable_frame/computerframe{anchored = 1} : /obj/machinery/constructable_frame/computerframe/deconstruct{anchored = @SKIP} + +# Hygiene/bathroom objects +/obj/structure/toilet : /obj/structure/hygiene/toilet{@OLD} +/obj/structure/sink/@SUBTYPES : /obj/structure/hygiene/sink/@SUBTYPES{@OLD} +/obj/machinery/shower : /obj/structure/hygiene/shower{@OLD} + +# Turbolift spawners +/obj/turbolift_map_holder/@SUBTYPES : /obj/abstract/turbolift_spawner/@SUBTYPES{@OLD} + +# Borosilicate windows +/obj/structure/window/phoronreinforced/@SUBTYPES : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD} + +# Tools; see 3763_pickaxe_hammer and 3959_hatchets for other migrations +/obj/item/knife/machete : /obj/item/tool/machete{@OLD} +/obj/item/knife/machete/hatchet : /obj/item/tool/axe/hatchet{@OLD} +/obj/item/tool/pickaxe/hand : /obj/item/tool/pickaxe/xeno/hand{@OLD} + +# RANDOM SPAWNERS +/obj/random/cigarettes : /obj/random/smokes{@OLD} +/obj/item/lipstick/random : /obj/random/lipstick +/obj/random/slimecore : /obj/item/slime_extract/random{@OLD} + +# MINING EQUIPMENT +/obj/machinery/mineral/processing_unit : /obj/machinery/material_processing/smeltery +/obj/machinery/mineral/stacking_machine : /obj/machinery/material_processing/stacker +/obj/machinery/mineral/unloading_machine : /obj/machinery/material_processing/unloader +/obj/machinery/mineral/output : @DELETE +/obj/machinery/mineral/input : @DELETE +/obj/machinery/mineral/mint : @DELETE +/obj/machinery/mineral/processing_unit_console : @DELETE + +# NEW PDAS (NO CARTRIDGES) +/obj/item/pda/@SUBTYPES : /obj/item/modular_computer/pda/@SUBTYPES{@OLD} +/obj/item/modular_computer/pda/captain : /obj/item/modular_computer/pda/heads/captain +/obj/item/storage/box/seccarts : @DELETE +/obj/item/cartridge/@SUBTYPES : @DELETE + +# MODULAR COMPUTER PRESETS +/obj/machinery/computer/card : /obj/machinery/computer/modular/preset/cardslot/personnel{@OLD} +/obj/machinery/computer/secure_data : /obj/machinery/computer/modular/preset/civilian{@OLD} +/obj/machinery/computer/skills : /obj/machinery/computer/modular/preset/civilian{@OLD} +/obj/machinery/computer/security/telescreen : /obj/machinery/computer/modular/telescreen/preset/security{@OLD} +/obj/machinery/computer/security/telescreen/entertainment : /obj/machinery/computer/modular/telescreen/preset/entertainment{@OLD} +/obj/machinery/computer/security/mining : /obj/machinery/computer/modular/preset/security{@OLD} +/obj/machinery/computer/security/@SUBTYPES : /obj/machinery/computer/modular/preset/@SUBTYPES +/obj/machinery/computer/crew : /obj/machinery/computer/modular/preset/medical{@OLD} +/obj/machinery/computer/rcon : /obj/machinery/computer/modular/preset/engineering/rcon{@OLD} +/obj/machinery/computer/communications : /obj/machinery/computer/modular/preset/cardslot/command{@OLD} +/obj/machinery/computer/med_data : /obj/machinery/computer/modular/preset/civilian{@OLD} +/obj/machinery/computer/med_data/laptop : /obj/item/modular_computer/laptop/preset/records{@OLD} + +# ATMOS REPATHS +/obj/machinery/atmospherics/pipe/tank/@SUBTYPES : /obj/machinery/atmospherics/unary/tank/@SUBTYPES +/obj/machinery/portable_atmospherics/canister/nitrous_oxide : /obj/machinery/portable_atmospherics/canister/sleeping_agent{@OLD} + +# MISC. ITEMS +/obj/item/moneybag/vault : /obj/item/bag/cash/filled{@OLD} +/obj/item/moneybag : /obj/item/bag/cash{@OLD} +/obj/item/chems/bucket : /obj/item/chems/glass/bucket{@OLD} +/obj/item/card/id/gold/captain/spare : /obj/item/card/id/captains_spare{@OLD} +/obj/item/chainofcommand : /obj/item/whip/chainofcommand{@OLD} +/obj/item/chems/drinks/bottle/specialwhiskey : /obj/item/chems/drinks/bottle/agedwhiskey{@OLD} + +# Tape is weird +/obj/item/taperoll/@SUBTYPES : /obj/item/stack/tape_roll/barricade_tape/@SUBTYPES{@OLD} +/obj/item/tape/@SUBTYPES : /obj/structure/tape_barricade/@SUBTYPES{@OLD} + +# Radio +/obj/item/radio/headset/headset_sec/alt : /obj/item/radio/headset/headset_sec/bowman{@OLD} +/obj/item/radio/headset/syndicate : /obj/item/radio/headset/hacked{@OLD} +/obj/item/radio/headset/syndicate/alt : /obj/item/radio/headset/hacked{@OLD} +/obj/item/phone : /obj/item/radio/phone{@OLD} + +# Singulo +/obj/machinery/the_singularitygen : /obj/machinery/singularity_generator{@OLD} + +# FABRICATORS +/obj/machinery/autolathe{hacked = 1} : /obj/machinery/fabricator/hacked{@OLD; hacked = @SKIP} +/obj/machinery/autolathe : /obj/machinery/fabricator{@OLD} +/obj/machinery/pipedispenser : /obj/machinery/fabricator/pipe{@OLD; anchored = 1} +/obj/machinery/pipedispenser/orderable : /obj/machinery/fabricator/pipe{@OLD} +/obj/machinery/pipedispenser/disposal : /obj/machinery/fabricator/pipe/disposal{@OLD; anchored = 1} +/obj/machinery/pipedispenser/disposal/orderable : /obj/machinery/fabricator/pipe/disposal{@OLD} +/obj/machinery/r_n_d/circuit_imprinter : /obj/machinery/fabricator/imprinter{@OLD} +/obj/machinery/pros_fabricator : /obj/machinery/fabricator/robotics{@OLD} +/obj/machinery/mecha_part_fabricator : /obj/machinery/fabricator/industrial{@OLD} +/obj/machinery/r_n_d/protolathe : /obj/machinery/fabricator/protolathe{@OLD} +/obj/machinery/bookbinder : /obj/machinery/fabricator/book{@OLD} +/obj/machinery/organ_printer/flesh : /obj/machinery/fabricator/bioprinter{@OLD} +/obj/machinery/vending/food : /obj/machinery/fabricator/replicator + +/obj/machinery/fabricator/@SUBTYPES : /obj/machinery/fabricator/@SUBTYPES{@OLD; res_max_amount = @SKIP; speed = @SKIP} + +# MMIs and posibrains +/obj/item/mmi/digital/@SUBTYPES : /obj/item/organ/internal/brain/robotic{@OLD} +/obj/item/mmi/@SUBTYPES : /obj/item/organ/internal/brain_interface/@SUBTYPES{@OLD} +/obj/item/organ/internal/posibrain : /obj/item/organ/internal/brain/robotic/positronic{@OLD} + +# Energy blades +/obj/item/energy/@SUBTYPES : /obj/item/energy_blade/@SUBTYPES{@OLD} + +# Medals and other accessories; see 3902_ties.txt for further migrations +/obj/item/clothing/accessory/medal/bronze_heart : /obj/item/clothing/accessory/medal/nanotrasen/bronze{@OLD} +/obj/item/clothing/accessory/medal/nobel_science : /obj/item/clothing/medal/nanotrasen/silver{@OLD} +/obj/item/clothing/accessory/scarf/white : /obj/item/clothing/accessory/scarf{@OLD} +/obj/item/clothing/accessory/storage/webbing : /obj/item/clothing/accessory/webbing/vest + +# MATERIALS +/obj/item/stack/material/plastic : /obj/item/stack/material/panel/mapped/plastic{@OLD} +/obj/item/stack/material/sandstone : /obj/item/stack/material/brick/mapped/sandstone{@OLD} +/obj/item/stack/material/mhydrogen : /obj/item/stack/material/segment/mapped/mhydrogen{@OLD} +/obj/item/stack/material/diamond : /obj/item/stack/material/gemstone/mapped/diamond{@OLD} +/obj/item/stack/material/wood : /obj/item/stack/material/plank/mapped/wood{@OLD} +/obj/item/stack/material/steel : /obj/item/stack/material/sheet/mapped/steel{@OLD} +/obj/item/stack/material/glass : /obj/item/stack/material/pane/mapped/glass{@OLD} +/obj/item/stack/material/phoron : /obj/item/stack/material/crystal/mapped/phoron{@OLD} +/obj/item/stack/material/algae : /obj/item/stack/material/puck/mapped/algae{@OLD} +/obj/item/stack/material/gold : /obj/item/stack/material/ingot/mapped/gold{@OLD} +/obj/item/stack/material/plasteel : /obj/item/stack/material/reinforced/mapped/plasteel{@OLD} +/obj/item/stack/material/glass/reinforced : /obj/item/stack/material/pane/mapped/rglass{@OLD} +/obj/item/stack/material/glass/phoronglass : /obj/item/stack/material/pane/mapped/borosilicate{@OLD} +/obj/item/stack/material/glass/phoronrglass : /obj/item/stack/material/pane/mapped/rborosilicate{@OLD} +/obj/item/stack/rods : /obj/item/stack/material/rods{@OLD} +/obj/item/packageWrap{amount = @UNSET} : /obj/item/stack/package_wrap/twenty_five{@OLD; amount = @SKIP} +/obj/item/packageWrap : /obj/item/stack/package_wrap{@OLD} +/obj/item/wrapping_paper{amount = @UNSET} : /obj/item/stack/package_wrap/gift/twenty_five{@OLD; amount = @SKIP} +/obj/item/wrapping_paper : /obj/item/stack/package_wrap{@OLD} + +/obj/structure/dispenser{phorontanks = 0} : /obj/structure/tank_rack/oxygen{@OLD; phorontanks = @SKIP} +/obj/structure/dispenser/oxygen{oxygentanks = 0} : /obj/structure/tank_rack/empty{@OLD; oxygentanks = @SKIP} +/obj/structure/dispenser/oxygen : /obj/structure/tank_rack/oxygen +/obj/structure/dispenser : /obj/structure/tank_rack + +# TELECOMMS REMOVAL +/obj/item/stock_parts/circuitboard/telecomms/@SUBTYPES : @DELETE +/obj/machinery/telecomms/@SUBTYPES : @DELETE +/obj/machinery/computer/telecomms/@SUBTYPES : @DELETE + +/obj/machinery/r_n_d/server/@SUBTYPES : /obj/machinery/design_database +/obj/machinery/r_n_d/destructive_analyzer : /obj/machinery/destructive_analyzer + +# SMES bullshit +/obj/machinery/power/smes/buildable/engine : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 1)} +/obj/machinery/power/smes/buildable/main : /obj/machinery/power/smes/buildable/preset{_fully_charged = 1; _input_maxed = 1; _input_on = 1; _output_maxed = 1; _output_on = 1; uncreated_component_parts = list(/obj/item/stock_parts/smes_coil = 4)} +/obj/machinery/power/smes/buildable/point_of_interest : /obj/machinery/power/smes/buildable/preset/hidden + +/obj/item/surgical/@SUBTYPES : /obj/item/@SUBTYPES{@OLD} +/obj/item/FixOVein : /obj/item/sutures{@OLD} + +/obj/structure/flora/skeleton : /obj/structure/skele_stand{@OLD} + +/obj/mecha/working/ripley/firefighter : /mob/living/exosuit/premade/firefighter +/obj/mecha/working/ripley/mining : /mob/living/exosuit/premade/powerloader + +/obj/structure/AIcore/@SUBTYPES : /obj/structure/aicore/@SUBTYPES +/obj/item/tank/vox : /obj/item/tank/nitrogen{@OLD} + +/obj/machinery/shuttle_sensor : /obj/machinery/airlock_sensor/shuttle{@OLD} + +/obj/machinery/computer/rdconsole/@SUBTYPES : /obj/machinery/computer/design_console{@OLD} +/obj/machinery/computer/rdservercontrol : /obj/machinery/computer/design_console{@OLD; name = @SKIP; badmin = @SKIP} +/obj/item/stock_parts/circuitboard/rdconsole : /obj/item/stock_parts/circuitboard/design_console{@OLD} +/obj/item/stock_parts/circuitboard/rdserver : /obj/item/stock_parts/circuitboard/design_database{@OLD} + +/obj/machinery/account_database : /obj/machinery/computer/account_database{@OLD} +/obj/machinery/shield_gen/@SUBTYPES : /obj/machinery/shield_generator + +/obj/structure/morgue/crematorium : /obj/structure/crematorium{@OLD} + +/obj/item/robot_parts/frame : /obj/item/robot_parts/robot_suit{@OLD} + +/obj/structure/closet/secure_closet/psych : /obj/structure/closet/secure_closet/psychiatry{@OLD} +/obj/structure/closet/firecloset/full : /obj/structure/closet/firecloset{@OLD} +/obj/structure/closet/firecloset/full/double : /obj/structure/closet/firecloset/chief{@OLD} +/obj/structure/closet/l3closet/scientist/double : /obj/structure/closet/l3closet/scientist/multi{@OLD} +/obj/structure/closet/secure_closet/hos2 : /obj/structure/closet/secure_closet/hos{@OLD} +/obj/structure/closet/secure_closet/medical_wall/synth_anesthetics : /obj/structure/closet/secure_closet/medical_wall/anesthetics/robotics{@OLD} +/obj/structure/closet/secure_closet/medical2 : /obj/structure/closet/secure_closet/anesthetics{@OLD} +/obj/structure/closet/walllocker/emerglocker/@SUBTYPES : /obj/structure/emergency_dispenser/@SUBTYPES{@OLD} + +/obj/item/firstaid_arm_assembly : /obj/item/firstaid/empty, /obj/item/robot_parts/l_arm +/obj/item/bucket_sensor : /obj/item/chems/glass/bucket, /obj/item/assembly/prox_sensor +/obj/item/broken_device : @DELETE + +/obj/item/spacecash/@SUBTYPES : /obj/item/cash/@SUBTYPES{@OLD} + +/obj/item/kit/paint/ripley : /obj/item/kit/paint/camouflage +/obj/item/kit/paint/ripley/death : /obj/item/kit/paint/camouflage/forest +/obj/item/kit/paint/ripley/flames_blue : /obj/item/kit/paint/flames_blue +/obj/item/kit/paint/ripley/flames_red : /obj/item/kit/paint/flames_red + +/obj/machinery/photocopier/faxmachine : /obj/machinery/faxmachine{@OLD; init_network_tag = @OLD:department; department = @SKIP} +/obj/item/tvcamera : /obj/item/camera/tvcamera{@OLD} +/obj/machinery/station_map : /obj/machinery/holomap{@OLD} + +/obj/item/ducttape : /obj/item/duct_tape{@OLD} +/obj/item/toy/katana : /obj/item/sword/katana/toy{@OLD} +/obj/item/toy/cultsword : /obj/item/sword/cult_toy{@OLD} +/obj/item/toy/sword : /obj/item/energy_blade/sword/toy{@OLD} +/obj/item/lipstick/jade : /obj/item/cosmetics/lipstick/green{@OLD} +/obj/item/rig_module/device/plasmacutter : /obj/item/rig_module/mounted/plasmacutter{@OLD} +/obj/item/storage/box/syndie_kit/ewar_voice : /obj/item/backpack/satchel/syndie_kit/ewar_voice +/obj/item/tray : /obj/item/plate/tray{@OLD} +/obj/item/weldingtool/weldpack : /obj/item/chems/weldpack{@OLD} +/obj/machinery/crystal : /obj/structure/crystal{@OLD} +/obj/item/organ/internal/stack : /obj/item/organ/internal/voxstack{@OLD} +/obj/item/radio_jammer : /obj/item/suit_sensor_jammer + +# AMMO BOXES +/obj/item/storage/box/beanbags/@SUBTYPES : /obj/item/box/ammo/beanbags/@SUBTYPES{@OLD} +/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} +/obj/item/storage/box/blanks/@SUBTYPES : /obj/item/box/ammo/blanks/@SUBTYPES{@OLD} +/obj/item/storage/box/empshells/@SUBTYPES : /obj/item/box/ammo/empshells/@SUBTYPES{@OLD} +/obj/item/storage/box/flashshells/@SUBTYPES : /obj/item/box/ammo/flashshells/@SUBTYPES{@OLD} +/obj/item/storage/box/shotgunammo/@SUBTYPES : /obj/item/box/ammo/shotgunammo/@SUBTYPES{@OLD} +/obj/item/storage/box/shotgunshells/@SUBTYPES : /obj/item/box/ammo/shotgunshells/@SUBTYPES{@OLD} +/obj/item/storage/box/sniperammo/@SUBTYPES : /obj/item/box/ammo/sniperammo/@SUBTYPES{@OLD} +/obj/item/storage/box/stunshells/@SUBTYPES : /obj/item/box/ammo/stunshells/@SUBTYPES{@OLD} + +/obj/item/extinguisher/@SUBTYPES : /obj/item/chems/spray/extinguisher/@SUBTYPES{@OLD} +/obj/item/camera_assembly : /obj/item/frame/camera/kit +/obj/structure/ladder/up : /obj/structure/ladder +/obj/random/mob/mouse : /obj/random/mouse + +/obj/structure/closet/grave/dirthole : /obj/structure/pit{@OLD} +/obj/structure/closet/grave/@SUBTYPES : /obj/structure/pit/closed/grave{@OLD} + +/obj/item/banner/nt : /obj/item/banner/nanotrasen{@OLD} + +/obj/item/box/syndie_kit/clerical : /obj/item/backpack/satchel/syndie_kit/clerical{@OLD} + +/obj/item/clothing/under/costume/rainbow : /obj/item/clothing/jumpsuit/rainbow{@OLD} +/obj/item/clothing/under/dress/dress_orange : /obj/item/clothing/dress/orange{@OLD} +/obj/item/clothing/head/collectable/xenos : /obj/item/clothing/head/xenos{@OLD} +/obj/item/clothing/head/collectable/bearpelt : /obj/item/clothing/head/bearpelt{@OLD} +/obj/item/clothing/head/collectable/philosopher_wig : /obj/item/clothing/head/philosopher_wig{@OLD} +/obj/item/clothing/head/collectable/plaguedoctorhat : /obj/item/clothing/head/plaguedoctorhat{@OLD} +/obj/item/clothing/head/helmet/space/void/security/riot : /obj/item/clothing/head/helmet/space/void/security/alt{@OLD} +/obj/item/clothing/suit/space/void/security/riot : /obj/item/clothing/suit/space/void/security/alt{@OLD} +/obj/item/clothing/head/helmet/space/vox/pressure : /obj/item/clothing/head/helmet/space/vox{@OLD} +/obj/item/clothing/under/vox/vox_utility : /obj/item/clothing/pants/vox{@OLD} +/obj/item/clothing/suit/space/pirate : /obj/item/clothing/suit/pirate{@OLD} +/obj/item/clothing/suit/costume : /obj/item/clothing/suit/pirate{@OLD} +/obj/item/clothing/suit/costume/hgpirate : /obj/item/clothing/suit/hgpirate{@OLD} +/obj/item/clothing/suit/storage/vest/heavy/merc : /obj/item/clothing/suit/armor/pcarrier/merc{@OLD} +/obj/item/clothing/suit/storage/vest/@SUBTYPES : /obj/item/clothing/suit/armor/vest/@SUBTYPES{@OLD} + +# Migrate/delete old mech parts +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster : @DELETE +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster : @DELETE +/obj/item/mecha_parts/mecha_equipment/repair_droid : @DELETE +/obj/item/mecha_parts/mecha_equipment/teleporter : @DELETE +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay : @DELETE +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer : @DELETE +/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill : /obj/item/mech_equipment/drill/diamond{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/extinguisher : /obj/item/mech_equipment/mounted_system/extinguisher{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp : /obj/item/mech_equipment/clamp{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/passenger : @DELETE +/obj/item/mecha_parts/mecha_equipment/tool/rcd : /obj/item/mech_equipment/mounted_system/rcd{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/sleeper : /obj/item/mech_equipment/sleeper{@OLD} +/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun : @DELETE +/obj/item/mecha_parts/mecha_equipment/weapon/energy/ion : /obj/item/mech_equipment/mounted_system/taser/ion{@OLD} +/obj/item/mecha_parts/mecha_equipment/weapon/energy/taser : /obj/item/mech_equipment/mounted_system/taser{@OLD} +/obj/item/mecha_parts/mecha_tracking : @DELETE + +############ +# VAREDITS # +############ +# Atmos varedits +/obj/machinery/computer/air_control/@SUBTYPES{sensors = @SET} : /obj/machinery/computer/air_control/@SUBTYPES{@OLD; sensors = @SKIP} +# move these to the external air subtype so frequencies work +/obj/machinery/atmospherics/unary/vent_pump/high_volume{frequency = 1379} : /obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{@OLD; frequency = @SKIP} +/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrub_id = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} +/obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{scrubbing_gas = list("phoron")} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; scrubbing_gas = list(/decl/material/solid/phoron)} +/obj/machinery/computer/area_atmos/tag/@SUBTYPES{scrub_id = @SET} : /obj/machinery/computer/area_atmos/tag/@SUBTYPES{@OLD; scrub_id = @SKIP; id_tag = @OLD:scrub_id} +# for the rest of this, frequency was moved to component presets and i'm too lazy to set that up +/obj/machinery/door/airlock/@SUBTYPES{frequency = @SET} : /obj/machinery/door/airlock/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/portable_atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/atmospherics/@SUBTYPES{frequency = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/airlock_sensor/@SUBTYPES{frequency = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/air_sensor/@SUBTYPES : /obj/machinery/air_sensor/@SUBTYPES{@OLD; frequency = @SKIP; output = @SKIP} +/obj/machinery/embedded_controller/radio/@SUBTYPES{frequency = @SET} : /obj/machinery/embedded_controller/radio/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/button/@SUBTYPES{frequency = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; frequency = @SKIP} +/obj/machinery/meter/@SUBTYPES{frequency = @SET} : /obj/machinery/portable_atmospherics/powered/scrubber/@SUBTYPES{@OLD; frequency = @SKIP} + +# id -> id_tag +/obj/machinery/door_timer/@SUBTYPES{id = @SET} : /obj/machinery/door_timer/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/conveyor/@SUBTYPES{id = @SET} : /obj/machinery/conveyor/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/conveyor_switch/@SUBTYPES{id = @SET} : /obj/machinery/conveyor_switch/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/door/@SUBTYPES{id = @SET} : /obj/machinery/door/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/button/@SUBTYPES{id = @SET} : /obj/machinery/button/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/atmospherics/@SUBTYPES{id = @SET} : /obj/machinery/atmospherics/@SUBTYPES{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/holosign/bar{id = @SET} : /obj/machinery/holosign/bar{@OLD; id_tag = @OLD:id; id = @SKIP} +/obj/machinery/meter/@SUBTYPES{id = @SET} : /obj/machinery/meter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/flasher/@SUBTYPES{id = @SET} : /obj/machinery/flasher/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/sparker/@SUBTYPES{id = @SET} : /obj/machinery/sparker/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/mass_driver/@SUBTYPES{id = @SET} : /obj/machinery/mass_driver/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} +/obj/machinery/emitter/@SUBTYPES{id = @SET} : /obj/machinery/emitter/@SUBTYPES{@OLD; id = @SKIP; id_tag = @OLD:id} + +# Camera preset_channels -> network +/obj/machinery/camera/network/@SUBTYPES{network = @SET} : /obj/machinery/camera/network/@SUBTYPES{@OLD; network = @SKIP; preset_channels = @OLD:network} + +# Remove button/sensor master_tag +/obj/machinery/airlock_sensor/@SUBTYPES{master_tag = @SET} : /obj/machinery/airlock_sensor/@SUBTYPES{@OLD; master_tag = @SKIP} +/obj/machinery/button/access/@SUBTYPES{master_tag = @SET} : /obj/machinery/button/access/@SUBTYPES{@OLD; master_tag = @SKIP} + +# Skip power sensor name_tag +/obj/machinery/power/sensor{name_tag = @SET} : /obj/machinery/power/sensor{@OLD; name_tag = @SKIP} + +# join_group -> unique_merge_identifier +/turf/wall/shuttle/@SUBTYPES{join_group = @SET} : /turf/wall/shuttle/@SUBTYPES{@OLD; unique_merge_identifier = @OLD:join_group} + +# Remove set icon states/density on windows, doors, shutters +/obj/machinery/door/blast/regular{icon_state = @SET} : /obj/machinery/door/blast/regular{@OLD; icon_state = @SKIP} +/obj/structure/window/borosilicate_reinforced/@SUBTYPES{icon_state = @SET} : /obj/structure/window/borosilicate_reinforced/@SUBTYPES{@OLD; icon_state = @SKIP} +/obj/machinery/door/blast/regular{p_open = 1} : /obj/machinery/door/blast/regular/open{@OLD; p_open = @SKIP} +/obj/machinery/door/blast/regular{p_open = 0} : /obj/machinery/door/blast/regular{@OLD; p_open = @SKIP} +/obj/machinery/door/blast/regular{density = 0} : /obj/machinery/door/blast/regular/open{@OLD; density = @SKIP; opacity = @SKIP} +/obj/structure/window/@SUBTYPES{icon_state = "fwindow"} : /obj/structure/window/@SUBTYPES{@OLD; icon_state = @SKIP} +/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} +/obj/structure/window/reinforced/reinforced/polarized/full{dir = 10} : /obj/structure/window/reinforced/polarized/full{@OLD; dir = @SKIP} + +# Prices -> markup +/obj/machinery/vending/@SUBTYPES{prices = list()} : /obj/machinery/vending/@SUBTYPES{@OLD; markup = 0; prices=@SKIP} +/obj/machinery/vending/@SUBTYPES{prices = @SET} : /obj/machinery/vending/@SUBTYPES{@OLD; prices = @SKIP} + +# Radio varedits +/obj/item/radio/@SUBTYPES{subspace_transmission = @SET} : @OLD {@OLD; subspace_transmission = @SKIP} +/obj/item/radio/@SUBTYPES{syndie = @SET} : @OLD {@OLD; decrypt_all_messages = 1; syndie = @SKIP} +/obj/item/radio/@SUBTYPES{adhoc_fallback = @SET} : @OLD {@OLD; can_use_analog = @SKIP} + +# Empty first-aid kits +/obj/item/storage/firstaid/regular{empty = 1} : /obj/item/firstaid/empty{@OLD; empty = @SKIP; name = @SKIP} + +/obj/machinery/floor_light/prebuilt{on = 1} : /obj/machinery/floor_light/prebuilt{@OLD; use_power = 2; on = @SKIP} + +/obj/structure/disposalpipe/sortjunction/@SUBTYPES{sortType = @SET} : /obj/structure/disposalpipe/sortjunction/@SUBTYPES{@OLD; sort_type = @OLD:sortType; sortType = @SKIP} From 9ec9711087e5fba4e9a6de51288e68721db81b63 Mon Sep 17 00:00:00 2001 From: ike709 Date: Sat, 14 Jun 2025 08:45:24 -0500 Subject: [PATCH 0702/1331] Rename a var from `usr` to `user` --- code/modules/character_info/_comment.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/character_info/_comment.dm b/code/modules/character_info/_comment.dm index ef15898c550b..3ab52a26d2e1 100644 --- a/code/modules/character_info/_comment.dm +++ b/code/modules/character_info/_comment.dm @@ -25,7 +25,7 @@ /datum/character_comment/proc/is_stale() return !get_config_value(/decl/config/num/hide_comments_older_than) || (REALTIMEOFDAY - last_updated) > (get_config_value(/decl/config/num/hide_comments_older_than)) -/datum/character_comment/proc/get_comment_html(var/datum/character_information/presenting, var/mob/usr, var/row_bkg_color) +/datum/character_comment/proc/get_comment_html(var/datum/character_information/presenting, var/mob/user, var/row_bkg_color) if(!istype(main_mood)) main_mood = GET_DECL(/decl/comment_mood/unknown) @@ -39,14 +39,14 @@ "
    [body]
    ", "" ) - if(usr) + if(user) . += "
    " - if(author_ckey == usr.ckey || check_rights(R_MOD)) + if(author_ckey == user.ckey || check_rights(R_MOD)) . += "Edit
    " . += "Delete
    " . += "Change primary mood
    " . += "Change border mood" - else if(usr.ckey == presenting.ckey) + else if(user.ckey == presenting.ckey) . += "Delete" else . += "Not editable." From 6e62ab2336ca3dc7601eadbbda9f7d162205afbf Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 14 Jun 2025 20:29:03 -0400 Subject: [PATCH 0703/1331] Remove defunct gateway code --- code/_onclick/ghost.dm | 12 - .../corpse.dm => abstract/corpse_spawner.dm} | 6 - code/modules/awaymissions/artillery.dm | 53 ---- code/modules/awaymissions/exile.dm | 36 --- code/modules/awaymissions/gateway.dm | 235 ------------------ code/modules/awaymissions/pamphlet.dm | 39 --- code/modules/awaymissions/trigger.dm | 40 --- icons/obj/items/paperwork/pamphlet.dmi | Bin 813 -> 0 bytes nebula.dme | 7 +- 9 files changed, 1 insertion(+), 427 deletions(-) rename code/modules/{awaymissions/corpse.dm => abstract/corpse_spawner.dm} (96%) delete mode 100644 code/modules/awaymissions/artillery.dm delete mode 100644 code/modules/awaymissions/exile.dm delete mode 100644 code/modules/awaymissions/gateway.dm delete mode 100644 code/modules/awaymissions/pamphlet.dm delete mode 100644 code/modules/awaymissions/trigger.dm delete mode 100644 icons/obj/items/paperwork/pamphlet.dmi diff --git a/code/_onclick/ghost.dm b/code/_onclick/ghost.dm index d36410ef6aea..a47458ff2f18 100644 --- a/code/_onclick/ghost.dm +++ b/code/_onclick/ghost.dm @@ -61,15 +61,3 @@ /obj/effect/portal/attack_ghost(mob/user) if(target) user.forceMove(get_turf(target)) - -/obj/machinery/gateway/centerstation/attack_ghost(mob/user) - if(awaygate) - user.forceMove(awaygate.loc) - else - to_chat(user, "[src] has no destination.") - -/obj/machinery/gateway/centeraway/attack_ghost(mob/user) - if(stationgate) - user.forceMove(stationgate.loc) - else - to_chat(user, "[src] has no destination.") diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/abstract/corpse_spawner.dm similarity index 96% rename from code/modules/awaymissions/corpse.dm rename to code/modules/abstract/corpse_spawner.dm index 62eb69dc0a6e..47319d03325b 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/abstract/corpse_spawner.dm @@ -1,9 +1,3 @@ -//These are meant for spawning on maps, namely Away Missions. - -//If someone can do this in a neater way, be my guest-Kor - -//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). - #define CORPSE_SPAWNER_RANDOM_NAME BITFLAG(0) #define CORPSE_SPAWNER_CUT_SURVIVAL BITFLAG(1) #define CORPSE_SPAWNER_CUT_ID_PDA BITFLAG(2) diff --git a/code/modules/awaymissions/artillery.dm b/code/modules/awaymissions/artillery.dm deleted file mode 100644 index 644356e53d76..000000000000 --- a/code/modules/awaymissions/artillery.dm +++ /dev/null @@ -1,53 +0,0 @@ -/obj/structure/artilleryplaceholder - name = "artillery" - desc = "The ship's old superluminal artillery cannon. Looks inoperative." - icon = 'icons/obj/machines/artillery.dmi' - anchored = TRUE - density = TRUE - -/obj/structure/artilleryplaceholder/decorative - density = FALSE - -/obj/machinery/artillerycontrol - name = "superluminal artillery control" - icon_state = "control_boxp1" - icon = 'icons/obj/machines/particle_accelerator2.dmi' - density = TRUE - anchored = TRUE - var/tmp/time_to_reload = 360 SECONDS // originally this was 180 ticks at 1 tick per 2 seconds - var/reload_cooldown = 0 // The world.time value after which we will be able to fire. - -/obj/machinery/artillerycontrol/interface_interact(mob/user) - interact(user) - return TRUE - -/obj/machinery/artillerycontrol/interact(mob/user) - user.set_machine(src) - var/dat = list("Superluminal Artillery Control:
    ") - dat += "Locked on
    " - dat += "Charge progress: [round(100 - ((reload_cooldown - world.time) / time_to_reload))]%
    " - dat += "Open Fire
    " - dat += "Deployment of weapon authorized by
    [global.using_map.company_name] Naval Command

    Remember, friendly fire is grounds for termination of your contract and life.
    " - show_browser(user, JOINTEXT(dat), "window=superlumcontrol") - onclose(user, "superlumcontrol") - -/obj/machinery/artillerycontrol/OnTopic(mob/user, href_list) - if((. = ..())) - return - - var/area/thearea = input("Area to jump bombard", "Open Fire") as null|anything in teleportlocs - thearea = thearea ? teleportlocs[thearea] : null - if (!thearea) - return - if (world.time < reload_cooldown) - return - command_announcement.Announce("Wormhole artillery fire detected. Brace for impact.") - log_and_message_admins("has launched an artillery strike.", user, get_turf(src)) - var/list/turf/candidates = list() - // this is slow (in world loop) but we don't have a better way unless we pre-register a list of areas -> z-levels - // and this code isn't hot code at all - for(var/turf/tile in thearea) - candidates += tile - var/turf/target_loc = pick(candidates) - explosion(target_loc, 2, 5, 11) - reload_cooldown = world.time + time_to_reload diff --git a/code/modules/awaymissions/exile.dm b/code/modules/awaymissions/exile.dm deleted file mode 100644 index 51e98d910082..000000000000 --- a/code/modules/awaymissions/exile.dm +++ /dev/null @@ -1,36 +0,0 @@ -//////Exile implants will allow you to use the station gate, but not return home. This will allow security to exile badguys/for badguys to exile their kill targets//////// - - -/obj/item/implanter/exile - name = "implanter-exile" - imp = /obj/item/implant/exile - -/obj/item/implant/exile - name = "exile" - desc = "Prevents you from returning from away missions." - -/obj/item/implant/exile/get_data() - var/dat = {" - Implant Specifications:
    - Name: [global.using_map.company_name] Employee Exile Implant
    - Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
    "} - return dat - -/obj/item/implantcase/exile - name = "glass case - 'Exile'" - desc = "A case containing an exile implant." - icon_state = "implantcase-r" - imp = /obj/item/implant/exile - -/obj/structure/closet/secure_closet/exile - name = "Exile Implants" - req_access = list(access_hos) - -/obj/structure/closet/secure_closet/exile/Initialize() - . = ..() - new /obj/item/implanter/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm deleted file mode 100644 index 3696cdeec715..000000000000 --- a/code/modules/awaymissions/gateway.dm +++ /dev/null @@ -1,235 +0,0 @@ -/obj/machinery/gateway - name = "gateway" - desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations." - icon = 'icons/obj/machines/gateway.dmi' - icon_state = "off" - density = TRUE - anchored = TRUE - var/active = 0 - - -/obj/machinery/gateway/Initialize() - update_icon() - if(dir == SOUTH) - set_density(0) - . = ..() - -/obj/machinery/gateway/on_update_icon() - if(active) - icon_state = "on" - return - icon_state = "off" - - - -//this is da important part wot makes things go -/obj/machinery/gateway/centerstation - density = TRUE - icon_state = "offcenter" - - //warping vars - var/list/linked = list() - var/ready = 0 //have we got all the parts for a gateway? - var/wait = 0 //this just grabs world.time at world start - var/obj/machinery/gateway/centeraway/awaygate = null - -/obj/machinery/gateway/centerstation/Initialize() - update_icon() - wait = world.time + get_config_value(/decl/config/num/gateway_delay) //+ thirty minutes default - awaygate = locate(/obj/machinery/gateway/centeraway) - . = ..() - -/obj/machinery/gateway/centerstation/on_update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - - - -/obj/machinery/gateway/centerstation/Process() - if(stat & (NOPOWER)) - if(active) toggleoff() - return - - if(active) - use_power_oneoff(5000) - - -/obj/machinery/gateway/centerstation/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in global.alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centerstation/proc/toggleon(mob/user) - if(!ready) - return - if(linked.len != 8) - return - if(stat & NOPOWER) - return - if(!awaygate) - to_chat(user, "Error: No destination found.") - return - if(world.time < wait) - to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centerstation/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centerstation/attack_hand(mob/user) - if(!user.check_dexterity(DEXTERITY_COMPLEX_TOOLS, TRUE)) - return ..() - if(!ready) - detect() - return TRUE - if(!active) - toggleon(user) - return TRUE - toggleoff() - return TRUE - -//okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/Bumped(atom/movable/M) - if(!ready) return - if(!active) return - if(!awaygate) return - if(awaygate.calibrated) - M.forceMove(get_step(awaygate.loc, SOUTH)) - M.set_dir(SOUTH) - return - -/obj/machinery/gateway/centerstation/attackby(obj/item/used_item, mob/user) - if(IS_MULTITOOL(used_item)) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return TRUE - return FALSE - -/////////////////////////////////////Away//////////////////////// - - -/obj/machinery/gateway/centeraway - density = TRUE - icon_state = "offcenter" - use_power = POWER_USE_OFF - var/calibrated = 1 - var/list/linked = list() //a list of the connected gateway chunks - var/ready = 0 - var/obj/machinery/gateway/centerstation/stationgate = null - - -/obj/machinery/gateway/centeraway/Initialize() - update_icon() - stationgate = locate(/obj/machinery/gateway/centerstation) - . = ..() - -/obj/machinery/gateway/centeraway/on_update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - - -/obj/machinery/gateway/centeraway/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in global.alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centeraway/proc/toggleon(mob/user) - if(!ready) return - if(linked.len != 8) return - if(!stationgate) - to_chat(user, "Error: No destination found.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centeraway/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centeraway/attack_hand(mob/user) - if(!user.check_dexterity(DEXTERITY_COMPLEX_TOOLS, TRUE)) - return ..() - if(!ready) - detect() - return TRUE - if(!active) - toggleon(user) - return TRUE - toggleoff() - return TRUE - -/obj/machinery/gateway/centeraway/Bumped(atom/movable/M) - if(!ready) return - if(!active) return - if(ishuman(M)) - for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents - if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - to_chat(M, "The remote gate has detected your exile implant and is blocking your entry.") - return - M.forceMove(get_step(stationgate.loc, SOUTH)) - M.set_dir(SOUTH) - -/obj/machinery/gateway/centeraway/attackby(obj/item/used_item, mob/user) - if(IS_MULTITOOL(used_item)) - if(calibrated) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return TRUE - else - to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") - calibrated = 1 - return TRUE - return FALSE diff --git a/code/modules/awaymissions/pamphlet.dm b/code/modules/awaymissions/pamphlet.dm deleted file mode 100644 index 49dbb1237f44..000000000000 --- a/code/modules/awaymissions/pamphlet.dm +++ /dev/null @@ -1,39 +0,0 @@ -/obj/item/paper/pamphlet - name = "pamphlet" - icon = 'icons/obj/items/paperwork/pamphlet.dmi' - info = "Welcome to the Gateway project...
    \ - Congratulations! If you're reading this, you and your superiors have decided that you're \ - ready to commit to a life spent colonising the rolling hills of far away worlds. You \ - must be ready for a lifetime of adventure, a little bit of hard work, and an award \ - winning dental plan- but that's not all the Gateway project has to offer.
    \ -
    Because we care about you, we feel it is only fair to make sure you know the risks \ - before you commit to joining the Gateway project. All away destinations have \ - been fully scanned by an expeditionary team, and are certified to be 100% safe. \ - We've even left a case of space beer along with the basic materials you'll need to expand \ - the Project's operational area and start your new life.

    \ - Gateway Operation Basics
    \ - All approved Gateways operate on the same basic principals. They operate off \ - area equipment power as you would expect, but they also require a backup wire with at least \ - 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ - and will reject all attempts at operation.

    \ - Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ - searching for an output location. The amount of time this takes is variable, but the Gateway \ - interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ - searching process. Influenza will not interrupt the searching process. Temporal anomalies \ - may cause the estimate to be inaccurate, but will not interrupt the searching process.

    \ - Life On The Other Side
    \ - Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ - This is a normal side effect of travelling vast distances in a short period of time. You should \ - survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ - expeditionary teams have ensured the complete safety of all away locations, but in a small \ - number of cases, the Gateway they have established may not be immediately obvious. \ - Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ -

    A New World
    \ - As a participant in the Gateway Project, you will be on the frontiers of space. \ - Though complete safety is assured, participants are advised to prepare for inhospitable \ - environs." - -//we don't want the silly text overlay! -/obj/item/paper/pamphlet/on_update_icon() - SHOULD_CALL_PARENT(FALSE) - return \ No newline at end of file diff --git a/code/modules/awaymissions/trigger.dm b/code/modules/awaymissions/trigger.dm deleted file mode 100644 index f1d2fa3405ee..000000000000 --- a/code/modules/awaymissions/trigger.dm +++ /dev/null @@ -1,40 +0,0 @@ -/obj/effect/step_trigger/message - var/message //the message to give to the mob - var/once = 1 - -/obj/effect/step_trigger/message/Trigger(mob/M) - if(M.client) - to_chat(M, "[message]") - if(once) - qdel(src) - -/obj/effect/step_trigger/teleport_fancy - var/locationx - var/locationy - var/uses = 1 //0 for infinite uses - var/entersparks = 0 - var/exitsparks = 0 - var/entersmoke = 0 - var/exitsmoke = 0 - -/obj/effect/step_trigger/teleport_fancy/Trigger(mob/M) - var/dest = locate(locationx, locationy, z) - M.Move(dest) - - if(entersparks) - spark_at(src, amount=4, cardinal_only = TRUE) - if(exitsparks) - spark_at(dest, amount=4, cardinal_only = TRUE) - - if(entersmoke) - var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread - s.set_up(4, 1, src, 0) - s.start() - if(exitsmoke) - var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread - s.set_up(4, 1, dest, 0) - s.start() - - uses-- - if(uses == 0) - qdel(src) \ No newline at end of file diff --git a/icons/obj/items/paperwork/pamphlet.dmi b/icons/obj/items/paperwork/pamphlet.dmi deleted file mode 100644 index 32dd05307de58d4890705fb8de112d2f1b45e411..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 813 zcmV+|1JeA7P)#-*{;{T)8(t%?cr>{|4gy}rKF&ro{^rLe^#~sX}s~f z-Mqly!OrcYd9vQJvaXYplai2sfqQ0e%qHT;dAQV=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRL zOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5sXV_ZCq;>iGbOXA7|1u|;!G<_%uR)`QB~=d zBo?INRI9Au>gNJ>0stPvCo~0I7Qz4k0RBltK~zYIV_+Bs^ahMfj7$WSFoOV2StcML zpqGV}jh#FLn2`Zl25@k4ar5x<@e2qF;Whvm2sjN877-N_6PJ*bl#<5n2RwjTMpjl% zURFU-Nm+$F15{+)2eEpdH0|Ens0)ojiASBev)zu~}EId4dJOd)5TwFt~ZKI=OV#zZg zJlr)Z$}rS6E Date: Sat, 14 Jun 2025 20:29:20 -0400 Subject: [PATCH 0704/1331] Remove gateway from Exodus map --- maps/exodus/exodus-2.dmm | 447 +++++++----------------------------- maps/exodus/exodus_areas.dm | 4 - maps/exodus/exodus_jobs.dm | 8 - 3 files changed, 86 insertions(+), 373 deletions(-) diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 7fbd910ebb10..c83892c7fd92 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -7410,8 +7410,9 @@ /turf/floor/tiled/dark/monotile, /area/shuttle/escape_pod_1) "apD" = ( -/turf/wall/prepainted, -/area/exodus/gateway) +/obj/structure/girder, +/turf/floor/plating, +/area/exodus/maintenance/evahallway) "apE" = ( /obj/machinery/light{ dir = 8 @@ -8588,16 +8589,6 @@ }, /turf/floor, /area/exodus/maintenance/evahallway) -"arS" = ( -/obj/machinery/light_switch{ - pixel_x = -24; - dir = 4 - }, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 4 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "arT" = ( /obj/structure/hygiene/sink/kitchen{ pixel_y = 21 @@ -8859,12 +8850,6 @@ /obj/structure/bed/padded, /turf/floor/tiled/steel_grid, /area/exodus/security/brig/solitaryB) -"asu" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "asv" = ( /obj/structure/disposalpipe/segment{ dir = 1; @@ -11123,11 +11108,6 @@ }, /turf/floor/tiled/dark, /area/exodus/crew_quarters/sleep) -"axv" = ( -/obj/effect/wallframe_spawn/reinforced, -/obj/machinery/door/firedoor, -/turf/floor/plating, -/area/exodus/hallway/primary/port) "axw" = ( /obj/structure/disposalpipe/segment, /obj/structure/cable/green{ @@ -12251,10 +12231,10 @@ /turf/floor/plating, /area/exodus/bridge) "azI" = ( -/obj/machinery/door/airlock/maintenance, /obj/machinery/door/firedoor, -/turf/floor/tiled/techfloor/grid, -/area/exodus/gateway) +/obj/structure/door_assembly, +/turf/floor/plating, +/area/exodus/maintenance/evahallway) "azJ" = ( /obj/machinery/camera/network/civilian_east{ c_tag = "Kitchen Cold Room" @@ -12608,30 +12588,6 @@ /obj/machinery/portable_atmospherics/hydroponics, /turf/floor/tiled/dark, /area/exodus/hydroponics) -"aAA" = ( -/obj/machinery/gateway{ - dir = 9 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aAB" = ( -/obj/machinery/light{ - dir = 8 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aAC" = ( -/obj/machinery/gateway{ - dir = 5 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aAD" = ( -/obj/machinery/gateway{ - dir = 1 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aAE" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -12645,11 +12601,9 @@ /turf/wall/r_wall/prepainted, /area/exodus/maintenance/auxsolarport) "aAG" = ( -/obj/machinery/light{ - dir = 4 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) +/obj/random/maintenance, +/turf/floor/plating, +/area/exodus/maintenance/evahallway) "aAH" = ( /obj/structure/sign/warning/airlock{ pixel_x = 32; @@ -13126,12 +13080,6 @@ "aBI" = ( /turf/wall/r_wall/prepainted, /area/exodus/hallway/secondary/entry/port) -"aBJ" = ( -/obj/machinery/gateway{ - dir = 8 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aBK" = ( /obj/machinery/door/airlock/external/bolted{ id_tag = "nuke_shuttle_dock_inner"; @@ -13146,18 +13094,9 @@ /turf/wall/prepainted, /area/exodus/hallway/secondary/entry/port) "aBM" = ( -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aBN" = ( -/obj/machinery/gateway{ - dir = 4 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aBO" = ( -/obj/machinery/gateway/centerstation, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) +/obj/random/trash, +/turf/floor/plating, +/area/exodus/maintenance/evahallway) "aBP" = ( /obj/machinery/atmospherics/unary/tank/air{ dir = 8 @@ -13554,59 +13493,6 @@ "aCK" = ( /turf/wall/prepainted, /area/exodus/storage/primary) -"aCL" = ( -/obj/machinery/gateway{ - dir = 10 - }, -/obj/abstract/landmark/latejoin/gateway, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aCN" = ( -/obj/machinery/gateway{ - dir = 6 - }, -/obj/abstract/landmark/latejoin/gateway, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aCO" = ( -/obj/machinery/gateway, -/obj/abstract/landmark/latejoin/gateway, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aCP" = ( -/obj/machinery/vending/coffee{ - dir = 4 - }, -/obj/machinery/camera/network/civilian_west{ - c_tag = "Gateway Arrival Area"; - dir = 4 - }, -/obj/structure/sign/warning/biohazard{ - pixel_x = -32 - }, -/obj/effect/floor_decal/industrial/warning/corner{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 1; - current_health = 1e+006 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aCQ" = ( -/obj/structure/sign/warning/biohazard{ - pixel_x = 32 - }, -/obj/structure/closet/wardrobe/xenos, -/obj/effect/floor_decal/industrial/warning/corner{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 1; - current_health = 1e+006 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aCS" = ( /obj/structure/table/reinforced, /obj/item/assembly/signaler, @@ -14173,12 +14059,6 @@ }, /turf/floor/tiled/dark, /area/exodus/security/nuke_storage) -"aEb" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aEc" = ( /obj/machinery/power/apc{ dir = 8; @@ -14208,21 +14088,6 @@ /obj/effect/wallframe_spawn/reinforced, /turf/floor/plating, /area/exodus/hallway/primary/starboard) -"aEf" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) -"aEg" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 6 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 10 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aEh" = ( /obj/structure/table/laminate, /obj/machinery/reagentgrinder, @@ -14934,12 +14799,6 @@ }, /turf/floor/tiled/steel_grid, /area/exodus/research/chargebay) -"aFA" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 1 - }, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aFB" = ( /obj/structure/extinguisher_cabinet{ pixel_x = 29; @@ -14986,14 +14845,6 @@ }, /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/central_two) -"aFF" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/effect/floor_decal/industrial/warning{ - dir = 1 - }, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aFG" = ( /obj/structure/closet/secure_closet/bar, /obj/machinery/power/apc{ @@ -15360,16 +15211,6 @@ }, /turf/floor/tiled/techfloor/grid, /area/exodus/hallway/secondary/exit) -"aGl" = ( -/obj/machinery/vending/cola{ - dir = 4 - }, -/obj/item/radio/intercom{ - dir = 4; - pixel_x = -22 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aGm" = ( /obj/machinery/recharger{ pixel_y = 4 @@ -15561,10 +15402,6 @@ }, /turf/floor/plating, /area/exodus/hallway/primary/central_two) -"aGK" = ( -/obj/item/stool, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aGL" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -15584,10 +15421,6 @@ }, /turf/floor/laminate/walnut, /area/exodus/library) -"aGN" = ( -/obj/machinery/hologram/holopad, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aGO" = ( /obj/effect/wallframe_spawn/reinforced, /obj/machinery/door/blast/shutters/open{ @@ -15618,19 +15451,6 @@ }, /turf/floor/plating, /area/exodus/maintenance/substation/civilian_west) -"aGR" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) -"aGS" = ( -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 24 - }, -/obj/structure/closet/wardrobe/black, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aGT" = ( /obj/machinery/suit_cycler/medical/prepared, /turf/floor/tiled/dark, @@ -16440,12 +16260,6 @@ }, /turf/floor/plating, /area/exodus/maintenance/bar) -"aIp" = ( -/obj/structure/cable/green{ - icon_state = "4-8" - }, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aIq" = ( /obj/structure/table, /obj/item/deck/cards, @@ -16468,17 +16282,6 @@ }, /turf/floor/plating, /area/exodus/maintenance/library) -"aIs" = ( -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) -"aIt" = ( -/obj/structure/cable/green{ - icon_state = "2-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aIu" = ( /obj/machinery/button/access/exterior{ id_tag = "xeno_airlock_control"; @@ -16767,13 +16570,6 @@ }, /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/bar/cabin) -"aJc" = ( -/obj/structure/closet/l3closet/scientist, -/obj/structure/window/reinforced{ - dir = 8 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "aJd" = ( /obj/structure/flaps{ opacity = 1 @@ -17279,12 +17075,6 @@ /obj/machinery/space_heater, /turf/floor/plating, /area/exodus/maintenance/locker) -"aKl" = ( -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 4 - }, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aKm" = ( /obj/structure/cable/green{ icon_state = "1-2" @@ -17299,24 +17089,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/port) -"aKn" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) -"aKo" = ( -/obj/structure/cable/green{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 8 - }, -/turf/floor/tiled/steel_grid, -/area/exodus/gateway) "aKp" = ( /obj/machinery/vending/fashionvend, /turf/floor/tiled/monotile, @@ -17945,18 +17717,6 @@ /obj/machinery/door/firedoor, /turf/floor/plating, /area/exodus/quartermaster/storage) -"aLG" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/glass{ - name = "Gateway Access" - }, -/obj/structure/cable/green{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/turf/floor/tiled/techfloor/grid, -/area/exodus/hallway/primary/port) "aLH" = ( /obj/structure/cable/green{ icon_state = "0-8" @@ -18800,14 +18560,9 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/port) "aNA" = ( -/obj/structure/cable/green{ - icon_state = "1-2" - }, /obj/structure/cable/green{ icon_state = "2-4" }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/effect/floor_decal/corner/lime{ dir = 5 }, @@ -19725,11 +19480,11 @@ /obj/structure/cable/green{ icon_state = "1-8" }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 4 +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 10 }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 10 }, /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/port) @@ -23931,8 +23686,7 @@ announcementConsole = 1; department = "Bridge"; name = "Bridge RC"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /obj/structure/cable/green{ icon_state = "4-8" @@ -26229,22 +25983,6 @@ /obj/structure/disposalpipe/segment, /turf/floor/tiled/techfloor/grid, /area/exodus/hallway/primary/starboard) -"bdL" = ( -/obj/machinery/power/apc{ - dir = 8; - name = "west bump"; - pixel_x = -24 - }, -/obj/structure/table, -/obj/item/deck/cards, -/obj/structure/cable/green{ - icon_state = "0-4" - }, -/obj/item/box/fancy/cigarettes{ - pixel_y = 2 - }, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "bdM" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 @@ -26839,8 +26577,7 @@ /obj/machinery/network/requests_console{ department = "Kitchen"; name = "Kitchen RC"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /obj/machinery/disposal, /obj/structure/disposalpipe/trunk{ @@ -29868,8 +29605,7 @@ announcementConsole = 1; department = "Bridge"; name = "Bridge RC"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -30768,7 +30504,6 @@ }, /obj/item/chems/glass/beaker/sulfuric, /obj/structure/reagent_dispensers/acid{ - density = 0; pixel_y = 32 }, /turf/floor/tiled/white/monotile, @@ -39350,8 +39085,7 @@ announcementConsole = 1; department = "Head of Personnel's Desk"; name = "Head of Personnel RC"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /obj/machinery/camera/network/command{ c_tag = "Bridge - HoP's Office"; @@ -45577,8 +45311,7 @@ /obj/item/grenade/chem_grenade/cleaner, /obj/machinery/network/requests_console{ department = "Janitorial"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /obj/item/chems/spray/cleaner, /obj/structure/table/steel, @@ -47571,15 +47304,6 @@ }, /turf/floor/tiled/white, /area/exodus/medical/medbay4) -"bUO" = ( -/obj/structure/table, -/obj/machinery/alarm{ - dir = 4; - pixel_x = -23 - }, -/obj/machinery/recharger, -/turf/floor/tiled/dark/monotile, -/area/exodus/gateway) "bUP" = ( /obj/item/radio/intercom{ pixel_y = 20 @@ -56223,8 +55947,7 @@ /obj/machinery/network/requests_console{ department = "Engineering"; name = "Engineering RC"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /turf/floor/tiled/steel_grid, /area/exodus/engineering/foyer) @@ -60505,8 +60228,7 @@ /obj/machinery/network/requests_console{ department = "Engineering"; name = "Engineering RC"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /obj/structure/reagent_dispensers/watertank, /turf/floor/tiled/steel_grid, @@ -64327,8 +64049,7 @@ "oWh" = ( /obj/machinery/network/requests_console{ department = "Arrival shuttle"; - pixel_y = -32; - dir = 2 + pixel_y = -32 }, /obj/effect/floor_decal/corner/white{ dir = 10 @@ -64485,6 +64206,10 @@ /obj/machinery/light, /turf/floor/bluegrid, /area/exodus/turret_protected/ai_upload) +"rAK" = ( +/obj/structure/girder/displaced, +/turf/floor/plating, +/area/exodus/maintenance/evahallway) "rNu" = ( /obj/structure/table{ name = "plastic table frame" @@ -87835,15 +87560,15 @@ anX anX anX ayI +anX +anX +anX apD apD -apD -apD -apD -apD -apD -apD -apD +anX +anX +anX +anX aLK aNw aPi @@ -88092,15 +87817,15 @@ aie aie anX axM -apD +anX aBM +auc +auc +auc +aAG +auc +auc aBM -aAB -arS -aCP -aGl -bdL -bUO aLK aNy aPi @@ -88350,15 +88075,15 @@ aie anX axM apD -aAA -aBJ -aCL -aEb -aFA -aGK -aIp -aKl -axv +auc +auc +auc +auc +auc +auc +auc +auc +aLK wBc aPi hgF @@ -88606,16 +88331,16 @@ awy aie anX axM -apD -aAD -aBO -aCO -aEg -aFF -aGR -aIt -aKo -aLG +anX +auc +auc +auc +auc +auc +auc +auc +auc +aLK aNA aPq bGp @@ -88863,16 +88588,16 @@ awy awy anX axM -apD -aAC -aBN -aCN -aEf -aFA -aGN -aIs -aKn -axv +anX +auc +aBM +auc +auc +auc +auc +auc +auc +aLK qHU xFu hgF @@ -89120,15 +88845,15 @@ awv awy atc axM -apD -aBM -aBM +anX +auc +auc aAG -asu -aCQ -aGS +auc +auc aBM -aJc +auc +auc aMb aNp aOQ @@ -89377,15 +89102,15 @@ aQt awy axK axM -apD -apD -apD -apD -apD -apD -apD +anX +anX +rAK +anX +anX +anX +anX azI -apD +anX aMb aNE vcu diff --git a/maps/exodus/exodus_areas.dm b/maps/exodus/exodus_areas.dm index 265523d92cb5..29ba42a4e778 100644 --- a/maps/exodus/exodus_areas.dm +++ b/maps/exodus/exodus_areas.dm @@ -1048,10 +1048,6 @@ icon_state = "teleporter" req_access = list(access_teleporter) -/area/exodus/gateway - name = "\improper Gateway" - icon_state = "teleporter" - // Thunderdome /area/tdome diff --git a/maps/exodus/exodus_jobs.dm b/maps/exodus/exodus_jobs.dm index 6ca8b4d8ab41..9310a431e2b1 100644 --- a/maps/exodus/exodus_jobs.dm +++ b/maps/exodus/exodus_jobs.dm @@ -1,11 +1,3 @@ -/decl/spawnpoint/gateway - name = "Gateway" - spawn_announcement = "has completed translation from offsite gateway" - uid = "spawn_exodus_gateway" - -/obj/abstract/landmark/latejoin/gateway - spawn_decl = /decl/spawnpoint/gateway - /datum/map/exodus default_job_type = /datum/job/standard/assistant default_department_type = /decl/department/civilian From 6858bc8f731b89a31d3aec1902c370cf7c6114cf Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:16:37 -0400 Subject: [PATCH 0705/1331] Remove some entirely-unused variables --- code/_global_vars/lists/names.dm | 2 -- code/game/turfs/floors/_floor.dm | 1 - code/game/turfs/turf.dm | 1 - code/game/turfs/walls/_wall.dm | 1 - code/modules/crafting/metalwork/metalwork_items.dm | 1 - .../maps/template_types/random_exoplanet/planetoid_data.dm | 4 ---- code/modules/materials/_materials.dm | 1 - code/modules/mob/living/simple_animal/friendly/cat.dm | 3 --- code/modules/multiz/map_data.dm | 1 - code/modules/organs/external/_external.dm | 1 - code/modules/overmap/ftl_shunt/core.dm | 1 - code/modules/overmap/internet/internet_uplink.dm | 1 - code/modules/overmap/ships/computers/comms.dm | 1 - code/modules/prometheus_metrics/metrics.dm | 3 --- code/modules/scent/_scent.dm | 4 ---- code/modules/status_conditions/_status_condition.dm | 3 --- 16 files changed, 29 deletions(-) diff --git a/code/_global_vars/lists/names.dm b/code/_global_vars/lists/names.dm index 2bd23f308908..78cd73fe25b0 100644 --- a/code/_global_vars/lists/names.dm +++ b/code/_global_vars/lists/names.dm @@ -1,8 +1,6 @@ // All variables here use double quotes to able load information on every startup. var/global/list/ai_names = file2list("config/names/ai.txt") -var/global/list/wizard_first = file2list("config/names/wizardfirst.txt") -var/global/list/wizard_second = file2list("config/names/wizardsecond.txt") var/global/list/verbs = file2list("config/names/verbs.txt") var/global/list/adjectives = file2list("config/names/adjectives.txt") diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index a2a53460c0a8..d92467b7e3ef 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -5,7 +5,6 @@ layer = PLATING_LAYER permit_ao = TRUE thermal_conductivity = 0.040 - heat_capacity = 10000 explosion_resistance = 1 turf_flags = TURF_IS_HOLOMAP_PATH initial_gas = GAS_STANDARD_AIRMIX diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 94b6618f8d86..285f4fc05c5c 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -19,7 +19,6 @@ //Properties for airtight tiles (/wall) var/thermal_conductivity = 0.05 - var/heat_capacity = 1 //Properties for both /// Does this turf contain air/let air through? diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 22141ceed4d8..ef02c8f94328 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -25,7 +25,6 @@ var/global/list/wall_fullblend_objects = list( density = TRUE blocks_air = 1 thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall explosion_resistance = 10 color = COLOR_STEEL turf_flags = TURF_IS_HOLOMAP_OBSTACLE diff --git a/code/modules/crafting/metalwork/metalwork_items.dm b/code/modules/crafting/metalwork/metalwork_items.dm index 5278aaf343ec..2727db4ada8f 100644 --- a/code/modules/crafting/metalwork/metalwork_items.dm +++ b/code/modules/crafting/metalwork/metalwork_items.dm @@ -17,7 +17,6 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME storage = /datum/storage/crucible obj_flags = OBJ_FLAG_NO_STORAGE - var/max_held = 10 /obj/item/chems/crucible/attackby(obj/item/used_item, mob/user) diff --git a/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm b/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm index c113ec46b77b..95df86b82f93 100644 --- a/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm +++ b/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm @@ -30,10 +30,6 @@ var/habitability_class = HABITABILITY_DEAD ///The cached planet's atmosphere that sub-levels of this planet should use. Can be a type path at definition, and an instance at runtime. var/datum/gas_mixture/atmosphere - ///The minimum temperature that can be reached on the planet.(For instance via meteo or sunlight/shade or whatever) - var/temperature_min = 0 CELSIUS - ///The maximum temperature that can be reached on the planet.(For instance via meteo or sunlight/shade or whatever) - var/temperature_max = 25 CELSIUS ///What weather state to use for this planet initially. If null, will not initialize any weather system. Must be a typepath rather than an instance. var/decl/state/weather/initial_weather_state = /decl/state/weather/calm diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index dba95990092c..5fe6466f5371 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -119,7 +119,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) var/shard_name = SHARD_SHRAPNEL as text // Path of debris object. var/shard_icon // Related to above. var/shard_can_repair = 1 // Can shards be turned into sheets with a welder? - var/list/recipes // Holder for all recipes usable with a sheet of this material. var/destruction_desc = "breaks apart" // Fancy string for barricades/tables/objects exploding. var/destruction_sound = "fracture" // As above, but the sound that plays. diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 50c1c1aae4f0..c36ff3e143b3 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -59,9 +59,6 @@ butchery_data = /decl/butchery_data/animal/cat base_animal_type = /mob/living/simple_animal/passive/cat ai = /datum/mob_controller/passive/hunter/cat - var/turns_since_scan = 0 - var/mob/living/simple_animal/passive/mouse/movement_target - var/mob/flee_target /mob/living/simple_animal/passive/cat/get_bodytype() return GET_DECL(/decl/bodytype/quadruped/animal/cat) diff --git a/code/modules/multiz/map_data.dm b/code/modules/multiz/map_data.dm index ea7f09545111..83d4f972daa2 100644 --- a/code/modules/multiz/map_data.dm +++ b/code/modules/multiz/map_data.dm @@ -4,7 +4,6 @@ icon_state = "map_data" var/height = 1 ///< The number of Z-Levels in the map. - var/turf/edge_type ///< What the map edge should be formed with. (null = world.turf) // If the height is more than 1, we mark all contained levels as connected. // This initializes immediately because it is an auxiliary effect specifically needed pre-SSatoms init. diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index d2ab14efc169..3f62d4a17485 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -49,7 +49,6 @@ var/list/children // Sub-limbs. var/list/internal_organs // Internal organs of this body part var/list/implants // Currently implanted objects. - var/base_miss_chance = 20 // Chance of missing. var/genetic_degradation = 0 // Amount of current genetic damage. //Forensics stuff diff --git a/code/modules/overmap/ftl_shunt/core.dm b/code/modules/overmap/ftl_shunt/core.dm index fe8407d230e7..ca95cf968b9b 100644 --- a/code/modules/overmap/ftl_shunt/core.dm +++ b/code/modules/overmap/ftl_shunt/core.dm @@ -17,7 +17,6 @@ var/shunt_x = 1 var/shunt_y = 1 var/chargepercent = 0 - var/last_percent_tick = 0 var/obj/machinery/computer/ship/ftl/ftl_computer var/required_fuel_joules var/required_charge //This is a function of the required fuel joules. diff --git a/code/modules/overmap/internet/internet_uplink.dm b/code/modules/overmap/internet/internet_uplink.dm index 53e7e4eb923b..8c32bc016983 100644 --- a/code/modules/overmap/internet/internet_uplink.dm +++ b/code/modules/overmap/internet/internet_uplink.dm @@ -164,7 +164,6 @@ var/global/list/internet_uplinks = list() idle_power_usage = 250 active_power_usage = 500 var/initial_id_tag = "plexus" - var/current_uplink = 1 /obj/machinery/computer/internet_uplink/Initialize() . = ..() diff --git a/code/modules/overmap/ships/computers/comms.dm b/code/modules/overmap/ships/computers/comms.dm index 6789f5bc68d0..3978b1a4c58f 100644 --- a/code/modules/overmap/ships/computers/comms.dm +++ b/code/modules/overmap/ships/computers/comms.dm @@ -109,7 +109,6 @@ req_access = list(access_tcomsat) icon = 'icons/obj/machines/tcomms/comms.dmi' var/effective_range = 1 // TODO: upgrade with components. - var/enabled = TRUE /obj/machinery/shipcomms/proc/toggle() if((stat & BROKEN) || use_power != POWER_USE_OFF) diff --git a/code/modules/prometheus_metrics/metrics.dm b/code/modules/prometheus_metrics/metrics.dm index ad98b6c178f9..7a5bca9be93a 100644 --- a/code/modules/prometheus_metrics/metrics.dm +++ b/code/modules/prometheus_metrics/metrics.dm @@ -1,8 +1,5 @@ // prometheus_metrics holds a list of metric_family datums and uses them to // create a json protobuf. -/decl/prometheus_metrics - var/list/metric_families - /decl/prometheus_metrics/proc/collect() var/list/out = list() diff --git a/code/modules/scent/_scent.dm b/code/modules/scent/_scent.dm index b002b8a43c63..68a96e3aa0b8 100644 --- a/code/modules/scent/_scent.dm +++ b/code/modules/scent/_scent.dm @@ -1,7 +1,3 @@ -var/global/const/SCENT_DESC_ODOR = "odour" -var/global/const/SCENT_DESC_SMELL = "smell" -var/global/const/SCENT_DESC_FRAGRANCE = "fragrance" - /***** Scent intensity *****/ diff --git a/code/modules/status_conditions/_status_condition.dm b/code/modules/status_conditions/_status_condition.dm index 285748a63b88..a377bed14b3d 100644 --- a/code/modules/status_conditions/_status_condition.dm +++ b/code/modules/status_conditions/_status_condition.dm @@ -7,9 +7,6 @@ var/global/list/status_marker_holders = list() var/check_flags = 0 var/list/victim_data - var/hud_icon - var/hud_state - var/decl/mob_modifier/associated_mob_modifier var/status_marker_icon = 'icons/effects/status.dmi' From 17031fa81a368cda2d07d1d6c2c788d62e04bc43 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:26:35 -0400 Subject: [PATCH 0706/1331] Remove entirely-unused procs --- .../preference_setup/loadout/loadout.dm | 3 -- code/modules/mob/living/human/human_organs.dm | 11 ----- .../modular_computers/networking/_network.dm | 5 -- code/modules/organs/external/standard.dm | 3 -- code/modules/organs/internal/lungs.dm | 6 --- .../overmap/ships/computers/shuttle.dm | 5 -- code/modules/power/sensors/powernet_sensor.dm | 49 ------------------- .../particle_accelerator/particle_emitter.dm | 7 --- .../random_map/dungeon/winding_dungeon.dm | 4 -- .../reagents/chems/random/chems_random.dm | 7 --- code/modules/reagents/reactions/_reaction.dm | 3 -- .../modules/security_levels/security_state.dm | 7 --- 12 files changed, 110 deletions(-) diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index 0e6fe8ed2887..d29b21ba850e 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -571,9 +571,6 @@ else to_chat(wearer, SPAN_DANGER("Dropping \the [item] on the ground!")) -/decl/loadout_option/proc/can_replace_existing(obj/item/candidate) - return istype(candidate, path) - /decl/loadout_option/proc/spawn_and_validate_item(mob/living/human/H, metadata) PRIVATE_PROC(TRUE) diff --git a/code/modules/mob/living/human/human_organs.dm b/code/modules/mob/living/human/human_organs.dm index 85c54cca6b24..a9dd1c86d45f 100644 --- a/code/modules/mob/living/human/human_organs.dm +++ b/code/modules/mob/living/human/human_organs.dm @@ -205,17 +205,6 @@ if (wound.infection_check()) wound.germ_level += 1 -/mob/living/human/proc/Check_Proppable_Object() - for(var/turf/T as anything in RANGE_TURFS(src, 1)) //we only care for non-space turfs - if(T.density && T.simulated) //walls work - return 1 - - for(var/obj/O in orange(1, src)) - if(O && O.density && O.anchored) - return 1 - - return 0 - /mob/living/human/on_lost_organ(var/obj/item/organ/O) if(!(. = ..())) return diff --git a/code/modules/modular_computers/networking/_network.dm b/code/modules/modular_computers/networking/_network.dm index 73adef3ab7d6..7ba3db6b0112 100644 --- a/code/modules/modular_computers/networking/_network.dm +++ b/code/modules/modular_computers/networking/_network.dm @@ -137,11 +137,6 @@ add_device(D) add_log("New main router set.", router.network_tag) -/datum/computer_network/proc/set_access_controller(datum/extension/network_device/D) - access_controller = D - devices |= D - add_log("New main access controller set.", D.network_tag) - // Returns list(signal type, signal strength) on success, null on failure. /datum/computer_network/proc/check_connection(datum/extension/network_device/D, specific_action) if(!router) diff --git a/code/modules/organs/external/standard.dm b/code/modules/organs/external/standard.dm index 84cb21e75a31..85c880a0a84c 100644 --- a/code/modules/organs/external/standard.dm +++ b/code/modules/organs/external/standard.dm @@ -20,9 +20,6 @@ cavity_name = "thoracic cavity" limb_flags = ORGAN_FLAG_HEALS_OVERKILL | ORGAN_FLAG_CAN_BREAK -/obj/item/organ/external/chest/proc/get_current_skin() - return - /obj/item/organ/external/chest/die() //Special handling for synthetics if(BP_IS_PROSTHETIC(src) || BP_IS_CRYSTAL(src)) diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 6fdca0adbeb1..003f7b851d15 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -63,12 +63,6 @@ . = ..() sync_breath_types() -// This call needs to be split out to make sure that all the ingested things are metabolised -// before the process call is made on any of the other organs -/obj/item/organ/internal/lungs/proc/metabolize() - if(is_usable()) - inhaled.metabolize() - /** * Set these lungs' breath types based on the lungs' species */ diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm index 6543aebddfc0..4f2e43fbe4cf 100644 --- a/code/modules/overmap/ships/computers/shuttle.dm +++ b/code/modules/overmap/ships/computers/shuttle.dm @@ -130,11 +130,6 @@ qdel(lz) to_chat(user, SPAN_WARNING("Invalid landing zone!")) -/obj/machinery/computer/shuttle_control/proc/end_landing() - var/datum/extension/eye/landing_eye = get_extension(src, /datum/extension/eye/) - if(landing_eye) - landing_eye.unlook() - /obj/machinery/computer/shuttle_control/explore/power_change() . = ..() if(. && (stat & (NOPOWER|BROKEN))) diff --git a/code/modules/power/sensors/powernet_sensor.dm b/code/modules/power/sensors/powernet_sensor.dm index 6a0134664cd3..66e5cee66f83 100644 --- a/code/modules/power/sensors/powernet_sensor.dm +++ b/code/modules/power/sensors/powernet_sensor.dm @@ -88,55 +88,6 @@ return L - -// Proc: return_reading_text() -// Parameters: None -// Description: Generates string which contains HTML table with reading data. -/obj/machinery/power/sensor/proc/return_reading_text() - // No powernet. Try to connect to one first. - if(!powernet) - connect_to_network() - var/out = "" - if(!powernet) // No powernet. - out = "# SYSTEM ERROR - NO POWERNET #" - return out - - - var/list/L = find_apcs() - var/total_apc_load = 0 - if(L.len <= 0) // No APCs found. - out = "No APCs located in connected powernet!" - else // APCs found. Create very ugly (but working!) HTML table. - - out += "
    NameEQUIPLIGHTENVIRONCELLLOAD" - - // These lists are used as replacement for number based APC settings - var/list/S = list("M-OFF","A-OFF","M-ON", "A-ON") - var/list/chg = list("N","C","F") - - // Split to multiple lines to make it more readable - for(var/obj/machinery/power/apc/A in L) - out += "
    \The [A.area]" // Add area name - out += "[S[A.equipment+1]][S[A.lighting+1]][S[A.environ+1]]" // Show status of channels - var/obj/item/cell/cell = A.get_cell() - if(cell) - out += "[round(cell.percent())]% - [chg[A.charging+1]]" - else - out += "NO CELL" - var/load = A.lastused_total // Load. - total_apc_load += load - load = reading_to_text(load) - out += "[load]" - - out += "
    TOTAL AVAILABLE: [reading_to_text(powernet.avail)]" - out += "
    APC LOAD: [reading_to_text(total_apc_load)]" - out += "
    OTHER LOAD: [reading_to_text(max(powernet.load - total_apc_load, 0))]" - out += "
    TOTAL GRID LOAD: [reading_to_text(powernet.viewload)] ([round((powernet.load / powernet.avail) * 100)]%)" - - if(powernet.problem) - out += "
    WARNING: Abnormal grid activity detected!" - return out - // Proc: return_reading_data() // Parameters: None // Description: Generates list containing all powernet data. Optimised for usage with NanoUI diff --git a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm b/code/modules/power/singularity/particle_accelerator/particle_emitter.dm index eba322bb19fb..7cfffcf5a8f1 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_emitter.dm @@ -20,13 +20,6 @@ icon_state = "emitter_right" reference = "emitter_right" -/obj/structure/particle_accelerator/particle_emitter/proc/set_delay(var/delay) - if(delay && delay >= 0) - src.fire_delay = delay - return 1 - return 0 - - /obj/structure/particle_accelerator/particle_emitter/proc/emit_particle(var/strength = 0) if((src.last_shot + src.fire_delay) <= world.time) src.last_shot = world.time diff --git a/code/modules/random_map/dungeon/winding_dungeon.dm b/code/modules/random_map/dungeon/winding_dungeon.dm index 1369aca46e92..1ecf9649028e 100644 --- a/code/modules/random_map/dungeon/winding_dungeon.dm +++ b/code/modules/random_map/dungeon/winding_dungeon.dm @@ -313,10 +313,6 @@ rooms += R return 1 -/datum/random_map/winding_dungeon/proc/add_loot(var/xorigin,var/yorigin,var/zorigin,var/type) - var/datum/room/room = pick(rooms) - return room.add_loot(type) - /datum/random_map/winding_dungeon/get_appropriate_path(var/value) switch(value) if(WALL_CHAR) diff --git a/code/modules/reagents/chems/random/chems_random.dm b/code/modules/reagents/chems/random/chems_random.dm index a38b74bc572c..a7418e164642 100644 --- a/code/modules/reagents/chems/random/chems_random.dm +++ b/code/modules/reagents/chems/random/chems_random.dm @@ -62,19 +62,12 @@ var/global/list/random_chem_interaction_blacklist = list( data_initialized = TRUE -/decl/material/liquid/random/proc/stable_at_temperature(temperature) - if(temperature > chilling_point && temperature < heating_point) - return TRUE - /decl/material/liquid/random/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() FOR_ALL_EFFECTS var/data = REAGENT_DATA(holder, src) effect.affect_blood(M, removed, LAZYACCESS(data, effect.type)) -/decl/material/liquid/random/proc/on_chemicals_analyze(mob/user) - to_chat(user, get_scan_data(user)) - /decl/material/liquid/random/proc/get_scan_data(mob/user) var/list/dat = list() var/chem_skill = user.get_skill_value(SKILL_CHEMISTRY) diff --git a/code/modules/reagents/reactions/_reaction.dm b/code/modules/reagents/reactions/_reaction.dm index fbc2eaa85d68..3a4b41044b5c 100644 --- a/code/modules/reagents/reactions/_reaction.dm +++ b/code/modules/reagents/reactions/_reaction.dm @@ -49,9 +49,6 @@ for(var/reagent in required_reagents) . += reagent -/decl/chemical_reaction/proc/get_alternate_reaction_indicator(var/datum/reagents/holder) - return 0 - /decl/chemical_reaction/proc/process(var/datum/reagents/holder, var/limit) var/data = send_data(holder) diff --git a/code/modules/security_levels/security_state.dm b/code/modules/security_levels/security_state.dm index 2e997845d411..73945c3c1358 100644 --- a/code/modules/security_levels/security_state.dm +++ b/code/modules/security_levels/security_state.dm @@ -127,10 +127,3 @@ log_and_message_admins("has changed the security level from [previous_security_level.name] to [new_security_level.name].") return TRUE - -// This proc decreases the current security level, if possible -/decl/security_state/proc/decrease_security_level(var/force_change = FALSE) - var/current_index = all_security_levels.Find(current_security_level) - if(current_index == 1) - return FALSE - return set_security_level(all_security_levels[current_index - 1], force_change) From 5b4d00919cc863de7938e3f3e256e9e2103b2289 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 15 Jun 2025 01:05:29 +0000 Subject: [PATCH 0707/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ tools/mapcapturemerge/README.md | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 928bd534bf95..f606e802e6ca 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,12 +57,6 @@

    Zandario updated:

    • Added a "Toggle Browser Inspect" debug verb for debugging UIs
    - -

    13 April 2025

    -

    Cerebulon updated:

    -
      -
    • Fixed tajaran sprite sheet overrides.
    • -
    diff --git a/tools/mapcapturemerge/README.md b/tools/mapcapturemerge/README.md index 5606211b2e65..15f398267edf 100644 --- a/tools/mapcapturemerge/README.md +++ b/tools/mapcapturemerge/README.md @@ -1,11 +1,11 @@ -Map capture merge tool manual -====== - -1. Capture desired Z levels using in game verb `Capture-map` -2. Before closing game window, go to cache folder usually located under Documents/BYOND and copy all map capture files named like `map_capture_x1_y33_z1_r32.png` to folder under this tool called `captures`. -3. Install *Python* and *Pillow* library (If you don't have them already). -4. Run `python merge.py` and wait for captures to be merged. -5. After execution you will find all z-level merged maps in the mapcapturemerge tool folder. - -Notes: +Map capture merge tool manual +====== + +1. Capture desired Z levels using in game verb `Capture-map` +2. Before closing game window, go to cache folder usually located under Documents/BYOND and copy all map capture files named like `map_capture_x1_y33_z1_r32.png` to folder under this tool called `captures`. +3. Install *Python* and *Pillow* library (If you don't have them already). +4. Run `python merge.py` and wait for captures to be merged. +5. After execution you will find all z-level merged maps in the mapcapturemerge tool folder. + +Notes: * This tool been tested with Python 2.7.6 under Windows 10 Bash. \ No newline at end of file From 7939f8c6fa4bc106b9abb6773a808c10cebc6b67 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:25:18 -0400 Subject: [PATCH 0708/1331] Improve computer scanner and printer behavior --- .../programs/engineering/network_monitoring.dm | 9 +++++---- .../file_system/programs/generic/email_client.dm | 6 +++--- .../file_system/programs/generic/file_manager.dm | 2 +- .../file_system/programs/generic/scanner.dm | 1 + .../programs/research/email_administration.dm | 2 +- .../modular_computers/hardware/scanners/scanner_paper.dm | 2 +- .../modular_computers/networking/emails/_email.dm | 1 + .../modular_computers/networking/network_files.dm | 1 + code/modules/modular_computers/os/components.dm | 4 ++-- 9 files changed, 16 insertions(+), 12 deletions(-) diff --git a/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm b/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm index 0b111c2306ae..779fc79ec5af 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm @@ -57,9 +57,10 @@ mainframes.Add(list(rdata)) data["mainframes"] = mainframes - if(length(network.get_mainframes_by_role(MF_ROLE_LOG_SERVER, user.GetAccess()))) + var/list/eligible_log_servers = network.get_mainframes_by_role(MF_ROLE_LOG_SERVER, user.GetAccess()) + if(length(eligible_log_servers)) var/list/logs[0] - for(var/datum/extension/network_device/mainframe/M in network.get_mainframes_by_role(MF_ROLE_LOG_SERVER, user)) + for(var/datum/extension/network_device/mainframe/M in eligible_log_servers) var/list/logdata[0] var/datum/computer_file/data/logfile/F = M.get_file("network_log", OS_LOGS_DIR, TRUE) if(F) @@ -143,9 +144,9 @@ if(href_list["toggle_function"]) var/feature = text2num(href_list["toggle_function"]) if(network.network_features_enabled & feature) - network.network_features_enabled &= ~feature + network.disable_network_feature(feature) else - network.network_features_enabled |= feature + network.enable_network_feature(feature) return TOPIC_REFRESH if(href_list["ban_nid"]) 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 61536217f86a..9bfa457e8487 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 @@ -80,7 +80,7 @@ var/list/msg = list() msg += "*--*\n" msg += "New mail received from [received_message.source]:\n" - msg += "Subject: [received_message.title]\nMessage:\n[digitalPencode2html(received_message.stored_data)]\n" + msg += "Subject: [received_message.title]\nMessage:\n[received_message.generate_file_data()]\n" if(received_message.attachment) msg += "Attachment: [received_message.attachment.filename].[received_message.attachment.filetype] ([received_message.attachment.size]GQ)\n" msg += "Reply\n" @@ -158,7 +158,7 @@ data["msg_attachment_size"] = msg_attachment.size else if (current_message) data["cur_title"] = current_message.title - data["cur_body"] = digitalPencode2html(current_message.stored_data) + data["cur_body"] = current_message.generate_file_data() data["cur_timestamp"] = current_message.timestamp data["cur_source"] = current_message.source data["cur_uid"] = current_message.uid @@ -187,7 +187,7 @@ for(var/datum/computer_file/data/email_message/message in message_source) all_messages.Add(list(list( "title" = message.title, - "body" = digitalPencode2html(message.stored_data), + "body" = message.generate_file_data(), "source" = message.source, "timestamp" = message.timestamp, "uid" = message.uid diff --git a/code/modules/modular_computers/file_system/programs/generic/file_manager.dm b/code/modules/modular_computers/file_system/programs/generic/file_manager.dm index be3f39c066ca..546aa102bf43 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_manager.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_manager.dm @@ -375,7 +375,7 @@ if(!(F.get_file_perms(accesses, user) & OS_READ_ACCESS)) file_error = "You do not have read access to this file." return - if(!computer.print_paper(digitalPencode2html(F.stored_data), F.filename)) + if(!computer.print_paper(F.generate_file_data(), F.filename, F.papertype, F.metadata)) file_error = "Hardware error: Unable to print the file." return TOPIC_REFRESH diff --git a/code/modules/modular_computers/file_system/programs/generic/scanner.dm b/code/modules/modular_computers/file_system/programs/generic/scanner.dm index ff721374a1cc..1e04b68050c7 100644 --- a/code/modules/modular_computers/file_system/programs/generic/scanner.dm +++ b/code/modules/modular_computers/file_system/programs/generic/scanner.dm @@ -78,6 +78,7 @@ var/datum/computer_file/data/scan_file = new scan_file_type() scan_file.stored_data = data_buffer + scan_file.metadata = metadata_buffer // This saves the file, so no additional handling on the program's end is required. view_file_browser(usr, "saving_file", scan_file_type, OS_WRITE_ACCESS, "Save scan file", scan_file) diff --git a/code/modules/modular_computers/file_system/programs/research/email_administration.dm b/code/modules/modular_computers/file_system/programs/research/email_administration.dm index 95d2a9410005..ea3ebbe44109 100644 --- a/code/modules/modular_computers/file_system/programs/research/email_administration.dm +++ b/code/modules/modular_computers/file_system/programs/research/email_administration.dm @@ -39,7 +39,7 @@ data["error"] = error else if(istype(current_message)) data["msg_title"] = current_message.title - data["msg_body"] = digitalPencode2html(current_message.stored_data) + data["msg_body"] = current_message.generate_file_data() data["msg_timestamp"] = current_message.timestamp data["msg_source"] = current_message.source else if(istype(current_account)) diff --git a/code/modules/modular_computers/hardware/scanners/scanner_paper.dm b/code/modules/modular_computers/hardware/scanners/scanner_paper.dm index a0d0c430ab1f..e5843c442212 100644 --- a/code/modules/modular_computers/hardware/scanners/scanner_paper.dm +++ b/code/modules/modular_computers/hardware/scanners/scanner_paper.dm @@ -27,7 +27,7 @@ driver.scan_file_type = target.scan_file_type if(target.type == /obj/item/paper/bodyscan) - driver.data_buffer = display_medical_data(target.metadata.Copy(),user.get_skill_value(SKILL_MEDICAL), TRUE) + driver.data_buffer = display_medical_data(driver.metadata_buffer,user.get_skill_value(SKILL_MEDICAL), TRUE) else driver.data_buffer = data diff --git a/code/modules/modular_computers/networking/emails/_email.dm b/code/modules/modular_computers/networking/emails/_email.dm index e522a1dcbd76..7b36c9fb7b9a 100644 --- a/code/modules/modular_computers/networking/emails/_email.dm +++ b/code/modules/modular_computers/networking/emails/_email.dm @@ -28,6 +28,7 @@ var/datum/computer_file/data/email_message/received_copy = received.Clone() received_copy.set_timestamp() recipient.inbox.Add(received_copy) + recipient.receive_mail(received_copy, src) for(var/weakref/os_ref in recipient.logged_in_os) var/datum/extension/interactive/os/os = os_ref.resolve() diff --git a/code/modules/modular_computers/networking/network_files.dm b/code/modules/modular_computers/networking/network_files.dm index 34229d711e23..ece73f53cbaa 100644 --- a/code/modules/modular_computers/networking/network_files.dm +++ b/code/modules/modular_computers/networking/network_files.dm @@ -90,6 +90,7 @@ continue // We only check if user is provided. If no user is provided, it's assumed to be an admin check. . |= M.network_tag +/// Returns the first (order is arbitrarily decided) fileserver with a given mainframe role. /datum/computer_network/proc/get_file_server_by_role(role, list/accesses) if(length(get_mainframes_by_role(role, accesses)) > 0) return get_mainframes_by_role(role, accesses)[1] diff --git a/code/modules/modular_computers/os/components.dm b/code/modules/modular_computers/os/components.dm index 5a50bd135e16..c3ea4c0134d7 100644 --- a/code/modules/modular_computers/os/components.dm +++ b/code/modules/modular_computers/os/components.dm @@ -14,10 +14,10 @@ /datum/extension/interactive/os/proc/has_component(var/part_type) return !!get_component(part_type) -/datum/extension/interactive/os/proc/print_paper(content, title) +/datum/extension/interactive/os/proc/print_paper(content, title, paper_type, list/metadata) var/obj/item/stock_parts/computer/nano_printer/printer = get_component(PART_PRINTER) if(printer) - return printer.print_text(content, title) + return printer.print_text(content, title, paper_type, metadata) /datum/extension/interactive/os/proc/get_network_tag() var/obj/item/stock_parts/computer/network_card/network_card = get_component(PART_NETWORK) From 909e9ce566cac064cf30abfd2678e5867faa97bd Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:29:21 -0400 Subject: [PATCH 0709/1331] Implement unused vars in paperwork code --- code/modules/paperwork/paper_sticky.dm | 26 +++++++++++++------------- code/modules/paperwork/photography.dm | 20 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/code/modules/paperwork/paper_sticky.dm b/code/modules/paperwork/paper_sticky.dm index 05e989afe20f..d8acfabf7598 100644 --- a/code/modules/paperwork/paper_sticky.dm +++ b/code/modules/paperwork/paper_sticky.dm @@ -2,18 +2,18 @@ // Sticky Note Pad //////////////////////////////////////////////// /obj/item/sticky_pad - name = "sticky note pad" - desc = "A pad of densely packed sticky notes." - color = COLOR_YELLOW - icon = 'icons/obj/stickynotes.dmi' - icon_state = "pad_full" - item_state = "paper" - w_class = ITEM_SIZE_SMALL - material = /decl/material/solid/organic/paper - var/papers = 50 - var/tmp/max_papers = 50 - var/paper_type = /obj/item/paper/sticky - var/obj/item/paper/top //The instantiated paper on the top of the pad, if there's one + name = "sticky note pad" + desc = "A pad of densely packed sticky notes." + color = COLOR_YELLOW + icon = 'icons/obj/stickynotes.dmi' + icon_state = "pad_full" + item_state = "paper" + w_class = ITEM_SIZE_SMALL + material = /decl/material/solid/organic/paper + var/const/max_papers = 50 + var/papers = max_papers + var/paper_type = /obj/item/paper/sticky + var/obj/item/paper/top //The instantiated paper on the top of the pad, if there's one /obj/item/sticky_pad/Initialize(ml, material_key) . = ..() @@ -35,7 +35,7 @@ . = ..() if(papers <= 15) icon_state = "pad_empty" - else if(papers <= 50) + else if(papers <= max_papers) icon_state = "pad_used" else icon_state = "pad_full" diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index c23a21c46fb9..51778e1f5e05 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -10,16 +10,16 @@ * film * *******/ /obj/item/camera_film - name = "film cartridge" - icon = 'icons/obj/photography.dmi' - desc = "A camera film cartridge. Insert it into a camera to reload it." - icon_state = "film" - item_state = "electropack" - w_class = ITEM_SIZE_SMALL - throw_range = 10 - material = /decl/material/solid/organic/plastic - var/tmp/max_uses = 10 - var/uses_left = 10 + name = "film cartridge" + icon = 'icons/obj/photography.dmi' + desc = "A camera film cartridge. Insert it into a camera to reload it." + icon_state = "film" + item_state = "electropack" + w_class = ITEM_SIZE_SMALL + throw_range = 10 + material = /decl/material/solid/organic/plastic + var/const/max_uses = 10 + var/uses_left = max_uses /obj/item/camera_film/Initialize(ml, material_key) set_extension(src, /datum/extension/base_icon_state, icon_state) From 1e66a43682119602eb012546908bea78069fe331 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:29:51 -0400 Subject: [PATCH 0710/1331] Clean unused fax machine code --- code/modules/paperwork/faxmachine.dm | 38 ---------------------------- 1 file changed, 38 deletions(-) diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index cabe90556973..fd66d7857a94 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -37,18 +37,6 @@ var/global/list/adminfaxes = list() //cache for faxes that have been sent to has_commands = TRUE long_range = TRUE -//////////////////////////////////////////////////////////////////////////////////////// -// Fax Machine Quick-Dial file -//////////////////////////////////////////////////////////////////////////////////////// -/datum/computer_file/data/fax_quick_dial - filetype = "FQD" - -/datum/computer_file/data/fax_quick_dial/proc/save_quick_dial(var/list/quick_dial_list) - stored_data = json_encode(quick_dial_list) - -/datum/computer_file/data/fax_quick_dial/proc/load_quick_dial() - return json_decode(stored_data) - //////////////////////////////////////////////////////////////////////////////////////// // Fax Machine //////////////////////////////////////////////////////////////////////////////////////// @@ -184,8 +172,6 @@ var/global/list/adminfaxes = list() //cache for faxes that have been sent to LAZYSET(., "has_disk_drive", !isnull(disk_reader)) LAZYSET(., "disk", D) LAZYSET(., "disk_name", D?.name) - LAZYSET(., "disk_has_qd", D?.contains_file_type("FQD")) //If the disk has a quick dial file - LAZYSET(., "disk_has_file", (D?.free_blocks < D?.block_capacity)) /obj/machinery/faxmachine/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, datum/nanoui/master_ui, datum/topic_state/state) var/list/data = ui_data(user, ui_key) @@ -556,30 +542,6 @@ var/global/list/adminfaxes = list() //cache for faxes that have been sent to SSnano.update_uis(src) update_icon() -/**Check if the card we inserted has enough credentials to print on the target fax machine on the network. */ -/obj/machinery/faxmachine/proc/can_send_fax_to(var/network_tag, var/network_id, var/list/provided_access) - var/datum/extension/network_device/fax/sender = get_extension(src, /datum/extension/network_device) - var/datum/computer_network/sender_net = sender?.get_network() - if(!sender_net) - return FALSE - if((network_id != sender_net.network_id) && !sender.has_internet_connection(network_id)) - return FALSE - - //Handle fake admin network addresses - var/target_uri = uppertext("[network_tag].[network_id]") - if(target_uri in global.using_map.map_admin_faxes) - var/list/admin_faxes = LAZYACCESS(global.using_map.map_admin_faxes, target_uri) - var/list/required_access = LAZYACCESS(admin_faxes, "access") - return has_access(required_access, provided_access) //With access we can send faxes to the selected admin address - - var/datum/computer_network/target_net - if(network_id != sender_net.network_id) - target_net = sender_net?.get_internet_connection(network_id) - else - target_net = sender_net - var/datum/extension/network_device/fax/target = target_net?.get_device_by_tag(network_tag) - return istype(target) && target.has_access(provided_access) - /**Plays print animation async. */ /obj/machinery/faxmachine/proc/on_printed_page() flick("faxreceive", src) From 7c36b36882e695e577bb6de4129b0906bfad5ab8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:31:02 -0400 Subject: [PATCH 0711/1331] Clean up and improve photocopier code --- code/modules/paperwork/photocopier.dm | 17 +++++++++++------ nano/templates/photocopier.tmpl | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index b0fbf90ee7a9..369418bb480e 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -40,8 +40,6 @@ var/obj/item/scanner_item //what's in the scanner var/obj/item/stock_parts/printer/printer //What handles the printing queue var/tmp/max_copies = 10 //how many copies can be copied at once- idea shamelessly stolen from bs12's copier! - var/tmp/busy = FALSE //Whether we should allow people to mess with the settings and contents - var/accept_refill = FALSE //Whether we should handle attackby paper to be sent to the paper bin, or to the scanner slot var/total_printing = 0 //The total number of pages we are printing in the current run /obj/machinery/photocopier/Initialize(mapload, d=0, populate_parts = TRUE) @@ -79,7 +77,7 @@ //Warning lights if(scanner_item) add_overlay("photocopier_ready") - if(!printer?.has_enough_to_print()) + if(!has_enough_to_print()) add_overlay("photocopier_bad") /obj/machinery/photocopier/proc/update_ui() @@ -107,7 +105,7 @@ else required_toner = TONER_USAGE_PAPER - if(!printer?.has_enough_to_print(required_toner, required_paper * copy_amount)) + if(!has_enough_to_print(required_toner, required_paper * copy_amount)) buzz("Warning: Not enough paper or toner!") return FALSE @@ -197,10 +195,14 @@ queue_copies(sanitize_integer(text2num(href_list["copy_amount"]), 1, max_copies, 1)) return TOPIC_REFRESH + if(href_list["cancel_queue"]) + stop_processing_queue() + return TOPIC_REFRESH + if(href_list["aipic"]) if(!issilicon(user)) return TOPIC_NOACTION - if(printer?.has_enough_to_print(TONER_USAGE_PHOTO)) + if(has_enough_to_print(TONER_USAGE_PHOTO)) var/mob/living/silicon/tempAI = user var/obj/item/camera/siliconcam/camera = tempAI.silicon_camera if(!camera) @@ -244,10 +246,13 @@ return TRUE /obj/machinery/photocopier/attackby(obj/item/used_item, mob/user) + if(printer.is_printing()) + to_chat(user, SPAN_WARNING("\The [src] is busy!")) + return TRUE if(istype(construct_state, /decl/machine_construction/default/panel_closed) && (istype(used_item, /obj/item/paper) || istype(used_item, /obj/item/photo) || istype(used_item, /obj/item/paper_bundle))) insert_item(used_item, user) return TRUE - return..() //Components attackby will handle refilling with paper and toner + return ..() //Components attackby will handle refilling with paper and toner /**Creates a clone of the specified item. Returns a list of cloned items. */ /obj/machinery/photocopier/proc/scan_item(var/obj/item/I) diff --git a/nano/templates/photocopier.tmpl b/nano/templates/photocopier.tmpl index 83a0434252ea..d76458b93026 100644 --- a/nano/templates/photocopier.tmpl +++ b/nano/templates/photocopier.tmpl @@ -10,7 +10,7 @@
    -
    +
    0) && (copies_left < copies_max))? copies_left : copies_max}} {{:copies_left == 0? 'disabled' : ''}} /> @@ -36,6 +36,7 @@ {{else}}
    Printing Page
    {{:data.left_printing}} out of {{:data.total_printing}} page(s)
    +
    {{:helper.link('Cancel', 'abort', {'cancel_queue': 1})}}
    {{/if}}
    {{ } }} From 882238d1c1df28281839971f328915e67b3b269b Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 16 Jun 2025 01:01:43 +0000 Subject: [PATCH 0712/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index f606e802e6ca..2aecab0a3e31 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -51,12 +51,6 @@ -->
    - -

    14 April 2025

    -

    Zandario updated:

    -
      -
    • Added a "Toggle Browser Inspect" debug verb for debugging UIs
    • -
    From b00e16fce7b7513fc4518756402ff70bbc609ba6 Mon Sep 17 00:00:00 2001 From: ike709 Date: Fri, 20 Jun 2025 10:33:27 -0500 Subject: [PATCH 0713/1331] Rename BYOND 516 var conflicts --- code/datums/traits/_traits.dm | 14 +++++------ code/game/antagonist/antagonist_panel.dm | 10 ++++---- code/game/jobs/job/_job.dm | 32 ++++++++++++------------ code/game/turfs/floors/_floor.dm | 4 +-- code/game/turfs/turf_footsteps.dm | 14 +++++------ code/modules/goals/_goal.dm | 6 ++--- code/modules/goals/goal_mind.dm | 4 +-- 7 files changed, 42 insertions(+), 42 deletions(-) diff --git a/code/datums/traits/_traits.dm b/code/datums/traits/_traits.dm index bd1cbbf26364..909caf8d3a45 100644 --- a/code/datums/traits/_traits.dm +++ b/code/datums/traits/_traits.dm @@ -194,10 +194,10 @@ return (istype(holder)) // Called by preferences selection for HTML display. -/decl/trait/proc/get_trait_selection_data(var/datum/category_item/player_setup_item/traits/caller, var/list/ticked_traits = list(), var/recurse_level = 0, var/ignore_children_if_unticked = 1, var/ignore_unticked) +/decl/trait/proc/get_trait_selection_data(var/datum/category_item/player_setup_item/traits/trait_caller, var/list/ticked_traits = list(), var/recurse_level = 0, var/ignore_children_if_unticked = 1, var/ignore_unticked) var/ticked = (type in ticked_traits) - if((ignore_unticked && !ticked) || (caller && !is_available_to_select(caller.pref))) + if((ignore_unticked && !ticked) || (trait_caller && !is_available_to_select(trait_caller.pref))) return "" var/result = "" @@ -211,10 +211,10 @@ incompatible_trait_taken = TRUE break - var/chargen_name = get_chargen_name(caller.pref) - var/chargen_desc = get_chargen_desc(caller.pref) - if(istype(caller) && (ticked || caller.get_trait_total() + trait_cost <= get_config_value(/decl/config/num/max_character_traits)) && !incompatible_trait_taken) - result += "[ticked ? "[chargen_name]" : "[chargen_name]"] ([trait_cost])" + var/chargen_name = get_chargen_name(trait_caller.pref) + var/chargen_desc = get_chargen_desc(trait_caller.pref) + if(istype(trait_caller) && (ticked || trait_caller.get_trait_total() + trait_cost <= get_config_value(/decl/config/num/max_character_traits)) && !incompatible_trait_taken) + result += "[ticked ? "[chargen_name]" : "[chargen_name]"] ([trait_cost])" else result += ticked ? "[chargen_name]" : "[chargen_name]" @@ -227,7 +227,7 @@ result += "" if(LAZYLEN(children) && !(ignore_children_if_unticked && !ticked)) for(var/decl/trait/trait in children) - result += trait.get_trait_selection_data(caller, ticked_traits, (recurse_level+1), ignore_children_if_unticked) + result += trait.get_trait_selection_data(trait_caller, ticked_traits, (recurse_level+1), ignore_children_if_unticked) return result /// Shows `show_to` a browser window describing the character setup traits taken by `src`. diff --git a/code/game/antagonist/antagonist_panel.dm b/code/game/antagonist/antagonist_panel.dm index f16e898af98a..959d0d3b0b55 100644 --- a/code/game/antagonist/antagonist_panel.dm +++ b/code/game/antagonist/antagonist_panel.dm @@ -17,7 +17,7 @@ /decl/special_role/proc/get_extra_panel_options() return -/decl/special_role/proc/get_check_antag_output(var/datum/admins/caller) +/decl/special_role/proc/get_check_antag_output(var/datum/admins/caller_admin) if(!current_antagonists || !current_antagonists.len) return "" @@ -33,7 +33,7 @@ if(M.stat == DEAD) dat += " (DEAD)" dat += "" - dat += "\[PM\]\[SR\]" + dat += "\[PM\]\[SR\]" else dat += "Mob not found/([player.key])!" dat += "" @@ -47,17 +47,17 @@ while(!isturf(disk_loc)) if(ismob(disk_loc)) var/mob/M = disk_loc - dat += "carried by [M.real_name] " + dat += "carried by [M.real_name] " if(istype(disk_loc, /obj)) var/obj/O = disk_loc dat += "in \a [O.name] " disk_loc = disk_loc.loc dat += "in [disk_loc.loc] at ([disk_loc.x], [disk_loc.y], [disk_loc.z])" dat += "" - dat += get_additional_check_antag_output(caller) + dat += get_additional_check_antag_output(caller_admin) dat += "
    " return dat //Overridden elsewhere. -/decl/special_role/proc/get_additional_check_antag_output(var/datum/admins/caller) +/decl/special_role/proc/get_additional_check_antag_output(var/datum/admins/caller_admin) return "" diff --git a/code/game/jobs/job/_job.dm b/code/game/jobs/job/_job.dm index 2dcadfe65666..eb27bd7793e3 100644 --- a/code/game/jobs/job/_job.dm +++ b/code/game/jobs/job/_job.dm @@ -255,9 +255,9 @@ return FALSE -/datum/job/proc/get_join_link(var/client/caller, var/href_string, var/show_invalid_jobs) - if(is_available(caller)) - if(is_restricted(caller.prefs)) +/datum/job/proc/get_join_link(var/client/calling_client, var/href_string, var/show_invalid_jobs) + if(is_available(calling_client)) + if(is_restricted(calling_client.prefs)) if(show_invalid_jobs) return "[title]
    [current_positions]
    Active: [get_active_count()]
    " else @@ -397,27 +397,27 @@ SSjobs.job_icons[title] = preview_icon return SSjobs.job_icons[title] -/datum/job/proc/get_unavailable_reasons(var/client/caller) +/datum/job/proc/get_unavailable_reasons(var/client/calling_client) var/list/reasons = list() - if(jobban_isbanned(caller, title)) + if(jobban_isbanned(calling_client, title)) reasons["You are jobbanned."] = TRUE - if(is_semi_antagonist && jobban_isbanned(caller, /decl/special_role/provocateur)) + if(is_semi_antagonist && jobban_isbanned(calling_client, /decl/special_role/provocateur)) reasons["You are semi-antagonist banned."] = TRUE - if(!player_old_enough(caller)) + if(!player_old_enough(calling_client)) reasons["Your player age is too low."] = TRUE if(!is_position_available()) reasons["There are no positions left."] = TRUE - if(!isnull(allowed_branches) && (!caller.prefs.branches[title] || !is_branch_allowed(caller.prefs.branches[title]))) + if(!isnull(allowed_branches) && (!calling_client.prefs.branches[title] || !is_branch_allowed(calling_client.prefs.branches[title]))) reasons["Your branch of service does not allow it."] = TRUE - else if(!isnull(allowed_ranks) && (!caller.prefs.ranks[title] || !is_rank_allowed(caller.prefs.branches[title], caller.prefs.ranks[title]))) + else if(!isnull(allowed_ranks) && (!calling_client.prefs.ranks[title] || !is_rank_allowed(calling_client.prefs.branches[title], calling_client.prefs.ranks[title]))) reasons["Your rank choice does not allow it."] = TRUE - var/decl/species/S = caller.prefs.get_species_decl() + var/decl/species/S = calling_client.prefs.get_species_decl() if(S) if(!is_species_allowed(S)) reasons["Your species choice does not allow it."] = TRUE - if(!S.check_background(src, caller.prefs)) + if(!S.check_background(src, calling_client.prefs)) reasons["Your background choices do not allow it."] = TRUE - var/special_blocker = check_special_blockers(caller.prefs) + var/special_blocker = check_special_blockers(calling_client.prefs) if(special_blocker) reasons["Your preferences do not allow it: '[special_blocker]'."] = TRUE return TRUE @@ -429,14 +429,14 @@ mannequin.delete_inventory(TRUE) equip_preview(mannequin, additional_skips = OUTFIT_ADJUSTMENT_SKIP_BACKPACK) -/datum/job/proc/is_available(var/client/caller) +/datum/job/proc/is_available(var/client/calling_client) if(!is_position_available()) return FALSE - if(jobban_isbanned(caller, title)) + if(jobban_isbanned(calling_client, title)) return FALSE - if(is_semi_antagonist && jobban_isbanned(caller, /decl/special_role/provocateur)) + if(is_semi_antagonist && jobban_isbanned(calling_client, /decl/special_role/provocateur)) return FALSE - if(!player_old_enough(caller)) + if(!player_old_enough(calling_client)) return FALSE return TRUE diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index d92467b7e3ef..f8a8f1af0b05 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -113,10 +113,10 @@ else physically_destroyed() -/turf/floor/get_footstep_sound(var/mob/caller) +/turf/floor/get_footstep_sound(var/mob/caller_mob) var/decl/flooring/use_flooring = get_topmost_flooring() if(istype(use_flooring)) - return get_footstep_for_mob(use_flooring.footstep_type, caller) + return get_footstep_for_mob(use_flooring.footstep_type, caller_mob) return ..() /turf/floor/get_movable_alpha_mask_state(atom/movable/mover) diff --git a/code/game/turfs/turf_footsteps.dm b/code/game/turfs/turf_footsteps.dm index afa6623a4985..aefe1b1456ee 100644 --- a/code/game/turfs/turf_footsteps.dm +++ b/code/game/turfs/turf_footsteps.dm @@ -1,15 +1,15 @@ -/proc/get_footstep_for_mob(var/footstep_type, var/mob/living/caller) - . = istype(caller) && caller.get_mob_footstep(footstep_type) +/proc/get_footstep_for_mob(var/footstep_type, var/mob/living/caller_mob) + . = istype(caller_mob) && caller_mob.get_mob_footstep(footstep_type) if(!.) var/decl/footsteps/footsteps = GET_DECL(footstep_type) . = pick(footsteps.footstep_sounds) -/turf/proc/get_footstep_sound(var/mob/caller) +/turf/proc/get_footstep_sound(var/mob/caller_mob) for(var/obj/structure/S in contents) if(S.footstep_type) - return get_footstep_for_mob(S.footstep_type, caller) + return get_footstep_for_mob(S.footstep_type, caller_mob) if(check_fluid_depth(10) && !is_flooded(TRUE)) - return get_footstep_for_mob(/decl/footsteps/water, caller) + return get_footstep_for_mob(/decl/footsteps/water, caller_mob) if(footstep_type) - return get_footstep_for_mob(footstep_type, caller) - return get_footstep_for_mob(/decl/footsteps/blank, caller) + return get_footstep_for_mob(footstep_type, caller_mob) + return get_footstep_for_mob(/decl/footsteps/blank, caller_mob) diff --git a/code/modules/goals/_goal.dm b/code/modules/goals/_goal.dm index b36b7b50a406..6a5906e1b5f4 100644 --- a/code/modules/goals/_goal.dm +++ b/code/modules/goals/_goal.dm @@ -23,13 +23,13 @@ owner = null . = ..() -/datum/goal/proc/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller ,var/position = 1) +/datum/goal/proc/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller_mob ,var/position = 1) . = "[description][get_summary_value()]" if(show_success) . += get_success_string() if(allow_modification) - if(can_abandon) . += " (Abandon)" - if(can_reroll) . += " (Reroll)" + if(can_abandon) . += " (Abandon)" + if(can_reroll) . += " (Reroll)" /datum/goal/proc/get_success_string() return check_success() ? " Success!" : " Failure." diff --git a/code/modules/goals/goal_mind.dm b/code/modules/goals/goal_mind.dm index debd412f1f57..b0c74498f259 100644 --- a/code/modules/goals/goal_mind.dm +++ b/code/modules/goals/goal_mind.dm @@ -8,12 +8,12 @@ if(LAZYLEN(goals)) to_chat(current, SPAN_NOTICE("

    You had the following personal goals this round:
    [jointext(summarize_goals(TRUE), "
    ")]")) -/datum/mind/proc/summarize_goals(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller) +/datum/mind/proc/summarize_goals(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller_mob) . = list() if(LAZYLEN(goals)) for(var/i = 1 to LAZYLEN(goals)) var/datum/goal/goal = goals[i] - . += "[i]. [goal.summarize(show_success, allow_modification, caller, position = i)]" + . += "[i]. [goal.summarize(show_success, allow_modification, caller_mob, position = i)]" // Create and display personal goals for this round. /datum/mind/proc/generate_goals(var/datum/job/job, var/adding_goals = FALSE, var/add_amount, var/is_spawning = FALSE) From ea0d96dd92179cfb8ea577265e177811e7d5cb76 Mon Sep 17 00:00:00 2001 From: Amy <3855802+amylizzle@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:32:26 +0100 Subject: [PATCH 0714/1331] Update goal_ambition.dm --- code/modules/goals/goal_ambition.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/goals/goal_ambition.dm b/code/modules/goals/goal_ambition.dm index 2029bc5cf23f..352bc29e6b0f 100644 --- a/code/modules/goals/goal_ambition.dm +++ b/code/modules/goals/goal_ambition.dm @@ -13,5 +13,5 @@ /datum/goal/ambition/get_success_string() return "" -/datum/goal/ambition/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller ,var/position = 1) +/datum/goal/ambition/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller_mob ,var/position = 1) . = SPAN_DANGER(..(show_success)) From 9b44b44853a9855e279edf71c30e690c6086da86 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 8 Jul 2025 05:58:54 -0400 Subject: [PATCH 0715/1331] Split preferences.copy_to across many, many procs --- .../preference_setup/background/01_species.dm | 3 + .../background/02_background.dm | 7 + .../background/03_language.dm | 6 + .../preference_setup/general/01_basic.dm | 19 +++ .../preference_setup/general/02_body.dm | 51 +++++++ .../preference_setup/general/03_traits.dm | 5 + .../preference_setup/general/04_equipment.dm | 16 +++ .../preference_setup/general/05_flavor.dm | 5 + .../preference_setup/preference_setup.dm | 48 +++++++ code/modules/client/preferences.dm | 131 +++++------------- code/modules/mob/mob_snapshot.dm | 17 ++- code/modules/mob/new_player/new_player.dm | 14 +- code/modules/sprite_accessories/_accessory.dm | 3 + .../tails/_accessory_tail.dm | 8 ++ mods/content/psionics/_psionics.dm | 5 +- 15 files changed, 229 insertions(+), 109 deletions(-) diff --git a/code/modules/client/preference_setup/background/01_species.dm b/code/modules/client/preference_setup/background/01_species.dm index 05fa8159196d..68992453f584 100644 --- a/code/modules/client/preference_setup/background/01_species.dm +++ b/code/modules/client/preference_setup/background/01_species.dm @@ -6,6 +6,9 @@ sort_order = 1 var/hide_species = TRUE +/datum/category_item/player_setup_item/background/species/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) + snapshot.root_species = pref.get_species_decl() + // This must always return a decl, NEVER null. /datum/preferences/proc/get_species_decl() RETURN_TYPE(/decl/species) diff --git a/code/modules/client/preference_setup/background/02_background.dm b/code/modules/client/preference_setup/background/02_background.dm index f1093e1eaa3d..6b8a0a162856 100644 --- a/code/modules/client/preference_setup/background/02_background.dm +++ b/code/modules/client/preference_setup/background/02_background.dm @@ -25,6 +25,13 @@ hidden[cat_type] = TRUE ..() +/datum/category_item/player_setup_item/background/details/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + if(is_preview_copy) + return + for(var/token in pref.background_info) + character.set_background_value(token, pref.background_info[token], defer_language_update = TRUE) + character.update_languages() + /datum/category_item/player_setup_item/background/details/sanitize_character() if(!islist(pref.background_info)) diff --git a/code/modules/client/preference_setup/background/03_language.dm b/code/modules/client/preference_setup/background/03_language.dm index 490b4bb5bfa3..b9a43ea24941 100644 --- a/code/modules/client/preference_setup/background/03_language.dm +++ b/code/modules/client/preference_setup/background/03_language.dm @@ -7,6 +7,12 @@ var/list/allowed_languages var/list/free_languages +/datum/category_item/player_setup_item/background/languages/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + if(is_preview_copy) + return + for(var/lang in pref.alternate_languages) + character.add_language(lang) + /datum/category_item/player_setup_item/background/languages/load_character(datum/pref_record_reader/R) pref.alternate_languages = list() var/list/language_names = R.read("language") diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm index 96a439422849..018b2ad3edde 100644 --- a/code/modules/client/preference_setup/general/01_basic.dm +++ b/code/modules/client/preference_setup/general/01_basic.dm @@ -18,6 +18,9 @@ name = "Basic" sort_order = 1 +/datum/category_item/player_setup_item/physical/basic/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + character.set_gender(pref.gender) + /datum/category_item/player_setup_item/physical/basic/preload_character(datum/pref_record_reader/R) pref.gender = R.read("gender") pref.bodytype = R.read("bodytype") @@ -67,6 +70,22 @@ bodytype = S.get_bodytype_by_pronouns(pronouns) pref.set_bodytype(bodytype.name) +/datum/category_item/player_setup_item/physical/basic/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) + snapshot.root_bodytype = pref.get_bodytype_decl() + var/new_real_name = pref.real_name + if(pref.be_random_name) + var/decl/background_detail/background = pref.get_background_datum_by_flag(BACKGROUND_FLAG_NAMING) + if(background) + new_real_name = background.get_random_name(pref.gender) + if(get_config_value(/decl/config/toggle/humans_need_surnames)) + var/firstspace = findtext(new_real_name, " ") + var/name_length = length(new_real_name) + if(!firstspace) //we need a surname + new_real_name += " [pick(global.using_map.last_names)]" + else if(firstspace == name_length) // someone tried to cheese it by putting a space at the end + new_real_name += "[pick(global.using_map.last_names)]" + snapshot.real_name = new_real_name + /datum/category_item/player_setup_item/physical/basic/content() . = list() diff --git a/code/modules/client/preference_setup/general/02_body.dm b/code/modules/client/preference_setup/general/02_body.dm index 65dcf28fbc78..b7dcde9470ec 100644 --- a/code/modules/client/preference_setup/general/02_body.dm +++ b/code/modules/client/preference_setup/general/02_body.dm @@ -14,6 +14,57 @@ name = "Body" sort_order = 2 +// apply sprite accessories to an existing mob +// actually this might not be necessary because we apply the snapshot +/* /datum/category_item/player_setup_item/physical/body/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) + for(var/obj/item/organ/external/O in character.get_external_organs()) + for(var/decl/sprite_accessory_category/sprite_category in O.get_sprite_accessory_categories()) + if(!sprite_category.clear_in_pref_apply) + continue + O.clear_sprite_accessories_by_category(sprite_category.type, skip_update = TRUE) + for(var/accessory_category in pref.sprite_accessories) + var/decl/sprite_accessory_category/acc_cat = GET_DECL(accessory_category) + var/list/accessories = pref.sprite_accessories[accessory_category] + acc_cat.prepare_character(character, accessories) + for(var/accessory in accessories) + var/decl/sprite_accessory/accessory_decl = GET_DECL(accessory) + var/accessory_metadata = accessories[accessory] + for(var/bodypart in accessory_decl.body_parts) + var/obj/item/organ/external/O = GET_EXTERNAL_ORGAN(character, bodypart) + if(O) + O.set_sprite_accessory(accessory, accessory_category, accessory_metadata, skip_update = TRUE) */ + +/datum/category_item/player_setup_item/physical/body/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) + snapshot.blood_type = pref.blood_type + // we don't check appearance_flags here, apply_appearance_to does that + snapshot.skin_color = pref.skin_colour + snapshot.skin_tone = pref.skin_tone + snapshot.eye_color = pref.eye_colour + // so this is the hellish part. + // pref.sprite_accessories is sprite_accessories[accessory_category.type][loaded_accessory.type] = metadata + // snapshot.sprite_accessories is sprite_accessories[organ_tag][accessory_category.type][loaded_accessory.type] = metadata + // we have to convert it here + var/list/new_sprite_accessories = list() + for(var/accessory_category in pref.sprite_accessories) + var/decl/sprite_accessory_category/acc_cat = GET_DECL(accessory_category) + var/list/accessories = pref.sprite_accessories[accessory_category] + acc_cat.prepare_mob_snapshot(snapshot, accessories) + // todo: copy this elsewhere + // ^ i have no clue if i ever did this or not. presumably bc i don't know what i meant needed copying + for(var/accessory in accessories) + var/decl/sprite_accessory/accessory_decl = GET_DECL(accessory) + var/accessory_metadata = accessories[accessory] + for(var/bodypart in accessory_decl.body_parts) + LAZYINITLIST(new_sprite_accessories[bodypart]) + LAZYSET(new_sprite_accessories[bodypart][accessory_category], accessory, accessory_metadata) + if(length(new_sprite_accessories)) + snapshot.sprite_accessories = new_sprite_accessories + +// this should probably be named copy_to_after_physical, lol +/datum/category_item/player_setup_item/physical/body/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + if(LAZYLEN(pref.appearance_descriptors)) + character.appearance_descriptors = pref.appearance_descriptors.Copy() + /datum/category_item/player_setup_item/physical/body/load_character(datum/pref_record_reader/R) pref.skin_colour = R.read("skin_colour") diff --git a/code/modules/client/preference_setup/general/03_traits.dm b/code/modules/client/preference_setup/general/03_traits.dm index d5120bff0a00..dd1f90dd6cf4 100644 --- a/code/modules/client/preference_setup/general/03_traits.dm +++ b/code/modules/client/preference_setup/general/03_traits.dm @@ -39,6 +39,11 @@ else pref.prune_invalid_traits() +/datum/category_item/player_setup_item/traits/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + character.clear_extrinsic_traits() + for(var/trait_type in pref.traits) + character.set_trait(trait_type, (pref.traits[trait_type] || TRAIT_LEVEL_EXISTS)) + /datum/category_item/player_setup_item/traits/save_character(datum/pref_record_writer/writer) var/list/trait_ids = list() for(var/trait_type in pref.traits) diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm index d1678ce103cf..b7da0a39937c 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/general/04_equipment.dm @@ -22,6 +22,22 @@ var/decl/backpack_outfit/backpack_outfit = bos[backpack_option] backpacks_by_name[backpack_outfit.name] = backpack_outfit +/datum/category_item/player_setup_item/physical/equipment/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + QDEL_NULL_LIST(character.worn_underwear) + character.worn_underwear = list() + for(var/underwear_category_name in pref.all_underwear) + var/datum/category_group/underwear/underwear_category = global.underwear.categories_by_name[underwear_category_name] + if(underwear_category) + var/underwear_item_name = pref.all_underwear[underwear_category_name] + var/datum/category_item/underwear/UWD = underwear_category.items_by_name[underwear_item_name] + var/metadata = pref.all_underwear_metadata[underwear_category_name] + var/obj/item/underwear/UW = UWD.create_underwear(character, metadata) + if(UW) + UW.ForceEquipUnderwear(character, FALSE) + else + pref.all_underwear -= underwear_category_name + character.backpack_setup = new(pref.backpack, pref.backpack_metadata["[pref.backpack]"]) + /datum/category_item/player_setup_item/physical/equipment/load_character(datum/pref_record_reader/R) pref.all_underwear = R.read("all_underwear") pref.all_underwear_metadata = R.read("all_underwear_metadata") diff --git a/code/modules/client/preference_setup/general/05_flavor.dm b/code/modules/client/preference_setup/general/05_flavor.dm index 27c6b79580eb..9d6d91c021d0 100644 --- a/code/modules/client/preference_setup/general/05_flavor.dm +++ b/code/modules/client/preference_setup/general/05_flavor.dm @@ -6,6 +6,11 @@ name = "Flavor" sort_order = 5 +/datum/category_item/player_setup_item/physical/flavor/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + if(is_preview_copy) + return + character.flavor_texts = pref.flavor_texts.Copy() + /datum/category_item/player_setup_item/physical/flavor/load_character(datum/pref_record_reader/R) pref.flavor_texts["general"] = R.read("flavor_texts_general") pref.flavor_texts["head"] = R.read("flavor_texts_head") diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm index 946628527a59..73c1ac2648c8 100644 --- a/code/modules/client/preference_setup/preference_setup.dm +++ b/code/modules/client/preference_setup/preference_setup.dm @@ -94,6 +94,19 @@ var/global/const/CHARACTER_PREFERENCE_INPUT_TITLE = "Character Preference" for(var/datum/category_group/player_setup_category/PS in categories) PS.save_preferences(writer) +/datum/category_collection/player_setup_collection/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) + // Assumes a character has already been loaded. + for(var/datum/category_group/player_setup_category/PS in categories) + PS.copy_to_physical(character, is_preview_copy) + +/datum/category_collection/player_setup_collection/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + for(var/datum/category_group/player_setup_category/PS in categories) + PS.copy_to_nonphysical(character, is_preview_copy) + +/datum/category_collection/player_setup_collection/proc/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) + for(var/datum/category_group/player_setup_category/PG in categories) + PG.populate_mob_snapshot(snapshot, is_preview_copy) + /datum/category_collection/player_setup_collection/proc/header() var/dat = "" for(var/datum/category_group/player_setup_category/PS in categories) @@ -165,6 +178,19 @@ var/global/const/CHARACTER_PREFERENCE_INPUT_TITLE = "Character Preference" for(var/datum/category_item/player_setup_item/PI in items) PI.save_preferences(writer) +/datum/category_group/player_setup_category/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) + // Assumes a character has already been loaded. + for(var/datum/category_item/player_setup_item/PI in items) + PI.copy_to_physical(character, is_preview_copy) + +/datum/category_group/player_setup_category/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + for(var/datum/category_item/player_setup_item/PI in items) + PI.copy_to_nonphysical(character, is_preview_copy) + +/datum/category_group/player_setup_category/proc/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) + for(var/datum/category_item/player_setup_item/PI in items) + PI.populate_mob_snapshot(snapshot, is_preview_copy) + /datum/category_group/player_setup_category/proc/content(var/mob/user) . = "
    " var/current = 0 @@ -230,6 +256,28 @@ var/global/const/CHARACTER_PREFERENCE_INPUT_TITLE = "Character Preference" /datum/category_item/player_setup_item/proc/save_preferences(datum/pref_record_writer/writer) return +/* +* Called when actually populating a character based on character creation preferences +*/ +/datum/category_item/player_setup_item/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) + return // Note that the prefs-level call will apply anything already done in populate_mob_snapshot + +/datum/category_item/player_setup_item/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + return + +/* need overrides for: +- name (done) +- eye colour (done) +- blood type (done) +- skin colour/tone (done) +- species (done) +- bodytype (done) +- sprite accessories (done) +- maybe genetic conditions from traits? +*/ +/datum/category_item/player_setup_item/proc/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) + return + /datum/category_item/player_setup_item/proc/content() return diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 4d0e7a1b37bf..12c13f2fc8a9 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -346,92 +346,50 @@ var/global/list/time_prefs_fixed = list() update_setup_window(usr) return 1 -/datum/preferences/proc/copy_to(mob/living/human/character, is_preview_copy = FALSE) - - if(!player_setup) - return // WHY IS THIS EVEN HAPPENING. +/datum/category_item/player_setup_item/records/character_info/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + if(is_preview_copy) + return + pref.validate_comments_record() // Make sure a record has been generated for this character. + character.comments_record_id = pref.comments_record_id - // Sanitizing rather than saving as someone might still be editing when copy_to occurs. +/datum/preferences/proc/create_character(spawn_turf) + // Sanitizing rather than saving as someone might still be editing. player_setup.sanitize_setup() - validate_comments_record() // Make sure a record has been generated for this character. - character.comments_record_id = comments_record_id - character.clear_extrinsic_traits() + // first, handle basic appearance stuff via mob_snapshot + var/datum/mob_snapshot/new_character_snapshot = new /datum/mob_snapshot + player_setup.populate_mob_snapshot(new_character_snapshot, FALSE) + var/mob/living/human/character = new(spawn_turf, null, new_character_snapshot) + copy_to_nonphysical(character, FALSE) + return character - var/decl/bodytype/new_bodytype = get_bodytype_decl() - var/decl/species/character_species = character.get_species() - if(species == character_species.uid) - character.set_bodytype(new_bodytype) - else - character.change_species(species, new_bodytype) - - if(be_random_name) - var/decl/background_detail/background = get_background_datum_by_flag(BACKGROUND_FLAG_NAMING) - if(background) - real_name = background.get_random_name(gender) - - if(get_config_value(/decl/config/toggle/humans_need_surnames)) - var/firstspace = findtext(real_name, " ") - var/name_length = length(real_name) - if(!firstspace) //we need a surname - real_name += " [pick(global.using_map.last_names)]" - else if(firstspace == name_length) - real_name += "[pick(global.using_map.last_names)]" - - character.fully_replace_character_name(real_name) - - character.set_gender(gender) - character.blood_type = blood_type - - character.set_skin_colour(skin_colour, skip_update = TRUE) - character.skin_tone = skin_tone - - QDEL_NULL_LIST(character.worn_underwear) - character.worn_underwear = list() - - for(var/underwear_category_name in all_underwear) - var/datum/category_group/underwear/underwear_category = global.underwear.categories_by_name[underwear_category_name] - if(underwear_category) - var/underwear_item_name = all_underwear[underwear_category_name] - var/datum/category_item/underwear/UWD = underwear_category.items_by_name[underwear_item_name] - var/metadata = all_underwear_metadata[underwear_category_name] - var/obj/item/underwear/UW = UWD.create_underwear(character, metadata) - if(UW) - UW.ForceEquipUnderwear(character, FALSE) - else - all_underwear -= underwear_category_name - character.backpack_setup = new(backpack, backpack_metadata["[backpack]"]) +/datum/preferences/proc/copy_to(mob/living/human/character, is_preview_copy = FALSE) + copy_to_physical(character, is_preview_copy) // this is effectively what create_character does, but on an existing mob + copy_to_nonphysical(character, is_preview_copy) // this is the stuff we need to share - if(length(traits)) - for(var/trait_type in traits) - character.set_trait(trait_type, (traits[trait_type] || TRAIT_LEVEL_EXISTS)) +/datum/preferences/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) + // Sanitizing rather than saving as someone might still be editing when copy_to occurs. + player_setup.sanitize_setup() + // todo: move this part into some sort of pre-copy sanitizing? move it into the trait stuff? check if it's even necessary? + // i guess this is for if we're using it to refresh an existing character from prefs via admin tools + character.clear_extrinsic_traits() + // first, handle basic appearance stuff via mob_snapshot + var/datum/mob_snapshot/new_character_snapshot = new /datum/mob_snapshot // we assume we want a full repopulation, so don't persist anything from the donor + player_setup.populate_mob_snapshot(new_character_snapshot, is_preview_copy) + new_character_snapshot.apply_appearance_to(character, do_update = FALSE) + // not sure why we have real_name on snapshot; it's only used in one spot in setup_human + character.set_real_name(new_character_snapshot.real_name) + // now actually load everything else + player_setup.copy_to_physical(character, is_preview_copy) + return character - character.set_eye_colour(eye_colour, skip_update = TRUE) +// this should honestly maybe be copy_to_after_physical +/datum/preferences/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) + player_setup.copy_to_nonphysical(character, is_preview_copy) - for(var/obj/item/organ/external/O in character.get_external_organs()) - for(var/decl/sprite_accessory_category/sprite_category in O.get_sprite_accessory_categories()) - if(!sprite_category.clear_in_pref_apply) - continue - O.clear_sprite_accessories_by_category(sprite_category.type, skip_update = TRUE) - - for(var/accessory_category in sprite_accessories) - var/decl/sprite_accessory_category/acc_cat = GET_DECL(accessory_category) - var/list/accessories = sprite_accessories[accessory_category] - acc_cat.prepare_character(character, accessories) - for(var/accessory in accessories) - var/decl/sprite_accessory/accessory_decl = GET_DECL(accessory) - var/accessory_metadata = accessories[accessory] - for(var/bodypart in accessory_decl.body_parts) - var/obj/item/organ/external/O = GET_EXTERNAL_ORGAN(character, bodypart) - if(O) - O.set_sprite_accessory(accessory, accessory_category, accessory_metadata, skip_update = TRUE) - - if(LAZYLEN(appearance_descriptors)) - character.appearance_descriptors = appearance_descriptors.Copy() - - character.force_update_limbs() + // this happens here because i didn't want to duplicate it between copy_to_physical and create_character + character.force_update_limbs() // contains update_body(0) character.update_genetic_conditions(0) - character.update_body(0) character.update_underwear(0) character.update_hair(0) character.update_icon() @@ -440,22 +398,7 @@ var/global/list/time_prefs_fixed = list() if(is_preview_copy) return - for(var/token in background_info) - character.set_background_value(token, background_info[token], defer_language_update = TRUE) - character.update_languages() - for(var/lang in alternate_languages) - character.add_language(lang) - - character.flavor_texts["general"] = flavor_texts["general"] - character.flavor_texts["head"] = flavor_texts["head"] - character.flavor_texts["face"] = flavor_texts["face"] - character.flavor_texts["eyes"] = flavor_texts["eyes"] - character.flavor_texts["torso"] = flavor_texts["torso"] - character.flavor_texts["arms"] = flavor_texts["arms"] - character.flavor_texts["hands"] = flavor_texts["hands"] - character.flavor_texts["legs"] = flavor_texts["legs"] - character.flavor_texts["feet"] = flavor_texts["feet"] - + // why is this in copy_to? jfc if(!character.isSynthetic()) character.set_nutrition(rand(140,360)) character.set_hydration(rand(140,360)) diff --git a/code/modules/mob/mob_snapshot.dm b/code/modules/mob/mob_snapshot.dm index 40cf7f02306c..950dc9b2cce6 100644 --- a/code/modules/mob/mob_snapshot.dm +++ b/code/modules/mob/mob_snapshot.dm @@ -14,6 +14,8 @@ var/list/sprite_accessories var/list/genetic_conditions + /// Please find a better way to do this. This is done to add tails if we have the tail accessory selected... + var/list/extra_limbs /datum/mob_snapshot/New(mob/living/donor, genetic_info_only = FALSE) @@ -59,7 +61,7 @@ return clone // Replaces UpdateAppearance(). -/datum/mob_snapshot/proc/apply_appearance_to(mob/living/target) +/datum/mob_snapshot/proc/apply_appearance_to(mob/living/target, do_update = TRUE) if(istype(root_species) && root_species != target.get_species()) if(istype(root_bodytype)) @@ -75,15 +77,22 @@ target.set_eye_colour(eye_color) target.set_skin_tone(skin_tone) + for(var/limb_data in extra_limbs) + var/limb_path = extra_limbs[limb_data]["path"] + var/obj/item/organ/external/new_limb = new limb_path(null, null, src) + target.add_organ(new_limb, null, TRUE, FALSE, FALSE, TRUE) + extra_limbs = null // can't reuse it! + for(var/obj/item/organ/organ in target.get_organs()) organ.copy_from_mob_snapshot(src) for(var/decl/genetic_condition/condition as anything in genetic_conditions) target.add_genetic_condition(condition.type) - target.force_update_limbs() - target.update_hair(update_icons = FALSE) - target.update_eyes() + if(do_update) + target.force_update_limbs() + target.update_hair(update_icons = FALSE) + target.update_eyes() return TRUE /mob/proc/get_mob_snapshot(check_dna = FALSE) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 5eaca24a6515..3cbd1dfb8a2b 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -374,20 +374,16 @@ INITIALIZE_IMMEDIATE(/mob/new_player) if(!check_species_allowed(chosen_species)) spawning = 0 //abort return null - new_character = new(spawn_turf, chosen_species.uid) - - if(!new_character) - new_character = new(spawn_turf) - - new_character.lastarea = get_area(spawn_turf) - - if(global.random_players) + if(global.random_players) // apply randomness prior to creating the character var/decl/species/current_species = client.prefs.get_species_decl() var/decl/pronouns/pronouns = pick(current_species.available_pronouns) client.prefs.gender = pronouns.name client.prefs.real_name = client.prefs.get_random_name() client.prefs.randomize_appearance_and_body_for(new_character) - client.prefs.copy_to(new_character) + new_character = client.prefs.create_character(spawn_turf) + new_character.lastarea = get_area(spawn_turf) + + // client.prefs.copy_to(new_character) // not anymore lol sound_to(src, sound(null, repeat = 0, wait = 0, volume = 85, channel = sound_channels.lobby_channel))// MAD JAMS cant last forever yo diff --git a/code/modules/sprite_accessories/_accessory.dm b/code/modules/sprite_accessories/_accessory.dm index f7f9c46c2110..592a94274176 100644 --- a/code/modules/sprite_accessories/_accessory.dm +++ b/code/modules/sprite_accessories/_accessory.dm @@ -263,3 +263,6 @@ /decl/sprite_accessory_category/proc/prepare_character(mob/living/character, list/accessories) return +/decl/sprite_accessory_category/proc/prepare_mob_snapshot(datum/mob_snapshot/snapshot, list/accessories) + return + diff --git a/code/modules/sprite_accessories/tails/_accessory_tail.dm b/code/modules/sprite_accessories/tails/_accessory_tail.dm index bef3c6677821..311e416366e6 100644 --- a/code/modules/sprite_accessories/tails/_accessory_tail.dm +++ b/code/modules/sprite_accessories/tails/_accessory_tail.dm @@ -13,6 +13,14 @@ var/obj/item/organ/external/tail/new_tail = new(null, null, character.get_mob_snapshot()) character.add_organ(new_tail, null, TRUE, FALSE, FALSE, TRUE) +/decl/sprite_accessory_category/tail/prepare_mob_snapshot(datum/mob_snapshot/snapshot, list/accessories) + if(!length(accessories)) + return + // Give us a tail if we need one. + var/decl/sprite_accessory/tail_data = GET_DECL(accessories[1]) + if(tail_data?.draw_accessory && !snapshot.root_bodytype.has_limbs[BP_TAIL]) + LAZYSET(snapshot.extra_limbs, BP_TAIL, list("path" = /obj/item/organ/external/tail)) + /decl/sprite_accessory/tail abstract_type = /decl/sprite_accessory/tail hidden_by_gear_slot = list(slot_w_uniform_str, slot_wear_suit_str) diff --git a/mods/content/psionics/_psionics.dm b/mods/content/psionics/_psionics.dm index fa58ed18f582..d66ca8d8c025 100644 --- a/mods/content/psionics/_psionics.dm +++ b/mods/content/psionics/_psionics.dm @@ -25,11 +25,12 @@ . += "Only available for living mobs, sorry." . = jointext(., null) -/datum/preferences/copy_to(mob/living/human/character, is_preview_copy = FALSE) +// this probably shouldn't be necessary anymore now that we have refresh_login()? +/* /datum/preferences/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) character = ..() var/datum/ability_handler/psionics/psi = !is_preview_copy && istype(character) && character.get_ability_handler(/datum/ability_handler/psionics) if(psi) - psi.update() + psi.update() */ /decl/ability/can_use_ability(mob/user, list/metadata, silent = FALSE) . = ..() From 69177dd22e0c3e5a836346737132cead924d503f Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Fri, 11 Jul 2025 11:04:32 -0400 Subject: [PATCH 0716/1331] Rename preference apply procs --- .../background/02_background.dm | 2 +- .../background/03_language.dm | 2 +- .../preference_setup/general/01_basic.dm | 2 +- .../preference_setup/general/02_body.dm | 23 +------------------ .../preference_setup/general/03_traits.dm | 2 +- .../preference_setup/general/04_equipment.dm | 2 +- .../preference_setup/general/05_flavor.dm | 2 +- .../preference_setup/preference_setup.dm | 20 ++++++++-------- code/modules/client/preferences.dm | 21 ++++++++--------- code/modules/mob/new_player/new_player.dm | 2 +- mods/content/psionics/_psionics.dm | 7 ------ 11 files changed, 28 insertions(+), 57 deletions(-) diff --git a/code/modules/client/preference_setup/background/02_background.dm b/code/modules/client/preference_setup/background/02_background.dm index 6b8a0a162856..6ce982e37d81 100644 --- a/code/modules/client/preference_setup/background/02_background.dm +++ b/code/modules/client/preference_setup/background/02_background.dm @@ -25,7 +25,7 @@ hidden[cat_type] = TRUE ..() -/datum/category_item/player_setup_item/background/details/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/background/details/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) if(is_preview_copy) return for(var/token in pref.background_info) diff --git a/code/modules/client/preference_setup/background/03_language.dm b/code/modules/client/preference_setup/background/03_language.dm index b9a43ea24941..486d6bffd109 100644 --- a/code/modules/client/preference_setup/background/03_language.dm +++ b/code/modules/client/preference_setup/background/03_language.dm @@ -7,7 +7,7 @@ var/list/allowed_languages var/list/free_languages -/datum/category_item/player_setup_item/background/languages/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/background/languages/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) if(is_preview_copy) return for(var/lang in pref.alternate_languages) diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm index 018b2ad3edde..83a11ec1a5d4 100644 --- a/code/modules/client/preference_setup/general/01_basic.dm +++ b/code/modules/client/preference_setup/general/01_basic.dm @@ -18,7 +18,7 @@ name = "Basic" sort_order = 1 -/datum/category_item/player_setup_item/physical/basic/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/physical/basic/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) character.set_gender(pref.gender) /datum/category_item/player_setup_item/physical/basic/preload_character(datum/pref_record_reader/R) diff --git a/code/modules/client/preference_setup/general/02_body.dm b/code/modules/client/preference_setup/general/02_body.dm index b7dcde9470ec..6f23e996d2ce 100644 --- a/code/modules/client/preference_setup/general/02_body.dm +++ b/code/modules/client/preference_setup/general/02_body.dm @@ -14,26 +14,6 @@ name = "Body" sort_order = 2 -// apply sprite accessories to an existing mob -// actually this might not be necessary because we apply the snapshot -/* /datum/category_item/player_setup_item/physical/body/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) - for(var/obj/item/organ/external/O in character.get_external_organs()) - for(var/decl/sprite_accessory_category/sprite_category in O.get_sprite_accessory_categories()) - if(!sprite_category.clear_in_pref_apply) - continue - O.clear_sprite_accessories_by_category(sprite_category.type, skip_update = TRUE) - for(var/accessory_category in pref.sprite_accessories) - var/decl/sprite_accessory_category/acc_cat = GET_DECL(accessory_category) - var/list/accessories = pref.sprite_accessories[accessory_category] - acc_cat.prepare_character(character, accessories) - for(var/accessory in accessories) - var/decl/sprite_accessory/accessory_decl = GET_DECL(accessory) - var/accessory_metadata = accessories[accessory] - for(var/bodypart in accessory_decl.body_parts) - var/obj/item/organ/external/O = GET_EXTERNAL_ORGAN(character, bodypart) - if(O) - O.set_sprite_accessory(accessory, accessory_category, accessory_metadata, skip_update = TRUE) */ - /datum/category_item/player_setup_item/physical/body/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) snapshot.blood_type = pref.blood_type // we don't check appearance_flags here, apply_appearance_to does that @@ -60,8 +40,7 @@ if(length(new_sprite_accessories)) snapshot.sprite_accessories = new_sprite_accessories -// this should probably be named copy_to_after_physical, lol -/datum/category_item/player_setup_item/physical/body/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/physical/body/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) if(LAZYLEN(pref.appearance_descriptors)) character.appearance_descriptors = pref.appearance_descriptors.Copy() diff --git a/code/modules/client/preference_setup/general/03_traits.dm b/code/modules/client/preference_setup/general/03_traits.dm index dd1f90dd6cf4..b61d8b02b201 100644 --- a/code/modules/client/preference_setup/general/03_traits.dm +++ b/code/modules/client/preference_setup/general/03_traits.dm @@ -39,7 +39,7 @@ else pref.prune_invalid_traits() -/datum/category_item/player_setup_item/traits/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/traits/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) character.clear_extrinsic_traits() for(var/trait_type in pref.traits) character.set_trait(trait_type, (pref.traits[trait_type] || TRAIT_LEVEL_EXISTS)) diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm index b7da0a39937c..437ec1c4cc46 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/general/04_equipment.dm @@ -22,7 +22,7 @@ var/decl/backpack_outfit/backpack_outfit = bos[backpack_option] backpacks_by_name[backpack_outfit.name] = backpack_outfit -/datum/category_item/player_setup_item/physical/equipment/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/physical/equipment/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) QDEL_NULL_LIST(character.worn_underwear) character.worn_underwear = list() for(var/underwear_category_name in pref.all_underwear) diff --git a/code/modules/client/preference_setup/general/05_flavor.dm b/code/modules/client/preference_setup/general/05_flavor.dm index 9d6d91c021d0..f10579925758 100644 --- a/code/modules/client/preference_setup/general/05_flavor.dm +++ b/code/modules/client/preference_setup/general/05_flavor.dm @@ -6,7 +6,7 @@ name = "Flavor" sort_order = 5 -/datum/category_item/player_setup_item/physical/flavor/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/physical/flavor/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) if(is_preview_copy) return character.flavor_texts = pref.flavor_texts.Copy() diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm index 73c1ac2648c8..f6621d517a73 100644 --- a/code/modules/client/preference_setup/preference_setup.dm +++ b/code/modules/client/preference_setup/preference_setup.dm @@ -94,14 +94,14 @@ var/global/const/CHARACTER_PREFERENCE_INPUT_TITLE = "Character Preference" for(var/datum/category_group/player_setup_category/PS in categories) PS.save_preferences(writer) -/datum/category_collection/player_setup_collection/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_collection/player_setup_collection/proc/apply_snapshot_to_mob(mob/living/human/character, is_preview_copy = FALSE) // Assumes a character has already been loaded. for(var/datum/category_group/player_setup_category/PS in categories) - PS.copy_to_physical(character, is_preview_copy) + PS.apply_snapshot_to_mob(character, is_preview_copy) -/datum/category_collection/player_setup_collection/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_collection/player_setup_collection/proc/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) for(var/datum/category_group/player_setup_category/PS in categories) - PS.copy_to_nonphysical(character, is_preview_copy) + PS.apply_post_snapshot_preferences(character, is_preview_copy) /datum/category_collection/player_setup_collection/proc/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) for(var/datum/category_group/player_setup_category/PG in categories) @@ -178,14 +178,14 @@ var/global/const/CHARACTER_PREFERENCE_INPUT_TITLE = "Character Preference" for(var/datum/category_item/player_setup_item/PI in items) PI.save_preferences(writer) -/datum/category_group/player_setup_category/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_group/player_setup_category/proc/apply_snapshot_to_mob(mob/living/human/character, is_preview_copy = FALSE) // Assumes a character has already been loaded. for(var/datum/category_item/player_setup_item/PI in items) - PI.copy_to_physical(character, is_preview_copy) + PI.apply_snapshot_to_mob(character, is_preview_copy) -/datum/category_group/player_setup_category/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_group/player_setup_category/proc/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) for(var/datum/category_item/player_setup_item/PI in items) - PI.copy_to_nonphysical(character, is_preview_copy) + PI.apply_post_snapshot_preferences(character, is_preview_copy) /datum/category_group/player_setup_category/proc/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy = FALSE) for(var/datum/category_item/player_setup_item/PI in items) @@ -259,10 +259,10 @@ var/global/const/CHARACTER_PREFERENCE_INPUT_TITLE = "Character Preference" /* * Called when actually populating a character based on character creation preferences */ -/datum/category_item/player_setup_item/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/proc/apply_snapshot_to_mob(mob/living/human/character, is_preview_copy = FALSE) return // Note that the prefs-level call will apply anything already done in populate_mob_snapshot -/datum/category_item/player_setup_item/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/proc/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) return /* need overrides for: diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 12c13f2fc8a9..cdfe05e2c66c 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -346,28 +346,28 @@ var/global/list/time_prefs_fixed = list() update_setup_window(usr) return 1 -/datum/category_item/player_setup_item/records/character_info/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) +/datum/category_item/player_setup_item/records/character_info/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) if(is_preview_copy) return pref.validate_comments_record() // Make sure a record has been generated for this character. character.comments_record_id = pref.comments_record_id -/datum/preferences/proc/create_character(spawn_turf) +/datum/preferences/proc/create_character_from_snapshot(spawn_turf) // Sanitizing rather than saving as someone might still be editing. player_setup.sanitize_setup() // first, handle basic appearance stuff via mob_snapshot var/datum/mob_snapshot/new_character_snapshot = new /datum/mob_snapshot player_setup.populate_mob_snapshot(new_character_snapshot, FALSE) var/mob/living/human/character = new(spawn_turf, null, new_character_snapshot) - copy_to_nonphysical(character, FALSE) + apply_post_snapshot_preferences(character, FALSE) return character /datum/preferences/proc/copy_to(mob/living/human/character, is_preview_copy = FALSE) - copy_to_physical(character, is_preview_copy) // this is effectively what create_character does, but on an existing mob - copy_to_nonphysical(character, is_preview_copy) // this is the stuff we need to share + apply_snapshot_to_mob(character, is_preview_copy) // this is effectively what create_character_from_snapshot does, but on an existing mob + apply_post_snapshot_preferences(character, is_preview_copy) // this is the stuff we need to share -/datum/preferences/proc/copy_to_physical(mob/living/human/character, is_preview_copy = FALSE) +/datum/preferences/proc/apply_snapshot_to_mob(mob/living/human/character, is_preview_copy = FALSE) // Sanitizing rather than saving as someone might still be editing when copy_to occurs. player_setup.sanitize_setup() // todo: move this part into some sort of pre-copy sanitizing? move it into the trait stuff? check if it's even necessary? @@ -380,14 +380,13 @@ var/global/list/time_prefs_fixed = list() // not sure why we have real_name on snapshot; it's only used in one spot in setup_human character.set_real_name(new_character_snapshot.real_name) // now actually load everything else - player_setup.copy_to_physical(character, is_preview_copy) + player_setup.apply_snapshot_to_mob(character, is_preview_copy) return character -// this should honestly maybe be copy_to_after_physical -/datum/preferences/proc/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) - player_setup.copy_to_nonphysical(character, is_preview_copy) +/datum/preferences/proc/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) + player_setup.apply_post_snapshot_preferences(character, is_preview_copy) - // this happens here because i didn't want to duplicate it between copy_to_physical and create_character + // this happens here because i didn't want to duplicate it between apply_snapshot_to_mob and create_character_from_snapshot character.force_update_limbs() // contains update_body(0) character.update_genetic_conditions(0) character.update_underwear(0) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 3cbd1dfb8a2b..2a460c53a435 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -380,7 +380,7 @@ INITIALIZE_IMMEDIATE(/mob/new_player) client.prefs.gender = pronouns.name client.prefs.real_name = client.prefs.get_random_name() client.prefs.randomize_appearance_and_body_for(new_character) - new_character = client.prefs.create_character(spawn_turf) + new_character = client.prefs.create_character_from_snapshot(spawn_turf) new_character.lastarea = get_area(spawn_turf) // client.prefs.copy_to(new_character) // not anymore lol diff --git a/mods/content/psionics/_psionics.dm b/mods/content/psionics/_psionics.dm index d66ca8d8c025..aa9e711f7ca7 100644 --- a/mods/content/psionics/_psionics.dm +++ b/mods/content/psionics/_psionics.dm @@ -25,13 +25,6 @@ . += "Only available for living mobs, sorry." . = jointext(., null) -// this probably shouldn't be necessary anymore now that we have refresh_login()? -/* /datum/preferences/copy_to_nonphysical(mob/living/human/character, is_preview_copy = FALSE) - character = ..() - var/datum/ability_handler/psionics/psi = !is_preview_copy && istype(character) && character.get_ability_handler(/datum/ability_handler/psionics) - if(psi) - psi.update() */ - /decl/ability/can_use_ability(mob/user, list/metadata, silent = FALSE) . = ..() if(. && is_supernatural) From 35503d57a7e9729438fceb0f2b44ffc88f040eaa Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 21 Jul 2025 17:19:50 -0400 Subject: [PATCH 0717/1331] Remove adjust_multi --- code/game/machinery/atmoalter/canister.dm | 3 ++- code/game/machinery/atmoalter/pump.dm | 3 ++- code/game/objects/items/devices/oxycandle.dm | 9 +++++---- code/game/objects/structures/mineral_bath.dm | 4 +++- code/game/objects/structures/transit_tubes.dm | 3 ++- code/modules/atmospherics/components/unary/tank.dm | 6 ++---- code/modules/reagents/chems/chems_explosives.dm | 4 +++- code/modules/reagents/chems/chems_fuel.dm | 5 ++++- code/modules/xgm/xgm_gas_mixture.dm | 11 ----------- code/unit_tests/atmospherics_tests.dm | 6 ++---- mods/content/supermatter/machinery/supermatter.dm | 4 ++-- 11 files changed, 27 insertions(+), 31 deletions(-) diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index ae85b4324a5f..267f68ff20d5 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -340,7 +340,8 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) /obj/machinery/portable_atmospherics/canister/air/Initialize() . = ..() var/list/air_mix = StandardAirMix() - air_contents.adjust_multi(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen], /decl/material/gas/nitrogen, air_mix[/decl/material/gas/nitrogen]) + air_contents.adjust_gas(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen], FALSE) + air_contents.adjust_gas(/decl/material/gas/nitrogen, air_mix[/decl/material/gas/nitrogen]) update_icon() diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 2c107d7aac1b..43297c50a477 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -27,7 +27,8 @@ . = ..() var/list/air_mix = StandardAirMix() - src.air_contents.adjust_multi(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen], /decl/material/gas/nitrogen, air_mix[/decl/material/gas/nitrogen]) + air_contents.adjust_gas(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen], FALSE) + air_contents.adjust_gas(/decl/material/gas/nitrogen, air_mix[/decl/material/gas/nitrogen]) /obj/machinery/portable_atmospherics/powered/pump/on_update_icon() overlays.Cut() diff --git a/code/game/objects/items/devices/oxycandle.dm b/code/game/objects/items/devices/oxycandle.dm index 0040ac660821..c24ced497aef 100644 --- a/code/game/objects/items/devices/oxycandle.dm +++ b/code/game/objects/items/devices/oxycandle.dm @@ -1,3 +1,4 @@ +// TODO: Make this an actual candle that burns something that produces oxygen as a waste product? That'd be doable now... /obj/item/oxycandle name = "oxygen candle" desc = "A steel tube with the words 'OXYGEN - PULL CORD TO IGNITE' stamped on the side.\nA small label reads 'WARNING: NOT FOR LIGHTING USE. WILL IGNITE FLAMMABLE GASSES'" @@ -34,8 +35,8 @@ air_contents = new /datum/gas_mixture() air_contents.volume = 200 //liters air_contents.temperature = T20C - var/list/air_mix = list(/decl/material/gas/oxygen = 1 * (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) - air_contents.adjust_multi(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen]) + var/const/OXYGEN_FRACTION = 1 // separating out the constant so it's clearer why it exists and how to modify it later + air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) START_PROCESSING(SSprocessing, src) // Process of Oxygen candles releasing air. Makes 200 volume of oxygen @@ -63,8 +64,8 @@ return environment.merge(removed) volume -= 200 - var/list/air_mix = list(/decl/material/gas/oxygen = 1 * (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) - air_contents.adjust_multi(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen]) + var/const/OXYGEN_FRACTION = 1 // separating out the constant so it's clearer why it exists and how to modify it later + air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) /obj/item/oxycandle/on_update_icon() . = ..() diff --git a/code/game/objects/structures/mineral_bath.dm b/code/game/objects/structures/mineral_bath.dm index 0129a5e61f74..1ed4d581c726 100644 --- a/code/game/objects/structures/mineral_bath.dm +++ b/code/game/objects/structures/mineral_bath.dm @@ -14,7 +14,9 @@ /obj/structure/mineral_bath/return_air() var/datum/gas_mixture/venus = new(CELL_VOLUME, SYNTH_HEAT_LEVEL_1 - 10) - venus.adjust_multi(/decl/material/gas/chlorine, MOLES_N2STANDARD, /decl/material/gas/hydrogen, MOLES_O2STANDARD) + + venus.adjust_gas(/decl/material/gas/chlorine, MOLES_N2STANDARD, FALSE) + venus.adjust_gas(/decl/material/gas/hydrogen, MOLES_O2STANDARD, TRUE) return venus /obj/structure/mineral_bath/grab_attack(obj/item/grab/grab, mob/user) diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm index 0c9982cc9a63..8c98af230f7a 100644 --- a/code/game/objects/structures/transit_tubes.dm +++ b/code/game/objects/structures/transit_tubes.dm @@ -59,7 +59,8 @@ /obj/structure/transit_tube_pod/Initialize() . = ..() - air_contents.adjust_multi(/decl/material/gas/oxygen, MOLES_O2STANDARD * 2, /decl/material/gas/nitrogen, MOLES_N2STANDARD) + air_contents.adjust_gas(/decl/material/gas/oxygen, MOLES_O2STANDARD * 2, FALSE) + air_contents.adjust_gas(/decl/material/gas/nitrogen, MOLES_N2STANDARD, TRUE) air_contents.temperature = T20C // Give auto tubes time to align before trying to start moving diff --git a/code/modules/atmospherics/components/unary/tank.dm b/code/modules/atmospherics/components/unary/tank.dm index 29968bd94450..52c6c6fc0278 100644 --- a/code/modules/atmospherics/components/unary/tank.dm +++ b/code/modules/atmospherics/components/unary/tank.dm @@ -29,11 +29,9 @@ air_contents.temperature = T20C if(filling) - var/list/gases = list() for(var/gas in filling) - gases += gas - gases += start_pressure * filling[gas] * (air_contents.volume)/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - air_contents.adjust_multi(arglist(gases)) + air_contents.adjust_gas(gas, start_pressure * filling[gas] * (air_contents.volume)/(R_IDEAL_GAS_EQUATION*air_contents.temperature), FALSE) + air_contents.update_values() update_icon() /obj/machinery/atmospherics/unary/tank/set_initial_level() diff --git a/code/modules/reagents/chems/chems_explosives.dm b/code/modules/reagents/chems/chems_explosives.dm index 614671fb3060..58573abdbb42 100644 --- a/code/modules/reagents/chems/chems_explosives.dm +++ b/code/modules/reagents/chems/chems_explosives.dm @@ -27,7 +27,9 @@ var/adj_power = round(boompower * activated_volume/60) var/datum/gas_mixture/products = new(_temperature = 5 * FLAMMABLE_GAS_FLASHPOINT) var/gas_moles = 3 * volume - products.adjust_multi(/decl/material/gas/carbon_dioxide, 0.5 * gas_moles, /decl/material/gas/nitrogen, 0.3 * gas_moles, /decl/material/liquid/water, 0.2 * gas_moles) + products.adjust_gas(/decl/material/gas/carbon_dioxide, 0.5 * gas_moles, FALSE) + products.adjust_gas(/decl/material/gas/nitrogen, 0.3 * gas_moles, FALSE) + products.adjust_gas(/decl/material/liquid/water, 0.2 * gas_moles, TRUE) T.assume_air(products) holder?.reagents?.remove_reagent(type, activated_volume) explosion(T, adj_power, adj_power + 1, adj_power*2 + 2) diff --git a/code/modules/reagents/chems/chems_fuel.dm b/code/modules/reagents/chems/chems_fuel.dm index 7853f2f5579a..539d0e1b2e8d 100644 --- a/code/modules/reagents/chems/chems_fuel.dm +++ b/code/modules/reagents/chems/chems_fuel.dm @@ -26,7 +26,10 @@ var/turf/T = get_turf(holder) var/datum/gas_mixture/products = new(_temperature = 5 * FLAMMABLE_GAS_FLASHPOINT) var/gas_moles = 3 * volume - products.adjust_multi(/decl/material/gas/nitricoxide, 0.1 * gas_moles, /decl/material/gas/nitrodioxide, 0.1 * gas_moles, /decl/material/gas/nitrogen, 0.6 * gas_moles, /decl/material/gas/hydrogen, 0.02 * gas_moles) + products.adjust_gas(/decl/material/gas/nitricoxide, 0.1 * gas_moles, FALSE) + products.adjust_gas(/decl/material/gas/nitrodioxide, 0.1 * gas_moles, FALSE) + products.adjust_gas(/decl/material/gas/nitrogen, 0.6 * gas_moles, FALSE) + products.adjust_gas(/decl/material/gas/hydrogen, 0.02 * gas_moles, TRUE) T.assume_air(products) if(volume > 500) explosion(T,1,2,4) diff --git a/code/modules/xgm/xgm_gas_mixture.dm b/code/modules/xgm/xgm_gas_mixture.dm index e567de48405c..823c729e3b1f 100644 --- a/code/modules/xgm/xgm_gas_mixture.dm +++ b/code/modules/xgm/xgm_gas_mixture.dm @@ -72,17 +72,6 @@ if(update) update_values() - -//Variadic version of adjust_gas(). Takes any number of gas and mole pairs and applies them. -/datum/gas_mixture/proc/adjust_multi() - ASSERT(!(args.len % 2)) - - for(var/i = 1; i < args.len; i += 2) - adjust_gas(args[i], args[i+1], update = 0) - - update_values() - - /// Merges all the gas from another mixture into this one. Respects group_multipliers and adjusts temperature correctly. /// Does not modify giver in any way. /datum/gas_mixture/proc/merge(const/datum/gas_mixture/giver) diff --git a/code/unit_tests/atmospherics_tests.dm b/code/unit_tests/atmospherics_tests.dm index 46837d818bf5..0fa4febe7d55 100644 --- a/code/unit_tests/atmospherics_tests.dm +++ b/code/unit_tests/atmospherics_tests.dm @@ -14,11 +14,9 @@ var/list/initial_gas = mix_data["initial_gas"] if(initial_gas.len) - var/list/gas_args = list() for(var/gasid in initial_gas) - gas_args += gasid - gas_args += initial_gas[gasid] - gas_mix.adjust_multi(arglist(gas_args)) + gas_mix.adjust_gas(gasid, initial_gas[gasid], FALSE) + gas_mix.update_values() gas_mixes[mix_name] = gas_mix return gas_mixes diff --git a/mods/content/supermatter/machinery/supermatter.dm b/mods/content/supermatter/machinery/supermatter.dm index e0a41706145b..2fb27aadac35 100644 --- a/mods/content/supermatter/machinery/supermatter.dm +++ b/mods/content/supermatter/machinery/supermatter.dm @@ -509,8 +509,8 @@ var/global/list/supermatter_delam_accent_sounds = list( //Release reaction gasses var/heat_capacity = removed.heat_capacity() - removed.adjust_multi(/decl/material/solid/exotic_matter, max(device_energy / product_release_modifier, 0), \ - /decl/material/gas/oxygen, max((device_energy + removed.temperature - T0C) / oxygen_release_modifier, 0)) + removed.adjust_gas(/decl/material/solid/exotic_matter, max(device_energy / product_release_modifier, 0), FALSE) + removed.adjust_gas(/decl/material/gas/oxygen, max((device_energy + removed.temperature - T0C) / oxygen_release_modifier, 0)) var/thermal_power = thermal_release_modifier * device_energy if (debug) From 4aea67ec3da420bf98886de131611165766ba438 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 21 Jul 2025 22:43:14 -0400 Subject: [PATCH 0718/1331] Rewrite CELLRATE macro for cleanliness --- code/__defines/machinery.dm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index c45a497ba5f3..787488e127f9 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -4,9 +4,8 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define MEGAWATTS *1000000 #define GIGAWATTS *1000000000 -#define MACHINERY_TICKRATE 2 // Tick rate for machinery in seconds. As it affects CELLRATE calculation it is kept as define here - -#define CELLRATE (1 / ( 3600 / MACHINERY_TICKRATE )) // Multiplier for charge units. Converts cell charge units(watthours) to joules. Takes into consideration that our machinery ticks once per two seconds. +/// Multiplier for charge units. Converts cell charge units(watthours) to joules. Takes into consideration that our machinery ticks once per two seconds. +#define CELLRATE (/datum/controller/subsystem/machines::wait / (1 HOUR)) // Doors! #define DOOR_CRUSH_DAMAGE 40 From 97331206ec612059956949b52460429d5f65cc02 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 08:10:09 -0400 Subject: [PATCH 0719/1331] Fix passing material instances to GET_DECL --- code/game/machinery/Sleeper.dm | 2 +- code/modules/scanners/mass_spectrometer.dm | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index eecfec8852bb..dc25d0685b56 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -442,7 +442,7 @@ to_chat(user, SPAN_WARNING("There's no suitable occupant in \the [src].")) return if(!emagged && canister.reagents?.primary_reagent) - var/decl/material/chem = GET_DECL(canister.reagents.primary_reagent) + var/decl/material/chem = canister.reagents.primary_reagent if(chem.overdose && REAGENT_VOLUME(occupant.reagents, canister.reagents.primary_reagent) + amount >= chem.overdose) to_chat(user, SPAN_WARNING("Injecting more [chem.name] presents an overdose risk to the subject.")) return diff --git a/code/modules/scanners/mass_spectrometer.dm b/code/modules/scanners/mass_spectrometer.dm index 510ea189fe03..7e186c85abfe 100644 --- a/code/modules/scanners/mass_spectrometer.dm +++ b/code/modules/scanners/mass_spectrometer.dm @@ -52,10 +52,9 @@ var/list/blood_traces = list() var/list/blood_doses = list() - if(length(reagents.reagent_volumes) == 1) - var/decl/material/liquid/random/random = GET_DECL(reagents.reagent_volumes[1]) - if(istype(random)) - return random.get_scan_data(user) + if(length(reagents.reagent_volumes) == 1 && istype(reagents.primary_reagent, /decl/material/liquid/random)) + var/decl/material/liquid/random/random = reagents.primary_reagent + return random.get_scan_data(user) for(var/decl/material/reagent as anything in reagents.reagent_volumes) if(!istype(reagent, /decl/material/liquid/blood)) From fc2d9f75a87d5f07172c767689ebab261dcd782c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 08:11:01 -0400 Subject: [PATCH 0720/1331] Make holy water use a reagent data field --- code/__defines/mobs.dm | 1 + code/__defines/reagent_data_fields.dm | 3 +++ code/game/objects/items/weapons/storage/bible.dm | 2 +- .../materials/definitions/liquids/materials_liquid_water.dm | 4 ++-- code/modules/reagents/reagent_containers/drinks/bottle.dm | 2 +- mods/gamemodes/cult/ritual.dm | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index e95fac6faa6e..55874616e038 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -248,6 +248,7 @@ // Misc general data. #define DATA_COOLDOWN_TIME /decl/reagent_data_field/cooldown_time +#define DATA_WATER_HOLINESS /decl/reagent_data_field/holy //Used by show_message() and emotes #define VISIBLE_MESSAGE 1 diff --git a/code/__defines/reagent_data_fields.dm b/code/__defines/reagent_data_fields.dm index db7b27ed7168..04998150bd8d 100644 --- a/code/__defines/reagent_data_fields.dm +++ b/code/__defines/reagent_data_fields.dm @@ -65,3 +65,6 @@ /decl/reagent_data_field/cooldown_time uid = "rdf_cooldown_time" + +/decl/reagent_data_field/holy + uid = "rdf_water_holy" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm index b0f2429fd34e..37a12e8d3dcc 100644 --- a/code/game/objects/items/weapons/storage/bible.dm +++ b/code/game/objects/items/weapons/storage/bible.dm @@ -89,7 +89,7 @@ if(proximity && user?.mind?.assigned_job?.is_holy) if(A.reagents && A.reagents.has_reagent(/decl/material/liquid/water)) //blesses all the water in the holder to_chat(user, SPAN_NOTICE("You bless \the [A].")) // I wish it was this easy in nethack - LAZYSET(A.reagents.reagent_data, /decl/material/liquid/water, list("holy" = TRUE)) + LAZYSET(A.reagents.reagent_data, /decl/material/liquid/water, list(DATA_WATER_HOLINESS = TRUE)) /obj/item/bible/attackby(obj/item/used_item, mob/user) if(storage?.use_sound) diff --git a/code/modules/materials/definitions/liquids/materials_liquid_water.dm b/code/modules/materials/definitions/liquids/materials_liquid_water.dm index c02b25369e93..3c1a5adbc39a 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_water.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_water.dm @@ -58,7 +58,7 @@ ..() if(ishuman(M)) var/list/data = REAGENT_DATA(holder, src) - if(data?["holy"]) + if(data?[DATA_WATER_HOLINESS]) affect_holy(M, removed, holder) /decl/material/liquid/water/proc/affect_holy(mob/living/M, removed, datum/reagents/holder) @@ -97,7 +97,7 @@ touching_turf.visible_message(SPAN_NOTICE("The water sizzles as it lands on \the [touching_turf]!")) var/list/data = REAGENT_DATA(holder, src) - if(LAZYACCESS(data, "holy")) + if(LAZYACCESS(data, DATA_WATER_HOLINESS)) touching_turf.turf_flags |= TURF_FLAG_HOLY /decl/material/liquid/water/touch_obj(var/obj/O, var/amount, var/datum/reagents/holder) diff --git a/code/modules/reagents/reagent_containers/drinks/bottle.dm b/code/modules/reagents/reagent_containers/drinks/bottle.dm index 779d4a274e46..3de103abcbb2 100644 --- a/code/modules/reagents/reagent_containers/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/drinks/bottle.dm @@ -314,7 +314,7 @@ center_of_mass = @'{"x":17,"y":10}' /obj/item/chems/drinks/bottle/holywater/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume, list("holy" = TRUE)) + add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume, list(DATA_WATER_HOLINESS = TRUE)) /obj/item/chems/drinks/bottle/vermouth name = "Goldeneye Vermouth" diff --git a/mods/gamemodes/cult/ritual.dm b/mods/gamemodes/cult/ritual.dm index a6c6a1a3f748..27c5390851a4 100644 --- a/mods/gamemodes/cult/ritual.dm +++ b/mods/gamemodes/cult/ritual.dm @@ -29,7 +29,7 @@ return if(A.reagents && A.reagents.has_reagent(/decl/material/liquid/water)) to_chat(user, SPAN_NOTICE("You desecrate \the [A].")) - LAZYSET(A.reagents.reagent_data, /decl/material/liquid/water, list("holy" = FALSE)) + LAZYSET(A.reagents.reagent_data, /decl/material/liquid/water, list(DATA_WATER_HOLINESS = FALSE)) /mob/proc/make_rune(var/rune, var/cost = 5, var/tome_required = 0) var/has_robes = 0 From 22c08267dda0ec847210148c190c2ca0ed8985b8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 20:54:16 -0400 Subject: [PATCH 0721/1331] Attempt to fix default bodytype accessories not applying --- .../client/preference_setup/general/02_body.dm | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/code/modules/client/preference_setup/general/02_body.dm b/code/modules/client/preference_setup/general/02_body.dm index 6f23e996d2ce..44496bd2601f 100644 --- a/code/modules/client/preference_setup/general/02_body.dm +++ b/code/modules/client/preference_setup/general/02_body.dm @@ -24,7 +24,24 @@ // pref.sprite_accessories is sprite_accessories[accessory_category.type][loaded_accessory.type] = metadata // snapshot.sprite_accessories is sprite_accessories[organ_tag][accessory_category.type][loaded_accessory.type] = metadata // we have to convert it here + // on the bright side bodytype.default_sprite_accessories uses the same format var/list/new_sprite_accessories = list() + // THIS IS HACKY. PLEASE FIND A BETTER WAY TO DO THIS + // Adds default bodytype accessories to the snapshot. + var/decl/bodytype/the_bodytype = pref.get_bodytype_decl() + for(var/accessory_category in the_bodytype.default_sprite_accessories) + var/decl/sprite_accessory_category/acc_cat = GET_DECL(accessory_category) + if(!acc_cat.always_apply_defaults) + continue + var/list/accessories = the_bodytype.default_sprite_accessories[accessory_category] + acc_cat.prepare_mob_snapshot(snapshot, accessories) + for(var/accessory in accessories) + var/decl/sprite_accessory/accessory_decl = GET_DECL(accessory) + var/accessory_metadata = accessories[accessory] + for(var/bodypart in accessory_decl.body_parts) + LAZYINITLIST(new_sprite_accessories[bodypart]) + LAZYSET(new_sprite_accessories[bodypart][accessory_category], accessory, accessory_metadata) + // jank shit end for(var/accessory_category in pref.sprite_accessories) var/decl/sprite_accessory_category/acc_cat = GET_DECL(accessory_category) var/list/accessories = pref.sprite_accessories[accessory_category] From bc2eda1a0381a5b34417de26dc7b812b85037d00 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 07:45:42 -0400 Subject: [PATCH 0722/1331] Fix incorrect usage of machinery uid var --- code/game/machinery/computer/guestpass.dm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/game/machinery/computer/guestpass.dm b/code/game/machinery/computer/guestpass.dm index fb0011651ebf..4fcf1b94292d 100644 --- a/code/game/machinery/computer/guestpass.dm +++ b/code/game/machinery/computer/guestpass.dm @@ -58,13 +58,14 @@ var/reason = "NOT SPECIFIED" var/duration_max = 60 var/duration = 5 + var/terminal_serial var/list/internal_log = list() var/mode = 0 // 0 - making pass, 1 - viewing logs /obj/machinery/computer/guestpass/Initialize() . = ..() - uid = "[random_id("guestpass_serial_number",100,999)]-G[rand(10,99)]" + terminal_serial = "[random_id("guestpass_serial_number",100,999)]-G[rand(10,99)]" /obj/machinery/computer/guestpass/attackby(obj/used_item, mob/user) if(istype(used_item, /obj/item/card/id)) @@ -158,7 +159,7 @@ . = TOPIC_REFRESH else if (href_list["print"]) - var/dat = "

    Activity log of guest pass terminal #[uid]


    " + var/dat = "

    Activity log of guest pass terminal #[terminal_serial]


    " for (var/entry in internal_log) dat += "[entry]

    " var/obj/item/paper/paper = new/obj/item/paper( loc ) From 23c61f6fba59cd5ebedb8df94b4eb117cc2ad157 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 07:51:40 -0400 Subject: [PATCH 0723/1331] Fix incorrect typehint in sound_player constructor --- code/modules/synthesized_instruments/sound_player.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/synthesized_instruments/sound_player.dm b/code/modules/synthesized_instruments/sound_player.dm index 20df81d5456c..dcedc7096001 100644 --- a/code/modules/synthesized_instruments/sound_player.dm +++ b/code/modules/synthesized_instruments/sound_player.dm @@ -21,7 +21,7 @@ var/list/datum/sound_token/instrument/tokens = list() var/list/seen_turfs -/datum/sound_player/New(datum/real_instrument/where, datum/instrument/what) +/datum/sound_player/New(obj/where, datum/instrument/what) src.song = new (src, what) src.actual_instrument = where src.echo = global.musical_config.echo_default.Copy() From f826601c47b0c20c730d2786059f0be255794d39 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 07:54:04 -0400 Subject: [PATCH 0724/1331] Fix incorrect return value in pick_area_turf_by_flag --- code/_helpers/areas.dm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/_helpers/areas.dm b/code/_helpers/areas.dm index e985579e0abe..c26f3d2e9969 100644 --- a/code/_helpers/areas.dm +++ b/code/_helpers/areas.dm @@ -42,7 +42,7 @@ continue valid_areas[candidate_area] = TRUE if(!length(valid_areas)) // no turfs at all have that flag - return FALSE + return null // Each area contents loop is an in-world loop, so we just do one here. for(var/turf/turf_candidate in world) var/area/candidate_area = get_area(turf_candidate) @@ -50,8 +50,7 @@ continue if(!predicates || all_predicates_true(list(turf_candidate), predicates)) LAZYADD(turfs, turf_candidate) - if(LAZYLEN(turfs)) - return pick(turfs) + return SAFEPICK(turfs) /proc/pick_area_turf(var/areatype, var/list/predicates) var/list/turfs = get_area_turfs(areatype, predicates) From 47ef0cf49961f4ebf443caabce99076dd89a6242 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 21:03:16 -0400 Subject: [PATCH 0725/1331] Apply hacky fix for psi handler self-deletion --- code/datums/extensions/abilities/ability_handler.dm | 4 ++-- .../psionics/system/psionics/complexus/complexus_process.dm | 4 ++-- mods/content/psionics/system/psionics/mob/mob.dm | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/code/datums/extensions/abilities/ability_handler.dm b/code/datums/extensions/abilities/ability_handler.dm index d215868a4c58..5ac742b00384 100644 --- a/code/datums/extensions/abilities/ability_handler.dm +++ b/code/datums/extensions/abilities/ability_handler.dm @@ -21,7 +21,7 @@ if(!istype(owner)) CRASH("Ability handler received invalid owner!") ..() - refresh_login() + refresh_login(being_created = TRUE) /datum/ability_handler/Process() @@ -211,7 +211,7 @@ stat(stat_strings[1], stat_strings[2]) /// Individual ability methods/disciplines (psioncs, etc.) so that mobs can have multiple. -/datum/ability_handler/proc/refresh_login() +/datum/ability_handler/proc/refresh_login(being_created = FALSE) SHOULD_CALL_PARENT(TRUE) if(LAZYLEN(screen_elements)) var/list/add_elements = list() diff --git a/mods/content/psionics/system/psionics/complexus/complexus_process.dm b/mods/content/psionics/system/psionics/complexus/complexus_process.dm index 175269b56405..82883b67dc62 100644 --- a/mods/content/psionics/system/psionics/complexus/complexus_process.dm +++ b/mods/content/psionics/system/psionics/complexus/complexus_process.dm @@ -1,4 +1,4 @@ -/datum/ability_handler/psionics/proc/update(var/force) +/datum/ability_handler/psionics/proc/update(var/force, can_delete = TRUE) set waitfor = FALSE @@ -23,7 +23,7 @@ var/rank_count = max(1, LAZYLEN(ranks)) if(force || last_rating != ceil(combined_rank/rank_count)) if(highest_rank <= 1) - if(highest_rank == 0) + if(highest_rank == 0 && can_delete) // hack to prevent deletion on update(TRUE) in New qdel(src) return else diff --git a/mods/content/psionics/system/psionics/mob/mob.dm b/mods/content/psionics/system/psionics/mob/mob.dm index 15cc79147421..c7bc95cc25c9 100644 --- a/mods/content/psionics/system/psionics/mob/mob.dm +++ b/mods/content/psionics/system/psionics/mob/mob.dm @@ -1,6 +1,7 @@ -/datum/ability_handler/psionics/refresh_login() +/datum/ability_handler/psionics/refresh_login(being_created) . = ..() - update(TRUE) + // stopgap to prevent us from deleting ourselves during init + update(TRUE, can_delete = !being_created) if(!suppressed) show_auras() From 2b02af7ff012229f7c76d0d621638926d4332445 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 21:04:41 -0400 Subject: [PATCH 0726/1331] Fix awkward typo in mind-read power --- mods/content/psionics/system/psionics/faculties/coercion.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/content/psionics/system/psionics/faculties/coercion.dm b/mods/content/psionics/system/psionics/faculties/coercion.dm index eca5436d6316..fd4943113522 100644 --- a/mods/content/psionics/system/psionics/faculties/coercion.dm +++ b/mods/content/psionics/system/psionics/faculties/coercion.dm @@ -67,7 +67,7 @@ return if(target.stat == DEAD || (target.status_flags & FAKEDEATH) || !target.client) - to_chat(user, SPAN_WARNING("\The [target] is in no state for a mind-ream.")) + to_chat(user, SPAN_WARNING("\The [target] is in no state for a mind-read.")) return TRUE user.visible_message(SPAN_WARNING("\The [user] touches \the [target]'s temple...")) From 5c86dccbc1edf25197a322df86790b649843827d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 21:04:54 -0400 Subject: [PATCH 0727/1331] Fix extraneous unused toggle button in psi HUD --- mods/content/psionics/system/psionics/complexus/complexus.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mods/content/psionics/system/psionics/complexus/complexus.dm b/mods/content/psionics/system/psionics/complexus/complexus.dm index 0ce5f4cfc189..b839c057513a 100644 --- a/mods/content/psionics/system/psionics/complexus/complexus.dm +++ b/mods/content/psionics/system/psionics/complexus/complexus.dm @@ -1,4 +1,6 @@ /datum/ability_handler/psionics + category_toggle_type = null // we don't use this at the moment, but maybe should eventually. + var/announced = FALSE // Whether or not we have been announced to our holder yet. var/suppressed = TRUE // Whether or not we are suppressing our psi powers. var/use_psi_armour = TRUE // Whether or not we should automatically deflect/block incoming damage. From bd3de2b248a7ec9b8145aa8cdf9facef17e16767 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 20:05:17 -0400 Subject: [PATCH 0728/1331] Add a message implying that skill affects planetoid sensor readings --- code/modules/overmap/planetoids/_planetoids.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/overmap/planetoids/_planetoids.dm b/code/modules/overmap/planetoids/_planetoids.dm index 492d842b9953..87df92be13dc 100644 --- a/code/modules/overmap/planetoids/_planetoids.dm +++ b/code/modules/overmap/planetoids/_planetoids.dm @@ -64,7 +64,7 @@ var/inaccuracy = rand(8,12)/10 . += "Atmosphere pressure [atmosphere.return_pressure()*inaccuracy] kPa, temperature [atmosphere.temperature*inaccuracy] K
    " else if(user.skill_check(SKILL_SCIENCE, SKILL_BASIC) || user.skill_check(SKILL_ATMOS, SKILL_BASIC)) - . += "Atmosphere present
    " + . += "Atmosphere present. Sensor suite calibration required for detailed scan. Contact a qualified technician for calibration assistance.
    " . += "
    " var/datum/planetoid_data/E = get_planetoid_data() From e8bc1bfb21f7abd5443071be6d8055221248140f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 24 Jul 2025 19:09:12 -0400 Subject: [PATCH 0729/1331] Clean up the Lar Maria away site --- .../away_sites/lar_maria/lar_maria-1.dmm | 129 +++++++++++- .../away_sites/lar_maria/lar_maria-2.dmm | 187 ++++++++++++------ 2 files changed, 249 insertions(+), 67 deletions(-) diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm b/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm index 7b0313e2d201..ac4c19eb0c57 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm @@ -245,6 +245,12 @@ /area/lar_maria/vir_main) "aL" = ( /obj/structure/closet/secure_closet/personal/patient, +/obj/random/contraband{ + spawn_nothing_percentage = 80 + }, +/obj/random/maintenance/medical{ + spawn_nothing_percentage = 70 + }, /turf/floor/tiled/white, /area/lar_maria/vir_main) "aM" = ( @@ -578,6 +584,8 @@ /area/lar_maria/vir_main) "bO" = ( /obj/structure/table/glass, +/obj/random/medical, +/obj/random/medical, /turf/floor/tiled/white, /area/lar_maria/vir_main) "bQ" = ( @@ -904,6 +912,7 @@ /obj/structure/table/glass, /obj/item/firstaid/surgery, /obj/machinery/light, +/obj/random/medical, /turf/floor/tiled/white, /area/lar_maria/vir_main) "cG" = ( @@ -920,6 +929,7 @@ "cI" = ( /obj/structure/table/glass, /obj/item/scalpel, +/obj/random/medical, /turf/floor/tiled/white, /area/lar_maria/vir_main) "cJ" = ( @@ -1206,6 +1216,12 @@ /obj/machinery/light{ dir = 1 }, +/obj/random/contraband{ + spawn_nothing_percentage = 80 + }, +/obj/random/maintenance/medical{ + spawn_nothing_percentage = 70 + }, /turf/floor/tiled/white, /area/lar_maria/vir_aux) "du" = ( @@ -1214,6 +1230,12 @@ /area/lar_maria/vir_aux) "dv" = ( /obj/structure/closet/secure_closet/personal/patient, +/obj/random/contraband{ + spawn_nothing_percentage = 80 + }, +/obj/random/maintenance/medical{ + spawn_nothing_percentage = 70 + }, /turf/floor/tiled/white, /area/lar_maria/vir_aux) "dw" = ( @@ -1621,6 +1643,7 @@ "eF" = ( /obj/structure/table/glass, /obj/item/box/syringes, +/obj/random/medical/lite, /turf/floor/tiled/white, /area/lar_maria/vir_aux) "eG" = ( @@ -1628,6 +1651,10 @@ dir = 4 }, /obj/structure/table/glass, +/obj/random/medical/lite, +/obj/random/maintenance/medical{ + spawn_nothing_percentage = 70 + }, /turf/floor/tiled/white, /area/lar_maria/vir_aux) "eH" = ( @@ -1649,6 +1676,9 @@ /obj/structure/table/steel_reinforced, /obj/random/smokes, /obj/item/gun/energy/taser, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "eK" = ( @@ -1669,7 +1699,15 @@ "eN" = ( /obj/structure/closet, /obj/item/clothing/jumpsuit/red, -/obj/item/gun/projectile/shotgun/pump, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, +/obj/random/projectile/sec{ + spawn_nothing_percentage = 40 + }, +/obj/random/projectile/sec{ + spawn_nothing_percentage = 40 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "eO" = ( @@ -1694,6 +1732,9 @@ dir = 8; pixel_x = 24 }, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "eQ" = ( @@ -1793,12 +1834,16 @@ /obj/machinery/light{ dir = 8 }, +/obj/random/medical/lite, /turf/floor/tiled, /area/lar_maria/sec_wing) "ff" = ( /obj/structure/table/steel_reinforced, /obj/item/chems/spray/pepper, /obj/item/deck/cards, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "fg" = ( @@ -1806,6 +1851,9 @@ /obj/item/baton, /obj/item/baton, /obj/item/baton, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "fh" = ( @@ -1856,11 +1904,18 @@ "fm" = ( /obj/structure/table/steel_reinforced, /obj/item/clothing/head/soft/zhp_cap, +/obj/random/smokes, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "fn" = ( /obj/structure/table/steel_reinforced, /obj/item/handcuffs, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "fo" = ( @@ -1887,7 +1942,9 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/computer/modular, +/obj/machinery/computer/modular{ + dir = 1 + }, /turf/floor/tiled/white, /area/lar_maria/vir_aux) "fr" = ( @@ -1900,6 +1957,10 @@ "fs" = ( /obj/structure/table/glass, /obj/item/box/autoinjectors, +/obj/random/medical/lite, +/obj/random/maintenance/medical{ + spawn_nothing_percentage = 70 + }, /turf/floor/tiled/white, /area/lar_maria/vir_aux) "ft" = ( @@ -2186,6 +2247,9 @@ /obj/structure/cable{ icon_state = "1-4" }, +/obj/structure/cable{ + icon_state = "2-4" + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "fT" = ( @@ -2437,6 +2501,10 @@ /obj/item/stack/material/aerogel/mapped/tritium/ten, /obj/item/wirecutters, /obj/item/wrench, +/obj/random/tech_supply, +/obj/random/tech_supply, +/obj/random/tech_supply, +/obj/random/tool/power, /turf/floor/plating, /area/lar_maria/sec_wing) "gu" = ( @@ -2538,6 +2606,7 @@ /area/lar_maria/vir_access) "gH" = ( /obj/machinery/power/smes/buildable, +/obj/structure/cable, /turf/floor/plating, /area/lar_maria/sec_wing) "gI" = ( @@ -2562,7 +2631,7 @@ /turf/floor/plating, /area/lar_maria/sec_wing) "gL" = ( -/obj/machinery/suit_cycler, +/obj/machinery/suit_cycler/engineering/prepared/atmospheric, /turf/floor/plating, /area/lar_maria/sec_wing) "gM" = ( @@ -3309,6 +3378,9 @@ name = "Cells Dock Lockdown"; pixel_y = 7 }, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, /turf/floor/tiled, /area/lar_maria/sec_wing) "iC" = ( @@ -3447,6 +3519,43 @@ }, /turf/floor/tiled/white, /area/lar_maria/sec_wing) +"ov" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/floor/plating, +/area/lar_maria/sec_wing) +"qL" = ( +/obj/structure/table/glass, +/obj/item/box/freezer, +/obj/random/medical/lite, +/obj/random/maintenance/medical{ + spawn_nothing_percentage = 70 + }, +/turf/floor/tiled/white, +/area/lar_maria/vir_aux) +"vg" = ( +/obj/structure/table/steel_reinforced, +/obj/random/maintenance/security{ + spawn_nothing_percentage = 50 + }, +/turf/floor/tiled, +/area/lar_maria/sec_wing) +"De" = ( +/obj/random/medical/lite, +/turf/floor/tiled/white, +/area/lar_maria/vir_aux) +"Fl" = ( +/obj/machinery/suit_cycler/medical/prepared, +/turf/floor/plating, +/area/lar_maria/sec_wing) +"Ux" = ( +/obj/machinery/door/airlock/security, +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/floor/plating, +/area/lar_maria/sec_wing) (1,1,1) = {" aa @@ -22541,9 +22650,9 @@ cO cO cO cO -hC +vg eK -hC +vg cO iB iK @@ -24555,8 +24664,8 @@ eO fj fy fS -gi -dC +Ux +ov gH cO he @@ -25769,7 +25878,7 @@ cO fN cO dC -gL +Fl cO hk hx @@ -27985,7 +28094,7 @@ du du em ds -eY +qL fq fD ds @@ -28998,7 +29107,7 @@ eG fb ft ds -ds +De dr dr aa diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm index c528322152bd..e6ba0c32dd3b 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm @@ -509,7 +509,7 @@ /turf/floor/plating, /area/lar_maria/solar_control) "br" = ( -/obj/machinery/suit_cycler, +/obj/machinery/suit_cycler/engineering/prepared/atmospheric, /turf/floor/plating, /area/lar_maria/solar_control) "bs" = ( @@ -618,6 +618,9 @@ /obj/machinery/light{ dir = 1 }, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, /turf/floor/tiled, /area/lar_maria/library) "bJ" = ( @@ -808,31 +811,49 @@ "cr" = ( /obj/structure/bookcase, /obj/item/book/manual/engineering_guide, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, /turf/floor/tiled, /area/lar_maria/library) "cs" = ( /obj/structure/bookcase, /obj/item/book/fluff/stasis, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, /turf/floor/tiled, /area/lar_maria/library) "cu" = ( /obj/structure/bookcase, /obj/item/book/manual/medical_diagnostics_manual, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, /turf/floor/tiled, /area/lar_maria/library) "cv" = ( /obj/structure/bookcase, /obj/item/book/manual/detective, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, /turf/floor/tiled, /area/lar_maria/library) "cx" = ( /obj/structure/bookcase, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, /turf/floor/tiled, /area/lar_maria/library) "cy" = ( /obj/structure/bookcase, /obj/item/book/fluff/anomaly_spectroscopy, /obj/item/book/fluff/anomaly_testing, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, /turf/floor/tiled, /area/lar_maria/library) "cz" = ( @@ -959,7 +980,7 @@ /turf/floor/plating, /area/lar_maria/atmos) "cR" = ( -/obj/machinery/atmospherics/binary/pump{ +/obj/machinery/atmospherics/binary/pump/on{ dir = 4 }, /turf/floor/plating, @@ -1094,7 +1115,11 @@ /area/lar_maria/atmos) "dn" = ( /obj/machinery/atmospherics/omni/mixer{ - dir = 4 + dir = 4; + tag_west = 1; + tag_north = 1; + tag_north_con = 0.79; + tag_west_con = 0.21 }, /turf/floor/plating, /area/lar_maria/atmos) @@ -1106,14 +1131,16 @@ /area/lar_maria/atmos) "dp" = ( /obj/machinery/atmospherics/omni/filter{ - dir = 4 + dir = 4; + tag_south = 8; + tag_filter_gas_south = "gas_carbon_dioxide"; + tag_east = 2; + tag_west = 1 }, -/obj/abstract/landmark/allowed_leak, /turf/floor/plating, /area/lar_maria/atmos) "dr" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/cyan, -/obj/abstract/landmark/allowed_leak, /turf/floor/plating, /area/lar_maria/atmos) "ds" = ( @@ -1179,12 +1206,10 @@ /area/lar_maria/atmos) "dD" = ( /obj/machinery/atmospherics/binary/oxyregenerator, -/obj/abstract/landmark/allowed_leak, /turf/floor/plating, /area/lar_maria/atmos) "dE" = ( /obj/machinery/atmospherics/pipe/simple/visible/blue, -/obj/abstract/landmark/allowed_leak, /turf/floor/plating, /area/lar_maria/atmos) "dF" = ( @@ -1853,6 +1878,7 @@ "fM" = ( /obj/structure/table, /obj/random/gloves, +/obj/random/medical/lite, /turf/floor/tiled, /area/lar_maria/office) "fN" = ( @@ -1866,6 +1892,7 @@ "fP" = ( /obj/structure/table/steel, /obj/random/firstaid, +/obj/random/medical/lite, /turf/floor/tiled, /area/lar_maria/office) "fQ" = ( @@ -2099,6 +2126,7 @@ "gC" = ( /obj/structure/table, /obj/random/trash, +/obj/random/medical/pillbottle, /turf/floor/plating, /area/lar_maria/hallway) "gD" = ( @@ -2248,6 +2276,15 @@ /area/lar_maria/mess_hall) "hb" = ( /obj/structure/closet/crate/trashcart, +/obj/random/trash, +/obj/random/trash, +/obj/random/trash, +/obj/random/useful{ + spawn_nothing_percentage = 70 + }, +/obj/item/book/union_charter{ + dat = "The contents of the book are heavily defaced and nearly illegible." + }, /turf/floor/plating, /area/lar_maria/hallway) "hc" = ( @@ -2367,15 +2404,6 @@ "hu" = ( /turf/floor/plating, /area/lar_maria/hallway) -"hv" = ( -/obj/machinery/power/apc{ - dir = 4; - name = "east bump"; - pixel_x = 24 - }, -/obj/random/trash, -/turf/floor/plating, -/area/lar_maria/hallway) "hw" = ( /obj/structure/cable{ icon_state = "0-4" @@ -2383,6 +2411,11 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, +/obj/machinery/power/apc{ + dir = 8; + name = "west bump"; + pixel_x = -24 + }, /turf/floor/tiled, /area/lar_maria/hallway) "hx" = ( @@ -2993,7 +3026,8 @@ /obj/machinery/button/access/interior{ id_tag = "ZHPDock_airlock"; pixel_x = 25; - pixel_y = -25 + pixel_y = -25; + dir = 8 }, /turf/floor/plating, /area/lar_maria/hallway) @@ -3026,7 +3060,8 @@ "jn" = ( /obj/machinery/airlock_sensor{ id_tag = "ZHPDock_airlock_sensor"; - pixel_x = 25 + pixel_x = 22; + dir = 8 }, /turf/floor/plating, /area/lar_maria/hallway) @@ -3089,6 +3124,14 @@ }, /turf/floor/plating, /area/lar_maria/solar_control) +"nm" = ( +/obj/structure/bookcase, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 + }, +/obj/item/book/manual/evaguide, +/turf/floor/tiled, +/area/lar_maria/library) "ob" = ( /obj/structure/ladder, /obj/machinery/light/small{ @@ -3097,13 +3140,6 @@ }, /turf/floor/plating, /area/lar_maria/solar_control) -"ok" = ( -/obj/machinery/atmospherics/binary/pump{ - dir = 4 - }, -/obj/abstract/landmark/allowed_leak, -/turf/floor/plating, -/area/lar_maria/atmos) "pb" = ( /obj/machinery/atmospherics/binary/pump{ dir = 8 @@ -3161,18 +3197,29 @@ /obj/machinery/light/small, /turf/floor/plating, /area/lar_maria/atmos) +"uf" = ( +/obj/machinery/atmospherics/omni/filter{ + dir = 4; + tag_south = 8; + tag_filter_gas_south = "gas_oxygen"; + tag_east = 2; + tag_west = 1 + }, +/turf/floor/plating, +/area/lar_maria/atmos) "vb" = ( /obj/structure/hygiene/sink{ dir = 4; pixel_x = 11 }, -/obj/structure/mirror{ - pixel_x = 30 - }, /obj/machinery/alarm{ pixel_y = 23; req_access = newlist() }, +/obj/structure/mirror{ + pixel_x = 29; + dir = 8 + }, /turf/floor/tiled/white, /area/lar_maria/head_m) "wb" = ( @@ -3181,11 +3228,21 @@ pixel_x = 11 }, /obj/structure/mirror{ - pixel_x = 30 + pixel_x = 29; + dir = 8 }, /obj/random/trash, /turf/floor/tiled/white, /area/lar_maria/head_m) +"wW" = ( +/obj/machinery/atmospherics/omni/filter{ + dir = 4; + tag_north = 8; + tag_east = 2; + tag_west = 1 + }, +/turf/floor/plating, +/area/lar_maria/atmos) "xb" = ( /obj/structure/hygiene/sink{ dir = 8; @@ -3193,35 +3250,51 @@ pixel_y = 2 }, /obj/structure/mirror{ - pixel_x = -30 + pixel_x = -29; + dir = 4 }, /turf/floor/tiled/white, /area/lar_maria/head_f) +"xx" = ( +/obj/machinery/atmospherics/pipe/simple/visible/universal, +/turf/floor/plating, +/area/lar_maria/atmos) "Ag" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ +/obj/machinery/atmospherics/pipe/manifold/visible/red{ dir = 8 }, -/obj/abstract/landmark/allowed_leak, /turf/floor/plating, /area/lar_maria/atmos) +"Am" = ( +/obj/machinery/embedded_controller/radio/airlock/airlock_controller{ + id_tag = "ZHPDock"; + tag_airpump = "ZHPEastSolar_pump"; + tag_chamber_sensor = "ZHPEastSolar_sensor"; + tag_exterior_door = "ZHPEastSolar_outer"; + tag_interior_door = "ZHPEastSolar_inner"; + dir = 4; + pixel_x = -22 + }, +/turf/floor/plating, +/area/lar_maria/hallway) "DG" = ( /obj/machinery/computer/modular{ dir = 1 }, /turf/floor/tiled, /area/lar_maria/office) -"FZ" = ( -/obj/machinery/atmospherics/pipe/simple/visible/red, -/obj/abstract/landmark/allowed_leak, +"Kg" = ( +/obj/machinery/suit_cycler/medical/prepared, /turf/floor/plating, -/area/lar_maria/atmos) -"YS" = ( -/obj/machinery/atmospherics/pipe/simple/visible/red{ - dir = 10 +/area/lar_maria/solar_control) +"NR" = ( +/obj/structure/bookcase, +/obj/random/single/textbook{ + spawn_nothing_percentage = 80 }, -/obj/abstract/landmark/allowed_leak, -/turf/floor/plating, -/area/lar_maria/atmos) +/obj/item/book/union_charter, +/turf/floor/tiled, +/area/lar_maria/library) (1,1,1) = {" aa @@ -20885,7 +20958,7 @@ bw bw bI cd -cx +NR cM di cd @@ -21489,7 +21562,7 @@ aA ad bw bw -cx +nm cf cx cO @@ -22503,7 +22576,7 @@ pb ci cA cR -ok +cR dB tb bx @@ -22517,7 +22590,7 @@ ex eu gC hd -hv +hc hS iq iN @@ -22704,7 +22777,7 @@ bx bM ci cn -YS +cl dn dB bM @@ -22901,13 +22974,13 @@ aY bg nb br -br +Kg bx bN ci cn cn -ok +cR dC dO bx @@ -23333,7 +23406,7 @@ je jg ji jm -hu +Am jo jq aa @@ -23511,10 +23584,10 @@ bA bG bQ ck -bQ cU +xx Ag -bQ +xx dR bG eA @@ -23713,8 +23786,8 @@ bB bx bR ci -cn cV +cn dp dD dS @@ -23917,7 +23990,7 @@ bS ci cA cn -dp +uf dE dT bx @@ -24118,8 +24191,8 @@ bx qb cl cB -FZ -dp +cB +wW dF ub bx From 64d3aaac0575dc66a7649e296623d8d1282ce16e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 21 Jul 2025 17:18:36 -0400 Subject: [PATCH 0730/1331] Improve atmos canister initialization --- code/game/machinery/atmoalter/canister.dm | 108 +++++++--------------- 1 file changed, 35 insertions(+), 73 deletions(-) diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 267f68ff20d5..af152d8b5028 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -21,12 +21,16 @@ var/canister_color = "yellow" var/can_label = TRUE var/temperature_resistance = 1000 + T0C + var/decl/material/start_gas = null /obj/machinery/portable_atmospherics/canister/Initialize(mapload, material) if(ispath(material)) matter = list() matter[material] = 10 * SHEET_MATERIAL_AMOUNT . = ..(mapload) + if(start_gas) + air_contents.adjust_gas(start_gas, MolesForPressure()) + lazy_update_icon() /obj/machinery/portable_atmospherics/canister/drain_power() return -1 @@ -36,45 +40,69 @@ icon_state = "redws" canister_color = "redws" can_label = FALSE + start_gas = /decl/material/gas/nitrous_oxide /obj/machinery/portable_atmospherics/canister/nitrogen name = "nitrogen canister" icon_state = "red" canister_color = "red" can_label = FALSE + start_gas = /decl/material/gas/nitrogen /obj/machinery/portable_atmospherics/canister/nitrogen/prechilled name = "cryogenic nitrogen canister" start_pressure = 20 ATM +/obj/machinery/portable_atmospherics/canister/nitrogen/prechilled/Initialize() + . = ..() + air_contents.temperature = 80 + lazy_update_icon() + /obj/machinery/portable_atmospherics/canister/oxygen name = "oxygen canister" icon_state = "blue" canister_color = "blue" can_label = FALSE + start_gas = /decl/material/gas/oxygen /obj/machinery/portable_atmospherics/canister/oxygen/prechilled name = "cryogenic oxygen canister" start_pressure = 20 ATM +/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/Initialize() + . = ..() + air_contents.temperature = 80 + lazy_update_icon() + /obj/machinery/portable_atmospherics/canister/hydrogen name = "hydrogen canister" icon_state = "purple" canister_color = "purple" can_label = FALSE + start_gas = /decl/material/gas/hydrogen /obj/machinery/portable_atmospherics/canister/carbon_dioxide name = "\improper CO2 canister" icon_state = "black" canister_color = "black" can_label = FALSE + start_gas = /decl/material/gas/carbon_dioxide +// This uses an Initialize override instead of start_gas. /obj/machinery/portable_atmospherics/canister/air name = "air canister" icon_state = "grey" canister_color = "grey" can_label = FALSE +// This doesn't use start_gas because it's a mix. +/obj/machinery/portable_atmospherics/canister/air/Initialize() + . = ..() + var/list/air_mix = StandardAirMix() + air_contents.adjust_gas(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen], FALSE) + air_contents.adjust_gas(/decl/material/gas/nitrogen, air_mix[/decl/material/gas/nitrogen]) + lazy_update_icon() + /obj/machinery/portable_atmospherics/canister/air/airlock start_pressure = 3 ATM @@ -301,65 +329,11 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) return STATUS_CLOSE return ..() -/obj/machinery/portable_atmospherics/canister/oxygen/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/oxygen, MolesForPressure()) - update_icon() - -/obj/machinery/portable_atmospherics/canister/hydrogen/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/hydrogen, MolesForPressure()) - update_icon() - -/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/Initialize() - . = ..() - air_contents.temperature = 80 - update_icon() - -/obj/machinery/portable_atmospherics/canister/sleeping_agent/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/nitrous_oxide, MolesForPressure()) - update_icon() - -/obj/machinery/portable_atmospherics/canister/nitrogen/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/nitrogen, MolesForPressure()) - update_icon() - -/obj/machinery/portable_atmospherics/canister/nitrogen/prechilled/Initialize() - . = ..() - air_contents.temperature = 80 - update_icon() - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/carbon_dioxide, MolesForPressure()) - update_icon() - - -/obj/machinery/portable_atmospherics/canister/air/Initialize() - . = ..() - var/list/air_mix = StandardAirMix() - air_contents.adjust_gas(/decl/material/gas/oxygen, air_mix[/decl/material/gas/oxygen], FALSE) - air_contents.adjust_gas(/decl/material/gas/nitrogen, air_mix[/decl/material/gas/nitrogen]) - update_icon() - - // Special types used for engine setup admin verb, they contain double amount of that of normal canister. -/obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/nitrogen, MolesForPressure()) - update_icon() - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/carbon_dioxide, MolesForPressure()) - update_icon() - -/obj/machinery/portable_atmospherics/canister/hydrogen/engine_setup/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/hydrogen, MolesForPressure()) - update_icon() +#define ENGINE_SETUP_CANISTER(BASE_TYPE) ##BASE_TYPE/engine_setup/start_pressure = BASE_TYPE::start_pressure * 2; +ENGINE_SETUP_CANISTER(/obj/machinery/portable_atmospherics/canister/nitrogen) +ENGINE_SETUP_CANISTER(/obj/machinery/portable_atmospherics/canister/carbon_dioxide) +ENGINE_SETUP_CANISTER(/obj/machinery/portable_atmospherics/canister/hydrogen) // Spawn debug tanks. /obj/machinery/portable_atmospherics/canister/helium @@ -367,31 +341,19 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) icon_state = "black" canister_color = "black" can_label = FALSE - -/obj/machinery/portable_atmospherics/canister/helium/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/helium, MolesForPressure()) - update_icon() + start_gas = /decl/material/gas/helium /obj/machinery/portable_atmospherics/canister/methyl_bromide name = "\improper CH3Br canister" icon_state = "black" canister_color = "black" can_label = FALSE - -/obj/machinery/portable_atmospherics/canister/methyl_bromide/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/methyl_bromide, MolesForPressure()) - update_icon() + start_gas = /decl/material/gas/methyl_bromide /obj/machinery/portable_atmospherics/canister/chlorine name = "chlorine canister" icon_state = "black" canister_color = "black" can_label = FALSE - -/obj/machinery/portable_atmospherics/canister/chlorine/Initialize() - . = ..() - air_contents.adjust_gas(/decl/material/gas/chlorine, MolesForPressure()) - update_icon() + start_gas = /decl/material/gas/chlorine // End debug tanks. From 97fe4fa53789e975188c08a4402ed2f8ab7d20e7 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 23 Jul 2025 08:02:10 -0400 Subject: [PATCH 0731/1331] Fix modkits passing species uid to GET_DECL --- code/game/objects/items/devices/modkit.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/devices/modkit.dm b/code/game/objects/items/devices/modkit.dm index b5eb44262b35..8b73d443025e 100644 --- a/code/game/objects/items/devices/modkit.dm +++ b/code/game/objects/items/devices/modkit.dm @@ -18,7 +18,7 @@ /obj/item/modkit/Initialize(ml, material_key) if(!target_bodytype) - var/decl/species/species = GET_DECL(global.using_map.default_species) + var/decl/species/species = decls_repository.get_decl_by_id(global.using_map.default_species) target_bodytype = species.default_bodytype.bodytype_flag . = ..() From 6938df126c74e630c61855666c14622b47633d27 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 28 Jul 2025 02:34:57 -0400 Subject: [PATCH 0732/1331] Fix incorrect indentation Co-authored-by: MistakeNot4892 --- code/modules/overmap/planetoids/_planetoids.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/overmap/planetoids/_planetoids.dm b/code/modules/overmap/planetoids/_planetoids.dm index 87df92be13dc..16ea48dfe92b 100644 --- a/code/modules/overmap/planetoids/_planetoids.dm +++ b/code/modules/overmap/planetoids/_planetoids.dm @@ -64,7 +64,7 @@ var/inaccuracy = rand(8,12)/10 . += "Atmosphere pressure [atmosphere.return_pressure()*inaccuracy] kPa, temperature [atmosphere.temperature*inaccuracy] K
    " else if(user.skill_check(SKILL_SCIENCE, SKILL_BASIC) || user.skill_check(SKILL_ATMOS, SKILL_BASIC)) - . += "Atmosphere present. Sensor suite calibration required for detailed scan. Contact a qualified technician for calibration assistance.
    " + . += "Atmosphere present. Sensor suite calibration required for detailed scan. Contact a qualified technician for calibration assistance.
    " . += "
    " var/datum/planetoid_data/E = get_planetoid_data() From f8c288f8c7b0e50ef0c7cc9f4c05df634e27623f Mon Sep 17 00:00:00 2001 From: Elizabeth Lavenza Date: Thu, 27 Mar 2025 21:38:33 -0400 Subject: [PATCH 0733/1331] Add a variant sprite for cobblestone paths --- code/game/turfs/flooring/flooring_path.dm | 1 + code/game/turfs/floors/subtypes/floor_path.dm | 2 +- icons/turf/flooring/path.dmi | Bin 1614 -> 1873 bytes 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/turfs/flooring/flooring_path.dm b/code/game/turfs/flooring/flooring_path.dm index 475573d57730..cb8c06d48c4a 100644 --- a/code/game/turfs/flooring/flooring_path.dm +++ b/code/game/turfs/flooring/flooring_path.dm @@ -27,6 +27,7 @@ icon_base = "cobble" icon_edge_layer = FLOOR_EDGE_PATH flooring_flags = TURF_REMOVE_CROWBAR + has_base_range = 1 /decl/flooring/path/running_bond name = "stone path" diff --git a/code/game/turfs/floors/subtypes/floor_path.dm b/code/game/turfs/floors/subtypes/floor_path.dm index 65e09e4733c7..6b6c1dfca159 100644 --- a/code/game/turfs/floors/subtypes/floor_path.dm +++ b/code/game/turfs/floors/subtypes/floor_path.dm @@ -5,7 +5,7 @@ desc = "A cobbled path made of loose stones." color = COLOR_GRAY icon = 'icons/turf/flooring/path.dmi' - icon_state = "cobble" + icon_state = "cobble0" _flooring = /decl/flooring/path/cobblestone floor_material = /decl/material/solid/stone/sandstone _base_flooring = /decl/flooring/dirt diff --git a/icons/turf/flooring/path.dmi b/icons/turf/flooring/path.dmi index 1f1d1798b3abc6a38fbe29641ec658f917163f70..6c99784bf5b0b75b0f807568d4924b91d1d2758c 100644 GIT binary patch delta 1764 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 Date: Mon, 12 May 2025 17:47:31 -0400 Subject: [PATCH 0734/1331] Add alternate running bond path tiles --- code/game/turfs/flooring/flooring_path.dm | 2 ++ code/game/turfs/floors/subtypes/floor_path.dm | 2 +- icons/turf/flooring/path.dmi | Bin 1873 -> 2151 bytes 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/code/game/turfs/flooring/flooring_path.dm b/code/game/turfs/flooring/flooring_path.dm index cb8c06d48c4a..cd821d713a86 100644 --- a/code/game/turfs/flooring/flooring_path.dm +++ b/code/game/turfs/flooring/flooring_path.dm @@ -33,10 +33,12 @@ name = "stone path" desc = "A rustic stone path, laid out in a running bond pattern." icon_base = "runningbond" + has_base_range = 3 gender = NEUTER /decl/flooring/path/herringbone name = "stone path" desc = "A rustic stone path, laid out in a herringbone pattern." icon_base = "herringbone" + has_base_range = null gender = NEUTER diff --git a/code/game/turfs/floors/subtypes/floor_path.dm b/code/game/turfs/floors/subtypes/floor_path.dm index 6b6c1dfca159..7d714f8efab9 100644 --- a/code/game/turfs/floors/subtypes/floor_path.dm +++ b/code/game/turfs/floors/subtypes/floor_path.dm @@ -28,7 +28,7 @@ LAZYADD(decals, moss) /turf/floor/path/running_bond - icon_state = "runningbond" + icon_state = "runningbond0" _flooring = /decl/flooring/path/running_bond /turf/floor/path/herringbone diff --git a/icons/turf/flooring/path.dmi b/icons/turf/flooring/path.dmi index 6c99784bf5b0b75b0f807568d4924b91d1d2758c..2f13c15ef5d6dc97fc6e37e4e6532efc77a03363 100644 GIT binary patch delta 2108 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 From aea4fa0b2b4bd694d72d40542b6dd9f103300613 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Fri, 20 Jun 2025 21:47:49 -0400 Subject: [PATCH 0735/1331] Add ambience to lake and river areas --- maps/shaded_hills/areas/grassland.dm | 3 +++ maps/shaded_hills/areas/woods.dm | 1 + 2 files changed, 4 insertions(+) diff --git a/maps/shaded_hills/areas/grassland.dm b/maps/shaded_hills/areas/grassland.dm index 803f9a5e9200..ae0827242f20 100644 --- a/maps/shaded_hills/areas/grassland.dm +++ b/maps/shaded_hills/areas/grassland.dm @@ -25,6 +25,8 @@ 'sound/ambience/ominous2.ogg', 'sound/ambience/ominous3.ogg', ) + // hopefully the sound environment makes this sound nicer? + forced_ambience = list('sound/ambience/shore.ogg') /area/shaded_hills/outside/poi name = "Deep Grassland" @@ -34,6 +36,7 @@ color = COLOR_BLUE description = "The soft susurration of running water mingles with the hum of insects and croak of frogs." area_blurb_category = /area/shaded_hills/outside/river + forced_ambience = list('sound/ambience/shore.ogg') /area/shaded_hills/outside/river/get_additional_fishing_results() var/static/list/additional_fishing_results = list( diff --git a/maps/shaded_hills/areas/woods.dm b/maps/shaded_hills/areas/woods.dm index 5a5a662ed04c..1ef25b471ca7 100644 --- a/maps/shaded_hills/areas/woods.dm +++ b/maps/shaded_hills/areas/woods.dm @@ -4,6 +4,7 @@ /area/shaded_hills/outside/river/lake name = "Woodland Lake" + forced_ambience = list('sound/ambience/shore.ogg') /area/shaded_hills/outside/river/lake/get_additional_fishing_results() var/static/list/additional_fishing_results = list( From 980e26f7041f1e56e8616a9178391da0dd980e06 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Fri, 20 Jun 2025 21:48:12 -0400 Subject: [PATCH 0736/1331] Add marsh ambience to swamp areas --- maps/shaded_hills/areas/swamp.dm | 3 +++ sound/ambience/marshy.ogg | Bin 0 -> 613708 bytes 2 files changed, 3 insertions(+) create mode 100644 sound/ambience/marshy.ogg diff --git a/maps/shaded_hills/areas/swamp.dm b/maps/shaded_hills/areas/swamp.dm index 0b4110cf2b6d..7c1de587f446 100644 --- a/maps/shaded_hills/areas/swamp.dm +++ b/maps/shaded_hills/areas/swamp.dm @@ -10,9 +10,11 @@ /area/shaded_hills/outside/swamp name = "Swamp" description = "The reek of stagnant water and the chirp of insects filter through the humid air." + forced_ambience = list('sound/ambience/marshy.ogg') /area/shaded_hills/outside/swamp/poi name = "Deep Swamp" + forced_ambience = list('sound/ambience/marshy.ogg') /area/shaded_hills/outside/river/swamp name = "Swampy River" @@ -20,6 +22,7 @@ /area/shaded_hills/caves/swamp name = "Southern Deep Tunnels" + forced_ambience = list('sound/ambience/marshy.ogg') /area/shaded_hills/caves/unexplored/swamp name = "Trackless Deeps - Far South" diff --git a/sound/ambience/marshy.ogg b/sound/ambience/marshy.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d481be1e091da70561ce0deff159f54716741b2e GIT binary patch literal 613708 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 gvv`Wc4<=j^v-Qp#BhzKP#V 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 z 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 A?c1}ydFl)5F?(6@j#%nHc8 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℞~{dnwK=~Yi6gtlE(M91gUClo2@F#Nj+ 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 z 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 sIA0S&Zm_BBliew&hJ-|@8aw&1~oR8_z46;p4% zWu8=|I!%t zRyr(1X;wJwL9#Qk=#?BZ z`Gv-trLe-2i`Lo-W``xGtHDhOfc#i_7 zQo}q?skOU&--8%;`YX02Q9Q@LKe`3v)|GcR)pnCvYks}pu6niQ(f_HOllx5@Os3_v zP`AuqaNP?Dp?%zk|0Ko8jcVbgP;1^~k(tYkpy!xP7qoTYYss!%pWT8q7cRTpkKMzB z(2lmTG9^VzF#x8Xx)wuMt}K{M_Ph=^!2saRG#hW2BQ4@#B65+_he(g!z2|ROI&Pl~;3b3o^Tk{d zjxw@QyJKAPq(35wFynhg6RRUP{jt|a0v9gmzt?ztm;XWH^7t>%$1gcq9YnVbZemPA z{rAs_*K= zv?<2#S1&HBbD@a*wyrC?kfwQh(xn>SiJ2nXgkNX8x7p*AX^Lo7*ce6o7TR{ zFoUkF#H{4fnk_vbD$WN7q))s(Y1rO(5pNAVWGwI`w0y#m8myLDDU>79(_Ohb7GAYe zRXej>LThJZkg|}x#FNPHM3^hHvOi(?kD++`i^3-*dH=;T;{~u*UvTODooPqJ<%b*e zqT#a-5u@lGSi#!BiZjiAtxew7(E6LMf@OQ)@BkoGyIcjuMH5mF5Ri3d?!ON8jP`d#^i8?5uS~n>y%ibLH>+%8AOwN)vy6xf`x|1|Qc`X zSIa+gt0$jTCF~FsA4&VcL+u?-&yRG%wlLy0Pkp3Trm?c2&5|?b7aQ(QL?7bxm6sM1 zw2Q=**JU2?_mDdp!c?~AnLt^fhJ=WofdHzqDidjUbO zjDQx3&$`fo$5!pmS+LlHrWsmk;VskjGrv1Lr%>GMl?B$Cy{FXETjKt#t@3Yu{l|tj zBRSK8s6eTn+hhr0RqWJuFZ^{^SezJOApRiB)%RoP1-Ym?(OHqwQ#*Z^T_5ibMk+4z z0tlt^c6sOLJ2b)Du?OUU-uWp{*xdr^ufD9}$Z*pASJv#>*`Rg8oQAs%| z3Swav25o-g?PCrFszcYmvdX1f*~zx(E+H8`vufE;I$lrwM7CXKOUqg-xX9!Nvt@1V zXg~CRp(WO`p_%$#?v4ggNU{HBqW+Aq7v#Q6ZJ8dXUU38H@kgV_E=wx|z^r55+7b7__;XECePi06J z%vNBPi=f*Vo8)Ha?u|s>yt>e2%t4<&G%*5SA(8o_K^N@hKB%M8dQnGFS;SRoO0ENX z3RS*(u|5!xYvc&-m?8@=5@yfDKjvbg1zV8aA z6XBBP&FwKskY+DJswDU##ons5`lT`}nPdbfCT7YHn~FlWgwuMebm%D5StmkA2nyD3 zP(1RG-&oVZci46bUx%@NKK;*q2T3t+H^;S)=c>reI%B3h7;@S;He+9F;z(88A%-q7 zSw_sf#Ovfx{t;%)WKcxc@o%%C%oH^!KAQwG=G17q`76atn5WByf|aF7!|%P?d}L0t zv^29(amNoTfSiN8&)}fqHdyBkKO5LBB z)RA@nwNTmUqWB9<=lCz*qYrM&^2E4U+|+HWUt%U&Wp_elrmvjE+iH(EnV(+f(~Hk& zKA_Wsq`x2(zN9=_a#>@cE{xW*TO4Sw^gG%yS zb*S-77?bZO0)tD2Ns`?B=qG9a;k-<3LW7$3x=uc)mxbz%5(-8E4L?Cf&g= z>z}mW8X3YA{nBGMm;8X>6r0;l9C)vkx6VthZV8C3e6C9{G=_rv|F`K_1&4H|p|abT zyfPe)omb11A8qIMU0-7B0%hCwaSM%ZY*3^^$<4gBzcr3lk{pLsfQ(tOMzt$5bvI#LLik)h{>6?CaLznu2Ap4} z)2XeEKOT|Xx8MkyM^7M|rm7WxZm(t$)Rk+swZpRS4g%J0_Y^M<3cV#hF7lf!WxM+L z(75ki<4{e&-$)|PKaUo_(d`%=^SFfOv}qp0@}EoZ$(N-qiO)ZglVJW%mCtD4LlV_% zp>*Mr#l{(}&8aQI^Ma+oI_fTfzRikQZwQ9k-+b2`L}0~ycqe#`ry-(j+TnY!4eUKN z9oZ&$7$e7(kqzv``GWmJQ=oah8jHz1pHehEl-#lWN~+rDeF5q>TKay52P2Z!J-WhF zYOgBdD*kXrcYV~CCP$oqxUbB2-YOlUL zCb!{kB0ST##`##-YaBf901K4Pqw^2mj)3y*0pqlUu$DcwF0%AzhPLDr{|ceF>KjT8 z+iJ{d7Tp3mLnU&0Y3-5zht$pnHg4OC3){)qwe+QKg)EfO`^xnieqJcqzd*~!2h9Ks z^V&k-!wd3dH^lgDh}SK^W-VHOEa}On58oSphClTL{e^^|ThP9dLMY;E@2Z*$a~5o1 zm0+^y@T30?a2Wib@(_zr9yXZ#gh`KXPmd07R~`=LcFuQ?w@$ZrgE@GzK@^pptq;<7jl!T*h*IHIGS;a;2cFd#0f1zPXw`=|yl>1scd$R0NyljkStn zAjPHBJeh^Ho<*J3NCya`&k`{7rVCYYY%sGNkf5GfVQGo&BzvHMdfGwug|=cMR)Sop zXPv_bv5Cwfrs^2|M$f+W_oPgWpvXn7%P%rmTO(W)7z$(%do0xqe0J4iWmKnB@P-$+ zoQ1yFI=U~Xr{0u8!MiYI$J?8 zxcOWE=qLB7kL44a+SNFi?9LV;60s{h^ObdESxtOgf#bD7fSqpjSRa)ocgA*@Yl(^< z#xT%_&c%EU>SRqUvSxWcGJ>HyFf(fQ6I>9A0dQg$Fz<9CnkyZc`|(K}EI8QX^k`t` zvuFV0YCG!Yvj)nnuKRYa{+zHAZB_8pe)jBr>4%QCVD!d|K6YkslB(?=JG>%L(IThtFTaf zO@kwheb8!p_FjMGx%u$}>kw)>vP85L6>P@Dz0ilPBc|wbQ%fQK-M_0(DAq1G1znf< zkM^$YCZAsYwcvuz?#xdw=C>0bi&&jD(|~{E@SYbCrwskjgL%(Tr9L!h!54JyOGz8J zn1wz-AFaPXI&}ki$t=NIE&LYHqMwbI+(aBj4^DuLYwLP4pFad+GIm~%p%PEz1v>j* zG9a%XHo$z>j}vKvYqb0gpt~DWUbG6=Ajq!zm@VX8gNF>u%(1FH3I9noEOnA#z-E_S zgAf-vTwYck77Cxca0kdhqT}*eJ6DcVu)(iL7PA(tPEF|-YO2gf|Fno@_CvkR+%d*5^QQ3Dw7v|g8$1S1!%do6CRTB%KOm7;5~E1act0-o z%3;z7SPfUCpdf$J$;gc!fj(ZH0-Abl0y!V$SaNXwr9E4z>8*%u-p69vpUZWl?O;mNoh*2p|E(_bN{6yT z%yZJzo(bvC6j;TyW=wA08eJH}9^G}L*ZPS4`OjYpHpx?ea?G*Uvg|7$6&eGKhux-y z2o)1k8#Y(sa90*JsO?Z7XCR-5OpdIxiO_2&dQF^=cOiQhGl@%+w+h=n{VXvl?fU-( zb=cJf6Jl~K4PE3#A^A7t9-DGp`2PMR^%;^66?Ud4u zg8GOA2DH`G@#f}!tBh@P%VnCc0D3)_QD%yzz2=4X+yzNg{PDT%^(%=A%n; z!jB6H$raa=RUPVR>Rxe)7h!r)G&Gzzlhn&k3N=|Z0{?~YdP$E4ql@;Zm?CE}$>6W3 zua$pT3vR3edJCSOK=MOk&7iNL7^BGEUf<*ctCOa2&q{DhfV+g(jcvyiy7sP<&S0wZ zV$*AG^{Be-f~ylIbaA7SL&ccAfgA@2H{F>{YT8ONh2)@-t4gpo=WWIR7JWucgcgG5W+sBXn zVCq{F0RfRMPW4s481Dr8>CZiJyQ9`R{E4(?@qftNg+N`rDT8D2?cc47i0wg*K9Xoc zl19CS=OTPAEG(%w=9O0-kd?=n1kaWnu`*L>jvCSPzrQmhwD~-*>_wGEZI{3mII@4s)uhJ=&4`zV0 zU99!9m$haL5eOv5(IO&@^xi07=yj0D#KBT8d1oD{J2d?EcY4$Q%5+w3p!lxEUn#*C zJP}&B-altlDxZ|jQNp$d6C_#Q=$Y05fzFwSU6$gBJDBS-yWEINlm=-~+h83?@4K^^ z+;IM&jM=7+XwxTu@vDWuWuC*LQGOIY~K6s5Z_MZU~V+S54i&cNB7d1)hR*RuI+`B&Z!)Zks3*D0}gZN7_} zaOF|ob$MFj`>l0&`fz{M)v^7XF<#dZFkEw6MsoVcO3i%dm+Zg9RV~qEBO((vR3LjF2_zOc2n)7bM`x|3%kBjtFdIj6 zOX0)UA}liS?mq<$Pix$It!Q!Ql{^^G*TYRz+I^iAs$6pk-+4*n$1DYO-}E&WC%^l{ zs@59^absq?Id-j!2F>iuE4`HSmqGdMKg>>&!2T;V1b)vx%cl@E;Y(||^cz9%|9~Bo zJ2xN%hRxm0Q`rkjEp7bjk0XkVOQ_Bsc2idI*2rr$fs#j02QjeMkHpE-!WEP83N#o3(*716iW_2Nwe7>`SgRytM^XT{~m_O^j55< z)Ku{t;c+Y_i!}E7ub{^e&a&oDfAup&G*0^~QS(rP1P~DX={js~k>2>0KO`7JZ9qN8Hm*grQH>9_&U=7*Ec<4>wy{&}at9lqbN@}N=Q(Aq}Td@HMQ zEv>SCW)G6u+ZhGRrP?6;dlR%jT;P+B2n^5bTq9H4(G_QoesxbK@K3>_eVBr(h3=D9 z)aTtUzF5*ByTPF(7?fkP)McXn<8R)LD)d}r<*;$K^Br~`c|x{X6BUDq9c4e*uuSD> z-zc}V6HuJ3pv27;<7Sk=?Yw22ycv0EOFJfE5?txTm#WHd`J}}VR!(I+S%2I>+?n8NDh$62gxPy z_Su;;`sN)9ggQsu@xoOu)#riiWQ5UUYGSPCSIfqXbcPB%S3GZHzu?b%;%2bL_X;Ld z9Ny`bHmYgx=d!+}Kc18v54{ypkYEXJ<1AF)VZ~2OiE%7Aeop57UhqkHyAzN9WY)7Y5zvZX$Z>0lqvFGBoqbl>XczF zb6q9Fk0rN!A;TG7^k^MVNO{TBDu+DeKPHS&aZs8C#DNz368|a-PcA_uahw>z#s*-^ zN_#$ke+q4UeK^4Y!gqFBr^k*eZ8je8kT*>r-~FQpaB$$1aZg)Rr|*QO@}l{gd0B(F zaV}fh7D*5?FizZJcN6Y6(i)7TBw~O&{Y5GDg+?&lr}ZHy!ZyN_Gi`91=(M^+6O`*l|eV# z1Cf%sM43$oU|QsjGddV~k#i8PYO=iKyqSDmaX|jzBr+EeIizXFqEJZ2JV7d6DaSw0 z=OjNsSG;yLUN{S1VcgV7x%woEESMW_;#!x2=y()L$$(IObI|sMwcB?B&J}p?TI17( z1iX2LS3eI?WTGQZO`7)S5~yii(pFrxhCAiE&aqXWDm&n{zf7%~`-)fIt>*9O!K*Rc zqBQf+nv_@nPX;f-KEq@uQ)M}!`MB&g_S%?`s4EV~)*eDi%SDlZ8FL@3rY*86MtA7n zXRjRZN;1kwl^}(Dk)GZUZR|Wrpa+OKvFuyWyh?BR;)&xdbbZK~E!x53fs;!-RmghJ zN>ECexeJ`9pqCKC?`~Aru2H_%J2i8{TkUh;lKzY-jIFw$9ZPArQ+Sl6o;QwX_O^PF zS4^Yqd^BFuY&A4n-qQ0AZ3m2}bHuzJjuuL|UvPhqVq^*`9VD?(xKmIIl?9m4(u^C1Ur-}QM3k%`9^%6I7&Gg3tjrvY z>rg!_E86Ay%j=6zmQupZnu#PHS@N3-IVc#olPazjpu+B2yh1(exNR}N^S9rY4`|cO ztw044pVXZm&4a5|gu8?3f6}sRuuE)|?`M{vhy0eMWEytb0je7*UIkR&R}%xceENGS z6ElQ-CRZi>hMK}tq|~s!_A-Y5x0~3f!2T%yn0G<{D;2oR5)h?fXYc0gm750Azbx(0 zCh(^oDi6=%i@XK}rxMA+Y|TbAcWV4NT(<S+!pSNPm)L*5S{B0UlYot{MJ*l} zh73A2t617(j!IN#*Wc#9rA?~+3L>et+4;gTMwcBMfD)7rhgbJM-7)vEm13u< z_wY2o1G!WdNz=48Q4lFB8up3I#k8mf+BNOaf3+2u2O)FuXtL6uUy7lk*1ntsk z{p=Th5bM0Baoxb}rQ=Nx$k`i95EjCC#&Fu-JM5&Ku0L{H>*G&?M}lT*vJJ>l8^ zcTQyepMDXC(JzYZnKDTaR*#QQ&kweD&Ni1%t`Cnlw=uVhqHx@O=B%dEwV$l~ntHd^j`dz(np+Z$j_H-4dZQ>t&C3`K<;cO}TJ*=( zz0WAb(cx6v#bP8s92g7|05x}(G;iQtv<06a5X}*B@Qudm>Co&XqO9pIYcnTxZQT$y z|3!YMyJyZC<375bO|E#(@J9)5iAvRwq&og+h=t(a0G7RzNQ{Gtg@|;t4;=7jgGZyU zNlY}dGl#kn;e)T)_j+sI_W~cBqM7BGeJSGpoX;Mt-TQag@VUm1dH9z0x5lF}QrjuU zr&T&a6rY5Oa~|>w5<=BJlwX`^__cg7GX`@?Btt0h;G80s2Y=%^}k#{S`)Qx z1~)&acxcpEO^7N!lIs?t5AVNu?|#9@H)z=Fn(0+Fr^GEc!z5_FiQmHJ#(u}RaI1L2 z`=~`t$)KV1;T(kf3;N?mOK!ftd22}u=z5auO z!+a=*$V~T5Z7*3 z?$#x#PhpKAp0vkdZnB*;`_rLxIvS5MW6E#vJGnL5Tq-Hx7n=ly{B<Uu1io1M zadLHQTgF;$E{)UziTG2;fcR6CC1yTN_uFK6r)Ld*D~MrK;THk3=8UVJzvA*!r_cHt zRh4@g!#JB68GCW=Jp>r?IL22d3O>>6W3^jw5%u=MW8O=BAMbHm74)b!4T3_+aIhP^ ziVKJn63}EDtMi=OI=ni_QkJE2vHWGt!LAa0^B;$Sox!#;Ar_p-e}HILKW5 zyooZ|-lg$*>;%@fKlNB5+lq0kM&1xQj7%Duiw=D-Oz%6{&!&*!+?paK3a#{iDK4S? zaf*xXvHnktjOk1e|raOIoQ zrOUYySf9-`q>>hFhPO?oGBOg)W4&hn4W#> zKC<)h`;ueAp6KL)I2+fcdU`#`bUI0C{qkpriS@X6G(rtj@H{0L4rgO z&vuvhlgyBC_nr5HCoUc{y(&hTz7%H7VPu|GFG|S@I9M|}>B|fx@*d?XdsZIUL9p`e z9)A!Bg}!@<_4ct-1L~)%3*JF|{WKfPp8XX6D*aoII6fgRt141)=ZA0D-KOqeA~U&{ z1J5U&5B@?_CAWp5lC*mHG(87TAtMzsxg_P(j81Qr>XAH{aeZqU6C$js7ZZN*Ck@^6ibyZPFr zyjBZgR~wq39Lno(tzu$#AsdjEHA(!WZO9NV&WmOo3ASNO@rk`lV|5DTAF64t;2SWA zK_^=Ur^0Mydy!?jXsWx)CjF}6xi`PfJle^n@WeQA&@XHSzArsOFKZvot=7DjhJMN5_1E( zxv;zrI@$X3a6g9Hyskc*yO@Zi{EhhJxSr&@KQ__#fJBImHMvFjpa`9SSLq;B;)do& zn^{FY+}BSJRCdf60sdPr9WbF|svXZ6q+a6W!0LrN&V!tPf?-hZvfUbjdPctEIWZ?n3R_g>p>dbsq|UwoO$+=Pw0@D^R6(Q|X^aoXD&pySPNc@X-U!PEcYj8sgC z*YaBQxcJDbUGVR#5-EMJ<1x~fCOC6Ir0>J#*+@MRsHEh$2uRyJQdC$#I?(^E01~nCugfvYA{ooVg6C z!CEDH!&|Z)LGXw1zMu)Gje;jwnvu`*8aRHSB+wIURWi{9p581+T}ZS0cw;OIg(tU{ zjz~4!TP3v`u~lcb&Txq{J#I;#In%2-3xZ0^m!bT*7JMN?nt+}+Gr#^z402GRg(XHs zp9d)4xxQW2=!{ra$HPsgJx^~0yd7xb7ZUjj*?R2vNON3Cxf*0!_!C^y8r?q~CUI}& zFjF&p3oGkns}I)to-6w=;!zjTZyhDFxhuy-`HK8&UOlFGro)&1?sfC$`Rz;F5C8U8 zUVOv)02eDu1jk*mF0y^9>)`j$X-~&0sJ@-*tav5hKM>k^yD1o9<*2ec5WJ<5!LX<^ zt(yd-*i#+-dVSY=2cxxu-8X~`ajIF7-cPaBCEKko!d&dD;{#RlY1QV?dE^7e4yPmu z-baBMhFjag+|?n`2z$iNvWKf9ek}$kfqyljB!1cJd?`#VbAJ5cs}M@h*P0Lo3COR zg~7+*{0L(rF>hcV@xK_qa^)eXIawF&n@XkRf|4Ji)!6b-H??V6pw851V@8)Xt2|Oi z9XZz>EA&;BN5KvZ4YAEsJ{Z(1i`zBd>IF(X@b2dOY`K}642R@>90#QfjVK;v=e6IU z%rN4pl6#bx0If#=wZH=<1~36)M}+N&=~lQdWUNW5^&3DMFNba1)*y|u&c5PWVjs*kO8cln1U zBlI<=T5qv^W;AdNjne{Npc(Ra}ibO|AEPr~GftcTP{F(-t}y)GbN`dR(7qD0@Sp8rM6Q32|qm|Idi zBj_3CvcxWrw~nnG$ut5_vW>^GlEb6!aHO}QBrN}_%f+tkea%6Rd%+eArp~$xx@|$8 z_Q}E+0rZVZy|U;V$@P>V3yDGv@JS&*Sk&i_Sri*b3vl?#-u*{&sGWqn_y%x#B(U;eFYRpp?`h?TG1xirpW5p3k8Uv*#jG2L zvh2w9mF7;anpZ~urqvd0MpnEID(nXDq0t-TGImR=SIHXU84RLc^IG4*-ha@Y2o^WX zQ3d}q3C&slyvZ^j0qz$9Y}eXOcu_tb?XM)hhkEMxA03aGeSSJ0H+5fHoAp9V@`V}` z%cREG`>`uEAl?A3t%NhqAb(hN340IfJ1E}0XH~W$RVG2+b8T+%e9+1j33`^VXP5M9 zmR{i-Q6kk1ZXe$}!I#wd2^ahQXfzG@!AbF&=TUp0*5=(Foy+d26Ynrp8vy)H z&NrrO^gj&$Ktby#^v zw=@??Fv+IR4PCC;W#7Y&moZ}glP`l8I>uP?k|b9NAnRQ4`EoZI4!u+Qn`IloLi#xD zLr46VxJ{@T@A{yqbZh;ykhG5;I@h(zs*1+qH(k6zl((bHJ$QvQDgq~?Xs(&7%Z-pl zL7Qd+>Ji!tf8P{>hHrh(U7gN!>>H}KwBv;{Fqs^}9Qizpvbk7vtUAqkI+iTWUn!OT zK@}iHg}~>dhZ<^XhEvzyM8T=Y*#GOZ*!(}80*uiq{C~_|lCB?ZuJ0V393P%tVg4TP z?;kU8QV8N;7<{RVNgh9owI5^3J@)lna(X1PSbxUn-=de+K}QI6M@@?|1?YioChz=^|4sw+Xn zo6Hk6@3kXpM15c|x3_L!>9owN@1`A2*+P^owDYx#!3Y>XAm=%K_?2>7_t4i*p^skt z%I*GB{h?Bc-a2_e0$C@6Ybk7xxk>ePM!vNkjQF?18#LQ}vWr=_-#~Y{DKTEK1eEPW zs!=p%Z+Bqb+UNjKo7r|bSvV*R213J+6wk~F@)BoqpZ9Cur z)bWAT^&M99%e_u1x`X~*Osf~&y0`vWo}C50yPdea#4#;eqt(vAV;sMfV0t{*huLyL zR+1ebI>Ntm&Z#5B9!{JJTw@9MsC$>TT0`IDGIG+ot6mx`bj)5IU>nYHe+7s>K%iLgnQoaUpJv&>kaA__&Sgn0<#%$ntEOyUZK+w;m1CXY#B+^ZC_q z3itRpDCs2+$>X6g)`zNHfi?-NX@n}XaaM5c8w(y(Mdz^AHPXn#*d71BXt|fG+SK=@ z$l?BhB@z8^M?PAZk}M7iOu^>=U?W+^zud|v#U}N&2e1-EN+izVyG%1FB%=?9kX7N* zcUvD47_txux>1*IxobtQ{Od%xx8thU{eC+KRgXAqf2P5s+X%47S9BMTPmDKqQ$B%0?% z%Mu>(GIofJd~$CQvtQHP)j}H~UNeiziqdWGadR+5h3ubgF^B8&zZkURxB;z)9zR+? zfAC;sML7;*TdqXQsufeRZaX>^AZ+jQ468MKr=?9{zRNS!^8x{W%|Nt#;gtxgX6_&| z<{bwk@r{3=mQoWf6rzEHD0y-;qH>)R>}vjs$UO=hRQz$Dtm$v z4jl-?nMzbYAO1+*Bd2-HvZ>QKCtjZ76Li(&JbQw1#*^!fK6eD+@NaxPX{*Xg_|AWU zcGXZp@Acg^tun;etOBBnlYG!JftP+kXUbsoX)p@X?tyG@yqu(D@!}AtPqEFQX1wJx zU6=HkZ8;$M!C{a`ya8D0%HD?CyXGre?q6c!Q)*B5uDDB~_m|UFd_ymYx{pn;__8x0 z@3cKgX7a@~%Hk)ktGD;v7hiYXx z)_Qjh%!B|v_VY45Lhc;g?NKX{{iq{HB*O}a;a-+E3E_eOMy~zV(C>r2x$d6sy?=I;K5Ya0aro0H)k6K zvi>)T#}7v~!5w_c&C!aYtV*r;59$Ov{GHwbwIH_q-Hyrh&sos7+C5erCk}I~O%s#1 z-zG)y?n;!ZhelL6#_sr#iJWth;&RiR958lwzmZkcvU9PVs}38LFT;FRd0^-yk3gjzw)Zw(gg^wmh>0B zviASS0yR7(2dIt|?I$HF<3Xe1qKCeJDA8gN|6_RmV9)zg6)9%C@@6vLr z^O&G=tC;$C)r~!{(<^CzWcPL-6hw<|vwToG`YCJptiew3wqNfH`AEkj=4IG~<_{fT zU!h+X=DeO6eftCv$fsjAJ`vozlG<^qo;JNgjT8xk|8|uNX=!10!<~j6A99kPu!n`b z3<#l{UhHs}NTmAksnB1TNw*Y%KK!fP{mJd*rVJUH;H7JtO%`+$yffh10Xf2o-d^~$B=yYB<6N^f50D?VIVA?UhMd7W;kr>|Q>@F@MwX}$^+!c0!;g3sbj?8!>TUbol zAB+0*Xsg6W7_U=b)@*pM>se_6E2DZsI%)O<-Ha|X-i52v^2XARqyLJ86lJvf78*ue zV2d!KtG>I7)PPBx!7^huiP3w;1^@LwWTHs5L5tXU2PSvKDv+_4DknjbSvXWnMoJkM zL)EeAkMpTOcYgm-NyjzRe3N&m)M-S2VdJB3W%TWZr)Czlm*rwdMsfBcYuK(rIzcAhvR7U)>7mPQhYfJ0@5a6WzQk9F zrnOk{nG`vLY1eGPEh$SjM+;8HeRH#48FU-MZi`Vq=x0KP>3{-kj2C8$$QAUOE*t(M zf;XAlK@)`Em0)QP#*99J1j+=vJ7zxRC-6D5(Bh}@l*;PjL#Tra-piPj^szo~FQ;2% zLwBk%42p7S3CchT{V{c1U$b>8?{nQb+TE~{tpJS1!LM;tMgEG6jLj_kU0XXgNZX2_lCJ#=iE9C^`;uz7imEwGnP`Fcv7D#NU+M2<`f44taOsDlcLHtYiQvoY1U(WL)-L-(-5yM+xU<`9np1x2 zduP;@u+KG1ph~XXHv~H0T>H_K{gpp^0G9)=_zPbaKXDu2`xkw9&e`Dpk;MIPm_JAu zv6amsBQ1V#1KtkuRdpN7$wb-<%VO3)?%oHw!a<8#n`4%QVqeZuO%;;-zZ0pFZ$LBx zz{5@w3BSGA6_VC|`7?>7lJhG*k%MrU`*K;9@Gw9Gk|P?e?l8pFmXdL|_G6~7zya^D z4i`CdVY6p#UV4KjMd#bY4qs;ex}_5Z989J}g^zwuMQzgkqNmhUuy-}CV?MboRw;Ol z(Ph}TvaG_!oM!NtIH=U|TZ*WZ9CztNHcgE4#-;eAzL8@j@fG%eJprKe2d{`qt4yuo zi8|E==;{%bPbh4*@nNx}>CmqWeG||}*|moYo3#nJ#jg#%TAN>hm{TmnOjLGq_j2Mi z4d19!g7s=h{L)`sB`Oz2Zdj*Eb{$#~e7?J^8VrX!Vfsy!ly;VsAFIE4MCMODiG!V+ z5q*0~h`pEUAy{DBFv6WJ$zf zshzm!+#`aqw3eR3z6(e5P{}wWFm54pI0AhTc~c$jT+tpJgf9X^bCGTH9yyoQ6YsLj z>75=xiUq|$t+@=AS)jF@`ud)YTak#1vmjS1H_imO-K!&@OgNTsL$pQe;?>QK8lkJo zA$XUJitFH|Y{Vyu<8@_;s0lKk&;=h}%xYL!YGnM;TRk4>GxP6o4Rd?*_p)a~`tykG zlz9sy@fjEFyY`{Kzo)2Z%>gro403g;JX^KDT_+4Xe*y3hx8{s!L<$9ndk@p4 zk3c0ssT&3^ZD{OoBQSNe<1;vfP^nm8&sf3$=~6L)_F!Ga<}pg{)UB*h#(OUV=cu2d zUHEVxIP@jn7MmB>Tg&jR+bM}z{{A~SW~d9CDerS}EMNKeb>5<@jjH?4-*9LmRF6xK zyefLvASqP(B%s9#67q_WOXm{pI5Y_Q}rP^dFhN0l$7QB6zIB{ao%J`6sE4Ra(3eo&WG_2FGAu zLZhC$$zg@O6QjC8OaCg4^9+f*=aP3P?tdr76oBKv*?c9uFimkL6@2Nmy`-+I@I=RJ=c|H4bOWhG>6!w?|}Ar zgMaA|<`Dgs{7(W5E=CkUS6}a#%(~i`-|*Y5cNMQO?-Zx`Ya_Uy@@Vk<8B{O%CzD0U zN0vzcDbv5<7lA?d_~xqQDSBwo@$K)Fi+$%@aCYm_TzI6Er`RR@^xTf$>er1d2)G3X zi%cw>zH;om89~FOiA_P*lRNB9_(9lR!3)$^YOEbrL^`t#DVkdOMw2On$IAaN`5Xr0 zHkQ!0NxWVn@<%T-=d(*nWV%TD8RV0hR!4dN?lW4I3ozfmmCgIg<892^MUIiEvDsRw zve~8R>dN?dK7S)!$_`x8PyOQLQ(0@XF>Vu=W_-Q$-fbtK?Q2+DeFnz-<^pFT?qN-~ zCa@to>MQoyAu3(zjfw$JXJ=CY0KhCL000000LNSa0FVFx00)Qb<`U}aErF_ z=j8I|?(63FJ}AYar-DFJQOe1AQUcTv2nrZz4)sbGrHZq7IF6K3NU1;j6Az|qt-S7W z);X=hsr~7%_GI3k|GuA3es9n2hmJ4pKb>FPe)2zFel`yvI+{oLwCU>|aok<9*oAfi zu9FVt(#2QTOZHtY4x;_>v0**slcS%B$h&-LnX`cK6 zQ2Lzp_PiH{OV!)!dcyU%?f%aHT@=UC(N6oDg*`#*Z(bQ9yR)3n%YMpLmf*(S1bjN% z1=8#ooA5E8Z3o5-1dnoq7$uW#ApBCA{JU#%iNN z#1r|uJt86LMReM8bQY32F*bdO5&h~U5KLLcD%xsX@;O=gWzZwJef~y|%~`+msI$(D zOu527H41~jWvTb|D5;I`WK(u|{@6LOc8RJn_eF7vSY2YIG1m8ZJi?}DupEf|8(qN zeQkCA_ubiE#A-xzvh1p#>3(OW*55yKefaxZpFMhV!J}a>4t;xQdmG{W0C&&15AP$a z%^^%_QsPN)uyC4=As5+7^5?&lCXP^w{}4Gz!*OsgoJ4O87l=MWpCcwH%K@u=pj)V| zt?%LJs?6eY*&pMDldzatyKKGlEve?mE7@!4S-JmKE@mgc{nxk3imt4eJ^!q@iAf@c zUg@TsBT+1%yP-vE zJ^T9XP4<@MXsd#v;{PA_GVnP6tfvm0`=^|Kdi~$=@cXwHi@!MW%AJSbuD2d)e2R_KBy9=KZS8f3>_M>wER%%xIzF26`V${!8}8TPQ&V zin(^QSuJ-|P`!XK}(;S6rSybp|ROT*NqPy&21$3E$ISt^TX{ z&jS7@C0Glcz!o6K21+4pxf_E7Yw#kfR+n9wOkmJO6b+!6(u6dAId*X8{xQtGK$V0TbU8Q^9X4c1d(kL2%l#ue$ zKho=1z(92K@%kv8-Er`aKXvZy-wjYdxW|5PJYS>DYh^L?qIvJvC7f6k3(VH*ZZK(8Emx?fEd%Z! z@LNMROwgW{fMXxuxIMK7HfkI$5Yu|exxveK{MR}T{wF2H0`w0Q9ptb-DV8k+0(!`? z-l2%Q(xDDhaac!6I%%Tt|LcPIsn1TY@=MyeTkn6o8V~Ouej0w3&fZ>}YyE5CN&hhW z_Wf!%pI-EHtn0eZ@hR5(i#BRWE)9wDB_*jO1EA`^eBC(_()Laq{?Jq~N9(b0Adt)* z<%JH>MJQ2!RGKtRn+Cn~|FAb{(3g;a#-vpJ{hcYl$`)tK%U>0%d8y5N+Oi#nv?w~B zSjD;?SH+Km=Mt)=Kg+*N982Qa5DcA==WMOtI<4i6Ie@`H2*t{p^Kau!TUJrVZb>B2 z|HHH$2OcOzW(q~1sF0)Yq!5$<0y5-K?_AGqn}XXGIPb02IMPa?0u}$Cyd87iQ^PlO*x2d~3ef?a%6^-NV}95 z%hLh9eeU^9RJ2yEGZ<&f14E@hMZYUMS>$a_pw0^_WIt- zOZDW?pL>%dwL|Lhal=Mb&g+CwDe=-Tl zvaV&=Q>vWE(sLg`ihe3@u^j1VBnbo<=9X*!K6Y-jB};0B0lo>H9jBvSO0ZY_?YHvT z0Ri%qeNjhV# z0RAWC*bFg$L``;dWVt;l0cvOnn&_$IP~YIQ-q+c(dqRPGyFt;G>d_P-EkcU_&yLiy zG~cGVuZp+&_U*2{UAriH&VIea2#v2U_fEG%iEn)??7zL~`R3l)BmaH(`4x}I6D{r> zn5R1!1ZO_q!VCyq$!YL;~%oS!W$i#V#9)!%Nq}09MWDx&UhJ0_P zz|YL!Z3=~a`9<8uV{Y5Go~>W``}uhs$3+2U9ptL;BkE4JDEvsmp9@p4d|`jgUu5Lt z9VxO2k*yrT8l#h7T(P`7*@+&^H=7+!0G=mRhzVwUkqy~X$ny831n5A0powT4>$faycoUQeV5axck}sP z=@WiCC*=BT=qJDayVocYCXx6ag8(9Yp8O)eZ3s9=T*#@&V$&HPae5Nuzk@{RYLhxS zqwP{wG!Z z91epadx{`O-${AULlKxcdnP&78*@>j)VvDGT0IUwAqB5SPyWe|rlN&r-mv~J`U4Km zT`(cv;MVv1a{n<>fz|fMbl(qigwJ33%&7hPsB=AwPJM}9kHq>%+*S*?CU(ibvas|d zO_CtWWRg@Q)=7nEA7~*1+PI*+{Tvb59RIcXC5c~O&qfuw!?MwVliz3aDIxqwa91uk z>?zF1{1;30=(gw%a2TBC^s&aAwac%b%o(XJboHoen*-`Cbf(P|zKFuX-iGS})e|Y1 zCV4u_XC}lA(|@98fA48MUMS__0Xjc0Exrme-k=nTIZ=qJ>75)o)a!k6DJZ<&YKL+! zI)SjD5*6bAlPr>O{c!NGk_H1uwe5^=efY#qjXNPd8@@2U%fSjS8sJy+2s(127SEstgoX88_F-Tb#t{Z zq$q<41EDJ}uKc#K9q!YcY7U%f>;il&ZMEZsL1p#r03@QJ`4dn7?#9;*#gUP{6#(8R zRpbiVepFlMTaab@qy!s7hakx|WLYm)?J867D}P(4=PUu0LW=(%G2Iuxe$j!Wqx1L2 z>ilCRb6=+{oCk^eO8|H|(>Ej9mc`@8BOABl|LmgQx!C`b>!aTxS6@YmCRe4cxBDXm zgJq`t{W(I?U$w8y3;OA-PjU?wk(@*lLb|hrBT@$eAdjcpvLQ^--oMc*d!|WKOjc3p zJ5u&?mPG&Sr?>}&*!Y^`?vsYc)8#4(H!!v6rQO4;{aiL{$1UF4B%k|yW1ZH42&En| zKt~;A(K}bPq3X)D*@*XgM)F_#0G=o1zz7vTIz{yqvRt2(Ag1(yVCp=TEbC@lg@WPS za=v(%pSv^I!Xkw%{{PR44#$5xZDue$`q6?bwp`Vu8)av{v$+(PF23LY?2?!74)kqQpQWX}lYrNk8-Q z)-#r@kGNd!hA#B2 zp6^S{V(CLkOf(3kC-*;{GZHsk8g+sEn^kjz`8$(aJ%8OHCT)I_2;~9q-#ybSVc2K+ z?)G=F^(wEwqEg8jFTV$qI8vD0ct2?)Sy@af6{9YTMB=f02Ah(_4=`R2w>Bg`-n8Z5 z1s5>=Nvy1^@<$6$v)G-9sdaBbbx;>IvIO2IRp^vj5S=FaTFLVAq!#PpA&{^_j&;#E z)NOw5JV2;N4N^)e|3B#l-A+R4&FRPQm|~lpZ^f?3qgysdyDf7N^5i}cqmE#;%k+3G@ec8UcWGaB?I$d%Mx*jaF9~tv)`d+DqeK=5Die^RgV~TsD`p{d=&K!oSxp^zctLfRHd~2g5mAzSLe>dZ!bO_-0am&v|klFq#LV8GEi}R z@~^$41>r<#A|khe!8y7JodNPDr(6l$ZC@iNPedX*ZH`WM-RZDST9Q5N15SubSown2AU zsgCXXJ=CY1n^%d000000LNSa z0FeLy0N*3~=neJf+Uw@$>Ez+;>gx7>D1~CGy`w1#s)K=?`X@!WrF8;C$g*yl^W{#4 z-a>D+THmyG3-?!nLIG`&sFdRWlX$_>@A^HLoA-^=UB33-{eRVX<2OBY9S{8MUYwkK z-=BZ6>%4IC>gU(XGhcbTY#zN&`#NY9+tJ`HqS04=sW?OkNj8tU%Lg$Hs^@1}!$T_4 zSrV7@zLPFLCg>Au+R2xH%jrH{sRGF)zH?L_&mO20)|O%iU;69CUq7UagW+j4RxQ6J z&;5$T#_#j>V_C$KF1EweWC98hA_&|_f)g5R3c?_F#|JLO4Xw$sAzC|NmJ9-xz z6(R2I=VQA+xFFh<=a7!9Tg>Ny5vi(QjzkxdCz7hkmOe!QtdrTL5jyMq$#l2LcCAL2 zZg2CL&$M3raJeus_e!tGiao_&^Pbw<@A z`>m$Ir+5muRe%Dar7Zvdy~Cq!g#H)r-6f4$J&zSI+zzRo&+jjmRLS2ncFB(z@ff~K z32P}aFV@j5e9A6!Fkx?K+DGj+O`^A#3~3(*l}8gq^(L5Jxi91c4n46(R|Vs<#fzXf z>C4$9=;SIZ6}zX}t3lLm@VWhw%}jm|FXH#J;xV=C|Er44{wuCd9@FR>a%9b0&BO;{ z&1y2a(*vel+TNu+UnwPp2&Jrwq-qlPwK2CUZ1?zT>%G9MSufhZCmtvjt_BFOK$iZK zVp@YTilmb)%F*8C-guSutKjVTyxmW1Szs82kmAq&>*@Y__v3OcviTc*eQUledeH0r zo!WXhI{G`@e(;U*Dz)@pt^7;DcguI>bw0ly{hyvadU%yaa;zYd;Jc3=%g^P{N%H@K z*n5an3u47w+y2H`}{V8 zEe(N+JnaAX`V;QomSbQ3D65a{Skq`v=Au)VW$NEAJbdJEGc6kbasB4y{d4~#kADKR z`M#32T#*)K$B;Ghbum_o_3$a9Kx<+-bFM_}t7;}v8qM95(Uf|Rp$GXJYXN`IA2vUL zd%$CKHJvFj_rz0JI`g}~Eu$CmjEh*jR9nZ$t@d+u+M%wu9M4ksRf~FIz>Z(Xvc51@ zyl0{iG8v_^SF1un-;%DG}6+GXsk8kv!AppK7g`foxP$7r@Ng1dC z1XReOZl=NydpOUqX#G}qt=`$vDg`P|*yQI5BUO5I=YF>z;@#WMSJNq?=V_HUx=8p7I;A0Ny7>Yz#qQsF0%%r9!vJLJCcW9P4s&ZwyA&f&cs3)pq=U>y?Ejd3y#f$!s? z96YRZW-<0X`+Z8<%~_|Vb#c4OLR_q-)~WZ_@pJ0?v*;+&ADk)W*Cjp%e^_p?b z8AJW`5Sgw*jSdvhfpG!K$+vz`GPnw9BeLn)C-te~w*Y=8g`ffafF81Jo)m)u@B=!? zVcx%P-cIM~RCJ%pv{*$^#Q$gi=Pwo8T{M2L{_eY${+-QiN55ZlUuFItcrCn}zpih4 zSU;cZS3m#L=iB3d6Q8$9Y7_c0`tFfXds6=9YqUA^_+tLb}N@b9_I*1V|v zy4j*Fk;ymZ$-jMT9P3Pch9o&3n5oJaq-LPJ-8gj)vy0`5FeMW1c|1sR*2&;k$DgXcYXN4ap@bI$z z*4Fjc$}*05N4qa+Xl-YIIM{FE_S?n#t zui@gARI6WQ4Dj;_r#Y;cC=CRl)PMH&D;C1jvQ&9?VSuH>SMPuTHR{?w%OhD z^joIgLU&i^Pq;`A|8IX0M>qfZRewLfy=k03>e&$LTPc+Qm{K4@Vj?M_(oO=p2^b00 zDSV?%)-1z4PKL;dGADOuIhM=m#CQ34s}4-(snWj$AzhhJQUwk^#KoDHs#ur?t1Trb z>CJOXh{&&TqrVuj{H=^VS=PmAsOP)+{#?eI9$|;h_4o_-j~H)VH{qO#VlkHVZ|l(H z2aDAhhon#&>??I@-aq38_2q1}0)8lka*R)OMn-fNkwXWiNRNegQVdOXG~`gPzpU`W zGx6WWaB%hd`tZ!p{o!x1Z)~}kKm0WQ zq#cWX91$-5DUK%Or*DeQTAW2x&@*OTVuNFuoY2~gh zB;-3nH^CMECHKkVh}RIZxBU9c40h`!ma6K!d3`;q0&!tws1r5A*zkeR`eIMebW7!- z1gUJ40co^~&FrYG_l^FyWARr=)Hv2(ah(TPl)|@()TsY5q3cFttUl|R@~eBn)cGE&VBuTy&zYe zWB~l=;YHl})o;Es7n?PBJVnkqhjhvV Date: Fri, 20 Jun 2025 21:45:29 -0400 Subject: [PATCH 0737/1331] Increase weather ambience volume --- code/modules/mob/living/life.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 171ad74570a9..189965c89c8c 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -315,7 +315,7 @@ // Push sound to client. Pipe dream TODO: crossfade between the new and old weather ambience. sound_to(src, sound(null, repeat = 0, wait = 0, volume = 0, channel = sound_channels.weather_channel)) if(send_sound) - sound_to(src, sound(send_sound, repeat = TRUE, wait = 0, volume = 30, channel = sound_channels.weather_channel)) + sound_to(src, sound(send_sound, repeat = TRUE, wait = 0, volume = 60, channel = sound_channels.weather_channel)) /mob/living/proc/handle_environment(var/datum/gas_mixture/environment) SHOULD_CALL_PARENT(TRUE) From c95b8f17ec6052cab0282921f41fd4d15dcf9552 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Fri, 20 Jun 2025 21:48:35 -0400 Subject: [PATCH 0738/1331] Make area ambience louder, more likely, and more frequent --- code/game/area/areas.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 41fc989dd0b7..dcc3343b8874 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -403,8 +403,8 @@ var/global/list/mob/living/forced_ambiance_list = new if(LAZYLEN(forced_ambience) && !(L in forced_ambiance_list)) forced_ambiance_list += L L.playsound_local(T,sound(pick(forced_ambience), repeat = 1, wait = 0, volume = 25, channel = sound_channels.lobby_channel)) - if(LAZYLEN(ambience) && prob(5) && (world.time >= L.client.played + 3 MINUTES)) - L.playsound_local(T, sound(pick(ambience), repeat = 0, wait = 0, volume = 15, channel = sound_channels.ambience_channel)) + if(LAZYLEN(ambience) && prob(35) && (world.time >= L.client.played + 1.5 MINUTES)) + L.playsound_local(T, sound(pick(ambience), repeat = 0, wait = 0, volume = 25, channel = sound_channels.ambience_channel)) L.client.played = world.time /area/proc/clear_ambience(var/mob/living/L) From 87fd3699674eb2d0d266d6032921f871b9ebe7fc Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Fri, 20 Jun 2025 21:22:42 -0400 Subject: [PATCH 0739/1331] Fix typo in item sharpening effect examine --- mods/content/item_sharpening/effect_sharpen.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/content/item_sharpening/effect_sharpen.dm b/mods/content/item_sharpening/effect_sharpen.dm index b5703b653f4b..511b83c6f349 100644 --- a/mods/content/item_sharpening/effect_sharpen.dm +++ b/mods/content/item_sharpening/effect_sharpen.dm @@ -18,4 +18,4 @@ if(uses > 0) to_chat(user, SPAN_NOTICE("\The [item] has been honed to a keen edge.")) else - to_chat(user, SPAN_NOTICE("\The [item] in need of sharpening.")) + to_chat(user, SPAN_NOTICE("\The [item] is in need of sharpening.")) From 6f0bd330f50c0fff4ce7ad1e0cb842021a91e2f5 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Tue, 20 May 2025 19:04:54 -0400 Subject: [PATCH 0740/1331] Add work sound support to cooking vessels --- .../cooking/cooking_vessels/_cooking_vessel.dm | 17 +++++++++++++++++ .../food/cooking/cooking_vessels/pot.dm | 1 + sound/effects/boiling-water.ogg | Bin 0 -> 374938 bytes 3 files changed, 18 insertions(+) create mode 100644 sound/effects/boiling-water.ogg diff --git a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm index 12dfbf6378a3..6b2f8917a820 100644 --- a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm +++ b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm @@ -11,10 +11,24 @@ color = /decl/material/solid/metal/stainlesssteel::color amount_per_transfer_from_this = 15 + // Used for work sounds. + var/datum/sound_token/work_sound_token + var/sound_id + var/work_sound + var/cooking_category var/started_cooking var/decl/recipe/last_recipe +/obj/item/chems/cooking_vessel/Initialize(ml, material_key) + . = ..() + if(work_sound) + sound_id = "[work_sound]" + +/obj/item/chems/cooking_vessel/Destroy() + QDEL_NULL(work_sound_token) + return ..() + // TODO: ladle /obj/item/chems/cooking_vessel/attackby(obj/item/used_item, mob/user) @@ -101,6 +115,7 @@ //TODO fail last recipe started_cooking = null last_recipe = null + QDEL_NULL(work_sound_token) return PROCESS_KILL if(isnull(started_cooking) || recipe != last_recipe) started_cooking = world.time @@ -116,6 +131,8 @@ last_recipe = null return last_recipe = recipe + if(!work_sound_token) + work_sound_token = play_looping_sound(src, sound_id, work_sound, volume = 30) update_icon() /obj/item/chems/cooking_vessel/on_update_icon() diff --git a/code/modules/food/cooking/cooking_vessels/pot.dm b/code/modules/food/cooking/cooking_vessels/pot.dm index 005521995890..72e07956854d 100644 --- a/code/modules/food/cooking/cooking_vessels/pot.dm +++ b/code/modules/food/cooking/cooking_vessels/pot.dm @@ -6,6 +6,7 @@ cooking_category = RECIPE_CATEGORY_POT presentation_flags = PRESENTATION_FLAG_NAME obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE + work_sound = 'sound/effects/boiling-water.ogg' var/last_boil_status var/last_boil_temp diff --git a/sound/effects/boiling-water.ogg b/sound/effects/boiling-water.ogg new file mode 100644 index 0000000000000000000000000000000000000000..c026945a819beadf4fffbdb6003f66e043fc58ea GIT binary patch literal 374938 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

    brmF!KXdh%rV_{c{ov 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 Date: Mon, 18 Aug 2025 18:51:08 -0400 Subject: [PATCH 0741/1331] Make bulk cooking in cooking vessels faster --- code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm index 6b2f8917a820..0efd5985f304 100644 --- a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm +++ b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm @@ -125,7 +125,7 @@ if(recipe && recipe == last_recipe && recipe.can_bulk_cook) // Bulk cooking has benefits like reduced cook time // we don't just do it instantly because there's messages each time - started_cooking = world.time + (recipe.cooking_time / 2) + started_cooking = world.time + (recipe.cooking_time / 5) else started_cooking = null last_recipe = null From 1e3b0116d478666e4d7e7a95a8cbbc6a847b8ed6 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sat, 28 Jun 2025 19:22:36 -0400 Subject: [PATCH 0742/1331] Add noise texture to stone walls --- .../solids/materials_solid_stone.dm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/modules/materials/definitions/solids/materials_solid_stone.dm b/code/modules/materials/definitions/solids/materials_solid_stone.dm index dc692ddb16f7..5cdac448041c 100644 --- a/code/modules/materials/definitions/solids/materials_solid_stone.dm +++ b/code/modules/materials/definitions/solids/materials_solid_stone.dm @@ -25,6 +25,15 @@ ore_result_amount = 4 sound_manipulate = 'sound/foley/rockscrape.ogg' sound_dropped = 'sound/foley/rockscrape.ogg' + var/image/texture + +/decl/material/solid/stone/Initialize() + . = ..() + texture = image('icons/turf/wall_texture.dmi', "concrete") + texture.blend_mode = BLEND_MULTIPLY + +/decl/material/solid/stone/get_wall_texture() + return texture /decl/material/solid/stone/sandstone name = "sandstone" @@ -114,12 +123,3 @@ melting_point = T0C + 1200 exoplanet_rarity_plant = MAT_RARITY_NOWHERE exoplanet_rarity_gas = MAT_RARITY_NOWHERE - var/image/texture - -/decl/material/solid/stone/concrete/Initialize() - . = ..() - texture = image('icons/turf/wall_texture.dmi', "concrete") - texture.blend_mode = BLEND_MULTIPLY - -/decl/material/solid/stone/concrete/get_wall_texture() - return texture From 50a030985936811da104355a9ba994d2709ace33 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Fri, 20 Jun 2025 21:22:16 -0400 Subject: [PATCH 0743/1331] Add basic wall cabinets --- code/game/objects/structures/wall_cabinet.dm | 35 ++++++++++++++++++ .../obj/structures/furniture/cabinet_duo.dmi | Bin 0 -> 558 bytes maps/shaded_hills/shaded_hills-inn.dmm | 24 ++++++++++++ maps/shaded_hills/shaded_hills-woods.dmm | 9 ++++- .../woods/chemistry_shack/chemistry_shack.dmm | 12 ++++++ .../suspicious_cabin/suspicious_cabin.dmm | 20 ++++++++++ nebula.dme | 1 + 7 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 code/game/objects/structures/wall_cabinet.dm create mode 100644 icons/obj/structures/furniture/cabinet_duo.dmi diff --git a/code/game/objects/structures/wall_cabinet.dm b/code/game/objects/structures/wall_cabinet.dm new file mode 100644 index 000000000000..3cc7b07bcc55 --- /dev/null +++ b/code/game/objects/structures/wall_cabinet.dm @@ -0,0 +1,35 @@ +// A wall-mounted storage object. +/obj/structure/wall_cabinet + name = "cabinet" + desc = "A wall-mounted cabinet used to store various goods and sundries, and also make use of all that wasted wall-space." + icon = 'icons/obj/structures/furniture/cabinet_duo.dmi' + icon_state = ICON_STATE_WORLD + anchored = TRUE + obj_flags = OBJ_FLAG_MOVES_UNSUPPORTED + layer = ABOVE_HUMAN_LAYER + material_alteration = MAT_FLAG_ALTERATION_ALL + directional_offset = @'{"NORTH":{"y":-1},"SOUTH":{"y":24}}' + storage = /datum/storage/wall_cabinet + +/obj/structure/wall_cabinet/on_update_icon() + . = ..() + if(storage?.opened) + // todo: door material? glass cabinet doors could be interesting if we add a 'full' sprite + var/image/open_overlay = overlay_image(icon, "[icon_state]_open", get_color(), RESET_COLOR|KEEP_APART) + open_overlay.layer = ABOVE_HUMAN_LAYER + 0.005 + add_overlay(open_overlay) + +/datum/storage/wall_cabinet + max_storage_space = BASE_STORAGE_CAPACITY(ITEM_SIZE_GARGANTUAN) // smaller than structure fwiw + open_sound = 'sound/foley/drawer-open.ogg' + close_sound = 'sound/foley/drawer-close.ogg' + +/obj/structure/wall_cabinet/walnut + color = /decl/material/solid/organic/wood/walnut::color + material = /decl/material/solid/organic/wood/walnut + reinf_material = /decl/material/solid/organic/wood/walnut + +/obj/structure/wall_cabinet/ebony + color = /decl/material/solid/organic/wood/ebony::color + material = /decl/material/solid/organic/wood/ebony + reinf_material = /decl/material/solid/organic/wood/ebony \ No newline at end of file diff --git a/icons/obj/structures/furniture/cabinet_duo.dmi b/icons/obj/structures/furniture/cabinet_duo.dmi new file mode 100644 index 0000000000000000000000000000000000000000..adc99dcb2055e32ee3637eefb1ad93ae140ed2a0 GIT binary patch literal 558 zcmV+}0@3}6P)t<7!okUkik`s0z!IMZ-T(jq0d!JMQvg8b*k%9#0CIX%Sad{Xb7OL8aCB*J zZU6vyoKseCa&`CgQ*iP1C^0t`#5P1z6`x;_nuklNvVyCh3)lq!`~N2p zkA-e)0003=NklKq2k@uhMZ+jMXCXSw!u^ zZxJbCo_~n#nJoK{ Date: Sat, 28 Jun 2025 20:57:10 -0400 Subject: [PATCH 0744/1331] Improve amanita and coffee plant sprites --- .../hydroponics/plant_types/seeds_misc.dm | 6 ++++-- icons/obj/hydroponics/hydroponics_growing.dmi | Bin 45299 -> 45415 bytes 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/modules/hydroponics/plant_types/seeds_misc.dm b/code/modules/hydroponics/plant_types/seeds_misc.dm index ea29e6249413..a36f8486b7f3 100644 --- a/code/modules/hydroponics/plant_types/seeds_misc.dm +++ b/code/modules/hydroponics/plant_types/seeds_misc.dm @@ -560,6 +560,7 @@ set_trait(TRAIT_POTENCY,10) set_trait(TRAIT_PRODUCT_ICON,"mushroom") set_trait(TRAIT_PRODUCT_COLOUR,"#ff4545") + set_trait(TRAIT_LEAVES_COLOUR,"#ff4545") // to make it so they aren't pale while immature set_trait(TRAIT_PLANT_COLOUR,"#e0ddba") set_trait(TRAIT_PLANT_ICON,"mushroom4") @@ -579,6 +580,7 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#ede8ea") set_trait(TRAIT_PLANT_COLOUR,"#e6d8dd") set_trait(TRAIT_PLANT_ICON,"mushroom5") + set_trait(TRAIT_LEAVES_COLOUR,null) /datum/seed/mushroom/towercap name = "towercap" @@ -1528,8 +1530,8 @@ set_trait(TRAIT_YIELD,5) set_trait(TRAIT_PRODUCT_ICON,"treefruit") set_trait(TRAIT_PRODUCT_COLOUR,"#a80000") - set_trait(TRAIT_PLANT_COLOUR,"#749733") - set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_PLANT_COLOUR,"#7acf3c") + set_trait(TRAIT_PLANT_ICON,"tree2") set_trait(TRAIT_IDEAL_HEAT, 299) set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) diff --git a/icons/obj/hydroponics/hydroponics_growing.dmi b/icons/obj/hydroponics/hydroponics_growing.dmi index 4cfbff45b529dae210a160e1d331386f480fea4c..9e4f944856b03f9cbd726054436a0e6396f5e42e 100644 GIT binary patch delta 19524 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

    ZH_m(dG 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 delta 19407 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 Date: Thu, 21 Aug 2025 20:12:58 +1000 Subject: [PATCH 0745/1331] Automatic changelog generation for PR #5105 [ci skip] --- html/changelogs/AutoChangeLog-pr-5105.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5105.yml diff --git a/html/changelogs/AutoChangeLog-pr-5105.yml b/html/changelogs/AutoChangeLog-pr-5105.yml new file mode 100644 index 000000000000..f729008ff764 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5105.yml @@ -0,0 +1,5 @@ +author: Sutures +changes: + - {soundadd: added ambience to Shaded Hills river and swamp areas} + - {tweak: ambience should be louder and more frequent overall} +delete-after: true From 15e0092556b100c7d9a2828504e8c6ee73dacfa9 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 21 Aug 2025 20:15:16 +1000 Subject: [PATCH 0746/1331] Automatic changelog generation for PR #5110 [ci skip] --- html/changelogs/AutoChangeLog-pr-5110.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5110.yml diff --git a/html/changelogs/AutoChangeLog-pr-5110.yml b/html/changelogs/AutoChangeLog-pr-5110.yml new file mode 100644 index 000000000000..c20f99b900fa --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5110.yml @@ -0,0 +1,5 @@ +author: Sutures +changes: + - {imageadd: added new amanita mushroom plant growth states} + - {tweak: changed coffee plant growth states} +delete-after: true From 9b2bc9540adc61a3153b23377efae8f8d7691601 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 21 Aug 2025 20:15:40 +1000 Subject: [PATCH 0747/1331] Automatic changelog generation for PR #5108 [ci skip] --- html/changelogs/AutoChangeLog-pr-5108.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5108.yml diff --git a/html/changelogs/AutoChangeLog-pr-5108.yml b/html/changelogs/AutoChangeLog-pr-5108.yml new file mode 100644 index 000000000000..f3809a3b2716 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5108.yml @@ -0,0 +1,4 @@ +author: Sutures +changes: + - {tweak: stone walls now have a slight noise effect on their sprite} +delete-after: true From 61578f888f08a992cbc0532f92f08c8b28f00b75 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 21 Aug 2025 20:17:58 +1000 Subject: [PATCH 0748/1331] Automatic changelog generation for PR #5107 [ci skip] --- html/changelogs/AutoChangeLog-pr-5107.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5107.yml diff --git a/html/changelogs/AutoChangeLog-pr-5107.yml b/html/changelogs/AutoChangeLog-pr-5107.yml new file mode 100644 index 000000000000..2f1151c46a2c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5107.yml @@ -0,0 +1,4 @@ +author: Sutures +changes: + - {soundadd: added a boiling-water sound to cooking pots on Shaded Hills} +delete-after: true From 22c8dfd3bf43ec9f3acad1e2373d9c5c72d24d3b Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 22 Aug 2025 00:58:47 +0000 Subject: [PATCH 0749/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 11 +++++++++++ html/changelogs/.all_changelog.yml | 8 ++++++++ html/changelogs/AutoChangeLog-pr-5105.yml | 5 ----- html/changelogs/AutoChangeLog-pr-5107.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5108.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5110.yml | 5 ----- 6 files changed, 19 insertions(+), 18 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5105.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5107.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5108.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5110.yml diff --git a/html/changelog.html b/html/changelog.html index 2aecab0a3e31..581a7cf42779 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -51,6 +51,17 @@ -->

    + +

    22 August 2025

    +

    Sutures updated:

    +
      +
    • added new amanita mushroom plant growth states
    • +
    • changed coffee plant growth states
    • +
    • added ambience to Shaded Hills river and swamp areas
    • +
    • ambience should be louder and more frequent overall
    • +
    • stone walls now have a slight noise effect on their sprite
    • +
    • added a boiling-water sound to cooking pots on Shaded Hills
    • +
    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 6358b713724c..b60666410e5c 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15030,3 +15030,11 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2025-04-14: Zandario: - admin: Added a "Toggle Browser Inspect" debug verb for debugging UIs +2025-08-22: + Sutures: + - imageadd: added new amanita mushroom plant growth states + - tweak: changed coffee plant growth states + - soundadd: added ambience to Shaded Hills river and swamp areas + - tweak: ambience should be louder and more frequent overall + - tweak: stone walls now have a slight noise effect on their sprite + - soundadd: added a boiling-water sound to cooking pots on Shaded Hills diff --git a/html/changelogs/AutoChangeLog-pr-5105.yml b/html/changelogs/AutoChangeLog-pr-5105.yml deleted file mode 100644 index f729008ff764..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5105.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Sutures -changes: - - {soundadd: added ambience to Shaded Hills river and swamp areas} - - {tweak: ambience should be louder and more frequent overall} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-5107.yml b/html/changelogs/AutoChangeLog-pr-5107.yml deleted file mode 100644 index 2f1151c46a2c..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5107.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Sutures -changes: - - {soundadd: added a boiling-water sound to cooking pots on Shaded Hills} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-5108.yml b/html/changelogs/AutoChangeLog-pr-5108.yml deleted file mode 100644 index f3809a3b2716..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5108.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Sutures -changes: - - {tweak: stone walls now have a slight noise effect on their sprite} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-5110.yml b/html/changelogs/AutoChangeLog-pr-5110.yml deleted file mode 100644 index c20f99b900fa..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5110.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Sutures -changes: - - {imageadd: added new amanita mushroom plant growth states} - - {tweak: changed coffee plant growth states} -delete-after: true From 998f944bb89ce45d66509b3f1e9c5fce7af21495 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 24 Aug 2025 09:25:11 -0400 Subject: [PATCH 0750/1331] Fix missing parent call in nugget box icon code --- code/game/objects/items/weapons/storage/nuggets.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/objects/items/weapons/storage/nuggets.dm b/code/game/objects/items/weapons/storage/nuggets.dm index 3ecc72ce2270..c8a8b1602fc2 100644 --- a/code/game/objects/items/weapons/storage/nuggets.dm +++ b/code/game/objects/items/weapons/storage/nuggets.dm @@ -32,6 +32,7 @@ . += /obj/item/food/nugget /obj/item/box/nuggets/on_update_icon() + . = ..() var/datum/storage/box/nuggets/nugget_box = storage if(length(contents) == 0 || !istype(nugget_box)) icon_state = "[initial(icon_state)]_empty" From c4748d3f62e9d9cd78d23a02276f74d4d8429b08 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 24 Aug 2025 09:16:31 -0400 Subject: [PATCH 0751/1331] Fix chat filters being broken --- code/modules/chat_filter/_chat_filter.dm | 44 +++++++++++++++--------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/code/modules/chat_filter/_chat_filter.dm b/code/modules/chat_filter/_chat_filter.dm index 7e9ce18e77a0..ea96abac60af 100644 --- a/code/modules/chat_filter/_chat_filter.dm +++ b/code/modules/chat_filter/_chat_filter.dm @@ -1,21 +1,29 @@ var/global/list/chat_blockers_in_use var/global/list/chat_modifiers_in_use -/proc/build_filter_lists() +/proc/_get_chat_filters_in_use() global.chat_blockers_in_use = list() global.chat_modifiers_in_use = list() - var/list/all_filters = decls_repository.get_decls_of_type(/decl/chat_filter) - for(var/filtertype in all_filters) - var/decl/chat_filter/chat_filter = all_filters[filtertype] - if(!chat_filter.disabled) - if(chat_filter.can_deny_message) - global.chat_blockers_in_use += chat_filter - if(chat_filter.can_modify_message) - global.chat_modifiers_in_use += chat_filter - return TRUE + for(var/decl/chat_filter/chat_filter in decls_repository.get_decls_of_type_unassociated(/decl/chat_filter)) + if(chat_filter.disabled) + continue + if(chat_filter.can_deny_message) + global.chat_blockers_in_use += chat_filter + if(chat_filter.can_modify_message) + global.chat_modifiers_in_use += chat_filter + +/proc/get_chat_blockers_in_use() + if(!global.chat_blockers_in_use) + _get_chat_filters_in_use() + return global.chat_blockers_in_use + +/proc/get_chat_modifiers_in_use() + if(!global.chat_modifiers_in_use) + _get_chat_filters_in_use() + return global.chat_modifiers_in_use /proc/filter_block_message(var/mob/speaker, var/msg) - for(var/decl/chat_filter/chat_filter in global.chat_blockers_in_use) + for(var/decl/chat_filter/chat_filter in get_chat_blockers_in_use()) var/match = chat_filter.match(msg) if(match) var/deny_msg = chat_filter.deny(speaker, match) @@ -25,7 +33,7 @@ var/global/list/chat_modifiers_in_use /proc/filter_modify_message(var/msg) . = msg - for(var/decl/chat_filter/chat_filter in global.chat_modifiers_in_use) + for(var/decl/chat_filter/chat_filter in get_chat_modifiers_in_use()) var/match = chat_filter.match(.) if(match) . = chat_filter.replace(., match) @@ -50,9 +58,10 @@ var/global/list/chat_modifiers_in_use /client/verb/check_message_filters() set name = "Check Message Filters" set category = "OOC" - if(length(global.chat_blockers_in_use)) + var/list/blockers_in_use = get_chat_blockers_in_use() + if(length(blockers_in_use)) to_chat(usr, "The following filters are being used to block messages:
    ") - for(var/decl/chat_filter/chat_filter in global.chat_blockers_in_use) + for(var/decl/chat_filter/chat_filter in blockers_in_use) to_chat(usr, "[chat_filter.name]
    [chat_filter.summary]
    ") else to_chat(usr, "There are no filters being used to block messages currently.") @@ -60,9 +69,10 @@ var/global/list/chat_modifiers_in_use /client/verb/check_message_modifiers() set name = "Check Message Modifiers" set category = "OOC" - if(length(global.chat_modifiers_in_use)) + var/list/modifiers_in_use = get_chat_modifiers_in_use() + if(length(modifiers_in_use)) to_chat(usr, "The following filters can be used to modify messages:
    ") - for(var/decl/chat_filter/chat_filter in global.chat_modifiers_in_use) + for(var/decl/chat_filter/chat_filter in modifiers_in_use) to_chat(usr, "[chat_filter.name]
    [chat_filter.summary]
    ") else - to_chat(usr, "There are no avaiable filters for modifying messages currently.") + to_chat(usr, "There are no available filters for modifying messages currently.") From cf4dee71d18086d8536c12f94c286cbab5ddae1e Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 28 Aug 2025 20:25:25 +1000 Subject: [PATCH 0752/1331] Automatic changelog generation for PR #5102 [ci skip] --- html/changelogs/AutoChangeLog-pr-5102.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5102.yml diff --git a/html/changelogs/AutoChangeLog-pr-5102.yml b/html/changelogs/AutoChangeLog-pr-5102.yml new file mode 100644 index 000000000000..845674f28c76 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5102.yml @@ -0,0 +1,4 @@ +author: Elizabeth +changes: + - {imageadd: added some variant sprites for cobblestone and running bond paths} +delete-after: true From 4b5945f733a464986193572fa4e64947513dfccd Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 29 Aug 2025 00:55:12 +0000 Subject: [PATCH 0753/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-5102.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5102.yml diff --git a/html/changelog.html b/html/changelog.html index 581a7cf42779..02e47675ee10 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    29 August 2025

    +

    Elizabeth updated:

    +
      +
    • added some variant sprites for cobblestone and running bond paths
    • +
    +

    22 August 2025

    Sutures updated:

    - - - +
    + + + diff --git a/html/templates/header.html b/html/templates/header.html index 09df40020d4b..6d078158aca2 100644 --- a/html/templates/header.html +++ b/html/templates/header.html @@ -1,53 +1,53 @@ - - - - Nebula13 Changelog - - - - - - - -" - if(!user.skillset?.skills_transferable) - skill_link = "" - // Begin assembling the actual HTML. index += 1 if((index >= limit) || (job.title in splitJobs)) From 2508c70829fc540c8142e1bebc6d3a1d4a66998c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 29 Oct 2025 16:14:46 -0400 Subject: [PATCH 0872/1331] Update BYOND and SpacemanDMM versions in CI --- .github/workflows/generate_documentation.yml | 2 +- .github/workflows/test.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index c3c4eced76b6..0f5480f5a2e3 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: env: - SPACEMAN_DMM_VERSION: suite-1.9 + SPACEMAN_DMM_VERSION: suite-1.11 jobs: generate_documentation: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 66af11e7167b..c3dc37b34642 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,9 +14,9 @@ on: workflow_dispatch: env: - BYOND_MAJOR: "515" - BYOND_MINOR: "1647" - SPACEMAN_DMM_VERSION: suite-1.9 + BYOND_MAJOR: "516" + BYOND_MINOR: "1669" + SPACEMAN_DMM_VERSION: suite-1.11 jobs: DreamChecker: From dc2da95971e93319c3db4656cf7a47ce162f9195 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 29 Oct 2025 16:33:35 -0400 Subject: [PATCH 0873/1331] Update CI to use ubuntu-latest --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3dc37b34642..4e7118fe154a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ env: jobs: DreamChecker: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Cache @@ -52,7 +52,7 @@ jobs: chmod +x send.sh ./send.sh failure $WEBHOOK_URL OpenDream: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Python setup @@ -69,7 +69,7 @@ jobs: - name: Run OpenDream run: ./DMCompiler_linux-x64/DMCompiler nebula.dme --define=UNIT_TEST --suppress-unimplemented --skip-anything-typecheck --version=${BYOND_MAJOR}.${BYOND_MINOR} | bash test/annotate_od.sh Code: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Cache @@ -95,7 +95,7 @@ jobs: chmod +x send.sh ./send.sh failure $WEBHOOK_URL Maps: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: matrix: map_path: [example, tradeship, exodus, ministation, shaded_hills, away_sites_testing, modpack_testing, planets_testing] From 8817d93efbd2400a46e8a4c5435c97e1c43a0a80 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:10:24 +1100 Subject: [PATCH 0874/1331] Various changes to allow digging through a turf and carving a ramp from one level up as a human or drake. --- .../abilities/abilities_predator.dm | 9 +++- code/game/base_turf.dm | 6 +++ code/game/turfs/floors/floor_digging.dm | 23 ++++++---- code/game/turfs/floors/floor_layers.dm | 2 +- code/game/turfs/open/_open.dm | 43 ++++++++++++++++++- code/game/turfs/turf.dm | 33 ++++++++------ code/game/turfs/turf_digging.dm | 5 ++- code/game/turfs/walls/wall_natural.dm | 11 ++++- code/game/turfs/walls/wall_natural_ramps.dm | 27 ++++++++---- .../archetypes/tool_archetype_definitions.dm | 2 +- 10 files changed, 123 insertions(+), 38 deletions(-) diff --git a/code/datums/extensions/abilities/abilities_predator.dm b/code/datums/extensions/abilities/abilities_predator.dm index 4c73a709cc7b..379316b3de80 100644 --- a/code/datums/extensions/abilities/abilities_predator.dm +++ b/code/datums/extensions/abilities/abilities_predator.dm @@ -56,7 +56,8 @@ to_chat(user, SPAN_WARNING("\The [victim] is too big for you to dismember.")) return TRUE - var/obj/item/organ/external/limb = victim.get_organ(user.get_target_zone()) + var/target_zone = user.get_target_zone() + var/obj/item/organ/external/limb = victim.get_organ(target_zone) if(!limb) to_chat(user, SPAN_WARNING("\The [victim] is missing that limb!")) return TRUE @@ -65,13 +66,17 @@ if(!do_after(user, max(2 SECONDS, victim.get_object_size() * 5), victim) || QDELETED(victim) || !victim.butchery_data || victim.stat != DEAD) return TRUE + // Changing zone means we cancel. + if(target_zone != user.get_target_zone()) + return + var/list/external_organs = victim.get_external_organs() if(length(external_organs) <= 1) user.visible_message(SPAN_DANGER("\The [user] tears \the [victim] apart!")) victim.gib() return TRUE - limb = victim.get_organ(user.get_target_zone()) // In case we changed zone or such in the meantime. + limb = victim.get_organ(target_zone) // In case it was removed in the interim. if(!limb) to_chat(user, SPAN_WARNING("\The [victim] is missing that limb!")) return TRUE diff --git a/code/game/base_turf.dm b/code/game/base_turf.dm index 0fa6f37889ec..22c582f53a8f 100644 --- a/code/game/base_turf.dm +++ b/code/game/base_turf.dm @@ -26,6 +26,12 @@ return A.base_turf return get_base_turf(T.z) +/proc/get_open_turf_type_by_area(var/turf/T) + if(!HasBelow(T.z)) + return + var/area/area = get_area(T) + return area?.open_turf || T.open_turf_type + // Returns the open turf of a Z-stack by finding the nearest non-open turf below. /proc/get_open_turf_type(var/turf/T) if(!HasBelow(T.z)) diff --git a/code/game/turfs/floors/floor_digging.dm b/code/game/turfs/floors/floor_digging.dm index 123458d009cd..24a4f9edb132 100644 --- a/code/game/turfs/floors/floor_digging.dm +++ b/code/game/turfs/floors/floor_digging.dm @@ -25,22 +25,27 @@ return FALSE return TRUE -/turf/floor/can_dig_trench(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) - return can_be_dug(tool_hardness, using_tool) && get_physical_height() > -(FLUID_DEEP) - /turf/floor/dig_trench(mob/user, tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) - if(flooring_is_diggable()) - handle_trench_digging(user) + if(!flooring_is_diggable()) + return -/turf/floor/proc/handle_trench_digging(mob/user) var/decl/flooring/flooring = get_topmost_flooring() if(!flooring.handle_turf_digging(src)) return - // Only drop mats if we actually changed the turf height sufficiently. + var/old_height = get_physical_height() var/new_height = max(old_height-TRENCH_DEPTH_PER_ACTION, -(FLUID_DEEP)) - var/height_diff = abs(old_height-new_height) - if(height_diff >= TRENCH_DEPTH_PER_ACTION) + var/height_diff = round(abs(old_height-new_height)) + if(new_height <= -(FLUID_DEEP)) + var/open_turf_path = get_open_turf_type_by_area(src) + if(!open_turf_path) + to_chat(user, SPAN_WARNING("You cannot dig any lower!")) + return + to_chat(user, SPAN_DANGER("You break through \the [src]!")) + drop_diggable_resources(user) + ChangeTurf(open_turf_path) + // Only drop mats if we actually changed the turf height sufficiently. + else if(height_diff >= TRENCH_DEPTH_PER_ACTION) drop_diggable_resources(user) set_physical_height(new_height) diff --git a/code/game/turfs/floors/floor_layers.dm b/code/game/turfs/floors/floor_layers.dm index 0b91d43f8af1..18cfa6f04fe9 100644 --- a/code/game/turfs/floors/floor_layers.dm +++ b/code/game/turfs/floors/floor_layers.dm @@ -9,7 +9,7 @@ if(islist(_flooring)) for(var/floor in _flooring) . += RESOLVE_TO_DECL(floor) - else if(ispath(_flooring)) + else if(!isnull(_flooring)) . += RESOLVE_TO_DECL(_flooring) if(_base_flooring) . += get_base_flooring() diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index 3836af3e8072..abbcfecc695a 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -101,4 +101,45 @@ T.drill_act() /turf/open/airless - initial_gas = null \ No newline at end of file + initial_gas = null + +/decl/interaction_handler/dig_ramp_from_above + name = "Dig Ramp From Above" + expected_target_type = /turf/open + examine_desc = "dig a ramp in the direction you are facing, one level down" + +/decl/interaction_handler/dig_ramp_from_above/is_possible(atom/target, mob/user, obj/item/prop) + . = ..() + if(.) + // TODO: check for blocking floors etc. + prop ||= user.get_usable_hand_slot_organ() // Allows drakes to dig. + if(!IS_PICK(prop) && !IS_SHOVEL(prop)) + return FALSE + var/turf/turf = get_turf(target) + if(!istype(turf) || !turf.is_open() || !HasBelow(turf.z)) + return FALSE + var/turf/wall/natural/wall = GetBelow(target) + if(!istype(wall)) + return FALSE + if(!user.Adjacent(target)) + return FALSE + return TRUE + +/decl/interaction_handler/dig_ramp_from_above/invoked(atom/target, mob/user, obj/item/prop) + var/turf/wall/natural/wall = GetBelow(target) + var/user_dir = get_dir(user, target) // opposite of regular wall carving as we are going downwards + if(!(user_dir in global.cardinal)) + to_chat(user, SPAN_WARNING("You must be standing at a cardinal angle to create a ramp.")) + return FALSE + prop ||= user.get_usable_hand_slot_organ() // Allows drakes to dig. + if(wall.material?.hardness > prop?.material?.hardness) + to_chat(user, SPAN_WARNING("Your [prop.name] is not hard enough to cut into \the [wall].")) + return FALSE + var/turf/wall/natural/support = get_step(wall, global.reverse_dir[user_dir]) + if(!istype(support) || support.ramp_slope_direction) + to_chat(user, SPAN_WARNING("You cannot cut a ramp into a wall with no additional walls behind it.")) + return FALSE + if(prop.do_tool_interaction((IS_PICK(prop) ? TOOL_PICK : TOOL_SHOVEL), user, wall, 3 SECONDS, suffix_message = ", forming it into a ramp") && !wall.ramp_slope_direction) + wall.make_ramp(user, user_dir) + return TRUE + return FALSE diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index a9a61db02d40..6a6df3ddcc8a 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -250,10 +250,6 @@ T.try_build_turf(user, src) return TRUE - if(IS_HOE(used_item) && can_dig_farm(used_item.material?.hardness)) - try_dig_farm(user, used_item) - return TRUE - if(IS_SHOVEL(used_item)) // TODO: move these checks into the interaction handlers. @@ -274,6 +270,10 @@ to_chat(user, SPAN_WARNING("You cannot dig anything out of \the [src] with \the [used_item].")) return TRUE + if(IS_HOE(used_item) && can_dig_farm(used_item.material?.hardness)) + try_dig_farm(user, used_item) + return TRUE + var/decl/material/material = get_material() if(IS_PICK(used_item) && material) @@ -856,21 +856,28 @@ var/obj/item/held = user ? (user.get_active_held_item() || user.get_usable_hand_slot_organ()) : null if(!istype(held)) return - if(IS_SHOVEL(held)) - if(can_dig_pit(held.material?.hardness)) - LAZYADD(., /decl/interaction_handler/dig/pit) - if(can_dig_trench(held.material?.hardness)) + if(is_open()) + if(HasBelow(z)) + LAZYADD(., /decl/interaction_handler/dig_ramp_from_above) + else + if(IS_SHOVEL(held)) + if(can_dig_pit(held.material?.hardness)) + LAZYADD(., /decl/interaction_handler/dig/pit) + if(can_dig_trench(held.material?.hardness)) + LAZYADD(., /decl/interaction_handler/dig/trench) + if(IS_PICK(held) && can_dig_trench(held.material?.hardness, using_tool = TOOL_PICK)) LAZYADD(., /decl/interaction_handler/dig/trench) - if(IS_PICK(held) && can_dig_trench(held.material?.hardness, using_tool = TOOL_PICK)) - LAZYADD(., /decl/interaction_handler/dig/trench) - if(IS_HOE(held) && can_dig_farm(held.material?.hardness)) - LAZYADD(., /decl/interaction_handler/dig/farm) + if(IS_HOE(held) && can_dig_farm(held.material?.hardness)) + LAZYADD(., /decl/interaction_handler/dig/farm) /// Contaminant may be the chemical decl of the footprint being provided, /// or null if we just want to know if we support footprints, at all, ever. /turf/proc/can_show_coating_footprints(decl/material/contaminant) return simulated +/turf/proc/is_purged() + return + /decl/interaction_handler/show_turf_contents name = "Show Turf Contents" expected_user_type = /mob @@ -929,5 +936,3 @@ /turf/take_vaporized_reagent(reagent, amount) return assume_gas(reagent, round(amount / REAGENT_UNITS_PER_GAS_MOLE)) - -/turf/proc/is_purged() \ No newline at end of file diff --git a/code/game/turfs/turf_digging.dm b/code/game/turfs/turf_digging.dm index 5f832ae8045c..d9944cc1c814 100644 --- a/code/game/turfs/turf_digging.dm +++ b/code/game/turfs/turf_digging.dm @@ -35,6 +35,9 @@ to_chat(user, SPAN_NOTICE("You unearth \a [thing]!")) LAZYADD(., loot) + for(var/obj/item/stack/stack in .) + stack.add_to_stacks() + clear_diggable_resources() // Procs for digging pits. @@ -66,7 +69,7 @@ // Proc for digging trenches. /turf/proc/can_dig_trench(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) - return can_be_dug(tool_hardness, using_tool) && get_physical_height() > -(FLUID_DEEP) + return can_be_dug(tool_hardness, using_tool) && (HasBelow(z) || get_physical_height() > -(FLUID_DEEP)) /turf/proc/try_dig_trench(mob/user, obj/item/tool, using_tool = TOOL_SHOVEL) if((!user && !tool) || tool.do_tool_interaction(using_tool, user, src, 2.5 SECONDS, check_skill = SKILL_HAULING, set_cooldown = TRUE)) diff --git a/code/game/turfs/walls/wall_natural.dm b/code/game/turfs/walls/wall_natural.dm index 095e8f7ed26e..c85396e05f5f 100644 --- a/code/game/turfs/walls/wall_natural.dm +++ b/code/game/turfs/walls/wall_natural.dm @@ -50,9 +50,18 @@ update_neighboring_ramps(destroying_self = TRUE) . = ..() +/turf/wall/natural/attack_hand(mob/user) + + // Allow species with digging limbs to dig (drakes) + var/obj/item/prop = user.get_usable_hand_slot_organ() + if(istype(prop)) + return attackby(prop, user) + + . = ..() + /turf/wall/natural/attackby(obj/item/used_item, mob/user, click_params) - if(user.check_dexterity(DEXTERITY_COMPLEX_TOOLS) && !ramp_slope_direction) + if(!ramp_slope_direction && user.check_dexterity(DEXTERITY_COMPLEX_TOOLS, silent = TRUE)) if(istype(used_item, /obj/item/depth_scanner)) var/obj/item/depth_scanner/C = used_item diff --git a/code/game/turfs/walls/wall_natural_ramps.dm b/code/game/turfs/walls/wall_natural_ramps.dm index 760c4a01f0d5..3cb6ea73728a 100644 --- a/code/game/turfs/walls/wall_natural_ramps.dm +++ b/code/game/turfs/walls/wall_natural_ramps.dm @@ -4,6 +4,9 @@ var/old_ao = permit_ao if(ramp_slope_direction) + + user?.visible_message(SPAN_NOTICE("\The [user] digs out \the [src], forming a ramp.")) + drop_ore() permit_ao = FALSE blocks_air = FALSE @@ -23,6 +26,9 @@ update_neighboring_ramps() else + + user?.visible_message(SPAN_NOTICE("\The [user] clears out \the [src].")) + permit_ao = initial(permit_ao) blocks_air = initial(blocks_air) density = initial(density) @@ -49,17 +55,18 @@ /turf/wall/natural/get_alt_interactions(mob/user) . = ..() - LAZYADD(., /decl/interaction_handler/drill_ramp) + LAZYADD(., /decl/interaction_handler/dig_ramp) -/decl/interaction_handler/drill_ramp - name = "Drill Ramp" +/decl/interaction_handler/dig_ramp + name = "Dig Ramp" expected_target_type = /turf/wall/natural - examine_desc = "drill a ramp in the direction you are facing" + examine_desc = "dig a ramp in the direction you are facing" -/decl/interaction_handler/drill_ramp/is_possible(atom/target, mob/user, obj/item/prop) +/decl/interaction_handler/dig_ramp/is_possible(atom/target, mob/user, obj/item/prop) . = ..() if(.) - if(!IS_PICK(prop)) + prop ||= user.get_usable_hand_slot_organ() // Allows drakes to dig. + if(!IS_PICK(prop) && !IS_SHOVEL(prop)) return FALSE var/turf/wall/natural/wall = target if(!HasAbove(wall.z)) @@ -68,17 +75,21 @@ return FALSE return TRUE -/decl/interaction_handler/drill_ramp/invoked(atom/target, mob/user, obj/item/prop) +/decl/interaction_handler/dig_ramp/invoked(atom/target, mob/user, obj/item/prop) var/turf/wall/natural/wall = target var/user_dir = get_dir(wall, user) if(!(user_dir in global.cardinal)) to_chat(user, SPAN_WARNING("You must be standing at a cardinal angle to create a ramp.")) return FALSE + prop ||= user.get_usable_hand_slot_organ() // Allows drakes to dig. + if(wall.material?.hardness > prop?.material?.hardness) + to_chat(user, SPAN_WARNING("Your [prop.name] is not hard enough to cut into \the [wall].")) + return FALSE var/turf/wall/natural/support = get_step(wall, global.reverse_dir[user_dir]) if(!istype(support) || support.ramp_slope_direction) to_chat(user, SPAN_WARNING("You cannot cut a ramp into a wall with no additional walls behind it.")) return FALSE - if(prop.do_tool_interaction(TOOL_PICK, user, wall, 3 SECONDS, suffix_message = ", forming it into a ramp") && !wall.ramp_slope_direction) + if(prop.do_tool_interaction((IS_PICK(prop) ? TOOL_PICK : TOOL_SHOVEL), user, wall, 3 SECONDS, suffix_message = ", forming it into a ramp") && !wall.ramp_slope_direction) wall.make_ramp(user, user_dir) return TRUE return FALSE diff --git a/code/modules/tools/archetypes/tool_archetype_definitions.dm b/code/modules/tools/archetypes/tool_archetype_definitions.dm index 2b2b2f14f4d6..bf8e02c84f86 100644 --- a/code/modules/tools/archetypes/tool_archetype_definitions.dm +++ b/code/modules/tools/archetypes/tool_archetype_definitions.dm @@ -37,7 +37,7 @@ /decl/tool_archetype/shovel name = "shovel" tool_sound = 'sound/items/shovel_dirt.ogg' - tool_message = "digging" + tool_message = "digging into" /decl/tool_archetype/pick name = "pick" From 36b4be55135c26e6bbec5b0885592f13f8343824 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 30 Oct 2025 11:11:37 -0400 Subject: [PATCH 0875/1331] Downgrade CI BYOND version to 1663 to avoid libcurl4 requirement --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e7118fe154a..5c6c414a8e26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ on: env: BYOND_MAJOR: "516" - BYOND_MINOR: "1669" + BYOND_MINOR: "1663" SPACEMAN_DMM_VERSION: suite-1.11 jobs: From 3119596246770e74d372c3eb0c5af4b0febb6410 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 31 Oct 2025 00:56:52 +0000 Subject: [PATCH 0876/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 94d93182b2f7..561c88760f9c 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,12 +57,6 @@

    MistakeNot4892 updated:

    • Crafted barrels will need barrel rims forged if the forging modpack is in use.
    - -

    29 August 2025

    -

    Elizabeth updated:

    -
      -
    • added some variant sprites for cobblestone and running bond paths
    • -
    - - - - -
    -
    Nebula13
    -
    A Space Station 13 Project
    - -

    - Code licensed under AGPLv3. Content licensed under CC BY-SA 3.0.

    -
    - -
    Nebula13 Credit List - - - - -
    - Contributor list: -Click Here-
    - Special thanks to: the developers of Baystation 12, ScavStation, /tg/station, /vg/station, GoonStation, the original Space Station 13, and BYOND.
    -

    Have a bug to report?
    Visit our Issue Tracker.
    -
    - - -
    + + + + Nebula13 Changelog + + + + + + + +" @@ -132,9 +130,9 @@ if(!branch_string) branch_string = "" if(player_branch) - var/ranks = branch_rank[player_branch.name] || using_branches.spawn_ranks(player_branch.name, S) + var/ranks = branch_rank[player_branch.name] || global.using_map.spawn_ranks(player_branch.name, S) if(LAZYLEN(ranks)) - player_rank = using_branches.get_rank(player_branch.name, pref.ranks[job.title]) + player_rank = global.using_map.get_rank(player_branch.name, pref.ranks[job.title]) if(player_rank) if(LAZYLEN(ranks) > 1) rank_branch_string += "" @@ -259,8 +257,6 @@ /datum/category_item/player_setup_item/proc/validate_branch_and_rank() - var/datum/mil_branches/using_branches = global.using_map.mil_branches - if(LAZYLEN(pref.branches)) for(var/job_name in pref.branches) if(!(job_name in SSjobs.titles_to_datums)) @@ -279,16 +275,16 @@ var/datum/job/job = SSjobs.get_by_title(job_name) - var/datum/mil_branch/player_branch = pref.branches[job.title] ? using_branches.get_branch(pref.branches[job.title]) : null - var/branch_rank = job.allowed_branches ? job.get_branch_rank(S) : using_branches.spawn_branches(S) + var/datum/mil_branch/player_branch = pref.branches[job.title] ? global.using_map.get_branch(pref.branches[job.title]) : null + var/branch_rank = job.allowed_branches ? job.get_branch_rank(S) : global.using_map.spawn_branches(S) if(!player_branch || !(player_branch.name in branch_rank)) - player_branch = LAZYLEN(branch_rank) ? using_branches.get_branch(branch_rank[1]) : null + player_branch = LAZYLEN(branch_rank) ? global.using_map.get_branch(branch_rank[1]) : null if(player_branch) - var/datum/mil_rank/player_rank = pref.ranks[job.title] ? using_branches.get_rank(player_branch.name, pref.ranks[job.title]) : null - var/ranks = branch_rank[player_branch.name] || using_branches.spawn_ranks(player_branch.name, S) + var/datum/mil_rank/player_rank = pref.ranks[job.title] ? global.using_map.get_rank(player_branch.name, pref.ranks[job.title]) : null + var/ranks = branch_rank[player_branch.name] || global.using_map.spawn_ranks(player_branch.name, S) if(!player_rank || !(player_rank.name in ranks)) - player_rank = LAZYLEN(ranks) ? using_branches.get_rank(player_branch.name, ranks[1]) : null + player_rank = LAZYLEN(ranks) ? global.using_map.get_rank(player_branch.name, ranks[1]) : null // Now make the assignments pref.branches[job.title] = player_branch.name @@ -301,7 +297,6 @@ pref.ranks -= job.title /datum/category_item/player_setup_item/occupation/OnTopic(href, href_list, user) - var/datum/mil_branches/using_branches = global.using_map.mil_branches if(href_list["reset_jobs"]) ResetJobs() return TOPIC_REFRESH @@ -345,9 +340,9 @@ var/datum/job/job = locate(href_list["checking_job"]) if(istype(job)) var/decl/species/S = pref.get_species_decl() - var/list/options = job.allowed_branches ? job.get_branch_rank(S) : using_branches.spawn_branches(S) + var/list/options = job.allowed_branches ? job.get_branch_rank(S) : global.using_map.spawn_branches(S) var/choice = input(user, "Choose your branch of service.", CHARACTER_PREFERENCE_INPUT_TITLE) as null|anything in options - if(choice && CanUseTopic(user) && using_branches.is_spawn_branch(choice, S)) + if(choice && CanUseTopic(user) && global.using_map.is_spawn_branch(choice, S)) pref.branches[job.title] = choice pref.ranks -= job.title pref.skills_allocated = pref.sanitize_skills(pref.skills_allocated) // Check our skillset is still valid @@ -358,12 +353,12 @@ else if(href_list["char_rank"]) var/datum/job/job = locate(href_list["checking_job"]) if(istype(job)) - var/datum/mil_branch/branch = using_branches.get_branch(pref.branches[job.title]) + var/datum/mil_branch/branch = global.using_map.get_branch(pref.branches[job.title]) var/decl/species/S = pref.get_species_decl() - var/list/branch_rank = job.allowed_branches ? job.get_branch_rank(S) : using_branches.spawn_branches(S) - var/list/options = branch_rank[branch.name] || using_branches.spawn_ranks(branch.name, S) + var/list/branch_rank = job.allowed_branches ? job.get_branch_rank(S) : global.using_map.spawn_branches(S) + var/list/options = branch_rank[branch.name] || global.using_map.spawn_ranks(branch.name, S) var/choice = input(user, "Choose your rank.", CHARACTER_PREFERENCE_INPUT_TITLE) as null|anything in options - if(choice && CanUseTopic(user) && using_branches.is_spawn_rank(branch.name, choice, pref.get_species_decl())) + if(choice && CanUseTopic(user) && global.using_map.is_spawn_rank(branch.name, choice, pref.get_species_decl())) pref.ranks[job.title] = choice pref.skills_allocated = pref.sanitize_skills(pref.skills_allocated) // Check our skillset is still valid validate_branch_and_rank() @@ -430,7 +425,7 @@ if(job.allowed_branches) dat += "You can be of following ranks:" for(var/T in job.allowed_branches) - var/datum/mil_branch/B = using_branches.get_branch_by_type(T) + var/datum/mil_branch/B = global.using_map.get_branch_by_type(T) dat += "
  • [B.name]: [job.get_ranks(B.name)]" dat += "
    " if(get_config_value(/decl/config/text/wikiurl)) diff --git a/code/modules/client/preference_setup/occupation/skill_selection.dm b/code/modules/client/preference_setup/occupation/skill_selection.dm index acb013ddc275..6909a811fd5d 100644 --- a/code/modules/client/preference_setup/occupation/skill_selection.dm +++ b/code/modules/client/preference_setup/occupation/skill_selection.dm @@ -24,7 +24,7 @@ if(job && job.min_skill) . = job.min_skill[S.type] if(!.) - var/datum/mil_branch/branch = global.using_map.mil_branches.get_branch(branches[job.title]) + var/datum/mil_branch/branch = global.using_map.get_branch(branches[job.title]) if(branch && branch.min_skill) . = branch.min_skill[S.type] if(!.) diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 7bc040ee5570..51565cfd714d 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -74,8 +74,8 @@ if((equip_preview_mob & EQUIP_PREVIEW_JOB) && previewJob) mannequin.job = previewJob.title - var/datum/mil_branch/branch = global.using_map.mil_branches.get_branch(branches[previewJob.title]) - var/datum/mil_rank/rank = global.using_map.mil_branches.get_rank(branches[previewJob.title], ranks[previewJob.title]) + var/datum/mil_branch/branch = global.using_map.get_branch(branches[previewJob.title]) + var/datum/mil_rank/rank = global.using_map.get_rank(branches[previewJob.title], ranks[previewJob.title]) previewJob.equip_preview(mannequin, player_alt_titles[previewJob.title], branch, rank) update_icon = TRUE diff --git a/code/modules/modular_computers/file_system/manifest.dm b/code/modules/modular_computers/file_system/manifest.dm index 9b07a16a2e1d..ee79e016bd94 100644 --- a/code/modules/modular_computers/file_system/manifest.dm +++ b/code/modules/modular_computers/file_system/manifest.dm @@ -26,8 +26,8 @@ mil_ranks[name] = "" if(global.using_map.flags & MAP_HAS_RANK) - var/datum/mil_branch/branch_obj = global.using_map.mil_branches.get_branch(CR.get_branch()) - var/datum/mil_rank/rank_obj = global.using_map.mil_branches.get_rank(CR.get_branch(), CR.get_rank()) + var/datum/mil_branch/branch_obj = global.using_map.get_branch(CR.get_branch()) + var/datum/mil_rank/rank_obj = global.using_map.get_rank(CR.get_branch(), CR.get_rank()) if(branch_obj && rank_obj) mil_ranks[name] = "[rank_obj.name_short] " diff --git a/code/modules/modular_computers/file_system/reports/crew_record.dm b/code/modules/modular_computers/file_system/reports/crew_record.dm index 1570b210e6bb..19af29eaeac7 100644 --- a/code/modules/modular_computers/file_system/reports/crew_record.dm +++ b/code/modules/modular_computers/file_system/reports/crew_record.dm @@ -266,7 +266,7 @@ FIELD_LONG("Exploitable Information", antag_record, access_hacked, access_hacked //Options builderes /datum/report_field/options/crew_record/rank/proc/record_ranks() var/datum/computer_file/report/crew_record/record = owner - var/datum/mil_branch/branch = global.using_map.mil_branches.get_branch(record.get_branch()) + var/datum/mil_branch/branch = global.using_map.get_branch(record.get_branch()) if(!branch) return . = list() @@ -287,8 +287,8 @@ FIELD_LONG("Exploitable Information", antag_record, access_hacked, access_hacked /datum/report_field/options/crew_record/branch/proc/record_branches() . = list() . |= "Unset" - for(var/branch in global.using_map.mil_branches.branches) - var/datum/mil_branch/branch_datum = global.using_map.mil_branches.branches[branch] + for(var/branch in global.using_map.branches) + var/datum/mil_branch/branch_datum = global.using_map.branches[branch] . |= branch_datum.name #undef GETTER_SETTER diff --git a/code/modules/modular_computers/file_system/reports/people.dm b/code/modules/modular_computers/file_system/reports/people.dm index 522c3e80c547..da2961c13adf 100644 --- a/code/modules/modular_computers/file_system/reports/people.dm +++ b/code/modules/modular_computers/file_system/reports/people.dm @@ -95,7 +95,7 @@ if(in_line && (global.using_map.flags & MAP_HAS_RANK)) var/datum/computer_file/report/crew_record/CR = get_crewmember_record(entry["name"]) if(CR) - var/datum/mil_rank/rank_obj = global.using_map.mil_branches.get_rank(CR.get_branch(), CR.get_rank()) + var/datum/mil_rank/rank_obj = global.using_map.get_rank(CR.get_branch(), CR.get_rank()) milrank = (rank_obj ? rank_obj.name_short : "") dat += format_output(entry["name"], in_line ? null : entry["rank"], milrank) return jointext(dat, in_line ? ", " : "
    ") diff --git a/code/modules/submaps/submap_join.dm b/code/modules/submaps/submap_join.dm index 615f96a05d22..4b1b6d3a4420 100644 --- a/code/modules/submaps/submap_join.dm +++ b/code/modules/submaps/submap_join.dm @@ -68,10 +68,9 @@ var/mob/living/human/user_human if(ishuman(character)) user_human = character - var/datum/mil_branches/using_branches = global.using_map.mil_branches - if(job.branch && global.using_map & MAP_HAS_BRANCH && using_branches) - user_human.char_branch = using_branches.get_branch(job.branch) - user_human.char_rank = using_branches.get_rank(job.branch, job.rank) + if(job.branch && (global.using_map.flags & MAP_HAS_BRANCH)) + user_human.char_branch = global.using_map.get_branch(job.branch) + user_human.char_rank = global.using_map.get_rank(job.branch, job.rank) // We need to make sure to use the abstract instance here; it's not the same as the one we were passed. character.skillset.obtain_from_client(SSjobs.get_by_path(job.type), character.client) diff --git a/maps/~mapsystem/map_ranks.dm b/maps/~mapsystem/map_ranks.dm index 5ced51e5f70b..8bd2dd54b04b 100644 --- a/maps/~mapsystem/map_ranks.dm +++ b/maps/~mapsystem/map_ranks.dm @@ -1,6 +1,13 @@ /datum/map - var/datum/mil_branches/mil_branches = new() - var/list/branch_types // list of branch datum paths for military branches available on this map + /// All branches that exist on this map + var/list/branches = list() + /// Branches that a player can choose for spawning on this map, not including species restrictions. + var/list/spawn_branches_ = list() + /// Branches that a player can choose for spawning on this map, with species restrictions. Populated on an as-needed basis + var/list/spawn_branches_by_species_ = list() + + /// list of branch datum paths for military branches available on this map + var/list/branch_types var/list/spawn_branch_types // subset of above for branches a player can spawn in with var/list/species_to_branch_whitelist = list() // List of branches which are allowed, per species. Checked before the blacklist. @@ -9,6 +16,100 @@ var/list/species_to_rank_whitelist = list() // List of ranks which are allowed, per branch and species. Checked before the blacklist. var/list/species_to_rank_blacklist = list() // Lists of ranks which are restricted, per species. +// todo: this will need heavy reworking if we promote submaps from second to first class map status +/** + * Populate the map branches list + */ +/datum/map/proc/populate_branches() + if(!(global.using_map.flags & MAP_HAS_BRANCH) && !(global.using_map.flags & MAP_HAS_RANK)) + branches = null + spawn_branches_ = null + spawn_branches_by_species_ = null + return 1 + + branches = list() + spawn_branches_ = list() + spawn_branches_by_species_ = list() + for(var/branch_path in global.using_map.branch_types) + if(!ispath(branch_path, /datum/mil_branch)) + PRINT_STACK_TRACE("populate_branches() attempted to instantiate object with path [branch_path], which is not a subtype of /datum/mil_branch.") + continue + + var/datum/mil_branch/branch = new branch_path () + branches[branch.name] = branch + + if(branch_path in global.using_map.spawn_branch_types) + spawn_branches_[branch.name] = branch + + return 1 + +/** + * Retrieve branch object by branch name + */ +/datum/map/proc/get_branch(var/branch_name) + if(ispath(branch_name, /datum/mil_branch)) + var/datum/mil_branch/branch = branch_name + branch_name = initial(branch.name) + if(branch_name && branch_name != "None") + return branches[branch_name] + +/** + * Retrieve branch object by branch type + */ +/datum/map/proc/get_branch_by_type(var/branch_type) + for(var/name in branches) + if (istype(branches[name], branch_type)) + return branches[name] + +/** + * Retrieve a rank object from given branch by name + */ +/datum/map/proc/get_rank(var/branch_name, var/rank_name) + if(ispath(rank_name)) + var/datum/mil_rank/rank = rank_name + rank_name = initial(rank.name) + if(rank_name && rank_name != "None") + var/datum/mil_branch/branch = get_branch(branch_name) + if(branch) + return branch.ranks[rank_name] + +/** + * Return all spawn branches for the given input + */ +/datum/map/proc/spawn_branches(var/decl/species/S) + if(!S) + return spawn_branches_.Copy() + . = LAZYACCESS(spawn_branches_by_species_, S) + if(!.) + . = list() + LAZYSET(spawn_branches_by_species_, S, .) + for(var/spawn_branch in spawn_branches_) + if(!global.using_map.is_species_branch_restricted(S, spawn_branches_[spawn_branch])) + . += spawn_branch + +/** + * Return all spawn ranks for the given input + */ +/datum/map/proc/spawn_ranks(var/branch_name, var/decl/species/S) + var/datum/mil_branch/branch = get_branch(branch_name) + return branch && branch.spawn_ranks(S) + +/** + * Return a true value if branch_name is a valid spawn branch key + */ +/datum/map/proc/is_spawn_branch(var/branch_name, var/decl/species/S) + return (branch_name in spawn_branches(S)) + +/** + * Return a true value if rank_name is a valid spawn rank in branch under branch_name + */ +/datum/map/proc/is_spawn_rank(var/branch_name, var/rank_name, var/decl/species/S) + var/datum/mil_branch/branch = get_branch(branch_name) + if(branch && (rank_name in branch.spawn_ranks(S))) + return TRUE + else + return FALSE + // The white, and blacklist are type specific, any subtypes (of both species and jobs) have to be added explicitly /datum/map/proc/is_species_branch_restricted(var/decl/species/S, var/datum/mil_branch/MB) if(!istype(S) || !istype(MB)) diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index ff76e866b038..c09d5bc24880 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -254,6 +254,8 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable /datum/map/proc/setup_map() + populate_branches() + if(!length(loadout_categories)) loadout_categories = list() for(var/decl_type in decls_repository.get_decls_of_type(/decl/loadout_category)) From 90d27f2826b502dfbeb19dd24e7bf5dfc6b6d7ce Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 8 Oct 2025 22:15:48 -0400 Subject: [PATCH 0814/1331] Fix YML files losing line endings on checkout --- .editorconfig | 1 + .gitattributes | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 2643d9b63442..6b3864a466d1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,6 +19,7 @@ indent_size = 4 [*.yml] indent_style = space indent_size = 2 +end_of_line = crlf [*.txt] end_of_line = crlf diff --git a/.gitattributes b/.gitattributes index e9d52dc929b5..d5d501e85607 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,4 +13,5 @@ html/changelog.html merge=union *.dm text eol=crlf *.dmm text eol=crlf *.txt text eol=crlf -*.md text eol=crlf \ No newline at end of file +*.md text eol=crlf +*.yml text eol=crlf \ No newline at end of file From b71a631151e2ada02b4177a059b6dbd3953bd216 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Oct 2025 11:29:43 -0400 Subject: [PATCH 0815/1331] Fix null skillset runtimes on new_player --- code/datums/mind/mind.dm | 2 +- code/modules/client/preference_setup/occupation/occupation.dm | 2 +- code/modules/mob/skills/skillset.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 27a740428272..1ffcf5a68b56 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -225,7 +225,7 @@ assigned_job = job assigned_role = job.title role_alt_title = new_role - if(current) + if(current?.skillset) current.skillset.obtain_from_client(job, current.client) else if (href_list["amb_edit"]) diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 5613c8466500..feb41bc98058 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -185,7 +185,7 @@ skill_link = "View Skills" skill_link = "
  • " - if(!user.skillset.skills_transferable) + if(!user.skillset?.skills_transferable) skill_link = "" // Begin assembling the actual HTML. diff --git a/code/modules/mob/skills/skillset.dm b/code/modules/mob/skills/skillset.dm index 5e9928b3bf4a..eef151360637 100644 --- a/code/modules/mob/skills/skillset.dm +++ b/code/modules/mob/skills/skillset.dm @@ -38,7 +38,7 @@ var/global/list/all_skill_verbs . += SB.buffs[skill_path] /datum/skillset/proc/obtain_from_mob(mob/living/mob) - if(!istype(mob) || !skills_transferable || !mob.skillset.skills_transferable) + if(!istype(mob) || !skills_transferable || !mob.skillset?.skills_transferable) return skill_list = mob.skillset.skill_list default_value = mob.skillset.default_value From 6a326a4ad43ff13d60f6d0de51b588b32e23ebbc Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Oct 2025 16:21:21 -0400 Subject: [PATCH 0816/1331] Incorporate Neerti's revised modpack readme --- mods/README.md | 56 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/mods/README.md b/mods/README.md index 3ad3e5f818b8..c6b7a62b11f8 100644 --- a/mods/README.md +++ b/mods/README.md @@ -1,45 +1,56 @@ # Modpacks ## What exactly are modpacks? - Modpacks are, effectively, self-contained (modular) feature bundles. They can contain new objects, new species, new UIs, overrides for existing code, and even more. If you want a feature to be optional on a per-map or per-server basis, while ensuring it's always checked for correctness in unit tests, it should be placed in a modpack. -## Wait, but I've heard that modular code is bad! +## Are modpacks the same as 'module' folders in other codebases? +No. Those were more or less just subdirectories that organized features into conceptual 'modules', but lacked any sort of defined structure or handling, and generally couldn't be selectively enabled or disabled. Nebula's modpacks are more rigorous, with scripts and tests for validation. Each modpack is tested as a unit (along with the base game) as well as in integration with other modpacks on the various included maps. -This is a common sentiment, but it's sort of a misconception. There's nothing *inherently* wrong with modular code; after all, it's just code that'd designed for easy extension and reusability, which is always desirable in a project like SS13. However, the way other codebases tend to handle modularity by just adding new DM files in a separate folder, directly included in the DME, creates issues. Some of the common objections are: +## Making a new modpack +All of Nebula's modpacks are kept inside of the `/mods` subdirectory. -### Load Order +Code inside of a modpack must depend on only itself and stock Nebula code, and not from other modpacks. Cross-modpack code goes elsewhere and is discussed further below. -With the typical way "modular" code is done, load order is difficult to manage and often results in folder names like `upstreamname`, `downstreamname`, `zzzz_servername`, etc., plus it requires editing the main DME. Modpacks have a defined, user-controlled load order, and cross-modpack compatibility is always handled after all modpacks are loaded. +Do NOT tick files inside of your modpack, as that'll add it to the base Nebula .dme file. Instead, you must add them to your modpack's .dme file. -### Organization and Navigation +### Modpack .dme +Each modpack contains its own .dme file, as the modpack is more or less a mini codebase contained within the larger stock Nebula codebase. The .dme needs to be at the base of your modpack directory. +By convention, the .dme should share the same name as the modpack itself, with a leading underscore in front. +The first line of the .dme should be an `#ifndef`, and then a define for your modpack, e.g. `#ifndef MODPACK_YOUR_MODPACK_NAME_HERE`. This needs to be unique across the codebase. +The second line should be a `#define`, and then the same thing as before, e.g. `#define MODPACK_YOUR_MODPACK_NAME_HERE`. +Below that, you should `#include` all of the paths of the files which are inside. The paths are relative to where the .dme is. +At the bottom, after all of the `#define`s, you must add `#endif`. Make sure to add `// BEGIN_INCLUDE` and `// END_INCLUDE` after the `#define` and before the `#endif`. -Similarly, that file-structure makes it very difficult to organize and navigate the codebase as a whole. The original modularization standards were fairly straightforward, in that the modular folder was treated as a straight-forward layer on top of the original codebase, with files being treated as though they're present in the original codebase structure. a trend that ended up being set by other servers going for modularization was adding subdirectories of "modules", with almost every unique piece of content being given it's own individual module folder. The problem with this is that modules don't really have any defined structure or handling, and tend to just amount to subdirectories and nothing more. With every piece of content having its own module, even if they just override or overwrite base-game definitions and nothing more, this leads to the codebase being incredibly hard to navigate even if you already know roughly where what you want to modify is. This even applies to the base game, because some code is separated by type (obj, datum, etc.) and others are separated into conceptual modules. +### Modpack Decl +Every modpack has a `/decl/modpack` subtype. -Nebula's modpacks are similar to modules in that they're separated by concept, but modpacks are more rigorous, with scripts and tests for validation. Each modpack is tested as a unit (along with the base game) as well as in integration with other modpacks on the various included maps. +By convention, the decl is defined at the base of the modpack directory, with the .dm file sharing the same name as the modpack, with a leading underscore, the same as the .dme file. -### Proc Overrides +The decl also lets players to see which modpacks a server is currently running, and holds some miscellaneous information to use for things such as dreams and the round-end credits screen. -Modular code relies heavily on overrides and even side-overrides (overrides defined after, but on the same type as, an existing proc override or definition), which can obfuscate control flow and lead to code breaking when assumptions made about the calling proc change. This is a valid concern! In a lot of cases, though, it's avoidable by making modular behaviour use explicit interfaces intended for change in modpacks, like specifying datum (often decl) handlers that receive one override each. `/decl/human_examination` is a good example of this. +You can have code run during server initialization, or the round starting, by overriding `pre_initialize()`, `initialize()`, `post_initialize()`, and `on_roundstart()` on your modpack's decl. -### Merge Conflicts +If your modpack has NanoUI templates, you must set the `nanoui_directory` variable to point to the path of the folder where the templates are, or NanoUI won't be able to find them in-game. -The issue with "modularity" as done elsewhere is that it just masks conflicts; upstream files are kept intact, but they might be unincluded or the methods they define/call might be completely replaced later on. This swaps out annoying but easily-resolvable merge conflicts for the far-worse issue of silent breakage. +### Load Order +Modpacks have a defined, user-controlled load order, and cross-modpack compatibility is always handled after all modpacks are loaded. -### Upstream Conflicts +### Enabling Your Modpack +Modpacks are enabled on a per-map basis. To activate a modpack, you `#include` the modpack's .dme in a map's .dme file. -More broadly, modular code on a downstream with an upstream that does frequent refactors and rewrites is inevitably going to break when the upstream codebase does anything. Names change, so do assumptions, and even design directions might diverge so far that reconciling them will be hard or even impossible. There's not really getting around that, but we can at least mitigate it by designing stable interfaces and documenting changes. When upstream code is written with modularity in mind, downstreams have a much easier time adding content. +### Overriding Stock Code +TODO: Actually write this section. It's distinct from the "How do I write upstream/core code with extension via modpacks in mind?" part because it's the *opposite,* this section is about overriding core code while the other section is about writing core code to be extended. Maybe do a basic explanation of side-overrides and associated footguns here. -### Implicit Dependencies +### Cross-modpack interactions +Sometimes, a modpack that's enabled might need to do something in response to another modpack also being enabled. Compatibility patches allow for this to happen without the modpacks in question requiring a hard dependency on each other. -The idea of modular code as independent falls apart when code modules begin to require each other to function, without explicitly noting that dependency. Worse yet, sometimes there are non-modular edits to base-game code that require a particular piece of modular code to be included. Since Nebula runs unit tests on an example map with no modpacks and no map-specific code, the core game will always function without any modpacks included. Similarly, modpacks are tested one-at-a-time as well as all together, to ensure there aren't any implicit dependencies. If one modpack requires another to function, there's three methods to resolve this. First, you could have one modpack include the other, making the dependency explicit. Second, you could merge the modpacks so they aren't separate at all. Third, you could use a compatibility patch to gate the content that requires both to be loaded. +An example of such an interaction is that the Supermatter content modpack can give the SM monitoring program to engineering jobs that exist inside the Standard Jobs modpack. Another example is allowing content in the Psionics content modpack to interact with content in the Cult gamemode modpack. -## Okay, but how do I make a modpack? +Cross-modpack code is kept inside of `/mods/~compatibility`. There is another README inside which has more information. -TODO: Write this section. Explain things like creating a DME, where to include the DME, etc. Also explain some common organizational conventions like `foo_overrides.dm` files and `overrides` folders to better organize overrides of base-game procs and functionality. +Some modpacks extend other modpacks and make no sense to include on their own, in which case it's generally okay to make the extension modpack have a hard dependency on the original one by `#include`ing the original modpack's DME in the new one. ### How do I modpack... - - A subsystem? - Check the psionics modpack for an example. - Something small? @@ -47,10 +58,11 @@ TODO: Write this section. Explain things like creating a DME, where to include t - NanoUI templates? - Check the inertial dampener or supermatter modpacks as examples. (Don't forget to set the nanoui_directory variable on the modpack decl!) -## How do I write upstream/core code with extension via modpacks in mind? +## Using modpacks as a downstream server +Modular code on a downstream with an upstream that does frequent refactors and rewrites is inevitably going to break when the upstream codebase does anything. Names change, so do assumptions, and even design directions might diverge so far that reconciling them will be hard or even impossible. There's not really getting around that, but we can at least mitigate it by designing stable interfaces and documenting changes. When upstream code is written with modularity in mind, downstreams have a much easier time adding content. -TODO: Write this section. Give examples like `/decl/atmos_grief_fix_step`, `/decl/human_examination`, the cocktails system, etc. Iterating over subtypes of a base type makes it easy for modpacks to add new code. Also maybe address some footguns like trying to make something modular before trying to make it actually work? Could also discuss the open-closed principle I guess, e.g. write code that gets *extended* rather than *modified* (so avoiding side-overrides, etc.). +## How do I write upstream/core code with extension via modpacks in mind? +TODO: Actually write this section. Give examples like `/decl/atmos_grief_fix_step`, `/decl/human_examination`, the cocktails system, etc. Iterating over subtypes of a base type makes it easy for modpacks to add new code. Also maybe address some footguns like trying to make something modular before trying to make it actually work? Could also discuss the open-closed principle I guess, e.g. write code that gets *extended* rather than *modified* (so avoiding side-overrides where possible, etc.). # Contribution - Please contribute to this README/guide. It's currently unfinished and doesn't cover a lot of important things. Thanks. \ No newline at end of file From 376c482da16ba6fae3a468d3bfc118c6ca8a2f7b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:24:06 -0400 Subject: [PATCH 0817/1331] Move APC content into a power subfolder --- code/modules/power/{apc.dm => apc/_apc.dm} | 110 +++++------------- .../items => modules/power/apc}/apc_frame.dm | 6 +- code/modules/power/apc/apc_presets.dm | 47 ++++++++ .../apc.dm => modules/power/apc/apc_wires.dm} | 39 +++---- nebula.dme | 7 +- 5 files changed, 97 insertions(+), 112 deletions(-) rename code/modules/power/{apc.dm => apc/_apc.dm} (92%) rename code/{game/objects/items => modules/power/apc}/apc_frame.dm (74%) create mode 100644 code/modules/power/apc/apc_presets.dm rename code/{datums/wires/apc.dm => modules/power/apc/apc_wires.dm} (84%) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc/_apc.dm similarity index 92% rename from code/modules/power/apc.dm rename to code/modules/power/apc/_apc.dm index 0df866c15b71..4d59d0f23f74 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc/_apc.dm @@ -7,82 +7,8 @@ var/global/list/all_apcs = list() // Generates a terminal based on the direction of the APC on spawn // There are three different power channels, lighting, equipment, and environment -// Each may have one of the following states - // Power channels set to Auto change when power levels rise or drop below a threshold -// Power channel defines have been shifted to power.dm in __defines. 2/25/2021 - -#define AUTO_THRESHOLD_LIGHTING 50 -#define AUTO_THRESHOLD_EQUIPMENT 25 -// The ENVIRON channel stays on as long as possible, and doesn't have a threshold - -#define CRITICAL_APC_EMP_PROTECTION 10 // EMP effect duration is divided by this number if the APC has "critical" flag -#define APC_UPDATE_ICON_COOLDOWN 100 // Time between automatically updating the icon (10 seconds) - -// Used to check whether or not to update the icon_state -#define UPDATE_CELL_IN 1 -#define UPDATE_OPENED1 2 -#define UPDATE_OPENED2 4 -#define UPDATE_MAINT 8 -#define UPDATE_BROKE 16 -#define UPDATE_BLUESCREEN 32 -#define UPDATE_WIREEXP 64 -#define UPDATE_ALLGOOD 128 - -// Used to check whether or not to update the overlay -#define APC_UPOVERLAY_CHARGEING0 1 -#define APC_UPOVERLAY_CHARGEING1 2 -#define APC_UPOVERLAY_CHARGEING2 4 -#define APC_UPOVERLAY_LOCKED 8 -#define APC_UPOVERLAY_OPERATING 16 - -// Various APC types -/obj/machinery/power/apc/inactive - lighting = 0 - equipment = 0 - environ = 0 - locked = FALSE - -/obj/machinery/power/apc/critical - is_critical = 1 - -/obj/machinery/power/apc/high - uncreated_component_parts = list( - /obj/item/cell/high - ) - -/obj/machinery/power/apc/high/inactive - lighting = 0 - equipment = 0 - environ = 0 - locked = FALSE - -/obj/machinery/power/apc/super - uncreated_component_parts = list( - /obj/item/cell/super - ) - -/obj/machinery/power/apc/super/critical - is_critical = 1 -/obj/machinery/power/apc/hyper - uncreated_component_parts = list( - /obj/item/cell/hyper - ) - -/obj/machinery/power/apc/derelict - lighting = 0 - equipment = 0 - environ = 0 - locked = 0 - uncreated_component_parts = list( - /obj/item/cell/crap/empty - ) - -/obj/machinery/power/apc/derelict/full - uncreated_component_parts = list( - /obj/item/cell/crap - ) // Main APC code /obj/machinery/power/apc @@ -112,7 +38,7 @@ var/global/list/all_apcs = list() var/operating = 1 // Bool for main toggle. var/charging = 0 // Whether or not it's charging. 0 - not charging but not full, 1 - charging, 2 - full var/chargemode = 1 // Whether charging is toggled on or off. - var/aidisabled = 0 + var/aidisabled = FALSE var/lastused_light = 0 // Internal stuff for UI and bookkeeping; can read off values but don't modify. var/lastused_equip = 0 var/lastused_environ = 0 @@ -125,7 +51,22 @@ var/global/list/all_apcs = list() var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment var/longtermpower = 10 // Counter to smooth out power state changes; do not modify. wires = /datum/wires/apc + // Values used for update_state. + var/const/UPDATE_CELL_IN = BITFLAG(0) + var/const/UPDATE_OPENED1 = BITFLAG(1) + var/const/UPDATE_OPENED2 = BITFLAG(2) + var/const/UPDATE_MAINT = BITFLAG(3) + var/const/UPDATE_BROKE = BITFLAG(4) + var/const/UPDATE_BLUESCREEN = BITFLAG(5) + var/const/UPDATE_WIREEXP = BITFLAG(6) + var/const/UPDATE_ALLGOOD = BITFLAG(7) var/update_state = -1 + // Used to check whether or not to update the overlay + var/const/UPOVERLAY_CHARGEING0 = BITFLAG(0) + var/const/UPOVERLAY_CHARGEING1 = BITFLAG(1) + var/const/UPOVERLAY_CHARGEING2 = BITFLAG(2) + var/const/UPOVERLAY_LOCKED = BITFLAG(3) + var/const/UPOVERLAY_OPERATING = BITFLAG(4) var/update_overlay = -1 var/list/update_overlay_chan // Used to determine if there is a change in channels var/is_critical = 0 @@ -142,6 +83,12 @@ var/global/list/all_apcs = list() var/autoname = 1 var/cover_removed = FALSE // Cover is gone; can't close it anymore. var/locked = TRUE // This is the interface, not the hardware. + /// EMP effect duration is divided by this number if the APC has "critical" flag + var/const/CRITICAL_EMP_PROTECTION = 10 + // Thresholds for lights/equipment turning off based on cell charge level + var/const/AUTO_THRESHOLD_LIGHTING = 50 + var/const/AUTO_THRESHOLD_EQUIPMENT = 25 + // The ENVIRON channel stays on as long as possible, and doesn't have a threshold base_type = /obj/machinery/power/apc/buildable stat_immune = 0 @@ -382,18 +329,18 @@ var/global/list/all_apcs = list() update_state |= UPDATE_ALLGOOD if(operating) - update_overlay |= APC_UPOVERLAY_OPERATING + update_overlay |= UPOVERLAY_OPERATING if(update_state & UPDATE_ALLGOOD) if(locked) - update_overlay |= APC_UPOVERLAY_LOCKED + update_overlay |= UPOVERLAY_LOCKED if(!charging) - update_overlay |= APC_UPOVERLAY_CHARGEING0 + update_overlay |= UPOVERLAY_CHARGEING0 else if(charging == 1) - update_overlay |= APC_UPOVERLAY_CHARGEING1 + update_overlay |= UPOVERLAY_CHARGEING1 else if(charging == 2) - update_overlay |= APC_UPOVERLAY_CHARGEING2 + update_overlay |= UPOVERLAY_CHARGEING2 update_overlay_chan["Equipment"] = equipment @@ -862,7 +809,7 @@ var/global/list/all_apcs = list() if(is_critical) // Critical APCs are considered EMP shielded and will be offline only for about half minute. Prevents AIs being one-shot disabled by EMP strike. // Critical APCs are also more resilient to cell corruption/power drain. - energy_fail(rand(240, 360) / severity / CRITICAL_APC_EMP_PROTECTION) + energy_fail(rand(240, 360) / severity / CRITICAL_EMP_PROTECTION) else // Regular APCs fail for normal time. energy_fail(rand(240, 360) / severity) @@ -935,4 +882,3 @@ var/global/list/all_apcs = list() if(area && !(processing_flags & MACHINERY_PROCESS_SELF)) START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) -#undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/game/objects/items/apc_frame.dm b/code/modules/power/apc/apc_frame.dm similarity index 74% rename from code/game/objects/items/apc_frame.dm rename to code/modules/power/apc/apc_frame.dm index 7275277cb13c..708f3dbadf3e 100644 --- a/code/game/objects/items/apc_frame.dm +++ b/code/modules/power/apc/apc_frame.dm @@ -12,14 +12,14 @@ /obj/item/frame/apc/try_build(turf/on_wall) var/area/A = get_area(src) if (A.requires_power == 0 || istype(A, /area/space)) - to_chat(usr, "APC cannot be placed in this area.") + to_chat(usr, SPAN_WARNING("An APC cannot be placed in this area.")) return if (A.get_apc()) - to_chat(usr, "This area already has an APC.") + to_chat(usr, SPAN_WARNING("This area already has an APC.")) return //only one APC per area for(var/obj/machinery/power/terminal/T in loc) if (T.master) - to_chat(usr, "There is another network terminal here.") + to_chat(usr, SPAN_WARNING("There is another network terminal here.")) return return ..() diff --git a/code/modules/power/apc/apc_presets.dm b/code/modules/power/apc/apc_presets.dm new file mode 100644 index 000000000000..a9ef717b7bca --- /dev/null +++ b/code/modules/power/apc/apc_presets.dm @@ -0,0 +1,47 @@ +// Various APC types +/obj/machinery/power/apc/inactive + lighting = 0 + equipment = 0 + environ = 0 + locked = FALSE + +/obj/machinery/power/apc/critical + is_critical = 1 + +/obj/machinery/power/apc/high + uncreated_component_parts = list( + /obj/item/cell/high + ) + +/obj/machinery/power/apc/high/inactive + lighting = 0 + equipment = 0 + environ = 0 + locked = FALSE + +/obj/machinery/power/apc/super + uncreated_component_parts = list( + /obj/item/cell/super + ) + +/obj/machinery/power/apc/super/critical + is_critical = 1 + +/obj/machinery/power/apc/hyper + uncreated_component_parts = list( + /obj/item/cell/hyper + ) + +/obj/machinery/power/apc/derelict + lighting = 0 + equipment = 0 + environ = 0 + locked = 0 + uncreated_component_parts = list( + /obj/item/cell/crap/empty + ) + +/obj/machinery/power/apc/derelict/full + uncreated_component_parts = list( + /obj/item/cell/crap + ) \ No newline at end of file diff --git a/code/datums/wires/apc.dm b/code/modules/power/apc/apc_wires.dm similarity index 84% rename from code/datums/wires/apc.dm rename to code/modules/power/apc/apc_wires.dm index b7d1221c081d..25d655eb5300 100644 --- a/code/datums/wires/apc.dm +++ b/code/modules/power/apc/apc_wires.dm @@ -1,8 +1,3 @@ -#define APC_WIRE_IDSCAN 1 -#define APC_WIRE_MAIN_POWER1 2 -#define APC_WIRE_MAIN_POWER2 4 -#define APC_WIRE_AI_CONTROL 8 - /datum/wires/apc holder_type = /obj/machinery/power/apc wire_count = 4 @@ -12,18 +7,21 @@ new /datum/wire_description(APC_WIRE_MAIN_POWER2, "This wire seems to be carrying a heavy current."), new /datum/wire_description(APC_WIRE_AI_CONTROL, "This wire connects to automated control systems.") ) + var/const/APC_WIRE_IDSCAN = BITFLAG(0) + var/const/APC_WIRE_MAIN_POWER1 = BITFLAG(1) + var/const/APC_WIRE_MAIN_POWER2 = BITFLAG(2) + var/const/APC_WIRE_AI_CONTROL = BITFLAG(3) /datum/wires/apc/GetInteractWindow(mob/user) var/obj/machinery/power/apc/A = holder . += ..() . += text("
    \n[(A.locked ? "The APC is locked." : "The APC is unlocked.")]
    \n[(A.shorted ? "The APCs power has been shorted." : "The APC is working properly!")]
    \n[(A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]") - /datum/wires/apc/CanUse(var/mob/living/L) var/obj/machinery/power/apc/A = holder if(istype(A.construct_state, /decl/machine_construction/wall_frame/panel_closed/hackable/hacking) && !(A.stat & BROKEN)) - return 1 - return 0 + return TRUE + return FALSE /datum/wires/apc/proc/reset_locked() var/obj/machinery/power/apc/A = holder @@ -38,7 +36,7 @@ /datum/wires/apc/proc/reset_ai_disabled() var/obj/machinery/power/apc/A = holder if(A && !IsIndexCut(APC_WIRE_AI_CONTROL)) - A.aidisabled = 0 + A.aidisabled = FALSE /datum/wires/apc/UpdatePulsed(var/index) @@ -47,17 +45,17 @@ switch(index) if(APC_WIRE_IDSCAN) - A.locked = 0 + A.locked = FALSE addtimer(CALLBACK(src, PROC_REF(reset_locked)), 30 SECONDS) if (APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) - if(A.shorted == 0) - A.shorted = 1 + if(!A.shorted) + A.shorted = TRUE addtimer(CALLBACK(src, PROC_REF(reset_shorted)), 2 MINUTES) if (APC_WIRE_AI_CONTROL) - if (A.aidisabled == 0) - A.aidisabled = 1 + if (!A.aidisabled) + A.aidisabled = TRUE addtimer(CALLBACK(src, PROC_REF(reset_ai_disabled)), 1 SECOND) /datum/wires/apc/UpdateCut(var/index, var/mended) @@ -65,20 +63,13 @@ switch(index) if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) - if(!mended) A.shock(usr, 50) - A.shorted = 1 + A.shorted = TRUE else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) - A.shorted = 0 + A.shorted = FALSE A.shock(usr, 50) if(APC_WIRE_AI_CONTROL) - - if(!mended) - if (A.aidisabled == 0) - A.aidisabled = 1 - else - if (A.aidisabled == 1) - A.aidisabled = 0 \ No newline at end of file + A.aidisabled = !mended \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index cfc424bd5655..2583aa940a34 100644 --- a/nebula.dme +++ b/nebula.dme @@ -729,7 +729,6 @@ #include "code\datums\vote\vote.dm" #include "code\datums\wires\airlock.dm" #include "code\datums\wires\alarm.dm" -#include "code\datums\wires\apc.dm" #include "code\datums\wires\camera.dm" #include "code\datums\wires\explosive.dm" #include "code\datums\wires\fabricator.dm" @@ -1070,7 +1069,6 @@ #include "code\game\objects\items\_item_melting.dm" #include "code\game\objects\items\_item_reagents.dm" #include "code\game\objects\items\_item_sharpness.dm" -#include "code\game\objects\items\apc_frame.dm" #include "code\game\objects\items\blackout.dm" #include "code\game\objects\items\blueprints.dm" #include "code\game\objects\items\bodybag.dm" @@ -3403,7 +3401,6 @@ #include "code\modules\posture\posture_mob.dm" #include "code\modules\posture\posture_subtypes.dm" #include "code\modules\power\admin_setup_engine.dm" -#include "code\modules\power\apc.dm" #include "code\modules\power\batteryrack.dm" #include "code\modules\power\breaker_box.dm" #include "code\modules\power\cable.dm" @@ -3425,6 +3422,10 @@ #include "code\modules\power\terminal.dm" #include "code\modules\power\tracker.dm" #include "code\modules\power\turbine.dm" +#include "code\modules\power\apc\_apc.dm" +#include "code\modules\power\apc\apc_frame.dm" +#include "code\modules\power\apc\apc_presets.dm" +#include "code\modules\power\apc\apc_wires.dm" #include "code\modules\power\fission\core.dm" #include "code\modules\power\fission\core_control.dm" #include "code\modules\power\fission\fission_circuits.dm" From 1756c5e3127b1cc21bc570a3adae88fad7234ac0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Oct 2025 20:16:06 -0400 Subject: [PATCH 0818/1331] Make APCs not a subtype of /obj/machinery/power --- code/_helpers/unsorted.dm | 2 +- code/_onclick/ai.dm | 2 +- code/_onclick/cyborg.dm | 2 +- .../subsystems/initialization/codex_dump.dm | 2 +- code/game/area/areas.dm | 6 +- code/game/gamemodes/events/power_failure.dm | 4 +- .../machinery/_machines_base/machinery.dm | 2 +- code/game/objects/alien_props.dm | 4 +- code/game/objects/items/blackout.dm | 4 +- code/game/objects/items/circuitboards/wall.dm | 2 +- code/game/objects/items/devices/powersink.dm | 2 +- .../secrets/fun_secrets/break_all_lights.dm | 2 +- .../secrets/fun_secrets/break_some_lights.dm | 2 +- code/modules/admin/verbs/debug.dm | 2 +- code/modules/codex/entries/engineering.dm | 2 +- code/modules/events/apc_damage.dm | 8 +- code/modules/events/electrical_storm.dm | 4 +- code/modules/events/meteors.dm | 2 +- code/modules/events/prison_break.dm | 2 +- code/modules/mob/living/silicon/ai/ai.dm | 2 +- code/modules/mob/living/silicon/ai/power.dm | 4 +- .../living/silicon/robot/drone/drone_items.dm | 4 +- .../programs/engineering/power_monitor.dm | 4 +- code/modules/overmap/ftl_shunt/core.dm | 6 +- code/modules/power/apc/_apc.dm | 88 +++-- code/modules/power/apc/apc_frame.dm | 4 +- code/modules/power/apc/apc_presets.dm | 18 +- code/modules/power/apc/apc_wires.dm | 16 +- code/modules/power/cable.dm | 4 +- code/modules/power/power.dm | 4 +- code/modules/power/powernet.dm | 2 +- code/modules/power/sensors/powernet_sensor.dm | 4 +- code/modules/power/smes_construction.dm | 6 +- .../artifacts/effects/cellcharge.dm | 2 +- .../artifacts/effects/celldrain.dm | 2 +- code/unit_tests/del_the_world.dm | 2 +- code/unit_tests/power_tests.dm | 4 +- maps/away/bearcat/bearcat-1.dmm | 26 +- maps/away/bearcat/bearcat-2.dmm | 48 +-- maps/away/casino/casino.dmm | 36 +- maps/away/derelict/derelict-station.dmm | 8 +- maps/away/errant_pisces/errant_pisces.dmm | 50 +-- maps/away/liberia/liberia.dmm | 46 +-- maps/away/liberia/liberia_machinery.dm | 2 +- .../lost_supply_base/lost_supply_base.dmm | 10 +- maps/away/magshield/magshield.dmm | 12 +- maps/away/mining/mining-signal.dmm | 2 +- maps/away/smugglers/smugglers.dmm | 6 +- maps/away/unishi/unishi-1.dmm | 4 +- maps/away/unishi/unishi-2.dmm | 20 +- maps/away/unishi/unishi-3.dmm | 10 +- maps/away/yacht/yacht.dmm | 6 +- maps/example/example-1.dmm | 2 +- maps/example/example-2.dmm | 2 +- maps/example/example-3.dmm | 2 +- maps/exodus/exodus-1.dmm | 20 +- maps/exodus/exodus-2.dmm | 328 +++++++++--------- maps/ministation/ministation-0.dmm | 38 +- maps/ministation/ministation-1.dmm | 26 +- maps/ministation/ministation-2.dmm | 30 +- maps/ministation/ministation-3.dmm | 2 +- .../crashed_pod/crashed_pod.dmm | 2 +- .../exoplanet_ruins/hydrobase/hydrobase.dmm | 24 +- .../exoplanet_ruins/playablecolony/colony.dmm | 32 +- maps/tradeship/tradeship-0.dmm | 14 +- maps/tradeship/tradeship-1.dmm | 26 +- maps/tradeship/tradeship-2.dmm | 56 +-- maps/tradeship/tradeship-3.dmm | 8 +- .../away_sites/lar_maria/lar_maria-1.dmm | 16 +- .../away_sites/lar_maria/lar_maria-2.dmm | 18 +- .../generic_shuttles/tanker/tanker.dmm | 2 +- .../ruins/ec_old_crash/ec_old_crash.dmm | 8 +- .../supermatter/endgame_cascade/universe.dm | 2 +- .../supermatter/machinery/supermatter.dm | 2 +- .../mercenary/maps/mercenary_base.dmm | 6 +- mods/species/ascent/machines/ship_machines.dm | 2 +- tools/map_migrations/5151_power_apc.txt | 2 + 77 files changed, 591 insertions(+), 597 deletions(-) create mode 100644 tools/map_migrations/5151_power_apc.txt diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 1c81acd2e27f..6b3c62d1249e 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -715,7 +715,7 @@ Turf and target are seperate in case you want to teleport some distance from a t Checks if that loc and dir has a item on the wall */ var/global/list/WALLITEMS = list( - /obj/machinery/power/apc, /obj/machinery/alarm, /obj/item/radio/intercom, + /obj/machinery/apc, /obj/machinery/alarm, /obj/item/radio/intercom, /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, /obj/machinery/status_display, /obj/machinery/network/requests_console, /obj/machinery/light_switch, /obj/structure/sign, /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 36fc54d2f3fc..b0ab2d79e09a 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -158,7 +158,7 @@ Topic(src, list("command"="bolts", "activate" = "1")) return TRUE -/obj/machinery/power/apc/AICtrlClick(mob/living/silicon/user) // turns off/on APCs. +/obj/machinery/apc/AICtrlClick(mob/living/silicon/user) // turns off/on APCs. if(user.incapacitated()) return FALSE Topic(src, list("breaker"="1")) diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index ee1eba20b7cf..b9100b54ba30 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -121,7 +121,7 @@ /obj/machinery/door/airlock/BorgCtrlClick(mob/living/silicon/robot/user) // Bolts doors. Forwards to AI code. return AICtrlClick(user) -/obj/machinery/power/apc/BorgCtrlClick(mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. +/obj/machinery/apc/BorgCtrlClick(mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. return AICtrlClick(user) /obj/machinery/turretid/BorgCtrlClick(mob/living/silicon/robot/user) //turret control on/off. Forwards to AI code. diff --git a/code/controllers/subsystems/initialization/codex_dump.dm b/code/controllers/subsystems/initialization/codex_dump.dm index b12dd686d78b..ee93934ea510 100644 --- a/code/controllers/subsystems/initialization/codex_dump.dm +++ b/code/controllers/subsystems/initialization/codex_dump.dm @@ -129,7 +129,7 @@ TODO: work out how to implement an external search function. // Copied from del_the_world UT exceptions list. var/static/list/skip_types = typesof( /obj/item/organ/external/chest, - /obj/machinery/power/apc, + /obj/machinery/apc, /obj/machinery/alarm, /obj/structure/stairs ) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index dcc3343b8874..838532499857 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -46,7 +46,7 @@ var/global/list/areas = list() var/has_gravity = TRUE var/air_doors_activated = FALSE - var/obj/machinery/power/apc/apc + var/obj/machinery/apc/apc var/list/all_doors //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area var/list/ambience = list( 'sound/ambience/ambigen1.ogg', @@ -459,9 +459,9 @@ var/global/list/mob/living/forced_ambiance_list = new M.throw_at(T, maxrange, speed) /area/proc/prison_break() - var/obj/machinery/power/apc/theAPC = get_apc() + var/obj/machinery/apc/theAPC = get_apc() if(theAPC && theAPC.operating) - for(var/obj/machinery/power/apc/temp_apc in src) + for(var/obj/machinery/apc/temp_apc in src) temp_apc.overload_lighting(70) for(var/obj/machinery/door/airlock/temp_airlock in src) temp_airlock.prison_open() diff --git a/code/game/gamemodes/events/power_failure.dm b/code/game/gamemodes/events/power_failure.dm index 49789e11e9af..0d9165d972fa 100644 --- a/code/game/gamemodes/events/power_failure.dm +++ b/code/game/gamemodes/events/power_failure.dm @@ -7,14 +7,14 @@ S.energy_fail(rand(15 * severity,30 * severity)) - for(var/obj/machinery/power/apc/C in SSmachines.machinery) + for(var/obj/machinery/apc/C in SSmachines.machinery) if(!C.is_critical && (!affected_z_levels || (C.z in affected_z_levels))) C.energy_fail(rand(30 * severity,60 * severity)) /proc/power_restore(var/announce = 1) if(announce) global.using_map.grid_restored_announcement() - for(var/obj/machinery/power/apc/C in SSmachines.machinery) + for(var/obj/machinery/apc/C in SSmachines.machinery) C.failure_timer = 0 var/obj/item/cell/cell = C.get_cell() if(cell) diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index 5f03a9d4ba72..0a6de242520c 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -379,7 +379,7 @@ Class Procs: if(electrocute_mob(user, get_area(src), src, 0.7)) var/area/temp_area = get_area(src) if(temp_area) - var/obj/machinery/power/apc/temp_apc = temp_area.get_apc() + var/obj/machinery/apc/temp_apc = temp_area.get_apc() var/obj/machinery/power/terminal/terminal = temp_apc && temp_apc.terminal() if(terminal && terminal.powernet) diff --git a/code/game/objects/alien_props.dm b/code/game/objects/alien_props.dm index 6297ef140f0b..5268d3e3a383 100644 --- a/code/game/objects/alien_props.dm +++ b/code/game/objects/alien_props.dm @@ -19,7 +19,7 @@ // APC #define APC_UPDATE_ALLGOOD 128 -/obj/machinery/power/apc/alien +/obj/machinery/apc/alien name = "alien device" desc = "It's affixed to the floor, with a thick wire going into it." icon = 'icons/obj/xenoarchaeology.dmi' @@ -30,7 +30,7 @@ /obj/item/cell/alien ) -/obj/machinery/power/apc/alien/on_update_icon() +/obj/machinery/apc/alien/on_update_icon() check_updates() if(update_state & APC_UPDATE_ALLGOOD) icon_state = "ano11" diff --git a/code/game/objects/items/blackout.dm b/code/game/objects/items/blackout.dm index 4bc3d51b6f41..68df55bfa2a9 100644 --- a/code/game/objects/items/blackout.dm +++ b/code/game/objects/items/blackout.dm @@ -74,8 +74,8 @@ var/datum/powernet/powernet = terminal_in.powernet for(var/obj/machinery/power/terminal/terminal_out in powernet.nodes) - if(istype(terminal_out.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = terminal_out.master + if(istype(terminal_out.master, /obj/machinery/apc)) + var/obj/machinery/apc/A = terminal_out.master A.energy_fail(rand(30 * severity, 60 * severity)) if(istype(terminal_out.master, /obj/machinery/power/smes/buildable)) var/obj/machinery/power/smes/buildable/S = terminal_out.master diff --git a/code/game/objects/items/circuitboards/wall.dm b/code/game/objects/items/circuitboards/wall.dm index fe150c173d20..fe1e20a8cfcd 100644 --- a/code/game/objects/items/circuitboards/wall.dm +++ b/code/game/objects/items/circuitboards/wall.dm @@ -29,7 +29,7 @@ matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) w_class = ITEM_SIZE_SMALL obj_flags = OBJ_FLAG_CONDUCTIBLE - build_path = /obj/machinery/power/apc/buildable + build_path = /obj/machinery/apc/buildable board_type = "wall" origin_tech = @'{"programming":1,"engineering":1}' req_components = list() diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 104617710a6e..9af027725ba4 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -139,7 +139,7 @@ // Enough power drained this tick, no need to torture more APCs if(drained >= drain_rate) break - var/obj/machinery/power/apc/A = T.master_machine() + var/obj/machinery/apc/A = T.master_machine() if(istype(A)) drained += A.drain_power(amount = drain_rate) power_drained += drained diff --git a/code/modules/admin/secrets/fun_secrets/break_all_lights.dm b/code/modules/admin/secrets/fun_secrets/break_all_lights.dm index 114a6ae96dff..51a5131e92d7 100644 --- a/code/modules/admin/secrets/fun_secrets/break_all_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/break_all_lights.dm @@ -4,6 +4,6 @@ /datum/admin_secret_item/fun_secret/break_all_lights/execute(var/mob/user) . = ..() if(.) - for(var/obj/machinery/power/apc/apc in SSmachines.machinery) + for(var/obj/machinery/apc/apc in SSmachines.machinery) apc.overload_lighting() CHECK_TICK diff --git a/code/modules/admin/secrets/fun_secrets/break_some_lights.dm b/code/modules/admin/secrets/fun_secrets/break_some_lights.dm index f3784b1c16d2..bfa45de4cb5d 100644 --- a/code/modules/admin/secrets/fun_secrets/break_some_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/break_some_lights.dm @@ -23,6 +23,6 @@ return for(var/obj/abstract/landmark/epicentre in epicentreList) - for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange)) + for(var/obj/machinery/apc/apc in range(epicentre,lightsoutRange)) apc.overload_lighting() CHECK_TICK diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index c22ef2c4ab04..22e04658b19a 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -188,7 +188,7 @@ if(!(A.type in areas_all)) areas_all.Add(A.type) - for(var/obj/machinery/power/apc/APC in SSmachines.machinery) + for(var/obj/machinery/apc/APC in SSmachines.machinery) var/area/A = get_area(APC) if(!(A.type in areas_with_APC)) areas_with_APC.Add(A.type) diff --git a/code/modules/codex/entries/engineering.dm b/code/modules/codex/entries/engineering.dm index f5e9ac99fa3d..c635697b7af4 100644 --- a/code/modules/codex/entries/engineering.dm +++ b/code/modules/codex/entries/engineering.dm @@ -1,5 +1,5 @@ /datum/codex_entry/apc - associated_paths = list(/obj/machinery/power/apc) + associated_paths = list(/obj/machinery/apc) mechanics_text = "An APC (Area Power Controller) regulates and supplies backup power for the area they are in. Their power channels are divided \ out into 'environmental' (items that manipulate airflow and temperature), 'lighting' (the lights), and 'equipment' (everything else that consumes power). \ Power consumption and backup power cell charge can be seen from the interface, further controls (turning a specific channel on, off or automatic, \ diff --git a/code/modules/events/apc_damage.dm b/code/modules/events/apc_damage.dm index 4855cc37b22d..137847f0313c 100644 --- a/code/modules/events/apc_damage.dm +++ b/code/modules/events/apc_damage.dm @@ -2,7 +2,7 @@ var/apcSelectionRange = 25 /datum/event/apc_damage/start() - var/obj/machinery/power/apc/A = acquire_random_apc() + var/obj/machinery/apc/A = acquire_random_apc() var/severity_range = 0 switch(severity) @@ -13,7 +13,7 @@ if(EVENT_LEVEL_MAJOR) severity_range = 15 - for(var/obj/machinery/power/apc/apc in range(severity_range,A)) + for(var/obj/machinery/apc/apc in range(severity_range,A)) if(is_valid_apc(apc)) apc.emagged = 1 apc.update_icon() @@ -30,7 +30,7 @@ return var/epicentre = pick(possibleEpicentres) - for(var/obj/machinery/power/apc/apc in range(epicentre,apcSelectionRange)) + for(var/obj/machinery/apc/apc in range(epicentre,apcSelectionRange)) if(is_valid_apc(apc)) apcs += apc // Greatly increase the chance for APCs in maintenance areas to be selected @@ -44,6 +44,6 @@ return pick(apcs) -/datum/event/apc_damage/proc/is_valid_apc(var/obj/machinery/power/apc/apc) +/datum/event/apc_damage/proc/is_valid_apc(var/obj/machinery/apc/apc) var/turf/T = get_turf(apc) return !apc.is_critical && !apc.emagged && T && isPlayerLevel(T.z) diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index ebeef2b276ac..5400a5e174cf 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -83,7 +83,7 @@ shielded = TRUE valid_apcs = list() - for(var/obj/machinery/power/apc/A as anything in global.all_apcs) + for(var/obj/machinery/apc/A as anything in global.all_apcs) if(!A.is_critical && (A.z in affecting_z)) valid_apcs.Add(A) @@ -94,7 +94,7 @@ for(var/i=0, i< severity*2, i++) // up to 2/4/6 APCs per tick depending on severity picked_apcs |= pick(valid_apcs) - for(var/obj/machinery/power/apc/T as anything in picked_apcs) + for(var/obj/machinery/apc/T as anything in picked_apcs) // Main breaker is turned off. Consider this APC protected. if(!T.operating || T.failure_timer) continue diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm index 5a4d60f7f628..03616164955f 100644 --- a/code/modules/events/meteors.dm +++ b/code/modules/events/meteors.dm @@ -422,7 +422,7 @@ var/global/list/meteors_major = list( /obj/effect/meteor/destroyer/meteor_effect() ..() explosion(src.loc, 1, 2, 3, 4, 0) - for(var/obj/machinery/power/apc/A in range(rand(12, 20), src)) + for(var/obj/machinery/apc/A in range(rand(12, 20), src)) A.energy_fail(round(10 * rand(8, 12))) /obj/effect/meteor/destroyer/get_shield_damage() diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index da7666b68cee..2d7d84aa6b7f 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -58,7 +58,7 @@ /datum/event/prison_break/tick() if(activeFor == releaseWhen && length(areas)) - var/obj/machinery/power/apc/theAPC = null + var/obj/machinery/apc/theAPC = null for(var/area/A in areas) theAPC = A.get_apc() if(theAPC && theAPC.operating) //If the apc's off, it's a little hard to overload the lights. diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index b487dfc897c4..e72e3798f8cc 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -82,7 +82,7 @@ var/global/list/ai_verbs_default = list( //NEWMALF VARIABLES var/malfunctioning = 0 // Master var that determines if AI is malfunctioning. - var/obj/machinery/power/apc/hack = null // APC that is currently being hacked. + var/obj/machinery/apc/hack = null // APC that is currently being hacked. var/list/hacked_apcs = null // List of all hacked APCs var/uncardable = 0 // Whether the AI can be carded when malfunctioning. var/hacked_apcs_hidden = 0 // Whether the hacked APCs belonging to this AI are hidden, reduces CPU generation from APCs. diff --git a/code/modules/mob/living/silicon/ai/power.dm b/code/modules/mob/living/silicon/ai/power.dm index 1785b37ecb5c..72c7e11cdff6 100644 --- a/code/modules/mob/living/silicon/ai/power.dm +++ b/code/modules/mob/living/silicon/ai/power.dm @@ -8,7 +8,7 @@ return to_chat(src, "Main power lost. System switched to internal capacitor. Beginning diagnostics.") - var/obj/machinery/power/apc/theAPC = null + var/obj/machinery/apc/theAPC = null var/connection_failures = 0 while(aiRestorePowerRoutine) // If the routine is running, proceed to another step. @@ -98,7 +98,7 @@ return 0 // Resets passed APC so the AI may function again. -/mob/living/silicon/ai/proc/reset_apc(var/obj/machinery/power/apc/A) +/mob/living/silicon/ai/proc/reset_apc(var/obj/machinery/apc/A) if(!istype(A)) return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index b56da846f44a..969dd3dac640 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -223,8 +223,8 @@ else to_chat(user, "Your gripper cannot hold \the [target].") - else if(istype(target,/obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = target + else if(istype(target,/obj/machinery/apc)) + var/obj/machinery/apc/A = target if(A.components_are_accessible(/obj/item/stock_parts/power/battery)) var/obj/item/stock_parts/power/battery/bat = A.get_component_of_type(/obj/item/stock_parts/power/battery) var/obj/item/cell/cell = bat.extract_cell(src) diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index 3b5321fdf4ba..15cddd991fc8 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -121,7 +121,7 @@ . = 1 if(href_list["toggle_breaker"]) - var/obj/machinery/power/apc/A = locate(href_list["toggle_breaker"]) + var/obj/machinery/apc/A = locate(href_list["toggle_breaker"]) if(!CanInteract(user, state) || QDELETED(A)) return 0 @@ -129,7 +129,7 @@ A.toggle_breaker() if(href_list["toggle_powerchannel_equip"] || href_list["toggle_powerchannel_light"] || href_list["toggle_powerchannel_enviro"]) //I'm sure there's a better way to do this. - var/obj/machinery/power/apc/A + var/obj/machinery/apc/A var/powerchannel = 0 var/power_setting diff --git a/code/modules/overmap/ftl_shunt/core.dm b/code/modules/overmap/ftl_shunt/core.dm index ca95cf968b9b..a74d6a20a280 100644 --- a/code/modules/overmap/ftl_shunt/core.dm +++ b/code/modules/overmap/ftl_shunt/core.dm @@ -356,7 +356,7 @@ if(prob(35)) L.flicker() - for(var/obj/machinery/power/apc/A in SSmachines.machinery) + for(var/obj/machinery/apc/A in SSmachines.machinery) if(!(A.z in ftl_computer.linked.map_z)) continue switch(shunt_sev) @@ -402,7 +402,7 @@ explosion(get_turf(src),-1,-1,8,10) //Effect Two: blow the windows out. - for(var/obj/machinery/power/apc/A in SSmachines.machinery) //Effect Three: shut down power across the ship. + for(var/obj/machinery/apc/A in SSmachines.machinery) //Effect Three: shut down power across the ship. if(!(A.z in ftl_computer.linked.map_z)) continue A.energy_fail(rand(60,80)) @@ -410,7 +410,7 @@ if(SHUNT_SABOTAGE_CRITICAL) announcetxt = shunt_sabotage_text_critical - for(var/obj/machinery/power/apc/A in SSmachines.machinery) //Effect One: shut down power across the ship. + for(var/obj/machinery/apc/A in SSmachines.machinery) //Effect One: shut down power across the ship. if(!(A.z in ftl_computer.linked.map_z)) continue A.energy_fail(rand(100,120)) diff --git a/code/modules/power/apc/_apc.dm b/code/modules/power/apc/_apc.dm index 4d59d0f23f74..0339f9ce702d 100644 --- a/code/modules/power/apc/_apc.dm +++ b/code/modules/power/apc/_apc.dm @@ -11,7 +11,7 @@ var/global/list/all_apcs = list() // Main APC code -/obj/machinery/power/apc +/obj/machinery/apc name = "area power controller" desc = "A control terminal for the area electrical systems." @@ -46,7 +46,6 @@ var/global/list/all_apcs = list() var/lastused_total = 0 var/main_status = 0 // UI var for whether we are getting external power. 0 = no external power at all, 1 = some, but not enough, 2 = getting enough. var/mob/living/silicon/ai/hacker = null // Malfunction var. If set AI hacked the APC and has full control. - powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment var/longtermpower = 10 // Counter to smooth out power state changes; do not modify. @@ -90,7 +89,7 @@ var/global/list/all_apcs = list() var/const/AUTO_THRESHOLD_EQUIPMENT = 25 // The ENVIRON channel stays on as long as possible, and doesn't have a threshold - base_type = /obj/machinery/power/apc/buildable + base_type = /obj/machinery/apc/buildable stat_immune = 0 frame_type = /obj/item/frame/apc construct_state = /decl/machine_construction/wall_frame/panel_closed/hackable @@ -99,17 +98,10 @@ var/global/list/all_apcs = list() ) stock_part_presets = list(/decl/stock_part_preset/terminal_setup) -/obj/machinery/power/apc/buildable +/obj/machinery/apc/buildable uncreated_component_parts = null -/obj/machinery/power/apc/connect_to_network() - //Override because the APC does not directly connect to the network; it goes through a terminal. - //The terminal is what the power computer looks for anyway. - var/obj/machinery/power/terminal/terminal = terminal() - if(terminal) - terminal.connect_to_network() - -/obj/machinery/power/apc/drain_power(var/drain_check, var/surge, var/amount = 0) +/obj/machinery/apc/drain_power(var/drain_check, var/surge, var/amount = 0) if(drain_check) return 1 @@ -123,7 +115,7 @@ var/global/list/all_apcs = list() return amount - use_power_oneoff(amount, LOCAL) -/obj/machinery/power/apc/Initialize(mapload, var/ndir, var/populate_parts = TRUE) +/obj/machinery/apc/Initialize(mapload, var/ndir, var/populate_parts = TRUE) global.all_apcs += src if(areastring) reset_area(null, get_area_by_name(strip_improper(areastring))) @@ -144,7 +136,7 @@ var/global/list/all_apcs = list() force_update_channels() power_change() -/obj/machinery/power/apc/Destroy() +/obj/machinery/apc/Destroy() if(area) reset_area(area, null) global.all_apcs -= src @@ -156,7 +148,7 @@ var/global/list/all_apcs = list() return ..() // Attempts to set the area and update all refs. Calling this outside of Initialize is experimental at best. -/obj/machinery/power/apc/proc/reset_area(area/old_area, area/new_area) +/obj/machinery/apc/proc/reset_area(area/old_area, area/new_area) if(new_area == old_area) return if(old_area && old_area == area) @@ -176,24 +168,24 @@ var/global/list/all_apcs = list() change_area_name(new_area, null, new_area.name) events_repository.register(/decl/observ/name_set, new_area, src, PROC_REF(change_area_name)) -/obj/machinery/power/apc/get_req_access() +/obj/machinery/apc/get_req_access() if(!locked) return list() return ..() -/obj/machinery/power/apc/proc/energy_fail(var/duration) +/obj/machinery/apc/proc/energy_fail(var/duration) if(emp_hardened) return if(!failure_timer && duration) playsound(src, 'sound/machines/apc_nopower.ogg', 75, 0) failure_timer = max(failure_timer, round(duration)) -/obj/machinery/power/apc/proc/terminal(var/functional_only) +/obj/machinery/apc/proc/terminal(var/functional_only) var/obj/item/stock_parts/power/terminal/term = get_component_of_type(/obj/item/stock_parts/power/terminal) if(term && (!functional_only || term.is_functional())) return term.terminal -/obj/machinery/power/apc/get_examine_strings(mob/user, distance, infix, suffix) +/obj/machinery/apc/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) if(stat & BROKEN) @@ -209,7 +201,7 @@ var/global/list/all_apcs = list() // update the APC icon to show the three base states // also add overlays for indicator lights -/obj/machinery/power/apc/on_update_icon() +/obj/machinery/apc/on_update_icon() if (!status_overlays) status_overlays = 1 status_overlays_lock = new @@ -302,7 +294,7 @@ var/global/list/all_apcs = list() else set_light(0) -/obj/machinery/power/apc/proc/check_updates() +/obj/machinery/apc/proc/check_updates() if(!update_overlay_chan) update_overlay_chan = new/list() var/last_update_state = update_state @@ -357,13 +349,13 @@ var/global/list/all_apcs = list() results += 2 return results -/obj/machinery/power/apc/set_broken(new_state, cause) +/obj/machinery/apc/set_broken(new_state, cause) . = ..() if(. && (stat & BROKEN)) operating = 0 update() -/obj/machinery/power/apc/cannot_transition_to(state_path, mob/user) +/obj/machinery/apc/cannot_transition_to(state_path, mob/user) if(ispath(state_path, /decl/machine_construction/wall_frame/panel_open)) for(var/obj/item/stock_parts/access_lock/lock in get_all_components_of_type(/obj/item/stock_parts/access_lock)) if(lock.locked) @@ -372,7 +364,7 @@ var/global/list/all_apcs = list() return SPAN_WARNING("You cannot close the cover: it was completely removed!") . = ..() -/obj/machinery/power/apc/proc/force_open_panel(mob/user) +/obj/machinery/apc/proc/force_open_panel(mob/user) var/decl/machine_construction/wall_frame/panel_closed/closed_state = construct_state if(!istype(closed_state)) return MCS_CONTINUE @@ -385,13 +377,13 @@ var/global/list/all_apcs = list() panel_open = TRUE queue_icon_update() -/obj/machinery/power/apc/attackby(obj/item/used_item, mob/user) +/obj/machinery/apc/attackby(obj/item/used_item, mob/user) if (istype(construct_state, /decl/machine_construction/wall_frame/panel_closed/hackable/hacking) && (IS_MULTITOOL(used_item) || IS_WIRECUTTER(used_item) || istype(used_item, /obj/item/assembly/signaler))) wires.Interact(user) return TRUE return ..() -/obj/machinery/power/apc/bash(obj/item/used_item, mob/user) +/obj/machinery/apc/bash(obj/item/used_item, mob/user) if (!(user.check_intent(I_FLAG_HARM)) || (used_item.item_flags & ITEM_FLAG_NO_BLUDGEON)) return FALSE @@ -412,7 +404,7 @@ var/global/list/all_apcs = list() // attack with hand - remove cell (if cover open) or interact with the APC -/obj/machinery/power/apc/emag_act(var/remaining_charges, var/mob/user) +/obj/machinery/apc/emag_act(var/remaining_charges, var/mob/user) if (!(emagged || (hacker && !hacker.hacked_apcs_hidden))) // trying to unlock with an emag card if(panel_open) to_chat(user, "You must close the cover to swipe an ID card.") @@ -433,10 +425,10 @@ var/global/list/all_apcs = list() to_chat(user, "You fail to [ locked ? "unlock" : "lock"] the APC interface.") return 1 -/obj/machinery/power/apc/CanUseTopicPhysical(var/mob/user) +/obj/machinery/apc/CanUseTopicPhysical(var/mob/user) return global.physical_topic_state.can_use_topic(nano_host(), user) -/obj/machinery/power/apc/physical_attack_hand(mob/user) +/obj/machinery/apc/physical_attack_hand(mob/user) //Human mob special interaction goes here. if(user.can_shred()) user.visible_message( @@ -454,11 +446,11 @@ var/global/list/all_apcs = list() return TRUE return FALSE -/obj/machinery/power/apc/interface_interact(mob/user) +/obj/machinery/apc/interface_interact(mob/user) ui_interact(user) return TRUE -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) return var/obj/item/cell/cell = get_cell() @@ -533,7 +525,7 @@ var/global/list/all_apcs = list() // auto update every Master Controller tick ui.set_auto_update(1) -/obj/machinery/power/apc/proc/update() +/obj/machinery/apc/proc/update() var/old_power_light = area.power_light var/old_power_environ = area.power_environ var/old_power_equip = area.power_equip @@ -563,7 +555,7 @@ var/global/list/all_apcs = list() else if(cell?.charge > 0) powered_down = FALSE -/obj/machinery/power/apc/CanUseTopic(mob/user, datum/topic_state/state) +/obj/machinery/apc/CanUseTopic(mob/user, datum/topic_state/state) if(user.current_posture.prone) to_chat(user, "You must stand to use [src]!") return STATUS_CLOSE @@ -584,7 +576,7 @@ var/global/list/all_apcs = list() to_chat(user, "You must have free hands to use [src].") . = min(., STATUS_UPDATE) -/obj/machinery/power/apc/OnTopic(mob/user, list/href_list, state) +/obj/machinery/apc/OnTopic(mob/user, list/href_list, state) if(href_list["reboot"] ) failure_timer = 0 update_icon() @@ -651,17 +643,17 @@ var/global/list/all_apcs = list() remote_control = !remote_control return TOPIC_REFRESH -/obj/machinery/power/apc/proc/force_update_channels() +/obj/machinery/apc/proc/force_update_channels() autoflag = -1 // This clears state, forcing a full recalculation update_channels(TRUE) update() queue_icon_update() -/obj/machinery/power/apc/proc/toggle_breaker() +/obj/machinery/apc/proc/toggle_breaker() operating = !operating force_update_channels() -/obj/machinery/power/apc/get_power_usage() +/obj/machinery/apc/get_power_usage() if(autoflag) return lastused_total // If not, we need to do something more sophisticated: compute how much power we would need in order to come back online. . = 0 @@ -674,7 +666,7 @@ var/global/list/all_apcs = list() if(autoset(environ, 1) >= POWERCHAN_ON) . += area.usage(ENVIRON) -/obj/machinery/power/apc/Process() +/obj/machinery/apc/Process() if(!area?.requires_power) return PROCESS_KILL @@ -737,7 +729,7 @@ var/global/list/all_apcs = list() else if (last_ch != charging) queue_icon_update() -/obj/machinery/power/apc/proc/update_channels(suppress_alarms = FALSE) +/obj/machinery/apc/proc/update_channels(suppress_alarms = FALSE) // Allow the APC to operate as normal if the cell can charge if(charging && longtermpower < 10) longtermpower += 1 @@ -781,7 +773,7 @@ var/global/list/all_apcs = list() // val 0=off, 1=off(auto) 2=on 3=on(auto) // on 0=off, 1=on, 2=autooff // defines a state machine, returns the new state -/obj/machinery/power/apc/proc/autoset(var/cur_state, var/on) +/obj/machinery/apc/proc/autoset(var/cur_state, var/on) //autoset will never turn on a channel set to off switch(cur_state) if(POWERCHAN_OFF_TEMP) @@ -801,7 +793,7 @@ var/global/list/all_apcs = list() // damage and destruction acts -/obj/machinery/power/apc/emp_act(severity) +/obj/machinery/apc/emp_act(severity) if(emp_hardened) return // Fail for 8-12 minutes (divided by severity) @@ -816,7 +808,7 @@ var/global/list/all_apcs = list() queue_icon_update() ..() -/obj/machinery/power/apc/on_component_failure(obj/item/stock_parts/component) +/obj/machinery/apc/on_component_failure(obj/item/stock_parts/component) var/was_broken = stat & BROKEN . = ..() if(!was_broken && (stat & BROKEN)) @@ -826,20 +818,20 @@ var/global/list/all_apcs = list() operating = 0 update() -/obj/machinery/power/apc/proc/set_chargemode(new_mode) +/obj/machinery/apc/proc/set_chargemode(new_mode) chargemode = new_mode var/obj/item/stock_parts/power/battery/power = get_component_of_type(/obj/item/stock_parts/power/battery) if(power) power.can_charge = chargemode power.charge_wait_counter = initial(power.charge_wait_counter) -/obj/machinery/power/apc/proc/change_area_name(var/area/A, var/old_area_name, var/new_area_name) +/obj/machinery/apc/proc/change_area_name(var/area/A, var/old_area_name, var/new_area_name) if(A != area || !autoname) return SetName("[A.proper_name] APC") // overload the lights in this APC area -/obj/machinery/power/apc/proc/overload_lighting(var/chance = 100) +/obj/machinery/apc/proc/overload_lighting(var/chance = 100) if(/* !get_connection() || */ !operating || shorted) return var/amount = use_power_oneoff(20, LOCAL) @@ -851,7 +843,7 @@ var/global/list/all_apcs = list() L.broken() sleep(1) -/obj/machinery/power/apc/proc/setsubsystem(val) +/obj/machinery/apc/proc/setsubsystem(val) switch(val) if(2) return POWERCHAN_OFF_AUTO @@ -859,7 +851,7 @@ var/global/list/all_apcs = list() return POWERCHAN_OFF_TEMP else return POWERCHAN_OFF -/obj/machinery/power/apc/proc/set_channel_state_manual(var/channel, var/state) +/obj/machinery/apc/proc/set_channel_state_manual(var/channel, var/state) switch(channel) if(APC_POWERCHAN_EQUIPMENT) equipment = state @@ -869,7 +861,7 @@ var/global/list/all_apcs = list() environ = state force_update_channels() -/obj/machinery/power/apc/area_changed(area/old_area, area/new_area) +/obj/machinery/apc/area_changed(area/old_area, area/new_area) . = ..() if(QDELETED(src)) return diff --git a/code/modules/power/apc/apc_frame.dm b/code/modules/power/apc/apc_frame.dm index 708f3dbadf3e..897401051961 100644 --- a/code/modules/power/apc/apc_frame.dm +++ b/code/modules/power/apc/apc_frame.dm @@ -6,7 +6,7 @@ icon = 'icons/obj/apc_repair.dmi' icon_state = "apc_frame" obj_flags = OBJ_FLAG_CONDUCTIBLE - build_machine_type = /obj/machinery/power/apc/buildable + build_machine_type = /obj/machinery/apc/buildable reverse = TRUE /obj/item/frame/apc/try_build(turf/on_wall) @@ -27,4 +27,4 @@ fully_construct = TRUE name = "APC kit" desc = "An all-in-one APC kit, comes preassembled." - build_machine_type = /obj/machinery/power/apc \ No newline at end of file + build_machine_type = /obj/machinery/apc \ No newline at end of file diff --git a/code/modules/power/apc/apc_presets.dm b/code/modules/power/apc/apc_presets.dm index a9ef717b7bca..62648417633b 100644 --- a/code/modules/power/apc/apc_presets.dm +++ b/code/modules/power/apc/apc_presets.dm @@ -1,38 +1,38 @@ // Various APC types -/obj/machinery/power/apc/inactive +/obj/machinery/apc/inactive lighting = 0 equipment = 0 environ = 0 locked = FALSE -/obj/machinery/power/apc/critical +/obj/machinery/apc/critical is_critical = 1 -/obj/machinery/power/apc/high +/obj/machinery/apc/high uncreated_component_parts = list( /obj/item/cell/high ) -/obj/machinery/power/apc/high/inactive +/obj/machinery/apc/high/inactive lighting = 0 equipment = 0 environ = 0 locked = FALSE -/obj/machinery/power/apc/super +/obj/machinery/apc/super uncreated_component_parts = list( /obj/item/cell/super ) -/obj/machinery/power/apc/super/critical +/obj/machinery/apc/super/critical is_critical = 1 -/obj/machinery/power/apc/hyper +/obj/machinery/apc/hyper uncreated_component_parts = list( /obj/item/cell/hyper ) -/obj/machinery/power/apc/derelict +/obj/machinery/apc/derelict lighting = 0 equipment = 0 environ = 0 @@ -41,7 +41,7 @@ /obj/item/cell/crap/empty ) -/obj/machinery/power/apc/derelict/full +/obj/machinery/apc/derelict/full uncreated_component_parts = list( /obj/item/cell/crap ) \ No newline at end of file diff --git a/code/modules/power/apc/apc_wires.dm b/code/modules/power/apc/apc_wires.dm index 25d655eb5300..6f6cb10deb75 100644 --- a/code/modules/power/apc/apc_wires.dm +++ b/code/modules/power/apc/apc_wires.dm @@ -1,5 +1,5 @@ /datum/wires/apc - holder_type = /obj/machinery/power/apc + holder_type = /obj/machinery/apc wire_count = 4 descriptions = list( new /datum/wire_description(APC_WIRE_IDSCAN, "This wire is connected to the ID scanning panel.", SKILL_EXPERT), @@ -13,34 +13,34 @@ var/const/APC_WIRE_AI_CONTROL = BITFLAG(3) /datum/wires/apc/GetInteractWindow(mob/user) - var/obj/machinery/power/apc/A = holder + var/obj/machinery/apc/A = holder . += ..() . += text("
    \n[(A.locked ? "The APC is locked." : "The APC is unlocked.")]
    \n[(A.shorted ? "The APCs power has been shorted." : "The APC is working properly!")]
    \n[(A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]") /datum/wires/apc/CanUse(var/mob/living/L) - var/obj/machinery/power/apc/A = holder + var/obj/machinery/apc/A = holder if(istype(A.construct_state, /decl/machine_construction/wall_frame/panel_closed/hackable/hacking) && !(A.stat & BROKEN)) return TRUE return FALSE /datum/wires/apc/proc/reset_locked() - var/obj/machinery/power/apc/A = holder + var/obj/machinery/apc/A = holder if(A) A.locked = TRUE /datum/wires/apc/proc/reset_shorted() - var/obj/machinery/power/apc/A = holder + var/obj/machinery/apc/A = holder if(A && !IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) A.shorted = FALSE /datum/wires/apc/proc/reset_ai_disabled() - var/obj/machinery/power/apc/A = holder + var/obj/machinery/apc/A = holder if(A && !IsIndexCut(APC_WIRE_AI_CONTROL)) A.aidisabled = FALSE /datum/wires/apc/UpdatePulsed(var/index) - var/obj/machinery/power/apc/A = holder + var/obj/machinery/apc/A = holder switch(index) @@ -59,7 +59,7 @@ addtimer(CALLBACK(src, PROC_REF(reset_ai_disabled)), 1 SECOND) /datum/wires/apc/UpdateCut(var/index, var/mended) - var/obj/machinery/power/apc/A = holder + var/obj/machinery/apc/A = holder switch(index) if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 56ef6ac56bf9..587bbe766db4 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -375,8 +375,8 @@ var/global/list/obj/structure/cable/all_cables = list() else powernet.add_cable(C) //the cable was powernetless, let's just add it to our powernet - else if(istype(AM,/obj/machinery/power/apc)) - var/obj/machinery/power/apc/N = AM + else if(istype(AM,/obj/machinery/apc)) + var/obj/machinery/apc/N = AM var/obj/machinery/power/terminal/terminal = N.terminal() if(!terminal) continue // APC are connected through their terminal diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index a3560b8c8db9..4449a9dd44a6 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -202,8 +202,8 @@ PN = power_source else if(istype(power_source,/obj/item/cell)) cell = power_source - else if(istype(power_source,/obj/machinery/power/apc)) - var/obj/machinery/power/apc/apc = power_source + else if(istype(power_source,/obj/machinery/apc)) + var/obj/machinery/apc/apc = power_source cell = apc.get_cell() var/obj/machinery/power/terminal/term = apc.terminal() if (term) diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 74669d031c19..10a8d5488a99 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -103,7 +103,7 @@ if(nodes && nodes.len) // Added to fix a bad list bug -- TLE for(var/obj/machinery/power/terminal/term in nodes) - if( istype( term.master_machine(), /obj/machinery/power/apc ) ) + if( istype( term.master_machine(), /obj/machinery/apc ) ) numapc++ netexcess = avail - load diff --git a/code/modules/power/sensors/powernet_sensor.dm b/code/modules/power/sensors/powernet_sensor.dm index 66e5cee66f83..19352222a15a 100644 --- a/code/modules/power/sensors/powernet_sensor.dm +++ b/code/modules/power/sensors/powernet_sensor.dm @@ -82,7 +82,7 @@ var/list/L = list() for(var/obj/machinery/power/terminal/term in powernet.nodes) - var/obj/machinery/power/apc/A = term.master_machine() + var/obj/machinery/apc/A = term.master_machine() if(istype(A)) L += A @@ -110,7 +110,7 @@ var/list/S = list("M-OFF", "DC-OFF","A-OFF","M-ON", "A-ON") var/list/chg = list("N","C","F") - for(var/obj/machinery/power/apc/A in L) + for(var/obj/machinery/apc/A in L) var/list/APC_entry = list() APC_entry["breaker"] = A.operating APC_entry["failure"] = A.failure_timer diff --git a/code/modules/power/smes_construction.dm b/code/modules/power/smes_construction.dm index 4d8d462e7a95..cb78bef66777 100644 --- a/code/modules/power/smes_construction.dm +++ b/code/modules/power/smes_construction.dm @@ -257,9 +257,9 @@ /obj/machinery/power/smes/buildable/proc/apcs_overload(var/failure_chance, var/overload_chance, var/reboot_chance) if (!src.powernet) return - var/list/obj/machinery/power/apc/apcs = list() + var/list/obj/machinery/apc/apcs = list() for(var/obj/machinery/power/terminal/T in powernet.nodes) - var/obj/machinery/power/apc/A = T.master_machine() + var/obj/machinery/apc/A = T.master_machine() if(istype(A)) if (prob(overload_chance)) A.overload_lighting() @@ -270,7 +270,7 @@ if (apcs.len) var/overload_damage = charge/100/apcs.len - for (var/obj/machinery/power/apc/A in apcs) + for (var/obj/machinery/apc/A in apcs) A.take_damage(overload_damage, ELECTROCUTE) // Proc: update_icon() diff --git a/code/modules/xenoarcheaology/artifacts/effects/cellcharge.dm b/code/modules/xenoarcheaology/artifacts/effects/cellcharge.dm index 1ccf80abd710..c4bcfcd61a8f 100644 --- a/code/modules/xenoarcheaology/artifacts/effects/cellcharge.dm +++ b/code/modules/xenoarcheaology/artifacts/effects/cellcharge.dm @@ -26,7 +26,7 @@ /datum/artifact_effect/cellcharge/proc/charge_cells_in_range(amount) var/turf/T = get_turf(holder) - for (var/obj/machinery/power/apc/A in range(effect_range, T)) + for (var/obj/machinery/apc/A in range(effect_range, T)) var/obj/item/cell/cell = A.get_cell() if(cell) cell.give(amount) diff --git a/code/modules/xenoarcheaology/artifacts/effects/celldrain.dm b/code/modules/xenoarcheaology/artifacts/effects/celldrain.dm index 9d5a6b626ac0..899f3983cb3c 100644 --- a/code/modules/xenoarcheaology/artifacts/effects/celldrain.dm +++ b/code/modules/xenoarcheaology/artifacts/effects/celldrain.dm @@ -26,7 +26,7 @@ /datum/artifact_effect/celldrain/proc/drain_cells_in_range(amount) var/turf/T = get_turf(holder) - for (var/obj/machinery/power/apc/A in range(effect_range, T)) + for (var/obj/machinery/apc/A in range(effect_range, T)) var/obj/item/cell/cell = A.get_cell() if(cell) cell.use(amount) diff --git a/code/unit_tests/del_the_world.dm b/code/unit_tests/del_the_world.dm index 411ef21b8d33..58a54cc64f0c 100644 --- a/code/unit_tests/del_the_world.dm +++ b/code/unit_tests/del_the_world.dm @@ -9,7 +9,7 @@ var/list/ignore = typesof( // will error if the area already has one - /obj/machinery/power/apc, + /obj/machinery/apc, // throw assert failures around non-null alarm area on spawn /obj/machinery/alarm, // Needs a level above. diff --git a/code/unit_tests/power_tests.dm b/code/unit_tests/power_tests.dm index 16df5f3e9d90..9d3aa0a558ad 100644 --- a/code/unit_tests/power_tests.dm +++ b/code/unit_tests/power_tests.dm @@ -54,8 +54,8 @@ /datum/unit_test/areas_apc_uniqueness/start_test() var/failure = "" for(var/area/A in global.areas) - var/obj/machinery/power/apc/found_apc = null - for(var/obj/machinery/power/apc/APC in A) + var/obj/machinery/apc/found_apc = null + for(var/obj/machinery/apc/APC in A) if(!found_apc) found_apc = APC continue diff --git a/maps/away/bearcat/bearcat-1.dmm b/maps/away/bearcat/bearcat-1.dmm index a4c908ce3778..cf784259590e 100644 --- a/maps/away/bearcat/bearcat-1.dmm +++ b/maps/away/bearcat/bearcat-1.dmm @@ -210,7 +210,7 @@ /obj/effect/floor_decal/industrial/loading{ dir = 1 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4 }, /obj/structure/cable{ @@ -272,7 +272,7 @@ /obj/effect/floor_decal/industrial/loading{ dir = 1 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 8 }, /obj/structure/cable{ @@ -395,7 +395,7 @@ pixel_x = 24; dir = 8 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /turf/floor/usedup, @@ -414,7 +414,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 9 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 8 }, /obj/effect/floor_decal/industrial/outline/yellow, @@ -472,7 +472,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/lower) "bf" = ( -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/structure/cable{ @@ -906,7 +906,7 @@ pixel_x = 24; dir = 8 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/structure/closet/crate/uranium, @@ -1012,7 +1012,7 @@ /obj/random/clothing, /obj/random/clothing, /obj/effect/decal/cleanable/cobweb, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /turf/floor/tiled/dark/airless, @@ -1353,7 +1353,7 @@ pixel_x = 24; dir = 8 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/item/radio/intercom{ @@ -1463,7 +1463,7 @@ /obj/random/gloves, /obj/random/clothing, /obj/random/clothing, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /turf/floor/tiled/dark/airless, @@ -1752,7 +1752,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc/derelict, +/obj/machinery/apc/derelict, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -1998,7 +1998,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/eva) "el" = ( -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/effect/floor_decal/corner/yellow{ @@ -2255,7 +2255,7 @@ /obj/item/stock_parts/micro_laser, /obj/item/stock_parts/micro_laser/ultra, /obj/structure/rack, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ name = "south bump"; pixel_y = -24 }, @@ -2364,7 +2364,7 @@ /obj/item/box/lights/bulbs, /obj/item/box/lights/mixed, /obj/structure/cable, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ name = "Tools Storage APC" }, /obj/effect/floor_decal/corner/yellow{ diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 13fbb9c4b12c..30150ba50d4e 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -129,7 +129,7 @@ /obj/structure/chair{ dir = 1 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 8 }, /obj/structure/cable{ @@ -242,7 +242,7 @@ /obj/structure/window/reinforced{ dir = 8 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1; name = "Communications APC" }, @@ -261,7 +261,7 @@ dir = 4; icon_state = "tube1" }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4; name = "Bridge APC" }, @@ -484,7 +484,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc/derelict/full{ +/obj/machinery/apc/derelict/full{ dir = 4; name = "Captain's Quarters APC" }, @@ -562,7 +562,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1; name = "Docking Area APC" }, @@ -1082,7 +1082,7 @@ /turf/floor/tiled/usedup, /area/ship/scrap/crew/saloon) "cm" = ( -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1; name = "Crew Areas APC" }, @@ -1535,7 +1535,7 @@ }, /obj/effect/decal/cleanable/dirt/visible, /obj/structure/cable, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4; name = "Bathrooms APC" }, @@ -1637,7 +1637,7 @@ /area/ship/scrap/crew/hallway/starboard) "dt" = ( /obj/structure/cable, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ name = "Dorms APC" }, /obj/machinery/light_switch{ @@ -1741,7 +1741,7 @@ /area/ship/scrap/crew/kitchen) "dH" = ( /obj/structure/disposalpipe/segment, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4; name = "Crew Deck APC" }, @@ -1874,7 +1874,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4; name = "Galley APC" }, @@ -2248,7 +2248,7 @@ /area/ship/scrap/crew/medbay) "eA" = ( /obj/item/pill_bottle/happy, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/structure/cable{ @@ -2382,7 +2382,7 @@ "eM" = ( /obj/effect/decal/cleanable/dirt/visible, /obj/structure/cable, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4; name = "Cargo Hold APC" }, @@ -2393,7 +2393,7 @@ /turf/floor/tiled/usedup, /area/ship/scrap/cargo) "eN" = ( -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4; name = "Crew Deck APC" }, @@ -2407,7 +2407,7 @@ "eO" = ( /obj/structure/table, /obj/structure/cable, -/obj/machinery/power/apc/derelict/full{ +/obj/machinery/apc/derelict/full{ name = "Medical Bay APC" }, /obj/machinery/light_switch{ @@ -2480,7 +2480,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/engine/starboard) "eU" = ( -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/structure/cable{ @@ -2676,7 +2676,7 @@ dir = 4; icon_state = "conpipe-c" }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1; name = "Washroom APC" }, @@ -3067,7 +3067,7 @@ /turf/floor/usedup, /area/ship/scrap/unused) "gc" = ( -/obj/machinery/power/apc/derelict/full{ +/obj/machinery/apc/derelict/full{ name = "Medical Bay APC" }, /obj/structure/cable, @@ -3396,7 +3396,7 @@ /area/ship/scrap/fire) "gL" = ( /obj/structure/cable, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ name = "south bump"; pixel_y = -24 }, @@ -3510,7 +3510,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/fuel{ dir = 4 }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/structure/cable{ @@ -3666,7 +3666,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /mob/living/simple_animal/hostile/carp, @@ -3691,7 +3691,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /obj/structure/sign/warning/compressed_gas{ @@ -4437,7 +4437,7 @@ /area/ship/scrap/maintenance/atmos) "iF" = ( /obj/machinery/atmospherics/pipe/simple/hidden/fuel, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 8 }, /obj/structure/cable, @@ -5125,7 +5125,7 @@ /obj/structure/closet/secure_closet/engineering_electrical/bearcat, /obj/item/cell/device/standard, /obj/item/cell/device/standard, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 8 }, /obj/structure/cable{ @@ -5262,7 +5262,7 @@ /turf/floor/tiled/airless, /area/ship/scrap/maintenance/atmos) "PP" = ( -/obj/machinery/power/apc/derelict/full{ +/obj/machinery/apc/derelict/full{ name = "Medical Bay APC" }, /obj/structure/cable{ diff --git a/maps/away/casino/casino.dmm b/maps/away/casino/casino.dmm index 9351a57a678b..dc523a800a1c 100644 --- a/maps/away/casino/casino.dmm +++ b/maps/away/casino/casino.dmm @@ -293,7 +293,7 @@ dir = 1 }, /obj/structure/flora/pottedplant/unusual, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -747,7 +747,7 @@ icon_state = "0-8" }, /obj/structure/flora/pottedplant/unusual, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -882,7 +882,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -943,7 +943,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 6 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -1243,7 +1243,7 @@ /obj/structure/cable/green{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -1686,7 +1686,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 10 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1723,7 +1723,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1757,7 +1757,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2031,7 +2031,7 @@ "fR" = ( /obj/effect/decal/cleanable/blood/splatter, /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -2644,7 +2644,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -3924,7 +3924,7 @@ dir = 8; level = 2 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -3971,7 +3971,7 @@ dir = 8; level = 2 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -4018,7 +4018,7 @@ dir = 8; level = 2 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -4273,7 +4273,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -4449,7 +4449,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 9 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -4967,7 +4967,7 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -5199,7 +5199,7 @@ "Oc" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -5449,7 +5449,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index dae85f046cff..efcdd0536054 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -607,7 +607,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc/high/inactive{ +/obj/machinery/apc/high/inactive{ dir = 8; name = "west bump"; pixel_x = -24 @@ -1633,7 +1633,7 @@ dir = 8; pixel_x = 24 }, -/obj/machinery/power/apc/high/inactive{ +/obj/machinery/apc/high/inactive{ name = "south bump"; pixel_y = -24 }, @@ -1870,7 +1870,7 @@ /obj/structure/cable/blue{ icon_state = "0-2" }, -/obj/machinery/power/apc/high/inactive{ +/obj/machinery/apc/high/inactive{ dir = 1; name = "north bump"; pixel_y = 24 @@ -2661,7 +2661,7 @@ /area/constructionsite/medical) "jp" = ( /obj/structure/cable/blue, -/obj/machinery/power/apc/high/inactive{ +/obj/machinery/apc/high/inactive{ name = "south bump"; pixel_y = -24 }, diff --git a/maps/away/errant_pisces/errant_pisces.dmm b/maps/away/errant_pisces/errant_pisces.dmm index 79a6b2c6d3a1..77c6de05aef4 100644 --- a/maps/away/errant_pisces/errant_pisces.dmm +++ b/maps/away/errant_pisces/errant_pisces.dmm @@ -218,7 +218,7 @@ /turf/floor/plating, /area/errant_pisces/bow_starboard) "aF" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -321,7 +321,7 @@ /turf/floor/plating, /area/errant_pisces/bow_port) "aR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -960,7 +960,7 @@ /turf/floor/plating, /area/errant_pisces/storage_port) "cV" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -1032,7 +1032,7 @@ /turf/floor/plating, /area/errant_pisces/atmos) "dd" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1070,7 +1070,7 @@ /turf/floor/plating, /area/errant_pisces/enginering) "dg" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1113,7 +1113,7 @@ /turf/floor/plating, /area/errant_pisces/storage_port) "dm" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1365,7 +1365,7 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -2115,7 +2115,7 @@ /turf/floor/plating, /area/errant_pisces/bow_maint) "ft" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2247,7 +2247,7 @@ dir = 8; pixel_x = 24 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -2289,7 +2289,7 @@ /turf/floor/plating, /area/errant_pisces/smes_room) "fM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -2327,7 +2327,7 @@ dir = 4; pixel_x = -32 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -2505,7 +2505,7 @@ /turf/floor/tiled, /area/errant_pisces/hallway) "gm" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2716,7 +2716,7 @@ /turf/floor/tiled, /area/errant_pisces/dorms) "gV" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2937,7 +2937,7 @@ /turf/floor/laminate, /area/errant_pisces/rooms) "hA" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -3643,7 +3643,7 @@ /turf/floor/tiled, /area/errant_pisces/infirmary) "jJ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -3864,7 +3864,7 @@ /turf/floor/tiled, /area/errant_pisces/cryo) "kq" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -4623,7 +4623,7 @@ /turf/floor/tiled, /area/errant_pisces/science_wing) "mD" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -4685,7 +4685,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 6 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -4719,7 +4719,7 @@ /turf/floor/tiled/freezer, /area/errant_pisces/prod_storage) "mQ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -4767,7 +4767,7 @@ /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -5297,7 +5297,7 @@ /turf/floor/tiled, /area/errant_pisces/aft_hallway) "nU" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -5356,7 +5356,7 @@ "oe" = ( /obj/structure/closet/crate/radiation, /obj/item/stack/material/aerogel/mapped/tritium/ten, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -5610,7 +5610,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -5812,7 +5812,7 @@ /area/errant_pisces/fishing_wing) "ps" = ( /obj/structure/cable/green, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -6431,7 +6431,7 @@ /obj/structure/mirror{ pixel_x = 30 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, diff --git a/maps/away/liberia/liberia.dmm b/maps/away/liberia/liberia.dmm index 4668690ed070..8d9c0495340a 100644 --- a/maps/away/liberia/liberia.dmm +++ b/maps/away/liberia/liberia.dmm @@ -146,7 +146,7 @@ }, /obj/effect/floor_decal/corner_techfloor_grid, /obj/effect/floor_decal/techfloor/corner, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "north bump"; pixel_y = 24 @@ -703,7 +703,7 @@ /area/liberia/mule) "bt" = ( /obj/effect/floor_decal/corner/brown/half, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "merchant north bump"; pixel_y = 24 @@ -1448,7 +1448,7 @@ /obj/effect/floor_decal/techfloor{ dir = 1 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "north bump"; pixel_y = 24 @@ -1921,7 +1921,7 @@ icon_state = "0-2" }, /obj/random/powercell, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "merchant north bump"; pixel_y = 24 @@ -2788,7 +2788,7 @@ /area/liberia/medbay) "eS" = ( /obj/machinery/body_scanconsole, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 4; name = "east bump"; pixel_x = 24 @@ -3123,7 +3123,7 @@ "fx" = ( /obj/effect/floor_decal/techfloor, /obj/structure/table, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ name = "south bump"; pixel_y = -24 }, @@ -3348,7 +3348,7 @@ /obj/structure/chair/wood/walnut{ dir = 8 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "north bump"; pixel_y = 24 @@ -3567,7 +3567,7 @@ /obj/effect/floor_decal/borderfloor/corner2{ dir = 8 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 8; name = "west bump"; pixel_x = -24 @@ -3762,7 +3762,7 @@ "gE" = ( /obj/structure/table, /obj/item/towel/random, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 8; name = "west bump"; pixel_x = -24 @@ -3891,7 +3891,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "north bump"; pixel_y = 24 @@ -4186,7 +4186,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 9 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "north bump"; pixel_y = 24 @@ -5204,7 +5204,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 8; name = "west bump"; pixel_x = -24 @@ -5218,7 +5218,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 4 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 4; name = "east bump"; pixel_x = 24 @@ -5538,7 +5538,7 @@ "ky" = ( /obj/structure/table/laminate, /obj/effect/floor_decal/spline/fancy/wood, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ name = "south bump"; pixel_y = -24 }, @@ -6062,7 +6062,7 @@ dir = 4 }, /obj/machinery/atmospherics/unary/vent_pump/on, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 4; name = "east bump"; pixel_x = 24 @@ -6627,7 +6627,7 @@ /turf/floor/plating, /area/liberia/hallway) "rx" = ( -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 8; name = "west bump"; pixel_x = -24 @@ -6736,7 +6736,7 @@ /obj/effect/floor_decal/techfloor{ dir = 4 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "north bump"; pixel_y = 24 @@ -7399,7 +7399,7 @@ /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 }, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 8; name = "west bump"; pixel_x = -24 @@ -7533,7 +7533,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/disposalpipe/segment, /obj/effect/catwalk_plated, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 4; name = "east bump"; pixel_x = 24 @@ -7561,7 +7561,7 @@ /obj/structure/janitorialcart, /obj/item/mop, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 1; name = "north bump"; pixel_y = 24 @@ -7589,7 +7589,7 @@ /obj/item/stack/material/ingot/mapped/copper/fifty, /obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/light/small, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ name = "south bump"; pixel_y = -24 }, @@ -7784,7 +7784,7 @@ /turf/floor/tiled, /area/liberia/hallway) "JS" = ( -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ dir = 8; name = "west bump"; pixel_x = -24 @@ -8121,7 +8121,7 @@ /area/liberia/engineeringstorage) "QA" = ( /obj/effect/floor_decal/techfloor, -/obj/machinery/power/apc/liberia{ +/obj/machinery/apc/liberia{ name = "south bump"; pixel_y = -24 }, diff --git a/maps/away/liberia/liberia_machinery.dm b/maps/away/liberia/liberia_machinery.dm index d47a001a09d5..cca1b41781d4 100644 --- a/maps/away/liberia/liberia_machinery.dm +++ b/maps/away/liberia/liberia_machinery.dm @@ -1,4 +1,4 @@ -/obj/machinery/power/apc/liberia +/obj/machinery/apc/liberia req_access = list(access_merchant) /obj/machinery/alarm/liberia diff --git a/maps/away/lost_supply_base/lost_supply_base.dmm b/maps/away/lost_supply_base/lost_supply_base.dmm index 4981010ff903..646d3271d4f2 100644 --- a/maps/away/lost_supply_base/lost_supply_base.dmm +++ b/maps/away/lost_supply_base/lost_supply_base.dmm @@ -241,7 +241,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -345,7 +345,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -561,7 +561,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -885,7 +885,7 @@ /obj/machinery/light{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -1580,7 +1580,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 diff --git a/maps/away/magshield/magshield.dmm b/maps/away/magshield/magshield.dmm index ff34e540ae8f..b8848b94ee56 100644 --- a/maps/away/magshield/magshield.dmm +++ b/maps/away/magshield/magshield.dmm @@ -915,7 +915,7 @@ /turf/floor/plating/airless, /area/magshield/smes_storage) "cH" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1108,7 +1108,7 @@ dir = 8; level = 2 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1300,7 +1300,7 @@ /turf/floor/plating/airless, /area/magshield/north) "dN" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2340,7 +2340,7 @@ /turf/wall, /area/magshield/west) "gQ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -2582,7 +2582,7 @@ /turf/floor/tiled, /area/magshield/west) "hG" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -3257,7 +3257,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 diff --git a/maps/away/mining/mining-signal.dmm b/maps/away/mining/mining-signal.dmm index 211bb4aa8333..c5c5cac0f940 100644 --- a/maps/away/mining/mining-signal.dmm +++ b/maps/away/mining/mining-signal.dmm @@ -1700,7 +1700,7 @@ dir = 4; icon_state = "0-2" }, -/obj/machinery/power/apc/high/inactive{ +/obj/machinery/apc/high/inactive{ name = "south bump"; pixel_y = -24 }, diff --git a/maps/away/smugglers/smugglers.dmm b/maps/away/smugglers/smugglers.dmm index ab98bd7f708f..76d0671080af 100644 --- a/maps/away/smugglers/smugglers.dmm +++ b/maps/away/smugglers/smugglers.dmm @@ -148,7 +148,7 @@ /obj/machinery/atmospherics/pipe/simple/visible/black{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -618,7 +618,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -831,7 +831,7 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 diff --git a/maps/away/unishi/unishi-1.dmm b/maps/away/unishi/unishi-1.dmm index 0261df5f22a4..69dafd229b14 100644 --- a/maps/away/unishi/unishi-1.dmm +++ b/maps/away/unishi/unishi-1.dmm @@ -26,7 +26,7 @@ /turf/floor, /area/unishi/engineering) "af" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -262,7 +262,7 @@ /turf/floor, /area/unishi/engineroom) "aQ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; diff --git a/maps/away/unishi/unishi-2.dmm b/maps/away/unishi/unishi-2.dmm index d80c7b18d961..5f96d95191cc 100644 --- a/maps/away/unishi/unishi-2.dmm +++ b/maps/away/unishi/unishi-2.dmm @@ -44,7 +44,7 @@ /turf/wall, /area/unishi/library) "al" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24; @@ -153,7 +153,7 @@ /turf/floor/tiled/white, /area/unishi/chem) "aC" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -434,7 +434,7 @@ /turf/floor/tiled/dark, /area/unishi/classroom) "bu" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -848,7 +848,7 @@ /turf/floor/tiled, /area/unishi/meeting) "cB" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -908,7 +908,7 @@ /obj/machinery/atmospherics/portables_connector{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -1225,7 +1225,7 @@ /turf/floor/tiled, /area/unishi/rnd) "dB" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -1370,7 +1370,7 @@ "dS" = ( /obj/machinery/atmospherics/valve, /obj/machinery/vending/hydroseeds, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -1571,7 +1571,7 @@ /obj/item/chems/spray/extinguisher, /obj/item/chems/spray/extinguisher, /obj/item/box/animal_cubes/monkeys, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -1888,7 +1888,7 @@ /turf/floor/tiled/dark, /area/unishi/common) "fl" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; @@ -2108,7 +2108,7 @@ /turf/floor/tiled/dark, /area/unishi/smresearch) "fR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24; diff --git a/maps/away/unishi/unishi-3.dmm b/maps/away/unishi/unishi-3.dmm index 81afee9709b0..f5ed1e294bf9 100644 --- a/maps/away/unishi/unishi-3.dmm +++ b/maps/away/unishi/unishi-3.dmm @@ -652,7 +652,7 @@ /turf/floor/plating/airless, /area/unishi/living) "cg" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -28; req_access = newlist() @@ -743,7 +743,7 @@ /turf/floor/tiled/white, /area/unishi/med) "cq" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 20; req_access = newlist() @@ -821,7 +821,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -28; req_access = newlist() @@ -1096,7 +1096,7 @@ /turf/floor/tiled, /area/unishi/living) "dh" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 20; req_access = newlist() @@ -1897,7 +1897,7 @@ /area/unishi/living) "fg" = ( /obj/machinery/door/firedoor, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = -30; req_access = newlist() diff --git a/maps/away/yacht/yacht.dmm b/maps/away/yacht/yacht.dmm index 9cc85f9666d9..43c5ed43367e 100644 --- a/maps/away/yacht/yacht.dmm +++ b/maps/away/yacht/yacht.dmm @@ -118,7 +118,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "Yacht bridge"; pixel_y = -24 }, @@ -640,7 +640,7 @@ /obj/structure/cable{ icon_state = "2-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Yacht engine"; pixel_y = 24 @@ -851,7 +851,7 @@ /area/yacht/engine) "cC" = ( /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "Yacht living"; pixel_x = -24 diff --git a/maps/example/example-1.dmm b/maps/example/example-1.dmm index 2aeb3f12d29a..129c5b08e972 100644 --- a/maps/example/example-1.dmm +++ b/maps/example/example-1.dmm @@ -728,7 +728,7 @@ /obj/structure/cable/yellow{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "west bump"; pixel_x = 24 diff --git a/maps/example/example-2.dmm b/maps/example/example-2.dmm index aea14459d2eb..f1bf69ddda70 100644 --- a/maps/example/example-2.dmm +++ b/maps/example/example-2.dmm @@ -700,7 +700,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 21 }, diff --git a/maps/example/example-3.dmm b/maps/example/example-3.dmm index f08650be723a..7dda2d9ff4ca 100644 --- a/maps/example/example-3.dmm +++ b/maps/example/example-3.dmm @@ -260,7 +260,7 @@ /turf/floor, /area/example/third) "QH" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 21 }, diff --git a/maps/exodus/exodus-1.dmm b/maps/exodus/exodus-1.dmm index a25c16112a44..2a76b49434c6 100644 --- a/maps/exodus/exodus-1.dmm +++ b/maps/exodus/exodus-1.dmm @@ -228,7 +228,7 @@ /turf/floor/plating, /area/exodus/maintenance/sub/fore) "aJ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -1425,7 +1425,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -1565,7 +1565,7 @@ /turf/floor/plating, /area/exodus/maintenance/sub/port) "eN" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -1596,7 +1596,7 @@ /turf/floor/plating, /area/exodus/maintenance/sub/central) "eQ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2051,7 +2051,7 @@ /turf/wall/r_wall/prepainted, /area/exodus/maintenance/sub/starboard) "fS" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2727,7 +2727,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -3605,7 +3605,7 @@ /obj/structure/cable/cyan{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -3655,7 +3655,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/maintenance/sub/aft) "jD" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -4159,7 +4159,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/atmos) "kX" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -6005,7 +6005,7 @@ "Nw" = ( /obj/structure/table/steel, /obj/item/box/cups, -/obj/machinery/power/apc/hyper{ +/obj/machinery/apc/hyper{ dir = 4; name = "east bump"; pixel_x = 24 diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 83affb57b561..ee4834d3cee5 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -287,7 +287,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/range) "aaP" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1122,7 +1122,7 @@ dir = 1; pixel_y = -22 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1416,7 +1416,7 @@ /turf/floor/plating, /area/exodus/crew_quarters/heads/hos) "adc" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1616,7 +1616,7 @@ /turf/floor/plating, /area/exodus/maintenance/security_starboard) "adC" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -2072,7 +2072,7 @@ /area/exodus/security/main) "aeA" = ( /obj/effect/floor_decal/corner/red, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2102,7 +2102,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/meeting) "aeE" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2579,7 +2579,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/warden) "afF" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -3367,7 +3367,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -3800,7 +3800,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/heads/hos) "ais" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -3914,7 +3914,7 @@ /obj/structure/chair{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -4455,7 +4455,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -5177,7 +5177,7 @@ /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -5642,7 +5642,7 @@ /turf/floor/lino, /area/exodus/security/detectives_office) "alU" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -5999,7 +5999,7 @@ /obj/structure/cable/green{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -6154,7 +6154,7 @@ id_tag = "permentryflash"; name = "Floor mounted flash" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -6379,7 +6379,7 @@ /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -7055,7 +7055,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/lobby) "aoR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -8340,7 +8340,7 @@ /turf/floor/tiled/dark, /area/exodus/lawoffice) "arr" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -8797,7 +8797,7 @@ /turf/floor/plating, /area/exodus/maintenance/dormitory) "asq" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -8838,7 +8838,7 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -9426,7 +9426,7 @@ /turf/floor/tiled/white/monotile, /area/exodus/crew_quarters/sleep/cryo) "atM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -9712,7 +9712,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/fore) "auy" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -10069,7 +10069,7 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -10146,7 +10146,7 @@ /area/exodus/hallway/primary/port) "avv" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -10248,7 +10248,7 @@ /area/exodus/hallway/primary/fore) "avJ" = ( /obj/structure/disposalpipe/segment, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -10959,7 +10959,7 @@ /area/exodus/maintenance/evahallway) "axd" = ( /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -11195,7 +11195,7 @@ /turf/floor/plating, /area/exodus/maintenance/arrivals) "axK" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -11435,7 +11435,7 @@ /turf/floor/plating, /area/exodus/maintenance/arrivals) "aym" = ( -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 8; name = "west bump"; pixel_x = -24 @@ -12132,7 +12132,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -12766,7 +12766,7 @@ /turf/floor/tiled/dark, /area/exodus/crew_quarters/sleep) "aAV" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -13379,7 +13379,7 @@ "aCw" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 4; name = "east bump"; pixel_x = 24 @@ -13725,7 +13725,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/secondary/entry/port) "aDr" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -13957,7 +13957,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/ai_monitored/storage/eva) "aDS" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -13999,7 +13999,7 @@ name = "xeno_spawn"; pixel_x = -1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -14060,7 +14060,7 @@ /turf/floor/tiled/dark, /area/exodus/security/nuke_storage) "aEc" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -14515,7 +14515,7 @@ /turf/floor/plating, /area/exodus/maintenance/library) "aEX" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -14763,7 +14763,7 @@ /turf/floor/plating, /area/exodus/maintenance/library) "aFx" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -14847,7 +14847,7 @@ /area/exodus/hallway/primary/central_two) "aFG" = ( /obj/structure/closet/secure_closet/bar, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -15055,7 +15055,7 @@ /turf/floor/plating, /area/exodus/maintenance/evahallway) "aFX" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -15441,7 +15441,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 6 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -15613,7 +15613,7 @@ /obj/structure/cable/green{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -16122,7 +16122,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -17010,7 +17010,7 @@ /turf/floor/laminate/walnut, /area/exodus/library) "aKc" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -17596,7 +17596,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/secondary/entry/starboard) "aLv" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -18115,7 +18115,7 @@ /turf/floor/tiled/dark, /area/exodus/hydroponics) "aMG" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -18376,7 +18376,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/hallway/primary/port) "aNi" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -19141,7 +19141,7 @@ /turf/floor/tiled/dark, /area/exodus/chapel/office) "aOF" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -20536,7 +20536,7 @@ /turf/floor/carpet, /area/exodus/library) "aRO" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -20557,7 +20557,7 @@ /area/exodus/hallway/secondary/entry/starboard) "aRQ" = ( /obj/structure/cable/green, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 4; name = "east bump"; pixel_x = 24 @@ -22081,7 +22081,7 @@ /obj/structure/cable/green{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -22176,7 +22176,7 @@ /turf/floor/plating, /area/exodus/maintenance/locker) "aVB" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -22207,7 +22207,7 @@ dir = 8; pixel_x = -24 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -22545,7 +22545,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hydroponics/garden) "aWs" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -23999,7 +23999,7 @@ /turf/floor/laminate/walnut, /area/exodus/crew_quarters/bar) "aZB" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -24175,7 +24175,7 @@ /turf/floor/lino, /area/exodus/security/vacantoffice) "baa" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -24515,7 +24515,7 @@ /obj/effect/floor_decal/corner/brown/diagonal{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -25064,7 +25064,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -25138,7 +25138,7 @@ /area/exodus/hallway/primary/central_one) "bcb" = ( /obj/structure/table/reinforced, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -25151,7 +25151,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/bridge) "bcc" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -25326,7 +25326,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/central_two) "bcu" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -25810,7 +25810,7 @@ /turf/floor/tiled/freezer, /area/exodus/crew_quarters/locker/locker_toilet) "bdt" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -25887,7 +25887,7 @@ /turf/floor/plating, /area/exodus/maintenance/locker) "bdB" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -26214,7 +26214,7 @@ /turf/floor/lino, /area/exodus/security/vacantoffice) "beu" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -26663,7 +26663,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/hallway/primary/starboard) "bfr" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -26804,7 +26804,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/captain) "bfI" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -29129,7 +29129,7 @@ /area/exodus/research/docking) "bks" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 4; name = "east bump"; pixel_x = 24 @@ -30079,7 +30079,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -30195,7 +30195,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/starboard) "bmC" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -30250,7 +30250,7 @@ /obj/structure/cable/cyan{ icon_state = "0-4" }, -/obj/machinery/power/apc/super/critical{ +/obj/machinery/apc/super/critical{ dir = 1; name = "north bump"; pixel_y = 24 @@ -30411,7 +30411,7 @@ /turf/wall/r_wall/prepainted, /area/exodus/medical/chemistry) "bmZ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -30962,7 +30962,7 @@ /obj/item/clothing/head/hardhat/red, /obj/item/flashlight, /obj/item/clothing/mask/gas, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -31140,7 +31140,7 @@ /turf/floor/laminate/walnut, /area/exodus/crew_quarters/captain) "boD" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -31310,7 +31310,7 @@ /turf/floor/tiled/white, /area/exodus/research/lab) "boU" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -31767,7 +31767,7 @@ /obj/machinery/porta_turret{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -33872,7 +33872,7 @@ /turf/floor/tiled/dark, /area/exodus/research/chargebay) "buf" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -34380,7 +34380,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/heads/hop) "buZ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -34585,7 +34585,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/medical/reception) "bvr" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -34743,7 +34743,7 @@ /turf/floor/plating, /area/exodus/maintenance/cargo) "bvG" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -34813,7 +34813,7 @@ /turf/floor/plating, /area/exodus/maintenance/cargo) "bvT" = ( -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; name = "north bump"; pixel_y = 24 @@ -35123,7 +35123,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -35174,7 +35174,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/heads/hop) "bwI" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -35369,7 +35369,7 @@ /turf/floor/tiled/white, /area/exodus/medical/chemistry) "bxb" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -35474,7 +35474,7 @@ /turf/floor/bluegrid, /area/exodus/turret_protected/ai_upload) "bxp" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -36036,7 +36036,7 @@ /turf/floor/tiled/white, /area/exodus/medical/medbay) "byy" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -36281,7 +36281,7 @@ /obj/machinery/light/small, /obj/structure/mopbucket, /obj/item/mop, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -36312,7 +36312,7 @@ pixel_x = -2; pixel_y = -1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -36648,7 +36648,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/miningdock) "bzM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -36663,7 +36663,7 @@ /turf/floor/tiled/dark, /area/exodus/turret_protected/ai_server_room) "bzN" = ( -/obj/machinery/power/apc/critical{ +/obj/machinery/apc/critical{ dir = 1; name = "north bump"; pixel_y = 24 @@ -36705,7 +36705,7 @@ /turf/floor/tiled/dark/monotile, /area/exodus/teleporter) "bzR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -36720,7 +36720,7 @@ /turf/floor/tiled/dark, /area/exodus/turret_protected/ai_upload_foyer) "bzS" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -36768,7 +36768,7 @@ /turf/floor/plating, /area/exodus/maintenance/engineering) "bzY" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -37141,7 +37141,7 @@ /obj/structure/cable/green{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -37382,7 +37382,7 @@ }, /obj/item/clothing/head/soft/orange, /obj/item/clothing/shoes/sandal, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -38965,7 +38965,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) "bEl" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -39007,7 +39007,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/office) "bEq" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -39057,7 +39057,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/central_three) "bEv" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -39304,7 +39304,7 @@ /turf/floor/tiled/white, /area/exodus/medical/medbay2) "bEZ" = ( -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 4; name = "east bump"; pixel_x = 24 @@ -39731,7 +39731,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/research/xenobiology) "bFR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -40676,7 +40676,7 @@ /obj/structure/cable/green{ icon_state = "0-4" }, -/obj/machinery/power/apc/critical{ +/obj/machinery/apc/critical{ dir = 1; name = "north bump"; pixel_y = 24 @@ -41066,7 +41066,7 @@ pixel_x = -22; pixel_y = 11 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -41254,7 +41254,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc/critical{ +/obj/machinery/apc/critical{ name = "south bump"; pixel_y = -24 }, @@ -41418,7 +41418,7 @@ /turf/floor/plating, /area/exodus/storage/tech) "bIX" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -42128,7 +42128,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -43096,7 +43096,7 @@ dir = 8; icon_state = "pipe-c" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -43282,7 +43282,7 @@ /turf/wall/prepainted, /area/exodus/medical/medbay2) "bMF" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -43348,7 +43348,7 @@ /area/exodus/crew_quarters/medbreak) "bML" = ( /obj/machinery/washing_machine, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -43801,7 +43801,7 @@ /obj/structure/cable/green{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -44233,7 +44233,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -44320,7 +44320,7 @@ /turf/floor/plating, /area/exodus/storage/tech) "bOM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -44639,7 +44639,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/janitor) "bPs" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -45079,7 +45079,7 @@ pixel_x = -25; pixel_y = -5 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -45849,7 +45849,7 @@ /obj/structure/cable/green{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -46751,7 +46751,7 @@ /area/exodus/medical/patient_a) "bTI" = ( /obj/structure/iv_drip, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -46794,7 +46794,7 @@ /area/exodus/medical/patient_b) "bTM" = ( /obj/structure/iv_drip, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -46842,7 +46842,7 @@ /turf/floor/tiled/white, /area/exodus/research/misc_lab) "bTQ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -46964,7 +46964,7 @@ pixel_y = -23; dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -47441,7 +47441,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/medical/patient_wing) "bVe" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -47592,7 +47592,7 @@ /obj/machinery/alarm{ pixel_y = 22 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -48602,7 +48602,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -48783,7 +48783,7 @@ /turf/floor/tiled/white, /area/exodus/medical/patient_wing) "bXR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -49141,7 +49141,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -49310,7 +49310,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/heads/chief) "bYP" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -49454,7 +49454,7 @@ /obj/machinery/light_switch{ pixel_y = 27 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -50120,7 +50120,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/medical/medbay4) "caz" = ( -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ name = "south bump"; pixel_y = -24 }, @@ -50175,7 +50175,7 @@ /turf/wall/prepainted, /area/exodus/medical/ward) "caF" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -50242,7 +50242,7 @@ pixel_x = 22; dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -50354,7 +50354,7 @@ /area/exodus/maintenance/research_starboard) "cbb" = ( /obj/structure/iv_drip, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -51388,7 +51388,7 @@ /obj/structure/cable/green{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -51745,7 +51745,7 @@ /area/exodus/maintenance/substation/research) "cdH" = ( /obj/structure/cable/green, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -52092,7 +52092,7 @@ /turf/floor/plating/airless, /area/exodus/research/test_area) "cet" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -52116,7 +52116,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/secondary/entry/fore) "cew" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -52301,7 +52301,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/medical/surgeryobs) "ceS" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -52774,7 +52774,7 @@ /area/exodus/engineering/break_room) "cfW" = ( /obj/machinery/light, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -53167,7 +53167,7 @@ /obj/random/tech_supply, /obj/random/tech_supply, /obj/random/tech_supply, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -53534,7 +53534,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/foyer) "chy" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -53698,7 +53698,7 @@ "chM" = ( /obj/structure/bedsheetbin/mapped, /obj/structure/table, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -53832,7 +53832,7 @@ /turf/floor/tiled/white, /area/exodus/research/xenobiology/xenoflora) "chZ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -53898,7 +53898,7 @@ /turf/floor/tiled/freezer, /area/exodus/research/xenobiology/xenoflora_storage) "cig" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -54026,7 +54026,7 @@ pixel_y = 3 }, /obj/item/briefcase/inflatable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -54185,7 +54185,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/locker_room) "ciQ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -54406,7 +54406,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -54769,7 +54769,7 @@ /obj/structure/cable/green{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -56643,7 +56643,7 @@ /obj/machinery/light{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -56787,7 +56787,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/research/xenobiology) "cot" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -57167,7 +57167,7 @@ /turf/floor/plating, /area/exodus/engineering/sublevel_access) "cpp" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -57824,7 +57824,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering) "crj" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -58213,7 +58213,7 @@ /obj/structure/table/reinforced, /obj/machinery/light, /obj/item/flashlight, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -58256,7 +58256,7 @@ /obj/structure/cable/green{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -58294,7 +58294,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/workshop) "csx" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -58352,7 +58352,7 @@ dir = 8; pixel_x = -24 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -58412,7 +58412,7 @@ dir = 4; pixel_x = 24 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -58437,7 +58437,7 @@ /area/space) "csN" = ( /obj/structure/closet/wardrobe/virology_white, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -58633,7 +58633,7 @@ /turf/floor/tiled/white, /area/exodus/crew_quarters/heads/hor) "ctA" = ( -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; name = "north bump"; pixel_y = 24 @@ -59723,7 +59723,7 @@ /turf/floor/plating, /area/exodus/engineering/storage) "cwy" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -60422,7 +60422,7 @@ /obj/structure/chair{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -61235,7 +61235,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/engine_monitoring) "cEM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -61535,7 +61535,7 @@ /turf/floor/plating, /area/exodus/engineering/engine_waste) "cGI" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -61654,7 +61654,7 @@ /area/space) "cHf" = ( /obj/machinery/light, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -62156,7 +62156,7 @@ /turf/floor/plating, /area/exodus/maintenance/engi_engine) "cJf" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "north bump"; pixel_y = 24 @@ -62725,7 +62725,7 @@ id_tag = "Engine Power"; name = "Powernet Sensor - Engine Power" }, -/obj/machinery/power/apc/super/critical{ +/obj/machinery/apc/super/critical{ dir = 1; name = "north bump"; pixel_y = 24 @@ -62917,7 +62917,7 @@ dir = 1; pixel_y = -22 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -63842,7 +63842,7 @@ /turf/floor/tiled/dark/monotile, /area/shuttle/arrival/station) "lFM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -64447,7 +64447,7 @@ /turf/floor/tiled/white, /area/exodus/research) "tGf" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -64831,7 +64831,7 @@ /area/exodus/teleporter) "yjb" = ( /obj/structure/cable/green, -/obj/machinery/power/apc/super/critical{ +/obj/machinery/apc/super/critical{ dir = 8; name = "west bump"; pixel_x = -24 diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 1ee33475b72d..0d703c96b6de 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -130,7 +130,7 @@ /turf/floor/tiled/steel_grid, /area/ministation/engine) "aL" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "_South APC"; pixel_y = -24 }, @@ -258,7 +258,7 @@ /turf/floor/tiled, /area/ministation/engine) "bp" = ( -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, @@ -2651,7 +2651,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, @@ -3588,7 +3588,7 @@ /turf/floor/tiled, /area/ministation/hall/n) "pz" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -4445,7 +4445,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, @@ -4773,7 +4773,7 @@ /turf/floor/tiled/steel_grid, /area/ministation/engine) "ve" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "_East APC"; pixel_x = 27; @@ -6706,7 +6706,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, @@ -6979,7 +6979,7 @@ /turf/floor/tiled/steel_grid, /area/ministation/engine) "Eb" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -8972,7 +8972,7 @@ /turf/floor/plating, /area/ministation/ai_upload) "IM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "_South APC"; pixel_y = -24 }, @@ -9582,7 +9582,7 @@ /area/ministation/ai_upload) "Ks" = ( /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "_South APC"; pixel_y = -24 }, @@ -9620,7 +9620,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -9867,7 +9867,7 @@ /turf/floor/plating, /area/ministation/ai_sat) "Lf" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "MiniSat Maint APC"; pixel_x = -24 @@ -10584,7 +10584,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, @@ -10611,7 +10611,7 @@ /obj/machinery/camera/network/engineering{ name = "SM North" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "SM APC"; pixel_y = 20; dir = 1 @@ -10872,7 +10872,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, @@ -10993,7 +10993,7 @@ /turf/floor/tiled, /area/ministation/engine) "OG" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "_South APC"; pixel_y = -24 }, @@ -11672,7 +11672,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, @@ -12089,7 +12089,7 @@ /area/ministation/dorms) "Sw" = ( /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "SM APC"; pixel_x = -27; pixel_y = null; @@ -13987,7 +13987,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 272d29948511..1ff78b8cade6 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -196,7 +196,7 @@ /turf/floor/tiled, /area/ministation/hall/w2) "aU" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -453,7 +453,7 @@ /obj/effect/floor_decal/floordetail/edgedrain{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "_West APC"; pixel_x = -25 @@ -1945,7 +1945,7 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "_South APC"; pixel_y = -24 }, @@ -2300,7 +2300,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "_West APC"; pixel_x = -25 @@ -2901,7 +2901,7 @@ /turf/floor/plating, /area/ministation/maint/secmaint) "oD" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -4392,7 +4392,7 @@ /turf/floor/lino, /area/ministation/cafe) "tT" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "_East APC"; pixel_x = 27; @@ -4438,7 +4438,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -4652,7 +4652,7 @@ /obj/structure/railing/mapped{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "_South APC"; pixel_y = -24 }, @@ -4839,7 +4839,7 @@ /turf/floor/plating, /area/ministation/maint/l2centrals) "vn" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -6805,7 +6805,7 @@ /turf/floor/carpet/blue3, /area/ministation/security) "CP" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -7853,7 +7853,7 @@ /turf/floor/tiled/white, /area/ministation/medical) "Jm" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "_East APC"; pixel_x = 27; @@ -8786,7 +8786,7 @@ /turf/floor/carpet/blue3, /area/ministation/security) "OF" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -9911,7 +9911,7 @@ /turf/floor/tiled, /area/ministation/hall/e2) "UY" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "_East APC"; pixel_x = 27; diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index 3b9ba99954de..c503f8dbd579 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -296,7 +296,7 @@ /turf/wall/r_wall, /area/ministation/telecomms) "bn" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Vault APC"; pixel_y = 25 @@ -719,7 +719,7 @@ /turf/floor/tiled, /area/ministation/bridge) "cu" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -1934,7 +1934,7 @@ /area/ministation/science) "iq" = ( /obj/effect/decal/cleanable/filth, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -2085,7 +2085,7 @@ /turf/floor/tiled, /area/ministation/hall/n3) "jd" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -2219,7 +2219,7 @@ /turf/floor/tiled/white, /area/ministation/science) "jy" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -3046,7 +3046,7 @@ /turf/wall/titanium, /area/ministation/shuttle/outgoing) "nI" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "_East APC"; pixel_x = 27; @@ -3303,7 +3303,7 @@ /turf/floor/laminate/walnut, /area/ministation/library) "pz" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -4003,7 +4003,7 @@ /area/ministation/science) "vJ" = ( /obj/effect/decal/cleanable/dirt/visible, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -4584,7 +4584,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "_West APC"; pixel_x = -25 @@ -5781,7 +5781,7 @@ /turf/floor/tiled/steel_grid, /area/ministation/science) "Il" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -6913,7 +6913,7 @@ /turf/floor/laminate/yew, /area/ministation/court) "Qk" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -6927,7 +6927,7 @@ /turf/floor/plating, /area/ministation/maint/l3se) "Qq" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -7031,7 +7031,7 @@ /turf/floor/laminate/mahogany, /area/ministation/library) "Re" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -7858,7 +7858,7 @@ pixel_y = 30; pixel_x = -33 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 @@ -8362,7 +8362,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -27 }, diff --git a/maps/ministation/ministation-3.dmm b/maps/ministation/ministation-3.dmm index e5b68a7fe209..0ffc162ec9aa 100644 --- a/maps/ministation/ministation-3.dmm +++ b/maps/ministation/ministation-3.dmm @@ -150,7 +150,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 20 }, diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm index 45318e7f6b90..8b1dd94704b9 100644 --- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm +++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm @@ -527,7 +527,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "_North APC"; pixel_y = 24 diff --git a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm index b5ae8b072cda..102a1b78168d 100644 --- a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm +++ b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm @@ -102,7 +102,7 @@ /area/map_template/hydrobase/station/growX) "as" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -342,7 +342,7 @@ /turf/floor/fixed/alium, /area/map_template/hydrobase/station/growD) "bc" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -718,7 +718,7 @@ /area/map_template/hydrobase/station/growD) "cc" = ( /obj/structure/cable/yellow, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 25 }, @@ -1006,7 +1006,7 @@ /turf/floor/tiled/white, /area/map_template/hydrobase/station/growC) "cS" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 25 }, @@ -1082,7 +1082,7 @@ /turf/floor/fixed/alium, /area/map_template/hydrobase/station/growF) "dc" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ pixel_y = -25 }, /obj/structure/cable/yellow{ @@ -1560,7 +1560,7 @@ /turf/floor/fixed/alium, /area/map_template/hydrobase/station/processing) "eq" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1706,7 +1706,7 @@ tag_interior_door = "solars_inner"; dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 25 }, @@ -1822,7 +1822,7 @@ /obj/structure/cable/yellow{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 25 }, @@ -1831,7 +1831,7 @@ /turf/floor/bluegrid, /area/map_template/hydrobase/station/growB) "eS" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -25 }, @@ -2204,7 +2204,7 @@ /turf/floor/fixed/alium, /area/map_template/hydrobase/station/growA) "fM" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -2233,7 +2233,7 @@ /area/map_template/hydrobase/station/shipaccess) "fP" = ( /obj/machinery/washing_machine, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 25 }, @@ -2494,7 +2494,7 @@ id_tag = "hydrodock_sensor"; pixel_y = 25 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index f98b8e3b8950..8f79725b6aac 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -662,7 +662,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -686,7 +686,7 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -1862,7 +1862,7 @@ /area/map_template/colony/engineering) "ej" = ( /obj/structure/table/steel_reinforced, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -2183,7 +2183,7 @@ /turf/floor/lino, /area/map_template/colony/messhall) "eO" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -2220,7 +2220,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -3034,7 +3034,7 @@ /obj/effect/floor_decal/techfloor{ dir = 10 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -3478,7 +3478,7 @@ /turf/floor/tiled/techfloor, /area/map_template/colony/atmospherics) "gY" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -3700,7 +3700,7 @@ /obj/effect/floor_decal/techfloor{ dir = 6 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -3940,7 +3940,7 @@ /turf/floor/laminate/walnut, /area/map_template/colony/commons) "hT" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -4176,7 +4176,7 @@ /area/map_template/colony/bathroom) "ip" = ( /obj/effect/floor_decal/borderfloorwhite, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -4423,7 +4423,7 @@ dir = 1 }, /obj/effect/floor_decal/techfloor, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -4676,7 +4676,7 @@ /area/map_template/colony/jail) "ji" = ( /obj/effect/floor_decal/techfloor, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -5362,7 +5362,7 @@ dir = 10; icon_state = "warning" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -5871,7 +5871,7 @@ /turf/floor/plating, /area/map_template/colony/jail) "lt" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -6868,7 +6868,7 @@ /turf/floor/plating, /area/map_template/colony/hydroponics) "nh" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 @@ -8002,7 +8002,7 @@ /area/map_template/colony/airlock) "Hy" = ( /obj/machinery/portable_atmospherics/hydroponics, -/obj/machinery/power/apc{ +/obj/machinery/apc{ locked = 0; name = "south bump"; pixel_y = -28 diff --git a/maps/tradeship/tradeship-0.dmm b/maps/tradeship/tradeship-0.dmm index 05ce95d98f90..5230d809f5ad 100644 --- a/maps/tradeship/tradeship-0.dmm +++ b/maps/tradeship/tradeship-0.dmm @@ -712,7 +712,7 @@ /turf/floor, /area/ship/trade/fore_port_underside_maint) "bD" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, @@ -932,7 +932,7 @@ /turf/floor, /area/ship/trade/aft_port_underside_maint) "bZ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, @@ -1499,7 +1499,7 @@ /turf/floor/tiled/white, /area/ship/trade/livestock) "xZ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, @@ -1724,7 +1724,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, @@ -1770,7 +1770,7 @@ /obj/structure/chair/comfy/green{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, @@ -1850,7 +1850,7 @@ /turf/wall/r_wall/hull, /area/ship/trade/undercomms) "Lx" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 22 }, @@ -2201,7 +2201,7 @@ /turf/floor, /area/ship/trade/aft_starboard_underside_maint) "Zz" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index c5b77f4f34ce..ef113b8ed76d 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -167,7 +167,7 @@ /turf/floor/plating, /area/ship/trade/cargo/lower) "aw" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -468,7 +468,7 @@ pixel_x = 24; dir = 8 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -493,7 +493,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 9 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -22 }, @@ -575,7 +575,7 @@ /turf/floor/plating, /area/ship/trade/maintenance/lower) "bj" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -888,7 +888,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 22 }, @@ -958,7 +958,7 @@ /obj/random/clothing, /obj/random/clothing, /obj/effect/decal/cleanable/cobweb, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -1502,7 +1502,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -1842,7 +1842,7 @@ level = 2 }, /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "Engineering Supply Storage APC"; pixel_x = 22 @@ -1876,7 +1876,7 @@ dir = 8; icon_state = "bulb1" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -2120,7 +2120,7 @@ /turf/wall/r_wall, /area/ship/trade/crew/dorms1) "lb" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -2396,7 +2396,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -2507,7 +2507,7 @@ dir = 4; icon_state = "bulb1" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ pixel_y = -22 }, /obj/machinery/alarm{ @@ -3117,7 +3117,7 @@ /obj/item/stock_parts/micro_laser, /obj/item/stock_parts/micro_laser/ultra, /obj/structure/rack, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index bdad0e37088e..03f9624c6d0b 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -135,7 +135,7 @@ icon_state = "1-2" }, /obj/structure/disposalpipe/segment, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "Crew Deck APC"; pixel_x = -22 @@ -1194,7 +1194,7 @@ /turf/wall, /area/ship/trade/crew/saloon) "cO" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Crew Areas APC"; pixel_y = 22 @@ -1387,7 +1387,7 @@ "ds" = ( /obj/structure/table, /obj/machinery/reagent_temperature, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -1666,7 +1666,7 @@ /obj/structure/window/reinforced/tinted, /obj/effect/decal/cleanable/dirt/visible, /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "Bathrooms APC"; pixel_x = 22 @@ -1806,7 +1806,7 @@ /area/space) "el" = ( /obj/structure/disposalpipe/segment, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "Crew Deck APC"; pixel_x = 22 @@ -1886,7 +1886,7 @@ icon_state = "0-2"; pixel_y = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "Galley APC"; pixel_x = 22 @@ -2177,7 +2177,7 @@ /area/ship/trade/crew/medbay) "fg" = ( /obj/item/pill_bottle/happy, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -2322,7 +2322,7 @@ /turf/floor/plating/airless, /area/ship/trade/maintenance/engine/starboard) "fA" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -2981,7 +2981,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/fuel{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -3055,7 +3055,7 @@ /turf/floor/plating, /area/ship/trade/maintenance/hallway) "hA" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "Medical Bay APC"; pixel_y = -22 }, @@ -3131,7 +3131,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 1; pixel_y = 22 }, @@ -4463,7 +4463,7 @@ /area/ship/trade/shuttle/outgoing/general) "ld" = ( /obj/machinery/atmospherics/pipe/simple/hidden/fuel, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 8; pixel_x = -22 }, @@ -4658,7 +4658,7 @@ /turf/wall/r_wall, /area/ship/trade/crew/medbay/chemistry) "nm" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 22 }, @@ -5057,7 +5057,7 @@ dir = 1; pixel_y = -24 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; pixel_x = 22 }, @@ -5248,7 +5248,7 @@ /obj/structure/handrail{ dir = 4 }, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 8; pixel_x = -22 }, @@ -5261,7 +5261,7 @@ /obj/structure/handrail{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -22 }, @@ -6061,7 +6061,7 @@ /turf/floor/tiled, /area/ship/trade/shuttle/rescue) "Dl" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "Medical Bay APC"; pixel_y = -22 }, @@ -6163,7 +6163,7 @@ dir = 4; icon_state = "tube1" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Communications APC"; pixel_y = 22 @@ -6202,7 +6202,7 @@ /obj/random/hat, /obj/random/hat, /obj/random/masks, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -22 }, @@ -6682,7 +6682,7 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Docking Area APC"; pixel_y = 22 @@ -6995,7 +6995,7 @@ dir = 4; icon_state = "conpipe-c" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Washroom APC"; pixel_y = 22 @@ -7187,7 +7187,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -22 }, @@ -7329,7 +7329,7 @@ "QH" = ( /obj/structure/table, /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "Medical Bay APC"; pixel_y = -22 }, @@ -7374,7 +7374,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "Captain's Quarters APC"; pixel_x = 22 @@ -7557,7 +7557,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Docking Area APC"; pixel_y = 22 @@ -7739,7 +7739,7 @@ /area/ship/trade/shuttle/outgoing/general) "Vc" = ( /obj/structure/cable, -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -22 }, @@ -7968,7 +7968,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, @@ -8086,7 +8086,7 @@ /obj/structure/closet/secure_closet/engineering_electrical, /obj/item/cell/device/standard, /obj/item/cell/device/standard, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -22 }, diff --git a/maps/tradeship/tradeship-3.dmm b/maps/tradeship/tradeship-3.dmm index 3ff3588e2d8f..bdbac519b57e 100644 --- a/maps/tradeship/tradeship-3.dmm +++ b/maps/tradeship/tradeship-3.dmm @@ -495,7 +495,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ pixel_y = -22 }, /obj/structure/cable{ @@ -714,7 +714,7 @@ /area/ship/trade/command/bridge_upper) "oq" = ( /obj/random/trash, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; pixel_x = -22 }, @@ -789,7 +789,7 @@ dir = 4; icon_state = "tube1" }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; name = "Communications APC"; pixel_y = 22 @@ -822,7 +822,7 @@ /area/space) "wc" = ( /obj/effect/floor_decal/steeldecal/steel_decals6, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 1; pixel_y = 22 }, diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm b/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm index ac4c19eb0c57..cc40f4f674a5 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm @@ -638,7 +638,7 @@ /turf/floor/plating, /area/lar_maria/morgue) "bZ" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -851,7 +851,7 @@ /turf/floor/plating, /area/lar_maria/morgue) "cA" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1711,7 +1711,7 @@ /turf/floor/tiled, /area/lar_maria/sec_wing) "eO" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -1742,7 +1742,7 @@ /turf/floor/tiled/white, /area/lar_maria/vir_ward) "eR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1933,7 +1933,7 @@ /turf/floor/tiled, /area/lar_maria/vir_hallway) "fq" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -1969,7 +1969,7 @@ /area/lar_maria/vir_aux) "fu" = ( /obj/structure/closet/crate/plastic/rations, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2515,7 +2515,7 @@ /turf/floor/plating, /area/lar_maria/sec_wing) "gv" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -2862,7 +2862,7 @@ /turf/floor/tiled, /area/lar_maria/vir_access) "hu" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm index e6ba0c32dd3b..3fa27c6e3b3a 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm @@ -573,7 +573,7 @@ /turf/floor/plating, /area/lar_maria/solar_control) "bB" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1314,7 +1314,7 @@ /turf/floor/plating, /area/lar_maria/atmos) "dS" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ name = "south bump"; pixel_y = -24 }, @@ -1399,7 +1399,7 @@ /turf/floor/tiled, /area/lar_maria/library) "eh" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1688,7 +1688,7 @@ /turf/floor/tiled/white, /area/lar_maria/head_m) "fl" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -1906,7 +1906,7 @@ /turf/floor/tiled, /area/lar_maria/office) "fR" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2042,7 +2042,7 @@ /turf/floor/laminate, /area/lar_maria/dorms) "gn" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 @@ -2328,7 +2328,7 @@ /turf/open, /area/lar_maria/hallway) "hi" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -2411,7 +2411,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 8; name = "west bump"; pixel_x = -24 @@ -2687,7 +2687,7 @@ /turf/floor/tiled/white, /area/lar_maria/head_f) "in" = ( -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4; name = "east bump"; pixel_x = 24 diff --git a/mods/content/generic_shuttles/tanker/tanker.dmm b/mods/content/generic_shuttles/tanker/tanker.dmm index c189bc9b389f..f109b52426b9 100644 --- a/mods/content/generic_shuttles/tanker/tanker.dmm +++ b/mods/content/generic_shuttles/tanker/tanker.dmm @@ -223,7 +223,7 @@ "vC" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ dir = 4; pixel_x = 24 }, diff --git a/mods/content/government/ruins/ec_old_crash/ec_old_crash.dmm b/mods/content/government/ruins/ec_old_crash/ec_old_crash.dmm index 42df960f6273..470059b9b2b6 100644 --- a/mods/content/government/ruins/ec_old_crash/ec_old_crash.dmm +++ b/mods/content/government/ruins/ec_old_crash/ec_old_crash.dmm @@ -426,7 +426,7 @@ dir = 4; pixel_x = -24 }, -/obj/machinery/power/apc/derelict, +/obj/machinery/apc/derelict, /turf/floor/tiled/airless, /area/map_template/ecship/cryo) "bh" = ( @@ -627,7 +627,7 @@ /obj/effect/floor_decal/corner/paleblue/diagonal{ dir = 4 }, -/obj/machinery/power/apc{ +/obj/machinery/apc{ dir = 4 }, /obj/structure/cable{ @@ -1095,7 +1095,7 @@ }, /obj/structure/closet/l3closet, /obj/structure/cable, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 4 }, /turf/floor/tiled/white/lowpressure, @@ -1256,7 +1256,7 @@ dir = 1; icon_state = "0-4" }, -/obj/machinery/power/apc/derelict{ +/obj/machinery/apc/derelict{ dir = 1 }, /turf/floor, diff --git a/mods/content/supermatter/endgame_cascade/universe.dm b/mods/content/supermatter/endgame_cascade/universe.dm index 46eb1efd5bf4..157dce06268c 100644 --- a/mods/content/supermatter/endgame_cascade/universe.dm +++ b/mods/content/supermatter/endgame_cascade/universe.dm @@ -89,7 +89,7 @@ alm.explosion_act(2) /datum/universal_state/supermatter_cascade/proc/APCSet() - for (var/obj/machinery/power/apc/APC in SSmachines.machinery) + for (var/obj/machinery/apc/APC in SSmachines.machinery) if (!(APC.stat & BROKEN) && !APC.is_critical) APC.chargemode = 0 var/obj/item/cell/cell = APC.get_cell() diff --git a/mods/content/supermatter/machinery/supermatter.dm b/mods/content/supermatter/machinery/supermatter.dm index 2fb27aadac35..cdf92f1538df 100644 --- a/mods/content/supermatter/machinery/supermatter.dm +++ b/mods/content/supermatter/machinery/supermatter.dm @@ -318,7 +318,7 @@ var/global/list/supermatter_delam_accent_sounds = list( to_chat(mob, "An invisible force slams you against the ground!") // Effect 2: Z-level wide electrical pulse - for(var/obj/machinery/power/apc/A in SSmachines.machinery) + for(var/obj/machinery/apc/A in SSmachines.machinery) if(!(A.z in affected_z)) continue diff --git a/mods/gamemodes/mercenary/maps/mercenary_base.dmm b/mods/gamemodes/mercenary/maps/mercenary_base.dmm index 152355b9aab0..7386a3cd58a0 100644 --- a/mods/gamemodes/mercenary/maps/mercenary_base.dmm +++ b/mods/gamemodes/mercenary/maps/mercenary_base.dmm @@ -378,7 +378,7 @@ /obj/structure/cable{ icon_state = "1-4" }, -/obj/machinery/power/apc/hyper{ +/obj/machinery/apc/hyper{ dir = 8; pixel_x = -21; req_access = list("ACCESS_MERCENARY") @@ -1143,7 +1143,7 @@ /turf/wall/titanium, /area/map_template/merc_shuttle/rear) "ch" = ( -/obj/machinery/power/apc/hyper{ +/obj/machinery/apc/hyper{ dir = 8; pixel_x = -21; req_access = list("ACCESS_MERCENARY") @@ -2360,7 +2360,7 @@ /turf/floor/plating, /area/map_template/merc_spawn) "HV" = ( -/obj/machinery/power/apc/high{ +/obj/machinery/apc/high{ pixel_y = -26 }, /obj/structure/cable, diff --git a/mods/species/ascent/machines/ship_machines.dm b/mods/species/ascent/machines/ship_machines.dm index 9db2e97943ca..9b9da51a4acf 100644 --- a/mods/species/ascent/machines/ship_machines.dm +++ b/mods/species/ascent/machines/ship_machines.dm @@ -1,4 +1,4 @@ -MANTIDIFY(/obj/machinery/power/apc/hyper, "mantid power node", "power controller") +MANTIDIFY(/obj/machinery/apc/hyper, "mantid power node", "power controller") MANTIDIFY(/obj/machinery/atmospherics/unary/vent_pump/on, "mantid atmosphere outlet", "vent") MANTIDIFY(/obj/machinery/atmospherics/unary/vent_scrubber/on, "mantid atmosphere intake", "scrubber") MANTIDIFY(/obj/machinery/hologram/holopad/longrange, "mantid holopad", "holopad") diff --git a/tools/map_migrations/5151_power_apc.txt b/tools/map_migrations/5151_power_apc.txt new file mode 100644 index 000000000000..3725ae8b5d13 --- /dev/null +++ b/tools/map_migrations/5151_power_apc.txt @@ -0,0 +1,2 @@ +# APCs are no longer a subtype of /obj/machinery/power +/obj/machinery/power/apc/@SUBTYPES : /obj/machinery/apc/@SUBTYPES{@OLD} \ No newline at end of file From ac2e3f5f74236a7bf0ea634d530cdeba58b5dbf8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Oct 2025 19:11:27 -0400 Subject: [PATCH 0819/1331] Clean up breaker box code --- .../programs/engineering/rcon_console.dm | 2 +- code/modules/power/breaker_box.dm | 112 ++++++++---------- 2 files changed, 52 insertions(+), 62 deletions(-) diff --git a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm index 10e61aa1ff08..224d0cdccc59 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm @@ -95,7 +95,7 @@ if(breaker.RCon_tag == href_list["toggle_breaker"]) toggle = breaker if(toggle) - if(toggle.update_locked) + if(toggle.lock_time > world.time) to_chat(usr, "The breaker box was recently toggled. Please wait before toggling it again.") else toggle.auto_toggle() diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index 9674fb2e47e8..2cd7d48af023 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -1,7 +1,6 @@ // Updated version of old powerswitch by Atlantis // Has better texture, and is now considered electronic device -// AI has ability to toggle it in 5 seconds -// Humans need 30 seconds (AI is faster when it comes to complex electronics) +// Requires 5 seconds to toggle and can be toggled once a minute // Used for advanced grid control (read: Substations) /obj/machinery/power/breakerbox @@ -9,24 +8,23 @@ icon = 'icons/obj/power.dmi' icon_state = "bbox_off" desc = "A large machine with heavy-duty switching circuits used for advanced grid control." - //directwired = 0 - var/icon_state_on = "bbox_on" - var/icon_state_off = "bbox_off" density = TRUE anchored = TRUE - var/on = 0 - var/busy = 0 - var/directions = list(1,2,4,8,5,6,9,10) - var/RCon_tag = "NO_TAG" - var/update_locked = 0 - construct_state = /decl/machine_construction/default/panel_closed stat_immune = 0 uncreated_component_parts = null base_type = /obj/machinery/power/breakerbox + var/icon_state_on = "bbox_on" + var/icon_state_off = "bbox_off" + var/on = FALSE + var/busy = FALSE + var/RCon_tag = "NO_TAG" + /// If world.time < lock_time, system is locked for interactions. + var/lock_time = 0 + /obj/machinery/power/breakerbox/activated - icon_state = "bbox_on" + icon_state = parent_type::icon_state_on // Enabled on server startup. Used in substations to keep them in bypass mode. /obj/machinery/power/breakerbox/activated/Initialize() @@ -34,75 +32,69 @@ return INITIALIZE_HINT_LATELOAD /obj/machinery/power/breakerbox/activated/LateInitialize() - set_state(1) + set_state(TRUE) . = ..() /obj/machinery/power/breakerbox/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(on) - . += "It seems to be online." + . += SPAN_GOOD("It seems to be online.") else - . += "It seems to be offline." + . += SPAN_WARNING("It seems to be offline.") -/obj/machinery/power/breakerbox/attack_ai(mob/living/silicon/ai/user) - if(update_locked) +/obj/machinery/power/breakerbox/proc/try_toggle_state(mob/living/user, digital = FALSE) + if(lock_time < world.time) to_chat(user, SPAN_WARNING("System locked. Please try again later.")) - return - - if(busy) - to_chat(user, SPAN_WARNING("System is busy. Please wait until current operation is finished before changing power settings.")) - return - - busy = 1 - to_chat(user, SPAN_GOOD("Updating power settings...")) - if(do_after(user, 50, src)) - set_state(!on) - to_chat(user, SPAN_GOOD("Update completed. New setting:[on ? "on": "off"]")) - update_locked = 1 - spawn(600) - update_locked = 0 - busy = 0 - -/obj/machinery/power/breakerbox/physical_attack_hand(mob/user) - if(update_locked) - to_chat(user, "System locked. Please try again later.") return TRUE if(busy) - to_chat(user, "System is busy. Please wait until current operation is finished before changing power settings.") + to_chat(user, SPAN_WARNING("System is busy. Please wait until current operation is finished before changing power settings.")) return TRUE - busy = 1 - user.visible_message(SPAN_NOTICE("\The [user] starts reprogramming \the [src]!")) - if(do_after(user, 50,src)) + busy = TRUE + if(digital) + to_chat(user, SPAN_GOOD("Updating power settings...")) + else + user.visible_message(SPAN_NOTICE("\The [user] starts reprogramming \the [src]!")) + if(do_after(user, 5 SECONDS, src)) set_state(!on) - user.visible_message( - SPAN_NOTICE("\The [user] [on ? "enabled" : "disabled"] \the [src]!"),\ - SPAN_NOTICE("You [on ? "enabled" : "disabled"] \the [src]!")) - update_locked = 1 - spawn(600) - update_locked = 0 - busy = 0 + if(digital) + to_chat(user, SPAN_GOOD("Update completed. New setting:[on ? "on": "off"]")) + else + user.visible_message( + SPAN_NOTICE("\The [user] [on ? "enabled" : "disabled"] \the [src]!"),\ + SPAN_NOTICE("You [on ? "enabled" : "disabled"] \the [src]!")) + lock_time = world.time + 1 MINUTE + busy = FALSE return TRUE -/obj/machinery/power/breakerbox/attackby(var/obj/item/used_item, var/mob/user) +/obj/machinery/power/breakerbox/attack_ai(mob/living/silicon/ai/user) + return try_toggle_state(user, digital = TRUE) + +/obj/machinery/power/breakerbox/physical_attack_hand(mob/user) + return try_toggle_state(user, digital = FALSE) + +/obj/machinery/power/breakerbox/attackby(obj/item/used_item, mob/user) if(IS_MULTITOOL(used_item)) var/newtag = input(user, "Enter new RCON tag. Use \"NO_TAG\" to disable RCON or leave empty to cancel.", "SMES RCON system") as text if(!CanPhysicallyInteract(user)) return TRUE if(newtag) RCon_tag = newtag - to_chat(user, "You changed the RCON tag to: [newtag]") + to_chat(user, SPAN_NOTICE("You changed the RCON tag to: [newtag]")) return TRUE - return ..() -/obj/machinery/power/breakerbox/proc/set_state(var/state) +/obj/machinery/power/breakerbox/on_update_icon() + . = ..() + icon_state = on ? icon_state_on : icon_state_off + +/obj/machinery/power/breakerbox/proc/set_state(state) on = state + update_icon() if(on) - icon_state = icon_state_on var/list/connection_dirs = list() - for(var/direction in directions) + for(var/direction in global.alldirs) for(var/obj/structure/cable/C in get_step(src,direction)) if(C.d1 == turn(direction, 180) || C.d2 == turn(direction, 180)) connection_dirs += direction @@ -112,7 +104,7 @@ var/obj/structure/cable/C = new/obj/structure/cable(src.loc) C.d1 = 0 C.d2 = direction - C.icon_state = "[C.d1]-[C.d2]" + C.update_icon() C.breaker_box = src var/datum/powernet/PN = new() @@ -121,18 +113,16 @@ C.mergeConnectedNetworks(C.d2) C.mergeConnectedNetworksOnTurf() - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + if(!IS_POWER_OF_TWO(C.d2))// if the cable is layed diagonally, check the others 2 possible directions C.mergeDiagonalsNetworks(C.d2) else - icon_state = icon_state_off for(var/obj/structure/cable/C in src.loc) qdel(C) // Used by RCON to toggle the breaker box. /obj/machinery/power/breakerbox/proc/auto_toggle() - if(!update_locked) - set_state(!on) - update_locked = 1 - spawn(600) - update_locked = 0 \ No newline at end of file + if(lock_time > world.time) + return FALSE // still on cooldown + set_state(!on) + lock_time = world.time + 1 MINUTE From b04c84ecb5d44b02e17b3a5317e1f531f74227e6 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 10 Oct 2025 12:19:58 +1100 Subject: [PATCH 0820/1331] Various supporting changes for modular map generator. --- code/_helpers/logging.dm | 6 ++++-- code/controllers/subsystems/mapping.dm | 7 ++++++- code/game/turfs/turf.dm | 2 +- code/modules/admin/verbs/map_template_loadverb.dm | 4 ++-- code/modules/maps/_map_template.dm | 2 ++ 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index 7e476912b248..942d9d9807f7 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -212,6 +212,8 @@ var/global/log_end= world.system_type == UNIX ? ascii2text(13) : "" return json_encode(d) return d.get_log_info_line() +var/global/_gag_report_progress = 0 /proc/report_progress(var/progress_message) - admin_notice("[progress_message]", R_DEBUG) - log_world(progress_message) + if(global._gag_report_progress <= 0) + admin_notice("[progress_message]", R_DEBUG) + to_world_log(progress_message) diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index cdba384cdf85..bcf8a6922368 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -10,6 +10,7 @@ SUBSYSTEM_DEF(mapping) var/list/submaps = list() var/list/map_templates_by_category = list() var/list/map_templates_by_type = list() + var/list/spawnable_map_templates = list() var/list/banned_maps = list() var/list/banned_template_names = list() @@ -169,11 +170,15 @@ SUBSYSTEM_DEF(mapping) map_templates = SSmapping.map_templates map_templates_by_category = SSmapping.map_templates_by_category map_templates_by_type = SSmapping.map_templates_by_type + spawnable_map_templates = SSmapping.spawnable_map_templates /datum/controller/subsystem/mapping/proc/register_map_template(var/datum/map_template/map_template) if(!validate_map_template(map_template) || !map_template.preload()) return FALSE - map_templates[map_template.name] = map_template + map_templates[map_template.name] = map_template + map_templates_by_type[map_template.type] = map_template + if(map_template.is_spawnable) + spawnable_map_templates += map_template for(var/temple_cat in map_template.template_categories) // :3 LAZYINITLIST(map_templates_by_category[temple_cat]) LAZYSET(map_templates_by_category[temple_cat], map_template.name, map_template) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 285f4fc05c5c..a9a61db02d40 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -644,7 +644,7 @@ return zone.air?.graphic if(external_atmosphere_participation && is_outside()) var/datum/level_data/level = SSmapping.levels_by_z[z] - return level.exterior_atmosphere.graphic + return level.exterior_atmosphere?.graphic var/datum/gas_mixture/environment = return_air() return environment?.graphic diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index 6d5b6f65e481..d588df659e21 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -5,7 +5,7 @@ if (!check_rights(R_FUN)) return - var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.map_templates + var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.spawnable_map_templates if(!map) return @@ -37,7 +37,7 @@ to_chat(usr, "Please wait for the master controller to initialize before loading maps!") return - var/map = input(usr, "Choose a Map Template to place on a new zlevel","Place Map Template") as null|anything in SSmapping.map_templates + var/map = input(usr, "Choose a Map Template to place on a new zlevel","Place Map Template") as null|anything in SSmapping.spawnable_map_templates if(!map) return diff --git a/code/modules/maps/_map_template.dm b/code/modules/maps/_map_template.dm index f7a22eaf01fe..7f485c209677 100644 --- a/code/modules/maps/_map_template.dm +++ b/code/modules/maps/_map_template.dm @@ -26,6 +26,8 @@ var/list/template_categories ///The initial type of level_data to instantiate new z-level with initially. (Is replaced by whatever is in the map file.) If null, will use default. var/level_data_type + /// Whether or not this should show up for admin map spawning. + var/is_spawnable = TRUE /// Various tags used for selecting templates for placement on a map. var/template_tags = 0 From 4b3144a3cfbb0a02164a8a9a08d6172244937e70 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 23 Jan 2025 17:35:02 +1100 Subject: [PATCH 0821/1331] Added nails. --- code/modules/materials/material_stack_nail.dm | 16 ++++++++++ icons/obj/items/stacks/nails.dmi | Bin 0 -> 533 bytes .../blacksmithy/forging_step_billets.dm | 29 +++++++++--------- .../content/blacksmithy/forging_step_tools.dm | 4 +++ nebula.dme | 1 + 5 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 code/modules/materials/material_stack_nail.dm create mode 100644 icons/obj/items/stacks/nails.dmi diff --git a/code/modules/materials/material_stack_nail.dm b/code/modules/materials/material_stack_nail.dm new file mode 100644 index 000000000000..fb0efb778825 --- /dev/null +++ b/code/modules/materials/material_stack_nail.dm @@ -0,0 +1,16 @@ +/obj/item/stack/material/nail + name = "nails" + singular_name = "nail" + plural_name = "nails" + icon_state = "nail" + plural_icon_state = "nail-mult" + max_icon_state = "nail-max" + stack_merge_type = /obj/item/stack/material/nail + crafting_stack_type = /obj/item/stack/material/nail + icon = 'icons/obj/items/stacks/nails.dmi' + is_spawnable_type = TRUE + matter_multiplier = 0.05 // 20 per standard sheet + material = /decl/material/solid/metal/iron + +/obj/item/stack/material/nail/twelve + amount = 12 diff --git a/icons/obj/items/stacks/nails.dmi b/icons/obj/items/stacks/nails.dmi new file mode 100644 index 0000000000000000000000000000000000000000..10ba6ebcc9891aa17743b06b2a3a0c8298f685bd GIT binary patch literal 533 zcmV+w0_y#VP)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3K9+FEKMmiHkEOv#1!zHRR$E@Q^ln|*hu>zMuWd&D17qCMC!!j|I zF4~Y)0003nNklrJd$BNN!l5b?CRF`WS|P6G80e~k3H=SL*JvEcOL*Q@U&bn+i~VPlfkq$V zU%#Y3N}|82nB)ic!>Dr-S^KV$oJ0hq&M1#0g1Fdw2S^f`c|yvc`Am|;d4Uuc*SQW! zBF@1F<{QrMXBGDc4@4=hr-wXw5Sf|`e+kzm^WZ=m-O09$nFzu`iMOqHz$Xuu&Q)aK zJa}L`uOVYHlNXcv%!10mS Date: Thu, 9 Oct 2025 20:35:53 -0400 Subject: [PATCH 0822/1331] Make the supermatter crystal not a machine --- maps/away/unishi/unishi-2.dmm | 2 +- maps/away/unishi/unishi.dm | 8 +- maps/exodus/exodus-2.dmm | 2 +- maps/ministation/ministation-0.dmm | 2 +- mods/content/supermatter/_supermatter.dme | 3 +- .../supermatter/admin_setup_supermatter.dm | 2 +- mods/content/supermatter/datums/sm_codex.dm | 2 +- mods/content/supermatter/datums/sm_follow.dm | 2 +- .../supermatter/datums/sm_grief_fix.dm | 2 +- .../supermatter/datums/sm_subsystem.dm | 7 ++ .../supermatter/datums/sm_supply_drop.dm | 2 +- .../supermatter/datums/sm_supply_pack.dm | 2 +- .../supermatter/datums/supermatter_monitor.dm | 12 +-- .../overrides/sm_fuel_compressor.dm | 2 +- .../supermatter/overrides/sm_trader.dm | 2 +- .../overrides/sm_xenoarchaeology.dm | 4 +- .../supermatter_crystal.dm} | 79 ++++++++++--------- 17 files changed, 74 insertions(+), 61 deletions(-) create mode 100644 mods/content/supermatter/datums/sm_subsystem.dm rename mods/content/supermatter/{machinery/supermatter.dm => structures/supermatter_crystal.dm} (92%) diff --git a/maps/away/unishi/unishi-2.dmm b/maps/away/unishi/unishi-2.dmm index 5f96d95191cc..d6e536822aa5 100644 --- a/maps/away/unishi/unishi-2.dmm +++ b/maps/away/unishi/unishi-2.dmm @@ -2727,7 +2727,7 @@ /area/unishi/smresearch) "ho" = ( /obj/machinery/atmospherics/pipe/simple/heat_exchanging, -/obj/machinery/power/supermatter/inert{ +/obj/structure/supermatter/inert{ color = "" }, /obj/machinery/mass_driver{ diff --git a/maps/away/unishi/unishi.dm b/maps/away/unishi/unishi.dm index 9876826f1ff6..665b85cc7ecf 100644 --- a/maps/away/unishi/unishi.dm +++ b/maps/away/unishi/unishi.dm @@ -49,12 +49,12 @@ landmark_tag = "nav_unishi_3" -/obj/machinery/power/supermatter/randomsample +/obj/structure/supermatter/randomsample name = "experimental supermatter sample" icon = 'icons/obj/supermatter_32.dmi' icon_state = "supermatter_shard" -/obj/machinery/power/supermatter/randomsample/Initialize() +/obj/structure/supermatter/randomsample/Initialize() . = ..() nitrogen_retardation_factor = rand(0.01, 1) //Higher == N2 slows reaction more thermal_release_modifier = rand(100, 1000000) //Higher == more heat released during reaction @@ -69,7 +69,7 @@ charging_factor = rand(0, 1) damage_rate_limit = rand( 1, 10) //damage rate cap at power = 300, scales linearly with power -/obj/machinery/power/supermatter/inert +/obj/structure/supermatter/inert name = "experimental supermatter sample" icon = 'icons/obj/supermatter_32.dmi' icon_state = "supermatter_shard" @@ -87,7 +87,7 @@ desc = "Are you sure you want to open this?" /obj/structure/closet/crate/secure/large/supermatter/experimentalsm/WillContain() - return list(/obj/machinery/power/supermatter/randomsample) + return list(/obj/structure/supermatter/randomsample) /obj/item/paper/prof1 name = "error log" info = " COMPUTER ID: 15231
    Attempting recovery of document directory.
    Three files recovered
    Printing file (1/2)
    ... about your concerns. I told you that the shielding is strong enough to avoid ANY leaks of radiation or hazardous materials. The entire lab is 100% isolated from the ship in terms of even the air supply. Leave me and my students the fuck alone. Your job is to maintain the fucking reactor an !#@!dqma211.
    File (2/3) Tested SM This thing has a lot of potential. It doesn't produce any measurable levels of gas, or even significant thermal signature. The potential is nearly limitless. We've had to fine tune our activation procedures as even a short beam of the emitter seems to activate this thing. CTI Engineering dept still won't fucking answer where they got this thing, but it's simply amazing. I've sent an ema #@^%da12k" diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index ee4834d3cee5..42b521da4439 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -56232,7 +56232,7 @@ /turf/floor/plating, /area/exodus/maintenance/portsolar) "cnh" = ( -/obj/machinery/power/supermatter, +/obj/structure/supermatter, /obj/machinery/mass_driver{ id_tag = "enginecore" }, diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 0d703c96b6de..d54f9cdb73f0 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -11507,7 +11507,7 @@ /turf/floor/tiled, /area/ministation/hall/n) "Qv" = ( -/obj/machinery/power/supermatter, +/obj/structure/supermatter, /obj/machinery/mass_driver{ id_tag = "eject" }, diff --git a/mods/content/supermatter/_supermatter.dme b/mods/content/supermatter/_supermatter.dme index fb6723cce50b..93f116effd95 100644 --- a/mods/content/supermatter/_supermatter.dme +++ b/mods/content/supermatter/_supermatter.dme @@ -7,6 +7,7 @@ #include "datums\sm_follow.dm" #include "datums\sm_grief_fix.dm" #include "datums\sm_looping_sound.dm" +#include "datums\sm_subsystem.dm" #include "datums\sm_supply_drop.dm" #include "datums\sm_supply_pack.dm" #include "datums\supermatter_monitor.dm" @@ -16,7 +17,6 @@ #include "items\sm_book.dm" #include "items\sm_grenade.dm" #include "machinery\sm_supply_beacon.dm" -#include "machinery\supermatter.dm" #include "machinery\supermatter_core_console.dm" #include "overrides\sm_fuel_compressor.dm" #include "overrides\sm_meteor.dm" @@ -26,5 +26,6 @@ #include "overrides\sm_unit_tests.dm" #include "overrides\sm_xenoarchaeology.dm" #include "structures\sm_closets.dm" +#include "structures\supermatter_crystal.dm" // END_INCLUDE #endif diff --git a/mods/content/supermatter/admin_setup_supermatter.dm b/mods/content/supermatter/admin_setup_supermatter.dm index 9664671e8361..e9de8a41e924 100644 --- a/mods/content/supermatter/admin_setup_supermatter.dm +++ b/mods/content/supermatter/admin_setup_supermatter.dm @@ -101,7 +101,7 @@ if(!last) return ENGINE_SETUP_DELAYED ..() - var/obj/machinery/power/supermatter/SM = locate() in get_turf(src) + var/obj/structure/supermatter/SM = locate() in get_turf(src) if(!SM) log_and_message_admins("## ERROR: Unable to locate supermatter core at [x] [y] [z]!") return ENGINE_SETUP_ERROR diff --git a/mods/content/supermatter/datums/sm_codex.dm b/mods/content/supermatter/datums/sm_codex.dm index 72810edb7a41..8dbf88ea7522 100644 --- a/mods/content/supermatter/datums/sm_codex.dm +++ b/mods/content/supermatter/datums/sm_codex.dm @@ -4,7 +4,7 @@ available_to_map_tech_level = MAP_TECH_LEVEL_SPACE /datum/codex_entry/supermatter - associated_paths = list(/obj/machinery/power/supermatter) + associated_paths = list(/obj/structure/supermatter) mechanics_text = "When energized by a laser (or something hitting it), it emits radiation and heat. If the heat reaches above 7000 kelvin, it will send an alert and start taking damage. \ After integrity falls to zero percent, it will delaminate, causing a massive explosion, station-wide radiation spikes, and hallucinations. \ Supermatter reacts badly to oxygen in the atmosphere. It'll also heat up really quick if it is in vacuum.
    \ diff --git a/mods/content/supermatter/datums/sm_follow.dm b/mods/content/supermatter/datums/sm_follow.dm index 5b221ad03d61..4f03605d61d7 100644 --- a/mods/content/supermatter/datums/sm_follow.dm +++ b/mods/content/supermatter/datums/sm_follow.dm @@ -1,3 +1,3 @@ /datum/follow_holder/supermatter sort_order = 10 - followed_type = /obj/machinery/power/supermatter \ No newline at end of file + followed_type = /obj/structure/supermatter \ No newline at end of file diff --git a/mods/content/supermatter/datums/sm_grief_fix.dm b/mods/content/supermatter/datums/sm_grief_fix.dm index 70dd9469abc1..12f2db8dbf2f 100644 --- a/mods/content/supermatter/datums/sm_grief_fix.dm +++ b/mods/content/supermatter/datums/sm_grief_fix.dm @@ -4,5 +4,5 @@ /decl/atmos_grief_fix_step/supermatter/act() // Depower the supermatter, as it would quickly blow up once we remove all gases from the pipes. - for(var/obj/machinery/power/supermatter/S in SSmachines.machinery) + for(var/obj/structure/supermatter/S in SSsupermatter.processing) S.power = 0 \ No newline at end of file diff --git a/mods/content/supermatter/datums/sm_subsystem.dm b/mods/content/supermatter/datums/sm_subsystem.dm new file mode 100644 index 000000000000..01b2c19da591 --- /dev/null +++ b/mods/content/supermatter/datums/sm_subsystem.dm @@ -0,0 +1,7 @@ +// A replacement for having the supermatter tick on SSmachinery, since it's no longer a machine. +PROCESSING_SUBSYSTEM_DEF(supermatter) + name = "Supermatter" + priority = SS_PRIORITY_MACHINERY + flags = SS_KEEP_TIMING|SS_NO_INIT + runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME + wait = 2 SECONDS \ No newline at end of file diff --git a/mods/content/supermatter/datums/sm_supply_drop.dm b/mods/content/supermatter/datums/sm_supply_drop.dm index 1560d1118d2c..fdff0e1d2fe2 100644 --- a/mods/content/supermatter/datums/sm_supply_drop.dm +++ b/mods/content/supermatter/datums/sm_supply_drop.dm @@ -2,4 +2,4 @@ name = "Supermatter" /datum/supply_drop_loot/supermatter/New() ..() - contents = list(/obj/machinery/power/supermatter) + contents = list(/obj/structure/supermatter) diff --git a/mods/content/supermatter/datums/sm_supply_pack.dm b/mods/content/supermatter/datums/sm_supply_pack.dm index 5261f06a0693..aa34afe44995 100644 --- a/mods/content/supermatter/datums/sm_supply_pack.dm +++ b/mods/content/supermatter/datums/sm_supply_pack.dm @@ -1,6 +1,6 @@ /decl/hierarchy/supply_pack/engineering/smbig name = "Power - Supermatter core" - contains = list(/obj/machinery/power/supermatter) + contains = list(/obj/structure/supermatter) containertype = /obj/structure/closet/crate/secure/large/supermatter containername = "\improper Supermatter crate (CAUTION)" access = access_ce \ No newline at end of file diff --git a/mods/content/supermatter/datums/supermatter_monitor.dm b/mods/content/supermatter/datums/supermatter_monitor.dm index 2c20605a6b16..73805c939678 100644 --- a/mods/content/supermatter/datums/supermatter_monitor.dm +++ b/mods/content/supermatter/datums/supermatter_monitor.dm @@ -32,7 +32,7 @@ /datum/nano_module/program/supermatter_monitor name = "Supermatter monitor" var/list/supermatters - var/obj/machinery/power/supermatter/active = null // Currently selected supermatter crystal. + var/obj/structure/supermatter/active = null // Currently selected supermatter crystal. var/screen = SM_MONITOR_SCREEN_MAIN // Which screen the monitor is currently on /datum/nano_module/program/supermatter_monitor/Destroy() @@ -44,7 +44,7 @@ ..() refresh() -/datum/nano_module/program/supermatter_monitor/proc/can_read(obj/machinery/power/supermatter/S) +/datum/nano_module/program/supermatter_monitor/proc/can_read(obj/structure/supermatter/S) if(!istype(S.loc, /turf/)) return FALSE if(S.exploded || S.grav_pulling) @@ -57,7 +57,7 @@ // Refreshes list of active supermatter crystals /datum/nano_module/program/supermatter_monitor/proc/refresh() supermatters = list() - for(var/obj/machinery/power/supermatter/S in SSmachines.machinery) + for(var/obj/structure/supermatter/S in SSsupermatter.processing) // Delaminating, not within coverage, not on a tile. if(!can_read(S)) continue @@ -70,7 +70,7 @@ /datum/nano_module/program/supermatter_monitor/proc/get_status() . = SUPERMATTER_INACTIVE var/needs_refresh - for(var/obj/machinery/power/supermatter/S in supermatters) + for(var/obj/structure/supermatter/S in supermatters) if(!can_read(S)) needs_refresh = TRUE continue @@ -161,7 +161,7 @@ else var/list/SMS = list() var/needs_refresh //need to refresh because some of crystals are not readable. For finding new crystals user can just refresh manually like a scrub - for(var/obj/machinery/power/supermatter/S in supermatters) + for(var/obj/structure/supermatter/S in supermatters) var/area/A = get_area(S) if(!A) continue @@ -211,7 +211,7 @@ return 1 if( href_list["set"] ) var/newuid = text2num(href_list["set"]) - for(var/obj/machinery/power/supermatter/S in supermatters) + for(var/obj/structure/supermatter/S in supermatters) if(S.uid == newuid) active = S return 1 diff --git a/mods/content/supermatter/overrides/sm_fuel_compressor.dm b/mods/content/supermatter/overrides/sm_fuel_compressor.dm index e259fa266258..0207c648c6ef 100644 --- a/mods/content/supermatter/overrides/sm_fuel_compressor.dm +++ b/mods/content/supermatter/overrides/sm_fuel_compressor.dm @@ -2,7 +2,7 @@ . = ..() if(.) return TRUE - if(istype(thing, /obj/machinery/power/supermatter/shard)) + if(istype(thing, /obj/structure/supermatter/shard)) var/exotic_matter_amount = thing?.matter?[/decl/material/solid/exotic_matter] if(exotic_matter_amount <= 0) return FALSE diff --git a/mods/content/supermatter/overrides/sm_trader.dm b/mods/content/supermatter/overrides/sm_trader.dm index ded45374bd98..dbadf5961a79 100644 --- a/mods/content/supermatter/overrides/sm_trader.dm +++ b/mods/content/supermatter/overrides/sm_trader.dm @@ -1,3 +1,3 @@ /datum/trader/ship/unique/rock/New() ..() - possible_trading_items[/obj/machinery/power/supermatter] = TRADER_ALL \ No newline at end of file + possible_trading_items[/obj/structure/supermatter] = TRADER_ALL \ No newline at end of file diff --git a/mods/content/supermatter/overrides/sm_xenoarchaeology.dm b/mods/content/supermatter/overrides/sm_xenoarchaeology.dm index 537de7a311cf..13861abcc41e 100644 --- a/mods/content/supermatter/overrides/sm_xenoarchaeology.dm +++ b/mods/content/supermatter/overrides/sm_xenoarchaeology.dm @@ -1,7 +1,7 @@ /datum/artifact_find/New() var/static/supermatter_injected = FALSE if(!supermatter_injected) - potential_finds[/obj/machinery/power/supermatter] = 5 - potential_finds[/obj/machinery/power/supermatter/shard] = 25 + potential_finds[/obj/structure/supermatter] = 5 + potential_finds[/obj/structure/supermatter/shard] = 25 supermatter_injected = TRUE ..() diff --git a/mods/content/supermatter/machinery/supermatter.dm b/mods/content/supermatter/structures/supermatter_crystal.dm similarity index 92% rename from mods/content/supermatter/machinery/supermatter.dm rename to mods/content/supermatter/structures/supermatter_crystal.dm index cdf92f1538df..cbd3e878fa57 100644 --- a/mods/content/supermatter/machinery/supermatter.dm +++ b/mods/content/supermatter/structures/supermatter_crystal.dm @@ -103,7 +103,7 @@ var/global/list/supermatter_delam_accent_sounds = list( var/rads = 500 SSradiation.radiate(source, rads) -/obj/machinery/power/supermatter +/obj/structure/supermatter name = "supermatter crystal" desc = "A strangely translucent and iridescent crystal. You get headaches just from looking at it." icon = 'icons/obj/supermatter_48.dmi' @@ -189,6 +189,9 @@ var/global/list/supermatter_delam_accent_sounds = list( var/datum/composite_sound/supermatter/soundloop + // A uniquely-identifying number assigned to this supermatter crystal. Mostly used by the supermatter monitoring console. + var/uid = 0 + var/damage_animation = FALSE //are we doing our damage animation? var/list/threshholds = list( // List of lists defining the amber/red labeling threshholds in readouts. Numbers are minminum red and amber and maximum amber and red, in that order @@ -198,26 +201,28 @@ var/global/list/supermatter_delam_accent_sounds = list( list("name" = SUPERMATTER_DATA_EPR, "min_h" = -1, "min_l" = 1.0, "max_l" = 2.5, "max_h" = 4.0) ) -/obj/machinery/power/supermatter/Initialize() +/obj/structure/supermatter/Initialize() . = ..() - uid = gl_uid++ + uid = sequential_id(/obj/structure/supermatter) soundloop = new(list(src), TRUE) update_icon() add_filter("outline", 1, list(type = "drop_shadow", size = 0, color = COLOR_WHITE, x = 0, y = 0)) + START_PROCESSING(SSsupermatter, src) -/obj/machinery/power/supermatter/Destroy() - . = ..() +/obj/structure/supermatter/Destroy() QDEL_NULL(soundloop) + STOP_PROCESSING(SSsupermatter, src) + . = ..() -/obj/machinery/power/supermatter/on_update_icon() +/obj/structure/supermatter/on_update_icon() . = ..() underlays.Cut() underlays += mutable_appearance(icon, "[icon_state]_underplate", flags = RESET_COLOR, plane = plane, layer = OBJ_LAYER) -/obj/machinery/power/supermatter/get_matter_amount_modifier() +/obj/structure/supermatter/get_matter_amount_modifier() . = ..() * (1/HOLLOW_OBJECT_MATTER_MULTIPLIER) * 10 // Big solid chunk of matter. -/obj/machinery/power/supermatter/proc/handle_admin_warnings() +/obj/structure/supermatter/proc/handle_admin_warnings() if(disable_adminwarn) return @@ -238,7 +243,7 @@ var/global/list/supermatter_delam_accent_sounds = list( else aw_EPR = FALSE -/obj/machinery/power/supermatter/proc/status_adminwarn_check(var/min_status, var/current_state, var/message, var/send_webhook = FALSE) +/obj/structure/supermatter/proc/status_adminwarn_check(var/min_status, var/current_state, var/message, var/send_webhook = FALSE) var/status = get_status() if(status >= min_status) if(!current_state) @@ -249,7 +254,7 @@ var/global/list/supermatter_delam_accent_sounds = list( else return FALSE -/obj/machinery/power/supermatter/proc/get_epr() +/obj/structure/supermatter/proc/get_epr() var/turf/T = get_turf(src) if(!istype(T)) return @@ -258,7 +263,7 @@ var/global/list/supermatter_delam_accent_sounds = list( return 0 return round((air.total_moles / air.group_multiplier) / 23.1, 0.01) -/obj/machinery/power/supermatter/proc/get_status() +/obj/structure/supermatter/proc/get_status() var/turf/T = get_turf(src) if(!T) return SUPERMATTER_ERROR @@ -286,7 +291,7 @@ var/global/list/supermatter_delam_accent_sounds = list( return SUPERMATTER_INACTIVE -/obj/machinery/power/supermatter/proc/explode() +/obj/structure/supermatter/proc/explode() set waitfor = 0 if(exploded) @@ -354,7 +359,7 @@ var/global/list/supermatter_delam_accent_sounds = list( explosion(TS, explosion_power/2, explosion_power, explosion_power * 2, explosion_power * 4, 1) qdel(src) -/obj/machinery/power/supermatter/get_examine_strings(mob/user, distance, infix, suffix) +/obj/structure/supermatter/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(user.skill_check(SKILL_ENGINES, SKILL_EXPERT)) var/integrity_message @@ -373,19 +378,19 @@ var/global/list/supermatter_delam_accent_sounds = list( . += "Eyeballing it, you place the relative EER at around [display_power] MeV/cm3." //Changes color and luminosity of the light to these values if they were not already set -/obj/machinery/power/supermatter/proc/shift_light(var/lum, var/clr) +/obj/structure/supermatter/proc/shift_light(var/lum, var/clr) if(lum != light_range || abs(power - last_power) > 10 || clr != light_color) set_light(lum, LIGHT_POWER_CALC, clr) last_power = power -/obj/machinery/power/supermatter/proc/get_integrity() +/obj/structure/supermatter/proc/get_integrity() var/integrity = damage / explosion_point integrity = round(100 - integrity * 100) integrity = integrity < 0 ? 0 : integrity return integrity -/obj/machinery/power/supermatter/proc/announce_warning() +/obj/structure/supermatter/proc/announce_warning() var/integrity = get_integrity() var/alert_msg = " Integrity at [integrity]%" @@ -418,7 +423,7 @@ var/global/list/supermatter_delam_accent_sounds = list( public_alert = 0 -/obj/machinery/power/supermatter/Process() +/obj/structure/supermatter/Process() var/turf/L = loc if(isnull(L)) // We have a null turf...something is wrong, stop processing this entity. @@ -553,7 +558,7 @@ var/global/list/supermatter_delam_accent_sounds = list( return 1 -/obj/machinery/power/supermatter/proc/start_damage_animation() +/obj/structure/supermatter/proc/start_damage_animation() if(damage_animation) return if(!get_filter("rays")) @@ -565,10 +570,10 @@ var/global/list/supermatter_delam_accent_sounds = list( animate(time = 2 SECONDS, size = 10, loop=-1, flags = ANIMATION_PARALLEL) addtimer(CALLBACK(src, PROC_REF(finish_damage_animation)), 12 SECONDS) -/obj/machinery/power/supermatter/proc/finish_damage_animation() +/obj/structure/supermatter/proc/finish_damage_animation() damage_animation = FALSE -/obj/machinery/power/supermatter/bullet_act(var/obj/item/projectile/Proj) +/obj/structure/supermatter/bullet_act(var/obj/item/projectile/Proj) var/turf/L = loc if(!istype(L)) // We don't run process() when we are in space return 0 // This stops people from being able to really power up the supermatter @@ -582,23 +587,23 @@ var/global/list/supermatter_delam_accent_sounds = list( damage += proj_damage * config_bullet_energy return 0 -/obj/machinery/power/supermatter/attack_robot(mob/user) +/obj/structure/supermatter/attack_robot(mob/user) ui_interact(user) return TRUE -/obj/machinery/power/supermatter/attack_ai(mob/living/silicon/ai/user) +/obj/structure/supermatter/attack_ai(mob/living/silicon/ai/user) ui_interact(user) return TRUE -/obj/machinery/power/supermatter/attack_ghost(mob/user) +/obj/structure/supermatter/attack_ghost(mob/user) ui_interact(user) return TRUE -/obj/machinery/power/supermatter/attack_hand(mob/user) +/obj/structure/supermatter/attack_hand(mob/user) return Consume(null, user, TRUE) || ..() // This is purely informational UI that may be accessed by AIs or robots -/obj/machinery/power/supermatter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/structure/supermatter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] data["integrity_percentage"] = round(get_integrity()) @@ -624,7 +629,7 @@ var/global/list/supermatter_delam_accent_sounds = list( ui.open() ui.set_auto_update(1) -/obj/machinery/power/supermatter/attackby(obj/item/used_item, mob/user) +/obj/structure/supermatter/attackby(obj/item/used_item, mob/user) if(istype(used_item, /obj/item/stack/tape_roll/duct_tape)) var/obj/item/stack/tape_roll/duct_tape/T = used_item @@ -648,11 +653,11 @@ var/global/list/supermatter_delam_accent_sounds = list( user.apply_damage(150, IRRADIATE, damage_flags = DAM_DISPERSED) return TRUE -/obj/machinery/power/supermatter/Bumped(atom/AM) +/obj/structure/supermatter/Bumped(atom/AM) if(!Consume(null, AM)) return ..() -/obj/machinery/power/supermatter/proc/Consume(var/mob/living/user, var/obj/item/thing, var/touched) +/obj/structure/supermatter/proc/Consume(var/mob/living/user, var/obj/item/thing, var/touched) . = try_supermatter_consume(user, thing, src, touched) if(. <= 0) return @@ -663,19 +668,19 @@ var/global/list/supermatter_delam_accent_sounds = list( for(var/atom/A in range(pull_range, target)) A.singularity_pull(target, pull_power) -/obj/machinery/power/supermatter/GotoAirflowDest(n) //Supermatter not pushed around by airflow +/obj/structure/supermatter/GotoAirflowDest(n) //Supermatter not pushed around by airflow return -/obj/machinery/power/supermatter/RepelAirflowDest(n) +/obj/structure/supermatter/RepelAirflowDest(n) return -/obj/machinery/power/supermatter/explosion_act(var/severity) +/obj/structure/supermatter/explosion_act(var/severity) . = ..() if(.) power *= max(1, 5 - severity) log_and_message_admins("WARN: Explosion near the Supermatter! New EER: [power].") -/obj/machinery/power/supermatter/singularity_act() +/obj/structure/supermatter/singularity_act() if(!src.loc) return @@ -689,10 +694,10 @@ var/global/list/supermatter_delam_accent_sounds = list( qdel(src) return 50000 -/obj/machinery/power/supermatter/get_artifact_scan_data() +/obj/structure/supermatter/get_artifact_scan_data() return "Superdense crystalline structure - appears to have been shaped or hewn, lattice is approximately 20 times denser than should be possible." -/obj/machinery/power/supermatter/shard //Small subtype, less efficient and more sensitive, but less boom. +/obj/structure/supermatter/shard //Small subtype, less efficient and more sensitive, but less boom. name = "supermatter shard" desc = "A strangely translucent and iridescent crystal that looks like it used to be part of a larger structure. You get headaches just from looking at it." icon = 'icons/obj/supermatter_32.dmi' @@ -708,14 +713,14 @@ var/global/list/supermatter_delam_accent_sounds = list( pull_time = 150 explosion_power = 3 -/obj/machinery/power/supermatter/medium +/obj/structure/supermatter/medium icon = 'icons/obj/supermatter_32.dmi' w_class = (ITEM_SIZE_STRUCTURE + ITEM_SIZE_LARGE_STRUCTURE) / 2 // halfway between a shard and a normal SM -/obj/machinery/power/supermatter/shard/announce_warning() //Shards don't get announcements +/obj/structure/supermatter/shard/announce_warning() //Shards don't get announcements return -/obj/machinery/power/supermatter/shard/singularity_act() +/obj/structure/supermatter/shard/singularity_act() src.forceMove(null) qdel(src) return 5000 From 1b1f08d6bddb2713875ac9a5edc606d9f7dac287 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Oct 2025 11:45:24 -0400 Subject: [PATCH 0823/1331] Fix the supermatter crystal melting --- mods/content/supermatter/_supermatter.dme | 1 + mods/content/supermatter/datums/sm_material.dm | 12 ++++++++++++ mods/content/supermatter/overrides/sm_strings.dm | 3 --- .../supermatter/structures/supermatter_crystal.dm | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 mods/content/supermatter/datums/sm_material.dm diff --git a/mods/content/supermatter/_supermatter.dme b/mods/content/supermatter/_supermatter.dme index 93f116effd95..abc090e244de 100644 --- a/mods/content/supermatter/_supermatter.dme +++ b/mods/content/supermatter/_supermatter.dme @@ -7,6 +7,7 @@ #include "datums\sm_follow.dm" #include "datums\sm_grief_fix.dm" #include "datums\sm_looping_sound.dm" +#include "datums\sm_material.dm" #include "datums\sm_subsystem.dm" #include "datums\sm_supply_drop.dm" #include "datums\sm_supply_pack.dm" diff --git a/mods/content/supermatter/datums/sm_material.dm b/mods/content/supermatter/datums/sm_material.dm new file mode 100644 index 000000000000..92d8b1c3d51e --- /dev/null +++ b/mods/content/supermatter/datums/sm_material.dm @@ -0,0 +1,12 @@ +/decl/material/solid/exotic_matter/supermatter + name = "supermatter" + uid = "solid_supermatter" + lore_text = "Hypercrystalline supermatter is a subset of non-baryonic 'exotic' matter with unusual properties that make it desirable for energy generation, and also very dangerous." + accelerant_value = FUEL_VALUE_NONE + vapor_products = null + melting_point = null + gas_flags = null + ignition_point = null + gas_symbol_html = "Sp*" + gas_symbol = "Sp*" + default_solid_form = /obj/item/stack/material/gemstone \ No newline at end of file diff --git a/mods/content/supermatter/overrides/sm_strings.dm b/mods/content/supermatter/overrides/sm_strings.dm index 2d7427f03d3d..72413b05d274 100644 --- a/mods/content/supermatter/overrides/sm_strings.dm +++ b/mods/content/supermatter/overrides/sm_strings.dm @@ -14,6 +14,3 @@ . = ..() . += "a supermatter engine" return . - -/decl/material/solid/exotic_matter - lore_text = "Hypercrystalline supermatter is a subset of non-baryonic 'exotic' matter. It is found mostly in the heart of large stars, and features heavily in all kinds of fringe physics-defying technology." \ No newline at end of file diff --git a/mods/content/supermatter/structures/supermatter_crystal.dm b/mods/content/supermatter/structures/supermatter_crystal.dm index cbd3e878fa57..415212605f44 100644 --- a/mods/content/supermatter/structures/supermatter_crystal.dm +++ b/mods/content/supermatter/structures/supermatter_crystal.dm @@ -112,8 +112,8 @@ var/global/list/supermatter_delam_accent_sounds = list( anchored = FALSE light_range = 4 layer = ABOVE_HUMAN_LAYER + material = /decl/material/solid/exotic_matter/supermatter matter = list( - /decl/material/solid/exotic_matter = MATTER_AMOUNT_PRIMARY, /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT ) w_class = ITEM_SIZE_LARGE_STRUCTURE From 9151a170e59ef4a31fa8f9bd0788cc92f5a3a1e2 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Oct 2025 11:58:49 -0400 Subject: [PATCH 0824/1331] Remove the last vestiges of struts --- .../crafting/stack_recipes/recipes_rods.dm | 1 - .../designs/general/designs_general.dm | 36 ++++-------------- .../materials/material_sheets_mapping.dm | 5 --- code/modules/materials/material_stack_misc.dm | 10 ----- icons/obj/items/stacks/materials.dmi | Bin 15853 -> 13892 bytes 5 files changed, 8 insertions(+), 44 deletions(-) diff --git a/code/modules/crafting/stack_recipes/recipes_rods.dm b/code/modules/crafting/stack_recipes/recipes_rods.dm index 8483c2346746..4b506210b577 100644 --- a/code/modules/crafting/stack_recipes/recipes_rods.dm +++ b/code/modules/crafting/stack_recipes/recipes_rods.dm @@ -2,7 +2,6 @@ abstract_type = /decl/stack_recipe/rods craft_stack_types = list( /obj/item/stack/material/rods, - /obj/item/stack/material/strut, /obj/item/stack/material/bone ) one_per_turf = TRUE diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index 4e09933a75e8..55d33d3e2941 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -116,44 +116,24 @@ /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) ) -/datum/fabricator_recipe/struts - name = "strut, steel" +/datum/fabricator_recipe/rods + name = "rod, steel" path = /obj/item/stack/material/rods/mapped/steel -/datum/fabricator_recipe/struts/get_resources() - resources = list( - /decl/material/solid/metal/steel = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - -/datum/fabricator_recipe/struts/plastic - name = "strut, plastic" +/datum/fabricator_recipe/rods/plastic + name = "rod, plastic" path = /obj/item/stack/material/rods/mapped/plastic -/datum/fabricator_recipe/struts/plastic/get_resources() - resources = list( - /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - -/datum/fabricator_recipe/struts/aluminium - name = "strut, aluminium" +/datum/fabricator_recipe/rods/aluminium + name = "rod, aluminium" path = /obj/item/stack/material/rods/mapped/aluminium fabricator_types = list(FABRICATOR_CLASS_INDUSTRIAL) -/datum/fabricator_recipe/struts/aluminium/get_resources() - resources = list( - /decl/material/solid/metal/aluminium = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - -/datum/fabricator_recipe/struts/titanium - name = "strut, titanium" +/datum/fabricator_recipe/rods/titanium + name = "rod, titanium" path = /obj/item/stack/material/rods/mapped/titanium fabricator_types = list(FABRICATOR_CLASS_INDUSTRIAL) -/datum/fabricator_recipe/struts/titanium/get_resources() - resources = list( - /decl/material/solid/metal/titanium = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - /datum/fabricator_recipe/umbrella path = /obj/item/umbrella diff --git a/code/modules/materials/material_sheets_mapping.dm b/code/modules/materials/material_sheets_mapping.dm index 49be21fa79f4..710db635acf5 100644 --- a/code/modules/materials/material_sheets_mapping.dm +++ b/code/modules/materials/material_sheets_mapping.dm @@ -109,11 +109,6 @@ STACK_SUBTYPES(beige, "beige cloth", solid/organic/cl STACK_SUBTYPES(lime, "lime cloth", solid/organic/cloth, bolt/lime, null) STACK_SUBTYPES(red, "red cloth", solid/organic/cloth, bolt/red, null) -STACK_SUBTYPES(steel, "steel", solid/metal/steel, strut, null) -STACK_SUBTYPES(plastic, "plastic", solid/organic/plastic, strut, null) -STACK_SUBTYPES(aluminium, "aluminium", solid/metal/aluminium, strut, null) -STACK_SUBTYPES(titanium, "titanium", solid/metal/titanium, strut, null) - STACK_SUBTYPES(steel, "steel", solid/metal/steel, rods, null) STACK_SUBTYPES(plastic, "plastic", solid/organic/plastic, rods, null) STACK_SUBTYPES(aluminium, "aluminium", solid/metal/aluminium, rods, null) diff --git a/code/modules/materials/material_stack_misc.dm b/code/modules/materials/material_stack_misc.dm index a9d5c479118b..afd7fbfe51ab 100644 --- a/code/modules/materials/material_stack_misc.dm +++ b/code/modules/materials/material_stack_misc.dm @@ -154,13 +154,3 @@ material = /decl/material/solid/organic/plantmatter/grass/dry drying_wetness = null dried_type = null - -/obj/item/stack/material/strut - name = "struts" - singular_name = "strut" - plural_name = "struts" - icon_state = "sheet-strut" - plural_icon_state = "sheet-strut-mult" - max_icon_state = "sheet-strut-max" - stack_merge_type = /obj/item/stack/material/strut - crafting_stack_type = /obj/item/stack/material/strut diff --git a/icons/obj/items/stacks/materials.dmi b/icons/obj/items/stacks/materials.dmi index cd2d910889527712fba723711f3ea8f23854da06..c6127f7fbf61f4620914d2914f4f56fed876c980 100644 GIT binary patch literal 13892 zcmb7rXH*kT^kqVTPy|9(BoG2fktQJ0455c29YN_L0!laZ0HOCP3MxpiQbds6yV60W zcciyS3w`77zkAMp*f0Ab=SaW4C)ta4wwf_1;xBDK7i{8Is|5z@ zk6!wF)e2K`yBA)m!Xi4qh5EmFBzp`x-H+(ly1L1Q_r22H9P2Fxq$I zJh*%=YXnCiyC&=21-kVsvAK-C9Fh50lPKBWK@z5a@8&(9*?l`L1tGebB)65}%_xyR z9X)`5)sCtEB+`nSj-nu&HiT(2$ZZtj-KzGeUdoDrNiGX%M>Lf@e8u(Uo}$n2NJpyP ze%70x$Guik5=nq^Bqxch@nb3#pu>y0>|l;BtYT__)LVBlIhe>O+0`V;XJvI%A19jf zNs^y(D{iNYyGV)=e<0%$%}jd>A&x6R61jdX8tgHH2{>^D*1p&TleI6Y(0vyZA$y4i z{Ip!YJiaO?CJ8r$z5UIBX9s&P`Tk}sL|FNry|{i* zSLPA!h`)V6qF2|R{b5S>7yuBrR#TEU@JicBryPbFzTG=X@gySWB8~__(lPwKnLwrx zAtGAXp`pTh^FkJq&9BAOabc7OQ|o*AZZB4#hszcsW}Uaz%0mh+fj>WYrd z=8Ym&%)On-tG*|ySr3dFxM-$)n`NWG-`Hdo`Se&% z6|^Y2x_BvZ45|5{V25mK&U~&4pQQ`bVRML*Yhf{IjHv1)^Rv_22VrfrvgMOskTzj zN}p6Jg+3xzNSe-?QZ>X$LO@X&Tw%(yPv+X)=I}GnU*zS=>-J--jo}-2UwxXY+Wo_$ zqEhL)`06hLJpL7sFHXKhPkV+))rtgC?q8qQw+-G);9A^cY2>qs#4T2%*)K9a z>+9FOzFs5I@|*pr?|u0z@AI6ORsqXS^)TE_Jsi+&oX z+BQp{eq2wN{?m8dT51e*nZNF`TYNjt<1s2VIXEmf`i*Ep&}BS2ACh(1+@5@WXW)bT zexd8~om9U9Hp_gibQ_O3?{PSXbW#C5`nDl?YvmAPdhT7o;9@wt6aH=yRPEnnu7za= zP|_MJZ)ET1mne9RurNy9w!r!Q#KMFiY40mvyw4kYX#2_QS)>$xK?*1RbX9$qVLLH= z*Ze+uVp!K&C4SPqyC0+)dsAvOX5O87Z2Upzja$;4H`o!fbRW(76F3z6BWQDSW+?l@ zKaAvfioQX7(cTWiN2iO%Er<^!faIY;m8 zBZevzwoeb%J}O5ub!7yf;+A`Qw&pMg1HkDev5J`LKhp<}C-VW<@&P8K>i23vWKUvN zalCIA<2tm!MP`Sd%C$>TeUv=~KHBf`5-q;_ZN?awq$vg_n;C(}#t{)OwxNG-g9@}# zMdo6lHJ@+Yilm7gNN8R^XKfOZdvRzi=78^A-#MSh?M&Awm+!pjE}{8oG~Qxb7%0Jo zz$+|Af2y%QjT)G@?OK_BCet`HreY72TuD&uKNyl@51?+9y<*dmA;a96f9E_@6MH2W z+&T7u;}!CW4z%0WmWGx++n|b?7fJ38!jY+}Ib-IlpNK z%$D|x>FQF_hmf(nA%vBL^DYe>K^&PNoC_s$6y^h%9{weRm99;PqmjwaCTx8a+Lp8E zr}^Tk+^<9nn(HiUy96!a$X0G zOn-?n?%B$V>i4vq;i`k(N@cdTm9ER(UX#amZNuyL9q=v(zo(r)c+Pvv(0zFHqAR*@ zsp{4Gu$Aj0yLo%|7^SWEOTJCP;y7}7C~HJ6K=um1jJx6@hatj)hcxMt&4TYH15B9w zX$%UeH)PmWzuP2CS9IlLrNsND?$~(!q6(S4=V40ORYfiH4ztx=jaO5jD{lLo6>%r_#)vCsCp(Pe z8s0xVnGsVT3R5{~=+Rnlp1&CXeIju}6WdvEc%<@byzIX3UX$mh4|@Neb+=`86`5KA zEQtu~h@b!KeZKc*O0f@Ljpzbra>DZ#9$3FCa;@#uh_)He`oWZUYI_q zeRDjh7pTkcF$DGO90U`ENGIM6CNBKe^zBb+&77#rj`)wpi@zE;X4~`Qci#I~;G7Mn zgQ=|+@kHgQlvq}&cFhUz?DtZuX39DYshE~z{P*doIjo6Y!G9} z;uCxn;Z#=nJ{Qv20-~#Wvg+x-BU7BH5LJA!))K2BC$`M>hRF@7mY&mcp8M2U|li< zKG0eT`Q15wrI{h|OEdM0iKz?QeIHFs?9k@hp%Yd6$&bF8AHErFJlsJhk;$X+KJ53h zsmBnXyJRviPcbsSzRiNwebdzmLg(D0jHVvHmecY_Vz&J^?>o&% zR<3<|vlLAku3R*}dNL>W@$A7-%l^ezs*&ugrjtvTJZ)Jx?b4oNC(QNx8RltED6hS5 zii84JcHERY9l!+nWi}3vnap&oFq^AStpa!iy}WJewH_J4fr?4o*nB`m~pi2 zGqyOy#+X91dZw53J5M1XVzMR01NQ6BEw}vUmwX@ZeBG)w##B2_zvx!tW4ja&n0t(tS3hRMpYHu zNLp6t>6R#*9InPRWEau+sU+er4rRJd5`*OJ1|KfR-~%3Yt%@#z4zmu`;YmcPB;}#| zQ>eo8FWX+;i;903Ht;i#P9ygEpzj!2Z!^O3baiZVE&PM#AF_$EwT!V^R*_$o*pw8| zN{d)jkqwO9l|eAm`}@%d7M#fXPi|oqvf{N28HN(@$sFZ58uXGf{?IP);l{*%l%DWH zA?BxpbOQ`YU&0q62Y-Q-6`Wk+NYV$c6mWQOzCoem#L zmChcszNs6?xWdn`hQ8H!>0WG9B<#ba`MvAU^$EC?-PGeznd4B?I;L`geaMiOZedVn zFQ)tlVljlC#r5L$7B(pid*qi9C7LQrjY&XYM%8!>f8%;$ZjGJwsv_uy2*GpF4&SGp ztA_c@GWSke)t`?;SPVh!kcta62XRfZRh+?a9G_kk6WN1@w{S8Xh9Q1u8wJ+)kOgvQ>p71OI}p#8yJJf$tb2T%!Q?bp zyvu^mFe%Z-&HarKHlOt@c|X6oM(;oD-_>iQBGZ|y_RoT8{I4TRWmI$p-m)wnBtDz8 zrW?^ey=V7f%LS&KxNF(OCP!HW-~M9N@$~g-N~(?sYARC*t*9!rx^YX>I80hwu|l+Y_q4LiSS7J(4D4@sXn z{IAdFd-Q+$5khp9p%(m7^y(+yxoh2HK3P@^c(T`B(li=rq1clf9N9MOhs2!`$byW3 zv$d&do%$6DDG`hZ=EdC)OQAMiv=BDB41U&|Kybnl#iR0`wbsRjKs@{9y6Q4Ds)%&8 z2tp~3C3_ni%0d%jd%Qg zw%yq=3&cZEd3F{s*_Mkx!pqF96}21rANzm2y*qHYF$Rv%A_FV!g2%pU@gBV}Kwl}# z)$t()V!dOogS{hwR3j9)3K=QlsLlHm)e&SX?v{2mi+zFa3RA$tmzbZ5y*L<2n=qmi zP>_Lx58o<65P8)nrTp_Qu1;`!aLUdLuLA$=ik_wM5OVtdtZ+6KVUvny$^vW<_T8|* zL>!A{p&?|}WqyC(WW=o9>x%9KPahnIdLT``en@8Zvq17hUBq12Ab~c0Dc@>xw6w-* zC8{{h+UG4z_@6bvc3QtCcmSFh=51+z|Ea=o-MLIYUXYgh>6)ZO90bAvQQrYeL{rU$ z^c*Hbe1<=r1;9;3ejb_5mIFTkRkt zg+XGWl>*&)ELk>{o%ItSOf{I#Ow$fjVB_rEbAwN*nx4NM25g^%r@br)5>B^QSPRdQ zFSr8^nuduuTVyEil1#Vf)xrset#gCnjPpY6?Le+Py~5B0z{odY-qs+|b`Zy60z}@c zkn_!5c_7OYXe7(Mv9yZ4tmcIaSSdr%iSoc;YcX5@E$AyW-V;o$qYqqpG5r1B&?}Kz zrtnSqm3Os}OZ$$GxX_?7X7b-{fq5g8)Lc|E&vY4AL?arNjV+9juYs^NF^7pSK-b+T1(ErxR{CzEkfM$akDZEG*c}Xbs z19_v{ZweF0+D?(iC@hQ^6@Wi>L6rrMkGBQd{2MR!jrP^K>GZ6}bU<>Hn00pyqWBtM z9+Eex+-3Yz35LLbTsPzw3irfFW^(bgr6=uRf0$qPlMH)^RZEW4bAb&`)`;??VJs0> z<#96AJ1R*|-dM`)IBmGnv?6PiIe>SAu!1&VgU80E*i5B2Owxu0@a;Iqrle$|#h=mx z`laFH1uzuCsUh>G4XcisGveF###?XGbe@9qk9bL^mzFm%NeH8M!a;}X*kZ^TBXu6r zN`&BHj_v6?Plzm;J7{G0TbtMalu{`}b?g?U_8Je)n1oCv*W0xOg#UU&_rivzz3b@z zj%?&XQ>oa5U3Zn&*6On*e&+BNm$Y%|=7Hd~^k1XDaCC;lXtTYrlmIzSFb;R?wmrR5 ziMuPZS$|j{D(VrP`6ux6`G!AZuIPHPj5Ink&Xtb9U$nCEJzaqzUTg`^G5!TIg~iov z=@4+ALIh3EoUIV{LckcsK^>d21`OR^*P||pLrEsocc$>?yYW1i$xK)RIq)Q}U_S^T zGF{`Jf6FqUI!ONHo`c60zY=ke7AA>=Fk^?86cfm-=E{Kg%`nIuw(nD0FsdJwW)Um~ za}ylX!+of{%q;sx&?a}TjC;9sru492kTpTyS|M8E*=SAPRU$!skEMuS`uKAV$QP9z zq5@4w`slk`SCAe=(2;&;Upt|5($%-Mwd1v{Uw%kM4sPSTeHiu3< zy*rXBTZsDk-<9t#X6xOH^p(FyQzU@gAppr6E`(dYGkx|`5CLd}mDVNr5mCY{d`Lui zn0p>I5o`yUyL@+6ahbUHWcZ76!Y+0+-9ox~^dSQsObtwgp{6n+-k|0c#E#;86A4uH zPc!4{PR)E-*Dcg&q=2IfQ>L^e1jeT@3Kc;+m~Zj3$Zd$}@En{9yf<&#eVxum6!7P* z(c>xRH^`6iGP{zmPyeLYBJS2FfRffPX58~)f269q`oS)@EJ6W#rFW^KthL*EnI^s@Arl0Q zQWH<>>OR~#km=HXTF$_uZMzDyd4uYz#argaXwBEm5OM7wy##y>x|0H#I?Z$KR3 zi4sEMj&BTdkAd-jtPQ#a){D*0mCV#@^ybM=mWRHg|k^A z6IKAMkYV`T&m0Vgw!~$Uz9GKewL%#5sP^0$;aCKw7P!kkQY<2>m8{8TU}Rq~0IAbw zL5PwcpHxh@&7T*837j_lrHC>VHAl&nw>(Z;#B$@TjnT-|ilD=muU_5GePx*U%%&5( z!j#FCMV?L&82!G6h2^#IMWeyVOvnwq+Myd2=`JOlI7t&Q0J<>F5nM@9$bV+jMd$)E z0xwaXV{I4vV%0p6_Q2#gXZ`RkOD`%?9dM7aX(K6p_Hsjgd9FeK14JiSQYMq1H3BLo z+)S)f4^pShL|<^_mItfR0~_ur;y3ZiGt92T^5AcBoLkS3Mwmz%mtvsdA7ljAxcjX8 z_ujw7-QCQv*By~xoiv{Uu(_Zt z9@#sB;B?}N6UHHYyRd1k$ceqzyypzTmJD3p3^T1B2I)w`8^86n=47Lriv&4E2<^2M z{@!DIgto}szDFsDfTaNPE{SXePevs9MnA1-K1lW0#Af#2OYne=;xCnR;-dM2 zBWZefu%j%)=n6iC0%~I>dtgZpC@GhD_J0P^{{k=j%>Uq>DnNlw+GFY-C$N7R>w^pb z{$O;M(tUjRx@U?4;^J;*=(oY$1xAs>LPBR~H%-;X^6S&;;oK{=Z}XzWCZ$DR0>2Vx zQsk0|IBJSBvUk0GDw9d52DrPz?3Z%jxwY9uD@Ze9Wc`6gk5N6C5eDAWGk*JTs&9AY zNebnEDgz8L3>Jj|zPh4l0RL?8ClY-I<(M)wQk*4hsE3v}YAK_P9)`H{ZHf7OvR`c> z5i+`*k3qKHz^*b$~jJnH|ATK5|c#$-i4!KPnJ5S>D;z`l#7U1 zvhM$@B-eRImdQph_$u^EXnH44si5)d6x#+%1R7(X`-)%%FpZi@H(8vD{J3T8pa_RI zPPsgRBlV~|4Kam+UV9(ldeooRGGZM4qDu)~?5TFgEfz*`Kr|WHtxSRd}bf9J8k8wrp-sKq- zle)Rki0A*R;%I1DxR3}|)TCDYy&`4-V@QBUL3JVCKo#mA_zUSk4cBGLI9pEmsWOkl+$3bQ6 zOdejTv$3QEow9v=>dN^D^Tkxx)IJ6T-yh~_q?X-B9vqHo_viy?+kNv`9B_fWlUgV~ zu-;tmAA7#Ctz-9AHSDGV2!>mPhuHUmJ=FGE&=@4#I+})94cBpYv}MiFmgC&|_fOwI zvEk>XQ~R;}Z=qpVLuJ#<+?^-FHGp4vq?H~!Q^12B_JGE^t+IwCd(?kECRL5!w^5gZ z-Qmf=xXE63-*!&@$LIy`ls6qel?^Rqb!i3SD;%xnY|gXcoh<AtrYp=NM1iS%<0_-!Xo-$8z+|c6AI(Mov#3NTxoy6OHw!#w}iE z%rSf4sjdX5ja&XMHF;n+`EA_%)u7Z`n%7gt(fg1Mya8Iv&2m2 zwQ6v4cNZuq$XGl+rSI70`{wmqG6;umFwkB>5gx)S&h=lv3i}ilD><;6_c~+*b(@9Xpq#fVsviJ!Ifa+P><+1nX zE#|ML!^_g8bkN8(Hpyxizn*ri>2i!z>Z=o6AI6(v?&Nbh0M0L#*{^GV9@{J|XL3t% z7^!E3mDiIhY87lI-z6s?n8AhkNX{RQ!XJS;0-7A?^;T50sXix>LRAl&uVf@#itz&4 z>Ob22_ei-$IQSv;(L;KDoO71wP{s(?vgkw0aw< z22{D!xHlAYhMj_XX@WuNFReZai7jSmz*F9BE;xJ57Dn*?#)->j*3*1>rBEhG36VZRb-3a&qAM?HI$G|bUNWIU*RM{RvrWs0pf>e;Mz2Z1sv^_G#>>GcBD zqJ}>@H%r+)S=ZlFL;Xf_EfcQ#FG-RT$BL7c1RVR^`X+=rGp=2xLXf|Diw_>D-@r!N5wg?K8Ic{NZMQqU${k&q_+JI3yk zSB8sl7KC!*Xrt`6I{?+yuYOOG%0Nq6|1Jn0!sfXNQp*3s(3ddx2#fIL`JLg4%J}=* zBz5Zv6DzaL0<{;dJX%Ctmt9FQ7U79}zNh#Tx|PQ;7IZ01%O$bxj~@;eAv*H`JV3&j z$#KG$Y1SK;DRJ|Z3{ulZzb4xt^8o}Bqu_I$w|qiT7P`YfNF7Gtlfeo9kg_S{v*k}i zOX)-tY!xXynk^vjwyPU*lz)1wrPb%+ty7LOZmDb3v6q0O>+Xm6yUq5=0KTYEGjXTq z2Fe61Wc*L!1?n*<-}tY{>-PZj_NSEtT3PpE zJXfM*32NPuiXukk#11}^&x%ytClf!TvU>@$*whPt^7XlzY~H<1?(TdehAx^xK7wNU zVlYh#ly3*_>{$!0VN@<6U8}Ubu*Vm+4ZD;m_k?-;S(Cf=mNlz!n#J1x%E>tv9k&SZ zJJ0`l|JCo*XyIn#AxZvF7be&S%MJ2@0=df#;~2Xr4@<57 zk4eh>=6iZM*|7rc2X~FiVhR8`6Xre}d4h~tyTnUk&pIRY;47w}q_Q?0RdJ+P2Ik8ua;B}MiqGZZD?3T$vmT9{yTBg<7W0(LVl{pRf-EC&H}0i z+JQVB9<7Y1vO6vsj!U4b;yZ0;YF)#W3VYZ7c@Yc|3Q}Ns+_hflBE8~2eCsnBsRz~7 z1MJs8B!lApsGS;dDhC$_Lp``*(hqw~!xh@n8(hJC+PO-+pQ*g!{}^j4Fx00jhEqF6 z)4*tbcI(#CmZb|Ulik{Y@)$dHE#5;|XyYnqr1F(QR%RT^T7cVK360#uG)re2m`WD# z>F3`hV~_6(-6IHWFDco8fy5XA{@k#M_}+9zt*%0SsKTfQXZOEs(JG}EQ;Gx0UH5XAQBX}n!9mERBy;oNdrU3c`` zWFU;{cA8wlaFZqG=8I|C&~6V|NM?d^by1gHoQ%{eiNaaG~Z+GVN zwfZLqvbM(D5_3u7I57IXi_Ih2i|`{Poq96JfeVgb2ZC#sMF$1H7~PG}B68!m9&7GnY0JM=N<%ZF)t4 zlaY5^ZLw3s=FZ=&hTYg5dx@45;Yv#fKkk+@oRzfOw!x5UTD;=o65k#kJ&K4+U2{mN z0wfrZthvxujmgQ(=A@>!M=F=yjT?}wA)5hairD}Y+pE`WoBeS+gCFmNnR5N8;#PA} zv*YAZbQIy3P9QP62GhcElkVeY1I_{G(?Gc-Z-RfAAE>!0a1^=KGS5Tr?G@SC1Cbvj z03+S_Hl)9nbM^m1y#9`RySckC#c=kzZl^U*srW|*jn zl`6yc%7@V zx&lq@EYw#Oc;n{6Z15OhIxU}}tn3n{;kanKxX_7b9L7e&*57{4158=+4rLWP9KNUt|6yc9@h6GugHgoggmCNvhh>8{?(<{H>#IvNSTga@^dP%FE(7GE`0HhL%mo zUl+^`Eei$+!eMV4JAL~Siyuyq=Tb`2nF`d3F#2U5L5#^5l3b3d#%BsIb#?VVaFMBA z=c>z=WpG0#@j*g_qB5jKb3$%!aewkVp8S2fmNCyp#3Fk?)IG*O%`rsByA z#PSJk8VO32WT}i&u|>xrf<`Ec$gu978f|N`9_%TTbaT4TL&A%f{9=QWvUWvWdE;O8 zH&t`C{HV)lgji8`M=01mt5|PDRFXX5IYshnJ><60v&S3VcQjKW@zDb^77TK=WGHEf z%uz8v7^O}@ZHR_Fn+wE&`Hn=-xCoO8#<>LnS^hz z#=GNSXGb)&{M}3sw`~pK6Y}`&ley`Iv#q`5afAkbG^D41a~bzWS^`QbBujS$BZyZ? zL29{3Z_{yIRj6Un?el|->lx4M%aa-iv(iC+R3*~%aQh@n>C2mac`ulPA(>TyMZmm$ zm>{OHTVI(Ug@Y;GB&Hi~y)oLZXyC!52`BlnRnJ8+3So_a44hwVb0sO!7;pqVX%F}I zr+UojG|Ut-d_>h5NSUZk@+M?G>TX$~fIl6C%{+wqQ;_r$*N_ z3{O=hn1GmWS5>SCxcn?sqMjS%fnf!qJvxh|5zgQKCNMH&?rvEYMzR{ku-$i4qeu}^vwlio}lngxC-q! z<7n)WXnIIF1xJaG@f{PqgF*qJ`4|?|K*$|WwWH$Kc@_RW8BM6U4?w})r<%`%E zQ`Iz0Q|?9y=eFo4;p*@u7A**K6}Ul&iY)EOFZWOv>!@rl3x@Z$W-NpE2~nia?Vo}B zB0lxhWLjj(8*n+iv_O0|uNKk{-e!-hv{bI7`WIlPEe7eY`n0J0B+%{+t^dfk;(t7x z#RT+~{kOq5PcvUslGcA||LVdWYaT{rwR~41rKMosW|tnBGWphJKw0zI%aX^mddZ{& z7=2=|ZOBJE>-|cO8q4|)A#M0T zV4!%#!A@(k4{-6ltap<4VIt2!Y0cGC|3i$&)}rdC`{7Utv(6*)fXgFCTG_3V%AekA zxd|Y3I--;#n?+8s`Ut%TV;Wl6Xd0!pjH?Z_nD^d84u#bbRino~H(;_}X8xytR?}V& z-b;16r}y>y4mOE2mnTJWHzJ;K<=kq2CXoAESKQiIppAU`BH<}1)9J6zAL+>+CE-g< zPKuw@9LKMI>5yY}rWbTJthk1J_?M5y)tXW=35fKtzSeAH?Q7l-6|VY8?w&s!8euq& zH6`C^MM9KE2)>J67DrpX!HzNOhH_>)@3ym!$nR|es(#K(iQ@XEa{PHM6O zm`ZOoH?P(o#^ql|kCoa<%@f{@o@8{k2fO3tQ)O+QeQ#pDv2*g_{;U3M<>+VQAQ&R! z`eJRlvaPCwV)Tl}Fao0O-iPT?&$INPm3~g~jx>@Zo_RD-Egv8&A^jnQ9;G3Jj6jF% ziOHWw`f1Uz_X{_l1Sa`PLy8X|6RMJ#(DXUY41{0nF@*2p5%0YO&1)EqI zux*)Rnsblk*azKC;5~vQGvyU^*(I9~VcS0EPBEqHO#I1l*>Ufa?CGH~X8*eF9EGLu zdz{74ujN~a>>m%;Y~xkYlTUNryNxD`WN6)uc+8}s2W`bC&OT^}8viw#P5IXFA=A3h0X zO-MhUSc24(^)KiB%2B{lYmXJ{W4Xq=WUR?}65e?%jL+SNx^h3-uDG5sLTV+%c?%4V zFr3F}Eslj|2j>hDeF>NtU&nX$iKL6#s0-ZSU{y;z6y}ob#>)ZD%t<|wtdW(L9uzO4 z)675=i9yQz^AB>J(Vu@Xz)CswY#SEOOqO?&(g0I`>%_Eqe*$Ilph6qHUCb8SGhyNt z@zIxD5_igKzQs#Hnp?$r7mAyt9qnvxLVTfoi^eMbzRpq~ z?`ZR+Ji33?ylyL%PSU6DyYB#NrY?pq3EX66m-QsG{M3*&OnoEs^+E>L;zgjXR z_8?H>bSQb_$zxyWKO=EP&}^1poJn;$jrvWMQN-NgvWy9OKs#bt7Yv@*S)t3+vezz1?gdwa zG}OV0GGC;JDWAPzwye353%7?fynSj6W@{A8@|qig*43cpErP)CppJFedo6vO zSt{1nfY6Q0JQnL|~& zbQqLzffNbZyw3=knkabK|3^{9x;spJi2&@??9q`uZ`G@nbs0P<$RT@p1bFeaV+UUW ztF_R1%_}>R`gc(Y+2WM8K$aQYe1N26m^#of5)k72cV%o@Li3X>pT7m#MO<0;xl0+a zX&%QzwT@5iu_K1u*^N`c4uvmRF@#VKUDwy`HwN4f@Etu3Bi7MPUR~J64v&oB$jN{q z>&2lKG`mW@er8^CAXnK48mg8CYg67w9SnK$1eJo+zr$+=(6Kbmd;^1h#IXi9g z@uJ!doa~V}!EtAXIfSe)f$1enKJP%=6RaJ|6kAb;yDENl)dE}e_k|0&bS zyP_PKw5y@Za4qg+%vMiIhTJdQRovBBDt1uTjT))-Z5y3oHs1dxM#!|~$%|`Y9SqP6 sd3Ec{l7#%g|AZ+2SG4l~E?%*Js>Dm7$XOBy{|*DFDQhW}DOd*mFHuB{W&i*H literal 15853 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 E01(itb^rhX From 0bb59f73f8699fcf66f0e6f0d80914de850db52d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Oct 2025 12:03:45 -0400 Subject: [PATCH 0825/1331] Remove unnecessary powernet == 0 check --- code/modules/power/cable.dm | 1 - code/modules/power/power.dm | 2 -- 2 files changed, 3 deletions(-) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 587bbe766db4..b18e5bd16812 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -440,7 +440,6 @@ var/global/list/obj/structure/cable/all_cables = list() . = list() // this will be a list of all connected power objects if(d1 == 0) for(var/obj/machinery/power/P in loc) - if(P.powernet == 0) continue // exclude APCs with powernet=0 if(!skip_assigned_powernets || !P.powernet) . += P diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 4449a9dd44a6..b88f26c3fe64 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -112,8 +112,6 @@ if(!cable_only && istype(AM,/obj/machinery/power)) var/obj/machinery/power/P = AM - if(P.powernet == 0) continue // exclude APCs which have powernet=0 - if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet if(d == 0) . += P From 56a1a612610060a77e5d499b6311ed635fc4fb48 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 12 Oct 2025 13:02:43 -0400 Subject: [PATCH 0826/1331] Move keybinding definitions into their own folder --- code/modules/keybindings/{ => binds}/admin.dm | 0 code/modules/keybindings/{ => binds}/client.dm | 0 .../keybindings/{ => binds}/communication.dm | 0 code/modules/keybindings/{ => binds}/human.dm | 0 code/modules/keybindings/{ => binds}/living.dm | 0 code/modules/keybindings/{ => binds}/mob.dm | 0 code/modules/keybindings/{ => binds}/movement.dm | 0 nebula.dme | 14 +++++++------- 8 files changed, 7 insertions(+), 7 deletions(-) rename code/modules/keybindings/{ => binds}/admin.dm (100%) rename code/modules/keybindings/{ => binds}/client.dm (100%) rename code/modules/keybindings/{ => binds}/communication.dm (100%) rename code/modules/keybindings/{ => binds}/human.dm (100%) rename code/modules/keybindings/{ => binds}/living.dm (100%) rename code/modules/keybindings/{ => binds}/mob.dm (100%) rename code/modules/keybindings/{ => binds}/movement.dm (100%) diff --git a/code/modules/keybindings/admin.dm b/code/modules/keybindings/binds/admin.dm similarity index 100% rename from code/modules/keybindings/admin.dm rename to code/modules/keybindings/binds/admin.dm diff --git a/code/modules/keybindings/client.dm b/code/modules/keybindings/binds/client.dm similarity index 100% rename from code/modules/keybindings/client.dm rename to code/modules/keybindings/binds/client.dm diff --git a/code/modules/keybindings/communication.dm b/code/modules/keybindings/binds/communication.dm similarity index 100% rename from code/modules/keybindings/communication.dm rename to code/modules/keybindings/binds/communication.dm diff --git a/code/modules/keybindings/human.dm b/code/modules/keybindings/binds/human.dm similarity index 100% rename from code/modules/keybindings/human.dm rename to code/modules/keybindings/binds/human.dm diff --git a/code/modules/keybindings/living.dm b/code/modules/keybindings/binds/living.dm similarity index 100% rename from code/modules/keybindings/living.dm rename to code/modules/keybindings/binds/living.dm diff --git a/code/modules/keybindings/mob.dm b/code/modules/keybindings/binds/mob.dm similarity index 100% rename from code/modules/keybindings/mob.dm rename to code/modules/keybindings/binds/mob.dm diff --git a/code/modules/keybindings/movement.dm b/code/modules/keybindings/binds/movement.dm similarity index 100% rename from code/modules/keybindings/movement.dm rename to code/modules/keybindings/binds/movement.dm diff --git a/nebula.dme b/nebula.dme index 7c527182f6f6..b49e271fc03c 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2613,16 +2613,16 @@ #include "code\modules\item_effects\item_effect_modifier.dm" #include "code\modules\keybindings\_defines.dm" #include "code\modules\keybindings\_keybindings.dm" -#include "code\modules\keybindings\admin.dm" #include "code\modules\keybindings\bindings_atom.dm" #include "code\modules\keybindings\bindings_client.dm" -#include "code\modules\keybindings\client.dm" -#include "code\modules\keybindings\communication.dm" -#include "code\modules\keybindings\human.dm" -#include "code\modules\keybindings\living.dm" -#include "code\modules\keybindings\mob.dm" -#include "code\modules\keybindings\movement.dm" #include "code\modules\keybindings\setup.dm" +#include "code\modules\keybindings\binds\admin.dm" +#include "code\modules\keybindings\binds\client.dm" +#include "code\modules\keybindings\binds\communication.dm" +#include "code\modules\keybindings\binds\human.dm" +#include "code\modules\keybindings\binds\living.dm" +#include "code\modules\keybindings\binds\mob.dm" +#include "code\modules\keybindings\binds\movement.dm" #include "code\modules\lighting\_lighting_defs.dm" #include "code\modules\lighting\ambient_group.dm" #include "code\modules\lighting\ambient_turf.dm" From 7c23c3a83282455f4a358c398730b53a74758eda Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 12 Oct 2025 13:51:11 -0400 Subject: [PATCH 0827/1331] Move pies to empty pies file --- .../food/baking/baked_goods.dm | 110 --------------- .../reagent_containers/food/baking/pies.dm | 133 ++++++++++++++++++ .../food/sliceable/cakes.dm | 24 ---- 3 files changed, 133 insertions(+), 134 deletions(-) diff --git a/code/modules/reagents/reagent_containers/food/baking/baked_goods.dm b/code/modules/reagents/reagent_containers/food/baking/baked_goods.dm index a03d9f836f06..035296bd59a6 100644 --- a/code/modules/reagents/reagent_containers/food/baking/baked_goods.dm +++ b/code/modules/reagents/reagent_containers/food/baking/baked_goods.dm @@ -12,27 +12,6 @@ nutriment_amt = 6 bitesize = 2 -/obj/item/food/bananapie - name = "banana cream pie" - desc = "Just like back home, on clown planet! HONK!" - icon = 'icons/obj/food/baked/pies/pie.dmi' - plate = /obj/item/plate - filling_color = "#fbffb8" - center_of_mass = @'{"x":16,"y":13}' - nutriment_desc = list("pie" = 3, "cream" = 2) - nutriment_amt = 4 - bitesize = 3 - -/obj/item/food/bananapie/populate_reagents() - . = ..() - add_to_reagents(/decl/material/liquid/nutriment/banana_cream, 5) - -/obj/item/food/bananapie/throw_impact(atom/hit_atom) - ..() - new/obj/effect/decal/cleanable/pie_smudge(src.loc) - src.visible_message("\The [src.name] splats.","You hear a splat.") - qdel(src) - /obj/item/food/berryclafoutis name = "berry clafoutis" desc = "No black birds, this is a good sign." @@ -128,75 +107,6 @@ nutriment_amt = 10 bitesize = 2 -/obj/item/food/meatpie - name = "meat-pie" - icon = 'icons/obj/food/baked/pies/meat.dmi' - desc = "An old barber recipe, very delicious!" - plate = /obj/item/plate - filling_color = "#948051" - center_of_mass = @'{"x":16,"y":13}' - bitesize = 2 - -/obj/item/food/meatpie/populate_reagents() - . = ..() - add_to_reagents(/decl/material/solid/organic/meat, 10) - -/obj/item/food/tofupie - name = "tofu-pie" - icon = 'icons/obj/food/baked/pies/meat.dmi' - desc = "A delicious tofu pie." - plate = /obj/item/plate - filling_color = "#fffee0" - center_of_mass = @'{"x":16,"y":13}' - nutriment_desc = list("tofu" = 2, "pie" = 8) - nutriment_amt = 10 - bitesize = 2 - -/obj/item/food/amanita_pie - name = "amanita pie" - desc = "Sweet and tasty poison pie." - icon = 'icons/obj/food/baked/pies/amanita.dmi' - filling_color = "#ffcccc" - center_of_mass = @'{"x":17,"y":9}' - nutriment_desc = list("sweetness" = 3, "mushroom" = 3, "pie" = 2) - nutriment_amt = 5 - bitesize = 3 - -/obj/item/food/amanita_pie/populate_reagents() - . = ..() - add_to_reagents(/decl/material/liquid/amatoxin, 3) - add_to_reagents(/decl/material/liquid/psychotropics, 1) - -/obj/item/food/plump_pie - name = "plump pie" - desc = "I bet you love stuff made out of plump helmets!" - icon = 'icons/obj/food/baked/pies/plumphelmet.dmi' - filling_color = "#b8279b" - center_of_mass = @'{"x":17,"y":9}' - nutriment_desc = list("heartiness" = 2, "mushroom" = 3, "pie" = 3) - nutriment_amt = 8 - bitesize = 2 - -/obj/item/food/plump_pie/populate_reagents() - . = ..() - if(prob(10)) //#TODO: have this depend on cook's skill within the recipe handling instead maybe? - name = "exceptional plump pie" - desc = "Microwave is taken by a fey mood! It has cooked an exceptional plump pie!" - add_to_reagents(/decl/material/liquid/regenerator, 5) - -/obj/item/food/xemeatpie - name = "xeno-pie" - icon = 'icons/obj/food/baked/pies/xeno.dmi' - desc = "A delicious meatpie. Probably heretical." - plate = /obj/item/plate - filling_color = "#43de18" - center_of_mass = @'{"x":16,"y":13}' - bitesize = 2 - -/obj/item/food/xemeatpie/populate_reagents() - . = ..() - add_to_reagents(/decl/material/solid/organic/meat/xeno, 10) - /obj/item/food/poppypretzel name = "poppy pretzel" desc = "It's all twisted up!" @@ -207,26 +117,6 @@ nutriment_amt = 5 bitesize = 2 -/obj/item/food/applepie - name = "apple pie" - desc = "A pie containing sweet sweet love... or apple." - icon = 'icons/obj/food/baked/pies/apple.dmi' - filling_color = "#e0edc5" - center_of_mass = @'{"x":16,"y":13}' - nutriment_desc = list("sweetness" = 2, "apple" = 2, "pie" = 2) - nutriment_amt = 4 - bitesize = 3 - -/obj/item/food/cherrypie - name = "cherry pie" - desc = "Taste so good, make a grown man cry." - icon = 'icons/obj/food/baked/pies/cherry.dmi' - filling_color = "#ff525a" - center_of_mass = @'{"x":16,"y":11}' - nutriment_desc = list("sweetness" = 2, "cherry" = 2, "pie" = 2) - nutriment_amt = 4 - bitesize = 3 - /obj/item/food/fortunecookie name = "fortune cookie" desc = "A true prophecy in each cookie!" diff --git a/code/modules/reagents/reagent_containers/food/baking/pies.dm b/code/modules/reagents/reagent_containers/food/baking/pies.dm index e69de29bb2d1..36c945ed89e8 100644 --- a/code/modules/reagents/reagent_containers/food/baking/pies.dm +++ b/code/modules/reagents/reagent_containers/food/baking/pies.dm @@ -0,0 +1,133 @@ +/obj/item/food/bananapie + name = "banana cream pie" + desc = "Just like back home, on clown planet! HONK!" + icon = 'icons/obj/food/baked/pies/pie.dmi' + plate = /obj/item/plate + filling_color = "#fbffb8" + center_of_mass = @'{"x":16,"y":13}' + nutriment_desc = list("pie" = 3, "cream" = 2) + nutriment_amt = 4 + bitesize = 3 + +/obj/item/food/bananapie/populate_reagents() + . = ..() + add_to_reagents(/decl/material/liquid/nutriment/banana_cream, 5) + +/obj/item/food/bananapie/throw_impact(atom/hit_atom) + ..() + new/obj/effect/decal/cleanable/pie_smudge(src.loc) + visible_message(SPAN_DANGER("\The [src] splats."), SPAN_DANGER("You hear a splat.")) + qdel(src) + +/obj/item/food/meatpie + name = "meat-pie" + icon = 'icons/obj/food/baked/pies/meat.dmi' + desc = "An old barber recipe, very delicious!" + plate = /obj/item/plate + filling_color = "#948051" + center_of_mass = @'{"x":16,"y":13}' + bitesize = 2 + +/obj/item/food/meatpie/populate_reagents() + . = ..() + add_to_reagents(/decl/material/solid/organic/meat, 10) + +/obj/item/food/tofupie + name = "tofu-pie" + icon = 'icons/obj/food/baked/pies/meat.dmi' + desc = "A delicious tofu pie." + plate = /obj/item/plate + filling_color = "#fffee0" + center_of_mass = @'{"x":16,"y":13}' + nutriment_desc = list("tofu" = 2, "pie" = 8) + nutriment_amt = 10 + bitesize = 2 + +/obj/item/food/amanita_pie + name = "amanita pie" + desc = "Sweet and tasty poison pie." + icon = 'icons/obj/food/baked/pies/amanita.dmi' + filling_color = "#ffcccc" + center_of_mass = @'{"x":17,"y":9}' + nutriment_desc = list("sweetness" = 3, "mushroom" = 3, "pie" = 2) + nutriment_amt = 5 + bitesize = 3 + +/obj/item/food/amanita_pie/populate_reagents() + . = ..() + add_to_reagents(/decl/material/liquid/amatoxin, 3) + add_to_reagents(/decl/material/liquid/psychotropics, 1) + +/obj/item/food/plump_pie + name = "plump pie" + desc = "I bet you love stuff made out of plump helmets!" + icon = 'icons/obj/food/baked/pies/plumphelmet.dmi' + filling_color = "#b8279b" + center_of_mass = @'{"x":17,"y":9}' + nutriment_desc = list("heartiness" = 2, "mushroom" = 3, "pie" = 3) + nutriment_amt = 8 + bitesize = 2 + +/obj/item/food/plump_pie/populate_reagents() + . = ..() + if(prob(10)) //#TODO: have this depend on cook's skill within the recipe handling instead maybe? + name = "exceptional plump pie" + desc = "Microwave is taken by a fey mood! It has cooked an exceptional plump pie!" + add_to_reagents(/decl/material/liquid/regenerator, 5) + +/obj/item/food/xemeatpie + name = "xeno-pie" + icon = 'icons/obj/food/baked/pies/xeno.dmi' + desc = "A delicious meatpie. Probably heretical." + plate = /obj/item/plate + filling_color = "#43de18" + center_of_mass = @'{"x":16,"y":13}' + bitesize = 2 + +/obj/item/food/xemeatpie/populate_reagents() + . = ..() + add_to_reagents(/decl/material/solid/organic/meat/xeno, 10) + +/obj/item/food/applepie + name = "apple pie" + desc = "A pie containing sweet sweet love... or apple." + icon = 'icons/obj/food/baked/pies/apple.dmi' + filling_color = "#e0edc5" + center_of_mass = @'{"x":16,"y":13}' + nutriment_desc = list("sweetness" = 2, "apple" = 2, "pie" = 2) + nutriment_amt = 4 + bitesize = 3 + +/obj/item/food/cherrypie + name = "cherry pie" + desc = "Taste so good, make a grown man cry." + icon = 'icons/obj/food/baked/pies/cherry.dmi' + filling_color = "#ff525a" + center_of_mass = @'{"x":16,"y":11}' + nutriment_desc = list("sweetness" = 2, "cherry" = 2, "pie" = 2) + nutriment_amt = 4 + bitesize = 3 + +/obj/item/food/sliceable/pumpkinpie + name = "pumpkin pie" + desc = "A delicious treat for the autumn months." + icon = 'icons/obj/food/baked/cakes/pumpkin.dmi' + slice_path = /obj/item/food/slice/pumpkinpie + slice_num = 5 + filling_color = "#f5b951" + center_of_mass = @'{"x":16,"y":10}' + nutriment_desc = list("pie" = 5, "cream" = 5, "pumpkin" = 5) + nutriment_amt = 15 + +/obj/item/food/slice/pumpkinpie + name = "pumpkin pie slice" + desc = "A slice of pumpkin pie, with whipped cream on top. Perfection." + icon = 'icons/obj/food/baked/cakes/slices/pumpkin.dmi' + plate = /obj/item/plate + filling_color = "#f5b951" + bitesize = 2 + center_of_mass = @'{"x":16,"y":12}' + whole_path = /obj/item/food/sliceable/pumpkinpie + +/obj/item/food/slice/pumpkinpie/filled + filled = TRUE \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/food/sliceable/cakes.dm b/code/modules/reagents/reagent_containers/food/sliceable/cakes.dm index 2d7f45c72ee7..a59b23872aee 100644 --- a/code/modules/reagents/reagent_containers/food/sliceable/cakes.dm +++ b/code/modules/reagents/reagent_containers/food/sliceable/cakes.dm @@ -267,27 +267,3 @@ /obj/item/food/slice/applecake/filled filled = TRUE - -/obj/item/food/sliceable/pumpkinpie - name = "pumpkin pie" - desc = "A delicious treat for the autumn months." - icon = 'icons/obj/food/baked/cakes/pumpkin.dmi' - slice_path = /obj/item/food/slice/pumpkinpie - slice_num = 5 - filling_color = "#f5b951" - center_of_mass = @'{"x":16,"y":10}' - nutriment_desc = list("pie" = 5, "cream" = 5, "pumpkin" = 5) - nutriment_amt = 15 - -/obj/item/food/slice/pumpkinpie - name = "pumpkin pie slice" - desc = "A slice of pumpkin pie, with whipped cream on top. Perfection." - icon = 'icons/obj/food/baked/cakes/slices/pumpkin.dmi' - plate = /obj/item/plate - filling_color = "#f5b951" - bitesize = 2 - center_of_mass = @'{"x":16,"y":12}' - whole_path = /obj/item/food/sliceable/pumpkinpie - -/obj/item/food/slice/pumpkinpie/filled - filled = TRUE From 4f1126b73ef5af77aad975e4d5accffbdb2fc7c5 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 12 Oct 2025 13:54:56 -0400 Subject: [PATCH 0828/1331] Remove two blank files --- code/game/machinery/kitchen/drying_oven.dm | 0 code/modules/mob/living/silicon/ai/ai_camera_proc.dm | 1 - nebula.dme | 2 -- 3 files changed, 3 deletions(-) delete mode 100644 code/game/machinery/kitchen/drying_oven.dm delete mode 100644 code/modules/mob/living/silicon/ai/ai_camera_proc.dm diff --git a/code/game/machinery/kitchen/drying_oven.dm b/code/game/machinery/kitchen/drying_oven.dm deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/code/modules/mob/living/silicon/ai/ai_camera_proc.dm b/code/modules/mob/living/silicon/ai/ai_camera_proc.dm deleted file mode 100644 index 8b137891791f..000000000000 --- a/code/modules/mob/living/silicon/ai/ai_camera_proc.dm +++ /dev/null @@ -1 +0,0 @@ - diff --git a/nebula.dme b/nebula.dme index 7c527182f6f6..5f7fb8572abe 100644 --- a/nebula.dme +++ b/nebula.dme @@ -966,7 +966,6 @@ #include "code\game\machinery\embedded_controller\embedded_program_base.dm" #include "code\game\machinery\embedded_controller\simple_docking_controller.dm" #include "code\game\machinery\embedded_controller\tin_can.dm" -#include "code\game\machinery\kitchen\drying_oven.dm" #include "code\game\machinery\kitchen\gibber.dm" #include "code\game\machinery\kitchen\icecream.dm" #include "code\game\machinery\kitchen\microwave.dm" @@ -2912,7 +2911,6 @@ #include "code\modules\mob\living\silicon\silicon.dm" #include "code\modules\mob\living\silicon\subsystems.dm" #include "code\modules\mob\living\silicon\ai\ai.dm" -#include "code\modules\mob\living\silicon\ai\ai_camera_proc.dm" #include "code\modules\mob\living\silicon\ai\ai_damage.dm" #include "code\modules\mob\living\silicon\ai\ai_movement.dm" #include "code\modules\mob\living\silicon\ai\ai_radio.dm" From 26ee7dcdbca1315131d10a6115311deda9628d4a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 12 Oct 2025 13:55:18 -0400 Subject: [PATCH 0829/1331] Fix readme files not being detected by validation on Unix --- nebula.dme | 2 -- tools/validate_dme.py | 8 +++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nebula.dme b/nebula.dme index 5f7fb8572abe..76e981eed758 100644 --- a/nebula.dme +++ b/nebula.dme @@ -443,7 +443,6 @@ #include "code\datums\extensions\abilities\ability_item.dm" #include "code\datums\extensions\abilities\ability_projectile.dm" #include "code\datums\extensions\abilities\ability_targeting.dm" -#include "code\datums\extensions\abilities\readme.dm" #include "code\datums\extensions\appearance\appearance.dm" #include "code\datums\extensions\appearance\base_icon_state.dm" #include "code\datums\extensions\appearance\cardborg.dm" @@ -2798,7 +2797,6 @@ #include "code\modules\mob\update_icons.dm" #include "code\modules\mob\grab\grab_datum.dm" #include "code\modules\mob\grab\grab_object.dm" -#include "code\modules\mob\grab\grab_readme.dm" #include "code\modules\mob\grab\normal\grab_normal.dm" #include "code\modules\mob\grab\normal\norm_aggressive.dm" #include "code\modules\mob\grab\normal\norm_kill.dm" diff --git a/tools/validate_dme.py b/tools/validate_dme.py index 4966ef550fea..cca8764cec9c 100644 --- a/tools/validate_dme.py +++ b/tools/validate_dme.py @@ -2,12 +2,14 @@ import functools import glob import sys +from os.path import normpath reading = False FORBID_INCLUDE = [ r'code/**/*_docs.dm', - r'code/**/README.dm', + r'code/**/*readme.dm', + r'code/**/*README.dm', ] IGNORE_INCLUDE = [ @@ -45,7 +47,7 @@ ignored = False for ignore in IGNORE_INCLUDE: - if not fnmatch.fnmatch(code_file, ignore): + if not fnmatch.fnmatchcase(code_file, normpath(ignore)): continue ignored = True @@ -55,7 +57,7 @@ continue for forbid in FORBID_INCLUDE: - if not fnmatch.fnmatch(code_file, forbid): + if not fnmatch.fnmatchcase(code_file, normpath(forbid)): continue forbid_include = True From 1f77779e11232f8d186306bf5e24c8176894ca6b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 24 Mar 2025 05:32:27 -0400 Subject: [PATCH 0830/1331] Use mend_fracture helper proc where possible --- code/modules/surgery/bones.dm | 2 +- mods/content/psionics/system/psionics/faculties/redaction.dm | 2 +- mods/gamemodes/cult/runes.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/surgery/bones.dm b/code/modules/surgery/bones.dm index cc0a53a81473..6b070b63a7cd 100644 --- a/code/modules/surgery/bones.dm +++ b/code/modules/surgery/bones.dm @@ -140,7 +140,7 @@ var/bone = affected.encased ? "\the [target]'s damaged [affected.encased]" : "damaged bones in [target]'s [affected.name]" user.visible_message("[user] has mended [bone] with \the [tool]." , \ "You have mended [bone] with \the [tool]." ) - affected.status &= ~ORGAN_BROKEN + affected.mend_fracture() affected.stage = 0 affected.update_wounds() ..() diff --git a/mods/content/psionics/system/psionics/faculties/redaction.dm b/mods/content/psionics/system/psionics/faculties/redaction.dm index bc5ece1b7598..7b8b9c1b9fd1 100644 --- a/mods/content/psionics/system/psionics/faculties/redaction.dm +++ b/mods/content/psionics/system/psionics/faculties/redaction.dm @@ -91,7 +91,7 @@ return TRUE if(E.status & ORGAN_BROKEN) to_chat(user, SPAN_NOTICE("You coax shattered bones to come together and fuse, mending the break.")) - E.status &= ~ORGAN_BROKEN + E.mend_fracture() E.stage = 0 return TRUE if(E.is_dislocated() && !E.is_parent_dislocated()) diff --git a/mods/gamemodes/cult/runes.dm b/mods/gamemodes/cult/runes.dm index 4ed65a929f5f..32cc1f3024f6 100644 --- a/mods/gamemodes/cult/runes.dm +++ b/mods/gamemodes/cult/runes.dm @@ -568,7 +568,7 @@ if(charges >= 15) for(var/obj/item/organ/external/e in user.get_external_organs()) if(e && e.status & ORGAN_BROKEN) - e.status &= ~ORGAN_BROKEN + e.mend_fracture() statuses += "bones in your [e.name] snap into place" charges -= 15 if(charges < 15) From de7a00cc555fe663e5559f173a792952f970bd34 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 12 Oct 2025 19:14:26 -0400 Subject: [PATCH 0831/1331] Add feedback for when a bone is too damaged to mend --- code/modules/surgery/bones.dm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/code/modules/surgery/bones.dm b/code/modules/surgery/bones.dm index 6b070b63a7cd..e6bc246c0f13 100644 --- a/code/modules/surgery/bones.dm +++ b/code/modules/surgery/bones.dm @@ -138,10 +138,14 @@ /decl/surgery_step/bone/finish/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) var/bone = affected.encased ? "\the [target]'s damaged [affected.encased]" : "damaged bones in [target]'s [affected.name]" - user.visible_message("[user] has mended [bone] with \the [tool]." , \ - "You have mended [bone] with \the [tool]." ) - affected.mend_fracture() - affected.stage = 0 + // if it's too damaged to mend/will just re-break, warn them and don't lower our stage + if(affected.mend_fracture()) + user.visible_message(SPAN_NOTICE("[user] has mended [bone] with \the [tool].") , \ + SPAN_NOTICE("You have mended [bone] with \the [tool].")) + affected.stage = 0 + else + user.visible_message(SPAN_WARNING("[user] attempted to mend [bone] with \the [tool], but it was too damaged!"), + SPAN_WARNING("You failed to mend [bone] with \the [tool], as it is too damaged.")) affected.update_wounds() ..() From 20eb63186fddffec3ad07ab795c4dddf24775c89 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:40:23 -0400 Subject: [PATCH 0832/1331] Move powernet sensor into main power module --- code/modules/power/{sensors => }/powernet_sensor.dm | 0 nebula.dme | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename code/modules/power/{sensors => }/powernet_sensor.dm (100%) diff --git a/code/modules/power/sensors/powernet_sensor.dm b/code/modules/power/powernet_sensor.dm similarity index 100% rename from code/modules/power/sensors/powernet_sensor.dm rename to code/modules/power/powernet_sensor.dm diff --git a/nebula.dme b/nebula.dme index 7c527182f6f6..fac8e40fce33 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3413,6 +3413,7 @@ #include "code\modules\power\port_gen.dm" #include "code\modules\power\power.dm" #include "code\modules\power\powernet.dm" +#include "code\modules\power\powernet_sensor.dm" #include "code\modules\power\smes.dm" #include "code\modules\power\smes_construction.dm" #include "code\modules\power\smes_presets.dm" @@ -3446,7 +3447,6 @@ #include "code\modules\power\geothermal\_geothermal.dm" #include "code\modules\power\geothermal\geothermal_circuit.dm" #include "code\modules\power\geothermal\geothermal_extension.dm" -#include "code\modules\power\sensors\powernet_sensor.dm" #include "code\modules\power\singularity\collector.dm" #include "code\modules\power\singularity\containment_field.dm" #include "code\modules\power\singularity\emitter.dm" From 00a9824b6a0aa2b7b733609abcfeb3ffb76f65bf Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Oct 2025 20:08:56 -0400 Subject: [PATCH 0833/1331] Make powernet sensors not a subtype of /obj/machinery/power --- code/game/machinery/pipe/construction.dm | 2 +- .../programs/engineering/power_monitor.dm | 16 +++---- code/modules/power/powernet_sensor.dm | 48 +++++++++---------- maps/away/bearcat/bearcat-2.dmm | 2 +- maps/exodus/exodus-1.dmm | 4 +- maps/exodus/exodus-2.dmm | 22 ++++----- maps/ministation/ministation-0.dmm | 4 +- maps/tradeship/tradeship-2.dmm | 2 +- tools/map_migrations/5120_powersensor.txt | 2 + 9 files changed, 50 insertions(+), 52 deletions(-) create mode 100644 tools/map_migrations/5120_powersensor.txt diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 005f27ca3465..34843e14fd26 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -168,4 +168,4 @@ Buildable meters icon = 'icons/obj/objects.dmi' icon_state = "floor_beacon" // If anyone wants to make better sprite, feel free to do so without asking me. w_class = ITEM_SIZE_NORMAL - build_type = /obj/machinery/power/sensor \ No newline at end of file + build_type = /obj/machinery/power_sensor \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index 15cddd991fc8..7c3a7e7faea0 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -48,10 +48,10 @@ // Checks whether there is an active alarm, if yes, returns 1, otherwise returns 0. /datum/nano_module/program/power_monitor/proc/has_alarm() - for(var/obj/machinery/power/sensor/S in grid_sensors) - if(S.check_grid_warning()) - return 1 - return 0 + for(var/obj/machinery/power_sensor/S in grid_sensors) + if(S.has_grid_warning()) + return TRUE + return FALSE // If PC is not null header template is loaded. Use PC.get_header_data() to get relevant nanoui data from it. All data entries begin with "PC_...." // In future it may be expanded to other modular computer devices. @@ -60,13 +60,13 @@ var/list/sensors = list() // Focus: If it remains null if no sensor is selected and UI will display sensor list, otherwise it will display sensor reading. - var/obj/machinery/power/sensor/focus = null + var/obj/machinery/power_sensor/focus = null // Build list of data from sensor readings. - for(var/obj/machinery/power/sensor/S in grid_sensors) + for(var/obj/machinery/power_sensor/S in grid_sensors) sensors.Add(list(list( "name" = html_encode(S.id_tag), - "alarm" = S.check_grid_warning() + "alarm" = S.has_grid_warning() ))) if(S.id_tag == active_sensor) focus = S @@ -88,7 +88,7 @@ /datum/nano_module/program/power_monitor/proc/refresh_sensors() grid_sensors = list() var/connected_z_levels = SSmapping.get_connected_levels(get_host_z()) - for(var/obj/machinery/power/sensor/S in SSmachines.machinery) + for(var/obj/machinery/power_sensor/S in SSmachines.machinery) if(get_z(S) in connected_z_levels) // Consoles have range on their Z-Level. Sensors with long_range var will work between Z levels. grid_sensors += S events_repository.register(/decl/observ/destroyed, S, src, TYPE_PROC_REF(/datum/nano_module/program/power_monitor, remove_sensor)) diff --git a/code/modules/power/powernet_sensor.dm b/code/modules/power/powernet_sensor.dm index 19352222a15a..0f52a584883a 100644 --- a/code/modules/power/powernet_sensor.dm +++ b/code/modules/power/powernet_sensor.dm @@ -7,7 +7,7 @@ // may be used in PDAs or similar applications. Second proc, return_reading_data will return list containing needed data. // This is used in NanoUI, for example. -/obj/machinery/power/sensor +/obj/machinery/power_sensor name = "powernet sensor" desc = "A sensor that records and transmits data about its connected power network." anchored = TRUE @@ -24,14 +24,14 @@ // Proc: New() // Parameters: None // Description: Automatically assigns name according to ID tag. -/obj/machinery/power/sensor/Initialize() +/obj/machinery/power_sensor/Initialize() . = ..() auto_set_name() // Proc: auto_set_name() // Parameters: None // Description: Sets name of this sensor according to the ID tag. -/obj/machinery/power/sensor/proc/auto_set_name() +/obj/machinery/power_sensor/proc/auto_set_name() if(!id_tag) var/area/A = get_area(src) if(!A) @@ -42,20 +42,17 @@ id_tag = "[A.proper_name][suffix ? " #[suffix]" : null]" name = "[id_tag] - powernet sensor" -// Proc: check_grid_warning() +// Proc: has_grid_warning() // Parameters: None -// Description: Checks connected powernet for warnings. If warning is found returns 1 -/obj/machinery/power/sensor/proc/check_grid_warning() - connect_to_network() - if(powernet) - if(powernet.problem) - return 1 - return 0 +// Description: Checks connected powernet for warnings. If warning is found returns TRUE +/obj/machinery/power_sensor/proc/has_grid_warning() + var/datum/powernet/net_to_check = get_powernet() + return net_to_check?.problem ? TRUE : FALSE // Proc: reading_to_text() // Parameters: 1 (amount - Power in Watts to be converted to W, kW or MW) // Description: Helper proc that converts reading in Watts to kW or MW (returns string version of amount parameter) -/obj/machinery/power/sensor/proc/reading_to_text(var/amount = 0) +/obj/machinery/power_sensor/proc/reading_to_text(var/amount = 0) var/units = "" // 10kW and less - Watts if(amount < 10000) @@ -76,12 +73,13 @@ // Proc: find_apcs() // Parameters: None // Description: Searches powernet for APCs and returns them in a list. -/obj/machinery/power/sensor/proc/find_apcs() - if(!powernet) +/obj/machinery/power_sensor/proc/find_apcs() + var/datum/powernet/net_to_check = get_powernet() + if(!net_to_check) return var/list/L = list() - for(var/obj/machinery/power/terminal/term in powernet.nodes) + for(var/obj/machinery/power/terminal/term in net_to_check.nodes) var/obj/machinery/apc/A = term.master_machine() if(istype(A)) L += A @@ -91,13 +89,11 @@ // Proc: return_reading_data() // Parameters: None // Description: Generates list containing all powernet data. Optimised for usage with NanoUI -/obj/machinery/power/sensor/proc/return_reading_data() - // No powernet. Try to connect to one first. - if(!powernet) - connect_to_network() +/obj/machinery/power_sensor/proc/return_reading_data() + var/datum/powernet/net_to_check = get_powernet() var/list/data = list() data["name"] = id_tag - if(!powernet) + if(!net_to_check) data["error"] = "# SYSTEM ERROR - NO POWERNET #" data["alarm"] = 0 // Runtime Prevention return data @@ -137,16 +133,16 @@ // Add load of this APC to total APC load calculation total_apc_load += A.lastused_total data["apc_data"] = APC_data - data["total_avail"] = reading_to_text(max(powernet.avail, 0)) + data["total_avail"] = reading_to_text(max(net_to_check.avail, 0)) data["total_used_apc"] = reading_to_text(max(total_apc_load, 0)) - data["total_used_other"] = reading_to_text(max(powernet.viewload - total_apc_load, 0)) - data["total_used_all"] = reading_to_text(max(powernet.viewload, 0)) + data["total_used_other"] = reading_to_text(max(net_to_check.viewload - total_apc_load, 0)) + data["total_used_all"] = reading_to_text(max(net_to_check.viewload, 0)) // Prevents runtimes when avail is 0 (division by zero) - if(powernet.avail) - data["load_percentage"] = round((powernet.viewload / powernet.avail) * 100) + if(net_to_check.avail) + data["load_percentage"] = round((net_to_check.viewload / net_to_check.avail) * 100) else data["load_percentage"] = 100 - data["alarm"] = powernet.problem ? 1 : 0 + data["alarm"] = net_to_check.problem ? 1 : 0 return data diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 30150ba50d4e..14b782b9dba0 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -3993,7 +3993,7 @@ /turf/floor/tiled/usedup, /area/ship/scrap/maintenance/power) "hK" = ( -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Main Grid" }, /obj/structure/cable{ diff --git a/maps/exodus/exodus-1.dmm b/maps/exodus/exodus-1.dmm index 2a76b49434c6..2fa3d828f5ed 100644 --- a/maps/exodus/exodus-1.dmm +++ b/maps/exodus/exodus-1.dmm @@ -1570,7 +1570,7 @@ name = "north bump"; pixel_y = 24 }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Command Sublevel Subgrid"; name = "Powernet Sensor - Command Sublevel Subgrid" }, @@ -3588,7 +3588,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/atmos) "jx" = ( -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Atmospherics Subgrid"; name = "Powernet Sensor - Atmospherics Subgrid" }, diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index ee4834d3cee5..39076a2ae103 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -5981,7 +5981,7 @@ dir = 4; pixel_x = -22 }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Security Subgrid"; name = "Powernet Sensor - Security Subgrid" }, @@ -14969,7 +14969,7 @@ /obj/structure/cable/green{ icon_state = "0-8" }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Civilian East Subgrid"; name = "Powernet Sensor - Civilian East Subgrid" }, @@ -16978,7 +16978,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Civilian West Subgrid"; name = "Powernet Sensor - Civilian West" }, @@ -27810,7 +27810,7 @@ /turf/floor/plating, /area/exodus/engineering/engine_room) "bhS" = ( -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "AI Subgrid"; name = "Powernet Sensor - AI Subgrid" }, @@ -31646,7 +31646,7 @@ icon_state = "0-4" }, /obj/structure/cable/green, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Command Subgrid"; name = "Powernet Sensor - Command Subgrid" }, @@ -37164,7 +37164,7 @@ icon_state = "0-4" }, /obj/structure/cable/green, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Medbay Subgrid"; name = "Powernet Sensor - Medbay Subgrid" }, @@ -50870,7 +50870,7 @@ icon_state = "0-2" }, /obj/structure/cable/green, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Research Subgrid"; name = "Powernet Sensor - Research Subgrid" }, @@ -54796,7 +54796,7 @@ /obj/structure/cable/green{ icon_state = "0-8" }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Engineering Subgrid"; name = "Powernet Sensor - Engineering Subgrid" }, @@ -61661,7 +61661,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Master"; name = "Powernet Sensor - Master Grid" }, @@ -61872,7 +61872,7 @@ dir = 4 }, /obj/structure/cable/yellow, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Engine Output"; name = "Powernet Sensor - Engine Output" }, @@ -62721,7 +62721,7 @@ /obj/structure/cable/cyan{ icon_state = "0-8" }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Engine Power"; name = "Powernet Sensor - Engine Power" }, diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 0d703c96b6de..2932af289c5b 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -6993,7 +6993,7 @@ /obj/structure/cable{ icon_state = "0-8" }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Station Power"; name = "Powernet Sensor - Station Power" }, @@ -7583,7 +7583,7 @@ /obj/structure/cable/yellow{ icon_state = "4-8" }, -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Engine Power"; name = "Powernet Sensor - Engine Power" }, diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 03f9624c6d0b..f476416e5731 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -3434,7 +3434,7 @@ /turf/floor/tiled/techfloor/grid, /area/ship/trade/maintenance/power) "ip" = ( -/obj/machinery/power/sensor{ +/obj/machinery/power_sensor{ id_tag = "Main Grid" }, /obj/structure/cable{ diff --git a/tools/map_migrations/5120_powersensor.txt b/tools/map_migrations/5120_powersensor.txt new file mode 100644 index 000000000000..076dbeb33356 --- /dev/null +++ b/tools/map_migrations/5120_powersensor.txt @@ -0,0 +1,2 @@ +# Powernet sensors are no longer a subtype of /obj/machinery/power +/obj/machinery/power/sensor/@SUBTYPES : /obj/machinery/power_sensor/@SUBTYPES{@OLD} \ No newline at end of file From c7ad5cc51ed5c4d3fdd6d57c29deb90b82a3d1bb Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:26:46 -0400 Subject: [PATCH 0834/1331] Move light fixtures and bulbs/tubes into their own module --- code/modules/lights/_light_defines.dm | 11 + code/modules/{power => lights}/floorlamp.dm | 0 .../lighting.dm => lights/light_fixture.dm} | 191 ------------------ code/modules/lights/light_items.dm | 172 ++++++++++++++++ nebula.dme | 6 +- 5 files changed, 187 insertions(+), 193 deletions(-) create mode 100644 code/modules/lights/_light_defines.dm rename code/modules/{power => lights}/floorlamp.dm (100%) rename code/modules/{power/lighting.dm => lights/light_fixture.dm} (72%) create mode 100644 code/modules/lights/light_items.dm diff --git a/code/modules/lights/_light_defines.dm b/code/modules/lights/_light_defines.dm new file mode 100644 index 000000000000..33a9fe364e30 --- /dev/null +++ b/code/modules/lights/_light_defines.dm @@ -0,0 +1,11 @@ +// status values shared between lighting fixtures and items +#define LIGHT_OK 0 +#define LIGHT_EMPTY 1 +#define LIGHT_BROKEN 2 +#define LIGHT_BURNED 3 + +#define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb +#define LIGHTING_POWER_FACTOR 5 //5W per luminosity * range + +#define LIGHTMODE_EMERGENCY "emergency_lighting" +#define LIGHTMODE_READY "ready" \ No newline at end of file diff --git a/code/modules/power/floorlamp.dm b/code/modules/lights/floorlamp.dm similarity index 100% rename from code/modules/power/floorlamp.dm rename to code/modules/lights/floorlamp.dm diff --git a/code/modules/power/lighting.dm b/code/modules/lights/light_fixture.dm similarity index 72% rename from code/modules/power/lighting.dm rename to code/modules/lights/light_fixture.dm index 39184fecc631..a9c5f84cc99b 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/lights/light_fixture.dm @@ -1,21 +1,3 @@ -// The lighting system -// -// consists of light fixtures (/obj/machinery/light) and light tube/bulb items (/obj/item/light) - - -// status values shared between lighting fixtures and items -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - -#define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb -#define LIGHTING_POWER_FACTOR 5 //5W per luminosity * range - - -#define LIGHTMODE_EMERGENCY "emergency_lighting" -#define LIGHTMODE_READY "ready" - // the standard tube light fixture /obj/machinery/light name = "light fixture" @@ -478,179 +460,6 @@ /obj/machinery/light/navigation/delay5 delay = 5 -// the light item -// can be tube or bulb subtypes -// will fit into empty /obj/machinery/light of the corresponding type - -/obj/item/light - icon = 'icons/obj/lighting.dmi' - w_class = ITEM_SIZE_SMALL - material = /decl/material/solid/metal/steel - atom_flags = ATOM_FLAG_CAN_BE_PAINTED - obj_flags = OBJ_FLAG_HOLLOW - var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN - var/base_state - var/switchcount = 0 // number of times switched - var/rigged = 0 // true if rigged to explode - var/broken_chance = 2 - - var/b_power = 0.7 - var/b_range = 5 - var/b_color = LIGHT_COLOR_HALOGEN - var/list/lighting_modes = list() - var/sound_on - -/obj/item/light/get_color() - return b_color - -/obj/item/light/set_color(color) - b_color = isnull(color) ? COLOR_WHITE : color - queue_icon_update() // avoid running update_icon before Initialize - -/obj/item/light/tube - name = "light tube" - desc = "A replacement light tube." - icon_state = "ltube" - base_state = "ltube" - item_state = "c_tube" - material = /decl/material/solid/glass - matter = list(/decl/material/solid/metal/aluminium = MATTER_AMOUNT_REINFORCEMENT) - - b_range = 8 - b_power = 0.8 - b_color = LIGHT_COLOR_HALOGEN - lighting_modes = list( - LIGHTMODE_EMERGENCY = list(l_range = 4, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), - ) - sound_on = 'sound/machines/lightson.ogg' - -/obj/item/light/tube/party/Initialize() //Randomly colored light tubes. Mostly for testing, but maybe someone will find a use for them. - . = ..() - b_color = rgb(pick(0,255), pick(0,255), pick(0,255)) - -/obj/item/light/tube/large - w_class = ITEM_SIZE_SMALL - name = "large light tube" - b_power = 4 - b_range = 12 - -/obj/item/light/tube/large/party/Initialize() //Randomly colored light tubes. Mostly for testing, but maybe someone will find a use for them. - . = ..() - b_color = rgb(pick(0,255), pick(0,255), pick(0,255)) - -/obj/item/light/bulb - name = "light bulb" - desc = "A replacement light bulb." - icon_state = "lbulb" - base_state = "lbulb" - item_state = "contvapour" - broken_chance = 3 - material = /decl/material/solid/glass - b_color = LIGHT_COLOR_TUNGSTEN - lighting_modes = list( - LIGHTMODE_EMERGENCY = list(l_range = 3, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), - ) - -/obj/item/light/bulb/red - color = LIGHT_COLOR_RED - b_color = LIGHT_COLOR_RED - -/obj/item/light/bulb/red/readylight - lighting_modes = list( - LIGHTMODE_READY = list(l_range = 5, l_power = 1, l_color = LIGHT_COLOR_GREEN), - ) - -/obj/item/light/throw_impact(atom/hit_atom) - ..() - shatter() - -/obj/item/light/bulb/fire - name = "fire bulb" - desc = "A replacement fire bulb." - icon_state = "fbulb" - base_state = "fbulb" - item_state = "egg4" - material = /decl/material/solid/glass - -// update the icon state and description of the light -/obj/item/light/on_update_icon() - . = ..() - var/broken - switch(status) - if(LIGHT_OK) - icon_state = base_state - desc = "A replacement [name]." - if(LIGHT_BURNED) - icon_state = "[base_state]_burned" - desc = "A burnt-out [name]." - if(LIGHT_BROKEN) - icon_state = "[base_state]_broken" - desc = "A broken [name]." - broken = TRUE - add_overlay(overlay_image(icon, "[base_state]_attachment[broken ? "_broken" : ""]", flags = RESET_COLOR|RESET_ALPHA)) - -/obj/item/light/Initialize(mapload) - . = ..() - update_icon() - -// attack bulb/tube with object -// if a syringe, can inject flammable liquids to make it explode -/obj/item/light/attackby(var/obj/item/used_item, var/mob/user) - ..() - if(istype(used_item, /obj/item/chems/syringe) && used_item.reagents?.total_volume) - var/obj/item/chems/syringe/S = used_item - to_chat(user, "You inject the solution into \the [src].") - for(var/decl/material/reagent as anything in S.reagents?.reagent_volumes) - if(reagent.accelerant_value > FUEL_VALUE_ACCELERANT) - rigged = TRUE - log_and_message_admins("injected a light with flammable reagents, rigging it to explode.", user) - break - S.reagents.clear_reagents() - return TRUE - . = ..() - -// called after an attack with a light item -// shatter light, unless it was an attempt to put it in a light socket -// now only shatter if the intent was harm - -/obj/item/light/afterattack(atom/target, mob/user, proximity) - if(!proximity) return - if(istype(target, /obj/machinery/light)) - return - if(!user.check_intent(I_FLAG_HARM)) - return - - shatter() - -/obj/item/light/shatter() - if(status == LIGHT_OK || status == LIGHT_BURNED) - src.visible_message("[name] shatters.","You hear a small glass object shatter.") - status = LIGHT_BROKEN - set_sharp(TRUE) - set_base_attack_force(5) - playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) - update_icon() - -/obj/item/light/proc/switch_on() - switchcount++ - if(rigged) - addtimer(CALLBACK(src, PROC_REF(do_rigged_explosion)), 0.2 SECONDS) - status = LIGHT_BROKEN - else if(prob(min(60, switchcount*switchcount*0.01))) - status = LIGHT_BURNED - else if(sound_on) - playsound(src, sound_on, 75) - return status - -/obj/item/light/proc/do_rigged_explosion() - if(!rigged) - return - log_and_message_admins("Rigged light explosion, last touched by [fingerprintslast]") - var/turf/T = get_turf(src) - explosion(T, 0, 0, 3, 5) - if(!QDELETED(src)) - QDEL_IN(src, 1) - /obj/machinery/light/do_simple_ranged_interaction(var/mob/user) if(lightbulb) remove_bulb() diff --git a/code/modules/lights/light_items.dm b/code/modules/lights/light_items.dm new file mode 100644 index 000000000000..8c596a523756 --- /dev/null +++ b/code/modules/lights/light_items.dm @@ -0,0 +1,172 @@ +// the light item +// can be tube or bulb subtypes +// will fit into empty /obj/machinery/light of the corresponding type + +/obj/item/light + icon = 'icons/obj/lighting.dmi' + w_class = ITEM_SIZE_SMALL + material = /decl/material/solid/metal/steel + atom_flags = ATOM_FLAG_CAN_BE_PAINTED + obj_flags = OBJ_FLAG_HOLLOW + var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN + var/base_state + var/switchcount = 0 // number of times switched + var/rigged = 0 // true if rigged to explode + var/broken_chance = 2 + + var/b_power = 0.7 + var/b_range = 5 + var/b_color = LIGHT_COLOR_HALOGEN + var/list/lighting_modes = list() + var/sound_on + +/obj/item/light/get_color() + return b_color + +/obj/item/light/set_color(color) + b_color = isnull(color) ? COLOR_WHITE : color + queue_icon_update() // avoid running update_icon before Initialize + +/obj/item/light/tube + name = "light tube" + desc = "A replacement light tube." + icon_state = "ltube" + base_state = "ltube" + item_state = "c_tube" + material = /decl/material/solid/glass + matter = list(/decl/material/solid/metal/aluminium = MATTER_AMOUNT_REINFORCEMENT) + + b_range = 8 + b_power = 0.8 + b_color = LIGHT_COLOR_HALOGEN + lighting_modes = list( + LIGHTMODE_EMERGENCY = list(l_range = 4, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), + ) + sound_on = 'sound/machines/lightson.ogg' + +/obj/item/light/tube/party/Initialize() //Randomly colored light tubes. Mostly for testing, but maybe someone will find a use for them. + . = ..() + b_color = rgb(pick(0,255), pick(0,255), pick(0,255)) + +/obj/item/light/tube/large + w_class = ITEM_SIZE_SMALL + name = "large light tube" + b_power = 4 + b_range = 12 + +/obj/item/light/tube/large/party/Initialize() //Randomly colored light tubes. Mostly for testing, but maybe someone will find a use for them. + . = ..() + b_color = rgb(pick(0,255), pick(0,255), pick(0,255)) + +/obj/item/light/bulb + name = "light bulb" + desc = "A replacement light bulb." + icon_state = "lbulb" + base_state = "lbulb" + item_state = "contvapour" + broken_chance = 3 + material = /decl/material/solid/glass + b_color = LIGHT_COLOR_TUNGSTEN + lighting_modes = list( + LIGHTMODE_EMERGENCY = list(l_range = 3, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), + ) + +/obj/item/light/bulb/red + color = LIGHT_COLOR_RED + b_color = LIGHT_COLOR_RED + +/obj/item/light/bulb/red/readylight + lighting_modes = list( + LIGHTMODE_READY = list(l_range = 5, l_power = 1, l_color = LIGHT_COLOR_GREEN), + ) + +/obj/item/light/throw_impact(atom/hit_atom) + ..() + shatter() + +/obj/item/light/bulb/fire + name = "fire bulb" + desc = "A replacement fire bulb." + icon_state = "fbulb" + base_state = "fbulb" + item_state = "egg4" + material = /decl/material/solid/glass + +// update the icon state and description of the light +/obj/item/light/on_update_icon() + . = ..() + var/broken + switch(status) + if(LIGHT_OK) + icon_state = base_state + desc = "A replacement [name]." + if(LIGHT_BURNED) + icon_state = "[base_state]_burned" + desc = "A burnt-out [name]." + if(LIGHT_BROKEN) + icon_state = "[base_state]_broken" + desc = "A broken [name]." + broken = TRUE + add_overlay(overlay_image(icon, "[base_state]_attachment[broken ? "_broken" : ""]", flags = RESET_COLOR|RESET_ALPHA)) + +/obj/item/light/Initialize(mapload) + . = ..() + update_icon() + +// attack bulb/tube with object +// if a syringe, can inject flammable liquids to make it explode +/obj/item/light/attackby(var/obj/item/used_item, var/mob/user) + ..() + if(istype(used_item, /obj/item/chems/syringe) && used_item.reagents?.total_volume) + var/obj/item/chems/syringe/S = used_item + to_chat(user, "You inject the solution into \the [src].") + for(var/decl/material/reagent as anything in S.reagents?.reagent_volumes) + if(reagent.accelerant_value > FUEL_VALUE_ACCELERANT) + rigged = TRUE + log_and_message_admins("injected a light with flammable reagents, rigging it to explode.", user) + break + S.reagents.clear_reagents() + return TRUE + . = ..() + +// called after an attack with a light item +// shatter light, unless it was an attempt to put it in a light socket +// now only shatter if the intent was harm + +/obj/item/light/afterattack(atom/target, mob/user, proximity) + if(!proximity) return + if(istype(target, /obj/machinery/light)) + return + if(!user.check_intent(I_FLAG_HARM)) + return + + shatter() + +/obj/item/light/shatter() + if(status == LIGHT_OK || status == LIGHT_BURNED) + src.visible_message("[name] shatters.","You hear a small glass object shatter.") + status = LIGHT_BROKEN + set_sharp(TRUE) + set_base_attack_force(5) + playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) + update_icon() + +/obj/item/light/proc/switch_on() + switchcount++ + if(rigged) + addtimer(CALLBACK(src, PROC_REF(do_rigged_explosion)), 0.2 SECONDS) + status = LIGHT_BROKEN + else if(prob(min(60, switchcount*switchcount*0.01))) + status = LIGHT_BURNED + else if(sound_on) + playsound(src, sound_on, 75) + return status + +/obj/item/light/proc/do_rigged_explosion() + if(!rigged) + return + log_and_message_admins("Rigged light explosion, last touched by [fingerprintslast]") + var/turf/T = get_turf(src) + explosion(T, 0, 0, 3, 5) + if(!QDELETED(src)) + QDEL_IN(src, 1) diff --git a/nebula.dme b/nebula.dme index 7c527182f6f6..e22351f46394 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2633,6 +2633,10 @@ #include "code\modules\lighting\lighting_source.dm" #include "code\modules\lighting\lighting_turf.dm" #include "code\modules\lighting\~lighting_undefs.dm" +#include "code\modules\lights\_light_defines.dm" +#include "code\modules\lights\floorlamp.dm" +#include "code\modules\lights\light_fixture.dm" +#include "code\modules\lights\light_items.dm" #include "code\modules\locks\key.dm" #include "code\modules\locks\keyring.dm" #include "code\modules\locks\lock.dm" @@ -3405,11 +3409,9 @@ #include "code\modules\power\cable.dm" #include "code\modules\power\cell.dm" #include "code\modules\power\debug_items.dm" -#include "code\modules\power\floorlamp.dm" #include "code\modules\power\generator.dm" #include "code\modules\power\gravitygenerator.dm" #include "code\modules\power\heavycable.dm" -#include "code\modules\power\lighting.dm" #include "code\modules\power\port_gen.dm" #include "code\modules\power\power.dm" #include "code\modules\power\powernet.dm" From f40272e36f5df8271e0527b4f8798961e16452d0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 13 Oct 2025 14:21:19 -0400 Subject: [PATCH 0835/1331] Reorganize lights module --- code/game/machinery/wall_frames.dm | 26 --- .../designs/general/designs_engineering.dm | 12 -- .../designs/general/designs_general.dm | 9 - code/modules/lights/_light_defines.dm | 11 -- .../{light_items.dm => light_bulbs_tubes.dm} | 32 ++-- code/modules/lights/light_fabrication.dm | 26 +++ ...light_fixture.dm => light_fixture_base.dm} | 155 ++++-------------- .../lights/light_fixture_construction.dm | 56 +++++++ ...loorlamp.dm => light_fixture_floorlamp.dm} | 20 +-- .../lights/light_fixture_navigation.dm | 36 ++++ code/modules/lights/light_fixture_small.dm | 26 +++ code/modules/lights/light_fixture_spot.dm | 7 + .../lights/light_replacer.dm} | 26 +-- .../lights/light_switch.dm} | 0 .../lights/light_switch_frames.dm} | 2 - nebula.dme | 18 +- 16 files changed, 224 insertions(+), 238 deletions(-) delete mode 100644 code/modules/lights/_light_defines.dm rename code/modules/lights/{light_items.dm => light_bulbs_tubes.dm} (84%) create mode 100644 code/modules/lights/light_fabrication.dm rename code/modules/lights/{light_fixture.dm => light_fixture_base.dm} (75%) create mode 100644 code/modules/lights/light_fixture_construction.dm rename code/modules/lights/{floorlamp.dm => light_fixture_floorlamp.dm} (75%) create mode 100644 code/modules/lights/light_fixture_navigation.dm create mode 100644 code/modules/lights/light_fixture_small.dm create mode 100644 code/modules/lights/light_fixture_spot.dm rename code/{game/objects/items/devices/lightreplacer.dm => modules/lights/light_replacer.dm} (89%) rename code/{game/machinery/lightswitch.dm => modules/lights/light_switch.dm} (100%) rename code/{game/objects/items/buttons.dm => modules/lights/light_switch_frames.dm} (97%) diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm index 56e7c788416d..13bf81ae5e2d 100644 --- a/code/game/machinery/wall_frames.dm +++ b/code/game/machinery/wall_frames.dm @@ -104,32 +104,6 @@ name = "wall-mounted relay kit" desc = "An all-in-one wall-mounted relay kit, comes preassembled." -/obj/item/frame/light - name = "light fixture frame" - desc = "Used for building lights." - icon = 'icons/obj/lighting.dmi' - icon_state = "tube-construct-item" - build_machine_type = /obj/machinery/light - reverse = 1 - -/obj/item/frame/light/small - name = "small light fixture frame" - icon_state = "bulb-construct-item" - material = /decl/material/solid/metal/steel - build_machine_type = /obj/machinery/light/small - -/obj/item/frame/light/spot - name = "spotlight fixture frame" - icon_state = "tube-construct-item" - material = /decl/material/solid/metal/steel - build_machine_type = /obj/machinery/light/spot - -/obj/item/frame/light/nav - name = "navigation light fixture frame" - icon_state = "tube-construct-item" - material = /decl/material/solid/metal/steel - build_machine_type = /obj/machinery/light/navigation - /obj/item/frame/button name = "button frame" icon = 'icons/obj/objects.dmi' diff --git a/code/modules/fabrication/designs/general/designs_engineering.dm b/code/modules/fabrication/designs/general/designs_engineering.dm index c877965c4ccf..bb76b11ed1f0 100644 --- a/code/modules/fabrication/designs/general/designs_engineering.dm +++ b/code/modules/fabrication/designs/general/designs_engineering.dm @@ -29,18 +29,6 @@ /datum/fabricator_recipe/engineering/wallcharger path = /obj/item/frame/button/wall_charger/kit -/datum/fabricator_recipe/engineering/light_switch - path = /obj/item/frame/button/light_switch/kit - -/datum/fabricator_recipe/engineering/window_tint - path = /obj/item/frame/button/light_switch/windowtint/kit - -/datum/fabricator_recipe/engineering/spotlight - path = /obj/item/frame/light/spot - -/datum/fabricator_recipe/engineering/navlight - path = /obj/item/frame/light/nav - /datum/fabricator_recipe/engineering/button_frame path = /obj/item/frame/button /datum/fabricator_recipe/engineering/button_kit diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index 55d33d3e2941..aef3b2e9a27a 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -46,15 +46,6 @@ /datum/fabricator_recipe/taperecorder_tape path = /obj/item/magnetic_tape -/datum/fabricator_recipe/tube/large - path = /obj/item/light/tube/large - -/datum/fabricator_recipe/tube - path = /obj/item/light/tube - -/datum/fabricator_recipe/bulb - path = /obj/item/light/bulb - /datum/fabricator_recipe/ashtray_glass path = /obj/item/ashtray/glass diff --git a/code/modules/lights/_light_defines.dm b/code/modules/lights/_light_defines.dm deleted file mode 100644 index 33a9fe364e30..000000000000 --- a/code/modules/lights/_light_defines.dm +++ /dev/null @@ -1,11 +0,0 @@ -// status values shared between lighting fixtures and items -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - -#define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb -#define LIGHTING_POWER_FACTOR 5 //5W per luminosity * range - -#define LIGHTMODE_EMERGENCY "emergency_lighting" -#define LIGHTMODE_READY "ready" \ No newline at end of file diff --git a/code/modules/lights/light_items.dm b/code/modules/lights/light_bulbs_tubes.dm similarity index 84% rename from code/modules/lights/light_items.dm rename to code/modules/lights/light_bulbs_tubes.dm index 8c596a523756..6923213ab2b8 100644 --- a/code/modules/lights/light_items.dm +++ b/code/modules/lights/light_bulbs_tubes.dm @@ -8,7 +8,15 @@ material = /decl/material/solid/metal/steel atom_flags = ATOM_FLAG_CAN_BE_PAINTED obj_flags = OBJ_FLAG_HOLLOW - var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN + var/const/MODE_EMERGENCY = "emergency_lighting" + var/const/MODE_READY = "ready" + + var/const/STATUS_OK = 0 + var/const/STATUS_EMPTY = 1 + var/const/STATUS_BROKEN = 2 + var/const/STATUS_BURNED = 3 + + var/status = STATUS_OK // STATUS_OK, STATUS_BURNED or STATUS_BROKEN var/base_state var/switchcount = 0 // number of times switched var/rigged = 0 // true if rigged to explode @@ -40,7 +48,7 @@ b_power = 0.8 b_color = LIGHT_COLOR_HALOGEN lighting_modes = list( - LIGHTMODE_EMERGENCY = list(l_range = 4, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), + MODE_EMERGENCY = list(l_range = 4, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), ) sound_on = 'sound/machines/lightson.ogg' @@ -68,7 +76,7 @@ material = /decl/material/solid/glass b_color = LIGHT_COLOR_TUNGSTEN lighting_modes = list( - LIGHTMODE_EMERGENCY = list(l_range = 3, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), + MODE_EMERGENCY = list(l_range = 3, l_power = 1, l_color = LIGHT_COLOR_EMERGENCY), ) /obj/item/light/bulb/red @@ -77,7 +85,7 @@ /obj/item/light/bulb/red/readylight lighting_modes = list( - LIGHTMODE_READY = list(l_range = 5, l_power = 1, l_color = LIGHT_COLOR_GREEN), + MODE_READY = list(l_range = 5, l_power = 1, l_color = LIGHT_COLOR_GREEN), ) /obj/item/light/throw_impact(atom/hit_atom) @@ -97,13 +105,13 @@ . = ..() var/broken switch(status) - if(LIGHT_OK) + if(STATUS_OK) icon_state = base_state desc = "A replacement [name]." - if(LIGHT_BURNED) + if(STATUS_BURNED) icon_state = "[base_state]_burned" desc = "A burnt-out [name]." - if(LIGHT_BROKEN) + if(STATUS_BROKEN) icon_state = "[base_state]_broken" desc = "A broken [name]." broken = TRUE @@ -143,9 +151,9 @@ shatter() /obj/item/light/shatter() - if(status == LIGHT_OK || status == LIGHT_BURNED) - src.visible_message("[name] shatters.","You hear a small glass object shatter.") - status = LIGHT_BROKEN + if(status == STATUS_OK || status == STATUS_BURNED) + src.visible_message(SPAN_WARNING("[name] shatters."), SPAN_WARNING("You hear a small glass object shatter.")) + status = STATUS_BROKEN set_sharp(TRUE) set_base_attack_force(5) playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) @@ -155,9 +163,9 @@ switchcount++ if(rigged) addtimer(CALLBACK(src, PROC_REF(do_rigged_explosion)), 0.2 SECONDS) - status = LIGHT_BROKEN + status = STATUS_BROKEN else if(prob(min(60, switchcount*switchcount*0.01))) - status = LIGHT_BURNED + status = STATUS_BURNED else if(sound_on) playsound(src, sound_on, 75) return status diff --git a/code/modules/lights/light_fabrication.dm b/code/modules/lights/light_fabrication.dm new file mode 100644 index 000000000000..dde35d626cfa --- /dev/null +++ b/code/modules/lights/light_fabrication.dm @@ -0,0 +1,26 @@ +// Bulbs and tubes +/datum/fabricator_recipe/tube/large + path = /obj/item/light/tube/large + +/datum/fabricator_recipe/tube + path = /obj/item/light/tube + +/datum/fabricator_recipe/bulb + path = /obj/item/light/bulb + +// Switches +/datum/fabricator_recipe/engineering/light_switch + path = /obj/item/frame/button/light_switch/kit + +/datum/fabricator_recipe/engineering/window_tint + path = /obj/item/frame/button/light_switch/windowtint/kit + +// Frames +/datum/fabricator_recipe/engineering/spotlight + path = /obj/item/frame/light/spot + +/datum/fabricator_recipe/engineering/navlight + path = /obj/item/frame/light/nav + +/datum/fabricator_recipe/engineering/floorlamp + path = /obj/item/machine_chassis/flamp \ No newline at end of file diff --git a/code/modules/lights/light_fixture.dm b/code/modules/lights/light_fixture_base.dm similarity index 75% rename from code/modules/lights/light_fixture.dm rename to code/modules/lights/light_fixture_base.dm index a9c5f84cc99b..0f2f5741a626 100644 --- a/code/modules/lights/light_fixture.dm +++ b/code/modules/lights/light_fixture_base.dm @@ -21,6 +21,10 @@ frame_type = /obj/item/frame/light directional_offset = @'{"NORTH":{"y":21}, "EAST":{"x":10}, "WEST":{"x":-10}}' + var/const/LIGHT_BULB_TEMPERATURE = 400 //K - used value for a 60W bulb + + var/const/WATTS_PER_LUM_RANGE = 5 // watts per luminosity-range + var/on = 0 // 1 if on, 0 if off var/flickering = 0 var/light_type = /obj/item/light/tube // the type of light item @@ -41,30 +45,6 @@ update_light_status(TRUE) update_icon() -// the smaller bulb light fixture -/obj/machinery/light/small - icon_state = "bulb_map" - base_state = "bulb" - desc = "A small lighting fixture." - light_type = /obj/item/light/bulb - accepts_light_type = /obj/item/light/bulb - base_type = /obj/machinery/light/small - frame_type = /obj/item/frame/light/small - -/obj/machinery/light/small/emergency - light_type = /obj/item/light/bulb/red - -/obj/machinery/light/small/red - light_type = /obj/item/light/bulb/red - -/obj/machinery/light/spot - name = "spotlight" - desc = "A more robust socket for light tubes that demand more power." - light_type = /obj/item/light/tube/large - accepts_light_type = /obj/item/light/tube/large - base_type = /obj/machinery/light/spot - frame_type = /obj/item/frame/light/spot - // create a new lighting fixture /obj/machinery/light/Initialize(mapload, d=0, populate_parts = TRUE) . = ..() @@ -98,7 +78,7 @@ if(currently_updating) // avoid infinite loops during power usage updates return currently_updating = TRUE - if(get_status() == LIGHT_OK) // we can't reuse this value later because update_use_power might change our status + if(get_bulb_status() == lightbulb::STATUS_OK) // we can't reuse this value later because update_use_power might change our status atom_flags |= ATOM_FLAG_CAN_BE_PAINTED else atom_flags &= ~ATOM_FLAG_CAN_BE_PAINTED @@ -109,12 +89,12 @@ set_light(arglist(lightbulb.lighting_modes[current_mode])) else set_light(lightbulb.b_range, lightbulb.b_power, lightbulb.b_color) - if(trigger && get_status() == LIGHT_OK) + if(trigger && get_bulb_status() == lightbulb::STATUS_OK) switch_check() else update_use_power(POWER_USE_OFF) set_light(0) - change_power_consumption((light_range * light_power) * LIGHTING_POWER_FACTOR, POWER_USE_ACTIVE) + change_power_consumption((light_range * light_power) * WATTS_PER_LUM_RANGE, POWER_USE_ACTIVE) currently_updating = FALSE /obj/machinery/light/update_use_power(new_use_power) @@ -137,12 +117,12 @@ // Extra overlays if we're active var/_state - switch(get_status()) // set icon_states - if(LIGHT_OK) + switch(get_bulb_status()) // set icon_states + if(lightbulb::STATUS_OK) _state = "[base_state][on]" - if(LIGHT_BURNED) + if(lightbulb::STATUS_BURNED) _state = "[base_state]_burned" - if(LIGHT_BROKEN) + if(lightbulb::STATUS_BROKEN) _state = "[base_state]_broken" if(istype(lightbulb, /obj/item/light)) @@ -153,15 +133,12 @@ if(on) compile_overlays() // force a compile so that we update prior to the light being set -/obj/machinery/light/proc/get_status() - if(!lightbulb) - return LIGHT_EMPTY - else - return lightbulb.status +/obj/machinery/light/proc/get_bulb_status() + return lightbulb ? lightbulb.status : lightbulb::STATUS_EMPTY /obj/machinery/light/proc/switch_check() lightbulb.switch_on() - if(get_status() != LIGHT_OK) + if(get_bulb_status() != lightbulb::STATUS_OK) set_light(0) /obj/machinery/light/proc/set_mode(var/new_mode) @@ -181,18 +158,18 @@ return if(enable) - if(LIGHTMODE_EMERGENCY in lightbulb.lighting_modes) - set_mode(LIGHTMODE_EMERGENCY) + if(lightbulb::MODE_EMERGENCY in lightbulb.lighting_modes) + set_mode(lightbulb::MODE_EMERGENCY) update_power_channel(ENVIRON) else - if(current_mode == LIGHTMODE_EMERGENCY) + if(current_mode == lightbulb::MODE_EMERGENCY) set_mode(null) update_power_channel(initial(power_channel)) // attempt to set the light's on/off status // will not switch on if broken/burned/empty /obj/machinery/light/proc/seton(var/state) - on = (state && get_status() == LIGHT_OK) + on = (state && get_bulb_status() == lightbulb::STATUS_OK) update_light_status(TRUE) update_icon() @@ -200,19 +177,20 @@ /obj/machinery/light/get_examine_strings(mob/user, distance, infix, suffix) . = ..() var/fitting = get_fitting_name() - switch(get_status()) - if(LIGHT_OK) + switch(get_bulb_status()) + if(lightbulb::STATUS_OK) . += "It is turned [on? "on" : "off"]." - if(LIGHT_EMPTY) + if(lightbulb::STATUS_EMPTY) . += "The [fitting] has been removed." - if(LIGHT_BURNED) + if(lightbulb::STATUS_BURNED) . += "The [fitting] is burnt out." - if(LIGHT_BROKEN) + if(lightbulb::STATUS_BROKEN) . += "The [fitting] has been smashed." /obj/machinery/light/proc/get_fitting_name() - var/obj/item/light/L = light_type - return initial(L.name) + // use accepts_light_type so stuff that begins blank is fine + var/obj/item/light/type_used = accepts_light_type + return type_used::name // attack with item - insert light (if right type), otherwise try to break the light @@ -263,7 +241,7 @@ // attempt to break the light //If xenos decide they want to smash a light bulb with a toolbox, who am I to stop them? /N - else if(lightbulb && (lightbulb.status != LIGHT_BROKEN) && !user.check_intent(I_FLAG_HELP)) + else if(lightbulb && (lightbulb.status != lightbulb::STATUS_BROKEN) && !user.check_intent(I_FLAG_HELP)) if(prob(1 + used_item.expend_attack_force(user) * 5)) @@ -295,13 +273,13 @@ if(flickering) return flickering = 1 spawn(0) - if(on && get_status() == LIGHT_OK) + if(on && get_bulb_status() == lightbulb::STATUS_OK) for(var/i = 0; i < amount; i++) - if(get_status() != LIGHT_OK) break + if(get_bulb_status() != lightbulb::STATUS_OK) break on = !on update_light_status(FALSE) sleep(rand(5, 15)) - on = (get_status() == LIGHT_OK) + on = (get_bulb_status() == lightbulb::STATUS_OK) update_light_status(FALSE) update_icon() flickering = 0 @@ -364,18 +342,18 @@ return if(!skip_sound_and_sparks) - if(lightbulb && !(lightbulb.status == LIGHT_BROKEN)) + if(lightbulb && !(lightbulb.status == lightbulb::STATUS_BROKEN)) playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) if(on) spark_at(src, cardinal_only = TRUE) - lightbulb.status = LIGHT_BROKEN + lightbulb.status = lightbulb::STATUS_BROKEN update_light_status(TRUE) update_icon() /obj/machinery/light/proc/fix() - if(get_status() == LIGHT_OK || !lightbulb) + if(get_bulb_status() == lightbulb::STATUS_OK || !lightbulb) return - lightbulb.status = LIGHT_OK + lightbulb.status = lightbulb::STATUS_OK on = TRUE update_light_status(TRUE) update_icon() @@ -412,70 +390,7 @@ broken() return ..() -/obj/machinery/light/small/readylight - light_type = /obj/item/light/bulb/red/readylight - var/state = 0 - -/obj/machinery/light/small/readylight/proc/set_state(var/new_state) - state = new_state - if(state) - set_mode(LIGHTMODE_READY) - else - set_mode(null) - -/obj/machinery/light/navigation - name = "navigation light" - desc = "A periodically flashing light." - icon = 'icons/obj/lighting_nav.dmi' - icon_state = "nav10" - base_state = "nav1" - light_type = /obj/item/light/tube/large - accepts_light_type = /obj/item/light/tube/large - on = TRUE - var/delay = 1 - base_type = /obj/machinery/light/navigation - frame_type = /obj/item/frame/light/nav - stat_immune = NOPOWER | NOINPUT | NOSCREEN - -/obj/machinery/light/navigation/on_update_icon() - . = ..() // this will handle pixel offsets - icon_state = "nav[delay][!!(lightbulb && on)]" - -/obj/machinery/light/navigation/attackby(obj/item/used_item, mob/user) - . = ..() - if(!. && IS_MULTITOOL(used_item)) - delay = 5 + ((delay + 1) % 5) - to_chat(user, SPAN_NOTICE("You adjust the delay on \the [src].")) - return TRUE - -/obj/machinery/light/navigation/delay2 - delay = 2 - -/obj/machinery/light/navigation/delay3 - delay = 3 - -/obj/machinery/light/navigation/delay4 - delay = 4 - -/obj/machinery/light/navigation/delay5 - delay = 5 - /obj/machinery/light/do_simple_ranged_interaction(var/mob/user) if(lightbulb) remove_bulb() - return TRUE - -// Partially-constructed presets for mapping -/obj/machinery/light/fixture - icon_state = "tube-construct-stage1" - -/obj/machinery/light/fixture/Initialize(mapload, d, populate_parts) - . = ..(mapload, d, populate_parts = FALSE) - construct_state.post_construct(src) - -/obj/machinery/light/small/fixture - icon_state = "bulb-construct-stage1" - -/obj/machinery/light/small/fixture/Initialize(mapload, d, populate_parts) - . = ..(mapload, d, populate_parts = FALSE) - construct_state.post_construct(src) \ No newline at end of file + return TRUE \ No newline at end of file diff --git a/code/modules/lights/light_fixture_construction.dm b/code/modules/lights/light_fixture_construction.dm new file mode 100644 index 000000000000..eb0b410aab85 --- /dev/null +++ b/code/modules/lights/light_fixture_construction.dm @@ -0,0 +1,56 @@ +// Frames +/obj/item/frame/light + name = "light fixture frame" + desc = "Used for building lights." + icon = 'icons/obj/lighting.dmi' + icon_state = "tube-construct-item" + build_machine_type = /obj/machinery/light + reverse = 1 + +/obj/item/frame/light/small + name = "small light fixture frame" + icon_state = "bulb-construct-item" + material = /decl/material/solid/metal/steel + build_machine_type = /obj/machinery/light/small + +/obj/item/frame/light/spot + name = "spotlight fixture frame" + icon_state = "tube-construct-item" + material = /decl/material/solid/metal/steel + build_machine_type = /obj/machinery/light/spot + +/obj/item/frame/light/nav + name = "navigation light fixture frame" + icon_state = "tube-construct-item" + material = /decl/material/solid/metal/steel + build_machine_type = /obj/machinery/light/navigation + +/obj/item/machine_chassis/flamp + name = "lamp fixture frame" + desc = "A bare frame for a standing lamp fixture. Must be secured to the floor with a wrench." + icon = 'icons/obj/floorlamp.dmi' + icon_state = "flamp-construct-item" + w_class = ITEM_SIZE_STRUCTURE + material = /decl/material/solid/metal/steel + build_type = /obj/machinery/light/flamp/noshade/deconstruct + +// Partially-constructed presets for mapping +/obj/machinery/light/fixture + icon_state = "tube-construct-stage1" + +/obj/machinery/light/fixture/Initialize(mapload, d, populate_parts) + . = ..(mapload, d, populate_parts = FALSE) + construct_state.post_construct(src) + +/obj/machinery/light/small/fixture + icon_state = "bulb-construct-stage1" + +/obj/machinery/light/small/fixture/Initialize(mapload, d, populate_parts) + . = ..(mapload, d, populate_parts = FALSE) + construct_state.post_construct(src) + +// Subtype used for creation via crafting. +/obj/machinery/light/flamp/noshade/deconstruct + light_type = null + panel_open = TRUE + construct_state = /decl/machine_construction/wall_frame/no_wires/simple \ No newline at end of file diff --git a/code/modules/lights/floorlamp.dm b/code/modules/lights/light_fixture_floorlamp.dm similarity index 75% rename from code/modules/lights/floorlamp.dm rename to code/modules/lights/light_fixture_floorlamp.dm index 067ee2b484be..a580651cb975 100644 --- a/code/modules/lights/floorlamp.dm +++ b/code/modules/lights/light_fixture_floorlamp.dm @@ -58,7 +58,7 @@ b_range = 6 b_power = 1 lighting_modes = list( - LIGHTMODE_EMERGENCY = list(l_range = 6, l_power = 0.45, l_color = LIGHT_COLOR_EMERGENCY), + MODE_EMERGENCY = list(l_range = 6, l_power = 0.45, l_color = LIGHT_COLOR_EMERGENCY), ) /obj/item/light/bulb/large/Initialize(mapload, obj/machinery/light/fixture) @@ -77,21 +77,3 @@ material = /decl/material/solid/organic/cloth obj_flags = OBJ_FLAG_HOLLOW w_class = ITEM_SIZE_NORMAL - -// Subtype used for creation via crafting. -/obj/machinery/light/flamp/noshade/deconstruct - light_type = null - panel_open = TRUE - construct_state = /decl/machine_construction/wall_frame/no_wires/simple - -/obj/item/machine_chassis/flamp - name = "lamp fixture frame" - desc = "A bare frame for a standing lamp fixture. Must be secured to the floor with a wrench." - icon = 'icons/obj/floorlamp.dmi' - icon_state = "flamp-construct-item" - w_class = ITEM_SIZE_STRUCTURE - material = /decl/material/solid/metal/steel - build_type = /obj/machinery/light/flamp/noshade/deconstruct - -/datum/fabricator_recipe/engineering/floorlamp - path = /obj/item/machine_chassis/flamp \ No newline at end of file diff --git a/code/modules/lights/light_fixture_navigation.dm b/code/modules/lights/light_fixture_navigation.dm new file mode 100644 index 000000000000..1e0046ef3fbb --- /dev/null +++ b/code/modules/lights/light_fixture_navigation.dm @@ -0,0 +1,36 @@ +/obj/machinery/light/navigation + name = "navigation light" + desc = "A periodically flashing light." + icon = 'icons/obj/lighting_nav.dmi' + icon_state = "nav10" + base_state = "nav1" + light_type = /obj/item/light/tube/large + accepts_light_type = /obj/item/light/tube/large + on = TRUE + var/delay = 1 + base_type = /obj/machinery/light/navigation + frame_type = /obj/item/frame/light/nav + stat_immune = NOPOWER | NOINPUT | NOSCREEN + +/obj/machinery/light/navigation/on_update_icon() + . = ..() // this will handle pixel offsets + icon_state = "nav[delay][!!(lightbulb && on)]" + +/obj/machinery/light/navigation/attackby(obj/item/used_item, mob/user) + . = ..() + if(!. && IS_MULTITOOL(used_item)) + delay = 5 + ((delay + 1) % 5) + to_chat(user, SPAN_NOTICE("You adjust the delay on \the [src].")) + return TRUE + +/obj/machinery/light/navigation/delay2 + delay = 2 + +/obj/machinery/light/navigation/delay3 + delay = 3 + +/obj/machinery/light/navigation/delay4 + delay = 4 + +/obj/machinery/light/navigation/delay5 + delay = 5 \ No newline at end of file diff --git a/code/modules/lights/light_fixture_small.dm b/code/modules/lights/light_fixture_small.dm new file mode 100644 index 000000000000..c3b745ee6602 --- /dev/null +++ b/code/modules/lights/light_fixture_small.dm @@ -0,0 +1,26 @@ +// the smaller bulb light fixture +/obj/machinery/light/small + icon_state = "bulb_map" + base_state = "bulb" + desc = "A small lighting fixture." + light_type = /obj/item/light/bulb + accepts_light_type = /obj/item/light/bulb + base_type = /obj/machinery/light/small + frame_type = /obj/item/frame/light/small + +/obj/machinery/light/small/emergency + light_type = /obj/item/light/bulb/red + +/obj/machinery/light/small/red + light_type = /obj/item/light/bulb/red + +/obj/machinery/light/small/readylight + light_type = /obj/item/light/bulb/red/readylight + var/state = 0 + +/obj/machinery/light/small/readylight/proc/set_state(var/new_state) + state = new_state + if(state) + set_mode(lightbulb::MODE_READY) + else + set_mode(null) diff --git a/code/modules/lights/light_fixture_spot.dm b/code/modules/lights/light_fixture_spot.dm new file mode 100644 index 000000000000..380a9d5a1563 --- /dev/null +++ b/code/modules/lights/light_fixture_spot.dm @@ -0,0 +1,7 @@ +/obj/machinery/light/spot + name = "spotlight" + desc = "A more robust socket for light tubes that demand more power." + light_type = /obj/item/light/tube/large + accepts_light_type = /obj/item/light/tube/large + base_type = /obj/machinery/light/spot + frame_type = /obj/item/frame/light/spot \ No newline at end of file diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/modules/lights/light_replacer.dm similarity index 89% rename from code/game/objects/items/devices/lightreplacer.dm rename to code/modules/lights/light_replacer.dm index df209e9cc0d5..0292a67ce847 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/modules/lights/light_replacer.dm @@ -32,12 +32,6 @@ // // The explosion cannot insta-kill anyone with 30% or more health. -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - - /obj/item/lightreplacer name = "light replacer" desc = "A lightweight automated device, capable of interfacing with and rapidly replacing standard light installations." @@ -95,12 +89,12 @@ if(istype(used_item, /obj/item/stack/material) && used_item.get_material_type() == /decl/material/solid/glass) var/obj/item/stack/G = used_item if(uses >= max_uses) - to_chat(user, "\The [src] is full.") + to_chat(user, SPAN_WARNING("\The [src] is full.")) else if(G.use(1)) AddUses(16) //Autolathe converts 1 sheet into 16 lights. // TODO: Make this use matter instead - to_chat(user, "You insert a piece of glass into \the [src]. You have [uses] light\s remaining.") + to_chat(user, SPAN_NOTICE("You insert a piece of glass into \the [src]. You have [uses] light\s remaining.")) else - to_chat(user, "You need one sheet of glass to replace lights.") + to_chat(user, SPAN_WARNING("You need one sheet of glass to replace lights.")) return TRUE if(istype(used_item, /obj/item/light)) @@ -145,12 +139,12 @@ /obj/item/lightreplacer/proc/ReplaceLight(var/obj/machinery/light/target, var/mob/living/U) - if(target.get_status() == LIGHT_OK) + if(target.get_bulb_status() == /obj/item/light::STATUS_OK) to_chat(U, "There is a working [target.get_fitting_name()] already inserted.") else if(!CanUse(U)) to_chat(U, "\The [src]'s refill light blinks red.") else if(Use(U)) - to_chat(U, "You replace the [target.get_fitting_name()] with \the [src].") + to_chat(U, SPAN_NOTICE("You replace the [target.get_fitting_name()] with \the [src].")) if(target.lightbulb) target.remove_bulb() @@ -171,12 +165,4 @@ /obj/item/lightreplacer/proc/CanUse(var/mob/living/user) src.add_fingerprint(user) //Not sure what else to check for. Maybe if clumsy? - if(uses > 0) - return 1 - else - return 0 - -#undef LIGHT_OK -#undef LIGHT_EMPTY -#undef LIGHT_BROKEN -#undef LIGHT_BURNED + return uses > 0 diff --git a/code/game/machinery/lightswitch.dm b/code/modules/lights/light_switch.dm similarity index 100% rename from code/game/machinery/lightswitch.dm rename to code/modules/lights/light_switch.dm diff --git a/code/game/objects/items/buttons.dm b/code/modules/lights/light_switch_frames.dm similarity index 97% rename from code/game/objects/items/buttons.dm rename to code/modules/lights/light_switch_frames.dm index 14d4978bf7e5..a415018a29c8 100644 --- a/code/game/objects/items/buttons.dm +++ b/code/modules/lights/light_switch_frames.dm @@ -1,5 +1,3 @@ -// Lightswitch Hull - /obj/item/frame/button/light_switch name = "light switch frame" desc = "Used for building a light switch." diff --git a/nebula.dme b/nebula.dme index e22351f46394..393a9424259b 100644 --- a/nebula.dme +++ b/nebula.dme @@ -841,7 +841,6 @@ #include "code\game\machinery\holosign.dm" #include "code\game\machinery\igniter.dm" #include "code\game\machinery\jukebox.dm" -#include "code\game\machinery\lightswitch.dm" #include "code\game\machinery\mass_driver.dm" #include "code\game\machinery\mech_recharger.dm" #include "code\game\machinery\message_server.dm" @@ -1072,7 +1071,6 @@ #include "code\game\objects\items\blackout.dm" #include "code\game\objects\items\blueprints.dm" #include "code\game\objects\items\bodybag.dm" -#include "code\game\objects\items\buttons.dm" #include "code\game\objects\items\candelabra.dm" #include "code\game\objects\items\chisel.dm" #include "code\game\objects\items\christmas.dm" @@ -1184,7 +1182,6 @@ #include "code\game\objects\items\devices\hailer.dm" #include "code\game\objects\items\devices\holowarrant.dm" #include "code\game\objects\items\devices\inducer.dm" -#include "code\game\objects\items\devices\lightreplacer.dm" #include "code\game\objects\items\devices\megaphone.dm" #include "code\game\objects\items\devices\modkit.dm" #include "code\game\objects\items\devices\multitool.dm" @@ -2633,10 +2630,17 @@ #include "code\modules\lighting\lighting_source.dm" #include "code\modules\lighting\lighting_turf.dm" #include "code\modules\lighting\~lighting_undefs.dm" -#include "code\modules\lights\_light_defines.dm" -#include "code\modules\lights\floorlamp.dm" -#include "code\modules\lights\light_fixture.dm" -#include "code\modules\lights\light_items.dm" +#include "code\modules\lights\light_bulbs_tubes.dm" +#include "code\modules\lights\light_fabrication.dm" +#include "code\modules\lights\light_fixture_base.dm" +#include "code\modules\lights\light_fixture_construction.dm" +#include "code\modules\lights\light_fixture_floorlamp.dm" +#include "code\modules\lights\light_fixture_navigation.dm" +#include "code\modules\lights\light_fixture_small.dm" +#include "code\modules\lights\light_fixture_spot.dm" +#include "code\modules\lights\light_replacer.dm" +#include "code\modules\lights\light_switch.dm" +#include "code\modules\lights\light_switch_frames.dm" #include "code\modules\locks\key.dm" #include "code\modules\locks\keyring.dm" #include "code\modules\locks\lock.dm" From a86982bc2687df13443ad4880e354cee90bfe302 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 17 Oct 2025 19:51:53 -0400 Subject: [PATCH 0836/1331] Eliminate useless flufftext 'module' --- code/modules/flufftext/TextFilters.dm | 125 -------------------------- code/modules/mob/mob_helpers.dm | 124 +++++++++++++++++++++++++ nebula.dme | 1 - 3 files changed, 124 insertions(+), 126 deletions(-) delete mode 100644 code/modules/flufftext/TextFilters.dm diff --git a/code/modules/flufftext/TextFilters.dm b/code/modules/flufftext/TextFilters.dm deleted file mode 100644 index e1b85dc1b3f7..000000000000 --- a/code/modules/flufftext/TextFilters.dm +++ /dev/null @@ -1,125 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/proc/NewStutter(phrase,stunned) - phrase = html_decode(phrase) - - var/list/split_phrase = splittext(phrase," ") //Split it up into words. - - var/list/unstuttered_words = split_phrase.Copy() - var/i = rand(1,3) - if(stunned) i = split_phrase.len - for(,i > 0,i--) //Pick a few words to stutter on. - - if (!unstuttered_words.len) - break - var/word = pick(unstuttered_words) - unstuttered_words -= word //Remove from unstuttered words so we don't stutter it again. - var/index = split_phrase.Find(word) //Find the word in the split phrase so we can replace it. - - //Search for dipthongs (two letters that make one sound.) - var/first_sound = copytext_char(word,1,3) - var/first_letter = copytext_char(word,1,2) - if(lowertext(first_sound) in list("ch","th","sh")) - first_letter = first_sound - - //Repeat the first letter to create a stutter. - var/rnum = rand(1,3) - switch(rnum) - if(1) - word = "[first_letter]-[word]" - if(2) - word = "[first_letter]-[first_letter]-[word]" - if(3) - word = "[first_letter]-[word]" - - split_phrase[index] = word - - return sanitize(jointext(split_phrase," ")) - -/* -RadioChat Filter. -args: -message - returns a distorted version of this -distortion_chance - the chance of a filter being applied to each character. -distortion_speed - multiplier for the chance increase. -distortion - starting distortion. -english_only - whether to use traditional english letters only (for use in NanoUI) -*/ -/proc/RadioChat(mob/living/user, message, distortion_chance = 60, distortion_speed = 1, distortion = 1, english_only = 0) - var/decl/language/language = user?.get_default_language() - message = html_decode(message) - var/new_message = "" - var/input_size = length(message) - var/cursor_position = 0 - if(input_size < 20) // Short messages get distorted too. Bit hacksy. - distortion += (20-input_size)/2 - while(cursor_position <= input_size) - var/newletter=copytext_char(message, cursor_position, cursor_position+1) - if(!prob(distortion_chance)) - new_message += newletter - cursor_position += 1 - continue - if(newletter != " ") - if(prob(0.08 * distortion)) // Major cutout - newletter = "*zzzt*" - cursor_position += rand(1, (length(message) - cursor_position)) // Skip some characters - distortion += 1 * distortion_speed - else if(prob(0.8 * distortion)) // Minor cut out - if(prob(25)) - newletter = ".." - else if(prob(25)) - newletter = " " - else - newletter = "" - distortion += 0.25 * distortion_speed - else if(prob(2 * distortion)) // Mishearing - if(language && language.syllables && prob(50)) - newletter = pick(language.syllables) - else - newletter = pick("a","e","i","o","u") - distortion += 0.25 * distortion_speed - else if(prob(1.5 * distortion)) // Mishearing - if(language && prob(50)) - if(language.syllables) - newletter = pick (language.syllables) - else - newletter = "*" - else - if(english_only) - newletter += "*" - else - newletter = pick("ø", "Ð", "%", "æ", "µ") - distortion += 0.5 * distortion_speed - else if(prob(0.75 * distortion)) // Incomprehensible - newletter = pick("<", ">", "!", "$", "%", "^", "&", "*", "~", "#") - distortion += 0.75 * distortion_speed - else if(prob(0.05 * distortion)) // Total cut out - if(!english_only) - newletter = "¦w¡¼b»%> -BZZT-" - else - newletter = "srgt%$hjc< -BZZT-" - new_message += newletter - break - else if(prob(2.5 * distortion)) // Sound distortion. Still recognisable, mostly. - switch(lowertext(newletter)) - if("s") - newletter = "$" - if("e") - newletter = "£" - if("w") - newletter = "ø" - if("y") - newletter = "¡" - if("x") - newletter = "æ" - if("u") - newletter = "µ" - else - if(prob(0.2 * distortion)) - newletter = " *crackle* " - distortion += 0.25 * distortion_speed - if(prob(20)) - capitalize(newletter) - new_message += newletter - cursor_position += 1 - return new_message diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 3a0b89f448b1..7a546009a86c 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -215,6 +215,130 @@ var/global/list/global/organ_rel_size = list( return returntext +/proc/NewStutter(phrase,stunned) + phrase = html_decode(phrase) + + var/list/split_phrase = splittext(phrase," ") //Split it up into words. + + var/list/unstuttered_words = split_phrase.Copy() + var/i = rand(1,3) + if(stunned) i = split_phrase.len + for(,i > 0,i--) //Pick a few words to stutter on. + + if (!unstuttered_words.len) + break + var/word = pick(unstuttered_words) + unstuttered_words -= word //Remove from unstuttered words so we don't stutter it again. + var/index = split_phrase.Find(word) //Find the word in the split phrase so we can replace it. + + //Search for dipthongs (two letters that make one sound.) + var/first_sound = copytext_char(word,1,3) + var/first_letter = copytext_char(word,1,2) + if(lowertext(first_sound) in list("ch","th","sh")) + first_letter = first_sound + + //Repeat the first letter to create a stutter. + var/rnum = rand(1,3) + switch(rnum) + if(1) + word = "[first_letter]-[word]" + if(2) + word = "[first_letter]-[first_letter]-[word]" + if(3) + word = "[first_letter]-[word]" + + split_phrase[index] = word + + return sanitize(jointext(split_phrase," ")) + +/* +RadioChat Filter. +args: +message - returns a distorted version of this +distortion_chance - the chance of a filter being applied to each character. +distortion_speed - multiplier for the chance increase. +distortion - starting distortion. +english_only - whether to use traditional english letters only (for use in NanoUI) +*/ +/proc/RadioChat(mob/living/user, message, distortion_chance = 60, distortion_speed = 1, distortion = 1, english_only = 0) + var/decl/language/language = user?.get_default_language() + message = html_decode(message) + var/new_message = "" + var/input_size = length(message) + var/cursor_position = 0 + if(input_size < 20) // Short messages get distorted too. Bit hacksy. + distortion += (20-input_size)/2 + while(cursor_position <= input_size) + var/newletter=copytext_char(message, cursor_position, cursor_position+1) + if(!prob(distortion_chance)) + new_message += newletter + cursor_position += 1 + continue + if(newletter != " ") + if(prob(0.08 * distortion)) // Major cutout + newletter = "*zzzt*" + cursor_position += rand(1, (length(message) - cursor_position)) // Skip some characters + distortion += 1 * distortion_speed + else if(prob(0.8 * distortion)) // Minor cut out + if(prob(25)) + newletter = ".." + else if(prob(25)) + newletter = " " + else + newletter = "" + distortion += 0.25 * distortion_speed + else if(prob(2 * distortion)) // Mishearing + if(language && language.syllables && prob(50)) + newletter = pick(language.syllables) + else + newletter = pick("a","e","i","o","u") + distortion += 0.25 * distortion_speed + else if(prob(1.5 * distortion)) // Mishearing + if(language && prob(50)) + if(language.syllables) + newletter = pick (language.syllables) + else + newletter = "*" + else + if(english_only) + newletter += "*" + else + newletter = pick("ø", "Ð", "%", "æ", "µ") + distortion += 0.5 * distortion_speed + else if(prob(0.75 * distortion)) // Incomprehensible + newletter = pick("<", ">", "!", "$", "%", "^", "&", "*", "~", "#") + distortion += 0.75 * distortion_speed + else if(prob(0.05 * distortion)) // Total cut out + if(!english_only) + newletter = "¦w¡¼b»%> -BZZT-" + else + newletter = "srgt%$hjc< -BZZT-" + new_message += newletter + break + else if(prob(2.5 * distortion)) // Sound distortion. Still recognisable, mostly. + switch(lowertext(newletter)) + if("s") + newletter = "$" + if("e") + newletter = "£" + if("w") + newletter = "ø" + if("y") + newletter = "¡" + if("x") + newletter = "æ" + if("u") + newletter = "µ" + else + if(prob(0.2 * distortion)) + newletter = " *crackle* " + distortion += 0.25 * distortion_speed + if(prob(20)) + capitalize(newletter) + new_message += newletter + cursor_position += 1 + return new_message + #define TICKS_PER_RECOIL_ANIM 2 #define PIXELS_PER_STRENGTH_VAL 16 diff --git a/nebula.dme b/nebula.dme index 9589372d005c..3ae62f5f686c 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2417,7 +2417,6 @@ #include "code\modules\fishing\bait.dm" #include "code\modules\fishing\fishing_line.dm" #include "code\modules\fishing\fishing_rod.dm" -#include "code\modules\flufftext\TextFilters.dm" #include "code\modules\fluids\_fluid.dm" #include "code\modules\fluids\fluid_flood.dm" #include "code\modules\fluids\fluid_mapped.dm" From c051af3071fe57e90b6c52c51c755743e90001ef Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 23 Jan 2025 17:37:36 +1100 Subject: [PATCH 0837/1331] Added barrel rims and basic coopery for crafted barrels. --- .../game/objects/structures/barrels/barrel.dm | 5 +- code/game/objects/structures/barrels/cask.dm | 3 + .../crafting/stack_recipes/recipes_planks.dm | 4 +- icons/obj/items/barrel_rim.dmi | Bin 0 -> 328 bytes mods/content/blacksmithy/_blacksmithy.dm | 1 - mods/content/blacksmithy/_blacksmithy.dme | 1 + mods/content/blacksmithy/barrel_rim.dm | 52 ++++++++++++++++++ .../blacksmithy/forging_step_blades.dm | 18 +++--- .../blacksmithy/forging_step_ornate.dm | 6 +- .../content/blacksmithy/forging_step_tools.dm | 4 ++ .../patches/fantasy/forging_fantasy.dm | 4 ++ 11 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 icons/obj/items/barrel_rim.dmi create mode 100644 mods/content/blacksmithy/barrel_rim.dm diff --git a/code/game/objects/structures/barrels/barrel.dm b/code/game/objects/structures/barrels/barrel.dm index 73424c453224..0e987ec6aa2b 100644 --- a/code/game/objects/structures/barrels/barrel.dm +++ b/code/game/objects/structures/barrels/barrel.dm @@ -18,9 +18,12 @@ tool_interaction_flags = TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT // Should we draw our lid and liquid contents as overlays? var/show_liquid_contents = TRUE - // Rivets, bands, etc. Currently just cosmetic. + // Rivets, bands, etc. Currently just cosmetic unless the forging modpack is ticked. var/decl/material/metal_material = /decl/material/solid/metal/iron +// Stub type for crafting (forging modpack modifies metal_material) +/obj/structure/reagent_dispensers/barrel/crafted + // Overrides due to wonky reagent_dispeners opencontainer flag handling. /obj/structure/reagent_dispensers/barrel/can_be_poured_from(mob/user, atom/target) return (reagents?.maximum_volume > 0) diff --git a/code/game/objects/structures/barrels/cask.dm b/code/game/objects/structures/barrels/cask.dm index d3755dfe55e6..603cf2d64335 100644 --- a/code/game/objects/structures/barrels/cask.dm +++ b/code/game/objects/structures/barrels/cask.dm @@ -6,6 +6,9 @@ show_liquid_contents = FALSE storage = null // Intended for storing liquids. +// Stub type for crafting (forging modpack modifies metal_material) +/obj/structure/reagent_dispensers/barrel/cask/crafted + // Horrible workaround for physical interaction checks. /obj/structure/reagent_dispensers/barrel/cask/nano_host() return istype(loc, /obj/structure/cask_rack) ? loc : src diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index 03464a086a4e..feac5e54b587 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -218,11 +218,11 @@ difficulty = MAT_VALUE_HARD_DIY /decl/stack_recipe/planks/furniture/barrel - result_type = /obj/structure/reagent_dispensers/barrel + result_type = /obj/structure/reagent_dispensers/barrel/crafted difficulty = MAT_VALUE_HARD_DIY /decl/stack_recipe/planks/furniture/barrel/cask - result_type = /obj/structure/reagent_dispensers/barrel/cask + result_type = /obj/structure/reagent_dispensers/barrel/cask/crafted /decl/stack_recipe/planks/furniture/barrel/cask_rack result_type = /obj/structure/cask_rack diff --git a/icons/obj/items/barrel_rim.dmi b/icons/obj/items/barrel_rim.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a5567e33c7c57c7276ebe585e14d8e63c61f693d GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*8>L*96EUN|NsAFfg z7<$()=}&DsnKU zZE!G{sOEtFk_F<8}6F_c+_@mYXsXo@Tk&$)#M`FK_+W&B}eXPAA6 V>qywtdS{^344$rjF6*2UngD^Eb1nb? literal 0 HcmV?d00001 diff --git a/mods/content/blacksmithy/_blacksmithy.dm b/mods/content/blacksmithy/_blacksmithy.dm index 978dbebe0d6e..0ec35cdf1ac6 100644 --- a/mods/content/blacksmithy/_blacksmithy.dm +++ b/mods/content/blacksmithy/_blacksmithy.dm @@ -1,3 +1,2 @@ /decl/modpack/blacksmithy name = "Blacksmithy" - diff --git a/mods/content/blacksmithy/_blacksmithy.dme b/mods/content/blacksmithy/_blacksmithy.dme index 3421d65d10fe..806ddef8d9a9 100644 --- a/mods/content/blacksmithy/_blacksmithy.dme +++ b/mods/content/blacksmithy/_blacksmithy.dme @@ -3,6 +3,7 @@ // BEGIN_INCLUDE #include "_blacksmithy.dm" #include "anvil.dm" +#include "barrel_rim.dm" #include "billet.dm" #include "boulder.dm" #include "forge_fire.dm" diff --git a/mods/content/blacksmithy/barrel_rim.dm b/mods/content/blacksmithy/barrel_rim.dm new file mode 100644 index 000000000000..4dca740435dc --- /dev/null +++ b/mods/content/blacksmithy/barrel_rim.dm @@ -0,0 +1,52 @@ +/obj/item/barrel_rim + name = "barrel rim" + desc = "A circular brace used to hold a barrel together." + icon_state = ICON_STATE_WORLD + icon = 'icons/obj/items/barrel_rim.dmi' + material = /decl/material/solid/metal/iron + material_alteration = MAT_FLAG_ALTERATION_ALL + +/decl/stack_recipe/rods/stick/barrel_rim + result_type = /obj/item/barrel_rim + one_per_turf = FALSE + on_floor = FALSE + category = "items" + +/obj/structure/reagent_dispensers/barrel + // Skill used for coopery. + var/work_skill = SKILL_CONSTRUCTION + +// Overrides to make barrel rims useful. +/obj/structure/reagent_dispensers/barrel/crafted + metal_material = null +/obj/structure/reagent_dispensers/barrel/cask/crafted + metal_material = null + +/obj/structure/reagent_dispensers/barrel/create_dismantled_products(turf/target) + if(metal_material) + new /obj/item/barrel_rim(target, metal_material) + metal_material = null + return ..() + +// Barrels with no reinforcement break apart when things are put inside. +/obj/structure/reagent_dispensers/barrel/Entered(atom/movable/atom, atom/old_loc) + . = ..() + if(!metal_material) + physically_destroyed() + +/obj/structure/reagent_dispensers/barrel/on_reagent_change() + . = ..() + if(!metal_material && reagents?.total_volume) + physically_destroyed() + +// Adding a rim to a crafted barrel. +/obj/structure/reagent_dispensers/barrel/attackby(obj/item/used_item, mob/user) + if(isnull(metal_material) && istype(used_item.material) && istype(used_item, /obj/item/barrel_rim)) + user.visible_message(SPAN_NOTICE("\The [user] begins securing \the [src] with \the [used_item].")) + if(user.do_skilled(5 SECONDS, work_skill, src, check_holding = TRUE) && user.try_unequip(used_item)) + metal_material = used_item.material + update_icon() + user.visible_message(SPAN_NOTICE("\The [user] secures \the [src] with \the [used_item].")) + qdel(used_item) + return TRUE + . = ..() diff --git a/mods/content/blacksmithy/forging_step_blades.dm b/mods/content/blacksmithy/forging_step_blades.dm index 253a1dd73321..8e9524336b17 100644 --- a/mods/content/blacksmithy/forging_step_blades.dm +++ b/mods/content/blacksmithy/forging_step_blades.dm @@ -3,8 +3,8 @@ billet_icon_state = "blade" billet_desc = "A roughly shaped, dull blade. It will need further refinement before it can be finished." steps = list( - /decl/forging_step/long_blade_blank, - /decl/forging_step/short_sword_blank + /decl/forging_step/long_blade_blank, + /decl/forging_step/short_sword_blank ) /decl/forging_step/long_blade_blank @@ -13,9 +13,9 @@ billet_desc = "A long, dull and unrefined blade, only a step from being a finished product." steps = list( - /decl/forging_step/product/longsword_blade, - /decl/forging_step/product/broadsword_blade, - /decl/forging_step/product/rapier_blade + /decl/forging_step/product/longsword_blade, + /decl/forging_step/product/broadsword_blade, + /decl/forging_step/product/rapier_blade ) /decl/forging_step/short_sword_blank @@ -23,10 +23,10 @@ billet_name_prefix = "short" billet_desc = "A short, dull and unrefined blade, only a step from being a finished product." steps = list( - /decl/forging_step/product/poignard_blade, - /decl/forging_step/product/knife_blade, - /decl/forging_step/product/shortsword_blade, - /decl/forging_step/product/spear_head + /decl/forging_step/product/poignard_blade, + /decl/forging_step/product/knife_blade, + /decl/forging_step/product/shortsword_blade, + /decl/forging_step/product/spear_head ) // TODO: make these blades, add weapon crafting. diff --git a/mods/content/blacksmithy/forging_step_ornate.dm b/mods/content/blacksmithy/forging_step_ornate.dm index 515d9d57d21f..3070d060a9be 100644 --- a/mods/content/blacksmithy/forging_step_ornate.dm +++ b/mods/content/blacksmithy/forging_step_ornate.dm @@ -4,9 +4,9 @@ billet_icon_state = "ornate" billet_desc = "An ornate piece of worked metal. It still needs some last touches to be made into something useful." steps = list( - /decl/forging_step/product/candelabra, - /decl/forging_step/product/decanter, - /decl/forging_step/product/goblet + /decl/forging_step/product/candelabra, + /decl/forging_step/product/decanter, + /decl/forging_step/product/goblet ) /decl/forging_step/product/candelabra diff --git a/mods/content/blacksmithy/forging_step_tools.dm b/mods/content/blacksmithy/forging_step_tools.dm index 4f7a4e24567a..4911ec5cedf3 100644 --- a/mods/content/blacksmithy/forging_step_tools.dm +++ b/mods/content/blacksmithy/forging_step_tools.dm @@ -58,3 +58,7 @@ /decl/forging_step/product/nails billet_name = "nails" product_type = /obj/item/stack/material/nail/twelve + +/decl/forging_step/product/barrel_rim + billet_name = "barrel rim" + product_type = /obj/item/barrel_rim diff --git a/mods/~compatibility/patches/fantasy/forging_fantasy.dm b/mods/~compatibility/patches/fantasy/forging_fantasy.dm index 6de02a927d29..b1817d411395 100644 --- a/mods/~compatibility/patches/fantasy/forging_fantasy.dm +++ b/mods/~compatibility/patches/fantasy/forging_fantasy.dm @@ -1,2 +1,6 @@ /decl/forging_step work_skill = SKILL_METALWORK + +// Changes from SKILL_CONSTRUCTION for coopery. +/obj/structure/reagent_dispensers/barrel + work_skill = SKILL_ARTIFICE From f513e6a6201fbfc8318c1016a5099c5af0af3279 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 19 Oct 2025 22:07:14 +1100 Subject: [PATCH 0838/1331] Backporting zombies/skellymans from Pyrelight. --- code/modules/organs/external/_external.dm | 6 +- maps/modpack_testing/modpack_testing.dm | 1 + mods/content/undead/_undead.dm | 2 + mods/content/undead/_undead.dme | 12 ++ mods/content/undead/mods/undead.dm | 10 ++ mods/content/undead/mods/undead_skeleton.dm | 43 ++++++ mods/content/undead/mods/undead_zombie.dm | 69 +++++++++ mods/content/undead/overrides.dm | 134 ++++++++++++++++++ mods/content/undead/skillset.dm | 2 + mods/content/undead/traits.dm | 4 + mods/~compatibility/patches/drakes.dm | 9 ++ .../fantasy_drakes.dm} | 0 .../patches/drakes/undead_drakes.dm | 9 ++ mods/~compatibility/patches/fantasy.dm | 9 +- .../patches/fantasy/undead_fantasy.dm | 53 +++++++ mods/~compatibility/~compatibility.dm | 6 +- 16 files changed, 362 insertions(+), 7 deletions(-) create mode 100644 mods/content/undead/_undead.dm create mode 100644 mods/content/undead/_undead.dme create mode 100644 mods/content/undead/mods/undead.dm create mode 100644 mods/content/undead/mods/undead_skeleton.dm create mode 100644 mods/content/undead/mods/undead_zombie.dm create mode 100644 mods/content/undead/overrides.dm create mode 100644 mods/content/undead/skillset.dm create mode 100644 mods/content/undead/traits.dm create mode 100644 mods/~compatibility/patches/drakes.dm rename mods/~compatibility/patches/{fantasy/drake_fantasy.dm => drakes/fantasy_drakes.dm} (100%) create mode 100644 mods/~compatibility/patches/drakes/undead_drakes.dm create mode 100644 mods/~compatibility/patches/fantasy/undead_fantasy.dm diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index 4cafbfe2e108..39c8df5d9a62 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -725,13 +725,17 @@ This function completely restores a damaged organ to perfect condition. /obj/item/organ/external/is_broken() return ((status & ORGAN_CUT_AWAY) || ((status & ORGAN_BROKEN) && !splinted)) +// Overridable for modpacks. +/obj/item/organ/external/proc/check_status_flags_for_process() + return (status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_MUTATED|ORGAN_DISLOCATED|ORGAN_DEAD)) + //Determines if we even need to process this organ. /obj/item/organ/external/proc/need_process() if(length(ailments)) return TRUE - if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_MUTATED|ORGAN_DISLOCATED|ORGAN_DEAD)) + if(check_status_flags_for_process()) return TRUE if((brute_dam || burn_dam) && !BP_IS_PROSTHETIC(src)) //Robot limbs don't autoheal and thus don't need to process when damaged diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 52d46b1e1396..2ad67edc3e01 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -29,6 +29,7 @@ #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/tabloids/_tabloids.dme" + #include "../../mods/content/undead/_undead.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/gamemodes/cult/_cult.dme" diff --git a/mods/content/undead/_undead.dm b/mods/content/undead/_undead.dm new file mode 100644 index 000000000000..37cb87794289 --- /dev/null +++ b/mods/content/undead/_undead.dm @@ -0,0 +1,2 @@ +/decl/modpack/undead + name = "Undead Content" \ No newline at end of file diff --git a/mods/content/undead/_undead.dme b/mods/content/undead/_undead.dme new file mode 100644 index 000000000000..2068815dbd2f --- /dev/null +++ b/mods/content/undead/_undead.dme @@ -0,0 +1,12 @@ +#ifndef MODPACK_UNDEAD +#define MODPACK_UNDEAD +// BEGIN_INCLUDE +#include "_undead.dm" +#include "overrides.dm" +#include "skillset.dm" +#include "traits.dm" +#include "mods\undead.dm" +#include "mods\undead_skeleton.dm" +#include "mods\undead_zombie.dm" +// END_INCLUDE +#endif diff --git a/mods/content/undead/mods/undead.dm b/mods/content/undead/mods/undead.dm new file mode 100644 index 000000000000..a42625998f73 --- /dev/null +++ b/mods/content/undead/mods/undead.dm @@ -0,0 +1,10 @@ +/mob/living/human/get_movement_delay(var/travel_dir) + . = ..() + if(has_trait(/decl/trait/undead)) + set_moving_slowly() + if(istype(default_walk_intent)) + . = max(., default_walk_intent.move_delay) // no runner zombies yet + +// Overridden by fantasy modpack. +/mob/living/human/proc/grant_basic_undead_equipment() + return diff --git a/mods/content/undead/mods/undead_skeleton.dm b/mods/content/undead/mods/undead_skeleton.dm new file mode 100644 index 000000000000..bc39f27a4e52 --- /dev/null +++ b/mods/content/undead/mods/undead_skeleton.dm @@ -0,0 +1,43 @@ +/datum/mob_controller/aggressive/skeleton + +// SKELETONS +// Immune to blind or deaf, but weak to physical damage. +/mob/living/human/proc/make_skeleton() + set_trait(/decl/trait/metabolically_inert, TRAIT_LEVEL_EXISTS) + set_trait(/decl/trait/undead, TRAIT_LEVEL_MODERATE) + + if(istype(ai)) + QDEL_NULL(ai) + ai = new /datum/mob_controller/aggressive/skeleton(src) + faction = "undead" + + if(!istype(skillset, /datum/skillset/undead) && !ispath(skillset, /datum/skillset/undead)) + if(istype(skillset)) + QDEL_NULL(skillset) + skillset = new /datum/skillset/undead(src) + + for(var/obj/item/organ/external/limb in get_external_organs()) + if(!BP_IS_PROSTHETIC(limb)) + limb.skeletonize() + + for(var/obj/item/organ/internal/organ in get_internal_organs()) + remove_organ(organ, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE) + qdel(organ) + + set_max_health(round(species.total_health / 3)) + vessel?.clear_reagents() + SET_HAIR_STYLE(src, /decl/sprite_accessory/hair/bald, FALSE) + update_body() + +/mob/living/human/skeleton + skillset = /datum/skillset/undead + +/mob/living/human/skeleton/post_setup(species_uid, datum/mob_snapshot/supplied_appearance) + . = ..() + make_skeleton() + grant_basic_undead_equipment() + +/mob/living/human/skeleton/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/human::uid + . = ..() diff --git a/mods/content/undead/mods/undead_zombie.dm b/mods/content/undead/mods/undead_zombie.dm new file mode 100644 index 000000000000..8d98b7919d0b --- /dev/null +++ b/mods/content/undead/mods/undead_zombie.dm @@ -0,0 +1,69 @@ +// ZOMBIES +// Dead and rotting, but still mobile and aggressive. + +/datum/mob_controller/aggressive/zombie + +/mob/living/human/proc/make_zombie() + set_trait(/decl/trait/metabolically_inert, TRAIT_LEVEL_EXISTS) + set_trait(/decl/trait/undead, TRAIT_LEVEL_MINOR) + + if(istype(ai)) + QDEL_NULL(ai) + ai = new /datum/mob_controller/aggressive/zombie(src) + faction = "undead" + + if(!istype(skillset, /datum/skillset/undead) && !ispath(skillset, /datum/skillset/undead)) + if(istype(skillset)) + QDEL_NULL(skillset) + skillset = new /datum/skillset/undead(src) + + for(var/obj/item/organ/organ in get_organs()) + organ.die() + organ.germ_level = INFECTION_LEVEL_THREE + + // die() sets to max damage + for(var/obj/item/organ/internal/organ in get_internal_organs()) + organ.set_organ_damage(0) + for(var/obj/item/organ/external/limb in get_internal_organs()) + limb.brute_dam = 0 + limb.burn_dam = 0 + + // Set this so nonhumans look appropriately gross. + reset_hair() + if(get_bodytype()?.appearance_flags & HAS_SKIN_COLOR) + _skin_colour = pick(COLOR_GRAY, COLOR_GRAY15, COLOR_GRAY20, COLOR_GRAY40, COLOR_GRAY80, COLOR_WHITE) + SET_HAIR_COLOR(src, _skin_colour, TRUE) + SET_FACIAL_HAIR_COLOR(src, _skin_colour, TRUE) + + var/obj/item/organ/external/head/head = get_organ(BP_HEAD) + if(istype(head)) + head.glowing_eyes = TRUE + set_eye_colour(COLOR_RED) + + for(var/obj/item/organ/external/limb in get_external_organs()) + if(!BP_IS_PROSTHETIC(limb)) + limb.sync_colour_to_human(src) + if(prob(10)) + limb.skeletonize() + else if(prob(15)) + if(prob(75)) + limb.createwound(CUT, rand(limb.max_damage * 0.25, limb.max_damage * 0.5)) + else + limb.createwound(BURN, rand(limb.max_damage * 0.25, limb.max_damage * 0.5)) + + set_max_health(round(species.total_health / 2)) + vessel.remove_any(vessel.total_volume * rand(0.2, 0.5)) + update_body() + +/mob/living/human/zombie + skillset = /datum/skillset/undead + +/mob/living/human/zombie/post_setup(species_uid, datum/mob_snapshot/supplied_appearance) + . = ..() + make_zombie() + grant_basic_undead_equipment() + +/mob/living/human/zombie/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/human::uid + . = ..() diff --git a/mods/content/undead/overrides.dm b/mods/content/undead/overrides.dm new file mode 100644 index 000000000000..38e0a6f9a9a3 --- /dev/null +++ b/mods/content/undead/overrides.dm @@ -0,0 +1,134 @@ +/mob/living/can_feel_pain(var/check_organ) + return !has_trait(/decl/trait/undead) && ..() + +/mob/living/human/get_gibber_type() // To avoid skeletons causing gibs. + return has_trait(/decl/trait/undead, TRAIT_LEVEL_MODERATE) ? null : ..() + +/mob/living/human/ssd_check() + if(has_trait(/decl/trait/undead)) + return FALSE + return ..() + +/mob/living/human/get_movement_delay(travel_dir) + . = ..() + if(has_trait(/decl/trait/undead)) + var/static/default_walk_delay = get_config_value(/decl/config/num/movement_walk) + . = max(., default_walk_delay) + +/mob/living/human/get_attack_telegraph_delay() + if(has_trait(/decl/trait/undead)) + return (0.6 SECONDS) + return ..() + +/mob/living/human/get_self_death_message(gibbed) + return has_trait(/decl/trait/undead, TRAIT_LEVEL_MODERATE) ? "You have crumbled." : ..() + +/mob/living/human/get_death_message(gibbed) + if(has_trait(/decl/trait/undead, TRAIT_LEVEL_MODERATE)) + return "crumbles and falls apart!" + return ..() + +/mob/living/human/death(gibbed) + . = ..() + if(stat == DEAD && !QDELETED(src) && !gibbed && has_trait(/decl/trait/undead, TRAIT_LEVEL_MODERATE)) + gib() + +/mob/living/human/get_eye_colour() + // Force an evil red glow for undead mobs. + if(stat == CONSCIOUS && has_trait(/decl/trait/undead)) + return COLOR_RED + return ..() + +/mob/living/human/death(gibbed) + . = ..() + if(!QDELETED(src) && has_trait(/decl/trait/undead)) + var/obj/item/organ/external/head/head = get_organ(BP_HEAD) + head.glowing_eyes = initial(head.glowing_eyes) + update_eyes() + +/mob/living/human/get_life_damage_types() + if(has_trait(/decl/trait/undead)) + // Undead human mobs use brute and burn damage instead of brain damage, a la simplemobs. + var/static/list/life_damage_types = list( + BURN, + BRUTE + ) + return life_damage_types + return ..() + +/mob/living/human/getOxyLoss(var/amount) + return has_trait(/decl/trait/undead) ? 0 : ..() + +/mob/living/human/setOxyLoss(var/amount) + return has_trait(/decl/trait/undead) ? 0 : ..() + +/mob/living/human/adjustOxyLoss(var/damage, var/do_update_health = TRUE) + return has_trait(/decl/trait/undead) ? 0 : ..() + +/mob/living/human/getToxLoss() + return has_trait(/decl/trait/undead) ? 0 : ..() + +/mob/living/human/setToxLoss(var/amount) + return has_trait(/decl/trait/undead) ? 0 : ..() + +/mob/living/human/adjustToxLoss(var/amount, var/do_update_health = TRUE) + return has_trait(/decl/trait/undead) ? 0 : ..() + +/mob/living/human/check_vital_organ_missing() + return !has_trait(/decl/trait/undead) && ..() + +/mob/living/human/process_internal_organs() + return has_trait(/decl/trait/undead) ? null : ..() + +/mob/living/human/should_have_organ(organ_to_check) + // It might be nice to have eyes etc. matter for zombies, but as all organs are dead it won't work currently. + return has_trait(/decl/trait/undead) ? FALSE : ..() + +/mob/living/human/get_vision_organ_tag() + // Where we're going, we don't need eyes. + return has_trait(/decl/trait/undead) ? null : ..() + +/mob/living/human/need_breathe() + return has_trait(/decl/trait/undead) ? FALSE : ..() + +// Undead don't get hungry/thirsty (except for brains) +/mob/living/human/get_nutrition() + return has_trait(/decl/trait/undead) ? get_max_nutrition() : ..() + +/mob/living/human/get_hydration() + return has_trait(/decl/trait/undead) ? get_max_hydration() : ..() + +// Overrides to handle dead flag separately. +/obj/item/organ/is_usable() + . = !(status & (ORGAN_CUT_AWAY|ORGAN_MUTATED)) + if(. && (status & ORGAN_DEAD)) + return owner?.has_trait(/decl/trait/undead) + +/obj/item/organ/external/check_status_flags_for_process() + if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_MUTATED|ORGAN_DISLOCATED)) + return TRUE + if((status & ORGAN_DEAD) && !owner?.has_trait(/decl/trait/undead)) + return TRUE + return FALSE + +/mob/living/human/set_status_condition(condition, amount) + if(has_trait(/decl/trait/undead)) + var/static/list/ignore_status_conditions = list( + STAT_BLIND, + STAT_DEAF, + STAT_CONFUSE, + STAT_DIZZY, + STAT_JITTER, + STAT_STUTTER, + STAT_SLUR, + STAT_ASLEEP, + STAT_DRUGGY, + STAT_DROWSY, + STAT_BLURRY, + STAT_BLIND, + STAT_TINNITUS, + STAT_DEAF + ) + if(condition in ignore_status_conditions) + return + . = ..() diff --git a/mods/content/undead/skillset.dm b/mods/content/undead/skillset.dm new file mode 100644 index 000000000000..479d7cfe5505 --- /dev/null +++ b/mods/content/undead/skillset.dm @@ -0,0 +1,2 @@ +/datum/skillset/undead + default_value = SKILL_BASIC diff --git a/mods/content/undead/traits.dm b/mods/content/undead/traits.dm new file mode 100644 index 000000000000..06d4f34e9572 --- /dev/null +++ b/mods/content/undead/traits.dm @@ -0,0 +1,4 @@ +/decl/trait/undead + name = "Undead" + description = "Your body is dead, but remains animated through some supernatural force." + levels = list(TRAIT_LEVEL_MINOR, TRAIT_LEVEL_MODERATE) // Moderate means skeleton, minor means zombie. diff --git a/mods/~compatibility/patches/drakes.dm b/mods/~compatibility/patches/drakes.dm new file mode 100644 index 000000000000..c128e102020a --- /dev/null +++ b/mods/~compatibility/patches/drakes.dm @@ -0,0 +1,9 @@ +// Undead drakes +#ifdef MODPACK_UNDEAD +#include "drakes/undead_drakes.dm" +#endif + +// Override drake lore and names for the fantasy modpack. +#ifdef MODPACK_FANTASY_SPECIES +#include "drakes/fantasy_drakes.dm" +#endif diff --git a/mods/~compatibility/patches/fantasy/drake_fantasy.dm b/mods/~compatibility/patches/drakes/fantasy_drakes.dm similarity index 100% rename from mods/~compatibility/patches/fantasy/drake_fantasy.dm rename to mods/~compatibility/patches/drakes/fantasy_drakes.dm diff --git a/mods/~compatibility/patches/drakes/undead_drakes.dm b/mods/~compatibility/patches/drakes/undead_drakes.dm new file mode 100644 index 000000000000..59425c43f468 --- /dev/null +++ b/mods/~compatibility/patches/drakes/undead_drakes.dm @@ -0,0 +1,9 @@ +/mob/living/human/skeleton/meredrake/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/grafadreka::uid + . = ..() + +/mob/living/human/zombie/meredrake/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/grafadreka::uid + . = ..() diff --git a/mods/~compatibility/patches/fantasy.dm b/mods/~compatibility/patches/fantasy.dm index b0abba63bcd4..090e04dca00a 100644 --- a/mods/~compatibility/patches/fantasy.dm +++ b/mods/~compatibility/patches/fantasy.dm @@ -1,8 +1,3 @@ -// Override drake lore and names for the fantasy modpack. -#ifdef MODPACK_DRAKES -#include "fantasy/drake_fantasy.dm" -#endif - // Make whetstones available for the fantasy modpack/ #ifdef MODPACK_ITEM_SHARPENING #include "fantasy/whetstone_fantasy.dm" @@ -10,4 +5,8 @@ #ifdef MODPACK_BLACKSMITHY #include "fantasy/forging_fantasy.dm" +#endif + +#ifdef MODPACK_UNDEAD +#include "fantasy/undead_fantasy.dm" #endif \ No newline at end of file diff --git a/mods/~compatibility/patches/fantasy/undead_fantasy.dm b/mods/~compatibility/patches/fantasy/undead_fantasy.dm new file mode 100644 index 000000000000..ef255509e96a --- /dev/null +++ b/mods/~compatibility/patches/fantasy/undead_fantasy.dm @@ -0,0 +1,53 @@ +/mob/living/human/skeleton/hnoll/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/hnoll::uid + . = ..() + +/mob/living/human/skeleton/kobaloi/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/kobaloi::uid + . = ..() + +/mob/living/human/zombie/hnoll/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/hnoll::uid + . = ..() + +/mob/living/human/zombie/kobaloi/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + if(!species_uid) + species_uid = /decl/species/kobaloi::uid + . = ..() + +/mob/living/human/grant_basic_undead_equipment() + + var/species_name = get_species_name() + if(species_name == /decl/species/human::name || species_name == /decl/species/hnoll::name) + var/pants_type = pick(/obj/item/clothing/pants/trousers, /obj/item/clothing/pants/trousers/braies) + equip_to_slot_or_del(new pants_type(src), slot_w_uniform_str) + + var/jerkin_type = pick(/obj/item/clothing/shirt/tunic, /obj/item/clothing/shirt/tunic/short, /obj/item/clothing/shirt/jerkin) + equip_to_slot_or_del(new jerkin_type(src), slot_w_uniform_str) + + if(prob(30)) + equip_to_slot_or_del(new /obj/item/clothing/suit/armor/forged/banded(src), slot_wear_suit_str) + else + equip_to_slot_or_del(new /obj/item/clothing/suit/armor/crafted/leather(src), slot_wear_suit_str) + if(prob(20)) + put_in_active_hand(new /obj/item/bladed/broadsword(src)) + else + put_in_active_hand(new /obj/item/bladed/shortsword(src)) + put_in_inactive_hand(new /obj/item/shield/crafted/buckler(src)) + return + + if(species_name == /decl/species/kobaloi::name) + + var/pants_type = pick(/obj/item/clothing/pants/trousers/braies, /obj/item/clothing/pants/loincloth) + equip_to_slot_or_del(new pants_type(src), slot_w_uniform_str) + if(prob(75)) + var/jerkin_type = pick(/obj/item/clothing/shirt/tunic/short, /obj/item/clothing/shirt/jerkin) + equip_to_slot_or_del(new jerkin_type(src), slot_w_uniform_str) + + if(prob(30)) + equip_to_slot_or_del(new /obj/item/clothing/suit/armor/crafted/leather(src), slot_wear_suit_str) + put_in_active_hand(new /obj/item/bladed/knife(src)) + return diff --git a/mods/~compatibility/~compatibility.dm b/mods/~compatibility/~compatibility.dm index 84f09c182555..261d4d018071 100644 --- a/mods/~compatibility/~compatibility.dm +++ b/mods/~compatibility/~compatibility.dm @@ -29,4 +29,8 @@ #ifdef CONTENT_PACK_CIRCUITS #include "patches/circuits.dm" -#endif \ No newline at end of file +#endif + +#ifdef MODPACK_DRAKES +#include "patches/drakes.dm" +#endif From c1d671c8d3a8219ee304441385b59c19cffcff5a Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 20 Oct 2025 07:27:47 +1100 Subject: [PATCH 0839/1331] Automatic changelog generation for PR #5155 [ci skip] --- html/changelogs/AutoChangeLog-pr-5155.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5155.yml diff --git a/html/changelogs/AutoChangeLog-pr-5155.yml b/html/changelogs/AutoChangeLog-pr-5155.yml new file mode 100644 index 000000000000..4cabef2465e6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5155.yml @@ -0,0 +1,5 @@ +author: MistakeNot4892 +changes: + - {tweak: Crafted barrels will need barrel rims forged if the forging modpack + is in use.} +delete-after: true From 59197d9f81c82600aa6d503db51ead38dbd2342f Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Mon, 20 Oct 2025 11:33:45 +1100 Subject: [PATCH 0840/1331] Lobotomises the laundry basket. --- .../items/weapons/storage/laundry_basket.dm | 70 +++--------------- icons/mob/onmob/items/lefthand.dmi | Bin 56681 -> 56204 bytes icons/mob/onmob/items/righthand.dmi | Bin 56796 -> 56668 bytes icons/obj/items/storage/laundry.dmi | Bin 0 -> 808 bytes icons/obj/janitor.dmi | Bin 9502 -> 8136 bytes 5 files changed, 11 insertions(+), 59 deletions(-) create mode 100644 icons/obj/items/storage/laundry.dmi diff --git a/code/game/objects/items/weapons/storage/laundry_basket.dm b/code/game/objects/items/weapons/storage/laundry_basket.dm index 4d03a5567929..95451bc35a7a 100644 --- a/code/game/objects/items/weapons/storage/laundry_basket.dm +++ b/code/game/objects/items/weapons/storage/laundry_basket.dm @@ -1,72 +1,24 @@ -// ----------------------------- -// Laundry Basket -// ----------------------------- -// An item designed for hauling the belongings of a character. -// So this cannot be abused for other uses, we make it two-handed and inable to have its storage looked into. /obj/item/laundry_basket name = "laundry basket" - icon = 'icons/obj/janitor.dmi' - icon_state = "laundry-empty" - item_state = "laundry" + icon = 'icons/obj/items/storage/laundry.dmi' + icon_state = ICON_STATE_WORLD desc = "The peak of thousands of years of laundry evolution." w_class = ITEM_SIZE_GARGANTUAN storage = /datum/storage/laundry_basket material = /decl/material/solid/organic/plastic obj_flags = OBJ_FLAG_HOLLOW - var/linked - -/obj/item/laundry_basket/attack_hand(mob/user) - if(!ishuman(user)) - return ..() - var/mob/living/human/H = user - var/obj/item/organ/external/temp = GET_EXTERNAL_ORGAN(H, H.get_active_held_item_slot()) - if(!temp) - to_chat(user, SPAN_WARNING("You need two hands to pick this up!")) - return TRUE - if(!user.get_empty_hand_slot()) - to_chat(user, SPAN_WARNING("You need a hand to be empty.")) - return TRUE - return ..() /obj/item/laundry_basket/attack_self(mob/user) - var/turf/T = get_turf(user) - to_chat(user, "You dump \the [src]'s contents onto \the [T].") + var/turf/dump_loc = get_turf(user) + if(length(contents) && dump_loc) + to_chat(user, SPAN_NOTICE("You dump \the [src]'s contents onto \the [dump_loc].")) + for(var/atom/movable/thing as anything in get_contained_external_atoms()) + thing.dropInto(dump_loc) + return TRUE return ..() -/obj/item/laundry_basket/on_picked_up(mob/user, atom/old_loc) - var/obj/item/laundry_basket/offhand/O = new(user) - O.SetName("[name] - second hand") - O.desc = "Your second grip on \the [src]." - O.linked = src - user.put_in_inactive_hand(O) - linked = O - return - /obj/item/laundry_basket/on_update_icon() . = ..() - if(contents.len) - icon_state = "laundry-full" - else - icon_state = "laundry-empty" - -/obj/item/laundry_basket/handle_mouse_drop(atom/over, mob/user, params) - if(over == user) - return TRUE - . = ..() - -/obj/item/laundry_basket/dropped(mob/user) - qdel(linked) - return ..() - -//Offhand -/obj/item/laundry_basket/offhand - icon = 'icons/mob/offhand.dmi' - icon_state = "offhand" - name = "second hand" - storage = null - -/obj/item/laundry_basket/offhand/dropped(mob/user) - ..() - user.drop_from_inventory(linked) - return - + icon_state = get_world_inventory_state() + if(length(contents)) + icon_state = "[icon_state]-full" diff --git a/icons/mob/onmob/items/lefthand.dmi b/icons/mob/onmob/items/lefthand.dmi index 40271107dd3cbc610460b48eb68ff184120d7ba3..d7947b3d21b12edd15259a08fb123422a4a36b06 100644 GIT binary patch delta 17502 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 delta 16052 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 literal 9502 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 CF Date: Mon, 20 Oct 2025 11:41:27 +1100 Subject: [PATCH 0841/1331] Greyscales laundry icons. --- .../items/weapons/storage/laundry_basket.dm | 18 +++++++++++++++++- icons/obj/items/storage/laundry.dmi | Bin 808 -> 841 bytes 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/weapons/storage/laundry_basket.dm b/code/game/objects/items/weapons/storage/laundry_basket.dm index 95451bc35a7a..deb912d9ba69 100644 --- a/code/game/objects/items/weapons/storage/laundry_basket.dm +++ b/code/game/objects/items/weapons/storage/laundry_basket.dm @@ -7,6 +7,7 @@ storage = /datum/storage/laundry_basket material = /decl/material/solid/organic/plastic obj_flags = OBJ_FLAG_HOLLOW + paint_color = COLOR_NAVY_BLUE /obj/item/laundry_basket/attack_self(mob/user) var/turf/dump_loc = get_turf(user) @@ -17,8 +18,23 @@ return TRUE return ..() +/obj/item/laundry_basket/Entered(atom/movable/atom, atom/old_loc) + . = ..() + update_clothing_icon() + +/obj/item/laundry_basket/Exited(atom/movable/atom, atom/old_loc) + . = ..() + update_clothing_icon() + /obj/item/laundry_basket/on_update_icon() . = ..() icon_state = get_world_inventory_state() + cut_overlays() + if(length(contents)) + add_overlay(overlay_image(icon, "[icon_state]-full", COLOR_WHITE, RESET_COLOR)) + +/obj/item/laundry_basket/get_mob_overlay(mob/user_mob, slot, bodypart, use_fallback_if_icon_missing, skip_adjustment) + var/image/res = ..() if(length(contents)) - icon_state = "[icon_state]-full" + res.overlays += overlay_image(res.icon, "[res.icon_state]-full", COLOR_WHITE, RESET_COLOR) + return res diff --git a/icons/obj/items/storage/laundry.dmi b/icons/obj/items/storage/laundry.dmi index 25a5eef434e4d06d785600182c67e42c0e952180..d7b34eda9204e7d5770ad458ca633bbbe32a106a 100644 GIT binary patch delta 718 zcmV;<0x|ul2FV6fiBL{Q4GJ0x0000DNk~Le0001>0001h1Oos70Det>C;$Ke6;Mo6 zMZmzo|NsBd(b2lPx}2PxfPjF&z`&^C4Q2oU00DGTPE!Ct=GbNc005kkUx%%j90s|f~%hkILrYsKXiqx{ufLD00KHmL_t(&f$f^#vYQ|f#`%*65NPj#r1ubW z?%SBYz{%wOU!@?53aJX?nO<~%<2a78yWfJDd|`qR091iBb7+SWha_Y-^z+mo%p8#q zi*88xYY@m3SIV85N`a=kJ$irGHB>mWD-%knT7hU#h=&xMX=@d?l>)VLE5iMfXla*^ zNL4~A@7<+)+`7m4-Y3M8W2s2Pk*rEcE~Jy)$#f?!b$>(Za1<(R+whJMp_y(y{N65y zj0#zQ#_MzHVsS{=+#jK3erZD>3rAE){fp0LFMShFi+o`T$T#x)`{#e_HKJ!EJO}o%oZDqus^&woyxPvN*U2bwo?}&eg94~h4%@VRT z$PJhjqWNCnzW8^PyOHy zR{dZla=fVfH$lR>AFLxLIYQyOAFKrYAOMW~0pe3o`BIGi`Tzg`07*qoM6N<$f|qSn A4FCWD delta 685 zcmV;e0#g0S2B-#KiBL{Q4GJ0x0000DNk~Le0001h0001B1Oos704G#udH?_bAy7r0CxZYr~m+F002|}|Nqg^(Ym_2002V3z`!GcQq*6^>YFH8UWMeOV7cR)J6aR0v1U`K~z|U?Uq4rn=l;3DI(f+A=|3m zh~eeJkk&F;I=@HanxG-A4Y0`Mz@L9Qz#^vwrR?@-7yxC zLPGJjfW*kaow-?%jZil71Hg4uRuQQ%seBBK48DOeNH~6)^mQo@vG#F;3kF})b-e|D zCJcXmZ1QlMueP}#C32VIL|``)v^PHR-F^7^db~dz{R86qx~$m6w?L45XT?FC0cvkD zvJW7O43Z%&BJdquJcA%T-iQQ1c)B6m!iH}FbZg=WqoHBp9v}Gh{oyr{T6_hVl--)_ z5H2Thk_I%ac>tE+Gy-aGhMf8RyMe`Gu~>gT9_=r`r}!6A&C{1n{V9AZ`VQ)T(9Rbd z*(YE~`wr@UAm1r=Wz-my(Vn8yr^BuHnXm+;_goKK;iN^H zL;ay!EEbF9eR;=zU^sudADE!-2PP={feGq Date: Mon, 20 Oct 2025 00:59:30 +0000 Subject: [PATCH 0842/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-5155.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5155.yml diff --git a/html/changelog.html b/html/changelog.html index 02e47675ee10..a1998ef1ef37 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ --> - +

    CAPACITOR STATUS

    - +
    Charge Precentage:
    - +
    {{:helper.displayBar(data.chargepercent, 0, 100)}}
    - +
    Maximum Capacitor Charge:
    - +
    {{:data.max_charge}} MJ
    - +
    Target Charge Level:
    - +
    {{:helper.link(data.target_charge, null, { 'adjust_target' : 1})}} MJ
    - +
    Estimated Charge Time:
    - +
    {{:data.chargetime}}
    - +
    Maximum Power Input: @@ -67,7 +67,7 @@ {{:data.max_power}} Kw
    - +
    Current Power Input: @@ -76,12 +76,12 @@ {{:helper.link(data.power_input, null, { 'adjust_power' : 1})}} Kw
    - +
    Charging Status:
    - +
    {{if data.charging == 1}} Charging @@ -90,22 +90,22 @@ {{/if}}
    - +
    {{:helper.link("Toggle Capacitor Charging", null, { 'toggle_charge' : 1})}}
    - +

    FUEL SYSTEM STATUS

    - +
    Remaining Fuel:
    - {{:data.fuel_joules}} MJ + {{:data.fuel_joules}} MJ / {{:data.maxfuel}} MJ
    - +
    Average Conversion Rate: @@ -114,9 +114,9 @@ {{:data.fuel_conversion}} MJ/cm3
    - +

    JUMP SOLUTION ESTIMATES

    - +
    Jump Solution Status: @@ -142,7 +142,7 @@ {{:data.jumpcost}} MJ/cm
    - +
    Estimated Power Requirement: @@ -169,7 +169,7 @@ {{:helper.link(data.to_plot_y, null, { 'set_shunt_y' : 1})}}
    - +
    Plotted Shunt X/Y Coordinates: @@ -181,27 +181,27 @@ Y {{:data.shunt_y}}
    - +

    JUMP CONTROLS

    -
    +
    {{:helper.link("Cancel Jump Plotting", null, { 'cancel_plot' : 1})}}
    - -
    + +
    {{:helper.link("Engage Jump Plotting", null, { 'plot_jump' : 1})}}
    - -
    + +
    {{:helper.link("Engage Shunt Drive", null, { 'start_shunt' : 1 })}}
    - +
    {{:helper.link("Disengage Shunt Drive", null, { 'cancel_shunt' : 1 })}} From bc0def1ab8735c5bb1a0481fa65c717c029d00fa Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 20 Oct 2025 19:47:06 -0400 Subject: [PATCH 0854/1331] Remove unused get_linked_to_desc helper --- mods/content/integrated_electronics/pins/_pins.dm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/mods/content/integrated_electronics/pins/_pins.dm b/mods/content/integrated_electronics/pins/_pins.dm index 08f9194f2ca7..1cd253dfcb93 100644 --- a/mods/content/integrated_electronics/pins/_pins.dm +++ b/mods/content/integrated_electronics/pins/_pins.dm @@ -144,12 +144,6 @@ D [1]/ || var/datum/integrated_io/io = linked[k] write_data_to_pin(io.data) -/datum/integrated_io/proc/get_linked_to_desc() - if(linked.len) - return "the [english_list(linked)]" - return "nothing" - - /datum/integrated_io/proc/connect_pin(datum/integrated_io/pin) pin.linked |= src linked |= pin From c876d1f012d9df6a91306e20142840dc75c16079 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 20 Oct 2025 19:50:00 -0400 Subject: [PATCH 0855/1331] Remove redundant prefab assembly_name variable --- mods/content/integrated_electronics/prefab/prefab.dm | 1 - mods/content/integrated_electronics/prefab/prefabs.dm | 3 +-- mods/content/integrated_electronics/prefab/test/testprefabs.dm | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mods/content/integrated_electronics/prefab/prefab.dm b/mods/content/integrated_electronics/prefab/prefab.dm index ae769b301a79..689fbbd687b4 100644 --- a/mods/content/integrated_electronics/prefab/prefab.dm +++ b/mods/content/integrated_electronics/prefab/prefab.dm @@ -5,7 +5,6 @@ return TRUE /decl/prefab/ic_assembly - var/assembly_name var/data var/power_cell_type diff --git a/mods/content/integrated_electronics/prefab/prefabs.dm b/mods/content/integrated_electronics/prefab/prefabs.dm index 41d1d8b8b4a1..a17305b58f9a 100644 --- a/mods/content/integrated_electronics/prefab/prefabs.dm +++ b/mods/content/integrated_electronics/prefab/prefabs.dm @@ -1,6 +1,5 @@ /decl/prefab/ic_assembly/hand_teleporter - assembly_name = "hand-teleporter" - data = @'{"assembly":{"type":"type-a electronic mechanism","name":"Hand Teleporter", "detail_color":"#5d99be"},"components":[{"type":"teleporter locator"},{"type":"wormhole generator"},{"type":"button","name":"Open Wormhole"}],"wires":[[[1,"O",1],[2,"I",1]],[[2,"A",1],[3,"A",1]]]}' + data = @'{"assembly":{"type":"type-a electronic mechanism","name":"hand teleporter", "detail_color":"#5d99be"},"components":[{"type":"teleporter locator"},{"type":"wormhole generator"},{"type":"button","name":"Open Wormhole"}],"wires":[[[1,"O",1],[2,"I",1]],[[2,"A",1],[3,"A",1]]]}' power_cell_type = /obj/item/cell/hyper /obj/abstract/prefab/hand_teleporter diff --git a/mods/content/integrated_electronics/prefab/test/testprefabs.dm b/mods/content/integrated_electronics/prefab/test/testprefabs.dm index 983536b1c873..96972a8bdbb8 100644 --- a/mods/content/integrated_electronics/prefab/test/testprefabs.dm +++ b/mods/content/integrated_electronics/prefab/test/testprefabs.dm @@ -1,6 +1,5 @@ /decl/prefab/ic_assembly/test_heatercooler - assembly_name = "heating-cooling-test" - data = @'{"assembly":{"type":"type-c electronic machine"},"components":[{"type":"starter"},{"type":"reagent funnel"},{"type":"big reagent storage"},{"type":"reagent pump","name":"Hot Pump","inputs":[[3,0,5]]},{"type":"reagent pump","name":"Cool Pump","inputs":[[3,0,5]]},{"type":"reagent heater","name":"Heater","inputs":[[1,0,80]]},{"type":"reagent cooler","name":"Cooler","inputs":[[1,0,-50]]},{"type":"button","name":"Heat And Cool"},{"type":"and gate","name":"Heater Active Check","inputs":[[1,0,0],[2,0,1]]},{"type":"and gate","name":"Cooler Active Check","inputs":[[1,0,0],[2,0,1]]},{"type":"custom delay circuit","name":"Heater Delay","inputs":[[1,0,100]]},{"type":"custom delay circuit","name":"Cooler Delay","inputs":[[1,0,100]]}],"wires":[[[1,"A",1],[3,"A",1]],[[1,"A",1],[6,"A",3]],[[1,"A",1],[7,"A",3]],[[2,"I",1],[3,"O",2]],[[3,"O",2],[4,"I",1]],[[3,"O",2],[5,"I",1]],[[4,"I",2],[6,"O",4]],[[4,"A",1],[8,"A",1]],[[4,"A",2],[6,"A",1]],[[5,"I",2],[7,"O",4]],[[5,"A",1],[8,"A",1]],[[5,"A",2],[7,"A",1]],[[6,"O",3],[9,"I",1]],[[6,"A",1],[11,"A",2]],[[6,"A",2],[9,"A",1]],[[7,"O",3],[10,"I",1]],[[7,"A",1],[12,"A",2]],[[7,"A",2],[10,"A",1]],[[9,"A",2],[11,"A",1]],[[10,"A",2],[12,"A",1]]]}' + data = @'{"assembly":{"type":"type-c electronic machine", "name":"heating-cooling test"},"components":[{"type":"starter"},{"type":"reagent funnel"},{"type":"big reagent storage"},{"type":"reagent pump","name":"Hot Pump","inputs":[[3,0,5]]},{"type":"reagent pump","name":"Cool Pump","inputs":[[3,0,5]]},{"type":"reagent heater","name":"Heater","inputs":[[1,0,80]]},{"type":"reagent cooler","name":"Cooler","inputs":[[1,0,-50]]},{"type":"button","name":"Heat And Cool"},{"type":"and gate","name":"Heater Active Check","inputs":[[1,0,0],[2,0,1]]},{"type":"and gate","name":"Cooler Active Check","inputs":[[1,0,0],[2,0,1]]},{"type":"custom delay circuit","name":"Heater Delay","inputs":[[1,0,100]]},{"type":"custom delay circuit","name":"Cooler Delay","inputs":[[1,0,100]]}],"wires":[[[1,"A",1],[3,"A",1]],[[1,"A",1],[6,"A",3]],[[1,"A",1],[7,"A",3]],[[2,"I",1],[3,"O",2]],[[3,"O",2],[4,"I",1]],[[3,"O",2],[5,"I",1]],[[4,"I",2],[6,"O",4]],[[4,"A",1],[8,"A",1]],[[4,"A",2],[6,"A",1]],[[5,"I",2],[7,"O",4]],[[5,"A",1],[8,"A",1]],[[5,"A",2],[7,"A",1]],[[6,"O",3],[9,"I",1]],[[6,"A",1],[11,"A",2]],[[6,"A",2],[9,"A",1]],[[7,"O",3],[10,"I",1]],[[7,"A",1],[12,"A",2]],[[7,"A",2],[10,"A",1]],[[9,"A",2],[11,"A",1]],[[10,"A",2],[12,"A",1]]]}' power_cell_type = /obj/item/cell/hyper /obj/abstract/prefab/test_heatcool From b4cdb48e271930dcc4b37a7b6dc7c232242421e5 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 20 Oct 2025 19:51:54 -0400 Subject: [PATCH 0856/1331] Implement added_to_assembly and removed_from_assembly methods --- mods/content/integrated_electronics/assemblies/_assemblies.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mods/content/integrated_electronics/assemblies/_assemblies.dm b/mods/content/integrated_electronics/assemblies/_assemblies.dm index a88f2c84e44c..c262a1bbc7ee 100644 --- a/mods/content/integrated_electronics/assemblies/_assemblies.dm +++ b/mods/content/integrated_electronics/assemblies/_assemblies.dm @@ -360,6 +360,7 @@ component.forceMove(get_object()) component.assembly = src assembly_components |= component + component.added_to_assembly(src) /obj/item/electronic_assembly/proc/try_remove_component(obj/item/integrated_circuit/component, mob/user, silent) @@ -391,6 +392,7 @@ component.dropInto(loc) component.assembly = null assembly_components.Remove(component) + component.removed_from_assembly(src) /obj/item/electronic_assembly/afterattack(atom/target, mob/user, proximity) From 16fb91404855579eaa95e1d2c22cfc297d5d5619 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 20 Oct 2025 19:58:46 -0400 Subject: [PATCH 0857/1331] Fix circuit assembly names always starting with an uppercase letter --- mods/content/integrated_electronics/assemblies/_assemblies.dm | 2 +- mods/content/integrated_electronics/circuit_serialization.dm | 4 ++-- .../integrated_electronics/components/_integrated_circuit.dm | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/content/integrated_electronics/assemblies/_assemblies.dm b/mods/content/integrated_electronics/assemblies/_assemblies.dm index c262a1bbc7ee..c129dca73355 100644 --- a/mods/content/integrated_electronics/assemblies/_assemblies.dm +++ b/mods/content/integrated_electronics/assemblies/_assemblies.dm @@ -279,7 +279,7 @@ if(!check_interactivity(M)) return var/input = input("What do you want to name this?", "Rename", src.name) as null|text - input = sanitize_name(input,allow_numbers = 1) + input = sanitize_name(input, allow_numbers = TRUE, force_first_letter_uppercase = FALSE) if(!check_interactivity(M)) return if(!QDELETED(src) && input) diff --git a/mods/content/integrated_electronics/circuit_serialization.dm b/mods/content/integrated_electronics/circuit_serialization.dm index 8954b3217e54..fd6eda7eabf7 100644 --- a/mods/content/integrated_electronics/circuit_serialization.dm +++ b/mods/content/integrated_electronics/circuit_serialization.dm @@ -52,7 +52,7 @@ var/init_name = initial(name) // Validate name if(component_params["name"]) - sanitize_name(component_params["name"],allow_numbers=TRUE) + sanitize_name(component_params["name"],allow_numbers=TRUE,force_first_letter_uppercase = FALSE) // Validate input values if(component_params["inputs"]) var/list/loaded_inputs = component_params["inputs"] @@ -138,7 +138,7 @@ /obj/item/electronic_assembly/proc/verify_save(list/assembly_params) // Validate name and color if(assembly_params["name"]) - if(sanitize_name(assembly_params["name"], allow_numbers = TRUE) != assembly_params["name"]) + if(sanitize_name(assembly_params["name"], allow_numbers = TRUE, force_first_letter_uppercase = FALSE) != assembly_params["name"]) return "Bad assembly name." if(assembly_params["desc"]) if(sanitize(assembly_params["desc"]) != assembly_params["desc"]) diff --git a/mods/content/integrated_electronics/components/_integrated_circuit.dm b/mods/content/integrated_electronics/components/_integrated_circuit.dm index 454b80a3b6ae..fce50fde07f8 100644 --- a/mods/content/integrated_electronics/components/_integrated_circuit.dm +++ b/mods/content/integrated_electronics/components/_integrated_circuit.dm @@ -113,7 +113,7 @@ a creative player the means to solve many problems. Circuits are held inside an if(!check_interactivity(M)) return - var/input = sanitize_name(input(M, "What do you want to name this?", "Rename", name) as null|text, allow_numbers = TRUE) + var/input = sanitize_name(input(M, "What do you want to name this?", "Rename", name) as null|text, allow_numbers = TRUE, force_first_letter_uppercase = FALSE) if(check_interactivity(M)) if(!input) input = name From 645501439c0250c552f76616ea5d03d1175274de Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 24 Oct 2025 00:52:35 +0000 Subject: [PATCH 0858/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index a1998ef1ef37..94d93182b2f7 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -63,17 +63,6 @@

    Elizabeth updated:

    • added some variant sprites for cobblestone and running bond paths
    - -

    22 August 2025

    -

    Sutures updated:

    -
      -
    • added new amanita mushroom plant growth states
    • -
    • changed coffee plant growth states
    • -
    • added ambience to Shaded Hills river and swamp areas
    • -
    • ambience should be louder and more frequent overall
    • -
    • stone walls now have a slight noise effect on their sprite
    • -
    • added a boiling-water sound to cooking pots on Shaded Hills
    • -
    +

    20 October 2025

    +

    MistakeNot4892 updated:

    +
      +
    • Crafted barrels will need barrel rims forged if the forging modpack is in use.
    • +
    +

    29 August 2025

    Elizabeth updated:

    + + + + +
    +
    Nebula13
    +
    A Space Station 13 Project
    + +

    + Code licensed under AGPLv3. Content licensed under CC BY-SA 3.0.

    +
    + +
    Nebula13 Credit List + + + + +
    + Contributor list: -Click Here-
    + Special thanks to: the developers of Baystation 12, ScavStation, /tg/station, /vg/station, GoonStation, the original Space Station 13, and BYOND.
    +

    Have a bug to report?
    Visit our Issue Tracker.
    +
    + + +
    diff --git a/interface/skin.dmf b/interface/skin.dmf index 3778ffcd7c19..c02a82497db3 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1,435 +1,435 @@ -macro "default" - elem ".winset :map.right-click=false" - name = "SHIFT+Shift" - elem "Shift" - name = "SHIFT" - command = ".winset :map.right-click=false" - elem "ShiftUp" - name = "SHIFT+UP" - command = ".winset :map.right-click=true" - -menu "menu" - elem - name = "&File" - command = "" - saved-params = "is-checked" - elem - name = "&Save screenshot\tF2" - command = ".auto" - category = "&File" - saved-params = "is-checked" - elem - name = "&Save screenshot as..." - command = ".screenshot" - category = "&File" - saved-params = "is-checked" - elem - name = "" - command = "" - category = "&File" - saved-params = "is-checked" - elem - name = "&Reconnect" - command = ".reconnect" - category = "&File" - saved-params = "is-checked" - elem - name = "&Check ping" - command = ".ping" - category = "&File" - saved-params = "is-checked" - elem - name = "&Quit" - command = ".quit" - category = "&File" - saved-params = "is-checked" - elem - name = "&Size" - command = "" - saved-params = "is-checked" - elem "icon128" - name = "&128x128" - command = "SetWindowIconSize 128" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon96" - name = "&96x96" - command = "SetWindowIconSize 96" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon64" - name = "&64x64" - command = "SetWindowIconSize 64" - category = "&Size" - is-checked = true - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon48" - name = "&48x48" - command = "SetWindowIconSize 48" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon32" - name = "&32x32" - command = "SetWindowIconSize 32" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem - name = "&Scaling" - command = "" - saved-params = "is-checked" - elem "ZM" - name = "&Smooth Scaling" - command = ".winset \"zoommode.is-checked=true?map.zoom-mode=normal:map.zoom-mode=distort\"" - category = "&Scaling" - is-checked = true - can-check = true - group = "scale" - saved-params = "is-checked" - elem "NN" - name = "&Nearest Neighbor" - command = ".winset \"mapwindow.map.zoom-mode=distort\"" - category = "&Scaling" - can-check = true - group = "scale" - saved-params = "is-checked" - elem "PS" - name = "&Point Sampling" - command = ".winset \"mapwindow.map.zoom-mode=normal\"" - category = "&Scaling" - can-check = true - group = "scale" - saved-params = "is-checked" - elem "BL" - name = "&Bilinear" - command = ".winset \"mapwindow.map.zoom-mode=blur\"" - category = "&Scaling" - can-check = true - group = "scale" - saved-params = "is-checked" - elem - name = "&Help" - command = "" - saved-params = "is-checked" - elem - name = "&Admin help\tF1" - command = "adminhelp" - category = "&Help" - saved-params = "is-checked" - -window "mainwindow" - elem "mainwindow" - type = MAIN - pos = 281,0 - size = 640x440 - anchor1 = none - anchor2 = none - is-default = true - background-color = #000000 - saved-params = "pos;size;is-minimized;is-maximized" - on-size = "OnResize" - is-maximized = true - icon = 'icons\\ss13_64.png' - macro = "default" - menu = "menu" - elem "split" - type = CHILD - pos = 3,0 - size = 640x440 - anchor1 = 0,0 - anchor2 = 100,100 - saved-params = "splitter" - left = "mapwindow" - right = "rpane" - is-vert = true - elem "asset_cache_browser" - type = BROWSER - pos = 0,0 - size = 200x200 - anchor1 = none - anchor2 = none - is-visible = false - saved-params = "" - elem "tooltip" - type = BROWSER - pos = 0,0 - size = 999x999 - anchor1 = none - anchor2 = none - background-color = none - is-visible = false - saved-params = "" - -window "mapwindow" - elem "mapwindow" - type = MAIN - pos = 281,0 - size = 640x480 - anchor1 = none - anchor2 = none - saved-params = "pos;size;is-minimized;is-maximized" - titlebar = false - statusbar = false - can-close = false - can-minimize = false - can-resize = false - is-pane = true - elem "map" - type = MAP - pos = 0,0 - size = 640x480 - anchor1 = 0,0 - anchor2 = 100,100 - font-family = "Arial Rounded MT Bold,Arial Black,Arial,sans-serif" - font-size = 7 - is-default = true - saved-params = "icon-size;zoom-mode" - elem "lobbybrowser" - type = BROWSER - pos = 0,0 - size = 640x480 - anchor1 = 0,0 - anchor2 = 100,100 - is-visible = false - is-disabled = true - saved-params = "" - elem "status_bar" - type = LABEL - pos = 0,464 - size = 280x16 - anchor1 = 0,100 - anchor2 = none - text-color = #ffffff - background-color = #222222 - is-visible = false - border = line - saved-params = "" - text = "" - align = left - -window "outputwindow" - elem "outputwindow" - type = MAIN - pos = 281,-30 - size = 640x480 - anchor1 = none - anchor2 = none - background-color = none - saved-params = "pos;size;is-minimized;is-maximized" - titlebar = false - statusbar = false - can-close = false - can-minimize = false - can-resize = false - is-pane = true - outer-size = 654x494 - elem "output" - type = OUTPUT - pos = 0,0 - size = 640x456 - anchor1 = 0,0 - anchor2 = 100,100 - is-default = true - saved-params = "max-lines" - style = ".system {color:#ff0000;}" - max-lines = 0 - elem "saybutton" - type = BUTTON - pos = 600,460 - size = 40x20 - anchor1 = 100,100 - anchor2 = none - background-color = none - saved-params = "is-checked" - text = "Chat" - command = ".winset \"saybutton.is-checked=true?input.command=\"!say \\\"\" macrobutton.is-checked=false:input.command=\"" - button-type = pushbox - elem "input" - type = INPUT - pos = 1,460 - size = 600x20 - anchor1 = 0,100 - anchor2 = 100,100 - background-color = #d3b5b5 - is-default = true - border = sunken - saved-params = "command" - -window "rpane" - elem "rpane" - type = MAIN - pos = 281,0 - size = 640x480 - anchor1 = none - anchor2 = none - saved-params = "pos;size;is-minimized;is-maximized" - is-pane = true - elem "rpanewindow" - type = CHILD - pos = 0,30 - size = 0x0 - anchor1 = 0,0 - anchor2 = 100,100 - saved-params = "splitter" - left = "infowindow" - right = "outputwindow" - is-vert = false - elem "github" - type = BUTTON - pos = 520,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "GitHub" - command = "GitHub" - elem "BugReport" - type = BUTTON - pos = 580,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Bug Report" - command = "Bug-Report" - elem "rulesb" - type = BUTTON - pos = 205,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Rules" - command = "rules" - group = "rpanemode" - elem "changelog" - type = BUTTON - pos = 460,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Changelog" - command = "Changelog" - group = "rpanemode" - elem "Lore" - type = BUTTON - pos = 265,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Lore" - command = "Lore" - group = "rpanemode" - elem "forumb" - type = BUTTON - pos = 385,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Forum" - command = "forum" - group = "rpanemode" - elem "wikib" - type = BUTTON - pos = 325,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Wiki" - command = "wiki" - group = "rpanemode" - elem "textb" - type = BUTTON - pos = 0,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Text" - command = ".winset \"rpanewindow.left=;\"" - is-checked = true - group = "rpanemode" - button-type = pushbox - elem "infob" - type = BUTTON - pos = 60,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Info" - command = ".winset \"rpanewindow.left=infowindow\"" - group = "rpanemode" - button-type = pushbox - elem "button_codex" - type = BUTTON - pos = 133,0 - size = 60x15 - anchor1 = none - anchor2 = none - saved-params = "is-checked" - text = "Codex" - command = "Codex" - group = "rpanemode" - -window "preferences_window" - elem "preferences_window" - type = MAIN - pos = 281,0 - size = 1000x800 - anchor1 = none - anchor2 = none - is-visible = false - saved-params = "pos;size;is-minimized;is-maximized" - statusbar = false - elem "preferences_browser" - type = BROWSER - pos = 0,0 - size = 800x800 - anchor1 = 0,0 - anchor2 = 80,100 - saved-params = "" - elem "character_preview_map" - type = MAP - pos = 800,0 - size = 200x800 - anchor1 = 80,0 - anchor2 = 100,100 - right-click = true - saved-params = "zoom;letterbox;zoom-mode" - -window "infowindow" - elem "infowindow" - type = MAIN - pos = 281,0 - size = 640x480 - anchor1 = none - anchor2 = none - saved-params = "pos;size;is-minimized;is-maximized" - title = "Info" - is-pane = true - elem "info" - type = INFO - pos = 0,0 - size = 638x477 - anchor1 = 0,0 - anchor2 = 100,100 - is-default = true - saved-params = "" - highlight-color = #00aa00 - on-show = ".winset\"rpane.infob.is-checked=true\"" - on-hide = ".winset\"rpane.infob.is-checked=false\"" - +macro "default" + elem ".winset :map.right-click=false" + name = "SHIFT+Shift" + elem "Shift" + name = "SHIFT" + command = ".winset :map.right-click=false" + elem "ShiftUp" + name = "SHIFT+UP" + command = ".winset :map.right-click=true" + +menu "menu" + elem + name = "&File" + command = "" + saved-params = "is-checked" + elem + name = "&Save screenshot\tF2" + command = ".auto" + category = "&File" + saved-params = "is-checked" + elem + name = "&Save screenshot as..." + command = ".screenshot" + category = "&File" + saved-params = "is-checked" + elem + name = "" + command = "" + category = "&File" + saved-params = "is-checked" + elem + name = "&Reconnect" + command = ".reconnect" + category = "&File" + saved-params = "is-checked" + elem + name = "&Check ping" + command = ".ping" + category = "&File" + saved-params = "is-checked" + elem + name = "&Quit" + command = ".quit" + category = "&File" + saved-params = "is-checked" + elem + name = "&Size" + command = "" + saved-params = "is-checked" + elem "icon128" + name = "&128x128" + command = "SetWindowIconSize 128" + category = "&Size" + can-check = true + group = "size" + saved-params = "is-checked" + elem "icon96" + name = "&96x96" + command = "SetWindowIconSize 96" + category = "&Size" + can-check = true + group = "size" + saved-params = "is-checked" + elem "icon64" + name = "&64x64" + command = "SetWindowIconSize 64" + category = "&Size" + is-checked = true + can-check = true + group = "size" + saved-params = "is-checked" + elem "icon48" + name = "&48x48" + command = "SetWindowIconSize 48" + category = "&Size" + can-check = true + group = "size" + saved-params = "is-checked" + elem "icon32" + name = "&32x32" + command = "SetWindowIconSize 32" + category = "&Size" + can-check = true + group = "size" + saved-params = "is-checked" + elem + name = "&Scaling" + command = "" + saved-params = "is-checked" + elem "ZM" + name = "&Smooth Scaling" + command = ".winset \"zoommode.is-checked=true?map.zoom-mode=normal:map.zoom-mode=distort\"" + category = "&Scaling" + is-checked = true + can-check = true + group = "scale" + saved-params = "is-checked" + elem "NN" + name = "&Nearest Neighbor" + command = ".winset \"mapwindow.map.zoom-mode=distort\"" + category = "&Scaling" + can-check = true + group = "scale" + saved-params = "is-checked" + elem "PS" + name = "&Point Sampling" + command = ".winset \"mapwindow.map.zoom-mode=normal\"" + category = "&Scaling" + can-check = true + group = "scale" + saved-params = "is-checked" + elem "BL" + name = "&Bilinear" + command = ".winset \"mapwindow.map.zoom-mode=blur\"" + category = "&Scaling" + can-check = true + group = "scale" + saved-params = "is-checked" + elem + name = "&Help" + command = "" + saved-params = "is-checked" + elem + name = "&Admin help\tF1" + command = "adminhelp" + category = "&Help" + saved-params = "is-checked" + +window "mainwindow" + elem "mainwindow" + type = MAIN + pos = 281,0 + size = 640x440 + anchor1 = none + anchor2 = none + is-default = true + background-color = #000000 + saved-params = "pos;size;is-minimized;is-maximized" + on-size = "OnResize" + is-maximized = true + icon = 'icons\\ss13_64.png' + macro = "default" + menu = "menu" + elem "split" + type = CHILD + pos = 3,0 + size = 640x440 + anchor1 = 0,0 + anchor2 = 100,100 + saved-params = "splitter" + left = "mapwindow" + right = "rpane" + is-vert = true + elem "asset_cache_browser" + type = BROWSER + pos = 0,0 + size = 200x200 + anchor1 = none + anchor2 = none + is-visible = false + saved-params = "" + elem "tooltip" + type = BROWSER + pos = 0,0 + size = 999x999 + anchor1 = none + anchor2 = none + background-color = none + is-visible = false + saved-params = "" + +window "mapwindow" + elem "mapwindow" + type = MAIN + pos = 281,0 + size = 640x480 + anchor1 = none + anchor2 = none + saved-params = "pos;size;is-minimized;is-maximized" + titlebar = false + statusbar = false + can-close = false + can-minimize = false + can-resize = false + is-pane = true + elem "map" + type = MAP + pos = 0,0 + size = 640x480 + anchor1 = 0,0 + anchor2 = 100,100 + font-family = "Arial Rounded MT Bold,Arial Black,Arial,sans-serif" + font-size = 7 + is-default = true + saved-params = "icon-size;zoom-mode" + elem "lobbybrowser" + type = BROWSER + pos = 0,0 + size = 640x480 + anchor1 = 0,0 + anchor2 = 100,100 + is-visible = false + is-disabled = true + saved-params = "" + elem "status_bar" + type = LABEL + pos = 0,464 + size = 280x16 + anchor1 = 0,100 + anchor2 = none + text-color = #ffffff + background-color = #222222 + is-visible = false + border = line + saved-params = "" + text = "" + align = left + +window "outputwindow" + elem "outputwindow" + type = MAIN + pos = 281,-30 + size = 640x480 + anchor1 = none + anchor2 = none + background-color = none + saved-params = "pos;size;is-minimized;is-maximized" + titlebar = false + statusbar = false + can-close = false + can-minimize = false + can-resize = false + is-pane = true + outer-size = 654x494 + elem "output" + type = OUTPUT + pos = 0,0 + size = 640x456 + anchor1 = 0,0 + anchor2 = 100,100 + is-default = true + saved-params = "max-lines" + style = ".system {color:#ff0000;}" + max-lines = 0 + elem "saybutton" + type = BUTTON + pos = 600,460 + size = 40x20 + anchor1 = 100,100 + anchor2 = none + background-color = none + saved-params = "is-checked" + text = "Chat" + command = ".winset \"saybutton.is-checked=true?input.command=\"!say \\\"\" macrobutton.is-checked=false:input.command=\"" + button-type = pushbox + elem "input" + type = INPUT + pos = 1,460 + size = 600x20 + anchor1 = 0,100 + anchor2 = 100,100 + background-color = #d3b5b5 + is-default = true + border = sunken + saved-params = "command" + +window "rpane" + elem "rpane" + type = MAIN + pos = 281,0 + size = 640x480 + anchor1 = none + anchor2 = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true + elem "rpanewindow" + type = CHILD + pos = 0,30 + size = 0x0 + anchor1 = 0,0 + anchor2 = 100,100 + saved-params = "splitter" + left = "infowindow" + right = "outputwindow" + is-vert = false + elem "github" + type = BUTTON + pos = 520,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "GitHub" + command = "GitHub" + elem "BugReport" + type = BUTTON + pos = 580,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Bug Report" + command = "Bug-Report" + elem "rulesb" + type = BUTTON + pos = 205,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Rules" + command = "rules" + group = "rpanemode" + elem "changelog" + type = BUTTON + pos = 460,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Changelog" + command = "Changelog" + group = "rpanemode" + elem "Lore" + type = BUTTON + pos = 265,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Lore" + command = "Lore" + group = "rpanemode" + elem "forumb" + type = BUTTON + pos = 385,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Forum" + command = "forum" + group = "rpanemode" + elem "wikib" + type = BUTTON + pos = 325,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Wiki" + command = "wiki" + group = "rpanemode" + elem "textb" + type = BUTTON + pos = 0,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Text" + command = ".winset \"rpanewindow.left=;\"" + is-checked = true + group = "rpanemode" + button-type = pushbox + elem "infob" + type = BUTTON + pos = 60,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Info" + command = ".winset \"rpanewindow.left=infowindow\"" + group = "rpanemode" + button-type = pushbox + elem "button_codex" + type = BUTTON + pos = 133,0 + size = 60x15 + anchor1 = none + anchor2 = none + saved-params = "is-checked" + text = "Codex" + command = "Codex" + group = "rpanemode" + +window "preferences_window" + elem "preferences_window" + type = MAIN + pos = 281,0 + size = 1000x800 + anchor1 = none + anchor2 = none + is-visible = false + saved-params = "pos;size;is-minimized;is-maximized" + statusbar = false + elem "preferences_browser" + type = BROWSER + pos = 0,0 + size = 800x800 + anchor1 = 0,0 + anchor2 = 80,100 + saved-params = "" + elem "character_preview_map" + type = MAP + pos = 800,0 + size = 200x800 + anchor1 = 80,0 + anchor2 = 100,100 + right-click = true + saved-params = "zoom;letterbox;zoom-mode" + +window "infowindow" + elem "infowindow" + type = MAIN + pos = 281,0 + size = 640x480 + anchor1 = none + anchor2 = none + saved-params = "pos;size;is-minimized;is-maximized" + title = "Info" + is-pane = true + elem "info" + type = INFO + pos = 0,0 + size = 638x477 + anchor1 = 0,0 + anchor2 = 100,100 + is-default = true + saved-params = "" + highlight-color = #00aa00 + on-show = ".winset\"rpane.infob.is-checked=true\"" + on-hide = ".winset\"rpane.infob.is-checked=false\"" + diff --git a/mods/gamemodes/cult/_cult.dme b/mods/gamemodes/cult/_cult.dme index 02138255d74c..88b4aa4b8af3 100644 --- a/mods/gamemodes/cult/_cult.dme +++ b/mods/gamemodes/cult/_cult.dme @@ -25,7 +25,7 @@ #include "abilities\construct.dm" #include "abilities\harvest.dm" #include "abilities\shade.dm" -#include "cultify\de-cultify.dm" +#include "cultify\de-cultify.dm" #include "cultify\defile.dm" #include "cultify\mob.dm" #include "cultify\turf.dm" @@ -34,11 +34,11 @@ #include "mobs\constructs\constructs.dm" #include "mobs\constructs\soulstone.dm" // END_INCLUDE -#endif -// BEGIN_INTERNALS -// END_INTERNALS -// BEGIN_FILE_DIR -#define FILE_DIR . -// END_FILE_DIR -// BEGIN_PREFERENCES -// END_PREFERENCES +#endif +// BEGIN_INTERNALS +// END_INTERNALS +// BEGIN_FILE_DIR +#define FILE_DIR . +// END_FILE_DIR +// BEGIN_PREFERENCES +// END_PREFERENCES diff --git a/nano/templates/agent_id_card.tmpl b/nano/templates/agent_id_card.tmpl index 7a41dcdf1b7f..a802efbc82dd 100644 --- a/nano/templates/agent_id_card.tmpl +++ b/nano/templates/agent_id_card.tmpl @@ -1,21 +1,21 @@ - -{{:helper.syndicateMode()}} -

    Available Entries

    - -{{for data.entries}} - - - -{{/for}} -
    {{:helper.link('', 'gear', {'set' : value.name})}}{{:value.name}}{{:value.value}}
    - - - - - - - -
    Electronic warfare:{{:helper.link('Enabled', null, {'electronic_warfare' : 1}, data.electronic_warfare ? 'selected' : null)}}{{:helper.link('Disabled',null, {'electronic_warfare' : 0}, data.electronic_warfare ? null : 'selected')}}
    + +{{:helper.syndicateMode()}} +

    Available Entries

    + +{{for data.entries}} + + + +{{/for}} +
    {{:helper.link('', 'gear', {'set' : value.name})}}{{:value.name}}{{:value.value}}
    + + + + + + + +
    Electronic warfare:{{:helper.link('Enabled', null, {'electronic_warfare' : 1}, data.electronic_warfare ? 'selected' : null)}}{{:helper.link('Disabled',null, {'electronic_warfare' : 0}, data.electronic_warfare ? null : 'selected')}}
    diff --git a/nano/templates/air_alarm.tmpl b/nano/templates/air_alarm.tmpl index 8d604e58e348..8e9f9a52f364 100644 --- a/nano/templates/air_alarm.tmpl +++ b/nano/templates/air_alarm.tmpl @@ -1,219 +1,219 @@ - -

    Air Status

    -{{if data.has_environment}} - {{for data.environment}} - {{:value.name}}: - {{if value.danger_level == 2}} - - {{else value.danger_level == 1}} - - {{else}} - - {{/if}} - {{:helper.fixed(value.value, 1)}} - {{:value.unit}}
    - {{/for}} - Local Status: {{if data.total_danger == 2}} - DANGER: Internals Required - {{else data.total_danger == 1}} - Caution - {{else}} - Optimal - {{/if}} -
    - Area Status: {{if data.atmos_alarm}}Atmosphere alert in area{{else data.fire_alarm}}Fire alarm in area{{else}}No alerts{{/if}} -{{else}} - Warning: Cannot obtain air sample for analysis. -{{/if}} -
    - - - - - - - - - - - - - -
    -
    -

    Remote Control

    -
    -

    Thermostat

    -
    -
    -
    -
    - {{:helper.link('Off', null, { 'rcon' : 1}, !data.rcon_access ? (data.rcon == 1 ? 'yellowButton' : 'disabled') : null, data.rcon == 1 ? 'selected' : null)}} - {{:helper.link('Auto', null, { 'rcon' : 2}, !data.rcon_access ? (data.rcon == 2 ? 'yellowButton' : 'disabled') : null, data.rcon == 2 ? 'selected' : null)}} - {{:helper.link('On', null, { 'rcon' : 3}, !data.rcon_access ? (data.rcon == 3 ? 'yellowButton' : 'disabled') : null, data.rcon == 3 ? 'selected' : null)}} -
    -
    -
    - {{:helper.link(data.target_temperature, null, { 'temperature' : 1})}} -
    -
    -{{if (data.locked && !data.remote_connection) || (data.remote_connection && ! data.remote_access)}} - {{if data.remote_connection}} - (Current remote control settings and alarm status restricts access.) - {{else}} - (Swipe ID card to unlock interface.) - {{/if}} -{{else}} - {{if data.screen != 1}} -
    {{:helper.link('Main Menu', null, { 'screen' : 1})}}
    - {{/if}} - {{if data.screen == 1}} -
    - {{if data.atmos_alarm}} - {{:helper.link('Reset - Area Atmospheric Alarm', null, { 'atmos_reset' : 1})}} - {{else}} - {{:helper.link('Activate - Area Atmospheric Alarm', null, { 'atmos_alarm' : 1})}} - {{/if}} -
    -
    -
    - {{:helper.link('Scrubbers Control', null, { 'screen' : 3})}} -
    -
    - {{:helper.link('Vents Control', null, { 'screen' : 2})}} -
    -
    - {{:helper.link('Set Environmental Mode', null, { 'screen' : 4})}} -
    -
    - {{:helper.link('Sensor Settings', null, { 'screen' : 5})}} -
    -
    - {{if data.mode==3}} - {{:helper.link('PANIC SIPHON ACTIVE - Turn siphoning off', null, { 'mode' : 1}, null, 'redButton')}} - {{else}} - {{:helper.link('ACTIVATE PANIC SIPHON IN AREA', null, { 'mode' : 3}, null, 'yellowButton')}} - {{/if}} - {{else data.screen == 2}} - {{for data.vents}} -
    - {{:value.long_name}}
    -
    -
    - Operating: -
    -
    - {{:helper.link(value.power ? 'On' : 'Off', null, { 'id_tag' : value.id_tag, 'command' : 'set_power', 'val' : value.power ? 0 : 1}, null, value.power ? null : 'redButton')}} -
    -
    -
    -
    - Operation Mode: -
    -
    - {{:value.direction == "siphon" ? 'Siphoning' : 'Pressurizing'}} -
    -
    -
    -
    - Pressure Checks: -
    -
    - {{:helper.link('External', null, { 'id_tag' : value.id_tag, 'command' : 'set_checks', 'val' : value.checks^1}, null, value.checks&1 ? 'selected' : null)}} - {{:helper.link('Internal', null, { 'id_tag' : value.id_tag, 'command' : 'set_checks', 'val' : value.checks^2}, null, value.checks&2 ? 'selected' : null)}} -
    -
    -
    -
    - External Pressure Bound: -
    -
    - {{:helper.link(helper.fixed(value.external,2), null, { 'id_tag' : value.id_tag, 'command' : 'set_external_pressure'})}} - {{:helper.link('Reset', null, { 'id_tag' : value.id_tag, 'command' : 'reset_external_pressure'})}} -
    -
    -
    - {{empty}} - No vents connected. - {{/for}} - {{else data.screen == 3}} - {{for data.scrubbers}} -
    - {{:value.long_name}}
    -
    -
    - Operating: -
    -
    - {{:helper.link(value.power ? 'On' : 'Off', null, { 'id_tag' : value.id_tag, 'command' : 'set_power', 'val' : value.power ? 0 : 1}, null, value.power ? null : 'redButton')}} -
    -
    -
    -
    - Operation Mode: -
    -
    - {{:helper.link('Scrub', null, { 'id_tag' : value.id_tag, 'command' : 'set_scrubbing', 'scrub_mode' : 'scrub'}, null, value.scrubbing == 'scrub' ? 'selected' : null)}} - {{:helper.link('Exchange', null, { 'id_tag' : value.id_tag, 'command' : 'set_scrubbing', 'scrub_mode' : 'exchange'}, null, value.scrubbing == 'exchange' ? 'selected' : null)}} - {{:helper.link('Siphon', null, { 'id_tag' : value.id_tag, 'command' : 'set_scrubbing', 'scrub_mode' : 'siphon'}, null, value.scrubbing == 'siphon' ? 'redButton' : null)}} -
    -
    -
    -
    - Filters: -
    -
    - {{for value.filters :filterValue:filterIndex}} - {{:helper.link(filterValue.name, null, { 'id_tag' : value.id_tag, 'command' : 'set_scrub_gas', 'gas_id' : filterValue.id, 'val' : filterValue.val ? 0 : 1}, null, filterValue.val ? 'selected' : null)}} - {{/for}} -
    -
    -
    - {{empty}} - No scrubbers connected. - {{/for}} - {{else data.screen == 4}} -

    Environmental Modes

    - {{for data.modes}} -
    - {{:helper.link(value.name, null, { 'mode' : value.mode }, null, value.selected ? (value.danger ? 'redButton' : 'selected') : null)}} -
    - {{/for}} - {{else data.screen == 5}} -

    Alarm Threshold

    - Partial pressure for gases. - - - - - {{for data.thresholds}} - - - {{for value.settings :settingsValue:settingsIndex}} - - {{/for}} - - {{/for}} -
    min2min1max1max2
    {{:value.name}} - {{:helper.link(settingsValue.selected >= 0 ? helper.fixed(settingsValue.selected, 2) : "Off", null, { 'command' : 'set_threshold', 'env' : settingsValue.env, 'var' : settingsValue.val })}} -
    - {{/if}} -{{/if}} + +

    Air Status

    +{{if data.has_environment}} + {{for data.environment}} + {{:value.name}}: + {{if value.danger_level == 2}} + + {{else value.danger_level == 1}} + + {{else}} + + {{/if}} + {{:helper.fixed(value.value, 1)}} + {{:value.unit}}
    + {{/for}} + Local Status: {{if data.total_danger == 2}} + DANGER: Internals Required + {{else data.total_danger == 1}} + Caution + {{else}} + Optimal + {{/if}} +
    + Area Status: {{if data.atmos_alarm}}Atmosphere alert in area{{else data.fire_alarm}}Fire alarm in area{{else}}No alerts{{/if}} +{{else}} + Warning: Cannot obtain air sample for analysis. +{{/if}} +
    +
    + + + + + + + + + + + + +
    +
    +

    Remote Control

    +
    +

    Thermostat

    +
    +
    +
    +
    + {{:helper.link('Off', null, { 'rcon' : 1}, !data.rcon_access ? (data.rcon == 1 ? 'yellowButton' : 'disabled') : null, data.rcon == 1 ? 'selected' : null)}} + {{:helper.link('Auto', null, { 'rcon' : 2}, !data.rcon_access ? (data.rcon == 2 ? 'yellowButton' : 'disabled') : null, data.rcon == 2 ? 'selected' : null)}} + {{:helper.link('On', null, { 'rcon' : 3}, !data.rcon_access ? (data.rcon == 3 ? 'yellowButton' : 'disabled') : null, data.rcon == 3 ? 'selected' : null)}} +
    +
    +
    + {{:helper.link(data.target_temperature, null, { 'temperature' : 1})}} +
    +
    +{{if (data.locked && !data.remote_connection) || (data.remote_connection && ! data.remote_access)}} + {{if data.remote_connection}} + (Current remote control settings and alarm status restricts access.) + {{else}} + (Swipe ID card to unlock interface.) + {{/if}} +{{else}} + {{if data.screen != 1}} +
    {{:helper.link('Main Menu', null, { 'screen' : 1})}}
    + {{/if}} + {{if data.screen == 1}} +
    + {{if data.atmos_alarm}} + {{:helper.link('Reset - Area Atmospheric Alarm', null, { 'atmos_reset' : 1})}} + {{else}} + {{:helper.link('Activate - Area Atmospheric Alarm', null, { 'atmos_alarm' : 1})}} + {{/if}} +
    +
    +
    + {{:helper.link('Scrubbers Control', null, { 'screen' : 3})}} +
    +
    + {{:helper.link('Vents Control', null, { 'screen' : 2})}} +
    +
    + {{:helper.link('Set Environmental Mode', null, { 'screen' : 4})}} +
    +
    + {{:helper.link('Sensor Settings', null, { 'screen' : 5})}} +
    +
    + {{if data.mode==3}} + {{:helper.link('PANIC SIPHON ACTIVE - Turn siphoning off', null, { 'mode' : 1}, null, 'redButton')}} + {{else}} + {{:helper.link('ACTIVATE PANIC SIPHON IN AREA', null, { 'mode' : 3}, null, 'yellowButton')}} + {{/if}} + {{else data.screen == 2}} + {{for data.vents}} +
    + {{:value.long_name}}
    +
    +
    + Operating: +
    +
    + {{:helper.link(value.power ? 'On' : 'Off', null, { 'id_tag' : value.id_tag, 'command' : 'set_power', 'val' : value.power ? 0 : 1}, null, value.power ? null : 'redButton')}} +
    +
    +
    +
    + Operation Mode: +
    +
    + {{:value.direction == "siphon" ? 'Siphoning' : 'Pressurizing'}} +
    +
    +
    +
    + Pressure Checks: +
    +
    + {{:helper.link('External', null, { 'id_tag' : value.id_tag, 'command' : 'set_checks', 'val' : value.checks^1}, null, value.checks&1 ? 'selected' : null)}} + {{:helper.link('Internal', null, { 'id_tag' : value.id_tag, 'command' : 'set_checks', 'val' : value.checks^2}, null, value.checks&2 ? 'selected' : null)}} +
    +
    +
    +
    + External Pressure Bound: +
    +
    + {{:helper.link(helper.fixed(value.external,2), null, { 'id_tag' : value.id_tag, 'command' : 'set_external_pressure'})}} + {{:helper.link('Reset', null, { 'id_tag' : value.id_tag, 'command' : 'reset_external_pressure'})}} +
    +
    +
    + {{empty}} + No vents connected. + {{/for}} + {{else data.screen == 3}} + {{for data.scrubbers}} +
    + {{:value.long_name}}
    +
    +
    + Operating: +
    +
    + {{:helper.link(value.power ? 'On' : 'Off', null, { 'id_tag' : value.id_tag, 'command' : 'set_power', 'val' : value.power ? 0 : 1}, null, value.power ? null : 'redButton')}} +
    +
    +
    +
    + Operation Mode: +
    +
    + {{:helper.link('Scrub', null, { 'id_tag' : value.id_tag, 'command' : 'set_scrubbing', 'scrub_mode' : 'scrub'}, null, value.scrubbing == 'scrub' ? 'selected' : null)}} + {{:helper.link('Exchange', null, { 'id_tag' : value.id_tag, 'command' : 'set_scrubbing', 'scrub_mode' : 'exchange'}, null, value.scrubbing == 'exchange' ? 'selected' : null)}} + {{:helper.link('Siphon', null, { 'id_tag' : value.id_tag, 'command' : 'set_scrubbing', 'scrub_mode' : 'siphon'}, null, value.scrubbing == 'siphon' ? 'redButton' : null)}} +
    +
    +
    +
    + Filters: +
    +
    + {{for value.filters :filterValue:filterIndex}} + {{:helper.link(filterValue.name, null, { 'id_tag' : value.id_tag, 'command' : 'set_scrub_gas', 'gas_id' : filterValue.id, 'val' : filterValue.val ? 0 : 1}, null, filterValue.val ? 'selected' : null)}} + {{/for}} +
    +
    +
    + {{empty}} + No scrubbers connected. + {{/for}} + {{else data.screen == 4}} +

    Environmental Modes

    + {{for data.modes}} +
    + {{:helper.link(value.name, null, { 'mode' : value.mode }, null, value.selected ? (value.danger ? 'redButton' : 'selected') : null)}} +
    + {{/for}} + {{else data.screen == 5}} +

    Alarm Threshold

    + Partial pressure for gases. + + + + + {{for data.thresholds}} + + + {{for value.settings :settingsValue:settingsIndex}} + + {{/for}} + + {{/for}} +
    min2min1max1max2
    {{:value.name}} + {{:helper.link(settingsValue.selected >= 0 ? helper.fixed(settingsValue.selected, 2) : "Off", null, { 'command' : 'set_threshold', 'env' : settingsValue.env, 'var' : settingsValue.val })}} +
    + {{/if}} +{{/if}} \ No newline at end of file diff --git a/nano/templates/alarm_monitor.tmpl b/nano/templates/alarm_monitor.tmpl index f39f248f0d99..4b5459ab48c6 100644 --- a/nano/templates/alarm_monitor.tmpl +++ b/nano/templates/alarm_monitor.tmpl @@ -1,37 +1,37 @@ - -{{for data.categories}} -

    {{:value.category}}

    - {{for value.alarms :alarmValue:alarmIndex}} - {{if alarmValue.origin_lost}} - {{:alarmValue.name}} Alarm Origin Lost
    - {{else}} - {{:alarmValue.name}}
    - {{/if}} - {{if alarmValue.has_cameras || alarmValue.lost_sources != ""}} -
    - {{if alarmValue.has_cameras}} -
    - {{for alarmValue.cameras :cameraValue:cameraIndex}} - {{if cameraValue.deact}} - {{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}} - {{else}} - {{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}} - {{/if}} - {{/for}} -
    - {{/if}} - {{if alarmValue.lost_sources != ""}} -
    -

    Lost Alarm Sources: {{:alarmValue.lost_sources}}

    -
    - {{/if}} -
    - {{/if}} - {{empty}} - --All Systems Nominal - {{/for}} -{{/for}} + +{{for data.categories}} +

    {{:value.category}}

    + {{for value.alarms :alarmValue:alarmIndex}} + {{if alarmValue.origin_lost}} + {{:alarmValue.name}} Alarm Origin Lost
    + {{else}} + {{:alarmValue.name}}
    + {{/if}} + {{if alarmValue.has_cameras || alarmValue.lost_sources != ""}} +
    + {{if alarmValue.has_cameras}} +
    + {{for alarmValue.cameras :cameraValue:cameraIndex}} + {{if cameraValue.deact}} + {{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}} + {{else}} + {{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}} + {{/if}} + {{/for}} +
    + {{/if}} + {{if alarmValue.lost_sources != ""}} +
    +

    Lost Alarm Sources: {{:alarmValue.lost_sources}}

    +
    + {{/if}} +
    + {{/if}} + {{empty}} + --All Systems Nominal + {{/for}} +{{/for}} diff --git a/nano/templates/appearance_changer.tmpl b/nano/templates/appearance_changer.tmpl index 00a1b62d1b5c..6d390e787157 100644 --- a/nano/templates/appearance_changer.tmpl +++ b/nano/templates/appearance_changer.tmpl @@ -1,89 +1,89 @@ -{{if data.change_race}} -
    -
    - Species: -
    -
    - {{for data.species}} - {{:helper.link(value.name, null, { 'race' : value.uid}, null, data.current_species_uid == value.uid ? 'selected' : null)}} - {{/for}} -
    -
    -{{/if}} - -{{if data.change_gender}} -
    -
    - Pronouns: -
    -
    - {{for data.genders}} - {{:helper.link(value.gender_name, null, { 'gender' : value.gender_key}, null, data.gender == value.gender_key ? 'selected' : null)}} - {{/for}} -
    -
    -{{/if}} - -{{if data.change_bodytype}} -
    -
    - Bodytype: -
    -
    - {{for data.bodytypes}} - {{:helper.link(value, null, { 'bodytype' : value}, null, data.bodytype == value ? 'selected' : null)}} - {{/for}} -
    -
    -{{/if}} - -{{if data.change_eye_color || data.change_skin_tone || data.change_skin_color || data.change_hair_color || data.change_facial_hair_color}} -
    -
    - Colors: -
    -
    - {{if data.change_eye_color}} - {{:helper.link('Change eye color', null, { 'eye_color' : 1})}} - {{/if}} - {{if data.change_skin_tone}} - {{:helper.link('Change skin tone', null, { 'skin_tone' : 1})}} - {{/if}} - {{if data.change_skin_color}} - {{:helper.link('Change skin color', null, { 'skin_color' : 1})}} - {{/if}} - {{if data.change_hair_color}} - {{:helper.link('Change hair color', null, { 'hair_color' : 1})}} - {{/if}} - {{if data.change_facial_hair_color}} - {{:helper.link('Change facial hair color', null, { 'facial_hair_color' : 1})}} - {{/if}} -
    -
    -{{/if}} - -{{if data.change_hair}} -
    -
    - Hair styles: -
    -
    - {{for data.hair_styles}} - {{:helper.link(value.hairstyle, null, { 'hair' : value.ref}, null, data.hair_style == value.hairstyle ? 'selected' : null)}} - {{/for}} -
    -
    -{{/if}} - -{{if data.change_facial_hair}} -
    -
    - Facial hair styles: -
    -
    - {{for data.facial_hair_styles}} - {{:helper.link(value.facialhairstyle, null, { 'facial_hair' : value.ref}, null, data.facial_hair_style == value.facialhairstyle ? 'selected' : null)}} - {{/for}} -
    -
    -{{/if}} +{{if data.change_race}} +
    +
    + Species: +
    +
    + {{for data.species}} + {{:helper.link(value.name, null, { 'race' : value.uid}, null, data.current_species_uid == value.uid ? 'selected' : null)}} + {{/for}} +
    +
    +{{/if}} + +{{if data.change_gender}} +
    +
    + Pronouns: +
    +
    + {{for data.genders}} + {{:helper.link(value.gender_name, null, { 'gender' : value.gender_key}, null, data.gender == value.gender_key ? 'selected' : null)}} + {{/for}} +
    +
    +{{/if}} + +{{if data.change_bodytype}} +
    +
    + Bodytype: +
    +
    + {{for data.bodytypes}} + {{:helper.link(value, null, { 'bodytype' : value}, null, data.bodytype == value ? 'selected' : null)}} + {{/for}} +
    +
    +{{/if}} + +{{if data.change_eye_color || data.change_skin_tone || data.change_skin_color || data.change_hair_color || data.change_facial_hair_color}} +
    +
    + Colors: +
    +
    + {{if data.change_eye_color}} + {{:helper.link('Change eye color', null, { 'eye_color' : 1})}} + {{/if}} + {{if data.change_skin_tone}} + {{:helper.link('Change skin tone', null, { 'skin_tone' : 1})}} + {{/if}} + {{if data.change_skin_color}} + {{:helper.link('Change skin color', null, { 'skin_color' : 1})}} + {{/if}} + {{if data.change_hair_color}} + {{:helper.link('Change hair color', null, { 'hair_color' : 1})}} + {{/if}} + {{if data.change_facial_hair_color}} + {{:helper.link('Change facial hair color', null, { 'facial_hair_color' : 1})}} + {{/if}} +
    +
    +{{/if}} + +{{if data.change_hair}} +
    +
    + Hair styles: +
    +
    + {{for data.hair_styles}} + {{:helper.link(value.hairstyle, null, { 'hair' : value.ref}, null, data.hair_style == value.hairstyle ? 'selected' : null)}} + {{/for}} +
    +
    +{{/if}} + +{{if data.change_facial_hair}} +
    +
    + Facial hair styles: +
    +
    + {{for data.facial_hair_styles}} + {{:helper.link(value.facialhairstyle, null, { 'facial_hair' : value.ref}, null, data.facial_hair_style == value.facialhairstyle ? 'selected' : null)}} + {{/for}} +
    +
    +{{/if}} diff --git a/nano/templates/atmos_alert.tmpl b/nano/templates/atmos_alert.tmpl index 92946e8d312d..a36b1bb9bafe 100644 --- a/nano/templates/atmos_alert.tmpl +++ b/nano/templates/atmos_alert.tmpl @@ -1,18 +1,18 @@ -

    Priority Alerts

    -{{for data.priority_alarms}} -
    - {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} -
    -{{empty}} - No priority alerts detected. -{{/for}} - -

    Minor Alerts

    -{{for data.minor_alarms}} -
    - {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} -
    -{{empty}} - No minor alerts detected. -{{/for}} - +

    Priority Alerts

    +{{for data.priority_alarms}} +
    + {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} +
    +{{empty}} + No priority alerts detected. +{{/for}} + +

    Minor Alerts

    +{{for data.minor_alarms}} +
    + {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} +
    +{{empty}} + No minor alerts detected. +{{/for}} + diff --git a/nano/templates/atmos_control.tmpl b/nano/templates/atmos_control.tmpl index e7f3c94d80f2..cca5724ad935 100644 --- a/nano/templates/atmos_control.tmpl +++ b/nano/templates/atmos_control.tmpl @@ -1,26 +1,26 @@ -
    - {{:helper.link('Refresh', 'refresh', {'refresh' : 1})}} -
    -
    -
    - Filter term: -
    -
    - {{:helper.link(data.filter, null, {'filter_set' : 1})}} -
    -
    -
    - {{for data.alarmsAlert}} - {{:helper.link(value.name, null, {'alarm' : value.ref}, null, 'redButton')}} - {{/for}} -
    -
    - {{for data.alarmsDanger}} - {{:helper.link(value.name, null, {'alarm' : value.ref}, null, 'yellowButton')}} - {{/for}} -
    -
    - {{for data.alarms}} - {{:helper.link(value.name, null, {'alarm' : value.ref}, null, null)}} - {{/for}} +
    + {{:helper.link('Refresh', 'refresh', {'refresh' : 1})}} +
    +
    +
    + Filter term: +
    +
    + {{:helper.link(data.filter, null, {'filter_set' : 1})}} +
    +
    +
    + {{for data.alarmsAlert}} + {{:helper.link(value.name, null, {'alarm' : value.ref}, null, 'redButton')}} + {{/for}} +
    +
    + {{for data.alarmsDanger}} + {{:helper.link(value.name, null, {'alarm' : value.ref}, null, 'yellowButton')}} + {{/for}} +
    +
    + {{for data.alarms}} + {{:helper.link(value.name, null, {'alarm' : value.ref}, null, null)}} + {{/for}}
    \ No newline at end of file diff --git a/nano/templates/door_control.tmpl b/nano/templates/door_control.tmpl index a1b0e35a2646..4197497ce8fb 100644 --- a/nano/templates/door_control.tmpl +++ b/nano/templates/door_control.tmpl @@ -1,65 +1,65 @@ - - -
    -
    - Main power is - {{if data.main_power_loss == 0}} - online - {{else data.main_power_loss == -1}} - offline - {{else}} - offline for {{:data.main_power_loss}} second{{:data.main_power_loss == 1 ? '' : 's'}} - {{/if}}. -
    -
    - {{:helper.link(data.main_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'main_power'}, data.main_power_loss == 0 ? null : 'disabled', data.main_power_loss == 0 ? 'redButton' : null)}} -
    -
    - -
    -
    - Backup power is - {{if data.backup_power_loss == 0}} - online - {{else data.backup_power_loss == -1}} - offline - {{else}} - offline for {{:data.backup_power_loss}} second{{:data.backup_power_loss == 1 ? '' : 's'}} - {{/if}}. -
    -
    - {{:helper.link(data.backup_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'backup_power'}, data.backup_power_loss == 0 ? null : 'disabled', data.backup_power_loss == 0 ? 'redButton' : null)}} -
    -
    - -
    -
    - Electrified Status: -
    -
    - {{:helper.link('Offline' , null, {'command' : 'electrify_permanently', 'activate' : "0" }, data.electrified == 0 ? 'selected' : null)}} - {{:helper.link(data.electrified <= 0 ? 'Temporary (30s)' : 'Temporary (' + data.electrified +'s)', null, {'command' : 'electrify_temporary', 'activate' : "1"}, data.electrified > 0 ? 'redButton' : null)}} - {{:helper.link('Permanent', null, {'command' : 'electrify_permanently', 'activate' : "1"}, data.electrified == -1 ? 'redButton' : null)}} -
    -
    - -
    - -
    - {{for data.commands}} - - - - - - {{/for}} -
    {{:value.name}}:{{:helper.link(value.enabled, null, {'command' : value.command, 'activate' : value.act ? 1 : 0}, value.active ? 'selected' : null)}}{{:helper.link(value.disabled,null, {'command' : value.command, 'activate' : value.act ? 0 : 1}, !value.active ? (value.danger ? 'redButton' : 'selected') : null)}}
    + + +
    +
    + Main power is + {{if data.main_power_loss == 0}} + online + {{else data.main_power_loss == -1}} + offline + {{else}} + offline for {{:data.main_power_loss}} second{{:data.main_power_loss == 1 ? '' : 's'}} + {{/if}}. +
    +
    + {{:helper.link(data.main_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'main_power'}, data.main_power_loss == 0 ? null : 'disabled', data.main_power_loss == 0 ? 'redButton' : null)}} +
    +
    + +
    +
    + Backup power is + {{if data.backup_power_loss == 0}} + online + {{else data.backup_power_loss == -1}} + offline + {{else}} + offline for {{:data.backup_power_loss}} second{{:data.backup_power_loss == 1 ? '' : 's'}} + {{/if}}. +
    +
    + {{:helper.link(data.backup_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'backup_power'}, data.backup_power_loss == 0 ? null : 'disabled', data.backup_power_loss == 0 ? 'redButton' : null)}} +
    +
    + +
    +
    + Electrified Status: +
    +
    + {{:helper.link('Offline' , null, {'command' : 'electrify_permanently', 'activate' : "0" }, data.electrified == 0 ? 'selected' : null)}} + {{:helper.link(data.electrified <= 0 ? 'Temporary (30s)' : 'Temporary (' + data.electrified +'s)', null, {'command' : 'electrify_temporary', 'activate' : "1"}, data.electrified > 0 ? 'redButton' : null)}} + {{:helper.link('Permanent', null, {'command' : 'electrify_permanently', 'activate' : "1"}, data.electrified == -1 ? 'redButton' : null)}} +
    +
    + +
    + + + {{for data.commands}} + + + + + + {{/for}} +
    {{:value.name}}:{{:helper.link(value.enabled, null, {'command' : value.command, 'activate' : value.act ? 1 : 0}, value.active ? 'selected' : null)}}{{:helper.link(value.disabled,null, {'command' : value.command, 'activate' : value.act ? 0 : 1}, !value.active ? (value.danger ? 'redButton' : 'selected') : null)}}
    diff --git a/nano/templates/law_manager.tmpl b/nano/templates/law_manager.tmpl index 1e0012bae7c1..6ba59e041797 100644 --- a/nano/templates/law_manager.tmpl +++ b/nano/templates/law_manager.tmpl @@ -1,253 +1,253 @@ - - -{{if data.isSlaved}} -
    Law synced to {{:data.isSlaved}}.
    -{{/if}} - -
    -
    - {{:helper.link('Law Management', null, {'set_view' : 0}, data.view == 0 ? 'selected' : null)}} - {{:helper.link('Law Sets', null, {'set_view' : 1}, data.view == 1 ? 'selected' : null)}} -
    -
    - -{{if data.view == 0}} - {{if data.has_ion_laws}} - - - {{if data.isMalf}} - - - {{/if}} - - -
    - {{:data.ion_law_nr}} Laws: -
    - {{for data.ion_laws}} - - - - - {{if data.isMalf}} - - - {{/if}} - - {{/for}} -
    IndexLawStateEditDelete
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state == 0 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref})}}{{:helper.link('Delete', null, {'delete_law': value.ref}, null, 'redButton')}}
    - {{/if}} - - {{if data.has_inherent_laws || data.has_zeroth_laws}} - - - {{if data.isMalf}} - - - {{/if}} - - -
    - Inherent Laws: -
    - - {{for data.zeroth_laws}} - - - - - {{if data.isMalf}} - - - {{/if}} - - {{/for}} - - {{for data.inherent_laws}} - - - - - {{if data.isMalf}} - - - {{/if}} - - {{/for}} -
    IndexLawStateEditDelete
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state != 1 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref}, data.isAdmin ? null : 'disabled')}}{{:helper.link('Delete', null, {'delete_law': value.ref}, data.isAdmin ? null : 'disabled', data.isAdmin ? 'redButton' : null)}}
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state == 0 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref})}}{{:helper.link('Delete', null, {'delete_law': value.ref}, null, 'redButton')}}
    - {{/if}} - - {{if data.has_supplied_laws}} - - - {{if data.isMalf}} - - - {{/if}} - - -
    - Supplied Laws: -
    - {{for data.supplied_laws}} - - - - - {{if data.isMalf}} - - - {{/if}} - - {{/for}} -
    IndexLawStateEditDelete
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state == 0 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref})}}{{:helper.link('Delete', null, {'delete_law': value.ref}, null, 'redButton')}}
    - {{/if}} - -
    -
    - Statement Channel: -
    -
    - {{for data.channels}} - {{:helper.link(value.channel, null, {'law_channel' : value.channel}, value.channel == data.channel ? 'selected' : null)}} - {{/for}} -
    -
    - -
    -
    - State Laws: -
    -
    - {{:helper.link('State Laws', null, {'state_laws' : 1})}} -
    -
    - - {{if data.isMalf}} -
    -
    - Add Laws: -
    -
    - - - {{if data.isAdmin && !data.has_zeroth_laws}} - - {{/if}} - - - -
    TypeLawIndexEditAdd
    Zero{{:data.zeroth_law}}N/A{{:helper.link('Edit', null, {'change_zeroth_law' : 1})}}{{:helper.link('Add', null, {'add_zeroth_law' : 1})}}
    Ion{{:data.ion_law}}N/A{{:helper.link('Edit', null, {'change_ion_law' : 1})}}{{:helper.link('Add', null, {'add_ion_law' : 1})}}
    Inherent{{:data.inherent_law}}N/A{{:helper.link('Edit', null, {'change_inherent_law' : 1})}}{{:helper.link('Add', null, {'add_inherent_law' : 1})}}
    Supplied{{:data.supplied_law}}{{:helper.link(data.supplied_law_position, null, {'change_supplied_law_position' : 1})}}{{:helper.link('Edit', null, {'change_supplied_law' : 1})}}{{:helper.link('Add', null, {'add_supplied_law' : 1})}}
    -
    -
    - {{/if}} - - {{if data.isAI}} -
    -
    - Law Notification: -
    -
    - {{:helper.link('Notify', null, {'notify_laws' : 1})}} -
    -
    - {{/if}} -{{else data.view == 1}} -
    Remember: Stating laws other than those currently loaded may be grounds for decommissioning.
    - - {{for data.law_sets}} -
    -
    -

    {{:value.name}}

    {{:value.header}} -
    - - {{if value.laws.has_ion_laws}} - - - {{for value.laws.ion_laws :lawValue:lawindex}} - - - - - {{/for}} -
    IndexLaw
    {{:lawValue.index}}.{{:lawValue.law}}
    - {{/if}} - - {{if value.laws.has_zeroth_laws || value.laws.has_inherent_laws}} - - - {{for value.laws.zeroth_laws :lawValue:lawindex}} - - - - - {{/for}} - {{for value.laws.inherent_laws :lawValue:lawindex}} - - - - - {{/for}} -
    IndexLaw
    {{:lawValue.index}}.{{:lawValue.law}}
    {{:lawValue.index}}.{{:lawValue.law}}
    - {{/if}} - - {{if value.laws.has_supplied_laws}} - - - {{for value.laws.supplied_laws :lawValue:lawindex}} - - - - - {{/for}} -
    IndexLaw
    {{:lawValue.index}}.{{:lawValue.law}}
    - {{/if}} - -
    -
    - {{:helper.link('Load Laws', null, {'transfer_laws' : value.ref}, data.isMalf ? null : 'disabled')}}{{:helper.link('State Laws', null, {'state_law_set' : value.ref})}} -
    -
    - {{/for}} + + +{{if data.isSlaved}} +
    Law synced to {{:data.isSlaved}}.
    +{{/if}} + +
    +
    + {{:helper.link('Law Management', null, {'set_view' : 0}, data.view == 0 ? 'selected' : null)}} + {{:helper.link('Law Sets', null, {'set_view' : 1}, data.view == 1 ? 'selected' : null)}} +
    +
    + +{{if data.view == 0}} + {{if data.has_ion_laws}} + + + {{if data.isMalf}} + + + {{/if}} + + +
    + {{:data.ion_law_nr}} Laws: +
    + {{for data.ion_laws}} + + + + + {{if data.isMalf}} + + + {{/if}} + + {{/for}} +
    IndexLawStateEditDelete
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state == 0 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref})}}{{:helper.link('Delete', null, {'delete_law': value.ref}, null, 'redButton')}}
    + {{/if}} + + {{if data.has_inherent_laws || data.has_zeroth_laws}} + + + {{if data.isMalf}} + + + {{/if}} + + +
    + Inherent Laws: +
    + + {{for data.zeroth_laws}} + + + + + {{if data.isMalf}} + + + {{/if}} + + {{/for}} + + {{for data.inherent_laws}} + + + + + {{if data.isMalf}} + + + {{/if}} + + {{/for}} +
    IndexLawStateEditDelete
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state != 1 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref}, data.isAdmin ? null : 'disabled')}}{{:helper.link('Delete', null, {'delete_law': value.ref}, data.isAdmin ? null : 'disabled', data.isAdmin ? 'redButton' : null)}}
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state == 0 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref})}}{{:helper.link('Delete', null, {'delete_law': value.ref}, null, 'redButton')}}
    + {{/if}} + + {{if data.has_supplied_laws}} + + + {{if data.isMalf}} + + + {{/if}} + + +
    + Supplied Laws: +
    + {{for data.supplied_laws}} + + + + + {{if data.isMalf}} + + + {{/if}} + + {{/for}} +
    IndexLawStateEditDelete
    {{:value.index}}.{{:value.law}}{{:helper.link('Yes', null, {'ref': value.ref, 'state_law' : 1}, value.state == 1 ? 'selected' : null)}}{{:helper.link('No', null, {'ref': value.ref, 'state_law' : 0}, value.state == 0 ? 'selected' : null)}}{{:helper.link('Edit', null, {'edit_law': value.ref})}}{{:helper.link('Delete', null, {'delete_law': value.ref}, null, 'redButton')}}
    + {{/if}} + +
    +
    + Statement Channel: +
    +
    + {{for data.channels}} + {{:helper.link(value.channel, null, {'law_channel' : value.channel}, value.channel == data.channel ? 'selected' : null)}} + {{/for}} +
    +
    + +
    +
    + State Laws: +
    +
    + {{:helper.link('State Laws', null, {'state_laws' : 1})}} +
    +
    + + {{if data.isMalf}} +
    +
    + Add Laws: +
    +
    + + + {{if data.isAdmin && !data.has_zeroth_laws}} + + {{/if}} + + + +
    TypeLawIndexEditAdd
    Zero{{:data.zeroth_law}}N/A{{:helper.link('Edit', null, {'change_zeroth_law' : 1})}}{{:helper.link('Add', null, {'add_zeroth_law' : 1})}}
    Ion{{:data.ion_law}}N/A{{:helper.link('Edit', null, {'change_ion_law' : 1})}}{{:helper.link('Add', null, {'add_ion_law' : 1})}}
    Inherent{{:data.inherent_law}}N/A{{:helper.link('Edit', null, {'change_inherent_law' : 1})}}{{:helper.link('Add', null, {'add_inherent_law' : 1})}}
    Supplied{{:data.supplied_law}}{{:helper.link(data.supplied_law_position, null, {'change_supplied_law_position' : 1})}}{{:helper.link('Edit', null, {'change_supplied_law' : 1})}}{{:helper.link('Add', null, {'add_supplied_law' : 1})}}
    +
    +
    + {{/if}} + + {{if data.isAI}} +
    +
    + Law Notification: +
    +
    + {{:helper.link('Notify', null, {'notify_laws' : 1})}} +
    +
    + {{/if}} +{{else data.view == 1}} +
    Remember: Stating laws other than those currently loaded may be grounds for decommissioning.
    + + {{for data.law_sets}} +
    +
    +

    {{:value.name}}

    {{:value.header}} +
    + + {{if value.laws.has_ion_laws}} + + + {{for value.laws.ion_laws :lawValue:lawindex}} + + + + + {{/for}} +
    IndexLaw
    {{:lawValue.index}}.{{:lawValue.law}}
    + {{/if}} + + {{if value.laws.has_zeroth_laws || value.laws.has_inherent_laws}} + + + {{for value.laws.zeroth_laws :lawValue:lawindex}} + + + + + {{/for}} + {{for value.laws.inherent_laws :lawValue:lawindex}} + + + + + {{/for}} +
    IndexLaw
    {{:lawValue.index}}.{{:lawValue.law}}
    {{:lawValue.index}}.{{:lawValue.law}}
    + {{/if}} + + {{if value.laws.has_supplied_laws}} + + + {{for value.laws.supplied_laws :lawValue:lawindex}} + + + + + {{/for}} +
    IndexLaw
    {{:lawValue.index}}.{{:lawValue.law}}
    + {{/if}} + +
    +
    + {{:helper.link('Load Laws', null, {'transfer_laws' : value.ref}, data.isMalf ? null : 'disabled')}}{{:helper.link('State Laws', null, {'state_law_set' : value.ref})}} +
    +
    + {{/for}} {{/if}} \ No newline at end of file diff --git a/nano/templates/nuclear_bomb.tmpl b/nano/templates/nuclear_bomb.tmpl index c22984160022..68fc9d42a64f 100644 --- a/nano/templates/nuclear_bomb.tmpl +++ b/nano/templates/nuclear_bomb.tmpl @@ -1,72 +1,72 @@ - -
    - Authorization Disk: {{if data.auth}}{{:helper.link('++++++++++', 'eject', {'auth' : 1})}} {{else}} {{:helper.link('----------', 'disk', {'auth' : 1})}}{{/if}} -
    -
    -
    -
    Status: {{:data.authstatus}} - {{:data.safe}}
    -
    Timer: {{:data.time}}
    -
    -
    -
    - {{if data.auth && data.yescode}} -
    - Timer: {{:helper.link('On', 'play', {'timer' : 1}, data.timer ? 'redButton' : '')}}{{:helper.link('Off', 'stop', {'timer' : 0}, !data.timer ? 'selected' : '')}} -
    -
    - Time: {{:helper.link('--', '', {'time' : -10}, data.time <= 120 ? 'disabled' : '')}}{{:helper.link('-', '', {'time' : -1}, data.time <= 120 ? 'disabled' : '')}} {{:data.time}} {{:helper.link('+', '', {'time' : 1})}}{{:helper.link('++', '', {'time' : 10})}} -
    - {{else}} -
    - Timer: {{:helper.link('On', 'play', null, 'disabled')}}{{:helper.link('Off', 'pause', null, 'disabled')}} -
    -
    - Time: {{:helper.link('-', '', null, 'disabled')}}{{:helper.link('-', '', null, 'disabled')}} {{:data.time}} {{:helper.link('+', '', null, 'disabled')}}{{:helper.link('++', '', null, 'disabled')}} -
    - {{/if}} -
    -
    - {{if data.auth && data.yescode}} -
    - Safety: {{:helper.link('Engaged', 'info', {'safety' : 1}, data.safety ? 'selected' : '')}}{{:helper.link('Disengaged', 'alert', {'safety' : 0}, data.safety ? '' : 'redButton')}} -
    - {{if data.moveable_anchor}} -
    - Anchor: {{:helper.link('Engaged', 'locked', {'anchor' : 1}, data.anchored ? 'selected' : '')}}{{:helper.link('Disengaged', 'unlocked', {'anchor' : 0}, data.anchored ? '' : 'selected')}} -
    - {{/if}} - {{else}} -
    - Safety: {{:helper.link('Engaged', 'info', null, 'disabled')}}{{:helper.link('Disengaged', 'alert', null, 'disabled')}} -
    - {{if data.moveable_anchor}} -
    - Anchor: {{:helper.link('Engaged', 'locked', null, 'disabled')}}{{:helper.link('Disengaged', 'unlocked', null, 'disabled')}} -
    - {{/if}} - {{/if}} -
    -
    -
    -
    -
    - >{{if data.message}} {{:data.message}}{{/if}} -
    -
    -
    - {{:helper.link('1', '', {'type' : 1})}}{{:helper.link('2', '', {'type' : 2})}}{{:helper.link('3', '', {'type' : 3})}} -
    -
    - {{:helper.link('4', '', {'type' : 4})}}{{:helper.link('5', '', {'type' : 5})}}{{:helper.link('6', '', {'type' : 6})}} -
    -
    - {{:helper.link('7', '', {'type' : 7})}}{{:helper.link('8', '', {'type' : 8})}}{{:helper.link('9', '', {'type' : 9})}} -
    -
    - {{:helper.link('R', '', {'type' : 'R'})}}{{:helper.link('0', '', {'type' : 0})}}{{:helper.link('E', '', {'type' : 'E'})}} -
    -
    -
    + +
    + Authorization Disk: {{if data.auth}}{{:helper.link('++++++++++', 'eject', {'auth' : 1})}} {{else}} {{:helper.link('----------', 'disk', {'auth' : 1})}}{{/if}} +
    +
    +
    +
    Status: {{:data.authstatus}} - {{:data.safe}}
    +
    Timer: {{:data.time}}
    +
    +
    +
    + {{if data.auth && data.yescode}} +
    + Timer: {{:helper.link('On', 'play', {'timer' : 1}, data.timer ? 'redButton' : '')}}{{:helper.link('Off', 'stop', {'timer' : 0}, !data.timer ? 'selected' : '')}} +
    +
    + Time: {{:helper.link('--', '', {'time' : -10}, data.time <= 120 ? 'disabled' : '')}}{{:helper.link('-', '', {'time' : -1}, data.time <= 120 ? 'disabled' : '')}} {{:data.time}} {{:helper.link('+', '', {'time' : 1})}}{{:helper.link('++', '', {'time' : 10})}} +
    + {{else}} +
    + Timer: {{:helper.link('On', 'play', null, 'disabled')}}{{:helper.link('Off', 'pause', null, 'disabled')}} +
    +
    + Time: {{:helper.link('-', '', null, 'disabled')}}{{:helper.link('-', '', null, 'disabled')}} {{:data.time}} {{:helper.link('+', '', null, 'disabled')}}{{:helper.link('++', '', null, 'disabled')}} +
    + {{/if}} +
    +
    + {{if data.auth && data.yescode}} +
    + Safety: {{:helper.link('Engaged', 'info', {'safety' : 1}, data.safety ? 'selected' : '')}}{{:helper.link('Disengaged', 'alert', {'safety' : 0}, data.safety ? '' : 'redButton')}} +
    + {{if data.moveable_anchor}} +
    + Anchor: {{:helper.link('Engaged', 'locked', {'anchor' : 1}, data.anchored ? 'selected' : '')}}{{:helper.link('Disengaged', 'unlocked', {'anchor' : 0}, data.anchored ? '' : 'selected')}} +
    + {{/if}} + {{else}} +
    + Safety: {{:helper.link('Engaged', 'info', null, 'disabled')}}{{:helper.link('Disengaged', 'alert', null, 'disabled')}} +
    + {{if data.moveable_anchor}} +
    + Anchor: {{:helper.link('Engaged', 'locked', null, 'disabled')}}{{:helper.link('Disengaged', 'unlocked', null, 'disabled')}} +
    + {{/if}} + {{/if}} +
    +
    +
    +
    +
    + >{{if data.message}} {{:data.message}}{{/if}} +
    +
    +
    + {{:helper.link('1', '', {'type' : 1})}}{{:helper.link('2', '', {'type' : 2})}}{{:helper.link('3', '', {'type' : 3})}} +
    +
    + {{:helper.link('4', '', {'type' : 4})}}{{:helper.link('5', '', {'type' : 5})}}{{:helper.link('6', '', {'type' : 6})}} +
    +
    + {{:helper.link('7', '', {'type' : 7})}}{{:helper.link('8', '', {'type' : 8})}}{{:helper.link('9', '', {'type' : 9})}} +
    +
    + {{:helper.link('R', '', {'type' : 'R'})}}{{:helper.link('0', '', {'type' : 0})}}{{:helper.link('E', '', {'type' : 'E'})}} +
    +
    +
    diff --git a/nano/templates/omni_filter.tmpl b/nano/templates/omni_filter.tmpl index 54f757ecb799..eaa587a77788 100644 --- a/nano/templates/omni_filter.tmpl +++ b/nano/templates/omni_filter.tmpl @@ -1,103 +1,103 @@ -
    -
    - {{:helper.link(data.power ? 'On' : 'Off', null, {'command' : 'power'}, data.config ? 'disabled' : null)}} -
    -
    - {{:helper.link('Configure', null, {'command' : 'configure'}, null, data.config ? 'selected' : null)}} -
    -
    -
    - {{if data.config}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    None
    - {{for data.ports}} -
    - {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'none', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Input
    - {{for data.ports}} -
    - {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'in', 'dir' : value.dir}, null, value.input ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Output
    - {{for data.ports}} -
    - {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'out', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Filtering
    - {{for data.ports}} -
    - {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'filtering', 'dir' : value.dir}, null, value.filter ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Filter
    - {{for data.ports}} -
    - {{:helper.link(value.f_type ? value.f_type : 'None', null, {'command' : 'switch_filter', 'mode' : value.f_type, 'dir' : value.dir}, value.filter ? null : 'disabled', value.f_type ? 'selected' : null)}} -
    - {{/for}} -
    -
    - -
    - Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s -
    -
    - {{:helper.link('Set Flow Rate Limit', null, {'command' : 'set_flow_rate'})}} -
    - - {{else}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    Mode
    - {{for data.ports}} -
    - {{if value.input}} - Input - {{else value.output}} - Output - {{else value.f_type}} - {{:value.f_type}} - {{else}} - Disabled - {{/if}} -
    - {{/for}} -
    -
    - -
    - Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s -
    - -
    - Flow Rate: {{:(data.last_flow_rate/10)}} L/s -
    - {{/if}} +
    +
    + {{:helper.link(data.power ? 'On' : 'Off', null, {'command' : 'power'}, data.config ? 'disabled' : null)}} +
    +
    + {{:helper.link('Configure', null, {'command' : 'configure'}, null, data.config ? 'selected' : null)}} +
    +
    +
    + {{if data.config}} + +
    +
    +
    Port
    + {{for data.ports}} +
    {{:value.dir}} Port
    + {{/for}} +
    +
    +
    None
    + {{for data.ports}} +
    + {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'none', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} +
    + {{/for}} +
    +
    +
    Input
    + {{for data.ports}} +
    + {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'in', 'dir' : value.dir}, null, value.input ? 'selected' : null)}} +
    + {{/for}} +
    +
    +
    Output
    + {{for data.ports}} +
    + {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'out', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} +
    + {{/for}} +
    +
    +
    Filtering
    + {{for data.ports}} +
    + {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'filtering', 'dir' : value.dir}, null, value.filter ? 'selected' : null)}} +
    + {{/for}} +
    +
    +
    Filter
    + {{for data.ports}} +
    + {{:helper.link(value.f_type ? value.f_type : 'None', null, {'command' : 'switch_filter', 'mode' : value.f_type, 'dir' : value.dir}, value.filter ? null : 'disabled', value.f_type ? 'selected' : null)}} +
    + {{/for}} +
    +
    + +
    + Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s +
    +
    + {{:helper.link('Set Flow Rate Limit', null, {'command' : 'set_flow_rate'})}} +
    + + {{else}} + +
    +
    +
    Port
    + {{for data.ports}} +
    {{:value.dir}} Port
    + {{/for}} +
    +
    +
    Mode
    + {{for data.ports}} +
    + {{if value.input}} + Input + {{else value.output}} + Output + {{else value.f_type}} + {{:value.f_type}} + {{else}} + Disabled + {{/if}} +
    + {{/for}} +
    +
    + +
    + Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s +
    + +
    + Flow Rate: {{:(data.last_flow_rate/10)}} L/s +
    + {{/if}}
    \ No newline at end of file diff --git a/nano/templates/omni_mixer.tmpl b/nano/templates/omni_mixer.tmpl index 77c4573880b0..985f49ecaae3 100644 --- a/nano/templates/omni_mixer.tmpl +++ b/nano/templates/omni_mixer.tmpl @@ -1,102 +1,102 @@ -
    -
    - {{:helper.link(data.power ? 'On' : 'Off', null, {'command' : 'power'}, data.config ? 'disabled' : null)}} -
    -
    - {{:helper.link('Configure', null, {'command' : 'configure'}, null, data.config ? 'selected' : null)}} -
    -
    -
    - {{if data.config}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    Input
    - {{for data.ports}} -
    - {{:helper.link(' ', null, value.input ? {'command' : 'switch_mode', 'mode' : 'none', 'dir' : value.dir} : {'command' : 'switch_mode', 'mode' : 'in', 'dir' : value.dir}, value.output ? 'disabled' : null, value.input ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Output
    - {{for data.ports}} -
    - {{:helper.link(' ', null, value.output ? null : {'command' : 'switch_mode', 'mode' : 'out', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Concentration
    - {{for data.ports}} -
    - {{:helper.link( value.input ? helper.round(value.concentration*100)+' %' : '-', null, {'command' : 'switch_con', 'dir' : value.dir}, value.input ? null : 'disabled')}} -
    - {{/for}} -
    -
    -
    Lock
    - {{for data.ports}} -
    - {{:helper.link(' ', value.con_lock ? 'locked' : 'unlocked', {'command' : 'switch_conlock', 'dir' : value.dir}, value.input ? null : 'disabled', value.con_lock ? 'selected' : null)}} -
    - {{/for}} -
    -
    - -
    - Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s -
    -
    - {{:helper.link('Set Flow Rate Limit', null, {'command' : 'set_flow_rate'})}} -
    - - {{else}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    Mode
    - {{for data.ports}} -
    - {{if value.input}} - Input - {{else value.output}} - Output - {{else}} - Disabled - {{/if}} -
    - {{/for}} -
    -
    -
    Concentration
    - {{for data.ports}} -
    - {{if value.input}} - {{:helper.round(value.concentration*100)}} % - {{else}} - - - {{/if}} -
    - {{/for}} -
    -
    - -
    - Flow Rate: {{:(data.last_flow_rate/10)}} L/s -
    - - {{/if}} +
    +
    + {{:helper.link(data.power ? 'On' : 'Off', null, {'command' : 'power'}, data.config ? 'disabled' : null)}} +
    +
    + {{:helper.link('Configure', null, {'command' : 'configure'}, null, data.config ? 'selected' : null)}} +
    +
    +
    + {{if data.config}} + +
    +
    +
    Port
    + {{for data.ports}} +
    {{:value.dir}} Port
    + {{/for}} +
    +
    +
    Input
    + {{for data.ports}} +
    + {{:helper.link(' ', null, value.input ? {'command' : 'switch_mode', 'mode' : 'none', 'dir' : value.dir} : {'command' : 'switch_mode', 'mode' : 'in', 'dir' : value.dir}, value.output ? 'disabled' : null, value.input ? 'selected' : null)}} +
    + {{/for}} +
    +
    +
    Output
    + {{for data.ports}} +
    + {{:helper.link(' ', null, value.output ? null : {'command' : 'switch_mode', 'mode' : 'out', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} +
    + {{/for}} +
    +
    +
    Concentration
    + {{for data.ports}} +
    + {{:helper.link( value.input ? helper.round(value.concentration*100)+' %' : '-', null, {'command' : 'switch_con', 'dir' : value.dir}, value.input ? null : 'disabled')}} +
    + {{/for}} +
    +
    +
    Lock
    + {{for data.ports}} +
    + {{:helper.link(' ', value.con_lock ? 'locked' : 'unlocked', {'command' : 'switch_conlock', 'dir' : value.dir}, value.input ? null : 'disabled', value.con_lock ? 'selected' : null)}} +
    + {{/for}} +
    +
    + +
    + Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s +
    +
    + {{:helper.link('Set Flow Rate Limit', null, {'command' : 'set_flow_rate'})}} +
    + + {{else}} + +
    +
    +
    Port
    + {{for data.ports}} +
    {{:value.dir}} Port
    + {{/for}} +
    +
    +
    Mode
    + {{for data.ports}} +
    + {{if value.input}} + Input + {{else value.output}} + Output + {{else}} + Disabled + {{/if}} +
    + {{/for}} +
    +
    +
    Concentration
    + {{for data.ports}} +
    + {{if value.input}} + {{:helper.round(value.concentration*100)}} % + {{else}} + - + {{/if}} +
    + {{/for}} +
    +
    + +
    + Flow Rate: {{:(data.last_flow_rate/10)}} L/s +
    + + {{/if}}
    \ No newline at end of file diff --git a/nano/templates/portpump.tmpl b/nano/templates/portpump.tmpl index 3e14aac8f92d..59ada0a54ac2 100644 --- a/nano/templates/portpump.tmpl +++ b/nano/templates/portpump.tmpl @@ -1,100 +1,100 @@ -

    Pump Status

    -
    -
    - Tank Pressure: -
    -
    - {{:data.tankPressure}} kPa -
    -
    - -
    -
    - Port Status: -
    -
    - {{:data.portConnected ? 'Connected' : 'Disconnected'}} -
    -
    - -
    -
    - Load: -
    -
    - {{:data.powerDraw}} W -
    -
    - -
    -
    - Cell Charge: -
    -
    - {{:helper.displayBar(data.cellCharge, 0, data.cellMaxCharge)}} -
    -
    - -

    Holding Tank Status

    -{{if data.hasHoldingTank}} -
    -
    - Tank Label: -
    -
    -
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} -
    -
    - -
    -
    - Tank Pressure: -
    -
    - {{:data.holdingTank.tankPressure}} kPa -
    -
    -{{else}} -
    No holding tank inserted.
    -
     
    -{{/if}} - - -

    Power Regulator Status

    -
    -
    - Target Pressure: -
    -
    - {{:helper.displayBar(data.targetpressure, data.minpressure, data.maxpressure)}} -
    - {{:helper.link('-', null, {'pressure_adj' : -1000}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -100}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -10}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -1}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} -
     {{:data.targetpressure}} kPa 
    - {{:helper.link('+', null, {'pressure_adj' : 1}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 10}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 100}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 1000}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Power Switch: -
    -
    - {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}} {{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} -
    -
    - -
    -
    - Pump Direction: -
    -
    - {{:helper.link('Out', 'arrowreturn-1-e', {'direction' : 1}, data.pump_dir ? 'selected' : null)}} {{:helper.link('In', 'arrowreturn-1-w', {'direction' : 1}, data.pump_dir ? null : 'selected')}} -
    -
    +

    Pump Status

    +
    +
    + Tank Pressure: +
    +
    + {{:data.tankPressure}} kPa +
    +
    + +
    +
    + Port Status: +
    +
    + {{:data.portConnected ? 'Connected' : 'Disconnected'}} +
    +
    + +
    +
    + Load: +
    +
    + {{:data.powerDraw}} W +
    +
    + +
    +
    + Cell Charge: +
    +
    + {{:helper.displayBar(data.cellCharge, 0, data.cellMaxCharge)}} +
    +
    + +

    Holding Tank Status

    +{{if data.hasHoldingTank}} +
    +
    + Tank Label: +
    +
    +
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} +
    +
    + +
    +
    + Tank Pressure: +
    +
    + {{:data.holdingTank.tankPressure}} kPa +
    +
    +{{else}} +
    No holding tank inserted.
    +
     
    +{{/if}} + + +

    Power Regulator Status

    +
    +
    + Target Pressure: +
    +
    + {{:helper.displayBar(data.targetpressure, data.minpressure, data.maxpressure)}} +
    + {{:helper.link('-', null, {'pressure_adj' : -1000}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} + {{:helper.link('-', null, {'pressure_adj' : -100}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} + {{:helper.link('-', null, {'pressure_adj' : -10}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} + {{:helper.link('-', null, {'pressure_adj' : -1}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} +
     {{:data.targetpressure}} kPa 
    + {{:helper.link('+', null, {'pressure_adj' : 1}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} + {{:helper.link('+', null, {'pressure_adj' : 10}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} + {{:helper.link('+', null, {'pressure_adj' : 100}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} + {{:helper.link('+', null, {'pressure_adj' : 1000}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} +
    +
    +
    + +
    +
    + Power Switch: +
    +
    + {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}} {{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} +
    +
    + +
    +
    + Pump Direction: +
    +
    + {{:helper.link('Out', 'arrowreturn-1-e', {'direction' : 1}, data.pump_dir ? 'selected' : null)}} {{:helper.link('In', 'arrowreturn-1-w', {'direction' : 1}, data.pump_dir ? null : 'selected')}} +
    +
    diff --git a/nano/templates/portscrubber.tmpl b/nano/templates/portscrubber.tmpl index c99f53344c41..f2f216f630b1 100644 --- a/nano/templates/portscrubber.tmpl +++ b/nano/templates/portscrubber.tmpl @@ -1,91 +1,91 @@ -

    Scrubber Status

    -
    -
    - Tank Pressure: -
    -
    - {{:data.tankPressure}} kPa -
    -
    - -
    -
    - Port Status: -
    -
    - {{:data.portConnected ? 'Connected' : 'Disconnected'}} -
    -
    - -
    -
    - Load: -
    -
    - {{:data.powerDraw}} W -
    -
    - -
    -
    - Cell Charge: -
    -
    - {{:helper.displayBar(data.cellCharge, 0, data.cellMaxCharge)}} -
    -
    - -

    Holding Tank Status

    -{{if data.hasHoldingTank}} -
    -
    - Tank Label: -
    -
    -
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} -
    -
    q - -
    -
    - Tank Pressure: -
    -
    - {{:data.holdingTank.tankPressure}} kPa -
    -
    -{{else}} -
    No holding tank inserted.
    -
     
    -{{/if}} - - -

    Power Regulator Status

    -
    -
    - Volume Rate: -
    -
    - {{:helper.displayBar(data.rate, data.minrate, data.maxrate)}} -
    - {{:helper.link('-', null, {'volume_adj' : -1000}, (data.rate > data.minrate) ? null : 'disabled')}} - {{:helper.link('-', null, {'volume_adj' : -100}, (data.rate > data.minrate) ? null : 'disabled')}} - {{:helper.link('-', null, {'volume_adj' : -10}, (data.rate > data.minrate) ? null : 'disabled')}} - {{:helper.link('-', null, {'volume_adj' : -1}, (data.rate > data.minrate) ? null : 'disabled')}} -
     {{:data.rate}} L/s 
    - {{:helper.link('+', null, {'volume_adj' : 1}, (data.rate < data.maxrate) ? null : 'disabled')}} - {{:helper.link('+', null, {'volume_adj' : 10}, (data.rate < data.maxrate) ? null : 'disabled')}} - {{:helper.link('+', null, {'volume_adj' : 100}, (data.rate < data.maxrate) ? null : 'disabled')}} - {{:helper.link('+', null, {'volume_adj' : 1000}, (data.rate < data.maxrate) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Power Switch: -
    -
    - {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}} {{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} -
    -
    +

    Scrubber Status

    +
    +
    + Tank Pressure: +
    +
    + {{:data.tankPressure}} kPa +
    +
    + +
    +
    + Port Status: +
    +
    + {{:data.portConnected ? 'Connected' : 'Disconnected'}} +
    +
    + +
    +
    + Load: +
    +
    + {{:data.powerDraw}} W +
    +
    + +
    +
    + Cell Charge: +
    +
    + {{:helper.displayBar(data.cellCharge, 0, data.cellMaxCharge)}} +
    +
    + +

    Holding Tank Status

    +{{if data.hasHoldingTank}} +
    +
    + Tank Label: +
    +
    +
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} +
    +
    q + +
    +
    + Tank Pressure: +
    +
    + {{:data.holdingTank.tankPressure}} kPa +
    +
    +{{else}} +
    No holding tank inserted.
    +
     
    +{{/if}} + + +

    Power Regulator Status

    +
    +
    + Volume Rate: +
    +
    + {{:helper.displayBar(data.rate, data.minrate, data.maxrate)}} +
    + {{:helper.link('-', null, {'volume_adj' : -1000}, (data.rate > data.minrate) ? null : 'disabled')}} + {{:helper.link('-', null, {'volume_adj' : -100}, (data.rate > data.minrate) ? null : 'disabled')}} + {{:helper.link('-', null, {'volume_adj' : -10}, (data.rate > data.minrate) ? null : 'disabled')}} + {{:helper.link('-', null, {'volume_adj' : -1}, (data.rate > data.minrate) ? null : 'disabled')}} +
     {{:data.rate}} L/s 
    + {{:helper.link('+', null, {'volume_adj' : 1}, (data.rate < data.maxrate) ? null : 'disabled')}} + {{:helper.link('+', null, {'volume_adj' : 10}, (data.rate < data.maxrate) ? null : 'disabled')}} + {{:helper.link('+', null, {'volume_adj' : 100}, (data.rate < data.maxrate) ? null : 'disabled')}} + {{:helper.link('+', null, {'volume_adj' : 1000}, (data.rate < data.maxrate) ? null : 'disabled')}} +
    +
    +
    + +
    +
    + Power Switch: +
    +
    + {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}} {{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} +
    +
    diff --git a/nano/templates/shuttle_control_console_antag.tmpl b/nano/templates/shuttle_control_console_antag.tmpl index 57214b5d8cea..28aa38c82888 100644 --- a/nano/templates/shuttle_control_console_antag.tmpl +++ b/nano/templates/shuttle_control_console_antag.tmpl @@ -1,94 +1,94 @@ -

    Shuttle Status

    -
    -
    - {{:data.shuttle_status}} -
    -
    - Cloaking field is {{ cloaked? "enabled" : "disabled" }}. {{:helper.link('Toggle', 'arrowreturn-1-s', {'toggle_cloaked' : '1'} }} -
    -
    -
    -
    -
    - Drive: -
    -
    - {{if data.shuttle_state == "idle"}} - IDLE - {{else data.shuttle_state == "warmup"}} - SPINNING UP - {{else data.shuttle_state == "in_transit"}} - ENGAGED - {{else}} - ERROR - {{/if}} -
    -
    -
    -
    -
    - ETA: -
    -
    - {{if data.shuttle_state == "warmup"}} - CALIBRATING - {{else data.shuttle_state == "in_transit"}} - {{if data.timeleft > 0}} - {{:data.timeleft}} s - {{else}} - IMMINENT - {{/if}} - {{else}} - NONE - {{/if}} -
    -
    -{{if data.has_docking}} -
    -
    -
    - Docking Status: -
    -
    - {{if data.docking_status == "docked"}} - DOCKED - {{else data.docking_status == "docking"}} - {{if !data.docking_override}} - DOCKING - {{else}} - DOCKING-MANUAL - {{/if}} - {{else data.docking_status == "undocking"}} - {{if !data.docking_override}} - UNDOCKING - {{else}} - UNDOCKING-MANUAL - {{/if}} - {{else data.docking_status == "undocked"}} - UNDOCKED - {{else}} - ERROR - {{/if}} -
    -
    -
    -{{/if}} -
    -
    - Current Destination: -
    - {{:data.destination_name}} -
    - {{:helper.link('Choose Destination', 'arrowreturn-1-s', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}} -
    -
    -

    Shuttle Control

    -
    -
    -
    - {{:helper.link('Launch Shuttle', 'arrowthickstop-1-e', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}} - {{:helper.link('Cancel Launch', 'cancel', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}} - {{:helper.link('Force Launch', 'alert', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}} -
    -
    +

    Shuttle Status

    +
    +
    + {{:data.shuttle_status}} +
    +
    + Cloaking field is {{ cloaked? "enabled" : "disabled" }}. {{:helper.link('Toggle', 'arrowreturn-1-s', {'toggle_cloaked' : '1'} }} +
    +
    +
    +
    +
    + Drive: +
    +
    + {{if data.shuttle_state == "idle"}} + IDLE + {{else data.shuttle_state == "warmup"}} + SPINNING UP + {{else data.shuttle_state == "in_transit"}} + ENGAGED + {{else}} + ERROR + {{/if}} +
    +
    +
    +
    +
    + ETA: +
    +
    + {{if data.shuttle_state == "warmup"}} + CALIBRATING + {{else data.shuttle_state == "in_transit"}} + {{if data.timeleft > 0}} + {{:data.timeleft}} s + {{else}} + IMMINENT + {{/if}} + {{else}} + NONE + {{/if}} +
    +
    +{{if data.has_docking}} +
    +
    +
    + Docking Status: +
    +
    + {{if data.docking_status == "docked"}} + DOCKED + {{else data.docking_status == "docking"}} + {{if !data.docking_override}} + DOCKING + {{else}} + DOCKING-MANUAL + {{/if}} + {{else data.docking_status == "undocking"}} + {{if !data.docking_override}} + UNDOCKING + {{else}} + UNDOCKING-MANUAL + {{/if}} + {{else data.docking_status == "undocked"}} + UNDOCKED + {{else}} + ERROR + {{/if}} +
    +
    +
    +{{/if}} +
    +
    + Current Destination: +
    + {{:data.destination_name}} +
    + {{:helper.link('Choose Destination', 'arrowreturn-1-s', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}} +
    +
    +

    Shuttle Control

    +
    +
    +
    + {{:helper.link('Launch Shuttle', 'arrowthickstop-1-e', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}} + {{:helper.link('Cancel Launch', 'cancel', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}} + {{:helper.link('Force Launch', 'alert', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}} +
    +
    \ No newline at end of file diff --git a/nano/templates/shuttle_control_console_multi.tmpl b/nano/templates/shuttle_control_console_multi.tmpl index aac27d274a69..f1de01362def 100644 --- a/nano/templates/shuttle_control_console_multi.tmpl +++ b/nano/templates/shuttle_control_console_multi.tmpl @@ -1,97 +1,97 @@ -

    Shuttle Status

    -
    -
    - {{:data.shuttle_status}} -
    -
    -
    -
    -
    - Drive: -
    -
    - {{if data.shuttle_state == "idle"}} - IDLE - {{else data.shuttle_state == "warmup"}} - SPINNING UP - {{else data.shuttle_state == "in_transit"}} - ENGAGED - {{else}} - ERROR - {{/if}} -
    -
    -
    -
    -
    - ETA: -
    -
    - {{if data.shuttle_state == "warmup"}} - CALIBRATING - {{else data.shuttle_state == "in_transit"}} - {{if data.timeleft > 0}} - {{:data.timeleft}} s - {{else}} - IMMINENT - {{/if}} - {{else}} - NONE - {{/if}} -
    -
    -{{if data.has_docking}} -
    -
    -
    - Docking Status: -
    -
    - {{if data.docking_status == "docked"}} - DOCKED - {{else data.docking_status == "docking"}} - {{if !data.docking_override}} - DOCKING - {{else}} - DOCKING-MANUAL - {{/if}} - {{else data.docking_status == "undocking"}} - {{if !data.docking_override}} - UNDOCKING - {{else}} - UNDOCKING-MANUAL - {{/if}} - {{else data.docking_status == "undocked"}} - UNDOCKED - {{else}} - ERROR - {{/if}} -
    -
    - Docking Codes: -
    -
    - {{:helper.link(data.docking_codes ? data.docking_codes : 'Not set', null, {'set_codes' : '1'}, null , null)}} -
    -
    -
    -{{/if}} -
    -
    - Current Destination: -
    - {{:data.destination_name}} -
    - {{:helper.link('Choose Destination', 'arrowreturn-1-s', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}} -
    -
    -

    Shuttle Control

    -
    -
    -
    - {{:helper.link('Launch Shuttle', 'arrowthickstop-1-e', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}} - {{:helper.link('Cancel Launch', 'cancel', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}} - {{:helper.link('Force Launch', 'alert', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}} -
    -
    +

    Shuttle Status

    +
    +
    + {{:data.shuttle_status}} +
    +
    +
    +
    +
    + Drive: +
    +
    + {{if data.shuttle_state == "idle"}} + IDLE + {{else data.shuttle_state == "warmup"}} + SPINNING UP + {{else data.shuttle_state == "in_transit"}} + ENGAGED + {{else}} + ERROR + {{/if}} +
    +
    +
    +
    +
    + ETA: +
    +
    + {{if data.shuttle_state == "warmup"}} + CALIBRATING + {{else data.shuttle_state == "in_transit"}} + {{if data.timeleft > 0}} + {{:data.timeleft}} s + {{else}} + IMMINENT + {{/if}} + {{else}} + NONE + {{/if}} +
    +
    +{{if data.has_docking}} +
    +
    +
    + Docking Status: +
    +
    + {{if data.docking_status == "docked"}} + DOCKED + {{else data.docking_status == "docking"}} + {{if !data.docking_override}} + DOCKING + {{else}} + DOCKING-MANUAL + {{/if}} + {{else data.docking_status == "undocking"}} + {{if !data.docking_override}} + UNDOCKING + {{else}} + UNDOCKING-MANUAL + {{/if}} + {{else data.docking_status == "undocked"}} + UNDOCKED + {{else}} + ERROR + {{/if}} +
    +
    + Docking Codes: +
    +
    + {{:helper.link(data.docking_codes ? data.docking_codes : 'Not set', null, {'set_codes' : '1'}, null , null)}} +
    +
    +
    +{{/if}} +
    +
    + Current Destination: +
    + {{:data.destination_name}} +
    + {{:helper.link('Choose Destination', 'arrowreturn-1-s', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}} +
    +
    +

    Shuttle Control

    +
    +
    +
    + {{:helper.link('Launch Shuttle', 'arrowthickstop-1-e', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}} + {{:helper.link('Cancel Launch', 'cancel', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}} + {{:helper.link('Force Launch', 'alert', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}} +
    +
    \ No newline at end of file diff --git a/nano/templates/turret.tmpl b/nano/templates/turret.tmpl index 476e203090c0..a2f2d4ab0e8c 100644 --- a/nano/templates/turret.tmpl +++ b/nano/templates/turret.tmpl @@ -1,88 +1,88 @@ -
    - {{if data.network}} -
    -
    {{:helper.link("Network Settings", null, { "settings" : 1 })}}
    -
    -
    - {{/if}} -

    Power System

    -
    -
    - Power: -
    -
    - {{:helper.link('On', 'power', {'set_enabled' : 1}, data.enabled ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'set_enabled' : 0}, data.enabled ? null : 'selected')}} -
    -
    -

    Weapon System

    -
    -
    - Installed Weapon: -
    -
    - {{if data.weaponName == null}} -
    - No weapon detected! -
    - {{else}} - {{:helper.link(data.weaponName, 'alert', {'eject_gun' : 1})}} - {{/if}} -
    -
    - {{if data.firemodes}} -
    -
    - Fire modes: -
    -
    - {{for data.firemodes}} - {{:helper.link(value.name, null, {'switch_firemode' : value.index}, value.selected ? 'selected' : null)}} - {{/for}} -
    -
    - {{/if}} -

    Targeting Settings

    - {{for data.settings}} -
    -
    - {{:value.category}} -
    -
    - {{:helper.link('On', null, {'targeting_comm' : value.setting, 'targeting_value' : 1}, value.value ? 'selected' : null)}} - {{:helper.link('Off',null, {'targeting_comm' : value.setting, 'targeting_value' : 0}, !value.value ? 'selected' : null)}} -
    -
    - {{/for}} -

    Manual Control

    -
    -
    - Bearing: -
    -
    - {{:helper.link('-10°', 'triangle-1-w', {'adjust_bearing' : -10})}} - {{:helper.link('-5°', 'triangle-1-w', {'adjust_bearing' : -5})}} - {{:helper.link(data.currentBearing+'°', null, {'set_bearing' : 1})}} - {{:helper.link('+5°', 'triangle-1-e', {'adjust_bearing' : 5})}} - {{:helper.link('+10°', 'triangle-1-e', {'adjust_bearing' : 10})}} -
    -
    -
    -
    - Default Bearing: -
    -
    - {{:helper.link(data.defaultBearing+'°', null, {'set_default' : 1})}} -
    -
    -
    -
    - Control: -
    -
    - {{:helper.link('Manual Fire', 'alert', {'manual_fire' : 1}, null, data.weaponName ? 'redButton' : 'disabled')}} -
    -
    -
    - - +
    + {{if data.network}} +
    +
    {{:helper.link("Network Settings", null, { "settings" : 1 })}}
    +
    +
    + {{/if}} +

    Power System

    +
    +
    + Power: +
    +
    + {{:helper.link('On', 'power', {'set_enabled' : 1}, data.enabled ? 'selected' : null)}} + {{:helper.link('Off', 'close', {'set_enabled' : 0}, data.enabled ? null : 'selected')}} +
    +
    +

    Weapon System

    +
    +
    + Installed Weapon: +
    +
    + {{if data.weaponName == null}} +
    + No weapon detected! +
    + {{else}} + {{:helper.link(data.weaponName, 'alert', {'eject_gun' : 1})}} + {{/if}} +
    +
    + {{if data.firemodes}} +
    +
    + Fire modes: +
    +
    + {{for data.firemodes}} + {{:helper.link(value.name, null, {'switch_firemode' : value.index}, value.selected ? 'selected' : null)}} + {{/for}} +
    +
    + {{/if}} +

    Targeting Settings

    + {{for data.settings}} +
    +
    + {{:value.category}} +
    +
    + {{:helper.link('On', null, {'targeting_comm' : value.setting, 'targeting_value' : 1}, value.value ? 'selected' : null)}} + {{:helper.link('Off',null, {'targeting_comm' : value.setting, 'targeting_value' : 0}, !value.value ? 'selected' : null)}} +
    +
    + {{/for}} +

    Manual Control

    +
    +
    + Bearing: +
    +
    + {{:helper.link('-10°', 'triangle-1-w', {'adjust_bearing' : -10})}} + {{:helper.link('-5°', 'triangle-1-w', {'adjust_bearing' : -5})}} + {{:helper.link(data.currentBearing+'°', null, {'set_bearing' : 1})}} + {{:helper.link('+5°', 'triangle-1-e', {'adjust_bearing' : 5})}} + {{:helper.link('+10°', 'triangle-1-e', {'adjust_bearing' : 10})}} +
    +
    +
    +
    + Default Bearing: +
    +
    + {{:helper.link(data.defaultBearing+'°', null, {'set_default' : 1})}} +
    +
    +
    +
    + Control: +
    +
    + {{:helper.link('Manual Fire', 'alert', {'manual_fire' : 1}, null, data.weaponName ? 'redButton' : 'disabled')}} +
    +
    +
    + + diff --git a/nano/templates/turret_control.tmpl b/nano/templates/turret_control.tmpl index a529bcbb108f..944f4a73d3fe 100644 --- a/nano/templates/turret_control.tmpl +++ b/nano/templates/turret_control.tmpl @@ -1,41 +1,41 @@ -
    -
    - Behaviour controls are {{:data.locked ? "locked" : "unlocked"}}. -
    -
    -
    -{{if data.access}} -
    -
    - Turret Status: -
    -
    - {{:helper.link('Enabled', null, {'command' : 'enable', 'value' : 1}, data.enabled ?'redButton' : null)}} - {{:helper.link('Disabled',null, {'command' : 'enable', 'value' : 0}, !data.enabled ? 'selected' : null)}} -
    -
    - - {{if data.is_lethal}} -
    -
    - Lethal Mode: -
    -
    - {{:helper.link('On', null, {'command' : 'lethal', 'value' : 1}, data.lethal ?'redButton' : null)}} - {{:helper.link('Off',null, {'command' : 'lethal', 'value' : 0}, !data.lethal ? 'selected' : null)}} -
    -
    - {{/if}} - - {{for data.settings}} -
    -
    - {{:value.category}} -
    -
    - {{:helper.link('On', null, {'command' : value.setting, 'value' : 1}, value.value ? 'selected' : null)}} - {{:helper.link('Off',null, {'command' : value.setting, 'value' : 0}, !value.value ? 'selected' : null)}} -
    -
    - {{/for}} -{{/if}} +
    +
    + Behaviour controls are {{:data.locked ? "locked" : "unlocked"}}. +
    +
    +
    +{{if data.access}} +
    +
    + Turret Status: +
    +
    + {{:helper.link('Enabled', null, {'command' : 'enable', 'value' : 1}, data.enabled ?'redButton' : null)}} + {{:helper.link('Disabled',null, {'command' : 'enable', 'value' : 0}, !data.enabled ? 'selected' : null)}} +
    +
    + + {{if data.is_lethal}} +
    +
    + Lethal Mode: +
    +
    + {{:helper.link('On', null, {'command' : 'lethal', 'value' : 1}, data.lethal ?'redButton' : null)}} + {{:helper.link('Off',null, {'command' : 'lethal', 'value' : 0}, !data.lethal ? 'selected' : null)}} +
    +
    + {{/if}} + + {{for data.settings}} +
    +
    + {{:value.category}} +
    +
    + {{:helper.link('On', null, {'command' : value.setting, 'value' : 1}, value.value ? 'selected' : null)}} + {{:helper.link('Off',null, {'command' : value.setting, 'value' : 0}, !value.value ? 'selected' : null)}} +
    +
    + {{/for}} +{{/if}} diff --git a/nano/templates/uplink.tmpl b/nano/templates/uplink.tmpl index c01d79dc9a30..ce47818e2af9 100644 --- a/nano/templates/uplink.tmpl +++ b/nano/templates/uplink.tmpl @@ -1,89 +1,89 @@ - - -{{:helper.syndicateMode()}} -

    {{:data.welcome}}

    -
    -
    -
    - Functions: -
    -
    - {{:helper.link('Request Items', 'gear', {'menu' : 0}, null, 'fixedLeftWider')}} - {{:helper.link('Exploitable Information', 'gear', {'menu' : 2}, null, 'fixedLeftWider')}} - {{:helper.link('Return', 'arrowreturn-1-w', {'return' : 1}, null, 'fixedLeft')}} - {{:helper.link('Close', 'gear', {'lock' : "1"}, null, 'fixedLeft')}} -
    -
    -
    - -{{if data.menu == 0 || data.menu == 1}} -
    -
    - Telecrystals: -
    -
    - {{:data.crystals}} -
    -
    - - {{if data.discount_amount < 100}} -
    -
    - Currently discounted: -
    -
    - {{:data.discount_category}}
    {{:data.discount_name}}
    {{:data.discount_amount}}% off. Offer will expire at: {{:data.offer_expiry}} -
    -
    - {{/if}} -{{/if}} - -{{if data.menu == 0}} -

    Categories:

    - {{for data.categories}} -
    - {{:helper.link(value.name, 'gear', {'menu' : 1, 'category' : value.ref})}} -
    - {{/for}} -{{else data.menu == 1}} -

    Request items:

    - Each item costs a number of tele-crystals as indicated by the number following their name. - - {{for data.items}} -
    - {{:helper.link(value.name, 'gear', {'buy_item' : value.ref}, value.can_buy ? null : 'disabled')}} - {{:value.cost}} -
    -
    - {{:value.description}} -
    - {{/for}} -{{else data.menu == 2}} -

    Information Record List:

    -
    -
    - Select a Record -
    -
    - {{for data.exploit_records}} -
    - {{:helper.link(value.Name, value.exploit ? 'gear' : 'document', {'menu' : 21, 'id' : value.id}, null, value.exploit ? 'redButton' : null)}} -
    - {{/for}} -{{else data.menu == 21}} -

    Information Record:

    -
    -
    -
    -
    - {{if data.exploit_exists == 1}} - {{for data.exploit.fields}} - {{:value.name}}: {{:value.value}}
    - {{/for}} - {{/if}} -
    -
    -
    -{{/if}} + + +{{:helper.syndicateMode()}} +

    {{:data.welcome}}

    +
    +
    +
    + Functions: +
    +
    + {{:helper.link('Request Items', 'gear', {'menu' : 0}, null, 'fixedLeftWider')}} + {{:helper.link('Exploitable Information', 'gear', {'menu' : 2}, null, 'fixedLeftWider')}} + {{:helper.link('Return', 'arrowreturn-1-w', {'return' : 1}, null, 'fixedLeft')}} + {{:helper.link('Close', 'gear', {'lock' : "1"}, null, 'fixedLeft')}} +
    +
    +
    + +{{if data.menu == 0 || data.menu == 1}} +
    +
    + Telecrystals: +
    +
    + {{:data.crystals}} +
    +
    + + {{if data.discount_amount < 100}} +
    +
    + Currently discounted: +
    +
    + {{:data.discount_category}}
    {{:data.discount_name}}
    {{:data.discount_amount}}% off. Offer will expire at: {{:data.offer_expiry}} +
    +
    + {{/if}} +{{/if}} + +{{if data.menu == 0}} +

    Categories:

    + {{for data.categories}} +
    + {{:helper.link(value.name, 'gear', {'menu' : 1, 'category' : value.ref})}} +
    + {{/for}} +{{else data.menu == 1}} +

    Request items:

    + Each item costs a number of tele-crystals as indicated by the number following their name. + + {{for data.items}} +
    + {{:helper.link(value.name, 'gear', {'buy_item' : value.ref}, value.can_buy ? null : 'disabled')}} - {{:value.cost}} +
    +
    + {{:value.description}} +
    + {{/for}} +{{else data.menu == 2}} +

    Information Record List:

    +
    +
    + Select a Record +
    +
    + {{for data.exploit_records}} +
    + {{:helper.link(value.Name, value.exploit ? 'gear' : 'document', {'menu' : 21, 'id' : value.id}, null, value.exploit ? 'redButton' : null)}} +
    + {{/for}} +{{else data.menu == 21}} +

    Information Record:

    +
    +
    +
    +
    + {{if data.exploit_exists == 1}} + {{for data.exploit.fields}} + {{:value.name}}: {{:value.value}}
    + {{/for}} + {{/if}} +
    +
    +
    +{{/if}} diff --git a/tools/TagMatcher/checkTagMatches.bat b/tools/TagMatcher/checkTagMatches.bat index 191c30bbba4c..2e53f85782db 100644 --- a/tools/TagMatcher/checkTagMatches.bat +++ b/tools/TagMatcher/checkTagMatches.bat @@ -1,3 +1,3 @@ -@echo off -call python tag-matcher.py ../.. -pause +@echo off +call python tag-matcher.py ../.. +pause diff --git a/tools/TagMatcher/tag-matcher.py b/tools/TagMatcher/tag-matcher.py index a9864d7605f3..a8e6a1322980 100644 --- a/tools/TagMatcher/tag-matcher.py +++ b/tools/TagMatcher/tag-matcher.py @@ -1,120 +1,120 @@ -''' -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -''' -import argparse, re, sys -from collections import defaultdict -from os import path, walk - -opt = argparse.ArgumentParser() -opt.add_argument('dir', help='The directory to scan for *.dm files with non-matching spans') -args = opt.parse_args() - -if(not path.isdir(args.dir)): - print('Not a directory') - sys.exit(1) - -# These tuples are expected to be ordered as: -# A unique human readable name (henceforth referred to as tuple name), a regex pattern matching an opening tag, a regex pattern matching a closing tag -tag_tuples = [ ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE)), - ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE)), - ('
    ', re.compile('
    ', re.IGNORECASE), re.compile('
    ', re.IGNORECASE)), - ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE)), - ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE))] - -# The keys of this dictionary will be the file path of each parsed *.dm file -# The values of this dictionary is a another dictionary with the key/value pair: tag/list of unmatched lines -mismatches_by_file = { } - -# Loops over all defined tag tuples and returns a dictionary with the key/value pair: tag/mismatch_count (positive means excess of opening tag, negative means excess of closing tags) -def get_tag_matches(line): - mismatch_count_by_tag = { } - for tag_tuple in tag_tuples: - mismatch_count = 0 - mismatch_count += len(tag_tuple[1].findall(line)) - mismatch_count -= len(tag_tuple[2].findall(line)) - if mismatch_count != 0: - mismatch_count_by_tag[tag_tuple[0]] = mismatch_count - return mismatch_count_by_tag - -# Support def that simply checks if a given dictionary in the format tag/list of unmatched lines has mismatch entries. -def has_mismatch(match_list): - for tag, list_of_mismatched_lines in match_list.items(): - if(len(list_of_mismatched_lines) > 0): - return 1 - return 0 - -def arrange_mismatches(mismatches_by_tag, mismatch_line, mismatch_counts): - for tag, mismatch_count in mismatch_counts.items(): - stack_of_existing_mismatches = mismatches_by_tag[tag] - for i in range(0, abs(mismatch_count)): - if len(stack_of_existing_mismatches) == 0: - if(mismatch_count > 0): - stack_of_existing_mismatches.append(mismatch_line) - else: - stack_of_existing_mismatches.append(-mismatch_line) - else: - if stack_of_existing_mismatches[0] > 0: - if mismatch_count > 0: - stack_of_existing_mismatches.append(mismatch_line) - else: - stack_of_existing_mismatches.pop() - else: - if mismatch_count < 0: - stack_of_existing_mismatches.append(-mismatch_line) - else: - stack_of_existing_mismatches.pop() - - -# This section parses all *.dm files in the given directory, recursively. -for root, subdirs, files in walk(args.dir): - for filename in files: - if filename.endswith('.dm'): - file_path = path.join(root, filename) - with open(file_path, 'r', encoding="latin-1") as file: - mismatches_by_file[file_path] = defaultdict(list) - for line_number, line in enumerate(file, 1): - # Then for each line in the file, conduct the tuple open/close matching. - mismatches_by_tag = get_tag_matches(line) - arrange_mismatches(mismatches_by_file[file_path], line_number, mismatches_by_tag) - -# Pretty printing section. -# Loops over all matches and checks if there is a mismatch of tags. -# If so, then and only then is the corresponding file path printed along with the number of unmatched open/close tags. -total_mismatches = 0 -for file, mismatches_by_tag in mismatches_by_file.items(): - if has_mismatch(mismatches_by_tag): - print(file) - for tag, mismatch_list in mismatches_by_tag.items(): - # A positive number means an excess of opening tag, a negative number means an excess of closing tags. - total_mismatches += len(mismatch_list) - if len(mismatch_list) > 0: - if mismatch_list[0] > 0: - print('\t{0} - Excess of {1} opening tag(s)'.format(tag, len(mismatch_list))) - elif mismatch_list[0] < 0: - print('\t{0} - Excess of {1} closing tag(s)'.format(tag, len(mismatch_list))) - for mismatch_line in sorted(set(mismatch_list)): - print('\t\tLine {0}'.format(abs(mismatch_line))) - -# Simply prints the total number of mismatches found and if so returns 1 to, for example, fail unit testing. -if(total_mismatches == 0): - print('No mismatches found.') -else: - print('') - print('Total number of mismatches: {0}'.format(total_mismatches)) - sys.exit(1) +''' +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +''' +import argparse, re, sys +from collections import defaultdict +from os import path, walk + +opt = argparse.ArgumentParser() +opt.add_argument('dir', help='The directory to scan for *.dm files with non-matching spans') +args = opt.parse_args() + +if(not path.isdir(args.dir)): + print('Not a directory') + sys.exit(1) + +# These tuples are expected to be ordered as: +# A unique human readable name (henceforth referred to as tuple name), a regex pattern matching an opening tag, a regex pattern matching a closing tag +tag_tuples = [ ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE)), + ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE)), + ('
    ', re.compile('
    ', re.IGNORECASE), re.compile('
    ', re.IGNORECASE)), + ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE)), + ('', re.compile('', re.IGNORECASE), re.compile('', re.IGNORECASE))] + +# The keys of this dictionary will be the file path of each parsed *.dm file +# The values of this dictionary is a another dictionary with the key/value pair: tag/list of unmatched lines +mismatches_by_file = { } + +# Loops over all defined tag tuples and returns a dictionary with the key/value pair: tag/mismatch_count (positive means excess of opening tag, negative means excess of closing tags) +def get_tag_matches(line): + mismatch_count_by_tag = { } + for tag_tuple in tag_tuples: + mismatch_count = 0 + mismatch_count += len(tag_tuple[1].findall(line)) + mismatch_count -= len(tag_tuple[2].findall(line)) + if mismatch_count != 0: + mismatch_count_by_tag[tag_tuple[0]] = mismatch_count + return mismatch_count_by_tag + +# Support def that simply checks if a given dictionary in the format tag/list of unmatched lines has mismatch entries. +def has_mismatch(match_list): + for tag, list_of_mismatched_lines in match_list.items(): + if(len(list_of_mismatched_lines) > 0): + return 1 + return 0 + +def arrange_mismatches(mismatches_by_tag, mismatch_line, mismatch_counts): + for tag, mismatch_count in mismatch_counts.items(): + stack_of_existing_mismatches = mismatches_by_tag[tag] + for i in range(0, abs(mismatch_count)): + if len(stack_of_existing_mismatches) == 0: + if(mismatch_count > 0): + stack_of_existing_mismatches.append(mismatch_line) + else: + stack_of_existing_mismatches.append(-mismatch_line) + else: + if stack_of_existing_mismatches[0] > 0: + if mismatch_count > 0: + stack_of_existing_mismatches.append(mismatch_line) + else: + stack_of_existing_mismatches.pop() + else: + if mismatch_count < 0: + stack_of_existing_mismatches.append(-mismatch_line) + else: + stack_of_existing_mismatches.pop() + + +# This section parses all *.dm files in the given directory, recursively. +for root, subdirs, files in walk(args.dir): + for filename in files: + if filename.endswith('.dm'): + file_path = path.join(root, filename) + with open(file_path, 'r', encoding="latin-1") as file: + mismatches_by_file[file_path] = defaultdict(list) + for line_number, line in enumerate(file, 1): + # Then for each line in the file, conduct the tuple open/close matching. + mismatches_by_tag = get_tag_matches(line) + arrange_mismatches(mismatches_by_file[file_path], line_number, mismatches_by_tag) + +# Pretty printing section. +# Loops over all matches and checks if there is a mismatch of tags. +# If so, then and only then is the corresponding file path printed along with the number of unmatched open/close tags. +total_mismatches = 0 +for file, mismatches_by_tag in mismatches_by_file.items(): + if has_mismatch(mismatches_by_tag): + print(file) + for tag, mismatch_list in mismatches_by_tag.items(): + # A positive number means an excess of opening tag, a negative number means an excess of closing tags. + total_mismatches += len(mismatch_list) + if len(mismatch_list) > 0: + if mismatch_list[0] > 0: + print('\t{0} - Excess of {1} opening tag(s)'.format(tag, len(mismatch_list))) + elif mismatch_list[0] < 0: + print('\t{0} - Excess of {1} closing tag(s)'.format(tag, len(mismatch_list))) + for mismatch_line in sorted(set(mismatch_list)): + print('\t\tLine {0}'.format(abs(mismatch_line))) + +# Simply prints the total number of mismatches found and if so returns 1 to, for example, fail unit testing. +if(total_mismatches == 0): + print('No mismatches found.') +else: + print('') + print('Total number of mismatches: {0}'.format(total_mismatches)) + sys.exit(1) diff --git a/tools/dmitool/git_merge_installer.bat b/tools/dmitool/git_merge_installer.bat index 18ab7c8026ab..c7ba2fa23a14 100644 --- a/tools/dmitool/git_merge_installer.bat +++ b/tools/dmitool/git_merge_installer.bat @@ -1,6 +1,6 @@ -@echo off -set tab= -echo. >> ../../.git/config -echo [merge "merge-dmi"] >> ../../.git/config -echo %tab%name = iconfile merge driver >> ../../.git/config -echo %tab%driver = ./tools/dmitool/dmimerge.sh %%O %%A %%B >> ../../.git/config +@echo off +set tab= +echo. >> ../../.git/config +echo [merge "merge-dmi"] >> ../../.git/config +echo %tab%name = iconfile merge driver >> ../../.git/config +echo %tab%driver = ./tools/dmitool/dmimerge.sh %%O %%A %%B >> ../../.git/config diff --git a/tools/expand_filedir_paths.py b/tools/expand_filedir_paths.py index d5a3d8d3f3a0..b58a8679398c 100644 --- a/tools/expand_filedir_paths.py +++ b/tools/expand_filedir_paths.py @@ -1,93 +1,93 @@ -#!/usr/bin/env python - -import re, os, sys, fnmatch - - -# Regex pattern to extract the directory path in a #define FILE_DIR -filedir_pattern = re.compile(r'^#define\s*FILE_DIR\s*"(.*?)"') - -# Regex pattern to extract any single quoted piece of text. This can also -# match single quoted strings inside of double quotes, which is part of a -# regular text string and should not be replaced. The replacement function -# however will any match that doesn't appear to be a filename so these -# extra matches should not be a problem. -rename_pattern = re.compile(r"'(.+?)'") - -# Only filenames matching this pattern will have their resources renamed -source_pattern = re.compile(r"^.*?\.(dm|dmm)$") - -# Open the .dme file and return a list of all FILE_DIR paths in it -def read_filedirs(filename): - result = [] - dme_file = file(filename, "rt") - - # Read each line from the file and check for regex pattern match - for row in dme_file: - match = filedir_pattern.match(row) - if match: - result.append(match.group(1)) - - dme_file.close() - return result - -# Search through a list of directories, and build a dictionary which -# maps every file to its full pathname (relative to the .dme file) -# If the same filename appears in more than one directory, the earlier -# directory in the list takes preference. -def index_files(file_dirs): - result = {} - - # Reverse the directory list so the earlier directories take precedence - # by replacing the previously indexed file of the same name - for directory in reversed(file_dirs): - for name in os.listdir(directory): - # Replace backslash path separators on Windows with forward slash - # Force "name" to lowercase when used as a key since BYOND resource - # names are case insensitive, even on Linux. - if name.find(".") == -1: - continue - result[name.lower()] = directory.replace('\\', '/') + '/' + name - - return result - -# Recursively search for every .dm/.dmm file in the .dme file directory. For -# each file, search it for any resource names in single quotes, and replace -# them with the full path previously found by index_files() -def rewrite_sources(resources): - # Create a closure for the regex replacement function to capture the - # resources dictionary which can't be passed directly to this function - def replace_func(name): - key = name.group(1).lower() - if key in resources: - replacement = resources[key] - else: - replacement = name.group(1) - return "'" + replacement + "'" - - # Search recursively for all .dm and .dmm files - for (dirpath, dirs, files) in os.walk("."): - for name in files: - if source_pattern.match(name): - path = dirpath + '/' + name - source_file = file(path, "rt") - output_file = file(path + ".tmp", "wt") - - # Read file one line at a time and perform replacement of all - # single quoted resource names with the fullpath to that resource - # file. Write the updated text back out to a temporary file. - for row in source_file: - row = rename_pattern.sub(replace_func, row) - output_file.write(row) - - output_file.close() - source_file.close() - - # Delete original source file and replace with the temporary - # output. On Windows, an atomic rename() operation is not - # possible like it is under POSIX. - os.remove(path) - os.rename(path + ".tmp", path) - -dirs = read_filedirs("tgstation.dme"); -resources = index_files(dirs) -rewrite_sources(resources) +#!/usr/bin/env python + +import re, os, sys, fnmatch + + +# Regex pattern to extract the directory path in a #define FILE_DIR +filedir_pattern = re.compile(r'^#define\s*FILE_DIR\s*"(.*?)"') + +# Regex pattern to extract any single quoted piece of text. This can also +# match single quoted strings inside of double quotes, which is part of a +# regular text string and should not be replaced. The replacement function +# however will any match that doesn't appear to be a filename so these +# extra matches should not be a problem. +rename_pattern = re.compile(r"'(.+?)'") + +# Only filenames matching this pattern will have their resources renamed +source_pattern = re.compile(r"^.*?\.(dm|dmm)$") + +# Open the .dme file and return a list of all FILE_DIR paths in it +def read_filedirs(filename): + result = [] + dme_file = file(filename, "rt") + + # Read each line from the file and check for regex pattern match + for row in dme_file: + match = filedir_pattern.match(row) + if match: + result.append(match.group(1)) + + dme_file.close() + return result + +# Search through a list of directories, and build a dictionary which +# maps every file to its full pathname (relative to the .dme file) +# If the same filename appears in more than one directory, the earlier +# directory in the list takes preference. +def index_files(file_dirs): + result = {} + + # Reverse the directory list so the earlier directories take precedence + # by replacing the previously indexed file of the same name + for directory in reversed(file_dirs): + for name in os.listdir(directory): + # Replace backslash path separators on Windows with forward slash + # Force "name" to lowercase when used as a key since BYOND resource + # names are case insensitive, even on Linux. + if name.find(".") == -1: + continue + result[name.lower()] = directory.replace('\\', '/') + '/' + name + + return result + +# Recursively search for every .dm/.dmm file in the .dme file directory. For +# each file, search it for any resource names in single quotes, and replace +# them with the full path previously found by index_files() +def rewrite_sources(resources): + # Create a closure for the regex replacement function to capture the + # resources dictionary which can't be passed directly to this function + def replace_func(name): + key = name.group(1).lower() + if key in resources: + replacement = resources[key] + else: + replacement = name.group(1) + return "'" + replacement + "'" + + # Search recursively for all .dm and .dmm files + for (dirpath, dirs, files) in os.walk("."): + for name in files: + if source_pattern.match(name): + path = dirpath + '/' + name + source_file = file(path, "rt") + output_file = file(path + ".tmp", "wt") + + # Read file one line at a time and perform replacement of all + # single quoted resource names with the fullpath to that resource + # file. Write the updated text back out to a temporary file. + for row in source_file: + row = rename_pattern.sub(replace_func, row) + output_file.write(row) + + output_file.close() + source_file.close() + + # Delete original source file and replace with the temporary + # output. On Windows, an atomic rename() operation is not + # possible like it is under POSIX. + os.remove(path) + os.rename(path + ".tmp", path) + +dirs = read_filedirs("tgstation.dme"); +resources = index_files(dirs) +rewrite_sources(resources) diff --git a/tools/mapcapturemerge/.gitignore b/tools/mapcapturemerge/.gitignore index 20379651e836..508577d9cb19 100644 --- a/tools/mapcapturemerge/.gitignore +++ b/tools/mapcapturemerge/.gitignore @@ -1,3 +1,3 @@ -*.png -*.pyc -captures/*.png +*.png +*.pyc +captures/*.png diff --git a/tools/mapcapturemerge/imageHelpers.py b/tools/mapcapturemerge/imageHelpers.py index b7cdb309552b..90c501021eee 100644 --- a/tools/mapcapturemerge/imageHelpers.py +++ b/tools/mapcapturemerge/imageHelpers.py @@ -1,15 +1,15 @@ -from PIL import Image -import re -reg_findnums = re.compile("[0-9]+") - -class ImageFile: - def __init__(self, filename, path): - self.filename = path - nums = reg_findnums.findall(filename) - self.cords = (int(nums[0]), int(nums[1]), int(nums[2])) - self.range = int(nums[3]) - - def getImage(self): - im = Image.open(self.filename) - im.load() - return im +from PIL import Image +import re +reg_findnums = re.compile("[0-9]+") + +class ImageFile: + def __init__(self, filename, path): + self.filename = path + nums = reg_findnums.findall(filename) + self.cords = (int(nums[0]), int(nums[1]), int(nums[2])) + self.range = int(nums[3]) + + def getImage(self): + im = Image.open(self.filename) + im.load() + return im diff --git a/tools/mapcapturemerge/merge.py b/tools/mapcapturemerge/merge.py index 66fcae967bef..02966a937162 100644 --- a/tools/mapcapturemerge/merge.py +++ b/tools/mapcapturemerge/merge.py @@ -1,41 +1,41 @@ -from PIL import Image -import os, re, imageHelpers - -def BYONDCordsToPixelCords(B, BMax): - y = (BMax[1] - B[1]) - 31 - return (B[0] * 32 - 32, y * 32 - 32) - -image_location = os.path.join(os.getcwd(), 'captures') - -print("Map capture merger by Karolis") - -capture_images = [] -capture_re = re.compile("map_capture_x[0-9]+_y[0-9]+_z[0-9]+_r32\.png") -for image_file in os.listdir(image_location): - if os.path.isfile(os.path.join(image_location, image_file)) and capture_re.match(image_file): - #print(image_file) - capture_images.append(imageHelpers.ImageFile(image_file, os.path.join(image_location, image_file))) - -max_x = 0 -max_y = 0 -z_levels = [] -captures = {} -for capture in capture_images: - max_x = max(max_x, capture.cords[0] + capture.range) - max_y = max(max_y, capture.cords[1] + capture.range) - if not capture.cords[2] in z_levels: - z_levels.append(capture.cords[2]) - captures[capture.cords[2]] = [] - captures[capture.cords[2]].append(capture) - -maxCords = (max_x, max_y) - -for z in z_levels: - print("Merging map from " + str(z) + " z level.") - map = Image.new("RGBA", ((max_x - 1) * 32, (max_y - 1) * 32)) - for capture in captures[z]: - part = capture.getImage() - map.paste(part, BYONDCordsToPixelCords(capture.cords, maxCords)) - filename = "map_z" + str(z) + ".png" - print("Saving map as: " + filename) - map.save(filename) +from PIL import Image +import os, re, imageHelpers + +def BYONDCordsToPixelCords(B, BMax): + y = (BMax[1] - B[1]) - 31 + return (B[0] * 32 - 32, y * 32 - 32) + +image_location = os.path.join(os.getcwd(), 'captures') + +print("Map capture merger by Karolis") + +capture_images = [] +capture_re = re.compile("map_capture_x[0-9]+_y[0-9]+_z[0-9]+_r32\.png") +for image_file in os.listdir(image_location): + if os.path.isfile(os.path.join(image_location, image_file)) and capture_re.match(image_file): + #print(image_file) + capture_images.append(imageHelpers.ImageFile(image_file, os.path.join(image_location, image_file))) + +max_x = 0 +max_y = 0 +z_levels = [] +captures = {} +for capture in capture_images: + max_x = max(max_x, capture.cords[0] + capture.range) + max_y = max(max_y, capture.cords[1] + capture.range) + if not capture.cords[2] in z_levels: + z_levels.append(capture.cords[2]) + captures[capture.cords[2]] = [] + captures[capture.cords[2]].append(capture) + +maxCords = (max_x, max_y) + +for z in z_levels: + print("Merging map from " + str(z) + " z level.") + map = Image.new("RGBA", ((max_x - 1) * 32, (max_y - 1) * 32)) + for capture in captures[z]: + part = capture.getImage() + map.paste(part, BYONDCordsToPixelCords(capture.cords, maxCords)) + filename = "map_z" + str(z) + ".png" + print("Saving map as: " + filename) + map.save(filename) From f9ea0f27a6a377d3f163e800e3244752618527fb Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 8 Oct 2025 21:38:12 -0400 Subject: [PATCH 0813/1331] Remove global military branch holder --- code/controllers/subsystems/jobs.dm | 6 +- code/controllers/verbs.dm | 3 - code/datums/mil_ranks.dm | 111 +----------------- code/game/jobs/job/_job.dm | 14 +-- code/game/movietitles.dm | 2 +- code/game/objects/items/weapons/cards_ids.dm | 4 +- .../items/weapons/cards_ids_syndicate.dm | 8 +- code/game/world.dm | 1 - .../preference_setup/loadout/loadout.dm | 2 +- .../preference_setup/occupation/occupation.dm | 39 +++--- .../occupation/skill_selection.dm | 2 +- .../mob/new_player/preferences_setup.dm | 4 +- .../modular_computers/file_system/manifest.dm | 4 +- .../file_system/reports/crew_record.dm | 6 +- .../file_system/reports/people.dm | 2 +- code/modules/submaps/submap_join.dm | 7 +- maps/~mapsystem/map_ranks.dm | 105 ++++++++++++++++- maps/~mapsystem/maps.dm | 2 + 18 files changed, 153 insertions(+), 169 deletions(-) diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 88e40e689d9b..35519221dbfe 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -387,7 +387,7 @@ SUBSYSTEM_DEF(jobs) if(player.client.prefs.alternate_option == BE_ASSISTANT) var/datum/job/ass = global.using_map.default_job_type if((global.using_map.flags & MAP_HAS_BRANCH) && player.client.prefs.branches[initial(ass.title)]) - var/datum/mil_branch/branch = global.using_map.mil_branches.get_branch(player.client.prefs.branches[initial(ass.title)]) + var/datum/mil_branch/branch = global.using_map.get_branch(player.client.prefs.branches[initial(ass.title)]) ass = branch.assistant_job assign_role(player, initial(ass.title), mode = mode) //For ones returning to lobby @@ -475,9 +475,9 @@ SUBSYSTEM_DEF(jobs) if(job) if(H.client) if(global.using_map.flags & MAP_HAS_BRANCH) - H.char_branch = global.using_map.mil_branches.get_branch(H.client.prefs.branches[job_title]) + H.char_branch = global.using_map.get_branch(H.client.prefs.branches[job_title]) if(global.using_map.flags & MAP_HAS_RANK) - H.char_rank = global.using_map.mil_branches.get_rank(H.client.prefs.branches[job_title], H.client.prefs.ranks[job_title]) + H.char_rank = global.using_map.get_rank(H.client.prefs.branches[job_title], H.client.prefs.ranks[job_title]) // Transfers the skill settings for the job to the mob H.skillset.obtain_from_client(job, H.client) diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 7bdb23ec96af..f9f58592fe19 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -34,8 +34,5 @@ if("Alt Appearance Manager") debug_variables(GET_DECL(/decl/appearance_manager)) SSstatistics.add_field_details("admin_verb", "DAltAppearanceManager") - if("Military Branches") - debug_variables(mil_branches) - SSstatistics.add_field_details("admin_verb", "DMilBranches") message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") return diff --git a/code/datums/mil_ranks.dm b/code/datums/mil_ranks.dm index e0aa19bc4811..29a14bc5e985 100644 --- a/code/datums/mil_ranks.dm +++ b/code/datums/mil_ranks.dm @@ -2,93 +2,14 @@ * Datums for military branches and ranks * * Map datums can optionally specify a list of /datum/mil_branch paths. These paths - * are used to initialize the global mil_branches object, which contains a list of - * branch objects the map uses. Each branch definition specifies a list of + * are used to initialize ranks for the map, which contains a list of + * branch objects used on the map. Each branch definition specifies a list of * /datum/mil_rank paths, which are ranks available to that branch. * * Which branches and ranks can be selected for spawning is specifed in global.using_map * and each branch datum definition, respectively. */ -/** - * Global object for handling branches - */ -/datum/mil_branches - /// All branches that exist - var/list/branches = list() - /// Branches that a player can choose for spawning, not including species restrictions. - var/list/spawn_branches_ = list() - /// Branches that a player can choose for spawning, with species restrictions. Populated on a needed basis - var/list/spawn_branches_by_species_ = list() - -/** - * Retrieve branch object by branch name - */ -/datum/mil_branches/proc/get_branch(var/branch_name) - if(ispath(branch_name, /datum/mil_branch)) - var/datum/mil_branch/branch = branch_name - branch_name = initial(branch.name) - if(branch_name && branch_name != "None") - return branches[branch_name] - -/** - * Retrieve branch object by branch type - */ -/datum/mil_branches/proc/get_branch_by_type(var/branch_type) - for(var/name in branches) - if (istype(branches[name], branch_type)) - return branches[name] - -/** - * Retrieve a rank object from given branch by name - */ -/datum/mil_branches/proc/get_rank(var/branch_name, var/rank_name) - if(ispath(rank_name)) - var/datum/mil_rank/rank = rank_name - rank_name = initial(rank.name) - if(rank_name && rank_name != "None") - var/datum/mil_branch/branch = get_branch(branch_name) - if(branch) - return branch.ranks[rank_name] - -/** - * Return all spawn branches for the given input - */ -/datum/mil_branches/proc/spawn_branches(var/decl/species/S) - if(!S) - return spawn_branches_.Copy() - . = LAZYACCESS(spawn_branches_by_species_, S) - if(!.) - . = list() - LAZYSET(spawn_branches_by_species_, S, .) - for(var/spawn_branch in spawn_branches_) - if(!global.using_map.is_species_branch_restricted(S, spawn_branches_[spawn_branch])) - . += spawn_branch - -/** - * Return all spawn ranks for the given input - */ -/datum/mil_branches/proc/spawn_ranks(var/branch_name, var/decl/species/S) - var/datum/mil_branch/branch = get_branch(branch_name) - return branch && branch.spawn_ranks(S) - -/** - * Return a true value if branch_name is a valid spawn branch key - */ -/datum/mil_branches/proc/is_spawn_branch(var/branch_name, var/decl/species/S) - return (branch_name in spawn_branches(S)) - - -/** - * Return a true value if rank_name is a valid spawn rank in branch under branch_name - */ -/datum/mil_branches/proc/is_spawn_rank(var/branch_name, var/rank_name, var/decl/species/S) - var/datum/mil_branch/branch = get_branch(branch_name) - if(branch && (rank_name in branch.spawn_ranks(S))) - return TRUE - else - return FALSE - /** * A single military branch, such as Fleet or Marines */ @@ -140,34 +61,6 @@ if(!global.using_map.is_species_rank_restricted(S, src, spawn_ranks_[spawn_rank])) . += spawn_rank - -// todo: should this be on /datum/map? this will need heavy reworking if we promote submaps from second to first class map status anyway -/** - * Populate the global branches list from global.using_map - */ -/datum/map/proc/populate_branches() - if(!(global.using_map.flags & MAP_HAS_BRANCH) && !(global.using_map.flags & MAP_HAS_RANK)) - mil_branches.branches = null - mil_branches.spawn_branches_ = null - mil_branches.spawn_branches_by_species_ = null - return 1 - - mil_branches.branches = list() - mil_branches.spawn_branches_ = list() - mil_branches.spawn_branches_by_species_ = list() - for(var/branch_path in global.using_map.branch_types) - if(!ispath(branch_path, /datum/mil_branch)) - PRINT_STACK_TRACE("populate_branches() attempted to instantiate object with path [branch_path], which is not a subtype of /datum/mil_branch.") - continue - - var/datum/mil_branch/branch = new branch_path () - mil_branches.branches[branch.name] = branch - - if(branch_path in global.using_map.spawn_branch_types) - mil_branches.spawn_branches_[branch.name] = branch - - return 1 - /** * A military rank * diff --git a/code/game/jobs/job/_job.dm b/code/game/jobs/job/_job.dm index 40e6b29f42b3..ddd275fdf323 100644 --- a/code/game/jobs/job/_job.dm +++ b/code/game/jobs/job/_job.dm @@ -292,11 +292,9 @@ species_branch_rank_cache_[S] = list() . = species_branch_rank_cache_[S] - var/datum/mil_branches/branches = global.using_map.mil_branches - - var/spawn_branches = branches.spawn_branches(S) + var/spawn_branches = global.using_map.spawn_branches(S) for(var/branch_type in allowed_branches) - var/datum/mil_branch/branch = branches.get_branch_by_type(branch_type) + var/datum/mil_branch/branch = global.using_map.get_branch_by_type(branch_type) if(branch.name in spawn_branches) if(!allowed_ranks || !(global.using_map.flags & MAP_HAS_RANK)) LAZYADD(., branch.name) @@ -320,7 +318,7 @@ if(branch_name == "None") return 0 - var/datum/mil_branch/branch = global.using_map.mil_branches.get_branch(branch_name) + var/datum/mil_branch/branch = global.using_map.get_branch(branch_name) if(!branch) PRINT_STACK_TRACE("unknown branch \"[branch_name]\" passed to is_branch_allowed()") @@ -345,7 +343,7 @@ if(branch_name == "None" || rank_name == "None") return 0 - var/datum/mil_rank/rank = global.using_map.mil_branches.get_rank(branch_name, rank_name) + var/datum/mil_rank/rank = global.using_map.get_rank(branch_name, rank_name) if(!rank) PRINT_STACK_TRACE("unknown rank \"[rank_name]\" in branch \"[branch_name]\" passed to is_rank_allowed()") @@ -360,14 +358,14 @@ /datum/job/proc/get_branches() . = list() for(var/branch in allowed_branches) - var/datum/mil_branch/branch_datum = global.using_map.mil_branches.get_branch_by_type(branch) + var/datum/mil_branch/branch_datum = global.using_map.get_branch_by_type(branch) . += branch_datum.name return english_list(.) //Same as above but ranks /datum/job/proc/get_ranks(branch) . = list() - var/datum/mil_branch/branch_datum = global.using_map.mil_branches.get_branch(branch) + var/datum/mil_branch/branch_datum = global.using_map.get_branch(branch) for(var/datum/mil_rank/rank as anything in allowed_ranks) if(branch_datum && !(initial(rank.name) in branch_datum.ranks)) continue diff --git a/code/game/movietitles.dm b/code/game/movietitles.dm index 1fc735fc1306..7afbacf338d8 100644 --- a/code/game/movietitles.dm +++ b/code/game/movietitles.dm @@ -68,7 +68,7 @@ var/global/list/end_titles var/used_name = H.real_name var/datum/computer_file/report/crew_record/record = get_crewmember_record(H.real_name) if(record && record.get_rank()) - var/datum/mil_rank/rank = global.using_map.mil_branches.get_rank(record.get_branch(), record.get_rank()) + var/datum/mil_rank/rank = global.using_map.get_rank(record.get_branch(), record.get_rank()) if(rank.name_short) used_name = "[rank.name_short] [used_name]" var/showckey = 0 diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index 0fc57c0989d2..c256274d7da0 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -338,7 +338,7 @@ var/global/const/NO_EMAG_ACT = -50 return if(ispath(var_value, /datum/mil_branch) || istext(var_value)) - var/datum/mil_branch/new_branch = global.using_map.mil_branches.get_branch(var_value) + var/datum/mil_branch/new_branch = global.using_map.get_branch(var_value) if(new_branch) if(new_branch != id.military_branch) id.military_branch = new_branch @@ -369,7 +369,7 @@ var/global/const/NO_EMAG_ACT = -50 var_value = rank.name if(istext(var_value)) - var/new_rank = global.using_map.mil_branches.get_rank(id.military_branch.name, var_value) + var/new_rank = global.using_map.get_rank(id.military_branch.name, var_value) if(new_rank) id.military_rank = new_rank return diff --git a/code/game/objects/items/weapons/cards_ids_syndicate.dm b/code/game/objects/items/weapons/cards_ids_syndicate.dm index 62fd8c26363b..d57a0910a492 100644 --- a/code/game/objects/items/weapons/cards_ids_syndicate.dm +++ b/code/game/objects/items/weapons/cards_ids_syndicate.dm @@ -214,15 +214,15 @@ to_chat(user, "All information has been deleted from \the [src].") . = 1 if("Branch") - var/new_branch = sanitize(input(user,"What branch of service would you like to put on this card?","Agent Card Branch") as null|anything in global.using_map.mil_branches.spawn_branches()) + var/new_branch = sanitize(input(user,"What branch of service would you like to put on this card?","Agent Card Branch") as null|anything in global.using_map.spawn_branches()) if(!isnull(new_branch) && CanUseTopic(user, state)) - src.military_branch = global.using_map.mil_branches.spawn_branches()[new_branch] + src.military_branch = global.using_map.spawn_branches()[new_branch] to_chat(user, "Branch changed to '[military_branch.name]'.") . = 1 if("Rank") - var/new_rank = sanitize(input(user,"What rank would you like to put on this card?","Agent Card Rank") as null|anything in global.using_map.mil_branches.spawn_ranks(military_branch.name)) + var/new_rank = sanitize(input(user,"What rank would you like to put on this card?","Agent Card Rank") as null|anything in global.using_map.spawn_ranks(military_branch.name)) if(!isnull(new_rank) && CanUseTopic(user, state)) - src.military_rank = global.using_map.mil_branches.spawn_ranks(military_branch.name)[new_rank] + src.military_rank = global.using_map.spawn_ranks(military_branch.name)[new_rank] to_chat(user, "Rank changed to '[military_rank.name]'.") . = 1 diff --git a/code/game/world.dm b/code/game/world.dm index 266e234d0a1b..1c65910d510c 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -101,7 +101,6 @@ GLOBAL_PROTECTED_UNTYPED(game_id, null) update_holiday() //Uncommenting ALLOW_HOLIDAYS in configuration will enable this. try_load_alien_whitelist() investigate_reset() - global.using_map.populate_branches() // Precache/build trait trees. for(var/decl/trait/trait in decls_repository.get_decls_of_type_unassociated(/decl/trait)) trait.build_references() diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index 4aab1de30764..f893c8b49080 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -243,7 +243,7 @@ var/good_branch = 0 entry += "
    " for(var/branch in branches) - var/datum/mil_branch/player_branch = global.using_map.mil_branches.get_branch(branch) + var/datum/mil_branch/player_branch = global.using_map.get_branch(branch) if(player_branch.type in gear.allowed_branches) branch_checks += "[player_branch.name]" good_branch = 1 diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 944bc128c249..5613c8466500 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -81,8 +81,6 @@ if(!SSmapping || !SSjobs.job_lists_by_map_name) return - var/datum/mil_branches/using_branches = global.using_map.mil_branches - var/decl/species/S = pref.get_species_decl() . = list() . += "" @@ -121,9 +119,9 @@ var/datum/mil_branch/player_branch var/branch_string = "" var/rank_branch_string = "" - var/branch_rank = job.allowed_branches ? job.get_branch_rank(S) : using_branches.spawn_branches(S) + var/branch_rank = job.allowed_branches ? job.get_branch_rank(S) : global.using_map.spawn_branches(S) if(global.using_map && (global.using_map.flags & MAP_HAS_BRANCH) && LAZYLEN(branch_rank)) - player_branch = using_branches.get_branch(pref.branches[job.title]) + player_branch = global.using_map.get_branch(pref.branches[job.title]) if(player_branch) if(LAZYLEN(branch_rank) > 1) branch_string += "
    [player_branch.name_short || player_branch.name]-[player_rank.name_short || player_rank.name][skill_link]
    From ea50f3fc278164bd08cb483b69dddcc7acc86196 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 19 Oct 2025 23:14:55 +1100 Subject: [PATCH 0859/1331] Generalizing some default colour retrieval code. --- .../living/simple_animal/_simple_animal.dm | 14 +++++++++++++ .../mob/living/simple_animal/passive/deer.dm | 4 +++- .../mob/living/simple_animal/passive/fox.dm | 20 ++++++++++++++----- .../mob/living/simple_animal/passive/horse.dm | 2 +- .../living/simple_animal/passive/rabbit.dm | 17 ++++++++++++---- .../mob/living/simple_animal/passive/wolf.dm | 4 +++- 6 files changed, 49 insertions(+), 12 deletions(-) diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index 28c2a4c16e30..ab53e5630e3f 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -114,6 +114,10 @@ /mob/living/simple_animal/Initialize() . = ..() + if(isnull(draw_visible_overlays)) + var/list/defaults = get_default_animal_colours() + draw_visible_overlays = defaults?.Copy() // do not mutate static list + if(length(ability_handlers)) for(var/handler in ability_handlers) add_ability_handler(handler) @@ -127,6 +131,9 @@ minbodytemp = 0 check_mob_icon_states(TRUE) + if(length(draw_visible_overlays)) + update_icon() + if(isnull(base_animal_type)) base_animal_type = type if(LAZYLEN(natural_armor)) @@ -603,3 +610,10 @@ var/global/list/simplemob_icon_bitflag_cache = list() /mob/living/simple_animal/is_space_movement_permitted(allow_movement = FALSE) return skip_spacemove ? SPACE_MOVE_PERMITTED : ..() + +/mob/living/simple_animal/proc/get_default_animal_colour(marking_type) + var/list/colors = get_default_animal_colours() + return LAZYACCESS(colors, marking_type) // Return null if unset, rather than forcing COLOR_BLACK or such. + +/mob/living/simple_animal/proc/get_default_animal_colours() + return diff --git a/code/modules/mob/living/simple_animal/passive/deer.dm b/code/modules/mob/living/simple_animal/passive/deer.dm index da09424bce87..c9590c54714d 100644 --- a/code/modules/mob/living/simple_animal/passive/deer.dm +++ b/code/modules/mob/living/simple_animal/passive/deer.dm @@ -23,11 +23,13 @@ ai = /datum/mob_controller/passive/deer eye_color = "#1a1a1a" - draw_visible_overlays = list( +/mob/living/simple_animal/passive/deer/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#b39161", "markings" = "#3a3329", "socks" = "#ddd5c9" ) + return default_colors /mob/living/simple_animal/passive/deer/get_bodytype() return GET_DECL(/decl/bodytype/quadruped/animal/deer) diff --git a/code/modules/mob/living/simple_animal/passive/fox.dm b/code/modules/mob/living/simple_animal/passive/fox.dm index c2db673e6e85..09567e7abf19 100644 --- a/code/modules/mob/living/simple_animal/passive/fox.dm +++ b/code/modules/mob/living/simple_animal/passive/fox.dm @@ -9,12 +9,15 @@ pass_flags = PASS_FLAG_TABLE butchery_data = /decl/butchery_data/animal/fox eye_color = "#1d628a" - draw_visible_overlays = list( + ability_handlers = list(/datum/ability_handler/predator) + +/mob/living/simple_animal/passive/fox/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#ed5a20", "markings" = "#efe9e6", "socks" = "#36221b" ) - ability_handlers = list(/datum/ability_handler/predator) + return default_colors /mob/living/simple_animal/passive/fox/get_available_postures() var/static/list/available_postures = list( @@ -51,31 +54,38 @@ name = "arctic fox" desc = "A cunning and graceful predatory mammal, known for leaping headfirst into snowbanks while hunting burrowing rodents." eye_color = "#7a6f3b" - draw_visible_overlays = list( + +/mob/living/simple_animal/passive/fox/arctic/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#ccc496", "markings" = "#efe9e6", "socks" = "#cab9b1" ) + return default_colors /mob/living/simple_animal/passive/fox/silver name = "silver fox" desc = "A cunning and graceful predatory mammal, known for the rarity and high value of their pelts." eye_color = "#2db1c9" - draw_visible_overlays = list( + +/mob/living/simple_animal/passive/fox/silver/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#2c2c2a", "markings" = "#3d3b39", "socks" = "#746d66" ) + return default_colors /mob/living/simple_animal/passive/fox/sparkle name = "sparklefox" desc = "A cunning and graceful predatory mammal, known for being really into hardstyle." /mob/living/simple_animal/passive/fox/sparkle/Initialize() - eye_color = get_random_colour(TRUE) draw_visible_overlays = list( "base" = get_random_colour(), "markings" = get_random_colour(TRUE), "socks" = get_random_colour() ) + eye_color = get_random_colour(TRUE) . = ..() + diff --git a/code/modules/mob/living/simple_animal/passive/horse.dm b/code/modules/mob/living/simple_animal/passive/horse.dm index 0e2c0d0c023e..5ab8801cddd8 100644 --- a/code/modules/mob/living/simple_animal/passive/horse.dm +++ b/code/modules/mob/living/simple_animal/passive/horse.dm @@ -15,7 +15,7 @@ max_rider_size = MOB_SIZE_MEDIUM ai = /datum/mob_controller/passive/horse color = "#806146" // preview color - draw_visible_overlays = null // e.g. list("base" = "#806146") + draw_visible_overlays = list() // to avoid applying any defaults /datum/mob_controller/passive/horse emote_speech = list("Neigh!","NEIGH!","Neigh?") diff --git a/code/modules/mob/living/simple_animal/passive/rabbit.dm b/code/modules/mob/living/simple_animal/passive/rabbit.dm index 44afe0ed8698..4fe5386ebc36 100644 --- a/code/modules/mob/living/simple_animal/passive/rabbit.dm +++ b/code/modules/mob/living/simple_animal/passive/rabbit.dm @@ -11,11 +11,14 @@ ai = /datum/mob_controller/passive/rabbit butchery_data = /decl/butchery_data/animal/rabbit eye_color = COLOR_BLACK - draw_visible_overlays = list( + +/mob/living/simple_animal/passive/rabbit/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#e6e5da", "markings" = "#c8b1a5", "socks" = "#e6e5da" ) + return default_colors /datum/mob_controller/passive/rabbit emote_hear = list("chitters") @@ -31,30 +34,36 @@ /mob/living/simple_animal/passive/rabbit/brown name = "brown rabbit" butchery_data = /decl/butchery_data/animal/rabbit/brown - draw_visible_overlays = list( + +/mob/living/simple_animal/passive/rabbit/brown/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#62472b", "markings" = "#958279", "socks" = "#62472b" ) + return default_colors /mob/living/simple_animal/passive/rabbit/black name = "black rabbit" butchery_data = /decl/butchery_data/animal/rabbit/black - draw_visible_overlays = list( + +/mob/living/simple_animal/passive/rabbit/black/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#4f4f4f", "markings" = "#958279", "socks" = "#4f4f4f" ) + return default_colors /mob/living/simple_animal/passive/rabbit/sparkle name = "sparklerabbit" desc = "A hopping mammal with long ears and a love for raves." /mob/living/simple_animal/passive/rabbit/sparkle/Initialize() - eye_color = get_random_colour(TRUE) draw_visible_overlays = list( "base" = get_random_colour(), "markings" = get_random_colour(TRUE), "socks" = get_random_colour() ) + eye_color = get_random_colour(TRUE) . = ..() diff --git a/code/modules/mob/living/simple_animal/passive/wolf.dm b/code/modules/mob/living/simple_animal/passive/wolf.dm index 980c2ca729f2..aae33697bfb8 100644 --- a/code/modules/mob/living/simple_animal/passive/wolf.dm +++ b/code/modules/mob/living/simple_animal/passive/wolf.dm @@ -11,11 +11,13 @@ eye_color = "#9b7214" ability_handlers = list(/datum/ability_handler/predator) - draw_visible_overlays = list( +/mob/living/simple_animal/passive/wolf/get_default_animal_colours() + var/static/list/default_colors = list( "base" = "#6a6a6d", "markings" = "#574938", "socks" = "#41414d" ) + return default_colors /datum/mob_controller/passive/hunter/wolf emote_speech = list("Awoo!","Aroo!","Rrr!") From 1cc7512cd034438234c31391e7aaebf282329b1e Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 19 Oct 2025 22:03:17 +1100 Subject: [PATCH 0860/1331] Backporting animal ears from Pyrelight as biomod modpack. --- code/modules/mob/mob_snapshot.dm | 21 +++++- maps/modpack_testing/modpack_testing.dm | 1 + mods/content/biomods/_biomods.dm | 2 + mods/content/biomods/_biomods.dme | 9 +++ .../content/biomods/ears.dm | 14 ++-- mods/content/biomods/ears_animal.dm | 26 +++++++ mods/content/biomods/icons/ears.dmi | Bin 0 -> 2740 bytes mods/content/biomods/icons/tails.dmi | Bin 0 -> 6696 bytes mods/content/biomods/tails.dm | 68 ++++++++++++++++++ nebula.dme | 1 - 10 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 mods/content/biomods/_biomods.dm create mode 100644 mods/content/biomods/_biomods.dme rename code/modules/sprite_accessories/ears/accessory_ears_pointed.dm => mods/content/biomods/ears.dm (54%) create mode 100644 mods/content/biomods/ears_animal.dm create mode 100644 mods/content/biomods/icons/ears.dmi create mode 100644 mods/content/biomods/icons/tails.dmi create mode 100644 mods/content/biomods/tails.dm diff --git a/code/modules/mob/mob_snapshot.dm b/code/modules/mob/mob_snapshot.dm index 950dc9b2cce6..01398160314a 100644 --- a/code/modules/mob/mob_snapshot.dm +++ b/code/modules/mob/mob_snapshot.dm @@ -78,9 +78,26 @@ target.set_skin_tone(skin_tone) for(var/limb_data in extra_limbs) + + // Grab our limb type for checking. var/limb_path = extra_limbs[limb_data]["path"] - var/obj/item/organ/external/new_limb = new limb_path(null, null, src) - target.add_organ(new_limb, null, TRUE, FALSE, FALSE, TRUE) + + // For whatever reason, we already have a limb in this slot. + // Creating a new one without removing the old one would cause limb overwrite runtimes. + var/obj/item/organ/external/limb = target.get_organ(limb_data) + if(istype(limb)) + // TODO: some way to cleanly remove and restitch an organ up the limb chain. + if(length(limb.children)) + continue + // If it's already the appropriate type, we're probably safe to leave it. + if(limb.type == limb_path) + continue + // Snip off the limb so we can replace it without issues. + target.remove_organ(limb, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE) + + // Create and install the new limb. + target.add_organ(new limb_path(null, null, src), null, TRUE, FALSE, FALSE, TRUE) + extra_limbs = null // can't reuse it! for(var/obj/item/organ/organ in target.get_organs()) diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 2ad67edc3e01..bdb2a0ebdca8 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -8,6 +8,7 @@ #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" + #include "../../mods/content/biomods/_biomods.dme" #include "../../mods/content/blacksmithy/_blacksmithy.dme" #include "../../mods/content/blob/_blob.dme" #include "../../mods/content/breath_holding/_breath_holding.dme" diff --git a/mods/content/biomods/_biomods.dm b/mods/content/biomods/_biomods.dm new file mode 100644 index 000000000000..e0695f8190b7 --- /dev/null +++ b/mods/content/biomods/_biomods.dm @@ -0,0 +1,2 @@ +/decl/modpack/biomods + name = "Biomodding Content" \ No newline at end of file diff --git a/mods/content/biomods/_biomods.dme b/mods/content/biomods/_biomods.dme new file mode 100644 index 000000000000..cdb7470d9b3d --- /dev/null +++ b/mods/content/biomods/_biomods.dme @@ -0,0 +1,9 @@ +#ifndef MODPACK_BIOMODS +#define MODPACK_BIOMODS +// BEGIN_INCLUDE +#include "_biomods.dm" +#include "ears.dm" +#include "ears_animal.dm" +#include "tails.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/code/modules/sprite_accessories/ears/accessory_ears_pointed.dm b/mods/content/biomods/ears.dm similarity index 54% rename from code/modules/sprite_accessories/ears/accessory_ears_pointed.dm rename to mods/content/biomods/ears.dm index 303e0611e326..bf929c74e068 100644 --- a/code/modules/sprite_accessories/ears/accessory_ears_pointed.dm +++ b/mods/content/biomods/ears.dm @@ -1,24 +1,28 @@ -/decl/sprite_accessory/ears/pointed +/decl/sprite_accessory/ears/biomods + abstract_type = /decl/sprite_accessory/ears/biomods + icon = 'mods/content/biomods/icons/ears.dmi' + +/decl/sprite_accessory/ears/biomods/pointed name = "Pointed Ears" icon_state = "ears_pointy" uid = "accessory_ears_pointy" -/decl/sprite_accessory/ears/pointed_long +/decl/sprite_accessory/ears/biomods/pointed_long name = "Long Pointed Ears" icon_state = "ears_pointy_long" uid = "accessory_ears_pointy_long" -/decl/sprite_accessory/ears/pointed_down +/decl/sprite_accessory/ears/biomods/pointed_down name = "Pointed Ears (Downward)" icon_state = "ears_pointy_down" uid = "accessory_ears_pointy_down" -/decl/sprite_accessory/ears/pointed_long_down +/decl/sprite_accessory/ears/biomods/pointed_long_down name = "Long Pointed Ears (Downward)" icon_state = "ears_pointy_long_down" uid = "accessory_ears_long_down" -/decl/sprite_accessory/ears/elven +/decl/sprite_accessory/ears/biomods/elven name = "Elven Ears" icon_state = "elfs" uid = "accessory_ears_elfs" diff --git a/mods/content/biomods/ears_animal.dm b/mods/content/biomods/ears_animal.dm new file mode 100644 index 000000000000..3e39b6567dd1 --- /dev/null +++ b/mods/content/biomods/ears_animal.dm @@ -0,0 +1,26 @@ +/decl/sprite_accessory/ears/biomods/animal + name = "Animal Ears" + icon_state = "ears_plain" + uid = "accessory_ears_animal" + accessory_metadata_types = list(SAM_COLOR, SAM_COLOR_INNER) + +/decl/sprite_accessory/ears/biomods/animal/lupine + name = "Lupine Ears" + icon_state = "wolf" + uid = "accessory_ears_wolf" + +/decl/sprite_accessory/ears/biomods/animal/fennec + name = "Fennec Ears" + icon_state = "fennec" + uid = "accessory_ears_fennec" + +/decl/sprite_accessory/ears/biomods/animal/vulpine + name = "Vulpine Ears" + icon_state = "fox" + uid = "accessory_ears_fox" + +/decl/sprite_accessory/ears/biomods/antlers + name = "Antlers" + icon_state = "antlers" + uid = "accessory_ears_antlers" + accessory_metadata_types = list(SAM_COLOR) diff --git a/mods/content/biomods/icons/ears.dmi b/mods/content/biomods/icons/ears.dmi new file mode 100644 index 0000000000000000000000000000000000000000..229f9ee647955d6c35addf54a4219c72632e90a7 GIT binary patch literal 2740 zcmb_ec{tST9{-IkOT~#$$C8dq_sEu|BD=9olo+xOb&wb;W~jlCq(VGYnx>K_ON1;# zS!Pm}J0USy#u%fy$vQLUSO#Op`FWoE*SXLA=RW><-|zEo&-?v+KJVxI-aYH#yhrw+ zEC2v|TwNT`0RRLHfk|m8uwAfwcNXm4-S9pi?-&+$@ml1S_{i8807!iL@T-;VC!^hx zlQioLm({_dH^FXhl)ON#$4(v+)q&B}h9+)aar_Zar*{7HTW%*1)$Y4c^cmeQ$Fz1^ zB0b8Cnx|`Y#p1G=R*qNj?FUFFqqDv3E%udZ7sSLL`WTE^>KI*FgIXD13@q7;F)H55O!U|b5%G%po>*XK$7-Wn<+!P=a-fYD4GS5p0W)M~56?(v@-t|IJRyxBJ- z!REYW2M6uMGy?#cCRay0@5HBUMt=<5N4cB#$u9L}yNl6jaY^q~KxOQ7HYDFaK#_sc zcoDSfS;}s;WfGPE6PQUr6GbQZx|Xf2q{Q2&hxZ01q9Q^~2s4_lu0Ps-as__SeI;=& zJkb#f#3LS!4u`K`YE4WqSd*-|Q;mkCc|kcGdPz9RTZ1ulJDf9#9OiN6O7ak0`&IDb zJMX=zDTZj?K=D5)9|q%Rerm#~#jyOePUlR`8YjUpx0AD;brF`|el7=G=6dYywBh|< zE6JnIJg!a@+;jPB7YR=r#30aE-;4%w%ksICz`ahg>n~z6rH5yH{ZOH{YnQQ-Mkc@J z8WP-_@QrIhBO?h+1yWoS%^`nx^f6p@64*QnKBVw`43am!{#h}GAr$;>zgfQSlU5KSqU%mLy_i3X4}hj_%=PqB38GOnWlH@ zRON%e#bQFDyyn!ziyBXgO6nogDyg)&^tb0ndlyzJk7EQ9ll7ufjd0(Q35OTVEC%c2 zaXMF|BKP6V29NuhW%xi{ya)|$Btu%sIA{%`Z9UP_RSITlt$}cIf0jg=ubJ0!m6}i? z(~@JMHW|s>txbye)LH_Phu7CkUl*<*Yl|$p$%DD6OL8HMEEPUi@I$UxbU_}!u^37! zEL|m+d!jcTEDQwZFc{MwpMf;){ErZnV?ajcMU%8+~F`=w05x_j6U+ z2a?os7`JIdL{Q1f_R>n!vctT?*5sFU*bU6ucH*U-Bd9 z+S6`E#o669S8vq!d-NWz0uU9XBe4myB|dfE}{z7@G1Q55yAJSR#2Vs2k{FnqGo>(H&gXSYgH5X%<6I`nqk3Ds7j#|E5;hj z+#q>jxSplf!I{@!{>eoTwnZ9w4CnuRdVtlyGSBWV^aMi#Lh-!YwhOfnQXZ8$@TUq9 zrX(ZtAp{DGw_0m}op~ts;&Dc>&x^z)W<51|1mS%&MN&%RA=>aKH z{)|B!`I3h0y5%dI%3C`Io~fs2)Y9AtBA=|O#$4%X5E_sB_OX5-HccN*Utbn*V-ucu zA<9H7K}(O=sFO<;`7QDcu{*s6?=Ugk8_!(n74ATq<&&rw8-dgMPe$1G9iq+T{5Fc z!oa_BoCrBuh%kJ&KX!sbp|q4neIp{zY)ap8`_kX9Cz&+F)(8+nK{3KxmiIk-_KaP- z-}cib(`(2KcU;g7EzmTQBzMlB|3}LI1T?w~umM-;v3#qOLkYsp;Fm`%F3qhp?S~H7 zxhO`p9R0?};-}%VshJc4*&Ej%6HZ>hT(015$dTj{tv3_ghszT;Ed$7tPX&Y=3sf}S zYpMnyP1g&mZTK5o=5le?Ndo}|W~)TT#Y*z|-x~N>dWxz%sie9$)7c6{$trncS=Xfg zR6llk{5}pB=C?|~(b#OZJgQ$Dq`C_M+3{NSUr>~5_td#jBPwI~qjM_vb8~}~pu60M zN1ec~yStmnZ@UJZ%046_{ekD=)#R-X=8z0%Kj-th%o8YVHgcBrxvrNnF@dg2`Oado zrm0kFLXI-+QRS14nz`P>jEODxI7j9~&{mY}Sro%XxkE5&a^??%JvA8XYdHM|;Mj&FjIl zv7qt-rr&4pstGl?)9d!J+`V{p2=1vQTv}dUF3S^}oa!mG0oTC!@3-r{CIqtMb$<&8 Wf&vPS73Ik7PpK=^!?D^vTAMWl(8 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 M_jKST>eiwE0Z3(D`Tzg` literal 0 HcmV?d00001 diff --git a/mods/content/biomods/tails.dm b/mods/content/biomods/tails.dm new file mode 100644 index 000000000000..d42ff27af93c --- /dev/null +++ b/mods/content/biomods/tails.dm @@ -0,0 +1,68 @@ +/decl/sprite_accessory/tail/biomods + abstract_type = /decl/sprite_accessory/tail/biomods + icon = 'mods/content/biomods/icons/tails.dmi' + accessory_metadata_types = list(SAM_COLOR) + +/decl/sprite_accessory/tail/biomods/twotone + abstract_type = /decl/sprite_accessory/tail/biomods/twotone + accessory_metadata_types = list(SAM_COLOR, SAM_COLOR_INNER) + +/decl/sprite_accessory/tail/biomods/twotone/skunk + name = "Skunk Tail" + icon_state = "skunktail" + uid = "acc_tail_skunk" + +/decl/sprite_accessory/tail/biomods/twotone/vulpine + name = "Vulpine Tail" + icon_state = "vulptail" + uid = "acc_tail_vulpine" + +/decl/sprite_accessory/tail/biomods/twotone/tuft + name = "Tuft Tail" + icon_state = "deertail" + uid = "acc_tail_deer" + +/decl/sprite_accessory/tail/biomods/twotone/long_tuft + name = "Long Tufted Tail" + icon_state = "easterntail" + uid = "acc_tail_eastern" + +/decl/sprite_accessory/tail/biomods/horse + name = "Horse Tail" + icon_state = "horse" + uid = "acc_tail_horse" + +/decl/sprite_accessory/tail/biomods/fantail + name = "Fantail" + icon_state = "fantail" + uid = "acc_tail_fan" + +/decl/sprite_accessory/tail/biomods/wagtail + name = "Wagtail" + icon_state = "wagtail" + uid = "acc_tail_wag" + +/decl/sprite_accessory/tail/biomods/mouse + name = "Mouse Tail" + icon_state = "mouse" + uid = "acc_tail_mouse" + +/decl/sprite_accessory/tail/biomods/straight + name = "Straight Tail" + icon_state = "straighttail" + uid = "acc_tail_straight" + +/decl/sprite_accessory/tail/biomods/spiky + name = "Spiky Tail" + icon_state = "ztail" + uid = "acc_tail_ztail" + +/decl/sprite_accessory/tail/biomods/fox + name = "Fox Tail" + icon_state = "fox_tail" + uid = "acc_tail_fox" + +/decl/sprite_accessory/tail/biomods/wolf + name = "Wolf Tail" + icon_state = "wolf_tail" + uid = "acc_tail_wolf" diff --git a/nebula.dme b/nebula.dme index eed1fb548983..4042ee05fc6e 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3797,7 +3797,6 @@ #include "code\modules\sprite_accessories\cosmetics\_accessory_cosmetics.dm" #include "code\modules\sprite_accessories\ears\_accessory_ears.dm" #include "code\modules\sprite_accessories\ears\accessory_ears_antennae.dm" -#include "code\modules\sprite_accessories\ears\accessory_ears_pointed.dm" #include "code\modules\sprite_accessories\facial\_accessory_facial.dm" #include "code\modules\sprite_accessories\facial\accessory_facial_hair.dm" #include "code\modules\sprite_accessories\frills\_accessory_frills.dm" From e5a542bf8e01a6f41c288ab03384d3882b1f044b Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Tue, 28 Oct 2025 19:47:46 +1100 Subject: [PATCH 0861/1331] Adding UID to flooring decls. --- code/game/turfs/flooring/_flooring.dm | 1 + code/game/turfs/flooring/flooring_carpet.dm | 28 +++++++++++++------ code/game/turfs/flooring/flooring_concrete.dm | 3 ++ code/game/turfs/flooring/flooring_grass.dm | 3 ++ .../game/turfs/flooring/flooring_holowater.dm | 1 + code/game/turfs/flooring/flooring_lava.dm | 1 + code/game/turfs/flooring/flooring_misc.dm | 7 +++++ code/game/turfs/flooring/flooring_mud.dm | 3 ++ code/game/turfs/flooring/flooring_natural.dm | 6 ++++ code/game/turfs/flooring/flooring_path.dm | 4 +++ code/game/turfs/flooring/flooring_plating.dm | 1 + .../turfs/flooring/flooring_reinforced.dm | 12 ++++++++ code/game/turfs/flooring/flooring_rock.dm | 1 + code/game/turfs/flooring/flooring_sand.dm | 6 ++++ code/game/turfs/flooring/flooring_snow.dm | 3 ++ code/game/turfs/flooring/flooring_tiled.dm | 18 ++++++++++++ code/game/turfs/flooring/flooring_wood.dm | 20 +++++++++++++ .../psionics/system/psionics/null/flooring.dm | 1 + mods/gamemodes/cult/flooring.dm | 1 + .../mobs/dionaea/mob/gestalt/gestalt_turfs.dm | 1 + mods/species/ascent/turfs/ship.dm | 2 ++ mods/species/skrell/turfs/flooring.dm | 6 ++++ 22 files changed, 120 insertions(+), 9 deletions(-) diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index a4e97fe7d360..f5f8083873f8 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -9,6 +9,7 @@ var/global/list/flooring_cache = list() /decl/flooring abstract_type = /decl/flooring + decl_flags = DECL_FLAG_MANDATORY_UID var/name var/desc diff --git a/code/game/turfs/flooring/flooring_carpet.dm b/code/game/turfs/flooring/flooring_carpet.dm index 8c7be4c850f1..4236c3c2597b 100644 --- a/code/game/turfs/flooring/flooring_carpet.dm +++ b/code/game/turfs/flooring/flooring_carpet.dm @@ -11,6 +11,7 @@ footstep_type = /decl/footsteps/carpet force_material = /decl/material/solid/organic/cloth constructed = TRUE + uid = "floor_carpet" burned_states = list( "burned0", "burned1" @@ -27,49 +28,58 @@ name = "blue carpet" icon_base = "blue1" build_type = /obj/item/stack/tile/carpet/blue + uid = "floor_carpet_blue" /decl/flooring/carpet/blue2 name = "pale blue carpet" icon_base = "blue2" build_type = /obj/item/stack/tile/carpet/blue2 + uid = "floor_carpet_blue2" /decl/flooring/carpet/blue3 name = "sea blue carpet" icon_base = "blue3" build_type = /obj/item/stack/tile/carpet/blue3 + uid = "floor_carpet_blue3" /decl/flooring/carpet/magenta name = "magenta carpet" icon_base = "purple" build_type = /obj/item/stack/tile/carpet/magenta + uid = "floor_carpet_magenta" /decl/flooring/carpet/purple name = "purple carpet" icon_base = "purple" build_type = /obj/item/stack/tile/carpet/purple + uid = "floor_carpet_purple" /decl/flooring/carpet/orange name = "orange carpet" icon_base = "orange" build_type = /obj/item/stack/tile/carpet/orange + uid = "floor_carpet_orange" /decl/flooring/carpet/green name = "green carpet" icon_base = "green" build_type = /obj/item/stack/tile/carpet/green + uid = "floor_carpet_green" /decl/flooring/carpet/red name = "red carpet" icon_base = "red" build_type = /obj/item/stack/tile/carpet/red + uid = "floor_carpet_red" /decl/flooring/carpet/rustic - name = "rustic carpet" - desc = "A stretch of simple woven carpet. Cozy, but a little itchy." - icon = 'icons/turf/flooring/simple_carpet.dmi' - icon_base = "carpet" - build_type = /obj/item/stack/tile/carpet/rustic - can_paint = TRUE - color = null - broken_states = null - burned_states = null + name = "rustic carpet" + desc = "A stretch of simple woven carpet. Cozy, but a little itchy." + icon = 'icons/turf/flooring/simple_carpet.dmi' + icon_base = "carpet" + build_type = /obj/item/stack/tile/carpet/rustic + can_paint = TRUE + color = null + broken_states = null + burned_states = null + uid = "floor_carpet_rustic" diff --git a/code/game/turfs/flooring/flooring_concrete.dm b/code/game/turfs/flooring/flooring_concrete.dm index 4b747fc28d2c..e5d6edd5f174 100644 --- a/code/game/turfs/flooring/flooring_concrete.dm +++ b/code/game/turfs/flooring/flooring_concrete.dm @@ -6,14 +6,17 @@ has_base_range = null force_material = /decl/material/solid/stone/concrete constructed = TRUE + uid = "floor_concrete" /decl/flooring/concrete/reinforced name = "reinforced concrete" icon_base = "hexacrete" desc = "A flat stretch of stone-like artificial material. It has been reinforced with an unknown compound." + uid = "floor_concrete_reinf" /decl/flooring/concrete/asphalt name = "asphalt" color = COLOR_GRAY40 icon_base = "concrete" desc = "A stretch of rough blacktop, probably part of a road." + uid = "floor_asphalt" diff --git a/code/game/turfs/flooring/flooring_grass.dm b/code/game/turfs/flooring/flooring_grass.dm index bc340140b5d5..dc53aaf46808 100644 --- a/code/game/turfs/flooring/flooring_grass.dm +++ b/code/game/turfs/flooring/flooring_grass.dm @@ -13,6 +13,7 @@ flooring_flags = TURF_REMOVE_SHOVEL force_material = /decl/material/solid/organic/plantmatter/grass growth_value = 1.2 // Shouldn't really matter since you can't plant on grass, it turns to dirt first. + uid = "floor_grass" var/harvestable = FALSE /decl/flooring/grass/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) @@ -33,6 +34,7 @@ has_base_range = null icon_edge_layer = FLOOR_EDGE_GRASS_WILD harvestable = TRUE + uid = "floor_grass_wild" /decl/flooring/grass/wild/get_movable_alpha_mask_state(atom/movable/mover) . = ..() || "mask_grass" @@ -53,3 +55,4 @@ color = "#5e7a3b" build_type = /obj/item/stack/tile/grass force_material = /decl/material/solid/organic/plastic + uid = "floor_grass_fake" diff --git a/code/game/turfs/flooring/flooring_holowater.dm b/code/game/turfs/flooring/flooring_holowater.dm index d873db21a10e..6bfe235d67e1 100644 --- a/code/game/turfs/flooring/flooring_holowater.dm +++ b/code/game/turfs/flooring/flooring_holowater.dm @@ -8,3 +8,4 @@ footstep_type = /decl/footsteps/water holographic = TRUE constructed = TRUE + uid = "floor_water_fake" diff --git a/code/game/turfs/flooring/flooring_lava.dm b/code/game/turfs/flooring/flooring_lava.dm index d8137d15b356..adf4f4b4f1af 100644 --- a/code/game/turfs/flooring/flooring_lava.dm +++ b/code/game/turfs/flooring/flooring_lava.dm @@ -10,6 +10,7 @@ turf_light_color = LIGHT_COLOR_LAVA turf_light_range = 2 turf_light_power = 0.7 + uid = "floor_lava" /decl/flooring/lava/handle_environment_proc(turf/floor/target) . = PROCESS_KILL diff --git a/code/game/turfs/flooring/flooring_misc.dm b/code/game/turfs/flooring/flooring_misc.dm index d72c2f54d96d..893b2e72dee4 100644 --- a/code/game/turfs/flooring/flooring_misc.dm +++ b/code/game/turfs/flooring/flooring_misc.dm @@ -9,6 +9,7 @@ footstep_type = /decl/footsteps/tiles force_material = /decl/material/solid/organic/plastic constructed = TRUE + uid = "floor_lino" /decl/flooring/crystal name = "crystal flooring" @@ -20,6 +21,7 @@ color = "#00ffe1" force_material = /decl/material/solid/gemstone/crystal constructed = TRUE + uid = "floor_crystal" /decl/flooring/glass name = "glass flooring" @@ -37,6 +39,7 @@ z_flags = ZM_MIMIC_DEFAULTS force_material = /decl/material/solid/glass constructed = TRUE + uid = "floor_glass" /decl/flooring/glass/boro name = "borosilicate glass flooring" @@ -44,6 +47,7 @@ color = GLASS_COLOR_SILICATE damage_temperature = T0C + 4000 force_material = /decl/material/solid/glass/borosilicate + uid = "floor_glass_boro" /decl/flooring/pool name = "pool floor" @@ -57,6 +61,7 @@ force_material = /decl/material/solid/stone/ceramic constructed = TRUE gender = NEUTER + uid = "floor_pool" /decl/flooring/woven name = "woven floor" @@ -71,6 +76,7 @@ force_material = /decl/material/solid/organic/plantmatter/grass/dry constructed = TRUE gender = NEUTER + uid = "floor_woven" /decl/flooring/straw name = "straw floor" @@ -86,3 +92,4 @@ force_material = /decl/material/solid/organic/plantmatter/grass/dry constructed = TRUE gender = NEUTER + uid = "floor_straw" diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index 6265343f2e7f..001a0c33ce90 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -11,6 +11,7 @@ growth_value = 1.1 can_collect = TRUE print_type = /obj/effect/footprints + uid = "floor_mud" /decl/flooring/mud/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) if(!target.reagents?.total_volume) @@ -43,6 +44,7 @@ color = "#ae9e66" dirt_color = "#ae9e66" force_material = /decl/material/solid/soil + uid = "floor_dry_mud" /decl/flooring/dry_mud/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) @@ -64,6 +66,7 @@ turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/soil growth_value = 1 + uid = "floor_dirt" /decl/flooring/dirt/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) diff --git a/code/game/turfs/flooring/flooring_natural.dm b/code/game/turfs/flooring/flooring_natural.dm index 63d3b2ec93c5..17bf186c0ad9 100644 --- a/code/game/turfs/flooring/flooring_natural.dm +++ b/code/game/turfs/flooring/flooring_natural.dm @@ -8,6 +8,7 @@ force_material = /decl/material/solid/sand gender = NEUTER footstep_type = /decl/footsteps/sand + uid = "floor_seafloor" /decl/flooring/shrouded name = "packed sand" @@ -19,6 +20,7 @@ turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/sand footstep_type = /decl/footsteps/asteroid + uid = "floor_shrouded" /decl/flooring/meat name = "fleshy ground" @@ -30,6 +32,7 @@ footstep_type = /decl/footsteps/mud force_material = /decl/material/solid/organic/meat print_type = /obj/effect/footprints + uid = "floor_meat" /decl/flooring/barren name = "ground" @@ -42,6 +45,7 @@ icon_edge_layer = FLOOR_EDGE_BARREN force_material = /decl/material/solid/soil growth_value = 0.1 + uid = "floor_barren" /decl/flooring/clay name = "clay" @@ -55,6 +59,7 @@ growth_value = 1.2 can_collect = TRUE print_type = /obj/effect/footprints + uid = "floor_clay" /decl/flooring/ice name = "ice" @@ -63,6 +68,7 @@ icon_base = "ice" color = COLOR_LIQUID_WATER force_material = /decl/material/solid/ice + uid = "floor_ice" /decl/flooring/ice/update_turf_icon(turf/floor/target) . = ..() diff --git a/code/game/turfs/flooring/flooring_path.dm b/code/game/turfs/flooring/flooring_path.dm index cd821d713a86..c0de0be830eb 100644 --- a/code/game/turfs/flooring/flooring_path.dm +++ b/code/game/turfs/flooring/flooring_path.dm @@ -7,6 +7,7 @@ neighbour_type = /decl/flooring/path color = null constructed = TRUE + uid = "floor_path" // If null, this is just skipped. var/paving_adjective = "cobbled" @@ -28,6 +29,7 @@ icon_edge_layer = FLOOR_EDGE_PATH flooring_flags = TURF_REMOVE_CROWBAR has_base_range = 1 + uid = "floor_path_cobble" /decl/flooring/path/running_bond name = "stone path" @@ -35,6 +37,7 @@ icon_base = "runningbond" has_base_range = 3 gender = NEUTER + uid = "floor_path_bond" /decl/flooring/path/herringbone name = "stone path" @@ -42,3 +45,4 @@ icon_base = "herringbone" has_base_range = null gender = NEUTER + uid = "floor_path_herring" diff --git a/code/game/turfs/flooring/flooring_plating.dm b/code/game/turfs/flooring/flooring_plating.dm index 505bad7a3edc..2b51940d1336 100644 --- a/code/game/turfs/flooring/flooring_plating.dm +++ b/code/game/turfs/flooring/flooring_plating.dm @@ -17,3 +17,4 @@ "broken3", "broken4" ) + uid = "floor_plating" diff --git a/code/game/turfs/flooring/flooring_reinforced.dm b/code/game/turfs/flooring/flooring_reinforced.dm index 97ce3b7302e4..38234d29ce8e 100644 --- a/code/game/turfs/flooring/flooring_reinforced.dm +++ b/code/game/turfs/flooring/flooring_reinforced.dm @@ -23,6 +23,7 @@ "broken3", "broken4" ) + uid = "floor_reinf" /decl/flooring/reinforced/circuit name = "processing strata" @@ -36,10 +37,12 @@ turf_light_range = 2 turf_light_power = 3 turf_light_color = COLOR_BLUE + uid = "floor_reinf_circ" /decl/flooring/reinforced/circuit/green icon_base = "gcircuit" turf_light_color = COLOR_GREEN + uid = "floor_reinf_gcirc" /decl/flooring/reinforced/circuit/red icon_base = "rcircuit" @@ -47,6 +50,7 @@ can_paint = FALSE turf_light_power = 2 turf_light_color = COLOR_RED + uid = "floor_reinf_rcirc" /decl/flooring/reinforced/shuttle name = "floor" @@ -57,24 +61,32 @@ can_paint = TRUE can_engrave = FALSE gender = NEUTER + uid = "floor_reinf_shuttle" /decl/flooring/reinforced/shuttle/blue icon_base = "floor" + uid = "floor_reinf_shuttle_blue" /decl/flooring/reinforced/shuttle/yellow icon_base = "floor2" + uid = "floor_reinf_shuttle_yellow" /decl/flooring/reinforced/shuttle/white icon_base = "floor3" + uid = "floor_reinf_shuttle_white" /decl/flooring/reinforced/shuttle/red icon_base = "floor4" + uid = "floor_reinf_shuttle_red" /decl/flooring/reinforced/shuttle/purple icon_base = "floor5" + uid = "floor_reinf_shuttle_purple" /decl/flooring/reinforced/shuttle/darkred icon_base = "floor6" + uid = "floor_reinf_shuttle_darkred" /decl/flooring/reinforced/shuttle/black icon_base = "floor7" + uid = "floor_reinf_shuttle_black" diff --git a/code/game/turfs/flooring/flooring_rock.dm b/code/game/turfs/flooring/flooring_rock.dm index 89b3fd455353..b5935e2109cc 100644 --- a/code/game/turfs/flooring/flooring_rock.dm +++ b/code/game/turfs/flooring/flooring_rock.dm @@ -7,6 +7,7 @@ color = null icon_edge_layer = FLOOR_EDGE_VOLCANIC gender = NEUTER + uid = "floor_reinf_shuttle_rock" /decl/flooring/rock/update_turf_strings(turf/floor/target) var/decl/material/material = target?.get_material() diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index caf29c810493..3a7c0843f653 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -10,6 +10,7 @@ force_material = /decl/material/solid/sand can_collect = TRUE print_type = /obj/effect/footprints + uid = "floor_sand" /decl/flooring/sand/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) if((exposed_temperature > T0C + 1700 && prob(5)) || exposed_temperature > T0C + 3000) @@ -31,15 +32,18 @@ color = "#d2e0b7" dirt_color = "#d2e0b7" footstep_type = /decl/footsteps/sand + uid = "floor_sand_chlorine" /decl/flooring/sand/chlorine/marsh name = "chlorine marsh" desc = "A pool of noxious liquid chlorine. It's full of silt and plant matter." + uid = "floor_sand_chlorine_marsh" /decl/flooring/sand/fake name = "holosand" desc = "Uncomfortably coarse and gritty for a hologram." holographic = TRUE + uid = "floor_sand_fake" /decl/flooring/fake_space name = "\proper space" @@ -49,6 +53,7 @@ has_base_range = 25 holographic = TRUE gender = NEUTER + uid = "floor_space_fake" /decl/flooring/melted_sand name = "molten silica" @@ -57,3 +62,4 @@ icon_base = "glass" has_base_range = null force_material = /decl/material/solid/glass + uid = "floor_sand_melted" diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 8dabb7167af5..78e5b5f0b486 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -11,6 +11,7 @@ can_collect = TRUE print_type = /obj/effect/footprints drop_material_on_remove = TRUE + uid = "floor_snow" /decl/flooring/snow/get_movement_delay(var/travel_dir, var/mob/mover) . = ..() @@ -51,8 +52,10 @@ icon = 'icons/turf/flooring/snow.dmi' icon_base = "permafrost" force_material = /decl/material/solid/ice + uid = "floor_permafrost" /decl/flooring/snow/fake name = "holosnow" desc = "Not quite the same as snow on an entertainment terminal, but close." holographic = TRUE + uid = "floor_snow_fake" diff --git a/code/game/turfs/flooring/flooring_tiled.dm b/code/game/turfs/flooring/flooring_tiled.dm index b8cf9e4c0154..e2b4af68c879 100644 --- a/code/game/turfs/flooring/flooring_tiled.dm +++ b/code/game/turfs/flooring/flooring_tiled.dm @@ -26,34 +26,41 @@ "broken3", "broken4" ) + uid = "floor_tiled" /decl/flooring/tiling/mono icon_base = "monotile" build_type = /obj/item/stack/tile/mono + uid = "floor_tiled_mono" /decl/flooring/tiling/mono/dark color = COLOR_DARK_GRAY build_type = /obj/item/stack/tile/mono/dark + uid = "floor_tiled_dark_mono" /decl/flooring/tiling/mono/white icon_base = "monotile_light" color = COLOR_OFF_WHITE build_type = /obj/item/stack/tile/mono/white + uid = "floor_tiled_mono_white" /decl/flooring/tiling/white icon_base = "tiled_light" desc = "A layer of sterile white tiles." color = COLOR_OFF_WHITE build_type = /obj/item/stack/tile/floor_white + uid = "floor_tiled_white" /decl/flooring/tiling/dark desc = "A layer of ominously dark tiles." color = COLOR_DARK_GRAY build_type = /obj/item/stack/tile/floor_dark + uid = "floor_tiled_dark" /decl/flooring/tiling/dark/mono icon_base = "monotile" build_type = null + uid = "floor_tiled_dark_monotile" /decl/flooring/tiling/freezer desc = "A section of non-slip tiles suitable for a cool room or freezer." @@ -61,48 +68,59 @@ color = null flooring_flags = TURF_REMOVE_CROWBAR build_type = /obj/item/stack/tile/floor_freezer + uid = "floor_tiled_freezer" /decl/flooring/tiling/tech icon = 'icons/turf/flooring/techfloor.dmi' icon_base = "techfloor_gray" build_type = /obj/item/stack/tile/techgrey color = null + uid = "floor_tiled_tech" /decl/flooring/tiling/tech/grid icon_base = "techfloor_grid" build_type = /obj/item/stack/tile/techgrid + uid = "floor_tiled_tech_grid" /decl/flooring/tiling/new_tile icon_base = "tile_full" color = null build_type = null + uid = "floor_tiled_new" /decl/flooring/tiling/new_tile/cargo_one icon_base = "cargo_one_full" build_type = null + uid = "floor_tiled_cargo" /decl/flooring/tiling/new_tile/kafel icon_base = "kafel_full" build_type = null + uid = "floor_tiled_kafel" /decl/flooring/tiling/stone icon_base = "stone" build_type = /obj/item/stack/tile/stone + uid = "floor_tiled_stone" /decl/flooring/tiling/new_tile/techmaint icon_base = "techmaint" build_type = /obj/item/stack/tile/techmaint + uid = "floor_tiled_techmaint" /decl/flooring/tiling/new_tile/monofloor icon_base = "monofloor" color = COLOR_GUNMETAL + uid = "floor_tiled_monofloor" /decl/flooring/tiling/new_tile/steel_grid icon_base = "grid" color = COLOR_GUNMETAL build_type = /obj/item/stack/tile/grid + uid = "floor_tiled_steel_grid" /decl/flooring/tiling/new_tile/steel_ridged icon_base = "ridged" color = COLOR_GUNMETAL build_type = /obj/item/stack/tile/ridge + uid = "floor_tiled_steel_ridged" diff --git a/code/game/turfs/flooring/flooring_wood.dm b/code/game/turfs/flooring/flooring_wood.dm index d7352fb03ae2..20a479a891e0 100644 --- a/code/game/turfs/flooring/flooring_wood.dm +++ b/code/game/turfs/flooring/flooring_wood.dm @@ -22,30 +22,37 @@ "broken5", "broken6" ) + uid = "floor_wood" /decl/flooring/wood/mahogany build_type = /obj/item/stack/tile/wood/mahogany force_material = /decl/material/solid/organic/wood/mahogany + uid = "floor_wood_mahogany" /decl/flooring/wood/maple build_type = /obj/item/stack/tile/wood/maple force_material = /decl/material/solid/organic/wood/maple + uid = "floor_wood_maple" /decl/flooring/wood/ebony build_type = /obj/item/stack/tile/wood/ebony force_material = /decl/material/solid/organic/wood/ebony + uid = "floor_wood_ebony" /decl/flooring/wood/walnut build_type = /obj/item/stack/tile/wood/walnut force_material = /decl/material/solid/organic/wood/walnut + uid = "floor_wood_walnut" /decl/flooring/wood/bamboo build_type = /obj/item/stack/tile/wood/bamboo force_material = /decl/material/solid/organic/wood/bamboo + uid = "floor_wood_bamboo" /decl/flooring/wood/yew build_type = /obj/item/stack/tile/wood/yew force_material = /decl/material/solid/organic/wood/yew + uid = "floor_wood_yew" // Rough-hewn floors. /decl/flooring/wood/rough @@ -57,30 +64,37 @@ has_base_range = 3 build_type = /obj/item/stack/tile/wood/rough broken_states = null + uid = "floor_wood_rough" /decl/flooring/wood/rough/mahogany build_type = /obj/item/stack/tile/wood/rough/mahogany force_material = /decl/material/solid/organic/wood/mahogany + uid = "floor_wood_rough_mahogany" /decl/flooring/wood/rough/maple build_type = /obj/item/stack/tile/wood/rough/maple force_material = /decl/material/solid/organic/wood/maple + uid = "floor_wood_rough_maple" /decl/flooring/wood/rough/ebony build_type = /obj/item/stack/tile/wood/rough/ebony force_material = /decl/material/solid/organic/wood/ebony + uid = "floor_wood_rough_ebony" /decl/flooring/wood/rough/walnut build_type = /obj/item/stack/tile/wood/rough/walnut force_material = /decl/material/solid/organic/wood/walnut + uid = "floor_wood_rough_walnut" /decl/flooring/wood/rough/bamboo build_type = /obj/item/stack/tile/wood/rough/bamboo force_material = /decl/material/solid/organic/wood/bamboo + uid = "floor_wood_rough_bamboo" /decl/flooring/wood/rough/yew build_type = /obj/item/stack/tile/wood/rough/yew force_material = /decl/material/solid/organic/wood/yew + uid = "floor_wood_rough_yew" // Chipboard/wood laminate floors. Uses older icons. /decl/flooring/laminate @@ -106,23 +120,29 @@ "broken5", "broken6" ) + uid = "floor_wood_lami" /decl/flooring/laminate/mahogany build_type = /obj/item/stack/tile/wood/laminate/mahogany force_material = /decl/material/solid/organic/wood/chipboard/mahogany + uid = "floor_wood_lami_mahogany" /decl/flooring/laminate/maple build_type = /obj/item/stack/tile/wood/laminate/maple force_material = /decl/material/solid/organic/wood/chipboard/maple + uid = "floor_wood_lami_maple" /decl/flooring/laminate/ebony build_type = /obj/item/stack/tile/wood/laminate/ebony force_material = /decl/material/solid/organic/wood/chipboard/ebony + uid = "floor_wood_lami_ebony" /decl/flooring/laminate/walnut build_type = /obj/item/stack/tile/wood/laminate/walnut force_material = /decl/material/solid/organic/wood/chipboard/walnut + uid = "floor_wood_lami_walnut" /decl/flooring/laminate/yew build_type = /obj/item/stack/tile/wood/laminate/yew force_material = /decl/material/solid/organic/wood/chipboard/yew + uid = "floor_wood_lami_yew" diff --git a/mods/content/psionics/system/psionics/null/flooring.dm b/mods/content/psionics/system/psionics/null/flooring.dm index b65ad250dd3e..581466a484e1 100644 --- a/mods/content/psionics/system/psionics/null/flooring.dm +++ b/mods/content/psionics/system/psionics/null/flooring.dm @@ -12,6 +12,7 @@ flooring_flags = TURF_REMOVE_SCREWDRIVER build_type = /obj/item/stack/tile/floor_nullglass psi_null = TRUE + uid = "floor_tiled_nullglass" /obj/item/stack/tile/floor_nullglass name = "nullglass floor tile" diff --git a/mods/gamemodes/cult/flooring.dm b/mods/gamemodes/cult/flooring.dm index 7169311366c7..17952ec6373f 100644 --- a/mods/gamemodes/cult/flooring.dm +++ b/mods/gamemodes/cult/flooring.dm @@ -6,6 +6,7 @@ build_type = null turf_flags = TURF_ACID_IMMUNE | TURF_REMOVE_WRENCH can_paint = FALSE + uid = "floor_cult" /decl/flooring/reinforced/cult/on_flooring_remove(turf/removing_from) var/decl/special_role/cultist/cult = GET_DECL(/decl/special_role/cultist) diff --git a/mods/mobs/dionaea/mob/gestalt/gestalt_turfs.dm b/mods/mobs/dionaea/mob/gestalt/gestalt_turfs.dm index ea5c20e36ef0..04d498ef7613 100644 --- a/mods/mobs/dionaea/mob/gestalt/gestalt_turfs.dm +++ b/mods/mobs/dionaea/mob/gestalt/gestalt_turfs.dm @@ -4,6 +4,7 @@ icon = 'mods/mobs/dionaea/icons/turfs.dmi' icon_base = "floor" flooring_flags = TURF_ACID_IMMUNE | TURF_REMOVE_SHOVEL + uid = "floor_diona" /turf/floor/diona name = "biomass" diff --git a/mods/species/ascent/turfs/ship.dm b/mods/species/ascent/turfs/ship.dm index e62ba1cbab16..320a2d8c30e2 100644 --- a/mods/species/ascent/turfs/ship.dm +++ b/mods/species/ascent/turfs/ship.dm @@ -3,6 +3,7 @@ icon = 'icons/turf/flooring/alium.dmi' burned_states = null broken_states = null + uid = "floor_ascent_plating" /decl/flooring/tiling_ascent name = "floor" @@ -15,6 +16,7 @@ constructed = TRUE burned_states = null broken_states = null + uid = "floor_ascent_tiled" /turf/wall/ascent color = COLOR_PURPLE diff --git a/mods/species/skrell/turfs/flooring.dm b/mods/species/skrell/turfs/flooring.dm index 8ddfdf54d665..0ce56ba08591 100644 --- a/mods/species/skrell/turfs/flooring.dm +++ b/mods/species/skrell/turfs/flooring.dm @@ -28,18 +28,24 @@ /decl/flooring/reinforced/shuttle/skrell icon = 'mods/species/skrell/icons/turf/skrellturf.dmi' icon_base = "skrellblack" + uid = "floor_skrell" /decl/flooring/reinforced/shuttle/skrell/white icon_base = "skrellwhite" + uid = "floor_skrell_white" /decl/flooring/reinforced/shuttle/skrell/red icon_base = "skrellred" + uid = "floor_skrell_red" /decl/flooring/reinforced/shuttle/skrell/blue icon_base = "skrellblue" + uid = "floor_skrell_blue" /decl/flooring/reinforced/shuttle/skrell/orange icon_base = "skrellorange" + uid = "floor_skrell_orange" /decl/flooring/reinforced/shuttle/skrell/green icon_base = "skrellgreen" + uid = "floor_skrell_green" From 28b0caddd3b38595a125ce0efdf4391dc6c2c10a Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:03:57 +1100 Subject: [PATCH 0862/1331] Predators can now eat internal organs and cannot rip off unamputatable limbs. --- .../abilities/abilities_predator.dm | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/code/datums/extensions/abilities/abilities_predator.dm b/code/datums/extensions/abilities/abilities_predator.dm index 4fc37d4a8281..4c73a709cc7b 100644 --- a/code/datums/extensions/abilities/abilities_predator.dm +++ b/code/datums/extensions/abilities/abilities_predator.dm @@ -56,6 +56,11 @@ to_chat(user, SPAN_WARNING("\The [victim] is too big for you to dismember.")) return TRUE + var/obj/item/organ/external/limb = victim.get_organ(user.get_target_zone()) + if(!limb) + to_chat(user, SPAN_WARNING("\The [victim] is missing that limb!")) + return TRUE + to_chat(user, SPAN_NOTICE("You dig into \the [victim], hunting for something edible.")) if(!do_after(user, max(2 SECONDS, victim.get_object_size() * 5), victim) || QDELETED(victim) || !victim.butchery_data || victim.stat != DEAD) return TRUE @@ -66,13 +71,24 @@ victim.gib() return TRUE - var/obj/item/organ/external/limb = victim.get_organ(user.get_target_zone()) + limb = victim.get_organ(user.get_target_zone()) // In case we changed zone or such in the meantime. if(!limb) to_chat(user, SPAN_WARNING("\The [victim] is missing that limb!")) return TRUE - user.visible_message(SPAN_DANGER("\The [user] tears \the [limb] from \the [victim]!")) - limb.dismember(FALSE, DISMEMBER_METHOD_EDGE, silent = TRUE) - if(!QDELETED(limb)) - user.put_in_hands(limb) + if(length(limb.internal_organs)) + var/obj/item/organ/internal/stolen = pick(limb.internal_organs) + user.visible_message(SPAN_DANGER("\The [user] tears \the [stolen] out of \the [victim]'s [limb.name]!")) + victim.remove_organ(stolen, TRUE, TRUE) + if(!QDELETED(stolen)) + user.put_in_hands(stolen) + return TRUE + + if(!(limb.limb_flags & ORGAN_FLAG_CAN_AMPUTATE)) + to_chat(user, SPAN_WARNING("You gnaw on \the [victim]'s [limb.name], but can't pull it loose.")) + else + user.visible_message(SPAN_DANGER("\The [user] tears \the [limb] from \the [victim]!")) + limb.dismember(FALSE, DISMEMBER_METHOD_EDGE, silent = TRUE) + if(!QDELETED(limb)) + user.put_in_hands(limb) return TRUE From 3fbdb748d226f5648e3ffc5952f4782c737588f4 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:04:17 +1100 Subject: [PATCH 0863/1331] Ramps no longer get colour applied twice. --- code/game/turfs/walls/wall_natural_icon.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/game/turfs/walls/wall_natural_icon.dm b/code/game/turfs/walls/wall_natural_icon.dm index e0231caf658a..8aeffa788445 100644 --- a/code/game/turfs/walls/wall_natural_icon.dm +++ b/code/game/turfs/walls/wall_natural_icon.dm @@ -66,8 +66,6 @@ else if(has_right_neighbor) state = "ramp-blend-right" var/image/I = image(material_icon_base, state, dir = ramp_slope_direction) - I.color = base_color - I.appearance_flags |= RESET_COLOR add_overlay(I) if(shine) I = image(material_icon_base, "[state]-shine", dir = ramp_slope_direction) From 73beee653b649a3cc92db81953d540e42608100c Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:04:36 +1100 Subject: [PATCH 0864/1331] Fixes drake spit/abilities not working. --- mods/species/drakes/sifsap.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/species/drakes/sifsap.dm b/mods/species/drakes/sifsap.dm index 5334839e70fa..75f0315b71e9 100644 --- a/mods/species/drakes/sifsap.dm +++ b/mods/species/drakes/sifsap.dm @@ -2,7 +2,7 @@ var/obj/item/organ/internal/drake_gizzard/gizzard = user.get_organ(BP_DRAKE_GIZZARD) if(!gizzard?.sap_crop?.total_volume) return FALSE - if(LAZYACCESS(gizzard.sap_crop.reagent_volumes, /decl/material/liquid/sifsap) < amount) + if(!gizzard.sap_crop.has_reagent(/decl/material/liquid/sifsap, amount)) return FALSE gizzard.sap_crop.remove_reagent(/decl/material/liquid/sifsap, amount) return TRUE From 808130711b842d0562bb1b5253981da67cf4d8a3 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:05:38 +1100 Subject: [PATCH 0865/1331] A turf becoming open will cause everything on it to fall. --- code/game/turfs/turf_changing.dm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index ac2e306ea590..dedebcfc16aa 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -188,6 +188,11 @@ for(var/atom/movable/AM as anything in changed_turf) AM.update_turf_alpha_mask() + // Anything on our turf needs to fall down. + if(HasBelow(z) && changed_turf.is_open() && !old_is_open) + for(var/atom/movable/thing in changed_turf.get_contained_external_atoms()) + thing.fall() + /turf/proc/transport_properties_from(turf/other, transport_air) if(transport_air && can_inherit_air && (other.zone || other.air)) if(!air) From bdf17c83a8d53d191a9f181886ee6f4a8c026d5e Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:06:03 +1100 Subject: [PATCH 0866/1331] Barren ground will no longer drop soil. --- code/game/turfs/flooring/flooring_natural.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/turfs/flooring/flooring_natural.dm b/code/game/turfs/flooring/flooring_natural.dm index 63d3b2ec93c5..e83234890ed4 100644 --- a/code/game/turfs/flooring/flooring_natural.dm +++ b/code/game/turfs/flooring/flooring_natural.dm @@ -40,7 +40,7 @@ footstep_type = /decl/footsteps/asteroid turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH icon_edge_layer = FLOOR_EDGE_BARREN - force_material = /decl/material/solid/soil + force_material = /decl/material/solid/sand growth_value = 0.1 /decl/flooring/clay From f77d5cfa6fe11daa3795c39e0f79286ca6a27756 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:08:16 +1100 Subject: [PATCH 0867/1331] get_all_flooring() no longer duplicates flooring for lists. --- code/game/turfs/floors/floor_layers.dm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/game/turfs/floors/floor_layers.dm b/code/game/turfs/floors/floor_layers.dm index 5396b3607cf2..0b91d43f8af1 100644 --- a/code/game/turfs/floors/floor_layers.dm +++ b/code/game/turfs/floors/floor_layers.dm @@ -9,8 +9,7 @@ if(islist(_flooring)) for(var/floor in _flooring) . += RESOLVE_TO_DECL(floor) - _flooring = . // ensure the list elements are resolved - else + else if(ispath(_flooring)) . += RESOLVE_TO_DECL(_flooring) if(_base_flooring) . += get_base_flooring() From 3d1c30c53cd3dd5b7709352b39ae4e15fc5ee3f2 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:08:59 +1100 Subject: [PATCH 0868/1331] Drakes will default to clawing as their unarmed attack, instead of randomly kicking people. --- mods/species/drakes/species.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mods/species/drakes/species.dm b/mods/species/drakes/species.dm index 78ace9a7326b..6b0398df1865 100644 --- a/mods/species/drakes/species.dm +++ b/mods/species/drakes/species.dm @@ -74,6 +74,10 @@ pain_emotes_with_pain_level = adult_pain_emotes_with_pain_level return ..() +/decl/species/grafadreka/handle_post_spawn(var/mob/living/human/H) + . = ..() + H.default_attack = GET_DECL(/decl/natural_attack/claws/strong/drake) + // Stub for muscle memory of the Sit verb on Polaris. /mob/living/human/proc/drake_sit() set name = "Sit" From 06f00cf78a3c073ec02dc48ab9887bc5babc8c69 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 29 Oct 2025 17:09:54 +1100 Subject: [PATCH 0869/1331] Drake claws work as picks (too soft for stone) and not as hoes (why would drakes farm). --- mods/species/drakes/species_bodytypes.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/species/drakes/species_bodytypes.dm b/mods/species/drakes/species_bodytypes.dm index 5d8bf5862b68..9057ac1fe228 100644 --- a/mods/species/drakes/species_bodytypes.dm +++ b/mods/species/drakes/species_bodytypes.dm @@ -321,8 +321,8 @@ . = ..() item_flags |= ITEM_FLAG_NO_BLUDGEON set_extension(src, /datum/extension/tool, list( - TOOL_SHOVEL = TOOL_QUALITY_GOOD, - TOOL_HOE = TOOL_QUALITY_GOOD + TOOL_PICK = TOOL_QUALITY_MEDIOCRE, + TOOL_SHOVEL = TOOL_QUALITY_GOOD )) /obj/item/organ/external/hand/quadruped/grafadreka/set_bodytype(decl/bodytype/new_bodytype, override_material, apply_to_internal_organs) @@ -341,8 +341,8 @@ . = ..() item_flags |= ITEM_FLAG_NO_BLUDGEON set_extension(src, /datum/extension/tool, list( - TOOL_SHOVEL = TOOL_QUALITY_GOOD, - TOOL_HOE = TOOL_QUALITY_GOOD + TOOL_PICK = TOOL_QUALITY_MEDIOCRE, + TOOL_SHOVEL = TOOL_QUALITY_GOOD )) /obj/item/organ/external/hand/right/quadruped/grafadreka/set_bodytype(decl/bodytype/new_bodytype, override_material, apply_to_internal_organs) From 31dc87ba864bce60ea1e6067ddc1c5813fb523b7 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 29 Oct 2025 16:16:52 -0400 Subject: [PATCH 0870/1331] Give new_player and observer mobs skillsets again --- code/modules/mob/new_player/new_player.dm | 1 + code/modules/mob/observer/observer.dm | 1 + 2 files changed, 2 insertions(+) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 2a460c53a435..77ef427f3e49 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -10,6 +10,7 @@ anchored = TRUE // don't get pushed around virtual_mob = null // Hear no evil, speak no evil is_spawnable_type = FALSE + skillset = /datum/skillset // moved here from /mob to avoid giving dview a skillset var/ready = 0 /// Referenced when you want to delete the new_player later on in the code. diff --git a/code/modules/mob/observer/observer.dm b/code/modules/mob/observer/observer.dm index e76f34229eb2..7311b0eb3f00 100644 --- a/code/modules/mob/observer/observer.dm +++ b/code/modules/mob/observer/observer.dm @@ -15,6 +15,7 @@ var/global/const/GHOST_IMAGE_ALL = ~GHOST_IMAGE_NONE stat = DEAD status_flags = GODMODE shift_to_open_context_menu = FALSE + skillset = /datum/skillset // moved here from /mob to avoid giving dview a skillset var/ghost_image_flag = GHOST_IMAGE_DARKNESS var/image/ghost_image = null //this mobs ghost image, for deleting and stuff From eedba7d0629e35912174a40c6541d83acd08dcb8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 29 Oct 2025 16:17:53 -0400 Subject: [PATCH 0871/1331] Remove unnecessary skill_link nulling --- code/modules/client/preference_setup/occupation/occupation.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index feb41bc98058..e8b60f09dab0 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -185,9 +185,6 @@ skill_link = "View Skills" skill_link = "
    [skill_link]
    From c9f08ec258a445a2ff9d504dbb831acc8ade051c Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 21:54:11 +1100 Subject: [PATCH 0877/1331] Adding a couple of decl-related helpers. --- code/__globals.dm | 3 ++- code/datums/repositories/decls.dm | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/code/__globals.dm b/code/__globals.dm index ecb9b3eb1fc1..a328e0649715 100644 --- a/code/__globals.dm +++ b/code/__globals.dm @@ -1,7 +1,8 @@ // Defined here due to being used immediately below. #define GET_DECL(D) (ispath(D, /decl) ? (decls_repository.fetched_decls[D] || decls_repository.get_decl(D)) : null) #define IMPLIED_DECL GET_DECL(__IMPLIED_TYPE__) -#define RESOLVE_TO_DECL(D) (istype(D, /decl) ? D : GET_DECL(D)) +#define RESOLVE_TO_DECL(D) (istype(D, /decl) ? D : (istext(D) ? decls_repository.get_decl_by_id(D, validate_decl_type = FALSE) : GET_DECL(D))) +#define DECLS_ARE_EQUIVALENT(F, S) ((RESOLVE_TO_DECL(F) == RESOLVE_TO_DECL(S))) // Defined here due to compile order; overrides in macros make the compiler complain. /decl/global_vars diff --git a/code/datums/repositories/decls.dm b/code/datums/repositories/decls.dm index 28dea46d5cd1..82476aae8e53 100644 --- a/code/datums/repositories/decls.dm +++ b/code/datums/repositories/decls.dm @@ -20,6 +20,12 @@ var/global/repository/decls/decls_repository = new +/proc/resolve_decl_uid_list(list/decl_uids) + for(var/uid in decl_uids) + var/decl/decl = decls_repository.get_decl_by_id(uid) + if(istype(decl)) + LAZYADD(., decl) + /repository/decls var/list/fetched_decls = list() var/list/fetched_decl_ids = list() From 6040b07b6355a19e5fda169aeff25b3066b4ede1 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 21:59:34 +1100 Subject: [PATCH 0878/1331] Flooring lists are now handled as JSON text. --- code/game/turfs/floors/_floor.dm | 6 +++++- code/game/turfs/floors/subtypes/floor_natural.dm | 11 ++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 22123008d757..d12e92b393cb 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -34,6 +34,11 @@ set_turf_materials(floor_material, skip_update = TRUE) + if(istext(_flooring)) + _flooring = resolve_decl_uid_list(cached_json_decode(_flooring)) + if(!length(_flooring)) + _flooring = null + if(!floortype && (ispath(_flooring) || islist(_flooring))) floortype = _flooring else @@ -49,7 +54,6 @@ if(ml) // We skipped the update above to avoid updating our neighbors, but we need to update ourselves. lazy_update_icon() - /turf/floor/ChangeTurf(turf/N, tell_universe, force_lighting_update, keep_air, update_open_turfs_above, keep_height) if(is_processing) STOP_PROCESSING(SSobj, src) diff --git a/code/game/turfs/floors/subtypes/floor_natural.dm b/code/game/turfs/floors/subtypes/floor_natural.dm index f265dea98b1f..ae7350535805 100644 --- a/code/game/turfs/floors/subtypes/floor_natural.dm +++ b/code/game/turfs/floors/subtypes/floor_natural.dm @@ -41,20 +41,13 @@ name = "snow" icon = 'icons/turf/flooring/snow.dmi' icon_state = "snow0" - _flooring = list( - /decl/flooring/grass, - /decl/flooring/snow - ) + _flooring = @'["' + /decl/flooring/grass::uid + @'","' + /decl/flooring/snow::uid + @'"]' /turf/floor/grass/wild name = "wild grass" icon = 'icons/turf/flooring/wildgrass.dmi' icon_state = "wildgrass" - _flooring = list( - /decl/flooring/grass, - /decl/flooring/grass/wild - ) - _base_flooring = /decl/flooring/dirt + _flooring = @'["' + /decl/flooring/grass::uid + @'","' + /decl/flooring/grass/wild::uid + @'"]' /turf/floor/ice name = "ice" From 24d01b7f50db764c889349d6001a9ad8b2fc4823 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 1 Nov 2025 12:35:44 +1100 Subject: [PATCH 0879/1331] Automatic changelog generation for PR #5185 [ci skip] --- html/changelogs/AutoChangeLog-pr-5185.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5185.yml diff --git a/html/changelogs/AutoChangeLog-pr-5185.yml b/html/changelogs/AutoChangeLog-pr-5185.yml new file mode 100644 index 000000000000..88680675a9e3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5185.yml @@ -0,0 +1,4 @@ +author: MistakeNot4892 +changes: + - {tweak: 'Drakes can no longer farm, but they can mine (in dirt).'} +delete-after: true From e39ec59d21ec7f9991e3eab97efb950eca9219ca Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Thu, 30 Oct 2025 11:23:38 +1100 Subject: [PATCH 0880/1331] Unified brick/log/stone/natural walls with a consistent ramp/style. --- icons/turf/walls/brick.dmi | Bin 2651 -> 2046 bytes icons/turf/walls/log.dmi | Bin 2681 -> 1851 bytes icons/turf/walls/stone.dmi | Bin 2943 -> 2766 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/turf/walls/brick.dmi b/icons/turf/walls/brick.dmi index c01691514259e70c2e912f684bccef2aff39d1b6..530e4e03027138e61291f4bb623451c1f8e42e41 100644 GIT binary patch delta 1850 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 B*O34K diff --git a/icons/turf/walls/log.dmi b/icons/turf/walls/log.dmi index d54b0750e288b8d0f57e3f9e5c3621b294e9d4dd..d4e2c05916568fca12ba4b7c152dbbf1d771a022 100644 GIT binary patch delta 1823 zcmZ8ic~H}L7XK!{92kR%AVUB#;ZP94M2ZJz!XZMrVgeGYMULgL2pc)9MfCUM5G(@~ zShPSu0;N_!kdz8p!x4~61nU9fK#CERS`edhCP3J9XJ@v%GjHbedGkK+&3p6t<0-dE zwuFDn_CKOcFeCr~w7tDNH~^qD6a_Gl1{zM@4bp%)+c)sA2EC7*+}PMiO)XU_m1Sjh zIXO4@{QTwR<;d`JU7ZgW78YuF#FRg~lQ{ldz?yy$rAdA&IBST;$qVwf!!456l2;pv z_7&qkqlt-gZ{yq-=DRH#zQ)#Er52vJUTgm5HY{E^5pMfnZ^W^`RpY3WDXf_tgAW^; zCNi_CGON<7vcgF{s2sCxi3tbf{ayU{utTj38NxW3!H`{Moa~RYdzQ#m1-y|h<*ogu zk0I5cS5Eyr4FFEZ+ru>wK3_IhCW;^TB4@1=x;ky2);h>*S)#OIlLR24wU~6j5ADN@ z7>6Ehd>D8-gJoA#T9kirK=5O+r{sQ#EWfB+fR!<}e|q+gDRJxwuP8Up^L7-E{&RUK z>dxLKbEP1Mxf=F5aYjtHnKO|;NlB@y}429MM{A-ybik=7q#39@Mk#|A4BO-U$+5pqr zS+;yZeFr>kU;k--#UV^e=X9do-pdWUWWGVj^4G;zK)PnWj&N(-ySmB`(lje=~R_AzM3GQ_Y-}6#B)c5 zC~HWRY#IvebZ)iozD3)R45*ihye9ls^IR(!Y`V=+9Im+=6+K&&+gB?c-b_Z-qk^-h zPt3(ii&YukZL@uljeBvs&EQ^lXgt-ZIJO1&G^=bRT z$?PRG@1ZNk4tUXuzKY$$%iWVgU~la*MQgT3FdSW-usF`F4ci6m>dMfHL7tv@Doy7~ zuJw}1_N1#*c= zG4jGX)Ud_t;wJ~v=P$gj*f0WFOdxXHjvURpY44=!Q{a$SB-WWDI7;N(E!CCory=PF zJgqsF@Rpc#nzrYVVL$&)u~`=-MXaJrHuv6%foaXFTYx8Siwnyb*H1D1#W?<(>$knN zDLu_S7kPCUc#?ofd^4m~z?PMgMj;uJQ5SGfhEa;G~Fr>Hhvex8- zHes)WS+iEyP7(E@=xH_2gF6=SV$*kPDL3G)Ofi$D(7vojD6ARMLcYe{ zOUbaX$r+wdv*-r-E6_U7T1#^Q9%`wVY4>_sh==7zyyt+l(U7c!%&wWdC*=laiJ6Q- zt?Ut`pGw&ciJ6amaON6$5MPxH7V(}se~Nax4O3V4KSzDbWN`H4YAG)WQ9)*K5K2=s zcau@c=rs!W?*-1eagMk2ui$ZvF*dT{C@oZLYhl^oFM)6&K3ovS0P(sG6eO9uYc71E8u literal 2681 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 Date: Thu, 30 Oct 2025 11:23:51 +1100 Subject: [PATCH 0881/1331] Implemented chisels and wall smoothing. --- code/__defines/tools.dm | 3 +- code/game/objects/items/chisel.dm | 7 ++- code/game/objects/items/tools/screwdriver.dm | 5 +- code/game/turfs/turf.dm | 2 +- code/game/turfs/walls/wall_engraving.dm | 16 ++++++ code/game/turfs/walls/wall_natural.dm | 46 ++++++++++++++++-- code/game/turfs/walls/wall_natural_icon.dm | 21 ++++++++ code/game/turfs/walls/wall_natural_ramps.dm | 1 + code/modules/gemstones/_gemstone.dm | 2 +- .../archetypes/tool_archetype_definitions.dm | 5 ++ icons/turf/walls/log.dmi | Bin 1851 -> 2097 bytes icons/turf/walls/stone.dmi | Bin 2766 -> 2807 bytes mods/content/fantasy/_fantasy.dm | 16 +++++- mods/content/fantasy/_fantasy.dme | 4 +- mods/content/fantasy/datum/skills.dm | 9 ---- mods/~compatibility/patches/drakes.dm | 2 +- mods/~compatibility/~compatibility.dm | 2 +- nebula.dme | 1 + 18 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 code/game/turfs/walls/wall_engraving.dm diff --git a/code/__defines/tools.dm b/code/__defines/tools.dm index 05931f41a2f4..4b0065e96b71 100644 --- a/code/__defines/tools.dm +++ b/code/__defines/tools.dm @@ -17,6 +17,7 @@ #define TOOL_PEN /decl/tool_archetype/pen #define TOOL_STAMP /decl/tool_archetype/stamp #define TOOL_SHEARS /decl/tool_archetype/shears +#define TOOL_CHISEL /decl/tool_archetype/chisel // Surgical tools. #define TOOL_SCALPEL /decl/tool_archetype/scalpel @@ -63,7 +64,7 @@ #define IS_HAMMER(A) IS_TOOL(A, TOOL_HAMMER) #define IS_HOE(A) IS_TOOL(A, TOOL_HOE) #define IS_SHEARS(A) IS_TOOL(A, TOOL_SHEARS) - +#define IS_CHISEL(A) IS_TOOL(A, TOOL_CHISEL) #define IS_HEMOSTAT(A) IS_TOOL(A, TOOL_HEMOSTAT) #define IS_RETRACTOR(A) IS_TOOL(A, TOOL_RETRACTOR) diff --git a/code/game/objects/items/chisel.dm b/code/game/objects/items/chisel.dm index 8bdcf2ab1a40..fb32a9888d3b 100644 --- a/code/game/objects/items/chisel.dm +++ b/code/game/objects/items/chisel.dm @@ -1,4 +1,3 @@ -// Stub for forging. TODO implement TOOL_CHISEL. /obj/item/tool/chisel name = "chisel" desc = "A hard, sharpened tool used to chisel stone, wood or bone." @@ -8,5 +7,11 @@ handle_material = /decl/material/solid/organic/plastic binding_material = null +/obj/item/tool/chisel/get_initial_tool_qualities() + var/static/list/tool_qualities = list( + TOOL_CHISEL = TOOL_QUALITY_DEFAULT + ) + return tool_qualities + /obj/item/tool/chisel/forged handle_material = null diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index ce927361ee62..9ec972e56958 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -20,7 +20,10 @@ if(prob(75)) pixel_y = rand(0, 16) . = ..() - set_extension(src, /datum/extension/tool, list(TOOL_SCREWDRIVER = TOOL_QUALITY_DEFAULT)) + set_extension(src, /datum/extension/tool, list( + TOOL_SCREWDRIVER = TOOL_QUALITY_DEFAULT, + TOOL_CHISEL = TOOL_QUALITY_BAD + )) /obj/item/screwdriver/on_update_icon() . = ..() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 6a6df3ddcc8a..7385ceef2fe1 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -882,7 +882,7 @@ name = "Show Turf Contents" expected_user_type = /mob interaction_flags = 0 - examine_desc = "list everything on $TARGET_THEM$" + examine_desc = "list everything on the turf" /decl/interaction_handler/show_turf_contents/invoked(atom/target, mob/user, obj/item/prop) target.show_atom_list_for_turf(user, get_turf(target)) diff --git a/code/game/turfs/walls/wall_engraving.dm b/code/game/turfs/walls/wall_engraving.dm new file mode 100644 index 000000000000..ee730aa2a220 --- /dev/null +++ b/code/game/turfs/walls/wall_engraving.dm @@ -0,0 +1,16 @@ +/datum/engraving + var/name = "nondescript design" + var/desc + var/icon + var/icon_state + var/dir + +/datum/engraving/New(var/decl/banner_symbol/engraving_symbol) + if(engraving_symbol) + icon = engraving_symbol.icon + icon_state = engraving_symbol.icon_state + name = engraving_symbol.name + +/datum/engraving/random/New() + var/banner_decls = decls_repository.get_decls_of_subtype_unassociated(/decl/banner_symbol) + ..(pick(banner_decls)) diff --git a/code/game/turfs/walls/wall_natural.dm b/code/game/turfs/walls/wall_natural.dm index c85396e05f5f..4710e89e5626 100644 --- a/code/game/turfs/walls/wall_natural.dm +++ b/code/game/turfs/walls/wall_natural.dm @@ -1,3 +1,5 @@ +var/global/_wall_chisel_skill = SKILL_CONSTRUCTION + /turf/wall/natural icon_state = "natural" desc = "A rough natural wall." @@ -11,6 +13,8 @@ var/static/list/exterior_wall_shine_cache = list() var/being_mined = FALSE var/gem_dropped = FALSE + var/smoothed + var/list/engravings /turf/wall/natural/flooded flooded = /decl/material/liquid/water @@ -20,7 +24,7 @@ return SPAN_NOTICE("It has been noticeably discoloured by the elements.") /turf/wall/natural/get_wall_icon() - return 'icons/turf/walls/natural.dmi' + return (smoothed && !ramp_slope_direction) ? 'icons/turf/walls/stone.dmi' : 'icons/turf/walls/natural.dmi' /turf/wall/natural/Initialize(var/ml, var/materialtype, var/rmaterialtype) . = ..() @@ -50,6 +54,11 @@ update_neighboring_ramps(destroying_self = TRUE) . = ..() +/turf/wall/natural/proc/get_engraving_for_dir(facing_dir) + for(var/datum/engraving/engraving in engravings) + if(engraving.dir == facing_dir) + return engraving + /turf/wall/natural/attack_hand(mob/user) // Allow species with digging limbs to dig (drakes) @@ -102,12 +111,39 @@ return FALSE /turf/wall/natural/update_strings() + var/modifier + if(length(engravings)) + modifier = "engraved" + else if(smoothed) + if(reinf_material) + modifier = "polished" + else + modifier = "smooth" + else if(!reinf_material) + modifier = "natural" + if(reinf_material) - SetName("[reinf_material.ore_name] deposit") - desc = "A natural cliff face composed of bare [material.solid_name] and a deposit of [reinf_material.ore_name]." + if(modifier) + SetName("[modifier] [reinf_material.ore_name] deposit") + else + SetName("[reinf_material.ore_name] deposit") + desc = "A natural wall composed of bare [material.solid_name] and a deposit of [reinf_material.ore_name]." else - SetName("natural [material.solid_name] wall") - desc = "A natural cliff face composed of bare [material.solid_name]." + SetName("[modifier] [material.solid_name] wall") + desc = "A natural wall composed of bare [material.solid_name]." + +/turf/wall/natural/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(length(engravings)) + for(var/datum/engraving/engraving in engravings) + var/engraving_line + if(engraving.name) + engraving_line = "It has been engraved with \a [engraving.name]." + else + engraving_line = "It has been engraved." + if(engraving.desc) + engraving_line = "[engraving_line] [engraving.desc]" + . += engraving_line /turf/wall/natural/update_material(var/update_neighbors) if(reinf_material?.ore_icon_overlay) diff --git a/code/game/turfs/walls/wall_natural_icon.dm b/code/game/turfs/walls/wall_natural_icon.dm index 8aeffa788445..715297e7339b 100644 --- a/code/game/turfs/walls/wall_natural_icon.dm +++ b/code/game/turfs/walls/wall_natural_icon.dm @@ -92,3 +92,24 @@ add_overlay(excav_overlay) if(archaeo_overlay) add_overlay(archaeo_overlay) + + // Might be worth having a dedicated wall engraving icon set in the future instead of using the banner/sign symbols. + // That would let us avoid this offsetting stuff and make the icons look less wonky on foreshortened faces. + for(var/datum/engraving/engraving in engravings) + // Not aware of a nice way to handle this. Would like to use BLEND_INSET_OVERLAY but we need BLEND_MULTIPLY. + if(engraving.dir == NORTH || !engraving.icon || !engraving.icon_state) + continue + + var/y_offset = 9 + var/x_offset = 0 + if(engraving.dir == SOUTH) + y_offset = 0 + else if(engraving.dir == EAST) + x_offset = -1 + else if(engraving.dir == WEST) + x_offset = 1 + + var/image/eng = image(icon = engraving.icon, icon_state = engraving.icon_state, dir = engraving.dir, pixel_x = x_offset, pixel_y = y_offset) + eng.blend_mode = BLEND_MULTIPLY + add_overlay(eng) + diff --git a/code/game/turfs/walls/wall_natural_ramps.dm b/code/game/turfs/walls/wall_natural_ramps.dm index 3cb6ea73728a..3ec738ff1221 100644 --- a/code/game/turfs/walls/wall_natural_ramps.dm +++ b/code/game/turfs/walls/wall_natural_ramps.dm @@ -1,6 +1,7 @@ /turf/wall/natural/proc/make_ramp(var/mob/user, var/new_slope, var/skip_icon_update = FALSE) ramp_slope_direction = new_slope + QDEL_NULL_LIST(engravings) var/old_ao = permit_ao if(ramp_slope_direction) diff --git a/code/modules/gemstones/_gemstone.dm b/code/modules/gemstones/_gemstone.dm index b95cb57181d3..02d698d4a130 100644 --- a/code/modules/gemstones/_gemstone.dm +++ b/code/modules/gemstones/_gemstone.dm @@ -38,7 +38,7 @@ var/global/list/_available_gemstone_cuts . = ..() * cut.worth_multiplier /obj/item/gemstone/attackby(obj/item/used_item, mob/user) - if(IS_HAMMER(used_item) && !user.check_intent(I_FLAG_HARM)) // TOOL_CHISEL when? + if(IS_CHISEL(used_item) && !user.check_intent(I_FLAG_HARM)) if(!cut.can_attempt_cut) to_chat(user, SPAN_WARNING("\The [src] has already been cut.")) return TRUE diff --git a/code/modules/tools/archetypes/tool_archetype_definitions.dm b/code/modules/tools/archetypes/tool_archetype_definitions.dm index bf8e02c84f86..61dfd1f5d668 100644 --- a/code/modules/tools/archetypes/tool_archetype_definitions.dm +++ b/code/modules/tools/archetypes/tool_archetype_definitions.dm @@ -63,6 +63,11 @@ tool_sound = 'sound/weapons/bladeslice.ogg' tool_message = "shearing" +/decl/tool_archetype/chisel + name = "chisel" + tool_sound = 'sound/items/shovel_dirt.ogg' + tool_message = "chiseling" + /decl/tool_archetype/knife name = "knife" tool_sound = 'sound/weapons/bladeslice.ogg' diff --git a/icons/turf/walls/log.dmi b/icons/turf/walls/log.dmi index d4e2c05916568fca12ba4b7c152dbbf1d771a022..f7f3e773a79b583c3ff47fc733988a3de0ccde2d 100644 GIT binary patch delta 2033 zcmZuyX*ApE8jm1JTasu^sVzxWuR3k1y*gx(T4zWs)m2bIV{9$jtK=_)SnfrwV^6w` zpfqVQstsDLlxnFMjBRvCiCRVm71G{J&-BAR_kMVP=eIrQJm>lHw!wKwOeV?0S$(I@ zP7nyB?&?DH0)dp!iU(3rRyfm2&1p9H*+)z(mFwp9TE=p3H#%7G!93)o z6Kw_40KL7``S^p~!uMguZpJ3YG~;!W9^001>>r`ZIs4U0G}5K8Xa8nSlb%K&3A8OW z8(*|8n{PcN|LJn%meZtkSA6f!;UlyA@E{Nr;z|VYKG!N1E8awnS!;H0nW{MI{V5qt zu7ubq#W(aTJyO>XC$?a?sHkF9;jlX zpBw@##K}J`1|5r}9R6T;UM*1NY%v@ccQ09dOqZpg=9r_8tic!2Xw(grnnoa{hY$-LD0k|B&w=|;L(6^x{F{uj zN5>ioQVDod2xG`-)npy6%Rp5)`gaF_Itk$D5e66%;2Zg=!6f0E%Ep#H)AAwOxIibR zry7G})7L8~=ah!VLdG1PYUD>0w?w#u@7ahobcRcM*B|lA_;C;VmZdRmukO6M;_9)l z*%+6^(<~Y?sav?KksCBV8AlL$xMU|9B5T^u+FQ0s%JevAZW&x(?MIpu*Z}?L+JUp6 zwcWJN!X}zv{a&nM6-rj9Ri=Bk@7)6rkK(2YZoHBUc(Gxfnn-J+sf>8mF<~^@R9}>b zoPd&(*JA>D4Mcu}?cN1p9L|L%i?HYZj^abgYj-|e1!wzpH$RCo(%#YZ!l&-x2rKW9 z?r=$(sKCXuG{65A^PTVafSc3OO~~3EWWw%wjkmjZN4Jv?*M5^TZW~R`l`L!mw0OWv z-~U4`wBS^|)k8AQ#z|e?BA{z=U2iiAncsOEQtSK;j@VN(YVb)zB~`>-Xd@2cN8&^# zAw_);7~^f-V2UVkQML*?r)}NgvHUNu_yfp;k{G!^$P-b`g;M~my>?Sj9cPycO!TG@ z%%@1hYaNsJ5-k7pSi{j^_Q!G-+>_!z{`wZTsb&8tx`)UcUa0En_Sa_%WG%*mtSkws zU09gTuG!VvGRnH|3z0<0Mh0|#)vwZPiLw^Q!-5s}oEnbg^>v>bZP^ylk=nekFaq(~ z0`LL4E{jk`1iA^uj8NXQBW9bux!p5B&YpuFAMN68vjfkeb5EdyHu0~mV-915&kpAN z4qe0PmS|+nKwGoz@=-I9x1Cp0$DNH8bbFqp=!B1UucTI#Bq^w)C}tZSO78x<5Ub7U z`$H#d7J9sqf0~{rbpSMb5tsMIDVk?QJVlfJm+PUgF#V|t{oZ%)SL!8l-o%T+@yWsr zhD?^T_WE7b5aq=y+^G%AAG&Z!W94C=`-^tWy)Z$IK@_shZYh4G3{mXgnXSYb!voc+8ILDpX~Y8a5?aw@ncJlGJNUd@V5O>XYZluqGo1IB4#X@kym<~w~N;3~tLKa`tuIsAI=wz*}Vs5+HvdXd$;dZPz`FG4T9ab706u#Ac* zto793cJ)rgxpZFwSal=SY^r-hOF3l=|4X}_##zyQ!}jcpD*#h0mL) zK1md8nC!R=TH#y$=Jlsa)W*>$CCi2{QwakyXRYs6mV(hH+8H|`H&Ylj0slq))r9LI zx_U4Rt@_X2M^xtW7D3ESe6xcF#PaqDIi_I%!C%WXV`Jo^yFbKz3m^~rQ^*CIlIx+N zjk=!CdO8th4a;q&A17y1w?t~h=NpKbD6}eY^;jN-CD>H!!u<~@G4eO@59FB&@Rp{A z{3M3~-TOKz0BHHf%@2PNKA>0n`{Sj4o*bHlYCV)=j+geSDQ4idMR>w^v RtQ5aE$n~TLv4Ief@*mG$&QbsX delta 1785 zcmZ8ic~Fyg8vZ38Ngx~vP=)|P!l597M2ZJz!cl}G5)+PCEOIPYku-9&7SZqHwhUBY z(FFn}P-^8Sr9x^rB65jfkwY9vF@jPHA|iKyu#p~h$0IHrIZcG4B`w|5dpnYn%aL+H_kgoqzk}o^KEj&IfE}EMV{Ury0E2SAD z0<}~s5i8BVegJM2y%N3Li?=PG@|uW`Uwjkmy1LSB+VC}|`X;#`@OF*K>pEDt8W?K* z=xCV#->R_WnI!tW!QkVD=IQjz%JjKwHly(@na^vmODeAff(ks3Q` z4PB5jW*7%Vlvcy;cfoxM~7t z4=Ygy>8G(g?PNa7;d3o#`rDo$2kRcykSD}P&Q%pY{g9A8LooM=)8$(XaBroLFZG1! zAa+@{PdxL>>=CJ*dIN0LLAU1e>O0{%+xibH<9CwVJKEP~rks0xjv-NGD>mr4Bk?6% z$6nIv@$<_EwTUG0p>d-~?*}!K?ym*fQMoibAgL7;v!j^XZu1QWa(PeVL5oz6`D%cD~jpBHXUvn`c%x$ zJ~{q*c)Jh&I0&!|)2CYpvliUwJ}3|A16gRA7X`{!%+*Adp-fS;6ZUO-4YFcegUui? zE0D-t9i^eX7g=*9aDB>ga3O0Q&3^2xU;{iTgTI|+;TyKm*HhDZt7=Pw|e4Ke9?*l(|1v8xc)0^xH0m7g8vgKrb~q2bQ*@l z;hbMKtFT|`3@_d0%~4du^LoS9FTZjc@(wUWV*DLGJ0zJVEWzJ>vm8~NqsXmP8H@;U zfl`$=adG7ms381JR<1jzPpI6>vh_cBNzXEyLnOb?_WNVVyTVv!!u{d;wHuLTxDp=q zrdi;qPSaEG1DHwfjZVR3w{^_r1lA%0WGWhq>gH|MzY8M5uPxB#)6c_`a?#X*rW0iE z&$e+IIO6^5u!BYSEXYwwh>{l6qQ*>L6+JzXx^neZ`JNugqyd5b0pwif9a{%k-x3yi zNuXakjU|P@QLCzOjfTYUag>%A{2P^oXv&dOy8Yb7BI7Pnl2B$PQtoYxf+;QAYQP<< z=0w-W_LGc$(dT?~yUtUU^i#@YI!fcd6%}iH8;-B#vmj6B7|H@#S_ie%%!G5TkWKp} z{CXLS4xQ5E2lYP4P@H+x&hND|Zcz+5C?H=G+*_`q1&ca^m)Q+-oCxp6JdU{BRnmqTj}I&Lc2h5C&dZ4$;BQ<-McA4Zf46k_W^N} zE^!MozGe81kmH*vq)`hLv&NBrGU+fRqz!vvP4>(ExUxjBhI7~WSJ6S2G4kf|XQ*#! zRHl|xE@t~7GRPSAL+$5GTqI;7dWXdNGT$*L*8Z;c4IGv_$w2n(#RYOzMGOP{1qch` z!ucUoz|piLA+ddrz*#3uTI-)on7{4heoJ*q&m2MLfrm?&Zk+G`G)Ah37fZVVJAOcJ zOk#pTvQ!3RL;ua#-(Wmdux8>SG~BZ;PQOSkZZpfkW(rRhHfZAcz;P2|LauFUW(fMf z5dR%RY>mizRsc~M@slkZHe!aHJ~cfGT(_WDY&24_gZgt~#iy4{ycu%Ub^n4ml}??* zf1QA!kMdpT2{*FF$V5Uljbgxr=?=23rEMBbj`?^s^h3Jr)sOq%40wKW)~&%MIQ5^+ CeH6F= diff --git a/icons/turf/walls/stone.dmi b/icons/turf/walls/stone.dmi index 4e53252374fc0498fcce95f764eb8ef837348823..3a9c3be5a1dbeea5209c16729b12ab9dd41c5689 100644 GIT binary patch delta 2750 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 Date: Sat, 1 Nov 2025 11:12:05 +1100 Subject: [PATCH 0882/1331] Fixes inverted ordinal logic. --- code/_helpers/unsorted.dm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 46cb031a043c..a53bbbf7802c 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -774,15 +774,17 @@ var/global/list/WALLITEMS = list( /**Returns a number string with its ordinal suffix th, st, nd, rd */ /proc/get_ordinal_string(var/num) - if(num < 10 && num > 20) //11, 12, 13 are exceptions in english, and just get 'th' like everything else + . = num + num %= 100 + if(num < 10 || num > 20) //11, 12, 13 are exceptions in english, and just get 'th' like everything else switch(num % 10) if(1) - return "[num]st" + return "[.]st" if(2) - return "[num]nd" + return "[.]nd" if(3) - return "[num]rd" - return "[num]th" + return "[.]rd" + return "[.]th" ///A do nothing proc used to prevent empty block warnings ///In hot code (like atmos checks), use EMPTY_BLOCK_GUARD instead. From c014ead9fff3b37d6bc5d146af977ac9b0ad7b83 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sun, 2 Nov 2025 01:00:36 +0000 Subject: [PATCH 0883/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-5185.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5185.yml diff --git a/html/changelog.html b/html/changelog.html index 561c88760f9c..b68d6c828dd7 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    02 November 2025

    +

    MistakeNot4892 updated:

    +
      +
    • Drakes can no longer farm, but they can mine (in dirt).
    • +
    +

    20 October 2025

    MistakeNot4892 updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index fb4a05ef19b2..c6ca91572aca 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15045,3 +15045,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. MistakeNot4892: - tweak: Crafted barrels will need barrel rims forged if the forging modpack is in use. +2025-11-02: + MistakeNot4892: + - tweak: Drakes can no longer farm, but they can mine (in dirt). diff --git a/html/changelogs/AutoChangeLog-pr-5185.yml b/html/changelogs/AutoChangeLog-pr-5185.yml deleted file mode 100644 index 88680675a9e3..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5185.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: MistakeNot4892 -changes: - - {tweak: 'Drakes can no longer farm, but they can mine (in dirt).'} -delete-after: true From db8d8b9f0ddde770c1b84352b70d9b36fafcc1e8 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sat, 1 Nov 2025 11:48:06 +1100 Subject: [PATCH 0884/1331] Moving paint_color to /atom. --- code/game/atoms.dm | 3 +++ code/game/objects/items/__item.dm | 1 - code/game/objects/structures/__structure.dm | 1 - code/game/turfs/turf.dm | 2 -- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d6d80c68478b..376472d4ffa5 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -64,6 +64,9 @@ /// (FLOAT) world.time of last on_reagent_update call, used to prevent recursion due to reagents updating reagents VAR_PRIVATE/_reagent_update_started = 0 + /// (STRING) A color applied over the top of any material color. Implemented on /obj/item, /obj/structure and /turf. + var/paint_color + /atom/proc/get_max_health() return max_health diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 171b27c8b98b..ece94ed6a6a9 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -112,7 +112,6 @@ /// Controls what method is used to resolve conflicts between equipped items and mob loadout. var/replaced_in_loadout = LOADOUT_CONFLICT_DELETE - var/paint_color var/paint_verb /// What dexterity is required to attack with this item? diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 2a90385995b3..43070098384d 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -17,7 +17,6 @@ var/footstep_type var/mob_offset - var/paint_color var/paint_verb /obj/structure/get_color() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 6a6df3ddcc8a..5c21bc62b0e2 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -84,8 +84,6 @@ // Temporary list of weakrefs of atoms who should be excepted from falling into us var/list/skip_height_fall_for - var/paint_color - /// Floorlike structures like catwalks. Updated/retrieved by get_supporting_platform() var/obj/structure/supporting_platform From 8a2f9653b8b35a54a0c2de8e987d74a01034b7ac Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 20:34:53 +1100 Subject: [PATCH 0885/1331] Reworking some really bad code in borg hypos. --- .../{borghydro.dm => borghypo.dm} | 88 ++++++++++++------- nebula.dme | 2 +- 2 files changed, 56 insertions(+), 34 deletions(-) rename code/modules/reagents/reagent_containers/{borghydro.dm => borghypo.dm} (67%) diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghypo.dm similarity index 67% rename from code/modules/reagents/reagent_containers/borghydro.dm rename to code/modules/reagents/reagent_containers/borghypo.dm index 35110bc10b99..5234b4c0ff72 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghypo.dm @@ -12,29 +12,39 @@ var/charge_tick = 0 var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) - var/list/reagent_ids = list(/decl/material/liquid/regenerator, /decl/material/liquid/stabilizer, /decl/material/liquid/antibiotics) - var/list/reagent_volumes = list() - var/list/reagent_names = list() +/obj/item/chems/borghypo/Initialize() + volume *= length(get_generated_reagents()) + . = ..() -/obj/item/chems/borghypo/surgeon - reagent_ids = list( +/obj/item/chems/borghypo/proc/get_generated_reagents() + var/static/list/_reagent_ids = list( + /decl/material/liquid/regenerator, + /decl/material/liquid/stabilizer, + /decl/material/liquid/antibiotics + ) + return _reagent_ids + +/obj/item/chems/borghypo/surgeon/get_generated_reagents() + var/static/list/_reagent_ids = list( /decl/material/liquid/brute_meds, /decl/material/liquid/oxy_meds, /decl/material/liquid/painkillers/strong ) + return _reagent_ids -/obj/item/chems/borghypo/crisis - reagent_ids = list( +/obj/item/chems/borghypo/crisis/get_generated_reagents() + var/static/list/_reagent_ids = list( /decl/material/liquid/regenerator, /decl/material/liquid/stabilizer, /decl/material/liquid/painkillers/strong ) + return _reagent_ids -/obj/item/chems/borghypo/Initialize() +/obj/item/chems/borghypo/populate_reagents() . = ..() + var/list/reagent_ids = get_generated_reagents() for(var/decl/material/reagent in decls_repository.get_decls_unassociated(reagent_ids)) - reagent_volumes[reagent.type] = volume - reagent_names += reagent.use_name // TODO: should we even bother precaching this? all of this code sucks anyway + reagents.add_reagent(reagent.type, round(reagents.maximum_volume / length(reagent_ids))) START_PROCESSING(SSobj, src) /obj/item/chems/borghypo/Destroy() @@ -43,21 +53,25 @@ /obj/item/chems/borghypo/Process() //Every [recharge_time] seconds, recharge some reagents for the cyborg+ if(++charge_tick < recharge_time) - return 0 + return charge_tick = 0 - - if(isrobot(loc)) - var/mob/living/silicon/robot/robot = loc - if(robot && robot.cell) - for(var/reagent in reagent_ids) - if(reagent_volumes[reagent] < volume) - robot.cell.use(charge_cost) - reagent_volumes[reagent] = min(reagent_volumes[reagent] + 5, volume) - return 1 + if(!isrobot(loc)) + return + var/mob/living/silicon/robot/robot = loc + if(!robot?.cell) + return + var/list/reagent_ids = get_generated_reagents() + var/max_per_reagent = round(reagents.maximum_volume / length(reagent_ids)) + for(var/reagent in reagent_ids) + var/has_reagent = REAGENT_VOLUME(reagents, GET_DECL(reagent)) + if(has_reagent < max_per_reagent) + robot.cell.use(charge_cost) + reagents.add_reagent(reagent, round(max_per_reagent - has_reagent)) /obj/item/chems/borghypo/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - if(!reagent_volumes[reagent_ids[mode]]) + var/list/reagent_ids = get_generated_reagents() + if(REAGENT_VOLUME(reagents, GET_DECL(reagent_ids[mode])) <= 0) to_chat(user, SPAN_WARNING("The injector is empty.")) return TRUE @@ -72,24 +86,26 @@ to_chat(target, SPAN_NOTICE("You feel a tiny prick!")) if(target.reagents) - var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) + var/t = min(amount_per_transfer_from_this, REAGENT_VOLUME(reagents, GET_DECL(reagent_ids[mode]))) target.add_to_reagents(reagent_ids[mode], t) - reagent_volumes[reagent_ids[mode]] -= t + reagents.remove_reagent(reagent_ids[mode], t) admin_inject_log(user, target, src, reagent_ids[mode], t) - to_chat(user, SPAN_NOTICE("[t] units injected. [reagent_volumes[reagent_ids[mode]]] units remaining.")) + to_chat(user, SPAN_NOTICE("[t] units injected. [REAGENT_VOLUME(reagents, GET_DECL(reagent_ids[mode])) || 0] unit\s remaining.")) return TRUE return ..() /obj/item/chems/borghypo/attack_self(mob/user) //Change the mode var/t = "" - for(var/i = 1 to reagent_ids.len) + var/list/reagent_ids = get_generated_reagents() + for(var/i = 1 to length(reagent_ids)) + var/decl/material/reagent = GET_DECL(reagent_ids[i]) if(t) t += ", " if(mode == i) - t += "[reagent_names[i]]" + t += "[reagent.liquid_name]" else - t += "[reagent_names[i]]" + t += "[reagent.liquid_name]" t = "Available reagents: [t]." to_chat(user, t) @@ -97,6 +113,7 @@ /obj/item/chems/borghypo/OnTopic(mob/user, href_list, datum/topic_state/state) if(href_list["reagent_index"]) + var/list/reagent_ids = get_generated_reagents() var/index = text2num(href_list["reagent_index"]) if(index > 0 && index <= reagent_ids.len) playsound(loc, 'sound/effects/pop.ogg', 50, 0) @@ -109,8 +126,9 @@ . = ..() if(distance > 2) return + var/list/reagent_ids = get_generated_reagents() var/decl/material/reagent = GET_DECL(reagent_ids[mode]) - . += SPAN_NOTICE("It is currently producing [reagent.use_name] and has [reagent_volumes[reagent_ids[mode]]] out of [volume] units left.") + . += SPAN_NOTICE("It is currently producing [reagent.use_name] and has [REAGENT_VOLUME(reagents, reagent)] out of [round(reagents.maximum_volume / length(reagent_ids))] units left.") /obj/item/chems/borghypo/service name = "cyborg drink synthesizer" @@ -121,7 +139,9 @@ recharge_time = 3 volume = 60 possible_transfer_amounts = @"[5,10,20,30]" - reagent_ids = list( + +/obj/item/chems/borghypo/service/get_generated_reagents() + var/static/list/_reagent_ids = list( /decl/material/liquid/alcohol/beer, /decl/material/liquid/alcohol/coffee, /decl/material/liquid/alcohol/whiskey, @@ -154,7 +174,8 @@ /decl/material/liquid/drink/citrussoda, /decl/material/liquid/alcohol/beer, /decl/material/liquid/alcohol/coffee - ) + ) + return _reagent_ids /obj/item/chems/borghypo/service/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) return FALSE @@ -166,7 +187,8 @@ if(!ATOM_IS_OPEN_CONTAINER(target) || !target.reagents) return - if(!reagent_volumes[reagent_ids[mode]]) + var/list/reagent_ids = get_generated_reagents() + if(REAGENT_VOLUME(reagents, GET_DECL(reagent_ids[mode])) <= 0) to_chat(user, "[src] is out of this reagent, give it some time to refill.") return @@ -174,8 +196,8 @@ to_chat(user, "[target] is full.") return - var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) + var/t = min(amount_per_transfer_from_this, REAGENT_VOLUME(reagents, GET_DECL(reagent_ids[mode]))) target.add_to_reagents(reagent_ids[mode], t) - reagent_volumes[reagent_ids[mode]] -= t + reagents.remove_reagent(reagent_ids[mode], t) to_chat(user, "You transfer [t] units of the solution to [target].") return diff --git a/nebula.dme b/nebula.dme index 4042ee05fc6e..9506f4371d19 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3634,7 +3634,7 @@ #include "code\modules\reagents\reagent_containers\_glass_edibility.dm" #include "code\modules\reagents\reagent_containers\beaker.dm" #include "code\modules\reagents\reagent_containers\blood_pack.dm" -#include "code\modules\reagents\reagent_containers\borghydro.dm" +#include "code\modules\reagents\reagent_containers\borghypo.dm" #include "code\modules\reagents\reagent_containers\bowl.dm" #include "code\modules\reagents\reagent_containers\bucket.dm" #include "code\modules\reagents\reagent_containers\drinks.dm" From 7f7887d8a45e9d78234c01f2e69eb821ec606bdf Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 21:46:00 +1100 Subject: [PATCH 0886/1331] Cleaning up current_health setting in atom definitions. --- code/game/machinery/doors/windowdoor.dm | 5 +---- code/game/objects/__objs.dm | 4 +++- code/game/objects/items/weapons/tape.dm | 1 - .../objects/items/welding/weldingtool_tank.dm | 1 - code/game/objects/structures/__structure.dm | 2 +- .../closets/secure/_secure_closets.dm | 4 +--- .../crates_lockers/closets/statue.dm | 2 +- code/game/objects/structures/pit.dm | 18 +++++++++--------- code/modules/barricade_tape/barricade_tape.dm | 2 +- .../modules/hydroponics/spreading/spreading.dm | 6 ++---- code/modules/mob/living/human/human.dm | 2 +- .../mob/living/silicon/robot/component.dm | 1 - .../mob/living/simple_animal/hostile/carp.dm | 2 +- .../hostile/retaliate/king_of_goats.dm | 2 +- code/modules/organs/organ.dm | 4 ++-- code/modules/paperwork/paper_bundle.dm | 3 +-- .../guns/launcher/bows/bow_string.dm | 2 +- code/modules/vehicles/bike.dm | 3 --- code/modules/vehicles/train.dm | 3 --- mods/species/ascent/mobs/insectoid_egg.dm | 1 - 20 files changed, 26 insertions(+), 42 deletions(-) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index f39b5849a088..57c7d8feb451 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -6,7 +6,6 @@ min_force = 4 hitsound = 'sound/effects/Glasshit.ogg' max_health = 150 //If you change this, consider changing ../door/window/brigdoor/ health at the bottom of this .dm file - current_health = 150 visible = 0.0 use_power = POWER_USE_OFF stat_immune = NOSCREEN | NOINPUT | NOPOWER @@ -213,11 +212,9 @@ icon = 'icons/obj/doors/windoor.dmi' icon_state = "leftsecure" base_state = "leftsecure" - max_health = 300 - current_health = 300.0 //Stronger doors for prison (regular window door health is 150) + max_health = 300 //Stronger doors for prison (regular window door health is 150) pry_mod = 0.65 - /obj/machinery/door/window/northleft dir = NORTH diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 62566afe1a1c..4510c03626d7 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -30,8 +30,10 @@ //Only apply directional offsets if the mappers haven't set any offsets already if(!pixel_x && !pixel_y && !pixel_w && !pixel_z) update_directional_offset() - if(isnull(current_health)) + if(isnull(current_health) || current_health == INFINITY) current_health = get_max_health() + else + current_health = min(current_health, get_max_health()) /obj/object_shaken() shake_animation() diff --git a/code/game/objects/items/weapons/tape.dm b/code/game/objects/items/weapons/tape.dm index d10c34c4f8e3..0e443936683c 100644 --- a/code/game/objects/items/weapons/tape.dm +++ b/code/game/objects/items/weapons/tape.dm @@ -11,7 +11,6 @@ max_amount = 32 w_class = ITEM_SIZE_SMALL material = /decl/material/solid/organic/plastic - current_health = 10 max_health = 10 matter_multiplier = 0.25 diff --git a/code/game/objects/items/welding/weldingtool_tank.dm b/code/game/objects/items/welding/weldingtool_tank.dm index b28b73115dff..023de068b878 100644 --- a/code/game/objects/items/welding/weldingtool_tank.dm +++ b/code/game/objects/items/welding/weldingtool_tank.dm @@ -12,7 +12,6 @@ obj_flags = OBJ_FLAG_HOLLOW volume = 20 presentation_flags = PRESENTATION_FLAG_NAME - current_health = 40 max_health = 40 material = /decl/material/solid/metal/steel var/can_refuel = TRUE diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 43070098384d..839530cbdbc3 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -124,7 +124,7 @@ return FALSE /obj/structure/take_damage(damage, damage_type = BRUTE, damage_flags, inflicter, armor_pen = 0, silent, do_update_health) - if(current_health == -1) // This object does not take damage. + if(current_health == ITEM_HEALTH_NO_DAMAGE) // This object does not take damage. return if(material && material.is_brittle()) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/_secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/_secure_closets.dm index ff568d29a611..1c2713ef45a2 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/_secure_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/_secure_closets.dm @@ -1,13 +1,11 @@ /obj/structure/closet/secure_closet name = "secure locker" desc = "It's a card-locked storage unit." - closet_appearance = /decl/closet_appearance/secure_closet setup = CLOSET_HAS_LOCK | CLOSET_CAN_BE_WELDED locked = TRUE - wall_mounted = 0 //never solid (You can always pass over it) - current_health = 200 + max_health = 200 /obj/structure/closet/secure_closet/slice_into_parts(obj/item/weldingtool/welder, mob/user) to_chat(user, "\The [src] is too strong to be taken apart.") diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index 946821ceff5a..a8bac2f5fa78 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -6,7 +6,7 @@ density = TRUE anchored = TRUE setup = 0 - current_health = 0 //destroying the statue kills the mob within + max_health = 1 //destroying the statue kills the mob within var/intialTox = 0 //these are here to keep the mob from taking damage from things that logically wouldn't affect a rock var/intialFire = 0 //it's a little sloppy I know but it was this or the GODMODE flag. Lesser of two evils. var/intialBrute = 0 diff --git a/code/game/objects/structures/pit.dm b/code/game/objects/structures/pit.dm index 658290a567ca..09c8ee2cca5d 100644 --- a/code/game/objects/structures/pit.dm +++ b/code/game/objects/structures/pit.dm @@ -1,13 +1,13 @@ /obj/structure/pit - name = "pit" - desc = "Watch your step, partner." - icon = 'icons/obj/structures/pit.dmi' - icon_state = "pit1" - blend_mode = BLEND_MULTIPLY - density = FALSE - anchored = TRUE - current_health = ITEM_HEALTH_NO_DAMAGE //You can't break a hole in the ground. - var/open = TRUE + name = "pit" + desc = "Watch your step, partner." + icon = 'icons/obj/structures/pit.dmi' + icon_state = "pit1" + blend_mode = BLEND_MULTIPLY + density = FALSE + anchored = TRUE + max_health = ITEM_HEALTH_NO_DAMAGE //You can't break a hole in the ground. + var/open = TRUE /obj/structure/pit/attackby(obj/item/used_item, mob/user) if(IS_SHOVEL(used_item)) diff --git a/code/modules/barricade_tape/barricade_tape.dm b/code/modules/barricade_tape/barricade_tape.dm index b0f81d840a3f..689039b34c09 100644 --- a/code/modules/barricade_tape/barricade_tape.dm +++ b/code/modules/barricade_tape/barricade_tape.dm @@ -25,7 +25,7 @@ var/global/list/image/hazard_overlays //Cached hazard floor overlays for the bar pass_flags = PASS_FLAG_TABLE //About the height of table anchored = TRUE material = /decl/material/solid/organic/plastic - current_health = 5 + max_health = 5 var/neighbors = 0 //Contains all the direction flags of all the neighboring tape_barricades var/is_lifted = 0 //Whether the tape is lifted and we're allowing everyone passage. var/is_crumpled = 0 //Whether the tape was damaged diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 2b772c7f15f4..523d2ec04c84 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -45,9 +45,7 @@ icon_state = "" pass_flags = PASS_FLAG_TABLE mouse_opacity = MOUSE_OPACITY_NORMAL - - current_health = 10 - max_health = 100 + max_health = 10 var/growth_threshold = 0 var/growth_type = 0 var/max_growth = 0 @@ -82,7 +80,7 @@ max_health = round(seed.get_trait(TRAIT_ENDURANCE)/2) if(start_matured) mature_time = 0 - current_health = max_health + current_health = get_max_health() if(seed.get_trait(TRAIT_SPREAD) == 2) mouse_opacity = MOUSE_OPACITY_PRIORITY diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 14fd42f76b5c..bd8bec8ebf45 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -9,7 +9,7 @@ /mob/living/human/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) - current_health = max_health + current_health = get_max_health() reset_hud_overlays() var/list/newargs = args.Copy(2) setup_human(arglist(newargs)) diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 3d4a7b1000d8..45a09beca266 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -224,7 +224,6 @@ icon = 'icons/obj/robot_component.dmi' icon_state = "working" material = /decl/material/solid/metal/steel - current_health = 30 max_health = 30 var/burn_damage = 0 var/brute_damage = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index 9d63b3a9b810..2212b6d6d3df 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -45,7 +45,7 @@ /mob/living/simple_animal/hostile/carp/proc/carp_randomify() max_health = rand(initial(max_health), (1.5 * initial(max_health))) - current_health = max_health + current_health = get_max_health() if(prob(1)) carp_color = pick(COLOR_WHITE, COLOR_BLACK) else diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm index 57710f9594f0..9e0c73835b94 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm @@ -222,7 +222,7 @@ phase3 = TRUE spellscast = 0 max_health = 750 - current_health = max_health + current_health = get_max_health() new /obj/item/grenade/flashbang/instant(src.loc) QDEL_NULL(boss_theme) boss_theme = play_looping_sound(src, sound_id, 'sound/music/Visager-Miniboss_Fight.ogg', volume = 10, range = 8, falloff = 4, prefer_mute = TRUE) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 6f483c9e85ec..4833f59de7ed 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -620,9 +620,9 @@ var/global/list/ailment_reference_cache = list() max_health = max_damage if(current_health == ITEM_HEALTH_NO_DAMAGE) - current_health = max_health + current_health = get_max_health() else - current_health = min(current_health, max_health) + current_health = min(current_health, get_max_health()) action_button_name = null screen_loc = null diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index ccb16e145bcf..36e3a047c95d 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -18,8 +18,7 @@ drop_sound = 'sound/foley/paperpickup1.ogg' pickup_sound = 'sound/foley/paperpickup2.ogg' item_flags = ITEM_FLAG_CAN_TAPE - current_health = 10 - max_health = 10 + max_health = 10 var/tmp/cur_page = 1 // current page var/tmp/max_pages = 100 //Maximum number of papers that can be in the bundle var/list/pages // Ordered list of pages as they are to be displayed. Can be different order than src.contents. diff --git a/code/modules/projectiles/guns/launcher/bows/bow_string.dm b/code/modules/projectiles/guns/launcher/bows/bow_string.dm index d2f9569221a7..72212cde43b1 100644 --- a/code/modules/projectiles/guns/launcher/bows/bow_string.dm +++ b/code/modules/projectiles/guns/launcher/bows/bow_string.dm @@ -12,7 +12,7 @@ . = ..() if(material) max_health = max(1, round(initial(max_health) * material.tensile_strength)) - current_health = max_health + current_health = get_max_health() /obj/item/bowstring/Destroy() if(istype(loc, /obj/item/gun/launcher/bow)) diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index 2dd01cbc386c..7522827dc86f 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -4,12 +4,9 @@ icon = 'icons/obj/bike.dmi' icon_state = "bike_off" dir = SOUTH - load_item_visible = 1 buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 5) - current_health = 100 max_health = 100 - locked = 0 fire_dam_coeff = 0.6 brute_dam_coeff = 0.5 diff --git a/code/modules/vehicles/train.dm b/code/modules/vehicles/train.dm index 7c62d658b2e8..0cb43501e1e8 100644 --- a/code/modules/vehicles/train.dm +++ b/code/modules/vehicles/train.dm @@ -1,10 +1,7 @@ /obj/vehicle/train name = "train" dir = EAST - move_delay = 1 - - current_health = 100 max_health = 100 fire_dam_coeff = 0.7 brute_dam_coeff = 0.5 diff --git a/mods/species/ascent/mobs/insectoid_egg.dm b/mods/species/ascent/mobs/insectoid_egg.dm index 0385817c1ec7..5a9fba0cd993 100644 --- a/mods/species/ascent/mobs/insectoid_egg.dm +++ b/mods/species/ascent/mobs/insectoid_egg.dm @@ -18,7 +18,6 @@ var/global/default_gyne /obj/structure/insectoid_egg name = "alien egg" desc = "A semi-translucent alien egg." - current_health = 100 max_health = 100 icon = 'mods/species/ascent/icons/egg.dmi' icon_state = "egg" From 9c2ba22b56100f3581b64acaad99699a7e2f60a5 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 22:47:23 +1100 Subject: [PATCH 0887/1331] Fixes issues with chest trim. --- code/game/objects/structures/crates_lockers/crates.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 4eaac0b2cc2e..6b85c90efb6d 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -327,14 +327,14 @@ var/decl/material/overlay_material = /decl/material/solid/metal/iron /obj/structure/closet/crate/chest/Initialize() - . = ..() if(ispath(overlay_material)) overlay_material = GET_DECL(overlay_material) + . = ..() // icon update is already queued in parent because of closet appearance /obj/structure/closet/crate/chest/update_material_desc(override_desc) ..() - if(overlay_material) + if(istype(overlay_material)) desc = "[desc] It has a trim made of [overlay_material.solid_name]." /obj/structure/closet/crate/chest/on_update_icon() From cf309cb8b67cd03e5889302a3a3bef3ab4943a8f Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 23:19:11 +1100 Subject: [PATCH 0888/1331] Moving various material references onto /atom and cleaning up use of ambiguous terms. --- code/datums/repositories/atom_info.dm | 86 +++++++++---------- code/game/atoms.dm | 6 ++ code/game/objects/items/__item.dm | 2 - code/game/objects/items/_item_drying.dm | 4 +- .../structures/_structure_materials.dm | 2 - code/game/turfs/flooring/flooring_path.dm | 8 +- code/game/turfs/flooring/flooring_rock.dm | 8 +- code/game/turfs/turf.dm | 17 ++-- code/game/turfs/turf_digging.dm | 4 +- code/game/turfs/walls/_wall.dm | 2 - code/modules/admin/verbs/debug.dm | 7 +- .../preference_setup/loadout/loadout.dm | 8 +- .../codex/categories/category_substances.dm | 8 +- code/modules/fabrication/designs/_design.dm | 4 +- code/modules/fabrication/fabricator_intake.dm | 12 +-- code/modules/fabrication/recycler.dm | 4 +- code/modules/materials/_material_stack.dm | 1 - code/modules/materials/_materials.dm | 24 +++--- .../solids/materials_solid_alien.dm | 2 +- code/modules/materials/material_debris.dm | 4 +- .../modules/materials/material_gas_overlay.dm | 1 - .../materials/material_product_spawning.dm | 4 +- code/modules/mechs/mech.dm | 3 - .../modules/power/fusion/kinetic_harvester.dm | 32 +++---- code/modules/reagents/Chemistry-Grinder.dm | 12 +-- code/modules/reagents/Chemistry-Holder.dm | 4 +- code/unit_tests/food_tests.dm | 4 +- code/unit_tests/materials.dm | 16 ++-- .../circuit_serialization.dm | 4 +- .../components/input.dm | 4 +- .../integrated_electronics/tools/printer.dm | 40 ++++----- mods/content/xenobiology/slime/items.dm | 4 +- mods/gamemodes/cult/materials.dm | 2 +- 33 files changed, 166 insertions(+), 177 deletions(-) diff --git a/code/datums/repositories/atom_info.dm b/code/datums/repositories/atom_info.dm index 9e842cf2feb0..a84f10736b76 100644 --- a/code/datums/repositories/atom_info.dm +++ b/code/datums/repositories/atom_info.dm @@ -9,79 +9,79 @@ var/global/repository/atom_info/atom_info_repository = new() var/list/matter_mult_cache = list() var/list/origin_tech_cache = list() -/repository/atom_info/proc/create_key_for(var/path, var/material, var/amount) - . = "[path]" - if(ispath(path, /obj) && material) // only objects take material as an arg - . = "[.]-[material]" - if(ispath(path, /obj/item/stack) && !isnull(amount)) // similarly for stacks and amount - . = "[.]-[amount]" +/repository/atom_info/proc/create_key_for(var/_path, var/_mat, var/_amount) + . = "[_path]" + if(ispath(_path, /obj) && _mat) // only objects take material as an arg + . = "[.]-[_mat]" + if(ispath(_path, /obj/item/stack) && !isnull(_amount)) // similarly for stacks and amount + . = "[.]-[_amount]" -/repository/atom_info/proc/get_instance_of(var/path, var/material, var/amount) - if(ispath(path, /obj/item/stack)) - . = new path(null, amount, material) - else if(ispath(path, /obj)) - . = new path(null, material) +/repository/atom_info/proc/get_instance_of(var/_path, var/_mat, var/_amount) + if(ispath(_path, /obj/item/stack)) + . = new _path(null, _amount, _mat) + else if(ispath(_path, /obj)) + . = new _path(null, _mat) else - . = new path + . = new _path -/repository/atom_info/proc/update_cached_info_for(var/path, var/material, var/amount, var/key) +/repository/atom_info/proc/update_cached_info_for(var/_path, var/_mat, var/_amount, var/key) var/atom/instance if(!matter_cache[key]) - instance = get_instance_of(path, material, amount) + instance = get_instance_of(_path, _mat, _amount) matter_cache[key] = instance.get_contained_matter() || list() if(!combined_worth_cache[key]) - instance = instance || get_instance_of(path, material, amount) + instance = instance || get_instance_of(_path, _mat, _amount) combined_worth_cache[key] = instance.get_combined_monetary_worth() if(!single_worth_cache[key]) - instance = instance || get_instance_of(path, material, amount) + instance = instance || get_instance_of(_path, _mat, _amount) single_worth_cache[key] = instance.get_single_monetary_worth() if(!name_cache[key]) - instance = instance || get_instance_of(path, material, amount) + instance = instance || get_instance_of(_path, _mat, _amount) name_cache[key] = instance.name if(!description_cache[key]) - instance = instance || get_instance_of(path, material, amount) + instance = instance || get_instance_of(_path, _mat, _amount) description_cache[key] = instance.desc - if(!matter_mult_cache[key] && ispath(path, /obj)) - var/obj/obj_instance = instance || get_instance_of(path, material, amount) + if(!matter_mult_cache[key] && ispath(_path, /obj)) + var/obj/obj_instance = instance || get_instance_of(_path, _mat, _amount) matter_mult_cache[key] = obj_instance.get_matter_amount_modifier() - if(!origin_tech_cache[key] && ispath(path, /obj/item)) - var/obj/item/item_instance = instance || get_instance_of(path, material, amount) + if(!origin_tech_cache[key] && ispath(_path, /obj/item)) + var/obj/item/item_instance = instance || get_instance_of(_path, _mat, _amount) origin_tech_cache[key] = cached_json_decode(item_instance.get_origin_tech()) if(!QDELETED(instance)) qdel(instance) -/repository/atom_info/proc/get_matter_for(var/path, var/material, var/amount) +/repository/atom_info/proc/get_matter_for(var/_path, var/_mat, var/_amount) RETURN_TYPE(/list) - var/key = create_key_for(path, material, amount) - update_cached_info_for(path, material, amount, key) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key) . = matter_cache[key] -/repository/atom_info/proc/get_combined_worth_for(var/path, var/material, var/amount) - var/key = create_key_for(path, material, amount) - update_cached_info_for(path, material, amount, key) +/repository/atom_info/proc/get_combined_worth_for(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key) . = combined_worth_cache[key] -/repository/atom_info/proc/get_single_worth_for(var/path, var/material, var/amount) - var/key = create_key_for(path, material, amount) - update_cached_info_for(path, material, amount, key) +/repository/atom_info/proc/get_single_worth_for(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key) . = single_worth_cache[key] -/repository/atom_info/proc/get_name_for(var/path, var/material, var/amount) - var/key = create_key_for(path, material, amount) - update_cached_info_for(path, material, amount, key) +/repository/atom_info/proc/get_name_for(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key) . = name_cache[key] -/repository/atom_info/proc/get_description_for(var/path, var/material, var/amount) - var/key = create_key_for(path, material, amount) - update_cached_info_for(path, material, amount, key) +/repository/atom_info/proc/get_description_for(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key) . = description_cache[key] -/repository/atom_info/proc/get_matter_multiplier_for(var/path, var/material, var/amount) - var/key = create_key_for(path, material, amount) - update_cached_info_for(path, material, amount, key) +/repository/atom_info/proc/get_matter_multiplier_for(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key) . = matter_mult_cache[key] -/repository/atom_info/proc/get_origin_tech_for(var/path, var/material, var/amount) - var/key = create_key_for(path, material, amount) - update_cached_info_for(path, material, amount, key) +/repository/atom_info/proc/get_origin_tech_for(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key) . = origin_tech_cache[key] \ No newline at end of file diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 376472d4ffa5..18af0e9a1886 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -67,6 +67,12 @@ /// (STRING) A color applied over the top of any material color. Implemented on /obj/item, /obj/structure and /turf. var/paint_color + /// (DATUM) Reference to material decl. If set to a /decl/material path, will init the item with that material. + /// Implemented on /mob/living/exosuit, /turf/wall, /obj/item and /obj/structure + var/decl/material/material + /// (DATUM) Similar to above, but largely used by /turf/wall, /obj/structure and /obj/item/stack/material + var/decl/material/reinf_material + /atom/proc/get_max_health() return max_health diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index ece94ed6a6a9..5c0a8f52c946 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -90,8 +90,6 @@ /// Assoc list of bodytype category to icon for producing onmob overlays when this item is held or worn. var/list/sprite_sheets - // Material handling for material weapons (not used by default, unless material is supplied or set) - var/decl/material/material // Reference to material decl. If set to a string corresponding to a material ID, will init the item with that material. ///Will apply the flagged modifications to the object var/material_alteration = MAT_FLAG_ALTERATION_NONE var/anomaly_shielding // 0..1 value of how well it shields against xenoarch anomalies diff --git a/code/game/objects/items/_item_drying.dm b/code/game/objects/items/_item_drying.dm index c3044b33be21..5357133fe505 100644 --- a/code/game/objects/items/_item_drying.dm +++ b/code/game/objects/items/_item_drying.dm @@ -77,9 +77,9 @@ if(!length(matter)) return FALSE for(var/mat in matter) - var/decl/material/material = GET_DECL(mat) + var/decl/material/heated_material = GET_DECL(mat) // We should burn if we're above the temperature damage threshold. - if(!isnull(material.temperature_damage_threshold) && exposed_temperature >= material.temperature_damage_threshold) + if(!isnull(heated_material.temperature_damage_threshold) && exposed_temperature >= heated_material.temperature_damage_threshold) return TRUE return FALSE diff --git a/code/game/objects/structures/_structure_materials.dm b/code/game/objects/structures/_structure_materials.dm index 533df4ca3d72..a6c98ee0a1bc 100644 --- a/code/game/objects/structures/_structure_materials.dm +++ b/code/game/objects/structures/_structure_materials.dm @@ -1,6 +1,4 @@ /obj/structure - var/decl/material/material - var/decl/material/reinf_material var/material_alteration var/dismantled var/name_prefix diff --git a/code/game/turfs/flooring/flooring_path.dm b/code/game/turfs/flooring/flooring_path.dm index c0de0be830eb..6a934d5c5eb3 100644 --- a/code/game/turfs/flooring/flooring_path.dm +++ b/code/game/turfs/flooring/flooring_path.dm @@ -16,11 +16,11 @@ var/paver_noun = "stones" /decl/flooring/path/update_turf_strings(turf/floor/target) - var/decl/material/material = target?.get_material() - ASSERT(material?.adjective_name) + var/decl/material/floor_material = target?.get_material() + ASSERT(floor_material?.adjective_name) ASSERT(paver_noun) - target.SetName("[material.adjective_name] [name]") - target.desc = "[jointext_no_nulls(list("A", paving_adjective, "path made of", paver_adjective, material.adjective_name, paver_noun), " ")]." + target.SetName("[floor_material.adjective_name] [name]") + target.desc = "[jointext_no_nulls(list("A", paving_adjective, "path made of", paver_adjective, floor_material.adjective_name, paver_noun), " ")]." /decl/flooring/path/cobblestone name = "cobblestones" diff --git a/code/game/turfs/flooring/flooring_rock.dm b/code/game/turfs/flooring/flooring_rock.dm index b5935e2109cc..13836a0a7da7 100644 --- a/code/game/turfs/flooring/flooring_rock.dm +++ b/code/game/turfs/flooring/flooring_rock.dm @@ -10,7 +10,7 @@ uid = "floor_reinf_shuttle_rock" /decl/flooring/rock/update_turf_strings(turf/floor/target) - var/decl/material/material = target?.get_material() - ASSERT(material?.adjective_name) - target.SetName("[material.adjective_name] [name]") - target.desc = "An expanse of bare [material.solid_name]." + var/decl/material/turf_material = target?.get_material() + ASSERT(turf_material?.adjective_name) + target.SetName("[turf_material.adjective_name] [name]") + target.desc = "An expanse of bare [turf_material.solid_name]." diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 5c21bc62b0e2..8f74908751a6 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -272,8 +272,8 @@ try_dig_farm(user, used_item) return TRUE - var/decl/material/material = get_material() - if(IS_PICK(used_item) && material) + var/decl/material/digging_material = get_material() + if(IS_PICK(used_item) && digging_material) // TODO: move these checks into the interaction handlers. var/atom/platform = get_supporting_platform() @@ -281,7 +281,7 @@ to_chat(user, SPAN_WARNING("\The [platform] [platform.get_pronouns().is] in the way!")) return TRUE - if(material?.hardness <= MAT_VALUE_FLEXIBLE) + if(digging_material?.hardness <= MAT_VALUE_FLEXIBLE) to_chat(user, SPAN_WARNING("\The [src] is too soft to be excavated with \the [used_item]. Use a shovel.")) return TRUE @@ -823,12 +823,7 @@ return null /turf/get_color() - if(paint_color) - return paint_color - var/decl/material/material = get_material() - if(material) - return material.color - return color + return paint_color || get_material()?.color || color /turf/proc/get_fishing_result(obj/item/food/bait) var/area/A = get_area(src) @@ -908,8 +903,8 @@ if(T.can_dig_trench(prop?.material?.hardness)) T.try_dig_trench(user, prop) else if(IS_PICK(prop)) - var/decl/material/material = T.get_material() - if(material?.hardness > MAT_VALUE_FLEXIBLE && T.can_dig_trench(prop?.material?.hardness, using_tool = TOOL_PICK)) + var/decl/material/digging_material = T.get_material() + if(digging_material?.hardness > MAT_VALUE_FLEXIBLE && T.can_dig_trench(prop?.material?.hardness, using_tool = TOOL_PICK)) T.try_dig_trench(user, prop, using_tool = TOOL_PICK) /decl/interaction_handler/dig/pit diff --git a/code/game/turfs/turf_digging.dm b/code/game/turfs/turf_digging.dm index d9944cc1c814..41be30031860 100644 --- a/code/game/turfs/turf_digging.dm +++ b/code/game/turfs/turf_digging.dm @@ -57,8 +57,8 @@ return get_plant_growth_rate() > 0 && can_be_dug(tool_hardness, using_tool) && !(locate(/obj/machinery/portable_atmospherics/hydroponics/soil) in src) /turf/proc/try_dig_farm(mob/user, obj/item/tool, using_tool = TOOL_HOE) - var/decl/material/material = get_material() - if(!material?.tillable) + var/decl/material/turf_material = get_material() + if(!turf_material?.tillable) return if((!user && !tool) || tool.do_tool_interaction(using_tool, user, src, 5 SECONDS, set_cooldown = TRUE, check_skill = SKILL_BOTANY)) return dig_farm(user, tool?.material?.hardness, using_tool) diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index ef02c8f94328..119683c5413c 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -36,8 +36,6 @@ var/global/list/wall_fullblend_objects = list( var/unique_merge_identifier var/damage = 0 var/can_open = 0 - var/decl/material/material - var/decl/material/reinf_material var/decl/material/girder_material = /decl/material/solid/metal/steel var/construction_stage var/hitsound = 'sound/weapons/Genhit.ogg' diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 22e04658b19a..ff76ca49fe9b 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -425,10 +425,9 @@ set name = "Spawn Material Stack" if(!check_rights(R_DEBUG)) return - var/decl/material/material = input("Select material to spawn") as null|anything in decls_repository.get_decls_of_subtype_unassociated(/decl/material) - if(!istype(material)) - return - material.create_object(get_turf(mob), 50) + var/decl/material/spawn_material = input("Select material to spawn") as null|anything in decls_repository.get_decls_of_subtype_unassociated(/decl/material) + if(istype(spawn_material)) + spawn_material.create_object(get_turf(mob), 50) /client/proc/force_ghost_trap_trigger() set category = "Debug" diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index f893c8b49080..ceb5eaaeac52 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -491,10 +491,10 @@ var/location var/material -/datum/gear_data/New(var/path, var/location, var/material) - src.path = path - src.location = location - src.material = material +/datum/gear_data/New(var/_path, var/_location, var/_mat) + src.path = _path + src.location = _location + src.material = _mat /datum/gear_data/proc/can_replace_existing(obj/item/candidate) return istype(candidate, path) diff --git a/code/modules/codex/categories/category_substances.dm b/code/modules/codex/categories/category_substances.dm index 7deca9a4156c..9d2c5ab8c95e 100644 --- a/code/modules/codex/categories/category_substances.dm +++ b/code/modules/codex/categories/category_substances.dm @@ -66,10 +66,10 @@ if(mat.dissolves_in != MAT_SOLVENT_IMMUNE && LAZYLEN(mat.dissolves_into)) var/chems = list() for(var/chemical in mat.dissolves_into) - var/decl/material/material = GET_DECL(chemical) - var/material_link = "[material.name]" - if(material.hidden_from_codex) - material_link = material.name + var/decl/material/chemical_decl = GET_DECL(chemical) + var/material_link = "[chemical_decl.name]" + if(chemical_decl.hidden_from_codex) + material_link = chemical_decl.name chems += "[material_link] ([mat.dissolves_into[chemical]*100]%)" var/solvent_needed if(mat.dissolves_in <= MAT_SOLVENT_NONE) diff --git a/code/modules/fabrication/designs/_design.dm b/code/modules/fabrication/designs/_design.dm index c37667e53ad3..0b4e7ecd7114 100644 --- a/code/modules/fabrication/designs/_design.dm +++ b/code/modules/fabrication/designs/_design.dm @@ -17,7 +17,7 @@ // Populate name and resources from the product type. /datum/fabricator_recipe/proc/get_product_name() - . = atom_info_repository.get_name_for(path, amount = 1) + . = atom_info_repository.get_name_for(path, _amount = 1) /datum/fabricator_recipe/New() ..() @@ -29,7 +29,7 @@ name = get_product_name() if(required_technology == TRUE) if(ispath(path, /obj/item)) - required_technology = atom_info_repository.get_origin_tech_for(path, amount = 1) + required_technology = atom_info_repository.get_origin_tech_for(path, _amount = 1) if(!islist(required_technology)) required_technology = list() if(!resources) diff --git a/code/modules/fabrication/fabricator_intake.dm b/code/modules/fabrication/fabricator_intake.dm index f3458e04a45f..3d2f8cf9e294 100644 --- a/code/modules/fabrication/fabricator_intake.dm +++ b/code/modules/fabrication/fabricator_intake.dm @@ -40,22 +40,22 @@ var/mat_colour = thing.color for(var/mat in thing.matter) - var/decl/material/material_def = GET_DECL(mat) - if(!material_def || !base_storage_capacity[material_def.type]) + var/decl/material/ingest_material = GET_DECL(mat) + if(!ingest_material || !base_storage_capacity[mat]) continue - var/taking_material = min(thing.matter[mat], storage_capacity[material_def.type] - stored_material[material_def.type]) + var/taking_material = min(thing.matter[mat], storage_capacity[mat] - stored_material[mat]) if(taking_material <= 0) continue if(!mat_colour) - mat_colour = material_def.color + mat_colour = ingest_material.color - stored_material[material_def.type] += taking_material + stored_material[mat] += taking_material if(stack_ref) stacks_used = max(stacks_used, ceil(taking_material/stack_matter_div)) - if(storage_capacity[material_def.type] == stored_material[material_def.type]) + if(storage_capacity[mat] == stored_material[mat]) . = SUBSTANCE_TAKEN_FULL else if(. != SUBSTANCE_TAKEN_FULL) . = SUBSTANCE_TAKEN_ALL diff --git a/code/modules/fabrication/recycler.dm b/code/modules/fabrication/recycler.dm index d6da2d6efd70..10ab9407a954 100644 --- a/code/modules/fabrication/recycler.dm +++ b/code/modules/fabrication/recycler.dm @@ -83,8 +83,8 @@ visible_message("[capitalize(english_list(fell_out))] fall out of \the overflowing [src]!") for(var/mat in munched_matter) - var/decl/material/material = GET_DECL(mat) - switch(material.phase_at_temperature()) + var/decl/material/munched_material = GET_DECL(mat) + switch(munched_material.phase_at_temperature()) if(MAT_PHASE_SOLID) // Dump the material out as a stack. diff --git a/code/modules/materials/_material_stack.dm b/code/modules/materials/_material_stack.dm index 759b9c08801f..113e54a57647 100644 --- a/code/modules/materials/_material_stack.dm +++ b/code/modules/materials/_material_stack.dm @@ -18,7 +18,6 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR var/can_be_pulverized = FALSE var/can_be_reinforced = FALSE - var/decl/material/reinf_material /obj/item/stack/material/Initialize(mapload, var/amount, var/_material, var/_reinf_material) diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 9defbc34504d..2ed880fd3b89 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -545,25 +545,25 @@ var/global/list/_descriptive_temperature_strings if(!_descriptive_temperature_strings) _descriptive_temperature_strings = list() - for(var/decl/material/material as anything in decls_repository.get_decls_of_subtype_unassociated(/decl/material)) + for(var/decl/material/desc_material as anything in decls_repository.get_decls_of_subtype_unassociated(/decl/material)) - if(material.type != material.temperature_burn_milestone_material) + if(desc_material.type != desc_material.temperature_burn_milestone_material) continue - if(!isnull(material.bakes_into_at_temperature) && material.bakes_into_material) - var/decl/material/cook = GET_DECL(material.bakes_into_material) - global._descriptive_temperature_strings["bake [material.name] into [cook.name]"] = material.bakes_into_at_temperature + if(!isnull(desc_material.bakes_into_at_temperature) && desc_material.bakes_into_material) + var/decl/material/cook = GET_DECL(desc_material.bakes_into_material) + global._descriptive_temperature_strings["bake [desc_material.name] into [cook.name]"] = desc_material.bakes_into_at_temperature continue - switch(material.phase_at_temperature()) + switch(desc_material.phase_at_temperature()) if(MAT_PHASE_SOLID) - if(!isnull(material.ignition_point)) - global._descriptive_temperature_strings["ignite [material.name]"] = material.ignition_point - else if(!isnull(material.melting_point)) - global._descriptive_temperature_strings["melt [material.name]"] = material.melting_point + if(!isnull(desc_material.ignition_point)) + global._descriptive_temperature_strings["ignite [desc_material.name]"] = desc_material.ignition_point + else if(!isnull(desc_material.melting_point)) + global._descriptive_temperature_strings["melt [desc_material.name]"] = desc_material.melting_point if(MAT_PHASE_LIQUID) - if(!isnull(material.boiling_point)) - global._descriptive_temperature_strings["boil [material.name]"] = material.boiling_point + if(!isnull(desc_material.boiling_point)) + global._descriptive_temperature_strings["boil [desc_material.name]"] = desc_material.boiling_point for(var/burn_string in global._descriptive_temperature_strings) if(temperature >= global._descriptive_temperature_strings[burn_string]) diff --git a/code/modules/materials/definitions/solids/materials_solid_alien.dm b/code/modules/materials/definitions/solids/materials_solid_alien.dm index 97ff03bfe45f..8b4c33935d44 100644 --- a/code/modules/materials/definitions/solids/materials_solid_alien.dm +++ b/code/modules/materials/definitions/solids/materials_solid_alien.dm @@ -26,5 +26,5 @@ melting_point = rand(400,11000) . = ..() -/decl/material/solid/metal/aliumium/place_dismantled_girder(var/turf/target, var/decl/material/reinf_material) +/decl/material/solid/metal/aliumium/place_dismantled_girder(var/turf/target, var/decl/material/r_mat) return \ No newline at end of file diff --git a/code/modules/materials/material_debris.dm b/code/modules/materials/material_debris.dm index aa5a3ffed5c3..0eece4d7788d 100644 --- a/code/modules/materials/material_debris.dm +++ b/code/modules/materials/material_debris.dm @@ -21,8 +21,8 @@ var/mat_amt = matter[mat] if(!highest_mat || matter[highest_mat] < mat_amt) highest_mat = mat - var/decl/material/material_decl = GET_DECL(mat) - mat_names += material_decl.solid_name + var/decl/material/scrap_material = GET_DECL(mat) + mat_names += scrap_material.solid_name total_matter += mat_amt // Safety check, although this should be prevented for player side interactions diff --git a/code/modules/materials/material_gas_overlay.dm b/code/modules/materials/material_gas_overlay.dm index a6384957de02..0eb154f43058 100644 --- a/code/modules/materials/material_gas_overlay.dm +++ b/code/modules/materials/material_gas_overlay.dm @@ -6,7 +6,6 @@ layer = FIRE_LAYER appearance_flags = RESET_COLOR mouse_opacity = MOUSE_OPACITY_UNCLICKABLE - var/decl/material/material INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) diff --git a/code/modules/materials/material_product_spawning.dm b/code/modules/materials/material_product_spawning.dm index 291e4c1ea8c8..8e5711291cd7 100644 --- a/code/modules/materials/material_product_spawning.dm +++ b/code/modules/materials/material_product_spawning.dm @@ -27,8 +27,8 @@ placed.dropInto(target) // Places a girder object when a wall is dismantled, also applies reinforced material. -/decl/material/proc/place_dismantled_girder(var/turf/target, var/decl/material/reinf_material) - return create_object(target, 1, /obj/structure/girder, ispath(reinf_material) ? reinf_material : reinf_material?.type) +/decl/material/proc/place_dismantled_girder(var/turf/target, var/decl/material/r_mat) + return create_object(target, 1, /obj/structure/girder, ispath(r_mat) ? r_mat : r_mat?.type) // General wall debris product placement. // Not particularly necessary aside from snowflakey cult girders. diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/mech.dm index ca7f1783cc39..2530ea6bd46d 100644 --- a/code/modules/mechs/mech.dm +++ b/code/modules/mechs/mech.dm @@ -47,9 +47,6 @@ var/hardpoints_locked var/maintenance_protocols - // Material - var/decl/material/material - // Cockpit access vars. var/hatch_closed = FALSE var/hatch_locked = FALSE diff --git a/code/modules/power/fusion/kinetic_harvester.dm b/code/modules/power/fusion/kinetic_harvester.dm index 51d150e0c8d0..34f832bf13fa 100644 --- a/code/modules/power/fusion/kinetic_harvester.dm +++ b/code/modules/power/fusion/kinetic_harvester.dm @@ -65,9 +65,9 @@ data["status"] = (use_power >= POWER_USE_ACTIVE) data["materials"] = list() for(var/mat in stored) - var/decl/material/material = GET_DECL(mat) + var/decl/material/stored_material = GET_DECL(mat) var/sheets = floor(stored[mat]/(SHEET_MATERIAL_AMOUNT * 1.5)) - data["materials"] += list(list("name" = material.solid_name, "amount" = sheets, "harvest" = harvesting[mat], "mat_ref" = "\ref[material]")) + data["materials"] += list(list("name" = stored_material.solid_name, "amount" = sheets, "harvest" = harvesting[mat], "mat_ref" = "\ref[stored_material]")) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -109,15 +109,15 @@ /obj/machinery/kinetic_harvester/OnTopic(var/mob/user, var/href_list, var/datum/topic_state/state) if(href_list["remove_mat"]) - var/decl/material/material = locate(href_list["remove_mat"]) - if(istype(material)) + var/decl/material/remove_material = locate(href_list["remove_mat"]) + if(istype(remove_material)) var/sheet_cost = (SHEET_MATERIAL_AMOUNT * 1.5) - var/sheets = floor(stored[material.type]/sheet_cost) + var/sheets = floor(stored[remove_material.type]/sheet_cost) if(sheets > 0) - material.create_object(loc, sheets) - stored[material.type] -= (sheets * sheet_cost) - if(stored[material.type] <= 0) - stored -= material.type + remove_material.create_object(loc, sheets) + stored[remove_material.type] -= (sheets * sheet_cost) + if(stored[remove_material.type] <= 0) + stored -= remove_material.type return TOPIC_REFRESH if(href_list["toggle_power"]) @@ -126,12 +126,12 @@ return TOPIC_REFRESH if(href_list["toggle_harvest"]) - var/decl/material/material = locate(href_list["toggle_harvest"]) - if(istype(material)) - if(harvesting[material.type]) - harvesting -= material.type + var/decl/material/harvest_material = locate(href_list["toggle_harvest"]) + if(istype(harvest_material)) + if(harvesting[harvest_material.type]) + harvesting -= harvest_material.type else - harvesting[material.type] = TRUE - if(!(material.type in stored)) - stored[material.type] = 0 + harvesting[harvest_material.type] = TRUE + if(!(harvest_material.type in stored)) + stored[harvest_material.type] = 0 return TOPIC_REFRESH diff --git a/code/modules/reagents/Chemistry-Grinder.dm b/code/modules/reagents/Chemistry-Grinder.dm index c5ff5f530bab..ca34cc020990 100644 --- a/code/modules/reagents/Chemistry-Grinder.dm +++ b/code/modules/reagents/Chemistry-Grinder.dm @@ -100,9 +100,9 @@ return if(istype(used_item,/obj/item/stack/material)) - var/decl/material/material = used_item.get_material() - if(!material) - to_chat(user, SPAN_NOTICE("\The [material.solid_name] cannot be ground down to any usable reagents.")) + var/decl/material/grind_material = used_item.get_material() + if(!grind_material) + to_chat(user, SPAN_NOTICE("\The [grind_material.solid_name] cannot be ground down to any usable reagents.")) return TRUE else if(!used_item.reagents?.total_volume) @@ -203,8 +203,8 @@ var/obj/item/stack/material/stack = thing if(istype(stack)) - var/decl/material/material = stack.get_material() - if(!material) + var/decl/material/grind_material = stack.get_material() + if(!grind_material) break var/amount_to_take = max(0,min(stack.amount, floor(remaining_volume / REAGENT_UNITS_PER_MATERIAL_SHEET))) @@ -212,7 +212,7 @@ stack.use(amount_to_take) if(QDELETED(stack)) holdingitems -= stack - beaker.add_to_reagents(material.type, (amount_to_take * REAGENT_UNITS_PER_MATERIAL_SHEET * skill_factor)) + beaker.add_to_reagents(grind_material.type, (amount_to_take * REAGENT_UNITS_PER_MATERIAL_SHEET * skill_factor)) continue else if(thing.reagents) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 079b274c59a4..db6297109b64 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -30,8 +30,8 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new var/obj/item/debris/scraps/scraps for(var/mat in materials) var/amount = materials[mat] - var/decl/material/material_data = GET_DECL(mat) - switch(material_data.phase_at_temperature(exposed_temperature, ambient_pressure)) + var/decl/material/burn_material = GET_DECL(mat) + switch(burn_material.phase_at_temperature(exposed_temperature, ambient_pressure)) if(MAT_PHASE_SOLID) if(!scraps) diff --git a/code/unit_tests/food_tests.dm b/code/unit_tests/food_tests.dm index d9192ca4a272..44fc77c01f66 100644 --- a/code/unit_tests/food_tests.dm +++ b/code/unit_tests/food_tests.dm @@ -127,8 +127,8 @@ grown = grown.dry_out(null, grown.get_max_drying_wetness() + 1) grown.forceMove(container) - for(var/material_key in recipe.reagents) - container.add_to_reagents(material_key, recipe.reagents[material_key]) + for(var/mat in recipe.reagents) + container.add_to_reagents(mat, recipe.reagents[mat]) if(!recipe_is_valid) QDEL_LIST(container.contents) // clean up prematurely diff --git a/code/unit_tests/materials.dm b/code/unit_tests/materials.dm index 8ad6424f271c..71791687f612 100644 --- a/code/unit_tests/materials.dm +++ b/code/unit_tests/materials.dm @@ -60,11 +60,11 @@ // This is obscene, but completeness requires it. for(var/stack_type in stack_types) for(var/tool_type in tool_types) - for(var/decl/material/material in test_materials) + for(var/decl/material/test_material in test_materials) for(var/decl/material/reinforced as anything in (test_materials + null)) // Get a linear list of all recipes available to this combination. - var/list/recipes = get_stack_recipes(material, reinforced, stack_type, tool_type, flat = TRUE) + var/list/recipes = get_stack_recipes(test_material, reinforced, stack_type, tool_type, flat = TRUE) if(!length(recipes)) continue @@ -72,7 +72,7 @@ for(var/decl/stack_recipe/recipe as anything in recipes) if(!recipe.result_type || ispath(recipe.result_type, /turf)) // Cannot exist without a loc and doesn't have matter, cannot assess here. continue - var/list/results = recipe.spawn_result(null, null, 1, material, reinforced, null) + var/list/results = recipe.spawn_result(null, null, 1, test_material, reinforced, null) var/atom/product = LAZYACCESS(results, 1) var/list/failed = list() if(!product) @@ -83,21 +83,21 @@ var/list/product_matter = list() for(var/obj/product_obj in results) product_matter = MERGE_ASSOCS_WITH_NUM_VALUES(product_matter, product_obj.get_contained_matter(include_reagents = FALSE)) - if(!material && !reinforced) + if(!test_material && !reinforced) if(length(product_matter)) failed += "unsupplied material types" - else if(material && (product_matter[material.type]) > recipe.req_amount) - failed += "excessive base material ([recipe.req_amount]/[ceil(product_matter[material.type])])" + else if(test_material && (product_matter[test_material.type]) > recipe.req_amount) + failed += "excessive base material ([recipe.req_amount]/[ceil(product_matter[test_material.type])])" else if(reinforced && (product_matter[reinforced.type]) > recipe.req_amount) failed += "excessive reinf material ([recipe.req_amount]/[ceil(product_matter[reinforced.type])])" else for(var/mat in product_matter) - if(mat != material?.type && mat != reinforced?.type) + if(mat != test_material?.type && mat != reinforced?.type) failed += "extra material type ([mat])" if(length(failed)) // Try to prune out some duplicate error spam, we have too many materials now if(!(recipe.type in seen_design_types)) - failed_designs += "[material?.type || "null mat"] - [reinforced?.type || "null reinf"] - [tool_type] - [stack_type] - [recipe.type] - [english_list(failed)]" + failed_designs += "[test_material?.type || "null mat"] - [reinforced?.type || "null reinf"] - [tool_type] - [stack_type] - [recipe.type] - [english_list(failed)]" seen_design_types += recipe.type failed_count++ else diff --git a/mods/content/integrated_electronics/circuit_serialization.dm b/mods/content/integrated_electronics/circuit_serialization.dm index fd6eda7eabf7..11eb269e0520 100644 --- a/mods/content/integrated_electronics/circuit_serialization.dm +++ b/mods/content/integrated_electronics/circuit_serialization.dm @@ -293,8 +293,8 @@ // Update estimated assembly complexity, taken space and material cost blocks["complexity"] += component.complexity blocks["used_space"] += component.size - for(var/material in component.matter) - blocks["cost"][material] += component.matter[material] + for(var/mat in component.matter) + blocks["cost"][mat] += component.matter[mat] // Check if the assembly requires printer upgrades if(!(component.spawn_flags & IC_SPAWN_DEFAULT)) diff --git a/mods/content/integrated_electronics/components/input.dm b/mods/content/integrated_electronics/components/input.dm index 0e0492966ced..30268d3cb5da 100644 --- a/mods/content/integrated_electronics/components/input.dm +++ b/mods/content/integrated_electronics/components/input.dm @@ -299,8 +299,8 @@ set_pin_data(IC_OUTPUT, i, null) if(plant.seed && (plant in view(get_turf(src)))) // Like the medbot's analyzer it can be used at range. for(var/chem_path in plant.seed.chems) - var/decl/material/material = GET_DECL(chem_path) - greagents.Add(material.use_name) + var/decl/material/seed_chem = GET_DECL(chem_path) + greagents.Add(seed_chem.use_name) set_pin_data(IC_OUTPUT, 1, greagents) push_data() diff --git a/mods/content/integrated_electronics/tools/printer.dm b/mods/content/integrated_electronics/tools/printer.dm index a27fd59fe930..43077bfe89e8 100644 --- a/mods/content/integrated_electronics/tools/printer.dm +++ b/mods/content/integrated_electronics/tools/printer.dm @@ -49,14 +49,14 @@ /obj/item/integrated_circuit_printer/proc/recycle(obj/item/used_item, mob/user, obj/item/electronic_assembly/assembly) if(!used_item.canremove) //in case we have an augment circuit return - for(var/material in used_item.matter) - if(materials[material] + used_item.matter[material] > metal_max) - var/decl/material/material_datum = GET_DECL(material) - if(material_datum) - to_chat(user, "[src] can't hold any more [material_datum.name]!") + for(var/mat in used_item.matter) + if(materials[mat] + used_item.matter[mat] > metal_max) + var/decl/material/recycle_material = GET_DECL(mat) + if(recycle_material) + to_chat(user, "[src] can't hold any more [recycle_material.name]!") return - for(var/material in used_item.matter) - materials[material] += used_item.matter[material] + for(var/mat in used_item.matter) + materials[mat] += used_item.matter[mat] if(assembly) assembly.remove_component(used_item) if(user) @@ -152,9 +152,9 @@ else HTML += "Materials: " var/list/dat = list() - for(var/material in materials) - var/decl/material/material_datum = GET_DECL(material) - dat += "[materials[material]]/[metal_max] [material_datum.name]" + for(var/mat in materials) + var/decl/material/print_material = GET_DECL(mat) + dat += "[materials[mat]]/[metal_max] [print_material.name]" HTML += jointext(dat, "; ") HTML += ".

      " @@ -296,8 +296,8 @@ if(!subtract_material_costs(cost, usr)) return var/cloning_time = 0 - for(var/material in cost) - cloning_time += cost[material] + for(var/mat in cost) + cloning_time += cost[mat] cloning_time = round(cloning_time/15) cloning_time = min(cloning_time, MAX_CIRCUIT_CLONE_TIME) cloning = TRUE @@ -313,19 +313,19 @@ to_chat(usr, "Cloning has been canceled. Cost has been refunded.") cloning = FALSE var/cost = program["cost"] - for(var/material in cost) - materials[material] = min(metal_max, materials[material] + cost[material]) + for(var/mat in cost) + materials[mat] = min(metal_max, materials[mat] + cost[mat]) interact(usr) /obj/item/integrated_circuit_printer/proc/subtract_material_costs(var/list/cost, var/mob/user) - for(var/material in cost) - if(materials[material] < cost[material]) - var/decl/material/material_datum = GET_DECL(material) - to_chat(user, "You need [cost[material]] [material_datum.name] to build that!") + for(var/mat in cost) + if(materials[mat] < cost[mat]) + var/decl/material/print_material = GET_DECL(mat) + to_chat(user, "You need [cost[mat]] [print_material.name] to build that!") return FALSE - for(var/material in cost) //Iterate twice to make sure it's going to work before deducting - materials[material] -= cost[material] + for(var/mat in cost) //Iterate twice to make sure it's going to work before deducting + materials[mat] -= cost[mat] return TRUE // FUKKEN UPGRADE DISKS diff --git a/mods/content/xenobiology/slime/items.dm b/mods/content/xenobiology/slime/items.dm index 769f8c631d61..19c61ced8d9b 100644 --- a/mods/content/xenobiology/slime/items.dm +++ b/mods/content/xenobiology/slime/items.dm @@ -32,8 +32,8 @@ return TRUE . = ..() -/obj/item/slime_extract/Initialize(var/ml, var/material, var/_stype = /decl/slime_colour/grey) - . = ..(ml, material) +/obj/item/slime_extract/Initialize(var/ml, var/mat, var/_stype = /decl/slime_colour/grey) + . = ..(ml, mat) slime_type = _stype if(!ispath(slime_type, /decl/slime_colour)) PRINT_STACK_TRACE("Slime extract initialized with non-decl slime colour: [slime_type || "NULL"].") diff --git a/mods/gamemodes/cult/materials.dm b/mods/gamemodes/cult/materials.dm index e014bd441ec4..a606e344ad5c 100644 --- a/mods/gamemodes/cult/materials.dm +++ b/mods/gamemodes/cult/materials.dm @@ -12,7 +12,7 @@ exoplanet_rarity_plant = MAT_RARITY_NOWHERE exoplanet_rarity_gas = MAT_RARITY_NOWHERE -/decl/material/solid/stone/cult/place_dismantled_girder(var/turf/target) +/decl/material/solid/stone/cult/place_dismantled_girder(var/turf/target, var/decl/material/r_mat) return list(new /obj/structure/girder/cult(target)) /decl/material/solid/stone/cult/reinforced From e9319b63a574e5f6a6d83ce008b85943ca91bcce Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 23:26:46 +1100 Subject: [PATCH 0889/1331] Replacing floor_material with material. --- code/game/atom_material.dm | 2 +- code/game/machinery/doors/airlock.dm | 2 +- code/game/machinery/doors/blast_door.dm | 2 +- code/game/objects/items/_item_materials.dm | 4 --- .../structures/_structure_materials.dm | 4 --- code/game/turfs/floors/_floor.dm | 4 +-- code/game/turfs/floors/floor_materials.dm | 17 +++++------- .../turfs/floors/subtypes/floor_concrete.dm | 2 +- code/game/turfs/floors/subtypes/floor_path.dm | 26 +++++++++---------- code/game/turfs/floors/subtypes/floor_rock.dm | 10 +++---- code/game/turfs/walls/_wall.dm | 4 --- .../random_exoplanet/planet_types/meat.dm | 2 +- 12 files changed, 32 insertions(+), 47 deletions(-) diff --git a/code/game/atom_material.dm b/code/game/atom_material.dm index 7313f51691fa..91bfa983ba44 100644 --- a/code/game/atom_material.dm +++ b/code/game/atom_material.dm @@ -2,7 +2,7 @@ //Will we ever need to return more than one value here? Or should we just return the "dominant" material. /atom/proc/get_material() RETURN_TYPE(/decl/material) - return + return material //mostly for convenience /atom/proc/get_material_type() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 8c44f0f99c0b..930c9329ae6e 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -42,7 +42,7 @@ var/aiDisabledIdScanner = 0 var/aiHacking = 0 autoclose = 1 - var/mineral = null + var/mineral = null // TODO: replace with material and get_material() var/justzap = 0 var/safe = 1 var/speaker = 1 diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index ca111e877bcf..0b1c0084639b 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -36,7 +36,7 @@ //turning this off prevents awkward zone geometry in places like medbay lobby, for example. block_air_zones = 0 - var/decl/material/implicit_material + var/decl/material/implicit_material // TODO: replace with material and get_material() autoset_access = FALSE // Uses different system with buttons. pry_mod = 1.35 diff --git a/code/game/objects/items/_item_materials.dm b/code/game/objects/items/_item_materials.dm index 893b901af7c8..2a269fac6c9b 100644 --- a/code/game/objects/items/_item_materials.dm +++ b/code/game/objects/items/_item_materials.dm @@ -53,10 +53,6 @@ material.place_shards(T) qdel(src) -/obj/item/get_material() - RETURN_TYPE(/decl/material) - return material - // TODO: Refactor more code to use this where necessary, and then make this use // some sort of generalized system for hitting with different parts of an item // e.g. pommel vs blade, rifle butt vs bayonet, knife hilt vs blade diff --git a/code/game/objects/structures/_structure_materials.dm b/code/game/objects/structures/_structure_materials.dm index a6c98ee0a1bc..ccb8ddf43076 100644 --- a/code/game/objects/structures/_structure_materials.dm +++ b/code/game/objects/structures/_structure_materials.dm @@ -5,10 +5,6 @@ /// The base alpha used to calculate material-based alpha in update_material_color(). var/base_alpha = 50 -/obj/structure/get_material() - RETURN_TYPE(/decl/material) - return material - /obj/structure/proc/get_material_health_modifier() . = 1 diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 22123008d757..bed01a51616d 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -32,7 +32,7 @@ . = ..(ml) - set_turf_materials(floor_material, skip_update = TRUE) + set_turf_materials(material, skip_update = TRUE) if(!floortype && (ispath(_flooring) || islist(_flooring))) floortype = _flooring @@ -44,7 +44,7 @@ fill_to_zero_height() // try to refill turfs that act as fluid sources - if(floor_material || get_topmost_flooring()) + if(material || get_topmost_flooring()) update_from_flooring(skip_update = ml) if(ml) // We skipped the update above to avoid updating our neighbors, but we need to update ourselves. lazy_update_icon() diff --git a/code/game/turfs/floors/floor_materials.dm b/code/game/turfs/floors/floor_materials.dm index 17dbe09f0a91..213deaca53b1 100644 --- a/code/game/turfs/floors/floor_materials.dm +++ b/code/game/turfs/floors/floor_materials.dm @@ -1,17 +1,14 @@ -/turf/floor - VAR_PROTECTED/decl/material/floor_material - /turf/floor/set_turf_materials(decl/material/new_material, decl/material/new_reinf_material, force, decl/material/new_girder_material, skip_update) if(ispath(new_material)) new_material = GET_DECL(new_material) - if(floor_material != new_material || force) - floor_material = new_material - if(!istype(floor_material)) - if(floor_material) - PRINT_STACK_TRACE("Floor turf has been supplied non-material '[istype(floor_material, /datum) ? floor_material.type : (floor_material || "NULL")]'.") - floor_material = get_default_material() + if(material != new_material || force) + material = new_material + if(!istype(material)) + if(material) + PRINT_STACK_TRACE("Floor turf has been supplied non-material '[istype(material, /datum) ? material.type : (material || "NULL")]'.") + material = get_default_material() . = TRUE if(. && !skip_update) @@ -21,4 +18,4 @@ var/decl/flooring/flooring = get_topmost_flooring() if(istype(flooring) && istype(flooring.force_material)) return flooring.force_material - return floor_material + return material diff --git a/code/game/turfs/floors/subtypes/floor_concrete.dm b/code/game/turfs/floors/subtypes/floor_concrete.dm index 7e8056a04c70..60303ef1ee79 100644 --- a/code/game/turfs/floors/subtypes/floor_concrete.dm +++ b/code/game/turfs/floors/subtypes/floor_concrete.dm @@ -4,7 +4,7 @@ icon_state = "inset" _flooring = /decl/flooring/concrete _base_flooring = /decl/flooring/dirt - floor_material = /decl/material/solid/stone/concrete + material = /decl/material/solid/stone/concrete /turf/floor/concrete/smooth icon_state = "concrete" diff --git a/code/game/turfs/floors/subtypes/floor_path.dm b/code/game/turfs/floors/subtypes/floor_path.dm index 7d714f8efab9..b67e06bf1b70 100644 --- a/code/game/turfs/floors/subtypes/floor_path.dm +++ b/code/game/turfs/floors/subtypes/floor_path.dm @@ -1,18 +1,18 @@ /turf/floor/path - name = "path" - gender = NEUTER - desc = "A cobbled path made of loose stones." - color = COLOR_GRAY - icon = 'icons/turf/flooring/path.dmi' - icon_state = "cobble0" - _flooring = /decl/flooring/path/cobblestone - floor_material = /decl/material/solid/stone/sandstone - _base_flooring = /decl/flooring/dirt + name = "path" + gender = NEUTER + desc = "A cobbled path made of loose stones." + color = COLOR_GRAY + icon = 'icons/turf/flooring/path.dmi' + icon_state = "cobble0" + _flooring = /decl/flooring/path/cobblestone + material = /decl/material/solid/stone/sandstone + _base_flooring = /decl/flooring/dirt /turf/floor/path/Initialize(mapload, no_update_icon) . = ..() - set_turf_materials(floor_material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) + set_turf_materials(material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) if(mapload && is_outside() && prob(20)) var/image/moss = image('icons/effects/decals/plant_remains.dmi', "leafy_bits", DECAL_LAYER) moss.pixel_x = rand(-6, 6) @@ -39,15 +39,15 @@ #define PATH_MATERIAL_SUBTYPES(material_name) \ /turf/floor/path/##material_name { \ color = /decl/material/solid/stone/##material_name::color; \ - floor_material = /decl/material/solid/stone/##material_name; \ + material = /decl/material/solid/stone/##material_name; \ } \ /turf/floor/path/herringbone/##material_name { \ color = /decl/material/solid/stone/##material_name::color; \ - floor_material = /decl/material/solid/stone/##material_name; \ + material = /decl/material/solid/stone/##material_name; \ } \ /turf/floor/path/running_bond/##material_name { \ color = /decl/material/solid/stone/##material_name::color; \ - floor_material = /decl/material/solid/stone/##material_name; \ + material = /decl/material/solid/stone/##material_name; \ } \ /turf/floor/path/##material_name/water { \ color = COLOR_SKY_BLUE; \ diff --git a/code/game/turfs/floors/subtypes/floor_rock.dm b/code/game/turfs/floors/subtypes/floor_rock.dm index de364ec1cc90..a0e1194fda70 100644 --- a/code/game/turfs/floors/subtypes/floor_rock.dm +++ b/code/game/turfs/floors/subtypes/floor_rock.dm @@ -6,12 +6,12 @@ /turf/floor/rock/Initialize(mapload, no_update_icon) . = ..() - set_turf_materials(floor_material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) + set_turf_materials(material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) /turf/floor/rock/volcanic - name = "volcanic floor" - floor_material = /decl/material/solid/stone/basalt + name = "volcanic floor" + material = /decl/material/solid/stone/basalt /turf/floor/rock/basalt - color = /decl/material/solid/stone/basalt::color - floor_material = /decl/material/solid/stone/basalt + color = /decl/material/solid/stone/basalt::color + material = /decl/material/solid/stone/basalt diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 119683c5413c..1d26dbb74a65 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -121,10 +121,6 @@ var/global/list/wall_fullblend_objects = list( if(!radiate()) return PROCESS_KILL -/turf/wall/get_material() - RETURN_TYPE(/decl/material) - return material - /turf/wall/bullet_act(var/obj/item/projectile/Proj) if(istype(Proj,/obj/item/projectile/beam)) burn(2500) diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm index 95ad399b57d1..533174a5fa11 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm @@ -132,7 +132,7 @@ icon = 'icons/turf/flooring/flesh.dmi' icon_state = "meat" _base_flooring = /decl/flooring/meat - floor_material = /decl/material/solid/organic/meat + material = /decl/material/solid/organic/meat /turf/floor/meat/acid name = "juices" From fc3dc4571f58bd7aae7bb195e16aeaa6c5e4e9e2 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 23:28:33 +1100 Subject: [PATCH 0890/1331] Replaces implicit_material with material. --- code/game/machinery/doors/blast_door.dm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index 0b1c0084639b..830d9f241672 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -36,7 +36,6 @@ //turning this off prevents awkward zone geometry in places like medbay lobby, for example. block_air_zones = 0 - var/decl/material/implicit_material // TODO: replace with material and get_material() autoset_access = FALSE // Uses different system with buttons. pry_mod = 1.35 @@ -54,7 +53,7 @@ base_type = /obj/machinery/door/blast /obj/machinery/door/blast/Initialize() - implicit_material = GET_DECL(/decl/material/solid/metal/plasteel) + material = GET_DECL(/decl/material/solid/metal/plasteel) . = ..() /obj/machinery/door/blast/get_examine_strings(mob/user, distance, infix, suffix) @@ -137,10 +136,6 @@ else force_close() -/obj/machinery/door/blast/get_material() - RETURN_TYPE(/decl/material) - return implicit_material - // Proc: attackby() // Parameters: 2 (used_item - Item this object was clicked with, user - Mob which clicked this object) // Description: If we are clicked with crowbar or wielded fire axe, try to manually open the door. From a8cee8a889ac66d0140161edca85d2bf5c0e5562 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 23:35:29 +1100 Subject: [PATCH 0891/1331] Replaces airlock mineral with material, replaces glass_material with reinf_material. --- code/game/machinery/doors/airlock.dm | 83 ++++++------- code/game/machinery/doors/airlock_subtypes.dm | 18 +-- code/game/machinery/doors/double.dm | 6 - code/game/objects/structures/door_assembly.dm | 113 ++++++++++-------- 4 files changed, 101 insertions(+), 119 deletions(-) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 930c9329ae6e..b19b2f20f189 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -15,14 +15,12 @@ icon_state = "preview" power_channel = ENVIRON interact_offline = FALSE - explosion_resistance = 10 - base_type = /obj/machinery/door/airlock frame_type = /obj/structure/door_assembly - icon_state_open = "open" icon_state_closed = "closed" + material = /decl/material/solid/metal/steel var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. var/hackProof = 0 // if 1, this door can't be hacked by the AI @@ -42,7 +40,7 @@ var/aiDisabledIdScanner = 0 var/aiHacking = 0 autoclose = 1 - var/mineral = null // TODO: replace with material and get_material() + var/justzap = 0 var/safe = 1 var/speaker = 1 @@ -75,29 +73,21 @@ var/stripe_color = null var/symbol_color = null var/window_color = null - var/window_material = /decl/material/solid/glass - - var/fill_file = 'icons/obj/doors/station/fill_steel.dmi' - var/color_file = 'icons/obj/doors/station/color.dmi' - var/color_fill_file = 'icons/obj/doors/station/fill_color.dmi' - var/stripe_file = 'icons/obj/doors/station/stripe.dmi' - var/stripe_fill_file = 'icons/obj/doors/station/fill_stripe.dmi' - var/glass_file = 'icons/obj/doors/station/fill_glass.dmi' - var/bolts_file = 'icons/obj/doors/station/lights_bolts.dmi' - var/deny_file = 'icons/obj/doors/station/lights_deny.dmi' - var/lights_file = 'icons/obj/doors/station/lights_green.dmi' - var/panel_file = 'icons/obj/doors/station/panel.dmi' - var/sparks_damaged_file = 'icons/obj/doors/station/sparks_damaged.dmi' - var/sparks_broken_file = 'icons/obj/doors/station/sparks_broken.dmi' - var/welded_file = 'icons/obj/doors/station/welded.dmi' - var/emag_file = 'icons/obj/doors/station/emag.dmi' -/obj/machinery/door/airlock/get_material() - RETURN_TYPE(/decl/material) - return GET_DECL(mineral ? mineral : /decl/material/solid/metal/steel) - -/obj/machinery/door/airlock/proc/get_window_material() - return GET_DECL(window_material) + var/fill_file = 'icons/obj/doors/station/fill_steel.dmi' + var/color_file = 'icons/obj/doors/station/color.dmi' + var/color_fill_file = 'icons/obj/doors/station/fill_color.dmi' + var/stripe_file = 'icons/obj/doors/station/stripe.dmi' + var/stripe_fill_file = 'icons/obj/doors/station/fill_stripe.dmi' + var/glass_file = 'icons/obj/doors/station/fill_glass.dmi' + var/bolts_file = 'icons/obj/doors/station/lights_bolts.dmi' + var/deny_file = 'icons/obj/doors/station/lights_deny.dmi' + var/lights_file = 'icons/obj/doors/station/lights_green.dmi' + var/panel_file = 'icons/obj/doors/station/panel.dmi' + var/sparks_damaged_file = 'icons/obj/doors/station/sparks_damaged.dmi' + var/sparks_broken_file = 'icons/obj/doors/station/sparks_broken.dmi' + var/welded_file = 'icons/obj/doors/station/welded.dmi' + var/emag_file = 'icons/obj/doors/station/emag.dmi' /obj/machinery/door/airlock/Process() if(main_power_lost_until > 0 && world.time >= main_power_lost_until) @@ -324,15 +314,15 @@ About the new airlock wires panel: set_light(0) - if(door_color && !(door_color == "none")) + if(door_color) var/ikey = "[airlock_type]-[door_color]-color" color_overlay = airlock_icon_cache["[ikey]"] if(!color_overlay) color_overlay = new(color_file) color_overlay.Blend(door_color, ICON_MULTIPLY) airlock_icon_cache["[ikey]"] = color_overlay - if(glass) - if (window_color && window_color != "none") + if(reinf_material) + if (window_color) var/ikey = "[airlock_type]-[window_color]-windowcolor" filling_overlay = airlock_icon_cache["[ikey]"] if (!filling_overlay) @@ -342,7 +332,7 @@ About the new airlock wires panel: else filling_overlay = glass_file else - if(door_color && !(door_color == "none")) + if(door_color) var/ikey = "[airlock_type]-[door_color]-fillcolor" filling_overlay = airlock_icon_cache["[ikey]"] if(!filling_overlay) @@ -866,9 +856,8 @@ About the new airlock wires panel: var/obj/structure/door_assembly/da = ..() // Note that we're deleted here already. Don't do unsafe stuff. . = da - if(mineral) - da.glass_material = mineral - da.glass = 1 + if(da.can_install_glass) + da.reinf_material = reinf_material da.paintable = paintable da.door_color = door_color @@ -1026,6 +1015,10 @@ About the new airlock wires panel: return ..(M) /obj/machinery/door/airlock/Initialize(var/mapload, var/d, var/populate_parts = TRUE, obj/structure/door_assembly/assembly = null) + + material = RESOLVE_TO_DECL(material) + reinf_material = RESOLVE_TO_DECL(reinf_material) + . = ..() //wires @@ -1048,27 +1041,22 @@ About the new airlock wires panel: else if(!begins_closed) queue_icon_update() - if (glass) + if(reinf_material) paintable |= PAINT_WINDOW_PAINTABLE - if (!window_color) - var/decl/material/window = get_window_material() - window_color = window.color + paint_window(reinf_material.color) /obj/machinery/door/airlock/inherit_from_assembly(obj/structure/door_assembly/assembly) //if assembly is given, create the new door from the assembly if (..(assembly)) - var/decl/material/mat = GET_DECL(assembly.glass_material) - - if(assembly.glass == 1) // supposed to use material in this case - mineral = assembly.glass_material - if(mat.opacity <= 0.7) - glass = TRUE + if(assembly.reinf_material && assembly.can_install_glass) + material = assembly.reinf_material + if(assembly.reinf_material.opacity <= 0.7) set_opacity(0) hitsound = 'sound/effects/Glasshit.ogg' max_health = 300 explosion_resistance = 5 else - door_color = mat.color + door_color = assembly.reinf_material.color else door_color = assembly.door_color @@ -1076,7 +1064,7 @@ About the new airlock wires panel: if(assembly.created_name) SetName(assembly.created_name) else - SetName("[mineral ? "[mat.solid_name || mat.name] airlock" : assembly.base_name]") + SetName("[material ? "[material.solid_name || material.name] airlock" : assembly.base_name]") paintable = assembly.paintable stripe_color = assembly.stripe_color @@ -1149,9 +1137,8 @@ About the new airlock wires panel: /obj/machinery/door/airlock/proc/paint_window(new_color) if (new_color) window_color = new_color - else if (window_material) - var/decl/material/window = get_window_material() - window_color = window.color + else if (reinf_material) + window_color = reinf_material.color else window_color = GLASS_COLOR queue_icon_update() diff --git a/code/game/machinery/doors/airlock_subtypes.dm b/code/game/machinery/doors/airlock_subtypes.dm index b3474720a5f6..f2eda23a2f77 100644 --- a/code/game/machinery/doors/airlock_subtypes.dm +++ b/code/game/machinery/doors/airlock_subtypes.dm @@ -11,7 +11,6 @@ stripe_color = COLOR_NT_RED /obj/machinery/door/airlock/engineering - name = "Maintenance Hatch" door_color = COLOR_AMBER /obj/machinery/door/airlock/medical @@ -27,7 +26,6 @@ stripe_color = COLOR_GREEN /obj/machinery/door/airlock/mining - name = "Mining Airlock" door_color = COLOR_PALE_ORANGE stripe_color = COLOR_BEASTY_BROWN @@ -53,24 +51,20 @@ stripe_color = COLOR_GRAY20 /obj/machinery/door/airlock/freezer - name = "Freezer Airlock" door_color = COLOR_WHITE /obj/machinery/door/airlock/maintenance - name = "Maintenance Access" stripe_color = COLOR_AMBER // Glass airlock presets - /obj/machinery/door/airlock/glass - name = "Glass Airlock" icon_state = "preview_glass" hitsound = 'sound/effects/Glasshit.ogg' max_health = 300 explosion_resistance = 5 opacity = FALSE - glass = TRUE + reinf_material = /decl/material/solid/glass /obj/machinery/door/airlock/glass/command door_color = COLOR_COMMAND_BLUE @@ -120,7 +114,6 @@ door_color = COLOR_WHITE /obj/machinery/door/airlock/glass/maintenance - name = "Maintenance Access" stripe_color = COLOR_AMBER /obj/machinery/door/airlock/glass/civilian @@ -130,8 +123,7 @@ // External airlock presets /obj/machinery/door/airlock/external - airlock_type = "External" - name = "External Airlock" + airlock_type = "external" icon = 'icons/obj/doors/external/door.dmi' fill_file = 'icons/obj/doors/external/fill_steel.dmi' color_file = 'icons/obj/doors/external/color.dmi' @@ -160,7 +152,6 @@ LAZYADD(., access_external_airlocks) /obj/machinery/door/airlock/external/escapepod - name = "Escape Pod" locked = TRUE /obj/machinery/door/airlock/external/escapepod/attackby(obj/item/used_item, mob/user) @@ -210,14 +201,12 @@ /obj/machinery/door/airlock/centcom airlock_type = "centcomm" - name = "\improper Airlock" icon = 'icons/obj/doors/centcomm/door.dmi' fill_file = 'icons/obj/doors/centcomm/fill_steel.dmi' paintable = PAINT_PAINTABLE|PAINT_STRIPABLE /obj/machinery/door/airlock/highsecurity airlock_type = "secure" - name = "Secure Airlock" icon = 'icons/obj/doors/secure/door.dmi' fill_file = 'icons/obj/doors/secure/fill_steel.dmi' explosion_resistance = 20 @@ -232,7 +221,6 @@ /obj/machinery/door/airlock/hatch airlock_type = "hatch" - name = "\improper Airtight Hatch" icon = 'icons/obj/doors/hatch/door.dmi' fill_file = 'icons/obj/doors/hatch/fill_steel.dmi' stripe_file = 'icons/obj/doors/hatch/stripe.dmi' @@ -249,7 +237,6 @@ paintable = PAINT_STRIPABLE /obj/machinery/door/airlock/hatch/maintenance - name = "Maintenance Hatch" stripe_color = COLOR_AMBER /obj/machinery/door/airlock/hatch/maintenance/bolted @@ -257,7 +244,6 @@ /obj/machinery/door/airlock/vault airlock_type = "vault" - name = "Vault" icon = 'icons/obj/doors/vault/door.dmi' fill_file = 'icons/obj/doors/vault/fill_steel.dmi' explosion_resistance = 20 diff --git a/code/game/machinery/doors/double.dm b/code/game/machinery/doors/double.dm index 4bfc77f73631..134f2a3ef11d 100644 --- a/code/game/machinery/doors/double.dm +++ b/code/game/machinery/doors/double.dm @@ -84,7 +84,6 @@ door_color = COLOR_NT_RED /obj/machinery/door/airlock/double/engineering - name = "Maintenance Hatch" door_color = COLOR_AMBER /obj/machinery/door/airlock/double/medical @@ -96,7 +95,6 @@ stripe_color = COLOR_GREEN /obj/machinery/door/airlock/double/mining - name = "Mining Airlock" door_color = COLOR_PALE_ORANGE stripe_color = COLOR_BEASTY_BROWN @@ -116,18 +114,15 @@ door_color = COLOR_BLUE_GRAY /obj/machinery/door/airlock/double/maintenance - name = "Maintenance Access" stripe_color = COLOR_AMBER /obj/machinery/door/airlock/double/civilian stripe_color = COLOR_CIVIE_GREEN /obj/machinery/door/airlock/double/freezer - name = "Freezer Airlock" door_color = COLOR_WHITE /obj/machinery/door/airlock/double/glass - name = "Glass Airlock" opacity = FALSE glass = TRUE @@ -175,7 +170,6 @@ door_color = COLOR_WHITE /obj/machinery/door/airlock/double/glass/maintenance - name = "Maintenance Access" stripe_color = COLOR_AMBER /obj/machinery/door/airlock/double/glass/civilian diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index cf7af2c01748..04cd6b7e88f7 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -5,22 +5,43 @@ anchored = FALSE density = TRUE obj_flags = OBJ_FLAG_MOVES_UNSUPPORTED + + material = /decl/material/solid/metal/steel + material_alteration = MAT_FLAG_ALTERATION_NAME + + var/can_install_glass = TRUE var/state = 0 - var/base_name = "Airlock" + var/base_name = "airlock assembly" var/obj/item/stock_parts/circuitboard/airlock_electronics/electronics = null var/airlock_type = /obj/machinery/door/airlock //the type path of the airlock once completed - var/glass = 0 // 0 = glass can be installed. -1 = glass can't be installed. 1 = glass is already installed. - var/glass_material = /decl/material/solid/glass // if this door was to built right now and be made of glass, what material should the glass be? var/created_name = null var/panel_icon = 'icons/obj/doors/station/panel.dmi' var/fill_icon = 'icons/obj/doors/station/fill_steel.dmi' var/glass_icon = 'icons/obj/doors/station/fill_glass.dmi' var/paintable = PAINT_PAINTABLE|PAINT_STRIPABLE - var/door_color = "none" - var/stripe_color = "none" - var/symbol_color = "none" + var/door_color + var/stripe_color + var/symbol_color var/width = 1 // For multi-tile doors +/obj/structure/door_assembly/update_material_name(override_name) + var/modifier + switch (state) + if(0) + if(anchored) + modifier = "secured " + if(1) + modifier = "wired " + if(2) + modifier = "near-finished " + if(reinf_material) + SetName("[modifier][reinf_material.solid_name] window [base_name]") + else + SetName("[modifier][base_name]") + +/obj/structure/door_assembly/window + reinf_material = /decl/material/solid/glass + /obj/structure/door_assembly/Initialize(mapload, _mat, _reinf_mat, _dir) . = ..(mapload, _mat, _reinf_mat) set_dir(_dir) @@ -52,10 +73,9 @@ if(0) LAZYADD(., "Use a wrench to [anchored ? "un" : ""]anchor it.") if(!anchored) - if(glass == 1) - var/decl/material/glass_material_datum = GET_DECL(glass_material) - if(glass_material_datum) - var/mat_name = glass_material_datum.solid_name || glass_material_datum.name + if(can_install_glass) + if(reinf_material) + var/mat_name = reinf_material.solid_name || reinf_material.name LAZYADD(., "Use a welder to remove the [mat_name] plating currently attached.") else LAZYADD(., "Use a welder to disassemble completely.") @@ -72,23 +92,23 @@ icon = 'icons/obj/doors/hatch/door.dmi' panel_icon = 'icons/obj/doors/hatch/panel.dmi' fill_icon = 'icons/obj/doors/hatch/fill_steel.dmi' - base_name = "Airtight Hatch" + base_name = "airtight hatch" airlock_type = /obj/machinery/door/airlock/hatch - glass = -1 + can_install_glass = FALSE /obj/structure/door_assembly/door_assembly_highsecurity // Borrowing this until WJohnston makes sprites for the assembly icon = 'icons/obj/doors/secure/door.dmi' fill_icon = 'icons/obj/doors/secure/fill_steel.dmi' - base_name = "High Security Airlock" + base_name = "high security airlock" airlock_type = /obj/machinery/door/airlock/highsecurity - glass = -1 + can_install_glass = FALSE paintable = 0 /obj/structure/door_assembly/door_assembly_ext icon = 'icons/obj/doors/external/door.dmi' fill_icon = 'icons/obj/doors/external/fill_steel.dmi' glass_icon = 'icons/obj/doors/external/fill_glass.dmi' - base_name = "External Airlock" + base_name = "external airlock" airlock_type = /obj/machinery/door/airlock/external paintable = 0 @@ -105,7 +125,7 @@ icon = 'icons/obj/doors/rapid_pdoor.dmi' icon_state = "pdoor1" airlock_type = /obj/machinery/door/blast/regular - glass = -1 + can_install_glass = FALSE paintable = 0 /obj/structure/door_assembly/blast/on_update_icon() @@ -135,23 +155,21 @@ created_name = t return TRUE - if(IS_WELDER(used_item) && (glass == 1 || !anchored)) + if(IS_WELDER(used_item) && (can_install_glass || !anchored)) var/obj/item/weldingtool/welder = used_item if (welder.weld(0, user)) playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) - if(glass == 1) - var/decl/material/glass_material_datum = GET_DECL(glass_material) - if(glass_material_datum) - var/mat_name = glass_material_datum.solid_name || glass_material_datum.name - user.visible_message("[user] welds the [mat_name] plating off the airlock assembly.", "You start to weld the [mat_name] plating off the airlock assembly.") - if(do_after(user, 4 SECONDS, src)) - if(!welder.isOn()) - return TRUE - to_chat(user, "You welded the [mat_name] plating off!") - glass_material_datum.create_object(get_turf(src), 2) - glass = 0 - update_icon() - return TRUE + if(reinf_material) + var/mat_name = reinf_material.solid_name + user.visible_message("[user] welds the [mat_name] plating off the airlock assembly.", "You start to weld the [mat_name] plating off the airlock assembly.") + if(do_after(user, 4 SECONDS, src)) + if(!welder.isOn()) + return TRUE + to_chat(user, "You welded the [mat_name] plating off!") + reinf_material.create_object(get_turf(src), 2) + reinf_material = null + update_icon() + return TRUE if(!anchored) user.visible_message("[user] dissassembles the airlock assembly.", "You start to dissassemble the airlock assembly.") if(do_after(user, 4 SECONDS, src)) @@ -243,17 +261,16 @@ update_icon() return TRUE - else if(istype(used_item, /obj/item/stack/material) && !glass) + else if(istype(used_item, /obj/item/stack/material) && can_install_glass && !reinf_material) var/obj/item/stack/material/S = used_item - var/material_name = S.get_material_type() + var/decl/material/sheet_material = S.get_material() if (S.get_amount() >= 2) playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") - if(do_after(user, 4 SECONDS, src) && !glass) + if(do_after(user, 4 SECONDS, src) && can_install_glass && !reinf_material) if (S.use(2)) - to_chat(user, "You installed reinforced glass windows into the airlock assembly.") - glass = 1 - glass_material = material_name + reinf_material = sheet_material + to_chat(user, "You installed [reinf_material.solid_name] windows into the airlock assembly.") update_icon() return TRUE return FALSE @@ -274,24 +291,22 @@ /obj/structure/door_assembly/on_update_icon() ..() + var/image/filling_overlay - var/image/panel_overlay - var/final_name = "" - if(glass == 1) + if(reinf_material) filling_overlay = image(glass_icon, "construction") + filling_overlay.color = reinf_material.color + filling_overlay.appearance_flags |= RESET_COLOR else filling_overlay = image(fill_icon, "construction") - switch (state) - if(0) - if (anchored) - final_name = "Secured " + if(filling_overlay) + add_overlay(filling_overlay) + + var/image/panel_overlay + switch(state) if(1) - final_name = "Wired " panel_overlay = image(panel_icon, "construction0") if(2) - final_name = "Near Finished " panel_overlay = image(panel_icon, "construction1") - final_name += "[glass == 1 ? "Window " : ""][istext(glass) ? "[glass] Airlock" : base_name] Assembly" - SetName(final_name) - add_overlay(filling_overlay) - add_overlay(panel_overlay) + if(panel_overlay) + add_overlay(panel_overlay) From e44fedf43f1b420e2ebbc887e2290dc935a28777 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sat, 25 Oct 2025 00:00:43 +1100 Subject: [PATCH 0892/1331] Reworking how chems are tracked on /datum/seed. --- code/__defines/hydroponics.dm | 6 + .../codex/categories/category_substances.dm | 6 +- .../genetics/plants/gene_biochemistry.dm | 15 +- code/modules/hydroponics/grown.dm | 56 ++-- .../hydroponics/plant_types/seeds_herbs.dm | 38 +-- .../hydroponics/plant_types/seeds_misc.dm | 283 ++++++++++-------- code/modules/hydroponics/seed.dm | 45 +-- code/modules/hydroponics/seed_composition.dm | 21 ++ .../hydroponics/spreading/spreading.dm | 2 +- .../hydroponics/spreading/spreading_growth.dm | 4 +- .../random_exoplanet/flora_generator.dm | 10 +- .../random_exoplanet/planet_types/meat.dm | 6 +- .../components/input.dm | 2 +- mods/species/drakes/sifpod.dm | 6 +- nebula.dme | 1 + 15 files changed, 279 insertions(+), 222 deletions(-) create mode 100644 code/modules/hydroponics/seed_composition.dm diff --git a/code/__defines/hydroponics.dm b/code/__defines/hydroponics.dm index e40a83850d8b..9d0163f585e6 100644 --- a/code/__defines/hydroponics.dm +++ b/code/__defines/hydroponics.dm @@ -62,3 +62,9 @@ #define GROWTH_VINES "vines" #define GROWTH_BIOMASS "mass" #define GROWTH_MOLD "mold" + +#define PLANT_SEG_BODY "body" // Non-dissectable plants + +#define PLANT_STATE_FRESH "fresh" +#define PLANT_STATE_ROASTED "roasted" +#define PLANT_STATE_DRIED "dried" diff --git a/code/modules/codex/categories/category_substances.dm b/code/modules/codex/categories/category_substances.dm index 9d2c5ab8c95e..1c7763343dbf 100644 --- a/code/modules/codex/categories/category_substances.dm +++ b/code/modules/codex/categories/category_substances.dm @@ -64,13 +64,13 @@ else if(mat.solvent_power <= MAT_SOLVENT_STRONG) material_info += "
    • It is a strong solvent and will burn exposed skin on contact.
    • " if(mat.dissolves_in != MAT_SOLVENT_IMMUNE && LAZYLEN(mat.dissolves_into)) - var/chems = list() + var/list/chemicals = list() for(var/chemical in mat.dissolves_into) var/decl/material/chemical_decl = GET_DECL(chemical) var/material_link = "[chemical_decl.name]" if(chemical_decl.hidden_from_codex) material_link = chemical_decl.name - chems += "[material_link] ([mat.dissolves_into[chemical]*100]%)" + chemicals += "[material_link] ([mat.dissolves_into[chemical]*100]%)" var/solvent_needed if(mat.dissolves_in <= MAT_SOLVENT_NONE) solvent_needed = "any liquid" @@ -80,7 +80,7 @@ solvent_needed = "a moderately strong solvent, like acetone" else if(mat.dissolves_in <= MAT_SOLVENT_STRONG) solvent_needed = "a strong solvent, like sulfuric acid" - material_info += "
    • It can be dissolved with [solvent_needed] solvent, producing [english_list(chems)].
    • " + material_info += "
    • It can be dissolved with [solvent_needed] solvent, producing [english_list(chemicals)].
    • " if(mat.radioactivity) material_info += "
    • It is radioactive.
    • " if(mat.flags & MAT_FLAG_FUSION_FUEL) diff --git a/code/modules/genetics/plants/gene_biochemistry.dm b/code/modules/genetics/plants/gene_biochemistry.dm index 296eebe4fbfe..74276d797a6f 100644 --- a/code/modules/genetics/plants/gene_biochemistry.dm +++ b/code/modules/genetics/plants/gene_biochemistry.dm @@ -19,22 +19,23 @@ seed.produces_pollen = LAZYACCESS(gene.values, TRAIT_POLLEN) - LAZYINITLIST(seed.chems) var/list/gene_value = LAZYACCESS(gene.values, TRAIT_CHEMS) for(var/rid in gene_value) var/list/gene_chem = gene_value[rid] - if(!seed.chems[rid]) - seed.chems[rid] = gene_chem.Copy() + if(!seed.get_chemical_amount(rid)) + seed.set_chemical_amount(rid, gene_chem.Copy()) continue for(var/i = 1 to length(gene_chem)) if(isnull(gene_chem[i])) gene_chem[i] = 0 - if(seed.chems[rid][i]) - seed.chems[rid][i] = max(1, round((gene_chem[i] + seed.chems[rid][i])/2)) + var/list/seed_chems = seed.get_chemical_amount(rid) + if(LAZYACCESS(seed_chems, i)) + seed_chems[i] = max(1, round((gene_chem[i] + seed_chems[i])/2)) else - seed.chems[rid][i] = gene_chem[i] + seed_chems[i] = gene_chem[i] + seed.set_chemical_amount(rid, seed_chems) var/list/new_gasses = LAZYACCESS(gene.values, TRAIT_EXUDE_GASSES) if(islist(new_gasses) && length(new_gasses)) @@ -49,7 +50,7 @@ return ..() /decl/plant_gene/biochemistry/copy_initial_seed_values(datum/plantgene/gene, datum/seed/seed) - LAZYSET(gene.values, TRAIT_CHEMS, seed.chems?.Copy()) + LAZYSET(gene.values, TRAIT_CHEMS, deepCopyList(seed.get_chemical_composition())) LAZYSET(gene.values, TRAIT_EXUDE_GASSES, seed.exude_gasses?.Copy()) LAZYSET(gene.values, TRAIT_POLLEN, seed.produces_pollen) return ..() diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 3f9b4b7f1503..99bd9f8962cc 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -11,6 +11,8 @@ drying_wetness = 45 dried_type = /obj/item/food/grown/dry allergen_flags = ALLERGEN_VEGETABLE + + var/plant_segment_type = PLANT_SEG_BODY // Used for growns produced via plant dissection. var/work_skill = SKILL_BOTANY var/seeds_extracted = FALSE var/datum/seed/seed @@ -35,27 +37,35 @@ else if(!seeds_extracted && seed.min_seed_extracted) . += SPAN_NOTICE("With a knife, you could extract at least [seed.min_seed_extracted] seed\s.") +/obj/item/food/grown/proc/update_base_name() + base_name = seed?.product_name || "grown" + /obj/item/food/grown/update_name() if(!seed) return ..() + update_base_name() var/descriptor = list() if(dry) descriptor += "dried" if(backyard_grilling_count > 0) descriptor += "roasted" if(length(descriptor)) - SetName("[english_list(descriptor)] [seed.product_name]") + SetName("[english_list(descriptor)] [base_name]") else - SetName("[seed.product_name]") - -/obj/item/food/grown/Initialize(mapload, material_key, skip_plate = FALSE, _seed) + SetName(base_name) +// Separated out of Initialize() for subtype overrides. +/obj/item/food/grown/proc/set_seed(_seed) if(isnull(seed) && _seed) seed = _seed - if(istext(seed)) seed = SSplants.seeds[seed] + if(!isnull(seed) && !istype(seed)) + seed = null + +/obj/item/food/grown/Initialize(mapload, material_key, skip_plate = FALSE, _seed) + set_seed(_seed) if(!istype(seed)) PRINT_STACK_TRACE("Grown initializing with null or invalid seed type '[seed || "NULL"]'") return INITIALIZE_HINT_QDEL @@ -65,7 +75,7 @@ slice_num = seed.slice_amount w_class = seed.product_w_class - if(!seed.chems && !(dry && seed.dried_chems) && !(backyard_grilling_count > 0 && seed.roasted_chems)) + if(!seed.get_chemical_composition() && !(dry && seed.get_chemical_composition(_state = PLANT_STATE_DRIED)) && !(backyard_grilling_count > 0 && seed.get_chemical_composition(_state = PLANT_STATE_ROASTED))) return INITIALIZE_HINT_QDEL // No reagent contents, no froot if(seed.scannable_result) @@ -88,7 +98,7 @@ /obj/item/food/grown/initialize_reagents(populate) if(reagents) reagents.clear_reagents() - if(!seed?.chems) + if(!length(seed?.get_chemical_composition(_segment = plant_segment_type))) return . = ..() //create_reagent and populate_reagents @@ -103,11 +113,13 @@ . = ..() // Fill the object up with the appropriate reagents. var/list/chems_to_fill + if(backyard_grilling_count > 0) - chems_to_fill ||= seed?.roasted_chems + chems_to_fill ||= seed?.get_chemical_composition(_segment = plant_segment_type, _state = PLANT_STATE_ROASTED) if(dry) - chems_to_fill ||= seed?.dried_chems - chems_to_fill ||= seed?.chems + chems_to_fill ||= seed?.get_chemical_composition(_segment = plant_segment_type, _state = PLANT_STATE_DRIED) + chems_to_fill ||= seed?.get_chemical_composition(_segment = plant_segment_type) + for(var/rid in chems_to_fill) var/list/reagent_amounts = chems_to_fill[rid] if(LAZYLEN(reagent_amounts)) @@ -180,9 +192,13 @@ . = ..() if(!seed) return - icon_state = "[seed.get_trait(TRAIT_PRODUCT_ICON)]-product" if(!dry && !backyard_grilling_count) color = seed.get_trait(TRAIT_PRODUCT_COLOUR) + update_grown_icon() + +// Separated for subtypes to override. +/obj/item/food/grown/proc/update_grown_icon() + icon_state = "[seed.get_trait(TRAIT_PRODUCT_ICON)]-product" if("[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf" in icon_states('icons/obj/hydroponics/hydroponics_products.dmi')) var/image/fruit_leaves = image('icons/obj/hydroponics/hydroponics_products.dmi',"[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf") if(!dry && !backyard_grilling_count) @@ -255,14 +271,16 @@ var/global/list/_wood_materials = list( seeds_extracted = TRUE return TRUE - if(IS_HATCHET(used_item) && seed.chems) - for(var/wood_mat in global._wood_materials) - if(!isnull(seed.chems[wood_mat])) - user.visible_message(SPAN_NOTICE("\The [user] makes planks out of \the [src].")) - for(var/obj/item/stack/material/stack in SSmaterials.create_object(wood_mat, user.loc, rand(1,2))) - stack.add_to_stacks(user, TRUE) - qdel(src) - return TRUE + if(IS_HATCHET(used_item)) + var/list/seed_chems = seed?.get_chemical_composition() + if(length(seed_chems)) + for(var/wood_mat in global._wood_materials) + if(!isnull(seed_chems[wood_mat])) + user.visible_message(SPAN_NOTICE("\The [user] makes planks out of \the [src].")) + for(var/obj/item/stack/material/stack in SSmaterials.create_object(wood_mat, user.loc, rand(1,2))) + stack.add_to_stacks(user, TRUE) + qdel(src) + return TRUE if(istype(used_item, /obj/item/paper)) diff --git a/code/modules/hydroponics/plant_types/seeds_herbs.dm b/code/modules/hydroponics/plant_types/seeds_herbs.dm index ec4273ebc4fc..246c81aeed4a 100644 --- a/code/modules/hydroponics/plant_types/seeds_herbs.dm +++ b/code/modules/hydroponics/plant_types/seeds_herbs.dm @@ -14,11 +14,6 @@ name = "yarrow" product_name = "yarrow flower" display_name = "yarrow patch" - chems = list( - /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/brute_meds/yarrow = list(1, 1) - ) - dried_chems = list(/decl/material/liquid/brute_meds/yarrow = list(10,10)) /datum/seed/herb/yarrow/New() ..() @@ -26,15 +21,14 @@ set_trait(TRAIT_PRODUCT_ICON,"flower4") set_trait(TRAIT_PLANT_COLOUR,"#6b8c5e") set_trait(TRAIT_PLANT_ICON,"flower4") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/brute_meds/yarrow, list(1, 1)) + set_chemical_amount(/decl/material/liquid/brute_meds/yarrow, list(10,10), _state = PLANT_STATE_DRIED) /datum/seed/herb/aloe name = "aloe" product_name = "aloe vera" display_name = "aloe patch" - chems = list( - /decl/material/liquid/nutriment = list(1,5), - /decl/material/liquid/burn_meds/aloe = list(10,10) - ) /datum/seed/herb/aloe/New() ..() @@ -42,16 +36,13 @@ set_trait(TRAIT_PRODUCT_ICON,"grass") set_trait(TRAIT_PLANT_COLOUR,"#2d7746") set_trait(TRAIT_PLANT_ICON,"ambrosia") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,5)) + set_chemical_amount(/decl/material/liquid/burn_meds/aloe, list(10,10)) /datum/seed/herb/ginseng name = "ginseng" product_name = "ginseng root" display_name = "ginseng patch" - chems = list( - /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/antitoxins/ginseng = list(1, 1) - ) - dried_chems = list(/decl/material/liquid/antitoxins/ginseng = list(10,10)) /datum/seed/herb/ginseng/New() ..() @@ -59,16 +50,14 @@ set_trait(TRAIT_PRODUCT_ICON,"pod") set_trait(TRAIT_PLANT_COLOUR,"#6b8c5e") set_trait(TRAIT_PLANT_ICON,"grass") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/antitoxins/ginseng, list(1, 1)) + set_chemical_amount(/decl/material/liquid/antitoxins/ginseng, list(10,10), _state = PLANT_STATE_DRIED) /datum/seed/herb/valerian name = "valerian" product_name = "valerian flower" display_name = "valerian patch" - chems = list( - /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/sedatives/valerian = list(1, 1) - ) - dried_chems = list(/decl/material/liquid/sedatives/valerian = list(10,10)) /datum/seed/herb/valerian/New() ..() @@ -76,16 +65,14 @@ set_trait(TRAIT_PRODUCT_ICON,"flower4") set_trait(TRAIT_PLANT_COLOUR,"#6b8c5e") set_trait(TRAIT_PLANT_ICON,"flower4") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/sedatives/valerian, list(1, 1)) + set_chemical_amount(/decl/material/liquid/sedatives/valerian, list(10,10), _state = PLANT_STATE_DRIED) /datum/seed/herb/foxglove name = "foxglove" product_name = "foxglove flower" display_name = "foxglove patch" - chems = list( - /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/stabilizer/foxglove = list(1, 1) - ) - dried_chems = list(/decl/material/liquid/stabilizer/foxglove = list(10,10)) /datum/seed/herb/foxglove/New() ..() @@ -93,3 +80,6 @@ set_trait(TRAIT_PRODUCT_ICON,"flowers") set_trait(TRAIT_PLANT_COLOUR,"#6b8c5e") set_trait(TRAIT_PLANT_ICON,"bush7") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/stabilizer/foxglove, list(1, 1)) + set_chemical_amount(/decl/material/liquid/stabilizer/foxglove, list(10,10), _state = PLANT_STATE_DRIED) diff --git a/code/modules/hydroponics/plant_types/seeds_misc.dm b/code/modules/hydroponics/plant_types/seeds_misc.dm index a36f8486b7f3..1ed4775af8fd 100644 --- a/code/modules/hydroponics/plant_types/seeds_misc.dm +++ b/code/modules/hydroponics/plant_types/seeds_misc.dm @@ -3,10 +3,6 @@ product_name = "cotton" display_name = "cotton patch" product_material = /decl/material/solid/organic/plantmatter/pith/husk - chems = list( - /decl/material/liquid/oil/plant = list(3,10), - /decl/material/solid/organic/cloth = list(10,1) - ) slice_product = null slice_amount = 0 @@ -21,27 +17,26 @@ set_trait(TRAIT_PRODUCT_COLOUR, "#ffffff") set_trait(TRAIT_PLANT_ICON,"bush2") set_trait(TRAIT_IDEAL_LIGHT, 6) + set_chemical_amount(/decl/material/liquid/oil/plant, list(3,10)) + set_chemical_amount(/decl/material/solid/organic/cloth, list(10,1)) /datum/seed/cotton/flax name = "flax" product_name = "flax" display_name = "flax patch" - // Do we want linseed oil at some point? - chems = list( - /decl/material/liquid/oil/plant = list(5,12), - /decl/material/solid/organic/cloth/linen = list(8,1) - ) /datum/seed/cotton/flax/New() ..() set_trait(TRAIT_PRODUCT_COLOUR, "#eee4c7") + // Do we want linseed oil at some point? + set_chemical_amount(/decl/material/liquid/oil/plant, list(5,12)) + set_chemical_amount(/decl/material/solid/organic/cloth/linen, list(8,1)) // Chili plants/variants. /datum/seed/chili name = "chili" product_name = "chili" display_name = "chili plants" - chems = list(/decl/material/liquid/capsaicin = list(3,5), /decl/material/liquid/nutriment = list(1,25)) mutants = list("icechili") grown_tag = "chili" allergen_flags = ALLERGEN_FRUIT @@ -58,13 +53,14 @@ set_trait(TRAIT_PLANT_ICON,"bush2") set_trait(TRAIT_IDEAL_HEAT, 298) set_trait(TRAIT_IDEAL_LIGHT, 7) + set_chemical_amount(/decl/material/liquid/capsaicin, list(3,5)) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,25)) /datum/seed/chili/ice name = "icechili" product_name = "chilly pepper" display_name = "chilly pepper plant" mutants = null - chems = list(/decl/material/liquid/frostoil = list(3,5), /decl/material/liquid/nutriment = list(1,50)) grown_tag = "icechili" /datum/seed/chili/ice/New() @@ -72,6 +68,8 @@ set_trait(TRAIT_MATURATION,4) set_trait(TRAIT_PRODUCTION,4) set_trait(TRAIT_PRODUCT_COLOUR,"#00edc6") + set_chemical_amount(/decl/material/liquid/frostoil, list(3,5)) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,50)) // Berry plants/variants. /datum/seed/berry @@ -79,7 +77,6 @@ product_name = "berries" display_name = "berry bush" mutants = list("glowberries","poisonberries","blueberries") - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/drink/juice/berry = list(10,10)) grown_tag = "berries" slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 @@ -98,13 +95,14 @@ set_trait(TRAIT_PLANT_ICON,"bush") set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/drink/juice/berry, list(10,10)) /datum/seed/berry/blue name = "blueberries" product_name = "blueberries" display_name = "blueberry bush" mutants = list("berries","poisonberries","glowberries") - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/drink/juice/berry = list(10,10)) grown_tag = "blueberries" /datum/seed/berry/blue/New() @@ -116,13 +114,14 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#1c225c") set_trait(TRAIT_WATER_CONSUMPTION, 5) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.2) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/drink/juice/berry, list(10,10)) /datum/seed/berry/glow name = "glowberries" product_name = "glowberries" display_name = "glowberry bush" mutants = null - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/solid/metal/uranium = list(3,5)) /datum/seed/berry/glow/New() ..() @@ -136,34 +135,29 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#c9fa16") set_trait(TRAIT_WATER_CONSUMPTION, 3) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/solid/metal/uranium, list(3,5)) /datum/seed/berry/poison name = "poisonberries" product_name = "poison berries" display_name = "poison berry bush" mutants = list("deathberries") - chems = list( - /decl/material/liquid/nutriment = list(1), - /decl/material/liquid/bromide = list(3,5), - /decl/material/liquid/poisonberryjuice = list(10,5) - ) /datum/seed/berry/poison/New() ..() set_trait(TRAIT_PRODUCT_COLOUR,"#6dc961") set_trait(TRAIT_WATER_CONSUMPTION, 3) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) + set_chemical_amount(/decl/material/liquid/bromide, list(3,5)) + set_chemical_amount(/decl/material/liquid/poisonberryjuice, list(10,5)) /datum/seed/berry/poison/death name = "deathberries" product_name = "death berries" display_name = "death berry bush" mutants = null - chems = list( - /decl/material/liquid/nutriment = list(1), - /decl/material/liquid/bromide = list(3,3), - /decl/material/gas/carbon_monoxide = list(1,5) - ) /datum/seed/berry/poison/death/New() ..() @@ -171,6 +165,9 @@ set_trait(TRAIT_POTENCY,50) set_trait(TRAIT_PRODUCT_COLOUR,"#7a5454") set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.35) + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) + set_chemical_amount(/decl/material/liquid/bromide, list(3,3)) + set_chemical_amount(/decl/material/gas/carbon_monoxide, list(1,5)) // Nettles/variants. /datum/seed/nettle @@ -178,11 +175,6 @@ product_name = "nettle" display_name = "nettle patch" mutants = list("deathnettle") - chems = list( - /decl/material/liquid/nutriment = list(1,50), - /decl/material/liquid/drink/juice/nettle = list(1,10), - /decl/material/liquid/nettle_histamine = list(1, 5) - ) grown_tag = "nettle" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -198,16 +190,15 @@ set_trait(TRAIT_PLANT_ICON,"bush5") set_trait(TRAIT_PRODUCT_ICON,"nettles") set_trait(TRAIT_PRODUCT_COLOUR,"#728a54") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,50)) + set_chemical_amount(/decl/material/liquid/drink/juice/nettle, list(1,10)) + set_chemical_amount(/decl/material/liquid/nettle_histamine, list(1, 5)) /datum/seed/nettle/death name = "deathnettle" product_name = "death nettle" display_name = "death nettle patch" mutants = null - chems = list( - /decl/material/liquid/nutriment = list(1,50), - /decl/material/liquid/acid/polyacid = list(0,1) - ) grown_tag = "deathnettle" /datum/seed/nettle/death/New() @@ -216,6 +207,8 @@ set_trait(TRAIT_YIELD,2) set_trait(TRAIT_PRODUCT_COLOUR,"#8c5030") set_trait(TRAIT_PLANT_COLOUR,"#634941") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,50)) + set_chemical_amount(/decl/material/liquid/acid/polyacid, list(0,1)) //Tomatoes/variants. /datum/seed/tomato @@ -223,7 +216,6 @@ product_name = "tomato" display_name = "tomato plant" mutants = list("bluetomato","bloodtomato") - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/drink/juice/tomato = list(10,10)) grown_tag = "tomato" allergen_flags = ALLERGEN_FRUIT | ALLERGEN_VEGETABLE // Which is it?? @@ -241,13 +233,14 @@ set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/drink/juice/tomato, list(10,10)) /datum/seed/tomato/blood name = "bloodtomato" product_name = "blood tomato" display_name = "blood tomato plant" mutants = list("killer") - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/blood = list(1,5)) splat_type = /obj/effect/decal/cleanable/blood/splatter allergen_flags = ALLERGEN_MEAT @@ -255,6 +248,8 @@ ..() set_trait(TRAIT_YIELD,3) set_trait(TRAIT_PRODUCT_COLOUR,"#ff0000") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/blood, list(1,5)) /datum/seed/tomato/killer name = "killertomato" @@ -274,22 +269,19 @@ product_name = "blue tomato" display_name = "blue tomato plant" mutants = list("quantumato") - chems = list(/decl/material/liquid/nutriment = list(1,20), /decl/material/liquid/lube = list(1,5)) /datum/seed/tomato/blue/New() ..() set_trait(TRAIT_PRODUCT_COLOUR,"#4d86e8") set_trait(TRAIT_PLANT_COLOUR,"#070aad") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/lube, list(1,5)) /datum/seed/tomato/blue/teleport name = "quantumato" product_name = "quantumato" display_name = "quantumato plant" mutants = null - chems = list( - /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/alcohol/bluecuracao = list(10,5) - ) /datum/seed/tomato/blue/teleport/New() ..() @@ -297,6 +289,8 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#00e5ff") set_trait(TRAIT_BIOLUM,1) set_trait(TRAIT_BIOLUM_COLOUR,"#4da4a8") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/alcohol/bluecuracao, list(10,5)) //Eggplants/varieties. /datum/seed/eggplant @@ -304,7 +298,6 @@ product_name = "eggplant" display_name = "eggplant vine" mutants = list("realeggplant") - chems = list(/decl/material/liquid/nutriment = list(1,10)) grown_tag = "eggplant" /datum/seed/eggplant/New() @@ -319,6 +312,7 @@ set_trait(TRAIT_PLANT_ICON,"bush4") set_trait(TRAIT_IDEAL_HEAT, 298) set_trait(TRAIT_IDEAL_LIGHT, 7) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) //Apples/varieties. /datum/seed/apple @@ -326,7 +320,6 @@ product_name = "apple" display_name = "apple tree" mutants = list("poisonapple","goldapple") - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/drink/juice/apple = list(10,10)) grown_tag = "apple" allergen_flags = ALLERGEN_FRUIT @@ -343,18 +336,22 @@ set_trait(TRAIT_FLESH_COLOUR,"#e8e39b") set_trait(TRAIT_IDEAL_LIGHT, 4) set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/drink/juice/apple, list(10,10)) /datum/seed/apple/poison name = "poisonapple" mutants = null - chems = list(/decl/material/liquid/cyanide = list(1,5)) + +/datum/seed/apple/poison/New() + ..() + set_chemical_amount(/decl/material/liquid/cyanide, list(1,5)) /datum/seed/apple/gold name = "goldapple" product_name = "golden apple" display_name = "gold apple tree" mutants = null - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/solid/metal/gold = list(1,5)) grown_tag = "goldapple" /datum/seed/apple/gold/New() @@ -364,6 +361,8 @@ set_trait(TRAIT_YIELD,3) set_trait(TRAIT_PRODUCT_COLOUR,"#ffdd00") set_trait(TRAIT_PLANT_COLOUR,"#d6b44d") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/solid/metal/gold, list(1,5)) //Ambrosia/varieties. /datum/seed/ambrosia @@ -371,13 +370,6 @@ product_name = "ambrosia vulgaris" display_name = "ambrosia vulgaris patch" mutants = list("ambrosiadeus", "hemp") - chems = list( - /decl/material/liquid/nutriment = list(1), - /decl/material/liquid/psychoactives = list(1,8), - /decl/material/liquid/burn_meds = list(1,8,1), - /decl/material/liquid/brute_meds = list(1,10,1), - /decl/material/liquid/bromide = list(1,10) - ) grown_tag = "ambrosiavulgaris" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -393,38 +385,42 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#9fad55") set_trait(TRAIT_PLANT_ICON,"ambrosia") set_trait(TRAIT_IDEAL_LIGHT, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) + set_chemical_amount(/decl/material/liquid/psychoactives, list(1,8)) + set_chemical_amount(/decl/material/liquid/burn_meds, list(1,8,1)) + set_chemical_amount(/decl/material/liquid/brute_meds, list(1,10,1)) + set_chemical_amount(/decl/material/liquid/bromide, list(1,10)) /datum/seed/ambrosia/hemp name = "hemp" product_name = "hemp" display_name = "hemp patch" mutants = null - chems = list( - /decl/material/liquid/oil/plant = list(3,10), - /decl/material/solid/organic/cloth/hemp = list(8,1), - /decl/material/liquid/nutriment = list(1) - ) grown_tag = null +/datum/seed/ambrosia/hemp/New() + ..() + set_chemical_amount(/decl/material/liquid/oil/plant, list(3,10)) + set_chemical_amount(/decl/material/solid/organic/cloth/hemp, list(8,1)) + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) + /datum/seed/ambrosia/deus name = "ambrosiadeus" product_name = "ambrosia deus" display_name = "ambrosia deus patch" mutants = null - chems = list( - /decl/material/liquid/nutriment = list(1), - /decl/material/liquid/brute_meds = list(1,8), - /decl/material/liquid/accumulated/antidepressants = list(1,8,1), - /decl/material/liquid/accumulated/stimulants = list(1,8,1), - /decl/material/liquid/amphetamines = list(1,10,1), - /decl/material/liquid/psychoactives = list(1,10) - ) grown_tag = "ambrosiadeus" /datum/seed/ambrosia/deus/New() ..() set_trait(TRAIT_PRODUCT_COLOUR,"#a3f0ad") set_trait(TRAIT_PLANT_COLOUR,"#2a9c61") + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) + set_chemical_amount(/decl/material/liquid/brute_meds, list(1,8)) + set_chemical_amount(/decl/material/liquid/accumulated/antidepressants, list(1,8,1)) + set_chemical_amount(/decl/material/liquid/accumulated/stimulants, list(1,8,1)) + set_chemical_amount(/decl/material/liquid/amphetamines, list(1,10,1)) + set_chemical_amount(/decl/material/liquid/psychoactives, list(1,10)) //Mushrooms/varieties. /datum/seed/mushroom @@ -433,7 +429,6 @@ seed_noun = SEED_NOUN_SPORES display_name = "chanterelle mushroom patch" mutants = list("reishi","amanita","plumphelmet") - chems = list(/decl/material/liquid/nutriment = list(1,25)) splat_type = /obj/effect/vine grown_tag = "mushroom" slice_product = /obj/item/food/processed_grown/chopped @@ -459,6 +454,7 @@ set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_IDEAL_HEAT, 288) set_trait(TRAIT_LIGHT_TOLERANCE, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,25)) /datum/seed/mushroom/mold name = "mold" @@ -481,7 +477,6 @@ product_name = "plump helmet" display_name = "plump helmet mushroom patch" mutants = list("walkingmushroom","towercap") - chems = list(/decl/material/liquid/nutriment = list(2,10)) grown_tag = "plumphelmet" /datum/seed/mushroom/plump/New() @@ -495,6 +490,7 @@ set_trait(TRAIT_PLANT_COLOUR,"#9e4f9d") set_trait(TRAIT_PLANT_ICON,"mushroom2") set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.35) + set_chemical_amount(/decl/material/liquid/nutriment, list(2,10)) /datum/seed/mushroom/plump/walking name = "walkingmushroom" @@ -516,7 +512,6 @@ product_name = "reishi" display_name = "reishi patch" mutants = list("libertycap","glowbell") - chems = list(/decl/material/liquid/nutriment = list(1,50), /decl/material/liquid/psychotropics = list(3,5)) /datum/seed/mushroom/hallucinogenic/New() ..() @@ -528,13 +523,14 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#ffb70f") set_trait(TRAIT_PLANT_COLOUR,"#f58a18") set_trait(TRAIT_PLANT_ICON,"mushroom6") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,50)) + set_chemical_amount(/decl/material/liquid/psychotropics, list(3,5)) /datum/seed/mushroom/hallucinogenic/strong name = "libertycap" product_name = "liberty cap" display_name = "liberty cap mushroom patch" mutants = null - chems = list(/decl/material/liquid/nutriment = list(1), /decl/material/liquid/sedatives = list(3,3), /decl/material/liquid/psychoactives = list(1,25)) /datum/seed/mushroom/hallucinogenic/strong/New() ..() @@ -544,13 +540,15 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#f2e550") set_trait(TRAIT_PLANT_COLOUR,"#d1ca82") set_trait(TRAIT_PLANT_ICON,"mushroom3") + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) + set_chemical_amount(/decl/material/liquid/sedatives, list(3,3)) + set_chemical_amount(/decl/material/liquid/psychoactives, list(1,25)) /datum/seed/mushroom/poison name = "amanita" product_name = "fly amanita" display_name = "fly amanita mushroom patch" mutants = list("destroyingangel","plastic") - chems = list(/decl/material/liquid/nutriment = list(1), /decl/material/liquid/amatoxin = list(3,3), /decl/material/liquid/psychotropics = list(1,25)) /datum/seed/mushroom/poison/New() ..() @@ -563,13 +561,15 @@ set_trait(TRAIT_LEAVES_COLOUR,"#ff4545") // to make it so they aren't pale while immature set_trait(TRAIT_PLANT_COLOUR,"#e0ddba") set_trait(TRAIT_PLANT_ICON,"mushroom4") + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) + set_chemical_amount(/decl/material/liquid/amatoxin, list(3,3)) + set_chemical_amount(/decl/material/liquid/psychotropics, list(1,25)) /datum/seed/mushroom/poison/death name = "destroyingangel" product_name = "destroying angel" display_name = "destroying angel mushroom patch" mutants = null - chems = list(/decl/material/liquid/nutriment = list(1,50), /decl/material/liquid/amatoxin = list(13,3), /decl/material/liquid/psychotropics = list(1,25)) /datum/seed/mushroom/poison/death/New() ..() @@ -581,12 +581,14 @@ set_trait(TRAIT_PLANT_COLOUR,"#e6d8dd") set_trait(TRAIT_PLANT_ICON,"mushroom5") set_trait(TRAIT_LEAVES_COLOUR,null) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,50)) + set_chemical_amount(/decl/material/liquid/amatoxin, list(13,3)) + set_chemical_amount(/decl/material/liquid/psychotropics, list(1,25)) /datum/seed/mushroom/towercap name = "towercap" product_name = "dwarf towercap" display_name = "dwarf towercap thicket" - chems = list(/decl/material/solid/organic/wood/fungal = list(10,1)) mutants = null product_type = /obj/item/stack/material/log/towercap @@ -600,13 +602,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#d3aca3") set_trait(TRAIT_PLANT_COLOUR,"#dcd9d9") set_trait(TRAIT_PLANT_ICON,"mushroom8") + set_chemical_amount(/decl/material/solid/organic/wood/fungal, list(10,1)) /datum/seed/mushroom/glowbell name = "glowbell" product_name = "glowbell" display_name = "glowbell patch" mutants = list("weepingmoon", "caverncandle") - chems = list(/decl/material/liquid/glowsap = list(1,20)) /datum/seed/mushroom/glowbell/New() ..() @@ -620,13 +622,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#90d7f0") set_trait(TRAIT_PLANT_COLOUR,"#75bdd7") set_trait(TRAIT_PLANT_ICON,"mushroom2") + set_chemical_amount(/decl/material/liquid/glowsap, list(1,20)) /datum/seed/mushroom/weepingmoon name = "weepingmoon" product_name = "weeping moon" display_name = "weeping moon patch" mutants = null - chems = list(/decl/material/liquid/glowsap = list(1,20)) /datum/seed/mushroom/weepingmoon/New() ..() @@ -640,13 +642,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#90d7f0") set_trait(TRAIT_PLANT_COLOUR,"#75bdd7") set_trait(TRAIT_PLANT_ICON,"mushroom4") + set_chemical_amount(/decl/material/liquid/glowsap, list(1,20)) /datum/seed/mushroom/caverncandle name = "caverncandle" product_name = "caverncandle" display_name = "cavern candle patch" mutants = null - chems = list(/decl/material/liquid/glowsap = list(1,20)) /datum/seed/mushroom/caverncandle/New() ..() @@ -660,13 +662,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#90d7f0") set_trait(TRAIT_PLANT_COLOUR,"#75bdd7") set_trait(TRAIT_PLANT_ICON,"mushroom3") + set_chemical_amount(/decl/material/liquid/glowsap, list(1,20)) /datum/seed/mushroom/plastic name = "plastic" product_name = "plastellium" display_name = "plastellium patch" mutants = null - chems = list(/decl/material/liquid/plasticide = list(1,10)) /datum/seed/mushroom/plastic/New() ..() @@ -678,13 +680,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#e6e6e6") set_trait(TRAIT_PLANT_COLOUR,"#e6e6e6") set_trait(TRAIT_PLANT_ICON,"mushroom10") + set_chemical_amount(/decl/material/liquid/plasticide, list(1,10)) //Flowers/varieties /datum/seed/flower name = "harebells" product_name = "harebell" display_name = "harebell patch" - chems = list(/decl/material/liquid/nutriment = list(1,20)) slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 produces_pollen = 1 @@ -699,15 +701,12 @@ set_trait(TRAIT_PLANT_COLOUR,"#6b8c5e") set_trait(TRAIT_PLANT_ICON,"flower") set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) /datum/seed/flower/poppy name = "poppies" product_name = "poppy" display_name = "poppy patch" - chems = list( - /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/painkillers/strong = list(1,10) - ) grown_tag = "poppy" /datum/seed/flower/poppy/New() @@ -722,14 +721,13 @@ set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 0.5) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/painkillers/strong, list(1,10)) /datum/seed/flower/sunflower name = "sunflowers" product_name = "sunflower" display_name = "sunflower patch" - chems = list( - /decl/material/liquid/oil/plant = list(10,10) - ) /datum/seed/flower/sunflower/New() ..() @@ -740,12 +738,12 @@ set_trait(TRAIT_IDEAL_LIGHT, 7) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/oil/plant, list(10,10)) /datum/seed/flower/lavender name = "lavender" product_name = "lavender" display_name = "lavender patch" - chems = list(/decl/material/liquid/nutriment = list(1,20), /decl/material/liquid/brute_meds = list(1,10)) /datum/seed/flower/lavender/New() ..() @@ -759,6 +757,8 @@ set_trait(TRAIT_IDEAL_LIGHT, 7) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.05) set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/brute_meds, list(1,10)) //Grapes/varieties /datum/seed/grapes @@ -766,7 +766,6 @@ product_name = "grapes" display_name = "grapevine" mutants = list("greengrapes") - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/nutriment/sugar = list(1,5), /decl/material/liquid/drink/juice/grape = list(10,10)) slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 allergen_flags = ALLERGEN_FRUIT @@ -784,27 +783,28 @@ set_trait(TRAIT_PLANT_ICON,"vine") set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/nutriment/sugar, list(1,5)) + set_chemical_amount(/decl/material/liquid/drink/juice/grape, list(10,10)) /datum/seed/grapes/green name = "greengrapes" product_name = "green grapes" display_name = "green grapevine" mutants = null - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/burn_meds = list(3,5), /decl/material/liquid/drink/juice/grape = list(10,10)) /datum/seed/grapes/green/New() ..() set_trait(TRAIT_PRODUCT_COLOUR,"42ed2f") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/burn_meds, list(3,5)) + set_chemical_amount(/decl/material/liquid/drink/juice/grape, list(10,10)) //Everything else /datum/seed/peanuts name = "peanut" product_name = "peanut" display_name = "peanut vine" - chems = list( - /decl/material/liquid/nutriment = list(1,10), - /decl/material/liquid/oil/plant = list(1,10) - ) slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 allergen_flags = ALLERGEN_NUTS @@ -820,12 +820,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#c4ae7a") set_trait(TRAIT_PLANT_ICON,"bush2") set_trait(TRAIT_IDEAL_LIGHT, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/oil/plant, list(1,10)) /datum/seed/peppercorn name = "peppercorn" product_name = "peppercorn" display_name = "black pepper plant" - chems = list(/decl/material/solid/blackpepper = list(10,10)) slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 @@ -840,12 +841,12 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#4d4d4d") set_trait(TRAIT_PLANT_ICON,"vine2") set_trait(TRAIT_IDEAL_LIGHT, 6) + set_chemical_amount(/decl/material/solid/blackpepper, list(10,10)) /datum/seed/cabbage name = "cabbage" product_name = "cabbage" display_name = "cabbage patch" - chems = list(/decl/material/liquid/nutriment = list(1,10)) grown_tag = "cabbage" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -864,12 +865,12 @@ set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) /datum/seed/banana name = "banana" product_name = "banana" display_name = "banana tree" - chems = list(/decl/material/liquid/drink/juice/banana = list(10,10), /decl/material/solid/potassium = list(2,3)) trash_type = /obj/item/bananapeel grown_tag = "banana" allergen_flags = ALLERGEN_FRUIT @@ -888,12 +889,13 @@ set_trait(TRAIT_IDEAL_LIGHT, 7) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/drink/juice/banana, list(10,10)) + set_chemical_amount(/decl/material/solid/potassium, list(2,3)) /datum/seed/corn name = "corn" product_name = "corn" display_name = "ears of corn" - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/oil/plant/corn = list(1,10)) grown_tag = "corn" trash_type = /obj/item/corncob backyard_grilling_product = /obj/item/food/popcorn @@ -914,12 +916,13 @@ set_trait(TRAIT_IDEAL_HEAT, 298) set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/oil/plant/corn, list(1,10)) /datum/seed/potato name = "potato" product_name = "potato" display_name = "potato patch" - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/drink/juice/potato = list(10,10)) grown_tag = "potato" grown_is_seed = TRUE min_seed_extracted = 0 @@ -938,12 +941,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#d4cab4") set_trait(TRAIT_PLANT_ICON,"bush2") set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/drink/juice/potato, list(10,10)) /datum/seed/garlic name = "garlic" product_name = "garlic" display_name = "garlic plant" - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/drink/juice/garlic = list(10,10)) grown_tag = "garlic" slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 @@ -962,12 +966,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#fff8dd") set_trait(TRAIT_PLANT_ICON,"stalk") set_trait(TRAIT_WATER_CONSUMPTION, 7) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/drink/juice/garlic, list(10,10)) /datum/seed/onion name = "onion" product_name = "onion" display_name = "onion patch" - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/drink/juice/onion = list(10,10)) grown_tag = "onion" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -983,16 +988,13 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#ffeedd") set_trait(TRAIT_PLANT_ICON,"stalk") set_trait(TRAIT_WATER_CONSUMPTION, 5) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/drink/juice/onion, list(10,10)) /datum/seed/soybean name = "soybeans" product_name = "soybeans" display_name = "soybean patch" - chems = list( - /decl/material/liquid/nutriment = list(1,20), - /decl/material/liquid/oil/plant = list(3,20), - /decl/material/liquid/drink/milk/soymilk = list(7,20) - ) grown_tag = "soybeans" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -1008,12 +1010,14 @@ set_trait(TRAIT_PRODUCT_ICON,"bean") set_trait(TRAIT_PRODUCT_COLOUR,"#ebe7c0") set_trait(TRAIT_PLANT_ICON,"stalk") + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/oil/plant, list(3,20)) + set_chemical_amount(/decl/material/liquid/drink/milk/soymilk, list(7,20)) /datum/seed/wheat name = "wheat" product_name = "wheat" display_name = "wheat patch" - chems = list(/decl/material/liquid/nutriment = list(1,25), /decl/material/liquid/nutriment/flour = list(15,15)) grown_tag = "wheat" slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 @@ -1031,12 +1035,13 @@ set_trait(TRAIT_PLANT_ICON,"stalk2") set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,25)) + set_chemical_amount(/decl/material/liquid/nutriment/flour, list(15,15)) /datum/seed/rice name = "rice" product_name = "rice" display_name = "rice paddy" - chems = list(/decl/material/liquid/nutriment = list(1,25), /decl/material/liquid/nutriment/rice = list(10,15)) grown_tag = "rice" slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 @@ -1054,12 +1059,13 @@ set_trait(TRAIT_PLANT_ICON,"stalk2") set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,25)) + set_chemical_amount(/decl/material/liquid/nutriment/rice, list(10,15)) /datum/seed/carrots name = "carrot" product_name = "carrot" display_name = "carrot patch" - chems = list(/decl/material/liquid/nutriment = list(1,20), /decl/material/liquid/eyedrops = list(3,5), /decl/material/liquid/drink/juice/carrot = list(10,20)) grown_tag = "carrot" slice_product = /obj/item/food/processed_grown/sticks slice_amount = 3 @@ -1074,6 +1080,9 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#ff9900") set_trait(TRAIT_PLANT_ICON,"carrot") set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/eyedrops, list(3,5)) + set_chemical_amount(/decl/material/liquid/drink/juice/carrot, list(10,20)) /datum/seed/weeds name = "weeds" @@ -1096,7 +1105,6 @@ name = "whitebeet" product_name = "white-beet" display_name = "white-beet patch" - chems = list(/decl/material/liquid/nutriment = list(0,20), /decl/material/liquid/nutriment/sugar = list(1,5)) grown_tag = "whitebeet" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -1112,12 +1120,13 @@ set_trait(TRAIT_PLANT_COLOUR,"#4d8f53") set_trait(TRAIT_PLANT_ICON,"carrot2") set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(0,20)) + set_chemical_amount(/decl/material/liquid/nutriment/sugar, list(1,5)) /datum/seed/sugarcane name = "sugarcane" product_name = "sugarcane" display_name = "sugarcane patch" - chems = list(/decl/material/liquid/nutriment/sugar = list(4,5)) /datum/seed/sugarcane/New() ..() @@ -1131,13 +1140,13 @@ set_trait(TRAIT_PLANT_COLOUR,"#6bbd68") set_trait(TRAIT_PLANT_ICON,"stalk3") set_trait(TRAIT_IDEAL_HEAT, 298) + set_chemical_amount(/decl/material/liquid/nutriment/sugar, list(4,5)) /datum/seed/watermelon name = "watermelon" product_name = "watermelon" display_name = "watermelon vine" product_w_class = ITEM_SIZE_LARGE - chems = list(/decl/material/liquid/nutriment = list(1,6), /decl/material/liquid/drink/juice/watermelon = list(10,6)) slice_product = /obj/item/food/processed_grown/slice/large allergen_flags = ALLERGEN_FRUIT @@ -1157,13 +1166,14 @@ set_trait(TRAIT_IDEAL_HEAT, 298) set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,6)) + set_chemical_amount(/decl/material/liquid/drink/juice/watermelon, list(10,6)) /datum/seed/pumpkin name = "pumpkin" product_name = "pumpkin" display_name = "pumpkin vine" product_w_class = ITEM_SIZE_LARGE - chems = list(/decl/material/liquid/nutriment = list(1,6)) grown_tag = "pumpkin" slice_product = /obj/item/clothing/head/pumpkinhead slice_amount = 1 @@ -1188,12 +1198,12 @@ set_trait(TRAIT_PLANT_COLOUR,"#bae8c1") set_trait(TRAIT_PLANT_ICON,"vine2") set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,6)) /datum/seed/citrus name = "lime" product_name = "lime" display_name = "lime tree" - chems = list(/decl/material/liquid/nutriment = list(1,20), /decl/material/liquid/drink/juice/lime = list(10,20)) grown_tag = "lime" allergen_flags = ALLERGEN_FRUIT @@ -1210,12 +1220,13 @@ set_trait(TRAIT_PLANT_ICON,"tree") set_trait(TRAIT_FLESH_COLOUR,"#3af026") set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/drink/juice/lime, list(10,20)) /datum/seed/citrus/lemon name = "lemon" product_name = "lemon" display_name = "lemon tree" - chems = list(/decl/material/liquid/nutriment = list(1,20), /decl/material/liquid/drink/juice/lemon = list(10,20)) grown_tag = "lemon" /datum/seed/citrus/lemon/New() @@ -1225,25 +1236,27 @@ set_trait(TRAIT_FLESH_COLOUR,"#f0e226") set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/drink/juice/lemon, list(10,20)) /datum/seed/citrus/orange name = "orange" product_name = "orange" display_name = "orange tree" grown_tag = "orange" - chems = list(/decl/material/liquid/nutriment = list(1,20), /decl/material/liquid/drink/juice/orange = list(10,20)) /datum/seed/citrus/orange/New() ..() set_trait(TRAIT_PRODUCT_COLOUR,"#ffc20a") set_trait(TRAIT_FLESH_COLOUR,"#ffc20a") set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) + set_chemical_amount(/decl/material/liquid/drink/juice/orange, list(10,20)) /datum/seed/grass name = "grass" product_name = "grass" display_name = "grass patch" - chems = list(/decl/material/liquid/nutriment = list(1,20)) grown_tag = "grass" product_type = /obj/item/stack/material/bundle/grass slice_product = /obj/item/food/processed_grown/chopped @@ -1262,12 +1275,12 @@ set_trait(TRAIT_WATER_CONSUMPTION, 0.5) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,20)) /datum/seed/cocoa name = "cocoa" product_name = "cacao" display_name = "cacao tree" - chems = list(/decl/material/liquid/nutriment = list(1,10), /decl/material/liquid/nutriment/coco = list(4,5)) slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 @@ -1284,13 +1297,14 @@ set_trait(TRAIT_IDEAL_HEAT, 298) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,10)) + set_chemical_amount(/decl/material/liquid/nutriment/coco, list(4,5)) /datum/seed/cherries name = "cherry" product_name = "cherry" seed_noun = SEED_NOUN_PITS display_name = "cherry tree" - chems = list(/decl/material/liquid/nutriment = list(1,15), /decl/material/liquid/nutriment/sugar = list(1,15), /decl/material/liquid/nutriment/cherryjelly = list(10,15)) grown_tag = "cherries" slice_product = /obj/item/food/processed_grown/crushed slice_amount = 3 @@ -1309,12 +1323,14 @@ set_trait(TRAIT_PLANT_ICON,"tree2") set_trait(TRAIT_PLANT_COLOUR,"#2f7d2d") set_trait(TRAIT_PHOTOSYNTHESIS, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,15)) + set_chemical_amount(/decl/material/liquid/nutriment/sugar, list(1,15)) + set_chemical_amount(/decl/material/liquid/nutriment/cherryjelly, list(10,15)) /datum/seed/kudzu name = "kudzu" product_name = "kudzu" display_name = "kudzu vine" - chems = list(/decl/material/liquid/nutriment = list(1,50), /decl/material/liquid/antitoxins = list(1,25)) slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -1330,12 +1346,13 @@ set_trait(TRAIT_PLANT_COLOUR,"#6f7a63") set_trait(TRAIT_PLANT_ICON,"vine2") set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,50)) + set_chemical_amount(/decl/material/liquid/antitoxins, list(1,25)) /datum/seed/shand name = "shand" product_name = "S'randar's hand" display_name = "S'randar's hand patch" - chems = list(/decl/material/liquid/brute_meds = list(0,10)) grown_tag = "shand" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -1352,12 +1369,12 @@ set_trait(TRAIT_PLANT_ICON,"tree5") set_trait(TRAIT_IDEAL_HEAT, 283) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/brute_meds, list(0,10)) /datum/seed/mtear name = "mtear" product_name = "Messa's tear" display_name = "Messa's tear patch" - chems = list(/decl/material/liquid/nutriment/honey = list(1,10), /decl/material/liquid/burn_meds = list(3,5)) grown_tag = "mtear" slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -1374,13 +1391,14 @@ set_trait(TRAIT_PLANT_ICON,"bush7") set_trait(TRAIT_IDEAL_HEAT, 283) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment/honey, list(1,10)) + set_chemical_amount(/decl/material/liquid/burn_meds, list(3,5)) /datum/seed/tobacco name = "tobacco" product_name = "tobacco" display_name = "tobacco plant" mutants = list("finetobacco", "puretobacco", "badtobacco") - chems = list(/decl/material/solid/tobacco = list(1,10)) slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 product_w_class = ITEM_SIZE_TINY // so that it can fit in bags of tobacco @@ -1398,12 +1416,12 @@ set_trait(TRAIT_IDEAL_LIGHT, 7) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/solid/tobacco, list(1,10)) /datum/seed/tobacco/finetobacco name = "finetobacco" product_name = "fine tobacco" display_name = "fine tobacco plant" - chems = list(/decl/material/solid/tobacco/fine = list(1,10)) /datum/seed/tobacco/finetobacco/New() ..() @@ -1411,12 +1429,12 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#33571b") set_trait(TRAIT_PLANT_COLOUR,"#33571b") set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.20) + set_chemical_amount(/decl/material/solid/tobacco/fine, list(1,10)) /datum/seed/tobacco/puretobacco //provides the pure nicotine reagent name = "puretobacco" product_name = "succulent tobacco" display_name = "succulent tobacco plant" - chems = list(/decl/material/liquid/nicotine = list(1,10)) /datum/seed/tobacco/puretobacco/New() ..() @@ -1425,22 +1443,22 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#b7c61a") set_trait(TRAIT_PLANT_COLOUR,"#b7c61a") set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.30) + set_chemical_amount(/decl/material/liquid/nicotine, list(1,10)) /datum/seed/tobacco/bad name = "badtobacco" product_name = "low-grade tobacco" display_name = "low-grade tobacco plant" mutants = list("tobacco") - chems = list(/decl/material/solid/tobacco/bad = list(1,10)) + +/datum/seed/tobacco/bad/New() + ..() + set_chemical_amount(/decl/material/solid/tobacco/bad, list(1,10)) /datum/seed/algae name = "algae" product_name = "algae" display_name = "algae patch" - chems = list( - /decl/material/liquid/nutriment = list(2,12), - /decl/material/liquid/bromide = list(3,8) - ) grown_tag = "algae" exude_gasses = list(/decl/material/gas/methyl_bromide = 3) slice_product = /obj/item/food/processed_grown/crushed @@ -1460,12 +1478,13 @@ set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment, list(2,12)) + set_chemical_amount(/decl/material/liquid/bromide, list(3,8)) /datum/seed/bamboo name = "bamboo" product_name = "bamboo" display_name = "bamboo thicket" - chems = list(/decl/material/solid/organic/wood/bamboo = list(6,1)) mutants = null slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 @@ -1483,13 +1502,12 @@ set_trait(TRAIT_IDEAL_HEAT, 298) set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_chemical_amount(/decl/material/solid/organic/wood/bamboo, list(6,1)) /datum/seed/tea name = "tea" product_name = "tea leaf" display_name = "tea plant" - chems = list(/decl/material/liquid/nutriment = list(1)) - dried_chems = list(/decl/material/liquid/nutriment/tea = list(10,10)) slice_product = /obj/item/food/processed_grown/chopped slice_amount = 3 allergen_flags = ALLERGEN_CAFFEINE | ALLERGEN_STIMULANT @@ -1508,13 +1526,13 @@ set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + set_chemical_amount(/decl/material/liquid/nutriment/tea, list(10,10), _state = PLANT_STATE_DRIED) + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) /datum/seed/coffee name = "coffee" product_name = "coffee cherries" display_name = "coffee plant" - chems = list(/decl/material/liquid/nutriment = list(1)) - roasted_chems = list(/decl/material/liquid/nutriment/coffee = list(10,10)) backyard_grilling_product = /obj/item/food/grown/grilled backyard_grilling_announcement = "roasts and darkens." product_material = /decl/material/solid/organic/plantmatter/pith @@ -1536,3 +1554,6 @@ set_trait(TRAIT_IDEAL_LIGHT, 6) set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + + set_chemical_amount(/decl/material/liquid/nutriment/coffee, list(10,10), _state = PLANT_STATE_ROASTED) + set_chemical_amount(/decl/material/liquid/nutriment, list(1)) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index bf56a5e39f18..236a5ae569a3 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -15,9 +15,6 @@ var/growth_stages = 0 // Number of stages the plant passes through before it is mature. var/list/_traits // Initialized in New() var/list/mutants // Possible predefined mutant varieties, if any. - var/list/chems // Chemicals that plant produces in products/injects into victim. - var/list/dried_chems // Chemicals that a dried plant product will have. - var/list/roasted_chems // Chemicals that a roasted/grilled plant product will have. var/list/consume_gasses // The plant will absorb these gasses during its life. var/list/exude_gasses // The plant will exude these gasses during its life. var/grown_tag // Used by the reagent grinder. @@ -100,8 +97,9 @@ return var/datum/reagents/R = new/datum/reagents(100, global.temp_reagents_holder) - if(chems.len) - for(var/rid in chems) + var/list/seed_chems = get_chemical_composition() + if(length(seed_chems)) + for(var/rid in seed_chems) var/injecting = min(5,max(1,get_trait(TRAIT_POTENCY)/3)) R.add_reagent(rid,injecting) @@ -157,7 +155,8 @@ return var/list/external_organs = target.get_external_organs() - if(chems && chems.len && target.reagents && LAZYLEN(external_organs)) + var/list/seed_chems = get_chemical_composition() + if(length(seed_chems) && target.reagents && LAZYLEN(external_organs)) var/obj/item/organ/external/affecting = pick(external_organs) for(var/slot in global.standard_clothing_slots) @@ -170,7 +169,7 @@ if(affecting) to_chat(target, "You are stung by \the [fruit] in your [affecting.name]!") - for(var/rid in chems) + for(var/rid in seed_chems) var/injecting = min(5,max(1,get_trait(TRAIT_POTENCY)/5)) target.add_to_reagents(rid,injecting) else @@ -213,16 +212,18 @@ if(!splat_reagents?.maximum_volume) // if thrown doesn't exist or has no reagents, use the seed's default reagents. splat_reagents = new /datum/reagents(INFINITY, global.temp_reagents_holder) var/potency = get_trait(TRAIT_POTENCY) - for(var/rid in chems) - var/list/reagent_amounts = chems[rid] - if(LAZYLEN(reagent_amounts)) - var/rtotal = reagent_amounts[1] - var/list/data = null - if(reagent_amounts?[2] && potency > 0) - rtotal += round(potency/reagent_amounts[2]) - if(rid == /decl/material/liquid/nutriment) - LAZYSET(data, product_name, max(1,rtotal)) - splat_reagents.add_reagent(rid,max(1,rtotal),data) + var/list/seed_chems = get_chemical_composition() + if(length(seed_chems)) + for(var/rid in seed_chems) + var/list/reagent_amounts = seed_chems[rid] + if(LAZYLEN(reagent_amounts)) + var/rtotal = reagent_amounts[1] + var/list/data = null + if(reagent_amounts?[2] && potency > 0) + rtotal += round(potency/reagent_amounts[2]) + if(rid == /decl/material/liquid/nutriment) + LAZYSET(data, product_name, max(1,rtotal)) + splat_reagents.add_reagent(rid,max(1,rtotal),data) if(splat_reagents) var/splat_range = min(10,max(1,get_trait(TRAIT_POTENCY)/15)) splat_reagents.splash_area(T, range = splat_range) @@ -442,14 +443,14 @@ gasses -= gas LAZYSET(exude_gasses, gas, rand(3,9)) - chems = list() + clear_chemical_composition() if(prob(80)) - chems[/decl/material/liquid/nutriment] = list(rand(1,10),rand(10,20)) + set_chemical_amount(/decl/material/liquid/nutriment, list(rand(1,10),rand(10,20))) if(length(liquids)) for(var/x = 1 to rand(0, 5)) var/new_chem = pickweight(liquids) liquids -= new_chem - chems[new_chem] = list(rand(1,10), rand(10,20)) + set_chemical_amount(new_chem, list(rand(1,10),rand(10,20))) if(prob(90)) set_trait(TRAIT_REQUIRES_NUTRIENTS,1) @@ -680,8 +681,8 @@ new_seed.product_type = product_type //Copy over everything else. + new_seed.copy_chemical_composition(src) if(mutants) new_seed.mutants = mutants.Copy() - if(chems) new_seed.chems = chems.Copy() if(consume_gasses) new_seed.consume_gasses = consume_gasses.Copy() if(exude_gasses) new_seed.exude_gasses = exude_gasses.Copy() @@ -750,9 +751,9 @@ //Seed traits clone._traits = deepCopyList(_traits) clone.mutants = mutants?.Copy() - clone.chems = chems?.Copy() clone.consume_gasses = consume_gasses?.Copy() clone.exude_gasses = exude_gasses?.Copy() + clone.copy_chemical_composition(src) //Appearence clone.growth_stages = growth_stages diff --git a/code/modules/hydroponics/seed_composition.dm b/code/modules/hydroponics/seed_composition.dm new file mode 100644 index 000000000000..e25b34789af1 --- /dev/null +++ b/code/modules/hydroponics/seed_composition.dm @@ -0,0 +1,21 @@ +/datum/seed + VAR_PRIVATE/list/_chemical_composition + +/datum/seed/proc/copy_chemical_composition(datum/seed/donor) + UNLINT(_chemical_composition = deepCopyList(donor._chemical_composition)) + +/datum/seed/proc/get_chemical_amount(_chem, _state = PLANT_STATE_FRESH, _segment = PLANT_SEG_BODY) + var/list/comp = get_chemical_composition(_state, _segment) + return LAZYACCESS(comp, _chem) + +/datum/seed/proc/set_chemical_amount(_chem, list/_amt, _state = PLANT_STATE_FRESH, _segment = PLANT_SEG_BODY) + LAZYINITLIST(_chemical_composition) + LAZYINITLIST(_chemical_composition[_state]) + LAZYSET(_chemical_composition[_state][_segment], _chem, _amt) + +/datum/seed/proc/get_chemical_composition(_state = PLANT_STATE_FRESH, _segment = PLANT_SEG_BODY) + var/list/comp = LAZYACCESS(_chemical_composition, _state) + return LAZYACCESS(comp, _segment) + +/datum/seed/proc/clear_chemical_composition() + LAZYCLEARLIST(_chemical_composition) diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 523d2ec04c84..18dc92c3ce02 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -129,7 +129,7 @@ layer = (seed && seed.force_layer) ? seed.force_layer : ABOVE_OBJ_LAYER if(growth_type in list(GROWTH_VINES,GROWTH_BIOMASS)) set_opacity(1) - if(islist(seed.chems) && !isnull(seed.chems[/decl/material/solid/organic/wood])) + if(seed.get_chemical_amount(/decl/material/solid/organic/wood)) set_density(1) set_opacity(1) diff --git a/code/modules/hydroponics/spreading/spreading_growth.dm b/code/modules/hydroponics/spreading/spreading_growth.dm index 6f3019810d30..dcc68137e6f5 100644 --- a/code/modules/hydroponics/spreading/spreading_growth.dm +++ b/code/modules/hydroponics/spreading/spreading_growth.dm @@ -29,7 +29,7 @@ if(blocked) continue if(floor.density) - if(!isnull(seed.chems[/decl/material/liquid/acid/polyacid])) + if(seed.get_chemical_amount(/decl/material/liquid/acid/polyacid)) spawn(rand(5,25)) floor.explosion_act(3) continue if(!Adjacent(floor) || !floor.Enter(src)) @@ -119,7 +119,7 @@ child.set_dir(child.calc_dir()) child.update_icon() // Some plants eat through plating. - if(islist(seed.chems) && !isnull(seed.chems[/decl/material/liquid/acid/polyacid])) + if(seed.get_chemical_amount(/decl/material/liquid/acid/polyacid)) target_turf.explosion_act(prob(80) ? 3 : 2) else qdel(child) diff --git a/code/modules/maps/template_types/random_exoplanet/flora_generator.dm b/code/modules/maps/template_types/random_exoplanet/flora_generator.dm index 6bf9ff36f00d..75c97b70ffc0 100644 --- a/code/modules/maps/template_types/random_exoplanet/flora_generator.dm +++ b/code/modules/maps/template_types/random_exoplanet/flora_generator.dm @@ -79,10 +79,10 @@ if(prob(50)) var/chem_type = SSmaterials.get_random_chem(TRUE, atmos.temperature || T0C) if(chem_type) - var/nutriment = S.chems[/decl/material/liquid/nutriment] - S.chems.Cut() - S.chems[/decl/material/liquid/nutriment] = nutriment - S.chems[chem_type] = list(rand(1,10),rand(10,20)) + var/nutriment = S.get_chemical_amount(_chem = /decl/material/liquid/nutriment) + S.clear_chemical_composition() + S.set_chemical_amount(/decl/material/liquid/nutriment, nutriment) + S.set_chemical_amount(chem_type, list(rand(1,10),rand(10,20))) return S @@ -162,6 +162,6 @@ S.set_trait(TRAIT_HARVEST_REPEAT, 1) S.set_trait(TRAIT_LARGE, 1) S.set_trait(TRAIT_LEAVES_COLOUR, color) - S.chems[/decl/material/solid/organic/wood] = list(1,0) //#TODO: Maybe look at Why the seed creates injectable wood? + S.set_chemical_amount(/decl/material/solid/organic/wood, list(1,0)) //#TODO: Maybe look at Why the seed creates injectable wood? adapt_seed(S, atmos) LAZYADD(big_flora_types, S) diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm index 533174a5fa11..d1515fe6582f 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm @@ -40,9 +40,9 @@ if(prob(75)) S.get_trait(TRAIT_STINGS, 1) - LAZYSET(S.chems, /decl/material/solid/organic/meat, list(10,30)) - LAZYSET(S.chems, /decl/material/liquid/blood, list(5,10)) - LAZYSET(S.chems, /decl/material/liquid/acid/stomach, list(5,10)) + S.set_chemical_amount(/decl/material/solid/organic/meat, list(10,30)) + S.set_chemical_amount(/decl/material/liquid/blood, list(5,10)) + S.set_chemical_amount(/decl/material/liquid/acid/stomach, list(5,10)) S.set_trait(TRAIT_PARASITE,1) diff --git a/mods/content/integrated_electronics/components/input.dm b/mods/content/integrated_electronics/components/input.dm index 30268d3cb5da..17d9c59cb75b 100644 --- a/mods/content/integrated_electronics/components/input.dm +++ b/mods/content/integrated_electronics/components/input.dm @@ -298,7 +298,7 @@ for(var/i=1, i<=outputs.len, i++) set_pin_data(IC_OUTPUT, i, null) if(plant.seed && (plant in view(get_turf(src)))) // Like the medbot's analyzer it can be used at range. - for(var/chem_path in plant.seed.chems) + for(var/chem_path in plant.seed.get_chemical_composition()) var/decl/material/seed_chem = GET_DECL(chem_path) greagents.Add(seed_chem.use_name) diff --git a/mods/species/drakes/sifpod.dm b/mods/species/drakes/sifpod.dm index 1e7e45c2744f..a3fed05a820b 100644 --- a/mods/species/drakes/sifpod.dm +++ b/mods/species/drakes/sifpod.dm @@ -10,10 +10,6 @@ grown_tag = "sifpod" backyard_grilling_product = /obj/item/food/roast_sifpod backyard_grilling_announcement = "crackles and pops as the roast hull splits open." - chems = list( - /decl/material/liquid/nutriment = list(1,5), - /decl/material/liquid/sifsap = list(10,20) - ) /datum/seed/sifpod/New() ..() @@ -27,6 +23,8 @@ set_trait(TRAIT_PLANT_ICON,"tree5") set_trait(TRAIT_FLESH_COLOUR,"#05157d") set_trait(TRAIT_IDEAL_LIGHT, 1) + set_chemical_amount(/decl/material/liquid/nutriment, list(1,5)) + set_chemical_amount(/decl/material/liquid/sifsap, list(10,20)) /obj/item/food/roast_sifpod name = "roast sifpod" diff --git a/nebula.dme b/nebula.dme index 4042ec7bc748..01ed6a26c97e 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2570,6 +2570,7 @@ #include "code\modules\hydroponics\processed_grown.dm" #include "code\modules\hydroponics\seed.dm" #include "code\modules\hydroponics\seed_appearance.dm" +#include "code\modules\hydroponics\seed_composition.dm" #include "code\modules\hydroponics\seed_datums_aquaculture.dm" #include "code\modules\hydroponics\seed_gene_mut.dm" #include "code\modules\hydroponics\seed_machines.dm" From 3ac58aed8694acb69f98bebdd4ac21f07f184f51 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sat, 25 Oct 2025 00:15:42 +1100 Subject: [PATCH 0893/1331] Adding plant dissection based on Pyrelight implementation. --- maps/modpack_testing/modpack_testing.dm | 1 + maps/tradeship/tradeship.dm | 2 + .../plant_dissection/_plant_dissection.dm | 6 + .../plant_dissection/_plant_dissection.dme | 10 ++ mods/content/plant_dissection/grown.dm | 106 ++++++++++++++++++ .../content/plant_dissection/grown_segment.dm | 44 ++++++++ .../plant_dissection/icons/segment.dmi | Bin 0 -> 354 bytes .../content/plant_dissection/plant_segment.dm | 88 +++++++++++++++ .../plant_dissection/seed_dissection.dm | 44 ++++++++ 9 files changed, 301 insertions(+) create mode 100644 mods/content/plant_dissection/_plant_dissection.dm create mode 100644 mods/content/plant_dissection/_plant_dissection.dme create mode 100644 mods/content/plant_dissection/grown.dm create mode 100644 mods/content/plant_dissection/grown_segment.dm create mode 100644 mods/content/plant_dissection/icons/segment.dmi create mode 100644 mods/content/plant_dissection/plant_segment.dm create mode 100644 mods/content/plant_dissection/seed_dissection.dm diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index bdb2a0ebdca8..068804e94192 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -25,6 +25,7 @@ #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/plant_dissection/_plant_dissection.dme" #include "../../mods/content/psionics/_psionics.dme" #include "../../mods/content/shackles/_shackles.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index ef453a040ce1..acaaf113f919 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -14,6 +14,8 @@ #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/plant_dissection/_plant_dissection.dme" + #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/blob/_blob.dme" diff --git a/mods/content/plant_dissection/_plant_dissection.dm b/mods/content/plant_dissection/_plant_dissection.dm new file mode 100644 index 000000000000..a8229cd13147 --- /dev/null +++ b/mods/content/plant_dissection/_plant_dissection.dm @@ -0,0 +1,6 @@ +#define PLANT_SEG_PETAL "petal" +#define PLANT_SEG_STAMEN "stamen" +#define PLANT_SEG_STIGMA "stigma" + +/decl/modpack/plant_dissection + name = "Plant Dissection" diff --git a/mods/content/plant_dissection/_plant_dissection.dme b/mods/content/plant_dissection/_plant_dissection.dme new file mode 100644 index 000000000000..ebf842d7a97e --- /dev/null +++ b/mods/content/plant_dissection/_plant_dissection.dme @@ -0,0 +1,10 @@ +// BEGIN_INCLUDE +#ifndef MODPACK_PLANT_DISSECTION +#define MODPACK_PLANT_DISSECTION +#include "_plant_dissection.dm" +#include "grown.dm" +#include "grown_segment.dm" +#include "plant_segment.dm" +#include "seed_dissection.dm" +#endif +// END_INCLUDE \ No newline at end of file diff --git a/mods/content/plant_dissection/grown.dm b/mods/content/plant_dissection/grown.dm new file mode 100644 index 000000000000..a5e74515f470 --- /dev/null +++ b/mods/content/plant_dissection/grown.dm @@ -0,0 +1,106 @@ +/obj/item/food/grown + var/examine_info + var/examine_info_skill = SKILL_BOTANY + var/examine_info_rank = SKILL_BASIC + var/can_dissect = TRUE + var/list/segments + +/obj/item/food/grown/set_seed() + ..() + if(!seed || !can_dissect) + return + var/list/all_segments = seed.get_physical_composition() + if(!length(all_segments)) + can_dissect = FALSE + return + for(var/datum/plant_segment/segment as anything in all_segments) + var/add_seg = 0 + if(islist(segment.dissect_amount)) + add_seg = rand(segment.dissect_amount[1], segment.dissect_amount[2]) + else if(isnum(segment.dissect_amount)) + add_seg = segment.dissect_amount + if(add_seg) + LAZYSET(segments, segment, add_seg) + +/obj/item/food/grown/initialize_reagents(populate) + var/segment_amount = 0 + for(var/datum/plant_segment/segment as anything in segments) + if(segment.contributes_to_reagents) + segment_amount += LAZYACCESS(segment.total_reagent_volume_by_state, (PLANT_STATE_FRESH)) + volume = max(volume, segment_amount) + return ..() + +/obj/item/food/grown/Destroy() + segments = null + return ..() + +/obj/item/food/grown/update_grown_icon() + . = ..() + if(can_dissect && length(segments)) + var/list/segment_count = list() + for(var/datum/plant_segment/segment as anything in segments) + if(!segment.grown_icon || !segment.grown_icon_state) + continue + for(var/i = 1 to segments[segment]) + add_overlay(image(segment.grown_icon, "[segment.grown_icon_state][++segment_count[segment.name]]")) + +/obj/item/food/grown/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(distance <= 1 && can_dissect && examine_info && (!examine_info_skill || !examine_info_rank || user.skill_check(examine_info_skill, examine_info_rank))) + . += examine_info + +/obj/item/food/grown/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + if(distance > 1 || !can_dissect) + return + + var/list/fruit_segment_strings = list() + for(var/datum/plant_segment/segment as anything in segments) + if(!segment.dissect_skill || !segment.dissect_skill_requirement || user.skill_check(segment.dissect_skill, segment.dissect_skill_requirement)) + var/decl/tool_archetype/tool = segment.dissect_tool && GET_DECL(segment.dissect_tool) + var/segment_string_index = tool?.name ? ADD_ARTICLE(tool.name) : "your hands" + LAZYINITLIST(fruit_segment_strings[segment_string_index]) + fruit_segment_strings[segment_string_index][segment.name] += LAZYACCESS(segments, segment) + + for(var/segment_ind in fruit_segment_strings) + var/list/segment_strings_count = list() + for(var/segment_string in fruit_segment_strings[segment_ind]) + segment_strings_count += "[fruit_segment_strings[segment_ind][segment_string]] [segment_string]\s" + if(length(segment_strings_count)) + LAZYADD(., SPAN_NOTICE("With [segment_ind], you could harvest [english_list(segment_strings_count)].")) + +/obj/item/food/grown/attackby(obj/item/W, mob/living/user) + if(can_dissect && !user?.check_intent(I_FLAG_HARM)) + for(var/datum/plant_segment/segment as anything in segments) + if(!segment.dissect_tool || !W.get_tool_quality(segment.dissect_tool)) + continue + segment.on_harvest(W, user, src) + return TRUE + return ..() + +/obj/item/food/grown/attack_self(mob/user) + if(can_dissect) + for(var/datum/plant_segment/segment as anything in segments) + if(segment.dissect_tool) + continue + if(!segment.dissect_skill || !segment.dissect_skill_requirement || user.skill_check(segment.dissect_skill, segment.dissect_skill_requirement)) + segment.on_harvest(null, user, src) + return TRUE + return ..() + +/obj/item/food/grown/proc/remove_segment(var/datum/plant_segment/segment) + + if(!can_dissect || !(segment in segments)) + return + + if(reagents?.total_volume && segment.contributes_to_reagents) + for(var/rid in segment.reagents) + reagents.remove_reagent(rid, segment.reagents[rid]) + + segments[segment]-- + if(segments[segment] <= 0) + LAZYREMOVE(segments, segment) + update_icon() + + if(!length(segments) && !QDELETED(src)) + qdel(src) diff --git a/mods/content/plant_dissection/grown_segment.dm b/mods/content/plant_dissection/grown_segment.dm new file mode 100644 index 000000000000..70b4a9c094a1 --- /dev/null +++ b/mods/content/plant_dissection/grown_segment.dm @@ -0,0 +1,44 @@ +/datum/unit_test/icon_test/food_shall_have_icon_states/assemble_skipped_types() + ..() + skip_types |= typesof(/obj/item/food/grown/segment) + +/obj/item/food/grown/segment + name = "abstract segment" + is_spawnable_type = FALSE + seeds_extracted = TRUE // no seed extraction from petals or stamen + can_dissect = FALSE + var/datum/plant_segment/segment_data + +/obj/item/food/grown/segment/Initialize(mapload, material_key, skip_plate = FALSE, _seed, datum/plant_segment/_segment, obj/item/_source) + if(!_segment) + PRINT_STACK_TRACE("Dissected segment created with no segment datum.") + return INITIALIZE_HINT_QDEL + segment_data = _segment + plant_segment_type = segment_data.plant_segment_type + . = ..() + update_desc() + +/obj/item/food/grown/segment/Destroy() + . = ..() + segment_data = null + +/obj/item/food/grown/segment/update_base_name() + // to allow for 'dried nightweave stamen' etc + if(seed) + base_name = "[seed.product_name] [segment_data.name]" + else + base_name = segment_data.name + +/obj/item/food/grown/segment/update_desc() + base_desc = segment_data.desc // Don't bother doing the descriptor stuff from /grown + desc = base_desc + +/obj/item/food/grown/segment/update_grown_icon() + . = ..() + set_icon(segment_data.segment_icon) + icon_state = segment_data.segment_icon_state + +/obj/item/food/grown/segment/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(distance <= 1 && segment_data.examine_info && (!segment_data.examine_info_skill || !segment_data.examine_info_rank || user.skill_check(segment_data.examine_info_skill, segment_data.examine_info_rank))) + . += segment_data.examine_info diff --git a/mods/content/plant_dissection/icons/segment.dmi b/mods/content/plant_dissection/icons/segment.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4a9a7d372822fe9597a735d00de1c45d7ae15d19 GIT binary patch literal 354 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|ei2$Dv*8>L*{Qv*|`0?{AR_yHR znwOeX=II^-6!d!Z)fK3Qu_VYZn8D%MjWi&qqADb!#3i*jxhS)sBr`t`C^9EFte~j$ z`BCEXA`f0Y&-%uL;qM!R ztfhbYm4I$2@pN$v@#suWkYH`*HQ?fU5U#LfNymML)DqS+TpBTAs>-P;cB?mc8h6e0 zJ9bKg>yndf0B=c{nUTYzO|8pX7ezhH$>~aRklLj@*O}QMwr>&Z{x2F5tcx3L6r4O5 wCo?j#Hl1tnF*vAyUgOOyhZCZU6D}|_Fbi_|WmGG01MO$ Date: Mon, 3 Nov 2025 14:52:05 +1100 Subject: [PATCH 0894/1331] Simplifying hand offset logic. --- code/_onclick/hud/hud_elements/hud_robot.dm | 1 - code/_onclick/hud/hud_types/_hud.dm | 34 ++++++--------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/code/_onclick/hud/hud_elements/hud_robot.dm b/code/_onclick/hud/hud_elements/hud_robot.dm index 687d673f3a27..9c37069baa3a 100644 --- a/code/_onclick/hud/hud_elements/hud_robot.dm +++ b/code/_onclick/hud/hud_elements/hud_robot.dm @@ -1,5 +1,4 @@ /datum/hud/robot - offset_hands_vertically = FALSE gun_mode_toggle_type = /obj/screen/gun/mode omit_hud_elements = list( /decl/hud_element/health, diff --git a/code/_onclick/hud/hud_types/_hud.dm b/code/_onclick/hud/hud_types/_hud.dm index c14dcf7c378e..9e65b258399d 100644 --- a/code/_onclick/hud/hud_types/_hud.dm +++ b/code/_onclick/hud/hud_types/_hud.dm @@ -99,8 +99,6 @@ VAR_PRIVATE/obj/screen/gun/item/gun_item_use_toggle VAR_PRIVATE/obj/screen/gun/radio/gun_radio_use_toggle - var/offset_hands_vertically = TRUE - /datum/hud/New(mob/_owner) if(istype(_owner)) owner = weakref(_owner) @@ -353,30 +351,18 @@ qdel(inv_box) // Rebuild offsets for the hand elements. + var/const/elems_per_row = 4 var/hand_y_offset = 21 var/list/elements = hud_elements_hands?.Copy() - if(length(elements)) - if(offset_hands_vertically) - while(length(elements)) - var/copy_index = min(length(elements), 2)+1 - var/list/sublist = elements.Copy(1, copy_index) - elements.Cut(1, copy_index) - var/obj/screen/inventory/inv_box - if(length(sublist) == 1) - inv_box = sublist[1] - inv_box.screen_loc = "CENTER,BOTTOM:[hand_y_offset]" - else - inv_box = sublist[1] - inv_box.screen_loc = "CENTER:-[world.icon_size/2],BOTTOM:[hand_y_offset]" - inv_box = sublist[2] - inv_box.screen_loc = "CENTER:[world.icon_size/2],BOTTOM:[hand_y_offset]" - hand_y_offset += world.icon_size - else - var/hand_x_offset = -((length(elements) * world.icon_size) / 2) + (world.icon_size/2) - for(var/obj/screen/inventory/inv_box in elements) - inv_box.screen_loc = "CENTER:[hand_x_offset],BOTTOM:[hand_y_offset]" - hand_x_offset += world.icon_size - hand_y_offset += world.icon_size + while(length(elements)) + var/copy_index = min(length(elements), elems_per_row)+1 + var/list/sublist = elements.Copy(1, copy_index) + elements.Cut(1, copy_index) + var/hand_x_offset = (world.icon_size/2) * (1 - length(sublist)) + for(var/obj/screen/inventory/inv_box in sublist) + inv_box.screen_loc = "CENTER:[hand_x_offset],BOTTOM:[hand_y_offset]" + hand_x_offset += world.icon_size + hand_y_offset += world.icon_size if(mymob.client && islist(hud_elements_hands) && length(hud_elements_hands)) mymob.client.screen |= hud_elements_hands From a0efe85d3eab3a30378f698a1657014e891d6341 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 19:05:09 +1100 Subject: [PATCH 0895/1331] /datum/composite_sound/var/volume -> play_volume --- code/datums/composite_sounds/_composite_sound.dm | 4 ++-- code/datums/composite_sounds/fire_sounds.dm | 4 ++-- code/datums/composite_sounds/loom.dm | 4 ++-- code/datums/composite_sounds/machinery_sounds.dm | 2 +- mods/content/supermatter/datums/sm_looping_sound.dm | 2 +- mods/content/supermatter/structures/supermatter_crystal.dm | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/code/datums/composite_sounds/_composite_sound.dm b/code/datums/composite_sounds/_composite_sound.dm index 8a9f563910f9..7a551b53e0fd 100644 --- a/code/datums/composite_sounds/_composite_sound.dm +++ b/code/datums/composite_sounds/_composite_sound.dm @@ -19,7 +19,7 @@ var/start_length var/end_sound var/chance - var/volume = 100 + var/play_volume = 100 var/max_loops var/direct var/timerid @@ -71,7 +71,7 @@ /datum/composite_sound/proc/play(soundfile) var/sound/S = sound(soundfile) for(var/atom/thing as anything in output_atoms) - playsound(thing, S, volume) + playsound(thing, S, play_volume) /datum/composite_sound/proc/get_sound(starttime, _mid_sounds) . = _mid_sounds || mid_sounds diff --git a/code/datums/composite_sounds/fire_sounds.dm b/code/datums/composite_sounds/fire_sounds.dm index 348962238046..ae6d8d714380 100644 --- a/code/datums/composite_sounds/fire_sounds.dm +++ b/code/datums/composite_sounds/fire_sounds.dm @@ -11,7 +11,7 @@ ) mid_length = 10 end_sound = 'sound/ambience/firecrackle06.ogg' - volume = 10 + play_volume = 10 /datum/composite_sound/grill start_sound = 'sound/machines/kitchen/grill/grill-start.ogg' @@ -19,4 +19,4 @@ mid_sounds = list('sound/machines/kitchen/grill/grill-mid1.ogg'=10) mid_length = 40 end_sound = 'sound/machines/kitchen/grill/grill-stop.ogg' - volume = 50 + play_volume = 50 diff --git a/code/datums/composite_sounds/loom.dm b/code/datums/composite_sounds/loom.dm index 4661196c2896..3f5c525c1049 100644 --- a/code/datums/composite_sounds/loom.dm +++ b/code/datums/composite_sounds/loom.dm @@ -9,7 +9,7 @@ 'sound/items/loom3.ogg' ) end_sound = 'sound/items/loomstop.ogg' - volume = 40 + play_volume = 40 // Spinning wheel sampled from 'Wooden Spinning Wheel' by Kessir on freesound.org: https://freesound.org/people/kessir/sounds/414554/ /datum/composite_sound/spinning_wheel_working @@ -22,4 +22,4 @@ 'sound/items/spinningwheel3.ogg' ) end_sound = 'sound/items/spinningwheelstop.ogg' - volume = 60 \ No newline at end of file + play_volume = 60 \ No newline at end of file diff --git a/code/datums/composite_sounds/machinery_sounds.dm b/code/datums/composite_sounds/machinery_sounds.dm index d8a261cac346..e9247a13cf32 100644 --- a/code/datums/composite_sounds/machinery_sounds.dm +++ b/code/datums/composite_sounds/machinery_sounds.dm @@ -4,4 +4,4 @@ mid_sounds = list('sound/machines/microwave/microwave-mid1.ogg'=10, 'sound/machines/microwave/microwave-mid2.ogg'=1) mid_length = 10 end_sound = 'sound/machines/microwave/microwave-end.ogg' - volume = 1 \ No newline at end of file + play_volume = 1 \ No newline at end of file diff --git a/mods/content/supermatter/datums/sm_looping_sound.dm b/mods/content/supermatter/datums/sm_looping_sound.dm index bfea4820cd3b..1cdcb1933531 100644 --- a/mods/content/supermatter/datums/sm_looping_sound.dm +++ b/mods/content/supermatter/datums/sm_looping_sound.dm @@ -1,4 +1,4 @@ /datum/composite_sound/supermatter mid_sounds = list('sound/machines/sm/loops/calm.ogg'=1) mid_length = 60 - volume = 40 \ No newline at end of file + play_volume = 40 \ No newline at end of file diff --git a/mods/content/supermatter/structures/supermatter_crystal.dm b/mods/content/supermatter/structures/supermatter_crystal.dm index 415212605f44..8bc697cd896d 100644 --- a/mods/content/supermatter/structures/supermatter_crystal.dm +++ b/mods/content/supermatter/structures/supermatter_crystal.dm @@ -454,7 +454,7 @@ var/global/list/supermatter_delam_accent_sounds = list( // (this is probably wrong since hydrogen heat cap is changed from phoron) // Capped to 20 volume since higher volumes get annoying and it sounds worse. // Formula previously was min(round(power/10)+1, 20) - soundloop.volume = clamp((50 + (power / 50)), 50, 100) + soundloop.play_volume = clamp((50 + (power / 50)), 50, 100) // Swap loops between calm and delamming. if(damage >= explosion_point * 0.25) From 2d3fd2f801962b5558b0da88147b8070d54e9641 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 19:08:43 +1100 Subject: [PATCH 0896/1331] /decl/music_track/var/volume -> music_volume --- code/datums/music_tracks/_music_track.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/datums/music_tracks/_music_track.dm b/code/datums/music_tracks/_music_track.dm index 290de89153a8..c1eba028164f 100644 --- a/code/datums/music_tracks/_music_track.dm +++ b/code/datums/music_tracks/_music_track.dm @@ -5,7 +5,7 @@ var/decl/license/license var/song var/url // Remember to include http:// or https:// or BYOND will be sad - var/volume = 70 + var/music_volume = 70 abstract_type = /decl/music_track /decl/music_track/Initialize() @@ -34,7 +34,7 @@ to_chat(listener, url) to_chat(listener, "License: [license.name]") - sound_to(listener, sound(song, repeat = 1, wait = 0, volume = volume, channel = sound_channels.lobby_channel)) + sound_to(listener, sound(song, repeat = 1, wait = 0, volume = music_volume, channel = sound_channels.lobby_channel)) // No VV editing anything about music tracks /decl/music_track/VV_static() From 1d38cb9eea2450ba0b8925bbf8808f0670a17d77 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 19:09:30 +1100 Subject: [PATCH 0897/1331] Jukebox and boombox volume -> music_volume --- code/game/machinery/jukebox.dm | 10 +++++----- code/game/objects/items/devices/boombox.dm | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 530e66903cbb..85a6ad930679 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -17,7 +17,7 @@ construct_state = /decl/machine_construction/default/panel_closed var/playing = 0 - var/volume = 20 + var/music_volume = 20 var/sound_id var/datum/sound_token/sound_token @@ -84,7 +84,7 @@ "current_track" = current_track != null ? current_track.title : "No track selected", "playing" = playing, "tracks" = juke_tracks, - "volume" = volume + "volume" = music_volume ) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -180,12 +180,12 @@ return // Jukeboxes cheat massively and actually don't share id. This is only done because it's music rather than ambient noise. - sound_token = play_looping_sound(src, sound_id, current_track.GetTrack(), volume = volume, range = 7, falloff = 3, prefer_mute = TRUE, preference = /datum/client_preference/play_game_music, streaming = TRUE) + sound_token = play_looping_sound(src, sound_id, current_track.GetTrack(), volume = music_volume, range = 7, falloff = 3, prefer_mute = TRUE, preference = /datum/client_preference/play_game_music, streaming = TRUE) playing = 1 update_use_power(POWER_USE_ACTIVE) /obj/machinery/media/jukebox/proc/AdjustVolume(var/new_volume) - volume = clamp(new_volume, 0, 50) + music_volume = clamp(new_volume, 0, 50) if(sound_token) - sound_token.SetVolume(volume) + sound_token.SetVolume(music_volume) diff --git a/code/game/objects/items/devices/boombox.dm b/code/game/objects/items/devices/boombox.dm index 01f0e716d581..0c3e1c273082 100644 --- a/code/game/objects/items/devices/boombox.dm +++ b/code/game/objects/items/devices/boombox.dm @@ -14,7 +14,7 @@ ) var/playing = 0 var/track_num = 1 - var/volume = 20 + var/music_volume = 20 var/max_volume = 40 var/frequency = 1 var/datum/sound_token/sound_token @@ -84,10 +84,10 @@ start() return TOPIC_HANDLED if(href_list["volup"]) - change_volume(volume + 10) + change_volume(music_volume + 10) return TOPIC_HANDLED if(href_list["voldown"]) - change_volume(volume - 10) + change_volume(music_volume - 10) return TOPIC_HANDLED /obj/item/boombox/attackby(var/obj/item/used_item, var/mob/user) @@ -180,7 +180,7 @@ /obj/item/boombox/proc/start() QDEL_NULL(sound_token) var/datum/track/T = tracks[track_num] - sound_token = play_looping_sound(src, sound_id, T.GetTrack(), volume = volume, frequency = frequency, range = 7, falloff = 4, prefer_mute = TRUE, preference = /datum/client_preference/play_game_music, streaming = TRUE) + sound_token = play_looping_sound(src, sound_id, T.GetTrack(), volume = music_volume, frequency = frequency, range = 7, falloff = 4, prefer_mute = TRUE, preference = /datum/client_preference/play_game_music, streaming = TRUE) playing = 1 update_icon() if(prob(break_chance)) @@ -193,9 +193,9 @@ stop() /obj/item/boombox/proc/change_volume(var/new_volume) - volume = clamp(new_volume, 0, max_volume) + music_volume = clamp(new_volume, 0, max_volume) if(sound_token) - sound_token.SetVolume(volume) + sound_token.SetVolume(music_volume) /obj/random_multi/single_item/boombox name = "boombox spawnpoint" From 1eca96a6f84642787010b297f977bd943d38c981 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 19:11:11 +1100 Subject: [PATCH 0898/1331] Atmospherics device/item volume -> air_volume --- code/game/machinery/alarm.dm | 2 +- code/game/machinery/atmoalter/canister.dm | 10 +++---- .../atmoalter/portable_atmospherics.dm | 6 ++-- code/game/machinery/atmoalter/pump.dm | 6 ++-- code/game/machinery/atmoalter/scrubber.dm | 6 ++-- code/game/machinery/cracker.dm | 2 +- code/game/objects/items/devices/oxycandle.dm | 14 ++++----- .../objects/items/devices/transfer_valve.dm | 6 ++-- code/game/objects/items/rescuebag.dm | 2 +- .../objects/items/weapons/tanks/tank_types.dm | 12 ++++---- .../game/objects/items/weapons/tanks/tanks.dm | 12 ++++---- code/modules/ZAS/Fire.dm | 12 ++++---- code/modules/ZAS/Zone.dm | 6 ++-- code/modules/admin/verbs/debug.dm | 2 +- code/modules/atmospherics/atmos_primitives.dm | 28 ++++++++--------- .../binary_devices/binary_atmos_base.dm | 4 +-- .../components/binary_devices/circulator.dm | 8 ++--- .../binary_devices/oxyregenerator.dm | 2 +- .../components/binary_devices/passive_gate.dm | 16 +++++----- .../components/binary_devices/pipeturbine.dm | 10 +++---- .../components/binary_devices/pump.dm | 10 +++---- .../components/omni_devices/_omni_extras.dm | 2 +- .../components/omni_devices/filter.dm | 10 +++---- .../components/omni_devices/mixer.dm | 8 ++--- .../trinary_devices/trinary_base.dm | 6 ++-- .../components/unary/cold_sink.dm | 2 +- .../components/unary/heat_source.dm | 2 +- .../components/unary/outlet_injector.dm | 6 ++-- .../atmospherics/components/unary/tank.dm | 6 ++-- .../components/unary/unary_base.dm | 4 +-- .../components/unary/vent_pump.dm | 8 ++--- .../components/unary/vent_scrubber.dm | 8 ++--- .../atmospherics/datum_pipe_network.dm | 6 ++-- code/modules/atmospherics/datum_pipeline.dm | 22 +++++++------- code/modules/atmospherics/he_pipes.dm | 2 +- code/modules/atmospherics/pipes.dm | 12 ++++---- code/modules/hydroponics/trays/tray.dm | 2 +- code/modules/mechs/components/body.dm | 6 ++-- code/modules/mob/living/human/life.dm | 2 +- code/modules/mob/living/living_breath.dm | 2 +- code/modules/multiz/pipes.dm | 2 +- code/modules/organs/internal/lungs.dm | 2 +- .../ships/device_types/gas_thruster.dm | 6 ++-- code/modules/power/stirling.dm | 8 ++--- .../projectiles/guns/launcher/pneumatic.dm | 2 +- code/modules/recycling/disposal.dm | 2 +- code/modules/scanners/gas.dm | 2 +- code/modules/xgm/xgm_gas_mixture.dm | 30 +++++++++---------- .../components/input.dm | 2 +- mods/species/ascent/items/rig.dm | 2 +- 50 files changed, 175 insertions(+), 175 deletions(-) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 4e52cbc8c9c1..884b99b45fd0 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -272,7 +272,7 @@ environment.merge(gas) /obj/machinery/alarm/proc/overall_danger_level(var/datum/gas_mixture/environment) - var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume + var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.air_volume var/environment_pressure = environment.return_pressure() var/other_moles = 0 diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index af152d8b5028..b5b96c98756a 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -9,7 +9,7 @@ construct_state = /decl/machine_construction/pipe/welder stat_immune = NOSCREEN | NOINPUT | NOPOWER start_pressure = 45 ATM - volume = 1000 + air_volume = 1000 interact_offline = TRUE matter = list( /decl/material/solid/metal/steel = 10 * SHEET_MATERIAL_AMOUNT @@ -195,7 +195,7 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) if((air_contents.temperature > 0) && (pressure_delta > 0)) var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) - transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.volume)*air_contents.total_moles) //flow rate limit + transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.air_volume)*air_contents.total_moles) //flow rate limit pump_gas_passive(src, air_contents, environment, transfer_moles) can_label = (air_contents?.return_pressure() < 1) @@ -207,13 +207,13 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) /obj/machinery/portable_atmospherics/canister/proc/return_temperature() var/datum/gas_mixture/GM = return_air() - if(GM && GM.volume>0) + if(GM && GM.air_volume>0) return GM.temperature return 0 /obj/machinery/portable_atmospherics/canister/proc/return_pressure() var/datum/gas_mixture/GM = return_air() - if(GM && GM.volume>0) + if(GM && GM.air_volume>0) return GM.return_pressure() return 0 @@ -241,7 +241,7 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) //Can not have a pressure delta that would cause environment pressure > tank pressure var/transfer_moles = 0 if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas + transfer_moles = pressure_delta*thejetpack.air_volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) thejetpack.merge(removed) to_chat(user, "You pulse-pressurize your jetpack from the tank.") diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 2958d2eab9ae..5ef82168e9d9 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -6,7 +6,7 @@ var/datum/gas_mixture/air_contents = new var/obj/item/tank/holding - var/volume = 0 + var/air_volume = 0 var/destroyed = 0 var/start_pressure = ONE_ATMOSPHERE @@ -19,7 +19,7 @@ /obj/machinery/portable_atmospherics/Initialize() ..() - air_contents.volume = volume + air_contents.air_volume = air_volume air_contents.temperature = T20C @@ -52,7 +52,7 @@ /decl/material/gas/nitrogen = N2STANDARD * MolesForPressure()) /obj/machinery/portable_atmospherics/proc/MolesForPressure(var/target_pressure = start_pressure) - return (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + return (target_pressure * air_contents.air_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) /obj/machinery/portable_atmospherics/on_update_icon() return null diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 43297c50a477..4febead3e39b 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -15,7 +15,7 @@ var/pressuremin = 0 var/pressuremax = 10 ATM - volume = 1000 + air_volume = 1000 power_rating = 7500 //7500 W ~ 10 HP power_losses = 150 @@ -76,11 +76,11 @@ var/air_temperature if(direction_out) pressure_delta = target_pressure - environment.return_pressure() - output_volume = environment.volume * environment.group_multiplier + output_volume = environment.air_volume * environment.group_multiplier air_temperature = environment.temperature? environment.temperature : air_contents.temperature else pressure_delta = environment.return_pressure() - target_pressure - output_volume = air_contents.volume * air_contents.group_multiplier + output_volume = air_contents.air_volume * air_contents.group_multiplier air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index bf3c3498dda1..0d93d05998fd 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -10,7 +10,7 @@ movable_flags = MOVABLE_FLAG_WHEELED var/volume_rate = 800 - volume = 750 + air_volume = 750 power_rating = 7500 //7500 W ~ 10 HP power_losses = 150 @@ -67,7 +67,7 @@ else environment = loc.return_air() - var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles + var/transfer_moles = min(1, volume_rate/environment.air_volume)*environment.total_moles power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) @@ -151,7 +151,7 @@ name = "huge air scrubber" icon_state = "scrubber:0" anchored = TRUE - volume = 50000 + air_volume = 50000 volume_rate = 5000 base_type = /obj/machinery/portable_atmospherics/powered/scrubber/huge diff --git a/code/game/machinery/cracker.dm b/code/game/machinery/cracker.dm index b3b31d1e54b0..a8287bd5038c 100644 --- a/code/game/machinery/cracker.dm +++ b/code/game/machinery/cracker.dm @@ -6,7 +6,7 @@ density = TRUE anchored = TRUE waterproof = TRUE - volume = 5000 + air_volume = 5000 use_power = POWER_USE_IDLE idle_power_usage = 100 active_power_usage = 10000 diff --git a/code/game/objects/items/devices/oxycandle.dm b/code/game/objects/items/devices/oxycandle.dm index c24ced497aef..bed530e8b26e 100644 --- a/code/game/objects/items/devices/oxycandle.dm +++ b/code/game/objects/items/devices/oxycandle.dm @@ -14,7 +14,7 @@ var/target_pressure = ONE_ATMOSPHERE var/datum/gas_mixture/air_contents = null - var/volume = 4600 + var/candle_volume = 4600 var/on = 0 var/activation_sound = 'sound/effects/flare.ogg' var/brightness_on = 1 // Moderate-low bright. @@ -33,10 +33,10 @@ update_icon() playsound(src.loc, activation_sound, 75, 1) air_contents = new /datum/gas_mixture() - air_contents.volume = 200 //liters + air_contents.air_volume = 200 //liters air_contents.temperature = T20C var/const/OXYGEN_FRACTION = 1 // separating out the constant so it's clearer why it exists and how to modify it later - air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) + air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.air_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) START_PROCESSING(SSprocessing, src) // Process of Oxygen candles releasing air. Makes 200 volume of oxygen @@ -44,7 +44,7 @@ if(!loc) return var/turf/pos = get_turf(src) - if(volume <= 0 || !pos || (pos.turf_flags & TURF_IS_WET)) //Now uses turf flags instead of whatever aurora did + if(candle_volume <= 0 || !pos || (pos.turf_flags & TURF_IS_WET)) //Now uses turf flags instead of whatever aurora did STOP_PROCESSING(SSprocessing, src) on = 2 update_icon() @@ -56,16 +56,16 @@ pos.hotspot_expose(1500, 5) var/datum/gas_mixture/environment = loc.return_air() var/pressure_delta = target_pressure - environment.return_pressure() - var/output_volume = environment.volume * environment.group_multiplier + var/output_volume = environment.air_volume * environment.group_multiplier var/air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) if (!removed) //Just in case return environment.merge(removed) - volume -= 200 + candle_volume -= 200 var/const/OXYGEN_FRACTION = 1 // separating out the constant so it's clearer why it exists and how to modify it later - air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) + air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.air_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) /obj/item/oxycandle/on_update_icon() . = ..() diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 40f7cc830eff..6d007fa27283 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -175,7 +175,7 @@ /obj/item/transfer_valve/proc/merge_gases() if(valve_open) return - tank_two.air_contents.volume += tank_one.air_contents.volume + tank_two.air_contents.air_volume += tank_one.air_contents.air_volume var/datum/gas_mixture/temp = tank_one.remove_air_ratio(1) tank_two.assume_air(temp) valve_open = 1 @@ -189,9 +189,9 @@ if(QDELETED(tank_one) || QDELETED(tank_two)) return - var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume + var/ratio1 = tank_one.air_contents.air_volume/tank_two.air_contents.air_volume var/datum/gas_mixture/temp = tank_two.remove_air_ratio(ratio1) - tank_two.air_contents.volume -= tank_one.air_contents.volume + tank_two.air_contents.air_volume -= tank_one.air_contents.air_volume tank_one.assume_air(temp) /* diff --git a/code/game/objects/items/rescuebag.dm b/code/game/objects/items/rescuebag.dm index df7b9225027f..565d538eef84 100644 --- a/code/game/objects/items/rescuebag.dm +++ b/code/game/objects/items/rescuebag.dm @@ -70,7 +70,7 @@ /obj/structure/closet/body_bag/rescue/Initialize() . = ..() atmo = new() - atmo.volume = 0.1*CELL_VOLUME + atmo.air_volume = 0.1*CELL_VOLUME START_PROCESSING(SSobj, src) /obj/structure/closet/body_bag/rescue/Destroy() diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 643125e14e14..2792f2dca8a2 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -16,7 +16,7 @@ icon = 'icons/obj/items/tanks/tank_blue.dmi' distribute_pressure = ONE_ATMOSPHERE*O2STANDARD starting_pressure = list(/decl/material/gas/oxygen = 6 ATM) - volume = 180 + air_volume = 180 /obj/item/tank/oxygen/yellow desc = "A tank of oxygen. This one is yellow." @@ -37,7 +37,7 @@ desc = "Mixed anyone?" icon = 'icons/obj/items/tanks/tank_blue.dmi' starting_pressure = list(/decl/material/gas/oxygen = (6 ATM) * O2STANDARD, /decl/material/gas/nitrogen = (6 ATM) * N2STANDARD) - volume = 180 + air_volume = 180 /* * Hydrogen @@ -69,7 +69,7 @@ attack_cooldown = DEFAULT_WEAPON_COOLDOWN melee_accuracy_bonus = -10 distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - volume = 40 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) + air_volume = 40 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) /obj/item/tank/emergency/oxygen name = "emergency oxygen tank" @@ -81,13 +81,13 @@ /obj/item/tank/emergency/oxygen/engi name = "extended-capacity emergency oxygen tank" icon = 'icons/obj/items/tanks/tank_emergency_engineer.dmi' - volume = 60 + air_volume = 60 /obj/item/tank/emergency/oxygen/double name = "double emergency oxygen tank" icon = 'icons/obj/items/tanks/tank_emergency_double.dmi' gauge_icon = "indicator_emergency_double" - volume = 90 + air_volume = 90 w_class = ITEM_SIZE_NORMAL /obj/item/tank/emergency/oxygen/double/red //firefighting tank, fits on belt, back or suitslot @@ -105,4 +105,4 @@ icon = 'icons/obj/items/tanks/tank_red.dmi' distribute_pressure = ONE_ATMOSPHERE*O2STANDARD starting_pressure = list(/decl/material/gas/nitrogen = 10 ATM) - volume = 180 \ No newline at end of file + air_volume = 180 \ No newline at end of file diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 55bedc9bca88..fa7a80942993 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -42,7 +42,7 @@ var/global/list/global/tank_gauge_cache = list() var/maxintegrity = 20 var/valve_welded = 0 var/obj/item/tankassemblyproxy/proxyassembly - var/volume = 70 + var/air_volume = 70 //Used by _onclick/hud/screen_objects.dm internals to determine if someone has messed with our tank or not. //If they have and we haven't scanned it with the PDA or gas analyzer then we might just breath whatever they put in it. var/manipulated_by = null @@ -56,9 +56,9 @@ var/global/list/global/tank_gauge_cache = list() proxyassembly = new /obj/item/tankassemblyproxy(src) proxyassembly.tank = src - air_contents = new /datum/gas_mixture(volume, T20C) + air_contents = new /datum/gas_mixture(air_volume, T20C) for(var/gas in starting_pressure) - air_contents.adjust_gas(gas, starting_pressure[gas]*volume/(R_IDEAL_GAS_EQUATION*T20C), 0) + air_contents.adjust_gas(gas, starting_pressure[gas]*air_volume/(R_IDEAL_GAS_EQUATION*T20C), 0) air_contents.update_values() START_PROCESSING(SSobj, src) @@ -366,7 +366,7 @@ var/global/list/global/tank_gauge_cache = list() var/datum/gas_mixture/removed = remove_air(distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) if(removed) - removed.volume = volume_to_return + removed.air_volume = volume_to_return return removed /obj/item/tank/Process() @@ -418,7 +418,7 @@ var/global/list/global/tank_gauge_cache = list() pressure = air_contents.return_pressure() var/strength = ((pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE) - var/mult = ((air_contents.volume/140)**(1/2)) * (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + var/mult = ((air_contents.air_volume/140)**(1/2)) * (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles //tanks appear to be experiencing a reduction on scale of about 0.64 total moles var/turf/T = get_turf(src) @@ -514,7 +514,7 @@ var/global/list/global/tank_gauge_cache = list() desc = initial(tank_copy.desc) icon = initial(tank_copy.icon) icon_state = initial(tank_copy.icon_state) - volume = initial(tank_copy.volume) + air_volume = initial(tank_copy.air_volume) // Set up explosive mix. air_contents.gas[DEFAULT_GAS_ACCELERANT] = 4 + rand(4) diff --git a/code/modules/ZAS/Fire.dm b/code/modules/ZAS/Fire.dm index b4b955ecf4e1..fe5c981c7890 100644 --- a/code/modules/ZAS/Fire.dm +++ b/code/modules/ZAS/Fire.dm @@ -112,15 +112,15 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin for(var/mob/living/L in loc) L.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the mobs! - loc.fire_act(air_contents, air_contents.temperature, air_contents.volume) + loc.fire_act(air_contents, air_contents.temperature, air_contents.air_volume) for(var/atom/A in loc) - A.fire_act(air_contents, air_contents.temperature, air_contents.volume) + A.fire_act(air_contents, air_contents.temperature, air_contents.air_volume) // prioritize nearby fuel overlays first for(var/direction in global.cardinal) var/turf/enemy_tile = get_step(my_tile, direction) if(istype(enemy_tile) && enemy_tile.reagents) - enemy_tile.hotspot_expose(air_contents.temperature, air_contents.volume) + enemy_tile.hotspot_expose(air_contents.temperature, air_contents.air_volume) //spread for(var/direction in global.cardinal) @@ -141,7 +141,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin enemy_tile.create_fire(firelevel) else - enemy_tile.adjacent_fire_act(loc, air_contents, air_contents.temperature, air_contents.volume) + enemy_tile.adjacent_fire_act(loc, air_contents, air_contents.temperature, air_contents.air_volume) animate(src, color = fire_color(air_contents.temperature), 5) set_light(l_color = color) @@ -209,8 +209,8 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin var/reaction_limit = min(total_oxidizers*(FIRE_REACTION_FUEL_AMOUNT/FIRE_REACTION_OXIDIZER_AMOUNT), total_fuel) //stoichiometric limit //vapour fuels are extremely volatile! The reaction progress is a percentage of the total fuel (similar to old zburn).) - var/firelevel = calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, volume*group_multiplier) / vsc.fire_firelevel_multiplier - var/min_burn = 0.30*volume*group_multiplier/CELL_VOLUME //in moles - so that fires with very small gas concentrations burn out fast + var/firelevel = calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, air_volume*group_multiplier) / vsc.fire_firelevel_multiplier + var/min_burn = 0.30*air_volume*group_multiplier/CELL_VOLUME //in moles - so that fires with very small gas concentrations burn out fast var/total_reaction_progress = min(max(min_burn, firelevel*total_fuel)*FIRE_GAS_BURNRATE_MULT, total_fuel) var/used_fuel = min(total_reaction_progress, reaction_limit) var/used_oxidizers = used_fuel*(FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT) diff --git a/code/modules/ZAS/Zone.dm b/code/modules/ZAS/Zone.dm index 8b3f477a586b..c62020538391 100644 --- a/code/modules/ZAS/Zone.dm +++ b/code/modules/ZAS/Zone.dm @@ -57,7 +57,7 @@ Class Procs: SSair.add_zone(src) air.temperature = TCMB air.group_multiplier = 1 - air.volume = CELL_VOLUME + air.air_volume = CELL_VOLUME /zone/proc/add(turf/T) #ifdef ZASDBG @@ -136,7 +136,7 @@ Class Procs: CHECK_TICK /zone/proc/add_tile_air(datum/gas_mixture/tile_air) - //air.volume += CELL_VOLUME + //air.air_volume += CELL_VOLUME air.group_multiplier = 1 air.multiply(contents.len) air.merge(tile_air) @@ -200,7 +200,7 @@ Class Procs: for(var/g in air.gas) var/decl/material/mat = GET_DECL(g) to_chat(M, "[capitalize(mat.gas_name)]: [air.gas[g]]") - to_chat(M, "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)") + to_chat(M, "P: [air.return_pressure()] kPa V: [air.air_volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)") to_chat(M, "O2 per N2: [(air.gas[/decl/material/gas/nitrogen] ? air.gas[/decl/material/gas/oxygen]/air.gas[/decl/material/gas/nitrogen] : "N/A")] Moles: [air.total_moles]") to_chat(M, "Simulated: [contents.len] ([air.group_multiplier])") to_chat(M, "Edges: [length(edges)]") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index ff76ca49fe9b..a76b2b3ef42c 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -17,7 +17,7 @@ t += "Temperature: [env.temperature]\n" t += "Pressure: [env.return_pressure()]kPa\n" for(var/g in env.gas) - t += "[g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.volume]kPa\n" + t += "[g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.air_volume]kPa\n" usr.show_message(t, 1) SSstatistics.add_field_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/atmospherics/atmos_primitives.dm b/code/modules/atmospherics/atmos_primitives.dm index 4d78c3a4553a..d5f55ac7c74f 100644 --- a/code/modules/atmospherics/atmos_primitives.dm +++ b/code/modules/atmospherics/atmos_primitives.dm @@ -45,7 +45,7 @@ //Update flow rate meter if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + A.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here if (A.debug) A.visible_message("[A]: source entropy: [round(source.specific_entropy(), 0.01)] J/Kmol --> sink entropy: [round(sink.specific_entropy(), 0.01)] J/Kmol") @@ -55,7 +55,7 @@ if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + P.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(transfer_moles) if (!removed) //Just in case @@ -86,13 +86,13 @@ //Update flow rate meter if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + A.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here if (A.debug) A.visible_message("[A]: moles transferred = [transfer_moles] mol") if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + P.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(transfer_moles) if(!removed) //Just in case @@ -148,10 +148,10 @@ //Update flow rate var if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here var/power_draw = 0 for (var/g in filtering) @@ -217,10 +217,10 @@ //Update flow rate var if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(total_transfer_moles) if (!removed) //Just in case @@ -290,10 +290,10 @@ //Update Flow Rate var if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here + P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(total_transfer_moles) if (!removed) //Just in case @@ -350,7 +350,7 @@ source_specific_power[source] = calculate_specific_power(source, sink)*mix_ratio/ATMOS_FILTER_EFFICIENCY total_specific_power += source_specific_power[source] - total_input_volume += source.volume + total_input_volume += source.air_volume total_input_moles += source.total_moles if (total_mixing_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing @@ -429,7 +429,7 @@ /proc/calculate_transfer_moles(datum/gas_mixture/source, datum/gas_mixture/sink, var/pressure_delta, var/sink_volume_mod=0) if(source.temperature == 0 || source.total_moles == 0) return 0 - var/output_volume = (sink.volume * sink.group_multiplier) + sink_volume_mod + var/output_volume = (sink.air_volume * sink.group_multiplier) + sink_volume_mod var/source_total_moles = source.total_moles * source.group_multiplier var/air_temperature = source.temperature @@ -448,8 +448,8 @@ if(source.temperature == 0) return 0 //Make the approximation that the sink temperature is unchanged after transferring gas - var/source_volume = source.volume * source.group_multiplier - var/sink_volume = sink.volume * sink.group_multiplier + var/source_volume = source.air_volume * source.group_multiplier + var/sink_volume = sink.air_volume * sink.group_multiplier var/source_pressure = source.return_pressure() var/sink_pressure = sink.return_pressure() diff --git a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm index bd58d0d112f0..a0323b68ecad 100644 --- a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm +++ b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm @@ -12,8 +12,8 @@ air1 = new air2 = new - air1.volume = 200 - air2.volume = 200 + air1.air_volume = 200 + air2.air_volume = 200 . = ..() /obj/machinery/atmospherics/binary/air_in_dir(direction) diff --git a/code/modules/atmospherics/components/binary_devices/circulator.dm b/code/modules/atmospherics/components/binary_devices/circulator.dm index 16f42c276e6f..58964df51c3d 100644 --- a/code/modules/atmospherics/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/components/binary_devices/circulator.dm @@ -31,7 +31,7 @@ /obj/machinery/atmospherics/binary/circulator/Initialize() . = ..() desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." - air1.volume = 400 + air1.air_volume = 400 /obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() var/datum/gas_mixture/removed @@ -45,11 +45,11 @@ if(air1.temperature > 0 && last_pressure_delta > 5) //Calculate necessary moles to transfer using PV = nRT - recent_moles_transferred = (last_pressure_delta*input.volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself - volume_capacity_used = min( (last_pressure_delta*input.volume/3)/(input_starting_pressure*air1.volume) , 1) //how much of the gas in the input air volume is consumed + recent_moles_transferred = (last_pressure_delta*input.air_volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself + volume_capacity_used = min( (last_pressure_delta*input.air_volume/3)/(input_starting_pressure*air1.air_volume) , 1) //how much of the gas in the input air volume is consumed //Calculate energy generated from kinetic turbine - stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * input.volume , input_starting_pressure*air1.volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency + stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * input.air_volume , input_starting_pressure*air1.air_volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency //Actually transfer the gas removed = air1.remove(recent_moles_transferred) diff --git a/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm b/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm index d38b7277bb12..0b505490c25e 100644 --- a/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm +++ b/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm @@ -90,7 +90,7 @@ var/pressure_delta = target_pressure - air2.return_pressure() if (pressure_delta > 0.01 && inner_tank.temperature > 0) var/datum/pipe_network/output = network_in_dir(dir) - var/transfer_moles = calculate_transfer_moles(inner_tank, air2, pressure_delta, output?.volume) + var/transfer_moles = calculate_transfer_moles(inner_tank, air2, pressure_delta, output?.air_volume) power_draw = pump_gas(src, inner_tank, air2, transfer_moles, power_rating*power_setting) if (power_draw >= 0) last_power_draw = power_draw diff --git a/code/modules/atmospherics/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/components/binary_devices/passive_gate.dm index caacc188ddab..7573cd540fb1 100644 --- a/code/modules/atmospherics/components/binary_devices/passive_gate.dm +++ b/code/modules/atmospherics/components/binary_devices/passive_gate.dm @@ -52,8 +52,8 @@ /obj/machinery/atmospherics/binary/passive_gate/Initialize() . = ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air1.air_volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air2.air_volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 /obj/machinery/atmospherics/binary/passive_gate/on_update_icon() icon_state = (unlocked && flowing)? "on" : "off" @@ -87,7 +87,7 @@ flowing = 1 //flow rate limit - var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles + var/transfer_moles = (set_flow_rate/air1.air_volume)*air1.total_moles //Figure out how much gas to transfer to meet the target pressure. switch (regulate_mode) @@ -95,7 +95,7 @@ transfer_moles = min(transfer_moles, air1.total_moles*(pressure_delta/input_starting_pressure)) if (REGULATE_OUTPUT) var/datum/pipe_network/output = network_in_dir(dir) - transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, output?.volume)) + transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, output?.air_volume)) //pump_gas() will return a negative number if no flow occurred returnval = pump_gas_passive(src, air1, air2, transfer_moles) @@ -174,11 +174,11 @@ set_flow_rate = 0 . = TOPIC_REFRESH if ("max") - set_flow_rate = air1.volume + set_flow_rate = air1.air_volume . = TOPIC_REFRESH if ("set") - var/new_flow_rate = input(user, "Enter new flow rate limit (0-[air1.volume]kPa)","Flow Rate Control",set_flow_rate) as num - set_flow_rate = clamp(new_flow_rate, 0, air1.volume) + var/new_flow_rate = input(user, "Enter new flow rate limit (0-[air1.air_volume]kPa)","Flow Rate Control",set_flow_rate) as num + set_flow_rate = clamp(new_flow_rate, 0, air1.air_volume) . = TOPIC_REFRESH /obj/machinery/atmospherics/binary/passive_gate/proc/toggle_unlocked() @@ -219,7 +219,7 @@ return machine.set_flow_rate /decl/public_access/public_variable/passive_gate_flow_rate/write_var(obj/machinery/atmospherics/binary/passive_gate/machine, new_value) - new_value = clamp(new_value, 0, machine.air1?.volume) + new_value = clamp(new_value, 0, machine.air1?.air_volume) . = ..() if(.) machine.set_flow_rate = new_value diff --git a/code/modules/atmospherics/components/binary_devices/pipeturbine.dm b/code/modules/atmospherics/components/binary_devices/pipeturbine.dm index 510fe486f1e5..53022013e628 100644 --- a/code/modules/atmospherics/components/binary_devices/pipeturbine.dm +++ b/code/modules/atmospherics/components/binary_devices/pipeturbine.dm @@ -21,9 +21,9 @@ /obj/machinery/atmospherics/pipeturbine/Initialize() . = ..() - air_in.volume = 200 - air_out.volume = 800 - volume_ratio = air_in.volume / (air_in.volume + air_out.volume) + air_in.air_volume = 200 + air_out.air_volume = 800 + volume_ratio = air_in.air_volume / (air_in.air_volume + air_out.air_volume) /obj/machinery/atmospherics/pipeturbine/get_initialize_directions() switch(dir) @@ -48,11 +48,11 @@ kin_energy *= 1 - kin_loss dP = max(air_in.return_pressure() - air_out.return_pressure(), 0) if(dP > 10) - kin_energy += 1/ADIABATIC_EXPONENT * dP * air_in.volume * (1 - volume_ratio**ADIABATIC_EXPONENT) * efficiency + kin_energy += 1/ADIABATIC_EXPONENT * dP * air_in.air_volume * (1 - volume_ratio**ADIABATIC_EXPONENT) * efficiency air_in.temperature *= volume_ratio**ADIABATIC_EXPONENT var/datum/gas_mixture/air_all = new - air_all.volume = air_in.volume + air_out.volume + air_all.air_volume = air_in.air_volume + air_out.air_volume air_all.merge(air_in.remove_ratio(1)) air_all.merge(air_out.remove_ratio(1)) diff --git a/code/modules/atmospherics/components/binary_devices/pump.dm b/code/modules/atmospherics/components/binary_devices/pump.dm index eeb70c699672..60fca50ad150 100644 --- a/code/modules/atmospherics/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/components/binary_devices/pump.dm @@ -5,9 +5,9 @@ node1, air1, network1 correspond to input node2, air2, network2 correspond to output Thus, the two variables affect pump operation are set in New(): - air1.volume + air1.air_volume This is the volume of gas available to the pump that may be transfered to the output - air2.volume + air2.air_volume Higher quantities of this cause more air to be perfected later but overall network volume is also increased as this increases... */ @@ -61,8 +61,8 @@ Thus, the two variables affect pump operation are set in New(): /obj/machinery/atmospherics/binary/pump/Initialize() . = ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + air1.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.air_volume = ATMOS_DEFAULT_VOLUME_PUMP /obj/machinery/atmospherics/binary/pump/on icon_state = "map_on" @@ -93,7 +93,7 @@ Thus, the two variables affect pump operation are set in New(): if(pressure_delta > 0.01 && air1.temperature > 0) //Figure out how much gas to transfer to meet the target pressure. var/datum/pipe_network/output = network_in_dir(dir) - var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, output?.volume) + var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, output?.air_volume) power_draw = pump_gas(src, air1, air2, transfer_moles, power_rating) if(transfer_moles > 0) diff --git a/code/modules/atmospherics/components/omni_devices/_omni_extras.dm b/code/modules/atmospherics/components/omni_devices/_omni_extras.dm index 1135f240029f..44357fdc75df 100644 --- a/code/modules/atmospherics/components/omni_devices/_omni_extras.dm +++ b/code/modules/atmospherics/components/omni_devices/_omni_extras.dm @@ -30,7 +30,7 @@ if(istype(M)) master = M air = new - air.volume = 200 + air.air_volume = 200 /datum/omni_port/Destroy() QDEL_NULL(network) diff --git a/code/modules/atmospherics/components/omni_devices/filter.dm b/code/modules/atmospherics/components/omni_devices/filter.dm index 1c140c763a77..198f93531b6d 100644 --- a/code/modules/atmospherics/components/omni_devices/filter.dm +++ b/code/modules/atmospherics/components/omni_devices/filter.dm @@ -39,7 +39,7 @@ rebuild_filtering_list() for(var/datum/omni_port/P in ports) - P.air.volume = ATMOS_DEFAULT_VOLUME_FILTER + P.air.air_volume = ATMOS_DEFAULT_VOLUME_FILTER /obj/machinery/atmospherics/omni/filter/Destroy() input = null @@ -57,7 +57,7 @@ if(P in gas_filters) gas_filters -= P - P.air.volume = ATMOS_DEFAULT_VOLUME_FILTER + P.air.air_volume = ATMOS_DEFAULT_VOLUME_FILTER switch(P.mode) if(ATM_INPUT) input = P @@ -82,13 +82,13 @@ var/datum/gas_mixture/input_air = input.air // it's completely happy with them if they're in a loop though i.e. "P.air.return_pressure()"... *shrug* var/delta = clamp(0, (output_air ? (max_output_pressure - output_air.return_pressure()) : 0), max_output_pressure) - var/transfer_moles_max = calculate_transfer_moles(input_air, output_air, delta, (output && output.network && output.network.volume) ? output.network.volume : 0) + var/transfer_moles_max = calculate_transfer_moles(input_air, output_air, delta, (output && output.network && output.network.air_volume) ? output.network.air_volume : 0) for(var/datum/omni_port/filter_output in gas_filters) delta = clamp(0, (filter_output.air ? (max_output_pressure - filter_output.air.return_pressure()) : 0), max_output_pressure) - transfer_moles_max = min(transfer_moles_max, (calculate_transfer_moles(input_air, filter_output.air, delta, (filter_output && filter_output.network && filter_output.network.volume) ? filter_output.network.volume : 0))) + transfer_moles_max = min(transfer_moles_max, (calculate_transfer_moles(input_air, filter_output.air, delta, (filter_output && filter_output.network && filter_output.network.air_volume) ? filter_output.network.air_volume : 0))) //Figure out the amount of moles to transfer - var/transfer_moles = clamp(0, ((set_flow_rate/input_air.volume)*input_air.total_moles), transfer_moles_max) + var/transfer_moles = clamp(0, ((set_flow_rate/input_air.air_volume)*input_air.total_moles), transfer_moles_max) var/power_draw = -1 if (transfer_moles > MINIMUM_MOLES_TO_FILTER) diff --git a/code/modules/atmospherics/components/omni_devices/mixer.dm b/code/modules/atmospherics/components/omni_devices/mixer.dm index 483e114a9f60..053803779eb4 100644 --- a/code/modules/atmospherics/components/omni_devices/mixer.dm +++ b/code/modules/atmospherics/components/omni_devices/mixer.dm @@ -54,7 +54,7 @@ con += max(0, tag_west_con) for(var/datum/omni_port/P in ports) - P.air.volume = ATMOS_DEFAULT_VOLUME_MIXER + P.air.air_volume = ATMOS_DEFAULT_VOLUME_MIXER /obj/machinery/atmospherics/omni/mixer/Destroy() inputs.Cut() @@ -80,7 +80,7 @@ P.concentration = 1 / max(1, inputs.len) if(output) - output.air.volume = ATMOS_DEFAULT_VOLUME_MIXER * 0.75 * inputs.len + output.air.air_volume = ATMOS_DEFAULT_VOLUME_MIXER * 0.75 * inputs.len output.concentration = 1 rebuild_mixing_inputs() @@ -117,8 +117,8 @@ for (var/datum/omni_port/P in inputs) if(!P.concentration) continue - transfer_moles += (set_flow_rate*P.concentration/P.air.volume)*P.air.total_moles - transfer_moles_max = min(transfer_moles_max, calculate_transfer_moles(P.air, output.air, delta, (output && output.network && output.network.volume) ? output.network.volume : 0)) + transfer_moles += (set_flow_rate*P.concentration/P.air.air_volume)*P.air.total_moles + transfer_moles_max = min(transfer_moles_max, calculate_transfer_moles(P.air, output.air, delta, (output && output.network && output.network.air_volume) ? output.network.air_volume : 0)) transfer_moles = clamp(0, transfer_moles, transfer_moles_max) var/power_draw = -1 diff --git a/code/modules/atmospherics/components/trinary_devices/trinary_base.dm b/code/modules/atmospherics/components/trinary_devices/trinary_base.dm index 38a0a30f0116..d4e72576e3dd 100644 --- a/code/modules/atmospherics/components/trinary_devices/trinary_base.dm +++ b/code/modules/atmospherics/components/trinary_devices/trinary_base.dm @@ -15,9 +15,9 @@ air2 = new air3 = new - air1.volume = 200 - air2.volume = 200 - air3.volume = 200 + air1.air_volume = 200 + air2.air_volume = 200 + air3.air_volume = 200 . = ..() /obj/machinery/atmospherics/trinary/air_in_dir(direction) diff --git a/code/modules/atmospherics/components/unary/cold_sink.dm b/code/modules/atmospherics/components/unary/cold_sink.dm index f65afc65962f..802b7c8377d3 100644 --- a/code/modules/atmospherics/components/unary/cold_sink.dm +++ b/code/modules/atmospherics/components/unary/cold_sink.dm @@ -123,7 +123,7 @@ power_rating = initial(power_rating) * cap_rating / 2 //more powerful heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient - air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + air_contents.air_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating set_power_level(power_setting) /obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting) diff --git a/code/modules/atmospherics/components/unary/heat_source.dm b/code/modules/atmospherics/components/unary/heat_source.dm index cb7df0f6098a..8b0a103476e5 100644 --- a/code/modules/atmospherics/components/unary/heat_source.dm +++ b/code/modules/atmospherics/components/unary/heat_source.dm @@ -110,7 +110,7 @@ max_power_rating = initial(max_power_rating) * cap_rating / 2 max_temperature = max(initial(max_temperature) - T20C, 0) * ((bin_rating * 4 + cap_rating) / 5) + T20C - air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + air_contents.air_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating set_power_level(power_setting) /obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting) diff --git a/code/modules/atmospherics/components/unary/outlet_injector.dm b/code/modules/atmospherics/components/unary/outlet_injector.dm index 1ed273483573..83ad2a6548cf 100644 --- a/code/modules/atmospherics/components/unary/outlet_injector.dm +++ b/code/modules/atmospherics/components/unary/outlet_injector.dm @@ -56,7 +56,7 @@ /obj/machinery/atmospherics/unary/outlet_injector/Initialize() . = ..() //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 + air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 /obj/machinery/atmospherics/unary/outlet_injector/on_update_icon() if(stat & NOPOWER) @@ -94,7 +94,7 @@ var/datum/gas_mixture/environment = loc.return_air() if(environment && air_contents.temperature > 0) - var/transfer_moles = (volume_rate/air_contents.volume)*air_contents.total_moles //apply flow rate limit + var/transfer_moles = (volume_rate/air_contents.air_volume)*air_contents.total_moles //apply flow rate limit power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) if(transfer_moles > 0) update_networks() @@ -147,7 +147,7 @@ return machine.volume_rate /decl/public_access/public_variable/volume_rate/write_var(obj/machinery/atmospherics/unary/outlet_injector/machine, new_value) - new_value = clamp(new_value, 0, machine.air_contents.volume) + new_value = clamp(new_value, 0, machine.air_contents.air_volume) . = ..() if(.) machine.volume_rate = new_value diff --git a/code/modules/atmospherics/components/unary/tank.dm b/code/modules/atmospherics/components/unary/tank.dm index 52c6c6fc0278..ad1672b32934 100644 --- a/code/modules/atmospherics/components/unary/tank.dm +++ b/code/modules/atmospherics/components/unary/tank.dm @@ -5,7 +5,7 @@ name = "Pressure Tank" desc = "A large vessel containing pressurized gas." - var/volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet + var/air_volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet var/start_pressure = 25 ATM var/filling // list of gas ratios to use. @@ -25,12 +25,12 @@ /obj/machinery/atmospherics/unary/tank/Initialize() . = ..() - air_contents.volume = volume + air_contents.air_volume = air_volume air_contents.temperature = T20C if(filling) for(var/gas in filling) - air_contents.adjust_gas(gas, start_pressure * filling[gas] * (air_contents.volume)/(R_IDEAL_GAS_EQUATION*air_contents.temperature), FALSE) + air_contents.adjust_gas(gas, start_pressure * filling[gas] * (air_contents.air_volume)/(R_IDEAL_GAS_EQUATION*air_contents.temperature), FALSE) air_contents.update_values() update_icon() diff --git a/code/modules/atmospherics/components/unary/unary_base.dm b/code/modules/atmospherics/components/unary/unary_base.dm index 290cd80db5be..308bb3294a93 100644 --- a/code/modules/atmospherics/components/unary/unary_base.dm +++ b/code/modules/atmospherics/components/unary/unary_base.dm @@ -19,7 +19,7 @@ /obj/machinery/atmospherics/unary/Initialize() air_contents = new - air_contents.volume = 200 + air_contents.air_volume = 200 if(controlled) reset_area(null, get_area(src)) . = ..() @@ -34,7 +34,7 @@ /obj/machinery/atmospherics/unary/physically_destroyed() if(loc && air_contents) loc.assume_air(air_contents) - . = ..() + . = ..() /obj/machinery/atmospherics/unary/dismantle() if(loc && air_contents) diff --git a/code/modules/atmospherics/components/unary/vent_pump.dm b/code/modules/atmospherics/components/unary/vent_pump.dm index c463a0687ce1..b50640cd3cf5 100644 --- a/code/modules/atmospherics/components/unary/vent_pump.dm +++ b/code/modules/atmospherics/components/unary/vent_pump.dm @@ -85,7 +85,7 @@ update_name() events_repository.register(/decl/observ/name_set, A, src, PROC_REF(change_area_name)) . = ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP update_sound() /obj/machinery/atmospherics/unary/vent_pump/proc/change_area_name(var/area/A, var/old_area_name, var/new_area_name) @@ -170,7 +170,7 @@ /obj/machinery/atmospherics/unary/vent_pump/high_volume/Initialize() . = ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 /obj/machinery/atmospherics/unary/vent_pump/on_update_icon() var/visible_directions = build_device_underlays() @@ -223,7 +223,7 @@ power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) else //external -> internal var/datum/pipe_network/network = network_in_dir(dir) - transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, network?.volume) / environment.group_multiplier // limit it to just one turf's worth of gas per tick + transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, network?.air_volume) / environment.group_multiplier // limit it to just one turf's worth of gas per tick power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) else @@ -562,7 +562,7 @@ /obj/machinery/atmospherics/unary/vent_pump/engine/Initialize() . = ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector + air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector /obj/machinery/atmospherics/unary/vent_pump/power_change() . = ..() diff --git a/code/modules/atmospherics/components/unary/vent_scrubber.dm b/code/modules/atmospherics/components/unary/vent_scrubber.dm index 9ab3bc3e4976..437ff5b505c7 100644 --- a/code/modules/atmospherics/components/unary/vent_scrubber.dm +++ b/code/modules/atmospherics/components/unary/vent_scrubber.dm @@ -68,7 +68,7 @@ if(g != /decl/material/gas/oxygen && g != /decl/material/gas/nitrogen) scrubbing_gas += g . = ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER + air_contents.air_volume = ATMOS_DEFAULT_VOLUME_FILTER /obj/machinery/atmospherics/unary/vent_scrubber/reset_area(area/old_area, area/new_area) if(!controlled) @@ -151,17 +151,17 @@ var/transfer_moles = 0 if(scrubbing == SCRUBBER_SIPHON) //Just siphon all air //limit flow rate from turfs - transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.volume) //group_multiplier gets divided out here + transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.air_volume) //group_multiplier gets divided out here power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) else //limit flow rate from turfs - transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.volume) //group_multiplier gets divided out here + transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.air_volume) //group_multiplier gets divided out here power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) if(scrubbing != SCRUBBER_SIPHON && power_draw <= 0) //99% of all scrubbers //Fucking hibernate because you ain't doing shit. hibernate = world.time + (rand(100,200)) else if(scrubbing == SCRUBBER_EXCHANGE) // after sleep check so it only does an exchange if there are bad gasses that have been scrubbed - transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.volume) + transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.air_volume) power_draw += pump_gas(src, environment, air_contents, transfer_moles / 4, power_rating) if (power_draw >= 0) diff --git a/code/modules/atmospherics/datum_pipe_network.dm b/code/modules/atmospherics/datum_pipe_network.dm index 37d8d1be7687..72e5c22cbe1f 100644 --- a/code/modules/atmospherics/datum_pipe_network.dm +++ b/code/modules/atmospherics/datum_pipe_network.dm @@ -1,6 +1,6 @@ /datum/pipe_network var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network - var/volume = 0 //caches the total volume for atmos machines to use in gas calculations + var/air_volume = 0 //caches the total volume for atmos machines to use in gas calculations var/list/obj/machinery/atmospherics/normal_members = list() var/list/datum/pipeline/line_members = list() @@ -71,7 +71,7 @@ //Go through membership roster and make sure gases is up to date gases = list() - volume = 0 + air_volume = 0 for(var/obj/machinery/atmospherics/normal_member in normal_members) var/result = normal_member.return_network_air(src) @@ -81,4 +81,4 @@ gases += line_member.air for(var/datum/gas_mixture/air in gases) - volume += air.volume + air_volume += air.air_volume diff --git a/code/modules/atmospherics/datum_pipeline.dm b/code/modules/atmospherics/datum_pipeline.dm index 7ece1fe43700..6465b8dbb63d 100644 --- a/code/modules/atmospherics/datum_pipeline.dm +++ b/code/modules/atmospherics/datum_pipeline.dm @@ -30,7 +30,7 @@ STOP_PROCESSING(SSprocessing, src) QDEL_NULL(network) - if(air?.volume || liquid?.total_volume) + if(air?.air_volume || liquid?.total_volume) temporarily_store_fluids() QDEL_NULL(air) @@ -62,15 +62,15 @@ //Update individual gas_mixtures by volume ratio var/liquid_transfer_per_pipe = min(REAGENT_UNITS_PER_PIPE, (liquid && length(members)) ? (liquid.total_volume / length(members)) : 0) - if(!air?.volume && !liquid_transfer_per_pipe) + if(!air?.air_volume && !liquid_transfer_per_pipe) return for(var/obj/machinery/atmospherics/pipe/member in members) - if(air?.volume) + if(air?.air_volume) member.air_temporary = new member.air_temporary.copy_from(air) - member.air_temporary.volume = member.volume - member.air_temporary.multiply(member.volume / air.volume) + member.air_temporary.air_volume = member.air_volume + member.air_temporary.multiply(member.air_volume / air.air_volume) if(liquid_transfer_per_pipe) member.liquid_temporary = new(REAGENT_UNITS_PER_PIPE, member) @@ -81,7 +81,7 @@ members = list(base) edges = list() - var/volume = base.volume + var/temp_volume = base.air_volume base.parent = src maximum_pressure = base.maximum_pressure @@ -110,7 +110,7 @@ members += item possible_expansions += item - volume += item.volume + temp_volume += item.air_volume item.parent = src maximum_pressure = min(maximum_pressure, item.maximum_pressure) @@ -134,7 +134,7 @@ possible_expansions -= borderline - air.volume = volume + air.air_volume = temp_volume liquid.maximum_volume = length(members) * REAGENT_UNITS_PER_PIPE /datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) @@ -194,11 +194,11 @@ /datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - if(air.volume <= 0) // Avoid div by zero. + if(air.air_volume <= 0) // Avoid div by zero. return var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.air_volume) var/datum/gas_mixture/target_air = target.return_air() if(total_heat_capacity <= 0) // Avoid div by zero. @@ -225,7 +225,7 @@ //surface must be the surface area in m^2 /datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) - var/gas_density = air.total_moles/air.volume + var/gas_density = air.total_moles/air.air_volume thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio var/heat_gain = get_thermal_radiation(air.temperature, surface, RADIATOR_EXPOSED_SURFACE_AREA_RATIO, thermal_conductivity) diff --git a/code/modules/atmospherics/he_pipes.dm b/code/modules/atmospherics/he_pipes.dm index a82bfab40318..0479a7827ccd 100644 --- a/code/modules/atmospherics/he_pipes.dm +++ b/code/modules/atmospherics/he_pipes.dm @@ -73,7 +73,7 @@ var/datum/gas_mixture/environment = turf.return_air() environment_temperature = environment?.temperature || 0 if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) - parent.temperature_interact(turf, volume, thermal_conductivity) + parent.temperature_interact(turf, air_volume, thermal_conductivity) // Burn mobs buckled to this pipe. if(buckled_mob) diff --git a/code/modules/atmospherics/pipes.dm b/code/modules/atmospherics/pipes.dm index f054a258a916..7775e036c593 100644 --- a/code/modules/atmospherics/pipes.dm +++ b/code/modules/atmospherics/pipes.dm @@ -24,7 +24,7 @@ var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke var/datum/reagents/liquid_temporary // used when reconstructing a pipeline that broke var/datum/pipeline/parent - var/volume = 0 + var/air_volume = 0 var/leaking = 0 // Do not set directly, use set_leaking(TRUE/FALSE) //minimum pressure before check_pressure(...) should be called @@ -210,7 +210,7 @@ update_sound(0) . = PROCESS_KILL else if(leaking) - parent.mingle_with_turf(loc, volume) + parent.mingle_with_turf(loc, air_volume) var/air = parent.air?.return_pressure() if(!sound_token && air) update_sound(1) @@ -225,7 +225,7 @@ name = "pipe" desc = "A one-meter section of regular pipe." - volume = ATMOS_DEFAULT_VOLUME_PIPE + air_volume = ATMOS_DEFAULT_VOLUME_PIPE dir = SOUTH initialize_directions = SOUTH|NORTH @@ -380,7 +380,7 @@ icon_state = "map" name = "pipe manifold" desc = "A manifold composed of regular pipes." - volume = ATMOS_DEFAULT_VOLUME_PIPE * 1.5 + air_volume = ATMOS_DEFAULT_VOLUME_PIPE * 1.5 dir = SOUTH initialize_directions = EAST|NORTH|WEST @@ -509,7 +509,7 @@ icon_state = "" name = "4-way pipe manifold" desc = "A manifold composed of regular pipes." - volume = ATMOS_DEFAULT_VOLUME_PIPE * 2 + air_volume = ATMOS_DEFAULT_VOLUME_PIPE * 2 dir = SOUTH initialize_directions = NORTH|SOUTH|EAST|WEST @@ -634,7 +634,7 @@ icon = 'icons/atmos/pipes.dmi' icon_state = "cap" level = LEVEL_ABOVE_PLATING - volume = 35 + air_volume = 35 pipe_class = PIPE_CLASS_UNARY dir = SOUTH diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index f3e74bd61470..5108dff2b679 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -5,7 +5,7 @@ icon_state = "hydrotray3" density = TRUE anchored = TRUE - volume = 100 + air_volume = 100 construct_state = /decl/machine_construction/default/panel_closed uncreated_component_parts = null stat_immune = 0 diff --git a/code/modules/mechs/components/body.dm b/code/modules/mechs/components/body.dm index 16fe9e6f62f0..7ec1cf569712 100644 --- a/code/modules/mechs/components/body.dm +++ b/code/modules/mechs/components/body.dm @@ -52,13 +52,13 @@ ) if(pilot_coverage >= 100) //Open cockpits dont get to have air cockpit = new - cockpit.volume = 200 + cockpit.air_volume = 200 if(loc) var/datum/gas_mixture/air = loc.return_air() if(air) //Essentially at this point its like we created a vacuum, but realistically making a bottle doesnt actually increase volume of a room and neither should a mech for(var/g in air.gas) - cockpit.gas[g] = (air.gas[g] / air.volume) * cockpit.volume + cockpit.gas[g] = (air.gas[g] / air.air_volume) * cockpit.air_volume cockpit.temperature = air.temperature cockpit.update_values() @@ -107,7 +107,7 @@ if(pressure_delta > 0) if(air_supply.air_contents.temperature > 0) var/transfer_moles = calculate_transfer_moles(air_supply.air_contents, cockpit, pressure_delta) - transfer_moles = min(transfer_moles, (air_supply.release_flow_rate/air_supply.air_contents.volume)*air_supply.air_contents.total_moles) + transfer_moles = min(transfer_moles, (air_supply.release_flow_rate/air_supply.air_contents.air_volume)*air_supply.air_contents.total_moles) pump_gas_passive(air_supply, air_supply.air_contents, cockpit, transfer_moles) changed = TRUE else if(pressure_delta < 0) //Release overpressure. diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm index b6b9c2961886..5550afd919ab 100644 --- a/code/modules/mob/living/human/life.dm +++ b/code/modules/mob/living/human/life.dm @@ -173,7 +173,7 @@ var/temperature_gain = heat_gain/HUMAN_HEAT_CAPACITY bodytemperature += temperature_gain //temperature_gain will often be negative - var/relative_density = (environment.total_moles/environment.volume) / (MOLES_CELLSTANDARD/CELL_VOLUME) + var/relative_density = (environment.total_moles/environment.air_volume) / (MOLES_CELLSTANDARD/CELL_VOLUME) if(relative_density > 0.02) //don't bother if we are in vacuum or near-vacuum var/loc_temp = environment.temperature diff --git a/code/modules/mob/living/living_breath.dm b/code/modules/mob/living/living_breath.dm index 69241dac54ae..de595f991f41 100644 --- a/code/modules/mob/living/living_breath.dm +++ b/code/modules/mob/living/living_breath.dm @@ -81,7 +81,7 @@ if(!can_breathe_air_above) breath = new if(!can_drown()) - breath.volume = volume_needed + breath.air_volume = volume_needed breath.temperature = my_turf.temperature // TODO: species-breathable gas instead of oxygen default. Maybe base it on the reagents being breathed breath.adjust_gas(/decl/material/gas/oxygen, ONE_ATMOSPHERE*volume_needed/(R_IDEAL_GAS_EQUATION*T20C)) diff --git a/code/modules/multiz/pipes.dm b/code/modules/multiz/pipes.dm index 8ed74828c58e..414fd5ff0b6d 100644 --- a/code/modules/multiz/pipes.dm +++ b/code/modules/multiz/pipes.dm @@ -8,7 +8,7 @@ name = "upwards pipe" desc = "A pipe segment to connect upwards." - volume = 70 + air_volume = 70 dir = SOUTH initialize_directions = SOUTH diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 003f7b851d15..199a8b4e8e4b 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -307,7 +307,7 @@ else temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed - var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * breath.volume/CELL_VOLUME) + var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * breath.air_volume/CELL_VOLUME) temp_adj *= relative_density if (temp_adj > BODYTEMP_HEATING_MAX) temp_adj = BODYTEMP_HEATING_MAX diff --git a/code/modules/overmap/ships/device_types/gas_thruster.dm b/code/modules/overmap/ships/device_types/gas_thruster.dm index b2fcd8711772..1656acd207d2 100644 --- a/code/modules/overmap/ships/device_types/gas_thruster.dm +++ b/code/modules/overmap/ships/device_types/gas_thruster.dm @@ -29,10 +29,10 @@ /datum/extension/ship_engine/gas/proc/get_propellant(var/sample_only = TRUE, var/partial = 1) var/obj/machinery/atmospherics/unary/engine/E = holder - if(istype(E) && E.air_contents?.volume > 0) - var/datum/gas_mixture/removed = E.air_contents.remove_ratio((volume_per_burn * thrust_limit * partial) / E.air_contents.volume) + if(istype(E) && E.air_contents?.air_volume > 0) + var/datum/gas_mixture/removed = E.air_contents.remove_ratio((volume_per_burn * thrust_limit * partial) / E.air_contents.air_volume) if(removed && sample_only) - var/datum/gas_mixture/sample = new(removed.volume) + var/datum/gas_mixture/sample = new(removed.air_volume) sample.copy_from(removed) E.air_contents.merge(removed) return sample diff --git a/code/modules/power/stirling.dm b/code/modules/power/stirling.dm index ea7b725c4cb1..0903296bdddb 100644 --- a/code/modules/power/stirling.dm +++ b/code/modules/power/stirling.dm @@ -196,10 +196,10 @@ if(!sound_id) sound_id = "[type]_[sequential_id(/obj/machinery/atmospherics/binary/stirling)]" if(active) - var/volume = 10 + 15*genlev + var/work_volume = 10 + 15*genlev if(!sound_token) - sound_token = play_looping_sound(src, sound_id, 'sound/machines/engine.ogg', volume = volume) - sound_token.SetVolume(volume) + sound_token = play_looping_sound(src, sound_id, 'sound/machines/engine.ogg', volume = work_volume) + sound_token.SetVolume(work_volume) else if(sound_token) QDEL_NULL(sound_token) @@ -225,7 +225,7 @@ slot_flags = null starting_pressure = list(/decl/material/gas/hydrogen = 2 ATM) - volume = 30 + air_volume = 30 failure_temp = 1000 /obj/item/tank/stirling/Initialize() diff --git a/code/modules/projectiles/guns/launcher/pneumatic.dm b/code/modules/projectiles/guns/launcher/pneumatic.dm index 5e1dcb93e82d..331322b76cfd 100644 --- a/code/modules/projectiles/guns/launcher/pneumatic.dm +++ b/code/modules/projectiles/guns/launcher/pneumatic.dm @@ -111,7 +111,7 @@ /obj/item/gun/launcher/pneumatic/update_release_force(obj/item/projectile) if(tank) - release_force = ((fire_pressure*tank.volume)/projectile.w_class)/force_divisor //projectile speed. + release_force = ((fire_pressure*tank.air_volume)/projectile.w_class)/force_divisor //projectile speed. if(release_force > 80) release_force = 80 //damage cap. else release_force = 0 diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index efcccf407aac..7f728007f023 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -364,7 +364,7 @@ var/global/list/diversion_junctions = list() var/power_draw = -1 if(env && env.temperature > 0) - var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here + var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.air_volume)*env.total_moles //group_multiplier is divided out here power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage) if (power_draw > 0) diff --git a/code/modules/scanners/gas.dm b/code/modules/scanners/gas.dm index b4bc1705cf26..5ad8c4bc9f02 100644 --- a/code/modules/scanners/gas.dm +++ b/code/modules/scanners/gas.dm @@ -79,7 +79,7 @@ . += "[capitalize(mat.gas_name)]: [percentage]%[perGas_add_string]" var/totalGas_add_string = "" if(mode == MV_MODE) - totalGas_add_string = ", Total moles: [round(mixture.total_moles, 0.01)], Volume: [mixture.volume]L" + totalGas_add_string = ", Total moles: [round(mixture.total_moles, 0.01)], Volume: [mixture.air_volume]L" . += "Temperature: [round(mixture.temperature-T0C)]°C / [round(mixture.temperature)]K[totalGas_add_string]" return diff --git a/code/modules/xgm/xgm_gas_mixture.dm b/code/modules/xgm/xgm_gas_mixture.dm index 823c729e3b1f..0c947ca8ba2a 100644 --- a/code/modules/xgm/xgm_gas_mixture.dm +++ b/code/modules/xgm/xgm_gas_mixture.dm @@ -8,7 +8,7 @@ //Sum of all the gas moles in this mix. Updated by update_values() var/total_moles = 0 //Volume of this mix. - var/volume = CELL_VOLUME + var/air_volume = CELL_VOLUME //Size of the group this gas_mixture is representing. 1 for singletons. var/group_multiplier = 1 @@ -21,14 +21,14 @@ /datum/gas_mixture/New(_volume, _temperature, _group_multiplier) if(!isnull(_volume)) - volume = _volume + air_volume = _volume if(!isnull(_temperature)) temperature = _temperature if(!isnull(_group_multiplier)) group_multiplier = _group_multiplier //Since we may have values defined on creation, update everything. - if(volume && length(gas)) + if(air_volume && length(gas)) update_values() /datum/gas_mixture/proc/get_gas(gasid) @@ -107,9 +107,9 @@ for(var/g in gas|sharer.gas) var/comb = gas[g] + sharer.gas[g] - comb /= volume + sharer.volume - gas[g] = comb * volume - sharer.gas[g] = comb * sharer.volume + comb /= air_volume + sharer.air_volume + gas[g] = comb * air_volume + sharer.gas[g] = comb * sharer.air_volume if(our_heatcap + share_heatcap) temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap) @@ -188,7 +188,7 @@ var/molar_mass = mat.molar_mass var/specific_heat = mat.gas_specific_heat var/safe_temp = max(temperature, TCMB) // We're about to divide by this. - return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*volume/(gas[gasid] * safe_temp)) * (molar_mass*specific_heat*safe_temp)**(2/3) + 1 ) + 15 ) + return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*air_volume/(gas[gasid] * safe_temp)) * (molar_mass*specific_heat*safe_temp)**(2/3) + 1 ) + 15 ) //alternative, simpler equation //var/partial_pressure = gas[gasid] * R_IDEAL_GAS_EQUATION * temperature / volume @@ -209,8 +209,8 @@ //Returns the pressure of the gas mix. Only accurate if there have been no gas modifications since update_values() has been called. /datum/gas_mixture/proc/return_pressure() - if(volume) - return total_moles * R_IDEAL_GAS_EQUATION * temperature / volume + if(air_volume) + return total_moles * R_IDEAL_GAS_EQUATION * temperature / air_volume return 0 @@ -249,7 +249,7 @@ gas[g] = gas[g] * (1 - ratio) removed.temperature = temperature - removed.volume = volume * group_multiplier / out_group_multiplier + removed.air_volume = air_volume * group_multiplier / out_group_multiplier update_values() removed.update_values() @@ -257,8 +257,8 @@ //Removes a volume of gas from the mixture and returns a gas_mixture containing the removed air with the given volume /datum/gas_mixture/proc/remove_volume(removed_volume) - var/datum/gas_mixture/removed = remove_ratio(removed_volume/(volume*group_multiplier), 1) - removed.volume = removed_volume + var/datum/gas_mixture/removed = remove_ratio(removed_volume/(air_volume*group_multiplier), 1) + removed.air_volume = removed_volume return removed //Removes moles from the gas mixture, limited by a given flag. Returns a gax_mixture containing the removed air. @@ -305,7 +305,7 @@ return 1 /datum/gas_mixture/GetCloneArgs() - return list(volume, temperature, group_multiplier) + return list(air_volume, temperature, group_multiplier) /datum/gas_mixture/PopulateClone(datum/gas_mixture/clone) clone.gas = gas.Copy() @@ -461,7 +461,7 @@ var/list/total_gas = list() for(var/datum/gas_mixture/gasmix in gases) - total_volume += gasmix.volume + total_volume += gasmix.air_volume var/temp_heatcap = gasmix.heat_capacity() total_thermal_energy += gasmix.temperature * temp_heatcap total_heat_capacity += temp_heatcap @@ -488,7 +488,7 @@ for(var/datum/gas_mixture/gasmix in gases) gasmix.gas = combined.gas.Copy() gasmix.temperature = combined.temperature - gasmix.multiply(gasmix.volume) + gasmix.multiply(gasmix.air_volume) return 1 diff --git a/mods/content/integrated_electronics/components/input.dm b/mods/content/integrated_electronics/components/input.dm index 17d9c59cb75b..1b9dadba0695 100644 --- a/mods/content/integrated_electronics/components/input.dm +++ b/mods/content/integrated_electronics/components/input.dm @@ -1109,7 +1109,7 @@ set_pin_data(IC_OUTPUT, 3, round(air_contents.get_total_moles(), 0.001)) set_pin_data(IC_OUTPUT, 4, round(air_contents.return_pressure(), 0.001)) set_pin_data(IC_OUTPUT, 5, round(air_contents.temperature, 0.001)) - set_pin_data(IC_OUTPUT, 6, round(air_contents.volume, 0.001)) + set_pin_data(IC_OUTPUT, 6, round(air_contents.air_volume, 0.001)) push_data() activate_pin(2) diff --git a/mods/species/ascent/items/rig.dm b/mods/species/ascent/items/rig.dm index 321d28d856af..0ff52ab6c846 100644 --- a/mods/species/ascent/items/rig.dm +++ b/mods/species/ascent/items/rig.dm @@ -135,7 +135,7 @@ name = "mantid gas tank" icon = 'mods/species/ascent/icons/tank.dmi' distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - volume = 180 + air_volume = 180 /obj/item/tank/mantid/methyl_bromide starting_pressure = list(/decl/material/gas/methyl_bromide = 6 ATM) From 2efd349dbcd5de1ae56c076b7c9bcc483ee78a52 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 19:25:18 +1100 Subject: [PATCH 0899/1331] Various local uses of var/volume -> disambiguated var names. --- code/game/objects/explosion.dm | 8 ++++---- code/game/objects/items/_item_damage.dm | 8 ++++---- code/game/objects/structures/fires.dm | 6 +++--- .../liquids/materials_liquid_water.dm | 4 ++-- .../solids/materials_solid_elements.dm | 6 +++--- .../solids/materials_solid_mineral.dm | 6 +++--- code/modules/materials/material_metabolism.dm | 6 +++--- code/modules/mob/living/living.dm | 18 +++++++++--------- .../modules/overmap/ships/computers/sensors.dm | 6 +++--- code/modules/power/port_gen.dm | 7 +++---- code/modules/reagents/Chemistry-Holder.dm | 16 ++++++++-------- code/modules/reagents/chems/chems_blood.dm | 6 +++--- code/modules/reagents/chems/chems_compounds.dm | 8 ++++---- code/modules/reagents/chems/chems_drinks.dm | 6 +++--- code/modules/reagents/chems/chems_drugs.dm | 6 +++--- .../modules/reagents/chems/chems_explosives.dm | 14 +++++++------- code/modules/reagents/chems/chems_fuel.dm | 14 +++++++------- code/modules/reagents/chems/chems_medicines.dm | 8 ++++---- .../reagents/chems/chems_painkillers.dm | 6 +++--- .../reagents/chems/chems_psychiatric.dm | 4 ++-- 20 files changed, 81 insertions(+), 82 deletions(-) diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index f037c65998aa..51ac6d43b52c 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -182,7 +182,7 @@ log_debug("iexpl: Beginning SFX phase.") time = REALTIMEOFDAY - var/volume = 10 + (power * 20) + var/explosion_volume = 10 + (power * 20) var/frequency = get_rand_frequency() var/close_dist = round(power + world.view - 2, 1) @@ -217,12 +217,12 @@ var/dist = get_dist(M, epicenter) || 1 if ((reception & EXPLFX_SOUND) && !HAS_STATUS(M, STAT_DEAF)) if (dist <= close_dist) - M.playsound_local(epicenter, explosion_sound, min(100, volume), 1, frequency, falloff = 5) + M.playsound_local(epicenter, explosion_sound, min(100, explosion_volume), 1, frequency, falloff = 5) //You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station. else - volume = M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', volume, 1, frequency, falloff = 1000) + explosion_volume = M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', explosion_volume, 1, frequency, falloff = 1000) - if ((reception & EXPLFX_SHAKE) && volume > 0) + if ((reception & EXPLFX_SHAKE) && explosion_volume > 0) shake_camera(M, min(30, max(2,(power*2) / dist)), min(3.5, ((power/3) / dist)),0.05) //Maximum duration is 3 seconds, and max strength is 3.5 //Becuse values higher than those just get really silly diff --git a/code/game/objects/items/_item_damage.dm b/code/game/objects/items/_item_damage.dm index 15affb6205ba..f63e42bcd930 100644 --- a/code/game/objects/items/_item_damage.dm +++ b/code/game/objects/items/_item_damage.dm @@ -77,14 +77,14 @@ /obj/item/throw_impact(atom/hit_atom, datum/thrownthing/TT) . = ..() if(isliving(hit_atom)) //Living mobs handle hit sounds differently. - var/volume = get_volume_by_throwforce_and_or_w_class() + var/impact_volume = get_volume_by_throwforce_and_or_w_class() if (get_thrown_attack_force() > 0) if(hitsound) - playsound(hit_atom, hitsound, volume, TRUE, -1) + playsound(hit_atom, hitsound, impact_volume, TRUE, -1) else - playsound(hit_atom, 'sound/weapons/genhit.ogg', volume, TRUE, -1) + playsound(hit_atom, 'sound/weapons/genhit.ogg', impact_volume, TRUE, -1) else - playsound(hit_atom, 'sound/weapons/throwtap.ogg', volume, TRUE, -1) + playsound(hit_atom, 'sound/weapons/throwtap.ogg', impact_volume, TRUE, -1) /obj/item/proc/eyestab(mob/living/M, mob/living/user) var/mob/living/human/H = M diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index b96ef8f2aef1..a8b408d77a45 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -374,12 +374,12 @@ if(reagent.accelerant_value <= FUEL_VALUE_SUPPRESSANT && reagent.phase_at_temperature(get_effective_burn_temperature(), ambient_pressure) == MAT_PHASE_GAS) do_steam = TRUE - var/volume = NONUNIT_CEILING(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_GAS_MOLE, 0.1) - var/list/waste_products = burn_material(reagent, volume) + var/result_volume = NONUNIT_CEILING(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_GAS_MOLE, 0.1) + var/list/waste_products = burn_material(reagent, result_volume) if(!isnull(waste_products)) for(var/product in waste_products) waste[product] += waste_products[product] - reagents.remove_reagent(reagent.type, volume) + reagents.remove_reagent(reagent.type, result_volume) dump_waste_products(loc, waste) diff --git a/code/modules/materials/definitions/liquids/materials_liquid_water.dm b/code/modules/materials/definitions/liquids/materials_liquid_water.dm index 3c1a5adbc39a..7524049c51de 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_water.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_water.dm @@ -89,9 +89,9 @@ touching_turf.assume_air(lowertemp) qdel(hotspot) - var/volume = REAGENT_VOLUME(holder, src) + var/affect_volume = REAGENT_VOLUME(holder, src) if (environment && environment.temperature > min_temperature) // Abstracted as steam or something - var/removed_heat = clamp(volume * WATER_LATENT_HEAT, 0, -environment.get_thermal_energy_change(min_temperature)) + var/removed_heat = clamp(affect_volume * WATER_LATENT_HEAT, 0, -environment.get_thermal_energy_change(min_temperature)) environment.add_thermal_energy(-removed_heat) if (prob(5) && environment && environment.temperature > T100C) touching_turf.visible_message(SPAN_NOTICE("The water sizzles as it lands on \the [touching_turf]!")) diff --git a/code/modules/materials/definitions/solids/materials_solid_elements.dm b/code/modules/materials/definitions/solids/materials_solid_elements.dm index 330c11ada5ab..2e1c477f4aa7 100644 --- a/code/modules/materials/definitions/solids/materials_solid_elements.dm +++ b/code/modules/materials/definitions/solids/materials_solid_elements.dm @@ -109,8 +109,8 @@ /decl/material/solid/potassium/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() - var/volume = REAGENT_VOLUME(holder, src) - if(volume > 3) + var/affect_volume = REAGENT_VOLUME(holder, src) + if(affect_volume > 3) M.add_chemical_effect(CE_PULSE, 1) - if(volume > 10) + if(affect_volume > 10) M.add_chemical_effect(CE_PULSE, 1) diff --git a/code/modules/materials/definitions/solids/materials_solid_mineral.dm b/code/modules/materials/definitions/solids/materials_solid_mineral.dm index 805cff68e0ef..d88305ae97c7 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mineral.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mineral.dm @@ -184,10 +184,10 @@ /decl/material/solid/potash/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() - var/volume = REAGENT_VOLUME(holder, src) - if(volume > 3) + var/affect_volume = REAGENT_VOLUME(holder, src) + if(affect_volume > 3) M.add_chemical_effect(CE_PULSE, 1) - if(volume > 10) + if(affect_volume > 10) M.add_chemical_effect(CE_PULSE, 1) /decl/material/solid/bauxite diff --git a/code/modules/materials/material_metabolism.dm b/code/modules/materials/material_metabolism.dm index db8c71101741..0223b10a6738 100644 --- a/code/modules/materials/material_metabolism.dm +++ b/code/modules/materials/material_metabolism.dm @@ -56,11 +56,11 @@ touching_turf.wet_floor(slipperiness) if(length(vapor_products)) - var/volume = REAGENT_VOLUME(holder, src) + var/result_volume = REAGENT_VOLUME(holder, src) var/temperature = holder?.my_atom?.temperature || T20C for(var/vapor in vapor_products) - touching_turf.assume_gas(vapor, (volume * vapor_products[vapor]), temperature) - holder.remove_reagent(src, volume) + touching_turf.assume_gas(vapor, (result_volume * vapor_products[vapor]), temperature) + holder.remove_reagent(src, result_volume) /decl/material/proc/on_mob_life(var/mob/living/M, var/metabolism_class, var/datum/reagents/holder, var/list/life_dose_tracker) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 043a2bc69b54..1e6497c10a04 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1672,23 +1672,23 @@ default behaviour is: return var/range = world.view - 2 - var/volume = 70 + var/step_volume = 70 if(MOVING_DELIBERATELY(src)) - volume -= 45 + step_volume -= 45 range -= 0.333 var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str) - volume = round(modify_footstep_volume(volume, shoes)) + step_volume = round(modify_footstep_volume(step_volume, shoes)) range = round(modify_footstep_range(range, shoes)) - if(volume > 0 && range > 0) - playsound(T, footsound, volume, 1, range) + if(step_volume > 0 && range > 0) + playsound(T, footsound, step_volume, 1, range) -/mob/living/proc/modify_footstep_volume(volume, obj/item/clothing/shoes/shoes) +/mob/living/proc/modify_footstep_volume(step_volume, obj/item/clothing/shoes/shoes) if(istype(shoes)) - return volume * shoes.footstep_volume_mod + return step_volume * shoes.footstep_volume_mod if(!shoes) - return volume - 60 - return volume + return step_volume - 60 + return step_volume /mob/living/proc/modify_footstep_range(range, obj/item/clothing/shoes/shoes) if(istype(shoes)) diff --git a/code/modules/overmap/ships/computers/sensors.dm b/code/modules/overmap/ships/computers/sensors.dm index d1a572768517..9e2494128f91 100644 --- a/code/modules/overmap/ships/computers/sensors.dm +++ b/code/modules/overmap/ships/computers/sensors.dm @@ -38,10 +38,10 @@ var/obj/machinery/shipsensors/sensors = get_sensors() if(linked && sensors?.use_power && !(sensors.stat & NOPOWER)) - var/volume = 10 + var/ping_volume = 10 if(!sound_token) - sound_token = play_looping_sound(src, sound_id, working_sound, volume = volume, range = 10) - sound_token.SetVolume(volume) + sound_token = play_looping_sound(src, sound_id, working_sound, volume = ping_volume, range = 10) + sound_token.SetVolume(ping_volume) else if(sound_token) QDEL_NULL(sound_token) diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 526fefb07ed4..0097a98ff0c0 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -37,11 +37,10 @@ if(!sound_id) sound_id = "[type]_[sequential_id(/obj/machinery/port_gen)]" if(active && HasFuel() && !IsBroken()) - var/volume = 10 + 15*power_output + var/work_volume = 10 + 15*power_output if(!sound_token) - - sound_token = play_looping_sound(src, sound_id, working_sound, volume = volume) - sound_token.SetVolume(volume) + sound_token = play_looping_sound(src, sound_id, working_sound, volume = work_volume) + sound_token.SetVolume(work_volume) else if(sound_token) QDEL_NULL(sound_token) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index db6297109b64..accaa0b35218 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -460,19 +460,19 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new for(var/decl/material/reagent as anything in liquid_volumes) if(scannable_only && !reagent.scannable) continue - var/volume = REAGENT_VOLUME(src, reagent) + var/scan_volume = REAGENT_VOLUME(src, reagent) if(precision) - volume = round(volume, precision) - if(volume) - . += "[reagent.get_reagent_name(src, MAT_PHASE_LIQUID)] ([volume])" + scan_volume = round(scan_volume, precision) + if(scan_volume) + . += "[reagent.get_reagent_name(src, MAT_PHASE_LIQUID)] ([scan_volume])" for(var/decl/material/reagent as anything in solid_volumes) if(scannable_only && !reagent.scannable) continue - var/volume = REAGENT_VOLUME(src, reagent) + var/scan_volume = REAGENT_VOLUME(src, reagent) if(precision) - volume = round(volume, precision) - if(volume) - . += "[reagent.get_reagent_name(src, MAT_PHASE_SOLID)] ([volume])" + scan_volume = round(scan_volume, precision) + if(scan_volume) + . += "[reagent.get_reagent_name(src, MAT_PHASE_SOLID)] ([scan_volume])" return english_list(., "EMPTY", "", ", ", ", ") /datum/reagents/proc/get_dirtiness() diff --git a/code/modules/reagents/chems/chems_blood.dm b/code/modules/reagents/chems/chems_blood.dm index 4642d9af3883..c4396a07c04f 100644 --- a/code/modules/reagents/chems/chems_blood.dm +++ b/code/modules/reagents/chems/chems_blood.dm @@ -65,10 +65,10 @@ /decl/material/liquid/blood/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) if(ishuman(M)) - var/volume = REAGENT_VOLUME(holder, src) + var/affect_volume = REAGENT_VOLUME(holder, src) var/mob/living/human/H = M - H.inject_blood(volume, holder) - holder.remove_reagent(type, volume) + H.inject_blood(affect_volume, holder) + holder.remove_reagent(type, affect_volume) . = ..() /decl/material/liquid/blood/get_reagent_color(datum/reagents/holder) diff --git a/code/modules/reagents/chems/chems_compounds.dm b/code/modules/reagents/chems/chems_compounds.dm index bb54882996bf..7467842c66e9 100644 --- a/code/modules/reagents/chems/chems_compounds.dm +++ b/code/modules/reagents/chems/chems_compounds.dm @@ -283,14 +283,14 @@ /decl/material/liquid/lactate/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() - var/volume = REAGENT_VOLUME(holder, src) + var/affect_volume = REAGENT_VOLUME(holder, src) M.add_chemical_effect(CE_PULSE, 1) - if(volume >= 10) + if(affect_volume >= 10) M.add_chemical_effect(CE_PULSE, 1) - M.add_chemical_effect(CE_SLOWDOWN, (volume/15) ** 2) + M.add_chemical_effect(CE_SLOWDOWN, (affect_volume/15) ** 2) else if(CHEM_DOSE(M, src) > 30) //after prolonged exertion ADJ_STATUS(M, STAT_JITTER, 5) - M.add_chemical_effect(CE_BREATHLOSS, 0.02 * volume) + M.add_chemical_effect(CE_BREATHLOSS, 0.02 * affect_volume) /decl/material/liquid/nanoblood name = "nanoblood" diff --git a/code/modules/reagents/chems/chems_drinks.dm b/code/modules/reagents/chems/chems_drinks.dm index b02703e26e11..95ee2c8611c7 100644 --- a/code/modules/reagents/chems/chems_drinks.dm +++ b/code/modules/reagents/chems/chems_drinks.dm @@ -369,10 +369,10 @@ if(M.has_trait(/decl/trait/metabolically_inert)) return - var/volume = REAGENT_VOLUME(holder, src) - if(volume > 15) + var/affect_volume = REAGENT_VOLUME(holder, src) + if(affect_volume > 15) M.add_chemical_effect(CE_PULSE, 1) - if(volume > 45) + if(affect_volume > 45) M.add_chemical_effect(CE_PULSE, 1) /decl/material/liquid/drink/coffee/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) diff --git a/code/modules/reagents/chems/chems_drugs.dm b/code/modules/reagents/chems/chems_drugs.dm index c00540af2334..7a998e6981d0 100644 --- a/code/modules/reagents/chems/chems_drugs.dm +++ b/code/modules/reagents/chems/chems_drugs.dm @@ -50,14 +50,14 @@ uid = "chem_nicotine" /decl/material/liquid/nicotine/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) - var/volume = REAGENT_VOLUME(holder, src) + var/affect_volume = REAGENT_VOLUME(holder, src) . = ..() - if(prob(volume*20)) + if(prob(affect_volume*20)) M.add_chemical_effect(CE_PULSE, 1) var/update_data = FALSE var/list/data = REAGENT_DATA(holder, src) - if(volume <= 0.02 && CHEM_DOSE(M, src) >= 0.05 && world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 3 MINUTES) + if(affect_volume <= 0.02 && CHEM_DOSE(M, src) >= 0.05 && world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 3 MINUTES) update_data = TRUE to_chat(M, "You feel antsy, your concentration wavers...") else if(world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 3 MINUTES) diff --git a/code/modules/reagents/chems/chems_explosives.dm b/code/modules/reagents/chems/chems_explosives.dm index 58573abdbb42..c23813eed550 100644 --- a/code/modules/reagents/chems/chems_explosives.dm +++ b/code/modules/reagents/chems/chems_explosives.dm @@ -11,22 +11,22 @@ /decl/material/liquid/anfo/explosion_act(obj/item/chems/holder, severity) . = ..() if(.) - var/volume = REAGENT_VOLUME(holder?.reagents, type) - var/activated_volume = volume + var/affect_volume = REAGENT_VOLUME(holder?.reagents, type) + var/activated_volume = affect_volume switch(severity) if(2) - if(prob(max(0, 2*(volume - 120)))) - activated_volume = rand(volume/4, volume) + if(prob(max(0, 2*(affect_volume - 120)))) + activated_volume = rand(affect_volume/4, affect_volume) if(3) - if(prob(max(0, 2*(volume - 60)))) - activated_volume = rand(volume, 120) + if(prob(max(0, 2*(affect_volume - 60)))) + activated_volume = rand(affect_volume, 120) if(activated_volume < 30) //whiff return var/turf/T = get_turf(holder) if(T) var/adj_power = round(boompower * activated_volume/60) var/datum/gas_mixture/products = new(_temperature = 5 * FLAMMABLE_GAS_FLASHPOINT) - var/gas_moles = 3 * volume + var/gas_moles = 3 * affect_volume products.adjust_gas(/decl/material/gas/carbon_dioxide, 0.5 * gas_moles, FALSE) products.adjust_gas(/decl/material/gas/nitrogen, 0.3 * gas_moles, FALSE) products.adjust_gas(/decl/material/liquid/water, 0.2 * gas_moles, TRUE) diff --git a/code/modules/reagents/chems/chems_fuel.dm b/code/modules/reagents/chems/chems_fuel.dm index 539d0e1b2e8d..c3adf5d8c90a 100644 --- a/code/modules/reagents/chems/chems_fuel.dm +++ b/code/modules/reagents/chems/chems_fuel.dm @@ -20,24 +20,24 @@ /decl/material/liquid/fuel/explosion_act(obj/item/chems/holder, severity) . = ..() if(.) - var/volume = REAGENT_VOLUME(holder?.reagents, type) - if(volume <= 50) + var/product_volume = REAGENT_VOLUME(holder?.reagents, type) + if(product_volume <= 50) return var/turf/T = get_turf(holder) var/datum/gas_mixture/products = new(_temperature = 5 * FLAMMABLE_GAS_FLASHPOINT) - var/gas_moles = 3 * volume + var/gas_moles = 3 * product_volume products.adjust_gas(/decl/material/gas/nitricoxide, 0.1 * gas_moles, FALSE) products.adjust_gas(/decl/material/gas/nitrodioxide, 0.1 * gas_moles, FALSE) products.adjust_gas(/decl/material/gas/nitrogen, 0.6 * gas_moles, FALSE) products.adjust_gas(/decl/material/gas/hydrogen, 0.02 * gas_moles, TRUE) T.assume_air(products) - if(volume > 500) + if(product_volume > 500) explosion(T,1,2,4) - else if(volume > 100) + else if(product_volume > 100) explosion(T,0,1,3) - else if(volume > 50) + else if(product_volume > 50) explosion(T,-1,1,2) - holder?.reagents?.remove_reagent(type, volume) + holder?.reagents?.remove_reagent(type, product_volume) /decl/material/liquid/fuel/hydrazine name = "hydrazine" diff --git a/code/modules/reagents/chems/chems_medicines.dm b/code/modules/reagents/chems/chems_medicines.dm index 9c22dfc135ef..fac296caebb6 100644 --- a/code/modules/reagents/chems/chems_medicines.dm +++ b/code/modules/reagents/chems/chems_medicines.dm @@ -226,18 +226,18 @@ uid = "chem_adrenaline" /decl/material/liquid/adrenaline/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) - var/volume = REAGENT_VOLUME(holder, src) + var/affect_volume = REAGENT_VOLUME(holder, src) var/dose = CHEM_DOSE(M, src) . = ..() if(dose < 0.2) //not that effective after initial rush - M.add_chemical_effect(CE_PAINKILLER, min(30*volume, 80)) + M.add_chemical_effect(CE_PAINKILLER, min(30*affect_volume, 80)) M.add_chemical_effect(CE_PULSE, 1) else if(dose < 1) - M.add_chemical_effect(CE_PAINKILLER, min(10*volume, 20)) + M.add_chemical_effect(CE_PAINKILLER, min(10*affect_volume, 20)) M.add_chemical_effect(CE_PULSE, 2) if(dose > 10) ADJ_STATUS(M, STAT_JITTER, 5) - if(volume >= 5 && M.is_asystole()) + if(affect_volume >= 5 && M.is_asystole()) holder.remove_reagent(type, 5) if(ishuman(M)) var/mob/living/human/H = M diff --git a/code/modules/reagents/chems/chems_painkillers.dm b/code/modules/reagents/chems/chems_painkillers.dm index 9a976f06e2de..f6185712f460 100644 --- a/code/modules/reagents/chems/chems_painkillers.dm +++ b/code/modules/reagents/chems/chems_painkillers.dm @@ -43,14 +43,14 @@ narcotic = TRUE /decl/material/liquid/painkillers/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) - var/volume = REAGENT_VOLUME(holder, src) + var/affect_volume = REAGENT_VOLUME(holder, src) var/dose = CHEM_DOSE(M, src) . = ..() var/effectiveness = 1 if(dose < effective_dose) //some ease-in ease-out for the effect effectiveness = dose/effective_dose - else if(volume < effective_dose) - effectiveness = volume/effective_dose + else if(affect_volume < effective_dose) + effectiveness = affect_volume/effective_dose M.add_chemical_effect(CE_PAINKILLER, (pain_power * effectiveness)) diff --git a/code/modules/reagents/chems/chems_psychiatric.dm b/code/modules/reagents/chems/chems_psychiatric.dm index 2bc88925516f..e81e3c6eba49 100644 --- a/code/modules/reagents/chems/chems_psychiatric.dm +++ b/code/modules/reagents/chems/chems_psychiatric.dm @@ -14,13 +14,13 @@ var/effect_cooldown = 5 MINUTES /decl/material/liquid/accumulated/affect_blood(mob/living/victim, removed, datum/reagents/holder) - var/volume = REAGENT_VOLUME(holder, src) + var/affect_volume = REAGENT_VOLUME(holder, src) . = ..() var/update_data = FALSE var/list/data = REAGENT_DATA(holder, src) var/is_off_cooldown = world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + effect_cooldown - if(volume <= required_volume && CHEM_DOSE(victim, src) >= required_dose) + if(affect_volume <= required_volume && CHEM_DOSE(victim, src) >= required_dose) update_data = discontinuation_effect(victim, removed, holder, is_off_cooldown) else update_data = positive_effect(victim, removed, holder, is_off_cooldown) From ed4b6d7600812366734e79884f527f7c520269e1 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 19:26:09 +1100 Subject: [PATCH 0900/1331] /datum/sound_player/var/volume -> play_volume --- code/modules/synthesized_instruments/real_instruments.dm | 4 ++-- .../real_instruments/Synthesizer/synthesizer.dm | 2 +- .../synthesized_instruments/real_instruments/Violin/violin.dm | 2 +- code/modules/synthesized_instruments/song.dm | 2 +- code/modules/synthesized_instruments/sound_player.dm | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/modules/synthesized_instruments/real_instruments.dm b/code/modules/synthesized_instruments/real_instruments.dm index b3cd8c900e2b..157e5fa1dab1 100644 --- a/code/modules/synthesized_instruments/real_instruments.dm +++ b/code/modules/synthesized_instruments/real_instruments.dm @@ -79,7 +79,7 @@ src.usage_info = new (owner, src.player) src.usage_info.ui_interact(user) if ("volume") - src.player.volume = min(max(min(player.volume+text2num(value), 100), 0), player.max_volume) + src.player.play_volume = min(max(min(player.play_volume+text2num(value), 100), 0), player.max_volume) if ("transposition") src.player.song.transposition = max(min(player.song.transposition+value, global.musical_config.highest_transposition), global.musical_config.lowest_transposition) if ("min_octave") @@ -151,7 +151,7 @@ ), "basic_options" = list( "cur_instrument" = src.player.song.instrument_data.name, - "volume" = src.player.volume, + "volume" = src.player.play_volume, "BPM" = round(600 / src.player.song.tempo), "transposition" = src.player.song.transposition, "octave_range" = list( diff --git a/code/modules/synthesized_instruments/real_instruments/Synthesizer/synthesizer.dm b/code/modules/synthesized_instruments/real_instruments/Synthesizer/synthesizer.dm index 4706caad9d8e..222a35e22002 100644 --- a/code/modules/synthesized_instruments/real_instruments/Synthesizer/synthesizer.dm +++ b/code/modules/synthesized_instruments/real_instruments/Synthesizer/synthesizer.dm @@ -1,7 +1,7 @@ //Synthesizer and minimoog. They work the same /datum/sound_player/synthesizer - volume = 40 + play_volume = 40 /obj/structure/synthesized_instrument/synthesizer name = "The Synthesizer 3.0" diff --git a/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm b/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm index 5eb4e3e773e3..791c664b0e37 100644 --- a/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm +++ b/code/modules/synthesized_instruments/real_instruments/Violin/violin.dm @@ -1,5 +1,5 @@ /datum/sound_player/violin - volume = 25 + play_volume = 25 range = 10 //Kinda don't want this horrible thing to be heard from far away /obj/item/synthesized_instrument/violin diff --git a/code/modules/synthesized_instruments/song.dm b/code/modules/synthesized_instruments/song.dm index 77fef5ee6e2d..86d00ab2edd0 100644 --- a/code/modules/synthesized_instruments/song.dm +++ b/code/modules/synthesized_instruments/song.dm @@ -82,7 +82,7 @@ var/current_volume = clamp(sound_copy.volume, 0, 100) sound_copy.volume = current_volume //Sanitize volume var/datum/sound_token/token = new /datum/sound_token/instrument(src.player.actual_instrument, src.sound_id, sound_copy, src.player.range, FALSE, use_env, player) - var/delta_volume = player.volume / src.sustain_timer + var/delta_volume = player.play_volume / src.sustain_timer var/tick = duration while ((current_volume > 0) && token) diff --git a/code/modules/synthesized_instruments/sound_player.dm b/code/modules/synthesized_instruments/sound_player.dm index dcedc7096001..855f53f05589 100644 --- a/code/modules/synthesized_instruments/sound_player.dm +++ b/code/modules/synthesized_instruments/sound_player.dm @@ -3,7 +3,7 @@ // Virtual object // It's the one used to modify shit var/range = 15 - var/volume = 30 + var/play_volume = 30 var/max_volume = 50 var/falloff = 2 var/apply_echo = 0 @@ -64,7 +64,7 @@ I.PrivLocateListeners(prior_turfs.Copy(), current_turfs.Copy()) /datum/sound_player/proc/apply_modifications(sound/what, note_num, which_line, which_note) // You don't need to override this - what.volume = volume + what.volume = play_volume what.falloff = falloff if (global.musical_config.env_settings_available) what.environment = global.musical_config.is_custom_env(src.virtual_environment_selected) ? src.env : src.virtual_environment_selected From f300289333918fae59e786c390f7dfa17132f7aa Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 20:02:38 +1100 Subject: [PATCH 0901/1331] Food, chem, various bespoke local volume vars -> chem_volume, many initialize_reagents() calls removed. --- code/datums/extensions/milkable/milkable.dm | 4 +-- code/game/machinery/kitchen/icecream.dm | 8 ++--- code/game/objects/__objs.dm | 9 +++++- .../game/objects/items/flame/flame_fuelled.dm | 14 +++------ .../items/flame/flame_fuelled_lantern.dm | 2 +- .../flame/flame_fuelled_lighter_zippo.dm | 10 +++---- code/game/objects/items/toys.dm | 2 +- code/game/objects/items/waterskin.dm | 2 +- code/game/objects/items/weapons/ecigs.dm | 2 +- .../objects/items/weapons/extinguisher.dm | 4 +-- code/game/objects/items/weapons/mop.dm | 7 ++--- code/game/objects/items/weapons/paint.dm | 2 +- code/game/objects/items/weapons/soap.dm | 8 ++--- .../items/weapons/storage/fancy/cigar.dm | 6 +--- .../items/weapons/storage/fancy/cigarettes.dm | 6 +--- .../items/weapons/storage/med_pouch.dm | 18 +++++------ code/game/objects/items/weapons/towels.dm | 6 +--- .../objects/items/welding/weldbackpack.dm | 16 +++++----- .../objects/items/welding/weldingtool_tank.dm | 10 +++---- .../game/objects/structures/barrels/barrel.dm | 2 +- .../game/objects/structures/beds/rollerbed.dm | 6 ++-- .../structures/chemistry/filter_stand.dm | 11 ++----- code/game/objects/structures/compost.dm | 2 +- code/game/objects/structures/fishtanks.dm | 15 ++-------- code/game/objects/structures/fountain.dm | 10 ++----- code/game/objects/structures/iv_drip.dm | 11 ++++--- code/game/objects/structures/well.dm | 2 +- code/modules/butchery/butchery_products.dm | 2 +- .../gloves/jewelry/rings/ring_reagent.dm | 13 +------- code/modules/clothing/masks/chewable.dm | 12 ++------ code/modules/clothing/masks/smokable.dm | 5 +--- code/modules/crafting/handmade_fancy.dm | 8 ++--- code/modules/crafting/handmade_items.dm | 14 ++++----- .../crafting/metalwork/metalwork_items.dm | 5 +--- .../crafting/pottery/pottery_moulds.dm | 11 +++---- code/modules/crafting/working/butter_churn.dm | 9 +----- code/modules/detectivework/tools/luminol.dm | 2 +- code/modules/detectivework/tools/rag.dm | 2 +- .../cooking/cooking_vessels/baking_dish.dm | 2 +- .../food/cooking/cooking_vessels/pot.dm | 18 +++++------ .../food/cooking/cooking_vessels/skillet.dm | 2 +- code/modules/hydroponics/grown.dm | 13 ++------ code/modules/hydroponics/seed_packets.dm | 6 +--- code/modules/mechs/equipment/engineering.dm | 2 +- .../robot/flying/module_flying_emergency.dm | 4 +-- .../robot/flying/module_flying_forensics.dm | 4 +-- .../mob/living/silicon/robot/robot_items.dm | 2 +- code/modules/organs/organ.dm | 10 +------ code/modules/paperwork/pen/quill_and_ink.dm | 2 +- code/modules/paperwork/pen/reagent_pen.dm | 9 +----- code/modules/paperwork/toner_cartridge.dm | 2 +- .../projectiles/ammunition/chemdart.dm | 12 ++------ .../projectiles/guns/projectile/dartgun.dm | 6 ++-- code/modules/reagents/dispenser/cartridge.dm | 12 ++++---- .../reagents/dispenser/cartridge_presets.dm | 4 +-- .../reagents/dispenser/cartridge_spawn.dm | 2 +- code/modules/reagents/reagent_containers.dm | 10 +------ .../reagents/reagent_containers/_glass.dm | 2 +- .../reagents/reagent_containers/beaker.dm | 18 +++++------ .../reagents/reagent_containers/blood_pack.dm | 6 ++-- .../reagents/reagent_containers/borghypo.dm | 4 +-- .../reagents/reagent_containers/bowl.dm | 2 +- .../reagents/reagent_containers/bucket.dm | 6 ++-- .../condiments/__condiment.dm | 2 +- .../condiments/_condiment_large.dm | 2 +- .../condiments/_condiment_small.dm | 2 +- .../condiments/condiments_small.dm | 2 +- .../drinkingglass/drinkingglass.dm | 4 +-- .../drinkingglass/glass_types.dm | 26 ++++++++-------- .../drinkingglass/shaker.dm | 2 +- .../reagents/reagent_containers/drinks.dm | 30 +++++++++---------- .../reagent_containers/drinks/bottle.dm | 4 +-- .../reagent_containers/drinks/cans.dm | 2 +- .../drinks/cocktailshaker.dm | 2 +- .../reagent_containers/drinks/juicebox.dm | 2 +- .../reagents/reagent_containers/dropper.dm | 4 +-- .../reagents/reagent_containers/food.dm | 11 +------ .../reagent_containers/food/burgers.dm | 2 +- .../reagents/reagent_containers/food/eggs.dm | 2 +- .../reagent_containers/glass/bottle.dm | 4 +-- .../reagent_containers/glass/bottle/robot.dm | 2 +- .../reagents/reagent_containers/hypospray.dm | 10 +++---- .../reagents/reagent_containers/inhaler.dm | 2 +- .../reagents/reagent_containers/mortar.dm | 2 +- .../reagents/reagent_containers/packets.dm | 2 +- .../reagents/reagent_containers/pill.dm | 6 ++-- .../reagents/reagent_containers/retort.dm | 14 ++++----- .../reagents/reagent_containers/spray.dm | 12 ++++---- .../reagents/reagent_containers/syringes.dm | 10 +++---- code/modules/reagents/reagent_dispenser.dm | 18 ++++------- mods/content/beekeeping/hive_frame.dm | 9 +----- .../components/reagents.dm | 18 +++++------ mods/content/xenobiology/slime/items.dm | 6 +--- 93 files changed, 251 insertions(+), 401 deletions(-) diff --git a/code/datums/extensions/milkable/milkable.dm b/code/datums/extensions/milkable/milkable.dm index 523a37e3e533..caa797e18786 100644 --- a/code/datums/extensions/milkable/milkable.dm +++ b/code/datums/extensions/milkable/milkable.dm @@ -85,7 +85,7 @@ to_chat(user, SPAN_WARNING("Wait for \the [critter] to stop moving before you try milking it.")) return TRUE - if(container.reagents.total_volume >= container.volume) + if(container.reagents.total_volume >= container.reagents.maximum_volume) to_chat(user, SPAN_WARNING("\The [container] is full.")) return TRUE @@ -112,7 +112,7 @@ to_chat(user, SPAN_WARNING("\The [critter]'s udder is dry. Wait a little longer.")) return TRUE - if(container.reagents.total_volume >= container.volume) + if(container.reagents.total_volume >= container.reagents.maximum_volume) to_chat(user, SPAN_NOTICE("\The [container] is full.")) return TRUE diff --git a/code/game/machinery/kitchen/icecream.dm b/code/game/machinery/kitchen/icecream.dm index 9671e16e3614..f4a52486e668 100644 --- a/code/game/machinery/kitchen/icecream.dm +++ b/code/game/machinery/kitchen/icecream.dm @@ -18,6 +18,7 @@ anchored = FALSE atom_flags = ATOM_FLAG_NO_CHEM_CHANGE | ATOM_FLAG_OPEN_CONTAINER idle_power_usage = 100 + chem_volume = 100 var/list/product_types = list() var/dispense_flavour = ICECREAM_VANILLA @@ -66,14 +67,9 @@ /obj/machinery/icecream_vat/Initialize(mapload, d, populate_parts) . = ..() - initialize_reagents() while(product_types.len < 8) product_types.Add(5) -/obj/machinery/icecream_vat/initialize_reagents(populate = TRUE) - create_reagents(100) - . = ..() - /obj/machinery/icecream_vat/populate_reagents() add_to_reagents(/decl/material/liquid/drink/milk, 5) add_to_reagents(/decl/material/liquid/nutriment/flour, 5) @@ -201,7 +197,7 @@ icon_state = "icecream_cone_waffle" //default for admin-spawned cones, href_list["cone"] should overwrite this all the time layer = ABOVE_OBJ_LAYER bitesize = 3 - volume = 20 + chem_volume = 20 nutriment_amt = 5 nutriment_type = /decl/material/liquid/nutriment nutriment_desc = list("crunchy waffle cone" = 1) diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 4510c03626d7..e1a861e46dd4 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -9,6 +9,9 @@ ///The current health of the obj. Leave to null, unless you want the object to start at a different health than max_health. current_health = null + // If >0 will create a reagent holder on Initialize() + var/chem_volume = 0 + var/obj_flags var/datum/talking_atom/talking_atom var/list/req_access @@ -34,6 +37,8 @@ current_health = get_max_health() else current_health = min(current_health, get_max_health()) + if(chem_volume > 0) + initialize_reagents() /obj/object_shaken() shake_animation() @@ -259,13 +264,15 @@ return TRUE /** - * Init starting reagents and/or reagent var. Not called at the /obj level. + * Init starting reagents and/or reagent var. Called if chem_volume > 0 in /obj/Initialize() * populate: If set to true, we expect map load/admin spawned reagents to be set. */ /obj/proc/initialize_reagents(var/populate = TRUE) SHOULD_CALL_PARENT(TRUE) if(reagents?.total_volume > 0) log_warning("\The [src] possibly is initializing its reagents more than once!") + if(chem_volume > 0) + create_reagents(chem_volume) if(populate) populate_reagents() diff --git a/code/game/objects/items/flame/flame_fuelled.dm b/code/game/objects/items/flame/flame_fuelled.dm index ce4c864b0a82..51cdb60beefe 100644 --- a/code/game/objects/items/flame/flame_fuelled.dm +++ b/code/game/objects/items/flame/flame_fuelled.dm @@ -5,8 +5,8 @@ can_manually_light = TRUE extinguish_on_dropped = FALSE watertight = TRUE + chem_volume = 5 - var/tmp/max_fuel = 5 var/tmp/start_fuelled = FALSE /// TODO: make this calculate a fuel amount via accelerant value or some other check. @@ -14,10 +14,9 @@ var/fuel_type /obj/item/flame/fuelled/Initialize() - . = ..() if(isnull(fuel_type)) fuel_type = global.using_map.default_liquid_fuel_type - initialize_reagents() + . = ..() // Boilerplate from /obj/item/chems/glass. TODO generalize to a lower level. /obj/item/flame/fuelled/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) @@ -84,14 +83,9 @@ return TRUE return FALSE -/obj/item/flame/fuelled/initialize_reagents(populate = TRUE) - if(!reagents) - create_reagents(max_fuel) - . = ..() - /obj/item/flame/fuelled/populate_reagents() - if(start_fuelled && fuel_type && max_fuel) - add_to_reagents(fuel_type, max_fuel) + if(start_fuelled && fuel_type && reagents?.maximum_volume) + add_to_reagents(fuel_type, reagents.maximum_volume) /obj/item/flame/fuelled/Process() . = ..() diff --git a/code/game/objects/items/flame/flame_fuelled_lantern.dm b/code/game/objects/items/flame/flame_fuelled_lantern.dm index 38d4ca1f22f2..31504a3c324c 100644 --- a/code/game/objects/items/flame/flame_fuelled_lantern.dm +++ b/code/game/objects/items/flame/flame_fuelled_lantern.dm @@ -10,7 +10,7 @@ slot_flags = SLOT_LOWER_BODY lit_light_power = 0.7 lit_light_range = 6 - max_fuel = 60 + chem_volume = 60 _fuel_spend_amt = (1 / 60) // a full lantern should last an hour material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC material = /decl/material/solid/metal/copper diff --git a/code/game/objects/items/flame/flame_fuelled_lighter_zippo.dm b/code/game/objects/items/flame/flame_fuelled_lighter_zippo.dm index 50a734b487f1..399bb5063b2d 100644 --- a/code/game/objects/items/flame/flame_fuelled_lighter_zippo.dm +++ b/code/game/objects/items/flame/flame_fuelled_lighter_zippo.dm @@ -1,9 +1,9 @@ /obj/item/flame/fuelled/lighter/zippo - name = "zippo lighter" - desc = "It's a zippo-styled lighter, using a replaceable flint in a fetching steel case. It makes a clicking sound that everyone loves." - icon = 'icons/obj/items/flame/zippo.dmi' - max_fuel = 10 - material = /decl/material/solid/metal/stainlesssteel + name = "zippo lighter" + desc = "It's a zippo-styled lighter, using a replaceable flint in a fetching steel case. It makes a clicking sound that everyone loves." + icon = 'icons/obj/items/flame/zippo.dmi' + chem_volume = 10 + material = /decl/material/solid/metal/stainlesssteel /obj/item/flame/fuelled/lighter/zippo/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE, skip_offset = FALSE) if(overlay && lit && (slot in global.all_hand_slots)) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 76255a19f118..545cc0b21be6 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -47,7 +47,7 @@ throw_range = 20 possible_transfer_amounts = null amount_per_transfer_from_this = 10 - volume = 10 + chem_volume = 10 material = /decl/material/solid/organic/plastic _base_attack_force = 0 diff --git a/code/game/objects/items/waterskin.dm b/code/game/objects/items/waterskin.dm index 89baf179ab28..729f6b1e9240 100644 --- a/code/game/objects/items/waterskin.dm +++ b/code/game/objects/items/waterskin.dm @@ -6,7 +6,7 @@ material = /decl/material/solid/organic/leather/gut color = /decl/material/solid/organic/leather/gut::color atom_flags = ATOM_FLAG_OPEN_CONTAINER - volume = 120 + chem_volume = 120 material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME var/decl/material/stopper_material = /decl/material/solid/organic/cloth/hemp diff --git a/code/game/objects/items/weapons/ecigs.dm b/code/game/objects/items/weapons/ecigs.dm index 353404026db9..305d0aa5f5ce 100644 --- a/code/game/objects/items/weapons/ecigs.dm +++ b/code/game/objects/items/weapons/ecigs.dm @@ -184,7 +184,7 @@ icon_state = "ecartridge" material = /decl/material/solid/metal/aluminium matter = list(/decl/material/solid/glass = MATTER_AMOUNT_REINFORCEMENT) - volume = 20 + chem_volume = 20 atom_flags = ATOM_FLAG_OPEN_CONTAINER /obj/item/chems/ecig_cartridge/get_examine_strings(mob/user, distance, infix, suffix) diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 5610bf4cf254..7c57b93689e5 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -16,7 +16,7 @@ possible_transfer_amounts = @"[30,60,120]" //units of liquid per spray - 120 -> same as splashing them with a bucket per spray possible_particle_amounts = @"[1,2,3]" //Amount of chempuff particles to spawn on spray amount_per_transfer_from_this = 120 - volume = 2000 + chem_volume = 2000 spray_particles = 3 //Amount of liquid particles to spawn on spray particle_move_delay = 5 //Spray effect move delay safety = TRUE @@ -36,7 +36,7 @@ possible_particle_amounts = @"[1,2]" amount_per_transfer_from_this = 80 spray_particles = 2 - volume = 1000 + chem_volume = 1000 material = /decl/material/solid/organic/plastic matter = list( /decl/material/solid/metal/steel = MATTER_AMOUNT_TRACE, diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index 2efa8c00554f..d46890df842d 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -11,6 +11,8 @@ matter = list( /decl/material/solid/organic/cloth = MATTER_AMOUNT_SECONDARY, ) + chem_volume = 30 + var/mopspeed = 40 var/static/list/moppable_types @@ -23,14 +25,9 @@ /obj/item/mop/Initialize() . = ..() - initialize_reagents() if(!moppable_types) populate_moppable_types() -/obj/item/mop/initialize_reagents(populate = TRUE) - create_reagents(30) - . = ..() - /obj/item/mop/afterattack(atom/A, mob/user, proximity) if(!proximity) return ..() diff --git a/code/game/objects/items/weapons/paint.dm b/code/game/objects/items/weapons/paint.dm index 49426d67046f..ddbe13a497ee 100644 --- a/code/game/objects/items/weapons/paint.dm +++ b/code/game/objects/items/weapons/paint.dm @@ -6,7 +6,7 @@ w_class = ITEM_SIZE_NORMAL amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[10,20,30,60]" - volume = 60 + chem_volume = 60 var/pigment /obj/item/chems/glass/bucket/paint/populate_reagents() diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm index ebb10a60b96d..47518ab9b3d0 100644 --- a/code/game/objects/items/weapons/soap.dm +++ b/code/game/objects/items/weapons/soap.dm @@ -14,8 +14,9 @@ material = /decl/material/liquid/cleaner/soap max_health = 5 _base_attack_force = 0 - var/key_data + chem_volume = SOAP_MAX_VOLUME + var/key_data var/list/valid_colors = list(COLOR_GREEN_GRAY, COLOR_RED_GRAY, COLOR_BLUE_GRAY, COLOR_BROWN, COLOR_PALE_PINK, COLOR_PALE_BTL_GREEN, COLOR_OFF_WHITE, COLOR_GRAY40, COLOR_GOLD) var/list/valid_scents = list("fresh air", "cinnamon", "mint", "cocoa", "lavender", "an ocean breeze", "a summer garden", "vanilla", "cheap perfume") var/list/scent_intensity = list("faintly", "strongly", "overbearingly") @@ -31,16 +32,11 @@ /obj/item/soap/crafted/generate_icon() return -/obj/item/soap/initialize_reagents(populate = TRUE) - create_reagents(SOAP_MAX_VOLUME) - . = ..() - /obj/item/soap/populate_reagents() wet() /obj/item/soap/Initialize() . = ..() - initialize_reagents() generate_icon() /obj/item/soap/proc/generate_icon() diff --git a/code/game/objects/items/weapons/storage/fancy/cigar.dm b/code/game/objects/items/weapons/storage/fancy/cigar.dm index a83021f72999..5128bed573ae 100644 --- a/code/game/objects/items/weapons/storage/fancy/cigar.dm +++ b/code/game/objects/items/weapons/storage/fancy/cigar.dm @@ -15,11 +15,7 @@ storage = /datum/storage/box/cigar /obj/item/box/fancy/cigar/Initialize(ml, material_key) - . = ..() - initialize_reagents() - -/obj/item/box/fancy/cigar/initialize_reagents(populate) - create_reagents(10 * max(1, storage?.storage_slots)) + chem_volume = 10 * max(1, /datum/storage/box/cigar::storage_slots) . = ..() /obj/item/box/fancy/cigar/WillContain() diff --git a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm index 379d275b5f8f..80610b50e2cc 100644 --- a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm +++ b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm @@ -17,11 +17,7 @@ return list(/obj/item/clothing/mask/smokable/cigarette = 6) /obj/item/box/fancy/cigarettes/Initialize(ml, material_key) - . = ..() - initialize_reagents() - -/obj/item/box/fancy/cigarettes/initialize_reagents(populate) - create_reagents(5 * max(storage?.max_storage_space, 1)) //so people can inject cigarettes without opening a packet, now with being able to inject the whole one + chem_volume = 5 * max(/datum/storage/box/cigarettes::max_storage_space, 1) //so people can inject cigarettes without opening a packet, now with being able to inject the whole one . = ..() /obj/item/box/fancy/cigarettes/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) diff --git a/code/game/objects/items/weapons/storage/med_pouch.dm b/code/game/objects/items/weapons/storage/med_pouch.dm index 5151fadcfdd9..a24f2ff187cb 100644 --- a/code/game/objects/items/weapons/storage/med_pouch.dm +++ b/code/game/objects/items/weapons/storage/med_pouch.dm @@ -189,22 +189,22 @@ Single Use Emergency Pouches // Pills /obj/item/chems/pill/pouch_pill - name = "emergency pill" - desc = "An emergency pill from an emergency medical pouch." - icon_state = "pill2" - volume = 15 + name = "emergency pill" + desc = "An emergency pill from an emergency medical pouch." + icon_state = "pill2" + chem_volume = 15 abstract_type = /obj/item/chems/pill/pouch_pill + var/_reagent_name /obj/item/chems/pill/pouch_pill/Initialize(ml, material_key) . = ..() if(!reagents?.total_volume) log_warning("[log_info_line(src)] was deleted for containing no reagents during init!") return INITIALIZE_HINT_QDEL - -/obj/item/chems/pill/pouch_pill/initialize_reagents(populate = TRUE) - . = ..() - if(populate && reagents?.get_primary_reagent_name()) - SetName("emergency [reagents.get_primary_reagent_name()] pill ([reagents.total_volume]u)") + if(reagents?.get_primary_reagent_name() && !_reagent_name) + _reagent_name = "emergency [reagents.get_primary_reagent_name()] pill ([reagents.total_volume]u)" + if(_reagent_name) + SetName(_reagent_name) /obj/item/chems/pill/pouch_pill/stabilizer/populate_reagents() add_to_reagents(/decl/material/liquid/stabilizer, reagents.maximum_volume) diff --git a/code/game/objects/items/weapons/towels.dm b/code/game/objects/items/weapons/towels.dm index 80cacb07e3da..623042a6c029 100644 --- a/code/game/objects/items/weapons/towels.dm +++ b/code/game/objects/items/weapons/towels.dm @@ -17,8 +17,8 @@ var/additional_description /obj/item/towel/Initialize() + chem_volume = round(50 * (w_class / ITEM_SIZE_NORMAL)) // larger towels have more room, smaller ones have less . = ..() - initialize_reagents() /obj/item/towel/Destroy() if(is_processing) @@ -66,10 +66,6 @@ if(!reagents?.total_volume) return PROCESS_KILL -/obj/item/towel/initialize_reagents() - create_reagents(round(50 * (w_class / ITEM_SIZE_NORMAL))) // larger towels have more room, smaller ones have less - . = ..() - /obj/item/towel/update_name() if(reagents?.total_volume) if(!REAGENTS_FREE_SPACE(reagents)) diff --git a/code/game/objects/items/welding/weldbackpack.dm b/code/game/objects/items/welding/weldbackpack.dm index 661197c56601..82db07b9617a 100644 --- a/code/game/objects/items/welding/weldbackpack.dm +++ b/code/game/objects/items/welding/weldbackpack.dm @@ -62,14 +62,14 @@ //Welder Pack //////////////////////////////////////////////////////////// /obj/item/chems/weldpack - name = "welding kit" - desc = "An unwieldy, heavy backpack with two massive fuel tanks. Comes with an attached welder gun." - icon = 'icons/obj/items/welderpack.dmi' - icon_state = ICON_STATE_WORLD - slot_flags = SLOT_BACK - w_class = ITEM_SIZE_HUGE - atom_flags = ATOM_FLAG_OPEN_CONTAINER - volume = 350 + name = "welding kit" + desc = "An unwieldy, heavy backpack with two massive fuel tanks. Comes with an attached welder gun." + icon = 'icons/obj/items/welderpack.dmi' + icon_state = ICON_STATE_WORLD + slot_flags = SLOT_BACK + w_class = ITEM_SIZE_HUGE + atom_flags = ATOM_FLAG_OPEN_CONTAINER + chem_volume = 350 var/obj/item/weldingtool/weldpack/welder = /obj/item/weldingtool/weldpack // Duplicated from welder tanks. diff --git a/code/game/objects/items/welding/weldingtool_tank.dm b/code/game/objects/items/welding/weldingtool_tank.dm index 023de068b878..ed87e2be8489 100644 --- a/code/game/objects/items/welding/weldingtool_tank.dm +++ b/code/game/objects/items/welding/weldingtool_tank.dm @@ -10,7 +10,7 @@ w_class = ITEM_SIZE_SMALL atom_flags = ATOM_FLAG_OPEN_CONTAINER obj_flags = OBJ_FLAG_HOLLOW - volume = 20 + chem_volume = 20 presentation_flags = PRESENTATION_FLAG_NAME max_health = 40 material = /decl/material/solid/metal/steel @@ -86,7 +86,7 @@ base_name = "small welding tank" icon_state = "tank_small" w_class = ITEM_SIZE_TINY - volume = 5 + chem_volume = 5 size_in_use = ITEM_SIZE_SMALL unlit_force = 5 lit_force = 7 @@ -97,7 +97,7 @@ base_name = "large welding tank" icon_state = "tank_large" w_class = ITEM_SIZE_SMALL - volume = 40 + chem_volume = 40 size_in_use = ITEM_SIZE_NORMAL _base_attack_force = 6 @@ -106,7 +106,7 @@ base_name = "huge welding tank" icon_state = "tank_huge" w_class = ITEM_SIZE_NORMAL - volume = 80 + chem_volume = 80 size_in_use = ITEM_SIZE_LARGE unlit_force = 9 lit_force = 15 @@ -117,7 +117,7 @@ base_name = "experimental welding tank" icon_state = "tank_experimental" w_class = ITEM_SIZE_NORMAL - volume = 40 + chem_volume = 40 can_refuel = FALSE size_in_use = ITEM_SIZE_LARGE unlit_force = 9 diff --git a/code/game/objects/structures/barrels/barrel.dm b/code/game/objects/structures/barrels/barrel.dm index 0e987ec6aa2b..c14f9007882d 100644 --- a/code/game/objects/structures/barrels/barrel.dm +++ b/code/game/objects/structures/barrels/barrel.dm @@ -12,7 +12,7 @@ wrenchable = FALSE storage = /datum/storage/barrel amount_dispensed = 10 - volume = 7500 + chem_volume = 7500 movable_flags = MOVABLE_FLAG_WHEELED throwpass = TRUE tool_interaction_flags = TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT diff --git a/code/game/objects/structures/beds/rollerbed.dm b/code/game/objects/structures/beds/rollerbed.dm index a3f332b4b044..39ad505e97d4 100644 --- a/code/game/objects/structures/beds/rollerbed.dm +++ b/code/game/objects/structures/beds/rollerbed.dm @@ -22,9 +22,9 @@ icon_state = "up" else icon_state = "down" - if(beaker) + if(beaker?.reagents) var/image/iv = image(icon, "iv[iv_attached]") - var/percentage = round((beaker.reagents.total_volume / beaker.volume) * 100, 25) + var/percentage = round((beaker.reagents.total_volume / max(beaker.reagents.maximum_volume, 1)) * 100, 25) var/image/filling = image(icon, "iv_filling[percentage]") filling.color = beaker.reagents.get_color() iv.overlays += filling @@ -74,7 +74,7 @@ if(SSobj.times_fired % 2) return - if(beaker.volume > 0) + if(beaker.reagents?.total_volume > 0) beaker.reagents.trans_to_mob(buckled_mob, beaker.amount_per_transfer_from_this, CHEM_INJECT) queue_icon_update() diff --git a/code/game/objects/structures/chemistry/filter_stand.dm b/code/game/objects/structures/chemistry/filter_stand.dm index 3cd598d2e7be..b236e3f3dd29 100644 --- a/code/game/objects/structures/chemistry/filter_stand.dm +++ b/code/game/objects/structures/chemistry/filter_stand.dm @@ -8,18 +8,11 @@ material = /decl/material/solid/organic/wood/oak material_alteration = MAT_FLAG_ALTERATION_ALL atom_flags = ATOM_FLAG_OPEN_CONTAINER + chem_volume = 200 var/obj/item/chems/filter/filter var/obj/item/chems/glass/loaded -/obj/structure/filter_stand/Initialize(ml, _mat, _reinf_mat) - . = ..() - initialize_reagents() - -/obj/structure/filter_stand/initialize_reagents(populate) - create_reagents(200) - . = ..() - /obj/structure/filter_stand/mapped/Initialize(ml, _mat, _reinf_mat) filter = new(src) set_loaded_vessel(new /obj/item/chems/glass/handmade/jar(src)) @@ -111,7 +104,7 @@ w_class = ITEM_SIZE_TINY material_alteration = MAT_FLAG_ALTERATION_ALL material = /decl/material/solid/organic/cloth - volume = 100 + chem_volume = 100 atom_flags = ATOM_FLAG_OPEN_CONTAINER /obj/item/chems/filter/on_reagent_change() diff --git a/code/game/objects/structures/compost.dm b/code/game/objects/structures/compost.dm index e8f3c18ec831..5d9c3dd623ed 100644 --- a/code/game/objects/structures/compost.dm +++ b/code/game/objects/structures/compost.dm @@ -16,7 +16,7 @@ var/global/const/COMPOST_WORM_HUNGER_FACTOR = MINIMUM_CHEMICAL_VOLUME material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC wrenchable = FALSE possible_transfer_amounts = @"[10,25,50,100]" - volume = 2000 + chem_volume = 2000 storage = /datum/storage/hopper/industrial/compost /obj/structure/reagent_dispensers/compost_bin/Initialize() diff --git a/code/game/objects/structures/fishtanks.dm b/code/game/objects/structures/fishtanks.dm index 313c92a5ccd7..a8855275998b 100644 --- a/code/game/objects/structures/fishtanks.dm +++ b/code/game/objects/structures/fishtanks.dm @@ -25,21 +25,19 @@ var/global/list/fishtank_cache = list() atom_flags = ATOM_FLAG_CHECKS_BORDER | ATOM_FLAG_CLIMBABLE mob_offset = TRUE max_health = 50 + chem_volume = 300 var/deleting var/fill_type - var/fill_amt var/obj/effect/glass_tank_overlay/tank_overlay // I don't like this, but there's no other way to get a mouse-transparent overlay :( /obj/structure/glass_tank/aquarium name = "aquarium" desc = "A clear glass box for keeping specimens in. This one is full of water." fill_type = /decl/material/liquid/water - fill_amt = 300 /obj/structure/glass_tank/Initialize(mapload) tank_overlay = new(loc, src) - initialize_reagents() . = ..() update_icon() if(!mapload) @@ -53,16 +51,9 @@ var/global/list/fishtank_cache = list() for(var/obj/structure/glass_tank/A in orange(1, oldloc)) A.update_icon() -/obj/structure/glass_tank/initialize_reagents(populate = TRUE) - if(!fill_amt) - return - create_reagents(fill_amt) - if(!fill_type) - return - . = ..() - /obj/structure/glass_tank/populate_reagents() - add_to_reagents(fill_type, reagents.maximum_volume) + if(fill_type) + add_to_reagents(fill_type, reagents.maximum_volume) /obj/structure/glass_tank/attack_hand(var/mob/user) if(user.check_intent(I_FLAG_HARM)) diff --git a/code/game/objects/structures/fountain.dm b/code/game/objects/structures/fountain.dm index f596d169c6e0..d4287c844c53 100644 --- a/code/game/objects/structures/fountain.dm +++ b/code/game/objects/structures/fountain.dm @@ -10,6 +10,8 @@ pixel_x = -16 light_range = 5 light_power = 0.5 + chem_volume = 500 + var/used = FALSE var/increase_age_prob = (100 / 6) @@ -114,14 +116,6 @@ light_range = null light_power = null -/obj/structure/fountain/mundane/Initialize(ml, _mat, _reinf_mat) - . = ..() - initialize_reagents(ml) - -/obj/structure/fountain/mundane/initialize_reagents(populate = TRUE) - create_reagents(500) - . = ..() - /obj/structure/fountain/mundane/populate_reagents() add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) //Don't give free water when building one diff --git a/code/game/objects/structures/iv_drip.dm b/code/game/objects/structures/iv_drip.dm index 796690f63565..e95fa1df572b 100644 --- a/code/game/objects/structures/iv_drip.dm +++ b/code/game/objects/structures/iv_drip.dm @@ -37,10 +37,9 @@ base.icon_state = "[beaker ? "beaker" : "nothing"][attached ? "_hooked" : ""]" add_overlay(base) - if(beaker) - var/datum/reagents/reagents = beaker.reagents - var/percent = round((reagents.total_volume / beaker.volume) * 100) - if(reagents.total_volume) + if(beaker?.reagents) + var/percent = round((beaker.reagents.total_volume / beaker.reagents.maximum_volume) * 100) + if(beaker.reagents.total_volume) var/mutable_appearance/filling = mutable_appearance(icon, "reagent") switch(percent) if(0) @@ -59,7 +58,7 @@ filling.icon_state = "reagent80" if(91 to INFINITY) filling.icon_state = "reagent100" - filling.color = reagents.get_color() + filling.color = beaker.reagents.get_color() add_overlay(filling) if(istype(beaker, /obj/item/chems/ivbag)) @@ -120,7 +119,7 @@ return if(mode) // Give blood - if(beaker.volume > 0) + if(beaker.reagents?.total_volume > 0) beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_INJECT) queue_icon_update() else // Take blood diff --git a/code/game/objects/structures/well.dm b/code/game/objects/structures/well.dm index 911413e67e9b..9480d5721d51 100644 --- a/code/game/objects/structures/well.dm +++ b/code/game/objects/structures/well.dm @@ -14,7 +14,7 @@ wrenchable = FALSE amount_dispensed = 10 possible_transfer_amounts = @"[10,25,50,100]" - volume = 10000 + chem_volume = 10000 can_toggle_open = FALSE var/auto_refill diff --git a/code/modules/butchery/butchery_products.dm b/code/modules/butchery/butchery_products.dm index 659416ea06c3..268f02b43d93 100644 --- a/code/modules/butchery/butchery_products.dm +++ b/code/modules/butchery/butchery_products.dm @@ -5,7 +5,7 @@ material = /decl/material/solid/organic/meat color = /decl/material/solid/organic/meat::color w_class = ITEM_SIZE_NORMAL - volume = 20 + chem_volume = 20 nutriment_type = /decl/material/solid/organic/meat nutriment_desc = list("umami" = 10) nutriment_amt = 20 diff --git a/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm b/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm index c19417797489..c38fdeaa1319 100644 --- a/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm +++ b/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm @@ -3,18 +3,7 @@ /obj/item/clothing/gloves/ring/reagent atom_flags = ATOM_FLAG_OPEN_CONTAINER origin_tech = @'{"materials":2,"esoteric":4}' - var/tmp/volume = 15 - -/obj/item/clothing/gloves/ring/reagent/Initialize(ml, material_key) - . = ..() - initialize_reagents() - -/obj/item/clothing/gloves/ring/reagent/initialize_reagents(populate = TRUE) - if(!reagents) - create_reagents(volume) - else - reagents.maximum_volume = max(volume, reagents.maximum_volume) - . = ..() + chem_volume = 15 /obj/item/clothing/gloves/ring/reagent/equipped(var/mob/living/human/H) ..() diff --git a/code/modules/clothing/masks/chewable.dm b/code/modules/clothing/masks/chewable.dm index 1df74f5bc5fa..c6d6285826bc 100644 --- a/code/modules/clothing/masks/chewable.dm +++ b/code/modules/clothing/masks/chewable.dm @@ -8,17 +8,11 @@ matter = null // no plastic/fiberglass var/type_butt = null - var/chem_volume = 0 var/chewtime = 0 var/brand /obj/item/clothing/mask/chewable/Initialize() - . = ..() atom_flags |= ATOM_FLAG_NO_CHEM_CHANGE // so it doesn't react until you light it - initialize_reagents() - -/obj/item/clothing/mask/chewable/initialize_reagents(populate = TRUE) - create_reagents(chem_volume) // making the cigarrete a chemical holder with a maximum volume of 15 . = ..() /obj/item/clothing/mask/chewable/equipped(var/mob/living/user, var/slot) @@ -107,9 +101,9 @@ icon = 'icons/clothing/mask/chewables/gum_nicotine.dmi' type_butt = /obj/item/trash/cigbutt/spitgum -/obj/item/clothing/mask/chewable/tobacco/nico/initialize_reagents(populate = TRUE) +/obj/item/clothing/mask/chewable/tobacco/nico/Initialize() . = ..() - color = reagents.get_color() + color = reagents?.get_color() /obj/item/clothing/mask/chewable/tobacco/nico/populate_reagents() add_to_reagents(/decl/material/liquid/nicotine, 2) @@ -137,7 +131,7 @@ /obj/item/clothing/mask/chewable/candy/proc/get_possible_initial_reagents() return -/obj/item/clothing/mask/chewable/candy/initialize_reagents() +/obj/item/clothing/mask/chewable/candy/Initialize() . = ..() if(reagents?.total_volume) set_color(reagents.get_color()) diff --git a/code/modules/clothing/masks/smokable.dm b/code/modules/clothing/masks/smokable.dm index 49aa63a031dd..34327fc8e7ca 100644 --- a/code/modules/clothing/masks/smokable.dm +++ b/code/modules/clothing/masks/smokable.dm @@ -9,7 +9,6 @@ var/lit = FALSE var/waterproof = FALSE var/type_butt = null - var/chem_volume = 0 var/smoketime = 0 var/genericmes = "USER lights their NAME with the FLAME." var/matchmes = "USER lights NAME with FLAME" @@ -38,7 +37,6 @@ /obj/item/clothing/mask/smokable/Initialize() . = ..() atom_flags |= ATOM_FLAG_NO_CHEM_CHANGE // so it doesn't react until you light it - create_reagents(chem_volume) // making the cigarrete a chemical holder with a maximum volume of 15 /obj/item/clothing/mask/smokable/Destroy() . = ..() @@ -223,7 +221,6 @@ /obj/item/clothing/mask/smokable/cigarette/Initialize() . = ..() - initialize_reagents() set_extension(src, /datum/extension/tool, list(TOOL_CAUTERY = TOOL_QUALITY_MEDIOCRE)) /obj/item/clothing/mask/smokable/cigarette/populate_reagents() @@ -395,7 +392,7 @@ if(!ATOM_IS_OPEN_CONTAINER(glass)) to_chat(user, SPAN_NOTICE("You need to take the lid off first.")) return TRUE - var/transfered = glass.reagents.trans_to_obj(src, chem_volume) + var/transfered = glass.reagents.trans_to_obj(src, glass.reagents.total_volume) if(transfered) //if reagents were transfered, show the message to_chat(user, SPAN_NOTICE("You dip \the [src] into \the [glass].")) else //if not, either the beaker was empty, or the cigarette was full diff --git a/code/modules/crafting/handmade_fancy.dm b/code/modules/crafting/handmade_fancy.dm index 9622296a587c..8fbe5e3dfc75 100644 --- a/code/modules/crafting/handmade_fancy.dm +++ b/code/modules/crafting/handmade_fancy.dm @@ -10,7 +10,7 @@ desc = "A masterfully made decanter with a fluted neck and graceful handle." icon = 'icons/obj/items/handmade/decanter.dmi' amount_per_transfer_from_this = 10 - volume = 120 + chem_volume = 120 obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE /obj/item/chems/glass/handmade/fancy/goblet @@ -18,21 +18,21 @@ desc = "An elegant goblet with a flared base, likely handmade by some master artisan." icon = 'icons/obj/items/handmade/cup_fancy.dmi' amount_per_transfer_from_this = 10 - volume = 60 + chem_volume = 60 /obj/item/chems/glass/handmade/fancy/bowl name = "bowl" desc = "A sleek, polished bowl, likely handmade by some master artisan." icon = 'icons/obj/items/handmade/bowl_fancy.dmi' amount_per_transfer_from_this = 10 - volume = 60 + chem_volume = 60 /obj/item/chems/glass/handmade/fancy/vase name = "vase" desc = "An elegant masterwork vase." icon = 'icons/obj/items/handmade/vase_fancy.dmi' amount_per_transfer_from_this = 20 - volume = 240 + chem_volume = 240 material = /decl/material/solid/stone/ceramic /obj/item/chems/glass/handmade/fancy/vase/fluted diff --git a/code/modules/crafting/handmade_items.dm b/code/modules/crafting/handmade_items.dm index 75a8ac9650f2..ab4636d25369 100644 --- a/code/modules/crafting/handmade_items.dm +++ b/code/modules/crafting/handmade_items.dm @@ -21,7 +21,7 @@ desc = "A handmade, slightly lumpy teapot." icon = 'icons/obj/items/handmade/teapot.dmi' amount_per_transfer_from_this = 10 - volume = 120 + chem_volume = 120 obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE /obj/item/chems/glass/handmade/cup @@ -29,14 +29,14 @@ desc = "A handmade, slightly lumpy cup." icon = 'icons/obj/items/handmade/cup.dmi' amount_per_transfer_from_this = 10 - volume = 30 + chem_volume = 30 /obj/item/chems/glass/handmade/mug name = "mug" desc = "A handmade, slightly lumpy mug." icon = 'icons/obj/items/handmade/mug.dmi' amount_per_transfer_from_this = 10 - volume = 60 + chem_volume = 60 obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE /obj/item/chems/glass/handmade/vase @@ -44,21 +44,21 @@ desc = "A handmade, slightly lumpy vase." icon = 'icons/obj/items/handmade/vase.dmi' amount_per_transfer_from_this = 20 - volume = 240 + chem_volume = 240 /obj/item/chems/glass/handmade/jar name = "jar" desc = "A handmade, slightly lumpy jar." icon = 'icons/obj/items/handmade/jar.dmi' amount_per_transfer_from_this = 10 - volume = 60 + chem_volume = 60 /obj/item/chems/glass/handmade/bottle name = "bottle" desc = "A handmade, slightly lumpy bottle." icon = 'icons/obj/items/handmade/bottle.dmi' amount_per_transfer_from_this = 10 - volume = 120 + chem_volume = 120 /obj/item/chems/glass/handmade/bottle/tall name = "tall bottle" @@ -73,7 +73,7 @@ desc = "A handmade, slightly lumpy bowl." icon = 'icons/obj/items/handmade/bowl.dmi' amount_per_transfer_from_this = 10 - volume = 60 + chem_volume = 60 /obj/item/chems/glass/handmade/cup/wood material = /decl/material/solid/organic/wood/oak diff --git a/code/modules/crafting/metalwork/metalwork_items.dm b/code/modules/crafting/metalwork/metalwork_items.dm index 2727db4ada8f..511d68ec109b 100644 --- a/code/modules/crafting/metalwork/metalwork_items.dm +++ b/code/modules/crafting/metalwork/metalwork_items.dm @@ -17,6 +17,7 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME storage = /datum/storage/crucible obj_flags = OBJ_FLAG_NO_STORAGE + chem_volume = 300 * REAGENT_UNITS_PER_MATERIAL_SHEET /obj/item/chems/crucible/attackby(obj/item/used_item, mob/user) @@ -66,7 +67,3 @@ I.alpha = 255 * primary_reagent.opacity I.appearance_flags |= RESET_COLOR add_overlay(I) - -/obj/item/chems/crucible/initialize_reagents() - create_reagents(300 * REAGENT_UNITS_PER_MATERIAL_SHEET) // holds a single full stack of 200 ore - return ..() diff --git a/code/modules/crafting/pottery/pottery_moulds.dm b/code/modules/crafting/pottery/pottery_moulds.dm index 6057a7a626a3..a3cdb2d587e1 100644 --- a/code/modules/crafting/pottery/pottery_moulds.dm +++ b/code/modules/crafting/pottery/pottery_moulds.dm @@ -7,12 +7,14 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME max_health = 100 atom_flags = ATOM_FLAG_OPEN_CONTAINER - volume = 20 // updated in populate_reagents() + chem_volume = 20 // updated in Initialize() var/product_type var/list/product_metadata var/work_skill = SKILL_CONSTRUCTION /obj/item/chems/mould/Initialize() + if(ispath(product_type, /obj)) + update_volume_from_product() . = ..() var/datum/extension/labels/lext = get_or_create_extension(src, /datum/extension/labels) if(ispath(product_type, /obj)) @@ -35,11 +37,6 @@ product_metadata = null return ..() -/obj/item/chems/mould/initialize_reagents() - if(ispath(product_type, /obj)) - update_volume_from_product() - ..() - /obj/item/chems/mould/proc/update_volume_from_product() var/required_volume = 0 var/list/matter_for_product = atom_info_repository.get_matter_for(product_type, /decl/material/placeholder, 1) @@ -53,7 +50,7 @@ else if(atom_flags & ATOM_FLAG_INITIALIZED) create_reagents(required_volume) else - volume = required_volume + chem_volume = required_volume else QDEL_NULL(reagents) diff --git a/code/modules/crafting/working/butter_churn.dm b/code/modules/crafting/working/butter_churn.dm index f4fe1205dd52..10a2790167d7 100644 --- a/code/modules/crafting/working/butter_churn.dm +++ b/code/modules/crafting/working/butter_churn.dm @@ -5,14 +5,7 @@ product_type = /obj/item/food/dairy/butter/stick work_sound = /datum/composite_sound/loom_working atom_flags = ATOM_FLAG_OPEN_CONTAINER - -/obj/structure/working/butter_churn/Initialize() - . = ..() - initialize_reagents() - -/obj/structure/working/butter_churn/initialize_reagents(populate) - create_reagents(200) - . = ..() + chem_volume = 200 /obj/structure/working/butter_churn/try_start_working(mob/user) diff --git a/code/modules/detectivework/tools/luminol.dm b/code/modules/detectivework/tools/luminol.dm index d4b7253df557..9a6556e31045 100644 --- a/code/modules/detectivework/tools/luminol.dm +++ b/code/modules/detectivework/tools/luminol.dm @@ -6,7 +6,7 @@ item_state = "cleaner" amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10]" - volume = 250 + chem_volume = 250 /obj/item/chems/spray/luminol/populate_reagents() add_to_reagents(/decl/material/liquid/luminol, reagents.maximum_volume) diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 60717340915e..bebd8ca7d69d 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -6,7 +6,7 @@ icon_state = "rag" amount_per_transfer_from_this = 5 possible_transfer_amounts = @"[5]" - volume = 10 + chem_volume = 10 item_flags = ITEM_FLAG_NO_BLUDGEON atom_flags = ATOM_FLAG_OPEN_CONTAINER material = /decl/material/solid/organic/cloth diff --git a/code/modules/food/cooking/cooking_vessels/baking_dish.dm b/code/modules/food/cooking/cooking_vessels/baking_dish.dm index ae30572f570d..e9cd7e643902 100644 --- a/code/modules/food/cooking/cooking_vessels/baking_dish.dm +++ b/code/modules/food/cooking/cooking_vessels/baking_dish.dm @@ -2,7 +2,7 @@ name = "baking dish" desc = "A large baking dish for baking things." icon = 'icons/obj/food/cooking_vessels/baking_dish.dmi' - volume = 100 + chem_volume = 100 cooking_category = RECIPE_CATEGORY_BAKING_DISH presentation_flags = PRESENTATION_FLAG_NAME obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE // TODO: dynamically add/remove OBJ_FLAG_INSULATED_HANDLE based on handle material? diff --git a/code/modules/food/cooking/cooking_vessels/pot.dm b/code/modules/food/cooking/cooking_vessels/pot.dm index 20bc6b8eb134..2152ea77b56f 100644 --- a/code/modules/food/cooking/cooking_vessels/pot.dm +++ b/code/modules/food/cooking/cooking_vessels/pot.dm @@ -2,7 +2,7 @@ name = "pot" desc = "A large pot for boiling things." icon = 'icons/obj/food/cooking_vessels/pot.dmi' - volume = 100 + chem_volume = 100 cooking_category = RECIPE_CATEGORY_POT presentation_flags = PRESENTATION_FLAG_NAME obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE @@ -53,14 +53,14 @@ last_boil_status = null /obj/item/chems/cooking_vessel/cauldron - name = "cauldron" - desc = "A large round-bodied vessel for making large quantities of potion or soup." - material = /decl/material/solid/metal/iron - color = /decl/material/solid/metal/iron::color - icon = 'icons/obj/food/cooking_vessels/cauldron.dmi' - volume = 1000 - w_class = ITEM_SIZE_STRUCTURE - density = TRUE + name = "cauldron" + desc = "A large round-bodied vessel for making large quantities of potion or soup." + material = /decl/material/solid/metal/iron + color = /decl/material/solid/metal/iron::color + icon = 'icons/obj/food/cooking_vessels/cauldron.dmi' + chem_volume = 1000 + w_class = ITEM_SIZE_STRUCTURE + density = TRUE /obj/item/chems/cooking_vessel/cauldron/can_be_picked_up(mob/user) return FALSE diff --git a/code/modules/food/cooking/cooking_vessels/skillet.dm b/code/modules/food/cooking/cooking_vessels/skillet.dm index 1f39c841ab69..761a6e0b0e73 100644 --- a/code/modules/food/cooking/cooking_vessels/skillet.dm +++ b/code/modules/food/cooking/cooking_vessels/skillet.dm @@ -2,7 +2,7 @@ name = "skillet" desc = "A shallow pan for frying food." icon = 'icons/obj/food/cooking_vessels/skillet.dmi' - volume = 30 + chem_volume = 30 cooking_category = RECIPE_CATEGORY_SKILLET obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 99bd9f8962cc..402232bc89d0 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -95,25 +95,18 @@ . = ..(mapload, material_key, skip_plate) //Init reagents -/obj/item/food/grown/initialize_reagents(populate) - if(reagents) - reagents.clear_reagents() - if(!length(seed?.get_chemical_composition(_segment = plant_segment_type))) - return - - . = ..() //create_reagent and populate_reagents - update_desc() if(reagents.total_volume > 0) bitesize = 1 + round(reagents.total_volume / 2, 1) - update_icon() /obj/item/food/grown/populate_reagents() . = ..() + if(!length(seed?.get_chemical_composition(_segment = plant_segment_type))) + return + // Fill the object up with the appropriate reagents. var/list/chems_to_fill - if(backyard_grilling_count > 0) chems_to_fill ||= seed?.get_chemical_composition(_segment = plant_segment_type, _state = PLANT_STATE_ROASTED) if(dry) diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index 2b291d791c3e..c86b81ef2e98 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -7,6 +7,7 @@ abstract_type = /obj/item/seeds max_health = 10 //Can't set a material, otherwise extracting seeds would generate free materials material = /decl/material/solid/organic/plantmatter/pith + chem_volume = 3 var/seed_mask_icon = 'icons/obj/seeds/seed_masks.dmi' var/seed_base_name = "packet" @@ -17,11 +18,6 @@ if(isnull(seed) && !isnull(_seed)) seed = _seed update_seed() - initialize_reagents() - . = ..() - -/obj/item/seeds/initialize_reagents() - create_reagents(3) . = ..() /obj/item/seeds/populate_reagents() diff --git a/code/modules/mechs/equipment/engineering.dm b/code/modules/mechs/equipment/engineering.dm index b287ef5713e3..d0ff7b2006b8 100644 --- a/code/modules/mechs/equipment/engineering.dm +++ b/code/modules/mechs/equipment/engineering.dm @@ -28,7 +28,7 @@ return null /obj/item/chems/spray/extinguisher/mech - volume = 4000 //Good is gooder + chem_volume = 4000 //Good is gooder icon_state = "mech_exting" /obj/item/chems/spray/extinguisher/mech/get_hardpoint_maptext() diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm index 49a95a47aa63..3e9944558ac7 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm @@ -70,8 +70,8 @@ /obj/item/robot_module/flying/emergency/respawn_consumable(var/mob/living/silicon/robot/robot, var/amount) var/obj/item/chems/spray/PS = emag - if(PS && PS.reagents.total_volume < PS.volume) - var/adding = min(PS.volume-PS.reagents.total_volume, 2*amount) + if(PS && PS.reagents.total_volume < PS.reagents.maximum_volume) + var/adding = min(PS.reagents.maximum_volume-PS.reagents.total_volume, 2*amount) if(adding > 0) PS.add_to_reagents(/decl/material/liquid/acid/polyacid, adding) ..() diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm index 2a942c74e968..9ea5d4ee7ef6 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm @@ -41,8 +41,8 @@ if(!luminol) luminol = new(src) equipment += luminol - if(luminol.reagents.total_volume < luminol.volume) - var/adding = min(luminol.volume-luminol.reagents.total_volume, 2*amount) + if(luminol.reagents.total_volume < luminol.reagents.maximum_volume) + var/adding = min(luminol.reagents.maximum_volume-luminol.reagents.total_volume, 2*amount) if(adding > 0) luminol.add_to_reagents(/decl/material/liquid/luminol, adding) ..() diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 0f61b2f14bcd..c8da4e16f144 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -315,7 +315,7 @@ /obj/item/chems/spray/cleaner/drone name = "space cleaner" desc = "BLAM!-brand non-foaming space cleaner!" - volume = 150 + chem_volume = 150 /obj/item/robot_rack name = "a generic robot rack" diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 4833f59de7ed..d3d49512d744 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -88,11 +88,11 @@ //Third argument may be a dna datum; if null will be set to holder's dna. /obj/item/organ/Initialize(mapload, material_key, datum/mob_snapshot/supplied_appearance) + chem_volume = 5 * (w_class-1)**2 . = ..(mapload, material_key) if(. == INITIALIZE_HINT_QDEL) return . setup_organ(supplied_appearance) - initialize_reagents() /obj/item/organ/proc/setup_organ(datum/mob_snapshot/supplied_appearance) //Null DNA setup @@ -123,14 +123,6 @@ else set_species(owner?.get_species() || global.using_map.default_species) -//Called on initialization to add the neccessary reagents - -/obj/item/organ/initialize_reagents(populate = TRUE) - if(reagents) - return - create_reagents(5 * (w_class-1)**2) - . = ..() - // todo: make this redundant with matter shenanigans /obj/item/organ/populate_reagents() var/reagent_to_add = /decl/material/solid/organic/meat diff --git a/code/modules/paperwork/pen/quill_and_ink.dm b/code/modules/paperwork/pen/quill_and_ink.dm index ff4ebba1db7a..7cb3ba4a1dc1 100644 --- a/code/modules/paperwork/pen/quill_and_ink.dm +++ b/code/modules/paperwork/pen/quill_and_ink.dm @@ -56,7 +56,7 @@ icon = 'icons/obj/items/inkwell.dmi' icon_state = ICON_STATE_WORLD desc = "An inkwell used to hold ink. Dip a quill pen into this to re-ink it." - volume = 30 + chem_volume = 30 /// The minimum amount of ink in the inkwell when populating reagents. var/starting_volume_low = 20 /// The maximum amount of ink in the inkwell when populating reagents. diff --git a/code/modules/paperwork/pen/reagent_pen.dm b/code/modules/paperwork/pen/reagent_pen.dm index 7334323851a1..dfd52e06b040 100644 --- a/code/modules/paperwork/pen/reagent_pen.dm +++ b/code/modules/paperwork/pen/reagent_pen.dm @@ -3,14 +3,7 @@ origin_tech = @'{"materials":2,"esoteric":5}' sharp = TRUE pen_quality = TOOL_QUALITY_MEDIOCRE - -/obj/item/pen/reagent/Initialize() - . = ..() - initialize_reagents() - -/obj/item/pen/reagent/initialize_reagents(populate = TRUE) - create_reagents(30) - . = ..() + chem_volume = 30 /obj/item/pen/reagent/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) diff --git a/code/modules/paperwork/toner_cartridge.dm b/code/modules/paperwork/toner_cartridge.dm index f8c14247fb56..8e4e6155f479 100644 --- a/code/modules/paperwork/toner_cartridge.dm +++ b/code/modules/paperwork/toner_cartridge.dm @@ -10,7 +10,7 @@ throw_speed = 4 w_class = ITEM_SIZE_NORMAL material = /decl/material/solid/organic/plastic - volume = 60 + chem_volume = 60 amount_per_transfer_from_this = 30 possible_transfer_amounts = @"[30,60]" atom_flags = ATOM_FLAG_OPEN_CONTAINER diff --git a/code/modules/projectiles/ammunition/chemdart.dm b/code/modules/projectiles/ammunition/chemdart.dm index e153ddb9c559..97ce9edf74ef 100644 --- a/code/modules/projectiles/ammunition/chemdart.dm +++ b/code/modules/projectiles/ammunition/chemdart.dm @@ -7,21 +7,13 @@ life_span = 15 //shorter range muzzle_type = null material = /decl/material/solid/glass - var/reagent_amount = 15 - -/obj/item/projectile/bullet/chemdart/Initialize() - . = ..() - initialize_reagents() - -/obj/item/projectile/bullet/chemdart/initialize_reagents(populate = TRUE) - create_reagents(reagent_amount) - . = ..() + chem_volume = 15 /obj/item/projectile/bullet/chemdart/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) if(reagents?.total_volume && blocked < 100 && isliving(target)) var/mob/living/L = target if(L.can_inject(null, def_zone) == CAN_INJECT) - reagents.trans_to_mob(L, reagent_amount, CHEM_INJECT) + reagents.trans_to_mob(L, reagents.total_volume, CHEM_INJECT) /obj/item/ammo_casing/chemdart name = "chemical dart" diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm index b5a33cfa8745..8b46af8c094a 100644 --- a/code/modules/projectiles/guns/projectile/dartgun.dm +++ b/code/modules/projectiles/guns/projectile/dartgun.dm @@ -22,7 +22,7 @@ var/list/starting_chems = null /obj/item/gun/projectile/dartgun/Initialize() - initialize_reagents() + chem_volume = length(starting_chems) * 60 . = ..() update_icon() @@ -89,8 +89,8 @@ //fills the given dart with reagents /obj/item/gun/projectile/dartgun/proc/fill_dart(var/obj/item/projectile/bullet/chemdart/dart) - if(mixing.len) - var/mix_amount = dart.reagent_amount/mixing.len + if(length(mixing)) + var/mix_amount = dart.reagents?.total_volume/length(mixing) for(var/obj/item/chems/glass/beaker/B in mixing) B.reagents.trans_to_obj(dart, mix_amount) diff --git a/code/modules/reagents/dispenser/cartridge.dm b/code/modules/reagents/dispenser/cartridge.dm index 60c0df079836..872653d4804e 100644 --- a/code/modules/reagents/dispenser/cartridge.dm +++ b/code/modules/reagents/dispenser/cartridge.dm @@ -4,24 +4,24 @@ icon = 'icons/obj/items/chem/chem_cartridge.dmi' icon_state = "cartridge" w_class = ITEM_SIZE_NORMAL - volume = CARTRIDGE_VOLUME_LARGE + chem_volume = CARTRIDGE_VOLUME_LARGE amount_per_transfer_from_this = 50 material = /decl/material/solid/stone/ceramic // Large, but inaccurate. Use a chem dispenser or beaker for accuracy. possible_transfer_amounts = @"[50,100]" -/obj/item/chems/chem_disp_cartridge/initialize_reagents(populate = TRUE) +/obj/item/chems/chem_disp_cartridge/Initialize() . = ..() - if(populate && reagents.primary_reagent) + if(reagents?.primary_reagent) setLabel(reagents.get_primary_reagent_name()) /obj/item/chems/chem_disp_cartridge/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "It has a capacity of [volume] units." - if(reagents.total_volume <= 0) + . += "It has a capacity of [reagents?.maximum_volume || 0] unit\s." + if(reagents?.total_volume <= 0) . += "It is empty." else - . += "It contains [reagents.total_volume] units of reagents." + . += "It contains [reagents?.total_volume || 0] unit\s of reagents." if(!ATOM_IS_OPEN_CONTAINER(src)) . += "The cap is sealed." diff --git a/code/modules/reagents/dispenser/cartridge_presets.dm b/code/modules/reagents/dispenser/cartridge_presets.dm index 79d763d1f945..553e42ba766f 100644 --- a/code/modules/reagents/dispenser/cartridge_presets.dm +++ b/code/modules/reagents/dispenser/cartridge_presets.dm @@ -1,8 +1,8 @@ /obj/item/chems/chem_disp_cartridge/small - volume = CARTRIDGE_VOLUME_SMALL + chem_volume = CARTRIDGE_VOLUME_SMALL /obj/item/chems/chem_disp_cartridge/medium - volume = CARTRIDGE_VOLUME_MEDIUM + chem_volume = CARTRIDGE_VOLUME_MEDIUM /** * Helper macro to define a new cartridge type for a given reagent. diff --git a/code/modules/reagents/dispenser/cartridge_spawn.dm b/code/modules/reagents/dispenser/cartridge_spawn.dm index 2d81ee8be767..50b6f7e7454d 100644 --- a/code/modules/reagents/dispenser/cartridge_spawn.dm +++ b/code/modules/reagents/dispenser/cartridge_spawn.dm @@ -8,7 +8,7 @@ if("medium") cartridge_type = /obj/item/chems/chem_disp_cartridge/medium if("large") cartridge_type = /obj/item/chems/chem_disp_cartridge var/obj/item/chems/chem_disp_cartridge/cartridge = new cartridge_type(usr.loc) - cartridge.add_to_reagents(reagent_type, cartridge.volume) + cartridge.add_to_reagents(reagent_type, cartridge.reagents?.maximum_volume) var/reagent_name = cartridge.reagents.get_primary_reagent_name() cartridge.setLabel(reagent_name) log_and_message_admins("spawned a [size] reagent container containing [reagent_name] ([reagent_type])") diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 4b2165d06896..639b4a2b87fe 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -8,10 +8,10 @@ obj_flags = OBJ_FLAG_HOLLOW abstract_type = /obj/item/chems watertight = TRUE + chem_volume = 30 var/amount_per_transfer_from_this = 5 var/possible_transfer_amounts = @"[5,10,15,25,30]" - var/volume = 30 var/label_text var/presentation_flags = 0 var/detail_color @@ -19,7 +19,6 @@ /obj/item/chems/Initialize(ml, material_key) . = ..() - initialize_reagents() if(!possible_transfer_amounts) src.verbs -= /obj/item/chems/verb/set_amount_per_transfer_from_this @@ -151,13 +150,6 @@ reagents.splash(get_turf(src), reagents.total_volume) . = ..() -/obj/item/chems/initialize_reagents(populate = TRUE) - if(!reagents) - create_reagents(volume) - else - reagents.maximum_volume = max(reagents.maximum_volume, volume) - . = ..() - /obj/item/chems/proc/set_detail_color(var/new_color) if(new_color != detail_color) detail_color = new_color diff --git a/code/modules/reagents/reagent_containers/_glass.dm b/code/modules/reagents/reagent_containers/_glass.dm index a1816287111e..8fd812e2cc78 100644 --- a/code/modules/reagents/reagent_containers/_glass.dm +++ b/code/modules/reagents/reagent_containers/_glass.dm @@ -9,7 +9,7 @@ item_state = "null" amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,25,30,60]" - volume = 60 + chem_volume = 60 w_class = ITEM_SIZE_SMALL atom_flags = ATOM_FLAG_OPEN_CONTAINER obj_flags = OBJ_FLAG_HOLLOW diff --git a/code/modules/reagents/reagent_containers/beaker.dm b/code/modules/reagents/reagent_containers/beaker.dm index cc2557974423..f8f844a21c0e 100644 --- a/code/modules/reagents/reagent_containers/beaker.dm +++ b/code/modules/reagents/reagent_containers/beaker.dm @@ -15,7 +15,7 @@ /obj/item/chems/glass/beaker/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "It can hold up to [volume] units." + . += "It can hold up to [reagents?.maximum_volume] units." /obj/item/chems/glass/beaker/on_picked_up(mob/user, atom/old_loc) . = ..() @@ -33,7 +33,7 @@ if(reagents?.total_volume) var/image/filling = mutable_appearance(icon, "[icon_state]1", reagents.get_color()) - var/percent = round((reagents.total_volume / volume) * 100) + var/percent = round((reagents.total_volume / reagents.maximum_volume) * 100) switch(percent) if(0 to 9) filling.icon_state = "[icon_state]1" if(10 to 24) filling.icon_state = "[icon_state]10" @@ -69,7 +69,7 @@ desc = "A large beaker." icon = 'icons/obj/items/chem/beakers/large.dmi' center_of_mass = @'{"x":16,"y":10}' - volume = 120 + chem_volume = 120 amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,25,30,60,120]" w_class = ITEM_SIZE_LARGE @@ -79,7 +79,7 @@ desc = "A large mixing bowl." icon = 'icons/obj/items/chem/mixingbowl.dmi' center_of_mass = @'{"x":16,"y":10}' - volume = 180 + chem_volume = 180 amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,25,30,60,180]" atom_flags = ATOM_FLAG_OPEN_CONTAINER @@ -96,7 +96,7 @@ desc = "A heavy kettle for heating water." icon = 'icons/obj/items/chem/kettle.dmi' icon_state = ICON_STATE_WORLD - volume = 180 + chem_volume = 180 amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,25,30,60,180]" atom_flags = ATOM_FLAG_OPEN_CONTAINER @@ -112,7 +112,7 @@ desc = "A cryostasis beaker that allows for chemical storage without reactions." icon = 'icons/obj/items/chem/beakers/stasis.dmi' center_of_mass = @'{"x":16,"y":8}' - volume = 60 + chem_volume = 60 amount_per_transfer_from_this = 10 atom_flags = ATOM_FLAG_OPEN_CONTAINER | ATOM_FLAG_NO_CHEM_CHANGE presentation_flags = PRESENTATION_FLAG_NAME @@ -126,7 +126,7 @@ desc = "An advanced beaker, powered by experimental technology." icon = 'icons/obj/items/chem/beakers/advanced.dmi' center_of_mass = @'{"x":16,"y":10}' - volume = 300 + chem_volume = 300 amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,25,30,60,120,150,200,250,300]" material_alteration = MAT_FLAG_ALTERATION_NONE @@ -143,7 +143,7 @@ desc = "A small glass vial." icon = 'icons/obj/items/chem/vial.dmi' center_of_mass = @'{"x":15,"y":8}' - volume = 30 + chem_volume = 30 w_class = ITEM_SIZE_TINY //half the volume of a bottle, half the size amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,30]" @@ -178,7 +178,7 @@ icon = 'icons/obj/items/chem/beakers/insulated_large.dmi' center_of_mass = @'{"x":16,"y":10}' matter = list(/decl/material/solid/organic/plastic = MATTER_AMOUNT_REINFORCEMENT) - volume = 120 + chem_volume = 120 /obj/item/chems/glass/beaker/sulfuric/populate_reagents() add_to_reagents(/decl/material/liquid/acid, reagents.maximum_volume) diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 2178c3da5c2b..4368f1486288 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -12,7 +12,7 @@ icon = 'icons/obj/bloodpack.dmi' icon_state = "empty" w_class = ITEM_SIZE_SMALL - volume = 120 + chem_volume = 120 possible_transfer_amounts = @"[0.2,1,2]" amount_per_transfer_from_this = REM atom_flags = ATOM_FLAG_OPEN_CONTAINER @@ -27,14 +27,14 @@ /obj/item/chems/ivbag/on_reagent_change() if(!(. = ..())) return - if(reagents?.total_volume > volume/2) + if(reagents?.total_volume > reagents?.maximum_volume / 2) w_class = ITEM_SIZE_NORMAL else w_class = ITEM_SIZE_SMALL /obj/item/chems/ivbag/on_update_icon() . = ..() - var/percent = round(reagents?.total_volume / volume * 100) + var/percent = round(reagents?.total_volume / reagents?.maximum_volume * 100) if(percent) add_overlay(overlay_image(icon, "[round(percent,25)]", reagents.get_color())) add_overlay(attached? "dongle" : "top") diff --git a/code/modules/reagents/reagent_containers/borghypo.dm b/code/modules/reagents/reagent_containers/borghypo.dm index 5234b4c0ff72..3a1e4df3bfcb 100644 --- a/code/modules/reagents/reagent_containers/borghypo.dm +++ b/code/modules/reagents/reagent_containers/borghypo.dm @@ -3,7 +3,7 @@ desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment." icon = 'icons/obj/hypospray_borg.dmi' amount_per_transfer_from_this = 5 - volume = 30 + chem_volume = 30 possible_transfer_amounts = null max_health = ITEM_HEALTH_NO_DAMAGE @@ -137,7 +137,7 @@ icon_state = "shaker" charge_cost = 5 recharge_time = 3 - volume = 60 + chem_volume = 60 possible_transfer_amounts = @"[5,10,20,30]" /obj/item/chems/borghypo/service/get_generated_reagents() diff --git a/code/modules/reagents/reagent_containers/bowl.dm b/code/modules/reagents/reagent_containers/bowl.dm index bc54aadbaa47..c7ad55d990e6 100644 --- a/code/modules/reagents/reagent_containers/bowl.dm +++ b/code/modules/reagents/reagent_containers/bowl.dm @@ -6,7 +6,7 @@ icon_state = ICON_STATE_WORLD material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME presentation_flags = PRESENTATION_FLAG_NAME - volume = 30 + chem_volume = 30 amount_per_transfer_from_this = 5 /obj/item/chems/glass/bowl/can_lid() diff --git a/code/modules/reagents/reagent_containers/bucket.dm b/code/modules/reagents/reagent_containers/bucket.dm index 2a3dd2a016eb..2f950c3dd9af 100644 --- a/code/modules/reagents/reagent_containers/bucket.dm +++ b/code/modules/reagents/reagent_containers/bucket.dm @@ -7,7 +7,7 @@ w_class = ITEM_SIZE_NORMAL amount_per_transfer_from_this = 20 possible_transfer_amounts = @"[10,20,30,60,120,150,180]" - volume = 180 + chem_volume = 180 atom_flags = ATOM_FLAG_OPEN_CONTAINER presentation_flags = PRESENTATION_FLAG_NAME material = /decl/material/solid/organic/plastic @@ -44,14 +44,14 @@ /obj/item/chems/glass/bucket/get_reagents_overlay(state_prefix) if(!ATOM_IS_OPEN_CONTAINER(src)) return null // no overlay while closed! - if(!reagents || (reagents.total_volume / volume) < 0.8) + if(!reagents || (reagents.total_volume / reagents.maximum_volume) < 0.8) return null // must be at least 80% full to show return ..() /obj/item/chems/glass/bucket/wood desc = "It's a wooden bucket. How rustic." icon = 'icons/obj/items/wooden_bucket.dmi' - volume = 200 + chem_volume = 200 material = /decl/material/solid/organic/wood/oak material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR // name is already modified /// The material used for the chain, belts, and rivets holding the wood together, typically iron or steel. diff --git a/code/modules/reagents/reagent_containers/condiments/__condiment.dm b/code/modules/reagents/reagent_containers/condiments/__condiment.dm index 9b5af2524d70..18fe2ee33785 100644 --- a/code/modules/reagents/reagent_containers/condiments/__condiment.dm +++ b/code/modules/reagents/reagent_containers/condiments/__condiment.dm @@ -7,7 +7,7 @@ possible_transfer_amounts = @"[1,5,10]" center_of_mass = @'{"x":16,"y":6}' randpixel = 6 - volume = 50 + chem_volume = 50 var/condiment_key var/morphic_container = TRUE var/use_condiment_name = TRUE diff --git a/code/modules/reagents/reagent_containers/condiments/_condiment_large.dm b/code/modules/reagents/reagent_containers/condiments/_condiment_large.dm index 877b64b928bd..0a260a574d22 100644 --- a/code/modules/reagents/reagent_containers/condiments/_condiment_large.dm +++ b/code/modules/reagents/reagent_containers/condiments/_condiment_large.dm @@ -1,6 +1,6 @@ /obj/item/chems/condiment/large name = "large condiment container" possible_transfer_amounts = @"[1,5,10,20,50,100]" - volume = 500 + chem_volume = 500 w_class = ITEM_SIZE_LARGE use_condiment_name = FALSE diff --git a/code/modules/reagents/reagent_containers/condiments/_condiment_small.dm b/code/modules/reagents/reagent_containers/condiments/_condiment_small.dm index 9c92a450df16..34c521eec487 100644 --- a/code/modules/reagents/reagent_containers/condiments/_condiment_small.dm +++ b/code/modules/reagents/reagent_containers/condiments/_condiment_small.dm @@ -3,5 +3,5 @@ name = "small condiment container" possible_transfer_amounts = @"[1,2,5,8,10,20]" amount_per_transfer_from_this = 1 - volume = 20 + chem_volume = 20 use_condiment_name = FALSE diff --git a/code/modules/reagents/reagent_containers/condiments/condiments_small.dm b/code/modules/reagents/reagent_containers/condiments/condiments_small.dm index 2ed3a0abe878..198f7ca470ba 100644 --- a/code/modules/reagents/reagent_containers/condiments/condiments_small.dm +++ b/code/modules/reagents/reagent_containers/condiments/condiments_small.dm @@ -2,7 +2,7 @@ name = "small condiment container" possible_transfer_amounts = @"[1,2,5,8,10,20]" amount_per_transfer_from_this = 1 - volume = 20 + chem_volume = 20 condiment_key = "small" #define MAPPED_CONDIMENT_TYPE(ID, TYPE) \ diff --git a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm index 4937cbe44298..5be3c085609c 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm @@ -12,7 +12,7 @@ var/global/const/DRINK_ICON_NOISY = "noise" icon_state = null base_icon = "square" // Base icon name filling_states = @"[20,40,60,80,100]" - volume = 30 + chem_volume = 30 material = /decl/material/solid/glass drop_sound = 'sound/foley/bottledrop1.ogg' pickup_sound = 'sound/foley/bottlepickup1.ogg' @@ -77,7 +77,7 @@ var/global/const/DRINK_ICON_NOISY = "noise" for(var/decl/material/reagent as anything in reagents.reagent_volumes) if("vapor" in reagent.glass_special) totalvape += REAGENT_VOLUME(reagents, reagent) - if(totalvape >= volume * 0.6) // 60% vapor by container volume + if(totalvape >= reagents.maximum_volume * 0.6) // 60% vapor by container volume return 1 return 0 diff --git a/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm b/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm index 6f7bafbe93a6..79a18bcae309 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/drink_glasses/square.dmi' desc = "Your standard drinking glass." filling_states = @"[20,40,60,80,100]" - volume = 30 + chem_volume = 30 possible_transfer_amounts = @"[5,10,15,30]" rim_pos = @'{"y":23,"x_left":13,"x_right":20}' @@ -16,7 +16,7 @@ icon_state = "rocks" icon = 'icons/obj/drink_glasses/rocks.dmi' filling_states = @"[25,50,75,100]" - volume = 20 + chem_volume = 20 possible_transfer_amounts = @"[5,10,20]" rim_pos = @'{"y":21,"x_left":10,"x_right":23}' @@ -27,7 +27,7 @@ icon_state = "shake" icon = 'icons/obj/drink_glasses/shake.dmi' filling_states = @"[25,50,75,100]" - volume = 30 + chem_volume = 30 possible_transfer_amounts = @"[5,10,15,30]" rim_pos = @'{"y":25,"x_left":13,"x_right":21}' @@ -38,7 +38,7 @@ icon_state = "cocktail" icon = 'icons/obj/drink_glasses/cocktail.dmi' filling_states = @"[33,66,100]" - volume = 15 + chem_volume = 15 possible_transfer_amounts = @"[5,10,15]" rim_pos = @'{"y":22,"x_left":13,"x_right":21}' @@ -49,7 +49,7 @@ icon_state = "shot" icon = 'icons/obj/drink_glasses/shot.dmi' filling_states = @"[33,66,100]" - volume = 5 + chem_volume = 5 possible_transfer_amounts = @"[1,2,5]" rim_pos = @'{"y":17,"x_left":13,"x_right":21}' @@ -59,7 +59,7 @@ icon_state = "pint" icon = 'icons/obj/drink_glasses/pint.dmi' filling_states = @"[16,33,50,66,83,100]" - volume = 60 + chem_volume = 60 possible_transfer_amounts = @"[5,10,15,30,60]" rim_pos = @'{"y":25,"x_left":12,"x_right":21}' @@ -70,7 +70,7 @@ icon_state = "mug" icon = 'icons/obj/drink_glasses/mug.dmi' filling_states = @"[25,50,75,100]" - volume = 40 + chem_volume = 40 possible_transfer_amounts = @"[5,10,20,40]" rim_pos = @'{"y":22,"x_left":12,"x_right":20}' @@ -81,7 +81,7 @@ icon_state = "wine" icon = 'icons/obj/drink_glasses/wine.dmi' filling_states = @"[20,40,60,80,100]" - volume = 25 + chem_volume = 25 possible_transfer_amounts = @"[5,10,15,25]" rim_pos = @'{"y":25,"x_left":12,"x_right":21}' @@ -91,7 +91,7 @@ base_icon = "flute" icon_state = "flute" icon = 'icons/obj/drink_glasses/flute.dmi' - volume = 25 + chem_volume = 25 filling_states = @"[20,40,60,80,100]" possible_transfer_amounts = @"[5,10,15,25]" rim_pos = @'{"y":24,"x_left":13,"x_right":19}' @@ -103,7 +103,7 @@ icon_state = "carafe" icon = 'icons/obj/drink_glasses/carafe.dmi' filling_states = @"[10,20,30,40,50,60,70,80,90,100]" - volume = 120 + chem_volume = 120 possible_transfer_amounts = @"[5,10,15,30,60,120]" rim_pos = @'{"y":26,"x_left":12,"x_right":21}' center_of_mass = @'{"x":16,"y":7}' @@ -114,7 +114,7 @@ icon = 'icons/obj/drink_glasses/coffecup.dmi' icon_state = "coffeecup" item_state = "coffee" - volume = 30 + chem_volume = 30 center_of_mass = @'{"x":15,"y":13}' filling_states = @"[40,80,100]" base_name = "cup" @@ -187,7 +187,7 @@ desc = "An unreasonably tall coffee cup, for when you really need to wake up in the morning." icon = 'icons/obj/drink_glasses/coffecup_tall.dmi' icon_state = "coffeecup_tall" - volume = 60 + chem_volume = 60 center_of_mass = @'{"x":15,"y":19}' filling_states = @"[50,70,90,100]" base_name = "tall cup" @@ -200,7 +200,7 @@ icon = 'icons/obj/drink_glasses/teacup.dmi' icon_state = "teacup" item_state = "coffee" - volume = 20 + chem_volume = 20 filling_states = @"[100]" base_name = "teacup" base_icon = "teacup" diff --git a/code/modules/reagents/reagent_containers/drinkingglass/shaker.dm b/code/modules/reagents/reagent_containers/drinkingglass/shaker.dm index 54d1b22bd65a..84959a660099 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/shaker.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/shaker.dm @@ -6,7 +6,7 @@ icon_state = "fitness-cup_black" base_icon = "fitness-cup" icon = 'icons/obj/drink_glasses/fitness.dmi' - volume = 100 + chem_volume = 100 material = /decl/material/solid/organic/plastic filling_states = @"[10,20,30,40,50,60,70,80,90,100]" possible_transfer_amounts = @"[5,10,15,25]" diff --git a/code/modules/reagents/reagent_containers/drinks.dm b/code/modules/reagents/reagent_containers/drinks.dm index 73f240c3b34f..acf8e61b283e 100644 --- a/code/modules/reagents/reagent_containers/drinks.dm +++ b/code/modules/reagents/reagent_containers/drinks.dm @@ -11,7 +11,7 @@ possible_transfer_amounts = null amount_per_transfer_from_this = 5 randpixel = 6 - volume = 50 + chem_volume = 50 abstract_type = /obj/item/chems/drinks watertight = FALSE // /drinks uses the open container flag for this @@ -69,17 +69,17 @@ return if(!reagents || reagents.total_volume == 0) . += SPAN_NOTICE("\The [src] is empty!") - else if (reagents.total_volume <= volume * 0.25) + else if (reagents.total_volume <= reagents.maximum_volume * 0.25) . += SPAN_NOTICE("\The [src] is almost empty!") - else if (reagents.total_volume <= volume * 0.66) + else if (reagents.total_volume <= reagents.maximum_volume * 0.66) . += SPAN_NOTICE("\The [src] is half full!") - else if (reagents.total_volume <= volume * 0.90) + else if (reagents.total_volume <= reagents.maximum_volume * 0.90) . += SPAN_NOTICE("\The [src] is almost full!") else . += SPAN_NOTICE("\The [src] is full!") /obj/item/chems/drinks/proc/get_filling_state() - var/percent = round((reagents.total_volume / volume) * 100) + var/percent = round((reagents.total_volume / reagents.maximum_volume) * 100) for(var/k in cached_json_decode(filling_states)) if(percent <= k) return k @@ -102,7 +102,7 @@ w_class = ITEM_SIZE_HUGE amount_per_transfer_from_this = 20 possible_transfer_amounts = null - volume = 150 + chem_volume = 150 atom_flags = ATOM_FLAG_OPEN_CONTAINER obj_flags = OBJ_FLAG_CONDUCTIBLE _base_attack_force = 14 @@ -134,7 +134,7 @@ /obj/item/chems/drinks/milk/smallcarton name = "small milk carton" - volume = 30 + chem_volume = 30 icon_state = "mini-milk" /obj/item/chems/drinks/milk/smallcarton/populate_reagents() @@ -190,7 +190,7 @@ desc = "A paper water cup." icon_state = "water_cup_e" possible_transfer_amounts = null - volume = 10 + chem_volume = 10 center_of_mass = @'{"x":16,"y":12}' /obj/item/chems/drinks/sillycup/on_update_icon() @@ -212,7 +212,7 @@ icon_state = "teapot" item_state = "teapot" amount_per_transfer_from_this = 10 - volume = 120 + chem_volume = 120 center_of_mass = @'{"x":17,"y":7}' material = /decl/material/solid/stone/ceramic obj_flags = OBJ_FLAG_HOLLOW | OBJ_FLAG_INSULATED_HANDLE @@ -221,7 +221,7 @@ name = "insulated pitcher" desc = "A stainless steel insulated pitcher. Everyone's best friend in the morning." icon_state = "pitcher" - volume = 120 + chem_volume = 120 amount_per_transfer_from_this = 10 center_of_mass = @'{"x":16,"y":9}' filling_states = @"[15,30,50,70,85,100]" @@ -233,7 +233,7 @@ name = "\improper Captain's flask" desc = "A metal flask belonging to the captain." icon_state = "flask" - volume = 60 + chem_volume = 60 center_of_mass = @'{"x":17,"y":7}' /obj/item/chems/drinks/flask/shiny @@ -250,21 +250,21 @@ name = "\improper Detective's flask" desc = "A metal flask with a leather band and golden badge belonging to the detective." icon_state = "detflask" - volume = 60 + chem_volume = 60 center_of_mass = @'{"x":17,"y":8}' /obj/item/chems/drinks/flask/barflask name = "flask" desc = "For those who can't be bothered to hang out at the bar to drink." icon_state = "barflask" - volume = 60 + chem_volume = 60 center_of_mass = @'{"x":17,"y":7}' /obj/item/chems/drinks/flask/vacuumflask name = "vacuum flask" desc = "Keeping your drinks at the perfect temperature since 1892." icon_state = "vacuumflask" - volume = 60 + chem_volume = 60 center_of_mass = @'{"x":15,"y":4}' //tea and tea accessories @@ -276,7 +276,7 @@ center_of_mass = @'{"x":16,"y":14}' filling_states = @"[100]" base_icon = "cup" - volume = 30 + chem_volume = 30 /obj/item/chems/drinks/tea/black name = "cup of black tea" diff --git a/code/modules/reagents/reagent_containers/drinks/bottle.dm b/code/modules/reagents/reagent_containers/drinks/bottle.dm index 3de103abcbb2..f6a7161ed3c4 100644 --- a/code/modules/reagents/reagent_containers/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/drinks/bottle.dm @@ -5,7 +5,7 @@ //#TODO: Maybe merge this with /obj/item/glass/bottle? /obj/item/chems/drinks/bottle amount_per_transfer_from_this = 10 - volume = 100 + chem_volume = 100 item_state = "broken_beer" //Generic held-item sprite until unique ones are made. material = /decl/material/solid/glass drop_sound = 'sound/foley/bottledrop1.ogg' @@ -600,7 +600,7 @@ //Small bottles /obj/item/chems/drinks/bottle/small - volume = 50 + chem_volume = 50 smash_duration = 1 atom_flags = 0 //starts closed rag_underlay = "rag_small" diff --git a/code/modules/reagents/reagent_containers/drinks/cans.dm b/code/modules/reagents/reagent_containers/drinks/cans.dm index 3b0974dd6e3a..c59761ee3e1e 100644 --- a/code/modules/reagents/reagent_containers/drinks/cans.dm +++ b/code/modules/reagents/reagent_containers/drinks/cans.dm @@ -1,5 +1,5 @@ /obj/item/chems/drinks/cans - volume = 40 //just over one and a half cups + chem_volume = 40 //just over one and a half cups amount_per_transfer_from_this = 5 atom_flags = 0 //starts closed material = /decl/material/solid/metal/aluminium diff --git a/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm b/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm index f8416cf3a1f0..4b503ded567a 100644 --- a/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm +++ b/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm @@ -4,7 +4,7 @@ icon_state = "shaker" amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,25,30,60]" //Professional bartender should be able to transfer as much as needed - volume = 120 + chem_volume = 120 center_of_mass = @'{"x":17,"y":10}' atom_flags = ATOM_FLAG_OPEN_CONTAINER | ATOM_FLAG_NO_REACT diff --git a/code/modules/reagents/reagent_containers/drinks/juicebox.dm b/code/modules/reagents/reagent_containers/drinks/juicebox.dm index 1ffae8ceb750..cc01e61767ae 100644 --- a/code/modules/reagents/reagent_containers/drinks/juicebox.dm +++ b/code/modules/reagents/reagent_containers/drinks/juicebox.dm @@ -3,7 +3,7 @@ icon_state = "juicebox_base" name = "juicebox" desc = "A small cardboard juicebox. Cheap and flimsy." - volume = 30 + chem_volume = 30 amount_per_transfer_from_this = 5 atom_flags = 0 material = /decl/material/solid/organic/cardboard diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm index e762458818d5..3686618113bd 100644 --- a/code/modules/reagents/reagent_containers/dropper.dm +++ b/code/modules/reagents/reagent_containers/dropper.dm @@ -10,7 +10,7 @@ possible_transfer_amounts = @"[1,2,3,4,5]" w_class = ITEM_SIZE_TINY slot_flags = SLOT_EARS - volume = 5 + chem_volume = 5 item_flags = ITEM_FLAG_NO_BLUDGEON /obj/item/chems/dropper/afterattack(var/obj/target, var/mob/user, var/proximity) @@ -96,7 +96,7 @@ desc = "A larger dropper. Transfers 10 units." amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[1,2,3,4,5,6,7,8,9,10]" - volume = 10 + chem_volume = 10 //////////////////////////////////////////////////////////////////////////////// /// Droppers. END diff --git a/code/modules/reagents/reagent_containers/food.dm b/code/modules/reagents/reagent_containers/food.dm index 3de925eb5c54..661eed567e3b 100644 --- a/code/modules/reagents/reagent_containers/food.dm +++ b/code/modules/reagents/reagent_containers/food.dm @@ -21,9 +21,8 @@ w_class = ITEM_SIZE_SMALL abstract_type = /obj/item/food needs_attack_dexterity = DEXTERITY_NONE + chem_volume = 50 - /// The maximum reagent volume of this food. Used in initialize_reagents. - var/volume = 50 /// Indicates the food should give a stress effect on eating. // This is set to 1 if the food is created by a recipe, -1 if the food is raw. var/cooked_food = FOOD_PREPARED @@ -59,19 +58,11 @@ else if(!istype(plate)) plate = null - initialize_reagents() if(isnull(_utensil_food_type)) _utensil_food_type = type if(slice_path && slice_num) utensil_flags |= UTENSIL_FLAG_SLICE -/obj/item/food/initialize_reagents(populate = TRUE) - if(!reagents) - create_reagents(volume) - else - reagents.maximum_volume = max(reagents.maximum_volume, volume) - return ..() - // Dummy type used solely for soup bowls/soup spoons. /obj/item/food/lump name = "lump" diff --git a/code/modules/reagents/reagent_containers/food/burgers.dm b/code/modules/reagents/reagent_containers/food/burgers.dm index baa622d0f766..728d89478596 100644 --- a/code/modules/reagents/reagent_containers/food/burgers.dm +++ b/code/modules/reagents/reagent_containers/food/burgers.dm @@ -108,7 +108,7 @@ desc = "This massive patty looks like poison. Beep." icon = 'icons/obj/food/burgers/roburger.dmi' filling_color = COLOR_GRAY80 - volume = 100 + chem_volume = 100 center_of_mass = @'{"x":16,"y":11}' bitesize = 0.1 diff --git a/code/modules/reagents/reagent_containers/food/eggs.dm b/code/modules/reagents/reagent_containers/food/eggs.dm index 8c4b82aa292e..5c3b600079ce 100644 --- a/code/modules/reagents/reagent_containers/food/eggs.dm +++ b/code/modules/reagents/reagent_containers/food/eggs.dm @@ -8,7 +8,7 @@ icon = 'icons/obj/food/eggs/egg.dmi' icon_state = ICON_STATE_WORLD filling_color = "#fdffd1" - volume = 10 + chem_volume = 10 center_of_mass = @'{"x":16,"y":13}' material = /decl/material/solid/organic/bone/eggshell obj_flags = OBJ_FLAG_HOLLOW diff --git a/code/modules/reagents/reagent_containers/glass/bottle.dm b/code/modules/reagents/reagent_containers/glass/bottle.dm index 54559692503d..13c52ae77249 100644 --- a/code/modules/reagents/reagent_containers/glass/bottle.dm +++ b/code/modules/reagents/reagent_containers/glass/bottle.dm @@ -14,7 +14,7 @@ w_class = ITEM_SIZE_SMALL item_flags = 0 obj_flags = 0 - volume = 60 + chem_volume = 60 material = /decl/material/solid/glass material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME @@ -39,7 +39,7 @@ /obj/item/chems/glass/bottle/update_overlays() if(reagents?.total_volume) - var/percent = round(reagents.total_volume / volume * 100, 25) + var/percent = round(reagents.total_volume / reagents.maximum_volume * 100, 25) add_overlay(mutable_appearance(icon, "[icon_state]_filling_[percent]", reagents.get_color())) var/image/overglass = mutable_appearance(icon, "[icon_state]_over", color) overglass.alpha = alpha * ((alpha/255) ** 3) diff --git a/code/modules/reagents/reagent_containers/glass/bottle/robot.dm b/code/modules/reagents/reagent_containers/glass/bottle/robot.dm index 54464dc89126..530cbf5362c2 100644 --- a/code/modules/reagents/reagent_containers/glass/bottle/robot.dm +++ b/code/modules/reagents/reagent_containers/glass/bottle/robot.dm @@ -5,7 +5,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10,15,25,30,50,100]" atom_flags = ATOM_FLAG_OPEN_CONTAINER - volume = 60 + chem_volume = 60 /obj/item/chems/glass/bottle/robot/Initialize() . = ..() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 908e70a81d5d..e0406f96dd66 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -10,7 +10,7 @@ abstract_type = /obj/item/chems/hypospray origin_tech = @'{"materials":4,"biotech":5}' amount_per_transfer_from_this = 5 - volume = 30 + chem_volume = 30 possible_transfer_amounts = null atom_flags = ATOM_FLAG_OPEN_CONTAINER slot_flags = SLOT_LOWER_BODY @@ -86,7 +86,7 @@ desc = "A sterile, air-needle autoinjector for rapid administration of drugs to patients. Uses a replaceable 30u vial." possible_transfer_amounts = @"[1,2,5,10,15,20,30]" amount_per_transfer_from_this = 5 - volume = 0 + chem_volume = 0 time = 0 // hyposprays are instant for conscious people single_use = FALSE material = /decl/material/solid/metal/steel @@ -116,7 +116,7 @@ loaded_vial = V reagents.maximum_volume = loaded_vial.reagents.maximum_volume - loaded_vial.reagents.trans_to_holder(reagents, volume) + loaded_vial.reagents.trans_to_holder(reagents, chem_volume) if(user) user.visible_message(SPAN_NOTICE("[user] has loaded [V] into \the [src]."), SPAN_NOTICE("[usermessage]")) @@ -128,7 +128,7 @@ /obj/item/chems/hypospray/vial/proc/remove_vial(var/mob/user, var/swap_mode, var/should_update_icon = TRUE) if(!loaded_vial) return - reagents.trans_to_holder(loaded_vial.reagents,volume) + reagents.trans_to_holder(loaded_vial.reagents,chem_volume) reagents.maximum_volume = 0 loaded_vial.update_icon() if(user) @@ -172,7 +172,7 @@ desc = "A rapid and safe way to administer small amounts of drugs by untrained or trained personnel." icon = 'icons/obj/autoinjector.dmi' amount_per_transfer_from_this = 5 - volume = 5 + chem_volume = 5 origin_tech = @'{"materials":2,"biotech":2}' slot_flags = SLOT_LOWER_BODY | SLOT_EARS w_class = ITEM_SIZE_SMALL diff --git a/code/modules/reagents/reagent_containers/inhaler.dm b/code/modules/reagents/reagent_containers/inhaler.dm index 7408e7545d33..6ed443810914 100644 --- a/code/modules/reagents/reagent_containers/inhaler.dm +++ b/code/modules/reagents/reagent_containers/inhaler.dm @@ -7,7 +7,7 @@ icon_state = ICON_STATE_WORLD center_of_mass = @'{"x":16,"y":11}' amount_per_transfer_from_this = 5 - volume = 5 + chem_volume = 5 w_class = ITEM_SIZE_SMALL possible_transfer_amounts = null atom_flags = ATOM_FLAG_OPEN_CONTAINER diff --git a/code/modules/reagents/reagent_containers/mortar.dm b/code/modules/reagents/reagent_containers/mortar.dm index 76cb59c9a98e..6af747cd6a1d 100644 --- a/code/modules/reagents/reagent_containers/mortar.dm +++ b/code/modules/reagents/reagent_containers/mortar.dm @@ -3,7 +3,7 @@ desc = "A hard, sturdy bowl used to hold organic matter for crushing." icon = 'icons/obj/items/chem/mortar.dmi' icon_state = ICON_STATE_WORLD - volume = 40 + chem_volume = 40 material = /decl/material/solid/stone/basalt color = /decl/material/solid/stone/basalt::color material_alteration = MAT_FLAG_ALTERATION_ALL diff --git a/code/modules/reagents/reagent_containers/packets.dm b/code/modules/reagents/reagent_containers/packets.dm index 391c3440471b..6b417f2254ea 100644 --- a/code/modules/reagents/reagent_containers/packets.dm +++ b/code/modules/reagents/reagent_containers/packets.dm @@ -5,7 +5,7 @@ w_class = ITEM_SIZE_TINY possible_transfer_amounts = @"[1,2,5,10]" amount_per_transfer_from_this = 1 - volume = 10 + chem_volume = 10 /obj/item/chems/packet/attack_self(mob/user) if(!ATOM_IS_OPEN_CONTAINER(src)) diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index e58ada9128be..3fe3c69ad6f0 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -12,7 +12,7 @@ possible_transfer_amounts = null w_class = ITEM_SIZE_TINY slot_flags = SLOT_EARS - volume = 30 + chem_volume = 30 material = /decl/material/solid/organic/plantmatter var/autolabel = TRUE // if set, will add label with the name of the first initial reagent var/static/list/colorizable_icon_states = list("pill1", "pill2", "pill3", "pill4", "pill5") // if using an icon state from here, color will be derived from reagents @@ -74,7 +74,7 @@ /obj/item/chems/pill/bromide desc = "Highly toxic." icon_state = "pill4" - volume = 50 + chem_volume = 50 /obj/item/chems/pill/bromide/populate_reagents() add_to_reagents(/decl/material/liquid/bromide, reagents.maximum_volume) @@ -84,7 +84,7 @@ name = "strange pill" desc = "It's marked 'KCN'. Smells vaguely of almonds." icon_state = "pillC" - volume = 50 + chem_volume = 50 autolabel = FALSE /obj/item/chems/pill/cyanide/populate_reagents() diff --git a/code/modules/reagents/reagent_containers/retort.dm b/code/modules/reagents/reagent_containers/retort.dm index c5640394ce48..6605adad776d 100644 --- a/code/modules/reagents/reagent_containers/retort.dm +++ b/code/modules/reagents/reagent_containers/retort.dm @@ -1,11 +1,11 @@ /obj/item/chems/glass/retort - name = "retort" - base_name = "retort" - desc = "A strangely-shaped vessel for separating chemicals when heated." - icon = 'icons/obj/items/retort.dmi' - icon_state = ICON_STATE_WORLD - volume = 120 - material = /decl/material/solid/glass + name = "retort" + base_name = "retort" + desc = "A strangely-shaped vessel for separating chemicals when heated." + icon = 'icons/obj/items/retort.dmi' + icon_state = ICON_STATE_WORLD + chem_volume = 120 + material = /decl/material/solid/glass material_alteration = MAT_FLAG_ALTERATION_ALL /obj/item/chems/glass/retort/can_lid() diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 9b53bcaedb9a..427175a7493e 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -12,7 +12,7 @@ throw_range = 10 attack_cooldown = DEFAULT_QUICK_COOLDOWN material = /decl/material/solid/organic/plastic - volume = 250 + chem_volume = 250 amount_per_transfer_from_this = 10 possible_transfer_amounts = @"[5,10]" var/tmp/possible_particle_amounts = @"[1,3]" ///Possible chempuff particles amount for each transfer amount setting @@ -160,7 +160,7 @@ icon = 'icons/obj/items/weapon/pepperspray.dmi' icon_state = ICON_STATE_WORLD possible_transfer_amounts = null - volume = 60 + chem_volume = 60 particle_move_delay = 1 safety = TRUE @@ -178,7 +178,7 @@ item_state = "sunflower" amount_per_transfer_from_this = 1 possible_transfer_amounts = null - volume = 10 + chem_volume = 10 /obj/item/chems/spray/waterflower/populate_reagents() add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) @@ -191,7 +191,7 @@ item_state = "chemsprayer" w_class = ITEM_SIZE_LARGE possible_transfer_amounts = null - volume = 600 + chem_volume = 600 origin_tech = @'{"combat":3,"materials":3,"engineering":3}' particle_move_delay = 2 //Was hardcoded to 2 before, and 8 was slower than most mob's move speed material = /decl/material/solid/metal/steel @@ -216,7 +216,7 @@ icon = 'icons/obj/hydroponics/hydroponics_machines.dmi' icon_state = "plantbgone" item_state = "plantbgone" - volume = 100 + chem_volume = 100 /obj/item/chems/spray/plantbgone/populate_reagents() add_to_reagents(/decl/material/liquid/weedkiller, reagents.maximum_volume) @@ -225,7 +225,7 @@ name = "deodorant" desc = "A can of Gold Standard spray deodorant - for when you're too lazy to shower." gender = PLURAL - volume = 35 + chem_volume = 35 icon = 'icons/obj/items/deodorant.dmi' icon_state = "deodorant" item_state = "deodorant" \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index f6128d0f6cd4..713453cb5b5e 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -14,7 +14,7 @@ material = /decl/material/solid/glass amount_per_transfer_from_this = 5 possible_transfer_amounts = @"[1,2,5]" - volume = 15 + chem_volume = 15 w_class = ITEM_SIZE_TINY slot_flags = SLOT_EARS sharp = TRUE @@ -100,7 +100,7 @@ return var/rounded_vol = 0 if (reagents?.total_volume > 0) - rounded_vol = clamp(round((reagents.total_volume / volume * 15),5), 5, 15) + rounded_vol = clamp(round((reagents.total_volume / max(1, reagents.maximum_volume * 15)),5), 5, 15) if(ismob(loc)) add_overlay((mode == SYRINGE_DRAW)? "[icon_state]_draw" : "[icon_state]_inject") icon_state = "[icon_state]_[rounded_vol]" @@ -317,7 +317,7 @@ name = "lethal injection syringe" desc = "A syringe used for lethal injections." amount_per_transfer_from_this = 60 - volume = 60 + chem_volume = 60 visible_name = "a giant syringe" time = 30 SECONDS mode = SYRINGE_INJECT @@ -389,7 +389,7 @@ name = "advanced syringe" desc = "An advanced syringe that can hold 60 units of chemicals." amount_per_transfer_from_this = 20 - volume = 60 + chem_volume = 60 icon = 'icons/obj/syringe_advanced.dmi' material = /decl/material/solid/glass matter = list( @@ -401,7 +401,7 @@ /obj/item/chems/syringe/noreact name = "cryostasis syringe" desc = "An advanced syringe that stops reagents inside from reacting. It can hold up to 20 units." - volume = 20 + chem_volume = 20 atom_flags = ATOM_FLAG_NO_CHEM_CHANGE icon = 'icons/obj/syringe_cryo.dmi' material = /decl/material/solid/glass diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index ecb39c854f4a..ba4d347b9fc3 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -10,17 +10,16 @@ matter = list(/decl/material/solid/metal/steel = MATTER_AMOUNT_SECONDARY) max_health = 100 tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT + chem_volume = 1000 var/wrenchable = TRUE var/unwrenched = FALSE - var/tmp/volume = 1000 var/amount_dispensed = 10 var/can_toggle_open = TRUE var/tmp/possible_transfer_amounts = @"[10,25,50,100,500]" /obj/structure/reagent_dispensers/Initialize(ml, _mat, _reinf_mat) . = ..() - initialize_reagents() if (!possible_transfer_amounts) verbs -= /obj/structure/reagent_dispensers/verb/set_amount_dispensed @@ -42,13 +41,6 @@ else tool_interaction_flags |= TOOL_INTERACTION_DECONSTRUCT -/obj/structure/reagent_dispensers/initialize_reagents(populate = TRUE) - if(!reagents) - create_reagents(volume) - else - reagents.maximum_volume = max(reagents.maximum_volume, volume) - . = ..() - /obj/structure/reagent_dispensers/proc/leak() var/turf/T = get_turf(src) if(reagents && T) @@ -126,7 +118,7 @@ icon_state = "watertank" amount_dispensed = 10 possible_transfer_amounts = @"[10,25,50,100]" - volume = 7500 + chem_volume = 7500 atom_flags = ATOM_FLAG_CLIMBABLE movable_flags = MOVABLE_FLAG_WHEELED @@ -140,8 +132,8 @@ icon_state = ICON_STATE_WORLD /obj/structure/reagent_dispensers/watertank/firefighter - name = "firefighting water reserve" - volume = 50000 + name = "firefighting water reserve" + chem_volume = 50000 /obj/structure/reagent_dispensers/watertank/attackby(obj/item/used_item, mob/user) //FIXME: Maybe this should be handled differently? Since it can essentially make the tank unusable. @@ -254,7 +246,7 @@ possible_transfer_amounts = null amount_dispensed = 5 anchored = TRUE - volume = 500 + chem_volume = 500 tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) var/cups = 12 var/tmp/max_cups = 12 diff --git a/mods/content/beekeeping/hive_frame.dm b/mods/content/beekeeping/hive_frame.dm index 0bfd7265f231..e126ec1a7fa2 100644 --- a/mods/content/beekeeping/hive_frame.dm +++ b/mods/content/beekeeping/hive_frame.dm @@ -3,16 +3,9 @@ icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_SMALL material_alteration = MAT_FLAG_ALTERATION_ALL + chem_volume = 20 var/destroy_on_centrifuge = FALSE -/obj/item/hive_frame/Initialize(ml, material_key) - . = ..() - initialize_reagents() - -/obj/item/hive_frame/initialize_reagents(populate = TRUE) - create_reagents(20) - . = ..() - /obj/item/hive_frame/on_reagent_change() . = ..() if(reagents?.total_volume) diff --git a/mods/content/integrated_electronics/components/reagents.dm b/mods/content/integrated_electronics/components/reagents.dm index b29a7e1c21c3..a89077170c70 100644 --- a/mods/content/integrated_electronics/components/reagents.dm +++ b/mods/content/integrated_electronics/components/reagents.dm @@ -7,15 +7,13 @@ /obj/item/integrated_circuit/reagent category_text = "Reagent" cooldown_per_use = 10 - var/volume = 0 /obj/item/integrated_circuit/reagent/solvent_can_melt(var/solvent_power = MAT_SOLVENT_STRONG) return FALSE /obj/item/integrated_circuit/reagent/Initialize() . = ..() - if(volume) - create_reagents(volume) + if(reagents?.maximum_volume) push_vol() /obj/item/integrated_circuit/reagent/proc/push_vol() @@ -30,7 +28,7 @@ into the smoke clouds when activated. The reagents are consumed when the smoke is made." ext_cooldown = 1 atom_flags = ATOM_FLAG_OPEN_CONTAINER - volume = 100 + chem_volume = 100 complexity = 20 cooldown_per_use = 1 SECONDS @@ -81,7 +79,7 @@ must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing. Negative given amounts makes the injector suck out reagents instead." atom_flags = ATOM_FLAG_OPEN_CONTAINER - volume = 30 + chem_volume = 30 complexity = 20 cooldown_per_use = 6 SECONDS @@ -117,7 +115,7 @@ else direction_mode = IC_REAGENTS_INJECT if(isnum(new_amount)) - new_amount = clamp(new_amount, 0, volume) + new_amount = clamp(new_amount, 0, chem_volume) transfer_amount = new_amount @@ -304,7 +302,7 @@ extended_desc = "This is effectively an internal beaker." atom_flags = ATOM_FLAG_OPEN_CONTAINER - volume = 60 + chem_volume = 60 complexity = 4 inputs = list() @@ -326,7 +324,7 @@ icon_state = "reagent_storage_big" desc = "Stores liquid inside the device away from electrical components. Can store up to 180u." - volume = 180 + chem_volume = 180 complexity = 16 spawn_flags = IC_SPAWN_RESEARCH @@ -359,7 +357,7 @@ "on fail" = IC_PINTYPE_PULSE_OUT, "push ref" = IC_PINTYPE_PULSE_IN ) - volume = 100 + chem_volume = 100 power_draw_per_use = 150 complexity = 16 spawn_flags = IC_SPAWN_RESEARCH @@ -552,7 +550,7 @@ complexity = 12 cooldown_per_use = 1 power_draw_per_use = 50 - volume = 30 + chem_volume = 30 var/active = 0 var/min_temp = 40 CELSIUS diff --git a/mods/content/xenobiology/slime/items.dm b/mods/content/xenobiology/slime/items.dm index 19c61ced8d9b..3235d8b0c1b6 100644 --- a/mods/content/xenobiology/slime/items.dm +++ b/mods/content/xenobiology/slime/items.dm @@ -10,6 +10,7 @@ atom_flags = ATOM_FLAG_OPEN_CONTAINER material = /decl/material/liquid/slimejelly _base_attack_force = 1 + chem_volume = 100 var/slime_type = /decl/slime_colour/grey var/Uses = 1 // uses before it goes inert var/enhanced = 0 //has it been enhanced before? @@ -38,13 +39,8 @@ if(!ispath(slime_type, /decl/slime_colour)) PRINT_STACK_TRACE("Slime extract initialized with non-decl slime colour: [slime_type || "NULL"].") SSstatistics.extracted_slime_cores_amount++ - initialize_reagents() update_icon() -/obj/item/slime_extract/initialize_reagents(populate) - create_reagents(100) - . = ..() - /obj/item/slime_extract/populate_reagents() add_to_reagents(/decl/material/liquid/slimejelly, 30) From 5c49df31c585f759a0fccc5f43dc1bfbc249ce01 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 20:10:26 +1100 Subject: [PATCH 0902/1331] Updating uses of volume in maps. --- maps/away/lost_supply_base/lost_supply_base.dmm | 2 +- maps/away/unishi/unishi-1.dmm | 2 +- maps/exodus/exodus-2.dmm | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/maps/away/lost_supply_base/lost_supply_base.dmm b/maps/away/lost_supply_base/lost_supply_base.dmm index 646d3271d4f2..204c6d60b768 100644 --- a/maps/away/lost_supply_base/lost_supply_base.dmm +++ b/maps/away/lost_supply_base/lost_supply_base.dmm @@ -1426,7 +1426,7 @@ /area/lost_supply_base/common) "eD" = ( /obj/machinery/atmospherics/unary/tank/hydrogen{ - volume = 3200 + air_volume = 3200 }, /turf/floor/plating/airless, /area/lost_supply_base) diff --git a/maps/away/unishi/unishi-1.dmm b/maps/away/unishi/unishi-1.dmm index 69dafd229b14..befa32bfaf72 100644 --- a/maps/away/unishi/unishi-1.dmm +++ b/maps/away/unishi/unishi-1.dmm @@ -251,7 +251,7 @@ /area/unishi/engineering) "aO" = ( /obj/machinery/atmospherics/unary/tank/oxygen{ - volume = 3200 + air_volume = 3200 }, /obj/effect/floor_decal/industrial/hatch/yellow, /turf/floor, diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 99e6fc86be8f..d333b7a254be 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -48513,7 +48513,7 @@ /area/exodus/engineering/storage) "bXs" = ( /obj/machinery/atmospherics/unary/tank/oxygen{ - volume = 3200 + air_volume = 3200 }, /obj/effect/floor_decal/industrial/outline/yellow, /turf/floor/tiled/steel_grid, @@ -48544,7 +48544,7 @@ /area/exodus/engineering/break_room) "bXv" = ( /obj/machinery/atmospherics/unary/tank/hydrogen{ - volume = 3200 + air_volume = 3200 }, /obj/effect/floor_decal/industrial/outline/yellow, /turf/floor/tiled/steel_grid, From 48a38670077c7b227520f6af3beaf00fc4cae097 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 20:24:36 +1100 Subject: [PATCH 0903/1331] create_reagents() -> create_or_update_reagents() or chem_volume set. --- code/datums/composite_sounds/_composite_sound.dm | 2 +- code/game/machinery/biogenerator.dm | 3 ++- code/game/machinery/kitchen/microwave.dm | 3 ++- code/game/machinery/washing_machine.dm | 5 +---- code/game/objects/__objs.dm | 9 ++++----- code/game/objects/effects/chem/chemsmoke.dm | 8 ++++---- code/game/objects/effects/chem/foam.dm | 11 ++++------- code/game/objects/effects/chem/water.dm | 2 ++ code/game/objects/effects/chem_holder.dm | 6 ++++++ .../objects/effects/decals/Cleanable/misc.dm | 4 ++-- .../items/weapons/grenades/chem_grenade.dm | 6 ++---- code/game/objects/structures/fires.dm | 2 +- code/game/objects/structures/iv_drip.dm | 2 +- code/game/objects/structures/janicart.dm | 10 +++------- code/game/objects/structures/mop_bucket.dm | 6 +----- code/game/objects/structures/watercloset.dm | 6 +----- code/game/turfs/turf_fluids.dm | 12 ++++-------- code/modules/crafting/pottery/pottery_moulds.dm | 7 ++----- code/modules/food/utensils/_utensil.dm | 16 ++++++++-------- code/modules/hydroponics/grown.dm | 10 ++-------- code/modules/hydroponics/trays/tray.dm | 9 ++++----- code/modules/implants/implant_types/chem.dm | 2 +- .../mining/machinery/material_extractor.dm | 8 ++++---- .../modules/mining/machinery/material_smelter.dm | 3 ++- code/modules/mob/living/living_powers.dm | 1 - .../silicon/robot/modules/module_clerical.dm | 2 +- code/modules/power/port_gen.dm | 5 +---- code/modules/reagents/Chemistry-Holder.dm | 13 +++++++++++-- code/modules/reagents/Chemistry-Machinery.dm | 8 ++------ code/modules/reagents/dispenser/cartridge.dm | 7 +++++-- .../reagents/reagent_containers/hypospray.dm | 6 +++--- .../modules/reagents/reagent_containers/spray.dm | 1 - code/modules/scanners/mass_spectrometer.dm | 5 +---- code/modules/sealant_gun/sealant_tank.dm | 2 +- code/modules/vehicles/engine.dm | 2 +- .../machinery/geosample_scanner.dm | 5 +---- code/unit_tests/chemistry_tests.dm | 4 ++-- code/unit_tests/food_tests.dm | 3 +-- .../components/power_passive.dm | 4 ++-- .../components/reagents.dm | 2 +- nebula.dme | 1 + 41 files changed, 98 insertions(+), 125 deletions(-) create mode 100644 code/game/objects/effects/chem_holder.dm diff --git a/code/datums/composite_sounds/_composite_sound.dm b/code/datums/composite_sounds/_composite_sound.dm index 7a551b53e0fd..c8b412c6c0a3 100644 --- a/code/datums/composite_sounds/_composite_sound.dm +++ b/code/datums/composite_sounds/_composite_sound.dm @@ -6,7 +6,7 @@ start_length (num) How long to wait before starting the main loop after playing start_sound end_sound (soundfile) The sound played after the main loop has concluded chance (num) Chance per loop to play a mid_sound - volume (num) Sound output volume + play_volume (num) Sound output volume max_loops (num) The max amount of loops to run for. direct (bool) If true plays directly to provided atoms instead of from them */ diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index 75e0387142c4..59ef63bfc7c1 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -16,6 +16,8 @@ construct_state = /decl/machine_construction/default/panel_closed uncreated_component_parts = null stat_immune = 0 + chem_volume = 1000 + var/processing = 0 var/obj/item/chems/glass/beaker = null var/points = 0 @@ -54,7 +56,6 @@ /obj/item/stack/material/skin/mapped/synthleather =30)) /obj/machinery/biogenerator/Initialize() - create_reagents(1000) beaker = new /obj/item/chems/glass/bottle(src) . = ..() diff --git a/code/game/machinery/kitchen/microwave.dm b/code/game/machinery/kitchen/microwave.dm index f1b754a8920e..1455ec5907ac 100644 --- a/code/game/machinery/kitchen/microwave.dm +++ b/code/game/machinery/kitchen/microwave.dm @@ -11,6 +11,8 @@ construct_state = /decl/machine_construction/default/panel_closed uncreated_component_parts = null stat_immune = 0 + chem_volume = 100 + var/operating = FALSE // Is it on? var/dirty = 0 // = {0..100} Does it need cleaning? var/broken = 0 // ={0,1,2} How broken is it??? @@ -37,7 +39,6 @@ /obj/machinery/microwave/Initialize() . = ..() - create_reagents(100) soundloop = new(list(src), FALSE) /obj/machinery/microwave/Destroy() diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index f6e04eb2d50f..1113f10ec678 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -19,6 +19,7 @@ obj_flags = OBJ_FLAG_ANCHORABLE clicksound = "button" clickvol = 40 + chem_volume = 100 // Power idle_power_usage = 10 @@ -57,10 +58,6 @@ ) return wash_blacklist -/obj/machinery/washing_machine/Initialize(mapload, d, populate_parts) - create_reagents(100) - . = ..() - /obj/machinery/washing_machine/get_examine_strings(mob/user, distance, infix, suffix) . = ..() . += SPAN_NOTICE("The detergent port is [atom_flags & ATOM_FLAG_OPEN_CONTAINER ? "open" : "closed"].") diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index e1a861e46dd4..7295fe42516e 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -9,8 +9,8 @@ ///The current health of the obj. Leave to null, unless you want the object to start at a different health than max_health. current_health = null - // If >0 will create a reagent holder on Initialize() - var/chem_volume = 0 + // If non-null and positive, will create a reagent holder on Initialize() + var/chem_volume var/obj_flags var/datum/talking_atom/talking_atom @@ -37,7 +37,7 @@ current_health = get_max_health() else current_health = min(current_health, get_max_health()) - if(chem_volume > 0) + if(!isnull(chem_volume) && chem_volume >= 0) // 0-volume holders perserved for legacy code reasons. Ideally shouldn't exist if <= 0 initialize_reagents() /obj/object_shaken() @@ -271,8 +271,7 @@ SHOULD_CALL_PARENT(TRUE) if(reagents?.total_volume > 0) log_warning("\The [src] possibly is initializing its reagents more than once!") - if(chem_volume > 0) - create_reagents(chem_volume) + create_or_update_reagents(chem_volume) if(populate) populate_reagents() diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index ccfd9b93b6e5..1305d6ee4112 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -7,13 +7,14 @@ layer = ABOVE_PROJECTILE_LAYER time_to_live = 300 pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GRILLE | PASS_FLAG_GLASS //PASS_FLAG_GLASS is fine here, it's just so the visual effect can "flow" around glass + chem_volume = 500 + var/splash_amount = 10 //atoms moving through a smoke cloud get splashed with up to 10 units of reagent var/turf/destination /obj/effect/effect/smoke/chem/Initialize(mapload, smoke_duration, turf/dest_turf = null, icon/cached_icon = null) . = ..() - create_reagents(500) if(cached_icon) icon = cached_icon @@ -76,7 +77,7 @@ ///////////////////////////////////////////// /datum/effect/effect/system/smoke_spread/chem smoke_type = /obj/effect/effect/smoke/chem - var/obj/chemholder + var/obj/effect/chem_holder/chemholder var/range var/list/targetTurfs var/list/wallList @@ -96,8 +97,7 @@ /datum/effect/effect/system/smoke_spread/chem/New() ..() - chemholder = new/obj() - chemholder.create_reagents(500) + chemholder = new(null, 500) //Sets up the chem smoke effect // Calculates the max range smoke can travel, then gets all turfs in that view range. diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm index 06a1a7d81f79..a961e82da284 100644 --- a/code/game/objects/effects/chem/foam.dm +++ b/code/game/objects/effects/chem/foam.dm @@ -11,6 +11,7 @@ layer = ABOVE_OBJ_LAYER mouse_opacity = MOUSE_OPACITY_UNCLICKABLE animate_movement = 0 + chem_volume = 10 var/amount = 3 var/metal = 0 @@ -56,11 +57,9 @@ F = new(T, metal) F.amount = amount - if(!metal) - F.create_reagents(10) - if(reagents) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) - F.add_to_reagents(reagent, 1, safety = 1) //added safety check since reagents in the foam have already had a chance to react + if(!metal && reagents?.total_volume) + for(var/decl/material/reagent as anything in reagents.reagent_volumes) + F.add_to_reagents(reagent, 1, safety = 1) //added safety check since reagents in the foam have already had a chance to react /obj/effect/effect/foam/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) // foam disolves when heated, except metal foams if(!metal && prob(max(0, exposed_temperature - 475))) @@ -107,8 +106,6 @@ F.amount = amount if(!metal) // don't carry other chemicals if a metal foam - F.create_reagents(10) - if(carried_reagents) for(var/id in carried_reagents) F.add_to_reagents(id, 1, safety = 1) //makes a safety call because all reagents should have already reacted anyway diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm index 8f4dbce7bb75..aafbd41b47df 100644 --- a/code/game/objects/effects/chem/water.dm +++ b/code/game/objects/effects/chem/water.dm @@ -4,6 +4,7 @@ icon_state = "extinguish" mouse_opacity = MOUSE_OPACITY_UNCLICKABLE pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GRILLE + chem_volume = 10 /obj/effect/effect/water/Initialize() . = ..() @@ -62,3 +63,4 @@ name = "chemicals" icon = 'icons/obj/chempuff.dmi' icon_state = "" + chem_volume = 10 diff --git a/code/game/objects/effects/chem_holder.dm b/code/game/objects/effects/chem_holder.dm new file mode 100644 index 000000000000..b6c0ea502f78 --- /dev/null +++ b/code/game/objects/effects/chem_holder.dm @@ -0,0 +1,6 @@ +/obj/effect/chem_holder + atom_flags = ATOM_FLAG_OPEN_CONTAINER + +/obj/effect/chem_holder/Initialize(mapload, _vol) + chem_volume = _vol + . = ..() diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 3da636a2c505..6758e59d78c8 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -81,12 +81,12 @@ icon_state = "vomit_1" persistent = TRUE generic_filth = TRUE + chem_volume = 30 /obj/effect/decal/cleanable/vomit/Initialize(ml, _age) random_icon_states = icon_states(icon) - . = ..() atom_flags |= ATOM_FLAG_OPEN_CONTAINER - create_reagents(30, src) + . = ..() if(prob(75)) set_rotation(pick(90, 180, 270)) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 060c4864e227..d5bb28f18f47 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -5,16 +5,14 @@ w_class = ITEM_SIZE_SMALL _base_attack_force = 2.0 det_time = null + chem_volume = 1000 + var/stage = 0 var/path = 0 var/obj/item/assembly_holder/detonator = null var/list/beakers = new/list() var/list/allowed_containers = list(/obj/item/chems/glass/beaker, /obj/item/chems/glass/bottle) -/obj/item/grenade/chem_grenade/Initialize() - . = ..() - create_reagents(1000) - /obj/item/grenade/chem_grenade/Destroy() QDEL_NULL(detonator) QDEL_NULL_LIST(beakers) diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index a8b408d77a45..6c1ae805e1ca 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -27,6 +27,7 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC abstract_type = /obj/structure/fire_source throwpass = TRUE + chem_volume = 100 // Counter for world.time, used to reduce lighting spam. var/next_light_spam_guard = 0 @@ -75,7 +76,6 @@ /obj/structure/fire_source/Initialize() . = ..() update_icon() - create_reagents(100) steam = new(name) steam.attach(get_turf(src)) steam.set_up(3, 0, get_turf(src)) diff --git a/code/game/objects/structures/iv_drip.dm b/code/game/objects/structures/iv_drip.dm index e95fa1df572b..e6dbe8176ab3 100644 --- a/code/game/objects/structures/iv_drip.dm +++ b/code/game/objects/structures/iv_drip.dm @@ -192,7 +192,7 @@ . += "The IV drip is [mode ? "injecting" : "taking blood"]." . += "It is set to transfer [transfer_amount]u of chemicals per cycle." if(beaker) - if(beaker.reagents && beaker.reagents.total_volume) + if(beaker.reagents?.total_volume) . += SPAN_NOTICE("Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid.") else . += SPAN_NOTICE("Attached is an empty [beaker].") diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 5d67e910f7cb..7f01952bc4b6 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -7,23 +7,19 @@ density = TRUE atom_flags = ATOM_FLAG_OPEN_CONTAINER | ATOM_FLAG_CLIMBABLE movable_flags = MOVABLE_FLAG_WHEELED + chem_volume = 180 + var/obj/item/bag/trash/mybag = null var/obj/item/mop/mymop = null var/obj/item/chems/spray/myspray = null var/obj/item/lightreplacer/myreplacer = null var/signs = 0 //maximum capacity hardcoded below - -/obj/structure/janitorialcart/Initialize() - . = ..() - create_reagents(180) - /obj/structure/janitorialcart/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) . += "\The [src] [html_icon(src)] contains [reagents.total_volume] unit\s of liquid!" - /obj/structure/janitorialcart/attackby(obj/item/used_item, mob/user) if(istype(used_item, /obj/item/bag/trash) && !mybag) if(!user.try_unequip(used_item, src)) @@ -191,6 +187,7 @@ density = TRUE material_alteration = MAT_FLAG_ALTERATION_NONE atom_flags = ATOM_FLAG_OPEN_CONTAINER + chem_volume = 100 movement_handlers = list( /datum/movement_handler/deny_multiz, /datum/movement_handler/delay = list(1), @@ -208,7 +205,6 @@ "[WEST]" = list("x" = 13, "y" = 7, "z" = 0) ) . = ..() - create_reagents(100) /obj/structure/janicart/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index 22eec93ece55..57cc6d0e03f6 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -6,11 +6,7 @@ density = TRUE w_class = ITEM_SIZE_NORMAL atom_flags = ATOM_FLAG_CLIMBABLE | ATOM_FLAG_OPEN_CONTAINER - - -/obj/structure/mopbucket/Initialize() - . = ..() - create_reagents(180) + chem_volume = 180 /obj/structure/mopbucket/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 1778979d9b52..76b4a59db265 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -248,6 +248,7 @@ var/global/list/hygiene_props = list() clogged = -1 can_drain = 1 drainage = 0.2 //showers are tiny, drain a little slower + chem_volume = 5 var/on = 0 var/next_mist = 0 @@ -259,11 +260,6 @@ var/global/list/hygiene_props = list() var/datum/sound_token/sound_token //add heat controls? when emagged, you can freeze to death in it? - -/obj/structure/hygiene/shower/Initialize() - . = ..() - create_reagents(5) - /obj/structure/hygiene/shower/Destroy() QDEL_NULL(sound_token) . = ..() diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index 48c6e74fc863..bb59234398df 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -106,18 +106,15 @@ ADD_ACTIVE_FLUID(src) /turf/get_reagents() - if(!reagents) - create_reagents(FLUID_MAX_DEPTH) + create_or_update_reagents(FLUID_MAX_DEPTH) return ..() /turf/add_to_reagents(reagent_type, amount, data, safety = FALSE, defer_update = FALSE, phase = null) - if(!reagents) - create_reagents(FLUID_MAX_DEPTH) + create_or_update_reagents(FLUID_MAX_DEPTH) return ..() /turf/get_reagent_space() - if(!reagents) - create_reagents(FLUID_MAX_DEPTH) + create_or_update_reagents(FLUID_MAX_DEPTH) return ..() /turf/fluid_act(var/datum/reagents/fluids) @@ -146,8 +143,7 @@ // No flowing of reagents without liquids, but this proc should not be called if liquids are not present regardless. if(!reagents?.total_liquid_volume) return - if(!target.reagents) - target.create_reagents(FLUID_MAX_DEPTH) + target.create_or_update_reagents(FLUID_MAX_DEPTH) // We reference total_volume instead of total_liquid_volume here because the maximum volume limits of the turfs still respect solid volumes, and depth is still determined by total volume. reagents.trans_to_turf(target, min(reagents.total_volume, min(target.reagents.maximum_volume - target.reagents.total_volume, amount)), defer_update = defer_update) diff --git a/code/modules/crafting/pottery/pottery_moulds.dm b/code/modules/crafting/pottery/pottery_moulds.dm index a3cdb2d587e1..42339a40f1ae 100644 --- a/code/modules/crafting/pottery/pottery_moulds.dm +++ b/code/modules/crafting/pottery/pottery_moulds.dm @@ -44,11 +44,8 @@ required_volume += matter_for_product[mat] required_volume = ceil(required_volume * REAGENT_UNITS_PER_MATERIAL_UNIT) if(required_volume > 0) - if(reagents) - reagents.maximum_volume = required_volume - reagents.update_total() - else if(atom_flags & ATOM_FLAG_INITIALIZED) - create_reagents(required_volume) + if(atom_flags & ATOM_FLAG_INITIALIZED) + create_or_update_reagents(required_volume) else chem_volume = required_volume else diff --git a/code/modules/food/utensils/_utensil.dm b/code/modules/food/utensils/_utensil.dm index 856ecd502bfb..d04d06979d96 100644 --- a/code/modules/food/utensils/_utensil.dm +++ b/code/modules/food/utensils/_utensil.dm @@ -12,13 +12,14 @@ /obj/item/utensil - abstract_type = /obj/item/utensil - icon_state = ICON_STATE_WORLD - w_class = ITEM_SIZE_SMALL - origin_tech = @'{"materials":1}' - attack_verb = list("attacked", "stabbed", "poked") - material = /decl/material/solid/metal/aluminium - material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME + abstract_type = /obj/item/utensil + icon_state = ICON_STATE_WORLD + w_class = ITEM_SIZE_SMALL + origin_tech = @'{"materials":1}' + attack_verb = list("attacked", "stabbed", "poked") + material = /decl/material/solid/metal/aluminium + material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME + chem_volume = 5 var/obj/item/food/loaded_food var/utensil_flags @@ -32,7 +33,6 @@ if (prob(60)) default_pixel_y = rand(0, 4) reset_offsets(0) - create_reagents(5) set_extension(src, /datum/extension/tool/variable/simple, list( TOOL_RETRACTOR = TOOL_QUALITY_BAD, TOOL_HEMOSTAT = TOOL_QUALITY_MEDIOCRE diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 402232bc89d0..eebf86d0ccb9 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -285,15 +285,9 @@ var/global/list/_wood_materials = list( return TRUE var/obj/item/clothing/mask/smokable/cigarette/rolled/R = new(get_turf(src)) - R.chem_volume = max(R.reagents?.maximum_volume, reagents?.total_volume) - if(R.reagents) - R.reagents.maximum_volume = R.chem_volume - R.reagents.update_total() - else - R.create_reagents(R.chem_volume) - + R.create_or_update_reagents(max(R.reagents?.maximum_volume, reagents?.total_volume)) R.brand = "[src] handrolled in \the [used_item]." - reagents.trans_to_holder(R.reagents, R.chem_volume) + reagents.trans_to_holder(R.reagents, R.reagents.total_volume) to_chat(user, SPAN_NOTICE("You roll \the [src] into \the [used_item].")) user.put_in_active_hand(R) qdel(used_item) diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index 5108dff2b679..a4000ce56bba 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -10,6 +10,7 @@ uncreated_component_parts = null stat_immune = 0 atom_flags = ATOM_FLAG_OPEN_CONTAINER | ATOM_FLAG_CLIMBABLE | ATOM_FLAG_NO_CHEM_CHANGE + chem_volume = 200 var/mechanical = 1 // Set to 0 to stop it from drawing the alert lights. var/base_name = "tray" @@ -38,7 +39,8 @@ var/lastproduce = 0 // Last time tray was harvested var/closed_system // If set, the tray will attempt to take atmos from a pipe. var/force_update // Set this to bypass the cycle time check. - var/obj/temp_chem_holder // Something to hold reagents during process_reagents() + /// Something to hold reagents during process_reagents() + var/obj/effect/chem_holder/temp_chem_holder // Counter used by bees. var/pollen = 0 @@ -164,10 +166,7 @@ if(!mechanical) construct_state = /decl/machine_construction/noninteractive . = ..() - temp_chem_holder = new() - temp_chem_holder.create_reagents(10) - temp_chem_holder.atom_flags |= ATOM_FLAG_OPEN_CONTAINER - create_reagents(200) + temp_chem_holder = new(null, 10) if(mechanical) connect() update_icon() diff --git a/code/modules/implants/implant_types/chem.dm b/code/modules/implants/implant_types/chem.dm index b78de9f03523..b00656a0cbf6 100644 --- a/code/modules/implants/implant_types/chem.dm +++ b/code/modules/implants/implant_types/chem.dm @@ -5,6 +5,7 @@ var/global/list/chem_implants = list() desc = "Injects things." origin_tech = @'{"materials":1,"biotech":2}' known = TRUE + chem_volume = 50 /obj/item/implant/chem/get_data() return {" @@ -26,7 +27,6 @@ var/global/list/chem_implants = list() /obj/item/implant/chem/Initialize() . = ..() global.chem_implants += src - create_reagents(50) /obj/item/implant/chem/Destroy() . = ..() diff --git a/code/modules/mining/machinery/material_extractor.dm b/code/modules/mining/machinery/material_extractor.dm index 809c4af4ac04..6b19126688c1 100644 --- a/code/modules/mining/machinery/material_extractor.dm +++ b/code/modules/mining/machinery/material_extractor.dm @@ -17,15 +17,15 @@ var/dispense_amount = 50 // Since reactions and heating products may overfill the reagent tank, the reagent tank has 1.25x this volume. - var/static/max_liquid = 3000 + var/const/MAX_LIQUID = 3000 /obj/machinery/material_processing/extractor/Initialize() + chem_volume = round(MAX_LIQUID * 1.25) . = ..() if(!gas_contents) gas_contents = new(800) set_extension(src, /datum/extension/atmospherics_connection, FALSE, gas_contents) - create_reagents(round(1.25*max_liquid)) queue_temperature_atoms(src) return INITIALIZE_HINT_LATELOAD @@ -70,7 +70,7 @@ if(!use_power || (stat & (BROKEN|NOPOWER))) return - if(reagents?.total_volume >= max_liquid) + if(reagents?.total_volume >= MAX_LIQUID) return if(input_turf) @@ -245,7 +245,7 @@ var/is_liquid = reagent.phase_at_temperature(temperature, ONE_ATMOSPHERE) == MAT_PHASE_LIQUID data["reagents"] += list(list("label" = "[reagent.liquid_name] ([reagents.reagent_volumes[reagent]] U)", "index" = index, "liquid" = is_liquid)) - data["full"] = reagents.total_volume >= max_liquid + data["full"] = reagents.total_volume >= MAX_LIQUID data["gas_pressure"] = gas_contents?.return_pressure() return data diff --git a/code/modules/mining/machinery/material_smelter.dm b/code/modules/mining/machinery/material_smelter.dm index 43cd4b1cf63a..b5f609877800 100644 --- a/code/modules/mining/machinery/material_smelter.dm +++ b/code/modules/mining/machinery/material_smelter.dm @@ -5,6 +5,8 @@ icon_state = "furnace" use_ui_template = "material_processing_smeltery.tmpl" atom_flags = ATOM_FLAG_CLIMBABLE | ATOM_FLAG_OPEN_CONTAINER + chem_volume = INFINITY + var/show_all_materials = FALSE var/list/casting var/static/list/always_show_materials = list( @@ -21,7 +23,6 @@ /obj/machinery/material_processing/smeltery/Initialize() show_materials = always_show_materials.Copy() . = ..() - create_reagents(INFINITY) queue_temperature_atoms(src) // Update displayed materials diff --git a/code/modules/mob/living/living_powers.dm b/code/modules/mob/living/living_powers.dm index 874941029beb..6b30fafc0e25 100644 --- a/code/modules/mob/living/living_powers.dm +++ b/code/modules/mob/living/living_powers.dm @@ -33,7 +33,6 @@ var/turf/T = get_turf(src) var/obj/effect/effect/water/chempuff/chem = new(T) - chem.create_reagents(10) chem.add_to_reagents(/decl/material/liquid/zombie, 2) chem.set_up(get_step(T, dir), 2, 10) playsound(T, 'sound/hallucinations/wail.ogg', 20, 1) diff --git a/code/modules/mob/living/silicon/robot/modules/module_clerical.dm b/code/modules/mob/living/silicon/robot/modules/module_clerical.dm index a43672e98d20..e09c3548c825 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_clerical.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_clerical.dm @@ -62,7 +62,7 @@ /obj/item/robot_module/clerical/butler/finalize_emag() . = ..() if(emag) - var/datum/reagents/reagent = emag.create_reagents(50) + var/datum/reagents/reagent = emag.create_or_update_reagents(50) reagent.add_reagent(/decl/material/liquid/paralytics, 10) reagent.add_reagent(/decl/material/liquid/sedatives, 15) reagent.add_reagent(/decl/material/liquid/alcohol/beer, 20) diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 0097a98ff0c0..7d2788d0ca3a 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -457,10 +457,7 @@ rad_power = 12 atom_flags = ATOM_FLAG_OPEN_CONTAINER anchored = TRUE - -/obj/machinery/port_gen/pacman/super/potato/Initialize() - create_reagents(120) - . = ..() + chem_volume = 120 /obj/machinery/port_gen/pacman/super/potato/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index accaa0b35218..a1e484aa57e4 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -905,8 +905,7 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new qdel(reagent) return - if(!target.reagents) - target.create_reagents(FLUID_MAX_DEPTH) + target.create_or_update_reagents(FLUID_MAX_DEPTH) . = trans_to_holder(target.reagents, amount, multiplier, copy, defer_update = defer_update, transferred_phases = transferred_phases) // Deferred updates are presumably being done by SSfluids. @@ -987,6 +986,16 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new reagents = new/datum/reagents(max_vol, src) return reagents +/atom/proc/create_or_update_reagents(_vol, override_volume) + if(reagents) + if(override_volume) + reagents.maximum_volume = _vol // should we remove excess reagents here? + else + reagents.maximum_volume = max(reagents.maximum_volume, _vol) + reagents.update_total() + return reagents + return create_reagents(_vol) + /// Infinite reagent sink: nothing is ever actually added to it, useful for complex, filtered deletion of reagents without holder churn. /datum/reagents/sink diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index e2cf9e4abe10..af6fa27d09f8 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -17,6 +17,7 @@ base_type = /obj/machinery/chem_master atom_flags = ATOM_FLAG_OPEN_CONTAINER core_skill = SKILL_CHEMISTRY + chem_volume = 120 var/obj/item/chems/beaker = null var/obj/item/pill_bottle/loaded_pill_bottle = null @@ -27,16 +28,11 @@ var/list/client/has_sprites = list() var/max_pill_count = 20 var/sloppy = 1 //Whether reagents will not be fully purified (sloppy = 1) or there will be reagent loss (sloppy = 0) on reagent add. - var/reagent_limit = 120 var/bottle_label_color = COLOR_WHITE var/bottle_lid_color = COLOR_OFF_WHITE -/obj/machinery/chem_master/Initialize() - . = ..() - create_reagents(reagent_limit) - /obj/machinery/chem_master/proc/get_remaining_volume() - return clamp(reagent_limit - reagents.total_volume, 0, reagent_limit) + return reagents ? clamp(reagents.maximum_volume - reagents.total_volume, 0, reagents.maximum_volume) : 0 /obj/machinery/chem_master/attackby(var/obj/item/used_item, var/mob/user) diff --git a/code/modules/reagents/dispenser/cartridge.dm b/code/modules/reagents/dispenser/cartridge.dm index 872653d4804e..96dbb3859411 100644 --- a/code/modules/reagents/dispenser/cartridge.dm +++ b/code/modules/reagents/dispenser/cartridge.dm @@ -9,11 +9,14 @@ material = /decl/material/solid/stone/ceramic // Large, but inaccurate. Use a chem dispenser or beaker for accuracy. possible_transfer_amounts = @"[50,100]" + var/_reagent_label /obj/item/chems/chem_disp_cartridge/Initialize() . = ..() - if(reagents?.primary_reagent) - setLabel(reagents.get_primary_reagent_name()) + if(reagents?.primary_reagent && !_reagent_label) + _reagent_label = reagents.get_primary_reagent_name() + if(_reagent_label) + setLabel(_reagent_label) /obj/item/chems/chem_disp_cartridge/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index e0406f96dd66..3353a13f981f 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -115,8 +115,8 @@ V.update_icon() loaded_vial = V - reagents.maximum_volume = loaded_vial.reagents.maximum_volume - loaded_vial.reagents.trans_to_holder(reagents, chem_volume) + create_or_update_reagents(loaded_vial.reagents.maximum_volume, override_volume = TRUE) + loaded_vial.reagents.trans_to_holder(reagents, reagents.maximum_volume) if(user) user.visible_message(SPAN_NOTICE("[user] has loaded [V] into \the [src]."), SPAN_NOTICE("[usermessage]")) @@ -128,7 +128,7 @@ /obj/item/chems/hypospray/vial/proc/remove_vial(var/mob/user, var/swap_mode, var/should_update_icon = TRUE) if(!loaded_vial) return - reagents.trans_to_holder(loaded_vial.reagents,chem_volume) + reagents.trans_to_holder(loaded_vial.reagents, reagents.maximum_volume) reagents.maximum_volume = 0 loaded_vial.update_icon() if(user) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 427175a7493e..390eff88e405 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -73,7 +73,6 @@ set waitfor = FALSE var/obj/effect/effect/water/chempuff/D = new(get_turf(src)) - D.create_reagents(amount_per_transfer_from_this) if(QDELETED(src)) return reagents.trans_to_obj(D, amount_per_transfer_from_this) diff --git a/code/modules/scanners/mass_spectrometer.dm b/code/modules/scanners/mass_spectrometer.dm index 7e186c85abfe..b1b194944993 100644 --- a/code/modules/scanners/mass_spectrometer.dm +++ b/code/modules/scanners/mass_spectrometer.dm @@ -7,12 +7,9 @@ window_width = 550 window_height = 300 scan_sound = 'sound/effects/scanbeep.ogg' + chem_volume = 5 var/details = 0 -/obj/item/scanner/spectrometer/Initialize() - . = ..() - create_reagents(5) - /obj/item/scanner/spectrometer/on_reagent_change() if((. = ..())) update_icon() diff --git a/code/modules/sealant_gun/sealant_tank.dm b/code/modules/sealant_gun/sealant_tank.dm index 890b4746992b..05244c89b98e 100644 --- a/code/modules/sealant_gun/sealant_tank.dm +++ b/code/modules/sealant_gun/sealant_tank.dm @@ -17,8 +17,8 @@ . += SPAN_NOTICE("\The [src] has about [foam_charges] liter\s of sealant left.") /obj/item/sealant_tank/Initialize(ml, material_key) + chem_volume = max_foam_charges . = ..() - create_reagents(max_foam_charges) /obj/item/sealant_tank/mapped/Initialize() . = ..() diff --git a/code/modules/vehicles/engine.dm b/code/modules/vehicles/engine.dm index db7e0dfb8e42..423db9ca032e 100644 --- a/code/modules/vehicles/engine.dm +++ b/code/modules/vehicles/engine.dm @@ -79,6 +79,7 @@ icon_state = "engine_fuel" trail_type = /datum/effect/effect/system/trail/thermal atom_flags = ATOM_FLAG_OPEN_CONTAINER + chem_volume = 500 var/datum/reagents/combustion_chamber var/fuel_points = 0 //fuel points are determined by differing reagents @@ -88,7 +89,6 @@ /obj/item/engine/thermal/Initialize() . = ..() - create_reagents(500) combustion_chamber = new(15, global.temp_reagents_holder) /obj/item/engine/thermal/attackby(var/obj/item/used_item, var/mob/user) diff --git a/code/modules/xenoarcheaology/machinery/geosample_scanner.dm b/code/modules/xenoarcheaology/machinery/geosample_scanner.dm index 486a5eb5ea83..28ec8b72504c 100644 --- a/code/modules/xenoarcheaology/machinery/geosample_scanner.dm +++ b/code/modules/xenoarcheaology/machinery/geosample_scanner.dm @@ -12,6 +12,7 @@ uncreated_component_parts = null maximum_component_parts = list(/obj/item/stock_parts = 15) stat_immune = 0 + chem_volume = 500 //var/obj/item/chems/glass/coolant_container var/scanning = 0 @@ -52,10 +53,6 @@ /decl/material/liquid/adminordrazine = 2 ) -/obj/machinery/radiocarbon_spectrometer/Initialize() - . = ..() - create_reagents(500) - /obj/machinery/radiocarbon_spectrometer/interface_interact(var/mob/user) ui_interact(user) return TRUE diff --git a/code/unit_tests/chemistry_tests.dm b/code/unit_tests/chemistry_tests.dm index cdc9dae00d42..666048b0a0b4 100644 --- a/code/unit_tests/chemistry_tests.dm +++ b/code/unit_tests/chemistry_tests.dm @@ -11,7 +11,7 @@ var/turf/test_loc = get_safe_turf() var/atom/from = new donor_type(test_loc) - from.create_reagents(container_volume) + from.create_or_update_reagents(container_volume) from.add_to_reagents(/decl/material/liquid/water, container_volume) var/atom/target @@ -22,7 +22,7 @@ else target = new recipient_type(test_loc) if(!target.reagents) - target.create_reagents(container_volume) + target.create_or_update_reagents(container_volume) if(ismob(target)) var/mob/victim = target victim.death() // to prevent reagent processing diff --git a/code/unit_tests/food_tests.dm b/code/unit_tests/food_tests.dm index 44fc77c01f66..fc8affd9b6e1 100644 --- a/code/unit_tests/food_tests.dm +++ b/code/unit_tests/food_tests.dm @@ -63,8 +63,7 @@ seeds_by_tag[seed.grown_tag] = seed_name var/failures = list() - var/obj/container = new // dummy container for holding ingredients - container.create_reagents(1000) + var/obj/effect/chem_holder/container = new(null, 1000) // dummy container for holding ingredients var/static/list/all_recipe_categories = list( RECIPE_CATEGORY_MICROWAVE, RECIPE_CATEGORY_POT, diff --git a/mods/content/integrated_electronics/components/power_passive.dm b/mods/content/integrated_electronics/components/power_passive.dm index f33dab219d4d..7a855c5a366d 100644 --- a/mods/content/integrated_electronics/components/power_passive.dm +++ b/mods/content/integrated_electronics/components/power_passive.dm @@ -95,7 +95,8 @@ outputs = list("volume used" = IC_PINTYPE_NUMBER, "self reference" = IC_PINTYPE_REF) activators = list("push ref" = IC_PINTYPE_PULSE_IN) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/volume = 60 + chem_volume = 60 + var/list/fuel = list( /decl/material/gas/hydrogen = 50000, /decl/material/gas/hydrogen/deuterium = 50000, @@ -109,7 +110,6 @@ /obj/item/integrated_circuit/passive/power/chemical_cell/Initialize() . = ..() - create_reagents(volume) extended_desc +="But no fuel can be compared with blood of living human." diff --git a/mods/content/integrated_electronics/components/reagents.dm b/mods/content/integrated_electronics/components/reagents.dm index a89077170c70..3cce58ca5c6a 100644 --- a/mods/content/integrated_electronics/components/reagents.dm +++ b/mods/content/integrated_electronics/components/reagents.dm @@ -115,7 +115,7 @@ else direction_mode = IC_REAGENTS_INJECT if(isnum(new_amount)) - new_amount = clamp(new_amount, 0, chem_volume) + new_amount = clamp(new_amount, 0, reagents.maximum_volume) transfer_amount = new_amount diff --git a/nebula.dme b/nebula.dme index 01ed6a26c97e..cd846616a915 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1017,6 +1017,7 @@ #include "code\game\objects\compass\compass_waypoint.dm" #include "code\game\objects\effects\_effect.dm" #include "code\game\objects\effects\bump_teleporter.dm" +#include "code\game\objects\effects\chem_holder.dm" #include "code\game\objects\effects\cig_smoke.dm" #include "code\game\objects\effects\dirty_floor.dm" #include "code\game\objects\effects\effect_system.dm" From deecd2e91d2c187e3511476c2d12a449fe6d5084 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 2 Nov 2025 13:50:19 +1100 Subject: [PATCH 0904/1331] Addressing some changes requested on the chems PR. --- code/datums/extensions/milkable/milkable.dm | 4 ++-- code/game/objects/items/weapons/storage/fancy/cigar.dm | 3 ++- code/game/objects/structures/fires.dm | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/code/datums/extensions/milkable/milkable.dm b/code/datums/extensions/milkable/milkable.dm index caa797e18786..a377c2293ce9 100644 --- a/code/datums/extensions/milkable/milkable.dm +++ b/code/datums/extensions/milkable/milkable.dm @@ -85,7 +85,7 @@ to_chat(user, SPAN_WARNING("Wait for \the [critter] to stop moving before you try milking it.")) return TRUE - if(container.reagents.total_volume >= container.reagents.maximum_volume) + if(REAGENTS_FREE_SPACE(container.reagents) <= 0) to_chat(user, SPAN_WARNING("\The [container] is full.")) return TRUE @@ -112,7 +112,7 @@ to_chat(user, SPAN_WARNING("\The [critter]'s udder is dry. Wait a little longer.")) return TRUE - if(container.reagents.total_volume >= container.reagents.maximum_volume) + if(REAGENTS_FREE_SPACE(container.reagents) <= 0) to_chat(user, SPAN_NOTICE("\The [container] is full.")) return TRUE diff --git a/code/game/objects/items/weapons/storage/fancy/cigar.dm b/code/game/objects/items/weapons/storage/fancy/cigar.dm index 5128bed573ae..a24b3da8a2b4 100644 --- a/code/game/objects/items/weapons/storage/fancy/cigar.dm +++ b/code/game/objects/items/weapons/storage/fancy/cigar.dm @@ -15,7 +15,8 @@ storage = /datum/storage/box/cigar /obj/item/box/fancy/cigar/Initialize(ml, material_key) - chem_volume = 10 * max(1, /datum/storage/box/cigar::storage_slots) + if(istype(storage)) + chem_volume = 10 * max(1, storage.storage_slots) . = ..() /obj/item/box/fancy/cigar/WillContain() diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index 6c1ae805e1ca..273c685709aa 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -374,12 +374,12 @@ if(reagent.accelerant_value <= FUEL_VALUE_SUPPRESSANT && reagent.phase_at_temperature(get_effective_burn_temperature(), ambient_pressure) == MAT_PHASE_GAS) do_steam = TRUE - var/result_volume = NONUNIT_CEILING(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_GAS_MOLE, 0.1) - var/list/waste_products = burn_material(reagent, result_volume) + var/result_amount = NONUNIT_CEILING(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_GAS_MOLE, 0.1) + var/list/waste_products = burn_material(reagent, result_amount) if(!isnull(waste_products)) for(var/product in waste_products) waste[product] += waste_products[product] - reagents.remove_reagent(reagent.type, result_volume) + reagents.remove_reagent(reagent.type, result_amount) dump_waste_products(loc, waste) From d1e4e2fa5954d0198b41b0248b0b3155cd894c66 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Tue, 4 Nov 2025 09:33:15 +1100 Subject: [PATCH 0905/1331] air_volume -> total_volume, gas_volume --- code/game/machinery/alarm.dm | 2 +- code/game/machinery/atmoalter/canister.dm | 10 +++---- .../atmoalter/portable_atmospherics.dm | 6 ++-- code/game/machinery/atmoalter/pump.dm | 6 ++-- code/game/machinery/atmoalter/scrubber.dm | 6 ++-- code/game/machinery/cracker.dm | 2 +- code/game/objects/items/devices/oxycandle.dm | 8 ++--- .../objects/items/devices/transfer_valve.dm | 6 ++-- code/game/objects/items/rescuebag.dm | 2 +- .../objects/items/weapons/tanks/tank_types.dm | 12 ++++---- .../game/objects/items/weapons/tanks/tanks.dm | 12 ++++---- code/modules/ZAS/Fire.dm | 12 ++++---- code/modules/ZAS/Zone.dm | 6 ++-- code/modules/admin/verbs/debug.dm | 2 +- code/modules/atmospherics/atmos_primitives.dm | 28 ++++++++--------- .../binary_devices/binary_atmos_base.dm | 4 +-- .../components/binary_devices/circulator.dm | 8 ++--- .../binary_devices/oxyregenerator.dm | 2 +- .../components/binary_devices/passive_gate.dm | 16 +++++----- .../components/binary_devices/pipeturbine.dm | 10 +++---- .../components/binary_devices/pump.dm | 10 +++---- .../components/omni_devices/_omni_extras.dm | 2 +- .../components/omni_devices/filter.dm | 10 +++---- .../components/omni_devices/mixer.dm | 8 ++--- .../trinary_devices/trinary_base.dm | 6 ++-- .../components/unary/cold_sink.dm | 2 +- .../components/unary/heat_source.dm | 2 +- .../components/unary/outlet_injector.dm | 6 ++-- .../atmospherics/components/unary/tank.dm | 6 ++-- .../components/unary/unary_base.dm | 2 +- .../components/unary/vent_pump.dm | 8 ++--- .../components/unary/vent_scrubber.dm | 8 ++--- .../atmospherics/datum_pipe_network.dm | 6 ++-- code/modules/atmospherics/datum_pipeline.dm | 22 +++++++------- code/modules/atmospherics/he_pipes.dm | 2 +- code/modules/atmospherics/pipes.dm | 12 ++++---- code/modules/hydroponics/trays/tray.dm | 2 +- code/modules/mechs/components/body.dm | 6 ++-- code/modules/mob/living/human/life.dm | 2 +- code/modules/mob/living/living_breath.dm | 2 +- code/modules/multiz/pipes.dm | 2 +- code/modules/organs/internal/lungs.dm | 2 +- .../ships/device_types/gas_thruster.dm | 6 ++-- code/modules/power/stirling.dm | 2 +- .../projectiles/guns/launcher/pneumatic.dm | 2 +- .../reagents/reagent_containers/borghypo.dm | 2 +- code/modules/recycling/disposal.dm | 2 +- code/modules/scanners/gas.dm | 2 +- code/modules/xgm/xgm_gas_mixture.dm | 30 +++++++++---------- .../lost_supply_base/lost_supply_base.dmm | 2 +- maps/away/unishi/unishi-1.dmm | 2 +- maps/exodus/exodus-2.dmm | 4 +-- .../components/input.dm | 2 +- mods/content/plant_dissection/grown.dm | 2 +- mods/species/ascent/items/rig.dm | 2 +- 55 files changed, 174 insertions(+), 174 deletions(-) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 884b99b45fd0..1770f09178cc 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -272,7 +272,7 @@ environment.merge(gas) /obj/machinery/alarm/proc/overall_danger_level(var/datum/gas_mixture/environment) - var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.air_volume + var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.total_volume var/environment_pressure = environment.return_pressure() var/other_moles = 0 diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index b5b96c98756a..bb0801f80b5b 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -9,7 +9,7 @@ construct_state = /decl/machine_construction/pipe/welder stat_immune = NOSCREEN | NOINPUT | NOPOWER start_pressure = 45 ATM - air_volume = 1000 + gas_volume = 1000 interact_offline = TRUE matter = list( /decl/material/solid/metal/steel = 10 * SHEET_MATERIAL_AMOUNT @@ -195,7 +195,7 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) if((air_contents.temperature > 0) && (pressure_delta > 0)) var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) - transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.air_volume)*air_contents.total_moles) //flow rate limit + transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.total_volume)*air_contents.total_moles) //flow rate limit pump_gas_passive(src, air_contents, environment, transfer_moles) can_label = (air_contents?.return_pressure() < 1) @@ -207,13 +207,13 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) /obj/machinery/portable_atmospherics/canister/proc/return_temperature() var/datum/gas_mixture/GM = return_air() - if(GM && GM.air_volume>0) + if(GM?.total_volume>0) return GM.temperature return 0 /obj/machinery/portable_atmospherics/canister/proc/return_pressure() var/datum/gas_mixture/GM = return_air() - if(GM && GM.air_volume>0) + if(GM?.total_volume>0) return GM.return_pressure() return 0 @@ -241,7 +241,7 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) //Can not have a pressure delta that would cause environment pressure > tank pressure var/transfer_moles = 0 if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*thejetpack.air_volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas + transfer_moles = pressure_delta*thejetpack.total_volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) thejetpack.merge(removed) to_chat(user, "You pulse-pressurize your jetpack from the tank.") diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 5ef82168e9d9..3de7c00f4368 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -6,7 +6,7 @@ var/datum/gas_mixture/air_contents = new var/obj/item/tank/holding - var/air_volume = 0 + var/gas_volume = 0 var/destroyed = 0 var/start_pressure = ONE_ATMOSPHERE @@ -19,7 +19,7 @@ /obj/machinery/portable_atmospherics/Initialize() ..() - air_contents.air_volume = air_volume + air_contents.total_volume = gas_volume air_contents.temperature = T20C @@ -52,7 +52,7 @@ /decl/material/gas/nitrogen = N2STANDARD * MolesForPressure()) /obj/machinery/portable_atmospherics/proc/MolesForPressure(var/target_pressure = start_pressure) - return (target_pressure * air_contents.air_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + return (target_pressure * air_contents.total_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) /obj/machinery/portable_atmospherics/on_update_icon() return null diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 4febead3e39b..492f0b0c26f5 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -15,7 +15,7 @@ var/pressuremin = 0 var/pressuremax = 10 ATM - air_volume = 1000 + gas_volume = 1000 power_rating = 7500 //7500 W ~ 10 HP power_losses = 150 @@ -76,11 +76,11 @@ var/air_temperature if(direction_out) pressure_delta = target_pressure - environment.return_pressure() - output_volume = environment.air_volume * environment.group_multiplier + output_volume = environment.total_volume * environment.group_multiplier air_temperature = environment.temperature? environment.temperature : air_contents.temperature else pressure_delta = environment.return_pressure() - target_pressure - output_volume = air_contents.air_volume * air_contents.group_multiplier + output_volume = air_contents.total_volume * air_contents.group_multiplier air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 0d93d05998fd..b6989e112bdd 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -10,7 +10,7 @@ movable_flags = MOVABLE_FLAG_WHEELED var/volume_rate = 800 - air_volume = 750 + gas_volume = 750 power_rating = 7500 //7500 W ~ 10 HP power_losses = 150 @@ -67,7 +67,7 @@ else environment = loc.return_air() - var/transfer_moles = min(1, volume_rate/environment.air_volume)*environment.total_moles + var/transfer_moles = min(1, volume_rate/environment.total_volume)*environment.total_moles power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) @@ -151,7 +151,7 @@ name = "huge air scrubber" icon_state = "scrubber:0" anchored = TRUE - air_volume = 50000 + gas_volume = 50000 volume_rate = 5000 base_type = /obj/machinery/portable_atmospherics/powered/scrubber/huge diff --git a/code/game/machinery/cracker.dm b/code/game/machinery/cracker.dm index a8287bd5038c..2de58f1e211c 100644 --- a/code/game/machinery/cracker.dm +++ b/code/game/machinery/cracker.dm @@ -6,7 +6,7 @@ density = TRUE anchored = TRUE waterproof = TRUE - air_volume = 5000 + gas_volume = 5000 use_power = POWER_USE_IDLE idle_power_usage = 100 active_power_usage = 10000 diff --git a/code/game/objects/items/devices/oxycandle.dm b/code/game/objects/items/devices/oxycandle.dm index bed530e8b26e..719223e6ec8f 100644 --- a/code/game/objects/items/devices/oxycandle.dm +++ b/code/game/objects/items/devices/oxycandle.dm @@ -33,10 +33,10 @@ update_icon() playsound(src.loc, activation_sound, 75, 1) air_contents = new /datum/gas_mixture() - air_contents.air_volume = 200 //liters + air_contents.total_volume = 200 //liters air_contents.temperature = T20C var/const/OXYGEN_FRACTION = 1 // separating out the constant so it's clearer why it exists and how to modify it later - air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.air_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) + air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.total_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) START_PROCESSING(SSprocessing, src) // Process of Oxygen candles releasing air. Makes 200 volume of oxygen @@ -56,7 +56,7 @@ pos.hotspot_expose(1500, 5) var/datum/gas_mixture/environment = loc.return_air() var/pressure_delta = target_pressure - environment.return_pressure() - var/output_volume = environment.air_volume * environment.group_multiplier + var/output_volume = environment.total_volume * environment.group_multiplier var/air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) @@ -65,7 +65,7 @@ environment.merge(removed) candle_volume -= 200 var/const/OXYGEN_FRACTION = 1 // separating out the constant so it's clearer why it exists and how to modify it later - air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.air_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) + air_contents.adjust_gas(/decl/material/gas/oxygen, OXYGEN_FRACTION * (target_pressure * air_contents.total_volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)) /obj/item/oxycandle/on_update_icon() . = ..() diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 6d007fa27283..31aaf31b8044 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -175,7 +175,7 @@ /obj/item/transfer_valve/proc/merge_gases() if(valve_open) return - tank_two.air_contents.air_volume += tank_one.air_contents.air_volume + tank_two.air_contents.total_volume += tank_one.air_contents.total_volume var/datum/gas_mixture/temp = tank_one.remove_air_ratio(1) tank_two.assume_air(temp) valve_open = 1 @@ -189,9 +189,9 @@ if(QDELETED(tank_one) || QDELETED(tank_two)) return - var/ratio1 = tank_one.air_contents.air_volume/tank_two.air_contents.air_volume + var/ratio1 = tank_one.air_contents.total_volume/tank_two.air_contents.total_volume var/datum/gas_mixture/temp = tank_two.remove_air_ratio(ratio1) - tank_two.air_contents.air_volume -= tank_one.air_contents.air_volume + tank_two.air_contents.total_volume -= tank_one.air_contents.total_volume tank_one.assume_air(temp) /* diff --git a/code/game/objects/items/rescuebag.dm b/code/game/objects/items/rescuebag.dm index 565d538eef84..e4c56dafd6bb 100644 --- a/code/game/objects/items/rescuebag.dm +++ b/code/game/objects/items/rescuebag.dm @@ -70,7 +70,7 @@ /obj/structure/closet/body_bag/rescue/Initialize() . = ..() atmo = new() - atmo.air_volume = 0.1*CELL_VOLUME + atmo.total_volume = 0.1*CELL_VOLUME START_PROCESSING(SSobj, src) /obj/structure/closet/body_bag/rescue/Destroy() diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 2792f2dca8a2..13c9162b87f9 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -16,7 +16,7 @@ icon = 'icons/obj/items/tanks/tank_blue.dmi' distribute_pressure = ONE_ATMOSPHERE*O2STANDARD starting_pressure = list(/decl/material/gas/oxygen = 6 ATM) - air_volume = 180 + gas_volume = 180 /obj/item/tank/oxygen/yellow desc = "A tank of oxygen. This one is yellow." @@ -37,7 +37,7 @@ desc = "Mixed anyone?" icon = 'icons/obj/items/tanks/tank_blue.dmi' starting_pressure = list(/decl/material/gas/oxygen = (6 ATM) * O2STANDARD, /decl/material/gas/nitrogen = (6 ATM) * N2STANDARD) - air_volume = 180 + gas_volume = 180 /* * Hydrogen @@ -69,7 +69,7 @@ attack_cooldown = DEFAULT_WEAPON_COOLDOWN melee_accuracy_bonus = -10 distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - air_volume = 40 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) + gas_volume = 40 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) /obj/item/tank/emergency/oxygen name = "emergency oxygen tank" @@ -81,13 +81,13 @@ /obj/item/tank/emergency/oxygen/engi name = "extended-capacity emergency oxygen tank" icon = 'icons/obj/items/tanks/tank_emergency_engineer.dmi' - air_volume = 60 + gas_volume = 60 /obj/item/tank/emergency/oxygen/double name = "double emergency oxygen tank" icon = 'icons/obj/items/tanks/tank_emergency_double.dmi' gauge_icon = "indicator_emergency_double" - air_volume = 90 + gas_volume = 90 w_class = ITEM_SIZE_NORMAL /obj/item/tank/emergency/oxygen/double/red //firefighting tank, fits on belt, back or suitslot @@ -105,4 +105,4 @@ icon = 'icons/obj/items/tanks/tank_red.dmi' distribute_pressure = ONE_ATMOSPHERE*O2STANDARD starting_pressure = list(/decl/material/gas/nitrogen = 10 ATM) - air_volume = 180 \ No newline at end of file + gas_volume = 180 \ No newline at end of file diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index fa7a80942993..9af831affc7d 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -42,7 +42,7 @@ var/global/list/global/tank_gauge_cache = list() var/maxintegrity = 20 var/valve_welded = 0 var/obj/item/tankassemblyproxy/proxyassembly - var/air_volume = 70 + var/gas_volume = 70 //Used by _onclick/hud/screen_objects.dm internals to determine if someone has messed with our tank or not. //If they have and we haven't scanned it with the PDA or gas analyzer then we might just breath whatever they put in it. var/manipulated_by = null @@ -56,9 +56,9 @@ var/global/list/global/tank_gauge_cache = list() proxyassembly = new /obj/item/tankassemblyproxy(src) proxyassembly.tank = src - air_contents = new /datum/gas_mixture(air_volume, T20C) + air_contents = new /datum/gas_mixture(gas_volume, T20C) for(var/gas in starting_pressure) - air_contents.adjust_gas(gas, starting_pressure[gas]*air_volume/(R_IDEAL_GAS_EQUATION*T20C), 0) + air_contents.adjust_gas(gas, starting_pressure[gas]*gas_volume/(R_IDEAL_GAS_EQUATION*T20C), 0) air_contents.update_values() START_PROCESSING(SSobj, src) @@ -366,7 +366,7 @@ var/global/list/global/tank_gauge_cache = list() var/datum/gas_mixture/removed = remove_air(distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) if(removed) - removed.air_volume = volume_to_return + removed.total_volume = volume_to_return return removed /obj/item/tank/Process() @@ -418,7 +418,7 @@ var/global/list/global/tank_gauge_cache = list() pressure = air_contents.return_pressure() var/strength = ((pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE) - var/mult = ((air_contents.air_volume/140)**(1/2)) * (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + var/mult = ((air_contents.total_volume/140)**(1/2)) * (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles //tanks appear to be experiencing a reduction on scale of about 0.64 total moles var/turf/T = get_turf(src) @@ -514,7 +514,7 @@ var/global/list/global/tank_gauge_cache = list() desc = initial(tank_copy.desc) icon = initial(tank_copy.icon) icon_state = initial(tank_copy.icon_state) - air_volume = initial(tank_copy.air_volume) + gas_volume = initial(tank_copy.gas_volume) // Set up explosive mix. air_contents.gas[DEFAULT_GAS_ACCELERANT] = 4 + rand(4) diff --git a/code/modules/ZAS/Fire.dm b/code/modules/ZAS/Fire.dm index fe5c981c7890..8e68d92a22f9 100644 --- a/code/modules/ZAS/Fire.dm +++ b/code/modules/ZAS/Fire.dm @@ -112,15 +112,15 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin for(var/mob/living/L in loc) L.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the mobs! - loc.fire_act(air_contents, air_contents.temperature, air_contents.air_volume) + loc.fire_act(air_contents, air_contents.temperature, air_contents.total_volume) for(var/atom/A in loc) - A.fire_act(air_contents, air_contents.temperature, air_contents.air_volume) + A.fire_act(air_contents, air_contents.temperature, air_contents.total_volume) // prioritize nearby fuel overlays first for(var/direction in global.cardinal) var/turf/enemy_tile = get_step(my_tile, direction) if(istype(enemy_tile) && enemy_tile.reagents) - enemy_tile.hotspot_expose(air_contents.temperature, air_contents.air_volume) + enemy_tile.hotspot_expose(air_contents.temperature, air_contents.total_volume) //spread for(var/direction in global.cardinal) @@ -141,7 +141,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin enemy_tile.create_fire(firelevel) else - enemy_tile.adjacent_fire_act(loc, air_contents, air_contents.temperature, air_contents.air_volume) + enemy_tile.adjacent_fire_act(loc, air_contents, air_contents.temperature, air_contents.total_volume) animate(src, color = fire_color(air_contents.temperature), 5) set_light(l_color = color) @@ -209,8 +209,8 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin var/reaction_limit = min(total_oxidizers*(FIRE_REACTION_FUEL_AMOUNT/FIRE_REACTION_OXIDIZER_AMOUNT), total_fuel) //stoichiometric limit //vapour fuels are extremely volatile! The reaction progress is a percentage of the total fuel (similar to old zburn).) - var/firelevel = calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, air_volume*group_multiplier) / vsc.fire_firelevel_multiplier - var/min_burn = 0.30*air_volume*group_multiplier/CELL_VOLUME //in moles - so that fires with very small gas concentrations burn out fast + var/firelevel = calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, total_volume*group_multiplier) / vsc.fire_firelevel_multiplier + var/min_burn = 0.30*total_volume*group_multiplier/CELL_VOLUME //in moles - so that fires with very small gas concentrations burn out fast var/total_reaction_progress = min(max(min_burn, firelevel*total_fuel)*FIRE_GAS_BURNRATE_MULT, total_fuel) var/used_fuel = min(total_reaction_progress, reaction_limit) var/used_oxidizers = used_fuel*(FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT) diff --git a/code/modules/ZAS/Zone.dm b/code/modules/ZAS/Zone.dm index c62020538391..cac84cb69bf5 100644 --- a/code/modules/ZAS/Zone.dm +++ b/code/modules/ZAS/Zone.dm @@ -57,7 +57,7 @@ Class Procs: SSair.add_zone(src) air.temperature = TCMB air.group_multiplier = 1 - air.air_volume = CELL_VOLUME + air.total_volume = CELL_VOLUME /zone/proc/add(turf/T) #ifdef ZASDBG @@ -136,7 +136,7 @@ Class Procs: CHECK_TICK /zone/proc/add_tile_air(datum/gas_mixture/tile_air) - //air.air_volume += CELL_VOLUME + //air.total_volume += CELL_VOLUME air.group_multiplier = 1 air.multiply(contents.len) air.merge(tile_air) @@ -200,7 +200,7 @@ Class Procs: for(var/g in air.gas) var/decl/material/mat = GET_DECL(g) to_chat(M, "[capitalize(mat.gas_name)]: [air.gas[g]]") - to_chat(M, "P: [air.return_pressure()] kPa V: [air.air_volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)") + to_chat(M, "P: [air.return_pressure()] kPa V: [air.total_volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)") to_chat(M, "O2 per N2: [(air.gas[/decl/material/gas/nitrogen] ? air.gas[/decl/material/gas/oxygen]/air.gas[/decl/material/gas/nitrogen] : "N/A")] Moles: [air.total_moles]") to_chat(M, "Simulated: [contents.len] ([air.group_multiplier])") to_chat(M, "Edges: [length(edges)]") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index a76b2b3ef42c..8ff55c421dea 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -17,7 +17,7 @@ t += "Temperature: [env.temperature]\n" t += "Pressure: [env.return_pressure()]kPa\n" for(var/g in env.gas) - t += "[g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.air_volume]kPa\n" + t += "[g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.total_volume]kPa\n" usr.show_message(t, 1) SSstatistics.add_field_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/atmospherics/atmos_primitives.dm b/code/modules/atmospherics/atmos_primitives.dm index d5f55ac7c74f..6a09490b3251 100644 --- a/code/modules/atmospherics/atmos_primitives.dm +++ b/code/modules/atmospherics/atmos_primitives.dm @@ -45,7 +45,7 @@ //Update flow rate meter if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + A.last_flow_rate = (transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here if (A.debug) A.visible_message("[A]: source entropy: [round(source.specific_entropy(), 0.01)] J/Kmol --> sink entropy: [round(sink.specific_entropy(), 0.01)] J/Kmol") @@ -55,7 +55,7 @@ if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + P.last_flow_rate = (transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(transfer_moles) if (!removed) //Just in case @@ -86,13 +86,13 @@ //Update flow rate meter if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + A.last_flow_rate = (transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here if (A.debug) A.visible_message("[A]: moles transferred = [transfer_moles] mol") if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + P.last_flow_rate = (transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(transfer_moles) if(!removed) //Just in case @@ -148,10 +148,10 @@ //Update flow rate var if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here var/power_draw = 0 for (var/g in filtering) @@ -217,10 +217,10 @@ //Update flow rate var if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(total_transfer_moles) if (!removed) //Just in case @@ -290,10 +290,10 @@ //Update Flow Rate var if (istype(M, /obj/machinery/atmospherics)) var/obj/machinery/atmospherics/A = M - A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here if (istype(M, /obj/machinery/portable_atmospherics)) var/obj/machinery/portable_atmospherics/P = M - P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.air_volume //group_multiplier gets divided out here + P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(total_transfer_moles) if (!removed) //Just in case @@ -350,7 +350,7 @@ source_specific_power[source] = calculate_specific_power(source, sink)*mix_ratio/ATMOS_FILTER_EFFICIENCY total_specific_power += source_specific_power[source] - total_input_volume += source.air_volume + total_input_volume += source.total_volume total_input_moles += source.total_moles if (total_mixing_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing @@ -429,7 +429,7 @@ /proc/calculate_transfer_moles(datum/gas_mixture/source, datum/gas_mixture/sink, var/pressure_delta, var/sink_volume_mod=0) if(source.temperature == 0 || source.total_moles == 0) return 0 - var/output_volume = (sink.air_volume * sink.group_multiplier) + sink_volume_mod + var/output_volume = (sink.total_volume * sink.group_multiplier) + sink_volume_mod var/source_total_moles = source.total_moles * source.group_multiplier var/air_temperature = source.temperature @@ -448,8 +448,8 @@ if(source.temperature == 0) return 0 //Make the approximation that the sink temperature is unchanged after transferring gas - var/source_volume = source.air_volume * source.group_multiplier - var/sink_volume = sink.air_volume * sink.group_multiplier + var/source_volume = source.total_volume * source.group_multiplier + var/sink_volume = sink.total_volume * sink.group_multiplier var/source_pressure = source.return_pressure() var/sink_pressure = sink.return_pressure() diff --git a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm index a0323b68ecad..70ccb935f755 100644 --- a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm +++ b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm @@ -12,8 +12,8 @@ air1 = new air2 = new - air1.air_volume = 200 - air2.air_volume = 200 + air1.total_volume = 200 + air2.total_volume = 200 . = ..() /obj/machinery/atmospherics/binary/air_in_dir(direction) diff --git a/code/modules/atmospherics/components/binary_devices/circulator.dm b/code/modules/atmospherics/components/binary_devices/circulator.dm index 58964df51c3d..723d76e56c0d 100644 --- a/code/modules/atmospherics/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/components/binary_devices/circulator.dm @@ -31,7 +31,7 @@ /obj/machinery/atmospherics/binary/circulator/Initialize() . = ..() desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." - air1.air_volume = 400 + air1.total_volume = 400 /obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() var/datum/gas_mixture/removed @@ -45,11 +45,11 @@ if(air1.temperature > 0 && last_pressure_delta > 5) //Calculate necessary moles to transfer using PV = nRT - recent_moles_transferred = (last_pressure_delta*input.air_volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself - volume_capacity_used = min( (last_pressure_delta*input.air_volume/3)/(input_starting_pressure*air1.air_volume) , 1) //how much of the gas in the input air volume is consumed + recent_moles_transferred = (last_pressure_delta*input.total_volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself + volume_capacity_used = min( (last_pressure_delta*input.total_volume/3)/(input_starting_pressure*air1.total_volume) , 1) //how much of the gas in the input air volume is consumed //Calculate energy generated from kinetic turbine - stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * input.air_volume , input_starting_pressure*air1.air_volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency + stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * input.total_volume , input_starting_pressure*air1.total_volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency //Actually transfer the gas removed = air1.remove(recent_moles_transferred) diff --git a/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm b/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm index 0b505490c25e..7061e5bab0e6 100644 --- a/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm +++ b/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm @@ -90,7 +90,7 @@ var/pressure_delta = target_pressure - air2.return_pressure() if (pressure_delta > 0.01 && inner_tank.temperature > 0) var/datum/pipe_network/output = network_in_dir(dir) - var/transfer_moles = calculate_transfer_moles(inner_tank, air2, pressure_delta, output?.air_volume) + var/transfer_moles = calculate_transfer_moles(inner_tank, air2, pressure_delta, output?.total_volume) power_draw = pump_gas(src, inner_tank, air2, transfer_moles, power_rating*power_setting) if (power_draw >= 0) last_power_draw = power_draw diff --git a/code/modules/atmospherics/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/components/binary_devices/passive_gate.dm index 7573cd540fb1..e34784fb7ee1 100644 --- a/code/modules/atmospherics/components/binary_devices/passive_gate.dm +++ b/code/modules/atmospherics/components/binary_devices/passive_gate.dm @@ -52,8 +52,8 @@ /obj/machinery/atmospherics/binary/passive_gate/Initialize() . = ..() - air1.air_volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - air2.air_volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air1.total_volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air2.total_volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 /obj/machinery/atmospherics/binary/passive_gate/on_update_icon() icon_state = (unlocked && flowing)? "on" : "off" @@ -87,7 +87,7 @@ flowing = 1 //flow rate limit - var/transfer_moles = (set_flow_rate/air1.air_volume)*air1.total_moles + var/transfer_moles = (set_flow_rate/air1.total_volume)*air1.total_moles //Figure out how much gas to transfer to meet the target pressure. switch (regulate_mode) @@ -95,7 +95,7 @@ transfer_moles = min(transfer_moles, air1.total_moles*(pressure_delta/input_starting_pressure)) if (REGULATE_OUTPUT) var/datum/pipe_network/output = network_in_dir(dir) - transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, output?.air_volume)) + transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, output?.total_volume)) //pump_gas() will return a negative number if no flow occurred returnval = pump_gas_passive(src, air1, air2, transfer_moles) @@ -174,11 +174,11 @@ set_flow_rate = 0 . = TOPIC_REFRESH if ("max") - set_flow_rate = air1.air_volume + set_flow_rate = air1.total_volume . = TOPIC_REFRESH if ("set") - var/new_flow_rate = input(user, "Enter new flow rate limit (0-[air1.air_volume]kPa)","Flow Rate Control",set_flow_rate) as num - set_flow_rate = clamp(new_flow_rate, 0, air1.air_volume) + var/new_flow_rate = input(user, "Enter new flow rate limit (0-[air1.total_volume]kPa)","Flow Rate Control",set_flow_rate) as num + set_flow_rate = clamp(new_flow_rate, 0, air1.total_volume) . = TOPIC_REFRESH /obj/machinery/atmospherics/binary/passive_gate/proc/toggle_unlocked() @@ -219,7 +219,7 @@ return machine.set_flow_rate /decl/public_access/public_variable/passive_gate_flow_rate/write_var(obj/machinery/atmospherics/binary/passive_gate/machine, new_value) - new_value = clamp(new_value, 0, machine.air1?.air_volume) + new_value = clamp(new_value, 0, machine.air1?.total_volume) . = ..() if(.) machine.set_flow_rate = new_value diff --git a/code/modules/atmospherics/components/binary_devices/pipeturbine.dm b/code/modules/atmospherics/components/binary_devices/pipeturbine.dm index 53022013e628..3891c252af87 100644 --- a/code/modules/atmospherics/components/binary_devices/pipeturbine.dm +++ b/code/modules/atmospherics/components/binary_devices/pipeturbine.dm @@ -21,9 +21,9 @@ /obj/machinery/atmospherics/pipeturbine/Initialize() . = ..() - air_in.air_volume = 200 - air_out.air_volume = 800 - volume_ratio = air_in.air_volume / (air_in.air_volume + air_out.air_volume) + air_in.total_volume = 200 + air_out.total_volume = 800 + volume_ratio = air_in.total_volume / (air_in.total_volume + air_out.total_volume) /obj/machinery/atmospherics/pipeturbine/get_initialize_directions() switch(dir) @@ -48,11 +48,11 @@ kin_energy *= 1 - kin_loss dP = max(air_in.return_pressure() - air_out.return_pressure(), 0) if(dP > 10) - kin_energy += 1/ADIABATIC_EXPONENT * dP * air_in.air_volume * (1 - volume_ratio**ADIABATIC_EXPONENT) * efficiency + kin_energy += 1/ADIABATIC_EXPONENT * dP * air_in.total_volume * (1 - volume_ratio**ADIABATIC_EXPONENT) * efficiency air_in.temperature *= volume_ratio**ADIABATIC_EXPONENT var/datum/gas_mixture/air_all = new - air_all.air_volume = air_in.air_volume + air_out.air_volume + air_all.total_volume = air_in.total_volume + air_out.total_volume air_all.merge(air_in.remove_ratio(1)) air_all.merge(air_out.remove_ratio(1)) diff --git a/code/modules/atmospherics/components/binary_devices/pump.dm b/code/modules/atmospherics/components/binary_devices/pump.dm index 60fca50ad150..27fe8910c85f 100644 --- a/code/modules/atmospherics/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/components/binary_devices/pump.dm @@ -5,9 +5,9 @@ node1, air1, network1 correspond to input node2, air2, network2 correspond to output Thus, the two variables affect pump operation are set in New(): - air1.air_volume + air1.total_volume This is the volume of gas available to the pump that may be transfered to the output - air2.air_volume + air2.total_volume Higher quantities of this cause more air to be perfected later but overall network volume is also increased as this increases... */ @@ -61,8 +61,8 @@ Thus, the two variables affect pump operation are set in New(): /obj/machinery/atmospherics/binary/pump/Initialize() . = ..() - air1.air_volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + air1.total_volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.total_volume = ATMOS_DEFAULT_VOLUME_PUMP /obj/machinery/atmospherics/binary/pump/on icon_state = "map_on" @@ -93,7 +93,7 @@ Thus, the two variables affect pump operation are set in New(): if(pressure_delta > 0.01 && air1.temperature > 0) //Figure out how much gas to transfer to meet the target pressure. var/datum/pipe_network/output = network_in_dir(dir) - var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, output?.air_volume) + var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, output?.total_volume) power_draw = pump_gas(src, air1, air2, transfer_moles, power_rating) if(transfer_moles > 0) diff --git a/code/modules/atmospherics/components/omni_devices/_omni_extras.dm b/code/modules/atmospherics/components/omni_devices/_omni_extras.dm index 44357fdc75df..9aeb0d2eb576 100644 --- a/code/modules/atmospherics/components/omni_devices/_omni_extras.dm +++ b/code/modules/atmospherics/components/omni_devices/_omni_extras.dm @@ -30,7 +30,7 @@ if(istype(M)) master = M air = new - air.air_volume = 200 + air.total_volume = 200 /datum/omni_port/Destroy() QDEL_NULL(network) diff --git a/code/modules/atmospherics/components/omni_devices/filter.dm b/code/modules/atmospherics/components/omni_devices/filter.dm index 198f93531b6d..64d99bb5140c 100644 --- a/code/modules/atmospherics/components/omni_devices/filter.dm +++ b/code/modules/atmospherics/components/omni_devices/filter.dm @@ -39,7 +39,7 @@ rebuild_filtering_list() for(var/datum/omni_port/P in ports) - P.air.air_volume = ATMOS_DEFAULT_VOLUME_FILTER + P.air.total_volume = ATMOS_DEFAULT_VOLUME_FILTER /obj/machinery/atmospherics/omni/filter/Destroy() input = null @@ -57,7 +57,7 @@ if(P in gas_filters) gas_filters -= P - P.air.air_volume = ATMOS_DEFAULT_VOLUME_FILTER + P.air.total_volume = ATMOS_DEFAULT_VOLUME_FILTER switch(P.mode) if(ATM_INPUT) input = P @@ -82,13 +82,13 @@ var/datum/gas_mixture/input_air = input.air // it's completely happy with them if they're in a loop though i.e. "P.air.return_pressure()"... *shrug* var/delta = clamp(0, (output_air ? (max_output_pressure - output_air.return_pressure()) : 0), max_output_pressure) - var/transfer_moles_max = calculate_transfer_moles(input_air, output_air, delta, (output && output.network && output.network.air_volume) ? output.network.air_volume : 0) + var/transfer_moles_max = calculate_transfer_moles(input_air, output_air, delta, (output && output.network && output.network.total_volume) ? output.network.total_volume : 0) for(var/datum/omni_port/filter_output in gas_filters) delta = clamp(0, (filter_output.air ? (max_output_pressure - filter_output.air.return_pressure()) : 0), max_output_pressure) - transfer_moles_max = min(transfer_moles_max, (calculate_transfer_moles(input_air, filter_output.air, delta, (filter_output && filter_output.network && filter_output.network.air_volume) ? filter_output.network.air_volume : 0))) + transfer_moles_max = min(transfer_moles_max, (calculate_transfer_moles(input_air, filter_output.air, delta, (filter_output && filter_output.network && filter_output.network.total_volume) ? filter_output.network.total_volume : 0))) //Figure out the amount of moles to transfer - var/transfer_moles = clamp(0, ((set_flow_rate/input_air.air_volume)*input_air.total_moles), transfer_moles_max) + var/transfer_moles = clamp(0, ((set_flow_rate/input_air.total_volume)*input_air.total_moles), transfer_moles_max) var/power_draw = -1 if (transfer_moles > MINIMUM_MOLES_TO_FILTER) diff --git a/code/modules/atmospherics/components/omni_devices/mixer.dm b/code/modules/atmospherics/components/omni_devices/mixer.dm index 053803779eb4..bff092218863 100644 --- a/code/modules/atmospherics/components/omni_devices/mixer.dm +++ b/code/modules/atmospherics/components/omni_devices/mixer.dm @@ -54,7 +54,7 @@ con += max(0, tag_west_con) for(var/datum/omni_port/P in ports) - P.air.air_volume = ATMOS_DEFAULT_VOLUME_MIXER + P.air.total_volume = ATMOS_DEFAULT_VOLUME_MIXER /obj/machinery/atmospherics/omni/mixer/Destroy() inputs.Cut() @@ -80,7 +80,7 @@ P.concentration = 1 / max(1, inputs.len) if(output) - output.air.air_volume = ATMOS_DEFAULT_VOLUME_MIXER * 0.75 * inputs.len + output.air.total_volume = ATMOS_DEFAULT_VOLUME_MIXER * 0.75 * inputs.len output.concentration = 1 rebuild_mixing_inputs() @@ -117,8 +117,8 @@ for (var/datum/omni_port/P in inputs) if(!P.concentration) continue - transfer_moles += (set_flow_rate*P.concentration/P.air.air_volume)*P.air.total_moles - transfer_moles_max = min(transfer_moles_max, calculate_transfer_moles(P.air, output.air, delta, (output && output.network && output.network.air_volume) ? output.network.air_volume : 0)) + transfer_moles += (set_flow_rate*P.concentration/P.air.total_volume)*P.air.total_moles + transfer_moles_max = min(transfer_moles_max, calculate_transfer_moles(P.air, output.air, delta, (output && output.network && output.network.total_volume) ? output.network.total_volume : 0)) transfer_moles = clamp(0, transfer_moles, transfer_moles_max) var/power_draw = -1 diff --git a/code/modules/atmospherics/components/trinary_devices/trinary_base.dm b/code/modules/atmospherics/components/trinary_devices/trinary_base.dm index d4e72576e3dd..ba82faaf6d1e 100644 --- a/code/modules/atmospherics/components/trinary_devices/trinary_base.dm +++ b/code/modules/atmospherics/components/trinary_devices/trinary_base.dm @@ -15,9 +15,9 @@ air2 = new air3 = new - air1.air_volume = 200 - air2.air_volume = 200 - air3.air_volume = 200 + air1.total_volume = 200 + air2.total_volume = 200 + air3.total_volume = 200 . = ..() /obj/machinery/atmospherics/trinary/air_in_dir(direction) diff --git a/code/modules/atmospherics/components/unary/cold_sink.dm b/code/modules/atmospherics/components/unary/cold_sink.dm index 802b7c8377d3..18ab66f03864 100644 --- a/code/modules/atmospherics/components/unary/cold_sink.dm +++ b/code/modules/atmospherics/components/unary/cold_sink.dm @@ -123,7 +123,7 @@ power_rating = initial(power_rating) * cap_rating / 2 //more powerful heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient - air_contents.air_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + air_contents.total_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating set_power_level(power_setting) /obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting) diff --git a/code/modules/atmospherics/components/unary/heat_source.dm b/code/modules/atmospherics/components/unary/heat_source.dm index 8b0a103476e5..435bf70e9f1e 100644 --- a/code/modules/atmospherics/components/unary/heat_source.dm +++ b/code/modules/atmospherics/components/unary/heat_source.dm @@ -110,7 +110,7 @@ max_power_rating = initial(max_power_rating) * cap_rating / 2 max_temperature = max(initial(max_temperature) - T20C, 0) * ((bin_rating * 4 + cap_rating) / 5) + T20C - air_contents.air_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + air_contents.total_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating set_power_level(power_setting) /obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting) diff --git a/code/modules/atmospherics/components/unary/outlet_injector.dm b/code/modules/atmospherics/components/unary/outlet_injector.dm index 83ad2a6548cf..8f3d287c78c1 100644 --- a/code/modules/atmospherics/components/unary/outlet_injector.dm +++ b/code/modules/atmospherics/components/unary/outlet_injector.dm @@ -56,7 +56,7 @@ /obj/machinery/atmospherics/unary/outlet_injector/Initialize() . = ..() //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. - air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 + air_contents.total_volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 /obj/machinery/atmospherics/unary/outlet_injector/on_update_icon() if(stat & NOPOWER) @@ -94,7 +94,7 @@ var/datum/gas_mixture/environment = loc.return_air() if(environment && air_contents.temperature > 0) - var/transfer_moles = (volume_rate/air_contents.air_volume)*air_contents.total_moles //apply flow rate limit + var/transfer_moles = (volume_rate/air_contents.total_volume)*air_contents.total_moles //apply flow rate limit power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) if(transfer_moles > 0) update_networks() @@ -147,7 +147,7 @@ return machine.volume_rate /decl/public_access/public_variable/volume_rate/write_var(obj/machinery/atmospherics/unary/outlet_injector/machine, new_value) - new_value = clamp(new_value, 0, machine.air_contents.air_volume) + new_value = clamp(new_value, 0, machine.air_contents.total_volume) . = ..() if(.) machine.volume_rate = new_value diff --git a/code/modules/atmospherics/components/unary/tank.dm b/code/modules/atmospherics/components/unary/tank.dm index ad1672b32934..96bb673ca228 100644 --- a/code/modules/atmospherics/components/unary/tank.dm +++ b/code/modules/atmospherics/components/unary/tank.dm @@ -5,7 +5,7 @@ name = "Pressure Tank" desc = "A large vessel containing pressurized gas." - var/air_volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet + var/gas_volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet var/start_pressure = 25 ATM var/filling // list of gas ratios to use. @@ -25,12 +25,12 @@ /obj/machinery/atmospherics/unary/tank/Initialize() . = ..() - air_contents.air_volume = air_volume + air_contents.total_volume = gas_volume air_contents.temperature = T20C if(filling) for(var/gas in filling) - air_contents.adjust_gas(gas, start_pressure * filling[gas] * (air_contents.air_volume)/(R_IDEAL_GAS_EQUATION*air_contents.temperature), FALSE) + air_contents.adjust_gas(gas, start_pressure * filling[gas] * (air_contents.total_volume)/(R_IDEAL_GAS_EQUATION*air_contents.temperature), FALSE) air_contents.update_values() update_icon() diff --git a/code/modules/atmospherics/components/unary/unary_base.dm b/code/modules/atmospherics/components/unary/unary_base.dm index 308bb3294a93..6cbe3cb184b6 100644 --- a/code/modules/atmospherics/components/unary/unary_base.dm +++ b/code/modules/atmospherics/components/unary/unary_base.dm @@ -19,7 +19,7 @@ /obj/machinery/atmospherics/unary/Initialize() air_contents = new - air_contents.air_volume = 200 + air_contents.total_volume = 200 if(controlled) reset_area(null, get_area(src)) . = ..() diff --git a/code/modules/atmospherics/components/unary/vent_pump.dm b/code/modules/atmospherics/components/unary/vent_pump.dm index b50640cd3cf5..49fcbbbb2765 100644 --- a/code/modules/atmospherics/components/unary/vent_pump.dm +++ b/code/modules/atmospherics/components/unary/vent_pump.dm @@ -85,7 +85,7 @@ update_name() events_repository.register(/decl/observ/name_set, A, src, PROC_REF(change_area_name)) . = ..() - air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + air_contents.total_volume = ATMOS_DEFAULT_VOLUME_PUMP update_sound() /obj/machinery/atmospherics/unary/vent_pump/proc/change_area_name(var/area/A, var/old_area_name, var/new_area_name) @@ -170,7 +170,7 @@ /obj/machinery/atmospherics/unary/vent_pump/high_volume/Initialize() . = ..() - air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + air_contents.total_volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 /obj/machinery/atmospherics/unary/vent_pump/on_update_icon() var/visible_directions = build_device_underlays() @@ -223,7 +223,7 @@ power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) else //external -> internal var/datum/pipe_network/network = network_in_dir(dir) - transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, network?.air_volume) / environment.group_multiplier // limit it to just one turf's worth of gas per tick + transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, network?.total_volume) / environment.group_multiplier // limit it to just one turf's worth of gas per tick power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) else @@ -562,7 +562,7 @@ /obj/machinery/atmospherics/unary/vent_pump/engine/Initialize() . = ..() - air_contents.air_volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector + air_contents.total_volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector /obj/machinery/atmospherics/unary/vent_pump/power_change() . = ..() diff --git a/code/modules/atmospherics/components/unary/vent_scrubber.dm b/code/modules/atmospherics/components/unary/vent_scrubber.dm index 437ff5b505c7..2a2aac82fd5e 100644 --- a/code/modules/atmospherics/components/unary/vent_scrubber.dm +++ b/code/modules/atmospherics/components/unary/vent_scrubber.dm @@ -68,7 +68,7 @@ if(g != /decl/material/gas/oxygen && g != /decl/material/gas/nitrogen) scrubbing_gas += g . = ..() - air_contents.air_volume = ATMOS_DEFAULT_VOLUME_FILTER + air_contents.total_volume = ATMOS_DEFAULT_VOLUME_FILTER /obj/machinery/atmospherics/unary/vent_scrubber/reset_area(area/old_area, area/new_area) if(!controlled) @@ -151,17 +151,17 @@ var/transfer_moles = 0 if(scrubbing == SCRUBBER_SIPHON) //Just siphon all air //limit flow rate from turfs - transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.air_volume) //group_multiplier gets divided out here + transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.total_volume) //group_multiplier gets divided out here power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) else //limit flow rate from turfs - transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.air_volume) //group_multiplier gets divided out here + transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.total_volume) //group_multiplier gets divided out here power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) if(scrubbing != SCRUBBER_SIPHON && power_draw <= 0) //99% of all scrubbers //Fucking hibernate because you ain't doing shit. hibernate = world.time + (rand(100,200)) else if(scrubbing == SCRUBBER_EXCHANGE) // after sleep check so it only does an exchange if there are bad gasses that have been scrubbed - transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.air_volume) + transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.total_volume) power_draw += pump_gas(src, environment, air_contents, transfer_moles / 4, power_rating) if (power_draw >= 0) diff --git a/code/modules/atmospherics/datum_pipe_network.dm b/code/modules/atmospherics/datum_pipe_network.dm index 72e5c22cbe1f..edfe6c240328 100644 --- a/code/modules/atmospherics/datum_pipe_network.dm +++ b/code/modules/atmospherics/datum_pipe_network.dm @@ -1,6 +1,6 @@ /datum/pipe_network var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network - var/air_volume = 0 //caches the total volume for atmos machines to use in gas calculations + var/total_volume = 0 //caches the total volume for atmos machines to use in gas calculations var/list/obj/machinery/atmospherics/normal_members = list() var/list/datum/pipeline/line_members = list() @@ -71,7 +71,7 @@ //Go through membership roster and make sure gases is up to date gases = list() - air_volume = 0 + total_volume = 0 for(var/obj/machinery/atmospherics/normal_member in normal_members) var/result = normal_member.return_network_air(src) @@ -81,4 +81,4 @@ gases += line_member.air for(var/datum/gas_mixture/air in gases) - air_volume += air.air_volume + total_volume += air.total_volume diff --git a/code/modules/atmospherics/datum_pipeline.dm b/code/modules/atmospherics/datum_pipeline.dm index 6465b8dbb63d..89e598b527ff 100644 --- a/code/modules/atmospherics/datum_pipeline.dm +++ b/code/modules/atmospherics/datum_pipeline.dm @@ -30,7 +30,7 @@ STOP_PROCESSING(SSprocessing, src) QDEL_NULL(network) - if(air?.air_volume || liquid?.total_volume) + if(air?.total_volume || liquid?.total_volume) temporarily_store_fluids() QDEL_NULL(air) @@ -62,15 +62,15 @@ //Update individual gas_mixtures by volume ratio var/liquid_transfer_per_pipe = min(REAGENT_UNITS_PER_PIPE, (liquid && length(members)) ? (liquid.total_volume / length(members)) : 0) - if(!air?.air_volume && !liquid_transfer_per_pipe) + if(!air?.total_volume && !liquid_transfer_per_pipe) return for(var/obj/machinery/atmospherics/pipe/member in members) - if(air?.air_volume) + if(air?.total_volume) member.air_temporary = new member.air_temporary.copy_from(air) - member.air_temporary.air_volume = member.air_volume - member.air_temporary.multiply(member.air_volume / air.air_volume) + member.air_temporary.total_volume = member.gas_volume + member.air_temporary.multiply(member.gas_volume / air.total_volume) if(liquid_transfer_per_pipe) member.liquid_temporary = new(REAGENT_UNITS_PER_PIPE, member) @@ -81,7 +81,7 @@ members = list(base) edges = list() - var/temp_volume = base.air_volume + var/temp_volume = base.gas_volume base.parent = src maximum_pressure = base.maximum_pressure @@ -110,7 +110,7 @@ members += item possible_expansions += item - temp_volume += item.air_volume + temp_volume += item.gas_volume item.parent = src maximum_pressure = min(maximum_pressure, item.maximum_pressure) @@ -134,7 +134,7 @@ possible_expansions -= borderline - air.air_volume = temp_volume + air.total_volume = temp_volume liquid.maximum_volume = length(members) * REAGENT_UNITS_PER_PIPE /datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) @@ -194,11 +194,11 @@ /datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - if(air.air_volume <= 0) // Avoid div by zero. + if(air.total_volume <= 0) // Avoid div by zero. return var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.air_volume) + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.total_volume) var/datum/gas_mixture/target_air = target.return_air() if(total_heat_capacity <= 0) // Avoid div by zero. @@ -225,7 +225,7 @@ //surface must be the surface area in m^2 /datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) - var/gas_density = air.total_moles/air.air_volume + var/gas_density = air.total_moles/air.total_volume thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio var/heat_gain = get_thermal_radiation(air.temperature, surface, RADIATOR_EXPOSED_SURFACE_AREA_RATIO, thermal_conductivity) diff --git a/code/modules/atmospherics/he_pipes.dm b/code/modules/atmospherics/he_pipes.dm index 0479a7827ccd..2a11c6cde77d 100644 --- a/code/modules/atmospherics/he_pipes.dm +++ b/code/modules/atmospherics/he_pipes.dm @@ -73,7 +73,7 @@ var/datum/gas_mixture/environment = turf.return_air() environment_temperature = environment?.temperature || 0 if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) - parent.temperature_interact(turf, air_volume, thermal_conductivity) + parent.temperature_interact(turf, gas_volume, thermal_conductivity) // Burn mobs buckled to this pipe. if(buckled_mob) diff --git a/code/modules/atmospherics/pipes.dm b/code/modules/atmospherics/pipes.dm index 7775e036c593..ef7fa5484345 100644 --- a/code/modules/atmospherics/pipes.dm +++ b/code/modules/atmospherics/pipes.dm @@ -24,7 +24,7 @@ var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke var/datum/reagents/liquid_temporary // used when reconstructing a pipeline that broke var/datum/pipeline/parent - var/air_volume = 0 + var/gas_volume = 0 var/leaking = 0 // Do not set directly, use set_leaking(TRUE/FALSE) //minimum pressure before check_pressure(...) should be called @@ -210,7 +210,7 @@ update_sound(0) . = PROCESS_KILL else if(leaking) - parent.mingle_with_turf(loc, air_volume) + parent.mingle_with_turf(loc, gas_volume) var/air = parent.air?.return_pressure() if(!sound_token && air) update_sound(1) @@ -225,7 +225,7 @@ name = "pipe" desc = "A one-meter section of regular pipe." - air_volume = ATMOS_DEFAULT_VOLUME_PIPE + gas_volume = ATMOS_DEFAULT_VOLUME_PIPE dir = SOUTH initialize_directions = SOUTH|NORTH @@ -380,7 +380,7 @@ icon_state = "map" name = "pipe manifold" desc = "A manifold composed of regular pipes." - air_volume = ATMOS_DEFAULT_VOLUME_PIPE * 1.5 + gas_volume = ATMOS_DEFAULT_VOLUME_PIPE * 1.5 dir = SOUTH initialize_directions = EAST|NORTH|WEST @@ -509,7 +509,7 @@ icon_state = "" name = "4-way pipe manifold" desc = "A manifold composed of regular pipes." - air_volume = ATMOS_DEFAULT_VOLUME_PIPE * 2 + gas_volume = ATMOS_DEFAULT_VOLUME_PIPE * 2 dir = SOUTH initialize_directions = NORTH|SOUTH|EAST|WEST @@ -634,7 +634,7 @@ icon = 'icons/atmos/pipes.dmi' icon_state = "cap" level = LEVEL_ABOVE_PLATING - air_volume = 35 + gas_volume = 35 pipe_class = PIPE_CLASS_UNARY dir = SOUTH diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index a4000ce56bba..c286fd0c874c 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -5,7 +5,7 @@ icon_state = "hydrotray3" density = TRUE anchored = TRUE - air_volume = 100 + gas_volume = 100 construct_state = /decl/machine_construction/default/panel_closed uncreated_component_parts = null stat_immune = 0 diff --git a/code/modules/mechs/components/body.dm b/code/modules/mechs/components/body.dm index 7ec1cf569712..90890403e105 100644 --- a/code/modules/mechs/components/body.dm +++ b/code/modules/mechs/components/body.dm @@ -52,13 +52,13 @@ ) if(pilot_coverage >= 100) //Open cockpits dont get to have air cockpit = new - cockpit.air_volume = 200 + cockpit.total_volume = 200 if(loc) var/datum/gas_mixture/air = loc.return_air() if(air) //Essentially at this point its like we created a vacuum, but realistically making a bottle doesnt actually increase volume of a room and neither should a mech for(var/g in air.gas) - cockpit.gas[g] = (air.gas[g] / air.air_volume) * cockpit.air_volume + cockpit.gas[g] = (air.gas[g] / air.total_volume) * cockpit.total_volume cockpit.temperature = air.temperature cockpit.update_values() @@ -107,7 +107,7 @@ if(pressure_delta > 0) if(air_supply.air_contents.temperature > 0) var/transfer_moles = calculate_transfer_moles(air_supply.air_contents, cockpit, pressure_delta) - transfer_moles = min(transfer_moles, (air_supply.release_flow_rate/air_supply.air_contents.air_volume)*air_supply.air_contents.total_moles) + transfer_moles = min(transfer_moles, (air_supply.release_flow_rate/air_supply.air_contents.total_volume)*air_supply.air_contents.total_moles) pump_gas_passive(air_supply, air_supply.air_contents, cockpit, transfer_moles) changed = TRUE else if(pressure_delta < 0) //Release overpressure. diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm index 5550afd919ab..21aa9577627e 100644 --- a/code/modules/mob/living/human/life.dm +++ b/code/modules/mob/living/human/life.dm @@ -173,7 +173,7 @@ var/temperature_gain = heat_gain/HUMAN_HEAT_CAPACITY bodytemperature += temperature_gain //temperature_gain will often be negative - var/relative_density = (environment.total_moles/environment.air_volume) / (MOLES_CELLSTANDARD/CELL_VOLUME) + var/relative_density = (environment.total_moles/environment.total_volume) / (MOLES_CELLSTANDARD/CELL_VOLUME) if(relative_density > 0.02) //don't bother if we are in vacuum or near-vacuum var/loc_temp = environment.temperature diff --git a/code/modules/mob/living/living_breath.dm b/code/modules/mob/living/living_breath.dm index de595f991f41..43d33f644d02 100644 --- a/code/modules/mob/living/living_breath.dm +++ b/code/modules/mob/living/living_breath.dm @@ -81,7 +81,7 @@ if(!can_breathe_air_above) breath = new if(!can_drown()) - breath.air_volume = volume_needed + breath.total_volume = volume_needed breath.temperature = my_turf.temperature // TODO: species-breathable gas instead of oxygen default. Maybe base it on the reagents being breathed breath.adjust_gas(/decl/material/gas/oxygen, ONE_ATMOSPHERE*volume_needed/(R_IDEAL_GAS_EQUATION*T20C)) diff --git a/code/modules/multiz/pipes.dm b/code/modules/multiz/pipes.dm index 414fd5ff0b6d..4f972191a0df 100644 --- a/code/modules/multiz/pipes.dm +++ b/code/modules/multiz/pipes.dm @@ -8,7 +8,7 @@ name = "upwards pipe" desc = "A pipe segment to connect upwards." - air_volume = 70 + gas_volume = 70 dir = SOUTH initialize_directions = SOUTH diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 199a8b4e8e4b..0fc0f1ea4945 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -307,7 +307,7 @@ else temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed - var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * breath.air_volume/CELL_VOLUME) + var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * breath.total_volume/CELL_VOLUME) temp_adj *= relative_density if (temp_adj > BODYTEMP_HEATING_MAX) temp_adj = BODYTEMP_HEATING_MAX diff --git a/code/modules/overmap/ships/device_types/gas_thruster.dm b/code/modules/overmap/ships/device_types/gas_thruster.dm index 1656acd207d2..1f09c65b3187 100644 --- a/code/modules/overmap/ships/device_types/gas_thruster.dm +++ b/code/modules/overmap/ships/device_types/gas_thruster.dm @@ -29,10 +29,10 @@ /datum/extension/ship_engine/gas/proc/get_propellant(var/sample_only = TRUE, var/partial = 1) var/obj/machinery/atmospherics/unary/engine/E = holder - if(istype(E) && E.air_contents?.air_volume > 0) - var/datum/gas_mixture/removed = E.air_contents.remove_ratio((volume_per_burn * thrust_limit * partial) / E.air_contents.air_volume) + if(istype(E) && E.air_contents?.total_volume > 0) + var/datum/gas_mixture/removed = E.air_contents.remove_ratio((volume_per_burn * thrust_limit * partial) / E.air_contents.total_volume) if(removed && sample_only) - var/datum/gas_mixture/sample = new(removed.air_volume) + var/datum/gas_mixture/sample = new(removed.total_volume) sample.copy_from(removed) E.air_contents.merge(removed) return sample diff --git a/code/modules/power/stirling.dm b/code/modules/power/stirling.dm index 0903296bdddb..bc329ce9e5cc 100644 --- a/code/modules/power/stirling.dm +++ b/code/modules/power/stirling.dm @@ -225,7 +225,7 @@ slot_flags = null starting_pressure = list(/decl/material/gas/hydrogen = 2 ATM) - air_volume = 30 + gas_volume = 30 failure_temp = 1000 /obj/item/tank/stirling/Initialize() diff --git a/code/modules/projectiles/guns/launcher/pneumatic.dm b/code/modules/projectiles/guns/launcher/pneumatic.dm index 331322b76cfd..c5cb01283104 100644 --- a/code/modules/projectiles/guns/launcher/pneumatic.dm +++ b/code/modules/projectiles/guns/launcher/pneumatic.dm @@ -111,7 +111,7 @@ /obj/item/gun/launcher/pneumatic/update_release_force(obj/item/projectile) if(tank) - release_force = ((fire_pressure*tank.air_volume)/projectile.w_class)/force_divisor //projectile speed. + release_force = ((fire_pressure*tank.gas_volume)/projectile.w_class)/force_divisor //projectile speed. if(release_force > 80) release_force = 80 //damage cap. else release_force = 0 diff --git a/code/modules/reagents/reagent_containers/borghypo.dm b/code/modules/reagents/reagent_containers/borghypo.dm index 3a1e4df3bfcb..468176dbde93 100644 --- a/code/modules/reagents/reagent_containers/borghypo.dm +++ b/code/modules/reagents/reagent_containers/borghypo.dm @@ -13,7 +13,7 @@ var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) /obj/item/chems/borghypo/Initialize() - volume *= length(get_generated_reagents()) + chem_volume *= length(get_generated_reagents()) . = ..() /obj/item/chems/borghypo/proc/get_generated_reagents() diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 7f728007f023..49527c33cdbb 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -364,7 +364,7 @@ var/global/list/diversion_junctions = list() var/power_draw = -1 if(env && env.temperature > 0) - var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.air_volume)*env.total_moles //group_multiplier is divided out here + var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.total_volume)*env.total_moles //group_multiplier is divided out here power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage) if (power_draw > 0) diff --git a/code/modules/scanners/gas.dm b/code/modules/scanners/gas.dm index 5ad8c4bc9f02..397ea46d74bf 100644 --- a/code/modules/scanners/gas.dm +++ b/code/modules/scanners/gas.dm @@ -79,7 +79,7 @@ . += "[capitalize(mat.gas_name)]: [percentage]%[perGas_add_string]" var/totalGas_add_string = "" if(mode == MV_MODE) - totalGas_add_string = ", Total moles: [round(mixture.total_moles, 0.01)], Volume: [mixture.air_volume]L" + totalGas_add_string = ", Total moles: [round(mixture.total_moles, 0.01)], Volume: [mixture.total_volume]L" . += "Temperature: [round(mixture.temperature-T0C)]°C / [round(mixture.temperature)]K[totalGas_add_string]" return diff --git a/code/modules/xgm/xgm_gas_mixture.dm b/code/modules/xgm/xgm_gas_mixture.dm index 0c947ca8ba2a..d88010d37564 100644 --- a/code/modules/xgm/xgm_gas_mixture.dm +++ b/code/modules/xgm/xgm_gas_mixture.dm @@ -8,7 +8,7 @@ //Sum of all the gas moles in this mix. Updated by update_values() var/total_moles = 0 //Volume of this mix. - var/air_volume = CELL_VOLUME + var/total_volume = CELL_VOLUME //Size of the group this gas_mixture is representing. 1 for singletons. var/group_multiplier = 1 @@ -21,14 +21,14 @@ /datum/gas_mixture/New(_volume, _temperature, _group_multiplier) if(!isnull(_volume)) - air_volume = _volume + total_volume = _volume if(!isnull(_temperature)) temperature = _temperature if(!isnull(_group_multiplier)) group_multiplier = _group_multiplier //Since we may have values defined on creation, update everything. - if(air_volume && length(gas)) + if(total_volume && length(gas)) update_values() /datum/gas_mixture/proc/get_gas(gasid) @@ -107,9 +107,9 @@ for(var/g in gas|sharer.gas) var/comb = gas[g] + sharer.gas[g] - comb /= air_volume + sharer.air_volume - gas[g] = comb * air_volume - sharer.gas[g] = comb * sharer.air_volume + comb /= total_volume + sharer.total_volume + gas[g] = comb * total_volume + sharer.gas[g] = comb * sharer.total_volume if(our_heatcap + share_heatcap) temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap) @@ -188,7 +188,7 @@ var/molar_mass = mat.molar_mass var/specific_heat = mat.gas_specific_heat var/safe_temp = max(temperature, TCMB) // We're about to divide by this. - return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*air_volume/(gas[gasid] * safe_temp)) * (molar_mass*specific_heat*safe_temp)**(2/3) + 1 ) + 15 ) + return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*total_volume/(gas[gasid] * safe_temp)) * (molar_mass*specific_heat*safe_temp)**(2/3) + 1 ) + 15 ) //alternative, simpler equation //var/partial_pressure = gas[gasid] * R_IDEAL_GAS_EQUATION * temperature / volume @@ -209,8 +209,8 @@ //Returns the pressure of the gas mix. Only accurate if there have been no gas modifications since update_values() has been called. /datum/gas_mixture/proc/return_pressure() - if(air_volume) - return total_moles * R_IDEAL_GAS_EQUATION * temperature / air_volume + if(total_volume) + return total_moles * R_IDEAL_GAS_EQUATION * temperature / total_volume return 0 @@ -249,7 +249,7 @@ gas[g] = gas[g] * (1 - ratio) removed.temperature = temperature - removed.air_volume = air_volume * group_multiplier / out_group_multiplier + removed.total_volume = total_volume * group_multiplier / out_group_multiplier update_values() removed.update_values() @@ -257,8 +257,8 @@ //Removes a volume of gas from the mixture and returns a gas_mixture containing the removed air with the given volume /datum/gas_mixture/proc/remove_volume(removed_volume) - var/datum/gas_mixture/removed = remove_ratio(removed_volume/(air_volume*group_multiplier), 1) - removed.air_volume = removed_volume + var/datum/gas_mixture/removed = remove_ratio(removed_volume/(total_volume*group_multiplier), 1) + removed.total_volume = removed_volume return removed //Removes moles from the gas mixture, limited by a given flag. Returns a gax_mixture containing the removed air. @@ -305,7 +305,7 @@ return 1 /datum/gas_mixture/GetCloneArgs() - return list(air_volume, temperature, group_multiplier) + return list(total_volume, temperature, group_multiplier) /datum/gas_mixture/PopulateClone(datum/gas_mixture/clone) clone.gas = gas.Copy() @@ -461,7 +461,7 @@ var/list/total_gas = list() for(var/datum/gas_mixture/gasmix in gases) - total_volume += gasmix.air_volume + total_volume += gasmix.total_volume var/temp_heatcap = gasmix.heat_capacity() total_thermal_energy += gasmix.temperature * temp_heatcap total_heat_capacity += temp_heatcap @@ -488,7 +488,7 @@ for(var/datum/gas_mixture/gasmix in gases) gasmix.gas = combined.gas.Copy() gasmix.temperature = combined.temperature - gasmix.multiply(gasmix.air_volume) + gasmix.multiply(gasmix.total_volume) return 1 diff --git a/maps/away/lost_supply_base/lost_supply_base.dmm b/maps/away/lost_supply_base/lost_supply_base.dmm index 204c6d60b768..a8783915b03d 100644 --- a/maps/away/lost_supply_base/lost_supply_base.dmm +++ b/maps/away/lost_supply_base/lost_supply_base.dmm @@ -1426,7 +1426,7 @@ /area/lost_supply_base/common) "eD" = ( /obj/machinery/atmospherics/unary/tank/hydrogen{ - air_volume = 3200 + gas_volume = 3200 }, /turf/floor/plating/airless, /area/lost_supply_base) diff --git a/maps/away/unishi/unishi-1.dmm b/maps/away/unishi/unishi-1.dmm index befa32bfaf72..865829cd4ee0 100644 --- a/maps/away/unishi/unishi-1.dmm +++ b/maps/away/unishi/unishi-1.dmm @@ -251,7 +251,7 @@ /area/unishi/engineering) "aO" = ( /obj/machinery/atmospherics/unary/tank/oxygen{ - air_volume = 3200 + gas_volume = 3200 }, /obj/effect/floor_decal/industrial/hatch/yellow, /turf/floor, diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index d333b7a254be..c9e92ad63477 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -48513,7 +48513,7 @@ /area/exodus/engineering/storage) "bXs" = ( /obj/machinery/atmospherics/unary/tank/oxygen{ - air_volume = 3200 + gas_volume = 3200 }, /obj/effect/floor_decal/industrial/outline/yellow, /turf/floor/tiled/steel_grid, @@ -48544,7 +48544,7 @@ /area/exodus/engineering/break_room) "bXv" = ( /obj/machinery/atmospherics/unary/tank/hydrogen{ - air_volume = 3200 + gas_volume = 3200 }, /obj/effect/floor_decal/industrial/outline/yellow, /turf/floor/tiled/steel_grid, diff --git a/mods/content/integrated_electronics/components/input.dm b/mods/content/integrated_electronics/components/input.dm index 1b9dadba0695..8d4d8995f115 100644 --- a/mods/content/integrated_electronics/components/input.dm +++ b/mods/content/integrated_electronics/components/input.dm @@ -1109,7 +1109,7 @@ set_pin_data(IC_OUTPUT, 3, round(air_contents.get_total_moles(), 0.001)) set_pin_data(IC_OUTPUT, 4, round(air_contents.return_pressure(), 0.001)) set_pin_data(IC_OUTPUT, 5, round(air_contents.temperature, 0.001)) - set_pin_data(IC_OUTPUT, 6, round(air_contents.air_volume, 0.001)) + set_pin_data(IC_OUTPUT, 6, round(air_contents.total_volume, 0.001)) push_data() activate_pin(2) diff --git a/mods/content/plant_dissection/grown.dm b/mods/content/plant_dissection/grown.dm index a5e74515f470..f4c6ea359e00 100644 --- a/mods/content/plant_dissection/grown.dm +++ b/mods/content/plant_dissection/grown.dm @@ -27,7 +27,7 @@ for(var/datum/plant_segment/segment as anything in segments) if(segment.contributes_to_reagents) segment_amount += LAZYACCESS(segment.total_reagent_volume_by_state, (PLANT_STATE_FRESH)) - volume = max(volume, segment_amount) + chem_volume = max(chem_volume, segment_amount) return ..() /obj/item/food/grown/Destroy() diff --git a/mods/species/ascent/items/rig.dm b/mods/species/ascent/items/rig.dm index 0ff52ab6c846..c43ba40dcfcb 100644 --- a/mods/species/ascent/items/rig.dm +++ b/mods/species/ascent/items/rig.dm @@ -135,7 +135,7 @@ name = "mantid gas tank" icon = 'mods/species/ascent/icons/tank.dmi' distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - air_volume = 180 + gas_volume = 180 /obj/item/tank/mantid/methyl_bromide starting_pressure = list(/decl/material/gas/methyl_bromide = 6 ATM) From da44a9c7a5c0d41003b2aa5b9c5af01c44499f23 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 31 Oct 2025 20:50:00 +1100 Subject: [PATCH 0906/1331] Reworks sealant gun code to use reagents instead of bespoke tracking vars. --- .../liquids/materials_liquid_chemistry.dm | 8 ++++ .../reactions/reaction_grenade_reaction.dm | 5 +-- code/modules/sealant_gun/sealant_gun.dm | 6 +-- code/modules/sealant_gun/sealant_injector.dm | 2 +- code/modules/sealant_gun/sealant_rack.dm | 1 + code/modules/sealant_gun/sealant_tank.dm | 42 ++++++++----------- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm b/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm index f7d289cc8693..f60b1158b6d6 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm @@ -16,6 +16,14 @@ value = 0.1 exoplanet_rarity_gas = MAT_RARITY_EXOTIC +/decl/material/liquid/foam + name = "foam" + uid = "liquid_foam" + lore_text = "A frothy, sticky, well-aerated fluid." + taste_description = "chemical blandness" + color = "#a59da4" + exoplanet_rarity_gas = MAT_RARITY_EXOTIC + /decl/material/liquid/lube name = "lubricant" uid = "liquid_lubricant" diff --git a/code/modules/reagents/reactions/reaction_grenade_reaction.dm b/code/modules/reagents/reactions/reaction_grenade_reaction.dm index 7827c6ac0548..4483f376e33b 100644 --- a/code/modules/reagents/reactions/reaction_grenade_reaction.dm +++ b/code/modules/reagents/reactions/reaction_grenade_reaction.dm @@ -133,9 +133,8 @@ ..() var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) - if(istype(location, /obj/item/sealant_tank)) - var/obj/item/sealant_tank/foam = location - foam.foam_charges = clamp(foam.foam_charges + created_volume, 0, foam.max_foam_charges) + if(istype(location, /obj/item/sealant_tank) && location.reagents?.maximum_volume) + location.reagents.add_reagent(/decl/material/liquid/foam, created_volume) return location = get_turf(location) if(location) diff --git a/code/modules/sealant_gun/sealant_gun.dm b/code/modules/sealant_gun/sealant_gun.dm index 23be0cf3efcd..cbe34aa3cb74 100644 --- a/code/modules/sealant_gun/sealant_gun.dm +++ b/code/modules/sealant_gun/sealant_gun.dm @@ -34,8 +34,8 @@ loaded_tank = /obj/item/sealant_tank/mapped /obj/item/gun/launcher/sealant/consume_next_projectile() - if(loaded_tank?.foam_charges >= foam_charges_per_shot) - loaded_tank.foam_charges -= foam_charges_per_shot + if(loaded_tank?.reagents?.has_reagent(/decl/material/liquid/foam, foam_charges_per_shot)) + loaded_tank.reagents.remove_reagent(/decl/material/liquid/foam, foam_charges_per_shot) . = new /obj/item/sealant(src) /obj/item/gun/launcher/sealant/Initialize() @@ -58,7 +58,7 @@ . = ..() if(loc == user) if(loaded_tank) - . += SPAN_NOTICE("The loaded tank has about [loaded_tank.foam_charges] liter\s of sealant left.") + . += SPAN_NOTICE("The loaded tank has about [REAGENT_VOLUME(loaded_tank.reagents, /decl/material/liquid/foam) || 0] charge\s of sealant left.") else . += SPAN_WARNING("\The [src] has no sealant loaded.") diff --git a/code/modules/sealant_gun/sealant_injector.dm b/code/modules/sealant_gun/sealant_injector.dm index 9bd7735bbd74..6485f401a8f2 100644 --- a/code/modules/sealant_gun/sealant_injector.dm +++ b/code/modules/sealant_gun/sealant_injector.dm @@ -63,7 +63,7 @@ to_chat(user, SPAN_WARNING("There is no tank loaded.")) return TRUE - var/fill_space = floor(loaded_tank.max_foam_charges - loaded_tank.foam_charges) / 5 + var/fill_space = floor(loaded_tank.reagents?.maximum_volume - loaded_tank.reagents?.total_volume) / 5 if(fill_space <= 0) to_chat(user, SPAN_WARNING("\The [loaded_tank] is full.")) return TRUE diff --git a/code/modules/sealant_gun/sealant_rack.dm b/code/modules/sealant_gun/sealant_rack.dm index 4f053d436bd0..84e925a7acdc 100644 --- a/code/modules/sealant_gun/sealant_rack.dm +++ b/code/modules/sealant_gun/sealant_rack.dm @@ -18,6 +18,7 @@ LAZYINITLIST(tanks) for(var/i = 1 to rand(1,max_tanks)) tanks += new /obj/item/sealant_tank(src) + update_icon() /obj/structure/sealant_rack/on_update_icon() ..() diff --git a/code/modules/sealant_gun/sealant_tank.dm b/code/modules/sealant_gun/sealant_tank.dm index 05244c89b98e..7dce9db17401 100644 --- a/code/modules/sealant_gun/sealant_tank.dm +++ b/code/modules/sealant_gun/sealant_tank.dm @@ -1,36 +1,30 @@ /obj/item/sealant_tank - name = "sealant tank" - desc = "A sealed tank used to keep hull sealant foam contained under pressure." - icon = 'icons/obj/sealant_tank.dmi' - icon_state = "tank" - material = /decl/material/solid/metal/steel - var/foam_charges = 0 - var/max_foam_charges = 60 + name = "sealant tank" + desc = "A sealed tank used to keep hull sealant foam contained under pressure." + icon = 'icons/obj/sealant_tank.dmi' + icon_state = "tank" + material = /decl/material/solid/metal/steel + chem_volume = 60 /obj/item/sealant_tank/on_update_icon() . = ..() - add_overlay("fill_[floor((foam_charges/max_foam_charges) * 5)]") + add_overlay("fill_[floor((reagents.total_volume/reagents.maximum_volume) * 5)]") /obj/item/sealant_tank/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(loc == user) - . += SPAN_NOTICE("\The [src] has about [foam_charges] liter\s of sealant left.") + . += SPAN_NOTICE("\The [src] has about [REAGENT_VOLUME(reagents, /decl/material/liquid/foam) || 0] charge\s of sealant left.") -/obj/item/sealant_tank/Initialize(ml, material_key) - chem_volume = max_foam_charges - . = ..() - -/obj/item/sealant_tank/mapped/Initialize() - . = ..() - foam_charges = max_foam_charges +/obj/item/sealant_tank/mapped/populate_reagents() + reagents.add_reagent(/decl/material/liquid/foam, reagents.maximum_volume) /obj/item/sealant_tank/physically_destroyed(var/skip_qdel) - if(foam_charges) - var/turf/T = get_turf(src) - if(T) - T.visible_message(SPAN_WARNING("The ruptured [src.name] spews out metallic foam!")) - var/datum/effect/effect/system/foam_spread/s = new() - s.set_up(foam_charges, T, reagents, 1) - s.start() - foam_charges = 0 + var/turf/my_turf = get_turf(src) + var/foam_amt = REAGENT_VOLUME(reagents, /decl/material/liquid/foam) + if(istype(my_turf) && foam_amt) + my_turf.visible_message(SPAN_WARNING("The ruptured [name] spews out foam!")) + var/datum/effect/effect/system/foam_spread/foam_spread = new() + foam_spread.set_up(foam_amt, my_turf, reagents, 1) + foam_spread.start() + reagents.clear_reagents() . = ..() From b26299874a635928339a3d91d0958420ce696f20 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 4 Dec 2024 20:31:32 +1100 Subject: [PATCH 0907/1331] Adding a quern-stone. --- code/game/objects/items/_item_reagents.dm | 6 +- code/game/objects/structures/__structure.dm | 3 + code/modules/crafting/working/quern.dm | 122 ++++++++++++++++++++ code/modules/reagents/reagent_dispenser.dm | 7 +- icons/obj/structures/quern.dmi | Bin 0 -> 539 bytes nebula.dme | 1 + 6 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 code/modules/crafting/working/quern.dm create mode 100644 icons/obj/structures/quern.dmi diff --git a/code/game/objects/items/_item_reagents.dm b/code/game/objects/items/_item_reagents.dm index e664024c9520..9431fb7e70c2 100644 --- a/code/game/objects/items/_item_reagents.dm +++ b/code/game/objects/items/_item_reagents.dm @@ -1,5 +1,5 @@ -/obj/item/proc/standard_dispenser_refill(mob/user, obj/structure/reagent_dispensers/target, skip_container_check = FALSE) // This goes into afterattack - if(!istype(target) || (!skip_container_check && (target.atom_flags & ATOM_FLAG_OPEN_CONTAINER))) +/obj/item/proc/standard_dispenser_refill(mob/user, obj/structure/target, skip_container_check = FALSE) // This goes into afterattack + if(!istype(target) || isnull(target.get_reagent_amount_dispensed()) || (!skip_container_check && (target.atom_flags & ATOM_FLAG_OPEN_CONTAINER))) return FALSE if(!target.reagents || !target.reagents.total_volume) @@ -10,7 +10,7 @@ to_chat(user, SPAN_NOTICE("[src] is full of reagents.")) return TRUE - var/trans = target.reagents.trans_to_obj(src, target.amount_dispensed) + var/trans = target.reagents.trans_to_obj(src, target.get_reagent_amount_dispensed()) to_chat(user, SPAN_NOTICE("You fill [src] with [trans] units of the contents of [target].")) return TRUE diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 839530cbdbc3..90bf4c09f19a 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -345,3 +345,6 @@ Note: This proc can be overwritten to allow for different types of auto-alignmen visible_message(SPAN_DANGER("\The [src] was hit by \the [AM].")) playsound(src.loc, hitsound, 100, 1) take_damage(AM.get_thrown_attack_force() * (TT.speed/THROWFORCE_SPEED_DIVISOR), AM.atom_damage_type) + +/obj/structure/proc/get_reagent_amount_dispensed() + return null diff --git a/code/modules/crafting/working/quern.dm b/code/modules/crafting/working/quern.dm new file mode 100644 index 000000000000..3278110bb870 --- /dev/null +++ b/code/modules/crafting/working/quern.dm @@ -0,0 +1,122 @@ +/datum/storage/hopper/mortar/quern + max_w_class = ITEM_SIZE_SMALL + max_storage_space = DEFAULT_BOX_STORAGE + +/obj/structure/working/quern + name = "quern-stone" + desc = "A pair of heavy stones connected by an axle, used to grind plants and minerals into powder." + icon = 'icons/obj/structures/quern.dmi' + material = /decl/material/solid/stone/granite + color = /decl/material/solid/stone/granite::color + storage = /datum/storage/hopper/mortar/quern + work_skill = SKILL_COOKING // Maybe? + var/tmp/volume = 1000 // Same as reagent dispensers. Possibly too large? + var/amount_dispensed = 10 + var/tmp/possible_transfer_amounts = @"[10,25,50,100,500]" + +/obj/structure/working/quern/Initialize() + . = ..() + atom_flags |= ATOM_FLAG_OPEN_CONTAINER + initialize_reagents() + +/obj/structure/working/quern/try_start_working(mob/user) + + if(!length(get_stored_inventory())) + to_chat(user, SPAN_WARNING("There is nothing in \the [src] to grind.")) + return TRUE + + start_working() + while(length(get_stored_inventory()) && user.do_skilled(1.5 SECONDS, work_skill, src)) + if(QDELETED(src) || QDELETED(user) || user.get_stamina() < 25 || !user.get_empty_hand_slot()) + break + var/list/stored = get_stored_inventory() + var/obj/item/grinding = stored[1] + if(!istype(grinding)) + break + if(!grind_item(grinding, user)) + visible_message(SPAN_WARNING("\The [src] clunks and grinds loudly, unable to crush \the [grinding].")) + break + user.adjust_stamina(-25) + + if(!QDELETED(user)) + to_chat(user, SPAN_NOTICE("You stop working \the [src].")) + + stop_working() + return TRUE + +/obj/structure/working/quern/proc/grind_item(obj/item/grinding, mob/user) + if(!istype(grinding)) + return + var/decl/material/attacking_material = get_material() + var/decl/material/crushing_material = grinding.get_material() + if(!attacking_material || !crushing_material || attacking_material.hardness <= crushing_material.hardness) + return FALSE + if(REAGENTS_FREE_SPACE(reagents) < grinding.reagents?.total_volume) + return FALSE + if(grinding.reagents?.total_volume) // if it has no reagents, skip all the fluff and destroy it instantly + grinding.reagents.trans_to(src, grinding.reagents.total_volume) + QDEL_NULL(grinding) + return TRUE + +// Reagent handling code copied from reagent dispensers. TODO: make reagent handling an extension or something. +/obj/structure/working/quern/attackby(obj/item/used_item, mob/user) + // We do this here to avoid putting the vessel straight into storage. + // This is usually handled by afterattack on /chems. + if(storage && ATOM_IS_OPEN_CONTAINER(used_item) && user.check_intent(I_FLAG_HELP)) + if(used_item.standard_dispenser_refill(user, src)) + return TRUE + if(used_item.standard_pour_into(user, src)) + return TRUE + return ..() + +/obj/structure/working/quern/initialize_reagents(populate = TRUE) + if(!reagents) + create_reagents(volume) + else + reagents.maximum_volume = max(reagents.maximum_volume, volume) + . = ..() + +/obj/structure/working/quern/verb/set_amount_dispensed() + set name = "Set amount dispensed" + set category = "Object" + set src in view(1) + if(!CanPhysicallyInteract(usr)) + to_chat(usr, SPAN_NOTICE("You're in no condition to do that!")) + return + var/new_amount = input("Amount dispensed:","[src]") as null|anything in cached_json_decode(possible_transfer_amounts) + if(!CanPhysicallyInteract(usr)) // because input takes time and the situation can change + to_chat(usr, SPAN_NOTICE("You're in no condition to do that!'")) + return + if (new_amount) + amount_dispensed = new_amount + +/obj/structure/working/quern/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(distance <= 2) + . += SPAN_NOTICE("It contains:") + if(LAZYLEN(reagents?.reagent_volumes)) + for(var/decl/material/reagent as anything in reagents.liquid_volumes) + . += SPAN_NOTICE("[LIQUID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID)].") + for(var/decl/material/reagent as anything in reagents.solid_volumes) + . += SPAN_NOTICE("[SOLID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)].") + +/obj/structure/working/quern/get_reagent_amount_dispensed() + return amount_dispensed + +/obj/structure/working/quern/get_alt_interactions(var/mob/user) + . = ..() + LAZYADD(., /decl/interaction_handler/set_transfer/quern) + +//Set amount dispensed +/decl/interaction_handler/set_transfer/quern + expected_target_type = /obj/structure/working/quern + +/decl/interaction_handler/set_transfer/quern/is_possible(var/atom/target, var/mob/user) + . = ..() + if(.) + var/obj/structure/working/quern/quern = target + return !!quern.possible_transfer_amounts + +/decl/interaction_handler/set_transfer/quern/invoked(atom/target, mob/user, obj/item/prop) + var/obj/structure/working/quern/quern = target + quern.set_amount_dispensed() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index ba4d347b9fc3..ac1dc15b6a4f 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -23,6 +23,9 @@ if (!possible_transfer_amounts) verbs -= /obj/structure/reagent_dispensers/verb/set_amount_dispensed +/obj/structure/reagent_dispensers/get_reagent_amount_dispensed() + return amount_dispensed + /obj/structure/reagent_dispensers/receive_mouse_drop(atom/dropping, mob/user, params) if(!(. = ..()) && user?.get_active_held_item() == dropping && isitem(dropping)) // Awful. Sorry. @@ -97,11 +100,11 @@ set category = "Object" set src in view(1) if(!CanPhysicallyInteract(usr)) - to_chat(usr, SPAN_NOTICE("You're in no condition to do that!'")) + to_chat(usr, SPAN_NOTICE("You're in no condition to do that!")) return var/N = input("Amount dispensed:","[src]") as null|anything in cached_json_decode(possible_transfer_amounts) if(!CanPhysicallyInteract(usr)) // because input takes time and the situation can change - to_chat(usr, SPAN_NOTICE("You're in no condition to do that!'")) + to_chat(usr, SPAN_NOTICE("You're in no condition to do that!")) return if (N) amount_dispensed = N diff --git a/icons/obj/structures/quern.dmi b/icons/obj/structures/quern.dmi new file mode 100644 index 0000000000000000000000000000000000000000..5867670c10e5e13da731231e55ef18a3358243f8 GIT binary patch literal 539 zcmV+$0_6RPP)fFDZ*Bkp zc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LY zR3KBSJijO>MTv_uC9|j)$T#HTOe;#vO@**gRp|oB?99A$?5Zt*qNzEFl^|gq6v(Bl z;OgfBb{zn`c`b=~p#cT}00ApWL_t(oh3%JJu7e;DMn_j5)xHDHT|fmEps)jE|66I( z+oV7(KR4-pX!zcaVUQS{lL2V?V1^98gl0GnV>Hc#2Jd7NFEqtM1l)_=*d@iz?c6Jv zokV46k|la)8m%6+9nx%`8}v%5vs6nd|E<793sMZ-Y_-sqmGbR@lScUq(hS{p`_6V* zOBuR}3$Y+6W$2mad|r7e10YfEGSOm&&JZ%Ke&4IOrRAGb`#;3}Kga#O z@09by2Sdd4EkArfBYy9T-*zr}$XP$c1?QX}SXT!@Nqxjp&y=Gj1f Date: Sat, 11 Oct 2025 00:00:00 +1100 Subject: [PATCH 0908/1331] Generalising some structure reagent-handling code. --- code/game/objects/structures/__structure.dm | 38 +++++++---- .../structures/_structure_construction.dm | 8 +++ .../objects/structures/structure_reagents.dm | 47 +++++++++++++ code/modules/assembly/igniter.dm | 2 +- code/modules/crafting/working/quern.dm | 55 ++-------------- .../interactions/interactions_reagents.dm | 8 ++- code/modules/reagents/reagent_dispenser.dm | 66 ++----------------- nebula.dme | 1 + 8 files changed, 99 insertions(+), 126 deletions(-) create mode 100644 code/game/objects/structures/structure_reagents.dm diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 90bf4c09f19a..704ab61cde54 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -69,18 +69,32 @@ lock = new /datum/lock(src, lock) if(!CanFluidPass()) fluid_update(TRUE) + if (!isnull(get_possible_reagent_transfer_amounts())) + verbs |= /obj/structure/proc/set_reagent_amount_dispensed_verb /obj/structure/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - if(distance <= 3) - if(distance <= 1 && lock) - . += SPAN_NOTICE("\The [src] appears to have a lock, opened by '[lock.lock_data]'.") - var/damage_desc = get_examined_damage_string() - if(length(damage_desc)) - . += damage_desc - if(paint_color) - var/decl/pronouns/structure_pronouns = get_pronouns() // so we can do 'have' for plural objects like sheets - . += "\The [src] [structure_pronouns.has] been [paint_verb]." + if(distance > 3) + return + if(distance <= 1 && lock) + . += SPAN_NOTICE("\The [src] appears to have a lock, opened by '[lock.lock_data]'.") + var/damage_desc = get_examined_damage_string() + if(length(damage_desc)) + . += damage_desc + if(paint_color) + var/decl/pronouns/structure_pronouns = get_pronouns() // so we can do 'have' for plural objects like sheets + . += "\The [src] [structure_pronouns.has] been [paint_verb]." + if(distance <= 2 && !isnull(get_possible_reagent_transfer_amounts()) && reagents) + . += SPAN_NOTICE("It contains:") + if(LAZYLEN(reagents.reagent_volumes)) + for(var/decl/material/reagent as anything in reagents.liquid_volumes) + . += SPAN_NOTICE("[LIQUID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID)].") + for(var/decl/material/reagent as anything in reagents.solid_volumes) + . += SPAN_NOTICE("[SOLID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)].") + else + . += SPAN_NOTICE("Nothing.") + if(reagents.maximum_volume) + . += "It may contain up to [reagents.maximum_volume] unit\s." /obj/structure/get_examine_hints(mob/user, distance, infix, suffix) . = ..() @@ -346,5 +360,7 @@ Note: This proc can be overwritten to allow for different types of auto-alignmen playsound(src.loc, hitsound, 100, 1) take_damage(AM.get_thrown_attack_force() * (TT.speed/THROWFORCE_SPEED_DIVISOR), AM.atom_damage_type) -/obj/structure/proc/get_reagent_amount_dispensed() - return null +/obj/structure/get_alt_interactions(var/mob/user) + . = ..() + if(!isnull(get_possible_reagent_transfer_amounts())) + LAZYADD(., /decl/interaction_handler/set_transfer/structure) diff --git a/code/game/objects/structures/_structure_construction.dm b/code/game/objects/structures/_structure_construction.dm index 85ffb92fa599..37d86d02566a 100644 --- a/code/game/objects/structures/_structure_construction.dm +++ b/code/game/objects/structures/_structure_construction.dm @@ -61,6 +61,14 @@ /obj/structure/attackby(obj/item/used_item, mob/user) + // We do this here to avoid putting the vessel straight into storage. + // This is usually handled by afterattack on /chems. + if(storage && !isnull(get_possible_reagent_transfer_amounts()) && ATOM_IS_OPEN_CONTAINER(used_item) && user.check_intent(I_FLAG_HELP)) + if(used_item.standard_dispenser_refill(user, src)) + return TRUE + if(used_item.standard_pour_into(user, src)) + return TRUE + if(used_item.user_can_attack_with(user, silent = TRUE)) var/force = used_item.expend_attack_force(user) if(force && user.check_intent(I_FLAG_HARM)) diff --git a/code/game/objects/structures/structure_reagents.dm b/code/game/objects/structures/structure_reagents.dm new file mode 100644 index 000000000000..f2d9decae039 --- /dev/null +++ b/code/game/objects/structures/structure_reagents.dm @@ -0,0 +1,47 @@ +/obj/structure/receive_mouse_drop(atom/dropping, mob/user, params) + if((. = ..()) || user?.get_active_held_item() != dropping || !isitem(dropping) || isnull(get_possible_reagent_transfer_amounts())) + return + // Awful. Sorry. + var/obj/item/item = dropping + var/old_atom_flags = atom_flags + atom_flags |= ATOM_FLAG_OPEN_CONTAINER + if(item.standard_pour_into(user, src)) + . = TRUE + atom_flags = old_atom_flags + +/obj/structure/proc/get_reagent_amount_dispensed() + return null + +/obj/structure/proc/set_reagent_amount_dispensed() + return null + +/obj/structure/proc/set_reagent_amount_dispensed_verb() + set name = "Set amount dispensed" + set category = "Object" + set src in view(1) + if(!CanPhysicallyInteract(usr)) + to_chat(usr, SPAN_NOTICE("You're in no condition to do that!")) + return + var/new_amount = input("Amount dispensed:","[src]") as null|anything in get_possible_reagent_transfer_amounts() + if(!CanPhysicallyInteract(usr)) // because input takes time and the situation can change + to_chat(usr, SPAN_NOTICE("You're in no condition to do that!'")) + return + if (new_amount) + set_reagent_amount_dispensed(new_amount) + +/obj/structure/proc/get_possible_reagent_transfer_amounts() + return null + +//Set amount dispensed. Added manually to querns and reagent dispensers. +/decl/interaction_handler/set_transfer/structure + expected_target_type = /obj/structure + +/decl/interaction_handler/set_transfer/structure/is_possible(var/atom/target, var/mob/user) + . = ..() + if(.) + var/obj/structure/dispenser = target + return !isnull(dispenser.get_possible_reagent_transfer_amounts()) + +/decl/interaction_handler/set_transfer/structure/invoked(atom/target, mob/user, obj/item/prop) + var/obj/structure/dispenser = target + dispenser.set_reagent_amount_dispensed_verb() diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index f692cd70b9d7..7f8b14ca0064 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -23,7 +23,7 @@ if(location) location.hotspot_expose(1000,1000) if (istype(src.loc,/obj/item/assembly_holder)) - if (istype(src.loc.loc, /obj/structure/reagent_dispensers/fueltank/)) + if (istype(src.loc.loc, /obj/structure/reagent_dispensers/fueltank)) var/obj/structure/reagent_dispensers/fueltank/tank = src.loc.loc if(tank) tank.try_detonate_reagents() diff --git a/code/modules/crafting/working/quern.dm b/code/modules/crafting/working/quern.dm index 3278110bb870..afe6821edc73 100644 --- a/code/modules/crafting/working/quern.dm +++ b/code/modules/crafting/working/quern.dm @@ -58,17 +58,6 @@ QDEL_NULL(grinding) return TRUE -// Reagent handling code copied from reagent dispensers. TODO: make reagent handling an extension or something. -/obj/structure/working/quern/attackby(obj/item/used_item, mob/user) - // We do this here to avoid putting the vessel straight into storage. - // This is usually handled by afterattack on /chems. - if(storage && ATOM_IS_OPEN_CONTAINER(used_item) && user.check_intent(I_FLAG_HELP)) - if(used_item.standard_dispenser_refill(user, src)) - return TRUE - if(used_item.standard_pour_into(user, src)) - return TRUE - return ..() - /obj/structure/working/quern/initialize_reagents(populate = TRUE) if(!reagents) create_reagents(volume) @@ -76,47 +65,11 @@ reagents.maximum_volume = max(reagents.maximum_volume, volume) . = ..() -/obj/structure/working/quern/verb/set_amount_dispensed() - set name = "Set amount dispensed" - set category = "Object" - set src in view(1) - if(!CanPhysicallyInteract(usr)) - to_chat(usr, SPAN_NOTICE("You're in no condition to do that!")) - return - var/new_amount = input("Amount dispensed:","[src]") as null|anything in cached_json_decode(possible_transfer_amounts) - if(!CanPhysicallyInteract(usr)) // because input takes time and the situation can change - to_chat(usr, SPAN_NOTICE("You're in no condition to do that!'")) - return - if (new_amount) - amount_dispensed = new_amount - -/obj/structure/working/quern/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(distance <= 2) - . += SPAN_NOTICE("It contains:") - if(LAZYLEN(reagents?.reagent_volumes)) - for(var/decl/material/reagent as anything in reagents.liquid_volumes) - . += SPAN_NOTICE("[LIQUID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID)].") - for(var/decl/material/reagent as anything in reagents.solid_volumes) - . += SPAN_NOTICE("[SOLID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)].") +/obj/structure/working/quern/set_reagent_amount_dispensed(new_amount) + amount_dispensed = new_amount /obj/structure/working/quern/get_reagent_amount_dispensed() return amount_dispensed -/obj/structure/working/quern/get_alt_interactions(var/mob/user) - . = ..() - LAZYADD(., /decl/interaction_handler/set_transfer/quern) - -//Set amount dispensed -/decl/interaction_handler/set_transfer/quern - expected_target_type = /obj/structure/working/quern - -/decl/interaction_handler/set_transfer/quern/is_possible(var/atom/target, var/mob/user) - . = ..() - if(.) - var/obj/structure/working/quern/quern = target - return !!quern.possible_transfer_amounts - -/decl/interaction_handler/set_transfer/quern/invoked(atom/target, mob/user, obj/item/prop) - var/obj/structure/working/quern/quern = target - quern.set_amount_dispensed() +/obj/structure/working/quern/get_possible_reagent_transfer_amounts() + return cached_json_decode(possible_transfer_amounts) diff --git a/code/modules/interactions/interactions_reagents.dm b/code/modules/interactions/interactions_reagents.dm index 7a50cfaacf59..80de48cc9177 100644 --- a/code/modules/interactions/interactions_reagents.dm +++ b/code/modules/interactions/interactions_reagents.dm @@ -27,15 +27,19 @@ return if(target == prop || target.reagents?.total_volume < FLUID_PUDDLE) return FALSE - if(!istype(prop) || (!isitem(target) && !istype(target, /obj/structure/reagent_dispensers))) + if(!istype(prop) || (!isitem(target) && !istype(target, /obj/structure))) return FALSE + if(istype(target, /obj/structure)) + var/obj/structure/struct = target + if(isnull(struct.get_possible_reagent_transfer_amounts())) + return FALSE // Not a dispenser return target.can_be_poured_from(user, prop) && prop.can_be_poured_into(user, target) /decl/interaction_handler/fill_from/invoked(atom/target, mob/user, obj/item/prop) if(isitem(target)) var/obj/item/vessel = target return vessel.standard_pour_into(user, prop) - if(istype(target, /obj/structure/reagent_dispensers)) + if(istype(target, /obj/structure)) // Reagent dispensers have some wonky assumptions due to old UX around filling/emptying so we skip the atom flags check. return prop.standard_dispenser_refill(user, target, skip_container_check = TRUE) return FALSE diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index ac1dc15b6a4f..8b2dc8e5dbab 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -18,23 +18,14 @@ var/can_toggle_open = TRUE var/tmp/possible_transfer_amounts = @"[10,25,50,100,500]" -/obj/structure/reagent_dispensers/Initialize(ml, _mat, _reinf_mat) - . = ..() - if (!possible_transfer_amounts) - verbs -= /obj/structure/reagent_dispensers/verb/set_amount_dispensed - /obj/structure/reagent_dispensers/get_reagent_amount_dispensed() return amount_dispensed -/obj/structure/reagent_dispensers/receive_mouse_drop(atom/dropping, mob/user, params) - if(!(. = ..()) && user?.get_active_held_item() == dropping && isitem(dropping)) - // Awful. Sorry. - var/obj/item/item = dropping - var/old_atom_flags = atom_flags - atom_flags |= ATOM_FLAG_OPEN_CONTAINER - if(item.standard_pour_into(user, src)) - . = TRUE - atom_flags = old_atom_flags +/obj/structure/reagent_dispensers/set_reagent_amount_dispensed(new_amount) + amount_dispensed = new_amount + +/obj/structure/reagent_dispensers/get_possible_reagent_transfer_amounts() + return cached_json_decode(possible_transfer_amounts) /obj/structure/reagent_dispensers/on_reagent_change() if(!(. = ..())) @@ -64,27 +55,9 @@ . += "Its refilling cap is open." else . += "Its refilling cap is closed." - . += SPAN_NOTICE("It contains:") - if(LAZYLEN(reagents?.reagent_volumes)) - for(var/decl/material/reagent as anything in reagents.liquid_volumes) - . += SPAN_NOTICE("[LIQUID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID)].") - for(var/decl/material/reagent as anything in reagents.solid_volumes) - . += SPAN_NOTICE("[SOLID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)].") - else - . += SPAN_NOTICE("Nothing.") - if(reagents?.maximum_volume) - . += "It may contain up to [reagents.maximum_volume] unit\s of fluid." /obj/structure/reagent_dispensers/attackby(obj/item/used_item, mob/user) - // We do this here to avoid putting the vessel straight into storage. - // This is usually handled by afterattack on /chems. - if(storage && ATOM_IS_OPEN_CONTAINER(used_item) && user.check_intent(I_FLAG_HELP)) - if(used_item.standard_dispenser_refill(user, src)) - return TRUE - if(used_item.standard_pour_into(user, src)) - return TRUE - if(wrenchable && IS_WRENCH(used_item)) unwrenched = !unwrenched visible_message(SPAN_NOTICE("\The [user] wrenches \the [src]'s tap [unwrenched ? "open" : "shut"].")) @@ -95,20 +68,6 @@ . = ..() -/obj/structure/reagent_dispensers/verb/set_amount_dispensed() - set name = "Set amount dispensed" - set category = "Object" - set src in view(1) - if(!CanPhysicallyInteract(usr)) - to_chat(usr, SPAN_NOTICE("You're in no condition to do that!")) - return - var/N = input("Amount dispensed:","[src]") as null|anything in cached_json_decode(possible_transfer_amounts) - if(!CanPhysicallyInteract(usr)) // because input takes time and the situation can change - to_chat(usr, SPAN_NOTICE("You're in no condition to do that!")) - return - if (N) - amount_dispensed = N - /obj/structure/reagent_dispensers/explosion_act(severity) . = ..() if(. && (severity == 1) || (severity == 2 && prob(50)) || (severity == 3 && prob(5))) @@ -323,24 +282,9 @@ //Interactions /obj/structure/reagent_dispensers/get_alt_interactions(var/mob/user) . = ..() - LAZYADD(., /decl/interaction_handler/set_transfer/reagent_dispenser) if(can_toggle_open) LAZYADD(., /decl/interaction_handler/toggle_open/reagent_dispenser) -//Set amount dispensed -/decl/interaction_handler/set_transfer/reagent_dispenser - expected_target_type = /obj/structure/reagent_dispensers - -/decl/interaction_handler/set_transfer/reagent_dispenser/is_possible(var/atom/target, var/mob/user) - . = ..() - if(.) - var/obj/structure/reagent_dispensers/R = target - return !!R.possible_transfer_amounts - -/decl/interaction_handler/set_transfer/reagent_dispenser/invoked(atom/target, mob/user, obj/item/prop) - var/obj/structure/reagent_dispensers/R = target - R.set_amount_dispensed() - //Allows normal refilling, or toggle back to normal reagent dispenser operation /decl/interaction_handler/toggle_open/reagent_dispenser name = "Toggle refilling cap" diff --git a/nebula.dme b/nebula.dme index 9413d6426c4f..27a7c09e2e86 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1459,6 +1459,7 @@ #include "code\game\objects\structures\snowman.dm" #include "code\game\objects\structures\sofa.dm" #include "code\game\objects\structures\stasis_cage.dm" +#include "code\game\objects\structures\structure_reagents.dm" #include "code\game\objects\structures\tables.dm" #include "code\game\objects\structures\tank_dispenser.dm" #include "code\game\objects\structures\target_stake.dm" From 3989f990495ebf0531091faff54f3ec001318ec4 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sat, 1 Nov 2025 00:25:08 +1100 Subject: [PATCH 0909/1331] Adding querns to the shadyhills map. --- maps/shaded_hills/shaded_hills-inn.dmm | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/maps/shaded_hills/shaded_hills-inn.dmm b/maps/shaded_hills/shaded_hills-inn.dmm index 11b1e507fb20..59e1397ce1d1 100644 --- a/maps/shaded_hills/shaded_hills-inn.dmm +++ b/maps/shaded_hills/shaded_hills-inn.dmm @@ -1195,6 +1195,10 @@ }, /turf/floor/wood/rough/walnut, /area/shaded_hills/shrine/kitchen) +"Fz" = ( +/obj/structure/working/quern, +/turf/floor/wood/rough/walnut, +/area/shaded_hills/storehouse) "FD" = ( /obj/structure/reagent_dispensers/barrel/ebony, /obj/item/seeds/extracted/rice, @@ -1293,6 +1297,11 @@ /obj/structure/wall_sconce/lantern, /turf/floor/path/herringbone/basalt, /area/shaded_hills/inn/kitchen) +"Hp" = ( +/obj/structure/table/wood/reinforced/ebony, +/obj/item/chems/glass/bucket/wood, +/turf/floor/wood/rough/walnut, +/area/shaded_hills/storehouse) "Hv" = ( /obj/structure/railing/mapped/wooden/walnut, /obj/structure/flora/bush/sparsegrass, @@ -9547,7 +9556,7 @@ uJ dH HI QQ -fR +Hp fR fR fR @@ -9699,7 +9708,7 @@ uJ Wg HI QQ -fR +Fz fR fR fR @@ -10003,7 +10012,7 @@ yz Wg HI UY -fR +Hp fR fR fR @@ -10155,7 +10164,7 @@ BL dH HI QQ -fR +Fz fR fR fR From 40f7471b685a69842143b10f0bc315289aea3644 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Tue, 11 Nov 2025 11:25:00 +1100 Subject: [PATCH 0910/1331] Adding maniple organ category. --- code/__defines/damage_organs.dm | 2 ++ code/_helpers/global_lists.dm | 9 ++++--- code/controllers/subsystems/jobs.dm | 2 +- code/modules/bodytype/_bodytype.dm | 7 +++--- .../mob/living/human/human_movement.dm | 24 +++++++++++++------ code/modules/mob/living/human/human_organs.dm | 16 +++++++------ code/modules/mob/living/living.dm | 8 ------- code/modules/organs/external/quadruped.dm | 8 +++---- code/modules/organs/external/standard.dm | 6 +++-- code/modules/organs/organ.dm | 6 ++--- code/modules/species/species.dm | 5 ++-- code/modules/submaps/submap_join.dm | 3 ++- code/unit_tests/codex.dm | 2 +- 13 files changed, 55 insertions(+), 43 deletions(-) diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index 4c300aeeec52..8fbc2d9a9b0f 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -77,6 +77,8 @@ #define ORGAN_CATEGORY_STANCE "stance" /// Limb is considered the 'root' of a given stance limb (leg) - also counted for stance damage a la ORGAN_CATEGORY_STANCE #define ORGAN_CATEGORY_STANCE_ROOT "stance_root" +// Limb is considered a manipulator, currently only used when trying to pilot a wheelchair. +#define ORGAN_CATEGORY_MANIPLE "maniple" // Droplimb types. #define DISMEMBER_METHOD_EDGE 0 diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 7bb01c2778df..daeb63811418 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -37,12 +37,15 @@ var/global/list/string_slot_flags = list( ) // Used to avoid constantly generating new lists during movement. +var/global/list/all_maniple_limbs = list( + (ORGAN_CATEGORY_MANIPLE) +) var/global/list/all_stance_limbs = list( - ORGAN_CATEGORY_STANCE, - ORGAN_CATEGORY_STANCE_ROOT + (ORGAN_CATEGORY_STANCE), + (ORGAN_CATEGORY_STANCE_ROOT) ) var/global/list/child_stance_limbs = list( - ORGAN_CATEGORY_STANCE + (ORGAN_CATEGORY_STANCE) ) // TODO: Replace keybinding datums with keybinding decls to make this unnecessary. diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 35519221dbfe..a166bb41242d 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -556,7 +556,7 @@ SUBSYSTEM_DEF(jobs) if(job.req_admin_notify) to_chat(H, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") - if(H.needs_wheelchair()) + if(H.cannot_stand()) equip_wheelchair(H) BITSET(H.hud_updateflag, ID_HUD) diff --git a/code/modules/bodytype/_bodytype.dm b/code/modules/bodytype/_bodytype.dm index e706f5b387ae..fd6206eefa45 100644 --- a/code/modules/bodytype/_bodytype.dm +++ b/code/modules/bodytype/_bodytype.dm @@ -415,20 +415,19 @@ var/global/list/bodytypes_by_category = list() var/list/organ_data = has_limbs[organ_tag] var/obj/item/organ/organ = organ_data["path"] organ_data["descriptor"] = initial(organ.name) - var/organ_cat = initial(organ.organ_category) - if(organ_cat) + for(var/organ_cat in cached_json_decode(initial(organ.organ_categories))) LAZYINITLIST(_organs_by_category) LAZYADD(_organs_by_category[organ_cat], organ) LAZYINITLIST(_organ_tags_by_category) LAZYADD(_organ_tags_by_category[organ_cat], organ_tag) + var/list/parent_organ_data = has_limbs[organ::parent_organ] if(parent_organ_data) parent_organ_data["has_children"]++ for(var/organ_tag in has_organ) var/obj/item/organ/organ = has_organ[organ_tag] - var/organ_cat = initial(organ.organ_category) - if(organ_cat) + for(var/organ_cat in cached_json_decode(initial(organ.organ_categories))) LAZYINITLIST(_organs_by_category) LAZYADD(_organs_by_category[organ_cat], organ) LAZYINITLIST(_organ_tags_by_category) diff --git a/code/modules/mob/living/human/human_movement.dm b/code/modules/mob/living/human/human_movement.dm index 012f9071302a..0d2c601638e9 100644 --- a/code/modules/mob/living/human/human_movement.dm +++ b/code/modules/mob/living/human/human_movement.dm @@ -25,20 +25,25 @@ if(can_feel_pain() && get_shock() >= 10) tally += (get_shock() / 10) //pain shouldn't slow you down if you can't even feel it + var/list/limbs_to_check if(istype(buckled, /obj/structure/chair/wheelchair)) - for(var/organ_name in list(BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM)) - var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, organ_name) - tally += E ? E.get_movement_delay(4) : 4 - else for(var/obj/item/I in get_equipped_items(include_carried = TRUE)) var/slot = get_equipped_slot_for_item(I) tally += LAZYACCESS(I.slowdown_per_slot, slot) tally += I.slowdown_general tally += I.slowdown_accessory + limbs_to_check = global.all_maniple_limbs + else + limbs_to_check = global.all_stance_limbs - for(var/organ_name in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, organ_name) - tally += E ? E.get_movement_delay(4) : 4 + var/missing_limbs = H ? H.bodytype.get_expected_organ_count_for_categories(limbs_to_check) : 4 + if(missing_limbs > 0) + var/max_delay_per_limb = ceil(16/missing_limbs) + for(var/obj/item/organ/external/limb in get_organs_by_categories(limbs_to_check)) + tally += limb.get_movement_delay(max_delay_per_limb) + missing_limbs-- + if(missing_limbs > 0) + tally += missing_limbs * max_delay_per_limb if(shock_stage >= 10 || get_stamina() <= 0) tally += 3 @@ -96,6 +101,11 @@ handle_leg_damage() species.handle_post_move(src) +/mob/living/human/forceMove() + . = ..() + if(.) + species.handle_post_move(src, exertion = FALSE) + /mob/living/human/proc/handle_leg_damage() if(!can_feel_pain()) return diff --git a/code/modules/mob/living/human/human_organs.dm b/code/modules/mob/living/human/human_organs.dm index 1b4884758d9c..3b27e39e2472 100644 --- a/code/modules/mob/living/human/human_organs.dm +++ b/code/modules/mob/living/human/human_organs.dm @@ -70,9 +70,10 @@ LAZYDISTINCTADD(external_organs, O) // Update our organ category lists, if neeed. - if(O.organ_category) - LAZYINITLIST(organs_by_category) - LAZYDISTINCTADD(organs_by_category[O.organ_category], O) + if(O.organ_categories) + for(var/category in cached_json_decode(O.organ_categories)) + LAZYINITLIST(organs_by_category) + LAZYDISTINCTADD(organs_by_category[category], O) // Update stat organs as well if(O.has_stat_info) @@ -112,10 +113,11 @@ LAZYREMOVE(external_organs, O) // Update our organ category lists, if neeed. - if(O.organ_category && islist(organs_by_category)) - organs_by_category[O.organ_category] -= O - if(LAZYLEN(organs_by_category[O.organ_category]) <= 0) - LAZYREMOVE(organs_by_category, O.organ_category) + if(O.organ_categories && islist(organs_by_category)) + for(var/category in cached_json_decode(O.organ_categories)) + organs_by_category[category] -= O + if(LAZYLEN(organs_by_category[category]) <= 0) + LAZYREMOVE(organs_by_category, category) // Update stat organs as well if(O.has_stat_info && stat_organs) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 1e6497c10a04..508476cc74d3 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -838,14 +838,6 @@ default behaviour is: if(saturation > 0) fluids.trans_to_holder(touching_reagents, saturation) -/mob/living/proc/needs_wheelchair() - var/tmp_stance_damage = 0 - for(var/limb_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, limb_tag) - if(!E || !E.is_usable()) - tmp_stance_damage += 2 - return tmp_stance_damage >= 4 - /mob/living/proc/seizure() set waitfor = 0 sleep(rand(5,10)) diff --git a/code/modules/organs/external/quadruped.dm b/code/modules/organs/external/quadruped.dm index 1411f8e91e10..5ea0d59dde9c 100644 --- a/code/modules/organs/external/quadruped.dm +++ b/code/modules/organs/external/quadruped.dm @@ -20,7 +20,7 @@ amputation_point = "front left knee" tendon_name = "cruciate ligament" artery_name = "femoral artery" - organ_category = ORGAN_CATEGORY_STANCE_ROOT + organ_categories = @"['" + ORGAN_CATEGORY_STANCE_ROOT + "']" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE /obj/item/organ/external/arm/right/quadruped @@ -29,7 +29,7 @@ amputation_point = "front right knee" tendon_name = "cruciate ligament" artery_name = "femoral artery" - organ_category = ORGAN_CATEGORY_STANCE_ROOT + organ_categories = @"['" + ORGAN_CATEGORY_STANCE_ROOT + "']" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE /obj/item/organ/external/hand/quadruped @@ -38,7 +38,7 @@ amputation_point = "front left ankle" tendon_name = "Achilles tendon" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE - organ_category = ORGAN_CATEGORY_STANCE + organ_categories = @"['" + ORGAN_CATEGORY_STANCE + "']" gripper_type = null /obj/item/organ/external/hand/right/quadruped @@ -47,5 +47,5 @@ amputation_point = "front right ankle" tendon_name = "Achilles tendon" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE - organ_category = ORGAN_CATEGORY_STANCE + organ_categories = @"['" + ORGAN_CATEGORY_STANCE + "']" gripper_type = null diff --git a/code/modules/organs/external/standard.dm b/code/modules/organs/external/standard.dm index 85c880a0a84c..570c39460722 100644 --- a/code/modules/organs/external/standard.dm +++ b/code/modules/organs/external/standard.dm @@ -61,6 +61,7 @@ artery_name = "basilic vein" arterial_bleed_severity = 0.75 limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE + organ_categories = @"['" + ORGAN_CATEGORY_MANIPLE + "']" /obj/item/organ/external/arm/right organ_tag = BP_R_ARM @@ -83,7 +84,7 @@ tendon_name = "cruciate ligament" artery_name = "femoral artery" arterial_bleed_severity = 0.75 - organ_category = ORGAN_CATEGORY_STANCE_ROOT + organ_categories = @"['" + ORGAN_CATEGORY_STANCE_ROOT + "']" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE /obj/item/organ/external/leg/right @@ -108,7 +109,7 @@ tendon_name = "Achilles tendon" arterial_bleed_severity = 0.5 limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE - organ_category = ORGAN_CATEGORY_STANCE + organ_categories = @"['" + ORGAN_CATEGORY_STANCE + "']" /obj/item/organ/external/foot/get_natural_attacks() var/static/list/unarmed_attacks = list( @@ -141,6 +142,7 @@ arterial_bleed_severity = 0.5 limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_FINGERPRINT | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE is_washable = TRUE + organ_categories = @"['" + ORGAN_CATEGORY_MANIPLE + "']" var/gripper_type = /datum/inventory_slot/gripper/left_hand /obj/item/organ/external/hand/get_natural_attacks() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index d3d49512d744..aa400ec3b073 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -13,15 +13,15 @@ // Strings. /// Unique identifier. var/organ_tag = "organ" - /// Identifier for use in organ collections, unused if unset. Would be nice to make this a list, but bodytypes rely on initial() with it. - var/organ_category + /// Identifiers for use in organ collections, unused if unset. Must be formatted as a JSON list string. + var/organ_categories /// Organ holding this object. var/parent_organ = BP_CHEST // Status tracking. /// Various status flags (such as robotic) var/status = 0 - /// A flag for telling what capabilities this organ has. ORGAN_PROP_PROSTHETIC, ORGAN_PROP_CRYSTAL, etc.. + /// A flag for telling what capabilities this organ has. ORGAN_PROP_PROSTHETIC, ORGAN_PROP_CRYSTAL, etc. var/organ_properties = 0 /// Cache var for vitality to current owner. var/vital_to_owner diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index 799a656f84dc..097223be2514 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -664,8 +664,9 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 // This assumes that if a pain-level has been defined it also has a list of emotes to go with it return pick(pain_emotes) -/decl/species/proc/handle_post_move(var/mob/living/human/H) - handle_exertion(H) +/decl/species/proc/handle_post_move(var/mob/living/human/H, exertion = TRUE) + if(exertion) + handle_exertion(H) /decl/species/proc/handle_exertion(mob/living/human/H) if (!exertion_effect_chance) diff --git a/code/modules/submaps/submap_join.dm b/code/modules/submaps/submap_join.dm index 4b1b6d3a4420..4a38fc4568cf 100644 --- a/code/modules/submaps/submap_join.dm +++ b/code/modules/submaps/submap_join.dm @@ -112,7 +112,8 @@ global.universe.OnPlayerLatejoin(character) log_and_message_admins("has joined the round as offsite role [character.mind.assigned_role].", character) RAISE_EVENT(/decl/observ/submap_join, src, character, job) - if(character.cannot_stand()) equip_wheelchair(character) + if(character.cannot_stand()) + equip_wheelchair(character) job.post_equip_job_title(character, job.title) qdel(joining) diff --git a/code/unit_tests/codex.dm b/code/unit_tests/codex.dm index 2130edd1f887..5ce1cca35035 100644 --- a/code/unit_tests/codex.dm +++ b/code/unit_tests/codex.dm @@ -1,5 +1,5 @@ /datum/unit_test/codex_string_uniqueness - name = "CODEX: All Codex Associated Strings Shall Be Unique" + name = "CODEX: All Codex Associated Strings Shall Be Unique" /datum/unit_test/codex_string_uniqueness/start_test() var/list/failures = list() From 9b9ebf6ee22b8c0bce326a1459d00c120b6af108 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Tue, 7 Oct 2025 16:34:27 -0500 Subject: [PATCH 0911/1331] lighting: Propagate ambient lights all the way up a Z-stack --- code/game/turfs/turf_changing.dm | 6 ++ code/modules/lighting/lighting_corner.dm | 95 ++++++++++++++++++++---- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index dedebcfc16aa..5c849feec9a2 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -64,6 +64,7 @@ var/old_affecting_lights = affecting_lights var/old_lighting_overlay = lighting_overlay var/old_dynamic_lighting = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) + var/old_z_opacity = z_flags & ZM_ALLOW_LIGHTING var/old_flooded = flooded var/old_outside = is_outside var/old_is_open = is_open() @@ -145,6 +146,11 @@ if (old_ambience != ambient_light || old_ambience_mult != ambient_light_multiplier) update_ambient_light(FALSE) + var/new_z_opacity = z_flags & ZM_ALLOW_LIGHTING + if (new_z_opacity != old_z_opacity) + for (var/datum/lighting_corner/corn in corners) + corn.rebuild_ztraversal(!new_z_opacity) + var/tidlu = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) if ((old_opacity != opacity) || (tidlu != old_dynamic_lighting) || force_lighting_update) reconsider_lights() diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 3d150896b47b..b9e977e22eb4 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -34,7 +34,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/self_g = 0 var/self_b = 0 - // The intensity we're inheriting from the turf below us, if we're a Z-turf. + // The intensity we're inheriting from the turf below us, if we're a Z-turf. This is a sum of all below turfs in the Z-stack. var/below_r = 0 var/below_g = 0 var/below_b = 0 @@ -201,10 +201,13 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, T = null if (TURF_IS_DYNAMICALLY_LIT(T)) - if (!T.corners || !T.corners[Ti]) - T.generate_missing_corners() - var/datum/lighting_corner/above = T.corners[Ti] - above.update_below_lumcount(delta_r, delta_g, delta_b, now) + do + if (!T.corners || !T.corners[Ti]) + T.generate_missing_corners() + + // Above corners never get instant updates; they're less important, so better to avoid risk of lag. + T.corners[Ti].update_below_lumcount(delta_r, delta_g, delta_b) + while ((T = T.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) // This needs to be down here instead of the above if so the lum values are properly updated. if (needs_update) @@ -216,7 +219,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, needs_update = TRUE SSlighting.corner_queue += src -/datum/lighting_corner/proc/update_below_lumcount(delta_r, delta_g, delta_b, now = FALSE) +/datum/lighting_corner/proc/update_below_lumcount(delta_r, delta_g, delta_b) if (!(delta_r + delta_g + delta_b)) return @@ -232,11 +235,8 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, if (needs_update) return - if (!now) - needs_update = TRUE - SSlighting.corner_queue += src - else - update_overlays(TRUE) + needs_update = TRUE + SSlighting.corner_queue += src /datum/lighting_corner/proc/update_ambient_lumcount(delta_r, delta_g, delta_b, skip_update = FALSE) @@ -303,8 +303,6 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, needs_update = TRUE SSlighting.corner_queue += src -#undef UPDATE_APPARENT - /datum/lighting_corner/proc/update_overlays(now = FALSE) var/lr = apparent_r var/lg = apparent_g @@ -339,6 +337,75 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, Ov.needs_update = TRUE SSlighting.overlay_queue += Ov +// This is called when our turf's downward Z-opacity changes. +/datum/lighting_corner/proc/rebuild_ztraversal(new_opacity) + /* + If opacity transitions to off: + - Look down stack, removing below lights contributing to us + - Look up stack, updating turfs with our new below lighting value + If opacity transitions to on: + - Look down stack, add below contributors + - Look up stack, updating turfs with our new below lighting value + */ + below_r = below_g = below_b = 0 + var/turf/T = null + var/datum/lighting_corner/Tcorn = src + var/Ti + + if (!new_opacity) + for (;;) + if (Tcorn.t1 && (T = Tcorn.t1.below || GET_BELOW(Tcorn.t1)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t1i + else if (Tcorn.t2 && (T = Tcorn.t2.below || GET_BELOW(Tcorn.t2)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t2i + else if (Tcorn.t3 && (T = Tcorn.t3.below || GET_BELOW(Tcorn.t3)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t3i + else if (Tcorn.t4 && (T = Tcorn.t4.below || GET_BELOW(Tcorn.t4)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t4i + else // Nothing above us that cares about below light. + break + + Tcorn = T.corners[Ti] + below_r += Tcorn.apparent_r + below_g += Tcorn.apparent_g + below_b += Tcorn.apparent_b + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + if (!needs_update) + needs_update = TRUE + SSlighting.corner_queue += src + + T = null + Tcorn = src + + for (;;) + if (Tcorn.t1 && (T = Tcorn.t1.above || GET_ABOVE(Tcorn.t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t1i + else if (Tcorn.t2 && (T = Tcorn.t2.above || GET_ABOVE(Tcorn.t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t2i + else if (Tcorn.t3 && (T = Tcorn.t3.above || GET_ABOVE(Tcorn.t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t3i + else if (Tcorn.t4 && (T = Tcorn.t4.above || GET_ABOVE(Tcorn.t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = Tcorn.t4i + else // Nothing above us that cares about below light. + break + + Tcorn = T.corners[Ti] + Tcorn.below_r += apparent_r + Tcorn.below_g += apparent_g + Tcorn.below_b += apparent_b + + UPDATE_APPARENT(Tcorn, r) + UPDATE_APPARENT(Tcorn, g) + UPDATE_APPARENT(Tcorn, b) + + if (!Tcorn.needs_update) + Tcorn.needs_update = TRUE + SSlighting.corner_queue += Tcorn + /datum/lighting_corner/Destroy(force = FALSE) PRINT_STACK_TRACE("Someone [force ? "force-" : ""]deleted a lighting corner.") if (!force) @@ -349,3 +416,5 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, /datum/lighting_corner/dummy/New() return + +#undef UPDATE_APPARENT From c07910eb5701c523700bbc41ae29c32104f3c5ca Mon Sep 17 00:00:00 2001 From: Lohikar Date: Fri, 21 Nov 2025 20:08:40 -0600 Subject: [PATCH 0912/1331] lighting: Speculative fix for ambience burn --- code/modules/lighting/lighting_corner.dm | 26 ++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index b9e977e22eb4..639b0a023872 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -189,13 +189,14 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/turf/T var/Ti // Grab the first master that's a Z-turf, if one exists. - if (t1 && (T = GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + // The above var cannot be relied on due to init ordering, but we can use it if it is set. + if (t1 && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) Ti = t1i - else if (t2 && (T = GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + else if (t2 && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) Ti = t2i - else if (t3 && (T = GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + else if (t3 && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) Ti = t3i - else if (t4 && (T = GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + else if (t4 && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) Ti = t4i else // Nothing above us that cares about below light. T = null @@ -251,26 +252,21 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/turf/T var/Ti - if (t1 && (t1.below || HasBelow(t1.z)) && (t1.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(t1)) + if (t1) T = t1 Ti = t1i - else if (t2 && (t2.below || HasBelow(t2.z)) && (t2.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(t2)) + else if (t2) T = t2 Ti = t2i - else if (t3 && (t3.below || HasBelow(t3.z)) && (t3.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(t3)) + else if (t3) T = t3 Ti = t3i - else if (t4 && (t4.below || HasBelow(t4.z)) && (t4.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(t4)) + else if (t4) T = t4 Ti = t4i - // No MZ candidates below, just update. - else if (needs_update || skip_update) - return else - // Always queue for this, not important enough to hit the synchronous path. - needs_update = TRUE - SSlighting.corner_queue += src - return + // This should be impossible to reach -- how do we exist without at least one master turf? + CRASH("Corner has no masters!") var/datum/lighting_corner/below = src From c59321f970a60ad80d3e813c036958a1c1fded34 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Mon, 24 Nov 2025 17:38:09 -0600 Subject: [PATCH 0913/1331] lighting: Turf opacity change fixes --- code/game/turfs/walls/wall_attacks.dm | 1 - code/modules/lighting/lighting_turf.dm | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/code/game/turfs/walls/wall_attacks.dm b/code/game/turfs/walls/wall_attacks.dm index 15cd5a1dbeec..d736a234400d 100644 --- a/code/game/turfs/walls/wall_attacks.dm +++ b/code/game/turfs/walls/wall_attacks.dm @@ -17,7 +17,6 @@ for(var/turf/turf in loc) if(turf.simulated) SSair.mark_for_update(turf) - set_light(density) update_icon() update_air() refresh_opacity() diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 724b2d46182e..08d7fadca5c6 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -126,6 +126,11 @@ recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates. reconsider_lights() +// The normal opacity logic doesn't work for entities not located inside turfs, such as turfs. +/turf/set_opacity(new_opacity) + . = ..() + reconsider_lights() + // This block isn't needed now, but it's here if supporting area dyn lighting changes is needed later. // /turf/change_area(area/old_area, area/new_area) From c3bf0801263ca5c8323b836f25c7001f977f295c Mon Sep 17 00:00:00 2001 From: Lohikar Date: Mon, 24 Nov 2025 20:17:42 -0600 Subject: [PATCH 0914/1331] lighting: Fix internal ambience corruption from light sources --- code/modules/lighting/ambient_turf.dm | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/code/modules/lighting/ambient_turf.dm b/code/modules/lighting/ambient_turf.dm index 5b9ba5a03d9a..4b34c863ed90 100644 --- a/code/modules/lighting/ambient_turf.dm +++ b/code/modules/lighting/ambient_turf.dm @@ -69,10 +69,6 @@ SSlighting.total_ambient_turfs -= 1 return - if (!ambient_active) - SSlighting.total_ambient_turfs += 1 - ambient_active = TRUE - // There are four corners per (lit) turf, we don't want to apply our light 4 times -- compensate by dividing by 4. lr /= 4 lg /= 4 @@ -86,15 +82,17 @@ ambient_light_old_g += lg ambient_light_old_b += lb - if (!corners || !lighting_corners_initialised) - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) + if (!corners || !lighting_corners_initialised) generate_missing_corners() - else - return - // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. - for (var/datum/lighting_corner/C in corners) - C.update_ambient_lumcount(lr, lg, lb, !update) + // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. + for (var/datum/lighting_corner/C in corners) + C.update_ambient_lumcount(lr, lg, lb, !update) + + if (!ambient_active) + SSlighting.total_ambient_turfs += 1 + ambient_active = TRUE /// Wipe the entire self-ambience channel. This will preserve ambience from ambience groups. /turf/proc/clear_ambient_light() From a7f90e742bf1424b0b6cd5592c76f18349f69d27 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Tue, 25 Nov 2025 15:59:26 +1100 Subject: [PATCH 0915/1331] Removes empty for loop. --- code/modules/random_map/noise/ore.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/modules/random_map/noise/ore.dm b/code/modules/random_map/noise/ore.dm index f23e1c2cd01c..a6bc904f0a8e 100644 --- a/code/modules/random_map/noise/ore.dm +++ b/code/modules/random_map/noise/ore.dm @@ -75,7 +75,6 @@ var/tx = ((origin_x-1)+x)*chunk_size var/ty = ((origin_y-1)+y)*chunk_size - for(var/T in range(locate(tx, ty, origin_z))) for(var/i=0,i Date: Fri, 28 Nov 2025 13:51:30 -0500 Subject: [PATCH 0916/1331] Move prometheus metrics into a modpack --- code/game/world_topic_commands.dm | 8 -------- maps/modpack_testing/modpack_testing.dm | 2 ++ .../utility/prometheus_metrics/_prometheus_metrics.dm | 4 ++++ .../prometheus_metrics/_prometheus_metrics.dme | 11 +++++++++++ .../utility}/prometheus_metrics/metric_family.dm | 0 mods/utility/prometheus_metrics/metric_topic.dm | 7 +++++++ .../utility}/prometheus_metrics/metrics.dm | 0 .../utility}/prometheus_metrics/metrics/byond.dm | 0 .../utility}/prometheus_metrics/metrics/ss13.dm | 0 nebula.dme | 5 ----- 10 files changed, 24 insertions(+), 13 deletions(-) rename code/modules/prometheus_metrics/_defines.dm => mods/utility/prometheus_metrics/_prometheus_metrics.dm (55%) create mode 100644 mods/utility/prometheus_metrics/_prometheus_metrics.dme rename {code/modules => mods/utility}/prometheus_metrics/metric_family.dm (100%) create mode 100644 mods/utility/prometheus_metrics/metric_topic.dm rename {code/modules => mods/utility}/prometheus_metrics/metrics.dm (100%) rename {code/modules => mods/utility}/prometheus_metrics/metrics/byond.dm (100%) rename {code/modules => mods/utility}/prometheus_metrics/metrics/ss13.dm (100%) diff --git a/code/game/world_topic_commands.dm b/code/game/world_topic_commands.dm index ab871592c196..43ef54a7e8f1 100644 --- a/code/game/world_topic_commands.dm +++ b/code/game/world_topic_commands.dm @@ -349,11 +349,3 @@ return "Ckey not found" else return "Database connection failed or not set up" - -/decl/topic_command/secure/prometheus_metrics - name = "prometheus_metrics" - uid = "topic_command_prometheus_metrics" - -/decl/topic_command/secure/prometheus_metrics/use() - var/static/decl/prometheus_metrics/prometheus_metrics = IMPLIED_DECL - return prometheus_metrics.collect() diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 068804e94192..835003882041 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -60,6 +60,8 @@ #include "../../mods/species/utility_frames/_utility_frames.dme" #include "../../mods/species/vox/_vox.dme" + #include "../../mods/utility/prometheus_metrics/_prometheus_metrics.dme" + #define USING_MAP_DATUM /datum/map/modpack_testing #elif !defined(MAP_OVERRIDE) diff --git a/code/modules/prometheus_metrics/_defines.dm b/mods/utility/prometheus_metrics/_prometheus_metrics.dm similarity index 55% rename from code/modules/prometheus_metrics/_defines.dm rename to mods/utility/prometheus_metrics/_prometheus_metrics.dm index ed81bed1926e..20811d914d74 100644 --- a/code/modules/prometheus_metrics/_defines.dm +++ b/mods/utility/prometheus_metrics/_prometheus_metrics.dm @@ -4,3 +4,7 @@ var/global/list/prometheus_metric_names = list("counter", "gauge") #define PROMETHEUS_METRIC_GAUGE 1 #define PROMETHEUS_METRIC_NAME(m) global.prometheus_metric_names[m + 1] + +/decl/modpack/prometheus_metrics + name = "Prometheus Metrics" + desc = "A system for providing gameserver metrics polling via world/Topic, with a JSON protobuf payload." \ No newline at end of file diff --git a/mods/utility/prometheus_metrics/_prometheus_metrics.dme b/mods/utility/prometheus_metrics/_prometheus_metrics.dme new file mode 100644 index 000000000000..ed635b5d7585 --- /dev/null +++ b/mods/utility/prometheus_metrics/_prometheus_metrics.dme @@ -0,0 +1,11 @@ +#ifndef MODPACK_UTILITY_PROMETHEUS +#define MODPACK_UTILITY_PROMETHEUS +// BEGIN_INCLUDE +#include "_prometheus_metrics.dm" +#include "metric_family.dm" +#include "metric_topic.dm" +#include "metrics.dm" +#include "metrics\byond.dm" +#include "metrics\ss13.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/code/modules/prometheus_metrics/metric_family.dm b/mods/utility/prometheus_metrics/metric_family.dm similarity index 100% rename from code/modules/prometheus_metrics/metric_family.dm rename to mods/utility/prometheus_metrics/metric_family.dm diff --git a/mods/utility/prometheus_metrics/metric_topic.dm b/mods/utility/prometheus_metrics/metric_topic.dm new file mode 100644 index 000000000000..f5574e593deb --- /dev/null +++ b/mods/utility/prometheus_metrics/metric_topic.dm @@ -0,0 +1,7 @@ +/decl/topic_command/secure/prometheus_metrics + name = "prometheus_metrics" + uid = "topic_command_prometheus_metrics" + +/decl/topic_command/secure/prometheus_metrics/use() + var/static/decl/prometheus_metrics/prometheus_metrics = IMPLIED_DECL + return prometheus_metrics.collect() \ No newline at end of file diff --git a/code/modules/prometheus_metrics/metrics.dm b/mods/utility/prometheus_metrics/metrics.dm similarity index 100% rename from code/modules/prometheus_metrics/metrics.dm rename to mods/utility/prometheus_metrics/metrics.dm diff --git a/code/modules/prometheus_metrics/metrics/byond.dm b/mods/utility/prometheus_metrics/metrics/byond.dm similarity index 100% rename from code/modules/prometheus_metrics/metrics/byond.dm rename to mods/utility/prometheus_metrics/metrics/byond.dm diff --git a/code/modules/prometheus_metrics/metrics/ss13.dm b/mods/utility/prometheus_metrics/metrics/ss13.dm similarity index 100% rename from code/modules/prometheus_metrics/metrics/ss13.dm rename to mods/utility/prometheus_metrics/metrics/ss13.dm diff --git a/nebula.dme b/nebula.dme index 27a7c09e2e86..6ba4f392fdba 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3547,11 +3547,6 @@ #include "code\modules\projectiles\targeting\targeting_mob.dm" #include "code\modules\projectiles\targeting\targeting_overlay.dm" #include "code\modules\projectiles\targeting\targeting_triggers.dm" -#include "code\modules\prometheus_metrics\_defines.dm" -#include "code\modules\prometheus_metrics\metric_family.dm" -#include "code\modules\prometheus_metrics\metrics.dm" -#include "code\modules\prometheus_metrics\metrics\byond.dm" -#include "code\modules\prometheus_metrics\metrics\ss13.dm" #include "code\modules\pronouns\_pronouns.dm" #include "code\modules\pronouns\pronouns_female.dm" #include "code\modules\pronouns\pronouns_male.dm" From e7c4d25dbfc692eea119e638ec822c42eb2f7280 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 28 Nov 2025 13:43:27 -0500 Subject: [PATCH 0917/1331] Move ventcrawl module into a modpack --- code/modules/mob/living/login.dm | 5 ----- code/modules/mob/living/simple_animal/alien/alien.dm | 1 - .../mob/living/simple_animal/friendly/possum.dm | 1 - .../mob/living/simple_animal/passive/mouse.dm | 1 - code/modules/species/station/monkey.dm | 1 - maps/exodus/exodus.dm | 1 + maps/ministation/ministation.dm | 1 + maps/modpack_testing/modpack_testing.dm | 1 + maps/tradeship/tradeship.dm | 1 + mods/content/ventcrawl/_ventcrawl.dm | 3 +++ mods/content/ventcrawl/_ventcrawl.dme | 12 ++++++++++++ mods/content/ventcrawl/mob_overrides.dm | 11 +++++++++++ mods/content/ventcrawl/species_overrides.dm | 2 ++ .../content}/ventcrawl/ventcrawl_atmospherics.dm | 0 .../content/ventcrawl/ventcrawl_mob.dm | 7 +++++++ .../content}/ventcrawl/ventcrawl_multiz.dm | 0 .../content}/ventcrawl/ventcrawl_verb.dm | 0 mods/content/xenobiology/slime/_slime.dm | 7 ------- mods/gamemodes/cult/overrides.dm | 10 ---------- mods/mobs/borers/mob/borer/borer.dm | 1 - mods/~compatibility/patches/ventcrawl.dm | 12 ++++++++++++ mods/~compatibility/patches/ventcrawl/borers.dm | 3 +++ mods/~compatibility/patches/ventcrawl/cult.dm | 9 +++++++++ mods/~compatibility/patches/ventcrawl/xenobio.dm | 9 +++++++++ mods/~compatibility/~compatibility.dm | 4 ++++ nebula.dme | 4 ---- 26 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 mods/content/ventcrawl/_ventcrawl.dm create mode 100644 mods/content/ventcrawl/_ventcrawl.dme create mode 100644 mods/content/ventcrawl/mob_overrides.dm create mode 100644 mods/content/ventcrawl/species_overrides.dm rename {code/modules => mods/content}/ventcrawl/ventcrawl_atmospherics.dm (100%) rename code/modules/ventcrawl/ventcrawl.dm => mods/content/ventcrawl/ventcrawl_mob.dm (96%) rename {code/modules => mods/content}/ventcrawl/ventcrawl_multiz.dm (100%) rename {code/modules => mods/content}/ventcrawl/ventcrawl_verb.dm (100%) create mode 100644 mods/~compatibility/patches/ventcrawl.dm create mode 100644 mods/~compatibility/patches/ventcrawl/borers.dm create mode 100644 mods/~compatibility/patches/ventcrawl/cult.dm create mode 100644 mods/~compatibility/patches/ventcrawl/xenobio.dm diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index db1058faccbb..ad212a096151 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -1,10 +1,5 @@ - /mob/living/Login() . = ..() - //login during ventcrawl - if(is_ventcrawling && istype(loc, /obj/machinery/atmospherics)) //attach us back into the pipes - remove_ventcrawl() - add_ventcrawl(loc) //Mind updates mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) mind.active = 1 //indicates that the mind is currently synced with a client diff --git a/code/modules/mob/living/simple_animal/alien/alien.dm b/code/modules/mob/living/simple_animal/alien/alien.dm index 952843112ff4..8c2bce14bff7 100644 --- a/code/modules/mob/living/simple_animal/alien/alien.dm +++ b/code/modules/mob/living/simple_animal/alien/alien.dm @@ -11,7 +11,6 @@ var/instance_num /mob/living/simple_animal/alien/Initialize() - verbs += /mob/living/proc/ventcrawl verbs += /mob/living/proc/hide instance_num = rand(1, 1000) diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm index d57bba3eff9b..ef8c318ade27 100644 --- a/code/modules/mob/living/simple_animal/friendly/possum.dm +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -97,7 +97,6 @@ /mob/living/simple_animal/opossum/Initialize() . = ..() - verbs += /mob/living/proc/ventcrawl verbs += /mob/living/proc/hide /mob/living/simple_animal/opossum/poppy diff --git a/code/modules/mob/living/simple_animal/passive/mouse.dm b/code/modules/mob/living/simple_animal/passive/mouse.dm index 39a71bc81699..72d3815596e2 100644 --- a/code/modules/mob/living/simple_animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_animal/passive/mouse.dm @@ -52,7 +52,6 @@ INVOKE_ASYNC(body, TYPE_PROC_REF(/mob/living/simple_animal, audible_emote), "snuffles.") /mob/living/simple_animal/passive/mouse/Initialize() - verbs += /mob/living/proc/ventcrawl verbs += /mob/living/proc/hide if(name == initial(name)) name = "[name] ([sequential_id(/mob/living/simple_animal/passive/mouse)])" diff --git a/code/modules/species/station/monkey.dm b/code/modules/species/station/monkey.dm index 60c0c3d97c74..8b49d6ffabfe 100644 --- a/code/modules/species/station/monkey.dm +++ b/code/modules/species/station/monkey.dm @@ -16,7 +16,6 @@ dusted_anim = "dust-m" death_message = "lets out a faint chimper as it collapses and stops moving..." - inherent_verbs = list(/mob/living/proc/ventcrawl) species_hud = /datum/hud_data/monkey butchery_data = /decl/butchery_data/humanoid/monkey diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 5a5d207d3c62..8c2255f018a8 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -16,6 +16,7 @@ #include "../../mods/content/psionics/_psionics.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" + #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/content/tabloids/_tabloids.dme" diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index 87ebbd49b8d8..0fdb554342db 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -32,6 +32,7 @@ Twice... #include "../../mods/content/psionics/_psionics.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" + #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/gamemodes/cult/_cult.dme" diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 068804e94192..667334ce0697 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -32,6 +32,7 @@ #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/tabloids/_tabloids.dme" #include "../../mods/content/undead/_undead.dme" + #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/gamemodes/cult/_cult.dme" diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index acaaf113f919..df97a257d412 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -31,6 +31,7 @@ #include "../../mods/content/psionics/_psionics.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" + #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/gamemodes/cult/_cult.dme" diff --git a/mods/content/ventcrawl/_ventcrawl.dm b/mods/content/ventcrawl/_ventcrawl.dm new file mode 100644 index 000000000000..39e8885300d7 --- /dev/null +++ b/mods/content/ventcrawl/_ventcrawl.dm @@ -0,0 +1,3 @@ +/decl/modpack/ventcrawl + name = "Ventcrawling" + desc = "Adds the ability for certain mobs to crawl through vents and scrubbers and travel through pipe networks." \ No newline at end of file diff --git a/mods/content/ventcrawl/_ventcrawl.dme b/mods/content/ventcrawl/_ventcrawl.dme new file mode 100644 index 000000000000..4427041cd805 --- /dev/null +++ b/mods/content/ventcrawl/_ventcrawl.dme @@ -0,0 +1,12 @@ +#ifndef CONTENT_PACK_VENTCRAWL +#define CONTENT_PACK_VENTCRAWL +// BEGIN_INCLUDE +#include "_ventcrawl.dm" +#include "mob_overrides.dm" +#include "species_overrides.dm" +#include "ventcrawl_atmospherics.dm" +#include "ventcrawl_mob.dm" +#include "ventcrawl_multiz.dm" +#include "ventcrawl_verb.dm" +// END_INCLUDE +#endif diff --git a/mods/content/ventcrawl/mob_overrides.dm b/mods/content/ventcrawl/mob_overrides.dm new file mode 100644 index 000000000000..f75b523b1798 --- /dev/null +++ b/mods/content/ventcrawl/mob_overrides.dm @@ -0,0 +1,11 @@ +/mob/living/simple_animal/alien/Initialize() + . = ..() + verbs += /mob/living/proc/ventcrawl + +/mob/living/simple_animal/opossum/Initialize() + . = ..() + verbs += /mob/living/proc/ventcrawl + +/mob/living/simple_animal/passive/mouse/Initialize() + . = ..() + verbs += /mob/living/proc/ventcrawl diff --git a/mods/content/ventcrawl/species_overrides.dm b/mods/content/ventcrawl/species_overrides.dm new file mode 100644 index 000000000000..6516ab3f9e24 --- /dev/null +++ b/mods/content/ventcrawl/species_overrides.dm @@ -0,0 +1,2 @@ +/decl/species/monkey + inherent_verbs = list(/mob/living/proc/ventcrawl) \ No newline at end of file diff --git a/code/modules/ventcrawl/ventcrawl_atmospherics.dm b/mods/content/ventcrawl/ventcrawl_atmospherics.dm similarity index 100% rename from code/modules/ventcrawl/ventcrawl_atmospherics.dm rename to mods/content/ventcrawl/ventcrawl_atmospherics.dm diff --git a/code/modules/ventcrawl/ventcrawl.dm b/mods/content/ventcrawl/ventcrawl_mob.dm similarity index 96% rename from code/modules/ventcrawl/ventcrawl.dm rename to mods/content/ventcrawl/ventcrawl_mob.dm index b9548f48bb91..76d23de4dfe4 100644 --- a/code/modules/ventcrawl/ventcrawl.dm +++ b/mods/content/ventcrawl/ventcrawl_mob.dm @@ -152,3 +152,10 @@ var/global/list/ventcrawl_machinery = list( client.images -= current_image client.eye = src LAZYCLEARLIST(pipes_shown) + +/mob/living/Login() + . = ..() + //login during ventcrawl + if(is_ventcrawling && istype(loc, /obj/machinery/atmospherics)) //attach us back into the pipes + remove_ventcrawl() + add_ventcrawl(loc) \ No newline at end of file diff --git a/code/modules/ventcrawl/ventcrawl_multiz.dm b/mods/content/ventcrawl/ventcrawl_multiz.dm similarity index 100% rename from code/modules/ventcrawl/ventcrawl_multiz.dm rename to mods/content/ventcrawl/ventcrawl_multiz.dm diff --git a/code/modules/ventcrawl/ventcrawl_verb.dm b/mods/content/ventcrawl/ventcrawl_verb.dm similarity index 100% rename from code/modules/ventcrawl/ventcrawl_verb.dm rename to mods/content/ventcrawl/ventcrawl_verb.dm diff --git a/mods/content/xenobiology/slime/_slime.dm b/mods/content/xenobiology/slime/_slime.dm index 37c5ebfb51bc..62197258a3bf 100644 --- a/mods/content/xenobiology/slime/_slime.dm +++ b/mods/content/xenobiology/slime/_slime.dm @@ -67,7 +67,6 @@ reagents = new /datum/reagents/metabolism(240, src, CHEM_TOUCH) render_target = "slime_\ref[src]" - verbs += /mob/living/proc/ventcrawl slime_type = _stype if(!ispath(slime_type, /decl/slime_colour)) @@ -322,12 +321,6 @@ ..() mind.assigned_role = "slime" -/mob/living/slime/can_ventcrawl() - if(feeding_on) - to_chat(src, SPAN_WARNING("You cannot ventcrawl while feeding.")) - return FALSE - . = ..() - /mob/living/slime/handle_airflow(differential, list/connecting_turfs, repelled) return FALSE diff --git a/mods/gamemodes/cult/overrides.dm b/mods/gamemodes/cult/overrides.dm index 2d822d252c77..15029601185d 100644 --- a/mods/gamemodes/cult/overrides.dm +++ b/mods/gamemodes/cult/overrides.dm @@ -21,16 +21,6 @@ /mob/living/silicon/ai shouldnt_see = list(/obj/effect/rune) -// Vent crawling whitelisted items, whoo -/mob/living/Initialize() - . = ..() - can_enter_vent_with += list( - /obj/item/clothing/head/culthood, - /obj/item/clothing/suit/cultrobes, - /obj/item/book/tome, - /obj/item/sword/cultblade - ) - /obj/item/vampiric material = /decl/material/solid/stone/cult diff --git a/mods/mobs/borers/mob/borer/borer.dm b/mods/mobs/borers/mob/borer/borer.dm index 379bbca678cc..344d5a07e231 100644 --- a/mods/mobs/borers/mob/borer/borer.dm +++ b/mods/mobs/borers/mob/borer/borer.dm @@ -88,7 +88,6 @@ . = ..() add_language(/decl/language/corticalborer) - verbs += /mob/living/proc/ventcrawl verbs += /mob/living/proc/hide generation = gen diff --git a/mods/~compatibility/patches/ventcrawl.dm b/mods/~compatibility/patches/ventcrawl.dm new file mode 100644 index 000000000000..93cdc598aaf6 --- /dev/null +++ b/mods/~compatibility/patches/ventcrawl.dm @@ -0,0 +1,12 @@ +// Add ventcrawling to slimes. +#ifdef CONTENT_PACK_XENOBIO +#include "ventcrawl/xenobio.dm" +#endif +// Add ventcrawling to borers. +#ifdef CONTENT_PACK_BORERS +#include "ventcrawl/borers.dm" +#endif +// Allow ventcrawling with cult gear. +#ifdef GAMEMODE_PACK_CULT +#include "ventcrawl/cult.dm" +#endif \ No newline at end of file diff --git a/mods/~compatibility/patches/ventcrawl/borers.dm b/mods/~compatibility/patches/ventcrawl/borers.dm new file mode 100644 index 000000000000..20b3be4126bf --- /dev/null +++ b/mods/~compatibility/patches/ventcrawl/borers.dm @@ -0,0 +1,3 @@ +/mob/living/simple_animal/borer/Initialize(var/mapload, var/gen=1) + . = ..() + verbs += /mob/living/proc/ventcrawl \ No newline at end of file diff --git a/mods/~compatibility/patches/ventcrawl/cult.dm b/mods/~compatibility/patches/ventcrawl/cult.dm new file mode 100644 index 000000000000..d24d4a8affcf --- /dev/null +++ b/mods/~compatibility/patches/ventcrawl/cult.dm @@ -0,0 +1,9 @@ +// Vent crawling whitelisted items, whoo +/mob/living/Initialize() + . = ..() + can_enter_vent_with += list( + /obj/item/clothing/head/culthood, + /obj/item/clothing/suit/cultrobes, + /obj/item/book/tome, + /obj/item/sword/cultblade + ) \ No newline at end of file diff --git a/mods/~compatibility/patches/ventcrawl/xenobio.dm b/mods/~compatibility/patches/ventcrawl/xenobio.dm new file mode 100644 index 000000000000..ac63b6114265 --- /dev/null +++ b/mods/~compatibility/patches/ventcrawl/xenobio.dm @@ -0,0 +1,9 @@ +/mob/living/slime/Initialize(mapload, var/_stype = /decl/slime_colour/grey) + . = ..() + verbs += /mob/living/proc/ventcrawl + +/mob/living/slime/can_ventcrawl() + if(feeding_on) + to_chat(src, SPAN_WARNING("You cannot ventcrawl while feeding.")) + return FALSE + . = ..() \ No newline at end of file diff --git a/mods/~compatibility/~compatibility.dm b/mods/~compatibility/~compatibility.dm index 971cba14f9d0..81c4f3403d12 100644 --- a/mods/~compatibility/~compatibility.dm +++ b/mods/~compatibility/~compatibility.dm @@ -34,3 +34,7 @@ #ifdef MODPACK_DRAKES #include "patches/drakes.dm" #endif + +#ifdef CONTENT_PACK_VENTCRAWL +#include "patches/ventcrawl.dm" +#endif \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index 27a7c09e2e86..bed9d7a265aa 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3911,10 +3911,6 @@ #include "code\modules\vehicles\engine.dm" #include "code\modules\vehicles\train.dm" #include "code\modules\vehicles\vehicle.dm" -#include "code\modules\ventcrawl\ventcrawl.dm" -#include "code\modules\ventcrawl\ventcrawl_atmospherics.dm" -#include "code\modules\ventcrawl\ventcrawl_multiz.dm" -#include "code\modules\ventcrawl\ventcrawl_verb.dm" #include "code\modules\weather\_weather.dm" #include "code\modules\weather\weather_debug.dm" #include "code\modules\weather\weather_effects.dm" From 7f780a57784d3c69c9165dc93bdf30a719096c99 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 28 Nov 2025 13:39:09 +1100 Subject: [PATCH 0918/1331] Replacing direct access to total_volume and maximum_volume with macros. --- code/__defines/chemistry.dm | 29 +++++-- code/_helpers/medical_scans.dm | 11 ++- code/controllers/subsystems/fluids.dm | 17 ++-- code/datums/extensions/milkable/milkable.dm | 4 +- .../inventory_gripper_subtypes.dm | 5 +- .../inventory_slots/slots/slot_shoes.dm | 4 +- code/datums/storage/_storage.dm | 2 +- code/datums/storage/subtypes_box.dm | 4 +- code/datums/storage/subtypes_misc.dm | 2 +- code/game/atoms.dm | 15 ++-- code/game/atoms_fluids.dm | 9 ++- code/game/atoms_interactions.dm | 2 +- code/game/atoms_movable.dm | 2 +- code/game/machinery/Sleeper.dm | 30 +++---- .../machinery/_machines_base/machinery.dm | 2 +- .../machinery_public_vars_common.dm | 8 +- code/game/machinery/centrifuge.dm | 2 +- code/game/machinery/computer/prisoner.dm | 2 +- code/game/machinery/cracker.dm | 32 ++++---- .../kitchen/cooking_machines/_cooker.dm | 5 +- code/game/machinery/kitchen/icecream.dm | 9 ++- code/game/machinery/kitchen/microwave.dm | 28 ++++--- code/game/machinery/smartfridge/foods.dm | 2 +- code/game/machinery/washing_machine.dm | 8 +- code/game/objects/__objs.dm | 15 ++-- code/game/objects/_objs_edibility.dm | 4 +- code/game/objects/effects/chem/chemsmoke.dm | 10 ++- code/game/objects/effects/chem/foam.dm | 6 +- code/game/objects/effects/chem/water.dm | 9 ++- .../objects/effects/decals/Cleanable/misc.dm | 2 +- code/game/objects/effects/decals/cleanable.dm | 6 +- code/game/objects/items/__item.dm | 18 ++--- code/game/objects/items/_item_edibility.dm | 4 +- code/game/objects/items/_item_reagents.dm | 15 ++-- code/game/objects/items/contraband.dm | 4 +- code/game/objects/items/flame/_flame.dm | 6 +- .../game/objects/items/flame/flame_fuelled.dm | 19 +++-- code/game/objects/items/flame/flame_torch.dm | 2 +- code/game/objects/items/toys.dm | 17 ++-- code/game/objects/items/waterskin.dm | 2 +- code/game/objects/items/weapons/ecigs.dm | 15 ++-- .../objects/items/weapons/extinguisher.dm | 2 +- .../items/weapons/grenades/chem_grenade.dm | 15 ++-- code/game/objects/items/weapons/mop.dm | 13 ++-- code/game/objects/items/weapons/paint.dm | 2 +- code/game/objects/items/weapons/soap.dm | 8 +- .../objects/items/weapons/storage/bible.dm | 2 +- .../items/weapons/storage/fancy/cigarettes.dm | 2 +- .../items/weapons/storage/med_pouch.dm | 16 ++-- .../objects/items/weapons/swords_axes_etc.dm | 2 +- code/game/objects/items/weapons/towels.dm | 30 +++---- .../objects/items/welding/weldbackpack.dm | 17 ++-- .../game/objects/items/welding/weldingtool.dm | 2 +- .../objects/items/welding/weldingtool_tank.dm | 16 ++-- code/game/objects/structures/__structure.dm | 11 +-- .../game/objects/structures/barrels/barrel.dm | 18 ++--- code/game/objects/structures/barrels/cask.dm | 8 +- .../game/objects/structures/beds/rollerbed.dm | 4 +- .../structures/chemistry/filter_stand.dm | 16 ++-- code/game/objects/structures/compost.dm | 13 ++-- code/game/objects/structures/fires.dm | 8 +- code/game/objects/structures/fishtanks.dm | 10 +-- code/game/objects/structures/fountain.dm | 2 +- code/game/objects/structures/iv_drip.dm | 12 +-- code/game/objects/structures/janicart.dm | 12 +-- code/game/objects/structures/mop_bucket.dm | 4 +- code/game/objects/structures/watercloset.dm | 6 +- code/game/objects/structures/well.dm | 18 ++--- code/game/turfs/flooring/flooring_mud.dm | 2 +- code/game/turfs/flooring/flooring_snow.dm | 2 +- code/game/turfs/floors/_floor.dm | 4 +- code/game/turfs/floors/floor_acts.dm | 2 +- code/game/turfs/turf.dm | 8 +- code/game/turfs/turf_enter.dm | 4 +- code/game/turfs/turf_fluids.dm | 37 ++++----- code/modules/atmospherics/datum_pipeline.dm | 12 +-- code/modules/atmospherics/pipes.dm | 2 +- code/modules/butchery/butchery_data.dm | 2 +- code/modules/butchery/butchery_products.dm | 6 +- .../gloves/jewelry/rings/ring_reagent.dm | 4 +- code/modules/clothing/masks/chewable.dm | 4 +- code/modules/clothing/masks/smokable.dm | 12 +-- .../spacesuits/rig/modules/utility.dm | 4 +- code/modules/crafting/handmade_items.dm | 6 +- .../crafting/metalwork/metalwork_items.dm | 10 +-- .../crafting/pottery/pottery_moulds.dm | 10 +-- code/modules/crafting/working/quern.dm | 30 +++---- code/modules/detectivework/tools/luminol.dm | 2 +- code/modules/detectivework/tools/rag.dm | 24 +++--- code/modules/economy/_worth.dm | 2 +- code/modules/fabrication/fabricator_intake.dm | 4 +- code/modules/fluids/_fluid.dm | 6 +- code/modules/food/cooking/_recipe.dm | 22 +++--- .../cooking_vessels/_cooking_vessel.dm | 24 +++--- .../food/cooking/cooking_vessels/pot.dm | 2 +- .../food/cooking/recipes/recipe_soup.dm | 2 +- code/modules/food/utensils/_utensil.dm | 2 +- code/modules/hydroponics/grown.dm | 18 ++--- code/modules/hydroponics/seed.dm | 2 +- code/modules/hydroponics/trays/tray.dm | 6 +- .../modules/hydroponics/trays/tray_process.dm | 10 +-- code/modules/hydroponics/trays/tray_soil.dm | 6 +- code/modules/implants/implant_types/chem.dm | 4 +- .../interactions/interactions_reagents.dm | 10 +-- code/modules/lights/light_bulbs_tubes.dm | 4 +- .../liquids/materials_liquid_toxins.dm | 2 +- .../liquids/materials_liquid_water.dm | 5 +- .../solids/materials_solid_elements.dm | 7 +- code/modules/materials/material_data.dm | 7 +- code/modules/materials/material_debris.dm | 2 +- code/modules/materials/material_display.dm | 19 +++-- code/modules/materials/material_drying.dm | 2 +- code/modules/materials/material_metabolism.dm | 3 +- code/modules/mechs/equipment/engineering.dm | 4 +- .../mining/machinery/material_compressor.dm | 4 +- .../mining/machinery/material_extractor.dm | 22 +++--- .../mining/machinery/material_smelter.dm | 6 +- code/modules/mob/living/bot/farmbot.dm | 14 ++-- code/modules/mob/living/bot/medibot.dm | 4 +- code/modules/mob/living/human/examine.dm | 4 +- code/modules/mob/living/human/human.dm | 8 +- code/modules/mob/living/human/human_blood.dm | 34 ++++---- code/modules/mob/living/human/human_organs.dm | 4 +- code/modules/mob/living/life.dm | 4 +- code/modules/mob/living/living.dm | 30 +++---- code/modules/mob/living/living_blood.dm | 2 +- code/modules/mob/living/living_breath.dm | 2 +- .../robot/flying/module_flying_emergency.dm | 4 +- .../robot/flying/module_flying_forensics.dm | 4 +- .../living/simple_animal/hostile/vagrant.dm | 2 +- code/modules/mob/transform_procs.dm | 2 +- code/modules/organs/internal/heart.dm | 2 +- code/modules/organs/internal/lungs.dm | 9 +-- code/modules/organs/internal/stomach.dm | 19 +++-- code/modules/organs/organ.dm | 13 ++-- code/modules/paperwork/pen/quill_and_ink.dm | 2 +- code/modules/paperwork/pen/reagent_pen.dm | 4 +- code/modules/paperwork/toner_cartridge.dm | 8 +- .../power/fuel_assembly/fuel_compressor.dm | 4 +- code/modules/power/port_gen.dm | 6 +- .../projectiles/ammunition/chemdart.dm | 4 +- .../projectiles/guns/launcher/syringe_gun.dm | 2 +- .../projectiles/guns/projectile/dartgun.dm | 14 ++-- code/modules/reagents/Chemistry-Grinder.dm | 14 ++-- code/modules/reagents/Chemistry-Holder.dm | 53 ++++++------- code/modules/reagents/Chemistry-Machinery.dm | 18 ++--- code/modules/reagents/chems/chems_blood.dm | 2 +- .../modules/reagents/chems/chems_compounds.dm | 7 +- code/modules/reagents/chems/chems_drugs.dm | 4 +- .../modules/reagents/chems/chems_medicines.dm | 6 +- .../reagents/chems/chems_painkillers.dm | 2 +- .../reagents/chems/chems_psychiatric.dm | 2 +- code/modules/reagents/cocktails.dm | 6 +- code/modules/reagents/dispenser/cartridge.dm | 16 ++-- .../reagents/dispenser/cartridge_presets.dm | 2 +- .../reagents/dispenser/cartridge_spawn.dm | 2 +- code/modules/reagents/dispenser/dispenser2.dm | 11 +-- code/modules/reagents/reactions/_reaction.dm | 2 +- .../reactions/reaction_grenade_reaction.dm | 2 +- .../reactions/reaction_recipe_food.dm | 2 +- .../reagents/reactions/reaction_synthesis.dm | 14 ++-- .../reagents/reagent_container_edibility.dm | 2 +- code/modules/reagents/reagent_containers.dm | 20 ++--- .../reagents/reagent_containers/_glass.dm | 23 +++--- .../reagents/reagent_containers/beaker.dm | 10 +-- .../reagents/reagent_containers/blood_pack.dm | 8 +- .../reagents/reagent_containers/borghypo.dm | 6 +- .../reagents/reagent_containers/bowl.dm | 6 +- .../reagents/reagent_containers/bucket.dm | 4 +- .../condiments/__condiment.dm | 5 +- .../drinkingglass/drinkingglass.dm | 26 ++++--- .../drinkingglass/glass_types.dm | 2 +- .../reagents/reagent_containers/drinks.dm | 39 +++++----- .../reagent_containers/drinks/bottle.dm | 78 +++++++++---------- .../reagent_containers/drinks/cans.dm | 43 +++++----- .../drinks/cocktailshaker.dm | 6 +- .../reagent_containers/drinks/juicebox.dm | 8 +- .../reagents/reagent_containers/dropper.dm | 12 +-- .../reagents/reagent_containers/food.dm | 13 ++-- .../reagent_containers/food/burgers.dm | 2 +- .../reagent_containers/food/canned/_canned.dm | 2 +- .../reagent_containers/food/dairy/_dairy.dm | 6 +- .../reagents/reagent_containers/food/eggs.dm | 4 +- .../reagent_containers/food/sandwich.dm | 2 +- .../food/sliceable/_sliceable.dm | 2 +- .../reagents/reagent_containers/food/sushi.dm | 6 +- .../reagent_containers/food_edibility.dm | 2 +- .../reagent_containers/glass/bottle.dm | 38 ++++----- .../reagent_containers/glass/bottle/robot.dm | 4 +- .../reagents/reagent_containers/hypospray.dm | 42 +++++----- .../reagents/reagent_containers/inhaler.dm | 14 ++-- .../reagents/reagent_containers/mortar.dm | 8 +- .../reagents/reagent_containers/packets.dm | 54 ++++++------- .../reagents/reagent_containers/pill.dm | 14 ++-- .../reagent_containers/pill_edibility.dm | 4 +- .../reagents/reagent_containers/retort.dm | 4 +- .../reagents/reagent_containers/spray.dm | 18 ++--- .../reagents/reagent_containers/syringes.dm | 40 +++++----- code/modules/reagents/reagent_dispenser.dm | 18 ++--- code/modules/scanners/breath.dm | 4 +- code/modules/scanners/health.dm | 16 ++-- code/modules/scanners/mass_spectrometer.dm | 14 ++-- code/modules/scanners/plant.dm | 7 +- code/modules/scanners/reagents.dm | 7 +- code/modules/scent/_scent.dm | 4 +- code/modules/sealant_gun/sealant_injector.dm | 10 +-- code/modules/sealant_gun/sealant_tank.dm | 4 +- code/modules/surgery/necrotic.dm | 2 +- code/modules/surgery/other.dm | 8 +- code/modules/vehicles/engine.dm | 8 +- .../xenoarcheaology/artifacts/artifact.dm | 2 +- .../machinery/geosample_scanner.dm | 8 +- code/unit_tests/chemistry_tests.dm | 10 +-- mods/content/beekeeping/hive_frame.dm | 10 +-- mods/content/beekeeping/hives/_hive.dm | 2 +- mods/content/bigpharma/extension.dm | 4 +- mods/content/blacksmithy/barrel_rim.dm | 2 +- .../components/input.dm | 4 +- .../components/power_passive.dm | 2 +- .../components/reagents.dm | 32 ++++---- mods/content/plant_dissection/grown.dm | 2 +- mods/content/xenobiology/colours/_colour.dm | 7 +- mods/content/xenobiology/slime/items.dm | 2 +- mods/content/xenobiology/slime/life.dm | 4 +- mods/gamemodes/cult/ritual.dm | 4 +- mods/gamemodes/cult/runes.dm | 4 +- mods/species/ascent/machines/ship_machines.dm | 4 +- mods/species/drakes/drake_organs.dm | 11 +-- mods/species/drakes/sifsap.dm | 9 ++- mods/species/vox/organs_vox.dm | 4 +- 230 files changed, 1156 insertions(+), 1071 deletions(-) diff --git a/code/__defines/chemistry.dm b/code/__defines/chemistry.dm index 5e70c1e79047..db1cd4817e1a 100644 --- a/code/__defines/chemistry.dm +++ b/code/__defines/chemistry.dm @@ -78,13 +78,30 @@ #define HANDLE_REACTIONS(_reagents) if(!QDELETED(_reagents)) { SSmaterials.active_holders[_reagents] = TRUE; } #define UNQUEUE_REACTIONS(_reagents) SSmaterials.active_holders -= _reagents -#define REAGENT_LIST(R) (R.reagents?.get_reagents() || "No reagent holder") +#define REAGENT_LIST(R) ((istype(R, /datum/reagents) && R:get_reagents()) || "No reagent holder") + +#define REAGENT_TOTAL_VOLUME(R) (UNLINT((istype(R, /datum/reagents) && R:total_volume) || 0)) +#define REAGENT_TOTAL_LIQUID_VOLUME(R) (UNLINT((istype(R, /datum/reagents) && R:total_liquid_volume) || 0)) + +#define REAGENT_MAXIMUM_VOLUME(R) (UNLINT((istype(R, /datum/reagents) && R:maximum_volume) || 0)) +#define REAGENTS_FREE_SPACE(R) (UNLINT(istype(R, /datum/reagents) ? (R.maximum_volume - R.total_volume) : 0)) + +#define REAGENT_VOLUMES(R) ( (istype(R, /datum/reagents) && UNLINT(R:reagent_volumes)) || null ) +#define REAGENT_SOLID_VOLUMES(R) ( (istype(R, /datum/reagents) && UNLINT(R:solid_volumes)) || null ) +#define REAGENT_LIQUID_VOLUMES(R) ( (istype(R, /datum/reagents) && UNLINT(R:liquid_volumes)) || null ) +#define REAGENT_GET_MAX_VOL(R) ( (istype(R, /datum/reagents) && UNLINT(R:maximum_volume)) || 0 ) +#define REAGENT_GET_ATOM(R) ( (istype(R, /datum/reagents) && UNLINT(R:my_atom)) || null ) + +#define REAGENT_VOLUME(R, M) ( istype(R, /datum/reagents) && UNLINT(R:reagent_volumes && R:reagent_volumes[RESOLVE_TO_DECL(M)]) ) +#define LIQUID_VOLUME(R, M) ( istype(R, /datum/reagents) && UNLINT(R:liquid_volumes && R:liquid_volumes[RESOLVE_TO_DECL(M)]) ) +#define SOLID_VOLUME(R, M) ( istype(R, /datum/reagents) && UNLINT(R:solid_volumes && R:solid_volumes[RESOLVE_TO_DECL(M)]) ) +#define REAGENT_DATA(R, M) ( istype(R, /datum/reagents) && UNLINT(R:reagent_data && R:reagent_data[RESOLVE_TO_DECL(M)]) ) + +#define REAGENT_SET_MAX_VOL(R, V) if(istype(R, /datum/reagents)) { UNLINT(R:maximum_volume = V) } +#define REAGENT_ADD_MAX_VOL(R, V) if(istype(R, /datum/reagents)) { UNLINT(R:maximum_volume += V) } +#define REAGENT_SET_ATOM(R, A) if(istype(R, /datum/reagents)) { UNLINT(R:my_atom = A) } +#define REAGENT_SET_DATA(R, M, D) if(istype(R, /datum/reagents)) { LAZYSET(UNLINT(R:reagent_data), M, D) } -#define REAGENTS_FREE_SPACE(R) (R?.maximum_volume - R?.total_volume) -#define REAGENT_VOLUME(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.reagent_volumes && REAGENT_HOLDER.reagent_volumes[RESOLVE_TO_DECL(REAGENT_TYPE)]) -#define LIQUID_VOLUME(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.liquid_volumes && REAGENT_HOLDER.liquid_volumes[RESOLVE_TO_DECL(REAGENT_TYPE)]) -#define SOLID_VOLUME(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.solid_volumes && REAGENT_HOLDER.solid_volumes[RESOLVE_TO_DECL(REAGENT_TYPE)]) -#define REAGENT_DATA(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.reagent_data && REAGENT_HOLDER.reagent_data[RESOLVE_TO_DECL(REAGENT_TYPE)]) #define CHEM_DOSE(M, R) LAZYACCESS(M._chem_doses, RESOLVE_TO_DECL(R)) diff --git a/code/_helpers/medical_scans.dm b/code/_helpers/medical_scans.dm index 5d4b84e8df90..8cf60c06560e 100644 --- a/code/_helpers/medical_scans.dm +++ b/code/_helpers/medical_scans.dm @@ -42,15 +42,14 @@ .["immune_system"] = get_immunity() .["reagents"] = list() - if(reagents?.total_volume) - for(var/decl/material/reagent as anything in reagents.liquid_volumes) + if(REAGENT_TOTAL_VOLUME(reagents)) + for(var/decl/material/reagent as anything in REAGENT_LIQUID_VOLUMES(reagents)) var/list/reagent_data = list() reagent_data["name"] = reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID) reagent_data["quantity"] = round(REAGENT_VOLUME(reagents, reagent),1) reagent_data["scannable"] = reagent.scannable .["reagents"] += list(reagent_data) - - for(var/decl/material/reagent as anything in reagents.solid_volumes) + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(reagents)) var/list/reagent_data = list() reagent_data["name"] = reagent.get_reagent_name(reagents, MAT_PHASE_SOLID) reagent_data["quantity"] = round(REAGENT_VOLUME(reagents, reagent),1) @@ -98,8 +97,8 @@ .["blood_pressure"] = get_blood_pressure() .["blood_o2"] = get_blood_oxygenation() if(vessel) - .["blood_volume"] = vessel.total_volume - .["blood_volume_max"] = vessel.maximum_volume + .["blood_volume"] = REAGENT_TOTAL_VOLUME(vessel) + .["blood_volume_max"] = REAGENT_MAXIMUM_VOLUME(vessel) /proc/display_medical_data_header(var/list/scan, skill_level = SKILL_DEFAULT) //In case of problems, abort. diff --git a/code/controllers/subsystems/fluids.dm b/code/controllers/subsystems/fluids.dm index 0b9faea243ed..b05b4ed11f8a 100644 --- a/code/controllers/subsystems/fluids.dm +++ b/code/controllers/subsystems/fluids.dm @@ -100,7 +100,7 @@ SUBSYSTEM_DEF(fluids) i++ current_fluid_holder = processing_fluids[i] - if(QDELETED(current_fluid_holder) || !current_fluid_holder.reagents?.total_volume) + if(QDELETED(current_fluid_holder) || !REAGENT_TOTAL_VOLUME(current_fluid_holder.reagents)) REMOVE_ACTIVE_FLUID(current_fluid_holder) continue @@ -110,7 +110,7 @@ SUBSYSTEM_DEF(fluids) reagent_holder = current_fluid_holder.reagents UPDATE_FLUID_BLOCKED_DIRS(current_fluid_holder) - current_depth = reagent_holder?.total_volume || 0 + current_depth = REAGENT_TOTAL_VOLUME(reagent_holder) // How is this happening if(QDELETED(reagent_holder) || current_depth == -1.#IND || current_depth == 1.#IND) @@ -123,7 +123,7 @@ SUBSYSTEM_DEF(fluids) current_depth = current_fluid_holder.get_fluid_depth() // Mimimum liquid depth for creation of slurries. Do this after evaporation since it may change the total depth. - if(reagent_holder?.total_liquid_volume < FLUID_SLURRY) + if(REAGENT_TOTAL_LIQUID_VOLUME(reagent_holder) < FLUID_SLURRY) current_fluid_holder.dump_solid_reagents() current_depth = current_fluid_holder.get_fluid_depth() @@ -135,7 +135,7 @@ SUBSYSTEM_DEF(fluids) // Wash our turf. current_fluid_holder.fluid_act(reagent_holder) - if(isspaceturf(current_fluid_holder) || (istype(current_fluid_holder, /turf/floor) && (current_fluid_holder.turf_flags & TURF_FLAG_ABSORB_LIQUID) && (current_fluid_holder.reagents?.total_volume + current_fluid_holder.get_physical_height()) > 0)) + if(isspaceturf(current_fluid_holder) || (istype(current_fluid_holder, /turf/floor) && (current_fluid_holder.turf_flags & TURF_FLAG_ABSORB_LIQUID) && (REAGENT_TOTAL_VOLUME(current_fluid_holder.reagents) + current_fluid_holder.get_physical_height()) > 0)) removing = round(current_depth * 0.5) if(removing > 0) current_fluid_holder.remove_fluids(removing, defer_update = TRUE) @@ -154,8 +154,9 @@ SUBSYSTEM_DEF(fluids) if(other_fluid_holder) UPDATE_FLUID_BLOCKED_DIRS(other_fluid_holder) if(!(other_fluid_holder.fluid_blocked_dirs & UP) && other_fluid_holder.CanFluidPass(UP)) - if(!QDELETED(other_fluid_holder) && other_fluid_holder.reagents?.total_volume < FLUID_MAX_DEPTH) - current_fluid_holder.transfer_fluids_to(other_fluid_holder, min(floor(current_depth*0.5), FLUID_MAX_DEPTH - other_fluid_holder.reagents?.total_volume)) + var/other_volume = REAGENT_TOTAL_VOLUME(other_fluid_holder.reagents) + if(!QDELETED(other_fluid_holder) && other_volume < FLUID_MAX_DEPTH) + current_fluid_holder.transfer_fluids_to(other_fluid_holder, min(floor(current_depth*0.5), FLUID_MAX_DEPTH - other_volume)) current_depth = current_fluid_holder.get_fluid_depth() // Flow into the lowest level neighbor. @@ -173,7 +174,7 @@ SUBSYSTEM_DEF(fluids) if((neighbor.fluid_blocked_dirs & coming_from) || !neighbor.CanFluidPass(coming_from) || neighbor.is_flooded(absolute = TRUE) || !neighbor.CanFluidPass(global.reverse_dir[spread_dir])) continue other_fluid_holder = neighbor - neighbor_depth = (other_fluid_holder?.reagents?.total_volume || 0) + neighbor.get_physical_height() + neighbor_depth = (REAGENT_TOTAL_VOLUME(other_fluid_holder.reagents)) + neighbor.get_physical_height() flow_amount = round((current_turf_depth - neighbor_depth)*0.5) // TODO: multiply flow amount or minimum transfer amount by some // viscosity calculation to allow for piles of jelly vs piles of water. @@ -235,7 +236,7 @@ SUBSYSTEM_DEF(fluids) if(current_fluid_holder.last_flow_strength >= 10) // Catwalks mean items will be above the turf; subtract the turf height from our volume. // TODO: somehow handle stuff that is on a catwalk or on the turf within the same turf. - var/effective_volume = current_fluid_holder.reagents?.total_volume + var/effective_volume = REAGENT_TOTAL_VOLUME(current_fluid_holder.reagents) if(current_fluid_holder.get_supporting_platform()) // Depth is negative height, hence +=. TODO: positive heights? No idea how to handle that. effective_volume += current_fluid_holder.get_physical_height() diff --git a/code/datums/extensions/milkable/milkable.dm b/code/datums/extensions/milkable/milkable.dm index a377c2293ce9..198dc791e7b7 100644 --- a/code/datums/extensions/milkable/milkable.dm +++ b/code/datums/extensions/milkable/milkable.dm @@ -73,7 +73,7 @@ if(critter.stat == DEAD) return FALSE - if(udder?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(udder) <= 0) to_chat(user, SPAN_WARNING("\The [critter]'s udder is dry. Wait a little longer.")) return TRUE @@ -108,7 +108,7 @@ if(critter.stat == DEAD) return FALSE - if(udder?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(udder) <= 0) to_chat(user, SPAN_WARNING("\The [critter]'s udder is dry. Wait a little longer.")) return TRUE diff --git a/code/datums/inventory_slots/inventory_gripper_subtypes.dm b/code/datums/inventory_slots/inventory_gripper_subtypes.dm index bced228d5d54..0205f4785b21 100644 --- a/code/datums/inventory_slots/inventory_gripper_subtypes.dm +++ b/code/datums/inventory_slots/inventory_gripper_subtypes.dm @@ -23,8 +23,9 @@ // This means critters can hoover up beakers as a kind of impromptu chem disposal // technique, so long as they're okay with the reagents reacting inside them. - if(prop.reagents?.total_volume) - prop.reagents.trans_to_mob(src, prop.reagents.total_volume, CHEM_INGEST) + var/prop_reagents = REAGENT_TOTAL_VOLUME(prop.reagents) + if(prop_reagents) + prop.reagents.trans_to_mob(src, prop_reagents, CHEM_INGEST) // It also means they can do the old school cartoon schtick of eating // an entire sandwich and spitting up an empty plate. Ptooie. diff --git a/code/datums/inventory_slots/slots/slot_shoes.dm b/code/datums/inventory_slots/slots/slot_shoes.dm index 6fc4889be0bd..366c8b549b3b 100644 --- a/code/datums/inventory_slots/slots/slot_shoes.dm +++ b/code/datums/inventory_slots/slots/slot_shoes.dm @@ -23,7 +23,7 @@ var/blood_color for(var/foot_tag in list(BP_L_FOOT, BP_R_FOOT)) var/obj/item/organ/external/stomper = GET_EXTERNAL_ORGAN(user, foot_tag) - if(stomper && stomper.coating?.total_volume) + if(REAGENT_TOTAL_VOLUME(stomper?.coating)) blood_color = stomper.coating.get_color() break if(blood_color) @@ -39,7 +39,7 @@ return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] on [pronouns.his] feet." for(var/bp in list(BP_L_FOOT, BP_R_FOOT)) var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(owner, bp) - if(E && E.coating?.total_volume) + if(REAGENT_TOTAL_VOLUME(E?.coating)) if(user == owner) return "There's something on your feet!" return "There's something on [pronouns.his] feet!" diff --git a/code/datums/storage/_storage.dm b/code/datums/storage/_storage.dm index cead225909e9..9e5989f7e5d6 100644 --- a/code/datums/storage/_storage.dm +++ b/code/datums/storage/_storage.dm @@ -193,7 +193,7 @@ var/global/list/_test_storage_items = list() if(!M.try_unequip(inserting)) return FALSE - if(holder.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(holder.reagents)) inserting.fluid_act(holder.reagents) if(QDELETED(inserting)) return FALSE diff --git a/code/datums/storage/subtypes_box.dm b/code/datums/storage/subtypes_box.dm index 479c9460eb88..3803830d60f4 100644 --- a/code/datums/storage/subtypes_box.dm +++ b/code/datums/storage/subtypes_box.dm @@ -51,7 +51,7 @@ if(istype(removing, /obj/item/clothing/mask/smokable/cigarette/cigar) && isatom(holder)) var/atom/atom_holder = holder if(atom_holder.reagents) - atom_holder.reagents.trans_to_obj(removing, (atom_holder.reagents.total_volume/max(1, length(get_contents())))) + atom_holder.reagents.trans_to_obj(removing, (REAGENT_TOTAL_VOLUME(atom_holder.reagents)/max(1, length(get_contents())))) return ..() /datum/storage/box/cigarettes @@ -63,7 +63,7 @@ if(istype(removing, /obj/item/clothing/mask/smokable/cigarette) && isatom(holder)) var/atom/atom_holder = holder if(atom_holder.reagents) - atom_holder.reagents.trans_to_obj(removing, (atom_holder.reagents.total_volume/max(1, length(get_contents())))) + atom_holder.reagents.trans_to_obj(removing, (REAGENT_TOTAL_VOLUME(atom_holder.reagents)/max(1, length(get_contents())))) return ..() /datum/storage/box/cigarettes/cigarello diff --git a/code/datums/storage/subtypes_misc.dm b/code/datums/storage/subtypes_misc.dm index 5f51b0ffc9de..f2788e711e97 100644 --- a/code/datums/storage/subtypes_misc.dm +++ b/code/datums/storage/subtypes_misc.dm @@ -124,7 +124,7 @@ . = ..() if(!.) return - return inserting_item.reagents?.total_volume > 0 + return REAGENT_TOTAL_VOLUME(inserting_item.reagents) > 0 /datum/storage/photo_album storage_slots = DEFAULT_BOX_STORAGE //yes, that's storage_slots. Photos are w_class 1 so this has as many slots equal to the number of photos you could put in a box diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 18af0e9a1886..c197b8580b9f 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -164,7 +164,7 @@ /atom/proc/on_reagent_change() SHOULD_CALL_PARENT(TRUE) - if(storage && reagents?.total_volume) + if(storage && REAGENT_TOTAL_VOLUME(reagents)) for(var/obj/item/thing in get_stored_inventory()) thing.fluid_act(reagents) return TRUE @@ -445,9 +445,10 @@ * Most useful for calculating worth or deconstructing something along with its contents. */ /atom/proc/get_contained_matter(include_reagents = TRUE) - if(include_reagents && length(reagents?.reagent_volumes)) + var/list/reagent_volumes = REAGENT_VOLUMES(reagents) + if(include_reagents && length(reagent_volumes)) LAZYINITLIST(.) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in reagent_volumes) .[reagent.type] += floor(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_MATERIAL_UNIT) for(var/atom/contained_obj as anything in get_contained_external_atoms()) // machines handle component parts separately . = MERGE_ASSOCS_WITH_NUM_VALUES(., contained_obj.get_contained_matter(include_reagents)) @@ -504,7 +505,7 @@ */ /atom/proc/try_detonate_reagents(var/severity = 3) if(reagents) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) reagent.explosion_act(src, severity) /** @@ -1026,13 +1027,13 @@ return istype(turf) ? turf.is_outside() : OUTSIDE_UNCERTAIN /atom/proc/can_be_poured_into(atom/source) - return (reagents?.maximum_volume > 0) && ATOM_IS_OPEN_CONTAINER(src) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) && ATOM_IS_OPEN_CONTAINER(src) /// This is whether it's physically possible to pour from this atom to the target atom, based on context like user intent and src being open, etc. /// This should not check things like whether there is actually anything in src to pour. /// It should also not check anything controlled by the target atom, because can_be_poured_into() already exists. /atom/proc/can_be_poured_from(mob/user, atom/target) - return (reagents?.maximum_volume > 0) && ATOM_IS_OPEN_CONTAINER(src) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) && ATOM_IS_OPEN_CONTAINER(src) /atom/proc/take_vaporized_reagent(reagent, amount) return @@ -1041,7 +1042,7 @@ return !ATOM_IS_OPEN_CONTAINER(src) /atom/proc/can_drink_from(mob/user) - return ATOM_IS_OPEN_CONTAINER(src) && reagents?.total_volume && user.check_has_mouth() + return ATOM_IS_OPEN_CONTAINER(src) && REAGENT_TOTAL_VOLUME(reagents) && user.check_has_mouth() /atom/proc/adjust_required_attack_dexterity(mob/user, required_dexterity) if(storage) // TODO: possibly check can_be_inserted() to avoid being able to shoot mirrors as a drake. diff --git a/code/game/atoms_fluids.dm b/code/game/atoms_fluids.dm index bf49a3469c05..127ec73d713e 100644 --- a/code/game/atoms_fluids.dm +++ b/code/game/atoms_fluids.dm @@ -3,9 +3,12 @@ /atom/proc/fluid_act(var/datum/reagents/fluids) SHOULD_CALL_PARENT(TRUE) - if(reagents && reagents != fluids && fluids?.total_volume >= FLUID_SHALLOW && !is_watertight()) - reagents.trans_to_holder(fluids, reagents.total_volume) - fluids.trans_to_holder(reagents, min(fluids.total_volume, reagents.maximum_volume)) + if(reagents && reagents != fluids && !is_watertight()) + var/fluid_volume = REAGENT_TOTAL_VOLUME(fluids) + if(fluid_volume >= FLUID_SHALLOW) + var/reagent_volume = REAGENT_MAXIMUM_VOLUME(reagents) + reagents.trans_to_holder(fluids, reagent_volume) + fluids.trans_to_holder(reagents, fluid_volume, reagent_volume) /atom/proc/check_fluid_depth(var/min = 1) return 0 diff --git a/code/game/atoms_interactions.dm b/code/game/atoms_interactions.dm index 1f8c07525c1b..d4e352ba6835 100644 --- a/code/game/atoms_interactions.dm +++ b/code/game/atoms_interactions.dm @@ -40,5 +40,5 @@ var/global/list/_reagent_interactions = list( RETURN_TYPE(/list) if(storage) LAZYADD(., /decl/interaction_handler/storage_open) - if(reagents?.maximum_volume) + if(REAGENT_MAXIMUM_VOLUME(reagents)) LAZYADD(., global._reagent_interactions) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 0d998a4a5ec1..94c8602eefee 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -285,7 +285,7 @@ if(isturf(loc)) var/turf/T = loc - if(T.reagents?.total_volume && submerged()) + if(REAGENT_TOTAL_VOLUME(T.reagents) && submerged()) fluid_act(T.reagents) for(var/mob/viewer in storage?.storage_ui?.is_seeing) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index dc25d0685b56..0d703b829e3b 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -59,7 +59,7 @@ to_chat(user, SPAN_WARNING("\The [src] cannot accept any more chemical canisters.")) return FALSE if(!emagged) - for(var/decl/material/reagent as anything in canister.reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(canister.reagents)) for(var/banned_type in banned_chem_types) if(istype(reagent, banned_type)) to_chat(user, SPAN_WARNING("Automatic safety checking indicates the presence of a prohibited substance in this canister.")) @@ -125,7 +125,7 @@ . += SPAN_NOTICE("There are no chemical canisters loaded.") /obj/machinery/sleeper/proc/has_room_in_beaker() - return beaker && beaker.reagents.total_volume < beaker.reagents.maximum_volume + return beaker && REAGENT_TOTAL_VOLUME(beaker.reagents) < REAGENT_MAXIMUM_VOLUME(beaker.reagents) /obj/machinery/sleeper/Process() if(stat & (NOPOWER|BROKEN)) @@ -142,7 +142,8 @@ if(filtering) if(has_room_in_beaker()) - var/trans_volume = LAZYLEN(occupant.reagents?.reagent_volumes) + var/trans_volumes = REAGENT_VOLUMES(occupant.reagents) + var/trans_volume = LAZYLEN(trans_volumes) if(trans_volume) occupant.reagents.trans_to_obj(beaker, pump_speed * trans_volume) occupant.vessel.trans_to_obj(beaker, trans_volume + 1) @@ -153,7 +154,8 @@ if(has_room_in_beaker()) var/datum/reagents/ingested = occupant.get_ingested_reagents() if(ingested) - var/trans_volume = LAZYLEN(ingested.reagent_volumes) + var/trans_volumes = REAGENT_VOLUMES(ingested) + var/trans_volume = LAZYLEN(trans_volumes) if(trans_volume) ingested.trans_to_obj(beaker, pump_speed * trans_volume) else @@ -163,7 +165,8 @@ if(has_room_in_beaker()) var/datum/reagents/inhaled = occupant.get_inhaled_reagents() if(inhaled) - var/trans_volume = LAZYLEN(inhaled?.reagent_volumes) + var/trans_volumes = REAGENT_VOLUMES(inhaled) + var/trans_volume = LAZYLEN(trans_volumes) if(trans_volume) inhaled.trans_to_obj(beaker, pump_speed * trans_volume) else @@ -208,7 +211,7 @@ var/empties = 0 var/list/loaded_reagents = list() for(var/obj/item/chems/chem_disp_cartridge/canister in loaded_canisters) - if(!canister.reagents || !canister.reagents.total_volume) + if(!canister.reagents || !REAGENT_TOTAL_VOLUME(canister.reagents)) empties++ continue var/list/reagent = list() @@ -218,7 +221,7 @@ else reagent ["name"] = "unlabeled" reagent["id"] = "\ref[canister]" - reagent["amount"] = canister.reagents.total_volume + reagent["amount"] = REAGENT_TOTAL_VOLUME(canister.reagents) loaded_reagents += list(reagent) data["reagents"] = loaded_reagents data["empty_canisters"] = empties @@ -258,7 +261,7 @@ if(href_list["eject_empties"]) . = TOPIC_NOACTION for(var/obj/item/canister in loaded_canisters) - if(!canister.reagents || !canister.reagents.total_volume) + if(!canister.reagents || !REAGENT_TOTAL_VOLUME(canister.reagents)) eject_reagent_canister(null, canister) . = TOPIC_REFRESH if(href_list["eject"]) @@ -435,17 +438,16 @@ if(!istype(canister) || canister.loc != src) to_chat(user, SPAN_WARNING("\The [src] cannot locate that canister.")) return - if(canister.reagents?.total_volume < amount) + if(REAGENT_TOTAL_VOLUME(canister.reagents) < amount) to_chat(user, SPAN_WARNING("\The [canister] has less than [amount] unit\s left.")) return if(!occupant || !occupant.reagents) to_chat(user, SPAN_WARNING("There's no suitable occupant in \the [src].")) return - if(!emagged && canister.reagents?.primary_reagent) - var/decl/material/chem = canister.reagents.primary_reagent - if(chem.overdose && REAGENT_VOLUME(occupant.reagents, canister.reagents.primary_reagent) + amount >= chem.overdose) - to_chat(user, SPAN_WARNING("Injecting more [chem.name] presents an overdose risk to the subject.")) - return + var/decl/material/chem = canister.reagents?.get_primary_reagent_decl() + if(!emagged && chem?.overdose && REAGENT_VOLUME(occupant.reagents, chem) + amount >= chem.overdose) + to_chat(user, SPAN_WARNING("Injecting more [chem.name] presents an overdose risk to the subject.")) + return canister.reagents.trans_to_mob(occupant, amount, target_transfer_type) to_chat(user, SPAN_NOTICE("You use \the [src] to [target_transfer_type == CHEM_INJECT ? "inject" : "infuse"] [amount] unit\s from \the [canister] into \the [occupant].")) diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index 0a6de242520c..eb27136f261c 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -472,7 +472,7 @@ Class Procs: // This is really pretty crap and should be overridden for specific machines. /obj/machinery/fluid_act(var/datum/reagents/fluids) ..() - if(!QDELETED(src) && !(stat & (NOPOWER|BROKEN)) && !waterproof && (fluids?.total_volume > FLUID_DEEP)) + if(!QDELETED(src) && !(stat & (NOPOWER|BROKEN)) && !waterproof && (REAGENT_TOTAL_VOLUME(fluids) > FLUID_DEEP)) explosion_act(3) /obj/machinery/Move() diff --git a/code/game/machinery/_machines_base/machinery_public_vars_common.dm b/code/game/machinery/_machines_base/machinery_public_vars_common.dm index 8906b31d08c5..82739c5e4f02 100644 --- a/code/game/machinery/_machines_base/machinery_public_vars_common.dm +++ b/code/game/machinery/_machines_base/machinery_public_vars_common.dm @@ -104,7 +104,7 @@ Public vars at /obj/machinery level. Just because they are here does not mean th var_type = VAR_FORMAT_LIST /decl/public_access/public_variable/reagents/access_var(obj/machinery/machine) - return machine?.reagents?.reagent_data + return istype(machine?.reagents) ? UNLINT(machine.reagents.reagent_data) : null /decl/public_access/public_variable/reagents/volumes name = "reagents volumes" @@ -112,7 +112,7 @@ Public vars at /obj/machinery level. Just because they are here does not mean th var_type = VAR_FORMAT_LIST /decl/public_access/public_variable/reagents/volumes/access_var(obj/machinery/machine) - return machine?.reagents?.reagent_volumes + return istype(machine?.reagents) ? UNLINT(REAGENT_VOLUMES(machine.reagents)) : null /decl/public_access/public_variable/reagents/free_space name = "reagents free space" @@ -128,7 +128,7 @@ Public vars at /obj/machinery level. Just because they are here does not mean th var_type = VAR_FORMAT_NUMBER /decl/public_access/public_variable/reagents/total_volume/access_var(obj/machinery/machine) - return machine?.reagents?.total_volume + return REAGENT_TOTAL_VOLUME(machine?.reagents) /decl/public_access/public_variable/reagents/maximum_volume name = "reagents maximum volume" @@ -136,7 +136,7 @@ Public vars at /obj/machinery level. Just because they are here does not mean th var_type = VAR_FORMAT_NUMBER /decl/public_access/public_variable/reagents/maximum_volume/access_var(obj/machinery/machine) - return machine?.reagents?.maximum_volume + return REAGENT_MAXIMUM_VOLUME(machine?.reagents) /decl/public_access/public_method/toggle_power name = "toggle power" diff --git a/code/game/machinery/centrifuge.dm b/code/game/machinery/centrifuge.dm index 217e63809e5e..f992197e156e 100644 --- a/code/game/machinery/centrifuge.dm +++ b/code/game/machinery/centrifuge.dm @@ -5,7 +5,7 @@ expected_type = /obj/machinery/centrifuge /datum/storage/hopper/industrial/centrifuge/proc/should_ingest(mob/user, obj/item/thing) - if(thing.reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(thing.reagents) <= 0) if(user) to_chat(user, SPAN_WARNING("\The [thing] is empty.")) return FALSE diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index 13b2847b3440..37b55f182398 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -26,7 +26,7 @@ continue // Out of range if(!chem_implant.implanted) continue - dat += "[chem_implant.imp_in.name] | Remaining Units: [chem_implant.reagents.total_volume] | Inject: " + dat += "[chem_implant.imp_in.name] | Remaining Units: [REAGENT_TOTAL_VOLUME(chem_implant.reagents)] | Inject: " dat += "((1))" dat += "((5))" dat += "((10))
      " diff --git a/code/game/machinery/cracker.dm b/code/game/machinery/cracker.dm index 2de58f1e211c..9c6951170b78 100644 --- a/code/game/machinery/cracker.dm +++ b/code/game/machinery/cracker.dm @@ -40,19 +40,21 @@ if(use_power == POWER_USE_IDLE) return - // Produce materials. var/turf/T = get_turf(src) - if(istype(T) && T.reagents?.total_volume) - - // Drink more water! - var/consuming = min(T.reagents.total_volume, fluid_consumption_per_tick) - T.remove_any_reagents(consuming) - T.show_bubbles() - - // Gas production. - var/datum/gas_mixture/produced = new - var/gen_amt = min(1, (gas_generated_per_tick * (consuming/fluid_consumption_per_tick))) - produced.adjust_gas(/decl/material/gas/oxygen, gen_amt) - produced.adjust_gas(/decl/material/gas/hydrogen, gen_amt * 2) - produced.temperature = T20C //todo water temperature - air_contents.merge(produced) + if(!istype(T)) + return + + var/local_fluid = REAGENT_VOLUME(T.reagents, /decl/material/liquid/water) // TODO: make this some list on the material? + if(local_fluid <= 0) + return + + var/consuming = min(REAGENT_TOTAL_VOLUME(T.reagents), fluid_consumption_per_tick) + T.remove_any_reagents(consuming) + T.show_bubbles() + + var/datum/gas_mixture/produced = new + var/gen_amt = min(1, (gas_generated_per_tick * (consuming/fluid_consumption_per_tick))) + produced.adjust_gas(/decl/material/gas/oxygen, gen_amt) + produced.adjust_gas(/decl/material/gas/hydrogen, gen_amt * 2) + produced.temperature = T20C //todo water temperature + air_contents.merge(produced) diff --git a/code/game/machinery/kitchen/cooking_machines/_cooker.dm b/code/game/machinery/kitchen/cooking_machines/_cooker.dm index a340029c6599..0eeac4289ae0 100644 --- a/code/game/machinery/kitchen/cooking_machines/_cooker.dm +++ b/code/game/machinery/kitchen/cooking_machines/_cooker.dm @@ -131,8 +131,9 @@ cook_path = /obj/item/food/variable var/obj/item/food/result = new cook_path(src) //Holy typepaths, Batman. - if(cooking_obj.reagents && cooking_obj.reagents.total_volume) - cooking_obj.reagents.trans_to(result, cooking_obj.reagents.total_volume) + var/cooking_reagents = REAGENT_TOTAL_VOLUME(cooking_obj.reagents) + if(cooking_reagents > 0) + cooking_obj.reagents.trans_to(result, cooking_reagents) // Set icon and appearance. change_product_appearance(result) diff --git a/code/game/machinery/kitchen/icecream.dm b/code/game/machinery/kitchen/icecream.dm index f4a52486e668..01586b8549e9 100644 --- a/code/game/machinery/kitchen/icecream.dm +++ b/code/game/machinery/kitchen/icecream.dm @@ -96,11 +96,11 @@ dat += "Chocolate cones: Dispense Make x5 [product_types[CONE_CHOC]] cones left. (Ingredients: flour, sugar, coco powder)
    " dat += "
    " dat += "VAT CONTENT
    " - for(var/decl/material/reagent as anything in reagents?.liquid_volumes) + for(var/decl/material/reagent as anything in REAGENT_LIQUID_VOLUMES(reagents)) dat += "[reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID)]: [LIQUID_VOLUME(reagents, reagent)]" dat += "Purge
    " - for(var/decl/material/reagent as anything in reagents?.solid_volumes) + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(reagents)) dat += "[reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)]: [SOLID_VOLUME(reagents, reagent)]" dat += "Purge
    " @@ -120,8 +120,9 @@ icecream.add_ice_cream(flavour_name) // if(beaker) // beaker.reagents.trans_to(icecream, 10) - if(icecream.reagents.total_volume < 10) - icecream.add_to_reagents(/decl/material/liquid/nutriment/sugar, 10 - icecream.reagents.total_volume) + var/icecream_volume = REAGENT_TOTAL_VOLUME(icecream.reagents) + if(icecream_volume < 10) + icecream.add_to_reagents(/decl/material/liquid/nutriment/sugar, 10 - icecream_volume) else to_chat(user, "There is not enough icecream left!") else diff --git a/code/game/machinery/kitchen/microwave.dm b/code/game/machinery/kitchen/microwave.dm index 1455ec5907ac..2e0e8d8d14ad 100644 --- a/code/game/machinery/kitchen/microwave.dm +++ b/code/game/machinery/kitchen/microwave.dm @@ -180,8 +180,9 @@ for(var/obj/used_item in get_contained_external_atoms()) data["cooking_items"][used_item.name]++ data["cooking_reagents"] = list() - for(var/decl/material/reagent as anything in reagents.reagent_volumes) - data["cooking_reagents"][reagent.name] = reagents.reagent_volumes[reagent] + var/reagent_volumes = REAGENT_VOLUMES(reagents) + for(var/decl/material/reagent as anything in reagent_volumes) + data["cooking_reagents"][reagent.name] = reagent_volumes[reagent] data["on"] = !!operating data["broken"] = broken > 0 data["dirty"] = dirty >= 100 @@ -209,12 +210,13 @@ if(stat & (NOPOWER|BROKEN)) return - if (!reagents.total_volume && !length(get_contained_external_atoms())) //dry run + var/reagent_volume = REAGENT_TOTAL_VOLUME(reagents) + if (!reagent_volume && !length(get_contained_external_atoms())) //dry run start() return - if (reagents.total_volume && prob(50)) // 50% chance a liquid recipe gets messy - dirty += ceil(reagents.total_volume / 10) + if (reagent_volume && prob(50)) // 50% chance a liquid recipe gets messy + dirty += ceil(reagent_volume / 10) var/decl/recipe/recipe = select_recipe(RECIPE_CATEGORY_MICROWAVE, src, cooking_temperature) if (!recipe) @@ -253,7 +255,7 @@ break //Any leftover reagents are divided amongst the foods - var/total = reagents.total_volume + var/total = REAGENT_TOTAL_VOLUME(reagents) for (var/obj/item/I in cooked_items) reagents.trans_to_holder(I.reagents, total/cooked_items.len) I.dropInto(loc) // since eject only ejects ingredients! @@ -347,11 +349,12 @@ /obj/machinery/microwave/proc/dispose(var/mob/user, var/message = TRUE) var/list/ingredients = get_contained_external_atoms() - if (!LAZYLEN(ingredients) && !reagents.total_volume) + var/reagent_volume = REAGENT_TOTAL_VOLUME(reagents) + if (!LAZYLEN(ingredients) && !reagent_volume) return for (var/obj/thing in ingredients) thing.dropInto(loc) - if (reagents.total_volume) + if (reagent_volume) dirty++ reagents.clear_reagents() if(user && message) @@ -367,7 +370,7 @@ SSnano.update_uis(src) /obj/machinery/microwave/proc/eject_reagent(var/mob/user, var/decl/material/reagent) - if(!reagents.reagent_volumes[reagent]) + if(!REAGENT_VOLUME(reagents, reagent)) SSnano.update_uis(src) return // should not happen, must be a UI glitch or href hacking var/obj/item/chems/held_container = user.get_active_held_item() @@ -404,8 +407,9 @@ for (var/obj/thing in ingredients) amount++ - if (thing.reagents && thing.reagents.primary_reagent) - amount += REAGENT_VOLUME(thing.reagents, thing.reagents.primary_reagent) + var/thing_reagent = istype(thing.reagents) && thing.reagents.get_primary_reagent_decl() + if (thing_reagent) + amount += REAGENT_VOLUME(thing.reagents, thing_reagent) qdel(thing) reagents.clear_reagents() SSnano.update_uis(src) @@ -432,7 +436,7 @@ return TOPIC_REFRESH if ("ejectreagent") - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.name == href_list["target"]) eject_reagent(user, reagent) break diff --git a/code/game/machinery/smartfridge/foods.dm b/code/game/machinery/smartfridge/foods.dm index 1d3cf0d5b89e..b023cc97a94b 100644 --- a/code/game/machinery/smartfridge/foods.dm +++ b/code/game/machinery/smartfridge/foods.dm @@ -11,4 +11,4 @@ /obj/item/chems/glass/bowl, /obj/item/chems/glass/handmade/bowl ) - return istype(O) && O.reagents?.total_volume && is_type_in_list(O, _food_types) + return istype(O) && REAGENT_TOTAL_VOLUME(O.reagents) && is_type_in_list(O, _food_types) diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index 1113f10ec678..00387a12bbbc 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -65,7 +65,7 @@ /obj/machinery/washing_machine/proc/wash() if(operable()) var/list/washing_atoms = get_contained_external_atoms() - var/amount_per_atom = floor(reagents.total_volume / length(washing_atoms)) + var/amount_per_atom = floor(REAGENT_TOTAL_VOLUME(reagents) / length(washing_atoms)) if(amount_per_atom > 0) var/decl/material/smelliest = get_smelliest_reagent(reagents) @@ -94,13 +94,13 @@ if(!(atom_flags & ATOM_FLAG_OPEN_CONTAINER)) to_chat(user, SPAN_WARNING("Open the detergent port first!")) return TRUE - if(reagents.total_volume >= reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(reagents) >= REAGENT_MAXIMUM_VOLUME(reagents)) to_chat(user, SPAN_WARNING("The detergent port is full!")) return TRUE if(!user.try_unequip(used_item)) return TRUE // Directly transfer to the holder to avoid touch reactions. - used_item.reagents?.trans_to_holder(reagents, used_item.reagents.total_volume) + used_item.reagents?.trans_to_holder(reagents, REAGENT_TOTAL_VOLUME(used_item.reagents)) to_chat(user, SPAN_NOTICE("You dissolve \the [used_item] in the detergent port.")) qdel(used_item) return TRUE @@ -200,7 +200,7 @@ to_chat(user, SPAN_WARNING("\The [src] isn't functioning!")) return - if(!reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_WARNING("There are no cleaning products loaded in \the [src]!")) return diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 7295fe42516e..01cc88b50e31 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -269,7 +269,7 @@ */ /obj/proc/initialize_reagents(var/populate = TRUE) SHOULD_CALL_PARENT(TRUE) - if(reagents?.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(reagents) > 0) log_warning("\The [src] possibly is initializing its reagents more than once!") create_or_update_reagents(chem_volume) if(populate) @@ -331,7 +331,7 @@ /obj/fluid_act(var/datum/reagents/fluids) ..() - if(!QDELETED(src) && fluids?.total_volume) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids)) fluids.touch_obj(src) // TODO: maybe iterate the entire matter list or do some partial damage handling @@ -345,8 +345,9 @@ . = ..() if(QDELETED(src)) return - if(reagents?.total_volume) - reagents.trans_to(loc, reagents.total_volume) + var/reagent_volume = REAGENT_TOTAL_VOLUME(reagents) + if(reagent_volume) + reagents.trans_to(loc, reagent_volume) dump_contents() return place_melted_product(meltable_materials) @@ -424,12 +425,12 @@ /obj/physically_destroyed(skip_qdel) var/dumped_reagents = FALSE var/atom/last_loc = loc - if(last_loc && reagents?.total_volume) - reagents.trans_to(loc, reagents.total_volume, defer_update = TRUE) + if(last_loc && REAGENT_TOTAL_VOLUME(reagents)) + reagents.trans_to(loc, REAGENT_TOTAL_VOLUME(reagents), defer_update = TRUE) dumped_reagents = TRUE reagents.clear_reagents() // We are qdeling, don't bother with a more nuanced update. . = ..() - if(dumped_reagents && last_loc && !QDELETED(last_loc) && last_loc.reagents?.total_volume) + if(dumped_reagents && last_loc && !QDELETED(last_loc) && REAGENT_TOTAL_VOLUME(last_loc.reagents)) last_loc.reagents.handle_update() HANDLE_REACTIONS(last_loc.reagents) diff --git a/code/game/objects/_objs_edibility.dm b/code/game/objects/_objs_edibility.dm index 65fc62ced6a4..ed8f1000448a 100644 --- a/code/game/objects/_objs_edibility.dm +++ b/code/game/objects/_objs_edibility.dm @@ -151,12 +151,12 @@ show_feed_message_start(user, target, consumption_method) if(!do_mob(user, target)) return EATEN_UNABLE - var/contained = json_encode(REAGENT_LIST(src)) + var/contained = json_encode(REAGENT_LIST(reagents)) admin_attack_log(user, target, "Fed the victim with [name] (Reagents: [contained])", "Was fed [src] (Reagents: [contained])", "used [src] (Reagents: [contained]) to feed") show_feed_message_end(user, target, consumption_method) if(consumption_method == EATING_METHOD_DRINK && target?.has_personal_goal(/datum/goal/achievement/specific_object/drink)) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) target.update_personal_goal(/datum/goal/achievement/specific_object/drink, reagent) handle_consumed(user, target, consumption_method) diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index 1305d6ee4112..ff08e746ae14 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -106,7 +106,7 @@ /datum/effect/effect/system/smoke_spread/chem/set_up(var/datum/reagents/carry = null, n = 10, c = 0, loca, direct) range = n * 0.3 cardinals = c - carry.trans_to_obj(chemholder, carry.total_volume, copy = 1) + carry.trans_to_obj(chemholder, REAGENT_TOTAL_VOLUME(carry), copy = 1) if(istype(loca, /turf/)) location = loca @@ -157,7 +157,8 @@ if(!location) return - if(LAZYLEN(chemholder.reagents.reagent_volumes)) + var/trans_volumes = REAGENT_VOLUMES(chemholder.reagents) + if(LAZYLEN(trans_volumes)) for(var/turf/T in (wallList|targetTurfs)) chemholder.reagents.touch_turf(T) @@ -216,8 +217,9 @@ else smoke = new /obj/effect/effect/smoke/chem(location, smoke_duration + rand(0, 20), T, I) - if(LAZYLEN(chemholder.reagents.reagent_volumes)) - chemholder.reagents.trans_to_obj(smoke, chemholder.reagents.total_volume / dist, copy = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents + var/trans_volumes = REAGENT_VOLUMES(chemholder.reagents) + if(LAZYLEN(trans_volumes)) + chemholder.reagents.trans_to_obj(smoke, REAGENT_TOTAL_VOLUME(chemholder.reagents) / dist, copy = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents //Kinda ugly, but needed unless the system is reworked if(splash_initial) diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm index a961e82da284..56e37741123b 100644 --- a/code/game/objects/effects/chem/foam.dm +++ b/code/game/objects/effects/chem/foam.dm @@ -57,8 +57,8 @@ F = new(T, metal) F.amount = amount - if(!metal && reagents?.total_volume) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + if(!metal && REAGENT_TOTAL_VOLUME(reagents)) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) F.add_to_reagents(reagent, 1, safety = 1) //added safety check since reagents in the foam have already had a chance to react /obj/effect/effect/foam/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) // foam disolves when heated, except metal foams @@ -92,7 +92,7 @@ // bit of a hack here. Foam carries along any reagent also present in the glass it is mixed with (defaults to water if none is present). Rather than actually transfer the reagents, this makes a list of the reagent ids and spawns 1 unit of that reagent when the foam disolves. if(carry && !metal) - for(var/decl/material/reagent as anything in carry.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(carry)) carried_reagents += reagent /datum/effect/effect/system/foam_spread/start() diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm index aafbd41b47df..5f7beccd2297 100644 --- a/code/game/objects/effects/chem/water.dm +++ b/code/game/objects/effects/chem/water.dm @@ -34,15 +34,16 @@ //each step splash 1/5 of the reagents on non-mobs //could determine the # of steps until target, but that would be complicated + // TODO: fix this logic so it isn't using an increasingly smaller amount each iteration. for(var/atom/A in splash_others) - reagents.splash(A, (reagents.total_volume/step_count)/splash_others.len) + reagents.splash(A, (REAGENT_TOTAL_VOLUME(reagents)/step_count)/splash_others.len) for(var/mob/living/M in splash_mobs) - reagents.splash(M, reagents.total_volume/splash_mobs.len) - if(reagents.total_volume < 1) + reagents.splash(M, REAGENT_TOTAL_VOLUME(reagents)/splash_mobs.len) + if(REAGENT_TOTAL_VOLUME(reagents) < 1) break if(T == get_turf(target)) for(var/atom/A in splash_others) - reagents.splash(A, reagents.total_volume/splash_others.len) //splash anything left + reagents.splash(A, REAGENT_TOTAL_VOLUME(reagents)/splash_others.len) //splash anything left break sleep(delay) diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 6758e59d78c8..634c454322af 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -101,7 +101,7 @@ /obj/effect/decal/cleanable/vomit/Crossed(atom/movable/AM) . = ..() - if(!QDELETED(src) && reagents?.total_volume >= 1 && isliving(AM)) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(reagents) >= 1 && isliving(AM)) var/mob/living/walker = AM walker.add_walking_contaminant(reagents, rand(2, 3)) diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index d8cd0b321ec1..036251e4ea38 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -72,7 +72,7 @@ /obj/effect/decal/cleanable/fluid_act(var/datum/reagents/fluid) SHOULD_CALL_PARENT(FALSE) - if(fluid?.total_liquid_volume && !QDELETED(src)) - if(reagents?.total_volume) - reagents.trans_to(fluid, reagents.total_volume) + if(REAGENT_TOTAL_LIQUID_VOLUME(fluid) && !QDELETED(src)) + if(REAGENT_TOTAL_VOLUME(reagents)) + reagents.trans_to(fluid, REAGENT_TOTAL_VOLUME(reagents)) qdel(src) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 5c0a8f52c946..f548bcbfada6 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -414,7 +414,7 @@ if(drying_wetness > 0 && drying_wetness != initial(drying_wetness)) desc_comp += "\The [src] is [get_dryness_text()]." - if(coating?.total_volume) + if(REAGENT_TOTAL_VOLUME(coating)) desc_comp += "It is covered in [coating.get_coated_name()]." // It is covered in dilute oily slimy bloody mud. if(check_rights(R_DEBUG, 0, user)) @@ -1057,14 +1057,14 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. if(!coating) return coating.remove_any(amount) - if(coating.total_volume <= MINIMUM_CHEMICAL_VOLUME) + if(REAGENT_TOTAL_VOLUME(coating) <= MINIMUM_CHEMICAL_VOLUME) clean(FALSE) /obj/item/proc/transfer_coating_to(atom/target, amount = 1, multiplier = 1, copy = 0, defer_update = FALSE, transferred_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID)) if(!coating) return coating.trans_to(target, amount, multiplier) - if(coating.total_volume <= MINIMUM_CHEMICAL_VOLUME) + if(REAGENT_TOTAL_VOLUME(coating) <= MINIMUM_CHEMICAL_VOLUME) clean(FALSE) /obj/item/clean(clean_forensics=TRUE) @@ -1211,9 +1211,9 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. var/decl/material/bait_mat = GET_DECL(mat) if(bait_mat.fishing_bait_value) . += MATERIAL_UNITS_TO_REAGENTS_UNITS(matter[mat]) * bait_mat.fishing_bait_value * BAIT_VALUE_CONSTANT - for(var/decl/material/reagent as anything in reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.fishing_bait_value) - . += reagents.reagent_volumes[reagent] * reagent.fishing_bait_value * BAIT_VALUE_CONSTANT + . += REAGENT_VOLUME(reagents, reagent) * reagent.fishing_bait_value * BAIT_VALUE_CONSTANT #undef BAIT_VALUE_CONSTANT /obj/item/proc/get_storage_cost() @@ -1277,7 +1277,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /// @returns: /// - reagent_overlay as /image|null - the overlay image representing the reagents in this object /obj/item/proc/get_reagents_overlay(state_prefix) - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) return var/decl/material/primary_reagent = reagents.get_primary_reagent_decl() if(!primary_reagent) @@ -1292,7 +1292,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. if(!reagents_state || !check_state_in_icon(reagents_state, icon)) return var/image/reagent_overlay = overlay_image(icon, reagents_state, reagents.get_color(), RESET_COLOR | RESET_ALPHA) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(!reagent.reagent_overlay) continue var/modified_reagent_overlay = state_prefix ? "[state_prefix]_[reagent.reagent_overlay]" : reagent.reagent_overlay @@ -1304,7 +1304,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /obj/item/on_reagent_change() . = ..() // You can't put liquids in clay/sand/dirt vessels, sorry. - if(reagents?.total_liquid_volume > 0 && material && material.hardness <= MAT_VALUE_MALLEABLE && !QDELETED(src)) + if(REAGENT_TOTAL_LIQUID_VOLUME(reagents) > 0 && material && material.hardness <= MAT_VALUE_MALLEABLE && !QDELETED(src)) visible_message(SPAN_DANGER("\The [src] falls apart!")) squash_item() if(!QDELETED(src)) @@ -1314,7 +1314,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. return null /obj/item/get_examine_prefix() - if(coating?.total_volume) + if(REAGENT_TOTAL_VOLUME(coating)) var/coating_string = coating.get_coated_adjectives() // component coloring is handled in here if(get_config_value(/decl/config/enum/colored_coating_names) == CONFIG_COATING_COLOR_MIXTURE) coating_string = FONT_COLORED(coating.get_color(), coating_string) diff --git a/code/game/objects/items/_item_edibility.dm b/code/game/objects/items/_item_edibility.dm index 4957dbecff8f..ecf12a115591 100644 --- a/code/game/objects/items/_item_edibility.dm +++ b/code/game/objects/items/_item_edibility.dm @@ -8,7 +8,7 @@ var/utensil_food_type = get_utensil_food_type() if(!istype(utensil) || !utensil_food_type) return - var/remove_amt = min(reagents?.total_volume, get_food_default_transfer_amount(user)) + var/remove_amt = min(REAGENT_TOTAL_VOLUME(reagents), get_food_default_transfer_amount(user)) if(remove_amt) // Create a dummy copy of the target food item. @@ -28,7 +28,7 @@ utensil.loaded_food.reagents.clear_reagents() reagents.trans_to(utensil.loaded_food, remove_amt) handle_chunk_separated() - if(!reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) handle_consumed(user) // it's not actually being consumed, so i'm not sure this is correct utensil.update_icon() diff --git a/code/game/objects/items/_item_reagents.dm b/code/game/objects/items/_item_reagents.dm index 9431fb7e70c2..24f8656fc072 100644 --- a/code/game/objects/items/_item_reagents.dm +++ b/code/game/objects/items/_item_reagents.dm @@ -2,7 +2,7 @@ if(!istype(target) || isnull(target.get_reagent_amount_dispensed()) || (!skip_container_check && (target.atom_flags & ATOM_FLAG_OPEN_CONTAINER))) return FALSE - if(!target.reagents || !target.reagents.total_volume) + if(!target.reagents || !REAGENT_TOTAL_VOLUME(target.reagents)) to_chat(user, SPAN_NOTICE("[target] is empty of reagents.")) return TRUE @@ -22,7 +22,7 @@ to_chat(user, SPAN_NOTICE("You can't splash people on help intent.")) return TRUE - if(!reagents || !reagents.total_volume) + if(!reagents || !REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_NOTICE("[src] is empty of reagents.")) return TRUE @@ -30,14 +30,14 @@ to_chat(user, SPAN_NOTICE("[target] is full of reagents.")) return TRUE - var/contained = REAGENT_LIST(src) + var/contained = REAGENT_LIST(reagents) admin_attack_log(user, target, "Used \the [name] containing [contained] to splash the victim.", "Was splashed by \the [name] containing [contained].", "used \the [name] containing [contained] to splash") user.visible_message( \ SPAN_DANGER("\The [target] has been splashed with the contents of \the [src] by \the [user]!"), \ SPAN_DANGER("You splash \the [target] with the contents of \the [src].")) - reagents.splash(target, reagents.total_volume) + reagents.splash(target, REAGENT_TOTAL_VOLUME(reagents)) return TRUE /obj/item/proc/standard_pour_into(mob/user, atom/target, amount = 5) // This goes into afterattack and yes, it's atom-level @@ -55,7 +55,7 @@ if(!can_be_poured_from(user, target)) return TRUE // don't splash if we can't pour - if(!reagents || !reagents.total_volume) + if(!reagents || !REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_NOTICE("[src] is empty of reagents.")) return TRUE @@ -63,7 +63,8 @@ to_chat(user, SPAN_NOTICE("[target] is full of reagents.")) return TRUE - var/had_liquids = length(reagents.liquid_volumes) + var/liquid_volumes = REAGENT_LIQUID_VOLUMES(reagents) + var/had_liquids = length(liquid_volumes) var/transferred_amount = reagents.trans_to(target, amount) if(had_liquids) @@ -71,5 +72,5 @@ else // Sounds more like pouring small pellets or dust. playsound(src, 'sound/effects/refill.ogg', 25, 1) - to_chat(user, SPAN_NOTICE("You transfer [transferred_amount] unit\s of the solution to \the [target]. \The [src] now contains [reagents.total_volume] unit\s.")) + to_chat(user, SPAN_NOTICE("You transfer [transferred_amount] unit\s of the solution to \the [target]. \The [src] now contains [REAGENT_TOTAL_VOLUME(reagents)] unit\s.")) return TRUE diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm index 8b62f298f8f7..669e5876b86f 100644 --- a/code/game/objects/items/contraband.dm +++ b/code/game/objects/items/contraband.dm @@ -48,10 +48,10 @@ add_to_reagents(reagent, picked_reagents[reagent]) var/list/names = new - for(var/decl/material/reagent as anything in reagents.liquid_volumes) + for(var/decl/material/reagent as anything in REAGENT_LIQUID_VOLUMES(reagents)) names += reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID) - for(var/decl/material/reagent as anything in reagents.solid_volumes) + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(reagents)) names += reagent.get_reagent_name(reagents, MAT_PHASE_SOLID) desc = "Contains [english_list(names)]." \ No newline at end of file diff --git a/code/game/objects/items/flame/_flame.dm b/code/game/objects/items/flame/_flame.dm index 86fa054e41ab..d57f5a8ce1f7 100644 --- a/code/game/objects/items/flame/_flame.dm +++ b/code/game/objects/items/flame/_flame.dm @@ -158,19 +158,19 @@ /obj/item/flame/fluid_act(var/datum/reagents/fluids) ..() - if(QDELETED(src) || !fluids?.total_volume || !lit) + if(QDELETED(src) || !REAGENT_TOTAL_VOLUME(fluids) || !lit) return var/turf/location = get_turf(src) if(location) location.hotspot_expose(700, 5) // Potentially set fire to fuel etc. - if(QDELETED(src) || !fluids?.total_volume) + if(QDELETED(src) || !REAGENT_TOTAL_VOLUME(fluids)) return if(waterproof) return - if(fluids.total_volume >= FLUID_PUDDLE) + if(REAGENT_TOTAL_VOLUME(fluids) >= FLUID_PUDDLE) snuff_out(no_message = TRUE) /obj/item/flame/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) diff --git a/code/game/objects/items/flame/flame_fuelled.dm b/code/game/objects/items/flame/flame_fuelled.dm index 51cdb60beefe..f956496fca12 100644 --- a/code/game/objects/items/flame/flame_fuelled.dm +++ b/code/game/objects/items/flame/flame_fuelled.dm @@ -22,9 +22,9 @@ /obj/item/flame/fuelled/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) if(get_attack_force() && !(item_flags & ITEM_FLAG_NO_BLUDGEON) && user.check_intent(I_FLAG_HARM)) . = ..() - if(reagents?.total_volume && !QDELETED(target)) + if(REAGENT_TOTAL_VOLUME(reagents) && !QDELETED(target)) target.visible_message(SPAN_DANGER("Some of the contents of \the [src] splash onto \the [target].")) - reagents.splash(target, reagents.total_volume) + reagents.splash(target, REAGENT_TOTAL_VOLUME(reagents)) return TRUE return FALSE @@ -40,9 +40,9 @@ return TRUE if(handle_eaten_by_mob(user, target) != EATEN_INVALID) return TRUE - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_NOTICE("You splash a small amount of the contents of \the [src] onto \the [target].")) - reagents.splash(target, min(reagents.total_volume, 5)) + reagents.splash(target, min(REAGENT_TOTAL_VOLUME(reagents), 5)) return TRUE . = ..() @@ -56,8 +56,9 @@ if(fuel_reagent) . += SPAN_NOTICE("\The [src] is designed to burn [fuel_reagent.liquid_name].") - if(reagents?.maximum_volume) - switch(reagents.total_volume / reagents.maximum_volume) + var/max_vol = REAGENT_MAXIMUM_VOLUME(reagents) + if(max_vol) + switch(REAGENT_TOTAL_VOLUME(reagents) / max_vol) if(0 to 0.1) . += SPAN_WARNING("\The [src] is nearly empty.") if(0.1 to 0.25) @@ -84,8 +85,10 @@ return FALSE /obj/item/flame/fuelled/populate_reagents() - if(start_fuelled && fuel_type && reagents?.maximum_volume) - add_to_reagents(fuel_type, reagents.maximum_volume) + if(start_fuelled && fuel_type) + var/max_vol = REAGENT_MAXIMUM_VOLUME(reagents) + if(max_vol) + add_to_reagents(fuel_type, max_vol) /obj/item/flame/fuelled/Process() . = ..() diff --git a/code/game/objects/items/flame/flame_torch.dm b/code/game/objects/items/flame/flame_torch.dm index 0e2f14c1022f..0ad60d0d86f7 100644 --- a/code/game/objects/items/flame/flame_torch.dm +++ b/code/game/objects/items/flame/flame_torch.dm @@ -20,7 +20,7 @@ return available_scents /obj/item/flame/torch/light(mob/user, no_message) - if(coating?.total_volume && coating.get_accelerant_value() < FUEL_VALUE_NONE) + if(REAGENT_TOTAL_VOLUME(coating) && coating.get_accelerant_value() < FUEL_VALUE_NONE) to_chat(user, SPAN_WARNING("You cannot light \the [src] while it is wet!")) return FALSE if(burnt) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 545cc0b21be6..a8c443c40ed9 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -52,42 +52,43 @@ _base_attack_force = 0 /obj/item/chems/water_balloon/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) - if(overlay && reagents?.total_volume <= 0) + if(overlay && REAGENT_TOTAL_VOLUME(reagents) <= 0) overlay.icon_state = "[overlay.icon_state]_empty" . = ..() /obj/item/chems/water_balloon/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) - . += "It's [reagents?.total_volume > 0? "filled with liquid sloshing around" : "empty"]." + . += "It's [REAGENT_TOTAL_VOLUME(reagents) > 0? "filled with liquid sloshing around" : "empty"]." /obj/item/chems/water_balloon/on_reagent_change() if(!(. = ..())) return - w_class = (reagents?.total_volume > 0)? ITEM_SIZE_SMALL : ITEM_SIZE_TINY + w_class = (REAGENT_TOTAL_VOLUME(reagents) > 0)? ITEM_SIZE_SMALL : ITEM_SIZE_TINY //#TODO: Maybe acids should handle eating their own containers themselves? - for(var/decl/material/reagent as anything in reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.solvent_power >= MAT_SOLVENT_STRONG) visible_message(SPAN_DANGER("\The [reagent] chews through \the [src]!")) physically_destroyed() /obj/item/chems/water_balloon/throw_impact(atom/hit_atom, datum/thrownthing/TT) ..() - if(reagents?.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(reagents) > 0) visible_message(SPAN_WARNING("\The [src] bursts!")) physically_destroyed() /obj/item/chems/water_balloon/physically_destroyed(skip_qdel) - if(reagents?.total_volume > 0) + var/reagent_volume = REAGENT_TOTAL_VOLUME(reagents) + if(reagent_volume > 0) new /obj/effect/temporary(src, 5, icon, "[get_world_inventory_state()]_burst") - reagents.splash_turf(get_turf(src), reagents.total_volume) + reagents.splash_turf(get_turf(src), reagent_volume) playsound(src, 'sound/effects/balloon-pop.ogg', 75, TRUE, 3) . = ..() /obj/item/chems/water_balloon/on_update_icon() . = ..() icon_state = get_world_inventory_state() - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) icon_state = "[icon_state]_empty" /obj/item/chems/water_balloon/afterattack(obj/target, mob/user, proximity) diff --git a/code/game/objects/items/waterskin.dm b/code/game/objects/items/waterskin.dm index 729f6b1e9240..94c7166bc657 100644 --- a/code/game/objects/items/waterskin.dm +++ b/code/game/objects/items/waterskin.dm @@ -65,4 +65,4 @@ /obj/item/chems/glass/waterskin/crafted/wine/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/wine, reagents?.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/wine, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/game/objects/items/weapons/ecigs.dm b/code/game/objects/items/weapons/ecigs.dm index 305d0aa5f5ce..ca7f2f6eb060 100644 --- a/code/game/objects/items/weapons/ecigs.dm +++ b/code/game/objects/items/weapons/ecigs.dm @@ -39,7 +39,7 @@ /obj/item/clothing/mask/smokable/ecig/simple/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(ec_cartridge) - . += SPAN_NOTICE("There are [round(ec_cartridge.reagents.total_volume, 1)] units of liquid remaining.") + . += SPAN_NOTICE("There are [round(REAGENT_TOTAL_VOLUME(ec_cartridge.reagents), 1)] units of liquid remaining.") else . += SPAN_NOTICE("There's no cartridge connected.") @@ -58,7 +58,7 @@ /obj/item/clothing/mask/smokable/ecig/util/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(ec_cartridge) - . += SPAN_NOTICE("There are [round(ec_cartridge.reagents.total_volume, 1)] units of liquid remaining.") + . += SPAN_NOTICE("There are [round(REAGENT_TOTAL_VOLUME(ec_cartridge.reagents), 1)] units of liquid remaining.") else . += SPAN_NOTICE("There's no cartridge connected.") @@ -74,7 +74,7 @@ /obj/item/clothing/mask/smokable/ecig/deluxe/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(ec_cartridge) - . += SPAN_NOTICE("There are [round(ec_cartridge.reagents.total_volume, 1)] units of liquid remaining.") + . += SPAN_NOTICE("There are [round(REAGENT_TOTAL_VOLUME(ec_cartridge.reagents), 1)] units of liquid remaining.") else . += SPAN_NOTICE("There's no cartridge connected.") @@ -102,8 +102,9 @@ if(ishuman(loc)) var/mob/living/human/user = loc - if (!lit || !ec_cartridge || !ec_cartridge.reagents.total_volume)//no cartridge - if(!ec_cartridge.reagents.total_volume) + var/cart_vol = REAGENT_TOTAL_VOLUME(ec_cartridge.reagents) + if (!lit || !ec_cartridge || !cart_vol)//no cartridge + if(!cart_vol) to_chat(user, SPAN_NOTICE("There's no liquid left in \the [src], so you shut it down.")) Deactivate() return @@ -152,7 +153,7 @@ if (!ec_cartridge) to_chat(user, SPAN_NOTICE("You can't use \the [src] with no cartridge installed!")) return - else if(!ec_cartridge.reagents.total_volume) + else if(!REAGENT_TOTAL_VOLUME(ec_cartridge.reagents)) to_chat(user, SPAN_NOTICE("You can't use \the [src] with no liquid left!")) return else if(!cell.check_charge(power_usage * CELLRATE)) @@ -189,7 +190,7 @@ /obj/item/chems/ecig_cartridge/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "The cartridge has [reagents.total_volume] units of liquid remaining." + . += "The cartridge has [REAGENT_TOTAL_VOLUME(reagents)] units of liquid remaining." //flavours /obj/item/chems/ecig_cartridge/blank diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 7c57b93689e5..074cdfc5de33 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -45,7 +45,7 @@ _base_attack_force = 3 /obj/item/chems/spray/extinguisher/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/spray/extinguisher/has_safety() return TRUE diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index d5bb28f18f47..b4c4612cc565 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -101,7 +101,7 @@ to_chat(user, "The grenade can not hold more containers.") return TRUE else - if(used_item.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(used_item.reagents)) if(!user.try_unequip(used_item, src)) return TRUE to_chat(user, "You add \the [used_item] to the assembly.") @@ -138,7 +138,7 @@ var/has_reagents = 0 for(var/obj/item/chems/glass/G in beakers) - if(G.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(G.reagents)) has_reagents = TRUE break @@ -162,13 +162,13 @@ M.toggle_throw_mode(FALSE) for(var/obj/item/chems/glass/G in beakers) - G.reagents.trans_to_obj(src, G.reagents.total_volume) + G.reagents.trans_to_obj(src, REAGENT_TOTAL_VOLUME(G.reagents)) anchored = TRUE set_invisibility(INVISIBILITY_MAXIMUM) // Visual effect to show the grenade going off. - if(reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) var/datum/effect/effect/system/steam_spread/steam = new steam.set_up(10, 0, get_turf(src)) steam.attach(src) @@ -177,13 +177,14 @@ // Allow time for reactions to proc. var/max_delays = 5 var/delays = 0 - while(reagents.total_volume && delays <= max_delays) + while(REAGENT_TOTAL_VOLUME(reagents) && delays <= max_delays) delays++ sleep(SSmaterials.wait) // The reactions didn't use up all reagents, dump them as a fluid. - if(reagents.total_volume) - reagents.trans_to(loc, reagents.total_volume) + var/reagent_volume = REAGENT_TOTAL_VOLUME(reagents) + if(reagent_volume) + reagents.trans_to(loc, reagent_volume) qdel(src) diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index d46890df842d..05ae7042beb4 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -35,13 +35,14 @@ if(!istype(moppable_turf)) return ..() - if(moppable_turf?.reagents?.total_volume > 0) - if(moppable_turf.reagents.total_volume > FLUID_SHALLOW) + var/mop_reagents = REAGENT_TOTAL_VOLUME(moppable_turf?.reagents) + if(mop_reagents > 0) + if(mop_reagents > FLUID_SHALLOW) to_chat(user, SPAN_WARNING("There is too much water here to be mopped up.")) return TRUE user.visible_message(SPAN_NOTICE("\The [user] begins to mop up \the [moppable_turf].")) if(do_after(user, 40, moppable_turf) && !QDELETED(moppable_turf)) - if(moppable_turf.reagents?.total_volume > FLUID_SHALLOW) + if(REAGENT_TOTAL_VOLUME(moppable_turf.reagents) > FLUID_SHALLOW) to_chat(user, SPAN_WARNING("There is too much water here to be mopped up.")) else to_chat(user, SPAN_NOTICE("You have finished mopping!")) @@ -51,7 +52,7 @@ if(!is_type_in_list(A, moppable_types)) return ..() - if(reagents?.total_volume < 1) + if(REAGENT_TOTAL_VOLUME(reagents) < 1) to_chat(user, SPAN_WARNING("\The [src] is dry!")) return TRUE @@ -59,7 +60,7 @@ user.visible_message(SPAN_DANGER("\The [user] begins to aggressively mop \the [moppable_turf]!")) else user.visible_message(SPAN_NOTICE("\The [user] begins to clean \the [moppable_turf].")) - if(do_after(user, mopspeed, moppable_turf) && reagents?.total_volume) + if(do_after(user, mopspeed, moppable_turf) && REAGENT_TOTAL_VOLUME(reagents)) reagents.touch_turf(moppable_turf) reagents.remove_any(1) to_chat(user, SPAN_NOTICE("You have finished mopping!")) @@ -103,7 +104,7 @@ playsound(user, 'sound/machines/click.ogg', 30, 1) /obj/item/mop/advanced/Process() - if(reagents.total_volume < reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(reagents) < REAGENT_MAXIMUM_VOLUME(reagents)) add_to_reagents(refill_reagent, refill_rate) /obj/item/mop/advanced/get_examine_strings(mob/user, distance, infix, suffix) diff --git a/code/game/objects/items/weapons/paint.dm b/code/game/objects/items/weapons/paint.dm index ddbe13a497ee..4c3c8685558c 100644 --- a/code/game/objects/items/weapons/paint.dm +++ b/code/game/objects/items/weapons/paint.dm @@ -10,7 +10,7 @@ var/pigment /obj/item/chems/glass/bucket/paint/populate_reagents() - var/amt = reagents.maximum_volume + var/amt = REAGENT_MAXIMUM_VOLUME(reagents) if(pigment) amt = round(amt/2) add_to_reagents(pigment, amt) diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm index 47518ab9b3d0..15e1cb314ab5 100644 --- a/code/game/objects/items/weapons/soap.dm +++ b/code/game/objects/items/weapons/soap.dm @@ -68,7 +68,7 @@ wet() return TRUE - if(reagents?.total_volume < 1) + if(REAGENT_TOTAL_VOLUME(reagents) < 1) to_chat(user, SPAN_WARNING("\The [src] is too dry to clean \the [target].")) return TRUE @@ -77,7 +77,7 @@ if(!isturf(target)) return ..() user.visible_message(SPAN_NOTICE("\The [user] starts scrubbing \the [target].")) - if(!do_after(user, 8 SECONDS, target) && reagents?.total_volume) + if(!do_after(user, 8 SECONDS, target) && REAGENT_TOTAL_VOLUME(reagents)) return TRUE to_chat(user, SPAN_NOTICE("You scrub \the [target] clean.")) else if(istype(target,/obj/effect/decal/cleanable)) @@ -97,11 +97,11 @@ if(user.get_target_zone() == BP_MOUTH && victim.check_has_mouth()) user.visible_message(SPAN_DANGER("\The [user] washes \the [target]'s mouth out with soap!")) if(reagents) - reagents.trans_to_mob(target, reagents.total_volume / 2, CHEM_INGEST) + reagents.trans_to_mob(target, REAGENT_TOTAL_VOLUME(reagents) / 2, CHEM_INGEST) else user.visible_message(SPAN_NOTICE("\The [user] cleans \the [target].")) if(reagents) - reagents.trans_to(target, reagents.total_volume / 8) + reagents.trans_to(target, REAGENT_TOTAL_VOLUME(reagents) / 8) target.clean() user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam return TRUE diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm index 37a12e8d3dcc..8787cc2814d0 100644 --- a/code/game/objects/items/weapons/storage/bible.dm +++ b/code/game/objects/items/weapons/storage/bible.dm @@ -89,7 +89,7 @@ if(proximity && user?.mind?.assigned_job?.is_holy) if(A.reagents && A.reagents.has_reagent(/decl/material/liquid/water)) //blesses all the water in the holder to_chat(user, SPAN_NOTICE("You bless \the [A].")) // I wish it was this easy in nethack - LAZYSET(A.reagents.reagent_data, /decl/material/liquid/water, list(DATA_WATER_HOLINESS = TRUE)) + REAGENT_SET_DATA(A.reagents, /decl/material/liquid/water, list(DATA_WATER_HOLINESS = TRUE)) /obj/item/bible/attackby(obj/item/used_item, mob/user) if(storage?.use_sound) diff --git a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm index 80610b50e2cc..79fa5b45156a 100644 --- a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm +++ b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm @@ -42,7 +42,7 @@ storage?.remove_from_storage(user, cig, null) user.equip_to_slot(cig, slot_wear_mask_str) - reagents.maximum_volume = 5 * contents.len + create_or_update_reagents(5 * contents.len) to_chat(user, SPAN_NOTICE("You take a cigarette out of the pack.")) update_icon() return TRUE diff --git a/code/game/objects/items/weapons/storage/med_pouch.dm b/code/game/objects/items/weapons/storage/med_pouch.dm index a24f2ff187cb..0bb25ee3ac04 100644 --- a/code/game/objects/items/weapons/storage/med_pouch.dm +++ b/code/game/objects/items/weapons/storage/med_pouch.dm @@ -198,36 +198,36 @@ Single Use Emergency Pouches /obj/item/chems/pill/pouch_pill/Initialize(ml, material_key) . = ..() - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) log_warning("[log_info_line(src)] was deleted for containing no reagents during init!") return INITIALIZE_HINT_QDEL if(reagents?.get_primary_reagent_name() && !_reagent_name) - _reagent_name = "emergency [reagents.get_primary_reagent_name()] pill ([reagents.total_volume]u)" + _reagent_name = "emergency [reagents.get_primary_reagent_name()] pill ([REAGENT_TOTAL_VOLUME(reagents)]u)" if(_reagent_name) SetName(_reagent_name) /obj/item/chems/pill/pouch_pill/stabilizer/populate_reagents() - add_to_reagents(/decl/material/liquid/stabilizer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/stabilizer, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/pill/pouch_pill/antitoxins/populate_reagents() - add_to_reagents(/decl/material/liquid/antitoxins, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antitoxins, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/pill/pouch_pill/oxy_meds/populate_reagents() - add_to_reagents(/decl/material/liquid/oxy_meds, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/oxy_meds, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/pill/pouch_pill/painkillers/populate_reagents() - add_to_reagents(/decl/material/liquid/painkillers, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/painkillers, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/pill/pouch_pill/brute_meds/populate_reagents() - add_to_reagents(/decl/material/liquid/brute_meds, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/brute_meds, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/pill/pouch_pill/burn_meds/populate_reagents() - add_to_reagents(/decl/material/liquid/burn_meds, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/burn_meds, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() // Injectors diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index eb8b3a9e3a8b..7a8a2308bde2 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -66,7 +66,7 @@ update_held_icon() /obj/item/telebaton/on_update_icon() - if(coating?.total_volume || blood_DNA) + if(REAGENT_TOTAL_VOLUME(coating) || blood_DNA) generate_coating_overlay(TRUE) // Force recheck. . = ..() if(on) diff --git a/code/game/objects/items/weapons/towels.dm b/code/game/objects/items/weapons/towels.dm index 623042a6c029..087980586f43 100644 --- a/code/game/objects/items/weapons/towels.dm +++ b/code/game/objects/items/weapons/towels.dm @@ -27,7 +27,7 @@ // Does not rely on ATOM_IS_OPEN_CONTAINER because we want to be able to pour in but not out. /obj/item/towel/can_be_poured_into(atom/source) - return (reagents?.maximum_volume > 0) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) /obj/item/towel/proc/update_material_description() if(!istype(material) || !(material_alteration & MAT_FLAG_ALTERATION_DESC)) @@ -39,9 +39,11 @@ /obj/item/towel/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - if(reagents?.total_volume && distance <= 1) + var/total_vol = REAGENT_TOTAL_VOLUME(reagents) + if(total_vol && distance <= 1) + var/max_vol = REAGENT_MAXIMUM_VOLUME(reagents) var/liquid_adjective = "damp" - switch(reagents.total_volume / reagents.maximum_volume) + switch(total_vol / max_vol) if(0 to 0.1) return // not enough to even bother worrying about if(0.4 to 0.6) @@ -61,13 +63,14 @@ // Slowly dry out. /obj/item/towel/Process() - if(reagents?.total_volume) - reagents.remove_any(max(MINIMUM_CHEMICAL_VOLUME, CHEMS_QUANTIZE(reagents.total_volume * 0.05))) - if(!reagents?.total_volume) + var/total_volume = REAGENT_TOTAL_VOLUME(reagents) + if(total_volume) + reagents.remove_any(max(MINIMUM_CHEMICAL_VOLUME, CHEMS_QUANTIZE(total_volume * 0.05))) + if(!REAGENT_TOTAL_VOLUME(reagents)) return PROCESS_KILL /obj/item/towel/update_name() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) if(!REAGENTS_FREE_SPACE(reagents)) name_prefix = "waterlogged" else @@ -80,7 +83,7 @@ if(!(. = ..())) return update_name() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) if(!is_processing) START_PROCESSING(SSobj, src) else if(is_processing) @@ -93,17 +96,17 @@ /obj/item/towel/proc/dry_mob(mob/living/target, mob/living/user) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - var/reagent_space = reagents.maximum_volume - reagents.total_volume + var/reagent_space = REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(reagents) if(reagent_space <= 0) to_chat(user, SPAN_WARNING("\The [src] is too saturated to dry [user == target ? "yourself" : "\the [target]"] off effectively.")) else var/decl/pronouns/pronouns = target.get_pronouns() var/datum/reagents/touching_reagents = target.get_contact_reagents() - if(!touching_reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(touching_reagents)) to_chat(user, SPAN_WARNING("[user == target ? "You are" : "\The [target] [pronouns.is]"] already dry.")) else user.visible_message(SPAN_NOTICE("\The [user] uses \the [src] to towel [user == target ? pronouns.self : "\the [target]"] dry.")) - touching_reagents.trans_to(src, min(touching_reagents.total_volume, reagent_space)) + touching_reagents.trans_to(src, min(REAGENT_TOTAL_VOLUME(touching_reagents), reagent_space)) playsound(user, 'sound/weapons/towelwipe.ogg', 25, 1) return TRUE @@ -142,10 +145,11 @@ variability = 70 // 30-170%, clamped to be 30-100% for(var/obj/item/target in targets) var/datum/reagents/target_coating = target.coating - if(!target_coating?.total_volume) + var/coating_volume = REAGENT_TOTAL_VOLUME(target_coating) + if(!coating_volume) continue var/fraction_cleaned = clamp(CHEMS_QUANTIZE(rand(100 - variability, 100 + variability) / 100), 0, 100) - target.transfer_coating_to(src, fraction_cleaned * target_coating.total_volume) + target.transfer_coating_to(src, fraction_cleaned * coating_volume) if(!REAGENTS_FREE_SPACE(reagents)) break diff --git a/code/game/objects/items/welding/weldbackpack.dm b/code/game/objects/items/welding/weldbackpack.dm index 82db07b9617a..3dba78bfd1ff 100644 --- a/code/game/objects/items/welding/weldbackpack.dm +++ b/code/game/objects/items/welding/weldbackpack.dm @@ -85,14 +85,15 @@ if(user.check_intent(I_FLAG_HARM)) if(standard_splash_mob(user, O)) return TRUE - if(reagents && reagents.total_volume) + var/total_vol = REAGENT_TOTAL_VOLUME(reagents) + if(reagents && total_vol) to_chat(user, SPAN_DANGER("You splash the contents of \the [src] onto \the [O].")) - reagents.splash(O, reagents.total_volume) + reagents.splash(O, total_vol) return TRUE return ..() /obj/item/chems/weldpack/populate_reagents() - add_to_reagents(/decl/material/liquid/fuel, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/fuel, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/weldpack/Initialize(ml, material_key) if(ispath(welder)) @@ -127,20 +128,20 @@ if(!tool.tank) to_chat(user, SPAN_WARNING("\The [tool] has no tank attached!")) return TRUE - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_WARNING("\The [src] is empty!")) return TRUE - reagents.trans_to_obj(tool.tank, tool.tank.reagents.maximum_volume) + reagents.trans_to_obj(tool.tank, REAGENT_MAXIMUM_VOLUME(tool.tank.reagents)) to_chat(user, SPAN_NOTICE("You refuel \the [used_item].")) playsound(src, 'sound/effects/refill.ogg', 50, TRUE, -6) return TRUE else if(istype(used_item, /obj/item/chems/welder_tank)) - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_WARNING("\The [src] is empty!")) return TRUE var/obj/item/chems/welder_tank/tank = used_item - reagents.trans_to_obj(tank, tank.reagents.maximum_volume) + reagents.trans_to_obj(tank, REAGENT_MAXIMUM_VOLUME(tank.reagents)) to_chat(user, SPAN_NOTICE("You refuel \the [used_item].")) playsound(src, 'sound/effects/refill.ogg', 50, TRUE, -6) return TRUE @@ -180,7 +181,7 @@ /obj/item/chems/weldpack/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "[html_icon(src)] [reagents.total_volume] unit\s of fuel left!" + . += "[html_icon(src)] [REAGENT_TOTAL_VOLUME(reagents)] unit\s of fuel left!" /obj/item/chems/weldpack/dropped(mob/user) . = ..() diff --git a/code/game/objects/items/welding/weldingtool.dm b/code/game/objects/items/welding/weldingtool.dm index 720957f0b6d0..c02b83119e9c 100644 --- a/code/game/objects/items/welding/weldingtool.dm +++ b/code/game/objects/items/welding/weldingtool.dm @@ -169,7 +169,7 @@ /obj/item/weldingtool/fluid_act(var/datum/reagents/fluids) ..() - if(!QDELETED(src) && fluids?.total_volume && welding && !waterproof) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids) && welding && !waterproof) var/turf/location = get_turf(src) if(location) location.hotspot_expose(WELDING_TOOL_HOTSPOT_TEMP_ACTIVE, 50, 1) diff --git a/code/game/objects/items/welding/weldingtool_tank.dm b/code/game/objects/items/welding/weldingtool_tank.dm index ed87e2be8489..ae40b17cb8fd 100644 --- a/code/game/objects/items/welding/weldingtool_tank.dm +++ b/code/game/objects/items/welding/weldingtool_tank.dm @@ -20,17 +20,18 @@ var/lit_force = 11 /obj/item/chems/welder_tank/populate_reagents() - add_to_reagents(/decl/material/liquid/fuel, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/fuel, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/welder_tank/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance > 1) return - if(reagents.total_volume <= 0) + var/total_vol = REAGENT_TOTAL_VOLUME(reagents) + if(total_vol <= 0) . += SPAN_WARNING("It is empty.") else - . += "It contains [reagents.total_volume] units of liquid." - . += "It can hold up to [reagents.maximum_volume] units." + . += "It contains [total_vol] units of liquid." + . += "It can hold up to [REAGENT_MAXIMUM_VOLUME(reagents)] units." /obj/item/chems/welder_tank/afterattack(obj/O, mob/user, proximity, click_parameters) if (!ATOM_IS_OPEN_CONTAINER(src) || !proximity) @@ -44,9 +45,10 @@ if(user.check_intent(I_FLAG_HARM)) if(standard_splash_mob(user, O)) return TRUE - if(reagents && reagents.total_volume) + var/total_vol = REAGENT_TOTAL_VOLUME(reagents) + if(reagents && total_vol) to_chat(user, SPAN_DANGER("You splash the contents of \the [src] onto \the [O].")) - reagents.splash(O, reagents.total_volume) + reagents.splash(O, total_vol) return TRUE return ..() @@ -136,7 +138,7 @@ return ..() /obj/item/chems/welder_tank/experimental/Process() - if(REAGENT_VOLUME(reagents, /decl/material/liquid/fuel) < reagents.maximum_volume) + if(REAGENT_VOLUME(reagents, /decl/material/liquid/fuel) < REAGENT_MAXIMUM_VOLUME(reagents)) var/gen_amount = ((world.time-last_gen)/25) add_to_reagents(/decl/material/liquid/fuel, gen_amount) last_gen = world.time \ No newline at end of file diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 704ab61cde54..be2c2102ae50 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -86,15 +86,16 @@ . += "\The [src] [structure_pronouns.has] been [paint_verb]." if(distance <= 2 && !isnull(get_possible_reagent_transfer_amounts()) && reagents) . += SPAN_NOTICE("It contains:") - if(LAZYLEN(reagents.reagent_volumes)) - for(var/decl/material/reagent as anything in reagents.liquid_volumes) + var/reagent_volumes = REAGENT_VOLUMES(reagents) + if(LAZYLEN(reagent_volumes)) + for(var/decl/material/reagent as anything in REAGENT_LIQUID_VOLUMES(reagents)) . += SPAN_NOTICE("[LIQUID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID)].") - for(var/decl/material/reagent as anything in reagents.solid_volumes) + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(reagents)) . += SPAN_NOTICE("[SOLID_VOLUME(reagents, reagent)] unit\s of [reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)].") else . += SPAN_NOTICE("Nothing.") - if(reagents.maximum_volume) - . += "It may contain up to [reagents.maximum_volume] unit\s." + if(REAGENT_MAXIMUM_VOLUME(reagents)) + . += "It may contain up to [REAGENT_MAXIMUM_VOLUME(reagents)] unit\s." /obj/structure/get_examine_hints(mob/user, distance, infix, suffix) . = ..() diff --git a/code/game/objects/structures/barrels/barrel.dm b/code/game/objects/structures/barrels/barrel.dm index c14f9007882d..25f8d585eadf 100644 --- a/code/game/objects/structures/barrels/barrel.dm +++ b/code/game/objects/structures/barrels/barrel.dm @@ -26,13 +26,13 @@ // Overrides due to wonky reagent_dispeners opencontainer flag handling. /obj/structure/reagent_dispensers/barrel/can_be_poured_from(mob/user, atom/target) - return (reagents?.maximum_volume > 0) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) /obj/structure/reagent_dispensers/barrel/can_be_poured_into(mob/user, atom/target) - return (reagents?.maximum_volume > 0) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) // Override to skip open container check. /obj/structure/reagent_dispensers/barrel/can_drink_from(mob/user) - return reagents?.total_volume && user.check_has_mouth() + return REAGENT_TOTAL_VOLUME(reagents) && user.check_has_mouth() /obj/structure/reagent_dispensers/barrel/Initialize() if(ispath(metal_material)) @@ -73,7 +73,7 @@ // Add lid/reagents overlay/lid metal. if(show_liquid_contents && ATOM_IS_OPEN_CONTAINER(src)) if(reagents) - var/overlay_amount = NONUNIT_CEILING(reagents.total_liquid_volume / reagents.maximum_volume * 100, 10) + var/overlay_amount = NONUNIT_CEILING(REAGENT_TOTAL_LIQUID_VOLUME(reagents) / REAGENT_MAXIMUM_VOLUME(reagents) * 100, 10) var/image/filling_overlay = overlay_image(icon, "[icon_state]-[overlay_amount]", reagents.get_color(), RESET_COLOR | RESET_ALPHA) add_overlay(filling_overlay) add_overlay(overlay_image(icon, "[icon_state]-lidopen", material?.color, RESET_COLOR)) @@ -89,7 +89,7 @@ /obj/structure/reagent_dispensers/barrel/get_standard_interactions(var/mob/user) . = ..() - if(reagents?.maximum_volume) + if(REAGENT_MAXIMUM_VOLUME(reagents)) LAZYADD(., global._reagent_interactions) // Disambiguation actions, since barrels can have several different potential interactions for @@ -120,16 +120,16 @@ /obj/structure/reagent_dispensers/barrel/ebony/water/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/barrel/ebony/beer/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/beer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/beer, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/barrel/ebony/wine/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/wine, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/wine, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/barrel/ebony/oil/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/oil, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/oil, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/game/objects/structures/barrels/cask.dm b/code/game/objects/structures/barrels/cask.dm index 603cf2d64335..7143cc94a952 100644 --- a/code/game/objects/structures/barrels/cask.dm +++ b/code/game/objects/structures/barrels/cask.dm @@ -30,16 +30,16 @@ /obj/structure/reagent_dispensers/barrel/cask/ebony/water/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/barrel/cask/ebony/beer/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/beer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/beer, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/barrel/cask/ebony/wine/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/wine, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/wine, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/barrel/cask/ebony/oil/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/oil, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/oil, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/game/objects/structures/beds/rollerbed.dm b/code/game/objects/structures/beds/rollerbed.dm index 39ad505e97d4..d7e7f102ec6d 100644 --- a/code/game/objects/structures/beds/rollerbed.dm +++ b/code/game/objects/structures/beds/rollerbed.dm @@ -24,7 +24,7 @@ icon_state = "down" if(beaker?.reagents) var/image/iv = image(icon, "iv[iv_attached]") - var/percentage = round((beaker.reagents.total_volume / max(beaker.reagents.maximum_volume, 1)) * 100, 25) + var/percentage = round((REAGENT_TOTAL_VOLUME(beaker.reagents) / max(REAGENT_MAXIMUM_VOLUME(beaker.reagents), 1)) * 100, 25) var/image/filling = image(icon, "iv_filling[percentage]") filling.color = beaker.reagents.get_color() iv.overlays += filling @@ -74,7 +74,7 @@ if(SSobj.times_fired % 2) return - if(beaker.reagents?.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(beaker.reagents) > 0) beaker.reagents.trans_to_mob(buckled_mob, beaker.amount_per_transfer_from_this, CHEM_INJECT) queue_icon_update() diff --git a/code/game/objects/structures/chemistry/filter_stand.dm b/code/game/objects/structures/chemistry/filter_stand.dm index b236e3f3dd29..3bffecf5ffe2 100644 --- a/code/game/objects/structures/chemistry/filter_stand.dm +++ b/code/game/objects/structures/chemistry/filter_stand.dm @@ -84,17 +84,17 @@ // Pouring directly into the filter via attackby() is not working, so we just dump our reagents into our filter or the turf. /obj/structure/filter_stand/on_reagent_change() . = ..() - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) return - if(filter?.reagents?.maximum_volume) - var/taking = min(reagents?.total_volume, REAGENTS_FREE_SPACE(filter.reagents)) + if(REAGENT_MAXIMUM_VOLUME(filter?.reagents)) + var/taking = min(REAGENT_TOTAL_VOLUME(reagents), REAGENTS_FREE_SPACE(filter.reagents)) if(taking > 0) reagents.trans_to_holder(filter.reagents, taking) - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) return var/turf/my_turf = get_turf(src) if(istype(my_turf)) - reagents.trans_to_turf(my_turf, reagents.total_volume) + reagents.trans_to_turf(my_turf, REAGENT_TOTAL_VOLUME(reagents)) /obj/item/chems/filter name = "filter" @@ -109,7 +109,7 @@ /obj/item/chems/filter/on_reagent_change() . = ..() - if(reagents?.total_liquid_volume) + if(REAGENT_TOTAL_LIQUID_VOLUME(reagents)) if(!is_processing) START_PROCESSING(SSobj, src) else @@ -117,11 +117,11 @@ STOP_PROCESSING(SSobj, src) /obj/item/chems/filter/Process() - if(!reagents?.total_liquid_volume) + if(!REAGENT_TOTAL_LIQUID_VOLUME(reagents)) return PROCESS_KILL var/dumping = 0 - var/dripping = min(reagents.total_liquid_volume, rand(3,5)) + var/dripping = min(REAGENT_TOTAL_LIQUID_VOLUME(reagents), rand(3,5)) var/obj/structure/filter_stand/stand = loc if(!istype(stand) || stand.filter != src || !stand.loaded?.reagents) dumping = dripping diff --git a/code/game/objects/structures/compost.dm b/code/game/objects/structures/compost.dm index 5d9c3dd623ed..b1929ed281e6 100644 --- a/code/game/objects/structures/compost.dm +++ b/code/game/objects/structures/compost.dm @@ -84,8 +84,9 @@ var/global/const/COMPOST_WORM_HUNGER_FACTOR = MINIMUM_CHEMICAL_VOLUME /obj/structure/reagent_dispensers/compost_bin/physically_destroyed() dump_contents() - if(reagents) - reagents.trans_to(loc, reagents.total_volume) + var/reagent_volume = REAGENT_TOTAL_VOLUME(reagents) + if(reagent_volume) + reagents.trans_to(loc, reagent_volume) return ..() /obj/structure/reagent_dispensers/compost_bin/attackby(obj/item/used_item, mob/user) @@ -158,8 +159,8 @@ var/global/const/COMPOST_WORM_HUNGER_FACTOR = MINIMUM_CHEMICAL_VOLUME for(var/obj/item/thing in composting.get_contained_external_atoms()) thing.forceMove(src) - if(composting.reagents?.total_volume) - composting.reagents.trans_to_holder(reagents, composting.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(composting.reagents)) + composting.reagents.trans_to_holder(reagents, REAGENT_TOTAL_VOLUME(composting.reagents)) composting.reagents.clear_reagents() composting.clear_matter() @@ -174,12 +175,12 @@ var/global/const/COMPOST_WORM_HUNGER_FACTOR = MINIMUM_CHEMICAL_VOLUME remains.update_primary_material() // Digest reagents. - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(istype(reagent, /decl/material/liquid/fertilizer)) continue if(!reagent.compost_value) continue - var/clamped_worm_drink_amount = min(round(worm_eat_amount * REAGENT_UNITS_PER_MATERIAL_UNIT), reagents.reagent_volumes[reagent]) + var/clamped_worm_drink_amount = min(round(worm_eat_amount * REAGENT_UNITS_PER_MATERIAL_UNIT), REAGENT_VOLUME(reagents, reagent)) reagents.add_reagent(/decl/material/liquid/fertilizer/compost, max(1, round(clamped_worm_drink_amount * reagent.compost_value))) reagents.remove_reagent(reagent, clamped_worm_drink_amount) break diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index 273c685709aa..48f1205c014c 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -126,8 +126,8 @@ /obj/structure/fire_source/fluid_act(datum/reagents/fluids) . = ..() - if(!QDELETED(src) && fluids?.total_volume && reagents) - var/transfer = min(reagents.maximum_volume - reagents.total_volume, max(max(1, round(fluids.total_volume * 0.25)))) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids) && reagents) + var/transfer = min(REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(reagents), max(max(1, round(REAGENT_TOTAL_VOLUME(fluids) * 0.25)))) if(transfer > 0) fluids.trans_to_obj(src, transfer) @@ -363,13 +363,13 @@ /obj/structure/fire_source/on_reagent_change() if(!(. = ..())) return - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) var/do_steam = FALSE var/datum/gas_mixture/our_air = return_air() var/ambient_pressure = our_air ? our_air.return_pressure() : ONE_ATMOSPHERE var/list/waste = list() - for(var/decl/material/reagent as anything in reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.accelerant_value <= FUEL_VALUE_SUPPRESSANT && reagent.phase_at_temperature(get_effective_burn_temperature(), ambient_pressure) == MAT_PHASE_GAS) do_steam = TRUE diff --git a/code/game/objects/structures/fishtanks.dm b/code/game/objects/structures/fishtanks.dm index a8855275998b..c9ab4bc5f72e 100644 --- a/code/game/objects/structures/fishtanks.dm +++ b/code/game/objects/structures/fishtanks.dm @@ -53,7 +53,7 @@ var/global/list/fishtank_cache = list() /obj/structure/glass_tank/populate_reagents() if(fill_type) - add_to_reagents(fill_type, reagents.maximum_volume) + add_to_reagents(fill_type, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/glass_tank/attack_hand(var/mob/user) if(user.check_intent(I_FLAG_HARM)) @@ -77,7 +77,7 @@ var/global/list/fishtank_cache = list() if(paint_color) shard.set_color(paint_color) if(!silent) - if(contents.len || reagents.total_volume) + if(contents.len || REAGENT_TOTAL_VOLUME(reagents)) visible_message(SPAN_DANGER("\The [src] shatters, spilling its contents everywhere!")) else visible_message(SPAN_DANGER("\The [src] shatters!")) @@ -90,8 +90,8 @@ var/global/list/fishtank_cache = list() /obj/structure/glass_tank/dump_contents(atom/forced_loc = loc, mob/user) . = ..() var/turf/T = get_turf(forced_loc) - if(reagents?.total_volume && T) - reagents.trans_to_turf(T, reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents) && T) + reagents.trans_to_turf(T, REAGENT_TOTAL_VOLUME(reagents)) var/global/list/global/aquarium_states_and_layers = list( "b" = FLY_LAYER - 0.02, @@ -119,7 +119,7 @@ var/global/list/global/aquarium_states_and_layers = list( tank_overlay.cut_overlays() for(var/i = 1 to 4) for(var/key_mod in global.aquarium_states_and_layers) - if(key_mod == "w" && (!reagents || !reagents.total_volume)) + if(key_mod == "w" && (!reagents || !REAGENT_TOTAL_VOLUME(reagents))) continue var/cache_key = "[c_states[i]][key_mod]-[i]" if(!global.fishtank_cache[cache_key]) diff --git a/code/game/objects/structures/fountain.dm b/code/game/objects/structures/fountain.dm index d4287c844c53..da68645ad82a 100644 --- a/code/game/objects/structures/fountain.dm +++ b/code/game/objects/structures/fountain.dm @@ -117,7 +117,7 @@ light_power = null /obj/structure/fountain/mundane/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) //Don't give free water when building one + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) //Don't give free water when building one /obj/structure/fountain/mundane/attack_hand(mob/user) if(user.check_intent(I_FLAG_HARM)) diff --git a/code/game/objects/structures/iv_drip.dm b/code/game/objects/structures/iv_drip.dm index e6dbe8176ab3..661a79f2e52b 100644 --- a/code/game/objects/structures/iv_drip.dm +++ b/code/game/objects/structures/iv_drip.dm @@ -38,8 +38,8 @@ add_overlay(base) if(beaker?.reagents) - var/percent = round((beaker.reagents.total_volume / beaker.reagents.maximum_volume) * 100) - if(beaker.reagents.total_volume) + var/percent = round((REAGENT_TOTAL_VOLUME(beaker.reagents) / REAGENT_MAXIMUM_VOLUME(beaker.reagents)) * 100) + if(REAGENT_TOTAL_VOLUME(beaker.reagents)) var/mutable_appearance/filling = mutable_appearance(icon, "reagent") switch(percent) if(0) @@ -119,11 +119,11 @@ return if(mode) // Give blood - if(beaker.reagents?.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(beaker.reagents) > 0) beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_INJECT) queue_icon_update() else // Take blood - var/amount = beaker.reagents.maximum_volume - beaker.reagents.total_volume + var/amount = REAGENT_MAXIMUM_VOLUME(beaker.reagents) - REAGENT_TOTAL_VOLUME(beaker.reagents) amount = min(amount, 4) if(amount == 0) // If the beaker is full, ping @@ -192,8 +192,8 @@ . += "The IV drip is [mode ? "injecting" : "taking blood"]." . += "It is set to transfer [transfer_amount]u of chemicals per cycle." if(beaker) - if(beaker.reagents?.total_volume) - . += SPAN_NOTICE("Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid.") + if(REAGENT_TOTAL_VOLUME(beaker.reagents)) + . += SPAN_NOTICE("Attached is \a [beaker] with [REAGENT_TOTAL_VOLUME(beaker.reagents)] units of liquid.") else . += SPAN_NOTICE("Attached is an empty [beaker].") else diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 7f01952bc4b6..75f4e5f78586 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -18,7 +18,7 @@ /obj/structure/janitorialcart/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) - . += "\The [src] [html_icon(src)] contains [reagents.total_volume] unit\s of liquid!" + . += "\The [src] [html_icon(src)] contains [REAGENT_TOTAL_VOLUME(reagents)] unit\s of liquid!" /obj/structure/janitorialcart/attackby(obj/item/used_item, mob/user) if(istype(used_item, /obj/item/bag/trash) && !mybag) @@ -31,11 +31,11 @@ return TRUE else if(istype(used_item, /obj/item/mop)) - if(used_item.reagents.total_volume < used_item.reagents.maximum_volume) //if it's not completely soaked we assume they want to wet it, otherwise store it - if(reagents.total_volume < 1) + if(REAGENT_TOTAL_VOLUME(used_item.reagents) < REAGENT_MAXIMUM_VOLUME(used_item.reagents)) //if it's not completely soaked we assume they want to wet it, otherwise store it + if(REAGENT_TOTAL_VOLUME(reagents) < 1) to_chat(user, "[src] is out of water!") else - reagents.trans_to_obj(used_item, used_item.reagents.maximum_volume) + reagents.trans_to_obj(used_item, REAGENT_MAXIMUM_VOLUME(used_item.reagents)) to_chat(user, "You wet [used_item] in [src].") playsound(loc, 'sound/effects/slosh.ogg', 25, 1) return TRUE @@ -209,14 +209,14 @@ /obj/structure/janicart/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) - . += "[html_icon(src)] This [callme] contains [reagents.total_volume] unit\s of water!" + . += "[html_icon(src)] This [callme] contains [REAGENT_TOTAL_VOLUME(reagents)] unit\s of water!" if(mybag) . += "\A [mybag] is hanging on the [callme]." /obj/structure/janicart/attackby(obj/item/used_item, mob/user) if(istype(used_item, /obj/item/mop)) - if(reagents.total_volume > 1) + if(REAGENT_TOTAL_VOLUME(reagents) > 1) reagents.trans_to_obj(used_item, 2) to_chat(user, SPAN_NOTICE("You wet [used_item] in the [callme].")) playsound(loc, 'sound/effects/slosh.ogg', 25, 1) diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index 57cc6d0e03f6..8e37b95f89f6 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -11,11 +11,11 @@ /obj/structure/mopbucket/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) - . += "\The [src] [html_icon(src)] contains [reagents.total_volume] unit\s of water!" + . += "\The [src] [html_icon(src)] contains [REAGENT_TOTAL_VOLUME(reagents)] unit\s of water!" /obj/structure/mopbucket/attackby(obj/item/used_item, mob/user) if(istype(used_item, /obj/item/mop)) - if(reagents.total_volume < 1) + if(REAGENT_TOTAL_VOLUME(reagents) < 1) to_chat(user, SPAN_WARNING("\The [src] is out of water!")) else if(REAGENTS_FREE_SPACE(used_item.reagents) >= 5) reagents.trans_to_obj(used_item, 5) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 76b4a59db265..b3ea8979b730 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -92,7 +92,7 @@ var/global/list/hygiene_props = list() visible_message("\The [src] gurgles and overflows!") next_gurgle = world.time + 80 playsound(T, pick(SSfluids.gurgles), 50, 1) - var/adding = min(flood_amt-T?.reagents?.total_volume, rand(30,50)*clogged) + var/adding = min(flood_amt - REAGENT_TOTAL_VOLUME(T?.reagents), rand(30,50)*clogged) if(adding > 0) T.add_to_reagents(/decl/material/liquid/water, adding) @@ -336,7 +336,7 @@ var/global/list/hygiene_props = list() add_to_reagents(/decl/material/liquid/water, REAGENTS_FREE_SPACE(reagents)) if(world.time >= next_wash) next_wash = world.time + (10 SECONDS) - reagents.splash(get_turf(src), reagents.total_volume, max_spill = 0) + reagents.splash(get_turf(src), REAGENT_TOTAL_VOLUME(reagents), max_spill = 0) /obj/structure/hygiene/shower/proc/process_heat(mob/living/M) if(!on || !istype(M)) @@ -370,7 +370,7 @@ var/global/list/hygiene_props = list() . = ..() if(!. && isitem(dropping) && ATOM_IS_OPEN_CONTAINER(dropping)) var/obj/item/thing = dropping - if(thing.reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(thing.reagents) <= 0) to_chat(usr, SPAN_WARNING("\The [thing] is empty.")) else visible_message(SPAN_NOTICE("\The [user] tips the contents of \the [thing] into \the [src].")) diff --git a/code/game/objects/structures/well.dm b/code/game/objects/structures/well.dm index 9480d5721d51..0faa71321e7c 100644 --- a/code/game/objects/structures/well.dm +++ b/code/game/objects/structures/well.dm @@ -20,12 +20,12 @@ // Override to skip open container check. /obj/structure/reagent_dispensers/well/can_drink_from(mob/user) - return reagents?.total_volume && user.check_has_mouth() + return REAGENT_TOTAL_VOLUME(reagents) && user.check_has_mouth() /obj/structure/reagent_dispensers/well/populate_reagents() . = ..() if(auto_refill) - add_to_reagents(auto_refill, reagents.maximum_volume) + add_to_reagents(auto_refill, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/well/Destroy() if(is_processing) @@ -34,7 +34,7 @@ /obj/structure/reagent_dispensers/well/on_update_icon() . = ..() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) add_overlay(overlay_image(icon, "[icon_state]-fluid", reagents.get_color(), (RESET_COLOR | RESET_ALPHA))) if(istype(reinf_material)) // reinf_material -> roof and posts, at this point in time var/image/roof_image = overlay_image(icon, "[icon_state]-roof", reinf_material.color, RESET_COLOR | RESET_ALPHA | KEEP_APART) @@ -50,25 +50,25 @@ // Overrides due to wonky reagent_dispeners opencontainer flag handling. /obj/structure/reagent_dispensers/well/can_be_poured_from(mob/user, atom/target) - return (reagents?.maximum_volume > 0) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) /obj/structure/reagent_dispensers/well/can_be_poured_into(mob/user, atom/target) - return (reagents?.maximum_volume > 0) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) /obj/structure/reagent_dispensers/well/get_standard_interactions(var/mob/user) . = ..() - if(reagents?.maximum_volume) + if(REAGENT_MAXIMUM_VOLUME(reagents)) LAZYADD(., global._reagent_interactions) /obj/structure/reagent_dispensers/well/Process() if(!reagents || !auto_refill) // if we're full, we only stop at the end of the proc; we need to check for contaminants first return PROCESS_KILL var/amount_to_add = rand(5, 10) - if(length(reagents.reagent_volumes) > 1) // we have impurities! + if(length(REAGENT_VOLUMES(reagents)) > 1) // we have impurities! reagents.remove_any(amount_to_add, defer_update = TRUE, skip_reagents = list(auto_refill)) // defer update until the add_reagent call below - if(reagents.total_volume < reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(reagents) < REAGENT_MAXIMUM_VOLUME(reagents)) reagents.add_reagent(auto_refill, amount_to_add) return // don't stop processing - else if(length(reagents.reagent_volumes) == 1 && reagents.get_primary_reagent_type() == auto_refill) + else if(length(REAGENT_VOLUMES(reagents)) == 1 && reagents.get_primary_reagent_type() == auto_refill) // only one reagent and it's our auto_refill, our work is done here return PROCESS_KILL // if we get here, it means we have a full well with contaminants, so we keep processing diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index 001a0c33ce90..bdce5b2859e8 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -14,7 +14,7 @@ uid = "floor_mud" /decl/flooring/mud/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(!target.reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(target.reagents)) if(target.get_topmost_flooring() == src) target.set_flooring(/decl/flooring/dry_mud) else if(target.get_base_flooring() == src) diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 78e5b5f0b486..881da81bdcce 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -25,7 +25,7 @@ . = max(., 0) /decl/flooring/snow/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(!target.reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(target.reagents)) if(target.get_topmost_flooring() == src) target.remove_flooring(src) else if(target.get_base_flooring() == src) diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index d31643ba2deb..3982c3dab82d 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -67,8 +67,8 @@ /turf/floor/proc/fill_to_zero_height() var/my_height = get_physical_height() - if(fill_reagent_type && my_height < 0 && (!reagents || !QDELING(reagents)) && reagents?.total_volume < abs(my_height)) - var/reagents_to_add = abs(my_height) - reagents?.total_volume + if(fill_reagent_type && my_height < 0 && (!reagents || !QDELING(reagents)) && REAGENT_TOTAL_VOLUME(reagents) < abs(my_height)) + var/reagents_to_add = abs(my_height) - REAGENT_TOTAL_VOLUME(reagents) add_to_reagents(fill_reagent_type, reagents_to_add, phase = MAT_PHASE_LIQUID) /turf/floor/can_climb_from_below(var/mob/climber) diff --git a/code/game/turfs/floors/floor_acts.dm b/code/game/turfs/floors/floor_acts.dm index b231da61241b..ee60bcbf367f 100644 --- a/code/game/turfs/floors/floor_acts.dm +++ b/code/game/turfs/floors/floor_acts.dm @@ -27,7 +27,7 @@ /turf/floor/fluid_act(var/datum/reagents/fluids) . = ..() - if(!QDELETED(fluids) && fluids.total_volume) + if(!QDELETED(fluids) && REAGENT_TOTAL_VOLUME(fluids)) for(var/decl/flooring/flooring in get_all_flooring()) if(flooring.fluid_act(src, fluids)) return diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 36fa7f51f578..33828543cd5d 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -211,8 +211,8 @@ if(weather) . += weather.get_movement_delay(return_air(), travel_dir) // TODO: check user species webbed feet, wearing swimming gear - if(!get_supporting_platform() && reagents?.total_volume > FLUID_PUDDLE) - . += (reagents.total_volume > FLUID_SHALLOW) ? 6 : 3 + if(!get_supporting_platform() && REAGENT_TOTAL_VOLUME(reagents) > FLUID_PUDDLE) + . += (REAGENT_TOTAL_VOLUME(reagents) > FLUID_SHALLOW) ? 6 : 3 /turf/attack_hand(mob/user) @@ -354,8 +354,8 @@ var/mob/mover_mob = mover if(!istype(mover_mob) || (!mover_mob.throwing && !mover_mob.can_overcome_gravity())) var/turf/old_turf = mover.loc - var/old_height = old_turf.get_physical_height() + old_turf.reagents?.total_volume - var/current_height = get_physical_height() + reagents?.total_volume + var/old_height = old_turf.get_physical_height() + REAGENT_TOTAL_VOLUME(old_turf.reagents) + var/current_height = get_physical_height() + REAGENT_TOTAL_VOLUME(reagents) if(abs(current_height - old_height) > FLUID_SHALLOW) if(current_height > old_height) return 0 diff --git a/code/game/turfs/turf_enter.dm b/code/game/turfs/turf_enter.dm index a83d87a855fe..cb1849868ab6 100644 --- a/code/game/turfs/turf_enter.dm +++ b/code/game/turfs/turf_enter.dm @@ -33,8 +33,8 @@ if(isturf(old_loc) && has_gravity() && A.can_fall() && !isnull(platform) && !(weakref(A) in skip_height_fall_for)) var/turf/old_turf = old_loc - var/old_height = old_turf.get_physical_height() + old_turf.reagents?.total_volume - var/current_height = get_physical_height() + reagents?.total_volume + var/old_height = old_turf.get_physical_height() + REAGENT_TOTAL_VOLUME(old_turf.reagents) + var/current_height = get_physical_height() + REAGENT_TOTAL_VOLUME(reagents) var/height_difference = abs(current_height - old_height) if(current_height < old_height && height_difference > FLUID_SHALLOW) diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index bb59234398df..fe248f689773 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -30,7 +30,7 @@ continue LAZYDISTINCTADD(spread_into_neighbors, neighbor) if(length(spread_into_neighbors)) - var/spreading = round(reagents.total_volume / length(spread_into_neighbors)) + var/spreading = round(REAGENT_TOTAL_VOLUME(reagents) / length(spread_into_neighbors)) if(spreading > 0) for(var/turf/spread_into_turf as anything in spread_into_neighbors) reagents.trans_to_turf(spread_into_turf, spreading) @@ -83,8 +83,8 @@ return FLUID_MAX_DEPTH var/obj/structure/glass_tank/aquarium = locate() in contents if(aquarium) - return aquarium.reagents?.total_volume * TANK_WATER_MULTIPLIER - return reagents?.total_volume || 0 + return REAGENT_TOTAL_VOLUME(aquarium.reagents) * TANK_WATER_MULTIPLIER + return REAGENT_TOTAL_VOLUME(reagents) /turf/proc/show_bubbles() set waitfor = FALSE @@ -102,7 +102,7 @@ T.fluid_update(TRUE) if(flooded) ADD_ACTIVE_FLUID_SOURCE(src) - else if(reagents?.total_volume > FLUID_QDEL_POINT) + else if(REAGENT_TOTAL_VOLUME(reagents) > FLUID_QDEL_POINT) ADD_ACTIVE_FLUID(src) /turf/get_reagents() @@ -119,10 +119,10 @@ /turf/fluid_act(var/datum/reagents/fluids) ..() - if(!QDELETED(src) && fluids?.total_volume) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids)) fluids.touch_turf(src, touch_atoms = FALSE) // Handled in fluid_act() below. // Wet items that are not supported on a platform or such. - var/effective_volume = fluids?.total_volume + var/effective_volume = REAGENT_TOTAL_VOLUME(fluids) if(get_supporting_platform()) // Depth is negative height, hence +=. TODO: positive heights? No idea how to handle that. effective_volume += get_physical_height() @@ -133,7 +133,7 @@ AM.fluid_act(fluids) /turf/proc/remove_fluids(var/amount, var/defer_update) - if(!reagents?.total_liquid_volume) + if(!REAGENT_TOTAL_LIQUID_VOLUME(reagents)) return remove_any_reagents(amount, defer_update = defer_update, removed_phases = MAT_PHASE_LIQUID) if(defer_update && !QDELETED(reagents)) @@ -141,12 +141,12 @@ /turf/proc/transfer_fluids_to(var/turf/target, var/amount, var/defer_update = TRUE) // No flowing of reagents without liquids, but this proc should not be called if liquids are not present regardless. - if(!reagents?.total_liquid_volume) + if(!REAGENT_TOTAL_LIQUID_VOLUME(reagents)) return target.create_or_update_reagents(FLUID_MAX_DEPTH) // We reference total_volume instead of total_liquid_volume here because the maximum volume limits of the turfs still respect solid volumes, and depth is still determined by total volume. - reagents.trans_to_turf(target, min(reagents.total_volume, min(target.reagents.maximum_volume - target.reagents.total_volume, amount)), defer_update = defer_update) + reagents.trans_to_turf(target, min(REAGENT_TOTAL_VOLUME(reagents), min(REAGENT_MAXIMUM_VOLUME(target.reagents) - REAGENT_TOTAL_VOLUME(target.reagents), amount)), defer_update = defer_update) if(defer_update) if(!QDELETED(reagents)) SSfluids.holders_to_update[reagents] = TRUE @@ -159,12 +159,12 @@ vaporize_fuel(air) /turf/proc/vaporize_fuel(datum/gas_mixture/air) - if(!length(reagents?.reagent_volumes) || !istype(air)) + if(!length(REAGENT_VOLUMES(reagents)) || !istype(air)) return var/update_air = FALSE - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.gas_flags & XGM_GAS_FUEL) - var/moles = round(reagents.reagent_volumes[reagent] / REAGENT_UNITS_PER_GAS_MOLE) + var/moles = round(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_GAS_MOLE) if(moles > 0) air.adjust_gas(reagent.type, moles, FALSE) remove_from_reagents(reagent, round(moles * REAGENT_UNITS_PER_GAS_MOLE)) @@ -179,10 +179,10 @@ if(!(. = ..())) return - if(reagents?.total_liquid_volume < FLUID_SLURRY) + if(REAGENT_TOTAL_LIQUID_VOLUME(reagents) < FLUID_SLURRY) dump_solid_reagents() - if(reagents?.total_volume > FLUID_QDEL_POINT) + if(REAGENT_TOTAL_VOLUME(reagents) > FLUID_QDEL_POINT) ADD_ACTIVE_FLUID(src) var/decl/material/primary_reagent = reagents.get_primary_reagent_decl() if(primary_reagent && (REAGENT_VOLUME(reagents, primary_reagent) >= primary_reagent.slippery_amount)) @@ -204,16 +204,17 @@ for(var/checkdir in global.cardinal) var/turf/neighbor = get_step_resolving_mimic(src, checkdir) - if(neighbor?.reagents?.total_volume > FLUID_QDEL_POINT) + if(REAGENT_TOTAL_VOLUME(neighbor?.reagents) > FLUID_QDEL_POINT) ADD_ACTIVE_FLUID(neighbor) /turf/proc/dump_solid_reagents(datum/reagents/solids) if(!istype(solids)) solids = reagents - if(LAZYLEN(solids?.solid_volumes)) + var/solid_volumes = REAGENT_SOLID_VOLUMES(solids) + if(LAZYLEN(solid_volumes)) var/list/matter_list = list() - for(var/decl/material/reagent as anything in solids.solid_volumes) - var/reagent_amount = solids.solid_volumes[reagent] + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(solid_volumes)) + var/reagent_amount = SOLID_VOLUME(solids, reagent) matter_list[reagent.type] = round(reagent_amount/REAGENT_UNITS_PER_MATERIAL_UNIT) solids.remove_reagent(reagent, reagent_amount, defer_update = TRUE, removed_phases = MAT_PHASE_SOLID) diff --git a/code/modules/atmospherics/datum_pipeline.dm b/code/modules/atmospherics/datum_pipeline.dm index 89e598b527ff..ef8b9c372610 100644 --- a/code/modules/atmospherics/datum_pipeline.dm +++ b/code/modules/atmospherics/datum_pipeline.dm @@ -30,7 +30,7 @@ STOP_PROCESSING(SSprocessing, src) QDEL_NULL(network) - if(air?.total_volume || liquid?.total_volume) + if(air?.total_volume || REAGENT_TOTAL_VOLUME(liquid)) temporarily_store_fluids() QDEL_NULL(air) @@ -61,7 +61,7 @@ /datum/pipeline/proc/temporarily_store_fluids() //Update individual gas_mixtures by volume ratio - var/liquid_transfer_per_pipe = min(REAGENT_UNITS_PER_PIPE, (liquid && length(members)) ? (liquid.total_volume / length(members)) : 0) + var/liquid_transfer_per_pipe = min(REAGENT_UNITS_PER_PIPE, (liquid && length(members)) ? (REAGENT_TOTAL_VOLUME(liquid) / length(members)) : 0) if(!air?.total_volume && !liquid_transfer_per_pipe) return @@ -119,9 +119,9 @@ air.merge(item.air_temporary) item.air_temporary = null - liquid.maximum_volume += REAGENT_UNITS_PER_PIPE + REAGENT_ADD_MAX_VOL(liquid, REAGENT_UNITS_PER_PIPE) if(item.liquid_temporary) - item.liquid_temporary.trans_to_holder(liquid, item.liquid_temporary.total_volume) + item.liquid_temporary.trans_to_holder(liquid, REAGENT_TOTAL_VOLUME(item.liquid_temporary)) item.liquid_temporary = null if(item.leaking) @@ -135,7 +135,7 @@ possible_expansions -= borderline air.total_volume = temp_volume - liquid.maximum_volume = length(members) * REAGENT_UNITS_PER_PIPE + REAGENT_SET_MAX_VOL(liquid, (length(members) * REAGENT_UNITS_PER_PIPE)) /datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) if(new_network.line_members.Find(src)) @@ -186,7 +186,7 @@ air.merge(air_sample) //turf_air already modified by equalize_gases() - if(liquid?.total_volume) + if(REAGENT_TOTAL_VOLUME(liquid)) liquid.trans_to_turf(target, FLUID_PUDDLE) if(network) diff --git a/code/modules/atmospherics/pipes.dm b/code/modules/atmospherics/pipes.dm index ef7fa5484345..5d7ee7e8cb1f 100644 --- a/code/modules/atmospherics/pipes.dm +++ b/code/modules/atmospherics/pipes.dm @@ -128,7 +128,7 @@ loc.assume_air(air_temporary) air_temporary = null if(liquid_temporary) - liquid_temporary.trans_to(loc, liquid_temporary.total_volume) + liquid_temporary.trans_to(loc, REAGENT_TOTAL_VOLUME(liquid_temporary)) liquid_temporary = null if(leaking) STOP_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) diff --git a/code/modules/butchery/butchery_data.dm b/code/modules/butchery/butchery_data.dm index 1cd4c1850cf0..9988721f2b15 100644 --- a/code/modules/butchery/butchery_data.dm +++ b/code/modules/butchery/butchery_data.dm @@ -85,7 +85,7 @@ for(var/obj/item/food/slab in .) LAZYADD(meat, slab) if(length(meat)) - var/reagent_split = round(donor.reagents.total_volume/length(meat), 1) + var/reagent_split = round(REAGENT_TOTAL_VOLUME(donor.reagents)/length(meat), 1) for(var/obj/item/food/slab as anything in meat) donor.reagents.trans_to_obj(slab, reagent_split) diff --git a/code/modules/butchery/butchery_products.dm b/code/modules/butchery/butchery_products.dm index 268f02b43d93..76e3440dbd80 100644 --- a/code/modules/butchery/butchery_products.dm +++ b/code/modules/butchery/butchery_products.dm @@ -171,7 +171,7 @@ /obj/item/food/butchery/offal/fluid_act(var/datum/reagents/fluids) . = ..() - if(!QDELETED(src) && fluids?.total_volume && material?.tans_to) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids) && material?.tans_to) if(!dried_type) dried_type = type drying_wetness = get_max_drying_wetness() @@ -264,8 +264,8 @@ /obj/item/food/butchery/stomach/get_dried_product() var/obj/item/chems/glass/waterskin/result = ..() - if(istype(result) && reagents?.total_volume) - reagents.trans_to_holder(result.reagents, reagents.total_volume) + if(istype(result) && REAGENT_TOTAL_VOLUME(reagents)) + reagents.trans_to_holder(result.reagents, REAGENT_TOTAL_VOLUME(reagents)) return result /obj/item/food/butchery/stomach/get_max_drying_wetness() diff --git a/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm b/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm index c38fdeaa1319..25d2752b5ded 100644 --- a/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm +++ b/code/modules/clothing/gloves/jewelry/rings/ring_reagent.dm @@ -10,10 +10,10 @@ if(istype(H) && H.get_equipped_item(slot_gloves_str) == src) to_chat(H, SPAN_DANGER("You feel a prick as you slip on the ring.")) - if(reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) if(H.reagents) var/contained_reagents = reagents.get_reagents() - var/trans = reagents.trans_to_mob(H, reagents.total_volume, CHEM_INJECT) + var/trans = reagents.trans_to_mob(H, REAGENT_TOTAL_VOLUME(reagents), CHEM_INJECT) admin_inject_log(usr, H, src, contained_reagents, trans) return diff --git a/code/modules/clothing/masks/chewable.dm b/code/modules/clothing/masks/chewable.dm index c6d6285826bc..f79a3ce67ba0 100644 --- a/code/modules/clothing/masks/chewable.dm +++ b/code/modules/clothing/masks/chewable.dm @@ -33,7 +33,7 @@ /obj/item/clothing/mask/chewable/proc/chew(amount) chewtime -= amount - if(reagents && reagents.total_volume) + if(reagents && REAGENT_TOTAL_VOLUME(reagents)) if(ishuman(loc)) var/mob/living/human/user = loc if (src == user.get_equipped_item(slot_wear_mask_str) && user.check_has_mouth()) @@ -133,7 +133,7 @@ /obj/item/clothing/mask/chewable/candy/Initialize() . = ..() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) set_color(reagents.get_color()) desc += " This one is labeled '[reagents.get_primary_reagent_name()]'." diff --git a/code/modules/clothing/masks/smokable.dm b/code/modules/clothing/masks/smokable.dm index 34327fc8e7ca..01cd25482a79 100644 --- a/code/modules/clothing/masks/smokable.dm +++ b/code/modules/clothing/masks/smokable.dm @@ -55,7 +55,7 @@ /obj/item/clothing/mask/smokable/proc/smoke(amount, manual) smoketime -= amount - if(reagents && reagents.total_volume) // check if it has any reagents at all + if(reagents && REAGENT_TOTAL_VOLUME(reagents)) // check if it has any reagents at all var/smoke_loc = loc if(ishuman(loc)) var/mob/living/human/user = loc @@ -120,7 +120,7 @@ /obj/item/clothing/mask/smokable/fluid_act(var/datum/reagents/fluids) ..() - if(!QDELETED(src) && fluids?.total_volume && !waterproof && lit) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids) && !waterproof && lit) var/turf/location = get_turf(src) if(location) location.hotspot_expose(700, 5) @@ -147,7 +147,7 @@ if(flavor_text) var/turf/T = get_turf(src) T.visible_message(flavor_text) - smoke_amount = reagents.total_volume / smoketime + smoke_amount = REAGENT_TOTAL_VOLUME(reagents) / smoketime START_PROCESSING(SSobj, src) /obj/item/clothing/mask/smokable/extinguish_fire(mob/user, no_message = FALSE) @@ -392,11 +392,11 @@ if(!ATOM_IS_OPEN_CONTAINER(glass)) to_chat(user, SPAN_NOTICE("You need to take the lid off first.")) return TRUE - var/transfered = glass.reagents.trans_to_obj(src, glass.reagents.total_volume) + var/transfered = glass.reagents.trans_to_obj(src, REAGENT_TOTAL_VOLUME(glass.reagents)) if(transfered) //if reagents were transfered, show the message to_chat(user, SPAN_NOTICE("You dip \the [src] into \the [glass].")) else //if not, either the beaker was empty, or the cigarette was full - if(!glass.reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(glass.reagents)) to_chat(user, SPAN_NOTICE("[glass] is empty.")) else to_chat(user, SPAN_NOTICE("[src] is full.")) @@ -549,7 +549,7 @@ return TRUE smoketime = 1000 if(grown.reagents) - grown.reagents.trans_to_obj(src, grown.reagents.total_volume) + grown.reagents.trans_to_obj(src, REAGENT_TOTAL_VOLUME(grown.reagents)) SetName("[grown.name]-packed [initial(name)]") qdel(grown) update_icon() diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index b024ab15dae1..61de910f0e97 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -192,13 +192,13 @@ if(!ATOM_IS_OPEN_CONTAINER(input_item)) return 0 - if(!input_item.reagents || !input_item.reagents.total_volume) + if(!input_item.reagents || !REAGENT_TOTAL_VOLUME(input_item.reagents)) to_chat(user, "\The [input_item] is empty.") return 0 // Magical chemical filtration system, do not question it. var/total_transferred = 0 - for(var/decl/material/reagent as anything in input_item.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(input_item.reagents)) for(var/chargetype in charges) var/datum/rig_charge/charge = charges[chargetype] if(charge.product_type == reagent.type) diff --git a/code/modules/crafting/handmade_items.dm b/code/modules/crafting/handmade_items.dm index ab4636d25369..047be5253bb3 100644 --- a/code/modules/crafting/handmade_items.dm +++ b/code/modules/crafting/handmade_items.dm @@ -86,12 +86,12 @@ /obj/item/chems/glass/handmade/bottle/beer/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/beer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/beer, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/glass/handmade/bottle/tall/wine/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/wine, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/wine, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/glass/handmade/bottle/wide/whiskey/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/alcohol/whiskey, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/whiskey, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/modules/crafting/metalwork/metalwork_items.dm b/code/modules/crafting/metalwork/metalwork_items.dm index 511d68ec109b..328646d809a4 100644 --- a/code/modules/crafting/metalwork/metalwork_items.dm +++ b/code/modules/crafting/metalwork/metalwork_items.dm @@ -33,23 +33,23 @@ if(istype(used_item, /obj/item/chems) && ATOM_IS_OPEN_CONTAINER(used_item) && used_item.reagents) // Pour contents into the crucible. - if(used_item.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(used_item.reagents)) var/obj/item/chems/pouring = used_item if(pouring.standard_pour_into(user, src)) return TRUE // Attempting to skim off slag. // TODO: check for appropriate vessel material? Check melting point against temperature of crucible? - if(reagents?.total_volume && length(reagents.reagent_volumes) > 1) + if(REAGENT_TOTAL_VOLUME(reagents) && length(REAGENT_VOLUMES(reagents)) > 1) var/removing = min(amount_per_transfer_from_this, REAGENTS_FREE_SPACE(used_item.reagents)) - if(removing < length(reagents.reagent_volumes)-1) + if(removing < length(REAGENT_VOLUMES(reagents))-1) to_chat(user, SPAN_WARNING("\The [used_item] is full.")) return TRUE // Remove a portion, excepting the primary reagent. - var/old_amt = used_item.reagents.total_volume + var/old_amt = REAGENT_TOTAL_VOLUME(used_item.reagents) var/decl/material/primary_mat = reagents.get_primary_reagent_decl() reagents.trans_to_holder(used_item.reagents, removing, skip_reagents = list(primary_mat.type)) - to_chat(user, SPAN_NOTICE("You skim [used_item.reagents.total_volume-old_amt] unit\s of slag from the top of \the [primary_mat].")) + to_chat(user, SPAN_NOTICE("You skim [REAGENT_TOTAL_VOLUME(used_item.reagents)-old_amt] unit\s of slag from the top of \the [primary_mat].")) return TRUE return ..() diff --git a/code/modules/crafting/pottery/pottery_moulds.dm b/code/modules/crafting/pottery/pottery_moulds.dm index 42339a40f1ae..4d07621fdd15 100644 --- a/code/modules/crafting/pottery/pottery_moulds.dm +++ b/code/modules/crafting/pottery/pottery_moulds.dm @@ -82,15 +82,15 @@ if(!product_type) return FALSE - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) to_chat(user, SPAN_WARNING("\The [src] is empty!")) return TRUE - if(reagents.total_volume < reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(reagents) < REAGENT_MAXIMUM_VOLUME(reagents)) to_chat(user, SPAN_WARNING("\The [src] is not full yet!")) return TRUE - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.melting_point && temperature >= reagent.melting_point) to_chat(user, SPAN_WARNING("The contents of \the [src] are still molten! Wait for it to cool down.")) return TRUE @@ -109,11 +109,11 @@ product.dropInto(loc) reagents.remove_reagent(product_mat.type, REAGENT_VOLUME(reagents, product_mat.type)) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.type == product_mat.type) continue LAZYINITLIST(product.matter) - product.matter[reagent.type] += max(1, round(reagents.reagent_volumes[reagent] / REAGENT_UNITS_PER_MATERIAL_UNIT)) + product.matter[reagent.type] += max(1, round(REAGENT_VOLUME(reagents, reagent) / REAGENT_UNITS_PER_MATERIAL_UNIT)) reagents.clear_reagents() if(length(product_metadata)) product.take_mould_metadata(product_metadata) diff --git a/code/modules/crafting/working/quern.dm b/code/modules/crafting/working/quern.dm index afe6821edc73..3ff1832811c7 100644 --- a/code/modules/crafting/working/quern.dm +++ b/code/modules/crafting/working/quern.dm @@ -3,21 +3,20 @@ max_storage_space = DEFAULT_BOX_STORAGE /obj/structure/working/quern - name = "quern-stone" - desc = "A pair of heavy stones connected by an axle, used to grind plants and minerals into powder." - icon = 'icons/obj/structures/quern.dmi' - material = /decl/material/solid/stone/granite - color = /decl/material/solid/stone/granite::color - storage = /datum/storage/hopper/mortar/quern - work_skill = SKILL_COOKING // Maybe? - var/tmp/volume = 1000 // Same as reagent dispensers. Possibly too large? + name = "quern-stone" + desc = "A pair of heavy stones connected by an axle, used to grind plants and minerals into powder." + icon = 'icons/obj/structures/quern.dmi' + material = /decl/material/solid/stone/granite + color = /decl/material/solid/stone/granite::color + storage = /datum/storage/hopper/mortar/quern + work_skill = SKILL_COOKING // Maybe? + chem_volume = 1000 // Same as reagent dispensers. Possibly too large? var/amount_dispensed = 10 var/tmp/possible_transfer_amounts = @"[10,25,50,100,500]" /obj/structure/working/quern/Initialize() . = ..() atom_flags |= ATOM_FLAG_OPEN_CONTAINER - initialize_reagents() /obj/structure/working/quern/try_start_working(mob/user) @@ -51,20 +50,13 @@ var/decl/material/crushing_material = grinding.get_material() if(!attacking_material || !crushing_material || attacking_material.hardness <= crushing_material.hardness) return FALSE - if(REAGENTS_FREE_SPACE(reagents) < grinding.reagents?.total_volume) + if(REAGENTS_FREE_SPACE(reagents) < REAGENT_TOTAL_VOLUME(grinding.reagents)) return FALSE - if(grinding.reagents?.total_volume) // if it has no reagents, skip all the fluff and destroy it instantly - grinding.reagents.trans_to(src, grinding.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(grinding.reagents)) // if it has no reagents, skip all the fluff and destroy it instantly + grinding.reagents.trans_to(src, REAGENT_TOTAL_VOLUME(grinding.reagents)) QDEL_NULL(grinding) return TRUE -/obj/structure/working/quern/initialize_reagents(populate = TRUE) - if(!reagents) - create_reagents(volume) - else - reagents.maximum_volume = max(reagents.maximum_volume, volume) - . = ..() - /obj/structure/working/quern/set_reagent_amount_dispensed(new_amount) amount_dispensed = new_amount diff --git a/code/modules/detectivework/tools/luminol.dm b/code/modules/detectivework/tools/luminol.dm index 9a6556e31045..e687cf00035e 100644 --- a/code/modules/detectivework/tools/luminol.dm +++ b/code/modules/detectivework/tools/luminol.dm @@ -9,4 +9,4 @@ chem_volume = 250 /obj/item/chems/spray/luminol/populate_reagents() - add_to_reagents(/decl/material/liquid/luminol, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/luminol, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index bebd8ca7d69d..96d91b2e784d 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -39,7 +39,7 @@ extinguish_fire() return TRUE - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) remove_contents(user) return TRUE @@ -62,7 +62,7 @@ /obj/item/chems/rag/update_name() if(is_on_fire()) name_prefix = "burning" - else if(reagents && reagents.total_volume) + else if(reagents && REAGENT_TOTAL_VOLUME(reagents)) name_prefix = "damp" else name_prefix = "dry" @@ -78,19 +78,19 @@ /obj/item/chems/rag/proc/remove_contents(mob/user, atom/trans_dest = null) if(!trans_dest && !user.loc) return - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) return var/target_text = trans_dest? "\the [trans_dest]" : "\the [user.loc]" user.visible_message( SPAN_NOTICE("\The [user] begins to wring out [src] over [target_text]."), SPAN_NOTICE("You begin to wring out \the [src] over [target_text].") ) - if(!do_after(user, reagents.total_volume*5, progress = 0) || !reagents?.total_volume) //50 for a fully soaked rag + if(!do_after(user, REAGENT_TOTAL_VOLUME(reagents)*5, progress = 0) || !REAGENT_TOTAL_VOLUME(reagents)) //50 for a fully soaked rag return if(trans_dest) - reagents.trans_to(trans_dest, reagents.total_volume) + reagents.trans_to(trans_dest, REAGENT_TOTAL_VOLUME(reagents)) else - reagents.splash(user.loc, reagents.total_volume) + reagents.splash(user.loc, REAGENT_TOTAL_VOLUME(reagents)) user.visible_message( SPAN_NOTICE("\The [user] wrings out \the [src] over [target_text]."), SPAN_NOTICE("You finish to wringing out \the [src].") @@ -99,7 +99,7 @@ /obj/item/chems/rag/proc/wipe_down(atom/target, mob/user) - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_WARNING("The [initial(name)] is dry.")) return @@ -120,7 +120,7 @@ target.ignite_fire() return TRUE - if(reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) if(user.get_target_zone() != BP_MOUTH) wipe_down(target, user) return TRUE @@ -184,8 +184,8 @@ var/total_fuel = 0 var/total_volume = 0 if(reagents) - total_volume += reagents.total_volume - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + total_volume += REAGENT_TOTAL_VOLUME(reagents) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) total_fuel += REAGENT_VOLUME(reagents, reagent) * reagent.accelerant_value . = (total_fuel >= 2 && total_fuel >= total_volume*0.5) @@ -233,7 +233,7 @@ qdel(src) return - if(reagents?.total_volume) - remove_from_reagents(/decl/material/liquid/fuel, reagents.maximum_volume/25) + if(REAGENT_TOTAL_VOLUME(reagents)) + remove_from_reagents(/decl/material/liquid/fuel, REAGENT_MAXIMUM_VOLUME(reagents)/25) update_name() burn_time-- diff --git a/code/modules/economy/_worth.dm b/code/modules/economy/_worth.dm index 7e623d2907d0..f2f476a776b9 100644 --- a/code/modules/economy/_worth.dm +++ b/code/modules/economy/_worth.dm @@ -10,7 +10,7 @@ /atom/proc/get_single_monetary_worth() . = get_base_value() * get_value_multiplier() if(reagents) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) . += reagent.get_value() * REAGENT_VOLUME(reagents, reagent) * REAGENT_WORTH_MULTIPLIER . = max(0, round(.)) diff --git a/code/modules/fabrication/fabricator_intake.dm b/code/modules/fabrication/fabricator_intake.dm index 3d2f8cf9e294..47ab2071e12c 100644 --- a/code/modules/fabrication/fabricator_intake.dm +++ b/code/modules/fabrication/fabricator_intake.dm @@ -6,7 +6,7 @@ /obj/machinery/fabricator/proc/take_reagents(var/obj/item/thing, var/mob/user, var/destructive = FALSE) if(!thing.reagents || (!destructive && !ATOM_IS_OPEN_CONTAINER(thing))) return SUBSTANCE_TAKEN_NONE - for(var/decl/material/reagent as anything in thing.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(thing.reagents)) if(!base_storage_capacity[reagent.type]) continue var/taking_reagent = min(REAGENT_VOLUME(thing.reagents, reagent), floor((storage_capacity[reagent.type] - stored_material[reagent.type]) * REAGENT_UNITS_PER_MATERIAL_UNIT)) @@ -24,7 +24,7 @@ // Otherwise take the first applicable and useful reagent. if(stored_material[reagent.type] == storage_capacity[reagent.type]) return SUBSTANCE_TAKEN_FULL - else if(thing.reagents.total_volume > 0) + else if(REAGENT_TOTAL_VOLUME(thing.reagents) > 0) return SUBSTANCE_TAKEN_SOME else return SUBSTANCE_TAKEN_ALL diff --git a/code/modules/fluids/_fluid.dm b/code/modules/fluids/_fluid.dm index 58f73cd55c46..8dd93caf8988 100644 --- a/code/modules/fluids/_fluid.dm +++ b/code/modules/fluids/_fluid.dm @@ -22,7 +22,7 @@ /atom/movable/fluid_overlay/on_update_icon() var/datum/reagents/loc_reagents = loc?.reagents - var/reagent_volume = loc_reagents?.total_volume + var/reagent_volume = REAGENT_TOTAL_VOLUME(loc_reagents) // Update layer. var/new_layer @@ -108,7 +108,7 @@ var/global/list/_fluid_edge_mask_cache = list() sleep(0) updating_edge_mask = FALSE - if(loc?.reagents?.total_volume <= FLUID_PUDDLE) + if(REAGENT_TOTAL_VOLUME(loc?.reagents) <= FLUID_PUDDLE) remove_filter("fluid_edge_mask") return @@ -117,7 +117,7 @@ var/global/list/_fluid_edge_mask_cache = list() var/list/connections for(var/checkdir in global.alldirs) var/turf/neighbor = get_step_resolving_mimic(loc, checkdir) - if(!neighbor || neighbor.density || neighbor?.reagents?.total_volume > FLUID_PUDDLE) + if(!neighbor || neighbor.density || REAGENT_TOTAL_VOLUME(neighbor?.reagents) > FLUID_PUDDLE) LAZYADD(connections, checkdir) else LAZYADD(ignored, checkdir) diff --git a/code/modules/food/cooking/_recipe.dm b/code/modules/food/cooking/_recipe.dm index aaba7b53fca2..371c6a048d95 100644 --- a/code/modules/food/cooking/_recipe.dm +++ b/code/modules/food/cooking/_recipe.dm @@ -122,7 +122,7 @@ var/global/list/_cooking_recipe_cache = list() /decl/recipe/proc/check_reagents(datum/reagents/avail_reagents) // SHOULD_BE_PURE(TRUE) Due to use of GET_DECL() in REAGENT_VOLUME, this cannot be pure. - if(length(avail_reagents?.reagent_volumes) < length(reagents)) + if(length(REAGENT_VOLUMES(avail_reagents)) < length(reagents)) return FALSE for(var/reagent in reagents) if(REAGENT_VOLUME(avail_reagents, reagent) < reagents[reagent]) @@ -185,8 +185,8 @@ var/global/list/_cooking_recipe_cache = list() if(!istype(container) || QDELETED(container) || !container.simulated) CRASH("Recipe trying to create a result with null or invalid container: [container || "NULL"], [container?.simulated || "NULL"]") - if(!container.reagents?.maximum_volume) - CRASH("Recipe trying to create a result in a container with null or zero capacity reagent holder: [container.reagents?.maximum_volume || "NULL"]") + if(!REAGENT_MAXIMUM_VOLUME(container.reagents)) + CRASH("Recipe trying to create a result in a container with null or zero capacity reagent holder: [istype(reagents) ? REAGENT_MAXIMUM_VOLUME(container.reagents) : "NULL"]") if(ispath(result, /atom/movable)) return create_result_atom(container, used_ingredients) @@ -194,7 +194,7 @@ var/global/list/_cooking_recipe_cache = list() if(ispath(result, /decl/material)) var/created_volume = result_quantity for(var/obj/item/ingredient in (used_ingredients[RECIPE_COMPONENT_ITEMS]|used_ingredients[RECIPE_COMPONENT_FRUIT])) - created_volume += ingredient.reagents?.total_volume + created_volume += REAGENT_TOTAL_VOLUME(ingredient.reagents) container.reagents?.add_reagent(result, created_volume, get_result_data(container, used_ingredients)) return null @@ -297,14 +297,14 @@ var/global/list/_cooking_recipe_cache = list() for(var/atom/item as anything in used_ingredients[RECIPE_COMPONENT_ITEMS]) if(item.reagents) if(buffer) - item.reagents.trans_to_holder(buffer, item.reagents.total_volume) + item.reagents.trans_to_holder(buffer, REAGENT_TOTAL_VOLUME(item.reagents)) else item.reagents.clear_reagents() item.physically_destroyed() for(var/atom/fruit as anything in used_ingredients[RECIPE_COMPONENT_FRUIT]) if(fruit.reagents) if(buffer) - fruit.reagents.trans_to_holder(buffer, fruit.reagents.total_volume) + fruit.reagents.trans_to_holder(buffer, REAGENT_TOTAL_VOLUME(fruit.reagents)) else fruit.reagents.clear_reagents() fruit.physically_destroyed() @@ -340,18 +340,18 @@ var/global/list/_cooking_recipe_cache = list() holder = temporary_holder if(length(.) > 1) for(var/atom/movable/result_obj in .) - result_obj.reagents.trans_to_holder(holder, result_obj.reagents.total_volume) + result_obj.reagents.trans_to_holder(holder, REAGENT_TOTAL_VOLUME(result_obj.reagents)) switch(reagent_mix) if(REAGENT_SUM) //Sum is easy, just shove the entire buffer into the result - buffer.trans_to_holder(holder, buffer.total_volume) + buffer.trans_to_holder(holder, REAGENT_TOTAL_VOLUME(buffer)) if(REAGENT_MAX) //We want the highest of each. //Iterate through everything in buffer. If the target has less than the buffer, then top it up - for (var/decl/material/reagent as anything in buffer.reagent_volumes) + for (var/decl/material/reagent as anything in REAGENT_VOLUMES(buffer)) var/rvol = REAGENT_VOLUME(holder, reagent) var/bvol = REAGENT_VOLUME(buffer, reagent) if (rvol < bvol) @@ -361,7 +361,7 @@ var/global/list/_cooking_recipe_cache = list() if(REAGENT_MIN) //Min is slightly more complex. We want the result to have the lowest from each side //But zero will not count. Where a side has zero its ignored and the side with a nonzero value is used - for (var/decl/material/reagent as anything in buffer.reagent_volumes) + for (var/decl/material/reagent as anything in REAGENT_VOLUMES(buffer)) var/rvol = REAGENT_VOLUME(holder, reagent) var/bvol = REAGENT_VOLUME(buffer, reagent) if (rvol == 0) //If the target has zero of this reagent @@ -376,7 +376,7 @@ var/global/list/_cooking_recipe_cache = list() if(length(.) > 1) // If we're here, then holder is a buffer containing the total reagents // for all the results. So now we redistribute it among them. - var/total = round(holder.total_volume / length(.)) + var/total = round(REAGENT_TOTAL_VOLUME(holder) / length(.)) for(var/atom/result as anything in .) holder.trans_to(result, total) diff --git a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm index d51bd227cc42..501a99f2282a 100644 --- a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm +++ b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm @@ -37,7 +37,7 @@ // Fill or take from the vessel. if(used_item.reagents && ATOM_IS_OPEN_CONTAINER(used_item)) - if(used_item.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(used_item.reagents)) if(istype(used_item, /obj/item/chems)) var/obj/item/chems/vessel = used_item if(vessel.standard_pour_into(user, src)) @@ -66,16 +66,18 @@ return TRUE if(handle_eaten_by_mob(user, target) != EATEN_INVALID) return TRUE + + var/total_vol = REAGENT_TOTAL_VOLUME(reagents) if(user.check_intent(I_FLAG_HARM)) if(standard_splash_mob(user,target)) return TRUE - if(reagents && reagents.total_volume) + if(reagents && total_vol) to_chat(user, SPAN_DANGER("You splash the contents of \the [src] onto \the [target].")) - reagents.splash(target, reagents.total_volume) + reagents.splash(target, total_vol) return TRUE - else if(reagents && reagents.total_volume) + else if(reagents && total_vol) to_chat(user, SPAN_NOTICE("You splash a small amount of the contents of \the [src] onto \the [target].")) - reagents.splash(target, min(reagents.total_volume, 5)) + reagents.splash(target, min(total_vol, 5)) return TRUE . = ..() // End boilerplate. @@ -87,18 +89,18 @@ for(var/obj/item/thing in get_stored_inventory()) . += "\the [thing]" - if(reagents?.total_volume) - for(var/decl/material/reagent as anything in reagents.solid_volumes) - . += "[reagents.solid_volumes[reagent]]u of [reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)]" + if(REAGENT_TOTAL_VOLUME(reagents)) + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(reagents)) + . += "[SOLID_VOLUME(reagents, reagent)]u of [reagent.get_reagent_name(reagents, MAT_PHASE_SOLID)]" var/datum/gas_mixture/environment = loc?.return_air() var/ambient_pressure = environment ? environment.return_pressure() : ONE_ATMOSPHERE - for(var/decl/material/reagent as anything in reagents.liquid_volumes) + for(var/decl/material/reagent as anything in REAGENT_LIQUID_VOLUMES(reagents)) var/reagent_name = reagent.get_reagent_name(reagents, MAT_PHASE_LIQUID) if(reagent.phase_at_temperature(temperature, ambient_pressure) == MAT_PHASE_GAS && reagent.soup_hot_desc) - . += "[reagents.liquid_volumes[reagent]]u of [reagent.soup_hot_desc] [reagent_name]" + . += "[LIQUID_VOLUME(reagents, reagent)]u of [reagent.soup_hot_desc] [reagent_name]" else - . += "[reagents.liquid_volumes[reagent]]u of [reagent_name]" + . += "[LIQUID_VOLUME(reagents, reagent)]u of [reagent_name]" /obj/item/chems/cooking_vessel/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/modules/food/cooking/cooking_vessels/pot.dm b/code/modules/food/cooking/cooking_vessels/pot.dm index 2152ea77b56f..d84c2eca7faf 100644 --- a/code/modules/food/cooking/cooking_vessels/pot.dm +++ b/code/modules/food/cooking/cooking_vessels/pot.dm @@ -39,7 +39,7 @@ last_boil_temp = use_temperature var/next_boil_status = FALSE - for(var/decl/material/reagent as anything in reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.phase_at_temperature(use_temperature, ambient_pressure) == MAT_PHASE_GAS) next_boil_status = TRUE break diff --git a/code/modules/food/cooking/recipes/recipe_soup.dm b/code/modules/food/cooking/recipes/recipe_soup.dm index 38dc8a8e1b3a..47ec28c55cd9 100644 --- a/code/modules/food/cooking/recipes/recipe_soup.dm +++ b/code/modules/food/cooking/recipes/recipe_soup.dm @@ -58,7 +58,7 @@ ingredients[meat.meat_name]++ if(precursor_type) - var/list/precursor_data = LAZYACCESS(container.reagents?.reagent_data, precursor_type) + var/list/precursor_data = REAGENT_DATA(container.reagents, precursor_type) var/list/precursor_taste = LAZYACCESS(precursor_data, DATA_TASTE) if(length(precursor_taste)) for(var/taste in precursor_taste) diff --git a/code/modules/food/utensils/_utensil.dm b/code/modules/food/utensils/_utensil.dm index d04d06979d96..9941844187f4 100644 --- a/code/modules/food/utensils/_utensil.dm +++ b/code/modules/food/utensils/_utensil.dm @@ -104,7 +104,7 @@ else show_slice_message(user, tool, src) - var/reagents_per_slice = max(1, round(reagents.total_volume / slice_num)) + var/reagents_per_slice = max(1, round(REAGENT_TOTAL_VOLUME(reagents) / slice_num)) for(var/i = 1 to slice_num) var/atom/movable/slice = create_slice() if(slice) diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index eebf86d0ccb9..7ed077da24fe 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -22,8 +22,8 @@ /obj/item/food/grown/handle_centrifuge_process(obj/machinery/centrifuge/centrifuge) if(!(. = ..())) return - if(reagents?.total_volume) - reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) + reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, REAGENT_TOTAL_VOLUME(reagents)) for(var/obj/item/thing in contents) thing.dropInto(centrifuge.loc) for(var/atom/movable/thing in convert_matter_to_lumps()) @@ -96,8 +96,8 @@ . = ..(mapload, material_key, skip_plate) //Init reagents update_desc() - if(reagents.total_volume > 0) - bitesize = 1 + round(reagents.total_volume / 2, 1) + if(REAGENT_TOTAL_VOLUME(reagents) > 0) + bitesize = 1 + round(REAGENT_TOTAL_VOLUME(reagents) / 2, 1) update_icon() /obj/item/food/grown/populate_reagents() @@ -143,7 +143,7 @@ var/list/descriptors = list() - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.fruit_descriptor) descriptors |= reagent.fruit_descriptor if(reagent.reflectiveness >= MAT_VALUE_SHINY) @@ -285,9 +285,9 @@ var/global/list/_wood_materials = list( return TRUE var/obj/item/clothing/mask/smokable/cigarette/rolled/R = new(get_turf(src)) - R.create_or_update_reagents(max(R.reagents?.maximum_volume, reagents?.total_volume)) + R.create_or_update_reagents(max(REAGENT_MAXIMUM_VOLUME(R.reagents), REAGENT_TOTAL_VOLUME(reagents))) R.brand = "[src] handrolled in \the [used_item]." - reagents.trans_to_holder(R.reagents, R.reagents.total_volume) + reagents.trans_to_holder(R.reagents, REAGENT_TOTAL_VOLUME(R.reagents)) to_chat(user, SPAN_NOTICE("You roll \the [src] into \the [used_item].")) user.put_in_active_hand(R) qdel(used_item) @@ -308,7 +308,7 @@ var/global/list/_wood_materials = list( . = ..() if(seed && seed.get_trait(TRAIT_STINGS)) - if(!reagents || reagents.total_volume <= 0) + if(!reagents || REAGENT_TOTAL_VOLUME(reagents) <= 0) return remove_any_reagents(rand(1,3)) seed.thrown_at(src, target) @@ -347,7 +347,7 @@ var/global/list/_wood_materials = list( var/mob/living/human/H = user if(istype(H) && H.get_equipped_item(slot_gloves_str)) return - if(!reagents || reagents.total_volume <= 0) + if(!reagents || REAGENT_TOTAL_VOLUME(reagents) <= 0) return remove_any_reagents(rand(1,3)) //Todo, make it actually remove the reagents the seed uses. var/affected = pick(BP_R_HAND,BP_L_HAND) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 236a5ae569a3..b6ace7bd1ec4 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -209,7 +209,7 @@ make_splat(T, thrown) var/datum/reagents/splat_reagents = thrown?.reagents - if(!splat_reagents?.maximum_volume) // if thrown doesn't exist or has no reagents, use the seed's default reagents. + if(!REAGENT_MAXIMUM_VOLUME(splat_reagents)) // if thrown doesn't exist or has no reagents, use the seed's default reagents. splat_reagents = new /datum/reagents(INFINITY, global.temp_reagents_holder) var/potency = get_trait(TRAIT_POTENCY) var/list/seed_chems = get_chemical_composition() diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index c286fd0c874c..f31aa5eb279c 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -230,12 +230,12 @@ if(!reagents) return - if(reagents.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) return - reagents.trans_to_obj(temp_chem_holder, min(reagents.total_volume,rand(1,3))) + reagents.trans_to_obj(temp_chem_holder, min(REAGENT_TOTAL_VOLUME(reagents),rand(1,3))) - for(var/decl/material/reagent as anything in temp_chem_holder.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(temp_chem_holder.reagents)) var/reagent_total = REAGENT_VOLUME(temp_chem_holder.reagents, reagent) diff --git a/code/modules/hydroponics/trays/tray_process.dm b/code/modules/hydroponics/trays/tray_process.dm index a62c81a8d1e2..d692dc841a51 100644 --- a/code/modules/hydroponics/trays/tray_process.dm +++ b/code/modules/hydroponics/trays/tray_process.dm @@ -6,15 +6,15 @@ var/growth_rate = get_growth_rate() var/turf/my_turf = get_turf(src) if(istype(my_turf) && !closed_system) - var/space_left = reagents ? (reagents.maximum_volume - reagents.total_volume) : 0 - if(space_left > 0 && reagents.total_volume < 10) + var/space_left = reagents ? (REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(reagents)) : 0 + if(space_left > 0 && REAGENT_TOTAL_VOLUME(reagents) < 10) // Handle nearby smoke if any. for(var/obj/effect/effect/smoke/chem/smoke in view(1, src)) - if(smoke.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(smoke.reagents)) smoke.reagents.trans_to_obj(src, 5, copy = 1) // Handle environmental effects like weather and flooding. - if(my_turf.reagents?.total_volume) - my_turf.reagents.trans_to_obj(src, min(space_left, min(my_turf.reagents.total_volume, rand(5,10)))) + if(REAGENT_TOTAL_VOLUME(my_turf.reagents)) + my_turf.reagents.trans_to_obj(src, min(space_left, min(REAGENT_TOTAL_VOLUME(my_turf.reagents), rand(5,10)))) if(istype(my_turf.weather?.weather_system?.current_state, /decl/state/weather/rain)) var/decl/state/weather/rain/rain = my_turf.weather.weather_system.current_state if(rain.is_liquid) diff --git a/code/modules/hydroponics/trays/tray_soil.dm b/code/modules/hydroponics/trays/tray_soil.dm index e0c42fa58163..7e74e1aa0147 100644 --- a/code/modules/hydroponics/trays/tray_soil.dm +++ b/code/modules/hydroponics/trays/tray_soil.dm @@ -80,14 +80,14 @@ return if(closed_system || !reagents || waterlevel >= 100) return - if((reagents.maximum_volume - reagents.total_volume) <= 0 || reagents.total_volume >= 10) + if((REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(reagents)) <= 0 || REAGENT_TOTAL_VOLUME(reagents) >= 10) return for(var/step_dir in global.alldirs) var/turf/neighbor = get_step_resolving_mimic(src, step_dir) - if(neighbor == my_turf || !neighbor?.reagents?.total_volume || !Adjacent(neighbor)) + if(neighbor == my_turf || !REAGENT_TOTAL_VOLUME(neighbor?.reagents) || !Adjacent(neighbor)) continue neighbor.reagents.trans_to_obj(src, rand(2,3)) - if((reagents.maximum_volume - reagents.total_volume) <= 0 || reagents.total_volume >= 10) + if((REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(reagents)) <= 0 || REAGENT_TOTAL_VOLUME(reagents) >= 10) break return ..() diff --git a/code/modules/implants/implant_types/chem.dm b/code/modules/implants/implant_types/chem.dm index b00656a0cbf6..10210ac18f74 100644 --- a/code/modules/implants/implant_types/chem.dm +++ b/code/modules/implants/implant_types/chem.dm @@ -42,11 +42,11 @@ var/global/list/chem_implants = list() /obj/item/implant/chem/attackby(obj/item/used_item, mob/user) if(istype(used_item, /obj/item/chems/syringe)) - if(reagents.total_volume >= reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(reagents) >= REAGENT_MAXIMUM_VOLUME(reagents)) to_chat(user, SPAN_WARNING("\The [src] is full.")) else if(do_after(user, 0.5 SECONDS, src)) used_item.reagents.trans_to_obj(src, 5) - to_chat(user, SPAN_NOTICE("You inject 5 units of the solution. The syringe now contains [used_item.reagents.total_volume] units.")) + to_chat(user, SPAN_NOTICE("You inject 5 units of the solution. The syringe now contains [REAGENT_TOTAL_VOLUME(used_item.reagents)] units.")) return TRUE return ..() diff --git a/code/modules/interactions/interactions_reagents.dm b/code/modules/interactions/interactions_reagents.dm index 80de48cc9177..213da09980da 100644 --- a/code/modules/interactions/interactions_reagents.dm +++ b/code/modules/interactions/interactions_reagents.dm @@ -4,7 +4,7 @@ examine_desc = "dip an item into $TARGET_THEM$" /decl/interaction_handler/dip_item/is_possible(atom/target, mob/user, obj/item/prop) - return ..() && target != prop && target.reagents?.total_volume >= FLUID_MINIMUM_TRANSFER && istype(prop) && target.can_be_poured_from(user, prop) + return ..() && target != prop && REAGENT_TOTAL_VOLUME(target.reagents) >= FLUID_MINIMUM_TRANSFER && istype(prop) && target.can_be_poured_from(user, prop) /decl/interaction_handler/dip_item/invoked(atom/target, mob/user, obj/item/prop) user.visible_message(SPAN_NOTICE("\The [user] dips \the [prop] into \the [target.reagents.get_primary_reagent_name()].")) @@ -13,7 +13,7 @@ var/transferring = min(target_obj.get_food_default_transfer_amount(user), REAGENTS_FREE_SPACE(prop.reagents)) if(transferring) target.reagents.trans_to_holder(prop.reagents, transferring) - if(target.reagents?.total_volume >= FLUID_MINIMUM_TRANSFER) + if(REAGENT_TOTAL_VOLUME(target.reagents) >= FLUID_MINIMUM_TRANSFER) prop.fluid_act(target.reagents) return TRUE @@ -25,7 +25,7 @@ /decl/interaction_handler/fill_from/is_possible(atom/target, mob/user, obj/item/prop) if(!(. = ..())) return - if(target == prop || target.reagents?.total_volume < FLUID_PUDDLE) + if(target == prop || REAGENT_TOTAL_VOLUME(target.reagents) < FLUID_PUDDLE) return FALSE if(!istype(prop) || (!isitem(target) && !istype(target, /obj/structure))) return FALSE @@ -52,7 +52,7 @@ /decl/interaction_handler/empty_into/is_possible(atom/target, mob/user, obj/item/prop) if(!(. = ..())) return - if(target == prop || !istype(prop) || prop.reagents?.total_volume <= 0) + if(target == prop || !istype(prop) || REAGENT_TOTAL_VOLUME(prop.reagents) <= 0) return FALSE return target.can_be_poured_into(user, prop) && prop.can_be_poured_from(user, target) @@ -128,7 +128,7 @@ target.show_food_no_mouth_message(user, user) return - if(!target?.reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(target?.reagents)) target.show_food_empty_message(user, EATING_METHOD_DRINK) return diff --git a/code/modules/lights/light_bulbs_tubes.dm b/code/modules/lights/light_bulbs_tubes.dm index 6923213ab2b8..403bf7f4662b 100644 --- a/code/modules/lights/light_bulbs_tubes.dm +++ b/code/modules/lights/light_bulbs_tubes.dm @@ -125,10 +125,10 @@ // if a syringe, can inject flammable liquids to make it explode /obj/item/light/attackby(var/obj/item/used_item, var/mob/user) ..() - if(istype(used_item, /obj/item/chems/syringe) && used_item.reagents?.total_volume) + if(istype(used_item, /obj/item/chems/syringe) && REAGENT_TOTAL_VOLUME(used_item.reagents)) var/obj/item/chems/syringe/S = used_item to_chat(user, "You inject the solution into \the [src].") - for(var/decl/material/reagent as anything in S.reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(S.reagents)) if(reagent.accelerant_value > FUEL_VALUE_ACCELERANT) rigged = TRUE log_and_message_admins("injected a light with flammable reagents, rigging it to explode.", user) diff --git a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm index 348abd21f1c3..0d6b27a9c82b 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm @@ -168,7 +168,7 @@ M.add_chemical_effect(CE_NOPULSE, 1) /decl/material/liquid/zombiepowder/on_leaving_metabolism(datum/reagents/metabolism/holder) - var/mob/M = holder?.my_atom + var/mob/M = REAGENT_GET_ATOM(holder) if(istype(M)) M.status_flags &= ~FAKEDEATH . = ..() diff --git a/code/modules/materials/definitions/liquids/materials_liquid_water.dm b/code/modules/materials/definitions/liquids/materials_liquid_water.dm index 7524049c51de..20950348514f 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_water.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_water.dm @@ -34,7 +34,8 @@ coated_adjective = "wet" /decl/material/liquid/water/build_coated_name(datum/reagents/coating, list/accumulator) - if(length(coating.reagent_volumes) > 1) + var/coating_volumes = REAGENT_VOLUMES(coating) + if(length(coating_volumes) > 1) accumulator.Insert(1, "dilute") // dilute always comes first! also this is intentionally not colored in component color mode return // don't insert 'wet' ..() @@ -50,7 +51,7 @@ /decl/material/liquid/water/get_reagent_name(datum/reagents/holder, phase = MAT_PHASE_LIQUID) . = ..() // length == 1 implies primary reagent, so checking both is redundant - if(phase == MAT_PHASE_LIQUID && length(holder?.reagent_volumes) == 1) + if(phase == MAT_PHASE_LIQUID && length(REAGENT_VOLUMES(holder)) == 1) return "fresh [.]" return diff --git a/code/modules/materials/definitions/solids/materials_solid_elements.dm b/code/modules/materials/definitions/solids/materials_solid_elements.dm index 2e1c477f4aa7..0cbed95b3386 100644 --- a/code/modules/materials/definitions/solids/materials_solid_elements.dm +++ b/code/modules/materials/definitions/solids/materials_solid_elements.dm @@ -43,9 +43,10 @@ /decl/material/solid/carbon/affect_ingest(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() var/datum/reagents/ingested = M.get_ingested_reagents() - if(ingested && LAZYLEN(ingested.reagent_volumes) > 1) - var/effect = 1 / (LAZYLEN(ingested.reagent_volumes) - 1) - for(var/decl/material/reagent as anything in ingested.reagent_volumes) + var/ingested_volumes = REAGENT_VOLUMES(ingested) + if(ingested && LAZYLEN(ingested_volumes) > 1) + var/effect = 1 / (LAZYLEN(ingested_volumes) - 1) + for(var/decl/material/reagent as anything in ingested_volumes) if(reagent.type != type) ingested.remove_reagent(reagent, removed * effect) diff --git a/code/modules/materials/material_data.dm b/code/modules/materials/material_data.dm index 322847dbeead..dddb985f8ada 100644 --- a/code/modules/materials/material_data.dm +++ b/code/modules/materials/material_data.dm @@ -5,7 +5,12 @@ .[DATA_INGREDIENT_FLAGS] |= allergen_flags /decl/material/proc/mix_data(var/datum/reagents/reagents, var/list/newdata, var/amount) - reagents.cached_color = null // colour masking may change + + if(!istype(reagents)) + return + + UNLINT(reagents.cached_color = null) // colour masking may change + . = REAGENT_DATA(reagents, src) if(!length(newdata) || !islist(newdata)) return diff --git a/code/modules/materials/material_debris.dm b/code/modules/materials/material_debris.dm index 0eece4d7788d..879efc811ad5 100644 --- a/code/modules/materials/material_debris.dm +++ b/code/modules/materials/material_debris.dm @@ -135,7 +135,7 @@ return if((REALTIMEOFDAY - time_created) < 5 SECONDS) return - if(!QDELETED(src) && fluids?.total_liquid_volume >= FLUID_SLURRY) + if(!QDELETED(src) && REAGENT_TOTAL_LIQUID_VOLUME(fluids) >= FLUID_SLURRY) var/free_space = REAGENTS_FREE_SPACE(fluids) for(var/matter_type in matter) if(free_space <= MINIMUM_CHEMICAL_VOLUME) diff --git a/code/modules/materials/material_display.dm b/code/modules/materials/material_display.dm index c0e6125a0f5f..6ec5d67c60a9 100644 --- a/code/modules/materials/material_display.dm +++ b/code/modules/materials/material_display.dm @@ -1,8 +1,9 @@ /decl/material/proc/get_presentation_name(var/obj/item/prop) - if(islist(prop?.reagents?.reagent_data)) - . = LAZYACCESS(prop.reagents.reagent_data[src], DATA_MASK_NAME) + var/list/presentation_data = REAGENT_DATA(prop.reagents, src) + if(islist(presentation_data)) + . = LAZYACCESS(presentation_data, DATA_MASK_NAME) . ||= glass_name || get_reagent_name(prop?.reagents) - if(prop?.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(prop?.reagents)) . = build_presentation_name_from_reagents(prop, .) /decl/material/proc/build_presentation_name_from_reagents(var/obj/item/prop, var/supplied) @@ -16,7 +17,7 @@ /decl/material/proc/get_presentation_desc(var/obj/item/prop) . = glass_desc - if(prop?.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(prop?.reagents)) . = build_presentation_desc_from_reagents(prop, .) /decl/material/proc/build_presentation_desc_from_reagents(var/obj/item/prop, var/supplied) @@ -29,8 +30,9 @@ /decl/material/proc/get_reagent_name(datum/reagents/holder, phase = MAT_PHASE_LIQUID) - if(istype(holder) && holder.reagent_data) - var/list/rdata = holder.reagent_data[src] + var/reagent_data = REAGENT_DATA(holder, src) + if(istype(holder) && islist(reagent_data)) + var/list/rdata = reagent_data[src] if(rdata) var/data_name = rdata[DATA_MASK_NAME] if(data_name) @@ -54,8 +56,9 @@ return "something" /decl/material/proc/get_reagent_color(datum/reagents/holder) - if(istype(holder) && holder.reagent_data) - var/list/rdata = holder.reagent_data[src] + var/list/reagent_data = REAGENT_DATA(holder, src) + if(istype(holder) && islist(reagent_data)) + var/list/rdata = reagent_data[src] if(rdata) var/data_color = rdata[DATA_MASK_COLOR] if(data_color) diff --git a/code/modules/materials/material_drying.dm b/code/modules/materials/material_drying.dm index 089ed0d251d6..eb57baa10228 100644 --- a/code/modules/materials/material_drying.dm +++ b/code/modules/materials/material_drying.dm @@ -1,6 +1,6 @@ /obj/item/stack/material/fluid_act(var/datum/reagents/fluids) . = ..() - if(!QDELETED(src) && fluids?.total_volume && material?.tans_to) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids) && material?.tans_to) if(!dried_type) dried_type = type drying_wetness = get_max_drying_wetness() diff --git a/code/modules/materials/material_metabolism.dm b/code/modules/materials/material_metabolism.dm index 0223b10a6738..71714cf27fb7 100644 --- a/code/modules/materials/material_metabolism.dm +++ b/code/modules/materials/material_metabolism.dm @@ -57,7 +57,8 @@ if(length(vapor_products)) var/result_volume = REAGENT_VOLUME(holder, src) - var/temperature = holder?.my_atom?.temperature || T20C + var/atom/reagent_atom = REAGENT_GET_ATOM(holder) + var/temperature = reagent_atom?.temperature || T20C for(var/vapor in vapor_products) touching_turf.assume_gas(vapor, (result_volume * vapor_products[vapor]), temperature) holder.remove_reagent(src, result_volume) diff --git a/code/modules/mechs/equipment/engineering.dm b/code/modules/mechs/equipment/engineering.dm index d0ff7b2006b8..607226f683e0 100644 --- a/code/modules/mechs/equipment/engineering.dm +++ b/code/modules/mechs/equipment/engineering.dm @@ -32,10 +32,10 @@ icon_state = "mech_exting" /obj/item/chems/spray/extinguisher/mech/get_hardpoint_maptext() - return "[reagents.total_volume]/[reagents.maximum_volume]" + return "[REAGENT_TOTAL_VOLUME(reagents)]/[REAGENT_MAXIMUM_VOLUME(reagents)]" /obj/item/chems/spray/extinguisher/mech/get_hardpoint_status_value() - return reagents.total_volume/reagents.maximum_volume + return REAGENT_TOTAL_VOLUME(reagents)/REAGENT_MAXIMUM_VOLUME(reagents) /obj/item/mech_equipment/mounted_system/extinguisher icon_state = "mech_exting" diff --git a/code/modules/mining/machinery/material_compressor.dm b/code/modules/mining/machinery/material_compressor.dm index 5814e0edc7a9..32ae10fdc219 100644 --- a/code/modules/mining/machinery/material_compressor.dm +++ b/code/modules/mining/machinery/material_compressor.dm @@ -16,8 +16,8 @@ for(var/obj/item/O in input_turf) if(!O.simulated || O.anchored) continue - for(var/decl/material/reagent as anything in O.reagents?.reagent_volumes) - stored[reagent.type] = stored[reagent.type] + floor((O.reagents.reagent_volumes[reagent] / REAGENT_UNITS_PER_MATERIAL_UNIT) * 0.75) // liquid reagents, lossy + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(O.reagents)) + stored[reagent.type] = stored[reagent.type] + floor((REAGENT_VOLUME(O.reagents, reagent) / REAGENT_UNITS_PER_MATERIAL_UNIT) * 0.75) // liquid reagents, lossy for(var/mat in O.matter) stored[mat] = stored[mat] + O.matter[mat] qdel(O) diff --git a/code/modules/mining/machinery/material_extractor.dm b/code/modules/mining/machinery/material_extractor.dm index 6b19126688c1..b93b539f40af 100644 --- a/code/modules/mining/machinery/material_extractor.dm +++ b/code/modules/mining/machinery/material_extractor.dm @@ -70,7 +70,7 @@ if(!use_power || (stat & (BROKEN|NOPOWER))) return - if(reagents?.total_volume >= MAX_LIQUID) + if(REAGENT_TOTAL_VOLUME(reagents) >= MAX_LIQUID) return if(input_turf) @@ -83,8 +83,8 @@ eating.dropInto(output_turf) continue eaten++ - if(eating.reagents?.total_volume) - eating.reagents.trans_to_obj(src, eating.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(eating.reagents)) + eating.reagents.trans_to_obj(src, REAGENT_TOTAL_VOLUME(eating.reagents)) for(var/mtype in eating.matter) add_to_reagents(mtype, floor(eating.matter[mtype] * REAGENT_UNITS_PER_MATERIAL_UNIT)) qdel(eating) @@ -97,7 +97,7 @@ return var/adjusted_reagents = FALSE - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) adjusted_reagents = max(adjusted_reagents, process_non_liquid(reagent)) if(adjusted_reagents) @@ -199,10 +199,10 @@ if(href_list["dispense"]) var/reagent_index = text2num(href_list["dispense"]) - if(!reagent_index || length(reagents.reagent_volumes) < reagent_index) + if(!reagent_index || length(REAGENT_VOLUMES(reagents)) < reagent_index) return TOPIC_HANDLED - var/mtype = reagents.reagent_volumes[reagent_index] + var/mtype = REAGENT_VOLUME(reagents, reagent_index) // Only liquids are allowed to dispense. Otherwise, try to process the reagent. if(process_non_liquid(mtype)) @@ -232,20 +232,20 @@ data["dispense_amount"] = dispense_amount if(output_container) - var/curr_volume = output_container.reagents?.total_volume || 0 - var/max_volume = output_container.reagents?.maximum_volume || 0 + var/curr_volume = REAGENT_TOTAL_VOLUME(output_container.reagents) + var/max_volume = REAGENT_MAXIMUM_VOLUME(output_container.reagents) data["container"] = "[output_container.name] ([curr_volume] / [max_volume] U)" data["reagents"] = list() var/index = 0 - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) index += 1 // TODO: Must be revised once state changes are in. Reagent names might be a litle odd in the meantime. var/is_liquid = reagent.phase_at_temperature(temperature, ONE_ATMOSPHERE) == MAT_PHASE_LIQUID - data["reagents"] += list(list("label" = "[reagent.liquid_name] ([reagents.reagent_volumes[reagent]] U)", "index" = index, "liquid" = is_liquid)) + data["reagents"] += list(list("label" = "[reagent.liquid_name] ([REAGENT_VOLUME(reagents, reagent)] U)", "index" = index, "liquid" = is_liquid)) - data["full"] = reagents.total_volume >= MAX_LIQUID + data["full"] = REAGENT_TOTAL_VOLUME(reagents) >= MAX_LIQUID data["gas_pressure"] = gas_contents?.return_pressure() return data diff --git a/code/modules/mining/machinery/material_smelter.dm b/code/modules/mining/machinery/material_smelter.dm index b5f609877800..489327ec51c4 100644 --- a/code/modules/mining/machinery/material_smelter.dm +++ b/code/modules/mining/machinery/material_smelter.dm @@ -31,7 +31,7 @@ if(!(. = ..()) || !reagents) return - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) show_materials |= reagent.type /obj/machinery/material_processing/smeltery/ProcessAtomTemperature() @@ -70,8 +70,8 @@ eating.dropInto(output_turf) continue eaten++ - if(eating.reagents?.total_volume) - eating.reagents.trans_to_obj(src, floor(eating.reagents.total_volume * 0.75)) // liquid reagents, lossy + if(REAGENT_TOTAL_VOLUME(eating.reagents)) + eating.reagents.trans_to_obj(src, floor(REAGENT_TOTAL_VOLUME(eating.reagents) * 0.75)) // liquid reagents, lossy for(var/mtype in eating.matter) add_to_reagents(mtype, floor(eating.matter[mtype] * REAGENT_UNITS_PER_MATERIAL_UNIT)) qdel(eating) diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm index 79229f89faf0..a5b195163b27 100644 --- a/code/modules/mob/living/bot/farmbot.dm +++ b/code/modules/mob/living/bot/farmbot.dm @@ -40,7 +40,7 @@ . = ..() . += "
    Water tank: " if(tank) - . += "[tank.reagents.total_volume]/[tank.reagents.maximum_volume]" + . += "[REAGENT_TOTAL_VOLUME(tank.reagents)]/[REAGENT_MAXIMUM_VOLUME(tank.reagents)]" else . += "error: not found" @@ -118,7 +118,7 @@ if(confirmTarget(tray)) target = tray return - if(!target && refills_water && tank && tank.reagents.total_volume < tank.reagents.maximum_volume) + if(!target && refills_water && tank && REAGENT_TOTAL_VOLUME(tank.reagents) < REAGENT_MAXIMUM_VOLUME(tank.reagents)) for(var/obj/structure/hygiene/sink/source in view(7, src)) target = source return @@ -171,13 +171,13 @@ update_icon() T.update_icon() else if(istype(A, /obj/structure/hygiene/sink)) - if(!tank || tank.reagents.total_volume >= tank.reagents.maximum_volume) + if(!tank || REAGENT_TOTAL_VOLUME(tank.reagents) >= REAGENT_MAXIMUM_VOLUME(tank.reagents)) return TRUE action = "water" update_icon() visible_message("[src] starts refilling its tank from \the [A].") busy = 1 - while(do_after(src, 10) && tank.reagents.total_volume < tank.reagents.maximum_volume) + while(do_after(src, 10) && REAGENT_TOTAL_VOLUME(tank.reagents) < REAGENT_MAXIMUM_VOLUME(tank.reagents)) tank.add_to_reagents(/decl/material/liquid/water, 100) if(prob(5)) playsound(loc, 'sound/effects/slosh.ogg', 25, 1) @@ -228,7 +228,7 @@ return 0 if(istype(target, /obj/structure/hygiene/sink)) - if(!tank || tank.reagents.total_volume >= tank.reagents.maximum_volume) + if(!tank || REAGENT_TOTAL_VOLUME(tank.reagents) >= REAGENT_MAXIMUM_VOLUME(tank.reagents)) return 0 return 1 @@ -242,13 +242,13 @@ if(tray.dead && removes_dead || tray.harvest && collects_produce) return FARMBOT_COLLECT - else if(refills_water && tray.waterlevel < 40 && !tray.reagents.has_reagent(/decl/material/liquid/water) && (tank?.reagents.total_volume > 0)) + else if(refills_water && tray.waterlevel < 40 && !tray.reagents.has_reagent(/decl/material/liquid/water) && (REAGENT_TOTAL_VOLUME(tank?.reagents) > 0)) return FARMBOT_WATER else if(uproots_weeds && tray.weedlevel > 3) return FARMBOT_UPROOT - else if(replaces_nutriment && tray.nutrilevel < 1 && tray.reagents.total_volume < 1) + else if(replaces_nutriment && tray.nutrilevel < 1 && REAGENT_TOTAL_VOLUME(tray.reagents) < 1) return FARMBOT_NUTRIMENT return 0 diff --git a/code/modules/mob/living/bot/medibot.dm b/code/modules/mob/living/bot/medibot.dm index 9d1b66b783d5..30a2c37e4250 100644 --- a/code/modules/mob/living/bot/medibot.dm +++ b/code/modules/mob/living/bot/medibot.dm @@ -199,7 +199,7 @@ . = ..() . += "
    Beaker: " if(reagent_glass) - . += "Loaded \[[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]\]" + . += "Loaded \[[REAGENT_TOTAL_VOLUME(reagent_glass.reagents)]/[REAGENT_MAXIMUM_VOLUME(reagent_glass.reagents)]\]" else . += "None loaded" @@ -306,7 +306,7 @@ // If they're injured, we're using a beaker, and they don't have on of the chems in the beaker if(reagent_glass && use_beaker && ((patient.get_damage(BRUTE) >= heal_threshold) || (patient.get_damage(TOX) >= heal_threshold) || (patient.get_damage(TOX) >= heal_threshold) || (patient.get_damage(OXY) >= (heal_threshold + 15)))) - for(var/decl/material/reagent as anything in reagent_glass.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagent_glass.reagents)) if(!patient.reagents.has_reagent(reagent)) return 1 continue diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index d33940b0c301..ad99cb4bc326 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -64,8 +64,8 @@ /decl/human_examination/contact_reagents/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) var/datum/reagents/touching_reagents = source.get_contact_reagents() - if(touching_reagents?.total_volume >= 1) - var/saturation = touching_reagents.total_volume / touching_reagents.maximum_volume + if(REAGENT_TOTAL_VOLUME(touching_reagents) >= 1) + var/saturation = REAGENT_TOTAL_VOLUME(touching_reagents) / REAGENT_MAXIMUM_VOLUME(touching_reagents) if(saturation > 0.9) . += "[pronouns.He] [pronouns.is] completely saturated." else if(saturation > 0.6) diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index bd8bec8ebf45..8f21c27edd60 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -302,7 +302,7 @@ var/obj/item/organ/internal/stomach/stomach = get_organ(BP_STOMACH, /obj/item/organ/internal/stomach) var/nothing_to_puke = FALSE if(should_have_organ(BP_STOMACH)) - if(!stomach || (stomach.ingested.total_volume <= 0 && stomach.contents.len == 0)) + if(!stomach || (REAGENT_TOTAL_VOLUME(stomach.ingested) <= 0 && stomach.contents.len == 0)) nothing_to_puke = TRUE else if(!(locate(/mob) in contents)) nothing_to_puke = TRUE @@ -328,8 +328,8 @@ var/turf/location = loc if(istype(location) && location.simulated) var/obj/effect/decal/cleanable/vomit/splat = new /obj/effect/decal/cleanable/vomit(location) - if(stomach.ingested.total_volume) - stomach.ingested.trans_to_obj(splat, min(15, stomach.ingested.total_volume)) + if(REAGENT_TOTAL_VOLUME(stomach.ingested)) + stomach.ingested.trans_to_obj(splat, min(15, REAGENT_TOTAL_VOLUME(stomach.ingested))) handle_additional_vomit_reagents(splat) splat.update_icon() @@ -880,7 +880,7 @@ /mob/living/human/fluid_act(var/datum/reagents/fluids) ..() - if(!QDELETED(src) && fluids?.total_volume) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids)) species.fluid_act(src, fluids) /mob/living/human/proc/set_background_value(var/cat_type, var/decl/background_detail/_background, var/defer_language_update) diff --git a/code/modules/mob/living/human/human_blood.dm b/code/modules/mob/living/human/human_blood.dm index fd5f388ee845..7c0e00da1270 100644 --- a/code/modules/mob/living/human/human_blood.dm +++ b/code/modules/mob/living/human/human_blood.dm @@ -30,18 +30,20 @@ make_blood() if(!should_have_organ(BP_HEART)) - vessel.clear_reagents() - vessel.maximum_volume = 0 + if(istype(vessel)) + vessel.clear_reagents() + REAGENT_SET_MAX_VOL(vessel, 0) return - if(vessel.total_volume < species.blood_volume) - vessel.maximum_volume = species.blood_volume - adjust_blood(species.blood_volume - vessel.total_volume) - else if(vessel.total_volume > species.blood_volume) - vessel.remove_any(vessel.total_volume - species.blood_volume) - vessel.maximum_volume = species.blood_volume + if(istype(vessel)) + if(REAGENT_TOTAL_VOLUME(vessel) < species.blood_volume) + REAGENT_SET_MAX_VOL(vessel, species.blood_volume) + adjust_blood(species.blood_volume - REAGENT_TOTAL_VOLUME(vessel)) + else if(REAGENT_TOTAL_VOLUME(vessel) > species.blood_volume) + vessel.remove_any(REAGENT_TOTAL_VOLUME(vessel) - species.blood_volume) + REAGENT_SET_MAX_VOL(vessel, species.blood_volume) - LAZYSET(vessel.reagent_data, species.blood_reagent, list( + REAGENT_SET_DATA(vessel, species.blood_reagent, list( DATA_BLOOD_DONOR = weakref(src), DATA_BLOOD_SPECIES = get_species_name(), DATA_BLOOD_DNA = get_unique_enzymes(), @@ -54,9 +56,9 @@ /mob/living/human/proc/drip(var/amt, var/tar = src, var/ddir) var/datum/reagents/bloodstream = get_injected_reagents() if(remove_blood(amt)) - if(bloodstream.total_volume && vessel.total_volume) - var/chem_share = round(0.3 * amt * (bloodstream.total_volume/vessel.total_volume), 0.01) - bloodstream.remove_any(chem_share * bloodstream.total_volume) + if(REAGENT_TOTAL_VOLUME(bloodstream) && REAGENT_TOTAL_VOLUME(vessel)) + var/chem_share = round(0.3 * amt * (REAGENT_TOTAL_VOLUME(bloodstream) / REAGENT_TOTAL_VOLUME(vessel)), 0.01) + bloodstream.remove_any(chem_share * REAGENT_TOTAL_VOLUME(bloodstream)) blood_splatter(tar, src, (ddir && ddir>0), spray_dir = ddir) return amt return 0 @@ -154,7 +156,7 @@ if(stress_modifier) amount *= 1-(get_config_value(/decl/config/num/health_stress_blood_recovery_constant) * stress_modifier) - var/blood_volume_raw = vessel.total_volume + var/blood_volume_raw = REAGENT_TOTAL_VOLUME(vessel) amount = max(0,min(amount, species.blood_volume - blood_volume_raw)) if(amount) adjust_blood(amount, get_blood_data()) @@ -170,16 +172,16 @@ reagents.trans_to_obj(container, amount) return 1 - if(vessel.total_volume < amount) + if(REAGENT_TOTAL_VOLUME(vessel) < amount) return null if(vessel.has_reagent(species.blood_reagent)) - LAZYSET(vessel.reagent_data, species.blood_reagent, get_blood_data()) + REAGENT_SET_DATA(vessel, species.blood_reagent, get_blood_data()) vessel.trans_to_holder(container.reagents, amount) return 1 //Percentage of maximum blood volume. /mob/living/human/proc/get_blood_volume() - return species.blood_volume ? round((vessel.total_volume/species.blood_volume)*100) : 0 + return species.blood_volume ? round((REAGENT_TOTAL_VOLUME(vessel)/species.blood_volume)*100) : 0 //Percentage of maximum blood volume, affected by the condition of circulation organs /mob/living/human/proc/get_blood_circulation() diff --git a/code/modules/mob/living/human/human_organs.dm b/code/modules/mob/living/human/human_organs.dm index 3b27e39e2472..6549dcb8a6f8 100644 --- a/code/modules/mob/living/human/human_organs.dm +++ b/code/modules/mob/living/human/human_organs.dm @@ -219,8 +219,8 @@ if(!(. = ..())) return //Move some blood over to the organ - if(!BP_IS_PROSTHETIC(O) && O.species && O.reagents?.total_volume < 5) - vessel.trans_to(O, 5 - O.reagents.total_volume, 1, 1) + if(!BP_IS_PROSTHETIC(O) && O.species && REAGENT_TOTAL_VOLUME(O.reagents) < 5) + vessel.trans_to(O, 5 - REAGENT_TOTAL_VOLUME(O.reagents), 1, 1) /mob/living/human/is_asystole() diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 189965c89c8c..ff9a8613746d 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -271,10 +271,10 @@ if(!loc) return var/datum/reagents/touching_reagents = get_contact_reagents() - if(touching_reagents?.total_volume <= FLUID_MINIMUM_TRANSFER) + if(REAGENT_TOTAL_VOLUME(touching_reagents) <= FLUID_MINIMUM_TRANSFER) touching_reagents?.clear_reagents() return - var/drip_amount = max(FLUID_MINIMUM_TRANSFER, round(touching_reagents.total_volume * 0.2)) + var/drip_amount = max(FLUID_MINIMUM_TRANSFER, round(REAGENT_TOTAL_VOLUME(touching_reagents) * 0.2)) if(drip_amount) touching_reagents.trans_to(loc, drip_amount) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 508476cc74d3..956ae903000c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -805,22 +805,22 @@ default behaviour is: var/inhale_amount = 0 if(inhaled) inhale_amount = rand(2,5) - T.reagents?.trans_to_holder(inhaled, min(T.reagents.total_volume, inhale_amount)) + T.reagents?.trans_to_holder(inhaled, min(REAGENT_TOTAL_VOLUME(T.reagents), inhale_amount)) if(ingested) var/ingest_amount = 5 - inhale_amount - reagents?.trans_to_holder(ingested, min(T.reagents.total_volume, ingest_amount)) + reagents?.trans_to_holder(ingested, min(REAGENT_TOTAL_VOLUME(T.reagents), ingest_amount)) T.show_bubbles() return TRUE // Presumably chemical smoke can't be breathed while you're underwater. /mob/living/fluid_act(var/datum/reagents/fluids) ..() - if(QDELETED(src) || fluids?.total_volume < FLUID_PUDDLE) + if(QDELETED(src) || REAGENT_TOTAL_VOLUME(fluids) < FLUID_PUDDLE) return fluids.touch_mob(src) - if(QDELETED(src) || fluids?.total_volume < FLUID_PUDDLE) + if(QDELETED(src) || REAGENT_TOTAL_VOLUME(fluids) < FLUID_PUDDLE) return - var/on_turf = fluids.my_atom == get_turf(src) + var/on_turf = REAGENT_GET_ATOM(fluids) == get_turf(src) for(var/atom/movable/A as anything in get_equipped_items(TRUE)) if(!A.simulated) continue @@ -829,12 +829,12 @@ default behaviour is: if(on_turf && !A.submerged()) continue A.fluid_act(fluids) - if(QDELETED(src) || !fluids.total_volume) + if(QDELETED(src) || !REAGENT_TOTAL_VOLUME(fluids)) return // TODO: review saturation logic so we can end up with more than like 15 water in our contact reagents. var/datum/reagents/touching_reagents = get_contact_reagents() if(touching_reagents) - var/saturation = min(fluids.total_volume, round(mob_size * 1.5 * reagent_permeability()) - touching_reagents.total_volume) + var/saturation = min(REAGENT_TOTAL_VOLUME(fluids), round(mob_size * 1.5 * reagent_permeability()) - REAGENT_TOTAL_VOLUME(touching_reagents)) if(saturation > 0) fluids.trans_to_holder(touching_reagents, saturation) @@ -985,7 +985,8 @@ default behaviour is: /mob/living/proc/get_food_satiation(consumption_method = EATING_METHOD_EAT) . = (consumption_method == EATING_METHOD_EAT) ? get_nutrition() : get_hydration() - . += get_ingested_reagents()?.total_volume * 5 + var/datum/reagents/ingested = get_ingested_reagents() + . += REAGENT_TOTAL_VOLUME(ingested) * 5 /mob/living/proc/get_ingested_reagents() RETURN_TYPE(/datum/reagents) @@ -1370,7 +1371,7 @@ default behaviour is: //flush away reagents on the skin var/datum/reagents/touching_reagents = get_contact_reagents() if(touching_reagents) - var/remove_amount = touching_reagents.maximum_volume * reagent_permeability() //take off your suit first + var/remove_amount = REAGENT_MAXIMUM_VOLUME(touching_reagents) * reagent_permeability() //take off your suit first touching_reagents.remove_any(remove_amount) var/obj/item/mask = get_equipped_item(slot_wear_mask_str) @@ -1554,11 +1555,11 @@ default behaviour is: var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str) if(istype(shoes)) shoes.handle_movement(src, MOVING_QUICKLY(src)) - if(shoes.coating && shoes.coating.total_volume > 1) + if(shoes.coating && REAGENT_TOTAL_VOLUME(shoes.coating) > 1) source = shoes else for(var/obj/item/organ/external/stomper in get_organs_by_categories(global.child_stance_limbs)) - if(stomper.coating?.total_volume > 1) + if(REAGENT_TOTAL_VOLUME(stomper.coating) > 1) source = stomper break @@ -1571,7 +1572,10 @@ default behaviour is: if(!use_move_trail) return - var/decl/material/contaminant = source.coating.reagent_volumes[1] // take [1] instead of primary reagent to match what remove_any will probably remove + if(!istype(source.coating)) + return + + var/decl/material/contaminant = UNLINT(source.coating.reagent_volumes[1]) // take [1] instead of primary reagent to match what remove_any will probably remove if(!T.can_show_coating_footprints(contaminant)) return /// An associative list of DNA unique enzymes -> blood type. Used by forensics, mostly. @@ -1946,7 +1950,7 @@ default behaviour is: . += SPAN_WARNING("\The [src] will be ready to be sheared in [ceil((shearable.next_fleece-world.time) / 10)] second\s.") if(has_extension(src, /datum/extension/milkable)) var/datum/extension/milkable/milkable = get_extension(src, /datum/extension/milkable) - if(milkable.udder.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(milkable.udder) > 0) . += SPAN_NOTICE("\The [src] can be milked into a bucket or other container.") else . += SPAN_WARNING("\The [src] cannot currently be milked.") diff --git a/code/modules/mob/living/living_blood.dm b/code/modules/mob/living/living_blood.dm index dffee4ed0dcc..bdd172360d72 100644 --- a/code/modules/mob/living/living_blood.dm +++ b/code/modules/mob/living/living_blood.dm @@ -18,7 +18,7 @@ data[DATA_BLOOD_SPECIES] = species_name var/list/temp_chem = list() - for(var/decl/material/reagent as anything in reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) temp_chem[reagent.type] = REAGENT_VOLUME(reagents, reagent) data[DATA_BLOOD_TRACE_CHEM] = temp_chem data[DATA_BLOOD_DOSE_CHEM] = _chem_doses?.Copy() || list() diff --git a/code/modules/mob/living/living_breath.dm b/code/modules/mob/living/living_breath.dm index 43d33f644d02..2731659ccbf1 100644 --- a/code/modules/mob/living/living_breath.dm +++ b/code/modules/mob/living/living_breath.dm @@ -132,7 +132,7 @@ return for(var/obj/effect/effect/smoke/chem/smoke in view(1, src)) - if(smoke.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(smoke.reagents)) smoke.reagents.trans_to_mob(src, 5, CHEM_INGEST, copy = 1) smoke.reagents.trans_to_mob(src, 5, CHEM_INJECT, copy = 1) // I dunno, maybe the reagents enter the blood stream through the lungs? diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm index 3e9944558ac7..9046c5549b4d 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_emergency.dm @@ -70,8 +70,8 @@ /obj/item/robot_module/flying/emergency/respawn_consumable(var/mob/living/silicon/robot/robot, var/amount) var/obj/item/chems/spray/PS = emag - if(PS && PS.reagents.total_volume < PS.reagents.maximum_volume) - var/adding = min(PS.reagents.maximum_volume-PS.reagents.total_volume, 2*amount) + if(PS && REAGENT_TOTAL_VOLUME(PS.reagents) < REAGENT_MAXIMUM_VOLUME(PS.reagents)) + var/adding = min(REAGENT_MAXIMUM_VOLUME(PS.reagents)-REAGENT_TOTAL_VOLUME(PS.reagents), 2*amount) if(adding > 0) PS.add_to_reagents(/decl/material/liquid/acid/polyacid, adding) ..() diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm index 9ea5d4ee7ef6..1ba23a2809c1 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_forensics.dm @@ -41,8 +41,8 @@ if(!luminol) luminol = new(src) equipment += luminol - if(luminol.reagents.total_volume < luminol.reagents.maximum_volume) - var/adding = min(luminol.reagents.maximum_volume-luminol.reagents.total_volume, 2*amount) + if(REAGENT_TOTAL_VOLUME(luminol.reagents) < REAGENT_MAXIMUM_VOLUME(luminol.reagents)) + var/adding = min(REAGENT_MAXIMUM_VOLUME(luminol.reagents)-REAGENT_TOTAL_VOLUME(luminol.reagents), 2*amount) if(adding > 0) luminol.add_to_reagents(/decl/material/liquid/luminol, adding) ..() diff --git a/code/modules/mob/living/simple_animal/hostile/vagrant.dm b/code/modules/mob/living/simple_animal/hostile/vagrant.dm index 2724d98aede4..28731e48512a 100644 --- a/code/modules/mob/living/simple_animal/hostile/vagrant.dm +++ b/code/modules/mob/living/simple_animal/hostile/vagrant.dm @@ -54,7 +54,7 @@ gripping = null else if(gripping.should_have_organ(BP_HEART)) - var/blood_volume = round(gripping.vessel.total_volume) + var/blood_volume = round(REAGENT_TOTAL_VOLUME(gripping.vessel)) if(blood_volume > 5) gripping.vessel.remove_any(blood_per_tick) heal_overall_damage(health_per_tick) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 43b674835701..bebc25e3b4b5 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -253,7 +253,7 @@ log_admin("[key_name(src)] has transformed into a zombie!") SET_STATUS_MAX(src, STAT_WEAK, 5) if (should_have_organ(BP_HEART)) - adjust_blood(species.blood_volume - vessel.total_volume) + adjust_blood(species.blood_volume - REAGENT_TOTAL_VOLUME(vessel)) for (var/o in get_external_organs()) var/obj/item/organ/organ = o if (!BP_IS_PROSTHETIC(organ)) diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm index 371c957ffc26..4617c8b74a22 100644 --- a/code/modules/organs/internal/heart.dm +++ b/code/modules/organs/internal/heart.dm @@ -147,7 +147,7 @@ blood_max += wound.damage / 40 if(temp.status & ORGAN_ARTERY_CUT) - var/bleed_amount = floor((owner.vessel.total_volume / (temp.applied_pressure || !open_wound ? 400 : 250))*temp.arterial_bleed_severity) + var/bleed_amount = floor((REAGENT_TOTAL_VOLUME(owner.vessel) / (temp.applied_pressure || !open_wound ? 400 : 250))*temp.arterial_bleed_severity) if(bleed_amount) if(open_wound) blood_max += bleed_amount diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 0fc0f1ea4945..fa5cbde82863 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -37,20 +37,19 @@ /obj/item/organ/internal/lungs/initialize_reagents(populate) if(!inhaled) inhaled = new/datum/reagents/metabolism(240, (owner || src), CHEM_INHALE) - if(!inhaled.my_atom) - inhaled.my_atom = src + REAGENT_SET_ATOM(inhaled, src) . = ..() /obj/item/organ/internal/lungs/do_install(mob/living/human/target, obj/item/organ/external/affected, in_place) if(!(. = ..())) return - inhaled.my_atom = owner + REAGENT_SET_ATOM(inhaled, owner) inhaled.parent = owner /obj/item/organ/internal/lungs/do_uninstall(in_place, detach, ignore_children) . = ..() if(inhaled) - inhaled.my_atom = src + REAGENT_SET_ATOM(inhaled, src) inhaled.parent = null /obj/item/organ/internal/lungs/proc/can_drown() @@ -376,5 +375,5 @@ last_cough = world.time // Coughing clears out 1-2 reagents from the lungs. - if(lung.inhaled.total_volume > 0 && loc) + if(REAGENT_TOTAL_VOLUME(lung.inhaled) > 0 && loc) lung.inhaled.splash(loc, rand(1, 2)) \ No newline at end of file diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm index 4cbde4e21255..5c98aeea901d 100644 --- a/code/modules/organs/internal/stomach.dm +++ b/code/modules/organs/internal/stomach.dm @@ -24,26 +24,25 @@ /obj/item/organ/internal/stomach/initialize_reagents(populate) if(!ingested) ingested = new/datum/reagents/metabolism(240, (owner || src), CHEM_INGEST) - if(!ingested.my_atom) - ingested.my_atom = src + REAGENT_SET_ATOM(ingested, src) . = ..() +/obj/item/organ/internal/stomach/do_install() + . = ..() + REAGENT_SET_ATOM(ingested, owner) + ingested.parent = owner + /obj/item/organ/internal/stomach/do_uninstall(in_place, detach, ignore_children) . = ..() if(ingested) //Don't bother if we're destroying - ingested.my_atom = src + REAGENT_SET_ATOM(ingested, src) ingested.parent = null -/obj/item/organ/internal/stomach/do_install() - . = ..() - ingested.my_atom = owner - ingested.parent = owner - /obj/item/organ/internal/stomach/proc/can_eat_atom(var/atom/movable/food) return !isnull(get_devour_time(food)) /obj/item/organ/internal/stomach/proc/is_full(var/atom/movable/food) - var/total = floor(ingested.total_volume / 10) + var/total = floor(REAGENT_TOTAL_VOLUME(ingested) / 10) for(var/a in contents + food) if(ismob(a)) var/mob/M = a @@ -123,7 +122,7 @@ owner.seizure() // Alcohol counts as double volume for the purposes of vomit probability - var/effective_volume = ingested.total_volume + alcohol_volume + var/effective_volume = REAGENT_TOTAL_VOLUME(ingested) + alcohol_volume // Just over the limit, the probability will be low. It rises a lot such that at double ingested it's 64% chance. var/vomit_probability = (effective_volume / STOMACH_VOLUME) ** 6 diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index aa400ec3b073..c255185fed0f 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -129,7 +129,7 @@ if(bodytype) reagent_to_add = bodytype.edible_reagent // can set this to null and skip the next block if(reagent_to_add) - add_to_reagents(reagent_to_add, reagents.maximum_volume) + add_to_reagents(reagent_to_add, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/organ/proc/copy_from_mob_snapshot(var/datum/mob_snapshot/supplied_appearance) if(supplied_appearance != organ_appearance) // Hacky. Is this ever used? Do any organs ever have DNA set before setup_as_organic? @@ -236,7 +236,7 @@ return if(!owner && reagents) - if(prob(40) && reagents.total_volume >= 0.1) + if(prob(40) && REAGENT_TOTAL_VOLUME(reagents) >= 0.1) if(reagents.has_reagent(/decl/material/liquid/blood)) blood_splatter(get_turf(src), src, 1) remove_any_reagents(0.1) @@ -261,8 +261,9 @@ /obj/item/organ/proc/handle_ailment(var/datum/ailment/ailment) if(ailment.treated_by_reagent_type) for(var/datum/reagents/source as anything in owner.get_metabolizing_reagent_holders()) - for(var/decl/material/reagent as anything in source.reagent_volumes) - if(ailment.treated_by_medication(reagent.type, source.reagent_volumes[reagent])) + var/source_volumes = REAGENT_VOLUMES(source) + for(var/decl/material/reagent as anything in source_volumes) + if(ailment.treated_by_medication(reagent.type, source_volumes[reagent])) ailment.was_treated_by_medication(source, reagent.type) return if(ailment.treated_by_chem_effect && owner.has_chemical_effect(ailment.treated_by_chem_effect, ailment.treated_by_chem_effect_strength)) @@ -423,8 +424,8 @@ var/obj/item/food/organ/yum = new(get_turf(src)) yum.SetName(name) yum.appearance = src - if(reagents && reagents.total_volume) - reagents.trans_to(yum, reagents.total_volume) + if(reagents && REAGENT_TOTAL_VOLUME(reagents)) + reagents.trans_to(yum, REAGENT_TOTAL_VOLUME(reagents)) transfer_fingerprints_to(yum) if(user) user.put_in_active_hand(yum) diff --git a/code/modules/paperwork/pen/quill_and_ink.dm b/code/modules/paperwork/pen/quill_and_ink.dm index 7cb3ba4a1dc1..c6cda0c11177 100644 --- a/code/modules/paperwork/pen/quill_and_ink.dm +++ b/code/modules/paperwork/pen/quill_and_ink.dm @@ -88,7 +88,7 @@ if(current_uses >= quill.max_uses) to_chat(user, SPAN_WARNING("\The [quill] doesn't need any more ink!")) return TRUE - if(reagents?.total_liquid_volume <= 0) + if(REAGENT_TOTAL_LIQUID_VOLUME(reagents) <= 0) to_chat(user, SPAN_WARNING("\The [src] is empty!")) return TRUE to_chat(user, SPAN_NOTICE("You dip \the [quill] into \the [src].")) diff --git a/code/modules/paperwork/pen/reagent_pen.dm b/code/modules/paperwork/pen/reagent_pen.dm index dfd52e06b040..cc0c50f51bed 100644 --- a/code/modules/paperwork/pen/reagent_pen.dm +++ b/code/modules/paperwork/pen/reagent_pen.dm @@ -16,7 +16,7 @@ to_chat(user, SPAN_NOTICE("You begin hunting for an injection port on your suit.")) if(!user.do_skilled(INJECTION_PORT_DELAY, SKILL_MEDICAL, target)) return TRUE - if(reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) if(target.reagents) var/contained_reagents = reagents.get_reagents() var/trans = reagents.trans_to_mob(target, 30, CHEM_INJECT) @@ -35,4 +35,4 @@ desc = "It's \a [stroke_color_name] [medium_name] pen with a sharp point and a carefully engraved \"Waffle Co.\"." /obj/item/pen/reagent/sleepy/populate_reagents() - add_to_reagents(/decl/material/liquid/paralytics, round(reagents.maximum_volume/2)) + add_to_reagents(/decl/material/liquid/paralytics, round(REAGENT_MAXIMUM_VOLUME(reagents)/2)) diff --git a/code/modules/paperwork/toner_cartridge.dm b/code/modules/paperwork/toner_cartridge.dm index 8e4e6155f479..7c1eedfb5991 100644 --- a/code/modules/paperwork/toner_cartridge.dm +++ b/code/modules/paperwork/toner_cartridge.dm @@ -18,12 +18,12 @@ /obj/item/chems/toner_cartridge/populate_reagents() //Normally this would be toner powder, but probably not worth making a material for that. - add_to_reagents(/decl/material/liquid/paint, reagents.maximum_volume/2) - add_to_reagents(/decl/material/liquid/pigment/black, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/paint, REAGENT_MAXIMUM_VOLUME(reagents)/2) + add_to_reagents(/decl/material/liquid/pigment/black, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/toner_cartridge/dump_contents(atom/forced_loc = loc, mob/user) . = ..() - reagents?.splash(get_turf(forced_loc), reagents.total_volume) + reagents?.splash(get_turf(forced_loc), REAGENT_TOTAL_VOLUME(reagents)) /obj/item/chems/toner_cartridge/physically_destroyed(skip_qdel) material.place_shards(get_turf(src), 2) @@ -40,7 +40,7 @@ /obj/item/chems/toner_cartridge/proc/get_amount_toner_max() //Since ink is paint + pigment in a 1:1 ratio, only half the volume is actually usable - return round(reagents.maximum_volume / 2, 0.01) + return round(REAGENT_MAXIMUM_VOLUME(reagents) / 2, 0.01) /obj/item/chems/toner_cartridge/proc/use_toner(var/amount) if(!reagents) diff --git a/code/modules/power/fuel_assembly/fuel_compressor.dm b/code/modules/power/fuel_assembly/fuel_compressor.dm index d07651603a4a..77e506b3883f 100644 --- a/code/modules/power/fuel_assembly/fuel_compressor.dm +++ b/code/modules/power/fuel_assembly/fuel_compressor.dm @@ -120,8 +120,8 @@ return add_material(used_item, user) || ..() /obj/machinery/fuel_compressor/proc/add_material(var/obj/item/thing, var/mob/user) - if(istype(thing) && thing.reagents && thing.reagents.total_volume && ATOM_IS_OPEN_CONTAINER(thing)) - for(var/decl/material/reagent as anything in thing.reagents.reagent_volumes) + if(istype(thing) && thing.reagents && REAGENT_TOTAL_VOLUME(thing.reagents) && ATOM_IS_OPEN_CONTAINER(thing)) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(thing.reagents)) var/taking_reagent = REAGENT_VOLUME(thing.reagents, reagent) thing.remove_from_reagents(reagent, taking_reagent) stored_material[reagent.type] += taking_reagent diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 7d2788d0ca3a..82591b2c3d4d 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -366,8 +366,8 @@ data["fuel_type"] = capitalize(sheet_name) data["uses_coolant"] = !!reagents - data["coolant_stored"] = reagents?.total_volume - data["coolant_capacity"] = reagents?.maximum_volume + data["coolant_stored"] = REAGENT_TOTAL_VOLUME(reagents) + data["coolant_capacity"] = REAGENT_MAXIMUM_VOLUME(reagents) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -461,7 +461,7 @@ /obj/machinery/port_gen/pacman/super/potato/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "Auxilary tank shows [reagents.total_volume]u of liquid in it." + . += "Auxilary tank shows [REAGENT_TOTAL_VOLUME(reagents)]u of liquid in it." /obj/machinery/port_gen/pacman/super/potato/UseFuel() if(reagents.has_reagent(/decl/material/liquid/alcohol/vodka)) diff --git a/code/modules/projectiles/ammunition/chemdart.dm b/code/modules/projectiles/ammunition/chemdart.dm index 97ce9edf74ef..bc6423ac6aab 100644 --- a/code/modules/projectiles/ammunition/chemdart.dm +++ b/code/modules/projectiles/ammunition/chemdart.dm @@ -10,10 +10,10 @@ chem_volume = 15 /obj/item/projectile/bullet/chemdart/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) - if(reagents?.total_volume && blocked < 100 && isliving(target)) + if(REAGENT_TOTAL_VOLUME(reagents) && blocked < 100 && isliving(target)) var/mob/living/L = target if(L.can_inject(null, def_zone) == CAN_INJECT) - reagents.trans_to_mob(L, reagents.total_volume, CHEM_INJECT) + reagents.trans_to_mob(L, REAGENT_TOTAL_VOLUME(reagents), CHEM_INJECT) /obj/item/ammo_casing/chemdart name = "chemical dart" diff --git a/code/modules/projectiles/guns/launcher/syringe_gun.dm b/code/modules/projectiles/guns/launcher/syringe_gun.dm index 3d1d65ad2847..ea57b6b0f4a1 100644 --- a/code/modules/projectiles/guns/launcher/syringe_gun.dm +++ b/code/modules/projectiles/guns/launcher/syringe_gun.dm @@ -60,7 +60,7 @@ //unfortuately we don't know where the dart will actually hit, since that's done by the parent. if(L.can_inject(null, ran_zone(TT.target_zone, 30, L)) == CAN_INJECT && syringe.reagents) var/reagent_log = syringe.reagents.get_reagents() - syringe.reagents.trans_to_mob(L, syringe.reagents.total_volume, CHEM_INJECT) + syringe.reagents.trans_to_mob(L, REAGENT_TOTAL_VOLUME(syringe.reagents), CHEM_INJECT) admin_inject_log(TT.thrower? TT.thrower : null, L, src, reagent_log, 15, violent=1) syringe.break_syringe(ishuman(hit_atom)? hit_atom : null) diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm index 8b46af8c094a..ddf20a47a306 100644 --- a/code/modules/projectiles/guns/projectile/dartgun.dm +++ b/code/modules/projectiles/guns/projectile/dartgun.dm @@ -57,10 +57,11 @@ if (beakers.len) . += SPAN_NOTICE("\The [src] contains:") for(var/obj/item/chems/glass/beaker/B in beakers) - if(B.reagents && LAZYLEN(B.reagents?.reagent_volumes)) - for(var/decl/material/reagent as anything in B.reagents.liquid_volumes) + var/reagent_volumes = REAGENT_VOLUMES(B.reagents) + if(B.reagents && LAZYLEN(reagent_volumes)) + for(var/decl/material/reagent as anything in REAGENT_LIQUID_VOLUMES(B.reagents)) . += SPAN_NOTICE("[LIQUID_VOLUME(B.reagents, reagent)] units of [reagent.get_reagent_name(B.reagents, MAT_PHASE_LIQUID)]") - for(var/decl/material/reagent as anything in B.reagents.solid_volumes) + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(B.reagents)) . += SPAN_NOTICE("[SOLID_VOLUME(B.reagents, reagent)] units of [reagent.get_reagent_name(B.reagents, MAT_PHASE_SOLID)]") /obj/item/gun/projectile/dartgun/attackby(obj/item/used_item, mob/user) @@ -90,7 +91,7 @@ //fills the given dart with reagents /obj/item/gun/projectile/dartgun/proc/fill_dart(var/obj/item/projectile/bullet/chemdart/dart) if(length(mixing)) - var/mix_amount = dart.reagents?.total_volume/length(mixing) + var/mix_amount = REAGENT_TOTAL_VOLUME(dart.reagents)/length(mixing) for(var/obj/item/chems/glass/beaker/B in mixing) B.reagents.trans_to_obj(dart, mix_amount) @@ -109,8 +110,9 @@ if(!istype(B)) continue dat += "Beaker [i] contains: " - if(B.reagents && LAZYLEN(B.reagents.reagent_volumes)) - for(var/decl/material/reagent as anything in B.reagents.reagent_volumes) + var/reagent_volumes = REAGENT_VOLUMES(B.reagents) + if(LAZYLEN(reagent_volumes)) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(B.reagents)) dat += "
    [REAGENT_VOLUME(B.reagents, reagent)] unit\s of [reagent.get_reagent_name(B.reagents)], " if(B in mixing) dat += "Mixing " diff --git a/code/modules/reagents/Chemistry-Grinder.dm b/code/modules/reagents/Chemistry-Grinder.dm index ca34cc020990..1c4f5c85190b 100644 --- a/code/modules/reagents/Chemistry-Grinder.dm +++ b/code/modules/reagents/Chemistry-Grinder.dm @@ -74,7 +74,7 @@ if(used_item.storage) var/failed = TRUE for(var/obj/item/G in used_item) - if(!G.reagents || !G.reagents.total_volume) + if(!G.reagents || !REAGENT_TOTAL_VOLUME(G.reagents)) continue failed = FALSE used_item.storage.remove_from_storage(user, G, src) @@ -105,7 +105,7 @@ to_chat(user, SPAN_NOTICE("\The [grind_material.solid_name] cannot be ground down to any usable reagents.")) return TRUE - else if(!used_item.reagents?.total_volume) + else if(!REAGENT_TOTAL_VOLUME(used_item.reagents)) to_chat(user, SPAN_NOTICE("\The [used_item] is not suitable for grinding.")) return TRUE @@ -133,7 +133,7 @@ data["beakercontents"] = list() if(beaker?.reagents) - for(var/decl/material/reagent as anything in beaker.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(beaker.reagents)) data["beakercontents"] += "[capitalize(reagent.get_reagent_name(beaker.reagents))] ([REAGENT_VOLUME(beaker.reagents, reagent)]u)" ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -182,7 +182,7 @@ return FALSE // Sanity check. - if (!beaker || (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)) + if (!beaker || (REAGENT_TOTAL_VOLUME(beaker.reagents) >= REAGENT_MAXIMUM_VOLUME(beaker.reagents))) return FALSE attempt_skill_effect(user) @@ -197,7 +197,7 @@ // Process. for (var/obj/item/thing in holdingitems) - var/remaining_volume = beaker.reagents.maximum_volume - beaker.reagents.total_volume + var/remaining_volume = REAGENT_MAXIMUM_VOLUME(beaker.reagents) - REAGENT_TOTAL_VOLUME(beaker.reagents) if(remaining_volume <= 0) break @@ -216,7 +216,7 @@ continue else if(thing.reagents) - thing.reagents.trans_to(beaker, thing.reagents.total_volume, skill_factor) + thing.reagents.trans_to(beaker, REAGENT_TOTAL_VOLUME(thing.reagents), skill_factor) holdingitems -= thing qdel(thing) @@ -274,4 +274,4 @@ return visible_message(SPAN_NOTICE("\The [src] whirrs violently and spills its contents all over \the [user]!")) if(beaker?.reagents) - beaker.reagents.splash(user, beaker.reagents.total_volume) \ No newline at end of file + beaker.reagents.splash(user, REAGENT_TOTAL_VOLUME(beaker.reagents)) \ No newline at end of file diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index a1e484aa57e4..937b0d2d8aae 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -11,9 +11,9 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new return reagents?.remove_any(amount, defer_update, removed_phases, skip_reagents) /atom/proc/get_reagent_space() - if(!reagents?.maximum_volume) + if(!REAGENT_MAXIMUM_VOLUME(reagents)) return 0 - return reagents.maximum_volume - reagents.total_volume + return REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(reagents) /atom/proc/get_reagents() return reagents @@ -55,23 +55,21 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new vapor?.update_values() liquids?.update_total() +// These vars are privated due to the potential for reagents to be either null, a list, or a datum. +// Always use the REAGENT_FOO macros! /datum/reagents - var/primary_reagent - var/primary_solid - var/primary_liquid - var/list/reagent_volumes - - var/list/liquid_volumes - var/list/solid_volumes // This should be taken as powders/flakes, rather than large solid pieces of material. - - var/list/reagent_data - var/total_volume = 0 - var/maximum_volume = 120 - - var/total_liquid_volume // Used to determine when to create fluids in the world and the like. - - var/atom/my_atom - var/cached_color + VAR_PRIVATE/list/reagent_volumes + VAR_PRIVATE/list/liquid_volumes + VAR_PRIVATE/list/solid_volumes // This should be taken as powders/flakes, rather than large solid pieces of material. + VAR_PRIVATE/list/reagent_data + VAR_PRIVATE/atom/my_atom + VAR_PRIVATE/cached_color + VAR_PRIVATE/primary_reagent + VAR_PRIVATE/primary_solid + VAR_PRIVATE/primary_liquid + VAR_PRIVATE/total_volume = 0 + VAR_PRIVATE/total_liquid_volume // Used to determine when to create fluids in the world and the like. + VAR_PRIVATE/maximum_volume = 120 /datum/reagents/New(var/maximum_volume = 120, var/atom/my_atom) src.maximum_volume = maximum_volume @@ -910,7 +908,7 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new . = trans_to_holder(target.reagents, amount, multiplier, copy, defer_update = defer_update, transferred_phases = transferred_phases) // Deferred updates are presumably being done by SSfluids. // Do an immediate fluid_act call rather than waiting for SSfluids to proc. - if(!defer_update && target.reagents.total_volume >= FLUID_PUDDLE) + if(!defer_update && REAGENT_TOTAL_VOLUME(target.reagents) >= FLUID_PUDDLE) target.fluid_act(target.reagents) // Objects may or may not have reagents; if they do, it's probably a beaker or something and we need to transfer properly; otherwise, just touch. @@ -979,22 +977,25 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /* Atom reagent creation - use it all the time */ /atom/proc/create_reagents(var/max_vol) - if(reagents) + if(istype(reagents)) log_debug("Attempted to create a new reagents holder when already referencing one: [log_info_line(src)]") - reagents.maximum_volume = max(reagents.maximum_volume, max_vol) - else + REAGENT_SET_MAX_VOL(reagents, max(REAGENT_MAXIMUM_VOLUME(reagents), max_vol)) + else if(!reagents) reagents = new/datum/reagents(max_vol, src) + else + return return reagents /atom/proc/create_or_update_reagents(_vol, override_volume) - if(reagents) + if(isnull(reagents)) + return create_reagents(_vol) + if(istype(reagents)) if(override_volume) - reagents.maximum_volume = _vol // should we remove excess reagents here? + REAGENT_SET_MAX_VOL(reagents, _vol) // should we remove excess reagents here? else - reagents.maximum_volume = max(reagents.maximum_volume, _vol) + REAGENT_SET_MAX_VOL(reagents, max(REAGENT_MAXIMUM_VOLUME(reagents), _vol)) reagents.update_total() return reagents - return create_reagents(_vol) /// Infinite reagent sink: nothing is ever actually added to it, useful for complex, filtered deletion of reagents without holder churn. /datum/reagents/sink diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index af6fa27d09f8..eb852d575ccf 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -32,7 +32,7 @@ var/bottle_lid_color = COLOR_OFF_WHITE /obj/machinery/chem_master/proc/get_remaining_volume() - return reagents ? clamp(reagents.maximum_volume - reagents.total_volume, 0, reagents.maximum_volume) : 0 + return reagents ? clamp(REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(reagents), 0, REAGENT_MAXIMUM_VOLUME(reagents)) : 0 /obj/machinery/chem_master/attackby(var/obj/item/used_item, var/mob/user) @@ -167,7 +167,7 @@ else if (href_list["createpill"] || href_list["createpill_multiple"]) var/count = 1 - if(reagents.total_volume/count < 1) //Sanity checking. + if(REAGENT_TOTAL_VOLUME(reagents)/count < 1) //Sanity checking. return TOPIC_HANDLED if (href_list["createpill_multiple"]) @@ -176,14 +176,14 @@ return TOPIC_HANDLED count = clamp(count, 1, max_pill_count) - var/amount_per_pill = min(reagents.total_volume/count, 30) + var/amount_per_pill = min(REAGENT_TOTAL_VOLUME(reagents)/count, 30) if(amount_per_pill < 1) // Sanity checking. return TOPIC_HANDLED var/name = sanitize_safe(input(usr,"Name:","Name your pill!","[reagents.get_primary_reagent_name()] ([amount_per_pill]u)"), MAX_NAME_LEN) if(!CanInteract(user, state)) return TOPIC_HANDLED - if(reagents.total_volume/count < 1) //Sanity checking. + if(REAGENT_TOTAL_VOLUME(reagents)/count < 1) //Sanity checking. return TOPIC_HANDLED while (count-- && count >= 0) var/obj/item/chems/pill/dispensed/P = new(loc) @@ -219,7 +219,7 @@ /obj/machinery/chem_master/proc/fetch_contaminants(mob/user, datum/reagents/reagents, decl/material/main_reagent) . = list() - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent != main_reagent && prob(user.skill_fail_chance(core_skill, 100))) . += reagent @@ -282,11 +282,11 @@ dat += "Eject Pill Bottle \[[loaded_pill_bottle.contents.len]/[loaded_pill_bottle.storage.max_storage_space]\]

    " else dat += "No pill bottle inserted.

    " - if(!R.total_volume) + if(!REAGENT_TOTAL_VOLUME(R)) dat += "Beaker is empty." else dat += "Add to buffer:
    " - for(var/decl/material/reagent as anything in R.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(R)) dat += "[reagent.use_name], [REAGENT_VOLUME(R, reagent)] Units - " dat += "(Analyze) " dat += "(1) " @@ -296,8 +296,8 @@ dat += "(Custom)
    " dat += "
    Transfer to [(!mode ? "disposal" : "beaker")]:
    " - if(reagents.total_volume) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + if(REAGENT_TOTAL_VOLUME(reagents)) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) dat += "[reagent.use_name], [REAGENT_VOLUME(reagents, reagent)] Units - " dat += "(Analyze) " dat += "(1) " diff --git a/code/modules/reagents/chems/chems_blood.dm b/code/modules/reagents/chems/chems_blood.dm index c4396a07c04f..b4a6f089ba82 100644 --- a/code/modules/reagents/chems/chems_blood.dm +++ b/code/modules/reagents/chems/chems_blood.dm @@ -52,7 +52,7 @@ if(!istype(touching_turf) || REAGENT_VOLUME(holder, src) < 3) return var/weakref/donor = LAZYACCESS(data, DATA_BLOOD_DONOR) - blood_splatter(touching_turf, donor?.resolve() || holder.my_atom, 1) + blood_splatter(touching_turf, donor?.resolve() || REAGENT_GET_ATOM(holder), 1) /decl/material/liquid/blood/affect_ingest(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() diff --git a/code/modules/reagents/chems/chems_compounds.dm b/code/modules/reagents/chems/chems_compounds.dm index 7467842c66e9..cb305d78e23c 100644 --- a/code/modules/reagents/chems/chems_compounds.dm +++ b/code/modules/reagents/chems/chems_compounds.dm @@ -30,8 +30,9 @@ H.update_eyes() /decl/material/liquid/glowsap/on_leaving_metabolism(datum/reagents/metabolism/holder) - if(ishuman(holder?.my_atom)) - var/mob/living/human/H = holder.my_atom + var/my_atom = REAGENT_GET_ATOM(holder) + if(ishuman(my_atom)) + var/mob/living/human/H = my_atom addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living/human, update_eyes)), 5 SECONDS) . = ..() @@ -382,7 +383,7 @@ var/list/data = REAGENT_DATA(holder, src) if(world.time > LAZYACCESS(data, DATA_COOLDOWN_TIME) + 3 MINUTES) LAZYSET(data, DATA_COOLDOWN_TIME, world.time) - LAZYSET(holder.reagent_data, type, data) + REAGENT_SET_DATA(holder, type, data) to_chat(M, SPAN_NOTICE("You feel faintly sore in the throat.")) /decl/material/liquid/nanitefluid diff --git a/code/modules/reagents/chems/chems_drugs.dm b/code/modules/reagents/chems/chems_drugs.dm index 7a998e6981d0..05a13fe3670c 100644 --- a/code/modules/reagents/chems/chems_drugs.dm +++ b/code/modules/reagents/chems/chems_drugs.dm @@ -66,7 +66,7 @@ if(update_data) LAZYSET(data, DATA_COOLDOWN_TIME, world.time) - LAZYSET(holder.reagent_data, type, data) + REAGENT_SET_DATA(holder, type, data) /decl/material/liquid/nicotine/affect_overdose(mob/living/victim, total_dose) ..() @@ -256,7 +256,7 @@ /decl/material/liquid/glowsap/gleam/on_leaving_metabolism(datum/reagents/metabolism/holder) . = ..() - var/mob/M = holder?.my_atom + var/mob/M = REAGENT_GET_ATOM(holder) if(istype(M)) M.remove_client_color(/datum/client_color/noir/thirdeye) diff --git a/code/modules/reagents/chems/chems_medicines.dm b/code/modules/reagents/chems/chems_medicines.dm index fac296caebb6..6578646b9e28 100644 --- a/code/modules/reagents/chems/chems_medicines.dm +++ b/code/modules/reagents/chems/chems_medicines.dm @@ -133,12 +133,12 @@ var/removing = (4 * removed * antitoxin_strength) var/datum/reagents/ingested = M.get_ingested_reagents() - for(var/decl/material/reagent as anything in ingested?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(ingested)) if((remove_generic && reagent.toxicity) || (reagent.type in remove_toxins)) ingested.remove_reagent(reagent, removing) return - for(var/decl/material/reagent as anything in M.reagents?.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(M.reagents)) if((remove_generic && reagent.toxicity) || (reagent.type in remove_toxins)) M.remove_from_reagents(reagent, removing) return @@ -368,7 +368,7 @@ var/charges = removed * DETOXIFIER_EFFECTIVENESS var/dosecharges = CHEM_DOSE(M, src) * DETOXIFIER_DOSE_EFFECTIVENESS for(var/datum/reagents/container as anything in M.get_metabolizing_reagent_holders()) - for(var/decl/material/reagent as anything in container.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(container)) var/decl/material/liquid/painkillers/painkiller = reagent if(!istype(painkiller) || !painkiller.narcotic) continue diff --git a/code/modules/reagents/chems/chems_painkillers.dm b/code/modules/reagents/chems/chems_painkillers.dm index f6185712f460..5f35447a5538 100644 --- a/code/modules/reagents/chems/chems_painkillers.dm +++ b/code/modules/reagents/chems/chems_painkillers.dm @@ -130,7 +130,7 @@ return var/datum/reagents/ingested = M.get_ingested_reagents() if(ingested) - var/list/pool = M.reagents.reagent_volumes | ingested.reagent_volumes + var/list/pool = REAGENT_VOLUMES(M.reagents) | REAGENT_VOLUMES(ingested) for(var/reagent in pool) var/decl/material/liquid/alcohol/booze = reagent if(!istype(booze) ||CHEM_DOSE(M, reagent) < 2) //let them experience false security at first diff --git a/code/modules/reagents/chems/chems_psychiatric.dm b/code/modules/reagents/chems/chems_psychiatric.dm index e81e3c6eba49..a6f64a93262c 100644 --- a/code/modules/reagents/chems/chems_psychiatric.dm +++ b/code/modules/reagents/chems/chems_psychiatric.dm @@ -27,7 +27,7 @@ if(update_data) LAZYSET(data, DATA_COOLDOWN_TIME, world.time) - LAZYSET(holder.reagent_data, type, data) + REAGENT_SET_DATA(holder, type, data) /// Returns TRUE to signal that the effect should go on cooldown. /decl/material/liquid/accumulated/proc/positive_effect(mob/living/victim, removed, datum/reagents/holder, is_off_cooldown) diff --git a/code/modules/reagents/cocktails.dm b/code/modules/reagents/cocktails.dm index 1815fa6e998b..fd688adbcd92 100644 --- a/code/modules/reagents/cocktails.dm +++ b/code/modules/reagents/cocktails.dm @@ -68,17 +68,17 @@ . = length(ratios) /decl/cocktail/proc/matches(var/obj/item/prop) - if(length(ratios) > length(prop.reagents.reagent_volumes)) + if(length(ratios) > length(REAGENT_VOLUMES(prop.reagents))) return FALSE var/list/check_ratios var/i = 0 for(var/rtype in ratios) i++ - if(!prop.reagents.has_reagent(rtype) || (order_specific && prop.reagents.reagent_volumes[i] != rtype)) + if(!prop.reagents.has_reagent(rtype) || (order_specific && REAGENT_VOLUME(prop.reagents, i) != rtype)) return FALSE if(isnum(ratios[rtype])) LAZYSET(check_ratios, rtype, ratios[rtype]) - var/effective_volume = prop.reagents.total_volume + var/effective_volume = REAGENT_TOTAL_VOLUME(prop.reagents) if(!(/decl/material/solid/ice in ratios)) effective_volume -= REAGENT_VOLUME(prop.reagents, /decl/material/solid/ice) for(var/rtype in check_ratios) diff --git a/code/modules/reagents/dispenser/cartridge.dm b/code/modules/reagents/dispenser/cartridge.dm index 96dbb3859411..35300fc4310a 100644 --- a/code/modules/reagents/dispenser/cartridge.dm +++ b/code/modules/reagents/dispenser/cartridge.dm @@ -13,18 +13,19 @@ /obj/item/chems/chem_disp_cartridge/Initialize() . = ..() - if(reagents?.primary_reagent && !_reagent_label) - _reagent_label = reagents.get_primary_reagent_name() + var/decl/material/primary_reagent = istype(reagents) && reagents.get_primary_reagent_decl() + if(primary_reagent && !_reagent_label) + _reagent_label = primary_reagent.name if(_reagent_label) setLabel(_reagent_label) /obj/item/chems/chem_disp_cartridge/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "It has a capacity of [reagents?.maximum_volume || 0] unit\s." - if(reagents?.total_volume <= 0) + . += "It has a capacity of [REAGENT_MAXIMUM_VOLUME(reagents)] unit\s." + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) . += "It is empty." else - . += "It contains [reagents?.total_volume || 0] unit\s of reagents." + . += "It contains [REAGENT_TOTAL_VOLUME(reagents)] unit\s of reagents." if(!ATOM_IS_OPEN_CONTAINER(src)) . += "The cap is sealed." @@ -72,8 +73,9 @@ if(user.check_intent(I_FLAG_HARM)) if(standard_splash_mob(user,target)) return TRUE - if(reagents && reagents.total_volume) + var/total_vol = REAGENT_TOTAL_VOLUME(reagents) + if(reagents && total_vol) to_chat(user, SPAN_DANGER("You splash the contents of \the [src] onto \the [target].")) - reagents.splash(target, reagents.total_volume) //FIXME: probably shouldn't throw the whole 500 units at the mob, since the bottle neck is a bottle neck. + reagents.splash(target, total_vol) //FIXME: probably shouldn't throw the whole 500 units at the mob, since the bottle neck is a bottle neck. return TRUE return ..() diff --git a/code/modules/reagents/dispenser/cartridge_presets.dm b/code/modules/reagents/dispenser/cartridge_presets.dm index 553e42ba766f..bb0cf4c89422 100644 --- a/code/modules/reagents/dispenser/cartridge_presets.dm +++ b/code/modules/reagents/dispenser/cartridge_presets.dm @@ -9,7 +9,7 @@ * CART_TYPE: the type suffix to append to the cartridge type path. * REAGENT_TYPE: The reagent decl path to fill the cartridge with. */ -#define DEFINE_CARTRIDGE_FOR_CHEM(CART_TYPE, REAGENT_TYPE) /obj/item/chems/chem_disp_cartridge/##CART_TYPE/populate_reagents(){add_to_reagents(REAGENT_TYPE, reagents.maximum_volume);} +#define DEFINE_CARTRIDGE_FOR_CHEM(CART_TYPE, REAGENT_TYPE) /obj/item/chems/chem_disp_cartridge/##CART_TYPE/populate_reagents(){add_to_reagents(REAGENT_TYPE, REAGENT_MAXIMUM_VOLUME(reagents));} // Multiple DEFINE_CARTRIDGE_FOR_CHEM(water, /decl/material/liquid/water) diff --git a/code/modules/reagents/dispenser/cartridge_spawn.dm b/code/modules/reagents/dispenser/cartridge_spawn.dm index 50b6f7e7454d..d601f2ae2dd6 100644 --- a/code/modules/reagents/dispenser/cartridge_spawn.dm +++ b/code/modules/reagents/dispenser/cartridge_spawn.dm @@ -8,7 +8,7 @@ if("medium") cartridge_type = /obj/item/chems/chem_disp_cartridge/medium if("large") cartridge_type = /obj/item/chems/chem_disp_cartridge var/obj/item/chems/chem_disp_cartridge/cartridge = new cartridge_type(usr.loc) - cartridge.add_to_reagents(reagent_type, cartridge.reagents?.maximum_volume) + cartridge.add_to_reagents(reagent_type, REAGENT_MAXIMUM_VOLUME(cartridge.reagents)) var/reagent_name = cartridge.reagents.get_primary_reagent_name() cartridge.setLabel(reagent_name) log_and_message_admins("spawned a [size] reagent container containing [reagent_name] ([reagent_type])") diff --git a/code/modules/reagents/dispenser/dispenser2.dm b/code/modules/reagents/dispenser/dispenser2.dm index 2c6882d9e60f..3ad90fe93db7 100644 --- a/code/modules/reagents/dispenser/dispenser2.dm +++ b/code/modules/reagents/dispenser/dispenser2.dm @@ -146,14 +146,15 @@ data["isBeakerLoaded"] = container ? 1 : 0 data["glass"] = accept_drinking var beakerD[0] - if(LAZYLEN(container?.reagents?.reagent_volumes)) - for(var/decl/material/reagent as anything in container.reagents.reagent_volumes) + var/beaker_volumes = REAGENT_VOLUMES(container?.reagents) + if(LAZYLEN(beaker_volumes)) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(container.reagents)) beakerD[++beakerD.len] = list("name" = reagent.use_name, "volume" = REAGENT_VOLUME(container.reagents, reagent)) data["beakerContents"] = beakerD if(container) // Container has had null reagents in the past; may be due to qdel without clearing reference. - data["beakerCurrentVolume"] = container.reagents?.total_volume || 0 - data["beakerMaxVolume"] = container.reagents?.maximum_volume || 0 + data["beakerCurrentVolume"] = REAGENT_TOTAL_VOLUME(container.reagents) + data["beakerMaxVolume"] = REAGENT_MAXIMUM_VOLUME(container.reagents) else data["beakerCurrentVolume"] = null data["beakerMaxVolume"] = null @@ -161,7 +162,7 @@ var chemicals[0] for(var/label in cartridges) var/obj/item/chems/chem_disp_cartridge/C = cartridges[label] - chemicals[++chemicals.len] = list("label" = label, "amount" = C.reagents.total_volume) + chemicals[++chemicals.len] = list("label" = label, "amount" = REAGENT_TOTAL_VOLUME(C.reagents)) data["chemicals"] = chemicals // update the ui if it exists, returns null if no ui is passed/found diff --git a/code/modules/reagents/reactions/_reaction.dm b/code/modules/reagents/reactions/_reaction.dm index 3a4b41044b5c..7a89aba6dd4c 100644 --- a/code/modules/reagents/reactions/_reaction.dm +++ b/code/modules/reagents/reactions/_reaction.dm @@ -52,7 +52,7 @@ /decl/chemical_reaction/proc/process(var/datum/reagents/holder, var/limit) var/data = send_data(holder) - var/reaction_volume = holder.maximum_volume + var/reaction_volume = REAGENT_GET_MAX_VOL(holder) for(var/reactant in required_reagents) var/A = CHEMS_QUANTIZE(REAGENT_VOLUME(holder, reactant) / required_reagents[reactant] / limit) // How much of this reagent we are allowed to use if(reaction_volume > A) diff --git a/code/modules/reagents/reactions/reaction_grenade_reaction.dm b/code/modules/reagents/reactions/reaction_grenade_reaction.dm index 4483f376e33b..bd6d911de3e5 100644 --- a/code/modules/reagents/reactions/reaction_grenade_reaction.dm +++ b/code/modules/reagents/reactions/reaction_grenade_reaction.dm @@ -133,7 +133,7 @@ ..() var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) - if(istype(location, /obj/item/sealant_tank) && location.reagents?.maximum_volume) + if(istype(location, /obj/item/sealant_tank) && REAGENT_MAXIMUM_VOLUME(location.reagents)) location.reagents.add_reagent(/decl/material/liquid/foam, created_volume) return location = get_turf(location) diff --git a/code/modules/reagents/reactions/reaction_recipe_food.dm b/code/modules/reagents/reactions/reaction_recipe_food.dm index 0ece0daefd2a..29f4e8824b35 100644 --- a/code/modules/reagents/reactions/reaction_recipe_food.dm +++ b/code/modules/reagents/reactions/reaction_recipe_food.dm @@ -33,7 +33,7 @@ /decl/chemical_reaction/recipe/food/dairy/send_data(var/datum/reagents/holder, var/reaction_limit) . = ..() for(var/reagent in required_reagents) - var/list/data = LAZYACCESS(holder.reagent_data, reagent) + var/list/data = REAGENT_DATA(holder, reagent) if(!islist(data)) continue for(var/milk_key in milk_data_keys) diff --git a/code/modules/reagents/reactions/reaction_synthesis.dm b/code/modules/reagents/reactions/reaction_synthesis.dm index fcbe27b4dfb5..59458d2f0d39 100644 --- a/code/modules/reagents/reactions/reaction_synthesis.dm +++ b/code/modules/reagents/reactions/reaction_synthesis.dm @@ -37,10 +37,11 @@ ) // Interferes with resin globules. /decl/chemical_reaction/synthesis/crystalization/can_happen(datum/reagents/holder) - . = ..() && length(holder.reagent_volumes) > 1 + var/holder_volumes = REAGENT_VOLUMES(holder) + . = ..() && length(holder_volumes) > 1 if(.) . = FALSE - for(var/decl/material/reagent as anything in holder.reagent_volumes) + for(var/decl/material/reagent as anything in holder_volumes) if(reagent.type != /decl/material/liquid/crystal_agent && REAGENT_VOLUME(holder, reagent) >= REAGENT_UNITS_PER_MATERIAL_SHEET) return TRUE @@ -48,7 +49,7 @@ var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) var/list/removing_reagents = list() - for(var/decl/material/reagent as anything in holder.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(holder)) if(reagent.type != /decl/material/liquid/crystal_agent) var/solidifying = floor(REAGENT_VOLUME(holder, reagent) / REAGENT_UNITS_PER_MATERIAL_SHEET) if(solidifying) @@ -67,10 +68,11 @@ inhibitors = list(/decl/material/liquid/crystal_agent) /decl/chemical_reaction/synthesis/aerogel/can_happen(datum/reagents/holder) - . = ..() && length(holder.reagent_volumes) > 1 + var/holder_volumes = REAGENT_VOLUMES(holder) + . = ..() && length(holder_volumes) > 1 if(.) . = FALSE - for(var/decl/material/reagent as anything in holder.reagent_volumes) + for(var/decl/material/reagent as anything in holder_volumes) if(REAGENT_VOLUME(holder, reagent) < REAGENT_UNITS_PER_MATERIAL_SHEET) continue if(reagent.default_solid_form != /obj/item/stack/material/aerogel) @@ -81,7 +83,7 @@ var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) var/list/removing_reagents = list() - for(var/decl/material/reagent as anything in holder.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(holder)) if(reagent.default_solid_form == /obj/item/stack/material/aerogel) var/solidifying = floor(REAGENT_VOLUME(holder, reagent) / REAGENT_UNITS_PER_MATERIAL_SHEET) if(solidifying) diff --git a/code/modules/reagents/reagent_container_edibility.dm b/code/modules/reagents/reagent_container_edibility.dm index f052809413c6..83d485d0c13f 100644 --- a/code/modules/reagents/reagent_container_edibility.dm +++ b/code/modules/reagents/reagent_container_edibility.dm @@ -1,5 +1,5 @@ /obj/item/chems/get_edible_material_amount(var/mob/eater) - return ATOM_IS_OPEN_CONTAINER(src) && reagents?.total_volume + return ATOM_IS_OPEN_CONTAINER(src) && REAGENT_TOTAL_VOLUME(reagents) /obj/item/chems/get_food_default_transfer_amount(mob/eater) return eater?.get_eaten_transfer_amount(amount_per_transfer_from_this) diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 639b4a2b87fe..89c9ecc84b34 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -99,16 +99,16 @@ // Skimming off cream, repurposed from crucibles. // TODO: potentially make this an alt interaction and unify with slag skimming. - if(istype(used_item, /obj/item/chems) && ATOM_IS_OPEN_CONTAINER(used_item) && used_item.reagents?.maximum_volume && reagents?.total_volume && length(reagents.reagent_volumes) > 1) + if(istype(used_item, /obj/item/chems) && ATOM_IS_OPEN_CONTAINER(used_item) && REAGENT_MAXIMUM_VOLUME(used_item.reagents) && REAGENT_TOTAL_VOLUME(reagents) && length(REAGENT_VOLUMES(reagents)) > 1) var/list/skimmable_reagents = reagents.get_skimmable_reagents() if(length(skimmable_reagents)) var/removing = min(amount_per_transfer_from_this, REAGENTS_FREE_SPACE(used_item.reagents)) if(removing <= 0) to_chat(user, SPAN_WARNING("\The [used_item] is full.")) else - var/old_amt = used_item.reagents.total_volume - reagents.trans_to_holder(used_item.reagents, removing, skip_reagents = (reagents.reagent_volumes - skimmable_reagents)) - to_chat(user, SPAN_NOTICE("You skim [used_item.reagents.total_volume-old_amt] unit\s of [used_item.reagents.get_primary_reagent_name()] from the top of \the [reagents.get_primary_reagent_name()].")) + var/old_amt = REAGENT_TOTAL_VOLUME(used_item.reagents) + reagents.trans_to_holder(used_item.reagents, removing, skip_reagents = (REAGENT_VOLUMES(reagents) - skimmable_reagents)) + to_chat(user, SPAN_NOTICE("You skim [REAGENT_TOTAL_VOLUME(used_item.reagents)-old_amt] unit\s of [used_item.reagents.get_primary_reagent_name()] from the top of \the [reagents.get_primary_reagent_name()].")) return TRUE if(used_item.user_can_attack_with(user, silent = TRUE)) @@ -147,7 +147,7 @@ /obj/item/chems/shatter(consumed) //Skip splashing if we are in nullspace, since splash isn't null guarded if(loc) - reagents.splash(get_turf(src), reagents.total_volume) + reagents.splash(get_turf(src), REAGENT_TOTAL_VOLUME(reagents)) . = ..() /obj/item/chems/proc/set_detail_color(var/new_color) @@ -161,17 +161,17 @@ . = ..() - if(QDELETED(src) || !reagents?.total_volume || !ATOM_IS_OPEN_CONTAINER(src) || !isatom(loc)) + if(QDELETED(src) || !REAGENT_TOTAL_VOLUME(reagents) || !ATOM_IS_OPEN_CONTAINER(src) || !isatom(loc)) return // Vaporize anything over its boiling point. var/update_reagents = FALSE var/datum/gas_mixture/environment = loc?.return_air() var/ambient_pressure = environment ? environment.return_pressure() : ONE_ATMOSPHERE - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.can_boil_to_gas && reagent.phase_at_temperature(temperature, ambient_pressure) == MAT_PHASE_GAS) // TODO: reduce atom temperature? - var/removing = min(reagent.boil_evaporation_per_run, reagents.reagent_volumes[reagent]) + var/removing = min(reagent.boil_evaporation_per_run, REAGENT_VOLUME(reagents, reagent)) reagents.remove_reagent(reagent, removing, defer_update = TRUE, removed_phases = MAT_PHASE_LIQUID) update_reagents = TRUE loc.take_vaporized_reagent(reagent, removing) @@ -179,7 +179,7 @@ reagents.update_total() /obj/item/chems/take_vaporized_reagent(reagent, amount) - if(!reagents?.maximum_volume) + if(!REAGENT_MAXIMUM_VOLUME(reagents)) return ..() var/take_reagent = min(amount, REAGENTS_FREE_SPACE(reagents)) if(take_reagent > 0) @@ -228,4 +228,4 @@ var/turf/T = get_turf(user) if(T) to_chat(user, SPAN_NOTICE("You empty \the [target] onto the floor.")) - target.reagents.trans_to(T, target.reagents.total_volume) + target.reagents.trans_to(T, REAGENT_TOTAL_VOLUME(target.reagents)) diff --git a/code/modules/reagents/reagent_containers/_glass.dm b/code/modules/reagents/reagent_containers/_glass.dm index 8fd812e2cc78..dde3d1891c02 100644 --- a/code/modules/reagents/reagent_containers/_glass.dm +++ b/code/modules/reagents/reagent_containers/_glass.dm @@ -47,8 +47,8 @@ . = ..() if(distance > 2) return - if(reagents?.total_volume) - . += SPAN_NOTICE("It contains [reagents.total_volume] units of reagents.") + if(REAGENT_TOTAL_VOLUME(reagents)) + . += SPAN_NOTICE("It contains [REAGENT_TOTAL_VOLUME(reagents)] units of reagents.") else . += SPAN_NOTICE("It is empty.") if(!ATOM_IS_OPEN_CONTAINER(src)) @@ -58,7 +58,7 @@ return TRUE /obj/item/chems/glass/proc/should_drink_from(mob/drinker) - . = reagents?.total_volume > 0 + . = REAGENT_TOTAL_VOLUME(reagents) > 0 if(.) var/decl/material/drinking = reagents.get_primary_reagent_decl() return drinking ? !drinking.is_unsafe_to_drink(drinker) : FALSE @@ -125,22 +125,23 @@ var/global/list/lid_check_glass_types = list() return TRUE if(handle_eaten_by_mob(user, target) != EATEN_INVALID) return TRUE + var/total_vol = REAGENT_TOTAL_VOLUME(reagents) if(user.check_intent(I_FLAG_HARM)) if(standard_splash_mob(user,target)) return TRUE - if(reagents && reagents.total_volume) + if(reagents && total_vol) to_chat(user, SPAN_DANGER("You splash the contents of \the [src] onto \the [target].")) - reagents.splash(target, reagents.total_volume) + reagents.splash(target, total_vol) return TRUE - else if(reagents && reagents.total_volume) + else if(reagents && total_vol) to_chat(user, SPAN_NOTICE("You splash a small amount of the contents of \the [src] onto \the [target].")) - reagents.splash(target, min(reagents.total_volume, 5)) + reagents.splash(target, min(total_vol, 5)) return TRUE . = ..() // Drinking out of bowls. /obj/item/chems/glass/get_edible_material_amount(mob/eater) - return reagents?.total_volume + return REAGENT_TOTAL_VOLUME(reagents) /obj/item/chems/glass/get_utensil_food_type() return /obj/item/food/lump @@ -157,11 +158,11 @@ var/global/list/lid_check_glass_types = list() if(utensil.loaded_food) to_chat(user, SPAN_WARNING("You already have something on \the [utensil].")) return TRUE - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_WARNING("\The [src] is empty.")) return TRUE separate_food_chunk(utensil, user) - if(utensil.loaded_food?.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(utensil.loaded_food?.reagents)) to_chat(user, SPAN_NOTICE("You scoop up some of \the [utensil.loaded_food.reagents.get_primary_reagent_name()] with \the [utensil].")) return TRUE @@ -169,7 +170,7 @@ var/global/list/lid_check_glass_types = list() /obj/item/chems/glass/get_alt_interactions(mob/user) . = ..() - if(reagents?.total_volume >= FLUID_PUDDLE) + if(REAGENT_TOTAL_VOLUME(reagents) >= FLUID_PUDDLE) LAZYADD(., /decl/interaction_handler/dip_item) LAZYADD(., /decl/interaction_handler/fill_from) if(user?.get_active_held_item()) diff --git a/code/modules/reagents/reagent_containers/beaker.dm b/code/modules/reagents/reagent_containers/beaker.dm index f8f844a21c0e..5df1a2aaf168 100644 --- a/code/modules/reagents/reagent_containers/beaker.dm +++ b/code/modules/reagents/reagent_containers/beaker.dm @@ -15,7 +15,7 @@ /obj/item/chems/glass/beaker/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "It can hold up to [reagents?.maximum_volume] units." + . += "It can hold up to [REAGENT_MAXIMUM_VOLUME(reagents)] units." /obj/item/chems/glass/beaker/on_picked_up(mob/user, atom/old_loc) . = ..() @@ -31,9 +31,9 @@ /obj/item/chems/glass/beaker/update_overlays() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) var/image/filling = mutable_appearance(icon, "[icon_state]1", reagents.get_color()) - var/percent = round((reagents.total_volume / reagents.maximum_volume) * 100) + var/percent = round((REAGENT_TOTAL_VOLUME(reagents) / REAGENT_MAXIMUM_VOLUME(reagents)) * 100) switch(percent) if(0 to 9) filling.icon_state = "[icon_state]1" if(10 to 24) filling.icon_state = "[icon_state]10" @@ -60,7 +60,7 @@ /obj/item/chems/glass/beaker/throw_impact(atom/hit_atom) . = ..() if(ATOM_IS_OPEN_CONTAINER(src)) - reagents.splash(hit_atom, rand(reagents.total_volume*0.25,reagents.total_volume), min_spill = 60, max_spill = 100) + reagents.splash(hit_atom, rand(REAGENT_TOTAL_VOLUME(reagents)*0.25,REAGENT_TOTAL_VOLUME(reagents)), min_spill = 60, max_spill = 100) take_damage(rand(4,8)) /obj/item/chems/glass/beaker/large @@ -181,4 +181,4 @@ chem_volume = 120 /obj/item/chems/glass/beaker/sulfuric/populate_reagents() - add_to_reagents(/decl/material/liquid/acid, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/acid, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 4368f1486288..593d6e37a74e 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -27,14 +27,14 @@ /obj/item/chems/ivbag/on_reagent_change() if(!(. = ..())) return - if(reagents?.total_volume > reagents?.maximum_volume / 2) + if(REAGENT_TOTAL_VOLUME(reagents) > REAGENT_MAXIMUM_VOLUME(reagents) / 2) w_class = ITEM_SIZE_NORMAL else w_class = ITEM_SIZE_SMALL /obj/item/chems/ivbag/on_update_icon() . = ..() - var/percent = round(reagents?.total_volume / reagents?.maximum_volume * 100) + var/percent = round(REAGENT_TOTAL_VOLUME(reagents) / REAGENT_MAXIMUM_VOLUME(reagents) * 100) if(percent) add_overlay(overlay_image(icon, "[round(percent,25)]", reagents.get_color())) add_overlay(attached? "dongle" : "top") @@ -68,7 +68,7 @@ if(!(src in M.get_held_items())) return - if(!reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) return reagents.trans_to_mob(attached, amount_per_transfer_from_this, CHEM_INJECT) @@ -88,7 +88,7 @@ /obj/item/chems/ivbag/blood/populate_reagents() if(blood_fill_type) - add_to_reagents(blood_fill_type, reagents.maximum_volume, get_initial_blood_data()) + add_to_reagents(blood_fill_type, REAGENT_MAXIMUM_VOLUME(reagents), get_initial_blood_data()) /obj/item/chems/ivbag/blood/nanoblood label_text = "synthetic" diff --git a/code/modules/reagents/reagent_containers/borghypo.dm b/code/modules/reagents/reagent_containers/borghypo.dm index 468176dbde93..a84c183b2bd2 100644 --- a/code/modules/reagents/reagent_containers/borghypo.dm +++ b/code/modules/reagents/reagent_containers/borghypo.dm @@ -44,7 +44,7 @@ . = ..() var/list/reagent_ids = get_generated_reagents() for(var/decl/material/reagent in decls_repository.get_decls_unassociated(reagent_ids)) - reagents.add_reagent(reagent.type, round(reagents.maximum_volume / length(reagent_ids))) + reagents.add_reagent(reagent.type, round(REAGENT_MAXIMUM_VOLUME(reagents) / length(reagent_ids))) START_PROCESSING(SSobj, src) /obj/item/chems/borghypo/Destroy() @@ -61,7 +61,7 @@ if(!robot?.cell) return var/list/reagent_ids = get_generated_reagents() - var/max_per_reagent = round(reagents.maximum_volume / length(reagent_ids)) + var/max_per_reagent = round(REAGENT_MAXIMUM_VOLUME(reagents) / length(reagent_ids)) for(var/reagent in reagent_ids) var/has_reagent = REAGENT_VOLUME(reagents, GET_DECL(reagent)) if(has_reagent < max_per_reagent) @@ -128,7 +128,7 @@ return var/list/reagent_ids = get_generated_reagents() var/decl/material/reagent = GET_DECL(reagent_ids[mode]) - . += SPAN_NOTICE("It is currently producing [reagent.use_name] and has [REAGENT_VOLUME(reagents, reagent)] out of [round(reagents.maximum_volume / length(reagent_ids))] units left.") + . += SPAN_NOTICE("It is currently producing [reagent.use_name] and has [REAGENT_VOLUME(reagents, reagent)] out of [round(REAGENT_MAXIMUM_VOLUME(reagents) / length(reagent_ids))] units left.") /obj/item/chems/borghypo/service name = "cyborg drink synthesizer" diff --git a/code/modules/reagents/reagent_containers/bowl.dm b/code/modules/reagents/reagent_containers/bowl.dm index c7ad55d990e6..41dd9943d960 100644 --- a/code/modules/reagents/reagent_containers/bowl.dm +++ b/code/modules/reagents/reagent_containers/bowl.dm @@ -155,18 +155,18 @@ add_to_reagents(filling, fillings[filling]) /obj/item/chems/glass/bowl/mystery/update_name() - if(!drained && reagents?.total_volume) + if(!drained && REAGENT_TOTAL_VOLUME(reagents)) SetName("mystery soup") else ..() /obj/item/chems/glass/bowl/mystery/on_reagent_change() - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) drained = TRUE . = ..() /obj/item/chems/glass/bowl/mystery/update_container_desc() - if(!drained && reagents?.total_volume) + if(!drained && REAGENT_TOTAL_VOLUME(reagents)) desc = "The mystery is, why aren't you eating it?" else ..() diff --git a/code/modules/reagents/reagent_containers/bucket.dm b/code/modules/reagents/reagent_containers/bucket.dm index 2f950c3dd9af..228bfb78d19e 100644 --- a/code/modules/reagents/reagent_containers/bucket.dm +++ b/code/modules/reagents/reagent_containers/bucket.dm @@ -30,7 +30,7 @@ /obj/item/chems/glass/bucket/attackby(var/obj/item/used_item, mob/user) if(istype(used_item, /obj/item/mop)) - if(reagents.total_volume < 1) + if(REAGENT_TOTAL_VOLUME(reagents) < 1) to_chat(user, SPAN_WARNING("\The [src] is empty!")) else if(REAGENTS_FREE_SPACE(used_item.reagents) >= 5) reagents.trans_to_obj(used_item, 5) @@ -44,7 +44,7 @@ /obj/item/chems/glass/bucket/get_reagents_overlay(state_prefix) if(!ATOM_IS_OPEN_CONTAINER(src)) return null // no overlay while closed! - if(!reagents || (reagents.total_volume / reagents.maximum_volume) < 0.8) + if(!reagents || (REAGENT_TOTAL_VOLUME(reagents) / REAGENT_MAXIMUM_VOLUME(reagents)) < 0.8) return null // must be at least 80% full to show return ..() diff --git a/code/modules/reagents/reagent_containers/condiments/__condiment.dm b/code/modules/reagents/reagent_containers/condiments/__condiment.dm index 18fe2ee33785..bd3c1e2666d8 100644 --- a/code/modules/reagents/reagent_containers/condiments/__condiment.dm +++ b/code/modules/reagents/reagent_containers/condiments/__condiment.dm @@ -17,7 +17,7 @@ . = ..() var/decl/condiment_appearance/condiment = GET_DECL(initial_condiment_type) if(condiment?.condiment_type) - add_to_reagents(condiment.condiment_type, reagents.maximum_volume) + add_to_reagents(condiment.condiment_type, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/condiment/attackby(var/obj/item/used_item, var/mob/user) if(IS_PEN(used_item)) @@ -51,7 +51,8 @@ /obj/item/chems/condiment/proc/get_current_condiment_appearance() if(!morphic_container) return GET_DECL(initial_condiment_type) - switch(LAZYLEN(reagents?.reagent_volumes)) + var/reagent_volumes = REAGENT_VOLUMES(reagents) + switch(LAZYLEN(reagent_volumes)) if(0) return GET_DECL(/decl/condiment_appearance/empty) if(1) diff --git a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm index 5be3c085609c..e82429d67e9d 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm @@ -46,38 +46,41 @@ var/global/const/DRINK_ICON_NOISY = "noise" . /= HOLLOW_OBJECT_MATTER_MULTIPLIER /obj/item/chems/drinks/glass2/proc/has_ice() - if(LAZYLEN(reagents.reagent_volumes)) + var/reagent_volumes = REAGENT_VOLUMES(reagents) + if(LAZYLEN(reagent_volumes)) var/decl/material/R = reagents.get_primary_reagent_decl() if(!((R.type == /decl/material/solid/ice) || ("ice" in R.glass_special))) // if it's not a cup of ice, and it's not already supposed to have ice in, see if the bartender's put ice in it - if(reagents.has_reagent(/decl/material/solid/ice, reagents.total_volume / 10)) // 10% ice by volume + if(reagents.has_reagent(/decl/material/solid/ice, REAGENT_TOTAL_VOLUME(reagents) / 10)) // 10% ice by volume return 1 return 0 /obj/item/chems/drinks/glass2/proc/has_fizz() - if(LAZYLEN(reagents.reagent_volumes)) + var/reagent_volumes = REAGENT_VOLUMES(reagents) + if(LAZYLEN(reagent_volumes)) var/decl/material/primary_reagent = reagents.get_primary_reagent_decl() if(("fizz" in primary_reagent.glass_special)) return 1 var/totalfizzy = 0 - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if("fizz" in reagent.glass_special) totalfizzy += REAGENT_VOLUME(reagents, reagent) - if(totalfizzy >= reagents.total_volume / 5) // 20% fizzy by volume + if(totalfizzy >= REAGENT_TOTAL_VOLUME(reagents) / 5) // 20% fizzy by volume return 1 return 0 /obj/item/chems/drinks/glass2/proc/has_vapor() - if(LAZYLEN(reagents.reagent_volumes) > 0) + var/reagent_volumes = REAGENT_VOLUMES(reagents) + if(LAZYLEN(reagent_volumes) > 0) if(temperature > T0C + 40) return 1 var/decl/material/primary_reagent = reagents.get_primary_reagent_decl() if(!("vapor" in primary_reagent.glass_special)) var/totalvape = 0 - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if("vapor" in reagent.glass_special) totalvape += REAGENT_VOLUME(reagents, reagent) - if(totalvape >= reagents.maximum_volume * 0.6) // 60% vapor by container volume + if(totalvape >= REAGENT_MAXIMUM_VOLUME(reagents) * 0.6) // 60% vapor by container volume return 1 return 0 @@ -134,7 +137,8 @@ var/global/const/DRINK_ICON_NOISY = "noise" underlays.Cut() icon_state = base_icon - if (LAZYLEN(reagents?.reagent_volumes) > 0) + var/reagent_volumes = REAGENT_VOLUMES(reagents) + if (LAZYLEN(reagent_volumes) > 0) var/decl/material/R = reagents.get_primary_reagent_decl() if(R.cocktail_ingredient) for(var/decl/cocktail/cocktail in SSmaterials.get_cocktails_by_primary_ingredient(R.type)) @@ -204,7 +208,7 @@ var/global/const/DRINK_ICON_NOISY = "noise" playsound(src, "shatter", 30, 1) if(reagents) user.visible_message("The contents of \the [src] splash all over [user]!") - reagents.splash(user, reagents.total_volume) + reagents.splash(user, REAGENT_TOTAL_VOLUME(reagents)) qdel(src) return TRUE user.visible_message("[user] gently strikes \the [src] with a spoon, calling the room to attention.") @@ -220,7 +224,7 @@ var/global/const/DRINK_ICON_NOISY = "noise" update_icon() /obj/item/chems/drinks/glass2/physically_destroyed(var/skip_qdel) - reagents.splash(loc, reagents.total_volume) + reagents.splash(loc, REAGENT_TOTAL_VOLUME(reagents)) if(istype(material)) playsound(src, "shatter", 30, 1) material.place_shards(get_turf(src), w_class) diff --git a/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm b/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm index 79a18bcae309..2691cea39a9c 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/glass_types.dm @@ -160,7 +160,7 @@ base_name = "#1 monkey cup" /obj/item/chems/drinks/glass2/coffeecup/punitelli/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/banana, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/banana, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/glass2/coffeecup/rainbow name = "rainbow coffee cup" diff --git a/code/modules/reagents/reagent_containers/drinks.dm b/code/modules/reagents/reagent_containers/drinks.dm index acf8e61b283e..852d1ee3c400 100644 --- a/code/modules/reagents/reagent_containers/drinks.dm +++ b/code/modules/reagents/reagent_containers/drinks.dm @@ -67,29 +67,30 @@ . = ..() if(distance > 1) return - if(!reagents || reagents.total_volume == 0) + var/total_volume = REAGENT_TOTAL_VOLUME(reagents) + var/max_volume = REAGENT_MAXIMUM_VOLUME(reagents) + if(total_volume <= 0) . += SPAN_NOTICE("\The [src] is empty!") - else if (reagents.total_volume <= reagents.maximum_volume * 0.25) + else if (total_volume <= max_volume * 0.25) . += SPAN_NOTICE("\The [src] is almost empty!") - else if (reagents.total_volume <= reagents.maximum_volume * 0.66) + else if (total_volume <= max_volume * 0.66) . += SPAN_NOTICE("\The [src] is half full!") - else if (reagents.total_volume <= reagents.maximum_volume * 0.90) + else if (total_volume <= max_volume * 0.90) . += SPAN_NOTICE("\The [src] is almost full!") else . += SPAN_NOTICE("\The [src] is full!") /obj/item/chems/drinks/proc/get_filling_state() - var/percent = round((reagents.total_volume / reagents.maximum_volume) * 100) + var/percent = round((REAGENT_TOTAL_VOLUME(reagents) / REAGENT_MAXIMUM_VOLUME(reagents)) * 100) for(var/k in cached_json_decode(filling_states)) if(percent <= k) return k /obj/item/chems/drinks/on_update_icon() . = ..() - if(LAZYLEN(reagents?.reagent_volumes) && filling_states) + if(REAGENT_TOTAL_VOLUME(reagents) && filling_states) add_overlay(overlay_image(icon, "[base_icon][get_filling_state()]", reagents.get_color())) - //////////////////////////////////////////////////////////////////////////////// /// Drinks. END //////////////////////////////////////////////////////////////////////////////// @@ -120,7 +121,7 @@ center_of_mass = @'{"x":16,"y":9}' /obj/item/chems/drinks/milk/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/milk, reagents.maximum_volume, data = list(DATA_MILK_DONOR = "cow")) + add_to_reagents(/decl/material/liquid/drink/milk, REAGENT_MAXIMUM_VOLUME(reagents), data = list(DATA_MILK_DONOR = "cow")) /obj/item/chems/drinks/soymilk name = "soymilk carton" @@ -130,7 +131,7 @@ center_of_mass = @'{"x":16,"y":9}' /obj/item/chems/drinks/soymilk/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/milk/soymilk, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/milk/soymilk, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/milk/smallcarton name = "small milk carton" @@ -138,14 +139,14 @@ icon_state = "mini-milk" /obj/item/chems/drinks/milk/smallcarton/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/milk, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/milk, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/milk/smallcarton/chocolate name = "small chocolate milk carton" desc = "It's milk! This one is in delicious chocolate flavour." /obj/item/chems/drinks/milk/smallcarton/chocolate/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/milk/chocolate, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/milk/chocolate, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/coffee name = "cup of coffee" @@ -154,7 +155,7 @@ center_of_mass = @'{"x":15,"y":10}' /obj/item/chems/drinks/coffee/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/coffee, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/coffee, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/ice name = "cup of ice" @@ -163,7 +164,7 @@ center_of_mass = @'{"x":15,"y":10}' /obj/item/chems/drinks/ice/populate_reagents() - add_to_reagents(/decl/material/solid/ice, reagents.maximum_volume) + add_to_reagents(/decl/material/solid/ice, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/h_chocolate name = "cup of hot cocoa" @@ -173,7 +174,7 @@ center_of_mass = @'{"x":15,"y":13}' /obj/item/chems/drinks/h_chocolate/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/hot_coco, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/hot_coco, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/dry_ramen name = "cup ramen" @@ -183,7 +184,7 @@ center_of_mass = @'{"x":16,"y":11}' /obj/item/chems/drinks/dry_ramen/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/dry_ramen, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/dry_ramen, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/sillycup name = "paper cup" @@ -195,7 +196,7 @@ /obj/item/chems/drinks/sillycup/on_update_icon() . = ..() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) icon_state = "water_cup" else icon_state = "water_cup_e" @@ -283,19 +284,19 @@ desc = "A tall plastic cup of hot black tea." /obj/item/chems/drinks/tea/black/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/tea/black, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/tea/black, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/tea/green name = "cup of green tea" desc = "A tall plastic cup of hot green tea." /obj/item/chems/drinks/tea/green/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/tea/green, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/tea/green, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/tea/chai name = "cup of chai tea" desc = "A tall plastic cup of hot chai tea." /obj/item/chems/drinks/tea/chai/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/tea/chai, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/tea/chai, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/modules/reagents/reagent_containers/drinks/bottle.dm b/code/modules/reagents/reagent_containers/drinks/bottle.dm index f6a7161ed3c4..e0bd34a5b01e 100644 --- a/code/modules/reagents/reagent_containers/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/drinks/bottle.dm @@ -55,9 +55,9 @@ // Dump reagents onto the turf. var/turf/T = against ? get_turf(against) : get_turf(newloc) - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) visible_message(SPAN_DANGER("The contents of \the [src] splash all over \the [against || T]!")) - reagents.splash(against || T, reagents.total_volume) + reagents.splash(against || T, REAGENT_TOTAL_VOLUME(reagents)) if(!T) qdel(src) return @@ -124,9 +124,9 @@ // bottle contents will be tainted by having the rag dipped // in it. if(rag && rag.reagents) - rag.reagents.trans_to(src, rag.reagents.total_volume) + rag.reagents.trans_to(src, REAGENT_TOTAL_VOLUME(rag.reagents)) if(reagents) - reagents.trans_to(rag, min(reagents.total_volume, rag.reagents.maximum_volume)) + reagents.trans_to(rag, min(REAGENT_TOTAL_VOLUME(reagents), REAGENT_MAXIMUM_VOLUME(rag.reagents))) rag.update_name() atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER @@ -177,7 +177,7 @@ //The reagents in the bottle splash all over the target, thanks for the idea Nodrak if(reagents) user.visible_message(SPAN_NOTICE("The contents of \the [src] splash all over [target]!")) - reagents.splash(target, reagents.total_volume) + reagents.splash(target, REAGENT_TOTAL_VOLUME(reagents)) if(rag?.is_on_fire() && istype(target)) target.ignite_fire() @@ -250,7 +250,7 @@ center_of_mass = @'{"x":16,"y":4}' /obj/item/chems/drinks/bottle/gin/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/gin, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/gin, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/whiskey name = "Uncle Git's Special Reserve" @@ -259,7 +259,7 @@ center_of_mass = @'{"x":16,"y":3}' /obj/item/chems/drinks/bottle/whiskey/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/whiskey, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/whiskey, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/agedwhiskey name = "aged whiskey" @@ -268,7 +268,7 @@ center_of_mass = @'{"x":16,"y":3}' /obj/item/chems/drinks/bottle/agedwhiskey/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/aged_whiskey, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/aged_whiskey, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/vodka name = "Tunguska Triple Distilled" @@ -277,7 +277,7 @@ center_of_mass = @'{"x":17,"y":3}' /obj/item/chems/drinks/bottle/vodka/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/vodka, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/vodka, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/tequila name = "Caccavo Guaranteed Quality tequila" @@ -286,7 +286,7 @@ center_of_mass = @'{"x":16,"y":3}' /obj/item/chems/drinks/bottle/tequila/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/tequila, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/tequila, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/patron name = "Wrapp Artiste Patron" @@ -295,7 +295,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/patron/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/tequila, reagents.maximum_volume - 5) + add_to_reagents(/decl/material/liquid/alcohol/tequila, REAGENT_MAXIMUM_VOLUME(reagents) - 5) add_to_reagents(/decl/material/solid/metal/silver, 5) /obj/item/chems/drinks/bottle/rum @@ -305,7 +305,7 @@ center_of_mass = @'{"x":16,"y":8}' /obj/item/chems/drinks/bottle/rum/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/rum, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/rum, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/holywater name = "Flask of Holy Water" @@ -314,7 +314,7 @@ center_of_mass = @'{"x":17,"y":10}' /obj/item/chems/drinks/bottle/holywater/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume, list(DATA_WATER_HOLINESS = TRUE)) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents), list(DATA_WATER_HOLINESS = TRUE)) /obj/item/chems/drinks/bottle/vermouth name = "Goldeneye Vermouth" @@ -323,7 +323,7 @@ center_of_mass = @'{"x":17,"y":3}' /obj/item/chems/drinks/bottle/vermouth/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/vermouth, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/vermouth, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/kahlua name = "Robert Robust's Coffee Liqueur" @@ -332,7 +332,7 @@ center_of_mass = @'{"x":17,"y":3}' /obj/item/chems/drinks/bottle/kahlua/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/coffee, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/coffee, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/goldschlager name = "College Girl Goldschlager" @@ -341,7 +341,7 @@ center_of_mass = @'{"x":15,"y":3}' /obj/item/chems/drinks/bottle/goldschlager/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/vodka, reagents.maximum_volume - 5) + add_to_reagents(/decl/material/liquid/alcohol/vodka, REAGENT_MAXIMUM_VOLUME(reagents) - 5) add_to_reagents(/decl/material/solid/metal/gold, 5) /obj/item/chems/drinks/bottle/cognac @@ -351,7 +351,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/cognac/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/cognac, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/cognac, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/wine name = "Doublebeard Bearded Special Wine" @@ -360,7 +360,7 @@ center_of_mass = @'{"x":16,"y":4}' /obj/item/chems/drinks/bottle/wine/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/wine, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/wine, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/absinthe name = "Jailbreaker Verte" @@ -369,7 +369,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/absinthe/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/absinthe, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/absinthe, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/melonliquor name = "Emeraldine Melon Liquor" @@ -378,7 +378,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/melonliquor/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/melonliquor, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/melonliquor, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/bluecuracao name = "Miss Blue Curacao" @@ -387,7 +387,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/bluecuracao/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/bluecuracao, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/bluecuracao, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/herbal name = "Liqueur d'Herbe" @@ -396,7 +396,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/herbal/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/herbal, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/herbal, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/grenadine name = "Briar Rose Grenadine Syrup" @@ -405,7 +405,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/grenadine/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/grenadine, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/grenadine, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/cola name = "\improper Space Cola" @@ -414,7 +414,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/cola/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/cola, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/cola, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/space_up name = "\improper Space-Up" @@ -423,7 +423,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/space_up/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/lemonade, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/lemonade, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/space_mountain_wind name = "\improper Space Mountain Wind" @@ -432,7 +432,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/space_mountain_wind/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/citrussoda, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/citrussoda, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/pwine name = "Warlock's Velvet" @@ -441,7 +441,7 @@ center_of_mass = @'{"x":16,"y":4}' /obj/item/chems/drinks/bottle/pwine/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/pwine, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/pwine, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/sake name = "Takeo Sadow's Combined Sake" @@ -450,7 +450,7 @@ center_of_mass = @'{"x":16,"y":4}' /obj/item/chems/drinks/bottle/sake/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/sake, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/sake, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/champagne @@ -462,7 +462,7 @@ var/opening /obj/item/chems/drinks/bottle/champagne/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/champagne, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/champagne, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/champagne/open(mob/user) if(ATOM_IS_OPEN_CONTAINER(src)) @@ -497,7 +497,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/bottle/jagermeister/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/jagermeister, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/jagermeister, REAGENT_MAXIMUM_VOLUME(reagents)) //////////////////////////PREMIUM ALCOHOL /////////////////////// /obj/item/chems/drinks/bottle/premiumvodka @@ -512,7 +512,7 @@ /obj/item/chems/drinks/bottle/premiumvodka/populate_reagents() make_random_name() - add_to_reagents(/decl/material/liquid/alcohol/vodka/premium, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/vodka/premium, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/premiumwine name = "Uve De Blanc" @@ -530,7 +530,7 @@ var/agedyear = rand(global.using_map.game_year - aged_max, global.using_map.game_year - aged_min) set_custom_name(make_random_name()) desc += " This bottle is marked as [agedyear] Vintage." - add_to_reagents(/decl/material/liquid/alcohol/wine/premium, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/wine/premium, REAGENT_MAXIMUM_VOLUME(reagents)) //////////////////////////JUICES AND STUFF /////////////////////// @@ -548,7 +548,7 @@ pickup_sound = 'sound/foley/paperpickup2.ogg' /obj/item/chems/drinks/bottle/orangejuice/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/orange, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/orange, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/cream name = "Milk Cream" @@ -564,7 +564,7 @@ pickup_sound = 'sound/foley/paperpickup2.ogg' /obj/item/chems/drinks/bottle/cream/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/milk/cream, reagents.maximum_volume, data = list(DATA_MILK_DONOR = "cow")) + add_to_reagents(/decl/material/liquid/drink/milk/cream, REAGENT_MAXIMUM_VOLUME(reagents), data = list(DATA_MILK_DONOR = "cow")) /obj/item/chems/drinks/bottle/tomatojuice name = "Tomato Juice" @@ -580,7 +580,7 @@ pickup_sound = 'sound/foley/paperpickup2.ogg' /obj/item/chems/drinks/bottle/tomatojuice/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/tomato, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/tomato, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/limejuice name = "Lime Juice" @@ -596,7 +596,7 @@ pickup_sound = 'sound/foley/paperpickup2.ogg' /obj/item/chems/drinks/bottle/limejuice/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/lime, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/lime, REAGENT_MAXIMUM_VOLUME(reagents)) //Small bottles /obj/item/chems/drinks/bottle/small @@ -613,7 +613,7 @@ center_of_mass = @'{"x":16,"y":12}' /obj/item/chems/drinks/bottle/small/beer/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/beer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/beer, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/small/ale name = "\improper Magm-Ale" @@ -623,7 +623,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/bottle/small/ale/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/ale, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/ale, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/bottle/small/gingerbeer name = "Ginger Beer" @@ -632,4 +632,4 @@ center_of_mass = @'{"x":16,"y":12}' /obj/item/chems/drinks/bottle/small/gingerbeer/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/gingerbeer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/gingerbeer, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/modules/reagents/reagent_containers/drinks/cans.dm b/code/modules/reagents/reagent_containers/drinks/cans.dm index c59761ee3e1e..88e6633f2d04 100644 --- a/code/modules/reagents/reagent_containers/drinks/cans.dm +++ b/code/modules/reagents/reagent_containers/drinks/cans.dm @@ -17,7 +17,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/cola/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/cola, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/cola, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/waterbottle name = "bottled water" @@ -27,7 +27,7 @@ material = /decl/material/solid/organic/plastic /obj/item/chems/drinks/cans/waterbottle/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/waterbottle/open(mob/user) playsound(loc,'sound/effects/bonebreak1.ogg', rand(10,50), 1) @@ -41,7 +41,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/space_mountain_wind/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/citrussoda, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/citrussoda, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/thirteenloko name = "\improper Thirteen Loko" @@ -50,7 +50,7 @@ center_of_mass = @'{"x":16,"y":8}' /obj/item/chems/drinks/cans/thirteenloko/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/thirteenloko, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/thirteenloko, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/dr_gibb name = "\improper Dr. Gibb" @@ -59,7 +59,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/dr_gibb/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/cherrycola, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/cherrycola, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/starkist name = "\improper Star-Kist" @@ -68,7 +68,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/starkist/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/orangecola, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/orangecola, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/space_up name = "\improper Space-Up" @@ -77,7 +77,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/space_up/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/lemonade, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/lemonade, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/lemon_lime name = "\improper Lemon-Lime" @@ -86,7 +86,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/lemon_lime/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/lemon_lime, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/lemon_lime, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/iced_tea name = "\improper Vrisk Serket Iced Tea" @@ -95,7 +95,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/iced_tea/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/tea/black, reagents.maximum_volume - 5) + add_to_reagents(/decl/material/liquid/drink/tea/black, REAGENT_MAXIMUM_VOLUME(reagents) - 5) add_to_reagents(/decl/material/solid/ice, 5) /obj/item/chems/drinks/cans/grape_juice @@ -105,7 +105,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/grape_juice/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/grape, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/grape, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/tonic name = "\improper T-Borg's Tonic Water" @@ -114,7 +114,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/tonic/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/tonic, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/tonic, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/sodawater name = "soda water" @@ -123,7 +123,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/sodawater/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/sodawater, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/sodawater, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/beastenergy name = "Beast Energy" @@ -132,7 +132,7 @@ center_of_mass = @'{"x":16,"y":6}' /obj/item/chems/drinks/cans/beastenergy/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/beastenergy, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/beastenergy, REAGENT_MAXIMUM_VOLUME(reagents)) //Items exclusive to the BODA machine on deck 4 and wherever else it pops up. First two are a bit jokey. Second two are genuine article. @@ -143,7 +143,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/syndicolax/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/potato, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/potato, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/artbru name = "\improper Arstotzka Bru" @@ -152,7 +152,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/artbru/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/turnip, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/turnip, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/syndicola name = "\improper TerraCola" @@ -161,7 +161,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/syndicola/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume - 5) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents) - 5) add_to_reagents(/decl/material/solid/metal/iron, 5) /obj/item/chems/drinks/glass2/square/boda @@ -170,7 +170,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/glass2/square/boda/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/sodawater, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/sodawater, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/glass2/square/bodaplus name = "tri kopeiki sirop boda" @@ -189,8 +189,9 @@ /decl/material/liquid/drink/juice/banana, /decl/material/liquid/drink/juice/berry, /decl/material/liquid/drink/juice/watermelon)) - add_to_reagents(/decl/material/liquid/drink/sodawater, reagents.maximum_volume / 2) - add_to_reagents(reag, reagents.maximum_volume / 2) + var/half_max_vol = REAGENT_MAXIMUM_VOLUME(reagents) / 2 + add_to_reagents(/decl/material/liquid/drink/sodawater, half_max_vol) + add_to_reagents(reag, half_max_vol) //Canned alcohols. @@ -202,7 +203,7 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/speer/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/beer/good, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/beer/good, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/cans/ale name = "\improper Magm-Ale" @@ -211,4 +212,4 @@ center_of_mass = @'{"x":16,"y":10}' /obj/item/chems/drinks/cans/ale/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/ale, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/ale, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm b/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm index 4b503ded567a..232146b1208c 100644 --- a/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm +++ b/code/modules/reagents/reagent_containers/drinks/cocktailshaker.dm @@ -19,14 +19,14 @@ return else user.visible_message(SPAN_NOTICE("\The [user] shakes \the [src] gingerly."), SPAN_NOTICE("You shake \the [src] gingerly.")) - if(prob(15) && (reagents && reagents.total_volume)) + if(prob(15) && (reagents && REAGENT_TOTAL_VOLUME(reagents))) user.visible_message(SPAN_WARNING("\The [user] spills the contents of \the [src] over themselves!"), SPAN_WARNING("You spill the contents of \the [src] over yourself!")) - reagents.splash(user, reagents.total_volume) + reagents.splash(user, REAGENT_TOTAL_VOLUME(reagents)) else mix() /obj/item/chems/drinks/shaker/proc/mix() - if(reagents && reagents.total_volume) + if(reagents && REAGENT_TOTAL_VOLUME(reagents)) atom_flags &= ~ATOM_FLAG_NO_REACT HANDLE_REACTIONS(reagents) addtimer(CALLBACK(src, PROC_REF(stop_react)), SSmaterials.wait) diff --git a/code/modules/reagents/reagent_containers/drinks/juicebox.dm b/code/modules/reagents/reagent_containers/drinks/juicebox.dm index cc01e61767ae..c18ff521f4b3 100644 --- a/code/modules/reagents/reagent_containers/drinks/juicebox.dm +++ b/code/modules/reagents/reagent_containers/drinks/juicebox.dm @@ -70,7 +70,7 @@ desc = "A small cardboard juicebox with a cartoon apple on it." /obj/item/chems/drinks/juicebox/apple/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/apple, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/apple, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/juicebox/apple/Initialize() . = ..() @@ -81,7 +81,7 @@ desc = "A small cardboard juicebox with a cartoon orange on it." /obj/item/chems/drinks/juicebox/orange/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/orange, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/orange, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/juicebox/orange/Initialize() . = ..() @@ -92,7 +92,7 @@ desc = "A small cardboard juicebox with some cartoon grapes on it." /obj/item/chems/drinks/juicebox/grape/populate_reagents() - add_to_reagents(/decl/material/liquid/drink/juice/grape, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/drink/juice/grape, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/drinks/juicebox/grape/Initialize() . = ..() @@ -114,7 +114,7 @@ var/decl/material/J = pick(drinktypes) add_to_reagents(J, 20) add_to_reagents(pick(drinktypes - J), 5) - return reagents.reagent_volumes + return REAGENT_VOLUMES(reagents) /obj/item/chems/drinks/juicebox/sensible_random/populate_reagents() var/list/chosen_reagents = juice_it() diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm index 3686618113bd..3efb2501163f 100644 --- a/code/modules/reagents/reagent_containers/dropper.dm +++ b/code/modules/reagents/reagent_containers/dropper.dm @@ -18,7 +18,7 @@ if(!target.reagents || !proximity) return - if(reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) if(!REAGENTS_FREE_SPACE(target.reagents)) to_chat(user, SPAN_WARNING("\The [target] is full.")) @@ -52,12 +52,12 @@ return var/mob/living/M = target - var/contained = REAGENT_LIST(src) + var/contained = REAGENT_LIST(reagents) admin_attack_log(user, M, "Squirted their victim with \a [src] (Reagents: [contained])", "Were squirted with \a [src] (Reagents: [contained])", "used \a [src] (Reagents: [contained]) to squirt at") var/spill_amt = M.incapacitated()? 0 : 30 - trans += reagents.splash(M, reagents.total_volume/2, max_spill = spill_amt) - trans += reagents.trans_to_mob(M, reagents.total_volume/2, CHEM_INJECT) //I guess it gets into the bloodstream through the eyes or something + trans += reagents.splash(M, REAGENT_TOTAL_VOLUME(reagents)/2, max_spill = spill_amt) + trans += reagents.trans_to_mob(M, REAGENT_TOTAL_VOLUME(reagents)/2, CHEM_INJECT) //I guess it gets into the bloodstream through the eyes or something user.visible_message(SPAN_DANGER("[user] squirts something into \the [target]'s eyes!"), SPAN_DANGER("You squirt [trans] unit\s into \the [target]'s eyes!")) return else @@ -70,7 +70,7 @@ to_chat(user, SPAN_NOTICE("You cannot directly remove reagents from [target].")) return - if(!target.reagents || !target.reagents.total_volume) + if(!target.reagents || !REAGENT_TOTAL_VOLUME(target.reagents)) to_chat(user, SPAN_NOTICE("[target] is empty.")) return @@ -86,7 +86,7 @@ /obj/item/chems/dropper/on_update_icon() . = ..() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) icon_state = "dropper1" else icon_state = "dropper0" diff --git a/code/modules/reagents/reagent_containers/food.dm b/code/modules/reagents/reagent_containers/food.dm index 661eed567e3b..31266aba67e5 100644 --- a/code/modules/reagents/reagent_containers/food.dm +++ b/code/modules/reagents/reagent_containers/food.dm @@ -69,7 +69,7 @@ /obj/item/food/lump/on_reagent_change() . = ..() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) SetName(reagents.get_primary_reagent_name()) filling_color = reagents.get_color() else @@ -86,7 +86,7 @@ // Does not rely on ATOM_IS_OPEN_CONTAINER because we want to be able to pour in but not out. /obj/item/food/can_be_poured_into(atom/source) - return (reagents?.maximum_volume > 0) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) /obj/item/food/attack_self(mob/user) if(is_edible(user) && handle_eaten_by_mob(user, user) != EATEN_INVALID) @@ -191,9 +191,8 @@ .[DATA_INGREDIENT_FLAGS] |= allergen_flags /obj/item/food/proc/set_nutriment_data(list/newdata) - if(reagents?.total_volume && reagents.has_reagent(nutriment_type, 1)) - LAZYINITLIST(reagents.reagent_data) - reagents.reagent_data[nutriment_type] = newdata + if(REAGENT_TOTAL_VOLUME(reagents) && reagents.has_reagent(nutriment_type, 1)) + REAGENT_SET_DATA(reagents, nutriment_type, newdata) /obj/item/food/get_utensil_food_type() return _utensil_food_type @@ -205,8 +204,8 @@ bitecount++ /obj/item/food/proc/add_allergen_flags(new_flags) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) var/list/newdata = reagent.mix_data(reagents, list(DATA_INGREDIENT_FLAGS = new_flags)) if(newdata) - LAZYSET(reagents.reagent_data, reagent, newdata) + REAGENT_SET_DATA(reagents, reagent, newdata) diff --git a/code/modules/reagents/reagent_containers/food/burgers.dm b/code/modules/reagents/reagent_containers/food/burgers.dm index 728d89478596..9945c504f4a0 100644 --- a/code/modules/reagents/reagent_containers/food/burgers.dm +++ b/code/modules/reagents/reagent_containers/food/burgers.dm @@ -114,7 +114,7 @@ /obj/item/food/roburgerbig/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/nanitefluid, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/nanitefluid, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/food/xenoburger name = "xenoburger" diff --git a/code/modules/reagents/reagent_containers/food/canned/_canned.dm b/code/modules/reagents/reagent_containers/food/canned/_canned.dm index 28c53e81f5cd..6b7c35f23dff 100644 --- a/code/modules/reagents/reagent_containers/food/canned/_canned.dm +++ b/code/modules/reagents/reagent_containers/food/canned/_canned.dm @@ -63,7 +63,7 @@ return //Bypass searching through the whole icon file for a filling icon /obj/item/food/can/can_be_poured_into(atom/source) - return (reagents?.maximum_volume > 0) && ATOM_IS_OPEN_CONTAINER(src) + return (REAGENT_MAXIMUM_VOLUME(reagents) > 0) && ATOM_IS_OPEN_CONTAINER(src) //Just a short line of Canned Consumables, great for treasure in faraway abandoned outposts diff --git a/code/modules/reagents/reagent_containers/food/dairy/_dairy.dm b/code/modules/reagents/reagent_containers/food/dairy/_dairy.dm index 02de63c6a72f..4265d7079e22 100644 --- a/code/modules/reagents/reagent_containers/food/dairy/_dairy.dm +++ b/code/modules/reagents/reagent_containers/food/dairy/_dairy.dm @@ -19,11 +19,11 @@ . = ..() - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) return - var/list/data = LAZYACCESS(reagents?.reagent_data, nutriment_type) - var/milk_name = LAZYACCESS(data, data_name_field) + var/list/data = REAGENT_DATA(reagents, nutriment_type) + var/milk_name = LAZYACCESS(data, data_name_field) if(milk_name) set_dairy_name(milk_name) set_color(LAZYACCESS(data, data_color_field) || get_default_dairy_color()) diff --git a/code/modules/reagents/reagent_containers/food/eggs.dm b/code/modules/reagents/reagent_containers/food/eggs.dm index 5c3b600079ce..a5a762da21bd 100644 --- a/code/modules/reagents/reagent_containers/food/eggs.dm +++ b/code/modules/reagents/reagent_containers/food/eggs.dm @@ -22,7 +22,7 @@ if(!(proximity && ATOM_IS_OPEN_CONTAINER(O))) // Must be adjacent and open. return to_chat(user, "You crack \the [src] into \the [O].") - reagents.trans_to(O, reagents.total_volume) + reagents.trans_to(O, REAGENT_TOTAL_VOLUME(reagents)) qdel(src) /obj/item/food/egg/throw_impact(atom/hit_atom) @@ -30,7 +30,7 @@ if(QDELETED(src)) return // Could potentially happen with unscupulous atoms on hitby() throwing again, etc. new/obj/effect/decal/cleanable/egg_smudge(src.loc) - reagents.splash(hit_atom, reagents.total_volume) + reagents.splash(hit_atom, REAGENT_TOTAL_VOLUME(reagents)) visible_message("\The [src] has been squashed!","You hear a smack.") qdel(src) diff --git a/code/modules/reagents/reagent_containers/food/sandwich.dm b/code/modules/reagents/reagent_containers/food/sandwich.dm index 0b68fb355dad..3b53ba0c7262 100644 --- a/code/modules/reagents/reagent_containers/food/sandwich.dm +++ b/code/modules/reagents/reagent_containers/food/sandwich.dm @@ -40,7 +40,7 @@ return TRUE to_chat(user, "You layer [used_item] over \the [src].") var/obj/item/chems/F = used_item - F.reagents.trans_to_obj(src, F.reagents.total_volume) + F.reagents.trans_to_obj(src, REAGENT_TOTAL_VOLUME(F.reagents)) ingredients += used_item update_icon() return TRUE diff --git a/code/modules/reagents/reagent_containers/food/sliceable/_sliceable.dm b/code/modules/reagents/reagent_containers/food/sliceable/_sliceable.dm index 1e25fdc14ef3..83dc713862a8 100644 --- a/code/modules/reagents/reagent_containers/food/sliceable/_sliceable.dm +++ b/code/modules/reagents/reagent_containers/food/sliceable/_sliceable.dm @@ -35,7 +35,7 @@ if(filled) var/obj/item/food/whole = new whole_path() if(whole && whole.slice_num) - var/reagent_amount = whole.reagents.total_volume/whole.slice_num + var/reagent_amount = REAGENT_TOTAL_VOLUME(whole.reagents)/whole.slice_num whole.reagents.trans_to_obj(src, reagent_amount) qdel(whole) diff --git a/code/modules/reagents/reagent_containers/food/sushi.dm b/code/modules/reagents/reagent_containers/food/sushi.dm index afb648168a04..e840beb60e33 100644 --- a/code/modules/reagents/reagent_containers/food/sushi.dm +++ b/code/modules/reagents/reagent_containers/food/sushi.dm @@ -26,7 +26,7 @@ fish_type = "tofu" if(topping.reagents) - topping.reagents.trans_to(src, topping.reagents.total_volume) + topping.reagents.trans_to(src, REAGENT_TOTAL_VOLUME(topping.reagents)) var/mob/M = topping.loc if(istype(M)) M.drop_from_inventory(topping) @@ -35,7 +35,7 @@ if(istype(rice)) if(rice.reagents) rice.reagents.trans_to(src, 1) - if(!rice.reagents || !rice.reagents.total_volume) + if(!rice.reagents || !REAGENT_TOTAL_VOLUME(rice.reagents)) var/mob/M = rice.loc if(istype(M)) M.drop_from_inventory(rice) qdel(rice) @@ -98,7 +98,7 @@ bitesize = slices update_icon() if(used_item.reagents) - used_item.reagents.trans_to(src, used_item.reagents.total_volume) + used_item.reagents.trans_to(src, REAGENT_TOTAL_VOLUME(used_item.reagents)) qdel(used_item) return TRUE diff --git a/code/modules/reagents/reagent_containers/food_edibility.dm b/code/modules/reagents/reagent_containers/food_edibility.dm index 6b9a4db8d4e7..85113627dc92 100644 --- a/code/modules/reagents/reagent_containers/food_edibility.dm +++ b/code/modules/reagents/reagent_containers/food_edibility.dm @@ -1,5 +1,5 @@ /obj/item/food/get_edible_material_amount(mob/eater) - return reagents?.total_volume + return REAGENT_TOTAL_VOLUME(reagents) /obj/item/food/get_food_consumption_method(mob/eater) return EATING_METHOD_EAT diff --git a/code/modules/reagents/reagent_containers/glass/bottle.dm b/code/modules/reagents/reagent_containers/glass/bottle.dm index 13c52ae77249..8b954cb90d07 100644 --- a/code/modules/reagents/reagent_containers/glass/bottle.dm +++ b/code/modules/reagents/reagent_containers/glass/bottle.dm @@ -38,8 +38,8 @@ update_icon() /obj/item/chems/glass/bottle/update_overlays() - if(reagents?.total_volume) - var/percent = round(reagents.total_volume / reagents.maximum_volume * 100, 25) + if(REAGENT_TOTAL_VOLUME(reagents)) + var/percent = round(REAGENT_TOTAL_VOLUME(reagents) / REAGENT_MAXIMUM_VOLUME(reagents) * 100, 25) add_overlay(mutable_appearance(icon, "[icon_state]_filling_[percent]", reagents.get_color())) var/image/overglass = mutable_appearance(icon, "[icon_state]_over", color) overglass.alpha = alpha * ((alpha/255) ** 3) @@ -59,7 +59,7 @@ /obj/item/chems/glass/bottle/populate_reagents() SHOULD_CALL_PARENT(TRUE) . = ..() - if(reagents?.total_volume > 0 && autolabel && !label_text) // don't override preset labels + if(REAGENT_TOTAL_VOLUME(reagents) > 0 && autolabel && !label_text) // don't override preset labels label_text = reagents.get_primary_reagent_name() update_name() @@ -67,46 +67,46 @@ desc = "A small bottle. Contains stabilizer - used to stabilize patients." /obj/item/chems/glass/bottle/stabilizer/populate_reagents() - add_to_reagents(/decl/material/liquid/stabilizer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/stabilizer, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/bromide desc = "A small bottle of bromide. Do not drink, it is poisonous." /obj/item/chems/glass/bottle/bromide/populate_reagents() - add_to_reagents(/decl/material/liquid/bromide, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/bromide, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/cyanide desc = "A small bottle of cyanide. Bitter almonds?" /obj/item/chems/glass/bottle/cyanide/populate_reagents() - add_to_reagents(/decl/material/liquid/cyanide, reagents.maximum_volume / 2) //volume changed to match chloral + add_to_reagents(/decl/material/liquid/cyanide, REAGENT_MAXIMUM_VOLUME(reagents) / 2) //volume changed to match chloral . = ..() /obj/item/chems/glass/bottle/sedatives desc = "A small bottle of soporific medication. Just the fumes make you sleepy." /obj/item/chems/glass/bottle/sedatives/populate_reagents() - add_to_reagents(/decl/material/liquid/sedatives, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/sedatives, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/antitoxin desc = "A small bottle of antitoxins. Counters poisons, and repairs damage. A wonder drug." /obj/item/chems/glass/bottle/antitoxin/populate_reagents() - add_to_reagents(/decl/material/liquid/antitoxins, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antitoxins, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/mutagenics desc = "A small bottle of unstable mutagen. Randomly changes the DNA structure of whoever comes in contact." /obj/item/chems/glass/bottle/mutagenics/populate_reagents() - add_to_reagents(/decl/material/liquid/mutagenics, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/mutagenics, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/ammonia/populate_reagents() - add_to_reagents(/decl/material/gas/ammonia, reagents.maximum_volume) + add_to_reagents(/decl/material/gas/ammonia, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/eznutrient @@ -117,7 +117,7 @@ material = /decl/material/solid/organic/plastic /obj/item/chems/glass/bottle/eznutrient/populate_reagents() - add_to_reagents(/decl/material/liquid/fertilizer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/fertilizer, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/left4zed @@ -128,8 +128,8 @@ material = /decl/material/solid/organic/plastic /obj/item/chems/glass/bottle/left4zed/populate_reagents() - var/mutagen_amount = round(reagents.maximum_volume / 6) - add_to_reagents(/decl/material/liquid/fertilizer, reagents.maximum_volume - mutagen_amount) + var/mutagen_amount = round(REAGENT_MAXIMUM_VOLUME(reagents) / 6) + add_to_reagents(/decl/material/liquid/fertilizer, REAGENT_MAXIMUM_VOLUME(reagents) - mutagen_amount) add_to_reagents(/decl/material/liquid/mutagenics, mutagen_amount) . = ..() @@ -141,13 +141,13 @@ material = /decl/material/solid/organic/plastic /obj/item/chems/glass/bottle/robustharvest/populate_reagents() - var/amonia_amount = round(reagents.maximum_volume / 6) - add_to_reagents(/decl/material/liquid/fertilizer, reagents.maximum_volume - amonia_amount) + var/amonia_amount = round(REAGENT_MAXIMUM_VOLUME(reagents) / 6) + add_to_reagents(/decl/material/liquid/fertilizer, REAGENT_MAXIMUM_VOLUME(reagents) - amonia_amount) add_to_reagents(/decl/material/gas/ammonia, amonia_amount) . = ..() /obj/item/chems/glass/bottle/pacid/populate_reagents() - add_to_reagents(/decl/material/liquid/acid/polyacid, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/acid/polyacid, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/adminordrazine desc = "A small bottle. Contains the liquid essence of the gods." @@ -156,19 +156,19 @@ label_color = COLOR_CYAN_BLUE /obj/item/chems/glass/bottle/adminordrazine/populate_reagents() - add_to_reagents(/decl/material/liquid/adminordrazine, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/adminordrazine, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/capsaicin desc = "A small bottle. Contains hot sauce." /obj/item/chems/glass/bottle/capsaicin/populate_reagents() - add_to_reagents(/decl/material/liquid/capsaicin, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/capsaicin, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/frostoil desc = "A small bottle. Contains cold sauce." /obj/item/chems/glass/bottle/frostoil/populate_reagents() - add_to_reagents(/decl/material/liquid/frostoil, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/frostoil, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() diff --git a/code/modules/reagents/reagent_containers/glass/bottle/robot.dm b/code/modules/reagents/reagent_containers/glass/bottle/robot.dm index 530cbf5362c2..501c6017362e 100644 --- a/code/modules/reagents/reagent_containers/glass/bottle/robot.dm +++ b/code/modules/reagents/reagent_containers/glass/bottle/robot.dm @@ -16,7 +16,7 @@ desc = "A small bottle. Contains stabilizer - used to stabilize patients." /obj/item/chems/glass/bottle/robot/stabilizer/populate_reagents() - add_to_reagents(/decl/material/liquid/stabilizer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/stabilizer, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/glass/bottle/robot/antitoxin @@ -26,5 +26,5 @@ icon_state = "bottle-4" /obj/item/chems/glass/bottle/robot/antitoxin/populate_reagents() - add_to_reagents(/decl/material/liquid/antitoxins, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antitoxins, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 3353a13f981f..572561fc288b 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -38,7 +38,7 @@ /obj/item/chems/hypospray/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_WARNING("\The [src] is empty.")) return TRUE @@ -62,7 +62,7 @@ if(!user.do_skilled(time, SKILL_MEDICAL, target)) return TRUE - if(single_use && reagents.total_volume <= 0) // currently only applies to autoinjectors + if(single_use && REAGENT_TOTAL_VOLUME(reagents) <= 0) // currently only applies to autoinjectors atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER // Prevents autoinjectors to be refilled. to_chat(user, SPAN_NOTICE("You inject [target] with [src].")) @@ -71,10 +71,10 @@ user.visible_message(SPAN_WARNING("[user] injects [target] with [src].")) if(target.reagents) - var/contained = REAGENT_LIST(src) + var/contained = REAGENT_LIST(reagents) var/trans = reagents.trans_to_mob(target, amount_per_transfer_from_this, CHEM_INJECT) admin_inject_log(user, target, src, contained, trans) - to_chat(user, SPAN_NOTICE("[trans] unit\s injected. [reagents.total_volume] unit\s remaining in \the [src].")) + to_chat(user, SPAN_NOTICE("[trans] unit\s injected. [REAGENT_TOTAL_VOLUME(reagents)] unit\s remaining in \the [src].")) return TRUE @@ -115,8 +115,8 @@ V.update_icon() loaded_vial = V - create_or_update_reagents(loaded_vial.reagents.maximum_volume, override_volume = TRUE) - loaded_vial.reagents.trans_to_holder(reagents, reagents.maximum_volume) + create_or_update_reagents(REAGENT_MAXIMUM_VOLUME(loaded_vial.reagents), override_volume = TRUE) + loaded_vial.reagents.trans_to_holder(reagents, REAGENT_MAXIMUM_VOLUME(reagents)) if(user) user.visible_message(SPAN_NOTICE("[user] has loaded [V] into \the [src]."), SPAN_NOTICE("[usermessage]")) @@ -126,10 +126,10 @@ return TRUE /obj/item/chems/hypospray/vial/proc/remove_vial(var/mob/user, var/swap_mode, var/should_update_icon = TRUE) - if(!loaded_vial) + if(!loaded_vial || !istype(reagents)) return - reagents.trans_to_holder(loaded_vial.reagents, reagents.maximum_volume) - reagents.maximum_volume = 0 + reagents.trans_to_holder(loaded_vial.reagents, REAGENT_MAXIMUM_VOLUME(reagents)) + REAGENT_SET_MAX_VOL(reagents, 0) loaded_vial.update_icon() if(user) user.put_in_hands(loaded_vial) @@ -190,8 +190,8 @@ /obj/item/chems/hypospray/autoinjector/populate_reagents() SHOULD_CALL_PARENT(TRUE) . = ..() - if(reagents?.total_volume > 0 && autolabel && !label_text) // don't override preset labels - label_text = "[reagents.get_primary_reagent_name()], [reagents.total_volume]u" + if(REAGENT_TOTAL_VOLUME(reagents) > 0 && autolabel && !label_text) // don't override preset labels + label_text = "[reagents.get_primary_reagent_name()], [REAGENT_TOTAL_VOLUME(reagents)]u" /obj/item/chems/hypospray/autoinjector/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) . = ..() @@ -200,12 +200,12 @@ /obj/item/chems/hypospray/autoinjector/on_update_icon() . = ..() - if(reagents?.total_volume <= 0) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0) icon_state = "[icon_state]_used" /obj/item/chems/hypospray/autoinjector/get_examine_strings(mob/user, distance, infix, suffix) . = ..(user) - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) . += SPAN_NOTICE("It is currently loaded.") else . += SPAN_NOTICE("It is spent.") @@ -214,14 +214,14 @@ // Autoinjector - Stabilizer //////////////////////////////////////////////////////////////////////////////// /obj/item/chems/hypospray/autoinjector/stabilizer/populate_reagents() - add_to_reagents(/decl/material/liquid/stabilizer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/stabilizer, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() //////////////////////////////////////////////////////////////////////////////// // Autoinjector - Adrenaline //////////////////////////////////////////////////////////////////////////////// /obj/item/chems/hypospray/autoinjector/adrenaline/populate_reagents() - add_to_reagents(/decl/material/liquid/adrenaline, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/adrenaline, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() //////////////////////////////////////////////////////////////////////////////// @@ -231,7 +231,7 @@ detail_color = COLOR_GREEN /obj/item/chems/hypospray/autoinjector/detox/populate_reagents() - add_to_reagents(/decl/material/liquid/antitoxins, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antitoxins, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() //////////////////////////////////////////////////////////////////////////////// @@ -241,7 +241,7 @@ detail_color = COLOR_PURPLE /obj/item/chems/hypospray/autoinjector/pain/populate_reagents() - add_to_reagents(/decl/material/liquid/painkillers, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/painkillers, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() //////////////////////////////////////////////////////////////////////////////// @@ -251,7 +251,7 @@ detail_color = COLOR_AMBER /obj/item/chems/hypospray/autoinjector/antirad/populate_reagents() - add_to_reagents(/decl/material/liquid/antirads, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antirads, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() //////////////////////////////////////////////////////////////////////////////// @@ -261,7 +261,7 @@ detail_color = COLOR_DARK_GRAY /obj/item/chems/hypospray/autoinjector/hallucinogenics/populate_reagents() - add_to_reagents(/decl/material/liquid/hallucinogenics, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/hallucinogenics, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() //////////////////////////////////////////////////////////////////////////////// @@ -274,9 +274,9 @@ /obj/item/chems/hypospray/autoinjector/clotting/populate_reagents() . = ..() - var/amt = round(reagents.maximum_volume*0.5) + var/amt = round(REAGENT_MAXIMUM_VOLUME(reagents)*0.5) add_to_reagents(/decl/material/liquid/stabilizer, amt) - add_to_reagents(/decl/material/liquid/clotting_agent, (reagents.maximum_volume - amt)) + add_to_reagents(/decl/material/liquid/clotting_agent, (REAGENT_MAXIMUM_VOLUME(reagents) - amt)) //////////////////////////////////////////////////////////////////////////////// // Autoinjector - Empty diff --git a/code/modules/reagents/reagent_containers/inhaler.dm b/code/modules/reagents/reagent_containers/inhaler.dm index 6ed443810914..ef51bccc8e99 100644 --- a/code/modules/reagents/reagent_containers/inhaler.dm +++ b/code/modules/reagents/reagent_containers/inhaler.dm @@ -28,7 +28,7 @@ . = ..() for(var/T in starts_with) add_to_reagents(T, starts_with[T]) - if(ATOM_IS_OPEN_CONTAINER(src) && reagents.total_volume > 0) + if(ATOM_IS_OPEN_CONTAINER(src) && REAGENT_TOTAL_VOLUME(reagents) > 0) atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER update_icon() @@ -37,7 +37,7 @@ . = ..() if(ATOM_IS_OPEN_CONTAINER(src)) add_overlay("[icon_state]_loaded") - if(reagents?.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(reagents) > 0) add_overlay("[icon_state]_reagents") /obj/item/chems/inhaler/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) @@ -49,7 +49,7 @@ to_chat(user, SPAN_NOTICE("You must secure the reagents inside \the [src] before using it!")) return FALSE - if(!reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_WARNING("\The [src] is empty.")) return TRUE @@ -78,12 +78,12 @@ SPAN_NOTICE("You stick \the [src] in \the [target]'s mouth and press the injection button.") ) - var/contained = REAGENT_LIST(src) + var/contained = REAGENT_LIST(reagents) var/trans = reagents.trans_to_mob(target, amount_per_transfer_from_this, CHEM_INHALE) if(trans) admin_inject_log(user, target, src, contained, trans) playsound(src.loc, 'sound/effects/hypospray.ogg', 50, 1) - to_chat(user, SPAN_NOTICE("[trans] units administered. [reagents.total_volume] units remaining in \the [src].")) + to_chat(user, SPAN_NOTICE("[trans] units administered. [REAGENT_TOTAL_VOLUME(reagents)] units remaining in \the [src].")) used = TRUE update_icon() @@ -91,7 +91,7 @@ /obj/item/chems/inhaler/attack_self(mob/user) if(ATOM_IS_OPEN_CONTAINER(src)) - if(reagents.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(reagents) > 0) to_chat(user, SPAN_NOTICE("With a quick twist of \the [src]'s lid, you secure the reagents inside.")) atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER update_icon() @@ -112,7 +112,7 @@ /obj/item/chems/inhaler/get_examine_strings(mob/user, distance, infix, suffix) . = ..(user) if(distance <= 1) - if(reagents.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(reagents) > 0) . += SPAN_NOTICE("It is currently loaded.") else . += SPAN_WARNING("It is spent.") diff --git a/code/modules/reagents/reagent_containers/mortar.dm b/code/modules/reagents/reagent_containers/mortar.dm index 6af747cd6a1d..e1c415a1ecdd 100644 --- a/code/modules/reagents/reagent_containers/mortar.dm +++ b/code/modules/reagents/reagent_containers/mortar.dm @@ -37,11 +37,11 @@ if(attacking_material.hardness <= crushing_material.hardness) to_chat(user, SPAN_NOTICE("\The [used_item] is not hard enough to crush \the [crushing_item].")) return TRUE - if(REAGENTS_FREE_SPACE(reagents) < crushing_item.reagents.total_volume) + if(REAGENTS_FREE_SPACE(reagents) < REAGENT_TOTAL_VOLUME(crushing_item.reagents)) to_chat(user, SPAN_WARNING("\The [src] is too full to grind \the [crushing_item], it'd spill everywhere!")) return TRUE - if(crushing_item.reagents?.total_volume) // if it has no reagents, skip all the fluff and destroy it instantly - var/stamina_to_consume = max(crushing_item.reagents.total_volume * (1 + user.get_stamina_skill_mod()/2), 5) + if(REAGENT_TOTAL_VOLUME(crushing_item.reagents)) // if it has no reagents, skip all the fluff and destroy it instantly + var/stamina_to_consume = max(REAGENT_TOTAL_VOLUME(crushing_item.reagents) * (1 + user.get_stamina_skill_mod()/2), 5) if(stamina_to_consume > 100) // TODO: add user.get_max_stamina()? to_chat(user, SPAN_WARNING("\The [crushing_item] is too large for you to grind in \the [src]!")) return TRUE @@ -58,7 +58,7 @@ if(QDELETED(crushing_item)) return TRUE // already been ground! user.adjust_stamina(-stamina_to_consume) - crushing_item.reagents.trans_to(src, crushing_item.reagents.total_volume, skill_factor) + crushing_item.reagents.trans_to(src, REAGENT_TOTAL_VOLUME(crushing_item.reagents), skill_factor) to_chat(user, SPAN_NOTICE("You finish grinding \the [crushing_item] with \the [used_item].")) QDEL_NULL(crushing_item) // If there's more to crush, try looping diff --git a/code/modules/reagents/reagent_containers/packets.dm b/code/modules/reagents/reagent_containers/packets.dm index 6b417f2254ea..c8f453ddf521 100644 --- a/code/modules/reagents/reagent_containers/packets.dm +++ b/code/modules/reagents/reagent_containers/packets.dm @@ -29,7 +29,7 @@ icon = 'icons/obj/food/condiments/packets/packet_white.dmi' /obj/item/chems/packet/salt/populate_reagents() - add_to_reagents(/decl/material/solid/sodiumchloride, reagents.maximum_volume/2) + add_to_reagents(/decl/material/solid/sodiumchloride, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/pepper name = "pepper packet" @@ -37,7 +37,7 @@ icon = 'icons/obj/food/condiments/packets/packet_black.dmi' /obj/item/chems/packet/pepper/populate_reagents() - add_to_reagents(/decl/material/solid/blackpepper, reagents.maximum_volume/2) + add_to_reagents(/decl/material/solid/blackpepper, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/sugar name = "sugar packet" @@ -45,7 +45,7 @@ icon = 'icons/obj/food/condiments/packets/packet_white.dmi' /obj/item/chems/packet/sugar/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/sugar, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/sugar, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/jelly name = "jelly packet" @@ -53,7 +53,7 @@ icon = 'icons/obj/food/condiments/packets/packet_medium.dmi' /obj/item/chems/packet/jelly/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/cherryjelly, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/nutriment/cherryjelly, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/honey name = "honey packet" @@ -61,7 +61,7 @@ icon = 'icons/obj/food/condiments/packets/packet_medium.dmi' /obj/item/chems/packet/honey/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/honey, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/nutriment/honey, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/honey_fake name = "'honey' packet" @@ -69,7 +69,7 @@ icon = 'icons/obj/food/condiments/packets/packet_medium.dmi' /obj/item/chems/packet/honey_fake/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/sugar, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/nutriment/sugar, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/capsaicin name = "hot sauce packet" @@ -77,7 +77,7 @@ icon = 'icons/obj/food/condiments/packets/packet_red.dmi' /obj/item/chems/packet/capsaicin/populate_reagents() - add_to_reagents(/decl/material/liquid/capsaicin, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/capsaicin, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/ketchup name = "ketchup packet" @@ -85,7 +85,7 @@ icon = 'icons/obj/food/condiments/packets/packet_red.dmi' /obj/item/chems/packet/ketchup/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/ketchup, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/ketchup, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/mayo name = "mayonnaise packet" @@ -93,7 +93,7 @@ icon = 'icons/obj/food/condiments/packets/packet_white.dmi' /obj/item/chems/packet/mayo/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/mayo, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/mayo, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/soy name = "soy sauce packet" @@ -101,56 +101,56 @@ icon = 'icons/obj/food/condiments/packets/packet_black.dmi' /obj/item/chems/packet/soy/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/soysauce, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/soysauce, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/coffee name = "instant coffee powder packet" desc = "Contains 5u of instant coffee powder. Mix with 25u of water." /obj/item/chems/packet/coffee/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/coffee/instant, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/coffee/instant, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/tea name = "instant tea powder packet" desc = "Contains 5u of instant black tea powder. Mix with 25u of water." /obj/item/chems/packet/tea/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/tea/instant, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/tea/instant, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/cocoa name = "cocoa powder packet" desc = "Contains 5u of cocoa powder. Mix with 25u of water and heat." /obj/item/chems/packet/cocoa/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/coco, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/coco, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/grape name = "grape juice powder packet" desc = "Contains 5u of powdered grape juice. Mix with 15u of water." /obj/item/chems/packet/grape/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/instantjuice/grape, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/instantjuice/grape, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/orange name = "orange juice powder packet" desc = "Contains 5u of powdered orange juice. Mix with 15u of water." /obj/item/chems/packet/orange/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/instantjuice/orange, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/instantjuice/orange, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/watermelon name = "watermelon juice powder packet" desc = "Contains 5u of powdered watermelon juice. Mix with 15u of water." /obj/item/chems/packet/watermelon/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/instantjuice/watermelon, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/instantjuice/watermelon, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/apple name = "apple juice powder packet" desc = "Contains 5u of powdered apple juice. Mix with 15u of water." /obj/item/chems/packet/apple/populate_reagents() - add_to_reagents(/decl/material/liquid/nutriment/instantjuice/apple, reagents.maximum_volume/2) + add_to_reagents(/decl/material/liquid/nutriment/instantjuice/apple, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/protein name = "protein powder packet" @@ -158,35 +158,35 @@ icon = 'icons/obj/food/condiments/packets/packet_medium.dmi' /obj/item/chems/packet/protein/populate_reagents() - add_to_reagents(/decl/material/solid/organic/meat, reagents.maximum_volume/2) + add_to_reagents(/decl/material/solid/organic/meat, REAGENT_MAXIMUM_VOLUME(reagents)/2) /obj/item/chems/packet/crayon name = "crayon powder packet" desc = "Contains 10u of powdered crayon. Mix with 30u of water." /obj/item/chems/packet/crayon/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/red/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/red, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/red, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/orange/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/orange, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/orange, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/yellow/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/yellow, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/yellow, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/green/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/green, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/green, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/blue/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/blue, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/blue, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/purple/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/purple, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/purple, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/grey/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/grey, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/grey, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/packet/crayon/brown/populate_reagents() - add_to_reagents(/decl/material/liquid/pigment/brown, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/pigment/brown, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index 3fe3c69ad6f0..74b6bdb129a0 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -36,8 +36,8 @@ /obj/item/chems/pill/populate_reagents() SHOULD_CALL_PARENT(TRUE) . = ..() - if(reagents?.total_volume > 0 && autolabel && !label_text) // don't override preset labels - label_text = "[reagents.get_primary_reagent_name()], [reagents.total_volume]u" + if(REAGENT_TOTAL_VOLUME(reagents) > 0 && autolabel && !label_text) // don't override preset labels + label_text = "[reagents.get_primary_reagent_name()], [REAGENT_TOTAL_VOLUME(reagents)]u" /obj/item/chems/pill/on_update_icon() . = ..() @@ -55,13 +55,13 @@ /obj/item/chems/pill/afterattack(obj/target, mob/user, proximity) if(proximity && ATOM_IS_OPEN_CONTAINER(target) && target.reagents) - if(!target.reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(target.reagents)) to_chat(user, SPAN_WARNING("\The [target] is empty. You can't dissolve \the [src] in it.")) return to_chat(user, SPAN_NOTICE("You dissolve \the [src] in \the [target].")) user.visible_message(SPAN_NOTICE("\The [user] puts something in \the [target]."), range = 2) - admin_attacker_log(user, "spiked \a [target] with a pill. Reagents: [REAGENT_LIST(src)]") - reagents.trans_to(target, reagents.total_volume) + admin_attacker_log(user, "spiked \a [target] with a pill. Reagents: [REAGENT_LIST(reagents)]") + reagents.trans_to(target, REAGENT_TOTAL_VOLUME(reagents)) qdel(src) return return ..() @@ -77,7 +77,7 @@ chem_volume = 50 /obj/item/chems/pill/bromide/populate_reagents() - add_to_reagents(/decl/material/liquid/bromide, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/bromide, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/pill/cyanide @@ -88,7 +88,7 @@ autolabel = FALSE /obj/item/chems/pill/cyanide/populate_reagents() - add_to_reagents(/decl/material/liquid/cyanide, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/cyanide, REAGENT_MAXIMUM_VOLUME(reagents)) . = ..() /obj/item/chems/pill/adminordrazine diff --git a/code/modules/reagents/reagent_containers/pill_edibility.dm b/code/modules/reagents/reagent_containers/pill_edibility.dm index 02034687bb8f..20b43f905702 100644 --- a/code/modules/reagents/reagent_containers/pill_edibility.dm +++ b/code/modules/reagents/reagent_containers/pill_edibility.dm @@ -2,10 +2,10 @@ return EATING_METHOD_EAT /obj/item/chems/pill/get_edible_material_amount(var/mob/eater) - return reagents?.total_volume + return REAGENT_TOTAL_VOLUME(reagents) /obj/item/chems/pill/get_food_default_transfer_amount(mob/eater) - return reagents?.total_volume // Always eat it in one bite. + return REAGENT_TOTAL_VOLUME(reagents) // Always eat it in one bite. /obj/item/chems/pill/show_feed_message_start(mob/user, mob/target, consumption_method = EATING_METHOD_EAT) target = target || user diff --git a/code/modules/reagents/reagent_containers/retort.dm b/code/modules/reagents/reagent_containers/retort.dm index 6605adad776d..2dfaf930e349 100644 --- a/code/modules/reagents/reagent_containers/retort.dm +++ b/code/modules/reagents/reagent_containers/retort.dm @@ -18,10 +18,10 @@ material = /decl/material/solid/stone/pottery /obj/item/chems/glass/retort/update_overlays() - if(reagents?.total_volume && (!material || material.opacity < 1)) + if(REAGENT_TOTAL_VOLUME(reagents) && (!material || material.opacity < 1)) var/datum/gas_mixture/environment = loc?.return_air() var/ambient_pressure = environment ? environment.return_pressure() : ONE_ATMOSPHERE - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(reagent.phase_at_temperature(temperature, ambient_pressure) == MAT_PHASE_GAS) add_overlay(overlay_image(icon, "[icon_state]-fill-boil", reagents.get_color(), (RESET_ALPHA|RESET_COLOR))) return diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 390eff88e405..dcea23312bde 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -40,7 +40,7 @@ if(standard_dispenser_refill(user, A)) return - if(reagents.total_volume < amount_per_transfer_from_this) + if(REAGENT_TOTAL_VOLUME(reagents) < amount_per_transfer_from_this) to_chat(user, SPAN_WARNING("\The [src] is empty!")) return @@ -101,7 +101,7 @@ /obj/item/chems/spray/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(loc == user) - . += "[round(reagents.total_volume)] unit\s left." + . += "[round(REAGENT_TOTAL_VOLUME(reagents))] unit\s left." if(has_safety() && distance <= 1) . += "The safety is [safety ? "on" : "off"]." @@ -137,21 +137,21 @@ particle_move_delay = 6 /obj/item/chems/spray/cleaner/populate_reagents() - add_to_reagents(/decl/material/liquid/cleaner, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/cleaner, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/spray/antiseptic name = "antiseptic spray" desc = "Great for hiding incriminating bloodstains and sterilizing scalpels." /obj/item/chems/spray/antiseptic/populate_reagents() - add_to_reagents(/decl/material/liquid/antiseptic, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antiseptic, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/spray/hair_remover name = "hair remover" desc = "Very effective at removing hair, feathers, spines and horns." /obj/item/chems/spray/hair_remover/populate_reagents() - add_to_reagents(/decl/material/liquid/hair_remover, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/hair_remover, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/spray/pepper name = "pepperspray" @@ -164,7 +164,7 @@ safety = TRUE /obj/item/chems/spray/pepper/populate_reagents() - add_to_reagents(/decl/material/liquid/capsaicin/condensed, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/capsaicin/condensed, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/spray/pepper/has_safety() return TRUE @@ -180,7 +180,7 @@ chem_volume = 10 /obj/item/chems/spray/waterflower/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/spray/chemsprayer name = "chem sprayer" @@ -204,7 +204,7 @@ var/list/the_targets = list(T, T1, T2) for(var/a = 1 to 3) - if(reagents.total_volume < 1) + if(REAGENT_TOTAL_VOLUME(reagents) < 1) break create_chempuff(the_targets[a], rand(6, 8)) return @@ -218,7 +218,7 @@ chem_volume = 100 /obj/item/chems/spray/plantbgone/populate_reagents() - add_to_reagents(/decl/material/liquid/weedkiller, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/weedkiller, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/spray/cleaner/deodorant name = "deodorant" diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 713453cb5b5e..bca089ca6fdd 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -33,7 +33,7 @@ /obj/item/chems/syringe/populate_reagents() SHOULD_CALL_PARENT(TRUE) . = ..() - if(reagents.total_volume > 0 && autolabel && !label_text) // don't override preset labels + if(REAGENT_TOTAL_VOLUME(reagents) > 0 && autolabel && !label_text) // don't override preset labels label_text = reagents.get_primary_reagent_name() update_name() @@ -99,12 +99,12 @@ icon_state = "[icon_state]_broken" return var/rounded_vol = 0 - if (reagents?.total_volume > 0) - rounded_vol = clamp(round((reagents.total_volume / max(1, reagents.maximum_volume * 15)),5), 5, 15) + if (REAGENT_TOTAL_VOLUME(reagents) > 0) + rounded_vol = clamp(round((REAGENT_TOTAL_VOLUME(reagents) / max(1, REAGENT_MAXIMUM_VOLUME(reagents) * 15)),5), 5, 15) if(ismob(loc)) add_overlay((mode == SYRINGE_DRAW)? "[icon_state]_draw" : "[icon_state]_inject") icon_state = "[icon_state]_[rounded_vol]" - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) var/image/filling = image(icon, "[icon_state]_underlay") filling.color = reagents.get_color() filling.appearance_flags |= RESET_COLOR @@ -125,13 +125,13 @@ return if(ismob(target))//Blood! - if(reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_NOTICE("There is already a blood sample in this syringe.")) return if(ishuman(target)) var/amount = REAGENTS_FREE_SPACE(reagents) var/mob/living/human/T = target - if(!T.vessel?.total_volume) + if(!REAGENT_TOTAL_VOLUME(T.vessel)) to_chat(user, SPAN_WARNING("You are unable to locate any blood.")) return @@ -168,7 +168,7 @@ user.visible_message(SPAN_NOTICE("\The [user] takes a blood sample from \the [target].")) else //if not mob - if(!target.reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(target.reagents)) to_chat(user, SPAN_NOTICE("[target] is empty.")) return @@ -190,7 +190,7 @@ syringestab(target, user) return - if(!reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) to_chat(user, SPAN_NOTICE("The syringe is empty.")) mode = SYRINGE_DRAW return @@ -211,8 +211,8 @@ return var/trans = reagents.trans_to(target, amount_per_transfer_from_this) - to_chat(user, SPAN_NOTICE("You inject \the [target] with [trans] units of the solution. \The [src] now contains [src.reagents.total_volume] units.")) - if(reagents.total_volume <= 0 && mode == SYRINGE_INJECT) + to_chat(user, SPAN_NOTICE("You inject \the [target] with [trans] units of the solution. \The [src] now contains [REAGENT_TOTAL_VOLUME(src.reagents)] units.")) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0 && mode == SYRINGE_INJECT) mode = SYRINGE_DRAW update_icon() @@ -257,11 +257,11 @@ var/trans = reagents.trans_to_mob(target, amount_per_transfer_from_this, CHEM_INJECT) if(target != user) - user.visible_message(SPAN_WARNING("\the [user] injects \the [target] with [visible_name]!"), SPAN_NOTICE("You inject \the [target] with [trans] units of the solution. \The [src] now contains [src.reagents.total_volume] units.")) + user.visible_message(SPAN_WARNING("\the [user] injects \the [target] with [visible_name]!"), SPAN_NOTICE("You inject \the [target] with [trans] units of the solution. \The [src] now contains [REAGENT_TOTAL_VOLUME(src.reagents)] units.")) else - to_chat(user, SPAN_NOTICE("You inject yourself with [trans] units of the solution. \The [src] now contains [src.reagents.total_volume] units.")) + to_chat(user, SPAN_NOTICE("You inject yourself with [trans] units of the solution. \The [src] now contains [REAGENT_TOTAL_VOLUME(src.reagents)] units.")) - if(reagents.total_volume <= 0 && mode == SYRINGE_INJECT) + if(REAGENT_TOTAL_VOLUME(reagents) <= 0 && mode == SYRINGE_INJECT) mode = SYRINGE_DRAW update_icon() @@ -297,7 +297,7 @@ user.visible_message(SPAN_DANGER("[user] stabs [target] with [src.name]!")) target.apply_damage(3, BRUTE) - var/syringestab_amount_transferred = rand(0, (reagents.total_volume - 5)) //nerfed by popular demand + var/syringestab_amount_transferred = rand(0, (REAGENT_TOTAL_VOLUME(reagents) - 5)) //nerfed by popular demand var/contained_reagents = reagents.get_reagents() var/trans = reagents.trans_to_mob(target, syringestab_amount_transferred, CHEM_INJECT) if(isnull(trans)) trans = 0 @@ -326,7 +326,7 @@ /obj/item/chems/syringe/ld50_syringe/populate_reagents() SHOULD_CALL_PARENT(FALSE) - add_to_reagents(/decl/material/liquid/heartstopper, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/heartstopper, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/syringe/ld50_syringe/drawReagents(var/target, var/mob/user) if(ismob(target)) // No drawing 60 units of blood at once @@ -343,7 +343,7 @@ mode = SYRINGE_INJECT /obj/item/chems/syringe/stabilizer/populate_reagents() - add_to_reagents(/decl/material/liquid/stabilizer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/stabilizer, REAGENT_MAXIMUM_VOLUME(reagents)) return ..() /obj/item/chems/syringe/antitoxin @@ -351,7 +351,7 @@ mode = SYRINGE_INJECT /obj/item/chems/syringe/antitoxin/populate_reagents() - add_to_reagents(/decl/material/liquid/antitoxins, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antitoxins, REAGENT_MAXIMUM_VOLUME(reagents)) return ..() /obj/item/chems/syringe/antibiotic @@ -359,7 +359,7 @@ mode = SYRINGE_INJECT /obj/item/chems/syringe/antibiotic/populate_reagents() - add_to_reagents(/decl/material/liquid/antibiotics, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/antibiotics, REAGENT_MAXIMUM_VOLUME(reagents)) return ..() /obj/item/chems/syringe/drugs @@ -367,7 +367,7 @@ mode = SYRINGE_INJECT /obj/item/chems/syringe/drugs/populate_reagents() - var/vol_each = round(reagents.maximum_volume / 3) + var/vol_each = round(REAGENT_MAXIMUM_VOLUME(reagents) / 3) add_to_reagents(/decl/material/liquid/psychoactives, vol_each) add_to_reagents(/decl/material/liquid/hallucinogenics, vol_each) add_to_reagents(/decl/material/liquid/presyncopics, vol_each) @@ -378,7 +378,7 @@ mode = SYRINGE_INJECT /obj/item/chems/syringe/steroid/populate_reagents() - var/vol_third = round(reagents.maximum_volume/3) + var/vol_third = round(REAGENT_MAXIMUM_VOLUME(reagents)/3) add_to_reagents(/decl/material/liquid/adrenaline, vol_third) add_to_reagents(/decl/material/liquid/amphetamines, 2 * vol_third) return ..() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 8b2dc8e5dbab..3dd12396af68 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -30,7 +30,7 @@ /obj/structure/reagent_dispensers/on_reagent_change() if(!(. = ..())) return - if(reagents?.total_volume > 0) + if(REAGENT_TOTAL_VOLUME(reagents) > 0) tool_interaction_flags &= ~TOOL_INTERACTION_DECONSTRUCT else tool_interaction_flags |= TOOL_INTERACTION_DECONSTRUCT @@ -38,7 +38,7 @@ /obj/structure/reagent_dispensers/proc/leak() var/turf/T = get_turf(src) if(reagents && T) - reagents.trans_to_turf(T, min(reagents.total_volume, FLUID_PUDDLE)) + reagents.trans_to_turf(T, min(REAGENT_TOTAL_VOLUME(reagents), FLUID_PUDDLE)) /obj/structure/reagent_dispensers/Move() . = ..() @@ -85,7 +85,7 @@ movable_flags = MOVABLE_FLAG_WHEELED /obj/structure/reagent_dispensers/watertank/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/watertank/high name = "high-capacity water tank" @@ -116,7 +116,7 @@ var/obj/item/assembly_holder/rig /obj/structure/reagent_dispensers/fueltank/populate_reagents() - add_to_reagents(/decl/material/liquid/fuel, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/fuel, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/fueltank/get_examine_strings(mob/user, distance, infix, suffix) . = ..() @@ -198,7 +198,7 @@ amount_dispensed = 45 /obj/structure/reagent_dispensers/peppertank/populate_reagents() - add_to_reagents(/decl/material/liquid/capsaicin/condensed, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/capsaicin/condensed, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/water_cooler name = "water cooler" @@ -215,7 +215,7 @@ var/tmp/cup_type = /obj/item/chems/drinks/sillycup /obj/structure/reagent_dispensers/water_cooler/populate_reagents() - add_to_reagents(/decl/material/liquid/water, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/water, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/water_cooler/attack_hand(var/mob/user) if(user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) @@ -251,7 +251,7 @@ /obj/structure/reagent_dispensers/water_cooler/on_reagent_change() . = ..() // Bubbles in top of cooler. - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) var/vend_state = "[icon_state]-vend" if(check_state_in_icon(vend_state, icon)) flick(vend_state, src) @@ -266,7 +266,7 @@ matter = list(/decl/material/solid/metal/stainlesssteel = MATTER_AMOUNT_TRACE) /obj/structure/reagent_dispensers/beerkeg/populate_reagents() - add_to_reagents(/decl/material/liquid/alcohol/beer, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/alcohol/beer, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/reagent_dispensers/acid name = "sulfuric acid dispenser" @@ -277,7 +277,7 @@ density = FALSE /obj/structure/reagent_dispensers/acid/populate_reagents() - add_to_reagents(/decl/material/liquid/acid, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/acid, REAGENT_MAXIMUM_VOLUME(reagents)) //Interactions /obj/structure/reagent_dispensers/get_alt_interactions(var/mob/user) diff --git a/code/modules/scanners/breath.dm b/code/modules/scanners/breath.dm index 24d83b582c71..0c596e83f7f6 100644 --- a/code/modules/scanners/breath.dm +++ b/code/modules/scanners/breath.dm @@ -100,9 +100,9 @@ print_reagent_default_message = FALSE var/datum/reagents/inhaled = target.get_inhaled_reagents() - if(inhaled && inhaled.total_volume) + if(inhaled && REAGENT_TOTAL_VOLUME(inhaled)) var/unknown = 0 - for(var/decl/material/reagent as anything in inhaled.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(inhaled)) if(reagent.scannable) print_reagent_default_message = FALSE . += "[capitalize(reagent.gas_name)] found in subject's breath." diff --git a/code/modules/scanners/health.dm b/code/modules/scanners/health.dm index 487327777b42..99d49d6bb72c 100644 --- a/code/modules/scanners/health.dm +++ b/code/modules/scanners/health.dm @@ -241,10 +241,10 @@ . += "[b]Reagent scan:[endb]" var/print_reagent_default_message = TRUE - if(H.reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(H.reagents)) var/unknown = 0 var/reagentdata[0] - for(var/decl/material/reagent as anything in H.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(H.reagents)) if(reagent.scannable) print_reagent_default_message = FALSE reagentdata[reagent.type] = "[round(REAGENT_VOLUME(H.reagents, reagent), 1)]u [reagent.use_name]" @@ -260,10 +260,10 @@ . += "Warning: Unknown substance[(unknown>1)?"s":""] detected in subject's blood." var/datum/reagents/touching_reagents = H.get_contact_reagents() - if(touching_reagents && touching_reagents.total_volume) + if(touching_reagents && REAGENT_TOTAL_VOLUME(touching_reagents)) var/unknown = 0 var/reagentdata[0] - for(var/decl/material/reagent as anything in touching_reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(touching_reagents)) if(reagent.scannable) print_reagent_default_message = FALSE reagentdata[reagent.type] = "[round(REAGENT_VOLUME(H.reagents, reagent), 1)]u [reagent.name]" @@ -279,9 +279,9 @@ . += "Warning: Unknown substance[(unknown>1)?"s":""] detected on subject's body." var/datum/reagents/ingested = H.get_ingested_reagents() - if(ingested && ingested.total_volume) + if(ingested && REAGENT_TOTAL_VOLUME(ingested)) var/unknown = 0 - for(var/decl/material/reagent as anything in ingested.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(ingested)) if(reagent.scannable) print_reagent_default_message = FALSE . += "[capitalize(reagent.use_name)] found in subject's stomach." @@ -292,9 +292,9 @@ . += "Non-medical reagent[(unknown > 1)?"s":""] found in subject's stomach." var/datum/reagents/inhaled = H.get_inhaled_reagents() - if(inhaled && inhaled.total_volume) + if(inhaled && REAGENT_TOTAL_VOLUME(inhaled)) var/unknown = 0 - for(var/decl/material/reagent as anything in inhaled.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(inhaled)) if(reagent.scannable) print_reagent_default_message = FALSE . += "[capitalize(reagent.use_name)] found in subject's lungs." diff --git a/code/modules/scanners/mass_spectrometer.dm b/code/modules/scanners/mass_spectrometer.dm index b1b194944993..6d66cacddbe4 100644 --- a/code/modules/scanners/mass_spectrometer.dm +++ b/code/modules/scanners/mass_spectrometer.dm @@ -17,11 +17,11 @@ /obj/item/scanner/spectrometer/on_update_icon() . = ..() icon_state = get_world_inventory_state() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) icon_state += "_loaded" /obj/item/scanner/spectrometer/is_valid_scan_target(atom/O) - if(!O.reagents || !O.reagents.total_volume) + if(!O.reagents || !REAGENT_TOTAL_VOLUME(O.reagents)) return FALSE return (O.atom_flags & ATOM_FLAG_OPEN_CONTAINER) || istype(O, /obj/item/chems/syringe) @@ -38,22 +38,22 @@ /obj/item/scanner/spectrometer/attack_self(mob/user) if(!can_use(user)) return - if(reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) scan(src, user) else ..() /proc/mass_spectrometer_scan(var/datum/reagents/reagents, mob/user, var/details) - if(!reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(reagents)) return SPAN_WARNING("No sample to scan.") var/list/blood_traces = list() var/list/blood_doses = list() - if(length(reagents.reagent_volumes) == 1 && istype(reagents.primary_reagent, /decl/material/liquid/random)) - var/decl/material/liquid/random/random = reagents.primary_reagent + var/decl/material/liquid/random/random = reagents.get_primary_reagent_decl() + if(length(REAGENT_VOLUMES(reagents)) == 1 && istype(random)) return random.get_scan_data(user) - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) if(!istype(reagent, /decl/material/liquid/blood)) return SPAN_WARNING("The sample was contaminated! Please insert another sample.") var/data = REAGENT_DATA(reagents, reagent) diff --git a/code/modules/scanners/plant.dm b/code/modules/scanners/plant.dm index e2768e691db6..fc532715c6ad 100644 --- a/code/modules/scanners/plant.dm +++ b/code/modules/scanners/plant.dm @@ -78,12 +78,13 @@ dat += "[uppertext(trait_name)][general_data[trait_name]]" dat += "" - if(LAZYLEN(grown_reagents?.reagent_volumes)) + var/grown_volumes = REAGENT_VOLUMES(grown_reagents) + if(LAZYLEN(grown_volumes)) dat += "

    Reagent Data

    " dat += "
    This sample contains: " - for(var/decl/material/reagent as anything in grown_reagents.liquid_volumes) + for(var/decl/material/reagent as anything in REAGENT_LIQUID_VOLUMES(grown_reagents)) dat += "
    - [reagent.get_reagent_name(grown_reagents, MAT_PHASE_LIQUID)], [LIQUID_VOLUME(grown_reagents, reagent)] unit(s)" - for(var/decl/material/reagent as anything in grown_reagents.solid_volumes) + for(var/decl/material/reagent as anything in REAGENT_SOLID_VOLUMES(grown_reagents)) dat += "
    - [reagent.get_reagent_name(grown_reagents, MAT_PHASE_SOLID)], [SOLID_VOLUME(grown_reagents, reagent)] unit(s)" dat += "

    Other Data

    " diff --git a/code/modules/scanners/reagents.dm b/code/modules/scanners/reagents.dm index 079117f25885..3252aea81854 100644 --- a/code/modules/scanners/reagents.dm +++ b/code/modules/scanners/reagents.dm @@ -17,11 +17,12 @@ /proc/reagent_scan_results(obj/O, details = 0) if(isnull(O.reagents)) return list("No significant chemical agents found in [O].") - if(!LAZYLEN(O.reagents.reagent_volumes)) + var/chem_volumes = REAGENT_VOLUMES(O.reagents) + if(!LAZYLEN(chem_volumes)) return list("No active chemical agents found in [O].") . = list("Chemicals found in [O]:") - var/one_percent = O.reagents.total_volume / 100 - for (var/decl/material/reagent as anything in O.reagents.reagent_volumes) + var/one_percent = REAGENT_TOTAL_VOLUME(O.reagents) / 100 + for (var/decl/material/reagent as anything in REAGENT_VOLUMES(O.reagents)) . += "[reagent.name][details ? ": [REAGENT_VOLUME(O.reagents, reagent) / one_percent]%" : ""]" /obj/item/scanner/reagent/adv diff --git a/code/modules/scent/_scent.dm b/code/modules/scent/_scent.dm index 68a96e3aa0b8..9af54db56df2 100644 --- a/code/modules/scent/_scent.dm +++ b/code/modules/scent/_scent.dm @@ -125,9 +125,9 @@ To add a scent extension to an atom using a reagent's info, where reagent. is th /proc/get_smelliest_reagent(var/datum/reagents/holder) var/decl/material/smelliest var/scent_intensity - if(!holder || !holder.total_volume) + if(!holder || !REAGENT_TOTAL_VOLUME(holder)) return - for(var/decl/material/reagent as anything in holder.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(holder)) if(!reagent.scent) continue var/decl/scent_intensity/scent = GET_DECL(reagent.scent_intensity) diff --git a/code/modules/sealant_gun/sealant_injector.dm b/code/modules/sealant_gun/sealant_injector.dm index 6485f401a8f2..42b68fe0c2ee 100644 --- a/code/modules/sealant_gun/sealant_injector.dm +++ b/code/modules/sealant_gun/sealant_injector.dm @@ -1,8 +1,8 @@ /obj/item/chems/chem_disp_cartridge/foaming_agent/populate_reagents() - add_to_reagents(/decl/material/liquid/foaming_agent, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/foaming_agent, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/chem_disp_cartridge/polyacid/populate_reagents() - add_to_reagents(/decl/material/liquid/acid/polyacid, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/acid/polyacid, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/structure/sealant_injector name = "sealant tank injector" @@ -63,18 +63,18 @@ to_chat(user, SPAN_WARNING("There is no tank loaded.")) return TRUE - var/fill_space = floor(loaded_tank.reagents?.maximum_volume - loaded_tank.reagents?.total_volume) / 5 + var/fill_space = floor(REAGENT_MAXIMUM_VOLUME(loaded_tank.reagents) - REAGENT_TOTAL_VOLUME(loaded_tank.reagents)) / 5 if(fill_space <= 0) to_chat(user, SPAN_WARNING("\The [loaded_tank] is full.")) return TRUE var/injected = FALSE for(var/obj/item/chems/chem_disp_cartridge/cart in cartridges) - if(cart.reagents?.total_volume <= cartridges[cart]) + if(REAGENT_TOTAL_VOLUME(cart.reagents) <= cartridges[cart]) visible_message("\The [src] flashes a red 'empty' light above \the [cart].") continue injected = TRUE - cart.reagents.trans_to_holder(loaded_tank.reagents, min(cart.reagents.total_volume, cartridges[cart] * fill_space)) + cart.reagents.trans_to_holder(loaded_tank.reagents, min(REAGENT_TOTAL_VOLUME(cart.reagents), cartridges[cart] * fill_space)) if(injected) playsound(loc, 'sound/effects/refill.ogg', 50, 1) diff --git a/code/modules/sealant_gun/sealant_tank.dm b/code/modules/sealant_gun/sealant_tank.dm index 7dce9db17401..769713fb9778 100644 --- a/code/modules/sealant_gun/sealant_tank.dm +++ b/code/modules/sealant_gun/sealant_tank.dm @@ -8,7 +8,7 @@ /obj/item/sealant_tank/on_update_icon() . = ..() - add_overlay("fill_[floor((reagents.total_volume/reagents.maximum_volume) * 5)]") + add_overlay("fill_[floor((REAGENT_TOTAL_VOLUME(reagents)/REAGENT_MAXIMUM_VOLUME(reagents)) * 5)]") /obj/item/sealant_tank/get_examine_strings(mob/user, distance, infix, suffix) . = ..() @@ -16,7 +16,7 @@ . += SPAN_NOTICE("\The [src] has about [REAGENT_VOLUME(reagents, /decl/material/liquid/foam) || 0] charge\s of sealant left.") /obj/item/sealant_tank/mapped/populate_reagents() - reagents.add_reagent(/decl/material/liquid/foam, reagents.maximum_volume) + reagents.add_reagent(/decl/material/liquid/foam, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/sealant_tank/physically_destroyed(var/skip_qdel) var/turf/my_turf = get_turf(src) diff --git a/code/modules/surgery/necrotic.dm b/code/modules/surgery/necrotic.dm index 32b74e3f5082..1a1ac046fccf 100644 --- a/code/modules/surgery/necrotic.dm +++ b/code/modules/surgery/necrotic.dm @@ -170,7 +170,7 @@ C.reagents.trans_to_holder(temp_reagents, amount) var/usable_amount = temp_reagents.has_reagent(/decl/material/liquid/regenerator) temp_reagents.clear_reagent(/decl/material/liquid/regenerator) //We'll manually calculate how much it should heal - temp_reagents.trans_to_mob(target, temp_reagents.total_volume, CHEM_INJECT) //And if there was something else, toss it in + temp_reagents.trans_to_mob(target, REAGENT_TOTAL_VOLUME(temp_reagents), CHEM_INJECT) //And if there was something else, toss it in if (usable_amount > 1) var/obj/item/organ/O = target.get_organ(target_organ) diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm index 684694d1406d..f172fa0e5999 100644 --- a/code/modules/surgery/other.dm +++ b/code/modules/surgery/other.dm @@ -207,7 +207,7 @@ var/temp_holder = new/obj() var/datum/reagents/temp_reagents = new(amount, temp_holder) container.reagents.trans_to_holder(temp_reagents, amount) - var/trans = temp_reagents.trans_to_mob(target, temp_reagents.total_volume, CHEM_INJECT) //technically it's contact, but the reagents are being applied to internal tissue + var/trans = temp_reagents.trans_to_mob(target, REAGENT_TOTAL_VOLUME(temp_reagents), CHEM_INJECT) //technically it's contact, but the reagents are being applied to internal tissue if (trans > 0) user.visible_message("[user] rubs [target]'s [affected.name] down with \the [tool]'s contents.", \ "You rub [target]'s [affected.name] down with \the [tool]'s contents.") @@ -240,15 +240,15 @@ if(!valid_container) return FALSE - if(!container.reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(container.reagents)) return FALSE // This check means it's impure. - if(length(container.reagents.reagent_volumes) > length(sterilizing_reagents)) + if(length(REAGENT_VOLUMES(container.reagents)) > length(sterilizing_reagents)) return FALSE // Check if we have sterilizing reagents and -only- sterilizing reagents. - for(var/decl/material/reagent as anything in container.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(container.reagents)) if(!(reagent in sterilizing_reagents)) return FALSE . = TRUE diff --git a/code/modules/vehicles/engine.dm b/code/modules/vehicles/engine.dm index 423db9ca032e..12b97929377a 100644 --- a/code/modules/vehicles/engine.dm +++ b/code/modules/vehicles/engine.dm @@ -98,14 +98,14 @@ /// Attempts to burn a sample of the fuel in our reagent holder. Returns TRUE if enough fuel points are produced to move, otherwise returns FALSE. /obj/item/engine/thermal/proc/burn_fuel() - if(!reagents || reagents.total_volume <= 0 || broken) + if(!reagents || REAGENT_TOTAL_VOLUME(reagents) <= 0 || broken) return FALSE - reagents.trans_to_holder(combustion_chamber, min(reagents.total_volume, 15)) + reagents.trans_to_holder(combustion_chamber, min(REAGENT_TOTAL_VOLUME(reagents), 15)) var/multiplier = 0 var/actually_flammable = FALSE - for(var/decl/material/reagent as anything in temp_reagents_holder.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(temp_reagents_holder.reagents)) var/new_multiplier = 1 - var/reagent_volume = combustion_chamber.reagent_volumes[reagent] + var/reagent_volume = REAGENT_VOLUME(combustion_chamber, reagent) if(reagent.accelerant_value < FUEL_VALUE_NONE) // suppresses fires rather than starts them // this means that FUEL_VALUE_SUPPRESSANT is on par with water in the old code new_multiplier = -(FUEL_VALUE_SUPPRESSANT + reagent.accelerant_value) / 2 * 0.4 diff --git a/code/modules/xenoarcheaology/artifacts/artifact.dm b/code/modules/xenoarcheaology/artifacts/artifact.dm index 96f468938b7b..333048a96932 100644 --- a/code/modules/xenoarcheaology/artifacts/artifact.dm +++ b/code/modules/xenoarcheaology/artifacts/artifact.dm @@ -136,7 +136,7 @@ /obj/structure/artifact/fluid_act(datum/reagents/fluids) ..() - if(!QDELETED(src) && fluids?.total_volume) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids)) check_triggers(/datum/artifact_trigger/proc/on_fluid_act, fluids) // Premade subtypes for mapping or testing. diff --git a/code/modules/xenoarcheaology/machinery/geosample_scanner.dm b/code/modules/xenoarcheaology/machinery/geosample_scanner.dm index 28ec8b72504c..e79e3137f854 100644 --- a/code/modules/xenoarcheaology/machinery/geosample_scanner.dm +++ b/code/modules/xenoarcheaology/machinery/geosample_scanner.dm @@ -78,14 +78,14 @@ //#TODO: The add coolant stuff could probably be handled by the default reagent handling code. And the emptying could be done with an alt interaction. if(choice == "Add coolant") var/obj/item/chems/glass/G = used_item - var/amount_transferred = min(src.reagents.maximum_volume - src.reagents.total_volume, G.reagents.total_volume) + var/amount_transferred = min(REAGENT_MAXIMUM_VOLUME(reagents) - REAGENT_TOTAL_VOLUME(src.reagents), REAGENT_TOTAL_VOLUME(G.reagents)) G.reagents.trans_to(src, amount_transferred) to_chat(user, SPAN_INFO("You empty [amount_transferred]u of coolant into [src].")) update_coolant() return TRUE else if(choice == "Empty coolant") var/obj/item/chems/glass/G = used_item - var/amount_transferred = min(G.reagents.maximum_volume - G.reagents.total_volume, src.reagents.total_volume) + var/amount_transferred = min(REAGENT_MAXIMUM_VOLUME(G.reagents) - REAGENT_TOTAL_VOLUME(G.reagents), REAGENT_TOTAL_VOLUME(src.reagents)) src.reagents.trans_to(G, amount_transferred) to_chat(user, SPAN_INFO("You remove [amount_transferred]u of coolant from [src].")) update_coolant() @@ -112,7 +112,7 @@ fresh_coolant = 0 coolant_purity = 0 var/num_reagent_types = 0 - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) var/cur_purity = coolant_reagents_purity[reagent.type] if(!cur_purity) cur_purity = 0.1 @@ -144,7 +144,7 @@ // data["coolant_usage_rate"] = "[coolant_usage_rate]" data["unused_coolant_abs"] = round(fresh_coolant) - data["unused_coolant_per"] = round(fresh_coolant / reagents.maximum_volume * 100) + data["unused_coolant_per"] = round(fresh_coolant / REAGENT_MAXIMUM_VOLUME(reagents) * 100) data["coolant_purity"] = "[coolant_purity * 100]" // data["optimal_wavelength"] = round(optimal_wavelength) diff --git a/code/unit_tests/chemistry_tests.dm b/code/unit_tests/chemistry_tests.dm index 666048b0a0b4..83aedc6ed755 100644 --- a/code/unit_tests/chemistry_tests.dm +++ b/code/unit_tests/chemistry_tests.dm @@ -62,13 +62,13 @@ var/datum/reagents/checking = get_first_reagent_holder(from) if(!checking) return "first holder is null." - if(checking?.total_volume != from_remaining_target) - return "first holder should have [from_remaining_target]u remaining but has [checking.total_volume]u." + if(REAGENT_TOTAL_VOLUME(checking) != from_remaining_target) + return "first holder should have [from_remaining_target]u remaining but has [REAGENT_TOTAL_VOLUME(checking)]u." checking = get_second_reagent_holder(target) if(!checking) return "second holder is null." - if(checking?.total_volume != to_holding_target) - return "second holder should hold [to_holding_target]u but has [checking.total_volume]u." + if(REAGENT_TOTAL_VOLUME(checking) != to_holding_target) + return "second holder should hold [to_holding_target]u but has [REAGENT_TOTAL_VOLUME(checking)]u." /datum/unit_test/chemistry/proc/validate_holders(var/atom/from, var/atom/target) if(QDELETED(from)) @@ -219,7 +219,7 @@ // Cleanup pt. 2 chem_refs.Cut() - if(spawn_spot.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(spawn_spot.reagents)) spawn_spot.reagents.clear_reagents() failures += "- spawn turf had fluids post-test" diff --git a/mods/content/beekeeping/hive_frame.dm b/mods/content/beekeeping/hive_frame.dm index e126ec1a7fa2..35ffd429777c 100644 --- a/mods/content/beekeeping/hive_frame.dm +++ b/mods/content/beekeeping/hive_frame.dm @@ -8,7 +8,7 @@ /obj/item/hive_frame/on_reagent_change() . = ..() - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) SetName("filled [initial(name)] ([reagents.get_primary_reagent_name()])") else SetName(initial(name)) @@ -19,7 +19,7 @@ var/mesh_state = "[icon_state]-mesh" if(check_state_in_icon(mesh_state, icon)) add_overlay(overlay_image(icon, mesh_state, COLOR_WHITE, RESET_COLOR)) - if(reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) var/comb_state = "[icon_state]-comb" if(check_state_in_icon(comb_state, icon)) add_overlay(overlay_image(icon, comb_state, reagents.get_color(), RESET_COLOR)) @@ -28,8 +28,8 @@ /obj/item/hive_frame/handle_centrifuge_process(obj/machinery/centrifuge/centrifuge) if(!(. = ..())) return - if(reagents.total_volume) - reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, reagents.total_volume) + if(REAGENT_TOTAL_VOLUME(reagents)) + reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, REAGENT_TOTAL_VOLUME(reagents)) for(var/obj/item/thing in contents) thing.dropInto(centrifuge.loc) if(destroy_on_centrifuge) @@ -52,4 +52,4 @@ /obj/item/hive_frame/crafted/filled/populate_reagents() . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/honey, reagents?.maximum_volume) + reagents.add_reagent(/decl/material/liquid/nutriment/honey, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/mods/content/beekeeping/hives/_hive.dm b/mods/content/beekeeping/hives/_hive.dm index 1ace43bbe1cf..990d148a16f9 100644 --- a/mods/content/beekeeping/hives/_hive.dm +++ b/mods/content/beekeeping/hives/_hive.dm @@ -71,7 +71,7 @@ to_chat(user, "There is no place for an another frame.") return TRUE var/obj/item/hive_frame/crafted/H = used_item - if(H.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(H.reagents)) to_chat(user, "\The [used_item] is full with beeswax and honey, empty it in the extractor first.") return TRUE ++frames diff --git a/mods/content/bigpharma/extension.dm b/mods/content/bigpharma/extension.dm index 81ed07f56055..04c672fa51af 100644 --- a/mods/content/bigpharma/extension.dm +++ b/mods/content/bigpharma/extension.dm @@ -35,7 +35,7 @@ /datum/extension/obfuscated_medication/pill_bottle/get_original_reagent(var/obj/item/donor) for(var/obj/item/chems/pill/pill in donor?.contents) - if(pill.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(pill.reagents)) return pill.reagents.get_primary_reagent_name(codex = TRUE) /datum/extension/obfuscated_medication/pill_bottle/update_appearance() @@ -48,7 +48,7 @@ /datum/extension/obfuscated_medication/foil_pack/get_original_reagent(var/obj/item/donor) for(var/obj/item/chems/pill/pill in donor?.contents) - if(pill.reagents?.total_volume) + if(REAGENT_TOTAL_VOLUME(pill.reagents)) return pill.reagents.get_primary_reagent_name(codex = TRUE) /datum/extension/obfuscated_medication/foil_pack/update_appearance() diff --git a/mods/content/blacksmithy/barrel_rim.dm b/mods/content/blacksmithy/barrel_rim.dm index 4dca740435dc..0175a04d3e02 100644 --- a/mods/content/blacksmithy/barrel_rim.dm +++ b/mods/content/blacksmithy/barrel_rim.dm @@ -36,7 +36,7 @@ /obj/structure/reagent_dispensers/barrel/on_reagent_change() . = ..() - if(!metal_material && reagents?.total_volume) + if(!metal_material && REAGENT_TOTAL_VOLUME(reagents)) physically_destroyed() // Adding a rim to a crafted barrel. diff --git a/mods/content/integrated_electronics/components/input.dm b/mods/content/integrated_electronics/components/input.dm index 8d4d8995f115..f39d70cfb867 100644 --- a/mods/content/integrated_electronics/components/input.dm +++ b/mods/content/integrated_electronics/components/input.dm @@ -351,8 +351,8 @@ var/mr = 0 var/tr = 0 if(H.reagents) - mr = H.reagents.maximum_volume - tr = H.reagents.total_volume + mr = REAGENT_MAXIMUM_VOLUME(H.reagents) + tr = REAGENT_TOTAL_VOLUME(H.reagents) set_pin_data(IC_OUTPUT, 6, mr) set_pin_data(IC_OUTPUT, 7, tr) set_pin_data(IC_OUTPUT, 8, H.density) diff --git a/mods/content/integrated_electronics/components/power_passive.dm b/mods/content/integrated_electronics/components/power_passive.dm index 7a855c5a366d..5dd6cf487560 100644 --- a/mods/content/integrated_electronics/components/power_passive.dm +++ b/mods/content/integrated_electronics/components/power_passive.dm @@ -121,7 +121,7 @@ /obj/item/integrated_circuit/passive/power/chemical_cell/on_reagent_change(changetype) if(!(. = ..())) return - set_pin_data(IC_OUTPUT, 1, reagents?.total_volume || 0) + set_pin_data(IC_OUTPUT, 1, REAGENT_TOTAL_VOLUME(reagents)) push_data() /obj/item/integrated_circuit/passive/power/chemical_cell/make_energy() diff --git a/mods/content/integrated_electronics/components/reagents.dm b/mods/content/integrated_electronics/components/reagents.dm index 3cce58ca5c6a..4a28e7772bb2 100644 --- a/mods/content/integrated_electronics/components/reagents.dm +++ b/mods/content/integrated_electronics/components/reagents.dm @@ -13,11 +13,11 @@ /obj/item/integrated_circuit/reagent/Initialize() . = ..() - if(reagents?.maximum_volume) + if(REAGENT_MAXIMUM_VOLUME(reagents)) push_vol() /obj/item/integrated_circuit/reagent/proc/push_vol() - set_pin_data(IC_OUTPUT, 1, reagents?.total_volume || 0) + set_pin_data(IC_OUTPUT, 1, REAGENT_TOTAL_VOLUME(reagents)) push_data() /obj/item/integrated_circuit/reagent/smoke @@ -54,7 +54,7 @@ /obj/item/integrated_circuit/reagent/smoke/do_work(ord) switch(ord) if(1) - if(!reagents || (reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS)) + if(!reagents || (REAGENT_TOTAL_VOLUME(reagents) < IC_SMOKE_REAGENTS_MINIMUM_UNITS)) return var/location = get_turf(src) var/datum/effect/effect/system/smoke_spread/chem/S = new @@ -115,7 +115,7 @@ else direction_mode = IC_REAGENTS_INJECT if(isnum(new_amount)) - new_amount = clamp(new_amount, 0, reagents.maximum_volume) + new_amount = clamp(new_amount, 0, REAGENT_MAXIMUM_VOLUME(reagents)) transfer_amount = new_amount @@ -175,7 +175,7 @@ return if(direction_mode == IC_REAGENTS_INJECT) - if(!reagents.total_volume || !AM.reagents || !REAGENTS_FREE_SPACE(AM.reagents)) + if(!REAGENT_TOTAL_VOLUME(reagents) || !REAGENTS_FREE_SPACE(AM?.reagents)) activate_pin(3) return @@ -204,7 +204,7 @@ reagents.trans_to(AM, transfer_amount) else if(direction_mode == IC_REAGENTS_DRAW) - if(reagents.total_volume >= reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(reagents) >= REAGENT_MAXIMUM_VOLUME(reagents)) acting_object.visible_message("\The [acting_object] tries to draw from [AM], but the injector is full.") activate_pin(3) return @@ -217,7 +217,7 @@ var/injection_delay = 3 SECONDS if(injection_status == INJECTION_PORT) injection_delay += INJECTION_PORT_DELAY - if(!H.vessel?.total_volume || !injection_status) + if(!REAGENT_TOTAL_VOLUME(H.vessel) || !injection_status) activate_pin(3) return H.visible_message( @@ -228,7 +228,7 @@ return else - if(!AM.reagents.total_volume) + if(!REAGENT_TOTAL_VOLUME(AM.reagents)) acting_object.visible_message("\The [acting_object] tries to draw from [AM], but it is empty!") activate_pin(3) return @@ -236,7 +236,7 @@ if(!ATOM_IS_OPEN_CONTAINER(AM)) activate_pin(3) return - tramount = min(tramount, AM.reagents.total_volume) + tramount = min(tramount, REAGENT_TOTAL_VOLUME(AM.reagents)) AM.reagents.trans_to(src, tramount) activate_pin(2) @@ -372,7 +372,7 @@ push_data() /obj/item/integrated_circuit/reagent/storage/grinder/proc/grind() - if(reagents.total_volume >= reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(reagents) >= REAGENT_MAXIMUM_VOLUME(reagents)) activate_pin(3) return FALSE var/obj/item/I = get_pin_data_as_type(IC_INPUT, 1, /obj/item) @@ -380,12 +380,12 @@ if(isnull(I)) return FALSE - if(!I.reagents || !I.reagents.total_volume) + if(!I.reagents || !REAGENT_TOTAL_VOLUME(I.reagents)) activate_pin(3) return FALSE - I.reagents.trans_to(src,I.reagents.total_volume) - if(!I.reagents.total_volume) + I.reagents.trans_to(src,REAGENT_TOTAL_VOLUME(I.reagents)) + if(!REAGENT_TOTAL_VOLUME(I.reagents)) qdel(I) activate_pin(2) @@ -415,7 +415,7 @@ switch(ord) if(1) var/cont[0] - for(var/decl/material/reagent as anything in reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) cont += reagent.name set_pin_data(IC_OUTPUT, 3, cont) push_data() @@ -478,10 +478,10 @@ if(!ATOM_IS_OPEN_CONTAINER(source) || ismob(source)) return - if(target.reagents.maximum_volume - target.reagents.total_volume <= 0) + if(REAGENT_MAXIMUM_VOLUME(target.reagents) - REAGENT_TOTAL_VOLUME(target.reagents) <= 0) return - for(var/decl/material/reagent as anything in source.reagents.reagent_volumes) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(source.reagents)) if(!direction_mode) if(reagent.name in demand) source.reagents.trans_type_to(target, reagent, transfer_amount) diff --git a/mods/content/plant_dissection/grown.dm b/mods/content/plant_dissection/grown.dm index f4c6ea359e00..30e778599fbc 100644 --- a/mods/content/plant_dissection/grown.dm +++ b/mods/content/plant_dissection/grown.dm @@ -93,7 +93,7 @@ if(!can_dissect || !(segment in segments)) return - if(reagents?.total_volume && segment.contributes_to_reagents) + if(REAGENT_TOTAL_VOLUME(reagents) && segment.contributes_to_reagents) for(var/rid in segment.reagents) reagents.remove_reagent(rid, segment.reagents[rid]) diff --git a/mods/content/xenobiology/colours/_colour.dm b/mods/content/xenobiology/colours/_colour.dm index 24a08bb42159..c5de84b8fbe1 100644 --- a/mods/content/xenobiology/colours/_colour.dm +++ b/mods/content/xenobiology/colours/_colour.dm @@ -25,12 +25,13 @@ if(!istype(core) || core.Uses <= 0) return - for(var/decl/material/reagent as anything in holder.reagent_volumes) - if(holder.reagent_volumes[reagent] < 1) + var/holder_volumes = REAGENT_VOLUMES(holder) + for(var/decl/material/reagent as anything in holder_volumes) + if(holder_volumes[reagent] < 1) continue var/call_proc = reaction_procs[reagent] if(call_proc && call(src, call_proc)(holder)) - holder.remove_reagent(reagent, holder.reagent_volumes[reagent]) + holder.remove_reagent(reagent, holder_volumes[reagent]) . = TRUE break diff --git a/mods/content/xenobiology/slime/items.dm b/mods/content/xenobiology/slime/items.dm index 3235d8b0c1b6..26304f3e971b 100644 --- a/mods/content/xenobiology/slime/items.dm +++ b/mods/content/xenobiology/slime/items.dm @@ -45,7 +45,7 @@ add_to_reagents(/decl/material/liquid/slimejelly, 30) /obj/item/slime_extract/on_reagent_change() - if((. = ..()) && reagents?.total_volume) + if((. = ..()) && REAGENT_TOTAL_VOLUME(reagents)) var/decl/slime_colour/slime_data = GET_DECL(slime_type) slime_data.handle_reaction(reagents) diff --git a/mods/content/xenobiology/slime/life.dm b/mods/content/xenobiology/slime/life.dm index d71a1d3edd4c..6cdecb39d6cc 100644 --- a/mods/content/xenobiology/slime/life.dm +++ b/mods/content/xenobiology/slime/life.dm @@ -51,7 +51,7 @@ /mob/living/slime/fluid_act(datum/reagents/fluids) . = ..() - if(!QDELETED(src) && fluids?.total_volume >= FLUID_SHALLOW && stat == DEAD) + if(!QDELETED(src) && REAGENT_TOTAL_VOLUME(fluids) >= FLUID_SHALLOW && stat == DEAD) var/turf/T = get_turf(src) if(T) T.add_to_reagents(/decl/material/liquid/slimejelly, (is_adult ? rand(30, 40) : rand(10, 30))) @@ -66,7 +66,7 @@ /mob/living/slime/fluid_act(datum/reagents/fluids) . = ..() - if(stat == DEAD && fluids?.total_volume && REAGENT_VOLUME(fluids, /decl/material/liquid/water) >= FLUID_SHALLOW) + if(stat == DEAD && REAGENT_TOTAL_VOLUME(fluids) && REAGENT_VOLUME(fluids, /decl/material/liquid/water) >= FLUID_SHALLOW) fluids.add_reagent(/decl/material/liquid/slimejelly, (is_adult ? rand(30, 40) : rand(10, 30))) visible_message(SPAN_DANGER("\The [src] melts away...")) // Slimes are water soluble. qdel(src) diff --git a/mods/gamemodes/cult/ritual.dm b/mods/gamemodes/cult/ritual.dm index 27c5390851a4..5d07ea311482 100644 --- a/mods/gamemodes/cult/ritual.dm +++ b/mods/gamemodes/cult/ritual.dm @@ -29,7 +29,7 @@ return if(A.reagents && A.reagents.has_reagent(/decl/material/liquid/water)) to_chat(user, SPAN_NOTICE("You desecrate \the [A].")) - LAZYSET(A.reagents.reagent_data, /decl/material/liquid/water, list(DATA_WATER_HOLINESS = FALSE)) + REAGENT_SET_DATA(A.reagents, /decl/material/liquid/water, list(DATA_WATER_HOLINESS = FALSE)) /mob/proc/make_rune(var/rune, var/cost = 5, var/tome_required = 0) var/has_robes = 0 @@ -100,7 +100,7 @@ return 0 /mob/living/human/make_rune(var/rune, var/cost, var/tome_required) - if(should_have_organ(BP_HEART) && vessel && vessel.total_volume < species.blood_volume * 0.7) + if(should_have_organ(BP_HEART) && REAGENT_TOTAL_VOLUME(vessel) < species.blood_volume * 0.7) to_chat(src, "You are too weak to draw runes.") return ..() diff --git a/mods/gamemodes/cult/runes.dm b/mods/gamemodes/cult/runes.dm index 32cc1f3024f6..8b6ca53d09f8 100644 --- a/mods/gamemodes/cult/runes.dm +++ b/mods/gamemodes/cult/runes.dm @@ -519,7 +519,7 @@ victim = M if(!victim) return fizzle(user) - if(victim.vessel.total_volume < 20) + if(REAGENT_TOTAL_VOLUME(victim.vessel) < 20) to_chat(user, SPAN_WARNING("This body has no blood in it.")) return fizzle(user) victim.vessel.remove_any(20) @@ -534,7 +534,7 @@ var/list/statuses = list() var/charges = 20 var/use - use = min(charges, user.species.blood_volume - user.vessel.total_volume) + use = min(charges, user.species.blood_volume - REAGENT_TOTAL_VOLUME(user.vessel)) if(use > 0) user.adjust_blood(use) charges -= use diff --git a/mods/species/ascent/machines/ship_machines.dm b/mods/species/ascent/machines/ship_machines.dm index 9b9da51a4acf..447467987067 100644 --- a/mods/species/ascent/machines/ship_machines.dm +++ b/mods/species/ascent/machines/ship_machines.dm @@ -53,10 +53,10 @@ MANTIDIFY(/obj/machinery/door/airlock/external/bolted, "mantid airlock", "door") MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") /obj/item/chems/chem_disp_cartridge/ascent/crystal/populate_reagents() - add_to_reagents(/decl/material/liquid/crystal_agent, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/crystal_agent, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/item/chems/chem_disp_cartridge/ascent/bromide/populate_reagents() - add_to_reagents(/decl/material/liquid/bromide, reagents.maximum_volume) + add_to_reagents(/decl/material/liquid/bromide, REAGENT_MAXIMUM_VOLUME(reagents)) /obj/machinery/sleeper/ascent name = "mantid sleeper" diff --git a/mods/species/drakes/drake_organs.dm b/mods/species/drakes/drake_organs.dm index 988fb597c48e..a1c6cab31cdf 100644 --- a/mods/species/drakes/drake_organs.dm +++ b/mods/species/drakes/drake_organs.dm @@ -20,16 +20,17 @@ /obj/item/organ/internal/drake_gizzard/Process() . = ..() - if(owner && owner.stat != DEAD && !is_broken() && sap_crop && sap_crop.total_volume < 10) + if(owner && owner.stat != DEAD && !is_broken() && sap_crop && REAGENT_TOTAL_VOLUME(sap_crop) < 10) sap_crop.add_reagent(/decl/material/liquid/sifsap, 0.5) /obj/item/organ/internal/drake_gizzard/do_uninstall(in_place, detach, ignore_children, update_icon) . = ..() - if(sap_crop?.total_volume) + var/sap_vol = REAGENT_TOTAL_VOLUME(sap_crop) + if(sap_vol) if(reagents) - sap_crop.trans_to_holder(reagents, sap_crop.total_volume) + sap_crop.trans_to_holder(reagents, sap_vol) else if(isatom(loc)) - sap_crop.splash(loc, sap_crop.total_volume) + sap_crop.splash(loc, sap_vol) sap_crop.clear_reagents() /obj/item/organ/internal/drake_gizzard/Destroy() @@ -37,4 +38,4 @@ . = ..() /obj/item/organ/internal/drake_gizzard/get_stat_info() - return list("Sap reserve", num2text(round((sap_crop?.total_volume || 0), 0.1))) + return list("Sap reserve", num2text(round((REAGENT_TOTAL_VOLUME(sap_crop) || 0), 0.1))) diff --git a/mods/species/drakes/sifsap.dm b/mods/species/drakes/sifsap.dm index 75f0315b71e9..a178721e6e89 100644 --- a/mods/species/drakes/sifsap.dm +++ b/mods/species/drakes/sifsap.dm @@ -1,6 +1,6 @@ /proc/drake_spend_sap(mob/living/user, amount) var/obj/item/organ/internal/drake_gizzard/gizzard = user.get_organ(BP_DRAKE_GIZZARD) - if(!gizzard?.sap_crop?.total_volume) + if(!REAGENT_TOTAL_VOLUME(gizzard?.sap_crop)) return FALSE if(!gizzard.sap_crop.has_reagent(/decl/material/liquid/sifsap, amount)) return FALSE @@ -9,13 +9,14 @@ /proc/drake_has_sap(mob/living/user, amount) var/obj/item/organ/internal/drake_gizzard/gizzard = user.get_organ(BP_DRAKE_GIZZARD) - return gizzard?.sap_crop?.total_volume >= amount + return REAGENT_TOTAL_VOLUME(gizzard?.sap_crop) >= amount /proc/drake_add_sap(mob/living/user, amount) var/obj/item/organ/internal/drake_gizzard/gizzard = user.get_organ(BP_DRAKE_GIZZARD) - if(!gizzard?.sap_crop?.maximum_volume) + var/max_volume = REAGENT_MAXIMUM_VOLUME(gizzard?.sap_crop) + if(!max_volume) return FALSE - if(LAZYACCESS(gizzard.sap_crop.reagent_volumes, /decl/material/liquid/sifsap) >= gizzard.sap_crop.maximum_volume) + if(REAGENT_VOLUME(gizzard.sap_crop, /decl/material/liquid/sifsap) >= max_volume) return FALSE gizzard.sap_crop.add_reagent(/decl/material/liquid/sifsap, amount) return TRUE diff --git a/mods/species/vox/organs_vox.dm b/mods/species/vox/organs_vox.dm index 6329e755d250..c1ba5b7de6c4 100644 --- a/mods/species/vox/organs_vox.dm +++ b/mods/species/vox/organs_vox.dm @@ -97,8 +97,8 @@ if(is_usable()) // Handle some post-metabolism reagent processing for generally inedible foods. - if(ingested.total_volume > 0) - for(var/decl/material/reagent as anything in ingested.reagent_volumes) + if(REAGENT_TOTAL_VOLUME(ingested) > 0) + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(ingested)) var/inedible_nutriment_amount = gains_nutriment_from_inedible_reagents[reagent.type] if(inedible_nutriment_amount > 0) owner.adjust_nutrition(inedible_nutriment_amount) From af4de4b25676959271de5214506a8afca179c45f Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 6 Oct 2025 15:34:45 -0400 Subject: [PATCH 0919/1331] Fix blank line in mob examine --- code/modules/mob/living/human/examine.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index ad99cb4bc326..3ea28d2aadad 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -284,7 +284,7 @@ continue if(last_divider) // insert the divider from the last entry . += last_divider - else if(length(.)) // we already have prior entries, insert our prefix + else if(length(.) && examiner.section_prefix) // we already have prior entries, insert our prefix . += examiner.section_prefix . += adding_text last_divider = examiner.section_postfix From e040d243866c60a83528e161fb1e5d532666da0d Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sun, 16 Nov 2025 13:17:39 -0500 Subject: [PATCH 0920/1331] Add 'in wallet' abstract inventory slot --- code/__defines/items_clothing.dm | 1 + code/_global_vars/lists/clothing.dm | 5 +++-- code/_global_vars/lists/names.dm | 3 ++- code/game/objects/items/__item.dm | 3 +++ code/modules/mob/inventory.dm | 16 +++++++++++++++- code/modules/species/species_hud.dm | 2 ++ maps/~mapsystem/maps.dm | 2 +- 7 files changed, 27 insertions(+), 5 deletions(-) diff --git a/code/__defines/items_clothing.dm b/code/__defines/items_clothing.dm index ffd20f690f91..eaa502407f58 100644 --- a/code/__defines/items_clothing.dm +++ b/code/__defines/items_clothing.dm @@ -82,6 +82,7 @@ #define slot_r_store_str "slot_r_store" #define slot_s_store_str "slot_s_store" #define slot_in_backpack_str "slot_in_backpack" +#define slot_in_wallet_str "slot_in_wallet" // Defined here for consistency, not actually used for slots, just for species clothing offsets. #define slot_undershirt_str "slot_undershirt" diff --git a/code/_global_vars/lists/clothing.dm b/code/_global_vars/lists/clothing.dm index a819fa7c527b..c77cb035a88b 100644 --- a/code/_global_vars/lists/clothing.dm +++ b/code/_global_vars/lists/clothing.dm @@ -30,6 +30,7 @@ var/global/list/airtight_slots = list( var/global/list/abstract_inventory_slots = list( slot_in_backpack_str, + slot_in_wallet_str, slot_undershirt_str, slot_underpants_str, slot_socks_str @@ -40,7 +41,7 @@ var/global/list/vitals_sensor_equip_slots = list( ) var/global/list/headphone_slots = list( - slot_l_ear_str, - slot_r_ear_str, + slot_l_ear_str, + slot_r_ear_str, slot_head_str ) diff --git a/code/_global_vars/lists/names.dm b/code/_global_vars/lists/names.dm index 78cd73fe25b0..741c433285d7 100644 --- a/code/_global_vars/lists/names.dm +++ b/code/_global_vars/lists/names.dm @@ -6,5 +6,6 @@ var/global/list/verbs = file2list("config/names/verbs.txt") var/global/list/adjectives = file2list("config/names/adjectives.txt") var/global/list/abstract_slot_names = list( - slot_in_backpack_str = "In Backpack" + slot_in_backpack_str = "In Backpack", + slot_in_wallet_str = "In Wallet" ) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index f548bcbfada6..d9164b23c5e2 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -726,6 +726,9 @@ if(slot == slot_in_backpack_str) var/obj/item/back = user.get_equipped_item(slot_back_str) return back?.storage?.can_be_inserted(src, user, TRUE) + if(slot == slot_in_wallet_str) + var/obj/item/wallet = user.get_equipped_item(slot_wear_id_str) + return wallet?.storage?.can_be_inserted(src, user, TRUE) var/datum/inventory_slot/inv_slot = user.get_inventory_slot_datum(slot) if(!inv_slot) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index b306780544b9..a565d1e1aae0 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -61,6 +61,14 @@ else prop.dropInto(loc) return TRUE + if(slot == slot_in_wallet_str) + remove_from_mob(prop) + var/obj/item/wallet = get_equipped_item(slot_wear_id_str) + if(wallet) + prop.forceMove(wallet) + else + prop.dropInto(loc) + return TRUE // Attempt to equip accessories if the slot is already blocked. if(!delete_old_item && get_equipped_item(slot)) @@ -126,10 +134,16 @@ /mob/proc/equip_to_storage(obj/item/newitem) // Try put it in their backpack var/obj/item/back = get_equipped_item(slot_back_str) - if(back?.storage?.can_be_inserted(newitem, null, 1)) + if(back?.storage?.can_be_inserted(newitem, null, TRUE)) back.storage.handle_item_insertion(src, newitem) return back + // Or in their wallet + var/obj/item/wallet = get_equipped_item(slot_wear_id_str) + if(wallet?.storage?.can_be_inserted(newitem, null, TRUE)) + wallet.storage.handle_item_insertion(src, newitem) + return wallet + // Try to place it in any item that can store stuff, on the mob. for(var/obj/item/thing in contents) if(thing?.storage?.can_be_inserted(newitem, null, 1)) diff --git a/code/modules/species/species_hud.dm b/code/modules/species/species_hud.dm index 6c67caea5d24..17ab4381d161 100644 --- a/code/modules/species/species_hud.dm +++ b/code/modules/species/species_hud.dm @@ -41,6 +41,8 @@ equip_slots |= slot_handcuffed_str if(slot_back_str in equip_slots) equip_slots |= slot_in_backpack_str + if(slot_wear_id_str in equip_slots) + equip_slots |= slot_in_wallet_str /datum/hud_data/monkey inventory_slots = list( diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index c09d5bc24880..7adfcd3d86b5 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -548,7 +548,7 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable var/obj/item/passport/pass = new passport_type(get_turf(H)) if(istype(pass)) pass.set_info(H) - if(!H.equip_to_slot(pass, slot_in_backpack_str)) + if(!H.equip_to_slot(pass, slot_in_wallet_str) && !H.equip_to_slot(pass, slot_in_backpack_str)) H.put_in_hands(pass) /datum/map/proc/populate_overmap_events() From 1989ef824aea9a607278d1c62892caef7b459099 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 17 Nov 2025 19:01:58 -0500 Subject: [PATCH 0921/1331] Allow quick-equip to place items in backpacks/wallets --- code/datums/inventory_slots/_inventory_slot.dm | 2 ++ code/datums/inventory_slots/slots/slot_back.dm | 1 + code/datums/inventory_slots/slots/slot_id.dm | 1 + code/modules/mob/living/inventory.dm | 6 ++++++ 4 files changed, 10 insertions(+) diff --git a/code/datums/inventory_slots/_inventory_slot.dm b/code/datums/inventory_slots/_inventory_slot.dm index 6f23b1b51a10..b27b2a930901 100644 --- a/code/datums/inventory_slots/_inventory_slot.dm +++ b/code/datums/inventory_slots/_inventory_slot.dm @@ -19,6 +19,8 @@ var/requires_slot_flags var/requires_organ_tag var/quick_equip_priority = 0 // Higher priority means it will be checked first. If null, will not be considered for quick equip. + /// Additional slot ID(s) to add in quick equip. Will always be added at the lowest priority. + var/list/additional_quick_equip_slots /// What depth of fluid is necessary for an item in this slot to be considered submerged? var/fluid_height = FLUID_SHALLOW // we're treating FLUID_SHALLOW as waist level, basically diff --git a/code/datums/inventory_slots/slots/slot_back.dm b/code/datums/inventory_slots/slots/slot_back.dm index 7c2496867edf..25b320f578ad 100644 --- a/code/datums/inventory_slots/slots/slot_back.dm +++ b/code/datums/inventory_slots/slots/slot_back.dm @@ -8,6 +8,7 @@ mob_overlay_layer = HO_BACK_LAYER quick_equip_priority = 14 fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level + additional_quick_equip_slots = list(slot_in_backpack_str) /datum/inventory_slot/back/simple requires_organ_tag = null diff --git a/code/datums/inventory_slots/slots/slot_id.dm b/code/datums/inventory_slots/slots/slot_id.dm index 67b759c682ca..ecde7fc83dba 100644 --- a/code/datums/inventory_slots/slots/slot_id.dm +++ b/code/datums/inventory_slots/slots/slot_id.dm @@ -7,6 +7,7 @@ mob_overlay_layer = HO_ID_LAYER quick_equip_priority = 13 fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level + additional_quick_equip_slots = list(slot_in_wallet_str) // try to go as late as possible /datum/inventory_slot/id/update_mob_equipment_overlay(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE) var/obj/item/clothing/clothes = user.get_equipped_item(slot_w_uniform_str) diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index 4d3d35090dc6..a7d0bb0ca26b 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -14,10 +14,16 @@ var/list/all_slots = list() for(var/slot in get_inventory_slots()) all_slots += get_inventory_slot_datum(slot) + var/list/low_priority_slots // Slots that will always be added at the end, in the order of their parent slots' priority. + // This is sort of due to technical limitations but mostly due to laziness. for(var/datum/inventory_slot/inv_slot as anything in sortTim(all_slots, /proc/cmp_inventory_slot_desc)) + if(LAZYLEN(inv_slot.additional_quick_equip_slots)) + for(var/extra_slot in inv_slot.additional_quick_equip_slots) + LAZYADD(low_priority_slots, extra_slot) if(isnull(inv_slot.quick_equip_priority)) // Never quick-equip into some slots. continue _inventory_slot_priority += inv_slot.slot_id + _inventory_slot_priority += low_priority_slots return _inventory_slot_priority /mob/living/get_inventory_slot_datum(var/slot) From 77cedf118d5fe060488e4713e78003b3b5781d16 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 28 Nov 2025 14:52:53 -0500 Subject: [PATCH 0922/1331] Remove human-level speech problems override --- code/modules/mob/living/human/say.dm | 14 --------- code/modules/mob/living/say.dm | 47 +++++++++++++++++----------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/code/modules/mob/living/human/say.dm b/code/modules/mob/living/human/say.dm index 4f41bcf192ac..3b7ce80dab1f 100644 --- a/code/modules/mob/living/human/say.dm +++ b/code/modules/mob/living/human/say.dm @@ -72,20 +72,6 @@ return verb -/mob/living/human/handle_speech_problems(var/list/message_data) - if(HAS_STATUS(src, STAT_SILENCE) || has_genetic_condition(GENE_COND_MUTED)) - to_chat(src, SPAN_WARNING("You are unable to speak!")) - message_data[1] = "" - return TRUE - - var/obj/item/clothing/mask/M = get_equipped_item(slot_wear_mask_str) - if(istype(M) && M.voicechange) - message_data[1] = pick(M.say_messages) - message_data[2] = pick(M.say_verbs) - return TRUE - - return ..(message_data) - /mob/living/human/handle_message_mode(message_mode, message, verb, speaking, used_radios) if(message_mode == MESSAGE_MODE_WHISPER) //It's going to get sanitized again immediately, so decode. whisper_say(html_decode(message), speaking) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 64d683017724..49e187b25a7f 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -26,27 +26,35 @@ //Takes a list of the form list(message, verb, whispering) and modifies it as needed //Returns 1 if a speech problem was applied, 0 otherwise -/mob/living/proc/handle_speech_problems(var/list/message_data) - var/message = message_data[1] - var/verb = message_data[2] - - . = 0 - - if(HAS_STATUS(src, STAT_SLUR)) - message = slur(message) - verb = pick("slobbers","slurs") - . = 1 +/mob/living/proc/handle_speech_problems(var/list/message_data, var/decl/language/spoken) + var/say_message = message_data[1] + var/say_verb = message_data[2] + + . = FALSE + var/obj/item/clothing/mask/M = get_equipped_item(slot_wear_mask_str) + if(istype(M) && M.voicechange) + say_message = pick(M.say_messages) + say_verb = pick(M.say_verbs) + . = TRUE + else if(HAS_STATUS(src, STAT_SILENCE) || has_genetic_condition(GENE_COND_MUTED)) + to_chat(src, SPAN_WARNING("You are unable to speak!")) + say_message = "" + . = TRUE + else if(HAS_STATUS(src, STAT_SLUR)) + say_message = slur(say_message) + say_verb = pick("slobbers","slurs") + . = TRUE else if(HAS_STATUS(src, STAT_STUTTER)) - message = NewStutter(message) - verb = pick("stammers","stutters") - . = 1 + say_message = NewStutter(say_message) + say_verb = pick("stammers","stutters") + . = TRUE else if(has_chemical_effect(CE_SQUEAKY, 1)) - message = "[message]" - verb = "squeaks" - . = 1 + say_message = "[say_message]" + say_verb = "squeaks" + . = TRUE - message_data[1] = message - message_data[2] = verb + message_data[1] = say_message + message_data[2] = say_verb // Grabs any radios equipped to the mob, with message_mode used to // determine relevancy. See handle_message_mode below. @@ -179,6 +187,9 @@ to_chat(src, SPAN_WARNING("You don't have the right equipment to communicate in that way!")) // weird phrasing, but needs to cover speaking and signing return + // The LANG_FLAG_NO_STUTTER check means nonvocal or unusually-produced + // languages (e.g. sign language, noise emotes, computer beeps) + // will not be affected by stuttering, slurring, silence, etc. effects. if(!(speaking && (speaking.flags & LANG_FLAG_NO_STUTTER))) var/list/message_data = list(message, verb, 0) if(handle_speech_problems(message_data)) From f9edce115615bfbf73775792e44407c4ee673fac Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 28 Nov 2025 15:06:29 -0500 Subject: [PATCH 0923/1331] Improve mask speech blocking check --- code/modules/clothing/masks/miscellaneous.dm | 3 +++ code/modules/mob/say.dm | 7 +++++-- code/modules/sealant_gun/sealant.dm | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 26982600a61e..03225c4afe38 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -20,6 +20,9 @@ say_messages = list("Mmfph!", "Mmmf mrrfff!", "Mmmf mnnf!") say_verbs = list("mumbles", "says") +/obj/item/clothing/mask/muzzle/blocks_speech_in_mouth(mob/wearer) + return TRUE + // Clumsy folks can't take the mask off themselves. /obj/item/clothing/mask/muzzle/attack_hand(mob/user) if(user.get_equipped_item(slot_wear_mask_str) != src || user.check_dexterity(DEXTERITY_HOLD_ITEM)) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index a7363022c794..db8faa6840aa 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -103,14 +103,17 @@ var/global/list/special_channel_keys = list( /mob/proc/is_silenced() . = !!get_item_blocking_speech() +/obj/item/proc/blocks_speech_in_mouth(mob/wearer) + return FALSE + /mob/proc/get_item_blocking_speech() // Can't talk with something in your mouth. var/datum/inventory_slot/mouth_slot = get_inventory_slot_datum(BP_MOUTH) . = mouth_slot?.get_equipped_item() if(!.) var/obj/item/mask = get_equipped_item(slot_wear_mask_str) - if(istype(mask, /obj/item/clothing/mask/muzzle) || istype(mask, /obj/item/sealant)) - . = mask + if(mask?.blocks_speech_in_mouth(src)) + return mask /// Adds punctuation to an emote or speech message automatically. /mob/proc/handle_autopunctuation(message) diff --git a/code/modules/sealant_gun/sealant.dm b/code/modules/sealant_gun/sealant.dm index 5a043f836a45..eeeacfbb689b 100644 --- a/code/modules/sealant_gun/sealant.dm +++ b/code/modules/sealant_gun/sealant.dm @@ -31,6 +31,9 @@ to_chat(user, SPAN_DANGER("Hardened globs of metal foam stick to you!")) hardened = TRUE +/obj/item/sealant/blocks_speech_in_mouth(mob/wearer) + return TRUE + /obj/item/sealant/attack_hand(mob/user) SHOULD_CALL_PARENT(FALSE) break_apart(user) From a4f500a9bea49de948cacdfe4ad6bcbea129a584 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 28 Nov 2025 15:06:59 -0500 Subject: [PATCH 0924/1331] Remove hardcoded sealant tank reference in metalfoam reaction --- code/game/atoms.dm | 3 +++ .../items/weapons/grenades/chem_grenade.dm | 3 +++ code/modules/reagents/Chemistry-Holder.dm | 2 +- .../reactions/reaction_grenade_reaction.dm | 27 ++++++++++--------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index c197b8580b9f..28a1dc589ec6 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1041,6 +1041,9 @@ /atom/proc/is_watertight() return !ATOM_IS_OPEN_CONTAINER(src) +/atom/proc/reaction_can_overflow(decl/chemical_reaction/reaction) + return ATOM_IS_OPEN_CONTAINER(src) + /atom/proc/can_drink_from(mob/user) return ATOM_IS_OPEN_CONTAINER(src) && REAGENT_TOTAL_VOLUME(reagents) && user.check_has_mouth() diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index b4c4612cc565..a00f439f1b42 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -18,6 +18,9 @@ QDEL_NULL_LIST(beakers) . = ..() +/obj/item/grenade/chem_grenade/reaction_can_overflow(decl/chemical_reaction/reaction) + return TRUE // will always overflow despite not being open + /obj/item/grenade/chem_grenade/attack_self(mob/user) if(!stage || stage==1) if(detonator) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 937b0d2d8aae..629091fd1096 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -110,7 +110,7 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new return clone /datum/reagents/proc/get_reaction_loc(chemical_reaction_flags) - if((chemical_reaction_flags & CHEM_REACTION_FLAG_OVERFLOW_CONTAINER) && ATOM_IS_OPEN_CONTAINER(my_atom)) + if((chemical_reaction_flags & CHEM_REACTION_FLAG_OVERFLOW_CONTAINER) && my_atom.reaction_can_overflow()) return get_turf(my_atom) return my_atom diff --git a/code/modules/reagents/reactions/reaction_grenade_reaction.dm b/code/modules/reagents/reactions/reaction_grenade_reaction.dm index bd6d911de3e5..6c55a14c4c4f 100644 --- a/code/modules/reagents/reactions/reaction_grenade_reaction.dm +++ b/code/modules/reagents/reactions/reaction_grenade_reaction.dm @@ -132,16 +132,15 @@ /decl/chemical_reaction/grenade_reaction/metalfoam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) - if(location) - if(istype(location, /obj/item/sealant_tank) && REAGENT_MAXIMUM_VOLUME(location.reagents)) - location.reagents.add_reagent(/decl/material/liquid/foam, created_volume) - return - location = get_turf(location) - if(location) - location.visible_message(SPAN_WARNING("The solution spews out a metallic foam!"), range = 5) - var/datum/effect/effect/system/foam_spread/s = new() - s.set_up(created_volume, location, holder, 1) - s.start() + // This will overflow the container if it's open and create it in the turf + if(isturf(location)) + location.visible_message(SPAN_WARNING("The solution spews out a metallic foam!"), range = 5) + var/datum/effect/effect/system/foam_spread/s = new() + s.set_up(created_volume, location, holder, 1) + s.start() + // We failed to overflow the container, try to add it as a reagent + else if(location && REAGENT_MAXIMUM_VOLUME(location.reagents)) + location.reagents.add_reagent(/decl/material/liquid/foam, created_volume) /decl/chemical_reaction/grenade_reaction/ironfoam name = "Iron Foam" @@ -152,9 +151,13 @@ /decl/chemical_reaction/grenade_reaction/ironfoam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() - var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) - if(location) + var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) + // This will overflow the container if it's open and create it in the turf + if(isturf(location)) location.visible_message(SPAN_WARNING("The solution spews out a metallic foam!"), range = 5) var/datum/effect/effect/system/foam_spread/s = new() s.set_up(created_volume, location, holder, 2) s.start() + // We failed to overflow the container, try to add it as a reagent + else if(location && REAGENT_MAXIMUM_VOLUME(location.reagents)) + location.reagents.add_reagent(/decl/material/liquid/foam, created_volume) \ No newline at end of file From e898511ae5b9b2e981ea6e4fb21ddbd451fbcaca Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 28 Nov 2025 15:30:54 -0500 Subject: [PATCH 0925/1331] Modpack sealant gun module --- maps/exodus/exodus.dm | 1 + maps/ministation/ministation.dm | 1 + maps/modpack_testing/modpack_testing.dm | 1 + maps/tradeship/tradeship.dm | 1 + mods/content/sealant_gun/_sealant_gun.dm | 3 +++ mods/content/sealant_gun/_sealant_gun.dme | 11 +++++++++++ .../content/sealant_gun/icons/sealant_glob.dmi | Bin .../content/sealant_gun/icons}/sealant_gun.dmi | Bin .../content/sealant_gun/icons}/sealant_props.dmi | Bin .../content/sealant_gun/icons}/sealant_tank.dmi | Bin .../content/sealant_gun/sealant_glob.dm | 4 ++-- .../content}/sealant_gun/sealant_gun.dm | 2 +- .../content}/sealant_gun/sealant_injector.dm | 2 +- .../content}/sealant_gun/sealant_rack.dm | 2 +- .../content}/sealant_gun/sealant_tank.dm | 2 +- nebula.dme | 5 ----- 16 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 mods/content/sealant_gun/_sealant_gun.dm create mode 100644 mods/content/sealant_gun/_sealant_gun.dme rename icons/effects/sealant.dmi => mods/content/sealant_gun/icons/sealant_glob.dmi (100%) rename {icons/obj/guns => mods/content/sealant_gun/icons}/sealant_gun.dmi (100%) rename {icons/obj/structures => mods/content/sealant_gun/icons}/sealant_props.dmi (100%) rename {icons/obj => mods/content/sealant_gun/icons}/sealant_tank.dmi (100%) rename code/modules/sealant_gun/sealant.dm => mods/content/sealant_gun/sealant_glob.dm (95%) rename {code/modules => mods/content}/sealant_gun/sealant_gun.dm (97%) rename {code/modules => mods/content}/sealant_gun/sealant_injector.dm (98%) rename {code/modules => mods/content}/sealant_gun/sealant_rack.dm (96%) rename {code/modules => mods/content}/sealant_gun/sealant_tank.dm (94%) diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 8c2255f018a8..4308072cd477 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -14,6 +14,7 @@ #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index 0fdb554342db..f4aafdce3206 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -30,6 +30,7 @@ Twice... #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 212920fd4ad0..d3aafca86084 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -27,6 +27,7 @@ #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/plant_dissection/_plant_dissection.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/shackles/_shackles.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index df97a257d412..542a6dcb22f4 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -29,6 +29,7 @@ #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" diff --git a/mods/content/sealant_gun/_sealant_gun.dm b/mods/content/sealant_gun/_sealant_gun.dm new file mode 100644 index 000000000000..567a8cb4e5b7 --- /dev/null +++ b/mods/content/sealant_gun/_sealant_gun.dm @@ -0,0 +1,3 @@ +/decl/modpack/sealant_gun + name = "Sealant Gun" + desc = "Adds a sealant gun that can be used to launch globs of metal foam to immobilize enemies and seal hull breaches." \ No newline at end of file diff --git a/mods/content/sealant_gun/_sealant_gun.dme b/mods/content/sealant_gun/_sealant_gun.dme new file mode 100644 index 000000000000..1531c31d431f --- /dev/null +++ b/mods/content/sealant_gun/_sealant_gun.dme @@ -0,0 +1,11 @@ +#ifndef CONTENT_PACK_SEALANT +#define CONTENT_PACK_SEALANT +// BEGIN_INCLUDE +#include "_sealant_gun.dm" +#include "sealant_glob.dm" +#include "sealant_gun.dm" +#include "sealant_injector.dm" +#include "sealant_rack.dm" +#include "sealant_tank.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/icons/effects/sealant.dmi b/mods/content/sealant_gun/icons/sealant_glob.dmi similarity index 100% rename from icons/effects/sealant.dmi rename to mods/content/sealant_gun/icons/sealant_glob.dmi diff --git a/icons/obj/guns/sealant_gun.dmi b/mods/content/sealant_gun/icons/sealant_gun.dmi similarity index 100% rename from icons/obj/guns/sealant_gun.dmi rename to mods/content/sealant_gun/icons/sealant_gun.dmi diff --git a/icons/obj/structures/sealant_props.dmi b/mods/content/sealant_gun/icons/sealant_props.dmi similarity index 100% rename from icons/obj/structures/sealant_props.dmi rename to mods/content/sealant_gun/icons/sealant_props.dmi diff --git a/icons/obj/sealant_tank.dmi b/mods/content/sealant_gun/icons/sealant_tank.dmi similarity index 100% rename from icons/obj/sealant_tank.dmi rename to mods/content/sealant_gun/icons/sealant_tank.dmi diff --git a/code/modules/sealant_gun/sealant.dm b/mods/content/sealant_gun/sealant_glob.dm similarity index 95% rename from code/modules/sealant_gun/sealant.dm rename to mods/content/sealant_gun/sealant_glob.dm index eeeacfbb689b..261bec5ee814 100644 --- a/code/modules/sealant_gun/sealant.dm +++ b/mods/content/sealant_gun/sealant_glob.dm @@ -1,7 +1,7 @@ /obj/item/sealant name = "glob of sealant" desc = "A blob of metal foam sealant." - icon = 'icons/effects/sealant.dmi' + icon = 'mods/content/sealant_gun/icons/sealant_glob.dmi' icon_state = ICON_STATE_WORLD color = "#cccdcc" slowdown_general = 3 @@ -86,7 +86,7 @@ /obj/effect/sealant name = "sealant glob" desc = "A blob of metal foam sealant." - icon = 'icons/effects/sealant.dmi' + icon = 'mods/content/sealant_gun/icons/sealant_glob.dmi' icon_state = "blank" layer = PROJECTILE_LAYER color = "#cccdcc" diff --git a/code/modules/sealant_gun/sealant_gun.dm b/mods/content/sealant_gun/sealant_gun.dm similarity index 97% rename from code/modules/sealant_gun/sealant_gun.dm rename to mods/content/sealant_gun/sealant_gun.dm index cbe34aa3cb74..23a753cc0102 100644 --- a/code/modules/sealant_gun/sealant_gun.dm +++ b/mods/content/sealant_gun/sealant_gun.dm @@ -1,7 +1,7 @@ /obj/item/gun/launcher/sealant name = "sealant gun" desc = "A heavy, unwieldy device used to spray metal foam sealant onto hull breaches or damaged flooring." - icon = 'icons/obj/guns/sealant_gun.dmi' + icon = 'mods/content/sealant_gun/icons/sealant_gun.dmi' icon_state = ICON_STATE_WORLD autofire_enabled = TRUE has_safety = FALSE diff --git a/code/modules/sealant_gun/sealant_injector.dm b/mods/content/sealant_gun/sealant_injector.dm similarity index 98% rename from code/modules/sealant_gun/sealant_injector.dm rename to mods/content/sealant_gun/sealant_injector.dm index 42b68fe0c2ee..f5ad42b6ee86 100644 --- a/code/modules/sealant_gun/sealant_injector.dm +++ b/mods/content/sealant_gun/sealant_injector.dm @@ -6,7 +6,7 @@ /obj/structure/sealant_injector name = "sealant tank injector" - icon = 'icons/obj/structures/sealant_props.dmi' + icon = 'mods/content/sealant_gun/icons/sealant_props.dmi' icon_state = "injector" density = TRUE anchored = TRUE diff --git a/code/modules/sealant_gun/sealant_rack.dm b/mods/content/sealant_gun/sealant_rack.dm similarity index 96% rename from code/modules/sealant_gun/sealant_rack.dm rename to mods/content/sealant_gun/sealant_rack.dm index 84e925a7acdc..776591fc94da 100644 --- a/code/modules/sealant_gun/sealant_rack.dm +++ b/mods/content/sealant_gun/sealant_rack.dm @@ -1,6 +1,6 @@ /obj/structure/sealant_rack name = "sealant tank rack" - icon = 'icons/obj/structures/sealant_props.dmi' + icon = 'mods/content/sealant_gun/icons/sealant_props.dmi' icon_state = "rack" density = TRUE anchored = TRUE diff --git a/code/modules/sealant_gun/sealant_tank.dm b/mods/content/sealant_gun/sealant_tank.dm similarity index 94% rename from code/modules/sealant_gun/sealant_tank.dm rename to mods/content/sealant_gun/sealant_tank.dm index 769713fb9778..0356c7e24a8c 100644 --- a/code/modules/sealant_gun/sealant_tank.dm +++ b/mods/content/sealant_gun/sealant_tank.dm @@ -1,7 +1,7 @@ /obj/item/sealant_tank name = "sealant tank" desc = "A sealed tank used to keep hull sealant foam contained under pressure." - icon = 'icons/obj/sealant_tank.dmi' + icon = 'mods/content/sealant_gun/icons/sealant_tank.dmi' icon_state = "tank" material = /decl/material/solid/metal/steel chem_volume = 60 diff --git a/nebula.dme b/nebula.dme index 5f72f4628ca0..cda6ad335fcc 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3747,11 +3747,6 @@ #include "code\modules\scent\scent_candle.dm" #include "code\modules\scent\scent_decls.dm" #include "code\modules\scent\scent_misc.dm" -#include "code\modules\sealant_gun\sealant.dm" -#include "code\modules\sealant_gun\sealant_gun.dm" -#include "code\modules\sealant_gun\sealant_injector.dm" -#include "code\modules\sealant_gun\sealant_rack.dm" -#include "code\modules\sealant_gun\sealant_tank.dm" #include "code\modules\security_levels\_security_level.dm" #include "code\modules\security_levels\alarm_appearance.dm" #include "code\modules\security_levels\keycard_authentication.dm" From 97668f3c4ee6f615b79a4b3ece8ecc7f55caea81 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 19 Dec 2025 20:30:41 -0500 Subject: [PATCH 0926/1331] Fix metal foam grenade contents --- code/game/objects/items/weapons/grenades/chem_grenade.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index a00f439f1b42..2b69b2c9e4a3 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -216,7 +216,7 @@ var/obj/item/chems/glass/beaker/B2 = new(src) B1.add_to_reagents(/decl/material/solid/metal/aluminium, 30) B2.add_to_reagents(/decl/material/liquid/foaming_agent, 10) - B2.add_to_reagents(/decl/material/liquid/acid/polyacid, 10) + B2.add_to_reagents(/decl/material/liquid/acid, 10) detonator = new/obj/item/assembly_holder/timer_igniter(src) beakers += B1 beakers += B2 From e4424444fdd8f8876cb9902d3d731dc5d8bb7487 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 26 Nov 2025 18:19:30 -0500 Subject: [PATCH 0927/1331] Add new grammar helpers --- code/_helpers/text.dm | 30 +++++ code/modules/mob/examine.dm | 12 +- code/modules/mob/mob.dm | 117 ++++++++++++++++++ code/modules/pronouns/_pronouns.dm | 1 + code/modules/pronouns/pronouns_female.dm | 1 + code/modules/pronouns/pronouns_male.dm | 1 + code/modules/pronouns/pronouns_neuter.dm | 1 + .../pronouns/pronouns_second_person.dm | 2 +- mods/species/skrell/datum/pronouns_skrell.dm | 1 + 9 files changed, 160 insertions(+), 6 deletions(-) diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index a264a7fff67a..1869be9fcdae 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -784,6 +784,36 @@ var/global/list/plural_words_unchanged = list( word = "[jointext(splited, " ", 1, length(splited))] [word]" return word +var/global/list/copula_verbs = list("is" = TRUE, "am" = TRUE, "are" = TRUE) +var/global/list/has_verbs = list("has" = TRUE, "have" = TRUE) +var/global/list/does_verbs = list("does" = TRUE, "do" = TRUE) +var/global/list/agreement_exception_verbs = list("can" = TRUE, "cannot" = TRUE, "can't" = TRUE, "will" = TRUE, "won't" = TRUE, "shall" = TRUE, "shan't" = TRUE, "may" = TRUE, "must" = TRUE, "ought" = TRUE, "could" = TRUE, "would" = TRUE, "should" = TRUE) +/proc/verb_agree_with_pronouns(var/use_verb, var/decl/pronouns/use_pronouns, is_after_pronoun = TRUE) + // deal with compound verb phrases here! + // basically, skip all our adverbs, inflect only the first non-adverb word we find (which is hopefully a verb) and then keep everything after unchanged + // this will properly handle "successfully finish doing" and "properly manage to do" + // but not "somehow finish doing" which will get turned into "somehows finish doing" :( + // todo: fix that + var/list/words = splittext(use_verb, " ") + if(length(words) > 1) + var/last_adverb = 1 + for(var/word_index in 1 to length(words)) + if(text_ends_with(words[word_index], "ly")) + last_adverb = max(word_index, last_adverb) + use_verb = words[last_adverb] + // This overengineered nonsense ensures that we shouldn't end up with any doubled or missing spaces. + return jointext_no_nulls(list(jointext(words.Copy(1, last_adverb), " "), verb_agree_with_pronouns(use_verb, use_pronouns, is_after_pronoun = TRUE), jointext(words.Copy(last_adverb + 1, length(words) + 1), " ")), " ") + if(global.copula_verbs[use_verb]) + return use_pronouns.is + else if(global.has_verbs[use_verb]) + return use_pronouns.has + else if(global.does_verbs[use_verb]) + return use_pronouns.does + else if(global.agreement_exception_verbs[use_verb]) + return use_verb + // text_make_plural is intended for nouns, but should hopefully work for "fly" -> "flies" and similar. + return use_pronouns.pluralize_verb ? text_make_plural(use_verb) : use_verb + // Surely we have this defined somewhere already?? /proc/repeatstring(str, num) . = list() diff --git a/code/modules/mob/examine.dm b/code/modules/mob/examine.dm index 6eb5ade8cbcf..2c8252a68272 100644 --- a/code/modules/mob/examine.dm +++ b/code/modules/mob/examine.dm @@ -4,9 +4,13 @@ return GET_DECL(/decl/pronouns) return get_pronouns() +// At some point this could have a client pref or server config option that switches second-person to first-person. +/mob/proc/get_self_pronouns() + return GET_DECL(/decl/pronouns/second_person_singular) + /mob/proc/get_visible_pronouns_for_viewer(mob/viewer, hideflags) if(viewer == src) - return GET_DECL(/decl/pronouns/self) + return get_self_pronouns() return get_visible_pronouns(hideflags) /mob/proc/get_equipment_visibility() @@ -27,10 +31,8 @@ if(slot_desc) . += slot_desc if(buckled) - if(user == src) - . += SPAN_WARNING("You are [html_icon(buckled)] buckled to [buckled]!") - else - . += SPAN_WARNING("[pronouns.He] [pronouns.is] [html_icon(buckled)] buckled to [buckled]!") + // pronouns can be second-person here if user == src so no need to handle that explicitly + . += SPAN_WARNING("[pronouns.He] [pronouns.is] [html_icon(buckled)] buckled to [buckled]!") /mob/proc/get_other_examine_strings(mob/user, distance, infix, suffix, hideflags, decl/pronouns/pronouns) return list() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 5ccfdad3ebe1..4b4438120ff3 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -123,6 +123,123 @@ if(bound_overlay) bound_overlay.visible_message(message, self_message, blind_message) +/mob/proc/get_action_string(is_self, var/using_verb, var/object_phrase, var/infix, var/postfix) + // use mob gender value because otherwise subject verb agreement gets messed up by disguised mobs + // todo: pseudoplural pronouns? we need "Unknown (as David Hasselhoff) spins around." and "You spin around." to both work + // without breaking "The bats spin around." + var/decl/pronouns/using_pronouns = is_self ? get_self_pronouns() : get_pronouns_by_gender(gender) + // A little kludgy/special-cased: we don't use the name for self messages. + var/actor_string = is_self ? using_pronouns.He : "\The [src]" + // this will hopefully handle is/does/has agreement properly + . = "[actor_string] [verb_agree_with_pronouns(using_verb, using_pronouns, is_after_pronoun = is_self)] [infix ? infix + " " : null][object_phrase][postfix ? " " + postfix : null]" + // uh oh, time to handle tokens. + . = replacetext(., "$USER_THEY$", using_pronouns.he) + . = replacetext(., "$USER_THEM$", using_pronouns.him) + . = replacetext(., "$USER_THEIR$", using_pronouns.his) + . = replacetext(., "$USER_SELF$", using_pronouns.self) + . = replacetext(., "$USER_DOES$", using_pronouns.does) + . = replacetext(., "$USER_HAS$", using_pronouns.has) + . = replacetext(., "$USER_IS$", using_pronouns.is) + . = replacetext(., "$USER_S$", using_pronouns.s) + . = replacetext(., "$USER_ES$", using_pronouns.es) + +/mob/proc/get_targeted_action_string(mob/target, is_self, var/using_verb, var/object_phrase, var/infix, var/postfix) + . = get_action_string(is_self, using_verb, object_phrase, infix, postfix) + var/target_is_self = target == src + // use mob gender value because otherwise subject verb agreement gets messed up by disguised mobs + var/decl/pronouns/target_pronouns = target_is_self ? target.get_self_pronouns() : get_pronouns_by_gender(target.gender) + // A little kludgy/special-cased: we don't use the name if it's self-targeted, regardless of who's viewing + . = replacetext(., "$TARGET$", target_is_self ? target_pronouns.self : "\the [target]") + . = replacetext(., "$TARGET_THEY$", target_is_self ? target_pronouns.he : "\the [target]") + . = replacetext(., "$TARGET_THEM$", target_is_self ? target_pronouns.self : "\the [target]") // reflexive, so use self instead of them + . = replacetext(., "$TARGET_THEIR$", target_is_self ? target_pronouns.his : "\the [target]'s") + . = replacetext(., "$TARGET_DOES$", target_pronouns.does) + . = replacetext(., "$TARGET_HAS$", target_pronouns.has) + . = replacetext(., "$TARGET_IS$", target_pronouns.is) + . = replacetext(., "$TARGET_S$", target_pronouns.s) + . = replacetext(., "$TARGET_ES$", target_pronouns.es) + +// Determines span styling used for visible_action_message. +/// Uses SPAN_NOTICE for both self and other messages. +var/global/const/ACTION_DANGER_NONE = 0 +/// Uses SPAN_DANGER for others and SPAN_WARNING for self. +var/global/const/ACTION_DANGER_OTHERS = 1 +/// Uses SPAN_DANGER for both self and others. +var/global/const/ACTION_DANGER_ALL = 2 +/** + Show an action message to all mobs and objects in sight of this mob. + + Used for atoms performing visible actions. Handles basic self-messages automatically. + + - `using_verb`: The verb to use in the message, e.g. "open", "is", "attack". Should be in the base form (no "s" at the end). + - `object_phrase`: The phrase to use after the verb, e.g. "\the [used_item]". Could be a phrase including a gerund or infinitive, like "repairing \the [machine]." + - `dangerous?`: One of the ACTION_DANGER_* constants, determining the styling of the message, OR a string style class. Default: ACTION_DANGER_NONE + - `blind_message?`: The string blind mobs will see. Example: "You hear something!" Default: null + - `range?`: The number of tiles away the message will be visible from. Default: world.view + - `self_infix?`: An optional infix to insert between the verb and object phrase in the self message. Default: null + - `self_postfix?`: An optional postfix to insert after the object phrase in the self message. Default: null + - `other_infix?`: An optional infix to insert between the verb and object phrase in the other message. Default: null + - `other_postfix?`: An optional postfix to insert after the object phrase in the other message. Default: null +*/ +/mob/proc/visible_action_message(var/using_verb, var/object_phrase, var/dangerous = ACTION_DANGER_NONE, var/blind_message = null, var/range = world.view, var/self_infix = null, var/self_postfix = null, var/other_infix = null, var/other_postfix = null) + var/self_message = get_action_string(TRUE, using_verb, object_phrase, self_infix, self_postfix) + var/other_message = get_action_string(FALSE, using_verb, object_phrase, other_infix, other_postfix) + switch(dangerous) + if(ACTION_DANGER_NONE) + other_message = SPAN_NOTICE(other_message) + self_message = SPAN_NOTICE(self_message) + if(ACTION_DANGER_OTHERS) + other_message = SPAN_DANGER(other_message) + self_message = SPAN_WARNING(self_message) + if(ACTION_DANGER_ALL) + other_message = SPAN_DANGER(other_message) + self_message = SPAN_DANGER(self_message) + else // fallback for stuff like lighter styling + other_message = SPAN_CLASS(dangerous, other_message) + self_message = SPAN_CLASS(dangerous, self_message) + visible_message( + other_message, + self_message, + blind_message, + range + ) + +/mob/proc/targeted_visible_action_message(var/mob/target, var/using_verb, var/object_phrase, var/dangerous = ACTION_DANGER_NONE, var/blind_message = null, var/range = world.view, var/self_infix = null, var/self_postfix = null, var/other_infix = null, var/other_postfix = null) + var/self_message = get_targeted_action_string(target, TRUE, using_verb, object_phrase, self_infix, self_postfix) + var/other_message = get_targeted_action_string(target, FALSE, using_verb, object_phrase, other_infix, other_postfix) + switch(dangerous) + if(ACTION_DANGER_NONE) + other_message = SPAN_NOTICE(other_message) + self_message = SPAN_NOTICE(self_message) + if(ACTION_DANGER_OTHERS) + other_message = SPAN_DANGER(other_message) + self_message = SPAN_WARNING(self_message) + if(ACTION_DANGER_ALL) + other_message = SPAN_DANGER(other_message) + self_message = SPAN_DANGER(self_message) + else // fallback for stuff like lighter styling + other_message = SPAN_CLASS(dangerous, other_message) + self_message = SPAN_CLASS(dangerous, self_message) + visible_message( + other_message, + self_message, + blind_message, + range + ) + +/mob/proc/self_action_message(var/using_verb, var/object_phrase, var/dangerous = ACTION_DANGER_NONE, var/infix, var/postfix) + var/the_message = get_targeted_action_string(src, TRUE, using_verb, object_phrase, infix, postfix) + switch(dangerous) + if(ACTION_DANGER_NONE) + the_message = SPAN_NOTICE(the_message) + if(ACTION_DANGER_OTHERS) + the_message = SPAN_WARNING(the_message) + if(ACTION_DANGER_ALL) + the_message = SPAN_DANGER(the_message) + else // fallback for stuff like lighter styling + the_message = SPAN_CLASS(dangerous, the_message) + to_chat(src, the_message) + // Show a message to all mobs and objects in earshot of this one // This would be for audible actions by the src mob // message is the message output to anyone who can hear. diff --git a/code/modules/pronouns/_pronouns.dm b/code/modules/pronouns/_pronouns.dm index 83f899499b0e..74181c938907 100644 --- a/code/modules/pronouns/_pronouns.dm +++ b/code/modules/pronouns/_pronouns.dm @@ -14,6 +14,7 @@ var/is = "are" var/does = "do" var/self = "themselves" + var/pluralize_verb = FALSE var/s = "" var/es = "" diff --git a/code/modules/pronouns/pronouns_female.dm b/code/modules/pronouns/pronouns_female.dm index 41a90f3cabae..fb287654b984 100644 --- a/code/modules/pronouns/pronouns_female.dm +++ b/code/modules/pronouns/pronouns_female.dm @@ -15,3 +15,4 @@ self = "herself" s = "s" es = "es" + pluralize_verb = TRUE diff --git a/code/modules/pronouns/pronouns_male.dm b/code/modules/pronouns/pronouns_male.dm index 47ea51748d06..8e179967f549 100644 --- a/code/modules/pronouns/pronouns_male.dm +++ b/code/modules/pronouns/pronouns_male.dm @@ -15,6 +15,7 @@ self = "himself" s = "s" es = "es" + pluralize_verb = TRUE // Thanks oldcoders. var/static/list/weird_euphemisms_for_your_balls = list( diff --git a/code/modules/pronouns/pronouns_neuter.dm b/code/modules/pronouns/pronouns_neuter.dm index faccf5a25893..2f4eda4b5f39 100644 --- a/code/modules/pronouns/pronouns_neuter.dm +++ b/code/modules/pronouns/pronouns_neuter.dm @@ -11,6 +11,7 @@ self = "itself" s = "s" es = "es" + pluralize_verb = TRUE // Alternative to plural neuter. // With thanks to https://tib.cjcs.com/genderless-pronouns-ey-em-and-eir-2/ diff --git a/code/modules/pronouns/pronouns_second_person.dm b/code/modules/pronouns/pronouns_second_person.dm index 4860a1afaaa5..b3760b4a0d14 100644 --- a/code/modules/pronouns/pronouns_second_person.dm +++ b/code/modules/pronouns/pronouns_second_person.dm @@ -1,4 +1,4 @@ -/decl/pronouns/self +/decl/pronouns/second_person_singular name = SECOND_PERSON_SINGULAR He = "You" he = "you" diff --git a/mods/species/skrell/datum/pronouns_skrell.dm b/mods/species/skrell/datum/pronouns_skrell.dm index 8fc3fd80c6f9..1fd612aa74b2 100644 --- a/mods/species/skrell/datum/pronouns_skrell.dm +++ b/mods/species/skrell/datum/pronouns_skrell.dm @@ -14,3 +14,4 @@ self = "vilself" s = "s" es = "es" + pluralize_verb = TRUE From c9b9b1b7649f78571a24cafd73a8354d50cecca0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 13:03:51 -0500 Subject: [PATCH 0928/1331] Add and implement pseudoplural pronouns --- code/__defines/mobs.dm | 1 + code/_helpers/text.dm | 16 ++++++++++++++-- code/modules/mob/examine.dm | 2 +- code/modules/mob/mob.dm | 8 ++------ code/modules/pronouns/_pronouns.dm | 14 +++++++++++--- code/modules/pronouns/pronouns_female.dm | 3 ++- code/modules/pronouns/pronouns_male.dm | 3 ++- code/modules/pronouns/pronouns_neuter.dm | 2 +- code/modules/pronouns/pronouns_pseudoplural.dm | 18 ++++++++++++++++++ code/modules/species/species.dm | 2 +- mods/species/adherent/datum/species.dm | 2 +- mods/species/drakes/species.dm | 2 +- .../random_species/random_species_species.dm | 3 ++- mods/species/skrell/datum/pronouns_skrell.dm | 2 +- mods/species/utility_frames/species.dm | 2 +- mods/species/vox/datum/species.dm | 2 +- nebula.dme | 1 + 17 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 code/modules/pronouns/pronouns_pseudoplural.dm diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 55874616e038..4b25f2443aa4 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -351,6 +351,7 @@ var/global/list/dexterity_levels = list( // Additional pronoun sets. #define NEUTER_ANIMATE "animate singular neutral" #define SECOND_PERSON_SINGULAR "second person singular" +#define PSEUDOPLURAL "pseudoplural" // Equipment Overlays Indices // #define HO_CONDITION_LAYER 1 diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index 1869be9fcdae..01f476481561 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -802,7 +802,9 @@ var/global/list/agreement_exception_verbs = list("can" = TRUE, "cannot" = TRUE, last_adverb = max(word_index, last_adverb) use_verb = words[last_adverb] // This overengineered nonsense ensures that we shouldn't end up with any doubled or missing spaces. - return jointext_no_nulls(list(jointext(words.Copy(1, last_adverb), " "), verb_agree_with_pronouns(use_verb, use_pronouns, is_after_pronoun = TRUE), jointext(words.Copy(last_adverb + 1, length(words) + 1), " ")), " ") + return jointext_no_nulls(list(jointext(words.Copy(1, last_adverb), " "), verb_agree_with_pronouns(use_verb, use_pronouns, is_after_pronoun = is_after_pronoun), jointext(words.Copy(last_adverb + 1, length(words) + 1), " ")), " ") + if(!is_after_pronoun && use_pronouns.pluralize_verb == /decl/pronouns::PLURALIZE_PSEUDO) + return use_verb // do not inflect pseudoplural unless after a pronoun if(global.copula_verbs[use_verb]) return use_pronouns.is else if(global.has_verbs[use_verb]) @@ -812,7 +814,17 @@ var/global/list/agreement_exception_verbs = list("can" = TRUE, "cannot" = TRUE, else if(global.agreement_exception_verbs[use_verb]) return use_verb // text_make_plural is intended for nouns, but should hopefully work for "fly" -> "flies" and similar. - return use_pronouns.pluralize_verb ? text_make_plural(use_verb) : use_verb + switch(use_pronouns.pluralize_verb) + if(/decl/pronouns::PLURALIZE_NONE) + return use_verb + if(/decl/pronouns::PLURALIZE_PSEUDO) + // technically only the plural side can get hit here + // but better safe than sorry + return is_after_pronoun ? text_make_plural(use_verb) : use_verb + if(/decl/pronouns::PLURALIZE_ALL) + return text_make_plural(use_verb) + else + CRASH("Invalid value [use_pronouns.pluralize_verb] for [use_pronouns]!") // Surely we have this defined somewhere already?? /proc/repeatstring(str, num) diff --git a/code/modules/mob/examine.dm b/code/modules/mob/examine.dm index 2c8252a68272..c2de3a0ce789 100644 --- a/code/modules/mob/examine.dm +++ b/code/modules/mob/examine.dm @@ -1,7 +1,7 @@ /mob/proc/get_visible_pronouns(hideflags) //suits/masks/helmets make it hard to tell their gender if((hideflags & HIDEJUMPSUIT) && (hideflags & HIDEFACE)) - return GET_DECL(/decl/pronouns) + return GET_DECL(/decl/pronouns/pseudoplural) return get_pronouns() // At some point this could have a client pref or server config option that switches second-person to first-person. diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 4b4438120ff3..96097c139ae8 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -124,10 +124,7 @@ bound_overlay.visible_message(message, self_message, blind_message) /mob/proc/get_action_string(is_self, var/using_verb, var/object_phrase, var/infix, var/postfix) - // use mob gender value because otherwise subject verb agreement gets messed up by disguised mobs - // todo: pseudoplural pronouns? we need "Unknown (as David Hasselhoff) spins around." and "You spin around." to both work - // without breaking "The bats spin around." - var/decl/pronouns/using_pronouns = is_self ? get_self_pronouns() : get_pronouns_by_gender(gender) + var/decl/pronouns/using_pronouns = is_self ? get_self_pronouns() : get_visible_pronouns() // A little kludgy/special-cased: we don't use the name for self messages. var/actor_string = is_self ? using_pronouns.He : "\The [src]" // this will hopefully handle is/does/has agreement properly @@ -146,8 +143,7 @@ /mob/proc/get_targeted_action_string(mob/target, is_self, var/using_verb, var/object_phrase, var/infix, var/postfix) . = get_action_string(is_self, using_verb, object_phrase, infix, postfix) var/target_is_self = target == src - // use mob gender value because otherwise subject verb agreement gets messed up by disguised mobs - var/decl/pronouns/target_pronouns = target_is_self ? target.get_self_pronouns() : get_pronouns_by_gender(target.gender) + var/decl/pronouns/target_pronouns = target_is_self ? target.get_self_pronouns() : target.get_visible_pronouns() // A little kludgy/special-cased: we don't use the name if it's self-targeted, regardless of who's viewing . = replacetext(., "$TARGET$", target_is_self ? target_pronouns.self : "\the [target]") . = replacetext(., "$TARGET_THEY$", target_is_self ? target_pronouns.he : "\the [target]") diff --git a/code/modules/pronouns/_pronouns.dm b/code/modules/pronouns/_pronouns.dm index 74181c938907..45e2efd77c7e 100644 --- a/code/modules/pronouns/_pronouns.dm +++ b/code/modules/pronouns/_pronouns.dm @@ -1,4 +1,5 @@ /decl/pronouns + uid = "pronouns_plural" var/name = PLURAL var/bureaucratic_term = "other" var/informal_term = "hoopy frood" @@ -14,9 +15,16 @@ var/is = "are" var/does = "do" var/self = "themselves" - var/pluralize_verb = FALSE var/s = "" var/es = "" + /// Never inflect verbs with this pronoun set, for both nouns ("The bats spin around.") and pronouns ("They spin around.") + var/const/PLURALIZE_NONE = 0 + /// Inflect only verbs after nouns ("Unknown (as David Hasselhoff) spins around.") but not after pronouns ("They spin around.") + var/const/PLURALIZE_PSEUDO = 1 + /// Inflect verbs after both nouns and pronouns, e.g. "David Hasselhoff spins around." and "He spins around." + var/const/PLURALIZE_ALL = 2 + /// When should we give verbs plural inflections? Valid values: PLURALIZE_NONE, PLURALIZE_PSEUDO, PLURALIZE_ALL. + var/pluralize_verb = PLURALIZE_NONE /decl/pronouns/Initialize() pronoun_string = "[He]/[him]/[his]" @@ -85,7 +93,7 @@ var/global/list/byond_genders = list(MALE, FEMALE, NEUTER, PLURAL) var/obj/item/suit = get_equipped_item(slot_wear_suit_str) var/obj/item/head = get_equipped_item(slot_head_str) if(suit && (suit.flags_inv & HIDEJUMPSUIT) && ((head && head.flags_inv & HIDEMASK) || get_equipped_item(slot_wear_mask_str))) - return GET_DECL(/decl/pronouns) + return GET_DECL(/decl/pronouns/pseudoplural) if(!pronouns) pronouns = get_pronouns_by_gender(get_gender()) - return pronouns || GET_DECL(/decl/pronouns) + return pronouns || GET_DECL(/decl/pronouns/pseudoplural) diff --git a/code/modules/pronouns/pronouns_female.dm b/code/modules/pronouns/pronouns_female.dm index fb287654b984..5127c3742b4d 100644 --- a/code/modules/pronouns/pronouns_female.dm +++ b/code/modules/pronouns/pronouns_female.dm @@ -1,4 +1,5 @@ /decl/pronouns/female + uid = "pronouns_female" name = FEMALE bureaucratic_term = "female" informal_term = "lady" @@ -15,4 +16,4 @@ self = "herself" s = "s" es = "es" - pluralize_verb = TRUE + pluralize_verb = PLURALIZE_ALL diff --git a/code/modules/pronouns/pronouns_male.dm b/code/modules/pronouns/pronouns_male.dm index 8e179967f549..ef1a15d413c2 100644 --- a/code/modules/pronouns/pronouns_male.dm +++ b/code/modules/pronouns/pronouns_male.dm @@ -1,5 +1,6 @@ /decl/pronouns/male name = MALE + uid = "pronouns_male" bureaucratic_term = "male" informal_term = "guy" honorific = "Mr." @@ -15,7 +16,7 @@ self = "himself" s = "s" es = "es" - pluralize_verb = TRUE + pluralize_verb = PLURALIZE_ALL // Thanks oldcoders. var/static/list/weird_euphemisms_for_your_balls = list( diff --git a/code/modules/pronouns/pronouns_neuter.dm b/code/modules/pronouns/pronouns_neuter.dm index 2f4eda4b5f39..58605ccaf525 100644 --- a/code/modules/pronouns/pronouns_neuter.dm +++ b/code/modules/pronouns/pronouns_neuter.dm @@ -11,7 +11,7 @@ self = "itself" s = "s" es = "es" - pluralize_verb = TRUE + pluralize_verb = PLURALIZE_ALL // Alternative to plural neuter. // With thanks to https://tib.cjcs.com/genderless-pronouns-ey-em-and-eir-2/ diff --git a/code/modules/pronouns/pronouns_pseudoplural.dm b/code/modules/pronouns/pronouns_pseudoplural.dm new file mode 100644 index 000000000000..94229cf28e36 --- /dev/null +++ b/code/modules/pronouns/pronouns_pseudoplural.dm @@ -0,0 +1,18 @@ +/decl/pronouns/pseudoplural + name = PSEUDOPLURAL + bureaucratic_term = "other" + informal_term = "hoopy frood" + honorific = "Mx." + + He = "They" + he = "they" + His = "Their" + his = "their" + him = "them" + has = "have" + is = "are" + does = "do" + self = "themself" + pluralize_verb = PLURALIZE_PSEUDO + s = "" + es = "" \ No newline at end of file diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index 097223be2514..762edd35c6d8 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -161,7 +161,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/decl/pronouns/default_pronouns var/list/available_pronouns = list( - /decl/pronouns, + /decl/pronouns/pseudoplural, /decl/pronouns/neuter/person, /decl/pronouns/female, /decl/pronouns/male diff --git a/mods/species/adherent/datum/species.dm b/mods/species/adherent/datum/species.dm index a63f58ceaee3..89913e40651c 100644 --- a/mods/species/adherent/datum/species.dm +++ b/mods/species/adherent/datum/species.dm @@ -25,7 +25,7 @@ blood_types = list(/decl/blood_type/coolant) - available_pronouns = list(/decl/pronouns) + available_pronouns = list(/decl/pronouns/pseudoplural) available_bodytypes = list( /decl/bodytype/crystalline/adherent, /decl/bodytype/crystalline/adherent/emerald, diff --git a/mods/species/drakes/species.dm b/mods/species/drakes/species.dm index 6b0398df1865..2fb5e78536b1 100644 --- a/mods/species/drakes/species.dm +++ b/mods/species/drakes/species.dm @@ -16,7 +16,7 @@ snow_slowdown_mod = -0.5 gluttonous = GLUT_TINY available_pronouns = list( - /decl/pronouns, + /decl/pronouns/pseudoplural, /decl/pronouns/neuter, /decl/pronouns/male, /decl/pronouns/female diff --git a/mods/species/random_species/random_species_species.dm b/mods/species/random_species/random_species_species.dm index 3db442f8b108..841303920e65 100644 --- a/mods/species/random_species/random_species_species.dm +++ b/mods/species/random_species/random_species_species.dm @@ -67,7 +67,8 @@ //Misc traits if(prob(40)) - available_pronouns = list(/decl/pronouns) + // 50% chance of pseudoplural or actually plural + available_pronouns = list(pick(/decl/pronouns/pseudoplural, /decl/pronouns)) if(prob(10)) species_flags |= SPECIES_FLAG_NO_SLIP if(prob(10)) diff --git a/mods/species/skrell/datum/pronouns_skrell.dm b/mods/species/skrell/datum/pronouns_skrell.dm index 1fd612aa74b2..cd0f2617002a 100644 --- a/mods/species/skrell/datum/pronouns_skrell.dm +++ b/mods/species/skrell/datum/pronouns_skrell.dm @@ -14,4 +14,4 @@ self = "vilself" s = "s" es = "es" - pluralize_verb = TRUE + pluralize_verb = PLURALIZE_ALL diff --git a/mods/species/utility_frames/species.dm b/mods/species/utility_frames/species.dm index 7201f13c3cb5..367ddeccad7f 100644 --- a/mods/species/utility_frames/species.dm +++ b/mods/species/utility_frames/species.dm @@ -32,7 +32,7 @@ preview_outfit = null available_pronouns = list( - /decl/pronouns, + /decl/pronouns/pseudoplural, /decl/pronouns/neuter ) available_background_info = list( diff --git a/mods/species/vox/datum/species.dm b/mods/species/vox/datum/species.dm index ec9468a13420..3570737342f6 100644 --- a/mods/species/vox/datum/species.dm +++ b/mods/species/vox/datum/species.dm @@ -79,7 +79,7 @@ available_pronouns = list( /decl/pronouns/neuter, /decl/pronouns/neuter/person, - /decl/pronouns, + /decl/pronouns/pseudoplural, /decl/pronouns/male, /decl/pronouns/female ) diff --git a/nebula.dme b/nebula.dme index 5f72f4628ca0..5cffd8b32162 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3551,6 +3551,7 @@ #include "code\modules\pronouns\pronouns_female.dm" #include "code\modules\pronouns\pronouns_male.dm" #include "code\modules\pronouns\pronouns_neuter.dm" +#include "code\modules\pronouns\pronouns_pseudoplural.dm" #include "code\modules\pronouns\pronouns_second_person.dm" #include "code\modules\radiation\radiation.dm" #include "code\modules\random_map\_random_map_setup.dm" From 67c40410cdbbb3754705fca21ecc919e8592553b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 13:11:25 -0500 Subject: [PATCH 0929/1331] Adjust terms of address to distinguish plural/pseudoplural pronouns --- code/modules/pronouns/_pronouns.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/pronouns/_pronouns.dm b/code/modules/pronouns/_pronouns.dm index 45e2efd77c7e..92e2c7fe9734 100644 --- a/code/modules/pronouns/_pronouns.dm +++ b/code/modules/pronouns/_pronouns.dm @@ -1,9 +1,9 @@ /decl/pronouns uid = "pronouns_plural" var/name = PLURAL - var/bureaucratic_term = "other" - var/informal_term = "hoopy frood" - var/honorific = "Mx." + var/bureaucratic_term = "others" + var/informal_term = "hoopy froods" + var/honorific = "Mxes." var/pronoun_string var/He = "They" From e648e5d9d041dc5e0b15d7c3a91129266b0cd6d2 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 13:04:03 -0500 Subject: [PATCH 0930/1331] Fix some improper gender handling --- code/datums/underwear/underwear.dm | 2 +- code/game/objects/structures/skele_stand.dm | 2 +- code/game/turfs/floors/floor_layers.dm | 3 ++- code/modules/mob/living/human/human_presets.dm | 2 +- code/modules/mob/living/simple_animal/friendly/cat.dm | 2 +- code/modules/mob/living/simple_animal/friendly/corgi.dm | 2 +- code/modules/organs/ailments/_ailment.dm | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/code/datums/underwear/underwear.dm b/code/datums/underwear/underwear.dm index ff0813a3e9f5..d1d2c0a3ca75 100644 --- a/code/datums/underwear/underwear.dm +++ b/code/datums/underwear/underwear.dm @@ -67,7 +67,7 @@ var/global/datum/category_collection/underwear/underwear = new() var/obj/item/underwear/UW = new underwear_type() UW.SetName(underwear_name) - UW.gender = underwear_gender + UW.set_gender(underwear_gender) UW.icon = icon UW.icon_state = icon_state diff --git a/code/game/objects/structures/skele_stand.dm b/code/game/objects/structures/skele_stand.dm index 4b3d9d944d18..27706743eda6 100644 --- a/code/game/objects/structures/skele_stand.dm +++ b/code/game/objects/structures/skele_stand.dm @@ -9,7 +9,7 @@ /obj/structure/skele_stand/Initialize() . = ..() - gender = pick(MALE, FEMALE, PLURAL) + set_gender(pick(MALE, FEMALE, PLURAL)) /obj/structure/skele_stand/proc/rattle_bones(mob/user, atom/thingy) if((world.time - cooldown) <= 1 SECOND) diff --git a/code/game/turfs/floors/floor_layers.dm b/code/game/turfs/floors/floor_layers.dm index 18cfa6f04fe9..8fb98f91fa6a 100644 --- a/code/game/turfs/floors/floor_layers.dm +++ b/code/game/turfs/floors/floor_layers.dm @@ -207,7 +207,8 @@ update_floor_strings() - gender = copy_from.gender + set_gender(copy_from.gender) + layer = copy_from.floor_layer turf_flags = copy_from.turf_flags z_flags = copy_from.z_flags diff --git a/code/modules/mob/living/human/human_presets.dm b/code/modules/mob/living/human/human_presets.dm index 29ec5b8d1411..802158c14986 100644 --- a/code/modules/mob/living/human/human_presets.dm +++ b/code/modules/mob/living/human/human_presets.dm @@ -73,6 +73,6 @@ /mob/living/human/monkey/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) if(gender == PLURAL) - gender = pick(MALE, FEMALE) + set_gender(pick(MALE, FEMALE)) species_uid = /decl/species/monkey::uid . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 985e71554779..99e38a6baa01 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -223,7 +223,7 @@ /mob/living/simple_animal/passive/cat/kitten/Initialize() . = ..() - gender = pick(MALE, FEMALE) + set_gender(pick(MALE, FEMALE)) /mob/living/simple_animal/passive/cat/fluff/ran name = "Runtime" diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm index 9264cd3be0f4..a125c209d960 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm @@ -163,7 +163,7 @@ /mob/living/simple_animal/corgi/puppy/Initialize() . = ..() - gender = pick(MALE, FEMALE) + set_gender(pick(MALE, FEMALE)) //pupplies cannot wear anything. /mob/living/simple_animal/corgi/puppy/OnTopic(mob/user, href_list) diff --git a/code/modules/organs/ailments/_ailment.dm b/code/modules/organs/ailments/_ailment.dm index 713dfcdc7aab..fc3853f0b4a8 100644 --- a/code/modules/organs/ailments/_ailment.dm +++ b/code/modules/organs/ailments/_ailment.dm @@ -94,7 +94,7 @@ . = replacetext(., "$TARGET$", "\the [target]") if(organ) . = replacetext(., "$ORGAN$", organ.name) - var/decl/pronouns/organ_pronouns = get_pronouns_by_gender(organ.gender) + var/decl/pronouns/organ_pronouns = organ.get_pronouns() . = replacetext(., "$ORGAN_DOES$", organ_pronouns.does) . = replacetext(., "$ORGAN_IS$", organ_pronouns.is) . = capitalize(trim(.)) From bd37423137856d6c189f52f3ac493fb72a68ce0c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 14:18:54 -0500 Subject: [PATCH 0931/1331] Replace bespoke gender grammar handling with helpers --- code/_helpers/lists.dm | 8 ++++++-- code/game/objects/items/_item_drying.dm | 3 ++- code/game/objects/structures/skele_stand.dm | 3 ++- code/modules/clothing/_clothing.dm | 7 ++++--- code/modules/clothing/glasses/_glasses.dm | 6 ++++-- code/modules/clothing/spacesuits/rig/rig.dm | 10 ++++++---- code/modules/mechs/components/frame.dm | 7 ++++--- code/modules/mechs/mech.dm | 3 ++- code/modules/tools/archetypes/tool_item.dm | 3 ++- mods/species/adherent/organs/organs_internal.dm | 6 ++++-- 10 files changed, 36 insertions(+), 20 deletions(-) diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm index b9d16ba65ab1..9635c472e181 100644 --- a/code/_helpers/lists.dm +++ b/code/_helpers/lists.dm @@ -27,13 +27,17 @@ input -= thing var/thing_string = isatom(thing) ? thing.name : "\proper [thing]" thing_count[thing_string] += 1 - thing_gender[thing_string] = isatom(thing) ? thing.gender : NEUTER + if(ismob(thing)) + var/mob/mob_thing = thing + thing_gender[thing_string] = mob_thing.get_gender() + else + thing_gender[thing_string] = isatom(thing) ? thing.gender : NEUTER input = list() for(var/thing_string in thing_count) if(thing_count[thing_string] == 1) input += "\the [thing_string]" else - input += "[thing_count[thing_string]] [thing_string][thing_gender[thing_string] == PLURAL ? "" : "s"]" + input += "[thing_count[thing_string]] [thing_gender[thing_string] == PLURAL ? text_make_plural(thing_string) : thing_string]" switch(length(input)) if(1) diff --git a/code/game/objects/items/_item_drying.dm b/code/game/objects/items/_item_drying.dm index 5357133fe505..5e69b30dd96f 100644 --- a/code/game/objects/items/_item_drying.dm +++ b/code/game/objects/items/_item_drying.dm @@ -38,7 +38,8 @@ if(color && dried_product_takes_color) thing.color = color if(isturf(loc) && !silent) - visible_message(SPAN_NOTICE("\The [src] [gender == PLURAL ? "are" : "is"] dry!")) + var/decl/pronouns/pronouns = get_pronouns() + visible_message(SPAN_NOTICE("\The [src] [pronouns.is] dry!")) if(thing != src) qdel(src) return thing diff --git a/code/game/objects/structures/skele_stand.dm b/code/game/objects/structures/skele_stand.dm index 27706743eda6..58ac90d99e01 100644 --- a/code/game/objects/structures/skele_stand.dm +++ b/code/game/objects/structures/skele_stand.dm @@ -54,7 +54,8 @@ for(var/slot in swag) var/obj/item/clothing/C = swag[slot] swagnames += C.get_examine_line() - . += "[gender == MALE ? "He" : "She"] is wearing [english_list(swagnames)]." + var/decl/pronouns/stand_pronouns = get_pronouns() + . += "[stand_pronouns.He] [stand_pronouns.is] wearing [english_list(swagnames)]." /obj/structure/skele_stand/attackby(obj/item/used_item, mob/user) if(IS_PEN(used_item)) diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index 354290fd7b2a..f6980156009e 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -102,8 +102,8 @@ to_chat(user, SPAN_WARNING("You should remove the accessories attached to \the [src] first.")) return TRUE if(!isturf(loc) && !(src in user.get_held_items())) - var/it = gender == PLURAL ? "them" : "it" - to_chat(user, SPAN_WARNING("You must either be holding \the [src], or [it] must be on the ground, before you can shred [it].")) + var/decl/pronouns/pronouns = get_pronouns() + to_chat(user, SPAN_WARNING("You must either be holding \the [src], or [pronouns.he] must be on the ground, before you can shred [pronouns.him].")) return TRUE playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1) user.visible_message(SPAN_DANGER("\The [user] begins ripping apart \the [src] with \the [used_item].")) @@ -268,7 +268,8 @@ else . = (bodytype_equip_flags & root_bodytype.bodytype_flag) if(!. && !disable_warning) - to_chat(user, SPAN_WARNING("\The [src] [gender == PLURAL ? "do" : "does"] not fit you.")) + var/decl/pronouns/pronouns = get_pronouns() + to_chat(user, SPAN_WARNING("\The [src] [pronouns.does] not fit you.")) /obj/item/clothing/equipped(var/mob/user) update_icon() diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 5802d8fe8b91..3e9aad057df7 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -51,12 +51,14 @@ if(electric) if(ishuman(src.loc)) var/mob/living/human/M = src.loc + var/decl/pronouns/pronouns = get_pronouns() + var/malfunction = verb_agree_with_pronouns("malfunction", pronouns, is_after_pronoun = FALSE) if(M.get_equipped_item(slot_glasses_str) != src) - to_chat(M, SPAN_DANGER("\The [src] malfunction[gender != PLURAL ? "s":""], releasing a small spark.")) + to_chat(M, SPAN_DANGER("\The [src] [malfunction], releasing a small spark.")) else SET_STATUS_MAX(M, STAT_BLIND, 2) SET_STATUS_MAX(M, STAT_BLURRY, 4) - to_chat(M, SPAN_DANGER("Your [name] malfunction[gender != PLURAL ? "s":""], blinding you!")) + to_chat(M, SPAN_DANGER("Your [name] [malfunction], blinding you!")) // Don't cure being nearsighted if(!M.has_genetic_condition(GENE_COND_NEARSIGHTED)) diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index a132830f78d1..3e715d6b89a4 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -119,7 +119,8 @@ for(var/obj/item/piece in list(helmet,gloves,chest,boots)) if(!piece || piece.loc != wearer) continue - . += "[html_icon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed." + var/decl/pronouns/piece_pronouns = piece.get_pronouns() + . += "[html_icon(piece)] \The [piece] [piece_pronouns.is] deployed." if(src.loc == user) . += "The access panel is [locked? "locked" : "unlocked"]." . += "The maintenance panel is [open ? "open" : "closed"]." @@ -731,7 +732,7 @@ holder = use_obj.loc if(istype(holder)) if(use_obj && check_slot == use_obj) - to_chat(wearer, SPAN_HARDSUIT("Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.")) + to_chat(wearer, SPAN_HARDSUIT("Your [use_obj.name] [verb_agree_with_pronouns("retract", use_obj.get_pronouns(), is_after_pronoun = FALSE)] swiftly.")) use_obj.canremove = 1 holder.drop_from_inventory(use_obj, src) use_obj.canremove = 0 @@ -743,10 +744,11 @@ if(!wearer.equip_to_slot_if_possible(use_obj, equip_to, 0, 1)) use_obj.forceMove(src) if(check_slot) - to_chat(initiator, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.") + var/decl/pronouns/check_slot_pronouns = check_slot.get_pronouns() + to_chat(initiator, SPAN_DANGER("You are unable to deploy \the [piece] as \the [check_slot] [check_slot_pronouns.is] in the way.")) return else - to_chat(wearer, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.") + to_chat(wearer, SPAN_HARDSUIT("Your [use_obj.name] [verb_agree_with_pronouns("deploy", use_obj.get_pronouns(), is_after_pronoun = FALSE)] swiftly.")) use_obj.icon_state = initial(use_obj.icon_state) if(piece == "helmet" && helmet) diff --git a/code/modules/mechs/components/frame.dm b/code/modules/mechs/components/frame.dm index 6ad92e633602..867a6b831ec7 100644 --- a/code/modules/mechs/components/frame.dm +++ b/code/modules/mechs/components/frame.dm @@ -295,9 +295,10 @@ return TRUE /obj/structure/heavy_vehicle_frame/proc/install_component(var/obj/item/thing, var/mob/user) - var/obj/item/mech_component/MC = thing - if(istype(MC) && !MC.ready_to_install()) - to_chat(user, SPAN_WARNING("\The [MC] [MC.gender == PLURAL ? "are" : "is"] not ready to install.")) + var/obj/item/mech_component/component = thing + if(istype(component) && !component.ready_to_install()) + var/decl/pronouns/component_pronouns = component.get_pronouns() + to_chat(user, SPAN_WARNING("\The [component] [component_pronouns.is] not ready to install.")) return 0 if(user) visible_message(SPAN_NOTICE("\The [user] begins installing \the [thing] into \the [src].")) diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/mech.dm index 2530ea6bd46d..25f9ecbf7873 100644 --- a/code/modules/mechs/mech.dm +++ b/code/modules/mechs/mech.dm @@ -192,7 +192,8 @@ for(var/obj/item/mech_component/thing in list(arms, legs, head, body)) if(!thing) continue - . += "Its [thing.name] [thing.gender == PLURAL ? "are" : "is"] [thing.get_damage_string()]." + var/decl/pronouns/component_pronouns = thing.get_pronouns() + . += "Its [thing.name] [component_pronouns.is] [thing.get_damage_string()]." . += "It menaces with reinforcements of [material]." /mob/living/exosuit/return_air() diff --git a/code/modules/tools/archetypes/tool_item.dm b/code/modules/tools/archetypes/tool_item.dm index 31496c04010a..51f0d6ed62a2 100644 --- a/code/modules/tools/archetypes/tool_item.dm +++ b/code/modules/tools/archetypes/tool_item.dm @@ -89,4 +89,5 @@ LAZYADD(tool_strings, tool_string) if(length(tool_strings)) - . += "[gender == PLURAL ? "They look" : "It looks"] like [english_list(tool_strings)]." + var/decl/pronouns/tool_pronouns = get_pronouns() + . += "[tool_pronouns.He] [verb_agree_with_pronouns("look", tool_pronouns)] like [english_list(tool_strings)]." diff --git a/mods/species/adherent/organs/organs_internal.dm b/mods/species/adherent/organs/organs_internal.dm index fc4c73332911..8719fee20b9b 100644 --- a/mods/species/adherent/organs/organs_internal.dm +++ b/mods/species/adherent/organs/organs_internal.dm @@ -61,7 +61,8 @@ var/obj/item/organ/internal/cell/cell = owner.get_organ(BP_CELL, /obj/item/organ/internal/cell) if(active && !(cell && cell.use(maintenance_cost))) active = FALSE - to_chat(owner, SPAN_DANGER("Your [name] [gender == PLURAL ? "are" : "is"] out of power!")) + var/decl/pronouns/pronouns = get_pronouns() + to_chat(owner, SPAN_DANGER("Your [name] [pronouns.is] out of power!")) refresh_action_button() /obj/item/organ/internal/powered/refresh_action_button() @@ -75,7 +76,8 @@ if(.) sound_to(user, sound('mods/species/adherent/sound/ding.ogg')) if(is_broken()) - to_chat(owner, "\The [src] [gender == PLURAL ? "are" : "is"] too damaged to function.") + var/decl/pronouns/pronouns = get_pronouns() + to_chat(owner, SPAN_WARNING("\The [src] [pronouns.is] too damaged to function.")) active = FALSE else active = !active From de102bfd4995c26eb76042259689bcb7bd74b969 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 13:10:59 -0500 Subject: [PATCH 0932/1331] Add UIDs to pronouns --- code/modules/pronouns/_pronouns.dm | 1 + code/modules/pronouns/pronouns_neuter.dm | 2 ++ code/modules/pronouns/pronouns_pseudoplural.dm | 1 + code/modules/pronouns/pronouns_second_person.dm | 1 + mods/species/skrell/datum/pronouns_skrell.dm | 1 + 5 files changed, 6 insertions(+) diff --git a/code/modules/pronouns/_pronouns.dm b/code/modules/pronouns/_pronouns.dm index 92e2c7fe9734..174e9edb8c03 100644 --- a/code/modules/pronouns/_pronouns.dm +++ b/code/modules/pronouns/_pronouns.dm @@ -1,5 +1,6 @@ /decl/pronouns uid = "pronouns_plural" + decl_flags = DECL_FLAG_MANDATORY_UID var/name = PLURAL var/bureaucratic_term = "others" var/informal_term = "hoopy froods" diff --git a/code/modules/pronouns/pronouns_neuter.dm b/code/modules/pronouns/pronouns_neuter.dm index 58605ccaf525..a9f32b216e06 100644 --- a/code/modules/pronouns/pronouns_neuter.dm +++ b/code/modules/pronouns/pronouns_neuter.dm @@ -1,4 +1,5 @@ /decl/pronouns/neuter + uid = "pronouns_neuter" name = NEUTER He = "It" he = "it" @@ -16,6 +17,7 @@ // Alternative to plural neuter. // With thanks to https://tib.cjcs.com/genderless-pronouns-ey-em-and-eir-2/ /decl/pronouns/neuter/person + uid = "pronouns_neuter_animate" name = NEUTER_ANIMATE He = "Ey" he = "ey" diff --git a/code/modules/pronouns/pronouns_pseudoplural.dm b/code/modules/pronouns/pronouns_pseudoplural.dm index 94229cf28e36..f7cbb29e4625 100644 --- a/code/modules/pronouns/pronouns_pseudoplural.dm +++ b/code/modules/pronouns/pronouns_pseudoplural.dm @@ -1,4 +1,5 @@ /decl/pronouns/pseudoplural + uid = "pronouns_pseudoplural" name = PSEUDOPLURAL bureaucratic_term = "other" informal_term = "hoopy frood" diff --git a/code/modules/pronouns/pronouns_second_person.dm b/code/modules/pronouns/pronouns_second_person.dm index b3760b4a0d14..239f4e99d7f0 100644 --- a/code/modules/pronouns/pronouns_second_person.dm +++ b/code/modules/pronouns/pronouns_second_person.dm @@ -1,4 +1,5 @@ /decl/pronouns/second_person_singular + uid = "pronouns_second_person_singular" name = SECOND_PERSON_SINGULAR He = "You" he = "you" diff --git a/mods/species/skrell/datum/pronouns_skrell.dm b/mods/species/skrell/datum/pronouns_skrell.dm index cd0f2617002a..19b3ffc04f55 100644 --- a/mods/species/skrell/datum/pronouns_skrell.dm +++ b/mods/species/skrell/datum/pronouns_skrell.dm @@ -1,4 +1,5 @@ /decl/pronouns/skrell + uid = "pronouns_skrell" name = "Skrell" bureaucratic_term = "Skrell" informal_term = "Skrell" From aed543a3ccdfb8dab5404b29b5cde44774a70e32 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 13:36:57 -0500 Subject: [PATCH 0933/1331] Expand action string token replacement --- code/modules/mob/mob.dm | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 96097c139ae8..dadb2de1b99d 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -130,30 +130,33 @@ // this will hopefully handle is/does/has agreement properly . = "[actor_string] [verb_agree_with_pronouns(using_verb, using_pronouns, is_after_pronoun = is_self)] [infix ? infix + " " : null][object_phrase][postfix ? " " + postfix : null]" // uh oh, time to handle tokens. + . = replacetext(., "$USER$", "\the [src]") + . = replacetext(., "$USER'S$", "\the [src]'s") . = replacetext(., "$USER_THEY$", using_pronouns.he) . = replacetext(., "$USER_THEM$", using_pronouns.him) . = replacetext(., "$USER_THEIR$", using_pronouns.his) . = replacetext(., "$USER_SELF$", using_pronouns.self) . = replacetext(., "$USER_DOES$", using_pronouns.does) - . = replacetext(., "$USER_HAS$", using_pronouns.has) - . = replacetext(., "$USER_IS$", using_pronouns.is) - . = replacetext(., "$USER_S$", using_pronouns.s) - . = replacetext(., "$USER_ES$", using_pronouns.es) + . = replacetext(., "$USER_HAS$", using_pronouns.has) + . = replacetext(., "$USER_IS$", using_pronouns.is) + . = replacetext(., "$USER_S$", using_pronouns.s) + . = replacetext(., "$USER_ES$", using_pronouns.es) /mob/proc/get_targeted_action_string(mob/target, is_self, var/using_verb, var/object_phrase, var/infix, var/postfix) . = get_action_string(is_self, using_verb, object_phrase, infix, postfix) var/target_is_self = target == src var/decl/pronouns/target_pronouns = target_is_self ? target.get_self_pronouns() : target.get_visible_pronouns() // A little kludgy/special-cased: we don't use the name if it's self-targeted, regardless of who's viewing - . = replacetext(., "$TARGET$", target_is_self ? target_pronouns.self : "\the [target]") - . = replacetext(., "$TARGET_THEY$", target_is_self ? target_pronouns.he : "\the [target]") - . = replacetext(., "$TARGET_THEM$", target_is_self ? target_pronouns.self : "\the [target]") // reflexive, so use self instead of them - . = replacetext(., "$TARGET_THEIR$", target_is_self ? target_pronouns.his : "\the [target]'s") - . = replacetext(., "$TARGET_DOES$", target_pronouns.does) - . = replacetext(., "$TARGET_HAS$", target_pronouns.has) - . = replacetext(., "$TARGET_IS$", target_pronouns.is) - . = replacetext(., "$TARGET_S$", target_pronouns.s) - . = replacetext(., "$TARGET_ES$", target_pronouns.es) + . = replacetext(., "$TARGET$", target_is_self ? target_pronouns.self : "\the [target]") + . = replacetext(., "$TARGET'S$", target_is_self ? target_pronouns.his : "\the [target]'s") + . = replacetext(., "$TARGET_THEM$", target_is_self ? target_pronouns.self : target_pronouns.him) // reflexive if self, so use self instead of them + . = replacetext(., "$TARGET_THEIR$", target_pronouns.his) + . = replacetext(., "$TARGET_THEY$", target_pronouns.he) + . = replacetext(., "$TARGET_DOES$", target_pronouns.does) + . = replacetext(., "$TARGET_HAS$", target_pronouns.has) + . = replacetext(., "$TARGET_IS$", target_pronouns.is) + . = replacetext(., "$TARGET_S$", target_pronouns.s) + . = replacetext(., "$TARGET_ES$", target_pronouns.es) // Determines span styling used for visible_action_message. /// Uses SPAN_NOTICE for both self and other messages. From 8cb04e9c2cab6e6b36846b31258cdb42991cedfb Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 13:56:55 -0500 Subject: [PATCH 0934/1331] Fix incorrect capitalization in emotes --- code/_helpers/text.dm | 11 +++++++++++ .../extensions/abilities/ability_decl.dm | 18 +++++++++--------- .../extensions/abilities/ability_handler.dm | 4 ++-- code/game/machinery/cryopod.dm | 4 ++-- code/modules/emotes/emote_define.dm | 6 +++--- code/modules/mob/hugs.dm | 4 ++-- code/modules/mob/language/language.dm | 4 ++-- code/modules/mob_modifiers/_modifiers.dm | 2 +- 8 files changed, 32 insertions(+), 21 deletions(-) diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index 01f476481561..cef6a0ba6e65 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -366,11 +366,22 @@ var/global/regex/starts_lowercase_regex = regex(@"^[a-z]") return trim_left(trim_right(text)) //Returns a string with the first element of the string capitalized. +// NOTE: This will not work if there are any HTML tags. /proc/capitalize(text) if(text) text = uppertext(text[1]) + copytext(text, 1 + length(text[1])) return text +// Returns a string with the first alphabetical element of the string capitalized, skipping HTML tags. +/proc/capitalize_proper_html(text) + var/static/regex/split_html_regex = regex(@"(^<[^>]*>)([A-Za-z])(.+)$") + if(!text) + return text + if(!split_html_regex.Find(text)) + return capitalize(text) + split_html_regex.group[2] = uppertext(split_html_regex.group[2]) + return JOINTEXT(split_html_regex.group) + //Returns a string with the first element of the every word of the string capitalized. /proc/capitalize_words(text) var/list/S = splittext(text, " ") diff --git a/code/datums/extensions/abilities/ability_decl.dm b/code/datums/extensions/abilities/ability_decl.dm index 9f271846750d..a3cb3726f89f 100644 --- a/code/datums/extensions/abilities/ability_decl.dm +++ b/code/datums/extensions/abilities/ability_decl.dm @@ -173,7 +173,7 @@ if(!prepare_to_cast(user, target, metadata, handler)) if(fail_cast_1p_str) - to_chat(user, SPAN_WARNING(capitalize(emote_replace_user_tokens(fail_cast_1p_str, user)))) + to_chat(user, SPAN_WARNING(capitalize_proper_html(emote_replace_user_tokens(fail_cast_1p_str, user)))) return if(projectile_type) @@ -209,25 +209,25 @@ /decl/ability/proc/show_cast_channel_msg(mob/user, atom/target, list/metadata) if(prepare_message_3p_str && prepare_message_1p_str) user.visible_message( - SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(prepare_message_3p_str, user), target))), - SPAN_NOTICE(capitalize(emote_replace_target_tokens(prepare_message_1p_str, target))) + SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(emote_replace_user_tokens(prepare_message_3p_str, user), target))), + SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(prepare_message_1p_str, target))) ) else if(prepare_message_1p_str) - user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(prepare_message_1p_str, target)))) + user.visible_message(SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(prepare_message_1p_str, target)))) else if(prepare_message_3p_str) - user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(prepare_message_3p_str, user), target)))) + user.visible_message(SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(emote_replace_user_tokens(prepare_message_3p_str, user), target)))) /decl/ability/proc/show_ability_cast_msg(mob/user, list/targets, list/metadata) var/atom/target = targets[1] if(cast_message_3p_str && cast_message_1p_str) user.visible_message( - SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(cast_message_3p_str, user), target))), - SPAN_NOTICE(capitalize(emote_replace_target_tokens(cast_message_1p_str, target))) + SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(emote_replace_user_tokens(cast_message_3p_str, user), target))), + SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(cast_message_1p_str, target))) ) else if(cast_message_1p_str) - user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(cast_message_1p_str, target)))) + user.visible_message(SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(cast_message_1p_str, target)))) else if(cast_message_3p_str) - user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(cast_message_3p_str, user), target)))) + user.visible_message(SPAN_NOTICE(capitalize_proper_html(emote_replace_target_tokens(emote_replace_user_tokens(cast_message_3p_str, user), target)))) /decl/ability/proc/prepare_to_cast(mob/user, atom/target, list/metadata, datum/ability_handler/handler) var/use_cooldown_time = get_cooldown_time(metadata) diff --git a/code/datums/extensions/abilities/ability_handler.dm b/code/datums/extensions/abilities/ability_handler.dm index 5ac742b00384..0b773cb469d5 100644 --- a/code/datums/extensions/abilities/ability_handler.dm +++ b/code/datums/extensions/abilities/ability_handler.dm @@ -226,7 +226,7 @@ if(!prepared_ability) return FALSE if(prepared_ability.cancel_ability_1p_str) - to_chat(owner, capitalize(emote_replace_user_tokens(prepared_ability.cancel_ability_1p_str), owner)) + to_chat(owner, capitalize_proper_html(emote_replace_user_tokens(prepared_ability.cancel_ability_1p_str), owner)) var/obj/screen/ability/button/button = LAZYACCESS(screen_elements, prepared_ability) prepared_ability = null if(istype(button)) @@ -238,7 +238,7 @@ return FALSE prepared_ability = ability if(ability.ready_ability_1p_str) - to_chat(owner, capitalize(emote_replace_user_tokens(ability.ready_ability_1p_str), owner)) + to_chat(owner, capitalize_proper_html(emote_replace_user_tokens(ability.ready_ability_1p_str), owner)) var/obj/screen/ability/button/button = LAZYACCESS(screen_elements, ability) if(istype(button)) button.update_icon() diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 7a0bf2fccd41..fd685f5d9a8f 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -386,7 +386,7 @@ control_computer._admin_logs += "[key_name(occupant)] ([role_alt_title]) at [stationtime2text()]" log_and_message_admins("[key_name(occupant)] ([role_alt_title]) entered cryostorage.") - visible_message(SPAN_NOTICE(emote_replace_user_tokens(emote_replace_target_tokens(on_store_visible_message, src), occupant))) + visible_message(SPAN_NOTICE(capitalize_proper_html(emote_replace_user_tokens(emote_replace_target_tokens(on_store_visible_message, src), occupant)))) do_telecomms_announcement(src, "[occupant.real_name], [role_alt_title], [on_store_message]", "[on_store_name]") despawn_character(occupant) set_occupant(null) @@ -398,7 +398,7 @@ return if(!user.incapacitated() && !user.anchored && user.Adjacent(src) && user.Adjacent(target)) if(target == user) - visible_message(SPAN_NOTICE(emote_replace_user_tokens(emote_replace_target_tokens(on_enter_visible_message, src), usr)), range = 3) + visible_message(SPAN_NOTICE(capitalize_proper_html(emote_replace_user_tokens(emote_replace_target_tokens(on_enter_visible_message, src), usr))), range = 3) else visible_message("[user] starts putting [target] into \the [src].", range = 3) if(!do_after(user, 20, src)|| QDELETED(target)) diff --git a/code/modules/emotes/emote_define.dm b/code/modules/emotes/emote_define.dm index b76c74f5c98a..3cf18471cd43 100644 --- a/code/modules/emotes/emote_define.dm +++ b/code/modules/emotes/emote_define.dm @@ -7,7 +7,7 @@ . = replacetext(., "$TARGET_THEM$", target_gender.him) . = replacetext(., "$TARGET_THEIR$", target_gender.his) . = replacetext(., "$TARGET_SELF$", target_gender.self) - . = replacetext(., "$TARGET$", "[target]") + . = replacetext(., "$TARGET$", "\the [target]") /proc/emote_replace_user_tokens(var/msg, var/atom/user) . = msg @@ -18,7 +18,7 @@ . = replacetext(., "$USER_THEM$", user_gender.him) . = replacetext(., "$USER_THEIR$", user_gender.his) . = replacetext(., "$USER_SELF$", user_gender.self) - . = replacetext(., "$USER$", "[user]") + . = replacetext(., "$USER$", "\the [user]") // Note about emote messages: // - $USER$ / $TARGET$ will be replaced with the relevant name, in bold. @@ -242,7 +242,7 @@ var/global/list/_emotes_by_key if(use_1p) if(target) use_1p = emote_replace_target_tokens(use_1p, target) - use_1p = "[capitalize(emote_replace_user_tokens(use_1p, user))]" + use_1p = "[capitalize_proper_html(emote_replace_user_tokens(use_1p, user))]" var/use_3p = get_emote_message_3p(user, target, extra_params) if(use_3p) diff --git a/code/modules/mob/hugs.dm b/code/modules/mob/hugs.dm index afa0d6264737..f38eec055bc8 100644 --- a/code/modules/mob/hugs.dm +++ b/code/modules/mob/hugs.dm @@ -40,8 +40,8 @@ var/global/list/_default_hug_messages = list( hug_3p = emote_replace_target_tokens(hug_3p, target) hug_1p = emote_replace_target_tokens(hug_1p, target) visible_message( - SPAN_NOTICE(capitalize(hug_3p)), - SPAN_NOTICE(capitalize(hug_1p)) + SPAN_NOTICE(capitalize_proper_html(hug_3p)), + SPAN_NOTICE(capitalize_proper_html(hug_1p)) ) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index 6b2f4b688bd1..25198d4fc8ec 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -100,7 +100,7 @@ if(!prob(understand_chance)) nword = scramble_word(w) if(new_sentence) - nword = capitalize(nword) + nword = capitalize_proper_html(nword) new_sentence = FALSE if(ends_sentence) nword = trim(nword) @@ -112,7 +112,7 @@ scrambled_text += nword . = jointext(scrambled_text, null) - . = capitalize(.) + . = capitalize_proper_html(.) . = trim(.) /decl/language/proc/get_next_scramble_token() diff --git a/code/modules/mob_modifiers/_modifiers.dm b/code/modules/mob_modifiers/_modifiers.dm index 677608454afb..cd019876af4e 100644 --- a/code/modules/mob_modifiers/_modifiers.dm +++ b/code/modules/mob_modifiers/_modifiers.dm @@ -56,7 +56,7 @@ . += "mob_overlay_state set but mob_overlay_icon not set" /decl/mob_modifier/proc/replace_tokens(message, mob/user) - return capitalize(emote_replace_user_tokens(message, user)) + return capitalize_proper_html(emote_replace_user_tokens(message, user)) /decl/mob_modifier/proc/on_modifier_datum_added(mob/living/_owner, datum/mob_modifier/modifier) if(on_add_message_3p) From 876094dc0c313519ae8c04e216d9c57b4256839b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 13:58:47 -0500 Subject: [PATCH 0935/1331] Remove redundant second-person examine strings for slots --- code/datums/inventory_slots/_inventory_slot.dm | 2 -- code/datums/inventory_slots/inventory_gripper.dm | 2 -- code/datums/inventory_slots/slots/slot_back.dm | 2 -- code/datums/inventory_slots/slots/slot_belt.dm | 2 -- code/datums/inventory_slots/slots/slot_cuffs.dm | 2 -- code/datums/inventory_slots/slots/slot_ears.dm | 2 -- code/datums/inventory_slots/slots/slot_glasses.dm | 2 -- code/datums/inventory_slots/slots/slot_gloves.dm | 4 ---- code/datums/inventory_slots/slots/slot_mask.dm | 10 ++-------- code/datums/inventory_slots/slots/slot_shoes.dm | 4 ---- code/datums/inventory_slots/slots/slot_suit_storage.dm | 2 -- 11 files changed, 2 insertions(+), 32 deletions(-) diff --git a/code/datums/inventory_slots/_inventory_slot.dm b/code/datums/inventory_slots/_inventory_slot.dm index 6f23b1b51a10..086db0ce8990 100644 --- a/code/datums/inventory_slots/_inventory_slot.dm +++ b/code/datums/inventory_slots/_inventory_slot.dm @@ -171,6 +171,4 @@ /datum/inventory_slot/proc/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding) - if(user == owner) - return "You are wearing [_holding.get_examine_line()]." return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()]." diff --git a/code/datums/inventory_slots/inventory_gripper.dm b/code/datums/inventory_slots/inventory_gripper.dm index 2f87394ca74e..14140fc65fd1 100644 --- a/code/datums/inventory_slots/inventory_gripper.dm +++ b/code/datums/inventory_slots/inventory_gripper.dm @@ -25,8 +25,6 @@ /datum/inventory_slot/gripper/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding) var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(owner, slot_id) - if(user == owner) - return "You are holding [_holding.get_examine_line()] in your [E?.name || lowertext(slot_name)]." return "[pronouns.He] [pronouns.is] holding [_holding.get_examine_line()] in [pronouns.his] [E?.name || lowertext(slot_name)]." /datum/inventory_slot/gripper/can_equip_to_slot(var/mob/user, var/obj/item/prop, var/disable_warning) diff --git a/code/datums/inventory_slots/slots/slot_back.dm b/code/datums/inventory_slots/slots/slot_back.dm index 7c2496867edf..1f900ae9f4d6 100644 --- a/code/datums/inventory_slots/slots/slot_back.dm +++ b/code/datums/inventory_slots/slots/slot_back.dm @@ -15,6 +15,4 @@ /datum/inventory_slot/back/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding) - if(user == owner) - return "You have [_holding.get_examine_line()] on your back." return "[pronouns.He] [pronouns.has] [_holding.get_examine_line()] on [pronouns.his] back." diff --git a/code/datums/inventory_slots/slots/slot_belt.dm b/code/datums/inventory_slots/slots/slot_belt.dm index c2521798b6c8..636d8e8d8ee1 100644 --- a/code/datums/inventory_slots/slots/slot_belt.dm +++ b/code/datums/inventory_slots/slots/slot_belt.dm @@ -25,6 +25,4 @@ /datum/inventory_slot/belt/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding) - if(user == owner) - return "You have [_holding.get_examine_line()] about your waist." return "[pronouns.He] [pronouns.has] [_holding.get_examine_line()] about [pronouns.his] waist." diff --git a/code/datums/inventory_slots/slots/slot_cuffs.dm b/code/datums/inventory_slots/slots/slot_cuffs.dm index 9ff985ec94bc..5bbac50af5df 100644 --- a/code/datums/inventory_slots/slots/slot_cuffs.dm +++ b/code/datums/inventory_slots/slots/slot_cuffs.dm @@ -24,6 +24,4 @@ /datum/inventory_slot/handcuffs/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding) - if(user == owner) - return SPAN_WARNING("You are [html_icon(_holding)] restrained with \the [_holding]!") return SPAN_WARNING("[pronouns.He] [pronouns.is] [html_icon(_holding)] restrained with \the [_holding]!") diff --git a/code/datums/inventory_slots/slots/slot_ears.dm b/code/datums/inventory_slots/slots/slot_ears.dm index 1e905433cfc3..96cba77d68ac 100644 --- a/code/datums/inventory_slots/slots/slot_ears.dm +++ b/code/datums/inventory_slots/slots/slot_ears.dm @@ -22,8 +22,6 @@ /datum/inventory_slot/ear/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDEEARS)) - if(user == owner) - return "You have [_holding.get_examine_line()] on your [lowertext(slot_name)]." return "[pronouns.He] [pronouns.has] [_holding.get_examine_line()] on [pronouns.his] [lowertext(slot_name)]." /datum/inventory_slot/ear/unequipped(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE) diff --git a/code/datums/inventory_slots/slots/slot_glasses.dm b/code/datums/inventory_slots/slots/slot_glasses.dm index d686ac93edb9..5d4bea87ae2b 100644 --- a/code/datums/inventory_slots/slots/slot_glasses.dm +++ b/code/datums/inventory_slots/slots/slot_glasses.dm @@ -15,6 +15,4 @@ /datum/inventory_slot/glasses/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDEEYES)) - if(user == owner) - return "You have [_holding.get_examine_line()] covering your eyes." return "[pronouns.He] [pronouns.has] [_holding.get_examine_line()] covering [pronouns.his] eyes." diff --git a/code/datums/inventory_slots/slots/slot_gloves.dm b/code/datums/inventory_slots/slots/slot_gloves.dm index 3877aaf32cf1..c48572fbffa2 100644 --- a/code/datums/inventory_slots/slots/slot_gloves.dm +++ b/code/datums/inventory_slots/slots/slot_gloves.dm @@ -32,11 +32,7 @@ /datum/inventory_slot/gloves/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDEGLOVES)) - if(user == owner) - return "You have [_holding.get_examine_line()] on your hands." return "[pronouns.He] [pronouns.has] [_holding.get_examine_line()] on [pronouns.his] hands." for(var/obj/item/organ/external/E in owner.get_hands_organs()) if(E.coating) - if(user == owner) - return "There's something on your hands!" return "There's something on [pronouns.his] hands!" diff --git a/code/datums/inventory_slots/slots/slot_mask.dm b/code/datums/inventory_slots/slots/slot_mask.dm index 53fa82f64213..319bbe36e9ff 100644 --- a/code/datums/inventory_slots/slots/slot_mask.dm +++ b/code/datums/inventory_slots/slots/slot_mask.dm @@ -39,12 +39,6 @@ /datum/inventory_slot/mask/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDEMASK)) if(_holding.body_parts_covered & SLOT_FACE) - if(user == owner) - return "You are wearing [_holding.get_examine_line()] on your face." - else - return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] on [pronouns.his] face." + return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] on [pronouns.his] face." else - if(user == owner) - return "You are wearing [_holding.get_examine_line()] around your neck." - else - return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] around [pronouns.his] neck." + return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] around [pronouns.his] neck." diff --git a/code/datums/inventory_slots/slots/slot_shoes.dm b/code/datums/inventory_slots/slots/slot_shoes.dm index 366c8b549b3b..44f7ce62aa4b 100644 --- a/code/datums/inventory_slots/slots/slot_shoes.dm +++ b/code/datums/inventory_slots/slots/slot_shoes.dm @@ -34,12 +34,8 @@ /datum/inventory_slot/shoes/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDESHOES)) - if(user == owner) - return "You are wearing [_holding.get_examine_line()] on your feet." return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] on [pronouns.his] feet." for(var/bp in list(BP_L_FOOT, BP_R_FOOT)) var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(owner, bp) if(REAGENT_TOTAL_VOLUME(E?.coating)) - if(user == owner) - return "There's something on your feet!" return "There's something on [pronouns.his] feet!" diff --git a/code/datums/inventory_slots/slots/slot_suit_storage.dm b/code/datums/inventory_slots/slots/slot_suit_storage.dm index 832a72563294..6fb3f2e7e1df 100644 --- a/code/datums/inventory_slots/slots/slot_suit_storage.dm +++ b/code/datums/inventory_slots/slots/slot_suit_storage.dm @@ -23,6 +23,4 @@ /datum/inventory_slot/suit_storage/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDESUITSTORAGE)) - if(user == owner) - return "[.]\nYou are carrying [_holding.get_examine_line()] on your [_holding.name]." return "[.]\n[pronouns.He] [pronouns.is] carrying [_holding.get_examine_line()] on [pronouns.his] [_holding.name]." From 5c8cdaa6c39b4a3337abbd8fabb60cf46b585495 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 22 Dec 2025 01:04:21 +0000 Subject: [PATCH 0936/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index b68d6c828dd7..c8d067f1305b 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,12 +57,6 @@

    MistakeNot4892 updated:

    • Drakes can no longer farm, but they can mine (in dirt).
    - -

    20 October 2025

    -

    MistakeNot4892 updated:

    -
      -
    • Crafted barrels will need barrel rims forged if the forging modpack is in use.
    • -
    From e982ae9485b18de7a65a5d4b9eba30e073292d89 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 28 Dec 2025 11:50:14 +1100 Subject: [PATCH 0937/1331] Removing assoc list syntax using numerical keys so that the game will compile on new BYOND. --- code/controllers/subsystems/event.dm | 7 ++++--- code/modules/events/event_container.dm | 3 ++- code/modules/food/cooking/_recipe.dm | 7 ++----- maps/~mapsystem/maps.dm | 15 ++++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/code/controllers/subsystems/event.dm b/code/controllers/subsystems/event.dm index e0abcf5b02c6..6412bef40015 100644 --- a/code/controllers/subsystems/event.dm +++ b/code/controllers/subsystems/event.dm @@ -28,10 +28,11 @@ SUBSYSTEM_DEF(event) /datum/controller/subsystem/event/Initialize() if(!event_containers) + // Order must conform to EVENT_LEVEL_MUNDANE, EVENT_LEVEL_MODERATE, EVENT_LEVEL_MAJOR. event_containers = list( - EVENT_LEVEL_MUNDANE = new global.using_map.event_container_mundane, - EVENT_LEVEL_MODERATE = new global.using_map.event_container_moderate, - EVENT_LEVEL_MAJOR = new global.using_map.event_container_major + new global.using_map.event_container_mundane, + new global.using_map.event_container_moderate, + new global.using_map.event_container_major ) all_events = null diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index 97dc8f97f3ff..cea2b1356a1c 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -1,4 +1,5 @@ -var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major") +// Ordering of these values must confirm to EVENT_LEVEL_MUNDANE, EVENT_LEVEL_MODERATE, EVENT_LEVEL_MAJOR. +var/global/list/severity_to_string = list("Mundane", "Moderate", "Major") /datum/event_container var/severity = -1 diff --git a/code/modules/food/cooking/_recipe.dm b/code/modules/food/cooking/_recipe.dm index 1cbafb646903..5c73d8430ef2 100644 --- a/code/modules/food/cooking/_recipe.dm +++ b/code/modules/food/cooking/_recipe.dm @@ -222,11 +222,8 @@ var/global/list/_cooking_recipe_cache = list() // We collected the used ingredients before removing them in case // the result proc needs to check the list for procedural products. - var/list/used_ingredients = list( - RECIPE_COMPONENT_ITEMS = list(), - RECIPE_COMPONENT_FRUIT = list(), - RECIPE_COMPONENT_CHEMS = list() - ) + // This mapping is indexed by RECIPE_COMPONENT_FOO defines. + var/list/used_ingredients = list(list(), list(), list()) // Find items we need. var/list/container_contents = container.get_contained_external_atoms() diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index ca7f5deff7ee..2476854e8388 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -153,14 +153,15 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable /decl/background_category/religion = /decl/background_detail/religion/other ) + // Order must conform to ACCESS_REGION_FOO defines. var/access_modify_region = list( - ACCESS_REGION_SECURITY = list(access_hos, access_change_ids), - ACCESS_REGION_MEDBAY = list(access_cmo, access_change_ids), - ACCESS_REGION_RESEARCH = list(access_rd, access_change_ids), - ACCESS_REGION_ENGINEERING = list(access_ce, access_change_ids), - ACCESS_REGION_COMMAND = list(access_change_ids), - ACCESS_REGION_GENERAL = list(access_change_ids), - ACCESS_REGION_SUPPLY = list(access_change_ids) + list(access_hos, access_change_ids), + list(access_cmo, access_change_ids), + list(access_rd, access_change_ids), + list(access_ce, access_change_ids), + list(access_change_ids), + list(access_change_ids), + list(access_change_ids) ) var/secrets_directory From 87e4cc8369f2d7555aab4200da667420fe23137b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 27 Nov 2025 18:57:53 -0500 Subject: [PATCH 0938/1331] Fix inverted can_slip check --- code/modules/species/species.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index f3f2dfdb6592..3c7aac5bbb76 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -631,7 +631,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 return var/randn = rand(1, 100) - skill_mod + state_mod - if(!target.can_slip() && randn <= 25) + if(target.can_slip() && randn <= 25) var/armor_check = 100 * target.get_blocked_ratio(affecting, BRUTE, damage = 20) target.apply_effect(push_mod, WEAKEN, armor_check) playsound(target.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) From ccb0315bdf6c612c6986a666224f029193095811 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 16:59:14 -0500 Subject: [PATCH 0939/1331] Move actors into a modpack --- maps/modpack_testing/modpack_testing.dm | 1 + .../outsider => mods/content}/actors.dm | 16 +++++++++++++++- nebula.dme | 1 - 3 files changed, 16 insertions(+), 2 deletions(-) rename {code/game/antagonist/outsider => mods/content}/actors.dm (82%) diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 212920fd4ad0..6848ad0cd4ce 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -3,6 +3,7 @@ #include "modpack_testing_lobby.dm" #include "blank.dmm" + #include "../../mods/content/actors.dm" #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" diff --git a/code/game/antagonist/outsider/actors.dm b/mods/content/actors.dm similarity index 82% rename from code/game/antagonist/outsider/actors.dm rename to mods/content/actors.dm index c18e449a882d..fbb88bad4975 100644 --- a/code/game/antagonist/outsider/actors.dm +++ b/mods/content/actors.dm @@ -1,3 +1,10 @@ +#ifndef MODPACK_ACTORS +#define MODPACK_ACTORS +#endif + +/decl/modpack/actors + name = "Actor Special Role" + /decl/special_role/actor name = "Actor" name_plural = "Actors" @@ -8,12 +15,15 @@ hard_cap_round = 10 initial_spawn_req = 1 initial_spawn_target = 1 - show_objectives_on_creation = 0 //actors are not antagonists and do not need the antagonist greet text + show_objectives_on_creation = FALSE //actors are not antagonists and do not need the antagonist greet text required_language = /decl/language/human/common default_outfit = /decl/outfit/actor default_access = list() id_title = "Actor" +/obj/abstract/landmark/actor_spawn + name = "ActorSpawn" + /decl/outfit/actor name = "Special Role - Actor" uniform = /obj/item/clothing/jumpsuit/chameleon @@ -37,6 +47,10 @@ if(!MayRespawn(1) || !actors.can_become_antag(usr.mind, 1)) return + if(!LAZYLEN(actors.starting_locations)) + to_chat(usr, "Actors do not have a spawn location on this map, and are unavailable.") + return + var/choice = alert("Are you sure you'd like to join as an actor?", "Confirmation","Yes", "No") if(choice != "Yes") return diff --git a/nebula.dme b/nebula.dme index 5cffd8b32162..57ff29a38fc2 100644 --- a/nebula.dme +++ b/nebula.dme @@ -775,7 +775,6 @@ #include "code\game\antagonist\antagonist_place.dm" #include "code\game\antagonist\antagonist_print.dm" #include "code\game\antagonist\antagonist_update.dm" -#include "code\game\antagonist\outsider\actors.dm" #include "code\game\antagonist\outsider\ert.dm" #include "code\game\antagonist\station\provocateur.dm" #include "code\game\antagonist\station\thrall.dm" From b4da4585f6c3ba4bca7e9a634d64882741784b53 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 16:30:55 -0500 Subject: [PATCH 0940/1331] Remove unused organ tags --- code/__defines/mobs.dm | 15 --------------- mods/species/ascent/ascent.dm | 1 + mods/species/serpentid/serpentid.dm | 4 ++++ mods/species/vox/_vox.dm | 11 +++++++---- mods/species/vox/datum/species.dm | 2 +- mods/species/vox/datum/species_bodytypes.dm | 4 ++-- mods/species/vox/organs_vox.dm | 2 +- 7 files changed, 16 insertions(+), 23 deletions(-) diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 4b25f2443aa4..75c9ac6ef8ce 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -150,30 +150,15 @@ #define BP_EYES "eyes" #define BP_HEART "heart" #define BP_LUNGS "lungs" -#define BP_TRACH "tracheae" #define BP_BRAIN "brain" #define BP_LIVER "liver" #define BP_KIDNEYS "kidneys" #define BP_STOMACH "stomach" -#define BP_PLASMA "plasma vessel" #define BP_APPENDIX "appendix" #define BP_CELL "cell" -#define BP_HIVE "hive node" -#define BP_NUTRIENT "nutrient vessel" -#define BP_ACID "acid gland" -#define BP_EGG "egg sac" -#define BP_RESIN "resin spinner" -#define BP_STRATA "neural strata" -#define BP_RESPONSE "response node" -#define BP_GBLADDER "gas bladder" -#define BP_POLYP "polyp segment" -#define BP_ANCHOR "anchoring ligament" -#define BP_ACETONE "acetone reactor" // Robo Organs. #define BP_VOICE "vocal synthesiser" -#define BP_STACK "stack" -#define BP_OPTICS "optics" //Augmetations #define BP_AUGMENT_R_ARM "right arm augment" diff --git a/mods/species/ascent/ascent.dm b/mods/species/ascent/ascent.dm index 611b84f81fc8..b0dcf2940b35 100644 --- a/mods/species/ascent/ascent.dm +++ b/mods/species/ascent/ascent.dm @@ -5,6 +5,7 @@ #define BODY_EQUIP_FLAG_GYNE BITFLAG(5) #define BP_SYSTEM_CONTROLLER "system controller" +#define BP_EGG "egg sac" #define MANTIDIFY(_thing, _name, _desc) \ ##_thing/ascent/name = _name; \ diff --git a/mods/species/serpentid/serpentid.dm b/mods/species/serpentid/serpentid.dm index 627bca7da6b1..84d299d55140 100644 --- a/mods/species/serpentid/serpentid.dm +++ b/mods/species/serpentid/serpentid.dm @@ -1,2 +1,6 @@ #define BODYTYPE_SNAKE "snakelike body" #define BODY_EQUIP_FLAG_SNAKE BITFLAG(12) +#define BP_TRACH "tracheae" + +/decl/modpack/serpentid + name = "Serpentid Species" \ No newline at end of file diff --git a/mods/species/vox/_vox.dm b/mods/species/vox/_vox.dm index 70f65067b0b4..dcf6527797dd 100644 --- a/mods/species/vox/_vox.dm +++ b/mods/species/vox/_vox.dm @@ -1,7 +1,10 @@ -#define BODYTYPE_VOX "reptoavian body" -#define BODYTYPE_VOX_LARGE "large reptoavian body" -#define BP_HINDTONGUE "hindtongue" -#define BODY_EQUIP_FLAG_VOX BITFLAG(8) +#define BODYTYPE_VOX "reptoavian body" +#define BODYTYPE_VOX_LARGE "large reptoavian body" +// Internal organs +#define BP_HINDTONGUE "hindtongue" +#define BP_VOXSTACK "vox stack" +// Bodytype equip flags +#define BODY_EQUIP_FLAG_VOX BITFLAG(8) /decl/modpack/vox name = "Vox Content" diff --git a/mods/species/vox/datum/species.dm b/mods/species/vox/datum/species.dm index 3570737342f6..e5f6c03faf25 100644 --- a/mods/species/vox/datum/species.dm +++ b/mods/species/vox/datum/species.dm @@ -155,7 +155,7 @@ var/global/list/vox_current_pressure_toggle = list() /decl/species/vox/handle_death(var/mob/living/human/H) ..() - var/obj/item/organ/internal/voxstack/stack = H.get_organ(BP_STACK, /obj/item/organ/internal/voxstack) + var/obj/item/organ/internal/voxstack/stack = H.get_organ(BP_VOXSTACK, /obj/item/organ/internal/voxstack) if (stack) stack.do_backup() diff --git a/mods/species/vox/datum/species_bodytypes.dm b/mods/species/vox/datum/species_bodytypes.dm index 6aca9f46c5cc..c74d02b11967 100644 --- a/mods/species/vox/datum/species_bodytypes.dm +++ b/mods/species/vox/datum/species_bodytypes.dm @@ -26,7 +26,7 @@ ) vital_organs = list( - BP_STACK, + BP_VOXSTACK, BP_BRAIN ) override_limb_types = list( @@ -45,7 +45,7 @@ BP_KIDNEYS = /obj/item/organ/internal/kidneys/vox, BP_BRAIN = /obj/item/organ/internal/brain, BP_EYES = /obj/item/organ/internal/eyes/vox, - BP_STACK = /obj/item/organ/internal/voxstack, + BP_VOXSTACK = /obj/item/organ/internal/voxstack, BP_HINDTONGUE = /obj/item/organ/internal/hindtongue ) default_sprite_accessories = list( diff --git a/mods/species/vox/organs_vox.dm b/mods/species/vox/organs_vox.dm index c1ba5b7de6c4..29b4e15191e6 100644 --- a/mods/species/vox/organs_vox.dm +++ b/mods/species/vox/organs_vox.dm @@ -166,7 +166,7 @@ name = "cortical stack" parent_organ = BP_HEAD icon_state = "cortical-stack" - organ_tag = BP_STACK + organ_tag = BP_VOXSTACK organ_properties = ORGAN_PROP_PROSTHETIC origin_tech = @'{"biotech":4,"materials":4,"magnets":2,"programming":3}' relative_size = 10 From 3de214f7933d1b161d80fb3fe609c4f0b2cdf734 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Oct 2025 12:37:42 -0400 Subject: [PATCH 0941/1331] Remove hardcoded space check in APC and alarm code --- code/game/area/areas.dm | 5 +++-- code/modules/power/apc/apc_frame.dm | 2 +- maps/exodus/exodus_areas.dm | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 838532499857..971f65825276 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -31,7 +31,8 @@ var/global/list/areas = list() var/lightswitch = TRUE var/requires_power = TRUE - var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() + /// Disables constructing or using APCs in this area. + var/always_unpowered = FALSE var/atmosalm = 0 var/power_equip = 1 // Status @@ -307,7 +308,7 @@ var/global/list/areas = list() #define DO_PARTY(COLOR) animate(color = COLOR, time = 0.5 SECONDS, easing = QUAD_EASING) /area/on_update_icon() - if((atmosalm || fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc. + if((atmosalm || fire || eject || party) && (!requires_power||power_environ) && !always_unpowered)//If it doesn't require power, can still activate this proc. if(fire && !atmosalm && !eject && !party) // FIRE color = "#ff9292" animate(src) // stop any current animations. diff --git a/code/modules/power/apc/apc_frame.dm b/code/modules/power/apc/apc_frame.dm index 897401051961..7241115768a1 100644 --- a/code/modules/power/apc/apc_frame.dm +++ b/code/modules/power/apc/apc_frame.dm @@ -11,7 +11,7 @@ /obj/item/frame/apc/try_build(turf/on_wall) var/area/A = get_area(src) - if (A.requires_power == 0 || istype(A, /area/space)) + if (A.requires_power == 0 || A.always_unpowered) to_chat(usr, SPAN_WARNING("An APC cannot be placed in this area.")) return if (A.get_apc()) diff --git a/maps/exodus/exodus_areas.dm b/maps/exodus/exodus_areas.dm index 29ba42a4e778..336403c6218d 100644 --- a/maps/exodus/exodus_areas.dm +++ b/maps/exodus/exodus_areas.dm @@ -86,8 +86,8 @@ req_access = list(access_engine) /area/exodus/solar - requires_power = 1 - always_unpowered = 1 + requires_power = TRUE + always_unpowered = TRUE has_gravity = FALSE base_turf = /turf/space From 1a89deaa0e1820bab83011ad316a6cbdaaaca4e1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Oct 2025 12:39:39 -0400 Subject: [PATCH 0942/1331] Remove hardcoded space check in level data --- .../random_exoplanet/random_planet_level_data.dm | 7 +++---- code/modules/multiz/level_data.dm | 9 +++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/code/modules/maps/template_types/random_exoplanet/random_planet_level_data.dm b/code/modules/maps/template_types/random_exoplanet/random_planet_level_data.dm index 26ad0dee2a5d..8166b19f7f61 100644 --- a/code/modules/maps/template_types/random_exoplanet/random_planet_level_data.dm +++ b/code/modules/maps/template_types/random_exoplanet/random_planet_level_data.dm @@ -80,7 +80,7 @@ //Try to adopt our parent planet's ambient lighting preferences apply_planet_ambient_lighting(parent_planetoid) //Rename the surface area if we have one yet - adapt_location_name(parent_planetoid.name) + adapt_to_location_name(parent_planetoid.name) ///If we're getting atmos from our parent planet, apply it. /datum/level_data/planetoid/proc/apply_planet_atmosphere(var/datum/planetoid_data/P) @@ -96,13 +96,12 @@ if(!ambient_light_color) ambient_light_level = P.surface_light_color -/datum/level_data/planetoid/adapt_location_name(location_name) - if(!(. = ..())) - return +/datum/level_data/planetoid/adapt_to_location_name(location_name) if(!ispath(base_area) || ispath(base_area, world.area)) return var/area/A = get_base_area_instance() //Make sure we're not going to rename the world's base area if(!istype(A, world.area)) + // vvvv This feels bad, should we be doing this? vvvv global.using_map.area_purity_test_exempt_areas |= A.type //Make sure we add any of those, so unit tests calm down when we rename A.SetName("[location_name]") diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index ea5984672f15..389f3e24923b 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -504,12 +504,9 @@ mob_count-- CHECK_TICK -///Changes anything named we may need to rename accordingly to the parent location name. For instance, exoplanets levels. -/datum/level_data/proc/adapt_location_name(var/location_name) - SHOULD_CALL_PARENT(TRUE) - if(!base_area || ispath(base_area, /area/space)) - return FALSE - return TRUE +///Changes anything named we may need to rename based on the parent location name. For instance, exoplanet surface areas. +/datum/level_data/proc/adapt_to_location_name(var/location_name) + return //#TODO: this could probably be done in a more elegant way. Since most map templates will never call this. ///Called before a runtime generated template is generated on our z-level. Only applies to templates generated onto new z-levels. From 845216ea97dd3c5e244b3026ee512e319f02f04a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Oct 2025 12:39:53 -0400 Subject: [PATCH 0943/1331] Remove hardcoded space check in gravity generator code --- code/game/area/areas.dm | 2 ++ code/modules/power/gravitygenerator.dm | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 971f65825276..3d2fef8f7903 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -45,6 +45,8 @@ var/global/list/areas = list() var/oneoff_light = 0 var/oneoff_environ = 0 var/has_gravity = TRUE + /// If FALSE, this area is unable to have its gravity overridden by a gravity generator. Used on /area/space. + var/can_have_gravity = TRUE var/air_doors_activated = FALSE var/obj/machinery/apc/apc diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index fa8764e8dc6d..e35be5c3d4e7 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -39,7 +39,7 @@ /obj/machinery/gravity_generator/proc/locatelocalareas() for(var/area/A in range(src,effectiverange)) - if(istype(A,/area/space)) + if(!A.can_have_gravity) continue // No (de)gravitizing space. localareas |= A From 296f97ece86ee990a67be3a89c1878974edce21e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Oct 2025 12:40:02 -0400 Subject: [PATCH 0944/1331] Remove hardcoded space check in buildmode --- code/modules/admin/buildmode/mode_areas.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/admin/buildmode/mode_areas.dm b/code/modules/admin/buildmode/mode_areas.dm index 39b28fa353c1..4df3b7e41797 100644 --- a/code/modules/admin/buildmode/mode_areas.dm +++ b/code/modules/admin/buildmode/mode_areas.dm @@ -39,7 +39,7 @@ /datum/build_mode/areas/Configurate() var/mode = alert("Pick or Create an area.", "Build Mode: Areas", "Pick", "Create", "Cancel") if (mode == "Pick") - var/area/path = select_subpath((selected_area?.type || /area/space), /area) + var/area/path = select_subpath((selected_area?.type || world.area), /area) if (path) for (var/area/build_area in global.areas) if (build_area.type == path) @@ -55,7 +55,7 @@ new_area.power_equip = 0 new_area.power_light = 0 new_area.power_environ = 0 - new_area.always_unpowered = 0 + new_area.always_unpowered = FALSE SelectArea(new_area) user.client.debug_variables(selected_area) to_chat(user, "Created area [new_area.proper_name]") From d53571fc811165a770882f1914e41f2be0fd69e8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 17:05:13 -0500 Subject: [PATCH 0945/1331] Use LAZYLEN macro where applicable --- code/_helpers/lists.dm | 2 +- code/datums/mind/mind.dm | 2 +- code/datums/trading/_trader.dm | 8 ++++---- code/datums/uplink/uplink_sources.dm | 2 +- code/game/antagonist/antagonist_objectives.dm | 2 +- code/game/antagonist/antagonist_panel.dm | 4 ++-- code/game/antagonist/antagonist_place.dm | 2 +- code/game/antagonist/antagonist_print.dm | 4 ++-- code/game/gamemodes/game_mode.dm | 6 +++--- .../_machines_base/machinery_components.dm | 2 +- .../objects/effects/decals/Cleanable/tracks.dm | 2 +- code/game/objects/items/devices/radio/radio.dm | 2 +- code/game/turfs/walls/wall_natural_xenoarch.dm | 4 ++-- code/modules/admin/admin.dm | 2 +- code/modules/admin/admin_attack_log.dm | 4 ++-- code/modules/admin/dbban/functions.dm | 8 ++++---- code/modules/augment/passive/boost.dm | 2 +- code/modules/client/asset_cache.dm | 2 +- code/modules/clothing/masks/voice.dm | 2 +- code/modules/clothing/spacesuits/breaches.dm | 6 +++--- .../clothing/spacesuits/rig/modules/modules.dm | 2 +- code/modules/clothing/spacesuits/rig/rig.dm | 2 +- .../clothing/spacesuits/rig/rig_attackby.dm | 2 +- code/modules/hydroponics/seed.dm | 6 +++--- .../hydroponics/spreading/spreading_growth.dm | 2 +- code/modules/mob/language/language.dm | 2 +- .../mob/living/silicon/robot/modules/_module.dm | 2 +- code/modules/mob/observer/eye/blueprints_eye.dm | 2 +- .../overmap/ships/machines/fusion_thruster.dm | 2 +- code/modules/paperwork/handlabeler.dm | 2 +- code/modules/pointdefense/pointdefense.dm | 2 +- code/modules/power/fusion/kinetic_harvester.dm | 2 +- code/modules/power/powernet.dm | 2 +- code/modules/random_map/dungeon/room_generation.dm | 2 +- code/modules/random_map/dungeon/winding_dungeon.dm | 14 +++++++------- code/modules/turbolift/turbolift.dm | 2 +- mods/content/dungeon_loot/loot_pile.dm | 2 +- mods/content/shackles/laws_pref.dm | 2 +- mods/content/ventcrawl/ventcrawl_mob.dm | 2 +- mods/mobs/dionaea/mob/gestalt/_gestalt.dm | 2 +- mods/mobs/dionaea/mob/gestalt/gestalt_attacks.dm | 2 +- 41 files changed, 63 insertions(+), 63 deletions(-) diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm index 9635c472e181..a39367a43233 100644 --- a/code/_helpers/lists.dm +++ b/code/_helpers/lists.dm @@ -176,7 +176,7 @@ //Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches') /proc/is_type_in_typecache(atom/A, list/cache) - if(!cache || !cache.len || !A) + if(!LAZYLEN(cache) || !A) return 0 return cache[A.type] diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 1ffcf5a68b56..88afba2ce3ee 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -108,7 +108,7 @@ out += "
    " out += "Objectives
    " - if(objectives && objectives.len) + if(LAZYLEN(objectives)) var/num = 1 for(var/datum/objective/O in objectives) out += "Objective #[num]: [O.explanation_text] " diff --git a/code/datums/trading/_trader.dm b/code/datums/trading/_trader.dm index a62d75f584e5..27444f108d19 100644 --- a/code/datums/trading/_trader.dm +++ b/code/datums/trading/_trader.dm @@ -67,7 +67,7 @@ /datum/trader/proc/add_to_pool(var/list/pool, var/list/possible, var/base_chance = 100, var/force = 0) var/divisor = 1 - if(pool && pool.len) + if(LAZYLEN(pool)) divisor = pool.len if(force || prob(base_chance/divisor)) var/new_item = get_possible_item(possible) @@ -160,7 +160,7 @@ return value /datum/trader/proc/offer_items_for_trade(var/list/offers, var/num, var/turf/location, skill = SKILL_MAX) - if(!offers || !offers.len) + if(!LAZYLEN(offers)) return TRADER_NOT_ENOUGH num = clamp(num, 1, trading_items.len) var/offer_worth = 0 @@ -229,7 +229,7 @@ return get_response(TRADER_COMPLIMENT_ACCEPT, "Thank you!") /datum/trader/proc/trade(var/list/offers, var/num, var/turf/location) - if(offers && offers.len) + if(LAZYLEN(offers)) for(var/offer in offers) if(ismob(offer)) var/text = mob_transfer_message @@ -263,7 +263,7 @@ /datum/trader/proc/sell_items(var/list/offers, skill = SKILL_MAX) if(!(trade_flags & TRADER_GOODS)) return TRADER_NO_GOODS - if(!offers || !offers.len) + if(!LAZYLEN(offers)) return TRADER_NOT_ENOUGH var/wanted diff --git a/code/datums/uplink/uplink_sources.dm b/code/datums/uplink/uplink_sources.dm index 37f30e941cc9..69fe364a6c1d 100644 --- a/code/datums/uplink/uplink_sources.dm +++ b/code/datums/uplink/uplink_sources.dm @@ -122,7 +122,7 @@ var/global/list/default_uplink_source_priority = list( if(M.client && M.client.prefs) priority_order = M.client.prefs.uplink_sources - if(!priority_order || !priority_order.len) + if(!LAZYLEN(priority_order)) priority_order = list() for(var/entry in global.default_uplink_source_priority) priority_order |= GET_DECL(entry) diff --git a/code/game/antagonist/antagonist_objectives.dm b/code/game/antagonist/antagonist_objectives.dm index 51caf655579f..a4ad2463e7a9 100644 --- a/code/game/antagonist/antagonist_objectives.dm +++ b/code/game/antagonist/antagonist_objectives.dm @@ -1,7 +1,7 @@ /decl/special_role/proc/create_global_objectives(var/override=0) if(get_config_value(/decl/config/enum/objectives_disabled) != CONFIG_OBJECTIVE_ALL && !override) return 0 - if(global_objectives && global_objectives.len) + if(LAZYLEN(global_objectives)) return 0 return 1 diff --git a/code/game/antagonist/antagonist_panel.dm b/code/game/antagonist/antagonist_panel.dm index d34d47f6fe4d..2d4904c726ff 100644 --- a/code/game/antagonist/antagonist_panel.dm +++ b/code/game/antagonist/antagonist_panel.dm @@ -5,7 +5,7 @@ if(is_antagonist(player)) dat += "\[-\]" dat += "\[equip\]" - if(starting_locations && starting_locations.len) + if(LAZYLEN(starting_locations)) dat += "\[move to spawn\]" if(extra) dat += "[extra]" else @@ -19,7 +19,7 @@ /decl/special_role/proc/get_check_antag_output(var/datum/admins/calling_admin) - if(!current_antagonists || !current_antagonists.len) + if(!LAZYLEN(current_antagonists)) return "" var/dat = "
    " diff --git a/code/game/antagonist/antagonist_place.dm b/code/game/antagonist/antagonist_place.dm index a81a4cb0dfb4..fb2210e8559a 100644 --- a/code/game/antagonist/antagonist_place.dm +++ b/code/game/antagonist/antagonist_place.dm @@ -21,7 +21,7 @@ return /decl/special_role/proc/place_mob(var/mob/living/mob) - if(!starting_locations || !starting_locations.len) + if(!LAZYLEN(starting_locations)) return var/turf/T = pick_mobless_turf_if_exists(starting_locations) mob.forceMove(T) diff --git a/code/game/antagonist/antagonist_print.dm b/code/game/antagonist/antagonist_print.dm index bcc921e5f413..dd875e8e0e17 100644 --- a/code/game/antagonist/antagonist_print.dm +++ b/code/game/antagonist/antagonist_print.dm @@ -19,7 +19,7 @@ text += "
    Their goals for today were..." text += "
    [ambitions.summarize()]" - if(global_objectives && global_objectives.len) + if(LAZYLEN(global_objectives)) text += "
    Their objectives were:" var/num = 1 for(var/datum/objective/O in global_objectives) @@ -38,7 +38,7 @@ var/role if(ply.assigned_role) role = ply.assigned_role - else + else role = ply.get_special_role_name("unknown role") role = "\improper [role]" diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index e6a2f45c19a4..584abf220f3f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -134,7 +134,7 @@ var/global/list/additional_antag_types = list() to_world("The current game mode is [capitalize(name)]!") if(round_description) to_world("[round_description]") if(round_autoantag) to_world("Antagonists will be added to the round automagically as needed.") - if(antag_templates && antag_templates.len) + if(LAZYLEN(antag_templates)) var/antag_summary = "Possible antagonist types: " var/i = 1 for(var/decl/special_role/antag in antag_templates) @@ -170,7 +170,7 @@ var/global/list/additional_antag_types = list() if(!antag) continue var/list/potential = list() - if(antag_templates && antag_templates.len) + if(LAZYLEN(antag_templates)) if(antag.flags & ANTAG_OVERRIDE_JOB) potential = antag.pending_antagonists else @@ -327,7 +327,7 @@ var/global/list/additional_antag_types = list() /decl/game_mode/proc/check_finished() if(SSevac.evacuation_controller?.round_over() || station_was_nuked) return 1 - if(end_on_antag_death && antag_templates && antag_templates.len) + if(end_on_antag_death && LAZYLEN(antag_templates)) var/has_antags = 0 for(var/decl/special_role/antag in antag_templates) if(!antag.antags_are_dead()) diff --git a/code/game/machinery/_machines_base/machinery_components.dm b/code/game/machinery/_machines_base/machinery_components.dm index d4fecb27c52d..da820f74e750 100644 --- a/code/game/machinery/_machines_base/machinery_components.dm +++ b/code/game/machinery/_machines_base/machinery_components.dm @@ -33,7 +33,7 @@ var/global/list/machine_path_to_circuit_type LAZYINITLIST(uncreated_component_parts) for(var/type in req_components) uncreated_component_parts[type] += (req_components[type] || 1) - if(initial_access && length(initial_access) > 0) + if(LAZYLEN(initial_access) > 0) for(var/access_list in initial_access) // Each part is an AND component. var/obj/item/stock_parts/network_receiver/network_lock/lock = install_component(/obj/item/stock_parts/network_receiver/network_lock/buildable, refresh_parts = FALSE) diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index d2e28c977da5..0b839df5ebea 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -27,7 +27,7 @@ /obj/effect/decal/cleanable/blood/tracks/reveal_blood() // don't reveal non-blood tracks if(ispath(chemical, /decl/material/liquid/blood) && !fluorescent) - if(stack && stack.len) + if(LAZYLEN(stack)) for(var/datum/fluidtrack/track in stack) track.basecolor = COLOR_LUMINOL ..() diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index fd4ef2152a3c..48244d9fcdae 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -76,7 +76,7 @@ /obj/item/radio/proc/can_decrypt(var/list/secured) if(decrypt_all_messages) return TRUE - if(!secured || !length(secured)) + if(!LAZYLEN(secured)) return TRUE if(!islist(secured)) secured = list(secured) diff --git a/code/game/turfs/walls/wall_natural_xenoarch.dm b/code/game/turfs/walls/wall_natural_xenoarch.dm index 85a49e5282fb..e1ea12d7dd46 100644 --- a/code/game/turfs/walls/wall_natural_xenoarch.dm +++ b/code/game/turfs/walls/wall_natural_xenoarch.dm @@ -78,12 +78,12 @@ var/excav_level = P.get_tool_property(TOOL_PICK, TOOL_PROP_EXCAVATION_DEPTH) excavation_level += excav_level //archaeo overlays - if(!archaeo_overlay && finds && finds.len) + if(!archaeo_overlay && LAZYLEN(finds)) var/datum/find/F = finds[1] if(F.excavation_required <= excavation_level + F.view_range) archaeo_overlay = image('icons/turf/excavation_overlays.dmi',"overlay_archaeo[rand(1,3)]") queue_icon_update() - else if(archaeo_overlay && (!finds || !finds.len)) + else if(archaeo_overlay && !LAZYLEN(finds)) archaeo_overlay = null queue_icon_update() diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 1cd43c395226..151a6da37714 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -277,7 +277,7 @@ var/global/BSACooldown = 0 var/savefile/info = new("data/player_saves/[copytext(key, 1, 2)]/[key]/info.sav") var/list/infos from_file(info, infos) - if(!infos || !infos.len) return 0 + if(!LAZYLEN(infos)) return 0 else return 1 diff --git a/code/modules/admin/admin_attack_log.dm b/code/modules/admin/admin_attack_log.dm index 433420e57eb1..ad2970300484 100644 --- a/code/modules/admin/admin_attack_log.dm +++ b/code/modules/admin/admin_attack_log.dm @@ -11,7 +11,7 @@ message_admins(user ? "[key_name_admin(user)] [message]" : "EVENT [message]") /proc/log_and_message_admins_many(var/list/mob/users, var/message) - if(!users || !users.len) + if(!LAZYLEN(users)) return var/list/user_keys = list() @@ -95,7 +95,7 @@ return FALSE /proc/admin_attacker_log_many_victims(var/mob/attacker, var/list/mob/victims, var/attacker_message, var/victim_message, var/admin_message) - if(!victims || !victims.len) + if(!LAZYLEN(victims)) return for(var/mob/victim in victims) diff --git a/code/modules/admin/dbban/functions.dm b/code/modules/admin/dbban/functions.dm index fc8594d64e76..f86f8d0cb4bb 100644 --- a/code/modules/admin/dbban/functions.dm +++ b/code/modules/admin/dbban/functions.dm @@ -415,13 +415,13 @@ if(playercid) cidsearch = "AND `computerid` = '[playercid]' " else - if(adminckey && length(adminckey) >= 3) + if(length(adminckey) >= 3) adminsearch = "AND `a_ckey` LIKE '[adminckey]%' " - if(playerckey && length(playerckey) >= 3) + if(length(playerckey) >= 3) playersearch = "AND `ckey` LIKE '[playerckey]%' " - if(playerip && length(playerip) >= 3) + if(length(playerip) >= 3) ipsearch = "AND `ip` LIKE '[playerip]%' " - if(playercid && length(playercid) >= 7) + if(length(playercid) >= 7) cidsearch = "AND `computerid` LIKE '[playercid]%' " if(dbbantype) diff --git a/code/modules/augment/passive/boost.dm b/code/modules/augment/passive/boost.dm index dad34c1554e2..35d63a124403 100644 --- a/code/modules/augment/passive/boost.dm +++ b/code/modules/augment/passive/boost.dm @@ -48,7 +48,7 @@ return 1 /obj/item/organ/internal/augment/boost/proc/buff() - if(!buffs || !buffs.len) + if(!LAZYLEN(buffs)) return 0 var/list/B = owner.fetch_buffs_of_type(buffpath, 0) for(var/datum/skill_buff/augment/D in B) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index b27ec40f29d6..67bb71649839 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -67,7 +67,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the return FALSE var/list/unreceived = asset_list - (client.cache + client.sending) - if(!unreceived || !unreceived.len) + if(!LAZYLEN(unreceived)) return 0 if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) to_chat(client, "Sending resources...") diff --git a/code/modules/clothing/masks/voice.dm b/code/modules/clothing/masks/voice.dm index 8ecc47d727ce..97d04acc3a3a 100644 --- a/code/modules/clothing/masks/voice.dm +++ b/code/modules/clothing/masks/voice.dm @@ -23,7 +23,7 @@ set src in usr var/voice = sanitize(name, MAX_NAME_LEN) - if(!voice || !length(voice)) return + if(!length(voice)) return changer.voice = voice to_chat(usr, "You are now mimicking [changer.voice].") diff --git a/code/modules/clothing/spacesuits/breaches.dm b/code/modules/clothing/spacesuits/breaches.dm index 87213ef88351..b454453a943a 100644 --- a/code/modules/clothing/spacesuits/breaches.dm +++ b/code/modules/clothing/spacesuits/breaches.dm @@ -48,7 +48,7 @@ //Repair a certain amount of brute or burn damage to the suit. /obj/item/clothing/suit/space/proc/repair_breaches(var/damtype, var/amount, var/mob/user) - if(!can_breach || !breaches || !breaches.len) + if(!can_breach || !LAZYLEN(breaches)) to_chat(user, "There are no breaches to repair on \the [src].") return @@ -146,7 +146,7 @@ burn_damage = 0 var/all_patched = TRUE - if(!can_breach || !breaches || !breaches.len) + if(!can_breach || !LAZYLEN(breaches)) SetName(initial(name)) return 0 @@ -260,7 +260,7 @@ /obj/item/clothing/suit/space/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - if(can_breach && breaches && breaches.len) + if(can_breach && LAZYLEN(breaches)) for(var/datum/breach/B in breaches) . += SPAN_DANGER("It has \a [B.descriptor].") diff --git a/code/modules/clothing/spacesuits/rig/modules/modules.dm b/code/modules/clothing/spacesuits/rig/modules/modules.dm index 2a8833f0c58f..ab631a4e18af 100644 --- a/code/modules/clothing/spacesuits/rig/modules/modules.dm +++ b/code/modules/clothing/spacesuits/rig/modules/modules.dm @@ -118,7 +118,7 @@ if(suit_overlay_inactive) suit_overlay = suit_overlay_inactive - if(charges && charges.len) + if(LAZYLEN(charges)) var/list/processed_charges = list() for(var/list/charge in charges) var/datum/rig_charge/charge_dat = new diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 3e715d6b89a4..54818e20751b 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -139,7 +139,7 @@ START_PROCESSING(SSobj, src) - if(initial_modules && initial_modules.len) + if(LAZYLEN(initial_modules)) for(var/path in initial_modules) var/obj/item/rig_module/module = new path(src) installed_modules += module diff --git a/code/modules/clothing/spacesuits/rig/rig_attackby.dm b/code/modules/clothing/spacesuits/rig/rig_attackby.dm index 46b880bac891..e8a48b533701 100644 --- a/code/modules/clothing/spacesuits/rig/rig_attackby.dm +++ b/code/modules/clothing/spacesuits/rig/rig_attackby.dm @@ -125,7 +125,7 @@ var/list/current_mounts = list() if(cell) current_mounts += "cell" if(air_supply) current_mounts += "tank" - if(installed_modules && installed_modules.len) current_mounts += "system module" + if(LAZYLEN(installed_modules)) current_mounts += "system module" var/to_remove = input("Which would you like to modify?") as null|anything in current_mounts if(!to_remove) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index b6ace7bd1ec4..9aa93b32bfb0 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -263,7 +263,7 @@ var/health_change = 0 // Handle gas consumption. - if(consume_gasses && consume_gasses.len) + if(LAZYLEN(consume_gasses)) var/missing_gas = 0 for(var/gas in consume_gasses) if(LAZYACCESS(environment?.gas, gas) >= consume_gasses[gas]) @@ -284,7 +284,7 @@ health_change += rand(1,3) * growth_rate // Handle gas production. - if(exude_gasses && exude_gasses.len && !check_only) + if(LAZYLEN(exude_gasses) && !check_only) for(var/gas in exude_gasses) environment.adjust_gas(gas, max(1,round((exude_gasses[gas]*(get_trait(TRAIT_POTENCY)/5))/exude_gasses.len))) @@ -508,7 +508,7 @@ //Returns a key corresponding to an entry in the global seed list. /datum/seed/proc/get_mutant_variant() - if(!mutants || !mutants.len || get_trait(TRAIT_IMMUTABLE) > 0) return 0 + if(!LAZYLEN(mutants) || get_trait(TRAIT_IMMUTABLE) > 0) return 0 return pick(mutants) //Mutates the plant overall (randomly). diff --git a/code/modules/hydroponics/spreading/spreading_growth.dm b/code/modules/hydroponics/spreading/spreading_growth.dm index dcc68137e6f5..fab8effc38a0 100644 --- a/code/modules/hydroponics/spreading/spreading_growth.dm +++ b/code/modules/hydroponics/spreading/spreading_growth.dm @@ -62,7 +62,7 @@ //Find a victim if(!buckled_mob) var/list/mob/living/targets = targets_in_range() - if(targets && targets.len && prob(round(seed.get_trait(TRAIT_POTENCY)/4))) + if(LAZYLEN(targets) && prob(round(seed.get_trait(TRAIT_POTENCY)/4))) entangle(pick(targets)) //Handle the victim diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index 25198d4fc8ec..1c7008ad9e63 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -121,7 +121,7 @@ return "..." /decl/language/proc/scramble_word(var/input) - if(!syllables || !syllables.len) + if(!LAZYLEN(syllables)) return stars(input) // If the input is cached already, move it to the end of the cache and return it diff --git a/code/modules/mob/living/silicon/robot/modules/_module.dm b/code/modules/mob/living/silicon/robot/modules/_module.dm index 5de1f45f8dec..29f7b69c5f74 100644 --- a/code/modules/mob/living/silicon/robot/modules/_module.dm +++ b/code/modules/mob/living/silicon/robot/modules/_module.dm @@ -195,7 +195,7 @@ F.update_icon() else if(F.times_used) F.times_used-- - if(!synths || !synths.len) + if(!LAZYLEN(synths)) return for(var/datum/matter_synth/T in synths) T.add_charge(T.recharge_rate * rate) diff --git a/code/modules/mob/observer/eye/blueprints_eye.dm b/code/modules/mob/observer/eye/blueprints_eye.dm index 7742f8f2c6bf..c7bae7b5f9c7 100644 --- a/code/modules/mob/observer/eye/blueprints_eye.dm +++ b/code/modules/mob/observer/eye/blueprints_eye.dm @@ -53,7 +53,7 @@ /mob/observer/eye/blueprints/proc/create_area() var/area_name = sanitize_safe(input("New area name:","Area Creation", ""), MAX_NAME_LEN) - if(!area_name || !length(area_name)) + if(!length(area_name)) return if(length(area_name) > MAX_NAME_LEN) to_chat(owner, SPAN_WARNING("That name is too long!")) diff --git a/code/modules/overmap/ships/machines/fusion_thruster.dm b/code/modules/overmap/ships/machines/fusion_thruster.dm index 3e4ac6748728..f3da75287c27 100644 --- a/code/modules/overmap/ships/machines/fusion_thruster.dm +++ b/code/modules/overmap/ships/machines/fusion_thruster.dm @@ -31,7 +31,7 @@ if(lan) var/list/fusion_cores = lan.get_devices(/obj/machinery/fusion_core) - if(fusion_cores && fusion_cores.len) + if(LAZYLEN(fusion_cores)) harvest_from = fusion_cores[1] return harvest_from diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 9121d00b60e3..23df3cdf036e 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -131,7 +131,7 @@ if(HAND_LABELER_MODE_ADD_NAME) to_chat(user, SPAN_NOTICE("You switch to labeling mode.")) var/str = sanitize_safe(input(user,"Label text?", "Set label", label), MAX_LNAME_LEN) - if(!str || !length(str)) + if(!length(str)) return label = str mode = HAND_LABELER_MODE_ADD diff --git a/code/modules/pointdefense/pointdefense.dm b/code/modules/pointdefense/pointdefense.dm index ce55afe2f299..9c23c78e1360 100644 --- a/code/modules/pointdefense/pointdefense.dm +++ b/code/modules/pointdefense/pointdefense.dm @@ -93,7 +93,7 @@ var/datum/local_network/lan = pointdefense.get_local_network() if(lan) var/list/pointdefense_controllers = lan.get_devices(/obj/machinery/pointdefense_control) - if(pointdefense_controllers && pointdefense_controllers.len > 1) + if(LAZYLEN(pointdefense_controllers) > 1) lan.remove_device(src) return TRUE return ..() diff --git a/code/modules/power/fusion/kinetic_harvester.dm b/code/modules/power/fusion/kinetic_harvester.dm index 34f832bf13fa..f9273dc7850c 100644 --- a/code/modules/power/fusion/kinetic_harvester.dm +++ b/code/modules/power/fusion/kinetic_harvester.dm @@ -47,7 +47,7 @@ if(lan) var/list/fusion_cores = lan.get_devices(/obj/machinery/fusion_core) - if(fusion_cores && fusion_cores.len) + if(LAZYLEN(fusion_cores)) harvest_from = fusion_cores[1] return harvest_from diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 10a8d5488a99..6220dc117d20 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -101,7 +101,7 @@ if(problem > 0) problem = max(problem - 1, 0) - if(nodes && nodes.len) // Added to fix a bad list bug -- TLE + if(LAZYLEN(nodes)) // Added to fix a bad list bug -- TLE for(var/obj/machinery/power/terminal/term in nodes) if( istype( term.master_machine(), /obj/machinery/apc ) ) numapc++ diff --git a/code/modules/random_map/dungeon/room_generation.dm b/code/modules/random_map/dungeon/room_generation.dm index fd62477f6f43..c480fac04ec1 100644 --- a/code/modules/random_map/dungeon/room_generation.dm +++ b/code/modules/random_map/dungeon/room_generation.dm @@ -18,7 +18,7 @@ return 1 /datum/random_room/proc/apply_loot(var/xorigin = 1,var/yorigin = 1,var/zorigin = 1, var/type) - if(!item_spawns || !item_spawns.len) + if(!LAZYLEN(item_spawns)) return 0 var/place = pick(item_spawns) if(istype(place,/obj)) //we assume what object we get is some sort of container. diff --git a/code/modules/random_map/dungeon/winding_dungeon.dm b/code/modules/random_map/dungeon/winding_dungeon.dm index 1ecf9649028e..762213b7640b 100644 --- a/code/modules/random_map/dungeon/winding_dungeon.dm +++ b/code/modules/random_map/dungeon/winding_dungeon.dm @@ -73,10 +73,10 @@ /datum/random_map/winding_dungeon/proc/get_appropriate_list(var/list/common, var/list/uncommon, var/list/rare, var/x, var/y) var/distance = sqrt((x - round(first_room_x+first_room_width/2)) ** 2 + (y - round(first_room_y+first_room_height/2)) ** 2) if(prob(distance)) - if(prob(distance/100) && rare && rare.len) + if(prob(distance/100) && LAZYLEN(rare)) logging("Returning rare list.") return rare - else if(uncommon && uncommon.len) + else if(LAZYLEN(uncommon)) logging("Returning uncommon list.") return uncommon logging("Returning common list.") @@ -92,12 +92,12 @@ var/num_of_loot = round(limit_x * limit_y * loot_multiplier) logging("Attempting to add [num_of_loot] # of loot") var/sanity = 0 - if((loot_common && loot_common.len) || (loot_uncommon && loot_uncommon.len) || (loot_rare && loot_rare.len)) //no monsters no problem + if((LAZYLEN(loot_common)) || (LAZYLEN(loot_uncommon)) || (LAZYLEN(loot_rare))) //no monsters no problem while(rooms.len && num_of_loot > 0) CHECK_TICK var/datum/room/R = pick(rooms) var/list/loot_list = get_appropriate_list(loot_common, loot_uncommon, loot_rare, round(R.x+R.width/2), round(R.y+R.height/2)) - if(!loot_list || !loot_list.len || R.add_loot(origin_x,origin_y,origin_z,pickweight(loot_list))) + if(!LAZYLEN(loot_list) || R.add_loot(origin_x,origin_y,origin_z,pickweight(loot_list))) num_of_loot-- sanity -= 10 //we hahve success so more tries continue @@ -110,7 +110,7 @@ rooms -= R qdel(R) - if((!monsters_common || !monsters_common.len) && (!monsters_uncommon || !monsters_uncommon.len) && (!monsters_rare || !monsters_rare.len)) //no monsters no problem + if(!LAZYLEN(monsters_common) && !LAZYLEN(monsters_uncommon) && !LAZYLEN(monsters_rare)) //no monsters no problem logging("No monster lists detected. Not spawning monsters.") return @@ -119,14 +119,14 @@ while(num_of_monsters > 0) CHECK_TICK - if(!monster_available || !monster_available.len) + if(!LAZYLEN(monster_available)) logging("There are no available turfs left.") num_of_monsters = 0 continue var/turf/T = pick(monster_available) monster_available -= T var/list/monster_list = get_appropriate_list(monsters_common, monsters_uncommon, monsters_rare, T.x, T.y) - if(monster_list && monster_list.len) + if(LAZYLEN(monster_list)) var/type = pickweight(monster_list) logging("Generating a monster of type [type] at position ([T.x],[T.y],[origin_z])") var/mob/M = new type(T) diff --git a/code/modules/turbolift/turbolift.dm b/code/modules/turbolift/turbolift.dm index 8f7e12a7c2e1..f343dce1d988 100644 --- a/code/modules/turbolift/turbolift.dm +++ b/code/modules/turbolift/turbolift.dm @@ -81,7 +81,7 @@ var/current_floor_index = floors.Find(current_floor) if(!target_floor) - if(!queued_floors || !queued_floors.len) + if(!LAZYLEN(queued_floors)) return 0 target_floor = queued_floors[1] queued_floors -= target_floor diff --git a/mods/content/dungeon_loot/loot_pile.dm b/mods/content/dungeon_loot/loot_pile.dm index 5289e4ea5aa2..7944fbcb6986 100644 --- a/mods/content/dungeon_loot/loot_pile.dm +++ b/mods/content/dungeon_loot/loot_pile.dm @@ -28,7 +28,7 @@ /obj/structure/loot_pile/Initialize() var/list/icon_states_to_use = get_icon_states_to_use() - if(icon_states_to_use && icon_states_to_use.len) + if(LAZYLEN(icon_states_to_use)) icon_state = pick(icon_states_to_use) . = ..() diff --git a/mods/content/shackles/laws_pref.dm b/mods/content/shackles/laws_pref.dm index 23fcfa6a6e2a..b92cbe5cc8d2 100644 --- a/mods/content/shackles/laws_pref.dm +++ b/mods/content/shackles/laws_pref.dm @@ -3,7 +3,7 @@ var/is_shackled = FALSE /datum/preferences/proc/get_lawset() - if(!laws || !laws.len) + if(!LAZYLEN(laws)) return var/datum/ai_laws/custom_lawset = new for(var/law in laws) diff --git a/mods/content/ventcrawl/ventcrawl_mob.dm b/mods/content/ventcrawl/ventcrawl_mob.dm index 76d23de4dfe4..c5df94dd2da4 100644 --- a/mods/content/ventcrawl/ventcrawl_mob.dm +++ b/mods/content/ventcrawl/ventcrawl_mob.dm @@ -67,7 +67,7 @@ var/global/list/ventcrawl_machinery = list( for(var/obj/machinery/atmospherics/unary/U in range(1)) if(is_type_in_list(U,ventcrawl_machinery) && Adjacent(U) && U.can_crawl_through()) pipes |= U - if(!pipes || !pipes.len) + if(!LAZYLEN(pipes)) to_chat(src, "There are no pipes that you can ventcrawl into within range!") return if(pipes.len == 1) diff --git a/mods/mobs/dionaea/mob/gestalt/_gestalt.dm b/mods/mobs/dionaea/mob/gestalt/_gestalt.dm index d4216b018272..e57e20ef1918 100644 --- a/mods/mobs/dionaea/mob/gestalt/_gestalt.dm +++ b/mods/mobs/dionaea/mob/gestalt/_gestalt.dm @@ -26,7 +26,7 @@ /obj/structure/diona_gestalt/on_update_icon() ..() add_overlay(eyes_overlay) - if(nymphs && nymphs.len) + if(LAZYLEN(nymphs)) var/matrix/M = matrix() M.Scale(clamp(nymphs.len * 0.1, 1, 2)) transform = M diff --git a/mods/mobs/dionaea/mob/gestalt/gestalt_attacks.dm b/mods/mobs/dionaea/mob/gestalt/gestalt_attacks.dm index 4ec0c9cf890a..fea9e8e64f4c 100644 --- a/mods/mobs/dionaea/mob/gestalt/gestalt_attacks.dm +++ b/mods/mobs/dionaea/mob/gestalt/gestalt_attacks.dm @@ -16,7 +16,7 @@ /obj/structure/diona_gestalt/explosion_act() SHOULD_CALL_PARENT(FALSE) var/shed_count = rand(1,3) - while(shed_count && nymphs && nymphs.len) + while(shed_count && LAZYLEN(nymphs)) shed_count-- shed_atom(forcefully = TRUE) From 9afb4b82f772d9b46dbafc0440c6c1e3667f520d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:31:47 -0400 Subject: [PATCH 0946/1331] Fix missing abstract_type values for power machines --- code/modules/power/debug_items.dm | 3 ++- code/modules/power/power.dm | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/code/modules/power/debug_items.dm b/code/modules/power/debug_items.dm index 3c725c6dd430..dcb428ebdf8e 100644 --- a/code/modules/power/debug_items.dm +++ b/code/modules/power/debug_items.dm @@ -1,4 +1,5 @@ -/obj/machinery/power/debug_items/ +/obj/machinery/power/debug_items + abstract_type = /obj/machinery/power/debug_items icon = 'icons/obj/power.dmi' icon_state = "tracker" anchored = TRUE diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index b88f26c3fe64..ff6d5274e982 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -9,6 +9,7 @@ ///////////////////////////// /obj/machinery/power + abstract_type = /obj/machinery/power name = null icon = 'icons/obj/power.dmi' anchored = TRUE From 837c64f23593cf61423498f6c4440281bcd2572a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 18:06:17 -0500 Subject: [PATCH 0947/1331] Move the beguiled special role into the psionics modpack --- mods/content/psionics/_psionics.dme | 1 + .../content/psionics/datum/antagonists/beguiled.dm | 0 nebula.dme | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename code/game/antagonist/station/thrall.dm => mods/content/psionics/datum/antagonists/beguiled.dm (100%) diff --git a/mods/content/psionics/_psionics.dme b/mods/content/psionics/_psionics.dme index d52fd0955164..523544804011 100644 --- a/mods/content/psionics/_psionics.dme +++ b/mods/content/psionics/_psionics.dme @@ -11,6 +11,7 @@ #include "datum\security_state.dm" #include "datum\surgery.dm" #include "datum\uplink.dm" +#include "datum\antagonists\beguiled.dm" #include "datum\antagonists\foundation.dm" #include "datum\antagonists\paramount.dm" #include "items\_items.dm" diff --git a/code/game/antagonist/station/thrall.dm b/mods/content/psionics/datum/antagonists/beguiled.dm similarity index 100% rename from code/game/antagonist/station/thrall.dm rename to mods/content/psionics/datum/antagonists/beguiled.dm diff --git a/nebula.dme b/nebula.dme index 5cffd8b32162..999929cd4ffc 100644 --- a/nebula.dme +++ b/nebula.dme @@ -778,7 +778,6 @@ #include "code\game\antagonist\outsider\actors.dm" #include "code\game\antagonist\outsider\ert.dm" #include "code\game\antagonist\station\provocateur.dm" -#include "code\game\antagonist\station\thrall.dm" #include "code\game\area\area_abstract.dm" #include "code\game\area\area_access.dm" #include "code\game\area\area_fishing.dm" From a656748c4b95827904855bae4733d60dc07bd007 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Mon, 29 Dec 2025 12:19:30 +1100 Subject: [PATCH 0948/1331] Separating boombox from music-playing item functionality. --- code/game/objects/items/devices/boombox.dm | 132 ++++-------------- .../objects/items/devices/music_player.dm | 101 ++++++++++++++ .../robot/modules/module_uncertified.dm | 2 +- maps/ministation/ministation-0.dmm | 2 +- maps/ministation/space.dmm | 2 +- maps/tradeship/tradeship-2.dmm | 4 +- nebula.dme | 1 + 7 files changed, 132 insertions(+), 112 deletions(-) create mode 100644 code/game/objects/items/devices/music_player.dm diff --git a/code/game/objects/items/devices/boombox.dm b/code/game/objects/items/devices/boombox.dm index 0c3e1c273082..6570fe37400d 100644 --- a/code/game/objects/items/devices/boombox.dm +++ b/code/game/objects/items/devices/boombox.dm @@ -1,10 +1,6 @@ -/obj/item/boombox +/obj/item/music_player/boombox name = "boombox" desc = "A device used to emit rhythmic sounds, colloquially referred to as a 'boombox'. It's in a retro style (massive), and absolutely unwieldy." - icon = 'icons/obj/items/device/boombox.dmi' - icon_state = ICON_STATE_WORLD - _base_attack_force = 7 - w_class = ITEM_SIZE_HUGE //forbid putting something that emits loud sounds forever into a backpack origin_tech = @'{"magnets":2,"combat":1}' material = /decl/material/solid/organic/plastic matter = list( @@ -12,85 +8,25 @@ /decl/material/solid/metal/aluminium = MATTER_AMOUNT_REINFORCEMENT, /decl/material/solid/silicon = MATTER_AMOUNT_TRACE, ) - var/playing = 0 - var/track_num = 1 - var/music_volume = 20 - var/max_volume = 40 - var/frequency = 1 - var/datum/sound_token/sound_token - var/list/datum/track/tracks - var/sound_id + player_name = "BOOMTASTIC 3000" + interact_sound = "switch" + icon = 'icons/obj/items/device/boombox.dmi' + var/break_chance = 3 var/broken var/panel = TRUE -/obj/item/boombox/attack_self(var/mob/user) - interact(user) - -/obj/item/boombox/Initialize() - . = ..() - sound_id = "[type]_[sequential_id(type)]" - tracks = setup_music_tracks(tracks) - -/obj/item/boombox/emp_act(severity) +/obj/item/music_player/boombox/emp_act(severity) boombox_break() -/obj/item/boombox/get_examine_strings(mob/user, distance, infix, suffix) +/obj/item/music_player/boombox/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(!panel) . += SPAN_NOTICE("The front panel is unhinged.") if(broken) . += SPAN_WARNING("It's broken.") -/obj/item/boombox/Destroy() - stop() - . = ..() - -/obj/item/boombox/interact(var/mob/user) - if(!CanPhysicallyInteract(user)) - return - var/dat = "NEXT" - dat += "PREV" - dat += "PLAY" - dat += "STOP" - dat += "VOL -" - dat += "VOL +" - var/datum/browser/popup = new(user, "boombox", "BOOMTASTIC 3000", 290, 110) - popup.set_content(dat) - popup.open() - -/obj/item/boombox/DefaultTopicState() - return global.physical_topic_state - -/obj/item/boombox/CouldUseTopic(var/mob/user) - ..() - playsound(src, "switch", 40) - -/obj/item/boombox/OnTopic(var/user, var/list/href_list) - if(href_list["tracknum"]) - var/diff = text2num(href_list["tracknum"]) - track_num += diff - if(track_num > tracks.len) - track_num = 1 - else if (track_num < 1) - track_num = tracks.len - if(playing) - start() - return TOPIC_REFRESH - if(href_list["stop"]) - stop() - return TOPIC_HANDLED - if(href_list["start"] && !broken) - start() - return TOPIC_HANDLED - if(href_list["volup"]) - change_volume(music_volume + 10) - return TOPIC_HANDLED - if(href_list["voldown"]) - change_volume(music_volume - 10) - return TOPIC_HANDLED - -/obj/item/boombox/attackby(var/obj/item/used_item, var/mob/user) +/obj/item/music_player/boombox/attackby(var/obj/item/used_item, var/mob/user) if(IS_SCREWDRIVER(used_item)) if(!panel) user.visible_message(SPAN_NOTICE("\The [user] re-attaches \the [src]'s front panel with \the [used_item]."), SPAN_NOTICE("You re-attach \the [src]'s front panel.")) @@ -115,7 +51,7 @@ else . = ..() -/obj/item/boombox/proc/AdjustFrequency(var/obj/item/used_item, var/mob/user) +/obj/item/music_player/boombox/proc/AdjustFrequency(var/obj/item/used_item, var/mob/user) var/const/MIN_FREQUENCY = 0.5 var/const/MAX_FREQUENCY = 1.5 @@ -126,9 +62,9 @@ var/tighten = "Tighten (play slower)" var/loosen = "Loosen (play faster)" - if(frequency > MIN_FREQUENCY) + if(music_frequency > MIN_FREQUENCY) options += tighten - if(frequency < MAX_FREQUENCY) + if(music_frequency < MAX_FREQUENCY) options += loosen var/operation = input(user, "How do you wish to adjust the player head?", "Adjust player", options[1]) as null|anything in options @@ -143,61 +79,43 @@ return FALSE if(operation == loosen) - frequency += 0.1 + music_frequency += 0.1 else if(operation == tighten) - frequency -= 0.1 - frequency = clamp(frequency, MIN_FREQUENCY, MAX_FREQUENCY) + music_frequency -= 0.1 + music_frequency = clamp(music_frequency, MIN_FREQUENCY, MAX_FREQUENCY) user.visible_message(SPAN_NOTICE("\The [user] adjusts \the [src]'s player head."), SPAN_NOTICE("You adjust \the [src]'s player head.")) playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(frequency > 1.0) + if(music_frequency > 1.0) to_chat(user, SPAN_NOTICE("\The [src] should be playing faster than usual.")) - else if(frequency < 1.0) + else if(music_frequency < 1.0) to_chat(user, SPAN_NOTICE("\The [src] should be playing slower than usual.")) else to_chat(user, SPAN_NOTICE("\The [src] should be playing as fast as usual.")) return TRUE -/obj/item/boombox/proc/MayAdjust(var/mob/user) +/obj/item/music_player/boombox/proc/MayAdjust(var/mob/user) if(playing) to_chat(user, "You can only adjust \the [src] when it's not playing.") return FALSE return TRUE -/obj/item/boombox/on_update_icon() - . = ..() - icon_state = get_world_inventory_state() - if(playing) - icon_state = "[icon_state]_on" - -/obj/item/boombox/proc/stop() - playing = 0 - update_icon() - QDEL_NULL(sound_token) - -/obj/item/boombox/proc/start() - QDEL_NULL(sound_token) - var/datum/track/T = tracks[track_num] - sound_token = play_looping_sound(src, sound_id, T.GetTrack(), volume = music_volume, frequency = frequency, range = 7, falloff = 4, prefer_mute = TRUE, preference = /datum/client_preference/play_game_music, streaming = TRUE) - playing = 1 - update_icon() - if(prob(break_chance)) - boombox_break() - -/obj/item/boombox/proc/boombox_break() +/obj/item/music_player/boombox/proc/boombox_break() audible_message(SPAN_WARNING("\The [src]'s speakers pop with a sharp crack!")) playsound(src.loc, 'sound/effects/snap.ogg', 100, 1) broken = TRUE stop() -/obj/item/boombox/proc/change_volume(var/new_volume) - music_volume = clamp(new_volume, 0, max_volume) - if(sound_token) - sound_token.SetVolume(music_volume) +/obj/item/music_player/boombox/start() + if(broken) + return + . = ..() + if(prob(break_chance)) + boombox_break() /obj/random_multi/single_item/boombox name = "boombox spawnpoint" id = "boomtastic" - item_path = /obj/item/boombox + item_path = /obj/item/music_player/boombox diff --git a/code/game/objects/items/devices/music_player.dm b/code/game/objects/items/devices/music_player.dm new file mode 100644 index 000000000000..249f10c0c5f8 --- /dev/null +++ b/code/game/objects/items/devices/music_player.dm @@ -0,0 +1,101 @@ +/obj/item/music_player + abstract_type = /obj/item/music_player + icon_state = ICON_STATE_WORLD + _base_attack_force = 7 + w_class = ITEM_SIZE_HUGE //forbid putting something that emits loud sounds forever into a backpack + + var/playing = 0 + var/track_num = 1 + var/music_volume = 20 + var/max_volume = 40 + var/datum/sound_token/sound_token + var/list/datum/track/tracks + var/sound_id + var/player_name = "Music Player" + var/interact_sound + var/music_frequency = 1 + +/obj/item/music_player/on_update_icon() + . = ..() + icon_state = get_world_inventory_state() + if(playing) + icon_state = "[icon_state]_on" + +/obj/item/music_player/proc/stop() + playing = 0 + update_icon() + QDEL_NULL(sound_token) + +/obj/item/music_player/proc/start() + QDEL_NULL(sound_token) + var/datum/track/T = tracks[track_num] + sound_token = play_looping_sound(src, sound_id, T.GetTrack(), volume = music_volume, frequency = music_frequency, range = 7, falloff = 4, prefer_mute = TRUE, preference = /datum/client_preference/play_game_music, streaming = TRUE) + playing = 1 + update_icon() + +/obj/item/music_player/attack_self(var/mob/user) + interact(user) + +/obj/item/music_player/Initialize() + . = ..() + sound_id = "[type]_[sequential_id(type)]" + tracks = setup_music_tracks(tracks) + +/obj/item/music_player/Destroy() + stop() + . = ..() + +/obj/item/music_player/interact(var/mob/user) + if(!CanPhysicallyInteract(user)) + return + var/dat = list() + var/datum/track/T = tracks[track_num] + if(istype(T) && T.title) + dat += "

    [T.title]


    " + dat += "NEXT" + dat += "PREV" + dat += "PLAY" + dat += "STOP" + dat += "VOL -" + dat += "VOL +" + var/datum/browser/popup = new(user, "music_player_[name]", "[player_name]", 350, 150) + popup.set_content(JOINTEXT(dat)) + popup.open() + +/obj/item/music_player/DefaultTopicState() + return global.physical_topic_state + +/obj/item/music_player/CouldUseTopic(var/mob/user) + ..() + if(interact_sound) + playsound(src, interact_sound, 40) + +/obj/item/music_player/OnTopic(var/user, var/list/href_list) + if(href_list["tracknum"]) + var/diff = text2num(href_list["tracknum"]) + track_num += diff + if(track_num > tracks.len) + track_num = 1 + else if (track_num < 1) + track_num = tracks.len + if(playing) + start() + interact(user) + return TOPIC_REFRESH + if(href_list["stop"]) + stop() + return TOPIC_HANDLED + if(href_list["start"]) + start() + return TOPIC_HANDLED + if(href_list["volup"]) + change_volume(music_volume + 10) + return TOPIC_HANDLED + if(href_list["voldown"]) + change_volume(music_volume - 10) + return TOPIC_HANDLED + +/obj/item/music_player/proc/change_volume(var/new_volume) + music_volume = clamp(new_volume, 0, max_volume) + if(sound_token) + sound_token.SetVolume(music_volume) diff --git a/code/modules/mob/living/silicon/robot/modules/module_uncertified.dm b/code/modules/mob/living/silicon/robot/modules/module_uncertified.dm index 6b21bb2b4209..a151a66d63e3 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_uncertified.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_uncertified.dm @@ -18,7 +18,7 @@ CAMERA_CHANNEL_TELEVISION ) equipment = list( - /obj/item/boombox, + /obj/item/music_player/boombox, /obj/item/bikehorn/airhorn, /obj/item/flashlight/party, /obj/item/gun/launcher/money diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index ac11d915541d..fc9f404b88e2 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -7267,7 +7267,7 @@ /turf/floor/laminate, /area/ministation/engine) "EM" = ( -/obj/item/boombox, +/obj/item/music_player/boombox, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, diff --git a/maps/ministation/space.dmm b/maps/ministation/space.dmm index 54f8c3513fd8..f97b25e1675d 100644 --- a/maps/ministation/space.dmm +++ b/maps/ministation/space.dmm @@ -47,7 +47,7 @@ /turf/floor/shuttle/blue, /area/shuttle/escape_shuttle) "am" = ( -/obj/item/boombox, +/obj/item/music_player/boombox, /obj/structure/table/steel_reinforced, /turf/floor/shuttle/blue, /area/shuttle/escape_shuttle) diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 9285dbee4c68..3e2abcf9d90a 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -5429,7 +5429,7 @@ /obj/item/backpack/dufflebag/syndie, /obj/item/box/ammo/shotgunshells, /obj/item/handcuffs, -/obj/item/boombox, +/obj/item/music_player/boombox, /obj/item/stack/tape_roll/barricade_tape/bureaucracy, /obj/machinery/keycard_auth, /turf/floor/laminate, @@ -6623,7 +6623,7 @@ }, /obj/random/powercell, /obj/random/powercell, -/obj/item/boombox, +/obj/item/music_player/boombox, /turf/floor/tiled/techfloor/grid, /area/ship/trade/maintenance/power) "Jg" = ( diff --git a/nebula.dme b/nebula.dme index 5cffd8b32162..05f3183d6b82 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1184,6 +1184,7 @@ #include "code\game\objects\items\devices\megaphone.dm" #include "code\game\objects\items\devices\modkit.dm" #include "code\game\objects\items\devices\multitool.dm" +#include "code\game\objects\items\devices\music_player.dm" #include "code\game\objects\items\devices\oxycandle.dm" #include "code\game\objects\items\devices\paicard.dm" #include "code\game\objects\items\devices\paint_sprayer.dm" From c1c4c4f5b6ce79898d4a6100aab09848cf551e32 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Fri, 26 Dec 2025 19:28:02 +1100 Subject: [PATCH 0949/1331] Porting quadbikes from Polaris. --- .../datums/composite_sounds/vehicle_engine.dm | 7 + code/datums/supplypacks/operations.dm | 4 +- code/game/turfs/flooring/_flooring.dm | 3 + code/game/turfs/flooring/flooring_grass.dm | 6 + code/game/turfs/flooring/flooring_mud.dm | 9 + code/game/turfs/flooring/flooring_natural.dm | 3 + code/game/turfs/flooring/flooring_rock.dm | 3 + code/game/turfs/flooring/flooring_snow.dm | 11 + code/game/turfs/floors/_floor.dm | 6 + code/modules/mob/mob.dm | 2 +- code/modules/vehicles/bike.dm | 15 +- code/modules/vehicles/cargo_train.dm | 300 +++++++----------- code/modules/vehicles/cargo_trolley.dm | 103 ++++++ code/modules/vehicles/quad_bike.dm | 198 ++++++++++++ code/modules/vehicles/quad_trailer.dm | 108 +++++++ code/modules/vehicles/train.dm | 21 +- code/modules/vehicles/vehicle.dm | 46 ++- icons/obj/custom_items_vehicle.dmi | Bin 0 -> 218 bytes icons/obj/vehicles_64x64.dmi | Bin 0 -> 19406 bytes maps/away/errant_pisces/errant_pisces.dmm | 8 +- maps/exodus/exodus-2.dmm | 4 +- maps/ministation/ministation-0.dmm | 4 +- .../government/away_sites/icarus/icarus-1.dmm | 6 +- nebula.dme | 4 + sound/machines/vehicle/engine_end.ogg | Bin 0 -> 49838 bytes sound/machines/vehicle/engine_mid.ogg | Bin 0 -> 40211 bytes sound/machines/vehicle/engine_start.ogg | Bin 0 -> 39943 bytes sound/machines/vehicle/ignition.ogg | Bin 0 -> 10613 bytes 28 files changed, 642 insertions(+), 229 deletions(-) create mode 100644 code/datums/composite_sounds/vehicle_engine.dm create mode 100644 code/modules/vehicles/cargo_trolley.dm create mode 100644 code/modules/vehicles/quad_bike.dm create mode 100644 code/modules/vehicles/quad_trailer.dm create mode 100644 icons/obj/custom_items_vehicle.dmi create mode 100644 icons/obj/vehicles_64x64.dmi create mode 100644 sound/machines/vehicle/engine_end.ogg create mode 100644 sound/machines/vehicle/engine_mid.ogg create mode 100644 sound/machines/vehicle/engine_start.ogg create mode 100644 sound/machines/vehicle/ignition.ogg diff --git a/code/datums/composite_sounds/vehicle_engine.dm b/code/datums/composite_sounds/vehicle_engine.dm new file mode 100644 index 000000000000..824d0044134b --- /dev/null +++ b/code/datums/composite_sounds/vehicle_engine.dm @@ -0,0 +1,7 @@ +/datum/composite_sound/vehicle_engine + start_sound = 'sound/machines/vehicle/engine_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/vehicle/engine_mid.ogg'=1) + mid_length = 6 + end_sound = 'sound/machines/vehicle/engine_end.ogg' + play_volume = 20 diff --git a/code/datums/supplypacks/operations.dm b/code/datums/supplypacks/operations.dm index 09c51789dc3c..0ec89477c5e1 100644 --- a/code/datums/supplypacks/operations.dm +++ b/code/datums/supplypacks/operations.dm @@ -3,13 +3,13 @@ /decl/hierarchy/supply_pack/operations/cargotrain name = "Equipment - Cargo Train Tug" - contains = list(/obj/vehicle/train/cargo/engine) + contains = list(/obj/vehicle/train/engine) containertype = /obj/structure/largecrate containername = "cargo train tug crate" /decl/hierarchy/supply_pack/operations/cargotrailer name = "Equipment - Cargo Train Trolley" - contains = list(/obj/vehicle/train/cargo/trolley) + contains = list(/obj/vehicle/train/trolley) containertype = /obj/structure/largecrate containername = "cargo train trolley crate" diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index f5f8083873f8..bca8e7bbea6e 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -391,3 +391,6 @@ var/global/list/flooring_cache = list() /// contaminant is, optionally, the material of the coating that wants to be added. /decl/flooring/proc/can_show_coating_footprints(turf/target, decl/material/contaminant) return TRUE + +/decl/flooring/proc/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle::base_speed diff --git a/code/game/turfs/flooring/flooring_grass.dm b/code/game/turfs/flooring/flooring_grass.dm index dc53aaf46808..7d8a10d29484 100644 --- a/code/game/turfs/flooring/flooring_grass.dm +++ b/code/game/turfs/flooring/flooring_grass.dm @@ -48,6 +48,9 @@ return TRUE return ..() +/decl/flooring/grass/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 + /decl/flooring/grass/fake desc = "Do they smoke grass out in space, Bowie? Or do they smoke AstroTurf?" icon = 'icons/turf/flooring/fakegrass.dmi' @@ -56,3 +59,6 @@ build_type = /obj/item/stack/tile/grass force_material = /decl/material/solid/organic/plastic uid = "floor_grass_fake" + +/decl/flooring/grass/fake/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle::base_speed diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index bdce5b2859e8..41591c57457d 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -33,6 +33,9 @@ return FALSE return ..() +/decl/flooring/mud/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 1.4 : 1.5 + /decl/flooring/dry_mud name = "dry mud" desc = "This was once mud, but forgot to keep hydrated." @@ -46,6 +49,9 @@ force_material = /decl/material/solid/soil uid = "floor_dry_mud" +/decl/flooring/dry_mud/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 + /decl/flooring/dry_mud/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) target.set_flooring(/decl/flooring/mud) @@ -76,3 +82,6 @@ target.set_base_flooring(/decl/flooring/mud) . = TRUE return . || ..() + +/decl/flooring/dirt/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 diff --git a/code/game/turfs/flooring/flooring_natural.dm b/code/game/turfs/flooring/flooring_natural.dm index 128bdc34cbc4..ea8adec43979 100644 --- a/code/game/turfs/flooring/flooring_natural.dm +++ b/code/game/turfs/flooring/flooring_natural.dm @@ -70,6 +70,9 @@ force_material = /decl/material/solid/ice uid = "floor_ice" +/decl/flooring/ice/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 0.8 : ..() + /decl/flooring/ice/update_turf_icon(turf/floor/target) . = ..() if(istype(target)) diff --git a/code/game/turfs/flooring/flooring_rock.dm b/code/game/turfs/flooring/flooring_rock.dm index 13836a0a7da7..3bf779b61da8 100644 --- a/code/game/turfs/flooring/flooring_rock.dm +++ b/code/game/turfs/flooring/flooring_rock.dm @@ -14,3 +14,6 @@ ASSERT(turf_material?.adjective_name) target.SetName("[turf_material.adjective_name] [name]") target.desc = "An expanse of bare [turf_material.solid_name]." + +/decl/flooring/rock/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 1.5 : ..() diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 881da81bdcce..cccda657fadd 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -46,6 +46,9 @@ return FALSE return ..() +/decl/flooring/snow/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 0.8 : 1.7 + /decl/flooring/permafrost name = "permafrost" desc = "A stretch of frozen soil that hasn't seen a thaw for many seasons." @@ -54,8 +57,16 @@ force_material = /decl/material/solid/ice uid = "floor_permafrost" +/decl/flooring/permafrost/get_vehicle_transit_delay(obj/vehicle/vehicle) + if(holographic) + return vehicle::base_speed + if(vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE) + return 0.8 + return ..() + /decl/flooring/snow/fake name = "holosnow" desc = "Not quite the same as snow on an entertainment terminal, but close." holographic = TRUE uid = "floor_snow_fake" + diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 3982c3dab82d..0f5109932c2f 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -189,3 +189,9 @@ /turf/floor/can_show_coating_footprints(decl/material/contaminant = null) return ..() && get_topmost_flooring()?.can_show_coating_footprints(src, contaminant) + +/turf/floor/proc/get_vehicle_transit_delay(obj/vehicle/vehicle) + var/decl/flooring/terrain = get_topmost_flooring() + if(!istype(vehicle) || QDELETED(vehicle) || !istype(terrain) || vehicle.vehicle_transit_type == vehicle::VEHICLE_GENERIC) + return vehicle::base_speed + return terrain.get_vehicle_transit_delay(vehicle) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dadb2de1b99d..e0952f1222c5 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -713,7 +713,7 @@ var/global/const/ACTION_DANGER_ALL = 2 return TRUE if(!anchored && istype(over, /obj/vehicle/train)) var/obj/vehicle/train/beep = over - if(!beep.load(src)) + if(!beep.load_onto_vehicle(src)) to_chat(user, SPAN_WARNING("You were unable to load \the [src] onto \the [over].")) return TRUE . = ..() diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index 7522827dc86f..49e6274920d1 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -32,7 +32,7 @@ update_icon() /obj/vehicle/bike/user_buckle_mob(mob/living/M, mob/user) - return load(M) + return load_onto_vehicle(M) /obj/vehicle/bike/verb/toggle() set name = "Toggle Engine" @@ -89,11 +89,12 @@ qdel(trail) trail = null -/obj/vehicle/bike/load(var/atom/movable/loading) +/obj/vehicle/bike/load_onto_vehicle(var/atom/movable/loading) + if(!isliving(loading)) + return FALSE var/mob/living/M = loading - if(!istype(M)) return 0 if(M.buckled || M.anchored || M.restrained() || !Adjacent(M) || !M.Adjacent(src)) - return 0 + return FALSE return ..(M) /obj/vehicle/bike/emp_act(var/severity) @@ -124,14 +125,14 @@ /obj/vehicle/bike/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() if(!. && istype(dropping, /atom/movable)) - if(!load(dropping)) + if(!load_onto_vehicle(dropping)) to_chat(user, SPAN_WARNING("You were unable to load \the [dropping] onto \the [src].")) return TRUE /obj/vehicle/bike/attack_hand(var/mob/user) if(user != load) return ..() - unload(load) + unload_from_vehicle(load) to_chat(user, "You unbuckle yourself from \the [src].") return TRUE @@ -139,7 +140,7 @@ if(user != load || !on) return if(user.incapacitated()) - unload(user) + unload_from_vehicle(user) visible_message("\The [user] falls off \the [src]!") return return Move(get_step(src, direction)) diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm index f828d2f4e1c5..4fcb9ee46ce8 100644 --- a/code/modules/vehicles/cargo_train.dm +++ b/code/modules/vehicles/cargo_train.dm @@ -1,18 +1,21 @@ -/obj/vehicle/train/cargo/engine +/obj/vehicle/train/engine name = "cargo train tug" - desc = "A rideable electric car designed for pulling cargo trolleys." + desc = "A ridable electric car designed for pulling cargo trolleys." icon = 'icons/obj/vehicles.dmi' icon_state = "cargo_engine" on = 0 powered = 1 locked = 0 + load_item_visible = 1 load_offset_x = 0 buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 7) charge_use = 1 KILOWATTS active_engines = 1 + var/car_limit = 3 //how many cars an engine can pull before performance degrades - var/obj/item/key/cargo_train/key + var/obj/item/key/key + var/key_type = /obj/item/key/cargo_train /obj/item/key/cargo_train desc = "A small key on a yellow fob reading \"Choo Choo!\"." @@ -24,34 +27,24 @@ icon_state = "train_keys" w_class = ITEM_SIZE_TINY -/obj/vehicle/train/cargo/trolley - name = "cargo train trolley" - icon = 'icons/obj/vehicles.dmi' - icon_state = "cargo_trailer" - anchored = FALSE - passenger_allowed = 0 - locked = 0 - buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 8) - - load_item_visible = 1 - load_offset_x = 0 - load_offset_y = 4 - - //------------------------------------------- // Standard procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/Initialize() +/obj/vehicle/train/engine/Initialize() . = ..() cell = new /obj/item/cell/high(src) - key = new(src) - var/image/I = new(icon = icon, icon_state = "cargo_engine_overlay") + key = new key_type(src) + update_icon() + turn_off() //so engine verbs are correctly set + +/obj/vehicle/train/engine/on_update_icon() + . = ..() + var/image/I = image(icon, "cargo_engine_overlay") I.plane = plane I.layer = layer - overlays += I - turn_off() //so engine verbs are correctly set + set_overlays(I) -/obj/vehicle/train/cargo/engine/Move(var/turf/destination) +/obj/vehicle/train/engine/Move(var/turf/destination) if(on && cell.charge < (charge_use * CELLRATE)) turn_off() update_stats() @@ -67,46 +60,23 @@ return ..() -/obj/vehicle/train/cargo/trolley/attackby(obj/item/used_item, mob/user) - if(open && IS_WIRECUTTER(used_item)) - passenger_allowed = !passenger_allowed - user.visible_message("[user] [passenger_allowed ? "cuts" : "mends"] a cable in [src].","You [passenger_allowed ? "cut" : "mend"] the load limiter cable.") - return TRUE - return ..() - -/obj/vehicle/train/cargo/engine/attackby(obj/item/used_item, mob/user) - if(istype(used_item, /obj/item/key/cargo_train)) +/obj/vehicle/train/engine/attackby(obj/item/used_item, mob/user) + if(istype(used_item, key_type)) if(!key && user.try_unequip(used_item, src)) key = used_item - verbs += /obj/vehicle/train/cargo/engine/verb/remove_key + verbs |= /obj/vehicle/train/engine/verb/remove_key return TRUE return ..() -//cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle/train/cargo/bullet_act(var/obj/item/projectile/Proj) - if(buckled_mob && prob(70)) - buckled_mob.bullet_act(Proj) - return - ..() - -/obj/vehicle/train/cargo/on_update_icon() - if(open) - icon_state = initial(icon_state) + "_open" - else - icon_state = initial(icon_state) - -/obj/vehicle/train/cargo/trolley/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) - return - -/obj/vehicle/train/cargo/engine/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) +/obj/vehicle/train/engine/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) ..() update_stats() -/obj/vehicle/train/cargo/engine/remove_cell(var/mob/living/human/H) +/obj/vehicle/train/engine/remove_cell(var/mob/living/human/H) ..() update_stats() -/obj/vehicle/train/cargo/engine/Bump(atom/Obstacle) +/obj/vehicle/train/engine/Bump(atom/Obstacle) var/obj/machinery/door/D = Obstacle var/mob/living/human/H = load if(istype(D) && istype(H)) @@ -114,52 +84,47 @@ ..() -/obj/vehicle/train/cargo/trolley/Bump(atom/Obstacle) - if(!lead) - return //so people can't knock others over by pushing a trolley around - ..() - //------------------------------------------- // Train procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/turn_on() +/obj/vehicle/train/engine/turn_on() if(!key) return + if(!cell) + return + ..() + update_stats() + if(on) + verbs |= /obj/vehicle/train/engine/verb/stop_engine + verbs -= /obj/vehicle/train/engine/verb/start_engine else - ..() - update_stats() - - verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine - - if(on) - verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine - else - verbs += /obj/vehicle/train/cargo/engine/verb/start_engine + verbs |= /obj/vehicle/train/engine/verb/start_engine + verbs -= /obj/vehicle/train/engine/verb/stop_engine -/obj/vehicle/train/cargo/engine/turn_off() +/obj/vehicle/train/engine/turn_off() ..() + if(!on) + verbs |= /obj/vehicle/train/engine/verb/start_engine + verbs -= /obj/vehicle/train/engine/verb/stop_engine + else + verbs |= /obj/vehicle/train/engine/verb/stop_engine + verbs -= /obj/vehicle/train/engine/verb/start_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine - if(!on) - verbs += /obj/vehicle/train/cargo/engine/verb/start_engine +/obj/vehicle/train/engine/on_update_icon() + if(open) + icon_state = initial(icon_state) + "_open" else - verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine + icon_state = initial(icon_state) -/obj/vehicle/train/cargo/crossed_mob(var/mob/living/victim) +/obj/vehicle/train/engine/crossed_mob(var/mob/living/victim) victim.apply_effects(5, 5) for(var/i = 1 to rand(1,5)) var/obj/item/organ/external/E = pick(victim.get_external_organs()) if(E) victim.apply_damage(rand(5,10), BRUTE, E.organ_tag) -/obj/vehicle/train/cargo/trolley/crossed_mob(var/mob/living/victim) - ..() - attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") - -/obj/vehicle/train/cargo/engine/crossed_mob(var/mob/living/victim) +/obj/vehicle/train/engine/crossed_mob(var/mob/living/victim) ..() if(is_train_head() && ishuman(load)) var/mob/living/human/D = load @@ -170,161 +135,96 @@ else attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") - //------------------------------------------- // Interaction procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/relaymove(mob/user, direction) +/obj/vehicle/train/engine/relaymove(mob/user, direction) if(user != load || user.incapacitated()) - return 0 - + return FALSE if(is_train_head()) if(direction == global.reverse_dir[dir] && tow) - return 0 + return FALSE if(Move(get_step(src, direction))) - return 1 - return 0 - else - return ..() + return TRUE + return FALSE + return ..() -/obj/vehicle/train/cargo/engine/get_examine_strings(mob/user, distance, infix, suffix) +/obj/vehicle/train/engine/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) . += "The power light is [on ? "on" : "off"].\nThere are[key ? "" : " no"] keys in the ignition." . += "The charge meter reads [cell? round(cell.percent(), 0.01) : 0]%" -/obj/vehicle/train/cargo/engine/verb/start_engine() +/obj/vehicle/train/engine/verb/start_engine() set name = "Start engine" - set category = "Object" + set category = "Vehicle" set src in view(0) if(!ishuman(usr)) return if(on) - to_chat(usr, "The engine is already running.") + to_chat(usr, SPAN_WARNING("The engine is already running.")) return turn_on() if (on) - to_chat(usr, "You start [src]'s engine.") + to_chat(usr, SPAN_NOTICE("You start \the [src]'s engine.")) else - if(cell.charge < charge_use) - to_chat(usr, "[src] is out of power.") + if(!cell) + to_chat(usr, SPAN_NOTICE("\The [src] doesn't appear to have a power cell!")) + else if(cell.charge < charge_use) + to_chat(usr, SPAN_NOTICE("\The [src] is out of power.")) else - to_chat(usr, "[src]'s engine won't start.") + to_chat(usr, SPAN_NOTICE("\The [src]'s engine won't start.")) -/obj/vehicle/train/cargo/engine/verb/stop_engine() +/obj/vehicle/train/engine/verb/stop_engine() set name = "Stop engine" - set category = "Object" + set category = "Vehicle" set src in view(0) if(!ishuman(usr)) return if(!on) - to_chat(usr, "The engine is already stopped.") + to_chat(usr, SPAN_WARNING("The engine is already stopped.")) return turn_off() if (!on) - to_chat(usr, "You stop [src]'s engine.") + to_chat(usr, SPAN_NOTICE("You stop [src]'s engine.")) -/obj/vehicle/train/cargo/engine/verb/remove_key() +/obj/vehicle/train/engine/verb/remove_key() set name = "Remove key" - set category = "Object" + set category = "Vehicle" set src in view(0) - if(!ishuman(usr)) - return - - if(!key || (load && load != usr)) + if(!isliving(usr) || !key || (load && load != usr)) return if(on) turn_off() + var/mob/living/user = usr + key.dropInto(get_turf(user)) usr.put_in_hands(key) key = null + verbs -= /obj/vehicle/train/engine/verb/remove_key - verbs -= /obj/vehicle/train/cargo/engine/verb/remove_key - -//------------------------------------------- -// Loading/unloading procs -//------------------------------------------- -/obj/vehicle/train/cargo/trolley/load(var/atom/movable/loading) - if(ismob(loading) && !passenger_allowed) - return 0 - if(!istype(loading,/obj/machinery) && !istype(loading,/obj/structure/closet) && !istype(loading,/obj/structure/largecrate) && !istype(loading,/obj/structure/reagent_dispensers) && !istype(loading,/obj/structure/ore_box) && !ishuman(loading)) - return 0 - - //if there are any items you don't want to be able to interact with, add them to this check - // ~no more shielded, emitter armed death trains - if(istype(loading, /obj/machinery)) - load_object(loading) - else - ..() - - if(load) - return 1 - -/obj/vehicle/train/cargo/engine/load(var/atom/movable/loading) - if(!ishuman(loading)) - return 0 - - return ..() - -//Load the object "inside" the trolley and add an overlay of it. -//This prevents the object from being interacted with until it has -// been unloaded. A dummy object is loaded instead so the loading -// code knows to handle it correctly. -/obj/vehicle/train/cargo/trolley/proc/load_object(var/atom/movable/loading) - if(!isturf(loading.loc)) //To prevent loading things from someone's inventory, which wouldn't get handled properly. - return 0 - if(load || loading.anchored) - return 0 - - var/datum/vehicle_dummy_load/dummy_load = new() - load = dummy_load - - if(!load) - return - dummy_load.actual_load = loading - loading.forceMove(src) - - if(load_item_visible) - loading.pixel_x += load_offset_x - loading.pixel_y += load_offset_y - loading.plane = plane - loading.layer = VEHICLE_LOAD_LAYER - - overlays += loading - - //we can set these back now since we have already cloned the icon into the overlay - loading.pixel_x = initial(loading.pixel_x) - loading.pixel_y = initial(loading.pixel_y) - loading.reset_plane_and_layer() - -/obj/vehicle/train/cargo/trolley/unload(var/mob/user, var/direction) - if(istype(load, /datum/vehicle_dummy_load)) - var/datum/vehicle_dummy_load/dummy_load = load - load = dummy_load.actual_load - dummy_load.actual_load = null - qdel(dummy_load) - overlays.Cut() - ..() +/obj/vehicle/train/engine/load_onto_vehicle(var/atom/movable/loading, var/mob/user) + return istype(loading, /mob/living/human) && ..() //------------------------------------------- // Latching/unlatching procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/latch(obj/vehicle/train/T, mob/user) +/obj/vehicle/train/engine/latch(obj/vehicle/train/T, mob/user) if(!istype(T) || !Adjacent(T)) return 0 //if we are attaching a trolley to an engine we don't care what direction // it is in and it should probably be attached with the engine in the lead - if(istype(T, /obj/vehicle/train/cargo/trolley)) + if(istype(T, /obj/vehicle/train/trolley)) T.attach_to(src, user) else var/T_dir = get_dir(src, T) //figure out where T is wrt src @@ -345,24 +245,40 @@ // more engines increases this limit by car_limit per // engine. //------------------------------------------------------- -/obj/vehicle/train/cargo/engine/update_car(var/train_length, var/active_engines) - src.train_length = train_length - src.active_engines = active_engines +/obj/vehicle/train/engine/update_vehicle_move_delay(atom/prev_loc) + ..() + if(is_train_head() && on) + move_delay = max(move_delay, (-car_limit * active_engines) + train_length - active_engines) //limits base overweight so you can't overspeed trains + move_delay *= (1 / max(1, active_engines)) * 2 //overweight penalty (scaled by the number of engines) - //Update move delay - if(!is_train_head() || !on) - move_delay = initial(move_delay) //so that engines that have been turned off don't lag behind - else - move_delay = max(0, (-car_limit * active_engines) + train_length - active_engines) // limits base overweight so you cant overspeed trains - move_delay *= (1 / max(1, active_engines)) * 2 // overweight penalty (scaled by the number of engines) - move_delay += get_config_value(/decl/config/num/movement_run) // base reference speed - move_delay *= 1.1 // makes cargo trains 10% slower than running when not overweight +/obj/vehicle/train/engine/get_alt_interactions(mob/user) + . = ..() + LAZYADD(., /decl/interaction_handler/train/toggle_ignition) + if(key) + LAZYADD(., /decl/interaction_handler/train/remove_key) -/obj/vehicle/train/cargo/trolley/update_car(var/train_length, var/active_engines) - src.train_length = train_length - src.active_engines = active_engines +/decl/interaction_handler/train + abstract_type = /decl/interaction_handler/train + expected_target_type = /obj/vehicle/train/engine - if(!lead && !tow) - anchored = FALSE +/decl/interaction_handler/train/toggle_ignition + name = "Toggle Ignition" + +/decl/interaction_handler/train/toggle_ignition/invoked(atom/target, mob/user, obj/item/prop) + var/obj/vehicle/train/engine/train = target + if(train.on) + train.stop_engine() else - anchored = TRUE + train.start_engine() + +/decl/interaction_handler/train/remove_key + name = "Remove Key" + +/decl/interaction_handler/train/remove_key/is_possible(atom/target, mob/user, obj/item/prop) + if((. = ..())) + var/obj/vehicle/train/engine/train = target + return train.key + +/decl/interaction_handler/train/remove_key/invoked(atom/target, mob/user, obj/item/prop) + var/obj/vehicle/train/engine/train = target + train.remove_key() diff --git a/code/modules/vehicles/cargo_trolley.dm b/code/modules/vehicles/cargo_trolley.dm new file mode 100644 index 000000000000..f41875d4cd96 --- /dev/null +++ b/code/modules/vehicles/cargo_trolley.dm @@ -0,0 +1,103 @@ +/obj/vehicle/train/trolley + name = "cargo train trolley" + desc = "A large, flat platform made for putting things on." + icon = 'icons/obj/vehicles.dmi' + icon_state = "cargo_trailer" + anchored = FALSE + passenger_allowed = 0 + locked = 0 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 8) + + load_item_visible = 1 + load_offset_x = 0 + load_offset_y = 4 + + var/static/list/can_load_types = list( + /obj/machinery, + /obj/structure/closet, + /obj/structure/largecrate, + /obj/structure/reagent_dispensers, + /obj/structure/ore_box, + /mob/living/human + ) + +/obj/vehicle/train/trolley/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) + return + +//------------------------------------------- +// Loading/unloading procs +//------------------------------------------- +/obj/vehicle/train/trolley/load_onto_vehicle(var/atom/movable/loading, var/mob/user) + if(ismob(loading) && !passenger_allowed) + return 0 + if(!is_type_in_list(loading, can_load_types)) + return 0 + //if there are any items you don't want to be able to interact with, add them to this check + // ~no more shielded, emitter armed death trains + if(istype(loading, /obj/machinery)) + load_object(loading) + else + ..(loading, user) + return !!load + +//Load the object "inside" the trolley and add an overlay of it. +//This prevents the object from being interacted with until it has +// been unloaded. A dummy object is loaded instead so the loading +// code knows to handle it correctly. +/obj/vehicle/train/trolley/proc/load_object(var/atom/movable/loading) + //To prevent loading things from someone's inventory, which wouldn't get handled properly. + if(!isturf(loading.loc) || load || loading.anchored) + return 0 + var/datum/vehicle_dummy_load/dummy_load = new + dummy_load.actual_load = loading + load = dummy_load + loading.forceMove(src) + update_icon() + +/obj/vehicle/train/trolley/unload_from_vehicle(var/mob/user, var/direction) + if(istype(load, /datum/vehicle_dummy_load)) + var/datum/vehicle_dummy_load/dummy_load = load + load = dummy_load.actual_load + dummy_load.actual_load = null + qdel(dummy_load) + update_icon() + ..() + +/obj/vehicle/train/trolley/on_update_icon() + cut_overlays() + var/datum/vehicle_dummy_load/dummy_load = load + if(istype(dummy_load) && dummy_load.actual_load && load_item_visible) + var/atom/movable/loading = dummy_load.actual_load + loading.pixel_x += load_offset_x + loading.pixel_y += load_offset_y + loading.plane = plane + loading.layer = VEHICLE_LOAD_LAYER + add_overlay(loading) + compile_overlays() // We want to reset the pixel values on our load after this. + //we can set these back now since we have already cloned the icon into the overlay + loading.pixel_x = initial(loading.pixel_x) + loading.pixel_y = initial(loading.pixel_y) + loading.layer = initial(loading.layer) + +/obj/vehicle/train/trolley/update_car(var/train_length, var/active_engines) + ..() + anchored = lead || tow + +/obj/vehicle/train/trolley/Bump(atom/Obstacle) + if(!lead) + return //so people can't knock others over by pushing a trolley around + ..() + +/obj/vehicle/train/trolley/attackby(obj/item/used_item, mob/user) + if(open && IS_WIRECUTTER(used_item)) + passenger_allowed = !passenger_allowed + user.visible_message( + SPAN_NOTICE("\The [user] [passenger_allowed ? "cuts" : "mends"] a cable in [src]."), + SPAN_NOTICE("You [passenger_allowed ? "cut" : "mend"] the load limiter cable.") + ) + return TRUE + return ..() + +/obj/vehicle/train/trolley/crossed_mob(var/mob/living/victim) + ..() + attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") diff --git a/code/modules/vehicles/quad_bike.dm b/code/modules/vehicles/quad_bike.dm new file mode 100644 index 000000000000..3d01876bc161 --- /dev/null +++ b/code/modules/vehicles/quad_bike.dm @@ -0,0 +1,198 @@ +/obj/vehicle/train/engine/quadbike //It's a train engine, so it can tow trailers. + name = "electric all terrain vehicle" + desc = "A ridable electric ATV designed for all terrain. Except space." + icon = 'icons/obj/vehicles_64x64.dmi' + icon_state = "quad" + on = 0 + powered = 1 + locked = 0 + load_item_visible = 1 + load_offset_x = 0 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 5) + pixel_x = -16 + base_speed = 0.45 + car_limit = 1 //It gets a trailer. That's about it. + active_engines = 1 + key_type = /obj/item/key/quadbike + paint_color = "#ffffff" + layer = OBJ_LAYER + vehicle_transit_type = VEHICLE_QUADBIKE + + var/frame_state = "quad" //Custom-item proofing! + var/paint_base = 'icons/obj/vehicles_64x64.dmi' + var/custom_frame = FALSE + var/datum/composite_sound/vehicle_engine/soundloop + +/obj/vehicle/train/engine/quadbike/Initialize() + cell = new /obj/item/cell/high(src) + key = new key_type(src) + soundloop = new(list(src), FALSE) + . = ..() + turn_off() + update_icon() + +/obj/vehicle/train/engine/quadbike/built/Initialize() + key = new key_type(src) + . = ..() + turn_off() + +/obj/vehicle/train/engine/quadbike/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/engine/quadbike/Destroy() + QDEL_NULL(soundloop) + return ..() + +/obj/item/key/quadbike + name = "key" + desc = "A keyring with a small steel key, and a blue fob reading \"ZOOM!\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "quad_keys" + w_class = ITEM_SIZE_TINY + +/obj/vehicle/train/engine/quadbike/forceMove(turf/destination) + var/atom/old_loc = loc + if((. = ..())) + update_vehicle_move_delay(old_loc) + handle_vehicle_icon() + +/obj/vehicle/train/engine/quadbike/Move(turf/destination) + var/atom/old_loc = loc + if((. = ..())) + update_vehicle_move_delay(old_loc) + handle_vehicle_icon() + +/obj/vehicle/train/engine/quadbike/update_vehicle_move_delay(atom/prev_loc) + ..() + update_car(train_length, active_engines) + +/obj/vehicle/train/engine/quadbike/proc/handle_vehicle_icon() + switch(dir) //Due to being a Big Boy sprite, it has to have special pixel shifting to look 'normal' when being driven. + if(1) + pixel_y = -6 + if(2) + pixel_y = -6 + if(4) + pixel_y = 0 + if(8) + pixel_y = 0 + +/obj/vehicle/train/engine/quadbike/attackby(obj/item/used_item, mob/user) + if(istype(used_item, /obj/item/multitool) && open) + var/new_paint = input("Please select a paint color.", "Trailer Color", paint_color) as color|null + if(new_paint && !QDELETED(src) && !QDELETED(used_item) && !QDELETED(user) && !user.incapacitated() && user.get_active_held_item() == used_item) + paint_color = new_paint + update_icon() + return TRUE + return ..() + +/obj/vehicle/train/engine/quadbike/on_update_icon() + ..() + cut_overlays() + + if(custom_frame) + var/image/Bodypaint = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_a") + Bodypaint.layer = layer + Bodypaint.color = paint_color + add_overlay(Bodypaint) + + var/image/Overmob = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_overlay") //over mobs + var/image/Overmob_color = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_overlay_a") //over the over mobs, gives the color. + Overmob.layer = layer + 0.2 + Overmob_color.layer = layer + 0.2 + Overmob_color.color = paint_color + add_overlay(Overmob) + add_overlay(Overmob_color) + return + + var/image/Bodypaint = new(icon = paint_base, icon_state = "[frame_state]_a", layer = src.layer) + Bodypaint.color = paint_color + add_overlay(Bodypaint) + + var/image/Overmob = new(icon = paint_base, icon_state = "[frame_state]_overlay", layer = src.layer + 0.2) //over mobs + var/image/Overmob_color = new(icon = paint_base, icon_state = "[frame_state]_overlay_a", layer = src.layer + 0.2) //over the over mobs, gives the color. + Overmob.layer = ABOVE_HUMAN_LAYER + Overmob_color.layer = ABOVE_HUMAN_LAYER + Overmob_color.color = paint_color + + add_overlay(Overmob) + add_overlay(Overmob_color) + +/obj/vehicle/train/engine/quadbike/Bump(atom/Obstacle) + if(!istype(Obstacle, /atom/movable)) + return + var/atom/movable/A = Obstacle + + if(!A.anchored) + var/turf/T = get_step(A, dir) + if(isturf(T)) + A.Move(T) //bump things away when hit + + if(istype(A, /mob/living)) + var/mob/living/M = A + visible_message(SPAN_DANGER("\The [src] knocks over \the [M]!")) + M.apply_effects(2, 2) // Knock people down for a short moment + M.apply_damages(8 / move_delay) // Smaller amount of damage than a tug, since this will always be possible because Quads don't have safeties. + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) // By the power of Bumpers TM, it won't throw them ahead of the quad's path unless it's emagged or the person turns. + take_damage(round(M.mob_size / 2)) + throw_dirs -= dir + throw_dirs -= get_dir(M, src) //Don't throw it AT the quad either. + else + take_damage(round(M.mob_size / 4)) // Less damage if they actually put the point in to emag it. + var/turf/T2 = get_step(A, pick(throw_dirs)) + M.throw_at(T2, 1, 1, src) + if(isliving(load)) + var/mob/living/D = load + to_chat(D, SPAN_DANGER("You hit \the [M]!")) + admin_attack_log(D, M, "Ran over with [src.name]") + +/obj/vehicle/train/engine/quadbike/crossed_mob(mob/living/victim) + . = ..() + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) + throw_dirs -= dir + if(tow) + throw_dirs -= get_dir(victim, tow) //Don't throw it at the trailer either. + var/turf/T = get_step(victim, pick(throw_dirs)) + victim.throw_at(T, 1, 1, src) + +/obj/vehicle/train/engine/quadbike/turn_on() + ..() + if(on) + visible_message(SPAN_NOTICE("\The [src] rumbles to life."), "You hear something rumble deeply.") + soundloop.start() + +/obj/vehicle/train/engine/quadbike/turn_off() + if(on) + visible_message(SPAN_NOTICE("\The [src] putters before turning off."), "You hear something putter slowly.") + soundloop.stop() + ..() + +/obj/vehicle/train/engine/quadbike/snowmobile + name = "snowmobile" + desc = "An electric snowmobile for traversing snow and ice with ease! Other terrain, not so much." + icon = 'icons/obj/vehicles.dmi' + icon_state = "snowmobile" + load_item_visible = 1 + base_speed = 0.6 + car_limit = 0 + key_type = /obj/item/key/snowmobile + frame_state = "snowmobile" + paint_base = 'icons/obj/vehicles.dmi' + pixel_x = 0 + water_delay = 6 + +/obj/item/key/snowmobile + name = "key" + desc = "A keyring with an ice-blue fob reading \"CHILL\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "sno_keys" + +/obj/vehicle/train/engine/quadbike/snowmobile/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/engine/quadbike/snowmobile/handle_vehicle_icon() + return diff --git a/code/modules/vehicles/quad_trailer.dm b/code/modules/vehicles/quad_trailer.dm new file mode 100644 index 000000000000..a730ab54b656 --- /dev/null +++ b/code/modules/vehicles/quad_trailer.dm @@ -0,0 +1,108 @@ +/* + * Trailer bits and bobs. + */ +/obj/vehicle/train/trolley/trailer + name = "all terrain trailer" + icon = 'icons/obj/vehicles_64x64.dmi' + icon_state = "quadtrailer" + anchored = FALSE + passenger_allowed = 1 + buckle_lying = 1 + locked = 0 + load_item_visible = 1 + load_offset_x = 0 + load_offset_y = 13 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 16) + pixel_x = -16 + paint_color = "#ffffff" + var/mob_offset_y = 16 + +/obj/vehicle/train/trolley/trailer/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/trolley/trailer/proc/update_load() + if(load) + var/y_offset = load_offset_y + if(istype(load, /mob/living)) + y_offset = mob_offset_y + load.pixel_x = (initial(load.pixel_x) + 16 + load_offset_x + pixel_x) //Base location for the sprite, plus 16 to center it on the 'base' sprite of the trailer, plus the x shift of the trailer, then shift it by the same pixel_x as the trailer to track it. + load.pixel_y = (initial(load.pixel_y) + y_offset + pixel_y) //Same as the above. + return 1 + return 0 +/obj/vehicle/train/trolley/trailer/Initialize() + . = ..() + update_icon() + +/obj/vehicle/train/trolley/trailer/Move() + var/atom/old_loc = loc + if((. = ..())) + update_trolley_offset(old_loc) + +/obj/vehicle/train/trolley/trailer/forceMove() + var/atom/old_loc = loc + if((. = ..())) + update_trolley_offset(old_loc) + +/obj/vehicle/train/trolley/trailer/proc/update_trolley_offset(var/atom/old_loc) + if(lead) + switch(dir) //Due to being a Big Boy sprite, it has to have special pixel shifting to look 'normal'. + if(1) + default_pixel_y = -10 + default_pixel_x = -16 + if(2) + default_pixel_y = 0 + default_pixel_x = -16 + if(4) + default_pixel_y = 0 + default_pixel_x = -25 + if(8) + default_pixel_y = 0 + default_pixel_x = -5 + else + default_pixel_x = initial(default_pixel_x) + default_pixel_y = initial(default_pixel_y) + reset_offsets(0) + update_load() + +/obj/vehicle/train/trolley/trailer/Bump(atom/Obstacle) + if(!istype(Obstacle, /atom/movable)) + return + + var/atom/movable/A = Obstacle + if(!A.anchored) + var/turf/T = get_step(A, dir) + if(isturf(T)) + A.Move(T) //bump things away when hit + + if(istype(A, /mob/living)) + var/mob/living/M = A + visible_message(SPAN_DANGER("\The [src] knocks over \the [M]!")) + M.apply_effects(1, 1) + M.apply_damages(8 / move_delay) + if(load) + M.apply_damages(4/move_delay) + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) + throw_dirs -= dir + var/turf/T2 = get_step(A, pick(throw_dirs)) + M.throw_at(T2, 1, 1, src) + if(isliving(load)) + var/mob/living/D = load + to_chat(D, SPAN_DANGER("You hit \the [M]!")) + admin_attack_log(D, M, "Ran over with \the [src]") + +/obj/vehicle/train/trolley/trailer/on_update_icon() + ..() + var/image/Bodypaint = new(icon = icon, icon_state = "[initial(icon_state)]_a", layer = src.layer) + Bodypaint.color = paint_color + set_overlays(Bodypaint) + +/obj/vehicle/train/trolley/trailer/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/multitool) && open) + var/new_paint = input("Please select paint color.", "Paint Color", paint_color) as color|null + if(new_paint) + paint_color = new_paint + update_icon() + return + ..() diff --git a/code/modules/vehicles/train.dm b/code/modules/vehicles/train.dm index 0cb43501e1e8..59665c6b326f 100644 --- a/code/modules/vehicles/train.dm +++ b/code/modules/vehicles/train.dm @@ -5,17 +5,18 @@ max_health = 100 fire_dam_coeff = 0.7 brute_dam_coeff = 0.5 + layer = ABOVE_HUMAN_LAYER var/passenger_allowed = 1 - var/active_engines = 0 var/train_length = 0 - var/obj/vehicle/train/lead var/obj/vehicle/train/tow + var/static/list/all_throw_dirs = list(NORTH, SOUTH, EAST, WEST, NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST) + /obj/vehicle/train/user_buckle_mob(mob/living/M, mob/user) - return load(M) + return load_onto_vehicle(M) //------------------------------------------- // Standard procs @@ -95,7 +96,7 @@ return 1 return 0 - unload(user, direction) + unload_from_vehicle(user, direction) to_chat(user, "You climb down from [src].") return 1 @@ -110,7 +111,7 @@ /obj/vehicle/train/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() if(!. && istype(dropping, /atom/movable)) - if(!load(dropping)) + if(!load_onto_vehicle(dropping)) to_chat(user, SPAN_WARNING("You were unable to load \the [dropping] onto \the [src].")) return TRUE @@ -121,9 +122,9 @@ if(user != load && (user in src)) user.forceMove(loc) else if(load) - unload(user) + unload_from_vehicle(user) else if(!load && !user.buckled) - load(user) + load_onto_vehicle(user) return TRUE /obj/vehicle/train/verb/unlatch_v() @@ -235,5 +236,7 @@ T.update_car(train_length, active_engines) T = T.lead -/obj/vehicle/train/proc/update_car(var/train_length, var/active_engines) - return +/obj/vehicle/train/proc/update_car(var/_train_length, var/_active_engines) + SHOULD_CALL_PARENT(TRUE) + train_length = _train_length + active_engines = _active_engines diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 111eb94818e2..6086aa1fab26 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -6,7 +6,7 @@ /obj/vehicle name = "vehicle" icon = 'icons/obj/vehicles.dmi' - layer = ABOVE_HUMAN_LAYER + layer = OBJ_LAYER density = TRUE anchored = TRUE animate_movement=1 @@ -17,6 +17,11 @@ buckle_movable = 1 buckle_lying = 0 + var/const/VEHICLE_GENERIC = 1 + var/const/VEHICLE_QUADBIKE = 2 + var/const/VEHICLE_SNOWMOBILE = 3 + + var/vehicle_transit_type = VEHICLE_GENERIC var/attack_log = null var/on = 0 var/fire_dam_coeff = 1.0 @@ -26,7 +31,13 @@ var/stat = 0 var/emagged = 0 var/powered = 0 //set if vehicle is powered and should use fuel when moving - var/move_delay = 1 //set this to limit the speed of the vehicle + + /// How long a single move takes with this vehicle. + var/move_delay = 1 + /// The base delay of a move with this vehicle, assuming no terrain modifiers. If null, uses default running + var/base_speed + /// Speed when a location is flooded. + var/water_delay = 4 var/obj/item/cell/cell var/charge_use = 200 // W @@ -35,10 +46,31 @@ var/load_item_visible = 1 //set if the loaded item should be overlayed on the vehicle sprite var/load_offset_x = 0 //pixel_x offset for item overlay var/load_offset_y = 0 //pixel_y offset for item overlay - //------------------------------------------- // Standard procs //------------------------------------------- +/obj/vehicle/Initialize(mapload) + update_vehicle_move_delay(null) + base_speed ||= get_config_value(/decl/config/num/movement_run) + . = ..() + +/obj/vehicle/proc/update_vehicle_move_delay(atom/prev_loc) + + var/turf/floor/prev_turf = prev_loc + var/turf/floor/this_turf = loc + if(istype(prev_turf) && istype(this_turf) && this_turf.get_topmost_flooring() == prev_turf.get_topmost_flooring() && this_turf.check_fluid_depth(FLUID_SHALLOW) == prev_turf.check_fluid_depth(FLUID_SHALLOW)) + return // Same speed if terrain type doesn't change + + var/terrain_mod + if(loc?.check_fluid_depth(FLUID_SHALLOW)) + terrain_mod = water_delay + else if(istype(this_turf)) + terrain_mod = this_turf.get_vehicle_transit_delay(src) + + if(isnull(terrain_mod)) + move_delay = base_speed + else + move_delay = base_speed * terrain_mod /obj/vehicle/Move() if(world.time > l_move_time + move_delay) @@ -162,7 +194,7 @@ /obj/vehicle/unbuckle_mob(mob/user) . = ..(user) if(load == .) - unload(.) + unload_from_vehicle(.) //------------------------------------------- // Vehicle procs @@ -207,7 +239,7 @@ var/mob/living/M = load M.apply_effects(5, 5) - unload() + unload_from_vehicle() new /obj/effect/gibspawner/robot(my_turf) new /obj/effect/decal/cleanable/blood/oil(src.loc) @@ -261,7 +293,7 @@ // the vehicle load() definition before // calling this parent proc. //------------------------------------------- -/obj/vehicle/proc/load(var/atom/movable/loading) +/obj/vehicle/proc/load_onto_vehicle(var/atom/movable/loading) //This loads objects onto the vehicle so they can still be interacted with. //Define allowed items for loading in specific vehicle definitions. if(!isturf(loading.loc)) //To prevent loading things from someone's inventory, which wouldn't get handled properly. @@ -293,7 +325,7 @@ return 1 -/obj/vehicle/proc/unload(var/mob/user, var/direction) +/obj/vehicle/proc/unload_from_vehicle(var/mob/user, var/direction) if(!load) return diff --git a/icons/obj/custom_items_vehicle.dmi b/icons/obj/custom_items_vehicle.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a6afb68bab511eff886dc4cefd510a46b62b596b GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRF$egBxE?rgAkBMU3XscK666=m z;PC858jzD!6%tY6l3JWxlvz-cnV-kNP%$Sste~j$`e1P4th#xHghs-Nqcg*% z%$UuUsI9%@*UpBstPD|Cq$YD3UVZ_zmEY6FF+^ixa)Jcw;)WUq1||mv#)MGQET9yF Mr>mdKI;Vst0PaOgjQ{`u literal 0 HcmV?d00001 diff --git a/icons/obj/vehicles_64x64.dmi b/icons/obj/vehicles_64x64.dmi new file mode 100644 index 0000000000000000000000000000000000000000..50cc6457594ae921f7c60c804cfae12eb109b3a4 GIT binary patch literal 19406 zcmbT8XIxX;y6#B=gx-5^0xC#vQbG|y1q7vwfPjQ5RXQY8QIsN}AV>g}UZwW{f~fRf zr1u^=1d`m~+UxAS?!6z*IUgWDX5KO8OqtL8&u2U@FZK1bD9PB!2nYx$?`Yq8KtKRQ z;(y>IApDju`6}i3heCZFLro+S$-uxMC@9Fr#wH~tB`hq=%gcM?#tnXcelIUCI2`Wo z?v89x{v<7Z^9mCuC#R|~i?XuvLmBSdB5VQz0@BjbD=UQ+74KFY?Z26sR7r_jSy>4Q z3F+wQJb(T?F){J;=g+@>{Td%1Z)|Mr?k2Fu@0TDI|4?0B-PF{yu&}_&%F4yX#lgWL zBO^m#FNHszwY4=I4!6I*pOKN#(9qD+(^FPfHa$IUy{9*U9}Vz+p!bkKb!Gf=uG0WR zQ}0_(y=*-l-Mt;%o)HlErGH8KMHMDaqdB&1B3)|)BCGYZ6^M$w0OmhQ5>%=*d<}Ek zqt*B(PtUAtKk0Se)xE7L1%I{peea{Gv zMPT}P`-=B&`;(D`tY3AnUcckMgZ~i>X!>YawmU6OXYjQdmmo(F4xddPb9W*j;32qk zOU=+PeLI*maH@K+sw}l^aO}0qHQjX6w_l#cPM#|}4hEj%U=wmu7PmY&<72OOdI>xt z_Y>x@5g&iqq0U1?LI@01{hvLAjgfw-9KA~k2~|_F3bJI&hRvXP(WB<^Gw( z(Cx~C+x+5h8-i{@%;ENL8li$0&YF18tHs<42u9v{GM;nMqcpjA04z-8bmfiZpl?Ok z<>W-b3(B@|xEiCst&Ik(kF(}I{Bkp6I6SQ;nw^H`fRTanK7E#VEk%L?bqeSf&f>`V z)+e~gXZH9-3kKU;RUWAX(0KcWt{x=&ORWFqn9s7YzTLNSW1hL=VPr)9C@M<`l{aZS5G&N6v^XmV;r^Rf&(z`vK@`KjDil1sR(~JHN8xZ zGh!MPLekoR(7fPlU&O)JC|MKT*GW$&kC<6&a}cSaLZhXeR}vqhM94GUgg-wD-BTex zW#My8_b^~+g#~fv0QQIa1z z5NxHEu#C`6v>B_PC#IuMb(x6h_w-Bx!52ST;aDIM3vR06ks-kz;tuc?)&uP&Rk{g% z3f}|jLBfF!vb1FKj3v+4=z(83~bs$n8X5FTO5MzFKCB2WD`nx{Eue^zglGIaze7$_ZPy41hNv-$O z^t*c(UoSI&DbtWAlShbvz(TYc1{w1Z`p5`U$X;?q4}h-Hod1U8QtApFPkV1kby_V4 zyaLdV$m`O&CK~1k+@KZd>Ag4SSrW*QBEym7tuwb-M_6jvvN=5KhWtXIKhVcU!di$R zrjY8a+8g_|yg!+?x!`#S&9p(%5~^M(3u_a(NAc-0j7XU=rEP8shr)jCNQunH3xvVm z(4WpN1>34+!$#4+UVkxr3tuifODCHwp(2!b==01COA%<*vF|0H^!AydqhFcsgZLum zR}r&W{ZHEoRh;{kuiSCtV!|AZSc#XE%A}k(_?~_~X+2PQ6ZSLFgsb4etEoT?*S-~9 z@mNk)L9Kaf)m9(eyWh!(y3D>Qs%<3Yq-lT#KN^M(wymlj+hMbcdX4z3F~2+1>Mpp7 zd2t?Cw87$VFbXs&WbE2hBoY`X;KhjO-3UEwXuF;u%WKQ~`Cm;~AE zQftn=Q_h@r{wWERs6957hKiJVeN%}c(cKy}#(0`Su8)y!k%tJx6VJtc3nC>Lon1VaCSQR z4d&KNsqQt%moPs&E%yhYnV0O;0}u_&({H2wlQb5jHZB|-xbZ~+eH!>f4P}@C&#>3% z_-w2r{3U_kxoiyN*wfc&>AsdeS#!^t+!CC}`XlOb=o$inWiSJk-QaOBx=Mo7Q|%6o zXFLP)U}+fp-6o8dg?h-)l2NzhEVm3Fp!jOjfC6#kXf{N;ca2|P<=Yr=6jc^~ z!?bEQ?e$wd;#QEFCti|GTyp}tg_ z(#H)UW2LZ!9@frtvnPjHOoT1l>*V)O>NbkMGY@?G893@6_N%ZTs5ALG4PWb%JVb76g}9TcND&2nEd&=3 zu^phwoT!|>*sitz47qupI?@hWbE7Dj#|!sYvv91)()mVBowmu471_cq9-hy8c^gkI z_@rC9p#yIB(kDyh1SJJ{g)q$7uu5z;nE|Esd)0jHNB^o`Vm(_9E0GiuuOv|>H!V)l zTq_*IS+M%CAe=Tt(c3PB(sDBL#qkM;dwYl$>;P-6>$Zfm(f-wURhz@K-ciTB8U03e z;irz$>@hE6GxsGB=5+b|YpjcqK#Z9zIhg`3wR+~6G2~Pmv&fI4xES5>(Yqg{LPf*$ z?bY&TKV%aAC@?S;#Cj;z1Dp)+rE=y(*-{dzP`4}+1{2H2-oRhFt^Q~5@HcG$=wIzn z|C-IpiNc=yTT0);H6C5peU!XmO*HvM@e|aS=t{YlOZX7B*S4KvBcUe~pM;;7?@ z-neU3S6CRiSL;lAVfEid2!!mfFMI+P0K1kiAqT{gbNAzkW*Z0Yp`RJ!m2|XJ6`rfR zp+v%Bm>^dvrlHhl|N1Y4Ao#s~xIM*AgPe6btmOUtQvaq!l#A^bv#KslJVrokkS;XH zdIiCq9J&q`16K<{8Kut{zkLtR-aS=NQv!dn=JVQh) z65s3!oxG8vsHyj6OLkNjtsqyM(SohZh4CT_Pgk}%NooTF=@)&LbpY$KB;^21JSh1= z3Me^enh)?k13qyZNQpLkMA2;tO9$V5Z=p{VlpZ)no4dR@bGI2>08fYn5|5W^7c=KD3vJ`-llaZa;tWVgfV}X5{m)xc+ z(gYYoVKIiEI4ag@_&)FZ64=fb$zDgLthO311na_i#Dl$uOB3Di20z4 z)?ut*<5z-YnG?5!d4fKvrmf61NP`~# zuiu!Ss(a=pb=q_ng&kpwHFXC2->A;***bKS@F;>feE z@pz@amII)u97Yh`0w&5Q=+zhKEb)n~7`L#}BE5LzgHSu{GlR_i%@nfE;(>y9+^!mS zuUhfD`~Pn-ewtxA(!yKm{A zclYYE-J`&0%fD$Pp|bg5G?sN!OO?Tda_@UNQ4z=VENqQgwZ*My^my{|;;;C2Zgm?HPpyCi>QcG zSJ=KKi(Yd}At<^~rRpru*QOdaj8=b`!w0aXp7mwUzPuwUM|?Z4y0bi_ElqX%6I}QX z9jGo*6sgWWcnbw*X1}5OKVSrjCI&4^tx8)pYsk`h7*S*lprmIs|M2{Jz`qeYlz!{2!rPWDU^ z;8t_ak&5jsgP}?wZVh4p1w+t481hw4sV9>bkG0-9kd& zcgvB^9?6v{Y%&%gb2)JFV~{(FYAN@l;3-cgAKTB(>l&(!p9XH%vW$aW%j7P^Av6_9G5@eL0 zm9luZF~yr@rC`wIXOyKGXR2v%(1AgR;GoZ!)QVJ|cT7~_o`)8#Z1;@Pdj`vk3bY^@ zwQhI19Wd)j9|bXu6G76egCW!yJd#xEreoLbXo^b0A2^Sp2=vdw_q4pP!!Q1UB?NMS zdDfu^#ujsILr^_nhRvNXJe?W0RO{o5GJXgTZT3N%Mvp3T9`;(oo*^mM`^2rce8Xqk zaQb0O9bn$Lmd#_eT%#{2V!iMIV!(`)Nix&WyQ|o!9WiHZfU|mqPp;`ZzBu#B>))y} zvhTG7vSK>kW`79jjW~XIZQ?@+d-iN~E(}Wc`aUk`j)5|Um44ysyLZ_ij(JzR2<_A! z10Q@N{zP5HbLx8E%CEl9$W6IfTd=4R`cbI1kxZq6UOeW?SsJ}OuW5=P2sIcLhJT2g|E%mQqW6L05h{M86g)y?2T7a;7ufQX@<5mJpXIyFass{ur}QmkF@tL#*!__H+H}H~KLGSH+Y(hUJ2vjr5h_ zI;7Ub3TQ^)NV+`F-%cGO>IhN5i@jgvMct%JBo4*)@DNKzCvRe|?_$h{#b|${=nA+e z+IO=Be&|+`?@RcRb98c;%rd!~%JKD6UEy3t*|*IpDuAgp^K|2JZS;bIB+B&M17XfL zL2DTG$)P?oz5?O6E%M|DlrvUW{-K}NP{sZ;_PDnMe_-LKiI+U4-*whk`TU*ddF!JJ ze=VBpA9MhV6n+B|zmcpjyS}Tnh5gieqX#d)7@a5X)xM=dMaN{=HoT6A9h2c6p*2*# zCcZRr)=m_SP2FVvUsxx2`awzRd&9=#KW1ECYeB4PYT&UKB9W9+eD`@7aC`9MK!sw& z;?(tN*u(et!q>R-aO#=Gzg$uQN4qQz*jN&{Fs;!Rb#H~ zO5(_uMD?C`YSPhI)N_DZ0(ZLClP1RviCRanP3TIq)5Sjz)cvh{<&{KMrH2{t=A`M* zZwYCkask_Dsn5h0#1dkl-entJ#;>G-~~Fs0-o@ul#d3$1)C z?)xg8ZNjxE?a=F)yK*3g^#O)^)pso_)_zARe-{aDHJS)ylCG6cyAPW1%s!3i@QW9>n zw1{+{SJGs@A7ZmfPST=I8b9*o-fm957alSyDB(5MQmcnc49Qc212j0p4z1p|i7{&e z(XYV!-GtKKhAv9AT%Yn7{6}eVE7?3 zSIQxj_n}r9I5KsfxGo;ElaIIv7Tn@H&Jq zwSk7Je;gF3TWiq?@bu)g5mL+w_vdhJ-8iy-_XEN9#l?JtyLJewixat8#HN z_4s;44nl@TvIMYt#h@sRkb11S5pLepu)z7_e*ex6;ClrWEZM$tp%l4iF9u&y1k?gJUQr_ zhMu0S`U-nbh==yEb!Xfeyq=lTS1$ng_2j^evGOseKxX){h#YwgmJ3th7D~;57t&W! znC?@t84ad8*+-MohRMd)H=^-Y3}@wk0GFHeKY$A=^IyPK{@fAW$~K38zeet@L6>tr zgiw*vq1rLZ#?Q;9T9|6hi&ZUWukySqj5=K$2O7uj>_QHOc!0m*e*@Sj?`z0&Z%Km0 zgT-|dHS{`ax4`NMjiP8jWd9j@=kw;a?rGbHnVU)@3-d_g>H+tK2<59I4z(z}Aw;K6 zt*yHC3+#O69@liL(;Jrq>9{`VrnI6GPu0)FdFieUp_zd_YWrPEJ{~CKV%H$ebvZqw^C8N0Ed`^hPF!p(olxJ z!St|zUlY!4Pl;g1e@z=DKseRNX6*6rA)Ka6@3e%cI%>3Y?kXDc5y@+*ap$7oBO}a#E=-EQ)E z3V9D9oQsF73iRreg=MlA5og7Byo(COO+rF}E>%2GWAMvYjPD&9_VQPFN zMVM4-U<6s*SAKyFn!fWG0@eI^_1$yKjax80>v`X*KCZ>zej7=C9u#T_D08IX;xg{R zL*Le=V|4G!NV`Ts3zuQ|k8Y6$@Ph=KOP5IsU;;e}e~3y=hDT~om9!{=YS!_t(vwQ8 zQW~C9CCmPBA0E?!iFM+LF-1l{o=Pdkgrs?l5=yfvXwQw~vYnUB*9w-L2&V8up{CYp z3I8vqft>M}2D=rg8EO(KoSc}f;E|=dW`_JvRNJ5~QvXQw|0Olp*wZhZx}tj5i+&R? zfsI1Ge932Qyq3y2$nvWmdz`tNEtb=V+`cSu5x^+KWA$t$@T8`)^6;L3V%Sf2JNp~L zOIuL$z(BmWpSbC^yG8amSt%M)haFgBc{ca@*3u%+6SXULA$-D1N^d{?Uzk>PZ0rT* z>)e0zf?b}U_Do*ew7){VCQjamP?@>O8JVaF<7!@`rUQ_VYUX)bu;~DXp?ut*>*GLCgVcmrGrCXXi>} zR;N#0n9sjs`1(=RQ$6+#6QPSUT-7g>FI-z=c4beWkLTw!6SN`ZV3~kVHCN7w7JL#g z_eJHmq$?+GF!HuDkl>ylFh?#&J#u$yo(nr6cW;dujsR_&gc9Ir=hamGWY)m*I7w%f zZ7F$5Ze5h>Za)w#vsA6W`=y1~4!`&n$35HVf3U|-GUsN5{F{KieL>ZPCoO1D0cskYc0`>lSWZnvz7Z>zs853)~Nuw=xCQ*K@ht6&)x>;7#s z@PQPCj5Vq@?!2=7$$1K^`y_Sw^fvd|jA=)?u{DAI0Rc$=roFj)2=lU_$|}{b_Z_(H z8Y{UL40`kR>6zM9+{*U$wkQkFUihAGWgkq=Ba3+@y2ia%-WjfGqF~9H$Ekpp>8L)>Z%$qgE(c z2$EVQLY7>-47UUZk*iyho@N(TA^rzbyDeB1{tyq~!h4GNX3}v1dtu1;mA$=g_&k41 zjWUN1yq)T`&v|X|@7)s=_w4Q1XuD;Q5z_f2F>b<-|{a_IskQp=g34qx@4?4X=yP#V>X<9i;3%X^)zLHdM(>5(UrNUDA?Kd*zCLlcgR(+uq3i!N>Ng^qXIW@ z0o3;wWp_V)ow2=A?uYBDnWgI|a40pGCJ@ZE=r%f2(_F)k<;CtE%)h5r2MMUK@g%Bz zumZBV%ci|HgXQ7B6bp+)(leU9X#}7fPlv8Sxr$9bD z*3>ur=DuHOSSvFX(xhRYw6K~-*4E2n1L`a+1HFPf>_2w0Z(hV_4NF}7S&i;JMX|PE z_s(LtLb!vaA!&VqnP-iML_Mp@gng?-AmVf7Od@KjxlQ~!A3@MZMo0tpX~+j)8;mx@ zzGbnwRZmNk5p1Xo zY!oZXK26tw`f+S}o9SZH%Q=9U&tH4DWn3tmZeJA7BP(ykf(yc|t-cY^#}<3sAO@6s zb#}HfhtT-Ug?hNsln_p~<*LrTjn%CdeAaH+Z_qi6pPzou18B|o= zJ0hJML`ON@m1!F1JYQ{dsRMOb#;6B+03OM-X-0Hm5-)zQ+6=Z95i-(0V>?w#^pAi< zBe?CHg4(mglRKCEHZP=kHe@4gjP~A%Fq_!ybj8+$7QOY&uUFDF2-kr?bK1V6adNosPcCCSw+LW!B-29*=Veh4I1{y z7o2GiMXtW$w1Y$|-DW5B+g-@KqDgs?Njlxe{Y^}-9|_gf)+!}OQC>(RPgvZ`3s`30 zd)X6V0B))+qYfgxg#t};jVFRS$5Gzqc2c8pjdPoW^%pD=hxET?o2Oo27IK)Yct@DZ z-~od4w%t6NVrUTkAT}J857&vAiC?`8O5;!s5Hn=3e;o z%u~-Jl-xh|9Uz-7xIn#@6@jXZObtE89pKjmuI4i*nN8U=hLI!`^wy<9ZJ z*PGERtI#u&yLVS!*w1L62m4#13LLN1E7%`!*y=HO>I^_hHuYEf6(lT-LUn{Vi ze{p~xUf;A~joliMv1F~jW_GH9ui1O1ul!Ezf0H`_hR!`#&A*scJ)EUBf+;wk9F-7wOgSGF>|q(7C7gQV^l3n>2=NA_LF;4krn$I9{u);JGoiY z%X!SLcq<}(RQWa=-U3Kmq>c&iygw&$NDb+L*PCbsg&oFyKi>0bnT4oCr5H@B)?6}> zf{Sd-HhubzSKmZ6Vm>o>+Xlp7C!1ahVao5glx?1^9!?u7(@6b^y;bD^|}U7K>4 zO*#-&sl{aGesuBZBJ!D!zlvC}gu99ui}+{6cLqE5vX|NQxGpI4Ms|enI|1`bows%*_-cJybj+ht%Nqqd)KzJovo98 zbXl&Y{IDx4r9<4`U&Fes&A@a|hn<|NkcL6INuy`#t@gzNf8EimQ5zhfg=avkHIN2^>?a!IZ+iY~y~ zu3XG&b3mK83j`MRlPH%g;gL=#?}0x)KT?6uj|^T|O1snEsoc3P^rE$BOuyFiRsZ!v zU$&r$KS2|*Y+P^&?swRH?)!kMu*Tt4_yAG6ZPnl}r&kjexbqnYm!B&!4xTTHkp<7v z5<>k?1`y_=-uIGiZ+wmLS*BLg&Q%u~{6WLp__--5d};fWP_c)M*>XH9<=f|j&p8;&TJ-QK! zu977yT5fNW+^Y8~v68z*@5x{SQ*NI>^Nz=g`eY9%ZfV=Hqj-;+t@9BOleVtl;|Bf1 zfhluWR=xKh{ra{nJ`sL1PUm()Mg=64alf<*M;XYTcs5!7U22eH{3z;AiUm`jA5wgz zikYjf62w^lc<0#qnLDIHv!$8?V_dVAOYe1*2(Z1rLt}$nyGp%QmUY!cX0Fc;85~UV z$f;F%q^m!u{eWwn|9ZT`jeY${R06vT(k$05p8VMKNobCa3}6gj>*_$C02srUwjKod z2ssv5DJttjSjE?=@t$YtqoYSX1Jo_QbF2=I!Ut4#sP!Y|x41zRDbc1c;W-vp2$aR~ z_Q=cekSL80O5z{)ukr{w0&gG6b;FZ?2v(-Wm*`hysxy3$OeQ!)qJn*SokrcY6Tx^F z1)$!!F~)QhKBR$h0Y%M@Y@=oF!xF*0htAdU!|;**u3z>{<$+r{c>Z9%UpXlh zWA~zc)wSm$AGej?Lh1)(%9^=_LWRH5#~UelBzUCsErLE#-)-=$W<1RDGG7z-Cto9a zmX8x&cLOHtJ-vtC*2he4;NQ@v>_=j4hbBNz%%to-8ywFhKGbm<+KfPeo=-z@z4ccs zj^#C{u8*;K6%tEcaKLx@w`^LKbX_6~A#DoocV7G){dV;@j0z~vYFlt4dW$*mtbt83 zmw&^B!VJvf?(%0x*2qwFRz})-DPhEVX~YuxOtY0r>#eUfQjz1{jE5t6(MtB-vlFP` zYh~*|wrGOQXUbDIAU1Kq9vx#2&F7*RsHyv?UNO6SKaZk7=KWj-TN8(9K==XRKx!bB z)RtQR7Uy1keeo#6s@AP>W}~1tE9|W3zOp$BSP%Tgnie(3XYMVzF{?Kw@z}fCSuL|+ z6(I%n0C10vMqn`C=VTLy&@56e0?uiYDUATa6xgI=6h63fVXO>ONK3GeNZPzYRt4M-K%EsqiK>noXE9hXU z%sLn(h}nv*EQ;2`B*%+mWIRh+a;X5ly9ohpNLKafd0H-1mhp0E=DF(3Ly+-34exATq=Xg+k9M8L@yH;1`)%B>@$brt&c4`azwl8DcZ zLy5P-IZ=E^tahi)e%AbiX|lnI27^Du{di=;qCbp4{94c!=+*b+N+#jVLuD030LAWj2YY6 zwUu;awN-c9O&CF;=)NRY`_><%`qa_xH^{0;8}9Bc&@@E9ZA7bDW7qJn*RJJu>-F`q zMYnF&JQz#=CJ=el#x8Ct{YZSS9l#+$MA$7M1PfGmH~cxr?*3}rdS6YHrlcuXOlf*m znU!^530UD6QH^AJ=8vYq4CH!rZK+Qlj-#ocO}@&~z(*)4BTI z5+QHi2+PvU%-g)QcHLHpfZ=6CowVH`ym&pljGcN|tA=jx-W1T~7&w#D&)-CM=+@(&`FvIko>dt+l zFo}Mjpdi+i)`AS&;(bt z_dA>2z25MHa9LIMlZHQcrR+~y`y`1}6@xYR=99o3Y9;~E<=S$Stg`3rDqUfl#P!q9 zWT$!nl5NM1Ewiv4iJ#k0`87v!h1D`O`}Q-mZG!o0Aee%S#iESOiR#+EK|IkIrQzSf zXTi{|%AIoSil}h9idrC$h8+x)aJvOvg8Y={7zY6~xIbgqt)RUwls`1y~(cfpm zE)x{*&{CYz>c0Fjp`x6XSe(K#qEVfC2_4O@dCe{Z$`IwF`nlgW`$@8*_-XNgVvW?h zdMwS9D6nRCUC<{oVc<`7iPYr8#DKEU9XHX>BLu0}I`_p0r+OBT-N!koroK})NRr*y zcf{3|W{8WcBOn^fVGkp_L~~RV6%Jd4p^YXp?78Dr4(hk2Uw0 z3b^;C7c3OA;B7}kfyaRl;#=D6?nYIzj<2q^m!Mhqk@`jswf(5bb=P)->%GH|oV-== zdDlCw>lEXv44*oeIkuj2PP=rqE4#w!2Av#?VNV}W<)0AU!(7zS-QKB~jm(al8Fl*Q zUcdD%(0-MTwd{rc3vPSlT(yU0XoPef9Y`? zojBh&iOi&+I9%K-ZTWz}5dXfbtWIIdD*wHDEs4^z6?};3Pby?XK_%y!RU$;I<}#e@ z9Gd6-sV%^-iMRe*DRDs#UuCyOri5`}grt`Xh#Lh#I|tl?e4vtqth1=qqsE&kL$1?H z8UuN6>^laan^z(Xnj;HJd5BlA*!I923XVH|5%0fi6w4KoPZ1gMWnMKQRi&nw^)=zc zGkUxrwN7q?RN27;CnPO~Qp&pMA`JY!z+Lqu*G&Hk@Pp^SSmH`s(OYusB zx#!ztl;bRq)=jYE+9Nw_;Yujtb8afs3OtyWSMZ`T7>^S_$E-sT)Gv0jD7J(YIaK6W zQCxbHvxG{m|MDATf5RD%A%3n^-CEYwK&xzYk#ednqHey`t|uGz(WvN6gcN#isXHIl12wP&_&owKW%3hp6Bl=(;T zZZiel%y!Cmze#e+VT~-8>FvP(#`_mRHe`(9BLN^{7nCl?yHw+(6uV{D<-fsO&(tvh^GC_M zHGhy|rpmzgjJE27WF=)eQ!DGQUu3Cx1bE&G2iOXU2Uxpz@Dit6-}9#|$!05!Cgwxd zKk|B*&TUY5HQct{oX|}qj_Ii}B@OdaNygXn&QsIW1Ennu)O?weH6Z%i6-4LL+v7zP z`c`>t;veP!FLI_DXscfwM)y(Lt%!38H2Nof<@0M1dEXH){sSc zZwkf@awE`f`a^l?X-i^tQXQ=#_O9PglsL#bpYDDC33}h*_2MnLN~$i+tP^mf21?zP zWTu}B){AQ`u!_vEQv+1IJbtv_koLGIoh46hw9GiP!k_9faS*aj={&%SsOFf!m@RQp z#;y)E(_#0$^E05^@>bK=o03buMlO7=CZS9gZbb&%S5Hl5pCY+d_oWY?dpc7~vqDD} zkjC9Vl!QWDKE45+$*&GN_2UqE7p7@SdCH_H^~E5)yqe;^E*t>kPqTZI$LB>ioLW&O zUv-+eoN-i&Rp=wdp&?=y*`qNB8_?r4E8%UAgrJj%-6>g=?ocp-@f=LX3h`#U_wV|o z;Z?<93s3fb?YXSR@bSP6;=@d&e{v-1v@Yc9f*~X(&Nxa{O83ERUCs-cTpp@vhFd`b z`Hm+8eE^?(c-v-MoVnX<|OHBZ2%9De_7^_2HD>9(JtQ_BhW46OFPsHxX!U2+0- zZ)g>t+fOx)dR}u6I;y5H4Hb3@OKP3oCGo2^;Vb05;1cCJ-cxSqlcEFtMbS;Aa1-vG z&|H8hsks>nX@t2({zT+z+b8W7UTT*I7>;H^a{fm{hBN8vrt>5@XtjvNE32N%=XrCjLDzOD& z)u6=Tlc$gb0AlE#S~~gOq(PxXx7{`0hSYM3GQ-F`;<7@Ty42<47X<7}rq&Z(h2BvZ z@|C#bunZ$2mS6bzPodFO>ngHhb&5>H0d9lMZ7_ZT|ZN1-4mB%QpW4$=r(}x)tCQG$5sRFQy$wD&NF@q7)G99{9Smh#>>B6ixte7&@vh#a|^$SUc&?s z`*!)Bte}OuLy5(&?euws3_!dvL!{ycbeeC5&L$4f4HL8%s=JJI7^zXjZR-L)LMdzD z-3)6@<_7x%XvBs zXZLQXYvP14nuIwnk&}e!+U*6#<`ZO^+Ao0GkfA$zTgx^0Y=AZXd3OU#8DOn&+;wY0 ztEn9y;ajucT$jc($v0fWy*j^Y;&hI3q5U7;X>wnuwqWYvyeo2gn5Gs~NNQli7kDh5 z@FVP|vVH&shs75mGyPc?z5$38=I|WfcqTg9VxzaBuxGpJ!qN3z;k~ht^TEILu8Du> z7F!(Lmgj2Un*GNmHRYDL@0Dvc-9JQxuyr9!vu}UeV;uE!Lk7o-AmsJ#7@B+i6L}Ew zjh(Je?p9&HRomrqBZtOU^NByz1Nh=OzwiomsJu~ZRPP)UQ88x)_DJ8UnEAjpnjR+a z{g7ra;d9QkmX#fA{{}I}Ogk>*u4*njK7;hW1o8%-DbTw(*c|(e@WR~Q=o9?hp77J3 zJ8gWR{o72toFj>?x*}I{Cl1?wPJNOO?3rIp5a3R4=hsn)){wh(U9T-m>?@GqvZF0S zrs3$(UBRhL2=#hhP%-*(bjVmqixknmsq8xRTT5+-E}C_0S75MebCndnX1fwTo8!U~ zSOVV?E^K-b<;in#vKCrL>fkDfOfvW*VR!Iz-({^WLA(#W&sAFhp`Kr_*+vIa)M~aY zuZtq3OELSW;uJ|An*^*)ApA?wL0oBa?sTc9#(W2@vVK4(cNfAXV*E3d%0yS@EY z^J8TteIS$CWArwDoj#jTDffL?JzqR@u0F()m@9vFg)fbeL5;H|h2MV3lR*p4SmXAI>>rFoBuYyB~RTDG=4?@ znf-G>6O*azK6-hx3g>wNQojbw5W;MtMa96ejN-9YH`YuFA0V9=%_CV^Ho~(IE>&Vsa#lKSm0!udDfd+z+s1%-CT7JSt;$%%|7vMZz{^I2uqg>)+0e|Cjzm5ughXFmI!8Gm4^R|K=TRFDL;;-?=B;vZ&j{s~Be z75@YzW#$Hg#$^9%i)ne#RgAppfKOO;W<|UA6x=GXtKvXWum3&4;@aiOPxRlH+<#V< z0?2$d=eDWP8khRp(iZs2ym++pw2`MoC;}PFa zJ&o*+BKEUygh8xQyItHb&o;kjA*(sQao*)W}$(6t7uXK%G6B`yov-qhiZ?RlA z6jG3Po#--oRjStPW=?namwMCPt;ia6-T>M4B@5TC-Fwn1YLx+|8tYh0EuLo{}$2 zNxw6AKu?n@k~ai;iT^UlCHRakq-)0SBlg>i5|#2+Pqu#+n@gfzDWtA`iz}}1;Nf1i zF#lN8lS-gnt(4cqPk*>b>#mcC6~UM8r7&UMDWoocgN*epJbExu1fkp*zR#9+1XW`$ z=B6hFkhxUmfS)1-bSI_r4k%9fS%`Lnij=C&-k3-PJ;3WKUdt#m1dbhl&ofFk+wa{k zCqyKmV|AkDNmxn|l9?lv=!$qQOcGpAZmm!K#$y=UFF{QD>FQYN9mpcu(L|L!PTMmn zyuMF$!a`x!mR>O+Wjydykn)L+!}m{vr}z&EH?Lxpsc>;|Es?rcP5n}XOE;kX%w_;M zUMC@aAEwcwR}x+O5ibrcnvMTTE8SCjeJb~F0gk(6qYR)gd)*DM;E5%ORheFl zJdP+hTB3+h*oe0P5US8GL$X$d{F-30PI10#_+!}05WjhgPFSv@9&Sl4!tU>ku8?#O z$CgnjDPaygZK|#j6&e5LvnzsoTzINkG18d+m-2F_!k3+H7h>OgSmPO0v;) zulqf5&_BwhjC&i^ zj#;o*tm~&|#1_$UBao}88^4PilBH^dS=w6&b!w+DnijZnbS~jEamyyqUG^nwYYT~v z1CrST19e4K=aMXJc}j)BXj96_JZQ&Yy++Y7D1wI?N~qV$s4Eu8d|qNDTTpWZpNo?= zqj~|<Cr*e)zq$&Q!L+H?o({#2)7Jf>;lV)jw+_xGKaeCMiV004akYtF|LV75KK! z2RJi4|ETXHL?K5+T?~MKT~f0IDG(`=qJ#S>;Z_aUx&z&$TLz2Xe#ecc&wL0W{b^xC zkKtn_u;U+US4reQY1hB&kNk>R7jRrm9`$7) z{CQyB=5lzqrC9M{e^D|-_Cb|mKsES75!v2P{PS)>46K*V$Y;DWqWSo$!)U_IYW-G` z*q^r&TITo;t2Pl3?u&4|l_6b3_9Zz>Kf>oT&u7HCfY5QCT^@p)4DVeXyFM;K$xB<{Zd_=1;h_NeMp{p(MnY{7 z+SGsUtJ99YeXCFf>B9XISc2$vx53%_oPDs-O$Z!v6WeR%{84Xo1!8bkF6*x!sFm|l znsY%~V3~FL-N>0fuEr{a zauMo)kL}-Y0N(sDPm7nSEcR?hq4|F1i3kU6r{%^t-HWMG+-{B4{K#MGo7eg2!Fdx7 zg>4UZYW>jOSNuB2T@!ZoFYy&4fR0(!#icE;e0n%P23f>W5nlw7K(yXrW4RpB2MclU zCnme$x~$8Yv1`!ps=#*%tnN(1tN@WPk)=5ne>>!KIRoRsFBJaPr@I3n zsncB3(W3?$eDe)Xvy@Z;|D^+SxMLnR^MQ;37aV5jK$Nn|J(&H)^ko1wpGZZiXl)AL zsm$t5JFMn=jK6i48H~v+Az4yL2}olYciReiY<*(~-N~ajz z&_us(iKxrE!#fl0UZw8sFqIpZ>Q;*h*P3Ztzhg3m=0|$PS;>!WCeyO_eeWvwpZ>i6 zKF|N3^E}_@`+c0KWkzV20ZzTi3Ie^_EZ~E5>m4=zKeID@s>Anp2isp+9sKcD_lG@n z45`?mQJwjB$}1*qnWZiC1lje1^cD~Dk%2zy1~aP&kCCQT* zen&!A=bE*WAME{0iDmYU&t6uP&}5;ksALh^1ffyKWm)mKWpalIeE(R3ekg4z$)^<3Lr5HJ zqHpanFNXKJM{3_7pHkE~JtG$ipbkWWOxq5yX*vtL19bi^qHUyg-p|trJ)e0159mS=0GQ2OaBhbN}%}#a9l_K8G46j z&dxC6H}@C3a@9QG5z*Yib$eTO;RV#n{0Md~8JUTlZh+Wv*J<34d+B$(=!DJ?1ou7- zLT06J_DeCs?a~lDvecnnc008EKk*-#yq-whckmw4G;CD%*qp7Sr8T0 zF}NYNalyd0g6{+CD5RQ;muP|)fBGJH(dO!Oz27--z9Byg-L!nAN&db3vf&nFNx80GdMf?jt?cESfAb9~3%42`nu?%+>qefh0<8Jzf znv}xTn@(Ef!)j71in!U7VRm-^=#%)SD)O*+jv&k4@|oL19YI$7xsl+a^?Xa9`P$Lt zs9Xt3k*;T_wTfKfT=i+=<5Zz$<^5Kb`x>}%?+h*bY+q-9y8f4ol1*Vf+b*=s@dwOf zZGtRTJ?pbQmRI@^(av7Kq!#dj!-;!;NsxvtU#adG3OQbo# zPm}<_MlF~%%*@Zz|B%+n!D`Pfb6G-`rEp`)SDw9NY=+QVb0eOCnc&1MBYzPv=@;nG zf?H*d%xX|%4jLBWu4uyd`1i1_S~8hm@QT&ijWw)U98PgN>EvBTBr)m&Gwy>YSVM4z z$jugy&cY{^S}8gi2_(r@eMBFy@Bg*))zS2Q4IA-He9n2j@1!ml9bDVX=B?3){Kqxc j8lz*_SNTD*Cql{Ph@&k=n}w$5R%T(FBRADj6FL6^^Uafq literal 0 HcmV?d00001 diff --git a/maps/away/errant_pisces/errant_pisces.dmm b/maps/away/errant_pisces/errant_pisces.dmm index 77c6de05aef4..6241f27598e5 100644 --- a/maps/away/errant_pisces/errant_pisces.dmm +++ b/maps/away/errant_pisces/errant_pisces.dmm @@ -451,7 +451,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/errant_pisces/enginering) "bp" = ( @@ -569,7 +569,7 @@ /turf/floor/plating, /area/errant_pisces/enginering) "bK" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/errant_pisces/enginering) "bL" = ( @@ -4380,11 +4380,11 @@ /turf/floor/tiled/white, /area/errant_pisces/science_wing) "lU" = ( -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /turf/floor/plating, /area/errant_pisces/general_storage) "lV" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/errant_pisces/general_storage) "lW" = ( diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index c9e92ad63477..1c4098ff2ea2 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -34922,7 +34922,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) "bwe" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /obj/effect/floor_decal/industrial/outline/yellow, /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) @@ -34933,7 +34933,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) "bwg" = ( -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/cable/green{ icon_state = "1-2" diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index ac11d915541d..e932f12856ff 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -3645,7 +3645,7 @@ /area/ministation/atmospherics) "pS" = ( /obj/effect/floor_decal/industrial/outline/yellow, -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/tiled, /area/ministation/cargo) "pT" = ( @@ -13140,7 +13140,7 @@ /area/ministation/engine) "Ws" = ( /obj/effect/floor_decal/industrial/outline/yellow, -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /turf/floor/tiled, /area/ministation/cargo) "Wt" = ( diff --git a/mods/content/government/away_sites/icarus/icarus-1.dmm b/mods/content/government/away_sites/icarus/icarus-1.dmm index 64d4c6491dbf..18c6c2ab63d1 100644 --- a/mods/content/government/away_sites/icarus/icarus-1.dmm +++ b/mods/content/government/away_sites/icarus/icarus-1.dmm @@ -1801,7 +1801,7 @@ /turf/floor/plating, /area/icarus/vessel) "fS" = ( -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /turf/floor/plating, /area/icarus/vessel) "fT" = ( @@ -1926,7 +1926,7 @@ /turf/floor/plating, /area/icarus/vessel) "gp" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/icarus/vessel) "gq" = ( @@ -2545,7 +2545,7 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/unsimulated/floor/sand, /area/icarus/open) "ii" = ( diff --git a/nebula.dme b/nebula.dme index 5cffd8b32162..52d262392e0a 100644 --- a/nebula.dme +++ b/nebula.dme @@ -397,6 +397,7 @@ #include "code\datums\composite_sounds\fire_sounds.dm" #include "code\datums\composite_sounds\loom.dm" #include "code\datums\composite_sounds\machinery_sounds.dm" +#include "code\datums\composite_sounds\vehicle_engine.dm" #include "code\datums\config\_config.dm" #include "code\datums\config\_config_categories.dm" #include "code\datums\config\config_enum.dm" @@ -3904,7 +3905,10 @@ #include "code\modules\turbolift\turbolift_turfs.dm" #include "code\modules\vehicles\bike.dm" #include "code\modules\vehicles\cargo_train.dm" +#include "code\modules\vehicles\cargo_trolley.dm" #include "code\modules\vehicles\engine.dm" +#include "code\modules\vehicles\quad_bike.dm" +#include "code\modules\vehicles\quad_trailer.dm" #include "code\modules\vehicles\train.dm" #include "code\modules\vehicles\vehicle.dm" #include "code\modules\weather\_weather.dm" diff --git a/sound/machines/vehicle/engine_end.ogg b/sound/machines/vehicle/engine_end.ogg new file mode 100644 index 0000000000000000000000000000000000000000..7f3c179ad802ed085ad74cf5cdfd7dd919822364 GIT binary patch literal 49838 zcmagG1yohR_dj|r-3`*x4br6`Dc#-O&Aou6NVl{!m+o$m5Gm>IlokO4K?LC)^!tnT ze{cQYS%+D3W}n%6X7A6QvuEE?H9I>k00H>Vm5=+cGSQV84WWj3yLnnVKp&bQfDhy^ z=^nfuD(w)pho1j^J@kBdqo*F<1@Gy@TmN%?MEu8y9i;0!xZ887dD_u9J6P)eWly6( z!^_RX%gxKpN5i7#;_Be;X#=%!g|R;Hh9Ljz$|@zT3jxr;79M4q!Qkmc9RR=s0CNUb zoD^FH){@i$7T=6i`G?vdcXUQ-^dPBK9M8~y6*TKe^mlq|7er9Q&mcrz0o>f%lS%<#!- zX{+dfmzRm2x6P8b*OGUjK}NVyd!RvkxY2UB$yK-oX~ci-Ux8a!5BJ}x(?9??y-4~l zYatzZ;U|$o#IT5J1i<=1B{b;Cbgaom3Z=G=wGP>>_EoJeV~reRjaYwQ0d0!(Fd6{v zkX8Esx@`^8ZT|mn3HuQ?Kmyd|um|O^2ZO98!>}g@>R%0y0H94JG&u%6`Q@Pe!_a5o zEeDhNn5sN+p^W=qMtCqg07&pq4th|21ht{bIp-;$2Nhd_sw{!3c+klI{d)ZuFW^LI zb8XWdgVFdBE&i${7-UOYfF_OgPZIb-RKXri)^yg72@#yEeRu^?_T2ku?QG_bij3^P z7IdB@+XZdOI+{5M&z#PJPAXt!mHeyu5@e&+RLp{HWQRF8Lu3} zOa6HjXjB^OL`f8~zfKa7;W^4$271N+Hr!W+DSWD#|8-A&qtqZ^1n(JMPAxt$9c>*w zZx@5NUTbaPCd*!1OMzQU@n(-A{!d{2x90$$(}ex?$yCcYp5dHORVnm;2L3P4@gN#b zqWqY|pw!5qGRraXonP$-{|uq52EU3HvB?av*BptBmY~U;fX$qa&4RbhN~?`gTcAPf ze;nqY+iWd){V&gXa1lC@gkAY$r2p|8I7`AO;RJexWCnv|Cf{_s=&Xv&yyJ>OjQ_)P z>|@F@V#=ao4x{0RR~Q z(1S(}!;A4ulyVi*N8>qQKrRqLDHtFPE5qZ%62Yc}B}y`jwI_>w!1}SR$wDln92(Ck zlt6>sUuvwdFN_8*dq{u=00i@l@C=Z8PAYta%S`g1=cbN`FqfpvDzI{=cpB5~0dc8vg{pfQGit7@^LBw$2)ficTw|3ZaV1g|-f%tPZixT)K)*i^~+h z&5E|p28qr>tIZlokY2ji7N3d%__zpCy_V7g%@$ouGXBF#h3GFUL7k;m0S%arhK`Ma zwvK_fjlq?U-eRBO(n-kYk6g7&1hqNc5T&3>pA8Jj+0LJ%G%nh#xD=l&YF`UkCRUBfKt}6 z(yGs$6`h@~C!HSStuB;Bm7jaq#`^0=8_!QVpEP^XYUp@vq?+vNf%9-N^T{*13U73I z+FNro3No1eB{Jnk*#UNIa-{&i5YPIM8)X+arOBEgNXTuh!3?w-xk8_ES{w4X5#7D? zDyqXmAASR31M+|k!cmC=T6TnBVF-Xidq|{($2Ld@K{LJL(@9HdNDS9GLJ zoo5@O(N0hp$w?tlv}aA7)kG~QQCD;-NL|!)YABtzJf$fnQFP!;oi%i<7@9YX!?(iY zJuJ4KR6s9jejtFX4&lW(Qi>(TL`sUBy5k^_i)JO|D~buU6e?OkRwczGN{T$KL`JSW zii1W>q^lR)XB;{(CSMQkw(s#vo)uNe;#tX#Smvi#5gYvz`Y zoI{J2H4nns8q$(M;RmaRaU!X+mLRKS%{Z`!k#k3Bx+6%)(u{36ScN(ZT+SO-Kgdu5 zzYx$j9Y1x!RVNq`kO)S%fj=xE4{j?Nr8%kKfaf_b!iFw2F2Y>lsVv3HsxvN$#@I3` z!pb_hD1ydVLZZaRIjHNz$_mbc6O23lx-E)I^dJNS+W;Mq0lj#L23&Vg$Vx~QjOP`nQgl zV_9PHM9{#rdD-y8logpPUV|E~@dBYxGB|GNSkt^LfmRc~$g9K%Lcz9XAj1%hV84I^ z`to-jVFVIU0SVF~P_Bp_p5?g65hXvkGE#=&unpye;z*$pgTny7RpTOHfhPqLz<949 z6f8yU90(Cd2`#?xF{6Ygl!&$%^4}{f_p|oPnpuSTrZONi#wZIYx;+BIfCqS7>&U1;JtBpyATudC8^876Nd(h?YK-HFAhL|%In0qpV;#%@W1LdbhtLE3$Uej$ zaUcPF90Ca zhJS5giT~Y(Cwlk~S|Fef|Gk&0F7+2$Xa!&!D-{SbmcKPdu!jCGfcp!^Faj`?O9eUq zYfB{nW&92AQZ*kC399CS@E59oYv{BO>iYXK?Z3Vcu^ zenb%n#sUD3WycfN#SxmpoUnAIutZNhOD=rP7!kP!8+o!p(uGu>5+W^4r=uLpI1RO2 zD@C3U)v3CQEJ-1=p4Bbh$b=zG)s~B&0n8UJNXZ z;V|f|10sMIHiDHDrH@5GSoylpQWVfPHXlI^#)^+`j>SCU?L#(M_o4}-L86Tsgb*bL zhZ4HJ{DiI6{vKUawgl(z1~?c1r~x1un}&`qOC$m34Q?V{5`Hp43SlY;UjWz!f^mUh z2IQ!yDBFah54(sAGS*?m1A9n}f5{I88rr|D3ew;DzYTI<%s=wO2KhmZ{)@eJMmjow zn75aYk2lQI!^gwR%hS`#3+4`U_4W^R_qBKP@pkiqdLxl|V6Q(-%BLT-$_3#5U#xjm zZEkvZ^@^1}** zT04pM-}6$%vua225ruN9*+^O&7SC9@%KSi?>D{ZnD+iSEeVsdOo)FR{NoSGnH@S0? z23w2ycBMrO{d9S=>irT62trTul)``HdNH-dL290VpMwQ4r>voy9k!rHQg)k?IaYX= ztI+4hr-NJ*&OQs!2Zae8eRyrbnJDZj>)z9K+D{z+as5+D-lgCwTTi{2b9sR&A9D>g zL^`|lt!!#_=(~a|v#KZh4rGth2qs7#M9zI*6S&PEyg_Lyjy(xnq>Ije{+3JEuK zn{JFf>Z+F27W?LAwZv4%(bBoOC4!Ew$dWm=38}wd(OORWPRU z`xT$+(ieCqjEK#^-iNZyVbeu{2@1H)(^!=XH*gDEAvP8p3U!h zCcSh`^`f%TC-psD#hm>RR?Qy7i*fOG&@Xu6zBBr|?*awN(WCP?QMt0>#SMN;S$1$; z^;*+9yC%REOgq7OPnzEyH{1f4q6@&JW|Y*rmws)aA+G#*g@_jH(VRzf>rqnfgXL4Y zDOavBRP~gK&i6B`(McB5YP025&N8n0c8U%q# zyv)7UH8Xew*-enx7x*QmE32txb?8|sprW-;nA>!?r2umM|N~hr78|j@g2L; zO?)=uhFG{{d|+?C?u?17%~tk1$9@Q7Yo;9wvt@msSAQ>9L)>*?N{q+rqjz2kCbP&E z8j9w_bT-z&(UJ;4tMamFr60BFPPR_TfAZU^ zE6{^>YQD6O5}|%w-Kn(mEl=CXyU(VK{i;&Yvx;JrQzzxb7dPfbQklzqyoQ+Scn3Wj z+DK?~2RIQvFp9fnB1<`_hs~@FFpAPS)%%=-9ax=bRTC|`VrSbV!4AwqtQ6%}%?=<( zMY60J@D{ksBI!t@LMa@Nd}l>}c3pB)ht>`4x60Lmtj}d2vWJbCzTzfWVEy^gHV~bD z?KW)fo8z?kj~YACF8#T2eiaFbsl&YGaEyL48tqds^rgR3u7_k1-bXA$CxOJ8vMNh$_)kZTc)Yz@dcRJ| zEhc72iA+~@VmeQ`J8il$MQlt_ZXxoBb6qE_RfW1Dm9FrGoSM!napjn5>1cG96WOA? zpt0XxSBPB>R00y<$7NTnVhZCw;zCpN2I2l;$*iGL-N(e2T^yKA)zr7FGTn zGh4g9zp(j~3ZGoV2?3v-E(syA(^^5F;}5Mwf9Z1#*?`O$1X0-jDe8?oIuLZt?0qgMV0%A# zsT)4^$FNT_8NSEy?0;Nc0K}CWk(q2D<@+;qa?^J_zV~{Z6M}C)e0xFr^I|N4V896NUzF-8$WBERVbL8@2T#YzyDI(zz$Vr z(1Sbq=^LU?L)z#X5uA5IjLHfQThf`xcUz^Qp(-$-1a`~k_&dadYIJ&XofGNf#Er6J z-5OAIwds6CD(6l5zOLNH<4%sA&|0dD)DJ$C>n{zbcQ2cqRBxZ1H zVOpZA>*1E%rqwSSPDLcoV`2{qmiom|mN(AGzq2K$M=ldBxd^mEPA1OE-kM;g!q?G% z^Xu!aoK{%Zx3*lF%M(TY35n4kZ8uao)tG||F#^fA0u42iR?NgeUOCc{3`6g1eZm%Y zD%Nn`D@`dt+fj-kFiX(2v?}yNp*?+k?T6%pxaK1}aZi)GVvH&j4iM(XAmdHO+ogF%EDQJu)N zV%~O0#aOtq=MS&C?&eJ)q%Xs<%6I2h|Ix*zJCndG%j}t@x98JDcr1-fSfl0n{BA2P zbQf&A9C9z~v zPD2%s#HHe2s7#F`@;?_wrH|IxZ{43IP5E>fCt%i}RgUZQ4N+sOI}P@h!6PDCUYjZ< zjpdI$jzc47M59hhL3>0BTXx9*qg@}J+!YA%R zej)+dpPs&V6BARmNNXba;?i%GRXH1B33)9T%exc?HRShHixV$lwV{G7W}I@aXp0(Fi}+Oa;54y}!?0_z|Ibl^_pr$g90 z7uChSP*Li##AQH*u^|G?C7F5lW8jHl zr%Y0C+M`k`aq=|s&6V_H(dTUZ2}BvW!f7Ig*$gO@oBqVb!0Rw0!)MQChazLO%AFulE>xkPBG<^pobH*xI zW8mu<&dB~N`4}4rD9uk6wfB4JA3&OQN#g1q6az@S6j3P_k{?=JA`Hj_LGe$7P=Ooq2^FFcF1 z8HlZ1Qkm2oWUJZ@FPs%-p?KG|x9rxR_HFTr8Iq&oSc?Gh4|g4=XDBpo_YiYb8XjZ0 zuS)K*#xG&vu6}sGFC+oe4t|b;%r~xsEzi}H*MIDyV8bVz7=<@soOn*CEBYHuT>B_i zZXKCd$fdbxvYPgCzTO(9l$=3I`soYS&Z(+lbmC9tZN8T=aS`C>xZOXef8xT0R&iDs zXPnJ1wvo{-S@@|iXS*Eguo$r&l=1`!5+QCZI0_H7x##Uf`33HiNR|9$f^_u(bDb4Z zWV{CCocl1#A1_aDJ8(|DrLd z9@{5(86H=d-7-s@788A9Ytx&)RTIo6h*Zt^#%YWS#iXaz?OcEW$U_&f36D2jfsp|b zR#O(Mq=5FQ?(}JpT+Dt5T*%+KJ2rCE1a0Gv_>C}{+3n|@EZXn9LY0X^>TK0QJx7Bg2bw$%IupOvELyF&&ZctUU4!S7TgKYZm)K#uzrvJ;v~QQd06hu@iq1b zyM|W6))=(y9;Yof$M}hf5D{4lH?)FwX1Ql?hMsN=&-Fo&XUom3-6N}Nn4;TQ0r3v& z#)A2w_}e>fP3`3yB3i~^S7iCHLOUjowdQ=xPm@EtWvql5i^w-gqIH!~>pyrHdd*Bz zl$OSx%k9bk9vIWP7_8UUlQc{-3~@gAS=*ZbTTI0%OUwXQ31*Y&@?s0eFP0tsYQJ+{ z@7GkH`Tb)1mPLCn&fUsI105cP+Qn5FS-4mvK9Js8y=bs5V)B*u+-@O9nAYEgK0)n| zNXF0de)Kj^SN#?eEhf%0U*;GK+sx_-^mhz#MXmDmECN${xb_5fyLH%6x4>=z1se*qx1(Lw-J( zwiR%c+e<%vKb?@$OTk$&C;V7j>Et(OEWC+t?YCp#fE_>P`Tahw>kSWq55Qs7?Gap4 zTi}?+{rQ8K!j8mVZ%?%WBDH}e#FJPU{oE$)9qf{ihPG2EpGA%BywKtA6N=SH6(FxOM`tosmebwW@TleI*kG#NJ z!P=ur-*t%d>r&h~?;X3}0XUgV#Qg52Y54@dW1Qy>&aQ|9m`JA%eXH)?==5*G1S%(g zqlW|=St0_P<?JCbY@{IaZBu*-?Pj}JMSbIH!qm$Cp+Zo3 zVy6@-`qud~DW3{>Z9%Q*nLwQBLJonA6%)N9<|)u;AS%#(_zo_XgLf)iwKwWtK3_)nUF{NL~M4`~KOFwJnepf1JW>g5i7>EQ_j^9w$1 zP^d4=-3jIcg?YPp!MtF;o?fnA-mtrNqF)l&dI`&3h@?lGG3a;z6CqBLahlxsLV?vB z>%d5Xup~u^=;`$)BJ8j8Dq(LkM&h*8tYaNf%LAc^;=$Ttql6_&3De8PqHE&) z9V=LShBDJf7DM6D@9Zhy2!}+kE+W+{_eKX0O{1+l;YZsqo;~6FB9=A{bSg}G@SWTs zTHFo{i13i9ky(?tRdVMB<{EghtsMg|*~gRzPB2D`mHl~w$qqkT(<$8>tR7M(il!1j zqalRwbp@e>T|zBQZPUX{KgQ#JAZ@bPuxphbIudr+zwnV&BR+J*0D{PV`bnj{^PH1I z)cw%lqb(o+DgM%!zV~JYStw6CbT^7>Uo-Evdmc0RpeNwli&xs!zCtu2%?B~}RLbd$ z2_M!(3q>MX_+AsY+GU46CQ(3iXqG=P4cqriq_F;YKi#LWgHoPA>*x?*zS#9ns1PDv zk=l&c`a*5Nsz*I}`EVBn&}Ph%Z~B>*o#g)*6P1%+0XiwlUQ4Q+psnRlHbIR}WT!V< zh1+*d)X-(3*)XG#`KLrB{?OH|m^x6AXO~Wl?@>iui%4yLT5!XvtM^ z7w0M0%F-|N->`eg1pH(eAKme9T&u(ah-W{l=$4Vy2~U%pNxQZFls1v#*`<_hVv&VR zCKDo7T4}$%F2hw;RI|k4y2v0d6sK`ZnVEn$R9s(+4pZqs)o2_v+usfuUPk2x<{_|C zT4dBVCT_dfihWU%JnT}d;}o}T)0zmMq=W=r!z^V(o$fl{)Drf&pNeofVgZhytR*tMeipH8GesuL+W|mQhum^{< zvZNr{{*Clg-#%vJn7Njli(LA5PlXWc9Y}X3=1_`@TU?t7gQ8&%b;NXe+>J5>-fP~7kH`*Fk*QT#d??lJk`A=6|J6$=&KA$ouoY*q$zkWXVV)>md z%3OrlqtKCZ$&*%-Gu|y68JMZ#LPp1QQhL!V`Y0rXiNI}p7th+bhM*b&|#3mPWt9M!gj48^_R?yEDq zLa)xEMpq^}a=cDQa1@4|;ln?;f;-1~SIKWmk&6f~P`Iaklu;0Xu!bpoWS6fUo}EUo zj2M(%4U&gw&nz4?A}Vzc_>z{ziA_hI-+QinE`XCd#?=2=4|=@#`;o=>p5oZ7tY<%p^b`Rw zvy)_Y*QzicDe=p^#G0_llzO~)mLPpae(i&`(@%!^7}?Da$+R(uty1<}yZSQIS!;_S z3{f;mm~lgvsp|wxCXDQDXti5CQVg`pduxf3LqkK`gg_pvO_1zzr>DJ8 zU&!mr8vr<+XJ81#2F$-}KgQhPAtc!X|@h{%FB)c-F!x%$Y!-`JK;v-Y%z<=EL^V-XG4XuXPwrUdSZOBfJ5~)k#U90 z?YD@KO*>LQg)!7oc2d@x!`CdeXsAr0itWs%`nP}5kA*jw$w%zPnGld$1c)ICdH7yL z1*Xw@;oFA0`4uwQD|}*nrMO%9h)4$Zv*~LRlc|wmMQ+Tic1Yc#(PspMugyx7H%UNS z!Spi`yOo;8(+yYbypDNW+D(<6?h|fUEdPv9sDiiGGqW?W&v@9v_^4*nEo07Zb+xw6i8m zz2S;5m#k1z?~DJ&%tXwm06l62}x)x>^eN+sXn z@XQJxH_98(KR_-dkd}0*o6brgY?Dv5grX+ubfQxIR|g0lhV@PFjs`|wltyL zUB}tHNNI=Bzu8h3+|Khy01VPz7Q2YYT~G>Kt!fK#88DLDp!D#Dzssh3zlstQ(jQQ@ zs+oy_^g3mxEPBghW24FBJ!#>mB8^#Z$70iZ?D%O;OR|l+EF{dh#G`Ga%nrpW$aFB6 z<5HvkW3KY8wvQIb6A|zmh&K~$c2Z9wpg7vpXn_PzmoDNTyA&;wCpiCEvfe}(y4{X? zhTd4hqL?gYD?;$cP4CB7vK@=eZ58oOeDlqD(%z%8 znK({TE=p>Eg;?&7(&PgAdg4@I^BCq3i}NW<`UgMv`qeEdGa%2rX!U!8zcsMB!7N&_ z-1tR(_@dRb$>i8`9ez{x3XWd}{Z<&sXHw}~Qv$($a#xHox?wE?8y(!=>? zAPI1xP~f$r#&v=JvA_H_8ykUNWbdt;%xJc5z+`{Ve@(DabXe$qp5UxjmPU+wuj#e9 zUdt8RLXeI?g&(7pnU^tdJeKyQojT6s9?=(yGouKByL?%vu@AxL>(2Wv$ote19iaZr z(Lh(d+=)NMjH15z;}%Umm>6Eza2upIJW%eGN&uZ27!{Acec>p@o%QJ_c?X|M7lw6~ zX+o=PI4^ObSS}_(MxBr(7s^}Cl=-CKYEJ!MPY)FZTaw;% z@u#HGU6}saRh{(1cf7YeO?)E=+_9=OXc!m^>kqlHkeN@tVG}@;Fq*-?hW=vq(G4-n zV2aM4*FSV_NOLo(E&ok2#wS1doC5pebTFe;0@w46KtLDw(apQJALrA_QWfKP1CxYN4u1f7S|gY@a|1Zv_5PPdMw3i}YHm~`JCW9e#rXYT|< zQ~SQI|ERDBxDzzwr;k=JmA>0@kudV3=GoPuQqQ3yzeS~5N^K}@3l5iezs(sVlyP#g!yP(z8a?8el&>OsFDOk9@7ZMSZG%I9y+VGsaH3})8Ex@gLtut3 zc&1T&@L4hB7qhP>KT_UD{!Th=XG$F(aW?g3zSv-WiM4P7i%7HdXx!hCbtuuy^W{*Z zPU2fvWX216!`>u=-CXojd^_V(pVC&HA1yS}9)i6xlyjXLLj5%}C*0Svc!kC*y-BZy zDSThES+nkEN%?dj^qMBbGWT1xIElo>SV|*MUl&_SY8m^{z1`vo#V+1L1nk-O@Q!9yrHymTE<=?^3+dU1THzYf1T?e+L) znTGJA^2pB4&gf0ox}Fk5dX34OooR-OSfky>6BkjkIcNDD%KnKGHie{7}LQVPCopsg?U5RM}C zZa&eN<4OBWoaM8$Iw3w4Yx{{f`!`qqex>sZ+Y@b}Q;}F`oD?@@-=(geq9a{V+R1)k zN)30HT6F4nC`XszGGZc_>`;CwqYpjSuf7rDw6Fnx#?xKV2fC956mUtHEM|RmHFL+aL=95eGYkJq!Xx<50cb$8i7PP-=a``Oy(qRTNdX4e&h zczJmEtRQ*(8u>)-)!2)fXVEgY-`iCj`b6ztcDBgcBq1Z;*;~&RG_OnMQ!^p%r=f%A zpH*_12G(_Ui$%8i^%|(km6sFs8%I;o>_1KDW@qi+kU71_H+@pep}nCKb4`u-Wk~c7XpDbeesh#_mz1tRzmfJh z$zW~#OH#SliUwu*$X4myWU_ew(kpc&9n#7URJ;>DF!zW&-^c@XwX91shHqBU#G!Lbr2B>pZdth+UvIxC>z z9a8uEOqMyH=+?8v7M`x>i6wS8^d=>woyh(r@sg0uYYyA@JEJOHuSxsdzx=Qww+D1q z6{kZMUN8?m57kp;z%IrgAgc}&v3@-ytsM-|DNvy`pZV?fBY<&V&R#a} z6GF}Q3zi9|;s6eIgtsT~?*{`QVx4T~~ ze#2zk_oCpek=9eiKVHgb=Gh(Albc)};LPSzSU|4al(ZHnz7RUHo-wfZC2o|-r-QJm zP6pM_7q%l5VvdRI3ECg~=s9F$7-^0DmWkmdFPX8Zn$`D2QxBoZdn@GJIN3;Chb?LJX{>4jonsSQxP|MJi-7!MyZ_;%U$>+E5i}#PYV_&kGIrmK5_4ykN#!vzds-8Rv;WM3wb!9o`1A}}$|8n9JI@Vl72PYb8F8+ zkb%4v*qMa2@eiUkTyo=A!y+DW>n)EpB8EM&3xAKCo z$@DoxGGpiIil>7Myc?VOs(o?LgDU1rcZP&EkA@-@G%VIIn6y7RwYq(~KfK)4QaTt^ z%@zBluJ5dwjh-7=(o)Au#CL zz8a}{Um*#m_u5kOiwf&-!Sj`>cFzt9d^6g}-(Ja!UUorGFl9uZ1a4C6{AdezI@|Im zW^38p$L9!{BW5*s%e8YkfJ4yWZ-$;}M|A*I2mZF@UI+7zZsdpRRKAqJ<}%Xvz8NLN zjb9tZ_B7AIe?T=W%$8g{1%(7p=%y-`$TeIOu`PPuMVlC=E4+N!DYu|s$*1N0nla`Z z7q1GZx}n$GA4u{wZ{B75uD~nc*Ou0=BGf~*%z(LjefRDtx5(y`=lBVkDw6Lf2vUI` z3n31HtQ1i^*Q(h7Zl;;U zi;p)>a~6N5{3 zT5E9k?gYz+e1+v@x0}AvV-hP&+pex%(@IzRgc~EsuM(rc(%WjjzWaNu-~Qlv1M!a+ z^F@w=37ewg5*}^_{D4&5Ld5t)q+Ls9+{wU5)FK(iT=$z9B?E(HVikg(GyK{Y&~BHx zH**2}*bI#P{X0kB_nAo^^zf|LC$;-R1XEc9zQpBo_{q1mOY zx2V`LhV8HVC=hACzy^Q3U9gpNZaRJisRtwyxJjDbmm|$K!m-s0Z0xxd;lg=2|~G-;(e>KY@_L4Inx6 z1e@IJ3*YOUw5Xa;@;vFPt92H5mGor4yLbgqT@HsQx_`aZI-QfB6ufP8uby93Br5Os zi;fM8P+@SAe*BG7{i5)u_OXN)r+_bK7d;+EbsIs3$$%>@8U?;_I@t)&Bl6R4h;!*( z%OWC9zhZ>bd3GfxB+QkXCN&ZE@><(iFg^Zz0)0iSz>xCrD+L(c$EJh#3I&Tric*Fw zCwU~en_*w5msA}`^)u=0-f!(KSBKfQC=BD1D|qIYDq|90ggo7LDp8;I@E{J9yW5>* zYux(+kvU^D+@4WhFZjH^D&z790n|o~JU4ei^GClMN7jD?dZ)^OT?W10PN9 z&7wMY?p2V3UEM2QW@$alXWq^X`%P)wz5)(k{12=|CojxRiEH;hr)~H3(Nye#Y5f*v z>4vTuPQRi@_tcEjV0ZxYl-2jxuW1_J(g4%RheXo=@^nHY4f|F%WpI_g!H<% zL)HS5!Ohvz@Dw4h9^tVxV*Q`&lOF5e_LXaaeeaiVm1MHFMV64Bi@21SVQQzd>fQ72)3 zLSFiDnP-GYA_r}lL0QAU^E(lv=Kegj+Nmj(Y48yZ-B@koKVAJoz(1|Q)82D9oO|BP zPx7Sy&-GYVi_Y2}eo zMbK43t`gYnNP=O=+&%E#@KTjCCK|~lkpw6^g^4KF3cj_fcCp&#jDsLx-rSwgdf_?ET#WdXkk}sNEuOOK0qaU<~;Y%HmOXMp%PJ z)R3!-ecOU43dGmhbF{fs`Yi(U*3NV9XG(2+fZ7MUyO)3J3K|s*>qGo>ifdGq&V6p* z_r>0~DdJHoVw5 zh);j%b4(vq>C-FpOvjwJ*^eh>+Jqq-c%C(m8}4%jXa6=LGVN_zF}Ct!jU{>RgW+zq zki%wdj0lyVKjMNQD~jHATZ6@_e~$J<0`JS5KLsaYwG#M|rv^%`#mm6%7~R_`oI z{j{|Mv3c-czP$duGMjg)>htn+yh#8qtBCm5Y4|)Nb2Z*O1o`tg9ti@L+rEGe*hG zcs(JglI^(7rt{O{v`pHUbMVP{JS@2`+1LiRyi^&tWYn?H1iL%dh=$MJ$0gWdW7uU1 zge`trwteQ87*N5qQsX73NICM7^13u_A2c=~ikT5`PccFe|Crji9TiXUGaw_4=u#XR zJ(x$7@#s|rcBUGq>39jYG3=tQYe6XGCx&5P{I<2}yWtF<300{IZaZq2mgN5V)YG?? zs%|>MKFyXDM2wx^2U~3$c}L+X>Axx8+5D+x{k-A**5Ua6H&wmk zI(wQ!FNKW}fftEp>cj^j}%dspK9}CN@Rt_$qf&ReApA5|4c6} zwnx~|EjsB*Zcern9q5(vjh=s5_vaJ6mu%KTnQdFan$O0ZV14f> zTlVMWY{CbbS2{m(y>42wc%4<|4bmgohrR_cZL-#s7U|ZlH@e=MmO8`zgVgfCgL)Wb zbS@K{v@SkAWP|cE0!ZN>YBL`pBar*p@4cHA$}FCddYpL6ZPbzLNXXR=Q+m)4*0k%K>2+(h62Zmi=uB!jc6uK7Drk!bNgns;p!psbOPkdr7k9+$ zJL~PXhI9|{Oqn=n*MAX;0REF~5dHVx6g-@8Kn9ZyyM}_f48Go;uHN=AsFw%S)f4LF z1@(H!I{0|GxWDxBgSxrA^nrN?bzIETX1(dzsC@F6rRj<&9tDqDns{ciBa>B{ zfzOre z0UYB@WWa=xH!m2XtzjKW2>r<62sCyE<=ODinb>311fa_U;w$sE{ZOvkkL&aV@Z{Z`S=hoGT2i3C1OvFs zROTxDwLK*IygE(a&P0MyBjxkL^T%99yNgRgTw~L}F14ph09TBkz-8mOZTDQKQ9F_alM4@7oSZirv-IxcpL07t z3%rdXu9jvjejtc^BXD4+|LOB*vu>n)z*qsKyF4CVVbrT?V(IOFsam*7yQaN_BDk3T zGa=;D`?^^L+P!ofm%7O;?$UOQ5NybrpAGWcK_&AR{*5Y&pL7Z%A;fcH44d-^7`4AI zbSfo3Y<2~8A9CxBsfa@nO*>#PIS_?&>@=ehR_BKVP5Pq74H)5W^qOJv>WQn&tDw5Gn6%f;F zKrBr?WkrW$EShm51KVvmA$gy0S+ZUTP&;wLz`%I^B80v+8^|nnwB87>X zkIL-@-!Np(Xq1}z#wr$7g7b=MdZEa|itN{!2Qsbnz5E@+N9)K^hL5-t&SCvRmN71* z!VKw>t&UKJb)?7(-Ff$dlDmzZF6k-8LS&#BN!&NJvFPaNg^;-ND8`tMx_Qno!*WSn zv#P`Io=!*I=Z{&Kg!?^P1E1-TDQN3_iM|x?P=zy$?R%G;#Z!6O)tR8ohe6cnC^8gS zMezhr%2=vCNz#@m77OGQqKbv74L|k?^V_bmN@Hn?yz}7_P3%}j1n$r|gBXfCF1{SC zxk*{k_&<*!11?%*CLBGxb?3G79ZQ*>j-!-4gTjdkMxUcLNM4IAKf~iLl!SSdH?lE| za&KTzOmye`p0J7Ksx`$nJejiS=qdlUh1JM#of(N`GurRC3@wR>UMM~lLm8MmdjF0I zFg;H7lO1mV{zA2vB2yB3IIk{65~0{iib3X>+7s0wks9kO1;QE2T4Irz9{Rz(l03m3 z5&CG#Gdth9wks4RX1i(y^?h22sj(D`m}3`3!MG>@^7y=m-f<8WImOjX{mWrgc;o;- z<@6^bsN&00nEo@0MDY7Q?VVu3f~-+(_CVa>@#LUtO(~(B8{D z^b`4@P-Q>ji>zrl$%%{oA80bWsECo1R>A*6)L90_(S_T326rb|aCdiicXxM(!95TH z1PJbK!QGwU?(QzZf;)GLd_qYGPmNG4W*gtRQGFot_ zQ>jDglQUDg0p>3Mjg)fO zDQz7qR=>)Q((eA5@1&%jF<7gsKErl^h-GI#Z}I+QmCo#1TnObq8|x3_nfJT8w_)IHW^GLukKnX zDGz2A>jk$T=6+ zyr9W<`(r$SHT0%>&Cnwe&{o(TBe&M%gM^RS9UhWtz;<6SR6ZR;hDA10VR1(Q+y=5GOv(@bD`;fU0d(i<^zOjVvJO+wON1 zd}>w~vFC7nGeh-0s{-Z-;-s6l{v4>e_pl$cRG)UC&??^_a>&Dg|2e277)#7w4{kFh zR{?$>17KZ=>lje@V}W7GlnY{c{_r2jYttV;L+23B!N| zGLL`3G2nB5MZV;cSxHd4SM+XOWpCv*H}S_}Vu<>j|?2 z5tr(bp`BzHz&Q#4tUZE;cmMRY2XX%iew7%EhM7@HrsVZ*mmQb_$*IY@Q_otQ2LA>2 zg9zoizD7Bl?%vh)65RjfkyGT-QqvCq)6LDIFTa1t#98V}kxc-}%>ZO?g^>*Oy@^w@ zpON}fFHLz>>kc^A)}*7LQ2-E?VO5fs#!$c9$jWK0-4?^a2iFNqp7k&$xz8s)#nH&8 z*0}GJNT)MZyWTx>de0B#q?XEA6{WdW)HVYWX*gAfUDo`}FoyLyxSiv_w|5M8vX(#F z0p*yEK5BxihAA%)K4z|9_&p`vv~zRQ;VVBqYoY`&0NtmcViNc5mD2rr5mnc#VH=>% z5C>a@OQUAVzdzRVW_4kj+S@i?-A3(@jBr5)_US|Y@RFeq!aeD6uQ>vIO)nntzTL#r zvdqeR@w}2py}t7cjr9AXeLp)bkiN9!FY08^JhkA_Ic2#ZjBXQ1zFh*K&7O^Eu1XTl;?mr?VjO3?!~M~ z*ql?2YLOBGSRelaFx*h}9gpO~zERbzLo`2Q$l*|65iJl#+j2_DM$&VMGgN)!uXr}I43O<4={ICQJ}A>@rL@L1 zmPWl0ho6(kKX8Y|>l$14Ta6Z{y%C(%HP7GbwnKB50p}U+T!vUvb6RtHLPE~7V=<9Q zN|Z2d)xL&OHq5IwgXrW?jcn22wmj6nuT2Uep1!wzTj z^0_n2#|aX|R|bB$_=lHbnwq|*6uEBl?nvo4GkaztW9S8TYm* z8ln&-Yzmu~Q@X?Fbxz@lh|gdWU#hQ}WSB`BCG@_HGo^})L|?%s{#4oc+O3#Br@YWu zydi}Q0(8T3I)PU(zS4`wu_6Z&KjSedlT|=MoB~@zfOr+n6wzfmxdEjkUy#CbjtXIj zr{~`;zuxYCgMX0_bag1n4s_k z4hy~O-CO~}L+TpCN>)=Wz>!e=E0V7v-{x8Y3E@BEY&C!YBD}@cU6)g-9iL{Xjv3(+h&r~R-YJwW6PCsm_D!K@R~aNjl-?w z9kI|`q=}lHwG}SRcY<%8wg*hN5RS}yUjBnR(@b)a!zjHa1V%1%L6;7kXJC7J6=WsK z&P3S$6#%(;>;wz2fd%}QeGw?+#vi~lPfeXC4b%Ss#X#&`+Waol`Oy#f27jjTLpSGg z!c{Un;-GS9#VjPXf2ip3`U~Wol>u&nnQEDeWJUoaRIQd7#&H|)=8ap!4WDpuLME^% z8}J{`PEyq6cEF?zzTa!0hYTRFq6sQsj#`2|ok5w3A~%w0oUl4V=$3pPsq65R$5m(7 zH5z-DpWLsAZJQio$&78bP>Zi0P-2z}zru;w!ctk=R`ffdF7A6e13qL(aV=~eJ~dbw zGBhpXFiNB7jga5;F5J$g!g}Bk z^c;}L_guWVo4B|2Ig?7j7vo%TfMcP4ch11Opd$z`uW`DE4kH_2KJ7czy=A;G@erep z7m2Qa|9ca0hYa#*(WNx(U$*bvj*iycQgkg*Oljg8n!W(Ja8S~@YRc)6f$lY%6JId( zzf|6m&SU~zH;*6R1GKznxB&R?3Un(Tx!)%WG*_zP_94N4B{%{ZW7MiS{x%5)4Z3)d z@q?WTs&-n+3@n7kRbx3hpb8`2ViS~TiW)8}LpIMyFQV&WZjaa|I@k5aPdk3aQdc z5d^%@$c`dhIWhwf!9F3aXwK%Zt9H#46->`C`~6;Rp`5SnyW3QKtcHR@KF^Y_(3ccN z{!KMaO6y8D5g&VoUu6RV zh&2y?Hi-X|9LD~?h0p{XYb_U;qB|~N)(?9^T-$I9NYs{8Norzm?wD%@<_+}W5ltY! zSCh&vuCd8*COS4m{Wan*$;Xho#gaInW=z6N`^mvsW8`Usqem-O^cX!vajmAbe&Ky| zKdU^UTU_3)#O9XvY~EGWD0G8ZfaZojbM;Z!S0$;!&pI~?lgi|cq3kA&TIBv6oF2h; z8U$}^4xaht!p(DY%OL<4Hqm*gXM@oC=7ru#y;aMrd4~A7Rw1k0O|dvKkMfRp#xY5w zgWs`GFcu|+KojJ`(0F?Ctu8*iaBPa*C0J}+UivY#nZeJ3>e#&@ib}N9-X;)4G{1y2 z<@oWPjLJX-yLkF7XN3V7Sjo+c&~Yj5Vi$jG40r_V1R2X%HGKKGlZj#Ilp9z6!P{@` zVnLd#Hu%T4hnB5xX8t^}ri^L1!z1u#g>xk*d*Tf}gKPM>%AreAb;hL^cVLZFLO4eV zh9xW$&^HqE!cysh1PIY`f~4I2CK>Pe0O9g4uz-`yR+@M}VSve1aX3bD$&jEOV?)tl zP4m#M>PRz_+uDv(B||xX8bO)Q{R~Id?7&c?VI1-=)ylbHwTS~aaL+||t9u(Kj9iH; z40n||gC+r7qoZ6ywEO^+AmVFDp-gLp0+8wu#s&&Nbed!(8g1|_+7krZU-yV=2M1qT zpMut#G+p7PHMI=wJIiJ%PL*{pr`YRdH~T#+A@B_PpF=VNK?3cPr=}ytOXB_ym#cPr z1Ju|=Gn8GYZ^SC*{DE;E7ur@&8uu{uSz5OpKM^gYx&?qj=~(9?yK|W1x7_S78^``h zG630f!uX}wEIf`YSJaE8q(^c9vQ_~Qd#Fvb*PPTHOILNR9y#T08u)}pHP###*@b% zAsBy4;cVz#$&##Zxsj&2+q(XO&(|-=lu19twbL9CMN?s_^$>8 z4|bvMg+eU<8o36U0gAgm4JZJ(1k(-q&tikoo;beT1bKwt?~}|snP{4dr?w)Sl>;H> z8}!UA+7S>Sdq(a6Qmv{=$a&kNYMN?sG2BmyZ{*Q z@BP$FlRt_T*>JaVn}9~ASXP7ThQ9NZA*xY!B+hBz52ejGx!g)MRi9|KLuVCKa0^j|UKTtDzp(Hy^VPG# zucdkleUI;y`zNuK03O#RAF(gAZdRqOXeAL0C161Pu7n&7XDi~6o80nHUlCd1x>2(z ztOX2)w&tvSWScib7A&(5giBmBrZBW5FcsAE?PXmNyL+k_+V}MfSh=M0J=zJqsmC4O z_lHk?OG%pJxr1rJW9$*uFo$C~43|ft(N}qkgJ2L9iO-M9C7AWOu0%2ja|42tg)s$t zvPQmU**&2lZ{UwR5&%*}Ea3?Q5KKvqa5x*^kbJHXI$ND#?=CLr-DOoRc-f-$4Py5Q z`&C`)b*6gUs7GvGL0P`6{nVEXjYUsPqr~$|A-Sxh-xFE5B}D@p5(zmlHR$KLO0}MXpq#clH*gwASZCN79JFdSfv}y{`DL^&eDKQ5MYtkh$2Adu zN}PwrE%4}g(n*z`bp6vq z_IYCm;*h6^3C(v!qDn{gu`!x4a|fWeMoTpER_j1xftEC^PiG}I_QQu-avZQ0BFWVO z$4(8$rn{gf((y-yL_5aNm-x8mJOP2Az*B5C-h-z1&JT3fhc~1sxtc#0^f6v!sdab@ z9y%INSv{8HaO-3WIstwU)gj{ybWq>F_?@;;+TRyAHS)o_A82QT4?1|@nxZP>_PS`W zO&urr*^2R{m&aFizT3W}7g~(!Eh(GJ%CufUW)Fe%44^reR5*0d* z=J2P}!fjidGhqK?H%2-x6QH$>B$_v^-njJtglx z8>KJQkw_5=Y7_0xBdwo7l+KP;+Bwtpzg#xXQhGE@5IC!D#x2~$4SG&kMjFU^GFGro zLWhGM7oDGG!jOoNG6**he**aC0}AE!mJQq{UPaRt;iY-yZlJqxRHy7@HBjF8ECAs= z3CG%(!nnObyeg+2TxsAZU2G>yFn+2A-KK`ryY;|=tyI?VT(A{c*Q|}DbzYi+5+hWe zJQv2L-R}NWBV(BDS%oOs)Nx_W$WH&E%$>twEXoTR)S~u#;7S6g5KT#D8%Npb#Cmtb z_}cKXNgf(K>|NkqcQaTAI?#{+_jp!~L(g0*O&D30lg(W=C+vbp^F%q5wpz zTq!u2&jlYVnyy8fY4;Hh>yV~Xm=C+|S9wn8m zLLmVp7Y#}(g};3CmyC(hUD^o_&?c3a^-NW)F{QKf*e1Og`_*#xp5)g@c4PqFz9PUecwniETdk zAiAG9N_L)xFkOT4iW-lHBLE`$q*U<6Iq#Zv>Y-V5SdWPsi%$1Vw{f45zeBfZ=t++@ zs%A^56-@lAw5a$Ojk2%g&2g9y{O{qyZ$)C%ZQ2^miuso^#|aIz*_*mPt1jqU=KKYN zxxCO=DDyBDAp%c1oc_3Xxzt{6%QqAN!5jd*2xN4?T0BUAfY8Y0A*0etOF{kBswF&@ zds9b$NXj-*xQJ zaBoSe`-BOAahL6Dy|D);sq}smV{}8$x05*?qDVuS zX9x+W%zk74BH!)A9Uc2yk&h$2-zkqzALu*!nEGnBhnA!>l}dl%hP^W&IEBWKC+VG0 z0SjPM#$hH|W%RPQ)Hbiowx}#Dban`N(8G!R0d_mp<&{wq&|RU+RV}S@89(IDlnz z>&OrSny+;z@_9rPtPHkcm3&Js6!s2;@Y`mL2b9%VlE#nU#gIt_F^+SVQ=~`f?!w1V zE*L5Gv(`HHea8!=%Rcb1CNP!+mD_bj^NikI7#Uh8jVdrE>aFt&1?By|U7@!+wLP6m z{KfMdTMX+k9?28L(=yq}-Vxt>@61Rtg$URJ1A=0`$+?$v)7!GANW((RhrwW-R}3f? z(Y`CJ!5TDtQIB*xW8i6FnMGvn)O9rJqH1oj(PfSnJ8Bsopa*9{0UNZYSxOptK0MB0 zqTr!-F9u+aSzI16C0_NirDFpCoXLy~ArdeUB6Fd?XZ}HD#02L&%|aB6ybB3>7-+MB z;LZC}^)yT&058KBhVo_#HsId4+}2|84GEw;%~USfTP2Kn&mu~1+PW4^KEHYK2bRHg zI`F<)B8%OEZ%wfcTG|b^D|f@`?$O>&b9;->&}OjpXWKxaSzTi2_ufH_>!Q* z7)~9kQn!G%$ll_HnE%C9j@oS=DV%R0r|{Kueixoi&TAIqB?Df^^u4S?*c`TE^as)` zAV1mF0jYihn)HD6^B@OgPQb?r*vN~1$SCvVg$w`t{z?e0Y?V5e$-oS@(-O9ShCD{Y zM3Mb@wnyfzZ;%Fi0cT%xuZHdy792#rUy$qsQDfBHLHR=lQArPf5EYRed!iCo)5%?9 z?b7jlgeG+ld9OnBqW~C90I-K_ zsWo~3Cn|-nugHnks$-+y&ENwPNHfr=(M~uqeh1hp>O1U<`ORCvC$BCUEhNwQS+rp= z_4L}cXP*B`E}5NH(}ZA~E@Onix9#U}L8x|a z6`R%Frz7vLl_I%EE|Ti=Q$YBS2=nH@$3k2(e*dRC)1_V1Up#kq-5c}ONdJpFX#H2? z008`lJHUdk+;{WPhly3ppTBe`S)y92Td|O8vkAhY$~*3X6(an>&KA}$LwCWl$xzw^CA7c9 zH>vG!7(vE~K4jo?l9l9<>mjP)>t=yP-s^@{DX@q>xK`n*FC{X&)2KT4P-a}gZM`qV zm7Kf+aqi!+f)xUp)u!j9RiX6Ui~$YY+6zAAK(An+RVO(+(;$;+ z&ud{v9U~x;NtCddxPB}TGDQ_)EMJ~ue-+n99D*ICg~R~!`N?j>V0hD3xVH0*)8Ic5 zRS&p-YflR34Re%5e@C^=wEfLdKU-YS9E?xcN1Lth+!1Me^zscJCB8R?qGy#6x}C5X zWv>!dh*t1pZWqCyh5oNs>uVY*7fFbyR#R4834+9&4Ir3U00Yg>G1c2t%fk;2Y_*16 zOYF)OzC*FYv>$IX0egA)#yLJcriD%l14|rjvQDW zq(vub!aPW-`>an1G+raGl862;RZ=Z-GfUtRh)+}hs6asmpsR4VaI#`0vf0wloxsGF z0L{IsCXbryYMZtrWtdHq(nK)BMdC$B40n@yIKGh)t>?~jVsx!A=1R(L#DVex zqGI8Sj7GhD?l7ME+P3+koA{LCOg_#SMXlq#!Pzh&cuO}i5~dBEsjDU2S}+-hD{TrD zfC&UNax0V{br|i)Q5xdWj!*SNNZd#tO^xc$kBP@CdySu~;q%NNY-a42>0zMsrd4KMcs#Syx87+NGd=S_=f+U{e{0+N;4swZq{b*i>Z8|9$;186yoEQp*=6M zis08RV$wr*PA|lrhg8Z=zJM(#iL(DE{!FVwbB}&)p}9gi=^rz5JVg&U9~o-ythMFk zE>2CNi(j&hG_C_4>4C*8fjaGo_ttu(*YR9F3K<#;4}>NM(VAQq!0R@krB4lI0is9; zznM!tH9&|L+f3rN^==`9It^(+q&Nr=On01iE#@H#i1B*1VMZr3?yejcDsX^XL&If0uMp6=-v~-Q zVCvMTn~n0nS!GR{FGPLWWc5T_D;GepK|qlGZ4vUT@?EU3JODpIG{3@MD5y4$BqlsH z9?3k1O`DFCVWwPJ7gofs_(T5?12z$0V}5K6R*mo62Hjl`l}~+V;u64je20YSJqw8% zDxCFA)vb6ZNR?$}7EM5g+qw8W)3{arC>e zr!+QKJg!1{v)~4ii$b03djjf>v;zqc+)o%N(Xi59cdp!!a3l2D4g?rVil%`IVK9g8 zh8l&I57FHetCtc(nJiAd5xz}P`5!n*E4Rj(5a%wG8pWt5~w*vO82Clnj2@ zKpb``5$z!!p;l@*vFAKxZH+M-K3LfdUSN9wemfGAXN%W2Nn$@QnS(e;R@OgPfiUk; zm6P+6qQsPbrRiZpEzm6$n=S+1h}}Og1tjjRCB6I+ps;t?5#Xg6SD(zZQFnk~Ou-FY z`EXP63;=hdbn)_%hG@v{p_kQlMz++n;c&7=t&@k;412@nn4uGHIu-6EO-8l}3_+ux zeifColNRjM9PBk_s%}UxO&GDXb*wZn5gUM_8f+MS!MF7DVeh=ip&c4z8i>%8V{2c$ zD2{%T19Yzxors_>BegpK`kQ{+A#eoOHB!;Gu<`CL|JuhZZ!t96;rB1#S96$3k>Dd6 zVsGJXh;I(g36Hfgo|)z#kjqMQP$cjv`sHu%-QU9#Q=^z!xyw*1EwYh1w&9#x0<+Nh zG%^F-j*DbhU{8B8G9DS65U?7$_1u-7`L&INGyw2323J>aQX~}F1rUEB;9%`>Ora&R zY0VJ+5|&YVILd`;?GF!E-2zO^y{G&Jk}T@Wha?PpgK*_U3V@$Cq)Ag~e^X>Romz`a zHlj!!H`5|obIY-JAJ~Di<|je^WslQ%i*eo4x)VeQ*g7dH$g5?o%Rxnyd)3IMS%G4q zfCQh`%j(l6|ARFjIC4M+@VMIODK6uT| zUF_l&`6CrlR*$VICV6p&N$z5LQ2%F5M&CB0K~fulR^ORe!NvreG-ymA%0$NuB!b5_ z6UEeal1~L<81~PC(Vj-rP1);I2*9=JH{BtocIQ66vlL^4F{S_r_YotbcI*B?zOnmd zhvds&R#KBJ$-9%VSzbMI%ba%saRl~;GHBSqUw;dyR2&;5)nUPD=(}zb_f7ADSX4E&?}Em^!l2J}R1qt94{cMf`s`c_8RjIE zUvS4gEPrQ+4FsyVghwkP1!KT!2Gl|!oJplFc}GImR6b;n(~_VdbIJS;8I1Q3_gaOZ zP}}6nZA*H3eTF!%Pur{Agktxqo;M=3gGti<=1>bMgl8G}=SOQJWz$`sF>;BZ_8XJZusY8w^}<%0L-(aa z3uq^8)fBK!!zz_x%2u$3RWW0JjnJ@)^BaE;bsmtyO87^j0mb5>O|({HuRxv(0LFct z`e0p8WB7u&|8w-E;Q?W<{?ZsspZa5~i*1mq>f@7l8gz2_KTD1}2opEimQxO4cOt`i zIIE1qQ*5hP#naScQAt5(twXEcGio+M@;qZ>e$w;HdIR(Nk)O0n|7b@}w5WaO7D2n! z(N%FR4!>zi1ET?3-bD-$8Gd9}i$$fK%DPMty{!;D`N&>J#j_(Q$45h_AnSSks)CDW zzlcfEL--X7bAeEE6Qwzdy&i>#SBzkV_DiuJE#x9&t)%F3IORH5sSmYyhA z9v(gz?N6?ItG_meRHT(tuCD0Ld>UHe5`w+$(uuwRKn`9^8JU~U?xptl8zEBbG;=Nn zMV=#J{Ky72P?fQv ztPhPKt_{n`3i0&4dS!;2DNdjLz}gv=-J3KmgyJq1# zpt#T+wa3~zRuy}DOa5pm!BUTaSaD;$CmmRyojtTi=Bp)8qL5!m zuWNrZgF*Vw=kO@l19x9Ky;+$64g;#`kbx}KfO_(JSNy~ho7~|qjA!!{dXilSlVx}@ zF!eNJdTtOa3lQpPg?AvSrw@`)m_}5%1;@1XOSnzg>w`YghA0Kid{K&eEdXUug)8a^0F$jw_1Hk)Alra6ugLE10I z1l#4sYHrS;>j#-G&-~=#Yp#P`<2n2Sg4X@iF()iChCyErbk&tUjbLeg2XCCtnQAI4 zY;l$ABzO!(3Q;DN2S-RFACQBjjO%WU(_44~a7nd}Fic)Q zVe9o@ls?htbK~u)Nt=#zgbTskiT>%;qg0T7W@sQ#?^=2sFcL$xfAg}aGV(ILQ)9_a zSukEsa^Z;zdZzfdSYQ?M$3hL7M|Hn~^9TPQ9a<}Y5>c=Tvr zhh({R{a_ej?=JJzSaKJJ;}U<$2EQC4@OVEK)cCC+&&}mOym+w~BVVc}ioAdLuUNtZ zj8+%eNdJT5o=s(RO`Y-3;A_cD6tK!ZemIpxr_A~kD$ORgcq`hBx33~raPG`tw(DeX zCj?6V4c5q@rmQsSKFlwoLx%hvs4icouyq1D=R8} zWC*pEdRMs&^44&79P|6^{)%$02UmiG-Lrtf5E@JPZoRKWk7*N^dlP{FxHPVf_chOW zFv*M4$g#I58l8cZg~H;K({wl~_t;6+!w5*UR!Oz1#hs(`_#-~yPtG(D!(>7!skWng z{kM8-IyoIO``FL=yOXQGn};uW^n|;cx3?!)PjGT~bM)}>@%C{Bs|^0$ZlFLTUTiC|fAR#IAB6t{ zx(%t~C0uF%f<4xSjM+9DaDx0Xie(&?24NdO){Z4K=QQLQVW3VJ4tI0SQ0XGYiuk~^ zFV3spQra*pX;IGCEDD@E)X8rFl39CMBbZ@DNUQU$CH~@7m_i4NfxmCQ+}@xFt#Y^( zGiZA*c?46%CE&*f2x$`nM0lJ96RQ6ufwG7iL+x>7G3)cJpuTP2k*p8UxQT-N0-0(J zS5*|3#{3X6Yz*C8FEt70S?d{HyPH>k9`976p;&ka^rYJ>QPDj@T)&(o zq{44$_ON4(M@isE8rA}d!O<4ez2ELw&TsZO;LH>ti$Q^Ox`qW7y5!zU)_-#4Ga|Ct z)A^9;47=yAjKJpB9I&K`dh}|2X~>G&m=-ZX2_f8SQCnPK8Ac>@xR`C6=IER0Ovb*R zbRC=e$@mWcp$*)Mx&M!kEUQD_Q?n-g&VU-D4**ydy)QwJKyN9H!Ma#%8dkILHcmiR8!4K>L|wT;8?7o-<_#I}_~u@K zhmbt_Wsw9!mqsb99Z`if4ht-lm)MaZaXS-e7{H#8TA81J-(v7yp%feiK5rQGj_wDG z1s7^@3fegwrjCKkg30?KW$}yTpwOm?xK3_t!k_h!u;`{@zN!sx)(1R0dC+8!ZS7%R zf=TB4I(4u%L>{{xorH8PRK6hc4cca0vY?CY>Eg7#B^n)cCt-YODY&)5Psu8!`CH8B z5qqizRcG#G%Gzj7`CpXplb5T7Q$Sa*m_1gfTn&p;U%2RV`U{AbZP7OXF}PpQgAE^V zr-{a*C$IBZ~f z$$g>vN2p;Sr%c9&E_th2m0)~XkG{7RvuJ=01Oe&o`HEK+f{hgUYRn@141ar$o!!|C zI-P<{?%SU%=biYlI6(7QJ7H6MzyIP3hj_V;LJE~F+=)k38O@X0loMIv0tdZ781ZA%dsXb1zV&c{Y#b|Svazu96uRr~!yNY6ip7k5SA-16vvi1AwpdJx=y)v+#5DJXqm{C+3c^7Q=D9FkA z1u{YaqKm5(gbNImvma^Cw-5rq)CV+#F~bm#Hp20^`2+>rZqXqIXNQuSerD+I=F#l1}g{pSpx-& z)~2d~?`ld&@L4PO5z;n%Rbl*4R?kO{B^A7Mq)fY*U;wZ*!Pir(U;kXTF#~WgxXH$# zjR|=}3S>T6m(re+vjv_}sGjTQY`X%n*BS>#HvKCfMSMEd0%X_DN_hfXh<8_4Z^4F#7e2 zao~^ni~y_)&ynf1buJb{J>=GEG!wd%!`I43Y0I!bLN(Hmh|!pi-}zY1c~|SEunZRI zPZJ)PnOxZ@1l-R{uiSD78o#ZZUW$YZwdx$8fVioz>~1Eo@ndBBJWl?_sHM5_{w1j@ zs;Vh^sMj6ttY&-3n#@RbxRK~RzANtuy+Xr#H5LgCxd(KEd1k2?Bi}jy-sq6?+a|YJ z@H?xUkgL7Cd1Kd;-SLHoHgoTWRE7eRW{DtEjJ%C=c54bjHVR}ed2tjPHU;9xlAnoy z6sSl%)3q*1h;=g1MOiHf_AqCXDuGo!Q(Afd+N^z6l+oS1th;plxFGfT%HzTZ3m|G+ z@RhV|cl*1p$P!@##zVDM0eOxwlsC(^WkDLSR_@AL{JD&6HH1rQXoa2s=2D6e^QU?) z)4y@&`aIM&7P3Id69>G?UX)38eESad&Tj~Vk{U8slI0l3W6R>1&(yS~pQ{?R+HDYDSxoot<1_CiopaZ(U6(Q~`@YvU)RWC#R4Vm>4> zvAysy5Zsy=M^B|cuOWZLp8k^%e43iN*_T5|aG5E*l`e?|*?1;!d+HcJti0aD6HhJp zwqSE3Od|8-C&8cX%Fam;$82H(jKC%W+03k{AhA?blwZ!6hy)~6)o-3*&P#6Tk?K=! zM)~JcD=Lj-$zyQ?FnNeYIn><1jD?b!ldeLL_V_=u-$7z`PIRj$9lWSbt2QA5hO`K% z{FO58Q*z5Gr83pq$-mPcz>x$kGBcMiRnJrLmVw37sq@9xivH#S;u zI~^y-0w9?XJHB}BxDweQS1tVeQ077`b0{)1Lc1Yvc>}w-t0vOL-x;4dO0ptifbrUo z7!~R{N)%BfLN!W!k~gNqRJ-kHpc$}Bu@CxD9pm~K2|WTzeMjmLHxv{6!rqr)oP3Y8 z{IIeBK%U=D4?+ajRicL(!^h}Hed#hTK&>dRYoxsGgbH0-NR6Int0or^IrfDS?;z#~ z6#5gJ#XqV}%{ZG_ z#Y1U(ItIj9W*(mcqL|zck0p*%hcE1gVkkl$qcsQgK< zkp;b0tu_H^GPp;q~Fq?{MZ#fvy3`Y^%+(eqMRQ;WfZw}N#CQjxC zJGyUSp9EbU;FJQ7_xYz@P}+8bZdrBd;5MCJkNaI2wyamvr5@#zau{03oKwH9BiGfY z>PEqOx@0_bn?x@^2xV`p;jfUzJw!YA$&6ueDq!mwwRNWmz#jQkL3h>eMDUF#>=_E^C zh+25@x!F|0pY@$ph{*FN_s zq+UDqk9xs)3W>ntv<@G;;ZJZ~oELC(RRX})@S=m1Np+9q$FCOINk0k@eCU19nhwx!5ME;-y)!z^uN5&1L*6%Gxp!7`A zBt5}YlpHG9FY)%7T&%Mz*jt&awO+EwUWBm_sL*Td`cAP@K1>SGz%pn+H&~7lJJ|c9 zJv(UJs$zuGFV4?OO7Ca zuo==udIs*@Oi;bFY}+ao0SKeJfQRSvJs&9TtTOR(1q-Qyi?q3!9Y>O} z5@JFpxLRFq$*4!BdO6Nm|6b$1LtYPMXP%HIC_ld4-VS5=8Sd(fQvVK2L8M=Fl0Wl(mFH#t0ps}%lA z+^1?N!(~B}G^O5_dsGlbf5|^z)#`1zJZ#+0Hl5$kiJ!8`IK^#IA{SWlPkB#+(h;SP8AG(esXg*~p z?uB1?qDJp6u9c85Pi$u=(EZSYk;=5sx95IQrbz+WKHLz;*v$Nue6g)hokcO8_MK>Z zo@PA|0pOV@i|r%~INc}X%!p(El@F}@DBZFHM5Y80Pcr>bm`nNSfA z+qrLE8!t+qP#BAr;w_7lsFyRy<|m!PrfFxTe7M|H~ZTNrFWW% zN1jlg;3NTl-Nax08wH?`SS`b_0Nu|D^*en_cXO}c;V?P5{NSPIR6f$e!oI?qjJ6d^ z6Y<2eb2t;HUmuDm-=+Giv0n>y$E~IKRDPz&$O?R~IXT}yiuwEvZG8GiHc}r0Cc@m* zQ**FYFB6?PjLPUx}TR3HRU}KZ4n7p z70SL1+`m5DY%{3Go2#Pj7^Hld71EZoae5Jk5+SrcVlQh8f2&agoi54)IP5KI ziYJk86Uzh)cN08=w!f|*Z8x~y0sDbij5$zl+H^sZzS(yrdk9^)Jzz+u46B}GW>(^A zzG%R$D45lFRZr1+W7+s3g>t(}zVsI4zAa-GOUIQ58ZpI4u8im}16C86<24!P>) zyiF)X`o8SR-)@P)FEfs^ENkA<@z0=0ene4+0wix9LC5&CnZ3kJ z`r2cC^YtH~;MC7%mk&oj75wuynsYySXS2(ZLvSr*fsfSLA~e>esIXx+tkNYF0jdj3 zT~ImuJ|b~w{z%ETEd8PiKhmU?Y$?307_izR)xlGyD|pEEBtfrH2@f>FuVRI8*eDf> zRxv=4{K2~J3>Czajp6SY>t*GZl=*!jrnVc|F1jFqW$`_cg z&$>c4qCdP&KF5^(@M7Q2B5-;Et`i`C5sh^8PBLEnyp=%{sox7|71KKDYG+R9u9;5! zdFal-@S=(g-ga?T@1#`$14HJ7J{iBkBMgB>EUiJ z#b9+zQrYc`mtpW7IUS(El{}+&d}Q&3F;mPYt2YQ5o_{&seV&>u_mi!0O7_1eMOu!K z3+5AA_+%T5J7whj3n_R0MH+77=F%PCI0}(j#z-}oas*jpw3m6oCIV%F$%CU#JE~}S zAaHbn_sHG$?EOOu?h(ySIKNtRnL50rLBNxSp&kdYH5$#9A2AzK+&w{aewz?!5Zp*_ zdJ?6Y%ZH(u0`ZrwZbbe#F1UbqCFuu+(!)JXY65mSA5=HNG@@*RVwcr=Pt~pzr4H$| zZ32yym0?`Nwcra8^5dvIw0oe0*x`^IHg_g~hp9@h_{%CQZwA|8U!&L zPm&i}bAHeFmCF*$;24Cr5gVz zs}&>bcrosI#Cz$C8Xc0z_t|J#(K&Qs!Sz)j*4>d=WTD`7Fr*ucZ344%81e9sN*q+< zNOWc4#AW>uboItWi%kaCwv9Me6l0Dg_4}O#{fV%TAPSPH_;pMT^Zym~jq!E8LANI- zwrx9U(%81`HnyEKc4OPNZ8f%y#%gT5=YQXOfA`b=wr9_N=F!ZWwY&;?rV&P4e52{X zY+cgWxcq~R6B{O0+<^Kl9mj?=qGuxsHv|fr<$*LIME5@mui)r8Kveq41wcKVlXup_ zu9>&wS+Lp1)2=usEt^78ceYMU8Y}T0J($3DRq!p2bsnjlh1oCu;(4ee=zpunVB*U| zXq(Dgxh_FVdc7hs83{3FKZ3?i;gKl6HvSe$(*&^A@fzgNM+OBi`S#lo9JVk)Rr8Gk zh}KP5P**o4oH2zgayJ|Rh{2MDrkD?}Qatrwq&q^=+O{|yOYmDS{jG7ia-AQC&Q8-?GqTEg^$1hKMpBhbi{hn*F8GI)$_T;|= z0$+i{wAR1FP2*I%zBnNWe*$yXh8XrD`T@u&>u=KYC|K+Y1^JAstw#6CWaeFQ-b>)o zvL(nTubb?qQ8L=!5-#vMnI8D@6<27MwSYX&6rnkLwXojQ}PmTugu-4A&Gu?Y5o zyMbfn=B)^ndiqSvu*^%}Up`*)R}kWzb&9Z+JVmkes>E9HpX5 zRj9r7ML5G*CNqHt-Hmln_t=#0EQR0My{xmbV8ph2vs?FEw$3Svyg%XV^;amB1M&xQ z2^kGPCi>0TJIeazi?txX4*OJDu+3VTx*Uue5PT4@c9B#LEd0!SvGTyxJ;sEAVG`ulk!vtn#f6Tt znY$X!T$Hpp;RWj%W8(>6D+Ct^@G(fC0wu(pLnT{LzrJ_;#kR8wABf`p8y$tqXdb2g z?XkNhyR7X<&oC<6!nbzuap=~~fec%Z}h-m?8h7{$IW~)2?@siz-rgQP4AEz9*jy(c8=JKHMt6cz)Z0B#5g&;%qoir+&TLb2G}em&Cou6JQrHX)X~)Klh0sp8i;V;i z@Oiq}M@s762Oj+Ri+@)O;HMHJ6}DW?W$b>XW3f4+U)UB7@dc?E8LR4Szl>61NaH5} z0X0RiV(YfGuS&&Pe)%FYN4o3J0L!_%xl3?@#Dx@tY~rZZ-6+@1*fJE7F{R&Kaj24_ z$2PmBzBKaKw3xX9AmSF+6YI0^M^=ja1Ov?1N2g#_0Q_UJGCN0#bGvtH9&s{?e+O}< z*@cOI*P7>DLOT!apONK72S(^~~ zzP9>^-tK4Js`TRbd3ZSWi}?(DvVLIhJ@35}Ur~Yit__g@52!r+XLE8VAON_VRnB8-)tz` zJ^z-ty(i$gpYtVtdqcMgQN}2bYD)w<617#RBi%DkiFg4cq&n|Eb$+7>fvzuOY^wnz zoThvjA{H}u;MHIQ^!^7!w`<)D6Ufu<=+@A8V$$V^_IJ*j$AgF)bLUq91l`(u)=?RC zb=dmNYY3ZKZDbXpHLCG1ppuH`&~_E0QcgMK=D*)CZSB-^dW20clL+80z5cjkjf9#m z;Gc^#9$%UI+l%hskIg;^44{1@iu%A!{4pUWY{8$6!4arjtz&P|Zwl2@v$kip)VK4;qGL@`lCNk1#;(NVE*`ACQqV-_~{_sYN? zN`WxGDggJcX#2cn`n>7I2_4UX8i(P`-TqBUySKT->6D>9R!4t;5{Ea$Dc#O*BaVe+Y?e-+Hkb7RgXN{G+OdYTNImq&Z*Y@iJ;VwxVi#Vl;>g7 zw-^Mw;qbvM@vz>bCqlpyMutV@wRWbXceTvHpHPyu*p2g-MpHEgSN0AjYq-d$)n}YM z_c~*?ijnNXgV>&z3JSubVFjk7uBK>>z|?GXV}12*r2id^ht|$>!2~`!y0sio2{MCP z;@ZV|bmZ-4ZnFKT5u^*w37k2-xTJ_*HY`88WO`E17*Nd@Q{t>Tv$}JUI$f%doeRLo zAkoiQ7zeXQyM4@fK2~0d$*lyJr;4>U0KQ{Ii(m)$h}dwGF>W80o`uqYI}V(E|!qJgf5(`WGn4cH!uY~c{eG--U|`iLrFD74A*e3M}9&Q^-w z1rRs0iAy{A_qiAA^yn$@^HyUq(J`E&<~S`iNe#YG4cg6MpY3duU3UF2&t9Ljcm$p4hfD|GW*4Ulk0yDGn9p+kC6Jtkuob z$JEr$Do=8e;HM_u=J)Kfs3j46BvxONfRE1qeeaO1G#UrTAA@tE)=DiT+^TWiGb92j zRk%c>tGNYk6~pM$fy&;g<1=PiWdkN~CMNM*rP8$0>WFxBJ0QdlJ66{**+Y%r16Y2jyw^43p-Fj<&?6$Rf-s0#&M# z?M7ODT6USG%U#LllzDQZDh&Q}?x-smBUZ&j@w*D4v9}L_9JbfNNknKGhuz^ym^#9O zI&&U~M|EhHl!UVn?qb0d5iyRr*704?n@%z!e=Nck6I=C-XV$}7=%wXAKa<+~MO+c3 zB1^k?*b|*sQ1$nVfvFEY6+LfLLvXIIR>@2}rE4Bf_)N)hZNBHlnUv0$E%Vpn71yq> z?60r-6iBg~tcLViIHK{WKLD!Mx%Q}^+)=Q$#-cGhZ7S|aVcOWl0!@BOj4+K*rO6H7 zn8?U>$-QDib-*sVy6+_12vasm$xUC&=hWdPUewwg+#vH$JTmomGQOu$U>w6;MU^no zc}c`@w@Pwsh$i^AThmxw)VONhCV{GfLs|eJJ}Uf$bH$z}_X%?_sU z%-{>^N1d~C#^9-0;rfu5?NjVGcK(xGlpI{lMe?66K|lI(-08G|dGd)UW4@28HF9=l z-Q#tv#w{zVLQ0Aq6Q&coW;m%i=hfq=$YQ2MA~QUI6Ev4lpG#sl93vvitb~o?b*TSt z2(DCQKb!jDDV|3&UD-dwv-nK<)6evje+lm)BC^_;Og{s!-D z=@vTHa5N_+0uhfTQ5Bs0B=bs-U!{g80a$LghIaP)lso>&5-VNP37so_fuCTULXrx0 zz4dn^ZeeHKnR?f%Qa}2o+%lx&`*?TidS6+bJwnzhfzVug9B8?NE5Yj=g{T^c>x1(k zk5M5a{DmXftDpZ{rvJ<~5BxK{NTi@$ilbo|Fejj_3;^-QJHKPdn)#+LI}AlM^3i={ zH&{VH$xEWTM5Mw6dF4h$1k0s9kXGSuVx93Mf8QRg^JfE!sw+9|}db+=?_Sr^0fB>@d@u7A2(!lt>E12Z26`%aOtILVTh(0z`+ z5slyCN?fKyeh_rx@Q)$_hkM%p?;jj}1dL-)b;5)Up&!IVK%P zlgn#sDRvEkL}M6z05<@ikjKmlTpi$2YCHzA&@g*qtW!gye!YSX`r+5Vx8{CuZlgAi z&uu-Tc{j9wU8j6V@VFLd=I)+MnxxldUvRFfJUV5ySKCJscLzF888J0i*9xYvsF-$S zp0arXs(0H@0*$7f1?g0>0I<(C6E$+Q_VPUy2=7muIe3(j2BH-n{`Ro6O>A@FQhlav zJmzM<&{IcWvcg}u&W=O)v0FzuyI+GDEuk>PA8TJ(@rEn$vnomlw&Nq(>7)bv1LH)G zB8m(*>vl4B_L9H2J_(>VIvCuHTl}fiTAyV&Kt#4{Y}0w|1agqfDBH*MBEYixg1wW0 z(j-~=ik$5X?dWDQ+xm9~6IqFUf#hV2peLPfHff|UT2^<+?h=3yt_t(I2ly5G;G#L$->ylY~iDhGxlLYfS7v%!LZQl zLJtwJPgDx4y172tt9}giw%mzV)2D9U2dQa-t|{%eov@lDZb|LG0)CV~^egAH=f4dj z`Srikw|8?5)bur=360Q7m8r%P#4@&5W#CC9 zGF455U{7T5ex;P5ni{zaQ=c-$>0RHh3I^4T97L-n=$ERAs~zTDzZO&wA~q$WM#=>; zu$D87w5vd(dqRS=kOF~PLdXS&A}0|-$)k5$f17RwtTpeJOlmcfn66=bWK4zUPnsXi z)a<9{Jp`DFFIeWYPBzHDGIAf=SEQe7=$Zr5a&rbSgv5uEDQBPlpR2^UyM%#a}XVj!T&_>93hlWNK!JbF=+Z-e-=4W{wS^Y9$zRj$aX7LYtZU~v^>&U0w zc3fhv9x|5Gr)uD*<^jcUVjWK4G}b8xMV=8pVQ%cUxxa-QF%^4hAuBckW{LL)K(D7p!mm6#hgueXFJ89%MLhgrm~hw~ALUv-ulpBWm`e4}0(-;I>Aa?4 zvI#?b8K>KLrq(ar--b`c*oSx@H8^<<5qbn1cz0%R8`;<_?sme~NBvr-hvz!)upL}riITuy19zyhazC_$g?vigobln3o`V>zcUES?WI z$VI8BKy}dMtft8$tQ0X}GG_lEf0}msVEbaclV5gG_IzP*Iu1{WZtK6i3;kX z%GQ5}nw#*k`etdWY<@;1?42Y<1mFnm(~C?Oubb;zo0AZT@*Q7e3SSge;4ulTV}L@F zo_HfIN!4joQ9h@^V3P;rfc`CbFr>JBw8t=%Qbf`}k7Yo5qPe?mPPT5nBE>-}Z>kq$ z)(sk#MdDgsg<-A;E3ksUOTld|Z8<|__O`}}?)~Oh6{iH@xK$60J>Z{HhM4iE@3y(*r58XQh@^y+Tk4$kiqQIA}DH_m_w*bYmAe?&e<`A4c96z3!HE zx0YS6k&$}sYCRe17+e};RZT46%)QW(V(y21ngw^J&t#fYzW*A*tNq8BO*6radGD8` z@DF*_%`?$;8t>QZK(0KGGZ)nnPy1GgwHF$TN~{Sy5^ zP7kDk#$-7{9F|FUGu?V|d(RnZe5PHNVgjwMTgWCveN@gh_$qqM(t+IQsUs#BnjUVHsllx#N0&|HdT=wPCbJ6~DtFX5tE`)H}-^Fj^n&huL z4P6UA&W3k6*TN8$YPJFAsX&mIY5+v|6QQmKZo{mi!;78j<9~j?h&;aiO0<`TRdBw@ z=bD4>jeL+bDnaZB5{6j6YRr-DCkI|F$8C7=-pPp+vPi`x&WBeq%^a3X3{RSwtUPsN z$2;6**oO+a(4iQYCaIFo_+j)Y%#Z=vRxZA+YQ;kNY9L@D7&2IRp;QYC zb%GWBx!(M{X3SB$$&N9V^-&iaU&COwn@ximX%6%myfLE#QA>#iJY@ShI}~wqN+NH& z_?Dt4wPWDrg5yL){`9v&#iB)veB=5(()g`;hu^?6sj9)>=D;Yl#=6l1;C)?#t!CLw z-=i_<1e9U4@PaRBmj4^*k{~wtrtaRs@;2*tS;Y_`R`uzq2^LKT6y3rqfbKM}+wGxo z{8>W6i#Bzg(B2yDs=3CO<&opk(EW&GGJ~rFBM+YW64Nj!*6*0F1&VSdmoVh4OW-;#L`w<6p- z<2C=P4fqv?IL@W&tFYQ6`mj3j5+NsEz*RW^5$khL?v78O|+KoNG3P{1A>ddT*a zA;Cp=z}bOIJm%Q(SBA_u>S4Nrb?L=0jws;6{OocS4{6}tD2MM`=R<+x`@evPg#Vv{ z03loelCWao6dtY~PR@>=uAa6aR!Jv&ClJ~Jq-Z=TIY40gSUd^OmC0g#c2{aJ37*71>;{U(mzjK z%QPK0Mc@ri&CzI*iXuY~$08@4%jgbZ>x`a!8n}K1ul?b1Lseh$^=;pZ`sIsT?kG=o z)*5^7STwmT$2MFNcG=9b60q(mz~aGcvT2rMD<^1u371Ju!fmgKVsN0<>Z|im*&_o) z4SvSz*+a`t7Q1ZT%FKJSXo4?>vKm?#Z^rYW)oh)POhQX7~d=Rb@!w+(EF z&$E6HJ{Z8MH^fE9Iqom2*>gOEpV_8Z)*eI=Ix(rDIC1WUJ1&bWKr3m8yZ<hN^-? zhgm%x7Baej8~9T}TEFg(EHx#ix;g$IW3t4;t% zAq17NgvU`JHhX9!y_Qt@wgLT-)!19VZl}Kp@p~WD>`a@EJ1U>{`;PUWrM@_~wBl+{ z^TM(jFHm7heKRQ9S?Dy5xs}aMqfQJByMJbC9f0_hm+C zUn`HjoK1N)hGe!gxHUjPqe(i-e-c0sa};(5j&k*9{yoJknQ9^9eV*geq}BAi=HUI_ zt~HUf`6$Q4LT0d}f>^t*$OV0s45m7LcFrS|V028sy^Y+f00yV=GgJ#);@$p|WER`{ z^&#{}!E!|12Bv8l2As#ik__o|*QGNhL-lj!BMk{?rZ<5>$pIOt#473Gk(a!3{u;L* z&bVK#OkAL6>$~5~*JGIKmQyKAUgDEJ(m%@MT$e2=Om89BWdDj$s;^vk@9lJFZ2Z6r zOS7sb3+EqD)8NFc@_k8UEtNH@%bJuI=eOxsh<~+44C?eslzfIy)5Qz%+eR?_Dv%a=_ zLD#^IA1UFn3L;0HKndc%P>T9#YeH7762p0TOSX_6WV0Xt#*7v&bbqr<`}X&5F?mEw4XX{PrpnZF%ll8lh70K zQ(spE@>P^^9xM%P8f;sAk_JJ<%pbG-XQ56I+rPIMX2};<+g%!LUB#_+{OM7G^s~*D zv?Gk*cvc5Zw0yLMbW}~?np01YM?M&7dKT@J#^w>-S6YBwr@d{~Aa7wx7&$%ilIWpB zkl_H4uQ0mS(oH|Hb=wl$s{-+Q-p>IYAjl*bVqDNcgM`IpEo_H|0zqo^ugxZJ_Zg}0 z{KR4D*;iBn>nT8Z=5qyBUzSgoKMCQf0of$^6y^b58K2Jw@Vi zHJNBJmC80+(BuiCIkh`n3z9uowi~0h?a{2XXvHo7(25HH8zpeimen4#mYRqS{f<6k zHhJ1L@xy0)YFaNn`nU*P(wy_41R4VWS2Fy+wSwH1(m>qR(qVKv_<_on?chNt=<;RU zYPd;ta7I+-aPfk3U|{B7NE{8R0us5G=AR<0pF@twY4%AQjEYa#Y>MC@lS*LWAd`bB z%z5%Um*u{v_r7wI20K%^=L_*4y{%2H83tgy2MF5;q$#;v?tC6*rY6g+V=m~I$`153 zrhHSKdhxz?^!L1UM&bXeu#w)hFi~j@w?{2?J(pgIu}AM1*4>YBWm|jvC1AR#feN6x z2|AS9AQenV_30D~t&1JcmFDwL@o)KlEw#w9E1a=7JPyPJmq5Rl<~4>yt;#;$N$W71 zguP+$X2h_Pe)q-0TiP{1_%!|VIYsv9vdeD9-??Fx)1YWNEW_zRgS8fAzO1?k?YAU^2AZDba+(koIx^IVV4&V zB*0qAjr=8?k=*O$ovUPb%25&UGLMAI`h7ybXXZyWp$RKt%|MK7QBW15MQ7{N*^nr;8v5Y3B`Tkn@Uhl~j z-&L)ri?9?u#=GV>*x620yc16A;Xy=y*HUis0^x8H+Nm77B`IBKyFvN^5ZZ9Q8>cY}^9o)L_)C z4kfxi`7!@zPR=)}uX>w)+o<8xV}x8j`5|T)w8>=*c~ztC`(;ohdqr`5T-Vv~txp%W!Vm%0Mw)#U04W{+&Z5!72l z_;jO^79)M9h6HEO+^>eTH;LqEbew2ZptCKwxtt_Qlm*=fI)Cy$b-Sg7P-7#3LKqvw%TE?SSf5s`(pTIV-MdW8 zgl^zAMl7}qw3x(zZCY5f*^O$^8pw?5nxMMV_;bx_hN(TFw;sSFVRV;gp~Q z7FL;UEZ+4#(p$m-7TEk`nvlRLLR+>{zGzMqRi36it-2flL8Lwz2do1RHz4V9gQmO5 zF8M0TR&c&{`4>%el~XEqp+A~}$8_T+6Py66GL@P6_+g$xW@%{r!txX{sREuRdx>hw zhCJ9aB+8GP=T)^>Z74HMbbg{c9jf`7dZVNtq`{kJ#Jl;!BPS2a~)nF^1`BcUS4Y`~hyP6D<@eb_Mc{oaI@|mcS z(wLsTivDsz{c}3RZk#?+D$$4kNHKx1+;vy9r`XqtfoQq9bF`LrDk&$Ivq@bT%8x=R zEiqRc1Os)o0Kf%I12Q>9s{)$~OB@%^lUEjBPIpg7uSUHE8I%~);vI$d-15>`)Tl3& z$SU-T2rKO}-O+iKiIYKtc)qYp^=AZh$2S<)ABJ23Gj2y6Oz1;VXgm{12yGjwB?0Pe zQDv99(3sm6%Js)Q00H$TFmeWPpusqdeS}zM%8doyG}61*=f2hQczNR*wxO20;UEnLqahk1Oat~05BZkJFo(>|B&aT+r{fj&4fn^e& ze;q$D)=fXgGh`J1RO8kNz9W*cAT0QaGAxsu7_72g@U_&s0KU(Ubib+s9muCyd;AX0N zZ@a0AQsQZf-S3F(8d18;)uk-cjxzIMrX6+2rwqLa53PN%Y1|lrrG7(GOeAF14yL-x zRN{eY=%yy;!BDV~bm{ihF>J`0EOq$4JDJ94k;uB)>)lyIpK1s&uym4r_I-E=!M_`* zPn!9D>`#IR)!^bYZq1tUN=i%<<(b0PsAP+2kdcWkpk@J4Sv=c(S=F5*GtlQe!J@5L zk!im8OHd*TArin|&V7jW*njI5kL(bBA=n?dgj%)ayCV9e&V%MbQ#k|<7%@q^O_fka zQpEk8-s8Uv%^&~#zLjLvUs6{Ll@UY8=l!$L!%Lz>R*Z)2w5X7KoJmk!)?@I92BI*s%CMhrA4Fw6Q3 zA-aSd)8)!q?idNzUr(3rw0{tGmwPxeAMYUhG#!LwV<3Jb(ER5qYLfpRY~6tko_v2(@orz?60gy!X|a_C8C&EI`aLmhP~5n zsU)U{7Wagn_Q%s_t3AknUiOR@z~mRK2@TW_%1ew}{eSeM|E{EU1^SMDzh@Ay)w}*> zeth4Q!&sG$OWHBZt1V$o_=uKFDA{#XKY>6RfKyd5CAw)HvrE;OyRu@CfN(oi8}M`2 z!O}Ys<;)e*=E&(9yOTIACRudkEVvBL_3RY~?EmBtAdqPO)28@e7y|<6sk?SrLuKn= z8Op6{?`50n2&9;U=2y;yAmFpA2GK*lJF^Ksk7`^-;F zk~k5<9PT4enT%&(%2)V!HLDT}ay|+rRW5izmqS}Uj)(--fe|lANG=I-5!gNC1*FFT z6yVB1(-Er0YnXH->QNZ{LOO2AtC){OtDF$jY@&+*Ipv~HIXd*>WxZ$3y(|T2=qbv~ z;#=~b5#SFQ(;`}6X=10hlAIdtpjj_}ZQa^i9DCwNEi6r|(e99_EKSgheph@5!EK4q zIsN-r3egd(KIo-?bu&YGz1$!dCTMY@# zSG~z(|FHuwRjcH^{reV9NnSj*CMRUw>m>hP{&DY!aQ@Cal2KyP;fJ&q3S}YJ0A5m8 z30010A{GSfuY0D(!r(}ZBYP*F!g>1Gu(<0nDsa_NXM$ZNyy+%8obA!gd@JGR>4MUy z{@jIs691}?Q850C?C#^VHX-zPIPVVS?aW7>1=@xY3kt5@i+Hb=%2k`>x5gM6h-59 zzN$SILGwrwp%LB=Cw`U2MweEpU}q=j2I^vJ^DMe3ze0)fyRgtY#HcX4Gq6XlQm>{Y z2iq#AitQH`#_5d`x&ZIzSK54sSl$tB!s} zmv)vH*6s}MLGxYJlp*mOa{e{V*)VxJpk0ijn3Mq<=D9c+Y-M(F&NEqzC!>1@xwhD+ z>~?Coi2enavZzgY@0#t`JQPa;vycG}~05|vIAaPxTq4zaxR zWLZAJLWbu=44oyATZ)Ia2V?(sAk#y)&$e(08Gg247Icg}2q0B6@IS)F`H6}UHhxSf z{o^u=|0u?0Li;U54Qrei5+r}zUxEq7cYanGv0G<`y2iS6zIgEM!RVRE6N&`0u@X9N zD)iAtZ|mkbY!7 z3!laT!-y)>ZWqXKU+mD6k|iP*n_(ma^BuKlQ=)ctO`g1RWAPQL0qUSYf%wz7i^kR) zf(*X)ji5uK0zfvX6Q#c@H~fYav-u)rf7JT2P0^J#2nUKFzWw zedfGRkZ!9I`<&fxU!biKsAqF}P=Xwz2(k_m)OGbq5OOL4{p!(i^vP;9?(q!BQ6JjA zp-m-uVMIfRY8VO(Sfu}<45I1a;M{!tiI)!H)b9L&Fo(wyZ|YQGyWmJ}s~6@`?S^}g zK^HI~xOgJ!9lEUMxfXC@BpYU{78v4yw(^(S5-t7e4p*}XVBn09?Rh$Y?I6Qt8wFKMPguc zj^~X-YEx-DoHvf@vuCxhD2wi*+u+jH{lXt=Iap=`@BOz($6aU7S5v@{Gxy&=m<JiOj|t0I>cLsxI9 zrk3(s@EaPt(0@y%-2NZt{(JA|?@$d!Lc(|xT2>|*{JFy&FIg=NIrK*AXia~=M2vyb zPdaMgkt0K36Ds&FKGf?ST<*k(dvNmIDHuD(i&y(rqVfrT0^N9*GAsGTk;*-N3Bpsa zD!4uPj0m&N5HvoJIsD_4i*T2rQW1tvYegM5RixyG&e#v}YpS-zL1#-KRMUElNeb{^)J@zMVG zTWnA`ieXMFPyfNYBAI~T&wd_y$eN?LOwE4%rl zY{tI<;$?Qn1+b+jSMWQ(PjM^|P2^Jx!gx#EbrZ#(D}+{N9I?zcICCENu+`Pektlb5 z-f9?9czu~yKX^ii{$Y`7>xqt{pH~u@liVw8gO+lT0foN_(=E3qjywt4Hv)XLd8hT+ zada0a)7QN2ZKo+Ky#>)gFQtmBx6K6RG6hbt00GpvuDbH~WWUBdq)um-1Ol zaM1~p&Xh6cuSy-0zsp)i3DBP{(cu&mR+qs1);4S}{t53pBsMF6+edo1) zgumH-y*$4~LbGHV`?a$lQ=8VUhAE04#2Ka^sdB1ri@f@uOZB(qZBYD})Mg_JO-e*i?s!Vpyh>vl!vcNyrhjF`YR<|V6pwIKFprC>r?;qR zwxr9{LZRVdS!xCc%E&|kK}5`ga$3cR$~CpI)=h{v1k?IpIqHjJchn%K`oeXYL*L*a zlOgy*Fw+FS3;6MWIk*>RediJ}l9V?V!8$&J<60~;JLKK;R~ynrvZt=xwozhsYcYsP zY8iAzK>TOfMiHldi~Cm9o)#}xWsJ_w+tpzYp_L--{e2$f(PI6zYz=vHeK#(kaqpXfhL&A8v`R7guiUfB$S17gFY^-*3YMs=E~M7}v;<2Cnty z;KpTxgP0b#eKV$Xm*hMe2s}!`j~(*G z*5wO&Df}~_{LoLn|IJ&*mxu9xL=>HliClMs_ItrW$WdowhZTBuJvm_(RVp>C<2b9Ari1E%{@ARt#45Fz<`@R2oJrOWyp-82x+HgP8@yhpfm`nyiGtxXcyIffSHP6x_T#J!`UMUZ+#Dpj zm%*R#x&ES1*qtR&WAL&37cgrtD!4!Nxf0@^W)(_Ikss=OVT|BnwgIgeqcYA3%L6){ zM2$&{0e)&AR#s*M$m1ed65tcTUQnWdK$;)}lC+TU0CkaPSyp@jd@40` zLU7YGoXZ8V%*Q-&^};3md70o99ns0os1`%NNnxS_2^I;|Ia=2MEI)R-lA4lajiuiD zG>1>vS&SB?-%c`BMn!ERz%4tK~hI7#?62b-T`6}xrm&4Hlc}bP{Ve?9;4S^i8kKET@Ak; zrXmevlyRgM!iTx04KW~KTaAD>5d#Up2#m0LuEG(;=Pv=I9cgFqRHQ?@9_X11_4cXx zKf38mwY7!{5IoF zpwHfPXGjMrbYQH8&cFq~pGX0&b8zlzkp8V)Y?}xH1th+UAttgU;7FgM7GAiE&DpX- zn4{UhO;tuuI@4^Ka@iXgv78{ci|MyHj-2YwLqKjgZ-erad~VORK+s#C5pyv~Y|%;p zvo`}TdSe2DxFUG_F3|GyLXlK7qd_i8lztOt6q7behNMH(h(u*|FZj z$Nu)}q8koBFyipEReS?|>n{TgFcMcnD?IM5J?>aw^>G?dXf`ohoAwm(h4VoUma%ah zTj8Wx<)iExAx(#>yf!Y;MtZ4*L9K zz3*D@ch+InoY`mgp4t1i=j_>cRNdZQ8$ba5bGf7ctCT>>C?M1jA9pV+N9aQn1n`CY zCEbJ9L!}*}{?PNkuZNxwZ*=TRbK!|Sy!AiVN5p@O*g?91qlW{Rx|cnTi=&m^U-mSL zG`!qAyxhFpd^9ZTu5OMVUbawMHyG;!ZwT_guB_5BdJq5&Y~fL%84Q_D)CB-M0I*rz(#tf2L$MFpPS3$#XLks{&KnM$ZLeYVu)1-(!DRY7s zT*OgLxQqrhR&!efz5kNQ+O4#?)Y)y4l@Sfw5E%gVBB?+dL8SFiSVhxDl7dAPrPl~; zS&kP5TRA)wgLi}`OojKjI7LnLxV$Jq^fOEIv@|AX^Q@8z&w`$HZ8wT(AaqjXFy}u7 z`rmobfMbzHA+y1gMtfi%&03n_1+w}#EgB#MoF*WjM5Ne2G}b^lF~gw#okNpQV1`dl zTSrwFyu3~IeQcL}yqA1}3^O8(+k*_-BaD|LOs^s=NhAMr{|ef=dbs~iodyEH=|$3a zSqtgN3qOezB8Ep+BLFrJDxpD7rejSmQY^J|s&&k6b*O4}9c$zmYsC8d3TRWLhtU9V z$E?!->$WpYxBdUWB^^fC07+1n!=9AGo(ytY48vX=sDCv)0)RG^)Z!TQ;+Kc=4?~}V zw>(VtW2(x;g$nL}8R5a~03gXjIp|6G5!8kj=bV>-K2&@Os=5TK;z1+-@9WiHynqv- z&9zH+3PIybwEU}<5Rfft0h$cfKS|&V(FJ=nS<_iRCPZ+u_Td#o*>mq>bh4Q{Dl)SF zTF_;ZY!|d8>uBa6Jaak=I;n`2Rr0UqN05zLQ!xv+ksapX43Sxa?>Ww9p;oLIfPD?8 zvkuBTh`?G*YBqCAht5BK|A9q?Sf&NISVOdX%_T&W^VLyL^L1Qz;t<}?5hj`tVE|5f~j z@;?+8C&#jlvNVnJj&VE`)gLBBPwGG5$zbt-DCQ6VQ5@fLkO!?5QE)D4Su|oVN!3zf zE&1nBpiyaT5~Wbc{yIryhSw--8R!-N+i+hQrtqm|{?|SAjZ%|<5xi%3Ikox3b#-+0 zeOwLSdat!bm@a#7Ed^~Y#hX8h{6B&9-<|`2P80suCsVEBc!qPr)TGh>8Th|E$CGF{ ziSlC-gK{H->MX~^cYgIB{4<1dn*6HT#HKUE-g6|j+JdHY0=9FywhKPCE3LN1Z9#^u z|8baqZnL%I{l7ft!A0mq5_T1mk^aYX;4BHBgcIl$lNk(?nf%i2W3nnT^NuSDG5!zF zafmI;h%JkbJ&aCZiAi^i$*SxqbQ!HZ>HPn_|J!rqy%@k7^c*=ahX3U`eLR#hpf@#g zsQ&mjNAVd@p)k2e|Jwin(3gm%{C6GE)Z$#w5?IjU)YTFHe|ro_UEo)p;|CR+1^{FL zKo1%@952=@QQA%10FCE>0l7c~rC@+GybO;IO9Yz^mMFz6-kvP-0qe)M77MYgN?1Ij zPy!8hf2oP$zAzfN>>&Y~01(16!ZSeXHL3U!E<4GCo|`%%!d#LvtH{ckPRhs1SV~~M zn^QEa_^~*ZR5>=cn7}%w2J8f&f`6C=(}~0oNg@ES!qUQEjY!p+WIagK8s*ui)KU^D z%h8?`L8A&E)ShG=rVJmK+Rq7_mSO<_nm!2lC(XidBM0Cy1KS=9Qz`t) zM2yOWa#KXMON6RRY5WuX0-8FyV}!a3I=X8ls=BR=s)VYl7dpCxa=OI2bLpzOEv{4i zwktZi8zj05t+s0u-SHKUF7*|k+Ct>>5@I8Hh}Dr;-28oxYHJ8MpcJWo2g14~)Q zN~=D1R&;i{opgGRx4KdmRetVa8|$whZ9G5eeA4VqtEua~k!rfD56;8Y+&9noDx%R< ztheT76l5^_OJvHOvIFeaR>RtvSDL_^8BAazm8xuJC4>XfFKM9Gmeb=JtKVrbqd4&NG& z_psPzQW3qR`GEkkI)oSFNGp{T6Dcck>WzayE}E56s3<1TR;*|NS(Ox%C@b-_5*fSk zEK-6!iV2JrS%-+UT{(xc=!a-PRv#aP|CO%TrSf#}q&j|DHsA^jDoSkZhK;eufq{>PS zEhNrvJO|`Tjx`U$D+Wo76F}kG&H|;2P7efQSMkLxs8Y@1yjDC&uy*ZU$O<_BubEpp zaSkn7)jSAiYe-85g&(XM#fhZOT7j&Rwc@}Y#x5PD=}sUaOY3#Z!79{6;OD$i^@9v$ z@CyNb({ZT>t~w!zfMf`|E&O2#d2m~)Xst;_M?9}_5jJ$`aS`SUFBNH4R^4$aG{%-m z5mwg0MG-W{5)x%L&OtqAR#tEpoM7DX*KJW$V+J7@*oNqUEa=5UG~l{}LRLZ=HceVg zWQ|8xLJHcYgvc7yEh!X7nvKkQQi_E&TtkVCH3Cmbk+B2}a4{&sc+O=y)qoNl(7$!W z0?P`ECz1xH!^?&ruA;kv(9 zvKA-~oKl4s7O1;Hy$3?7(H9yJ8ZH7pcp$8XjAWE*1`V8RX-gKZss)xn5Vss;IV*t# z=ZOvgSVu+$>JcgA1er-l@){!=+UW0N|{hCEjGrI^oONm-bjfPhqH2r`zxHAb+8{x5+03&wB)FqBIL zIsa=*B>-jo4ewI59uNtt=7I1Rs()+fv=8d~`!emnz7Me=2vNj`Rha&Wp=gxOg@30X zQNv+G2?)Ug0MBKo6V}BMn!%j#bmj0wFFY$Qe63g!`374BvO&^?RGtzdZ7t`c9IH4@ z^;~Nuo)6WjdP*!wp|f7qEk5Jmj*1jbbIj$TfO}~x~Na=jN&(%ZY}^^<2h>7S-x>`Na5P z1wyTzMEmb~sp466qWOrzIMr>XY>bL$tleaPpv?5{)!vl@D)@db9kx#h>5^o!NcWpO zI7vfn#QnO`B8M(r->mw)zygBN)4ZhdAGuvjZE=uV5)|2r(};6 z-{mUyx%25F*F><-0`$S*LPsB7S#l-{d&zn9be;AS$A4V^l#=&TaFwm6-rS|Uz>JT% zh8iN1UHVoowL0uw!IgQ{6NB;u3$An{obkePk{3X*L-=TdgQumz)6pl0h&+9p_Jjb; z+Dei72)|?BRZu>~kp6imd#pOXpRPhr=Q~uU#~0r{^(JSdP5gRHwaw_#3AJ5RMHhvH z8@kOl#vXN5%jt-JbGKe%s^e(s+}sjDM^|FWoZ5uc->+ydFPL#v+*Yv%Aoi5=*Y7Ht z(D?s~Pj&4Jx)VmkW?=6_+2%2pa>`L@9A>fa8r-W!uje>yX>)R0f``9On$m;8vf|tr zT?M01R`W_RGDSONKN`_w?mg4VF+5$cTn%}y8B7&a#{*ElJ?IEZ=~y&8BYY`@#)ef5 zIlH~FkcH%Rs0`5*kk*@pjNZWa7Rv=DP0!1(>l8rz!pL~!Ff-b-yJvH0+^u-z@%rC)w`E|ZJ;5p{CJ6o7UJ2QM|100Qtpf8 zTe>MNz=`>pWs`9!=YQ9Vzx@|GdbLPSozemgy zXF~@GZSDXk(icW?w@hRu5B0R2wFO2|I;VP{aj*lc^Q`KkMOW->nCdi9ZtBpwq5amm+K~0R3`F+uF|(K41PiQ}AMJwB z>DO+<*SJVy#O4Y`!oSJ6@1`k9v%WqO>7UCPj%Zr)Z=n{#s+@^P7O_=n>DiS4;2L zDY?bPjVO`ns!q)2DR-yMR;GweD9SBG9&xVghPSFxSESMvK9^V5eJP<5TP+iV?s_6u zloveq+xrT!%h7uFM_k7cI-~dN&Mr3so9;>9@6@NE4VN;UMo)$k-bPH(xWMPr6xX85 zpJ8U})b|%QpHktIYdRy~v(qIZBzD>;8gTrfmFzElrYRSgIfEby+doCU@jwTHulfC@ z4SFwhx<}7ZNIlM<$;%+XtP2e~n~vq;KieE8Jg>OIHf4p?HP5)~OWq^#yzJ;_)Ygcp zeHIm7VI9#-W>H)*fw7|3msy9PoTQvK{n01F>{V{|%K~v%a#C9`LTjZ%TqhkY^$BY4 zCogr!r~VlJNjAgpIG+8Fn=630aw9U64WxX3icW6!j>qp_pL0U+?T2sAX)iCv5(owY zuv6cL?#)o)kQoR@ay(Y()9xDcL~k{}^1CZNBysTnA;&iRJFSRM<;>ei4}%-JO2I#u zfF;mpxkjsay)?ZgLlk1#FA17{9 zoa)wqqN`1pDY2k%tn`N~JJE zS_{(>-P{hhWl&c^wmbQn1u7hOxYHLH?aBH9hh((Ta;eJM?7Ytn95RRw{fQ z{WrgX{>o{EO?_+2m4yOP^qjfPEn^HBsLgCm=QII z%qr&XhEz?2D|`O%YUpj=6hitkoT~hGZVet?{PbWFcxjbAv-I|Snh1}jkqK+GJfGiv zrG@T-jo0(FvBahL0pX;LNOb0Xpx5!|)p`nj#EIHO(=R19rdzsJykJISY4eGg_W~x% zuQC4XA*_u?my#=U5`cQ8xRG``o?`5pyP*9rys!ZnioqbeL+kne$(f3NEkQd#$9G}D zdv<(E>gX|!k7#h^fS&^v_cveeU<>7`<%+y^mc*Aa(gC?-Gb@`-H53vyjKz%3lw(-H zd7_RyeMGM^t`91;#8;WjIJT(>r5e|4d_wTcEnp`F?d2V)rNDTOVkP|Z)?E$=`K&CS zY{qG%>Y2Dy{0o(-aYW&AVN~X5o&DD1Y0{K$he-lv{aNL>Zr>0!wubXyZy7u?vgMVT za?)7-*yA`faz-@jq(p9Dq(%LwXvb^KeIz*fTi-f}rO!yyN*ng(@kI|U?}mSod#{Gp z>W@n#K)Oakyembg0Fd(!l6d|Ug~iYWpC1=zpmyO8T1XG zvUu`g7Vx-9uKu)@R$0p^;_JbSaBOTV=wX5i{We86ZXLyd&DBkk09UB}hF;byk# zJjW@U6q5F+luCj;jeK(@{aExF8-D^(My_z0h*35J3gu=1aWU{}jm&EowG($VrV7SK z3a6Q_5*Gr@KJwdznwSXx%IM9kgIv8EgE6xN1gS}7y|~)aV#!@!DUs10@y!}*Zj+uYX-W3jwA1%c1suFZ~0^ur5hny@O%^i9bbDi-lx{d_F~?9tOd>1c@CM z7m6_7UT)ETxXc+jG8ilNI}Bjn=a(bKr4UM(vK^~Bh+O^Ynf>xveMlD{^G5J@>h$E_ zVoRUTp7}oXP z)ellWraU*GSr;`>=NRKDXA(~G32zL{kJ^(XMmw{A8vOpu#pQC{_|Y_S0*)ap)Km(?@#--_{1E^NolM_fcS@pF4I#K8uxpM1u6}X ziTqb(kJlzIU=eQqc)u^C0J9E$j)KfLZi6k)G?LeU?4n@9C!86DH({K3&ZsK}8%$jL zDAw*BnODfAxoC1)4)T6Jnr4)o!O8~d3pOsPYTY_{=$0z{RG70}4s~3N+zw880tAZ?Hx?X4gxTKncB1?O_erG60kXk*27ra` z3MsP@rzOX^N=f1YhWpW$HcJw7LZY5iY@}n?>5$lC^1ct6Ds>6-E>$5qemI<;O!X}7R&G^Q7j0(lHr`7#jfB?ut7qE?pH(P;` z0g={ImaC+I&Zyq>X|R0kekfchz@__j)Tk-i#vSn+VKnpG&pTPP-+6^96NNP7e}|Tp zHydl)J)4&Mol8BrLefljABV%2@Y%A$LG4LZU)gj(S}vf#QF~bb`ww#PM}8N11<{)0t%R+yXxlx`TWn786BD5#aun`p1?|l8Pu~oQZ4A%#L6B$5&22oQs%n^G+E@XJ z4x7e;`JwpRJ8mtVEC&HpW~>YOERh^q{<&2)Xfh2#I49sO#* zb6)?~RG-EDV*8e5doRx2%0&Ym9)u7JY7)z=*5 zj2O>$=~h;1>y)v-cXC6zPLZ_C>`Fpe3J#o?1kA4aVM7t^UdARre7UECf>qQMLVV`a z(cS$7lR_2WXr@`lERvCjp$K!=xdxJ}i-krzMwzCX5q4KTvSf4(GASiQFd9xa<@^-B zC}~YPfTO$ZyAhk9J(SE6&=j%TD;AVPZdt_!!&BWu7(b%o4PKeAEeKohL>{}dm3GX} z2h+9!PV#%{r|+i|QhF&kE9QhB>nNZ6=6nrr;#>Re6f|JZk9mH-kLz~BL*NT=Sa*Ad z)YKL@rE!1$Ag;J0x!2oMZHP#1C&m zlfThJLyWBufz5K8FDQ#9LdQjMHbJz^Af=svbS1P0#N)X%|QO|_xXo310tAaz-jBpXK?fOfWGkbf`R!3 zUw0_f59Z+v^M%5ET)ko5Fh4JEH*X)@!Jb;N1C&?sD{(GUo zYK~1%lt6frl4Q*EdJ_@$*Ll_Ow;3aG+Uhp1olwhzpo7+Bkv0+`I^v^*CCUlY%f+H= z68#-3SbIjY(?^y=5i#!^DBuW(L@zI*G%EK-2N2C-Y&zjb+s~gq;rk+E>?6`m7E2mC;=!5|TlU@2tr@Zr; zlSkD1(BP{hAOI=;(wM&YW(8R&PbX|QnrdGw@3wm$GvuHr@Z0m3I@NwcG$PFhvG-Ie z>5K^<)c5>lx$*= zg-#|DB34@KyuB{NRZ&v6!r{8eATE@kaZj0buM=;|^g@ z4jC0GL9+cD88N>;W|P>tmYa)Q`gdYNi1m)7I}>v##l;&YRF}qV3}*aF=0Y zCA(*J!ftr*L}T@a$U#6-_30ZO&8us(m#254V;=mcD{h@`oZ_EP85B?KnD$>in|r?e z&JJZRQv6ZaNV(KWtLYi<7LF{;%xNK`V>&6l=p}tL62e5#wu7rzZCpceLnvZamktGr z6JF=Day2ih3dEk{3xDap2o1D(-EA6A!qpT4{qSjao$bpn{w15!gCbvXeQUc-n&=FY$h4@x7M>HY@Adk0N~~ z0L<(pncuZ4jz>xUvM8}3Y%-%BFPtrFdr+o`5~D$7O_>@fos=5b~&ITkiC7 z5b6tk_45V*PUjgIg0KOL?>dh$H+Tq1whCS37@()#v{I!hoLS$66GVkO;kPWjk4o3& z3q5Z(rAV4>0>yaQF`%1I36$8ZckL$JXZfpqI?7JUdJFiPl{O8PX>_r@`BeY*CH+`LgSkTFUYscbxn-a@l8~q0 zMRZUatv9}1gu8zsgM;EH#+ORFm5+#IVV6x`lbFnmjVf|uU$#T)7L7k67=CS5ro2f4 z+6tzhirBBzG@fp_Vdr(s+aXsy+Dx{+a~leA7iACyfX$u;{W1FrsU25`V+35KPfDHDj>`0tglSjh#PIl#{KMu`RKD17Cb7<% zaE*p5!d$XKE&VS68#5EJp91x%6_Im2gFg~=(tVwIPW7ZIv_FrT87)L`O;ii}kr|aj zhvQRgc-$y&VE+KQkU(0}Pra1xVI=!9EWY;GX9L3P7TU{buoXSp6;YKO*OQmkm^)Rc z2tr<1-J05QNl!yjcm!lg*ejQ-7*hTwKy00Ll`_M+HTBJP4x;A&Mzh|7?X+!m#WH{xA3-TPIP*wFsK zs#UE_45U{nGi5Pbo*NrYrte7$KNV@t`ZyJv)nms`b6SyY)MX)I#w8wY8)tSXRY9gh zz#Nx4^&bn>Z?%22K%R(z|3JLCXtT3M5&^~0re+HyWV&<_|Jb!?kvzfW&yvk1!qDw@ z^i%Z45*DRoX*&^uKkoWJzLM=&W+vA zl*&z>*GDW`e_i_eOWM{6`#|m`w&PuGO??J|orW#T3l~DFHum(rnV?=c6ZtmE921_O zNzJ*LfGPPYmQt>U&5;-VH}tZeIqI|4Ofcj5OR}W>KyOVbl_iwCw)-At35m<4Rm@_> zc}@1iwY9V;pU_sN){jg%pm!1{+D0tydyB5Kjp)pHZXT~2=2!{IJjsC87h}JQbWtt- z7VCCmxT#`o@~t4X(gipB%ABZ^xh$NV(oSyB^jj|J^m&^o0+`I{p^JNy*H{}!3NAgI zZw8V8R|-X5dum))_#cO#-)3J&;uksi=p{3nuNyKs-1A=(Y!n?9dYmV?sF$S?1~$N1(!w)y~Yzm^T?q`_fJwXL^t52gR9Dg1}wBtkXDz;`4Rq{TAdEvqA@` ze{(d@RWEnqPcfrtZ2q`KQwSl37dG4m>kkitjB*>@}lHx*{YeDNGYHa~zGAXN#(&L^YU|(_}95Bz-W1><3UvCEd zqWULGEpdqhAi!PuKp~-@s|)PMTy=H()zXp?&|K5xf+=xM?>p#7KJoJgmPMEsdZB|T zJ729TeykLP0~AGfx9G3Im^K_i)d)wiG&wlcEyH66=QRLOhWNX6wC!<@vhp=7J0HBX2gX zd65tbs08@L_e?r%3Qy2^I6XyG}OOT^FQc2iZ^Tvuik7Bi@bJym(Dl>oTZ z-76k@)wyW8Nb2PQDb8y@i7@W%osC(I3ko}}-@+506Z8yW-$Mg5;bHl<5MZGHjR6|m zjvY)O5qZ6j!9+c9XCb#Z&o70Lb0;J)l+^v1&l#WY`iaXq>m_A<9_MRs>Uu)=T;$1B z2HB-bWadB#G~QseZy4^_xzp$w$E0!<*?U20c6iSw=U^8S_Uk3`yM+^dho~6KXPN>t zbRjd15`#~RA-|aYwD^(oKJs_c>9|np`bw~AEc3k%F_2t~Ah3)wPmjU<9aV=C!#rOO zHSQ$7bwg&npf~DGGThBYKgG8zBjYL9D@!@osVUT7GjqayEr(ZVveKLM zN|?g$6`Kv~ewMUv2STq|!fWP!>lSB`*jOtW1nTQzD=BRgf4a9@JYm?yTZn)I`yT$W zGXx;*J!M#oUQD>YeIFZlY$SocAE@%J79pNtN(?-NlEX{a0hReclA<5Shx+UAtMgt@ zK$cl3KPr#h?CgyGbWM(ci-1PZXQG60A}q@Yq!2Ew)|Ld$GnwZ~2zM)SJ@mY-Jx4># z#(q;vMG#5Vv=oiZ^*31Fnp2N-ngz4<=M308_k=26ly`_eK^TsjwXqvZ)zNBF9JfqR z>0bPdEhEyyQZV&C2c=JRW|!avEi|*Un=4|!+bfq!V4ws2{iEC`*fc}LOFsk{`QYz5 zkTT?3R1m?KM8K>6o3haNyKa>kFcYcmZ3(HotVUW8^3RmbH2GsE&6ZN|Wdm(}sfTbB zsdw{<<{VGjXW}g1rPT?Esn>R&h_iol-{`VQ6&6wFwQI-k!#JkC(_se}@Z7Y(}Bf zGj`Rn6j%_9U3H!+3A;b8$$3Q%bNks&YuQ?2#su_yR5_D`b{}wF@bqcKI<@~y;5|Z0 zO!sEsbm?hMdLtj&Nzl=h`ISTZ)`lE2^E8jwtFXF1Goy8P*-&1mCiDB*+UKI5V`R*3 zD+KWh@Q7JK^7u9KiQKEP=QB@ZWbMAUt2*|HI=tv?k+V%gM!s{fnJs8um&&JRLflV7 z2hTsN<}wYe>+TkdZ1d|kP?f7JCmJ-4rlL7~n$XM6+P@)levfbVq?SWxLpS!C8u81J z!82R!zHEsA;tX_)*T#QDwjbNsaLA!C{(14uN!~+BrUC)yt@B)DUXB138SHJ{k9O~e z+?W@Gjma-5m0oKal=Yi2bOiX=q~f*9tq5gShw61zkY)8|X_+EOcYZ(a}+ zhLvfvFz>e}rh39*yv$BSXs&JKXpd4`U0;vqkH2R@OD(eBA^j;;8`LvMi5ppLh6T7_ zUxXvkO69DV)Ji5{0>Kshj!~GsFe3fy4c20Zz96?u2O#Z51-z1CaP#mi;9`mfEwr8b znSg3B4@Y1HXS@zvLeaS0sz=1jwPSm--S~#p_o#=PEmOX@jA9c5<>g_`?qhKl-L^Q$ zDpQPMr%%)O=RgW=gw#c3#QA{6f!6@c2TXB&KceY`wfJw3dk9$wxr?vCT!2JjyVdR^P<3HC|ELXc=UCuAq5&Wc3! z^?MR+nK!HwXSKinv2(OA-iX9AvqVBWeGUpr9dbZfY)AcN|#2$yl*UfFm$|Vw8Gk7!E&Bu9{_XDRe&ZYs8hg?Lv z$Hn3|OvZh03eFlCeKq{!rF>?d-C=$C$<+bQY(B*W!?CZvV@8It_SkRPSYGmy8Oy3!11~ghut0_spK(lH zovfPZ+pQZ2TCmCErATa(pVmiB zGpS%cflF2%R$9(B57;~*VgMw_h^>acv8!|J`XVwSo?bx!y^ta6ovFUNg#RO@Q6TgzkKOQ=8f@4w9h2uXI0JxVb-PVi4`RID99L7Dk_coWJWh1B)NeWz-nxfD<)p7FWAa*+h81G`VM;YWrnpOb@D13%ocIR2 zAcSQInYki|jdVI0(raH@ASQM`PWnh60JEr5Ij%!wF|t-3yGm>s`J)Ms|MFDIzh--G z?P(}7khcOmle97UL9~Xep(kh(5Uhq~p*SXrUMYBQYn{{EB`qrW&Qa3-mzm(Hw^6V2 z>|>@6yVkHMnX4<$mi|j8hS?<+h>SJv>5B!~tc01jOM+Bmxa+2(HL=fADe4Nx_TFF^ z(KZjWe%nNq4Ct>z_=nx+*pqAZtetOvzbt<% zFXWocfHO4n^*mkibVz|uV>4g19}aqO#eC_`kkIDQP?Vyk*v8BEEEYr z!PgGeNX`3-Nih9aR#IP7S%(XrtyHypbx`1&(?MO^&xXYlVN-AvE&s+r+vs8uXB<2A6u(c2xNgBJqaotln79A_D^cYN1RG?s7-2 zoA24oQ+=G?A?eG0uG{`RWk_Rrt~!VIY42iAF@mFhZ#T_}bk01VGE5Bz!j{;FGJlPHj^swT?Eb#(OWT>*%-`1}1!SUnGl-f?AcJ$FY4B=je%_FaA zlv8B12Y2sIuzbl^SYC9y8yG((vBtFP>e@A{bfZtWF^2pqF%Bxdt>){yzsLF=0G>CH z_;@j22AmmNY^byj!#6{w`9hh42(oCl3~nsznM`sG+ZWDCFnWBuYC^f zcAa}O7s!vzz{uafbM$?mnUt{tjyo++EmdXqBSmziqi!N3+21lA`pMtYpoa6AF#-~r zeY!@Asy$=){;IDckR%~(N`o7*-t{t%~@BqYfftif4T$LdWp zvR=sO4LhFj9TW%#d@HfkIBSxSTv=J;UgL&Qg5%d|Ouxf1V>i=@)om=yydz|30O1w+ zSPky`MYzh?3FGI&bbY4-d@^C1dOegPO-&0t&KHdOkj<5g;BXtR^`rAGNx$|s_cY#+~U+%lBcOccz;fO?!ueaK#a|)A!w~Zdv^Q%fk z<^BFKufro%8JuMvf8*4+D7>kCEa}ZD;K$iTkB3p+Mv!4T;6{r^fp3ydHUjjBT>1}j zF1>46M8xS=igZ5DuEd0dyHV4mCc<7^>zD|p$A3?tuXrsmq%!EOL$!D5k; zv=Pfm9trMd_!sIWHK$R7Ogj7bTYJmZ;dU*G!}#QiUiqaem;@N1V%yFo8q=Ph#6j|R zyR&SKdtZROk1)?Dle(wgrb48wWJeOFIB39v(_r|sRA$jHW7T7Ffr4)pW@vrI_<29@ z(d6DNYH;UX1v}c;z2s$<(Z_u1 zUhj6uMqo0eIeQwOBIMm8JeEdmaLG>o;oYG$0YlYk)0I<{+G1*g2a=ub#pMzjyLLo( zI-D8<(ky#cax-Y2Q>nVB=x}x25rFaYO@gBqke3-JN{3fOF?K5%(`{>5ga|)X<|h+% z7Un19r5~4lN_Zr4(1sbDHT*lj6ES-3kC^pNO{r{yuV~oDY8(IQ>K6k3X-%H?p2OkX z^JadMC;fk}$Ff><*Y+4!Z@S>Sv!!)($O2TvzK_0A?bHB6Jin03!rYj^z~8%ex1-%d zW7s{Ct`c&Uz-CVp0z>BRf%is~s-7{?NG*vZK-nowMY&e+ZPav%?fn7DQ1XelA|4&80GL5tz4jp7}ggZsP;gKiJ>B_)}NVsAyCl>aSZ|qpE!F zd;7lc^?jQX9;Fhtxf*!7LuA2UcBJz{54J0Wh)6z?cTb6mEtyqQqrsk(R(a<)V{N1! zgRzAu_RD}{`l!l)Ua@C7_PotuJSo#Q9O1z0ta03EpDQH$w=t1fZ_|p2wLj}?l4rgc z9@YyvY$g^stBgb`*Dzev1Wm6vN{n4m$R>KcZKUQy;#`56mEuUE%>mG;@nKMnedJ^P z&a%{BM<-B+;F3}_r6g|!iIjQ&JwbHZx10e2WI;`%=ebvNJ~Ahy`6;HN9Wiv~5?ryE zrp&6}%2*@AdZ+Qkn4a#L7wZ(xi2ZCb97JkF2YOa6_9aof?PlFVUqP_0mgK;L?X6E4 zr8497g`moIsM{jDSaqF@nU$)W+@Tc#6xwj5MO3 z63FNwJfe(8FDtM!)j7?^OR!B~7j<0=LMfLRMt$+yHfHaJGkho1q${}XsbShu`{z?) zZ>`kab%lMKttyBZJHHRM+BNcy!c)?JQ@*qPQ_K2!!{@Ez@%_nd%`NZo2{HEFui|Dx zj~$DRUdfIvRG?h#7sDR~e1h#;Ln7;ak1fu$A`CIE%MOT+w4Ix>t18pYcTASGlRl{) zyu{R7vY#C85o`4F+UmSA0;iZZ8+=j$q_QH$N&GA&8n|XX@7}Z`F+JO19%mVnT{CZ7 zY&?H1HRsp>o6g!*i~>I~KyH7A-Tr*oU9HV}uy(dn7)DO~{zuk( z!A}io^H{iw3U;EiWywlD-=q!IpG51Fy?lLFWEPMw7IvZQZ#h53xuBN6X>!6mCN1Do z@u)g`nnOQ@jS+ztiDv4=7d0{vnbDocsP?p7X_%S83!sFzJ2Sq;Y3C@3} zml5A1Z0Hu9^dh$)+ldMCPWeX9zpQupME?bwjZkLWR*2TqF_-u<%wY4w>8~GgOf*^F zJIR&(c`=*tLH4EYk6iDY)+}BZ)p^78DE6UmflQmMHKj#*b?c38w`Qd-@PJ_TJn*0% z1{s~}#3rq)uP@o4!i)e?#E06sWq9Z5o|3M)c=;DfK9tvRR|&Wd5j>i@i!a zklA}BaF8}$FY&cdSzzQj%!=bx&`TAHK2cN0kB}pBwW|u+;z5$fJ-TRq$Hg+{^~l8? zvHLFiyRD(!Lp)Qajym;Ugd&0eBpXEk{Wk>6=$U0?XZe1bbJ=4rFu^lVfQ4Or(QEohV;)T`~P6 zG~W=8aV9cg%E+4+0@2a5i6Vr4;SDV*P+x)p z+-0h875>^D5`9*kW?*kB$*7s~dEwb(F5}(BB_Xb{>0dv!uXi;0C+-bO7W!4vchLvO zyIV4M*AB!G-=?ABd@p$FkcjH%6)k`(&QIXFaoo0huG^>+#e&I&hb%$P8-rPT_wmoU zJ)b4s#t>IaGZsG(Oui8`u+uO0?CGpKX&*3F0O>A|hgTT)s+n5(1pHJhT%}#pSwaz9 zOutMB{q(+WR*`lu9mln9GK;&k9U~MQvgU7#{B}^;qJ@8>%JPyhvaE_g3G}8L~kbu08VIqKf>MZc5QO;L=w$>4n z#)}KQytsjp1%mCug!+#(#9n3v|PPy6Oc?{ zqUNJ=f6g}ynKK@xroOR$jVj4`MK!%pWNA(I>&z3GR_0#ej^U$CR4Kzp+zFTPej%$^ zS5jeybg5P+D8o8Z)P>%>M?uNmMoyQ^6k{PW(2OMEm)cl#bo5+ELS+l;}`{GmP!~l$^y=dD+*QqRfXw)afWP z6j?>_1W(FXsy<24mM9eqnjDq8OvH?k-0wl!M(Bq z!5tC$Xv$N2zq+<76lG@nYDJBGTFI%g6wBCSS0%x?XaMr~yocUt5EVJa&0OQlVRS^) z06*pQCu6A6i&L1vQ;J0J`#$e4P1B0=>6X95WIxC(3J9rP#LL`B+8=Vx3VdH(t(?%_ z%RF?6d{C&mANfVjEP~|3)!`2`nO#)G*!lk>>MVoeYQk+jgS(R;f#B}$?(XjHI=BZ3 z5Fog_1$TFMcXtmK+`04BIj8O~s`#^O*L3afUh7%wRn3#V()rteUqhanH|(3ca~Z|I z)1lC=@X4MbSr2oU_eRXV1g=RbJ+4Hyfo)n{E3}00hnKG^Nb9`Q(|)*$G$!)-u+9YJ zj@|O{2#l(9T6=k=<1eJb%X?vFX%fq_iI-$_;3`H4@jefTmgZ~*WA;p2eg}!he?IW& z!{*qiBtDVaCluX-OrJcJ|16Uu?9JIAlF8;d8PLx=iS5YvOis+-XoG2$LXbF4r<2p< zc9aI>c_+&ZV6C>6%>KOSg8%i8apC0Ts;XD9S#;r}aJ1v?s&xB^kkE^vVoaq}F!?Nn zo%T04(Z8N5l9g56nC`0h%B7eq=Op>uMzoW>@)8mK=40s$oqda&$wy1x$iz}$3ct{^ zH`)L%-}ci2tT*^*lpWRb?`TvPz%I~(H*o(84>L-C6)f))SHtfX;@nzP^}YQ~9br?QF$HKB z)6uRWoEE+i7c??CjF&nN<_s3|3nFAWlOK4;VCJrX06W5x5PEtwFm5o++0m#sk+iyN zrl2^OQS_B3;~%!Xy1VIVB~2C9%3FG_VyCpG&ad*M?3)ZU%9W-Lp6iaIM<+NgF#`6v z*ro+_p4*?}e$2r)Rcrcg34qpuu4tLHMlU2h%&u@TPZF1HF0E-xFA3+XZiY@NAr>C- zyOWxLbz{6uLk{C4w(fw{v#|=T3o3a3hxF!Cv4L38X}X8sS$-6)s~Vgv-&;xi0)Fg< zqTo?7JBd7p;hE^G_FCjKhJR1IY3hF6f(6PdN}*v>5$Jvy9JCFkx~hyZVKxMw-Tm$D0%8E0iD% zSRixz7Z?q`_m}61&t7j>@`XZL4XM$>uU~49N4&mr*xtf%OD#kK9gx`G>AAycczgCH zhAf3C3anPOWAik8W#t8S2s4HI!r2x&D0O8ic}rF)1Clt8O|6v_OGtV+8{%r-cq}JO z;)R^5Muv8hU;yVR0I>E58s6>eZ42P+3w#wDjDnd_NFwLlK_Tb+9lV(v{smWMD6GCdtC}i6qk)s+{kh**)invQtWAEDBSd%WIkd2~_OL!%k~HCK$sy?VOHrp>6HM9n59V zHb5DMqmSyq%3<;ggpZjk7+!aA7tNfUH28{7_v%PK3_#Z@sEEjQd!=N5UP#&bYS;>> zHN?hJ?$n@O{IAb)-lR4}U31&&o6D#Tk^v4#&o*tSA6`88L9jb5_BET2r}4!t&b#aT zv^1mQUL3dhQIGfhLIdr-aPO}UGo&xgx!K`4=1p_QSBLz>i47akP+$os$wYjXRYx0e*N_|I7wn>F< zVjQ>jUXuLg(5?<6rcGJX^hlIFs1}M5x!h{T{W?0;1yCVwcMB&?IORSdO1-E5k8?3I z0yg`UtxBjEAJ)q^9}G8ChT;-k*fuB|Hg|U3E3}Q@rT)?>Z)_fa$;TycWC!sbpZ;Nn z0>#L*Mtq5@N{xRBH(mKXJ7+S#163_dmVk!>_+>XE+tGRCK%C)J*1MhrFSy}WiL3%2 z7}qJ#r?rTBP6cg00EtZxG@xE|5OZ}t&E@U3+#)82JO#TEYfD-r@8D2Sr)lztOJt)1 zjfA&EDTC+=shwTTC15>kp82IH$rhK*(KB4y+RMGE<+Gjl7~|1 zd<^I`0)Xk^lEJWRxUx%u0eC5w(mI2ZoME&}##|NcqE##Is zhLWfkqVRKKc?YhrxSeC`KC4lp)HnRI+NOD1T{dX0Qs6wpozoC=N_IDbs)02c+?Jb4=-MPdv^x1)tpxg4otp&)!|iq6)afnPd)VPD zZXQ><`B;AZxC;L-7yodRjZ@Or6e8A*-tEclXJ*fgr1U+&j?M2vHHeQ=Ir6-SS=6@Y zAlh}2)S1H8lz_G?NxXzCFkAwrcF6}JFEJlXH;Hy=AejtEN= z%K9jT@tcCCW#q2#xgAruLZUO6-!E0yjM7cS4dQ#=#u-vXg`%!t6MiXeeCv|Wn^Rn9 zDB6%f1_8R@*&V<;7*EN?<5-~`k&odRl+h|6K30yU-cPiWYKrhOjZ}}^o+m(VIa`S! z$ld*Kr%z8;zuvzH2%1`yq;X=g^N4aB41=!lH?(?&g)?kVgp&olho|p;$Hv$z%aLrg z&TQ~-K9x!H6(heJC_r5qPN-G+g%y}AtR(_2iw^uteDu&YM zSI1{@nJrfHsIu-f#FZv*%2$fOd|S}Z2B%yZ>A00IQT`{aGL9p}5k2DkYRM)VL}~h| zzIF>e>s=gvfy6#5 z`Q3Mcw+JH@8%qlum{9y5?$!qkw-EM>dmg@nTGI?N5yL1w#rOtJa{-rj>}OzmdKF|L z%*sH}_6-2JdF%iSu!05rk$&MT;KUoiHBCvGCl1m507XOWU0VGq)%w{F_yK<=_ftFj za>7|WEc~EiXvHKbrGKdK@%jtooP{1vzKL?Fl6ZPPJyeZ`3C3|N@aBzE%>|EOa6-zz zFbnV>&rVd>>2koJ2!7tHznc`mzq}F3Z;n!oG>uM?f-EP3VVs~UT;P^;9jWv1l*?Ib z*EtG%mzUJ1kY$?`V#$PMwm^fYA5d(P0>8qJ*vwQ>(^~k)uQoO`jSdeosHg_E7LO9F z4CxyevKge3^@PiAdKPTwP+&cJRZ5rw*CPH%H`eobhQAC!GA9(`Z%$vrt}&SkhO4nC zXesJBJA-&hf<7slZ!v|o07F$}In-gJ6#35;-gbEKc19FW2yeQY5z4%6>UWg9&j3W8 zURpLt;EQ1nIKZ(`w>zijncp6Oo7*tmO@omIFrD_E>e@0~n0ScR z#En4Lz5la`xI+T@wCGe4@-NH#ZbwUFZYipUFuEjR4NaGiR4^d%TsislNKgBk#epY~ z@?Q#faYquqwu{@(P(KaN84dv6yBy7mTTbXizWPdK>^>y;rvyhJqYYZr$KNKwpg|`$ zGG3rVe&tScsh*j@xN;0T8&pB~TTHwHRbl;QMbPFM@kNx4DcLs`mHM}ZNg@|HDnI+F zgAg8z#NLC6U(qyQjQ3j3K+r#>=eY<;Im5ig*Xu7HBSInTX&h)TGFD)62_XJ^rhqcF z1is%3mGmgWl|3T>5$qFE3ukZszG_oXR>E`-vEA>{6v+P8w!2Ny%dF2Y;Pou-41Gyf z=-XJ`sIabZ6aKN+LB*~>hNMemxO@(Q*-y#m?+Xb~pbCa7Q;M8u28z0rxNcqU{8iM$ zhgfs-Wr6rV$zkmOOAat#$pKGuW|hp<$=TD%*~iww&CS!>)4|ov)x*;fj6Aq{x;neM zfdX8?{DT4RqW945m_$~0fmJTpg##=eBaK!A0V97c&f`y~7FPQT!?tgi2%8mmar1|u zj)W(URLF&W?4;vs1w2wF%G{}U?4nh+%m&5rpEOKI-D*sf^P0Il<(*rjA>J?vRsjW( z{)ikA3L%rDezce-C_l-7gb|4#@v@fdg}=vFC`9Ct44SU2XmcHnWQ{&I4uc={4Y)M= z{$~=u=Np&cr4OP_DmNZR@iMCZ`Ig21a~LN}64ksWypT#>7mvoZ_%cU;Wg! zSu#7_mSikT* zx}Q~?&@L+LQebgOeKzeZY!J9X%tv#e(Q)u4#d1Vo&ArYOX#Wjz!QaXJgD)sYhA+JHzP2 z(ZNtG6pTf20nh}gAT+LyY>Sf@Hyn$6S1}d~hlg%7bw=PbzbbZ5kh}slrKb@DA=PgI zbs1hf2ZK^j{w}U=^I3skI#yECB6MuBtH?#4l^z#=EkAuJvzj+AXVP~V8pVc{fAF?j zyI7E>%Jsf+ZNa7Mn;E}OEXkvr?{M*bnc>vqOpGo>V9zdAv3oPIyqlGHYM-X6iRTPGiR6N9QLtkHb zSlu+Vt31-g;Ig*kP(fG5n~GoRbw9&aIXf`apdX9;Te)IxSY_hC1>AGd)#BR94kJ_S z48vJzN~eww*I+MGA0;~g#gF(}Tp-mFE(fGIgt3AG5S}Jl2uJBX3wH;=_SZh5+Q7k= z)Fq?!BulY1a#yz}dr8>;>2%d* ztA`qsV1lyi@Z-CZDX)L5+l8jZliEE@U8cru`!7T@i7q~%KpNJ$(C!@O_$?7j390)=C~sD02iVJDQt-2E=<#q)k!9%{GfZnyTG%jYoYyq7@0FQCeyXCsVwQm zKdsAqJZv#{Ofs+^f*E*h@%8jme{JLH@E>8Qgte98(p199|@Y*3RNBDjT->t z8QM?DF!{4co&{$srx9pyie)jVs_#8d9;6&;L*$qW{!m(tlgg}AQS=ID*>zMxHH;s4 zocH+YG)hY2%^(lgHdXwTEUj?Li&S)PHRwNw@mJ9Al6I980=E#A>tw>S@d^qKGhRLG z{a&gg({&3a-#>{V2XHwrd5L_XcCjdFK`RcYD+UAVcg3V=U$?>!IZ4eAb>)%et{c<~ zLz=;0XiN6WN0w<5Wd1ViK$zG?Lo!`+JVSmR&tB%$cUN~MecRrCJ`1Nbo<|#jH`Um~ z`~I-0AIXVxTz4?dxb)qEYNl{ZhhefPRJuxUu@H2^LUDPKIry_)*A+;5Aud30vM@S- zPujrSB&$0(=nedFM*=_!iNrr)00PNL5e{eL>XXjpf@iDJZCyqAJv%L`_%E9^en9LV zVZW+Myv|gO8+41z%PGp1ww-#DpfTx)s1>_^DIk@$_jw`-6KC#|EA?aKL|C>pDi=|f zG99E+Y*80^tV@T^F1>RZ-ksCDbORuPT35&#vgXO;{a;@C7A7-AZyl=c_2-D}ow9`} zD@oA?t4wLcWlBMx3XGIcgZtt`zh9G}B_afQ+{CDQ%Zn>DzX`VwpY&dPKSJhN{*8tb zVKsT#5{u70%H`t=@IS?7;XY`5@AyDxet1KQl&S8!ppEt*NvXwM zaMMzI%Ir2Dhg&C+)AI9qs0tdVqk#(j;&a+eZhN2a(7*%hdZ3vFzUbhBYl3ra|7M^Re8!rS`Fom#|#k0 z6@RRGDTvb(z^!!Z#*qqs)5SKDc*CbE&}~Xkol7??*h*y%%K=-FwN08>8s{a+DA59C zNpm3_nr*I6)l&Ld?v;q*jqMkf^sKZWik#VOhQi#C0nI9Z2Cl@u7N99eZGTlXII-N_ zFuc}(Y?OsY4|(Ui*WL`&g7(+P$2p!=A(Fxbhhsig52WX8kJdl`I&hsWt?>X?c~UM(MZr*wkDN1g+OS?kO$ zI)v(^#WJNCQ|QmXHgR%(U(3QF8ZU~B%F$4_AY%C+$|^l8fdUa&dm(aqFD9$q+Dx$? z2EmX3qKkS3rGnpHx=V(HX-;kU2WXRu%R0tNmY9-Rxh#_&^!+N?drz|KBRf(6Pj4Yd z8>DG}?#%d@Y0uoLII&))g4UDI1B|)J9$bzg7Lg*OPt)BKY~R`8F9bmU)Pa^&$tu_MP+-gvVAX$Uh>+e<{ zJP=*aY{ffILzvD1xrGhK!{GoSU1AD&!|ZqUTGil88mz|zwMB>f#@pD>h(EzwRJ6oL z8_C)&e ztB0ee*@AQWVh8Y({0Imfnc#O26j;To$gQc3yHZTVdidBtglCFjP;TlvM=DNkzoylZ zt-i}(C1|vQl|K49qPD<5*RZON51HMP1fP?k{6?6Ss5&Hj-z%p!2d;0ntNcwE)&P~M zDYMyd@_HcuO+)rvBnQ_RxvM@ij}Urx?I?N<&C<@QgBOMjd_7LGqljvCnRg%^3ljw& zg>Y_(DSHL+fw7luYdtXsCuK`#y^7}TIq&ZF`!6zN>ZNXS2S@H#F=S)3y&H+O;juBo zyxM4I3KLQh8_zKEy2zHnIQSXL5q{}iE|ySmbj?n>V?>uAlUf;6dnQ5|G>5(4{d7TiR?cb~Z zjxsRY#yGISS=_`F&JADcGp<+5bFMO)&6EuR1Ft;mmQ zbn8eT0-C2~DB^iU9jpwtVio^LDiHMahw#~^j{}ren-j;4-$j#11kjJOmyxAKYVX2F zlP?%3^fTAk_lDy7(_|gES>hXtgUW0=qPRxyE)4W76G!Ff6Li*j1p;#a+^*1CoLZkw zCH&_4gDrw}7>DEz;%c63U~P}0-5#=}6c&MN{G z32)mKRAUYpzNkaGozZhQGtVTnbm%;qbW%1o+vqe!iy1W!^V5MdB7+TBQ!gQoI3FHo zGm>-DxfcO2MlUW88Gm2(u%=-F031mS^daIg5F&D*Lo@nN=`q1MPm>@y1J45dZaV5L zAo%e9R5cA#0KiQ*gdx9~g7v#MEVDLSd_w{#PBWD8_f!gE-ZKf)8n>)Pkk^uf|O zPy63jiDj~y@vOphs4(%DlxGp?Bl?1~3 zYz$POLwTSA45+bPhFHB0qrt+qY3@5AK!|hp{9nBsKibnnY5gw>l%2tVEsdUUh8x29*XUJnT zOcd!~XL}@`x_YUw7jU-K_bTW=b3 zyE*yhJ1r{G_G@m3`NVUV+fb89==Zidshc;gpm7?=B;IgDw>#c@Q?i;rz1u!^7X&Kj z7BQJ!y;`!q8p+~&q(UiPzxV|EgcvvfJr>}Q@cKU8887Xk{^q)~>DrjDLi%6aLF2y~ z2k;sL;|{Rkzd@Z^?4%y99v+UKZXV!92RC024`)|rZ%-dj4_i-v4>xeW0_5fD4knK4 zR7Vgw_ueW+W&^>9Mo;QdWzwZK8DOQ|+`5{CemIByB}z$o+8y6KYw?`s1=i=%@J=S3 zIVpH${v^uXEt`Q^#Fw{ZCrPYIqf5SkVzUv#tkN^~ff*v~!NwZaCtZ8Nyis4$2qn0` z*gLWHZwP++i7ur7bE1X#k@F#{{_AGGS?=qGMG3HwHn2wSsW&+ytHYow=TK@~&Skwf z$eEP19C7a7u$%<~iN&V-q(y<`+>9O-+~dX@#)~3x?3B~UI1sKnUF)y8>uJ3K31*@% zw@9$)g(19?+kN_~g2QY6HMQQ-_b=J-?8SLt=x!!wdm<8M<+dcd2C(>5w9q>ZYa*`Q zCX-w{45s7UQCL66D@_BDmW;Q=DZ_28-F((hYJZ0Y6)wNDB5^P9lnuR{j#`!I>`aYB zsx7yf6?F`sP%1&pY~uQ{EWj95fWB;bj`dYk7jX!7lo}EP%;zV$41?iKYr&e1Gj_fI zL{uH%{;e%BpeMv$8vPyBI>Y)8Tit9?9b+IKK`(Wdu48+I_0h`@c$B!FXtM5AdgwNS zB9y%fQ~_%KkGWk0UnbhW9xbn_#2iFH!Wxa4wZ#Zxb5?-B?|c|&KK3b|#u{!uaA2!7 z%F?4%RXA6hp=^6#9OYn^xIIRa;H>Y_Mr z6%HCS;zo>v#M;lgwb&qmnBmkOnS2G6-Wt8&t)skWwZ8&bN-gfbc1r8-qOBU}V-xY%$Pv77xH3E_IqJUd3`3Vn{a^hPWw z*Do>#uFzoA!|M*?ske2TC#sQ0A=c>Qj9%C>&J&ys6M#2&5g}sO(3-kh!l?n1aX6C3 zP=1&|Km(^-*-^W}jtsdzF7^0SKZMwgL>vM+}I{!w=H)I>6EER142asfIyn#)N2tpVL6%Sv5K?`KpZ2)u-_tl4r1Ybq zg6@*o9C0{uWlj7Wgidm`((m!8Hd z6`@d?>Cb|N8qUBi`|!5NX8kLp=oTWGMbQ)gZ#C-?j}-TatTb;&1oZz=BKivWea!-@beNx%AJqKUc#1Y-&= z;L3-a;%5N38>N$*n>a{KdJny{wj-jsy7enNOXNCfQ1!4UT(${1!KOpOUgBg#E8h?_ z`sp`e85>Fd4)wtvLx!sQw37G{b8GtwJtL6;D2l=Q(HA^(A1~I9i)`wlL56{Fbs3hn z#fze-CmBH3O5uqR`Z7|R9iYGQj|~D_U~K~hbu$b1?(*+_+_GkUlO0~)d|nm1sbn!8 zk|EY+?)tcxwbcXrqijl z*dzn8lyMUc!Znv{Ti1adC`(=<&Ya3+AU#saW53V&TzsS^!65qF{Emc)7TA#=YwaWhy{O+GPMqjb4>b>7 zb8;3txrO>9f=cVK)kVZF&M--xj1TJi)}(Z;)9b}G0cdp{3FRydu!)0)WWo$Ij6gzo zY!hKjO$XT&AclVbEEw%+Fy55DPJsYi8~@NAVrX;h)Ei^)gK!-&Fle*v_vabA zU$#rS{B0pI$&|D^37hHBEwjvi=NF4_t1pFy4gCF=96G6{ICl&SU}{G71@#Z!H}W1p zs?0W`+<_aJ6hO(oUR)IxoQA&ZB68jI%#T4;Q3>TY{2c;)uBD7v&V6W|eAR2?TtGJ` zruc$0?q>ccU1Y#t$tf&K9w`t5R^6`#3gJv5Wyv!Fy1L>aYn+-01(`$YPtahTo2bVs z1ewYvM^0WLUiga$MF2IWInqEABR- zZlZewTr&EcOiLyrO;T!)&&`cd2%)5ft&{t4S1SY-X2L`DU#bxFM*DtOJU-w@36_&2 zc}mQNTX0Ze(0QlsDjdKXUiL=eiCcZc(X}qTFs(fHoo(b2LFEr7xqelyMaqS>CY$z4 zy9Urk(xTCCor+l^*_b7N4Xb>{^ctal_3IzJJ=A$XGBZJ+SUrl_L#uF&+Frga1ptiu zI`qOipGNZpaQ^4$OTq&}UVSAo7(R8!Ru@|#l~u2y@O~8^wG$+4vMeVb!tO+b zaj{n#h9z59GK;3F#Gn#`&RT|6J!e#`1Z26!#(X5_mvsi_bt68hm;O ztD-7no9+HkmH0;iwmb{zAkuwEt`-YRIux}TAbMILxbl!aj*4bSP>zp=PC=IQx|R7C z&pzRk!iVrHW~O|>rbY^LWP9Cm;jb8ha&4C)KbuK~##)HcWxkeaU8Ot-dJW9(vZ;ce z@VU5nU^G8D?k)aW=~EC_OgTHFJMyS$go*L@v`Hp-0|42$(WN9VUb~l?<8K5=Ez^uS z7-YHj1aTu9*g(zsEbZs=_O#O$Y`2~j_U45*sMD!+`h4TI4PE8WQ}GVBX++Gi4w7GR zj<8--{5V!jBP-vh?^P?(U5vl>+77Jkfeh3F&6=BJFUO235q>6_v3ZBYetpj~ubMu*WIMqHayjcItZ%M>T*aiyUW zD{bO!#cHW3d{NVV%(oTw-f`<5H{~#f4qg0beKD45IK+wz^F8sv`t0nXEiz9Hz5{vNpd(&5d*2yp0EMS~1vsshxJ);Z%P6kFvCf1y8{C({w{JeVxS zjfSbC8q;xsSXqEjMJu=iNj!ZJ1;aF;!Y$Zmyrzn>nUam}BUO}53`UYhiA;Q5RDPL8 zfUfZ!C;KMUH%&WJr`DwLqsD6yt{ni5qWDWB7(8RH%Tw{dh(m7Py0KUV;x*2(jqp={ zF~r|4D^hWB1YJK!b-L#z62>>AGD<>R;Pr;IsZke_jJ?U<>e zu)r2ox=xhL_Uhpa!I@F;tG-F^#k-z~l`9u!Pp{Q|3&H&em*zco*K1kNrpS&&zvAX ztmOIQWkeV5sGw)Ee~bARL49T_&|J# zrKGK=tG}=YIxi$2aQBi@Hm|vDSUZ;6wbzGLJjH%qe;%@89)I85r@n>a&|>`JVTntN z_H9U-Q`-lI9`^1sPlYLGVK_G7mvrFEAp)1@V}7;I3i8}s-ouLrlOgh@a)QwNhwq9x zJiuUefra=#IPTe4O54~G7Y)9K#7GXS^y8;Paa8iGSAoK8LbIp5)p*+~VmbTH3}%~F z)^@zV_}@UabV~9HqprieLK$M~$!+vsw!2Tc0aPjH)>oAqA=g!K>p_21r0F4`4j499Q}tlULizGI z|I{yztDX{?Fe|B%j@L|b>^qc6Z+_yLdzmAcA%#e*^DV``qLr8e2lD=ZZob^!pa`t8 zxfIc9x-Yo}QpCpN#rO$m5&(p_9Qos`{w0Dk2^)fKze;1)sb2Fgr zN5(SoS3HZ5buW01ZlKlNUgkid{1tPtpErxra-LdRHtg*nEDL^Kj9PxBD6D)M`y@j;z*vmKR)bS+S}AoK&;YFs?OljZ5+w5>S`9dsvVcxcYQwZcozETR5K z#NZKosv1>m?qtf+U{3L0r1z7DvzdcmXOD<2R)ECmR^Angk11Q)t80Bok+T&d6un95ErNj{43ALMxuAZzs$gVTOVs<9!X7S z;fE81OKom+8oRGM{h_^zVuhah2f;3;z6G)+bahaN5WP+jSVsT_MsZBYDhxdHx4Y$J zWPJSTAplWDm2!gldWu;O)q2p^>NjGt(PDDuw%Vm!d6b9s8Mj;*5sBYL8rK%N6&7X( zzl-I2Nv#lYkqL)P)+7lKKt!fr@h5p#%;xXw-x5rc)35o!V*>oo8kvuv*5Rvi!-vv39#Slcz@;Na>cx0HfVmN#j!ND7=dzUvfQ`;Y zIvQ<^&vPbS9ZLXy(M6bC(?N$42WJM{8naD91RT=975?)_fME1{YAH_g$-defABX(} z(#?Gq93#BH#OP_AV%Un;EsL)?eK5w%clT{ws`4_E{>cah( zsIsuKy6~Y+d$^;DdeufBXEBox$6oPal$W;8|C^%#a#ET_6 z6AsB&o^YmZSsWkZpr?(pnjh$9${G*L$;_;Qsi3b)y z*t*~?ZrIRI{wWi7aitJ zbzi3a;LP!QsA(u*f{^|0_bPo+D&GDh6zZK<9|k2QmVifjoU;f!ESSH53G`C*?EfpNsvYbzGBYqQyPgIy3KqAgg32BVeHAbne&0i%(AH^MAyA zNMvAn;i1F7H8PBvN_$>I{)jpKC&vFYHFdKugAngDQ*bL;90Rg)kLPsPGJIHhy@~ri zwcy>1&51CH%$1i2f3_<W`+5`OGHNcWRD4nK~hxy;TmSYJ7a5fLz? zfluM9kYSsgQ${Y4q1r|oN__xF6tKux?>1Vjr^&&{H8V3i(eYN}x~>g`UOmEC6uaQG z!koDrWz>W~4VIK%fo&g3-K|GpYv$Co0+nXK<2U;0`t04=^}(5wBm(TXqP61+WW5}fu+X85g&4+QWJZKGeeSY)R#Rtnq>H~ZUUTFmh2H^& zYd@ovDCfvggpmkT$nl8Y7!FfxwxfV1z)tyI=totI>tiJJa46Lsi9?)VOz?ueFUB|t zjW&O?FatoI-%bzyfQ~YcCVG>x>QHJv@mqMRDZ`W1Wto;`Dv|da<)Yu`=`-VL#zngD zpLu$Q<8`?tQ_N}*Q*2r)!eR{nW@PW*M4(AmrYYXtGER-)k}*LWhQy#)e^YYh;9gPm zygBgScimrvwY#Bq>OU7e=-i9e2B1_>`y<;BfdJwiz~~4i$I`YS|GR77sjVP6Z@Si@ zOjbTRR4zSw%qf_a|&V24eMiI|A+w5B8#TkqMIS(Qt1cc)5s-sejHT_oPCSP!$4}cz;p<4igKd-qeME~ zx$4zYj&&O-SbccTmSk!Qc7+q`_6LSA-`{F9G&U?HgP>-b7|6=2Q5kevdl%CS#ST#7 zhetQ8bQ7WsUF$1jqFzFavJ2Q8lKxWJ-aJoWPtJT+0EKsGxav_X(-3%@N=0DFL6WM72R?tT0j?#F%Y}*WjGWGpH0?is{X;k`tDQ_2?h< z1<`0i*Zd+tc7t*G5{l0z6Wg-x291EC^xJbe`M(J_l@FkLv)zW0YSV+QjwEFcQ1Re4 zBr=m>*?QT(H+iKDRPNTqOJ2?X%JMt`8%4>~_-o%@K9vI6-5_edvOe#VybSWt>c4S7 z{zI&tcLnp&+wzexF9wv50Awb>9lWf|)C|*PMA`j9wBC)5OK`Q1Q^l4V@iEWd`%h2x zAAlG>r|RGb@%kQ&>AI1hEYj9`3QKWuyZY=&&ytF>29R)8VK0tBoSbExH!ZU#B@@$a zCz*1&)EHnY@OgnNZ=^E#?+2TRbI#{561e}ohc$E8ad<4OM7O=f*m^W+T!Ktw7pIv! z2A^EVZo30*^)FRrGDX0$G^92wxhH~Xt#%sEhgK>?N7>;Cs~d5VmG?G(ka68)1&Yy) z>=XQb^}DUB1i=p!hQjFSLpFe3qD74z*ceW-8;aA+Zs_j)-`e8=T=8oPVTeqRV*&F6 zemO}Q;sTZBZeiEs4X|;V0f)ukRgMvs|U@ zF$qR}V(#_uhRVT<#{Ca{b&(UknNAsc-)Sd0BWRJ8jAwLuj3lwa0o}wsq9_Bed57Sj zA3>K{3V@#Y7#?DkgdILY&@s0?C;Z+r^j&LkEA)rOW``QtSkls`kVw<*pCX3h#Po&> zVJ9!)`ct!S(E8XXPRr_9@Tk>Z2ds4h1B@QDGKHRI{ixDY~d@Pws> zZTYwb5gmebROipXl4{^*Vra+~=6M1z{&ap5jiF6ixZIT9Fo5tc!mu2x*HfE$w}%w# zMuo=RGw#aufXs5_yVCFQ_zf=wSA~W-#zG=9dB#Dz--Q~AT$0ozk5a#|B%FSvJL`*I ziTDC+(=bdW0Cz>=vRjBpcO1!^3agHRI~2Q0(_;bbI*LC8kQz##AC4;a+GsHDMt?P# zuN}DhS2OOt3b2L!ph?|1b!HHN%t;!8B-bcavpz}@) zGx@mFWQPzROg{|V+4GPdq1Qx;Mjr=Jt+sSt?Qt_4(BI@fj+3J%w7#D%*hYzlxQm4D*jJ>eL!GONpxOyZK#ieuIq(8rfS#o-ey~p-RkU-e}QrJ-UovY z%|GK@4Xl%R0C8NdB_-92SX0cCd}@U@TGW}o22G%sg%nETX7<;A7@^co$@Py;^T^kf zp;xnmuGB;v|LK+fl4g5jn)ZUdV5`dWm{bZZyN3bHrOP;T=DnSXU6!O}h9|8*(XJEi zs(F%VjsJ-}UbxDVvB@cYsLlA?m8Zl5+#EY3Bn$RN`h;wgVm7?Q^g4Vdb(<#oxLnqc z(`j^Yu`FG$xa(nGJ7m-%0!N_0Yd60D{I%bFV!{Cg0sJ3VaLmFLJS7uc80iQCxq&y}I?Qmy=8C561{ia2AJ(+E z39tb?y@jI*vs8V<9eoSCHjUMPGeC$KeTYlgk&--~4?iR{Aks1vJH{Mh8m^y? zVeJkN>GeU;|0iR5PsI&)5O#j0Rt(Ds6hAjUw1l-si@{Y6MNfGEr^` zJELvNS0x`o1)q!Rm^?^-YWC9IRs&BkF^NA3llTDy3^6Fj7$OEy5N9%!E&Ciu$4DSk z%iOvpe3DnUS?)z1n)>@RfDsWLig5>#$JxAC8*`0}i79wp)!w zi((-4u^GZhAS8=lJm~)g(3Sg^X=QxXSUbZ*29p${Q7*!}W6{GO1W2Hq;UE~M7!Aa+ zE>M>KtFh}oslu4_2r0j|E9rRkA8nuM$UP^*L1BGlDF~5&Hv6(Hcr)I`Uyue_qhY_O zN|{E(d-FK>`5>g%vv`zW;WGGs>C3jP&DR7%Jlva8^@qk}Oqs#@kAmzZa>1d-%k3yI z>$`160xZP*6Altc=*NI`39rMq+tpC0(k4= z6OgpWTIbvQiUji8G!fs-EM`Mn6dMh2$!T>yp2I?!X5o6&t$(j>FWkA76tUUb<&4tF zvD%Sdwm^Zx2V;!bb|UmC+2B&MUZEF1K%jGeMUf*0fUD?@$<&SURTj^@jI%PC-C79$ zubdBcD;v@>WArH;!CDW$hl^G$EgbUcjB?k>rk&}F&Oc!$d1(Yu%n`u5Z~pYzova9t z18dZxE_-_#;<-fCj_-Nhpa1z>BHu8KIS!>OEbKLvfPxf3RklmL*AJQHl_-_?Sk z*!01blDfdenU4(z1 z#8`e71>FNC8P0^Exq`x`d#>zv5=jcCea-IoYEncf;_tnPg-@v-r6}O7qHgagG?*`d zl_?PT;MHv-3TP;JFnjU+i2Q#6F&570Kz7I4Y?hV@U-qkrVeZ7p`;p#>}(r9OHk%Uh|U&j>uD>O|@Era9^ zzIBP+3Vjm)=n97tXh7gl`hEGzhgAR`iDbGV5Th^Z`7|lA+(d`5&yXB?JCt_g$*T|j zeMfd_nMap(Otd8dEgJptB0`R__u+rA%%>c+37y&oDdmPH$MT&Sg#DGfH_^)UdR33aj&!d!Tw-@jQ_1ML0h zxdFhK?U+Zg(l}p^{X=VicrAq(ggrs=`?1> z2XX1Gvy9&>10@65`#EL+Pzz{qR?}*Fm$}+(s7#EboV3DkWth`t&0EtfOk5|_yfYQw z9WIFv|Gp3*?&vxAq`oMSA<-S+NquTow4M4%3O@(eAT6Lb5p5nK*(i<>3grc7E+ODR z-e#2l2i_S?u^>iy;f;0t0_A;%>J1mbm^ee^+J4G66mXAgEb*kTkMg_hJ{ye9TsG%s z&E_Ip6{QKRd%YcMn^2GFF1Jr*+*{?naNcSm?=WY~9hIM%wQmN05AbGX8XcM-J6$(-c9uq zn$0kpp{;d>OZCN^aq$tne_yDwjB@A6xXfZW;SkDriOz$J>GfBpKr}(A`{{Qz(L*~aTnDjc-ZU&1pt1z^lV3kx$>lS0|qc0`#g~Vpekoc`cSSK zY_V`G6f3IdX=T@l7-^n&>CK8^@8daKu(VEB7P<>&kPEWHUGG?0>ynzvDN`7upOy&R zCfxA5?dtg4Mlw#^40=qn7Ph!;b`|?=s~Ur6*i|e5{~&&Nzs7&{GS;b zK#khUG~TLn&GKvDHkb+$dg4C3ftzz}q2By8$FL0Am>=L0=|4Q5(j6W+O|&h%M9gd#X=L0i74JnshtfOJLj#NZ`At zDm%AphF4C708N>O<1n0@JOXsGN_eh(^iRY?a&N$#MhR@Z>cX0`8& zR=Tkv0Dc*DdPWgf&boFPkZ524!?EZ8A_xG|)+nF;$~TTU8{3=G`bwCDjk;igkGgsW z3+RcTw{|Qa^Et4saR~d!+dc`?D@x6%|MFYeEGdFl<#? zu{8j)!VK_Up$Cy1$^_o&jJlv!Y#glT0~GhZ9^e2-QrY^HXKI`oB^ZkM$|drYejlI< z>eKcrX9pp$M{DHK2?xe{K3gs(^>$mYhcJ zi78*br9vO50iuxC^Q~Q5If-R3l-7?78y-1jtmEjVgYNm8k)nY}{{R5pXd03hstXH1 zt#()a;?gxOWAqi@gdEv_1}~LLh-1U(Na_*b*QyNlUq?e%zbK?2qh7}WX<{SN>xx@f zxL1~qGL_5xM7{$1$ynm9foxIdeLz+OG~e2w7>b2YCf=`Uztqa!0AAU2X2NqWZCv~J z^0Wrb*B4VN?%1v}ZM_R%&#Z6SJ73FMSesP~L*K892H%NhFWbb`rH zkzKceB@zsW(XKjo>m(6uQKwAy?CNE8qpV8ZZe9G8*hkHs>DewXdY&}B?PmtWl8uKt z$E2IqXa#Zjc)Ymg);k;Cjg*LLRN$pczIgEBsyy88fRI0z}aSknHz?eLUV`} zh64=dCP?+J_4P`*c9jbdVZ4{}1e0C|0smaVTNbXLjkkm4Q+L0R9Q}b;bvIX`}AnNwEg9&v_XDpzSjqAu7(>RJ@o;LTW0C z5k&m{DuOe}^aoIqSbj&wD%tNQ3)1HUB~@DjW(izK2d9f!v$Ep;i7~{V6knB_D!i*$ z>9poX1!%s;CkEgp0>6c?o1i@%&CL#9$uqDh8JGYb*<@zKM{(hadjB${q`c3+ZU8{n z7cEKn{P(|mx53kd*I48hd+|u)LE0O^#SAM&=ED`1TR3H>`|=!Sq7KC?x zV;nU59adSn%2ZQHFw9=Ww*EaPG;s-bt&tQ!r~4TE$OI>AoAjQA>)ZfabDUKhcM=LK zooJiK-vj@nvg4Qz|?VY~e6uK_tkoc{l>FCG5r^sy01Zr#Y|R~brs zhfx54wmc_jH8N6>Sdr;{GbHW?Rs8QHua*VEDZ|`VAi{+4UZ=jOjs7~QnM$7ji0gkT zz29aXQ)_sJQTxF$q@0^xY4ufreoz|6Wfpr2zWKoL5vY7JGM2+ifV*=S0A2}|W+jqd z*}7g6j4&Mg{I|+tB_JJzFF||9y=>NBshFCqHFT@wqS?}zOt-&IJhbAR#PoR5Y=u8U zrnPE1oMwo_4C7vtpJ;XpB)x_s+-Ln;MJ7o}-ro}P3xEbt?@%~VZ5&#Fc_8+C4CWL7 zy*5``1ODkOCZa}eys?~592EDyCP;vGlm)5YUmv|P6u)MpDG8I1`_nHEncNlEnkuU1 zq_I_WuacHT&JsY67z1J`Gn-GR^x(CAb1@H~H0ob(c(PZYEeG^<&r(b5HjFfVXkEi- zqQTM4{T=o*R-HdUFBt<_0Dd`@X+os9bfas(qii7i{PDzzO&~XI6D8Q61&~KQsfMKt zL#I}rt5XKX&|7*|D-Lm%<{yhf>qm|x^I=3E0a(8jrSg&s+f*!tDuN8^aLYl4>;R1~ zGsB-T+XxdpYV~ukN9tEaPP(~S;d2bkXxpo}X#h`WXHx(K(ar<_00000ZwUYZ3IG5A z-@$by7jANPaddTUad>%md24KKba-`ia&dAXUdi-gMkSX{zqsb_U?6*k${GNqqf;ck zn5)aOce+n!n!94g;O;ZoJ2ZdCv&3a-;t@?sXx{nNelcXT6HdJw74$L;rq@wNa|~Q2 zhxu728ECaPu~vql;ZOH2%bgxaOwI03!n$uDzpj z1|Vt6SqWMi*2i7#I2%NB(hN_IqDVt+vAkk}00sFGtrV-3%F|x!xnA81(cc3XG`>4L zQWhyP3fgZLu-dHlnf(LOotM>#YDO{>ZPgNR zE88_H3s02X4hjNyT&;f=UI_W@cPNdb#{I=)^*5M-ndF8Ek9RLQp&z-6Z4bZkqW~IiPbk>%?r>a;ceGSv|&08!4z?5Bs z7jQ&|IHydu;I2jOcIPBDWo=|0thWnUp86?D2a7O%2M++I!cA1uTC*ys?G^8&^21=U z7XVnWt(VzUQ^NQOfC&Je*_3X+*4+IH67-zrY21Vr&NI!D)g_EaAM zL6eISjBz>QmdU`fg+dsai*WraO0l-&HspS_p@YX_24+#!{V|vxehD>kA+ox1=u!YB z!?EXhat*zJ>T}kkKWed47UlaI?FA{-0wb4gRQP>`G-^ zGw9OHNYQZLTUV(70F@oJ1dX7niTfH2aiK??k;&xkBXD$rUha~{@y?DSE3ZZ%#gDEv z5TomDmeMY2Oxvwvf~iI^*Yui_L_w{6EtW++?IJ+0=WO~I@U3eqU->j7ku=k~a0FaJR6Ir;78P|{JbsSnrPa0~UVh#lLH_?r*mzXnI zQw9x^UtzArG?MkMOW3e`n)7Goal@{$p4z8)_<2j zt0VxJWJf8IHXP<{~^JU25A03A~davbC!S>n$FygW3LR;a1mCjnjw zb*!S+Uf8JTmmx(1?DG#c5dh=p8K3`G#XiRd*~>;nLekGn8v)V`KKLxwTs~LYM!32n zi-F$|&$aT?&Y`H>GDB?Y7NjM@0tu#4#MN5{HfJiLnvj$G$DjV`o(jMq@PvT^KL|89 z)jKZ3-mvHyc}U_bItBs0IW=)2l3qIK_U~XId;gaM0H{$oA}z0G+9hkyS892*@*K(6 za$DM%yZ(YD9|%71(&-l+NtBp8ObIara!qxfo7g2yr7EF@zJeHA+kW9e@JA7IfsQ5; zxJR37EP!XPqwuym{6aJ}3<0&Yf^ds55H=cO%2ndA^g9lv`1wR)HPk7p-n&7H!1!qc1E!oEJ z^)J6k6O(0cSEUZmTusRKse26qRU1CZG-5}sxN^j<1HeG{oKAoYfHpOT>5$1p^jVW; z*3l^vlNl4h>DY^%%xhXH-|nGtv8D%)Vi9t&NF3=|s=EE~xKSZq=#kWfnrGSEwE{AK zJ4?ziY=hh}kf{f))lCI}9p;YNM71Kw*Tw<VG=A-0V0yI8)wU z#M(IZ+)7Az`$M$FeF|ubn&poB0oaB44z2v~Xw4oqG=A`p!7KoN88vbu5SN~GJ-{>2 zJ*U-{0ANhy0U$u*c?+bttMWJ%L>O5~;&c&X<4V0N_A?2nR$9 z8JIXJv~+?qCJNn^lY4=??wrhJU$4!*;~}u{a4Ap=LIyJcP$tzliNL zk4*r6x%8r-QfzF|IzLOpDD1t)4GpjYOd=_iY*~<$t^b_mZz|d#$BmNxH@X?L+*wML zY#RLv8MiIp{L6_$_L#{+*lQ2=LEx$&Qb@~TaoDg)}j8gVl!U> z;HRld^tHbk>0=||KiFLZ{uzz2A&j{2L^ppKDH>p(Pcg#+v=f;W{NUGXIjHZ<^b)W= z4eqSCr8yV=WP`lN(nl<*`q|*o@M>clw^_F>!L7xx)UpvS^H6m+WMNiO4?1Q$Qx}J3X z6L0yT?Y^;b+${G8A(eo&p(f?O62lk*0EU(f*FsEl$Y zGI7mU>DA1oVakU4J}*-MpdGE&^p&@+s2g9KN|mZ!cIxAdFo!723>ceV$^0Cn9V%|9 zpZ>Sii&8CZwU2&P|$j?@Wqqi3xeT{nT*}z{?G|y=*Q9pr(Y;s!{o? o1%UY)0KQcGwdZ&c*P-=kT*H$hD*%kHDFA?_rIzvXHJact0J{RgrT_o{ literal 0 HcmV?d00001 diff --git a/sound/machines/vehicle/engine_start.ogg b/sound/machines/vehicle/engine_start.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e4b5ac744b9dcca2e69a2303eaf37da8c918886b GIT binary patch literal 39943 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
    +

    30 December 2025

    +

    Penelope Haze updated:

    +
      +
    • Iron can now optionally be used instead of aluminum when filling sealant tanks for sealant guns.
    • +
    +

    02 November 2025

    MistakeNot4892 updated:

    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 b%xqOodx6}??PAp&D!$ZZL=BD3eSrKQ-_dRd literal 0 HcmV?d00001 From 88d07f3abebea398092aa09a77e6fca94f7ec582 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 28 Dec 2025 13:34:10 +1100 Subject: [PATCH 0950/1331] Porting cliffs and mines from Polaris. --- code/game/atoms.dm | 23 +- code/game/atoms_movable.dm | 4 + code/game/objects/__objs.dm | 1 - code/game/objects/effects/mines.dm | 98 ------ code/game/objects/effects/mines/_mine.dm | 202 ++++++++++++ .../objects/effects/mines/_mine_payload.dm | 22 ++ .../objects/effects/mines/mine_assembly.dm | 67 ++++ code/game/objects/effects/mines/mine_emp.dm | 12 + code/game/objects/effects/mines/mine_frag.dm | 23 ++ .../objects/effects/mines/mine_incendiary.dm | 16 + code/game/objects/effects/mines/mine_kick.dm | 17 + .../game/objects/effects/mines/mine_napalm.dm | 15 + .../objects/effects/mines/mine_radiation.dm | 14 + .../objects/effects/mines/mine_sleeping.dm | 14 + code/game/objects/effects/mines/mine_stun.dm | 14 + .../objects/effects/mines/mine_training.dm | 15 + code/game/objects/structures/catwalk.dm | 1 - code/game/objects/structures/cliffs.dm | 300 ++++++++++++++++++ code/game/objects/structures/fishtanks.dm | 20 +- code/game/objects/structures/railing.dm | 10 +- code/game/turfs/flooring/_flooring.dm | 1 + code/game/turfs/flooring/flooring_concrete.dm | 1 + code/game/turfs/flooring/flooring_grass.dm | 2 + code/game/turfs/flooring/flooring_mud.dm | 1 + code/game/turfs/flooring/flooring_snow.dm | 1 + code/game/turfs/turf.dm | 24 ++ code/game/turfs/turf_changing.dm | 2 + code/modules/clothing/shoes/_shoes.dm | 1 + .../designs/general/designs_arms_ammo.dm | 6 +- code/modules/mob/living/living.dm | 9 + code/modules/mob/mob_movement.dm | 10 + code/modules/multiz/movement.dm | 42 ++- .../modules/reagents/chems/chems_compounds.dm | 7 +- icons/obj/mine.dmi | Bin 0 -> 403 bytes icons/obj/structures/cliffs.dmi | Bin 0 -> 73393 bytes nebula.dme | 14 +- sound/effects/break_stone.ogg | Bin 0 -> 35871 bytes 37 files changed, 861 insertions(+), 148 deletions(-) delete mode 100644 code/game/objects/effects/mines.dm create mode 100644 code/game/objects/effects/mines/_mine.dm create mode 100644 code/game/objects/effects/mines/_mine_payload.dm create mode 100644 code/game/objects/effects/mines/mine_assembly.dm create mode 100644 code/game/objects/effects/mines/mine_emp.dm create mode 100644 code/game/objects/effects/mines/mine_frag.dm create mode 100644 code/game/objects/effects/mines/mine_incendiary.dm create mode 100644 code/game/objects/effects/mines/mine_kick.dm create mode 100644 code/game/objects/effects/mines/mine_napalm.dm create mode 100644 code/game/objects/effects/mines/mine_radiation.dm create mode 100644 code/game/objects/effects/mines/mine_sleeping.dm create mode 100644 code/game/objects/effects/mines/mine_stun.dm create mode 100644 code/game/objects/effects/mines/mine_training.dm create mode 100644 code/game/objects/structures/cliffs.dm create mode 100644 icons/obj/mine.dmi create mode 100644 icons/obj/structures/cliffs.dmi create mode 100644 sound/effects/break_stone.ogg diff --git a/code/game/atoms.dm b/code/game/atoms.dm index c197b8580b9f..fc5d6249d26a 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -742,17 +742,19 @@ - `post_climb_check?`: If we should check if the user can continue climbing - Return: `TRUE` if they can climb, otherwise `FALSE` */ -/atom/proc/can_climb(var/mob/living/user, post_climb_check=0) +/atom/proc/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) if (!(atom_flags & ATOM_FLAG_CLIMBABLE) || !user.can_touch(src) || (!post_climb_check && climbers && (user in climbers))) return FALSE if (!user.Adjacent(src)) - to_chat(user, "You can't climb there, the way is blocked.") + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) return FALSE var/obj/occupied = turf_is_crowded(user) if(occupied) - to_chat(user, "There's \a [occupied] in the way.") + if(!silent) + to_chat(user, SPAN_WARNING("There's \a [occupied] in the way.")) return FALSE return TRUE @@ -1062,3 +1064,18 @@ if(blood_color) return FONT_COLORED(blood_color, "stained") return null + +// Used to mark a turf as containing objects that are dangerous to step onto. +/atom/proc/register_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.register_dangerous_object(src) + +/atom/proc/unregister_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.unregister_dangerous_object(src) + +// Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. +/atom/proc/is_safe_to_step(mob/living/stepper) + return TRUE diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 94c8602eefee..628b5880042e 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -628,3 +628,7 @@ /atom/movable/proc/is_valid_merchant_pad_target() return simulated + +// TODO reimplement this properly. +/atom/movable/proc/is_incorporeal() + return !simulated diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 01cc88b50e31..123678e0cee3 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -493,4 +493,3 @@ if(anchored) return FALSE return ..() - diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm deleted file mode 100644 index ac59e8322d4a..000000000000 --- a/code/game/objects/effects/mines.dm +++ /dev/null @@ -1,98 +0,0 @@ -/obj/effect/mine - name = "Mine" - desc = "I Better stay away from that thing." - density = TRUE - anchored = TRUE - layer = OBJ_LAYER - icon = 'icons/obj/items/weapon/landmine.dmi' - icon_state = "uglymine" - var/triggerproc = PROC_REF(explode) // the proc that's called when the mine is triggered - var/triggered = 0 - -/obj/effect/mine/Initialize() - . = ..() - icon_state = "uglyminearmed" - -/obj/effect/mine/Crossed(atom/movable/AM) - if(!isobserver(AM)) - Bumped(AM) - -/obj/effect/mine/Bumped(mob/M) - - if(triggered) return - - if(ishuman(M)) - visible_message(SPAN_DANGER("\The [M] triggered \the [src]!")) - triggered = 1 - call(src,triggerproc)(M) - -/obj/effect/mine/proc/triggerrad(obj) - spark_at(src, cardinal_only = TRUE) - if(ismob(obj)) - var/mob/victim = obj - victim.radiation += 50 - if(ismob(obj)) - var/mob/mob = obj - mob.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) - qdel(src) - -/obj/effect/mine/proc/triggerstun(obj) - if(ismob(obj)) - var/mob/M = obj - SET_STATUS_MAX(M, STAT_STUN, 30) - spark_at(src, cardinal_only = TRUE) - qdel(src) - -/obj/effect/mine/proc/triggern2o(obj) - //example: n2o triggerproc - //note: im lazy - - for (var/turf/target in range(1,src)) - if(target.simulated && !target.blocks_air) - target.assume_gas(/decl/material/gas/nitrous_oxide, 30) - - qdel(src) - -/obj/effect/mine/proc/triggerflame(obj) - for (var/turf/target in range(1,src)) - if(target.simulated && !target.blocks_air) - target.assume_gas(/decl/material/gas/hydrogen, 30) - target.hotspot_expose(1000, CELL_VOLUME) - - qdel(src) - -/obj/effect/mine/proc/triggerkick(obj) - spark_at(src, cardinal_only = TRUE) - if(ismob(obj)) - var/mob/victim = obj - qdel(victim.client) - qdel(src) - -/obj/effect/mine/proc/explode(obj) - explosion(loc, 0, 1, 2, 3) - qdel(src) - -/obj/effect/mine/dnascramble - name = "Radiation Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerrad) - -/obj/effect/mine/flame - name = "Incendiary Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerflame) - -/obj/effect/mine/kick - name = "Kick Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerkick) - -/obj/effect/mine/n2o - name = "N2O Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggern2o) - -/obj/effect/mine/stun - name = "Stun Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerstun) diff --git a/code/game/objects/effects/mines/_mine.dm b/code/game/objects/effects/mines/_mine.dm new file mode 100644 index 000000000000..a377a9d82869 --- /dev/null +++ b/code/game/objects/effects/mines/_mine.dm @@ -0,0 +1,202 @@ +/obj/item/mine + name = "mine" + desc = "A small landmine." + density = FALSE + anchored = FALSE + icon = 'icons/obj/mine.dmi' + icon_state = "mine" + + var/actual_name + var/actual_desc + var/actual_icon_state + var/hidden_alpha = 255 + + var/panel_open = FALSE + var/armed = FALSE + var/triggering = FALSE + var/datum/mine_payload/payload = /datum/mine_payload/explosive + +/obj/item/mine/Initialize() + . = ..() + if(ispath(payload)) + payload = new payload + register_dangerous_to_step() + + // We store and hide our appearance if we're armed, to avoid people gaming mines via desc. + actual_name = name + actual_desc = desc + actual_icon_state = icon_state + update_icon() + +/obj/item/mine/Destroy() + if(istype(payload)) + QDEL_NULL(payload) + unregister_dangerous_to_step() + return ..() + +/obj/item/mine/on_update_icon() + . = ..() + alpha = initial(alpha) + cut_overlays() + if(panel_open) + add_overlay("[icon_state]_open") + else if(armed) + add_overlay("[icon_state]_armed") + alpha = hidden_alpha + else + add_overlay("[icon_state]_safe") + +/obj/item/mine/attack_self(mob/user) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times + if(armed) + to_chat(user, SPAN_WARNING("\The [src] is already armed!")) + return TRUE + add_fingerprint(user) + msg_admin_attack("[key_name_admin(user)] armed \the [src]") + user.visible_message( + SPAN_DANGER("\The [user] starts arming \the [src]."), + SPAN_DANGER("You start arming \the [src]. Hold still!") + ) + + if(user.do_skilled(10 SECONDS, SKILL_DEVICES, src)) + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + prime(user) + else if(prob(user.skill_fail_chance(SKILL_DEVICES, 50, SKILL_ADEPT))) + visible_message( + SPAN_DANGER("\The [user] accidentally triggers \the [src]!"), + SPAN_DANGER("You accidentally trigger \the [src]!") + ) + prime(user) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + +// debug proc, replace with proper disarm minigame +/obj/item/mine/proc/disarm() + armed = FALSE + triggering = FALSE + anchored = FALSE + name = actual_name + desc = actual_desc + icon_state = actual_icon_state + hidden_alpha = 255 + update_icon() + +/obj/item/mine/attack_hand(mob/living/user) + if(armed) + trigger_payload() + return TRUE + return ..() + +/obj/item/mine/attackby(obj/item/W, mob/living/user) + + if(IS_SCREWDRIVER(W)) + if(W.do_tool_interaction(TOOL_SCREWDRIVER, user, src, 15 SECONDS, start_message = "carefully adjusting \the [src]'s casing", check_skill = SKILL_DEVICES)) + panel_open = !panel_open + visible_message(SPAN_NOTICE("\The [user] carefully [(panel_open ? "opens" : "closes")] the casing of \the [src].")) + update_icon() + else if(armed) + if(prob(user.skill_fail_chance(SKILL_DEVICES, 75, SKILL_PROF))) + to_chat(user, SPAN_DANGER("You set off \the [src]!")) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + + if(armed) + if(panel_open && IS_WIRECUTTER(W)) + if(W.do_tool_interaction(TOOL_WIRECUTTERS, user, src, 30 SECONDS, start_message = "painstakingly disarming \the [src]", check_skill = SKILL_DEVICES)) + visible_message(SPAN_NOTICE("\The [user] disarms \the [src]!")) + disarm() + return TRUE + if(armed) // checking again in case the do_after() stacks + if(prob(user.skill_fail_chance(SKILL_DEVICES, 75, SKILL_PROF))) + to_chat(user, SPAN_DANGER("You set off \the [src]!")) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + + return ..() + +/obj/item/mine/proc/prime(mob/user) + + if(armed) + return + + if(user) + visible_message(SPAN_NOTICE("\The [src] beeps as the priming sequence completes.")) + user.drop_from_inventory(src, get_turf(user)) + add_fingerprint(user) + + anchored = TRUE + armed = TRUE + + if(istype(loc, /turf/floor) && prob(65)) + var/turf/floor/floor = loc + var/decl/flooring/flooring = floor.get_topmost_flooring() + if(flooring.can_conceal_hazards) + hidden_alpha = pick(50, 90, 120) + + name = "mine" + desc = "A small landmine." + icon_state = "mine" + update_icon() + +/obj/item/mine/forceMove() + var/turf/old_turf = get_turf(loc) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf?.unregister_dangerous_object(src) + new_turf?.register_dangerous_object(src) + +/obj/item/mine/Move() + var/turf/old_turf = get_turf(loc) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf?.unregister_dangerous_object(src) + new_turf?.register_dangerous_object(src) + +/obj/item/mine/proc/trigger_payload(var/mob/living/M) + if(!triggering && payload && armed) + triggering = TRUE + if(ismob(loc)) + var/mob/holder = loc + holder.drop_from_inventory(src) + visible_message("\The [src] goes off!") + payload.trigger_payload(src, M) + disarm() // the mine can be reused if the payload doesn't destroy it. + return TRUE + return FALSE + +/obj/item/mine/bullet_act() + if(prob(50)) + trigger_payload() + if(!QDELETED(src)) + ..() + +/obj/item/mine/explosion_act(severity) + if(severity <= 2 || prob(50)) + trigger_payload() + if(!QDELETED(src)) + . = ..() + +/obj/item/mine/Crossed(atom/movable/AM) + . = ..() + if(istype(AM) && !AM.is_incorporeal()) + Bumped(AM) + +/obj/item/mine/Bumped(atom/movable/AM) + . = ..() + if(armed && !QDELETED(src) && !is_safe_to_step(AM)) + trigger_payload(AM) + +// This tells AI mobs to not be dumb and step on mines willingly. +/obj/item/mine/is_safe_to_step(mob/living/stepper) + if(!armed) + return TRUE + return !armed || stepper.can_overcome_gravity() diff --git a/code/game/objects/effects/mines/_mine_payload.dm b/code/game/objects/effects/mines/_mine_payload.dm new file mode 100644 index 000000000000..99139be493d7 --- /dev/null +++ b/code/game/objects/effects/mines/_mine_payload.dm @@ -0,0 +1,22 @@ +/datum/mine_payload + var/do_sparks = TRUE + var/destroy_self_on_trigger = TRUE + +/datum/mine_payload/proc/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + if(do_sparks) + var/datum/effect/effect/system/spark_spread/s = new + s.set_up(3, 1, owner) + s.start() + if(destroy_self_on_trigger) + if(!QDELETED(owner)) + QDEL_IN(owner, 1) + else + owner.disarm() // some mines can be reused + +/datum/mine_payload/proc/remove_from_mine() + return + +/datum/mine_payload/explosive/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] detonates!") + explosion(owner.loc, 0, 2, 3, 4) //land mines are dangerous, folks. diff --git a/code/game/objects/effects/mines/mine_assembly.dm b/code/game/objects/effects/mines/mine_assembly.dm new file mode 100644 index 000000000000..6a40fa0ae30d --- /dev/null +++ b/code/game/objects/effects/mines/mine_assembly.dm @@ -0,0 +1,67 @@ +/obj/item/mine/assembly + name = "mine assembly" + desc = "A small pressure-triggered device. Accepts grenades and tank transfer valves." + payload = null + + var/static/list/accepts_items = list( + /obj/item/transfer_valve = /datum/mine_payload/assembly/tank_transfer_valve, + /obj/item/grenade = /datum/mine_payload/assembly/grenade + ) + +/obj/item/mine/assembly/mapped + armed = TRUE + +/obj/item/mine/assembly/attackby(obj/item/W, mob/living/user) + if(!armed && !triggering) + var/datum/mine_payload/assembly/attached_payload = payload + if(attached_payload?.attached) + if(IS_SCREWDRIVER(W)) + to_chat(user, "You disconnect \the [src]'s [attached_payload.attached.name] and remove it.") + attached_payload.attached.forceMove(get_turf(user)) + payload.remove_from_mine() + QDEL_NULL(payload) + else + for(var/loadtype in accepts_items) + if(istype(W, loadtype)) + user.drop_from_inventory(W) + W.forceMove(src) + var/payload_type = accepts_items[loadtype] + attached_payload = new payload_type + attached_payload.attached = W + payload = attached_payload + return TRUE + return ..() + +/datum/mine_payload/assembly + var/obj/item/attached + +/datum/mine_payload/assembly/New(var/obj/item/_attaching) + ..() + attached = _attaching + +/datum/mine_payload/assembly/Destroy() + QDEL_NULL(attached) + . = ..() + +/datum/mine_payload/assembly/remove_from_mine() + attached = null + +/datum/mine_payload/assembly/tank_transfer_valve/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(istype(attached, /obj/item/transfer_valve)) + var/obj/item/transfer_valve/ttv = attached + ttv.forceMove(get_turf(owner)) + ttv.toggle_valve() + remove_from_mine() + +/datum/mine_payload/assembly/grenade/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(istype(attached, /obj/item/grenade)) + var/obj/item/grenade/grenade = attached + grenade.forceMove(get_turf(owner)) + if(ismob(trigger)) + var/mob/victim = trigger + if(victim.ckey) + msg_admin_attack("[key_name_admin(victim)] stepped on \a [owner], triggering [grenade]") + grenade.activate() + remove_from_mine() diff --git a/code/game/objects/effects/mines/mine_emp.dm b/code/game/objects/effects/mines/mine_emp.dm new file mode 100644 index 000000000000..5f8a54ad12f8 --- /dev/null +++ b/code/game/objects/effects/mines/mine_emp.dm @@ -0,0 +1,12 @@ +/obj/item/mine/emp + name = "\improper EMP mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + payload = /datum/mine_payload/emp + +/obj/item/mine/emp/mapped + armed = TRUE + +/datum/mine_payload/emp/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] flashes violently before disintegrating!") + empulse(owner.loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade diff --git a/code/game/objects/effects/mines/mine_frag.dm b/code/game/objects/effects/mines/mine_frag.dm new file mode 100644 index 000000000000..de11402ca5a2 --- /dev/null +++ b/code/game/objects/effects/mines/mine_frag.dm @@ -0,0 +1,23 @@ +/obj/item/mine/frag + name = "fragmentation mine" + desc = "A small explosive mine with 'FRAG' and a grenade symbol on the side." + payload = /datum/mine_payload/frag + +/obj/item/mine/frag/mapped + armed = TRUE + +/datum/mine_payload/frag + var/fragment_types = list(/obj/item/projectile/bullet/pellet/fragment) + var/num_fragments = 20 //total number of fragments produced by the grenade + //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern + var/spread_range = 7 + var/explosion_size = 3 + +/datum/mine_payload/frag/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] detonates!") + var/turf/O = get_turf(owner) + if(O) + owner.fragmentate(O, num_fragments, spread_range, fragment_types) + if(explosion_size) + explosion(O, -1, -1, round(explosion_size/2), explosion_size, FALSE) diff --git a/code/game/objects/effects/mines/mine_incendiary.dm b/code/game/objects/effects/mines/mine_incendiary.dm new file mode 100644 index 000000000000..f8b8a49ab2d6 --- /dev/null +++ b/code/game/objects/effects/mines/mine_incendiary.dm @@ -0,0 +1,16 @@ +/obj/item/mine/incendiary + name = "incendiary mine" + desc = "A small explosive mine with a fire symbol on the side." + payload = /datum/mine_payload/incendiary + +/obj/item/mine/incendiary/mapped + armed = TRUE + +/datum/mine_payload/incendiary/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + for(var/turf/floor/target in range(1, owner)) + if(!target.blocks_air) + target.assume_gas(/decl/material/gas/hydrogen, 10) + target.assume_gas(/decl/material/gas/oxygen, 5) + target.hotspot_expose(1000, CELL_VOLUME) + owner.visible_message("\The [owner] spews a cloud of flaming gas!") diff --git a/code/game/objects/effects/mines/mine_kick.dm b/code/game/objects/effects/mines/mine_kick.dm new file mode 100644 index 000000000000..ab38a2a2fb31 --- /dev/null +++ b/code/game/objects/effects/mines/mine_kick.dm @@ -0,0 +1,17 @@ +/obj/item/mine/kick + name = "kick mine" + desc = "Concentrated war crimes. Handle with care." + payload = /datum/mine_payload/kick + +/obj/item/mine/kick/mapped + armed = TRUE + +/datum/mine_payload/kick/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isexosuit(trigger)) + var/mob/living/exosuit/mech = trigger + for(var/mob/pilot in mech.pilots) + qdel(pilot.client) + if(ismob(trigger)) + var/mob/M = trigger + qdel(M.client) diff --git a/code/game/objects/effects/mines/mine_napalm.dm b/code/game/objects/effects/mines/mine_napalm.dm new file mode 100644 index 000000000000..6bceace3f0e6 --- /dev/null +++ b/code/game/objects/effects/mines/mine_napalm.dm @@ -0,0 +1,15 @@ +/obj/item/mine/napalm + name = "napalm mine" + desc = "A small explosive mine with a fire symbol on the side." + payload = /datum/mine_payload/napalm + +/obj/item/mine/napalm/mapped + armed = TRUE + +/datum/mine_payload/napalm/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isliving(trigger)) + var/mob/living/M = trigger + M.adjust_fire_intensity(5) + M.fire_act() + owner.visible_message(SPAN_DANGER("\The [owner] bursts into flames!")) diff --git a/code/game/objects/effects/mines/mine_radiation.dm b/code/game/objects/effects/mines/mine_radiation.dm new file mode 100644 index 000000000000..08b00d2a6594 --- /dev/null +++ b/code/game/objects/effects/mines/mine_radiation.dm @@ -0,0 +1,14 @@ +/obj/item/mine/radiation + name = "radiation mine" + desc = "A small explosive mine with a radiation symbol on the side." + payload = /datum/mine_payload/radiation + +/obj/item/mine/radiation/mapped + armed = TRUE + +/datum/mine_payload/radiation/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isliving(trigger)) + var/mob/living/victim = trigger + victim.apply_random_mutation(50) + owner.visible_message(SPAN_DANGER("\The [owner] flashes violently before disintegrating!")) diff --git a/code/game/objects/effects/mines/mine_sleeping.dm b/code/game/objects/effects/mines/mine_sleeping.dm new file mode 100644 index 000000000000..b6d5ad9a0dee --- /dev/null +++ b/code/game/objects/effects/mines/mine_sleeping.dm @@ -0,0 +1,14 @@ +/obj/item/mine/sleeping + name = "nitrous oxide mine" + desc = "A small explosive mine with three Z's on the side." + payload = /datum/mine_payload/sleeping + +/obj/item/mine/sleeping/mapped + armed = TRUE + +/datum/mine_payload/sleeping/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + for (var/turf/floor/target in range(1, owner)) + if(!target.blocks_air) + target.assume_gas(/decl/material/gas/nitrous_oxide, 30) + owner.visible_message("\The [owner] sprays a cloud of gas!") diff --git a/code/game/objects/effects/mines/mine_stun.dm b/code/game/objects/effects/mines/mine_stun.dm new file mode 100644 index 000000000000..26b5704b304b --- /dev/null +++ b/code/game/objects/effects/mines/mine_stun.dm @@ -0,0 +1,14 @@ +/obj/item/mine/stun + name = "stun mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + payload = /datum/mine_payload/stun + +/obj/item/mine/stun/mapped + armed = TRUE + +/datum/mine_payload/stun/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(ismob(trigger)) + var/mob/M = trigger + SET_STATUS_MAX(M, STAT_STUN, 30) + owner.visible_message("\The [owner] flashes violently before disintegrating!") diff --git a/code/game/objects/effects/mines/mine_training.dm b/code/game/objects/effects/mines/mine_training.dm new file mode 100644 index 000000000000..13b23f31945c --- /dev/null +++ b/code/game/objects/effects/mines/mine_training.dm @@ -0,0 +1,15 @@ +/obj/item/mine/training + name = "training mine" + desc = "A mine with its payload removed, for EOD training and demonstrations." + payload = /datum/mine_payload/training + +/obj/item/mine/training/mapped + armed = TRUE + +/datum/mine_payload/training + do_sparks = FALSE + destroy_self_on_trigger = FALSE + +/datum/mine_payload/training/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner]'s light flashes rapidly as it 'explodes'.") diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 3f5b94ea0716..8598d9db8d55 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -60,7 +60,6 @@ /obj/structure/catwalk/can_climb_from_below(var/mob/climber) return TRUE - /obj/structure/catwalk/proc/redraw_nearby_catwalks() for(var/direction in global.alldirs) var/obj/structure/catwalk/L = locate() in get_step(src, direction) diff --git a/code/game/objects/structures/cliffs.dm b/code/game/objects/structures/cliffs.dm new file mode 100644 index 000000000000..5745913052ab --- /dev/null +++ b/code/game/objects/structures/cliffs.dm @@ -0,0 +1,300 @@ +/* +Cliffs give a visual illusion of depth by separating two places while presenting a 'top' and 'bottom' side. + +Ported from Polaris. + +Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile, +where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them. + +Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table. + +Flying mobs can pass over all cliffs with no risk of falling. + +Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff. +This makes fighting something that is on top of a cliff more challenging. + +As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'. + +When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to +two tiles on initialization, and which way a cliff is facing may change during maploading. +*/ + +/obj/structure/cliff + name = "cliff" + desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." + icon = 'icons/obj/structures/cliffs.dmi' + anchored = TRUE + density = TRUE + opacity = FALSE + atom_flags = ATOM_FLAG_CLIMBABLE + appearance_flags = KEEP_TOGETHER + climb_speed_mult = 2 + + var/icon_variant = null // Used to make cliffs less repetitive by having a selection of sprites to display. + var/corner = FALSE // Used for icon things. + var/ramp = FALSE // Ditto. + var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons. + + var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks. + var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff. + +/obj/structure/cliff/Initialize() + . = ..() + register_dangerous_to_step() + +/obj/structure/cliff/Destroy() + unregister_dangerous_to_step() + if(is_double_cliff && !bottom) + var/turf/other = get_step(src, SOUTH) + if(istype(other)) + for(var/obj/structure/cliff/bottom/bottom in other) + qdel(bottom) + . = ..() + +/obj/structure/cliff/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + var/static/desc_string = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.
    \ + You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.
    \ + Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \ + on top." + LAZYADD(., desc_string) + +/obj/structure/cliff/Move() + var/turf/old_turf = get_turf(src) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf.unregister_dangerous_object(src) + new_turf.register_dangerous_object(src) + +// These arrange their sprites at runtime, as opposed to being statically placed in the map file. +/obj/structure/cliff/automatic + icon_state = "cliffbuilder" + dir = NORTH + +/obj/structure/cliff/automatic/corner + icon_state = "cliffbuilder-corner" + dir = NORTHEAST + corner = TRUE + +// Tiny part that doesn't block, used for making 'ramps'. +/obj/structure/cliff/automatic/ramp + icon_state = "cliffbuilder-ramp" + dir = NORTHEAST + density = FALSE + ramp = TRUE + +// Made automatically as needed by automatic cliffs. +/obj/structure/cliff/bottom + bottom = TRUE + is_spawnable_type = FALSE + +/obj/structure/cliff/automatic/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH. +/obj/structure/cliff/automatic/LateInitialize() + if(dir in global.cardinal) + icon_variant = pick("a", "b", "c") + + if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made. + make_bottom() + + update_icon() + +/obj/structure/cliff/proc/make_bottom() + // First, make sure there's room to put the bottom side. + var/turf/turf = locate(x, y - 1, z) + if(!istype(turf)) + return FALSE + + // Now make the bottom cliff have mostly the same variables. + var/obj/structure/cliff/bottom/bottom_cliff = new(turf) + is_double_cliff = TRUE + climb_speed_mult /= 2 // Since there are two cliffs to climb when going north, both take half the time. + + bottom_cliff.dir = dir + bottom_cliff.is_double_cliff = TRUE + bottom_cliff.climb_speed_mult = climb_speed_mult + bottom_cliff.icon_variant = icon_variant + bottom_cliff.corner = corner + bottom_cliff.ramp = ramp + bottom_cliff.layer = layer - 0.1 + bottom_cliff.density = density + bottom_cliff.update_icon() + +/obj/structure/cliff/set_dir(new_dir) + ..() + update_icon() + +/obj/structure/cliff/on_update_icon() + icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]" + + // Now for making the top-side look like a different turf. + var/turf/turf = get_step(src, dir) + if(!istype(turf)) + return + + underlays.Cut() + var/subtraction_icon_state = "[icon_state]-subtract" + if(turf && (check_state_in_icon(subtraction_icon_state, icon))) + var/image/subtract = image(icon, subtraction_icon_state) + subtract.blend_mode = BLEND_SUBTRACT + underlays += subtract + +// Movement-related code. +/obj/structure/cliff/CanPass(atom/movable/mover, turf/target) + if(isliving(mover)) + var/mob/living/faller = mover + if(faller.can_overcome_gravity()) // Flying mobs can always pass. + return TRUE + return ..() + + else if(!istype(mover, /obj/item/projectile) && !mover.throwing) // 'sliding' objects can fall / bump into cliffs. + return ..() + + // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. + else if(istype(mover, /obj)) + var/obj/O = mover + if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. + if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. + return FALSE + return TRUE + +/obj/structure/cliff/Bumped(atom/movable/mover) + if(!istype(mover, /obj/item/projectile) && !mover.throwing && should_fall(mover)) + fall_off_cliff(mover) + return + ..() + +/obj/structure/cliff/proc/should_fall(atom/movable/mover) + if(isliving(mover)) + var/mob/living/faller = mover + if(faller.can_overcome_gravity()) + return FALSE + var/turf/turf = get_turf(mover) + if(turf && get_dir(turf, loc) & global.reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it. + return TRUE + return FALSE + +/obj/structure/cliff/proc/fall_off_cliff(atom/movable/mover) + . = FALSE + + var/mob/living/faller + if(isliving(mover)) + faller = mover + var/turf/turf = get_step(src, global.reverse_dir[dir]) + var/displaced = FALSE + + if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth. + for(var/i = 1 to 2) + var/turf/new_T = locate(turf.x, turf.y - i, turf.z) + if(!new_T || locate(/obj/structure/cliff) in new_T) + break + turf = new_T + displaced = TRUE + + if(!istype(turf)) + return + + var/safe_fall = FALSE + if(istype(faller)) + safe_fall = faller.can_overcome_gravity() + else if(istype(mover, /obj/vehicle/bike)) + var/obj/vehicle/bike/Bi = mover + if(Bi.on) + safe_fall = TRUE + + // Buckled people can't react to save themselves, if they're not on a vehicle. + if(!istype(mover, /obj/vehicle) && !isexosuit(mover) && !faller && mover.buckled_mob) + faller = mover.buckled_mob + + if(safe_fall) + visible_message(SPAN_NOTICE("\The [mover] glides down from \the [src].")) + else + visible_message(SPAN_DANGER("\The [mover] falls off \the [src]!")) + + mover.forceMove(turf) + + var/harm = !is_double_cliff ? 1 : 0.5 + if(!safe_fall) + // Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice. + if(faller) + SET_STATUS_MAX(faller, STAT_WEAK, (5 * harm)) + + if(istype(mover, /obj/vehicle)) + var/obj/vehicle/vehicle = mover + vehicle.take_damage(40 * harm) + vehicle.visible_message(SPAN_WARNING("\The [vehicle] absorbs some of the impact, damaging it.")) + harm = round(harm * 0.5) + if(vehicle.buckled_mob) + var/damage = clamp(vehicle.buckled_mob.get_max_health() * 0.4, 20, 100) + vehicle.buckled_mob.take_damage(damage * harm, BRUTE, inflicter = src) + shake_camera(vehicle.buckled_mob, 1, 1) + else if(isexosuit(mover)) + var/mob/living/exosuit/Mech = mover + harm = round(harm * 0.5) + var/list/passengers = list() + for(var/mob/living/passenger in Mech.pilots) + passengers |= passenger + passenger.take_damage(clamp(faller.get_max_health() * 0.4, 10, 50) * harm, BRUTE, inflicter = src) + shake_camera(passenger, 1, 1) + to_chat(passenger, SPAN_DANGER("\The [Mech] shakes, bouncing you violently!")) + Mech.take_damage(clamp(Mech.get_max_health() * 0.4 * harm, 50, 300)) + if(QDELETED(Mech) && length(passengers)) // Damage caused the mech to explode, or otherwise vanish. + for(var/mob/living/victim in passengers) + to_chat(victim, SPAN_DANGER("The exosuit shears apart around you, throwing you from the debris!")) + victim.throw_at_random(FALSE,2,1, 32) + + playsound(mover, 'sound/effects/break_stone.ogg', 70, 1) + + var/fall_time = 3 + if(displaced) // Make the fall look more natural when falling sideways. + mover.pixel_z = 32 * 2 + animate(mover, pixel_z = 0, time = fall_time) + + sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect. + + if(QDELETED(src) || QDELETED(mover) || QDELETED(turf)) + return + + if(safe_fall) + visible_message(SPAN_NOTICE("\The [mover] lands on \the [turf].")) + playsound(mover, "rustle", 25, 1) + return + + playsound(mover, "punch", 70, 1) + + visible_message(SPAN_DANGER("\The [mover] hits \the [turf]!")) + + if(faller) + // The bigger they are, the harder they fall. + // They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health. + // This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health. + faller.take_damage(clamp(faller.get_max_health() * 0.4, 20, 100) * harm, BRUTE, ran_zone(), inflicter = src) + shake_camera(faller, 1, 1) + + // Now fall off more cliffs below this one if they exist. + var/obj/structure/cliff/bottom_cliff = locate() in turf + if(bottom_cliff && !QDELETED(mover)) // Exosuits are deleted when destroyed. This is to prevent phantom exosuits. + visible_message(SPAN_DANGER("\The [mover] rolls down towards \the [bottom_cliff]!")) + addtimer(CALLBACK(bottom_cliff, TYPE_PROC_REF(/obj/structure/cliff, fall_off_cliff), mover), 5) + +/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) + // Cliff climbing requires climbing gear. + if(ishuman(user)) + var/mob/living/human/H = user + var/obj/item/clothing/shoes/shoes = H.get_equipped_item(slot_shoes_str) + if(shoes?.rock_climbing) + return ..() // Do the other checks too. + if(!silent) + to_chat(user, SPAN_WARNING("\The [src] is too steep to climb unassisted.")) + return FALSE + +// This tells AI mobs to not be dumb and step off cliffs willingly. +/obj/structure/cliff/is_safe_to_step(mob/living/stepper) + if(should_fall(stepper)) + return FALSE + return ..() diff --git a/code/game/objects/structures/fishtanks.dm b/code/game/objects/structures/fishtanks.dm index c9ab4bc5f72e..2e828ed6e803 100644 --- a/code/game/objects/structures/fishtanks.dm +++ b/code/game/objects/structures/fishtanks.dm @@ -138,25 +138,25 @@ var/global/list/global/aquarium_states_and_layers = list( for(var/atom/movable/AM in get_contained_external_atoms()) add_overlay(AM) -/obj/structure/glass_tank/can_climb(var/mob/living/user, post_climb_check=0) +/obj/structure/glass_tank/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) if (!user.can_touch(src) || !(atom_flags & ATOM_FLAG_CLIMBABLE) || (!post_climb_check && (user in climbers))) - return 0 - + return FALSE if (!Adjacent(user)) - to_chat(user, SPAN_DANGER("You can't climb there, the way is blocked.")) - return 0 - + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) + return FALSE var/obj/occupied = turf_is_crowded() if(occupied) - to_chat(user, SPAN_DANGER("There's \a [occupied] in the way.")) - return 0 - return 1 + if(!silent) + to_chat(user, SPAN_WARNING("There's \a [occupied] in the way.")) + return FALSE + return TRUE /obj/structure/glass_tank/do_climb(var/mob/living/user) if(!istype(user) || !can_climb(user)) return user.visible_message(SPAN_WARNING("\The [user] starts climbing into \the [src]!")) - if(!do_after(user,50)) + if(!do_after(user, 5 SECONDS)) return if (!can_climb(user)) return diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index b04f59f9a150..003d51d5b13d 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -314,13 +314,13 @@ WOOD_RAILING_SUBTYPE(yew) if(!QDELETED(src)) qdel(src) -/obj/structure/railing/can_climb(var/mob/living/user, post_climb_check=0) - . = ..() - if(. && get_turf(user) == get_turf(src)) +/obj/structure/railing/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) + if((. = ..()) && get_turf(user) == get_turf(src)) var/turf/T = get_step(src, dir) if(T.turf_is_crowded(user)) - to_chat(user, "You can't climb there, the way is blocked.") - return 0 + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) + return FALSE /obj/structure/railing/do_climb(var/mob/living/user) . = ..() diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index f5f8083873f8..10aa426edbbe 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -26,6 +26,7 @@ var/global/list/flooring_cache = list() var/damage_temperature var/icon_edge_layer = FLOOR_EDGE_NONE var/has_environment_proc + var/can_conceal_hazards = FALSE /// Unbuildable if not set. Must be /obj/item/stack. var/build_type diff --git a/code/game/turfs/flooring/flooring_concrete.dm b/code/game/turfs/flooring/flooring_concrete.dm index e5d6edd5f174..89ed297a1f7a 100644 --- a/code/game/turfs/flooring/flooring_concrete.dm +++ b/code/game/turfs/flooring/flooring_concrete.dm @@ -7,6 +7,7 @@ force_material = /decl/material/solid/stone/concrete constructed = TRUE uid = "floor_concrete" + can_conceal_hazards = TRUE /decl/flooring/concrete/reinforced name = "reinforced concrete" diff --git a/code/game/turfs/flooring/flooring_grass.dm b/code/game/turfs/flooring/flooring_grass.dm index dc53aaf46808..aa2396b3ba0e 100644 --- a/code/game/turfs/flooring/flooring_grass.dm +++ b/code/game/turfs/flooring/flooring_grass.dm @@ -14,6 +14,8 @@ force_material = /decl/material/solid/organic/plantmatter/grass growth_value = 1.2 // Shouldn't really matter since you can't plant on grass, it turns to dirt first. uid = "floor_grass" + can_conceal_hazards = TRUE + var/harvestable = FALSE /decl/flooring/grass/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index bdce5b2859e8..ea49cccd030a 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -67,6 +67,7 @@ force_material = /decl/material/solid/soil growth_value = 1 uid = "floor_dirt" + can_conceal_hazards = TRUE /decl/flooring/dirt/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 881da81bdcce..b806c89f5d54 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -12,6 +12,7 @@ print_type = /obj/effect/footprints drop_material_on_remove = TRUE uid = "floor_snow" + can_conceal_hazards = TRUE /decl/flooring/snow/get_movement_delay(var/travel_dir, var/mob/mover) . = ..() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 33828543cd5d..026732283b8f 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -7,6 +7,9 @@ temperature_sensitive = TRUE atom_flags = ATOM_FLAG_OPEN_CONTAINER + // Linear lazylist of weakrefs to dangerous things on this turf. + var/list/dangerous_objects + /// Will participate in ZAS, join zones, etc. var/zone_membership_candidate = FALSE /// Will participate in external atmosphere simulation if the turf is outside and no zone is set. @@ -929,3 +932,24 @@ /turf/take_vaporized_reagent(reagent, amount) return assume_gas(reagent, round(amount / REAGENT_UNITS_PER_GAS_MOLE)) + +// Tells the turf that it currently contains something that automated movement should consider if planning to enter the tile. +// This uses lazy list macros to reduce memory footprint, since for 99% of turfs the list would've been empty anyways. +/turf/proc/register_dangerous_object(atom/thing) + if(!istype(thing)) + return FALSE + LAZYDISTINCTADD(dangerous_objects, weakref(thing)) + +// Similar to above, for when the dangerous object stops being dangerous/gets deleted/moved/etc. +/turf/proc/unregister_dangerous_object(atom/thing) + if(!istype(thing)) + return FALSE + LAZYREMOVE(dangerous_objects, weakref(thing)) + +/turf/proc/is_safe_to_enter(mob/living/stepper) + if(LAZYLEN(dangerous_objects)) + for(var/weakref/ref in dangerous_objects) + var/atom/thing = ref.resolve() + if(istype(thing) && !QDELETED(thing) && !thing.is_safe_to_step(stepper)) + return FALSE + return TRUE diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 5c849feec9a2..ee3311a90cb5 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -80,6 +80,7 @@ var/old_ambient_light_old_r = ambient_light_old_r var/old_ambient_light_old_g = ambient_light_old_g var/old_ambient_light_old_b = ambient_light_old_b + var/old_dangerous_objects = dangerous_objects var/old_zone_membership_candidate = zone_membership_candidate @@ -104,6 +105,7 @@ // Set our observation bookkeeping lists back. changed_turf.event_listeners = old_event_listeners changed_turf._listening_to = old_listening_to + changed_turf.dangerous_objects = old_dangerous_objects changed_turf.affecting_heat_sources = old_affecting_heat_sources diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 589a7f0d79e1..8b90dd6119a0 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -17,6 +17,7 @@ fallback_slot = slot_shoes_str _base_attack_force = 5 + var/rock_climbing = FALSE var/can_fit_under_magboots = TRUE var/can_add_cuffs = TRUE var/obj/item/handcuffs/attached_cuffs = null diff --git a/code/modules/fabrication/designs/general/designs_arms_ammo.dm b/code/modules/fabrication/designs/general/designs_arms_ammo.dm index eb92c1ef0f3f..2fd82b739386 100644 --- a/code/modules/fabrication/designs/general/designs_arms_ammo.dm +++ b/code/modules/fabrication/designs/general/designs_arms_ammo.dm @@ -81,4 +81,8 @@ /datum/fabricator_recipe/arms_ammo/hidden/speedloader_laser name = "ammunition (speedloader, laserbulb)" - path = /obj/item/ammo_magazine/speedloader/laser_revolver \ No newline at end of file + path = /obj/item/ammo_magazine/speedloader/laser_revolver + +/datum/fabricator_recipe/arms_ammo/hidden/mine_assembly + name = "mine assembly" + path = /obj/item/mine/assembly diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 956ae903000c..36a2184fdf0c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2001,3 +2001,12 @@ default behaviour is: /mob/living/is_cloaked() return has_mob_modifier(/decl/mob_modifier/cloaked) + +/mob/living/proc/apply_random_mutation(radiation_amount) + set_unique_enzymes(num2text(random_id(/mob, 1000000, 9999999))) + if(prob(98)) + add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) + else + add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/superpower))) + if(radiation_amount) + apply_damage(radiation_amount, IRRADIATE, armor_pen = 100) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 27ae2d426d93..360f26b75557 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -5,6 +5,16 @@ if(DoMove(direction, src) & MOVEMENT_HANDLED) return TRUE // Doesn't necessarily mean the atom physically moved +/mob/living/SelfMove(var/direction) + // If on walk intent, don't willingly step into hazardous tiles. + // Unless the walker is confused. + var/turf/destination = get_step(src, direction) + if(istype(destination) && MOVING_DELIBERATELY(src) && !HAS_STATUS(src, STAT_CONFUSE)) + if(!destination.is_safe_to_enter(src)) + to_chat(src, SPAN_WARNING("\The [destination] is dangerous to move into.")) + return FALSE // In case any code wants to know if movement happened. + return ..() // Parent call should make the mob move. + /mob/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) . = current_posture.prone || ..() || !mover.density diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 4240ed507b22..923bfe97a847 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -48,29 +48,27 @@ /mob/proc/can_overcome_gravity() return FALSE -/mob/living/human/can_overcome_gravity() +/mob/living/can_overcome_gravity() //First do species check - if(species && species.can_overcome_gravity(src)) - return 1 - else - var/turf/T = loc - if(((T.get_physical_height() + T.get_fluid_depth()) >= FLUID_DEEP) || T.get_fluid_depth() >= FLUID_MAX_DEPTH) - if(can_float()) - return 1 - - for(var/atom/a in src.loc) - if(a.atom_flags & ATOM_FLAG_CLIMBABLE) - return 1 - - //Last check, list of items that could plausibly be used to climb but aren't climbable themselves - var/list/objects_to_stand_on = list( - /obj/item/stool, - /obj/structure/bed, - ) - for(var/type in objects_to_stand_on) - if(locate(type) in src.loc) - return 1 - return 0 + var/decl/species/my_species = get_species() + if(my_species?.can_overcome_gravity(src)) + return TRUE + var/turf/T = loc + if(((T.get_physical_height() + T.get_fluid_depth()) >= FLUID_DEEP) || T.get_fluid_depth() >= FLUID_MAX_DEPTH) + if(can_float()) + return TRUE + for(var/atom/climbable in src.loc) + if((climbable.atom_flags & ATOM_FLAG_CLIMBABLE) && climbable.can_climb(src, silent = TRUE)) + return TRUE + //Last check, list of items that could plausibly be used to climb but aren't climbable themselves + var/static/list/objects_to_stand_on = list( + /obj/item/stool, + /obj/structure/bed, + ) + for(var/type in objects_to_stand_on) + if(locate(type) in src.loc) + return TRUE + return FALSE //FALLING STUFF diff --git a/code/modules/reagents/chems/chems_compounds.dm b/code/modules/reagents/chems/chems_compounds.dm index cb305d78e23c..f080406738f4 100644 --- a/code/modules/reagents/chems/chems_compounds.dm +++ b/code/modules/reagents/chems/chems_compounds.dm @@ -263,12 +263,7 @@ if(!M.has_genetic_information()) return if(prob(removed * 0.1)) // Approx. one mutation per 10 injected/20 ingested/30 touching units - M.set_unique_enzymes(num2text(random_id(/mob, 1000000, 9999999))) - if(prob(98)) - M.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) - else - M.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/superpower))) - M.apply_damage(10 * removed, IRRADIATE, armor_pen = 100) + M.apply_random_mutation(10 * removed) /decl/material/liquid/lactate name = "lactate" diff --git a/icons/obj/mine.dmi b/icons/obj/mine.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1c28f5775952936c83bc532c23f90cf9acda535b GIT binary patch literal 403 zcmeAS@N?(olHy`uVBq!ia0vp^2|(|NlQTEjQ~KP$OeWkY6x^!?PP{Ku&j6 zNJNQCYH@N=WKhlO#F@nvH#g*lHV9hnrO5Ugo#tRLFhZLGR^g~SQdCy#uzz0UZyZU_`P`doa; zGr{GXEDU+uXF7y0S@KLJtVw2J^E%y)8s?_08nvgm*461Hyk}x)4|h%Q_<2bl=&V{# z7sn8f&g29M*2N9W92y!sC#vwaaq%7c@kElfwYQgf0!z708wx}yMIPK`qSe4?o-pM_z@<5sqVt7#)Fo%PwF#}S6X|IRG~jY$G)!PN v@06;+-u6{1-oD!M literal 0 HcmV?d00001 diff --git a/icons/obj/structures/cliffs.dmi b/icons/obj/structures/cliffs.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4713360313f725719630a372a313110bb1e03d3d GIT binary patch literal 73393 zcmXtf1ymbd*EN*~DQ+bN3dIQ?oZ=3_gL@&kyGtqV7Nj&tv0|Y(#VJs`9w6$X}tMq2Vej$UxB0p5>vokC^DFmi?>-XEe0u zDt=me9x|5hP&Zo_4_jv^G&CQ0c7}@cq69(2*tKp2Q7MyVHMQbM2sQ&kkiKkDI?={K zxk_)|&YO5Tci5=WKf+9U<;j^!oexmct{qOx#MRux;UV92nGCaHD)58MdG!_1@8&jMSdFs@Rb4;T3B@)eFh{oU!1Cz)0Fy77@UB=?ml#Tt2ENM@6c6Vy}Dn4)r3B(`a zNZSj|ij@8m#ySJ*x9$Vx$%yeJpfQ}j*>?_TT$tv}Hf=n4t_z?NprI-RC0};<5JFX2 z7k_2-{V5tv&KgcEksdW0+qUOt<|uldK3OH{3XHEH>0Wt9xB9M|)TyTnS2vFk+rx-P zm957$T(Fwp7wJZi6ia0in5}T*Rq6jbd?f28UNu#3=FO=ag?pIY*ND4sgj>$x=H!mS z>k>-;qUYRS?QEWGyiMC~>fj#HO1JW}%o5hmYAJ6KaxCUu^3-@r;AtiKlv#?CPA?cA zSx&djkB5_82|yUCb<8L)Or|%~IY;6T=P&;aA-rh&i|TH_8u`Hav(lr;pVcGp;P<%Z zL};#JW zuD?^>YySC{7g{-NO%Jp7rT6U=8xtLo0?zFz>4==C``X-&r}?{oUA9ZUXkw4o-386~ zbKlpL(e{f1w{n8dN6T$J2-A35R-fg;Xc?|!#l@ek2R{~f-*qzO26&KMtrulD(c9y$ z`I}*Nu03s{7rkQ;S}d29g(MeG?qV3{1^iV^VRBVd+a5?UbZ*b$7yi|7<{%t=-iNW? zjUQYz7=&s;2$%+L#a>?`=7>A-*L>*?Z_zscH1A-D{|Msc{EHvtBAQ)kkLDG)9(mlF z{Gp>!`VS)peI2?zUwQa{nWSFeXxRc#pUY<7(Aus4zuf?!JAdNrR$HvMJiON+eikj- z$<35sLOk$F)U&3{3j2}1|6*TYYM{)I{6Qqmx!wjLBy790CJuyLx_M2CxS1X=2hn+k zB7cbo@w^Q>QjdNcO6hm^?(XR%aZPz;-4}UDk#TN^gH=RRC;^T(KE|S%(Miw2L#xz22$qE#*A%{FZx$qq`Iwu0OccA7aP&G-TK%Qz%d&>vbg@(JUb(ra z9}3F)B4FxkKDvjRQS?w=-7)&+C9g*!65UI2KSBMuir24;It{uT&g6gJtZ@Pv8nS;G zghHN+XII$E1^Yf(GvUsXvlyBoev-fFtl@(@Q<72q>h*g5p;qYWG1o~xk;yWiY$-Z6 zeluWGuc3y?ez>SDCz}`bx@f|fSZ;y&uzVjqwe8Y?FEXubJ!fOUfIWS-r)k#PTg&Q~ zxqdmps(FPQBOQVJ7rcJs287b|Mg6^2QoyueMVO02ewrL`C(y`&5falwFkt}lTeyQ! zD*889OM`xg{?Z%alC49BS{(nf2pckpPYxL4l8u|WUe{3efXYwi07c(nXvK|z=v%3D z2TgKhx^J}-Od;Sr+Eu2gEKALkgS6Y|{%(_wARbZwBC7>0g8BoEUVTMJ;j#Q#Hp6(x zt79S%qZC1ZYZ*)PPioRIDrg~vh_?1K0x9~wFmJ3Pk|CYi627I;*bu4Y{4Skz7;SXa zEV?~wIwE;_-02LF7!-5h_2u{*4E48QAaJo8gh7sl3f_lrbXV(+w5 zAh+Q6Td0>7%tF{ea1hbM<>w`=CNB0yAq)}|gmd6!a>?Is5*wrl+-RfgiJBlpE?0O! ze%1{d%!0WDF1Lm=5*_E-*zw0NX0nU%X36osYj?zgfX=b%AQIQKW{#|KjU`vUQ(QV zPbl%0>_uf1?W4tz_j}QzkcqcSt z<38o4ZB+nJwYXRAYEOU*Rq3IjY%QBqJghn7DnQvou~DroI9+63we97$?)PYW9GZ-06_btE@r=Pn5SVlz;xInG{k zF3i0@a87uN4`NY+!=C?vP#3^9%!S{Q9-MLkT{iwS=XeejU2!o~40v*ZH1uQf3AH-O z%~Fdz0uE+iL?N<>tc13*u?6Kf32+;>3hWnR0*lMQfn6 zMd2QTa6iSV2Za%EHTShdO1QkkKsJ?+Xl#yy(@fG`cAX$l~8@nkRFncGM0Rv zP-=&d6@0BO2(t;BWe>QqulW66-9eQc*#h6+S#>QwENa_J9F{MCrfzYOvRsbmJso9U@2XfmHYJ%AeVrL;X!pi1xUz)pYMyx2R_(7`C&69B)c_0D?Z~piY zSVH1|8AQrZ0$M388=L~{nqNRONAp_T4BRAMnR9r~w1@P1ZqSnE4E`37GF+>E8+9^; z8#0xe<%rDfa#7nhWNlpYV|VnzE1K5mQpHbI6T49W7R?_lXT+%+>t=|3aXW&h-sOs7 z)hwvGLmt)lnx%lfR&*me^*VsGA1!a18V_Ao-K(<`UWYW#s|p^Y&QYx(mhC&VFY!-q zOkuh0cR2>^IAmX8g_3W8P)y@+au9IN3rYTB>+P7BduQ)!NU~g}2^(g1n-h2Qr19TuCkv>)Vygm}*BQs+DLB7DMP6F2I1wGwhHVa8ilzm>al1pz zK~B6h!SV>7;|vM`6V=`yMh@RFq+A)^m+Z%2cwh?qY3vFu3de=PAiUb56u=OqN5lu> z^6<~U=gCY1PB@?D_a6g`DnNZvygUk5hl)5g2RF~uZvWYx>KqCleZ4m|iQ2S{^V#eh zfkB$&toR)or(k@NVRtWxK&jFk8 z620)NoaRb}Qm$uwg_{ge1?xx{h1=k2+m)8DT+---lkNF>YveMalx?paRiAf&<7Wt1 z@K~RneM$AbS%F33tR8Iiru}>K_`l!#uO)DM#`!6!Lf0gaft2k*^6TS2P{R*=8=4z7 zN|%_zjj>xb<(|DX>d}@qZBZ@jr;5)bK4n|KGNkmpbwkhm6tnv3v4J;uA?kdkRXhJ={2z2%5dkv&BX{c| zKswh~ZL>41hX)$Z)#Aqq`%?=YsC5T(g6|6&C!I3Y8Qt89k+jp$P$Mdaa6Q6;1C7u9 zPMOyyiK}3A>FhJnq4441Xpmct1J~XUx-J-zoULm5}W%{EZb^Tb8$cK3yL zP;gDzyjNHLoiuARw*QWWcJ=;?e|gD&YN|lv+n|rNQ&}immWo=~D>e$e-)bFJ%Ys?! z>X}B4T_~oWI#E8}4C;qY#vs45zrzbl+cw{)a!uPzom@KdFm`QLfLFf0-#N>H5qVm= zj;1OIelIES;bPP<0UPfn1v=3B;@b+RDroV8gcj%|wDp^5J|Nn+JPKhy+#s&aH4e>D z6Z>NgONSFO5@`Dux3x1;?lsd!2O?MeQ#cIiZC=s=Kc4wrk#%S_t_JX>J+GPa9O?Di zFyFLdyV@DiwR-Utwt~r)r5o<>~H7f z_|=r{tYsX>fZVDHk4r1+1CExsuy-H*|7t^y4#gYaDrp?7X&GkRWrwWks_ICotQ=4> zmB+*~m7tvj0gu58tep++B3H^UTtB>u=1@c z`f4S^FL|0Jbbr|g#KfhlCk^EcQU%7)AMd{BeE9p(DTOk5f^;rN$ZRT+7g0oUY5jX? zIqCd=aX1A70-mblla8y&Sotq$-bqvialNsMSHQRTuX_;tA2dHlWucn?lB!++(!v%b z6VB)T&-Iz*q>a6Pp(AEkvF2xs%sB8cHYM-U4ypts{klmwb>FVF9O*b#5ed7+Tl#wc z_0?q+`g5u=g5{0~@$(kv)c^)k1q`2xz1#2HFa~oVUf)B3_wSP$ zOzD;N)i~K7EuXQGRqZqJk{7YIW*|-VGvV~L;x}8H1+DlffGb+`((}L3Az>(jMpmA_ z#$ejWp-{lU7#IN}(p>jgfy||E*T~uSItSHSwyXp^Eja|>ZTFIy-Va^eZq<^R=%t$H z?=uz;xL3a&9~|d*YnK$wd3KBJN2S6lD+Jbw-&fpp&ANryKq2&E$)`wB9+ zYwrGGgMYSQVE=lgo!r=7lwwf2|2tqx9ylm%X^i6pdVa;aeivLRlNOoda&zz?8*}yj ztBl%^4m7Tb*N`#nU+P{M^Hqpf@E;GLgY9nXW*MSAl&hvgN)xuA4%?@a8 zH^?_cTx85XqH=TOr(vf5Vp#72)!7&SIV^>QMHV=I3B7ft&(qUk$3usFApYY+#}htQ zCN^Q}r$KVYT&^yRHFj-Z8K2K0pc7+$9p9?^0KlXX zBSh~9jN}tyR#Ly1#XNI9Cth)ChAru{rRTnZS`Ri;)u_&0r|saTndz(yjf?&Bgb`DYS=m zjJc@=#qLUcHbWKhxbskX%f$=-bUW~KYd(UM*^QbvygD5~OC986B)7XW zl97sHQ$%U#%{mwW#WIe)l9UfUOw>wY{=tD`cAlpTG#9seacark56c~7^T$Zj)@Oc3 z|4;&75;dtLImnQ&M}yn-5c2Y_(VF$TED==R8#A zdIoAuq?k#seyR59nIF88@}Iwc1KqLK9i6d4CN%I|DsiqTpym?<@9JN6MbLGSi5k-r zjXu<7j~v1GWo6a3TIVbqsT1Jpy`FLW^e0-TBeC^%vHZ{CO#0fRQ{>g1(dj@LV_(0Z z$~D3D#rF=J>Q(2sgpq3!UwEJhpM+__ef5X#$bmUqna8HnG2@{Q-WTWZfD;zrlid$L zGEACL@c~}BFaKT!1tuWea?tu-e@;#AdWj@Y!xJPY+x1=WJX#FCm|#Ma1*9VPIyTs| zOZ>6entXW~={TWaG9<5tnT#)Xy~TWat}=ki2V$5UE9l9BPp8C{7nc3@tspuR$0Ac( z>T)mA8%osyai`Y+n=eK@6vPiC6N4Y>7z-ulgw1h!Fu(1mPicqFW~~KOOS6s%GO)Y= zvzqe)-ky<2jofwki5#E0bQE!}^4`92x|2>jjHh@4`F)(S<0J@Hw0JK-ciXV~zbSN)p`XtNx@lG~> zF1Z-u5o*0)Fe+etG@VycQC!1~mRD13hpj;ggOIz{$^TyjKYT;!$~ch7WzqZ=9 zu!>M+Gmu_W%B3m5F^CU?f8UMlufd`h0m>QWC;+5;d_RWKPOAN`{#KYC5O%%~u_=h6 zF-Lrk*9cMmkbBIGbuTX#z;Lc*&kk0~rKct#xBU$Qsu?Rut`01^S+?aJn=PqTWUwl{ zE$rcvgMG3rIC07={e5E}5yT5O^#P5esJntPUK9m({XjK?qIE?b{Szl>o^Iklzr8Yj zyd?`Br(qW=2EJ^?o;To0_#FO?H2Q&uXd~Qw!pd@$bW$%#fv}EU3Mz80>3o7dVsZy9pK9xa*<#}QQ1F>c^fO| zej&rt6hPhZ2tjCq#uNRnHv#It55?SYcHS0AlTk~nMPKSdyXM66z+c|EhUQW5V>y_P z`ne56nPVI`x{Q08R9Vc$%e=|`DEf1{GxA zAs}%ytX@!MW+42FiiWxNJuxDcjivQgGqXK{&yposAqncwL#&!CYTfJD!{D?w6xjuR zVQP8v6sgBX^;-Ny0kkaL*=KmUX{nG`P~1^psQ|l=M;U?p6uusj+n9ai5Lihbg7wG* z6LIOmo(&H88wNj~ztn7}szZom6BMlsZ{;C4gO9fgH6^0l;7dB7INSEwh32mmqR=Qk zsE}ysk9j$-HsqddIEBSg1}Z<|7<+=iDw#XR0(~HhG!N~EagUjs>zuah|Cll=-kRpH zZR8RtviFrE)81mW9s7RTd^L;+#R|lZ^mrkg=9S^5are8C&SK|yHkMVz`JmT^wpo7P zJ9FD0C(AsfbCvxUk%~A`PY*`CGQD)8gO zKl=R7q2YJaX8ciq^EkG}z=>^lU2;g+fMv|ZsQY%s=Oq<3!;xBDJ5@E7W*Qe19G>j< zn@nqvEN_rnV0bTYWl5!=u?J*$JXnY!iKIMX_Whk^R8YnOvvV3)0kl?H7$8>?VMdsaJvQqC`Qv-j$xW!ltM0s?io-{fNpB%YO z<@FFH<)@xqxxXOIc(saxs;ETUfBo+CR|<-6dmO*JjU4@+vAhV2FE?YHZO@9 z`ncZHtfI;yRswF$JT5_FuE#l>qis~w0Xd!pfNUn&KbWFxZvWB5r0P9FLOkLVPI>xR z_WIUCk)v%n{#WpTUsfM#DXi_zb~jo%;_FT{3^cTBFh0(7ovz-QW~;G5Cp5upKdzpB zB^_RF-2JEOzg(MN-%cVCkZK;X;D0E8@*aCr$+iaL2oppEm9P&Hcd&f(?PG2Oz;I%^ zD8_g(>bq{1G1C{p4#4$qVdp}?Wu~*DavurTV19i{KqR(chR@EP6p6Ct=1LFt9C?uc zuj)glJ+RpXitJisbaSZ(?@qQjAuV?-fe9*SPA(pxnD7 zj-?2og=I?d$)oBKKm7-q6j^Zregi)Pf~m0OUh!%avVWA|AJDJKgU?$97gL^ z>&flVfPaWsd^+uB=5ug#m;IM?VX~!#7pa83B+= zcoi9%Cu$}3IGRm%_mLF(-j=+8b&yEgzX~57&9wCUKmi#k%(JyX?Vc+X%C@YFzTvjG zDWGouY_4Ext}k}*UbYkf-MQa-VC%k{d(^(0HrNbYHy2iT3rPStdTpgU#$t<6;Dy$c zQ{&9CL{ZrjcQYSnBGsg|~4Ei*(-1&8w2WqWz@~R^Uo^Jy5>u#*g(Naq)y6(d8 z6B-cjSQNJ~;_IIwN#(HVP2R-f3ri4SyKaaezx03k-jPzlhmR0{Va22Z?DY%0UGVaE z^|+y)Z|v3ot|l&gU~s1Gmg#g2<^80+7Wn&f-NMn*IweN~K!Uo}ht#5usTe0Z+WM_| zr+@flmh~tL?d0JM#z?Q#pbP9=a?;>yT|UCC@foTd41LVU|xKF zHBq<&e~$6-I5?Br(kD&ll$((#whyA4<#;R`Vi~DVXR=)RMUM?Jnk$nx%6$G*z5cJVo>2xU77H?~+YYxjWIP50TC|Wl|2?SN`5t^K zuy=d$lqsdfMxoMTQ_3>L^|QA(y+Ow&wc3l9shoT=>@4e$3lw+fnc@+olh6Rdk+x{! z#eqryKM(wdFBcT2H!v;Qq}LZ67~{b^-JdSk*tq)sK0PER5HbHE&~GG&UKXN`rXiG) zU>)(Z{qw7*<)Csz4X_rhmdLecVlVi@Wl(Ei5I1?i_^Yi&I7B5r zoMc?iO>)e{I4=!(){>lBWH!<1_0dtl3V&rI6i_HjcZN)ZIC$T2^mJ$1P^e($U}kVU^`KzAYopmmZS=!`i2iJZ*`@v}Y^2G}SU^C6% z1MQF{uk06TCHAiT_0Likf^~x!4a$wy?eutYmtu#T%l^L>pnZr7p5zMwZ|vH|)^B`x z``Vux9Q|=t$(PfsWz#mqy`%MdBR06nAUmMJ0ML+)w9poX#p}aMATGhpmB0uaRL!*S zD1;qj*TZ-UR}!X0D=!=|)1|(x(e#ZeLYNF{#q!^gu`*7!!U zi#(lqw#%IK*xA7Y%c(G;p&|e+f&?fx{z~9IM?(l|Xl+g>gmtzydp!-ge+kLU0Zftg z==METy)Q?Ho*WsbNYZ9|lA>6WiFo4~Bch}(>3ctSE&eN-_mwl1zp}r~eu1JgwtN15 z;S~ACgu9g8S^|8gA$Q2HqN4mvDg^7d|CqK7F~GQxe|TUf_OSn7k$*--cFmuWnz@^! z@290Sd*8QCIiRW__55WH!SmQ+TEeqiv^29OX3?SsVKq(rIt}H5&IB1u$y%F(qF386 z!%mT4N69JjAa84#^3FeHJA=_1!S{XDCE?FJ+bIO~bvzzk0WZ>5GsuZe~Q4+!Ssy5X_oIuu-(T>tftH zf8cj=sitOkl#SoBi#(Z4r0aF{JF@HEy8OF+BUcHnv%ucoPi4LUAeW(Wg!Y}j1E7X_ z%faysP?ohSnc%UY{tTdPERibcDLag7KS(fr2F06)}s=3=5mG ze2)(KK0%9b%B%L+dMx%Oh78%=XNDf#B>QbdlBg`}6^nThzCG=B1bD-O>8b z#PWTMNXCpEYcw63I$jLVvHR7s&#rC;>IB?)MH2j&kB1+>=kO9;T0sMi;}WmDivYjD znpx`Ww-%u5crYHz7EUl0cgYdPoL1@0pXebN?{nF%qv=&}l(lV0`}r%%yNO*Hd@h~0 z6d%{!BltdI!2LqY${t7h1N^zuX`JSV`jYBC$Q&s+bT%T)XID|t{>t?gQ9U1D+RRIa z!kEP_H!~#J>|LkV?gDOzlk!=~GVtfwSc5}Dg1v4yk>5q#MpL=UsC-hIFR(Ri0(dJ4 z`tOJPku|i&1~pZ!DU05GMH~(fmOEzIA{l58BPF8H$qM5Kc4-T+pMwa5g!n#hGnyLd z#xzd~;%`g|K`SrEKwh#&I_HgB_- zjLbCZ^fluYVv5K;_<7a#t<-WwSB-B}NXkWdlrW|01tR%a3*1lekmSPg0e2TC*^ZWw zd5eL1JEtOpv|h#NKu$*~mXD|iZ%V;fH;KNqZU3!Uzo>zQCA&sx#QBWkA8D;l13Kw9 zXJaH%s@h9$tPUfXP27+F^hF_f8yW(RZy16fR~W3B_-4t|u}AVJaG(|dx>&hdBby37 zc=q)Djdup>q9S-Scq%@{VeP`qim8a+<&PE8$}%ej2U<64WN=rog>sa`6TyUOPlgQ< z1hktZnfH&TxHEo?5TQ>&kBgsBbp9^-7+18U+p5OQh4tF>dq+*ChLr?@qHETd zYH=xvK%S91R4F+6EWWC&?o&`4uV;6D8vT}_Vv6ZZG68Q(CEZu+-O9-j7%vbQ;gnxS z`t4i(k7KyEva~N2L?=OgZhV$eI>7DWEZ%xSM_G)Z!2A?6w<8$xLNo7&J#bJYfoL0% z&Nu3RYukmCm>4? zf-PK5Z2{ag2V&zf>c1s`pX*Mky*_^NI+~@Pza~PJL{rQ4vEnQw)(A>t7-yaYmk}|r zg?qmdzhW-nVtlquU?-(ulMmR1l_=*^SX%jm|MsrncB>;5Vacpa5l;`joi#Is7V|sz zV_aOv;W*L7C4y^yqqBUb%ZG}XFI4j1#B)raTa3RSrB8y8<=0ysY~Lq~aCobEQB`JZ z;Sg6C=wIE;@~BRV4+AuUx--X;22I+Ce7GZ`x~ojcAKHEgxAD8Ys)i~N z(%{Q%bVGK9)f4e2+YfW$FU(`iXAC&_@sO0BX@6C>-JLMo0o~{QM&n;Y7BBGZiBgH( zK7KZ{kpwn8&?hIH@z#d}{^C`jQxlcQD8DW$#Ht)+_a5I+pRE`4_GGANN=@7PARzoJ z4aNGX8A~gF)PYynHDm+k37&(1FrwL#nOD>=v25+k_&#y%XyN6?(ybuzHv5>p+Wgm7 zhjQ`tga6ZMTa%!Uh1HWipyONhL`rf$5&l1 zgcJXj%+v;bx_ZvZ7jHhN=+;)*b9VY{v@wBT`zLV`UZ<~ZgPxV?c{a4=Py_5*Q=-qG z4j?k1V8QO*YMNIK2wK(DP4k&8*usbF0#IyMr>0xz?L_njg95Ma?ucALed{6jsm@~l z7o6xBPd?TuK#_S9H7$~i81*qG<3c4_g_M)R$eTQ-FhZF;TUO;F8@u?Srl{D%9xEiK zB+-fGMgfYN5?1(+9AfTLBS3qDK2U&ivt=Y-R7<9Z33X^AI`iw<3{$UkVHE_N=CFrw z=H5!ka49Flt!}JA>l_Pm06p@I3Q0#MJV0~vj?>$06zj;TDow^|yWNIiR4Fc|`p1Yq zNOQdhutQ%5epSxyXeBPkHamD$QU>oFijlNe5l6q(SYPOc03fe=2Wm#S@-0|M!OX~; zjI)FRMGl_rW4zx#xrWW8XSmnw0F1Y0U6Gl=922vjh)}u#_pV3bFg;1wbuU$0a(JajHv;|%)atll+$VXHKdNI-%=S}Xh5n|)Z1)A_^Yu$ zUwwtI^`uSxH#f@Ws9n2H*$n_>SIy2U4RI-o{`RwVm&pb6bym99X^+(N45dx=I(SGY z9sd>E*)`lOu!4(l6y7M;$ID!K7KPnQOHqt)9nyX34vMKdP6bG_355MqBzr7{R<(&fVuPMOG)dL5-WS{=&CrA8J!;#e{sX9;br+{!mm)HFFuUuqLVa zE?_4(R(xJ?@zjJmD)m&^6e-{`WR_lGDBhC*iY36w7iChwuhJ0&^A7SHUN1ll#RJik zDxcoYY0FM2ki8_pU%TrhLvr|=--S~Y@VpUt3I%_TKtxG?cwI!ARvIPQjP-H-WIw#_ zeWcMrX18WjSYwk&je@>`8}^{e3ydYRaOrA00wHDglF zbR#5e3?qhQTPV$!M=Y$V|3)3W zMaYU*r|%fqRddts?O)xvWX>Z`!Q%x-43x}c`m+VWI_k=~V|TJe=Dj|mR6tXAeF*bt zHwO}>YC!Qn_*%4o*0YcO#OcU^3`UF;XhN@&na9avC$a2%K{jMwUf`{H+w}OK7)k;@ zI*85;?lZae?}+V;U7VXnSRv(C3K22A^!S0Bw?M%egYDxbg&xmP35826J#54oha zcuflwH^RRanm6Evm)o+qb+>K(BFN3{GG0=B4F==;p5gWcjjPpY6BoEn&3%Y3W94=f ztvW1jg3D6G&pwYfMM)P|)()R?S;;e{c@bAW?<8=5sTeqe5>Z5?2sf1hc>fT(L@Dzt zF6oz23tnvGLW14QoKbuP=KaSx%W>v;eJE{QAjGIuSgll9HTumUmw`zA^CpEr`F=U6 z#n~|WbBi>2Y}lCoTNKs(8A7fQ2LH%5+#nk74yUC7 z{tx~Ib?4m!A5>XCy7YeeN^w_u_UnCi_YIl>v_fPppVw&I-C_#2E5(jt#BKqcFAlzY z{blJA04=ezEw46t)nh$KMXerY5T_P>JH)90zZ>&5c6xy2RnnHv4)e+V=eFV*eR$Jz z)J51F((x3@-qFG%Wuen78^g`mb$k?e{~+#wwbFUiHNf}&=Ey9?+OxS*zdoWlrbijD z*DPAc8~11Zt(LpiH5c9aKQUvvV~8z@9I`bUqG#ypHJQf~O4_M|PS&Guzn`@|3AWfG zmJ8)09W&COoOVkHNZ^f6H?uGR$}yp{hjs4uB->Jm3NwpE5syztTUWAG7kBN#pA@T% z3>FRY%*iZNwp(qBP}y8nLza7-n=xOiz)>XqkS}F%KN&K^tXbU3Y-f%k(vNaI_AK!I zy5!x>3E}hvzG=m?3sJzuz8I;A=mBgYX-jD$XphcD6Nf=+8(1iy zuY@u@ii}EXxJoY^FWk@`_$N9*SH?42^28ALBV6V+ zModK|Ov3mhm?{N2b#c%PqIg5o(nHeZJyE-oAeDld90b4uaj7N)$3$KY5T%GP(cne# z%;VF6`h}}j5|&$bk^53zk*H@{@DQbEvX{*J*}S;VWrozxX=(1qiw-IQ-TY6qyq$BQ zO{)MMo{(}0AJL_~mf{T7Qp&*>yO+t%uH*GoQcf@bhAlyPWCq{iH!{uGEt{QgK<8!l zt(N_xbe#k4(m7rbN2BpGFGdrN)S{tC+jqyAJ$cbeiwuXX={hJ|T*#nXncOG`GogsPwW5$dDpH(qo7vG;8-878KXB zQa2j71?stT-Fg>Oi(sf0*=`*iDEn$_H@i>z{7muG=QLSNbCcCj-seQYXyM~SiFbv8y42pqAhd=dQSiHxMGA|R#|>2f*$eka$!GbIq3fOQJ*O~s19D8 z!po6+M#B{cF*UFFeNbrm*=!^zX*pzh4sCA;+!K`nYY{u3nM*R{0~YNx1Kl_ZO3Rey z2)gZkelPsD3A1lW87_(jjcTYj;TH@UQ|>+2#6JIHWDci9DOCHgkoOLPWneiiJ$qzd z&g|ftc3L*>XZO=uk?yvP+RV7xjxL%XJdP%_B8-%s;^0gnc_f@7E!SW>FBI&M zLRF@YhSG>#fA9-O>cp6x^Kg$(5S$u^NaVTWG`ZL9-+WQ{4H<)#W zBSsI_mSTGa;uea;_B|JlagEZ_j@V?maZ=~?=zrK%fg?Gm5aHmW8{&& z%v>{aAp7*8T>-DtKyfdPEv|Yp)e%l4$C{6C!+yY;>rPc3PRNk#9Hj;B7E}pvZA?u@ zthxt*Mg~q{B&lc2X@%tCkw|TAOCQBOrHTggfQmkytju@xq4+G&i=3pcI9u{^1|saO zPc8kKrP~Y|0xEdYg+Sl`aAS)GD!5DR!@lHALzDxq_$&RlmId|afOU;i4x>xn=z zgRbs|)Aq>8G!qiE_y-rd#WHKiH3uiO(sI9M3!|#C1fP4q)p&Cf8X3>&59~C@!+G&< znpgedx$5sfDCd`!$91vzHFu_-(;PBnt~+m67H{T(mV*}_ zva3jRm6Ilw)dheP!#IK;DEz_haaBtFT~~f77bDV9hxFTimN2p=!wK#} zGkh12*?JdiT6Qs6V-SUBz%iGLKvi_xeAXPkIM>4PFo(ANjv@kslg|sb9!!G@_rUPR z7P7YwTpNtJ*`IrYAHJKeVPZMf%WogOoW)eNQyIm!9M-0YAA1{FuOXGXT~kwCOi!Ks z@*)r!Q2WJ7PmV%#^Za3bH!^xah^5?y`*l11R|b6gnK>#V_ikwfNmf7aKs`cddbX;B zVIe(=e!EDIShj^rOO95|P?24w-;i{RqwqcEBT5xc`h&f7<9b(Odv3gX{53wgyMd5k2qsE_ zQR`{sAP-@Vch{s6W*1V+cF*{_#kZ*3px^(!xSH3I56PrCxRroo`v+c#p&;3AU+d`+ z%9Li+`d_GOjm|M0aw)lm%&ii9vMr6x#q--L>-iLYLIGd2Kbcexb8`CZ#=6Q>`+B3o z00qiPx;x=)k&t+5BYCR6H?dk*cP@EqV>ii!fQB(;n~uyx)ozY#-BOfBCN6hhj<$J& zKE%t+S(<{YnW{x(z>zOeoT#bc-)w7x+5M>k8rllfGzvt1PDim+J9qabpOx`bOdt24 zGt(?rJ8`vBB{1dYhvwO`w(lj%R5aP<5hE{eZS6{fX|UQkOd4f`ev-X+=@XR*Qaz-L z7|g^eD7^W*zx=wm&Qe@NJ(4pLwfv^$Gj8aXxl|?A>GKoq=S%wbr+1CI8)@v*BY~=e zET365bx9rR5S`%Q>dA=0^O%&2mn??S0;O!5nd*a8C9FizoJT6lO-R8tnIog?xW-rh z$|=YHB@pPTz4`iDch*7yYLWSTRmlSCA+eo-5G*sm8u}HPl!k!9d&+Yd$GDfyW?W(obewjYTYW!&GAIe zPa4Wj9{_LDGzhj^0;7C5uT}=i2f6;!cXz@w%x{wS{TY0=iZN)A2~Scpitu2dW>U?& z${F$MY^t~??#Z()BQIAHQyL8e@Q%R7;FDYTP9Y^PHU+s7x`&OFdT!h$_yKqJ1NBAd zC8c)e1ja<>Jwo#ik#X+|l{2H2RWG?!PyOvJzvItD9aujj&7Q{Hc<4Z_qH+$4x%V9W zCqL`jTan&r5fgzuyJ5ar?^c`U#RAtEXu&HD%Z$EXc}7!x!zw3Y$yyMMI@OFT$Y6e_ zm@m#ya!4h>iCxWTRx5Day=sV6s&0H$C;uh5y|jGUOBzp8JKBtZ(nM4H)Y;KcQ&%Tu ztU}J-CJR+tzEFNZC@P#b3JMg&V(cqJhM+~Si_6#_-sNaOqG8YOQ{ZZE>`K360j{7p z5EvD-0NbP6t6`L4xH(w zxU}xOL9T0sSLQzEu=nL4f4@zSeqPpL{y*C@NFAW%K?v>Ei{wmoTde24k*LT zxkCzX;+4aw8HttoLg?fzn$8H}v&ndx!oZe-;s(g3CC-?MZ4*kGyHBpAKQ#`>tUQ%MrUI%ivCK1x*=X|i|l$xsvj-kCpfYR27_C(EicxGo)7m#P~OdAqnV>jNFMl{jw-yW1*|Gzf79X$FWHyt z$1FQvnskrd|nAF(+x^q)6Lx zOAlE`aYe}dB^0onrhhe6v81XTNUM@)e8b&yx0&9I2(8eUIXLqj{(2|Ju3yn?#B1tcTA)R5xIeQ?1UCv@CT zZLZy~$jl>OeX*EPF1vh2`%>cbe)nKovX~Q);>FJ!?G$^kr0a%7TC6ye-i{w)a(RYa z$&$DQPR)8@G)f@_E4x0uDZJ$QNc5)@i<>Pg>sE}Y>%lEENe4XUsU=nhUrOP4jr^iI zY8aORCrlf2jw2E+uiN53F*a4(Qmjw(Vd0uhk)LGrq=w&OHB%96TEq>6&%3<_ zYcx!fYm?_$wQUBzsjd;=-Zr4QUH;Ed$K^sR>c8zyva(=(XV=?`dSxfmmdmHwD~=IQ zW8JLvMrx$1sZY{N$z-=?IN?Frc_AGnh442aEepo%6mWo+3Re664u8u?Xm0r|!Ao}# zsN}T>E~0NNmjeJlrNe8zaHnThCbIJ}#hxwuulgU)gw|g*k-d}nNJ8@$V)QEFW9*&a z-Lm_oU*P^7UTuBaoGoOIbvEqK%=5$3fd)6N*sNY+X=Wkp|7g0(sJ6PM8{A!ryGwD0 z;x5Gt6c17e?(XjH?(R}34#i!9ThSJGDE6h#x8D3o*1aoPxo75_*|YbYInU~sBxd!! z_s9pO;%Z?@4+Jc??70{_9b|(tL#^I)Ui-R9SHrlVDVCM#fK!iO`d<4adl#{vGehKNf7R4am; zS=`nZdB(?}eP`z3LUGMqn28Lfh(65Apukql3JXI38()8UL#j0BU{^*0O(i0e$0!}) z3JZMvaYBdXB_G6%#HFb}&m09GG|WZbrz05}Zqb3!4}kVp(TLUdzH$0rEkLuS5P=2! zt6j$WPM~V30CSWury62Q{hm!Sq`auT+~hc$gdlKYEsU%CCC3~zCa3@=ns#Eb97849{p$^U{5v%cuQgcF zzxdEAHW|2lVeO*cgD=yu+FCXkGr~Fy`xA_iC+susJH+tIZy}cytd~W6>=qnFy=u6Z zLnu!!?yOC=vR;ZSNADvFb1O5-CD9kIok1N$i?aIYvzHM?Mtp@!u5u`Ko(#Y^(d&||;Il*4(L z1!RRy(l?O6u=G#(8strX2JZ67PnV@=9zP1?7Pt^#I+{2}WzI-A&cc;qBw<@04RC6*$ZAeoDvMl~Y$fX(Qf_vQ6MowN)`eP*=XoZc$J={kC zkIp~R9fj#DLi- z?l|X02cdTkwucPPeqbi4Ep&7mVRfXv>*Q;dx=NAa0LPqCL?}gMc^VNuVtWF;r!ubL zQiVg*y?!+h<0y4(wVJt;;RK2H7&u*%#8e*1`mw?`9zlbTYP1}MH40`OgADa^^0-O- zN>d;yzrm*Ime*k&kkp)xm}7lyZPzEt-szIUVTWGSj0|aNF-Fr?JzDn35zb%w5{DeH z@F4b}`9u(DojPiP0cT;|2;o zDBljoTM~THJHoWe6N`U&8+S&;&();-%|KIWUA`J>7O(yVe%gM&^&;{zY)Y;p;Vli1 zkEgW3)`~GUvxk1wa=WwK=&n3u{44vPg0q)|ywjGO4e7$pfLx$Ep-2e%9FD6lD{x^vD#(Y`7Kvg{dt(I!Y>XRnhX zamL&cV^B=1yYzLm!BDipV2*LJSjP{Vu>zt(#|Yn}`JdOaCj=|zvi@`i6`sytShEWS z$VP}4C0nBtEb4^An|9&5z4+D1URQ|YN=NJwXbrCR<<>%Ti5xmMhao7`ny*F+eT9>K zjg=%Bi6}x$#Gm}QGtmPCCtXibrLB!g#%U z?3s}Z379vzOx+QF&*r(4YUb8=+2C`MVpr)?l9SO9vLM*utP;a@#4(g_;oOJnB+9z^ zQh#Kd*}QJzDEg)Xr%zFOpI(zdlG3u8>yGNjFq)jDu*f5hOh*t)9WBmh&1LNrVpqh@ zcMOOlwYO!t9xtlU&91CqjWIu}7a3Oxm@^ya>lp099_+br6Dzdp<*HKULmTfrR3~aA z2}?ru)Oxl=p>YhUf@+z6bEdkT&xq87!*CdhyyS9u*>Z1l)){1>a5XnZ+nsic>x-fQ z3`Oqj+V}f*7!pa2MUdvqR}jHphr|fiTQgJ|=l+7ylw|;s*{=|4ojtf^z&Q>F8oAP1xuN}44lT}BF>H4uFyK!6Qz|XQvkr6UWVHuR75jLaDxx3=~D;P=i<;E!5I9I zkvLOk`!0n-AMg6zgL>Z>#}YMdv=*lgatC2W3GmE&2D`@*!e?KivzlO6l2F%fCMK+e zA>_t;n3y#A4-4EfSq$%lF?7w~!YV{*wH7ok0Ku(k*$X>-Y(AP*q{?7lF0jH1*Kwh< zrna|ZPUZBWlPJ0z+vi9>2<4~~GM^}S(9DCmWwMns0L%A$*-6M?9Sq{Ml`gG5z3CQc z3K*JMZhb?Co|h$MoS{US8ru1YS)H^(W8*NXG{@x3i0?{JWwb;X;6G`N&x`u?=1#}% z)VJPMr1-Hdx2L;gP0B!elNfO|!V(UD@~@I_lbwf8h){0){ee!f)+1`za0>WK5_6E2Jj>E0u!ol1(>V&1u&T?aeG-J0DPI^A%MOsxKQo4QUX2PrQW_s6e3a9xLi=3rQ*nIWZz z(q&rYBZ>f`QAVsu_0zVHi(5Dm5rG(!2cyGSVxvz4SAU9?A zcWWX8$k^o5PYo0ioe7pFWl7dP#u^c3)ba7Bd2?9COq}zI>4$EkSM89(brWzw#j(!g zOb-(EFCZI}wJv2`QQQx&-#mKVw?yWlKRdV#09~S8FW2$$2Q(g>>VcgQMSpyp^LiY+ zW;aGkT3-eUC>q;ak&A#MNBKHD@!JnCOqzDNN>W#9tRj>Lg|aYJ3OIau5s*Mh)ll$p z249Z#$}$>hIxmi0C?O!ATzi+e^}aMl4yrp&ie9Y(ErA9rX=tGu#p8R^&-+m-<^f~M zd{ox4hsu4ar^pG>oF$Kt*zMy5DX1azA)V;6!v3>d?7X(A__sSE6~trD-5m!XXx|fX z3tS_Eu?~jc_D4nN8S#61vzO|fII>PzdTY)sXU9BM82i(cE4905g8_!;|NH0*%MaYi z8_BRT_&do1F3OFK<`Ja}{gx|b;M_JPKF)8AmJh5Qs$3ruz|f?pNCG*XK1pnxt#4uk9M z&Gn=u?r*2;)W8D)c=qCtWbZqLY#v_QS)(g96m3ViFNqCiQmawMh*AI{T9HnH70Jvz zzv*<9PT~529!RW=0}o*WPmb~R1KZqh8rB6UNlU1c;lb2KaD~FO8_8oBGt-vM&3v2D zcgz~Qiq>KGY4q8%(qmV=!z0`tZ^ONfpyAa8AS;P1GFUP&TsPRn!f{V!Q-(d*aR11c z0KmaSl!tOX=X4y5nX}DLw9%tc#DdxxDTA$eDt+#zZXsyw>3w~}@s00!Ehf<{M@(iC z$1<=Ao`eJ0-mfj1Mw$J%aiILFFI1I|$O6qRj7P=u_A~#x1pAJdoZI;DHTy}yHk?!t zKbXLUa32#>(_|a1<#AeoVORJvIDV@CJnBWJL%g28AT`h`C%z7~D5B%Lur09=3#Eu| z*`6-@a!mGVVLYPS$5=|2WXJd-238WRtT3(x?p;pcCIPA}Pt!aF+jq_Mp(uGvQp+N9 z+ci{P5=D)4TjRk4;CU$2!$;Z29sy`BPhf^YzvstB?#RSp4qp3I;l)418>rM|vg?82 zZ2VHSh`QQ^l_MerWo@HP$a+r~gV;5sT+J{q-K|TAGw{8h)lx(wa>-W|0!CaoF@K8S zD9NnryZ>^Y#r$y#>Z!0fp=p=Zhx&&4@C_tcQEeSLgJ%@&3cOsJg60#saatgVX@Z8} zPqR?aTsS!Nu3?RJ=G&8BJ?Tn56M{nHh8=Cc>(tS3R3D}+k~v2JB?7c>efta3%X0v6K)Q%Qy2muIc3p94mu;%8zhx^7HAJb{E zc2H-UF84d6iEd)&`N_Cx2ms@?77t0@=)IjTre zX?mYDdH7gD0pm^mty7eBn=Gm+mm=sEQVh)DMb^V=B;UI*#|71|Vn0oG92)UWI*Z8=WfD(bX|aA}9*_g|&oR(Ky2Pktqonpd_%JWk&>$pzsPEiN-&oOB4RN>gq7_ zjO;QU4$CxzrsWB!2%QQ07EauX*VabNug<{ak%E^_C{&qG&H=1oF+#!aHcW3O-xHRv zmg${fkOI1T1CiMg$Y6(I(xg}~NM>Rk?s<=8W;uiA_mop%Ind%w5|DezDAa3VT@1JY z1yzK9l#Qb@i<%tLB1P9@*J79W#SQ5rJ(6*PU}$8RoE%&+ISxc$?o zD2OhCqzs?e6s7cumW017!G9hS^4G!rKQ zu>%r{r7gife$ae=Qn=x`s(>-#Q+J|;fpyUc_5XXe%kc3a{&j7NB)c6MlOsEby1ozP zI!mq1^^>vzTd21?NT;|0b*3_w^8%Blsir@69WRXVDKCdy$1Us~%lRCM0hMb2a*>nm z*9t*!u3A`Am8vHE*Fi55+{HWWK9x~{lTb?Cz|};MIU}z<-gh^y?bhL77K+_~!PP-> z*K9L)QDo>iW-avfLG1Q+E;HT0PP>E;u?QHXgB)%+v7Niw1D?1##;?kzGH7UC`V&if z&{-7S9=UPf8P}D8nmmJQq>fqWMi=Q4MJ%k)(431)6Kmq~fi){Ch9dYV2on2G}x^(L(Dac-yPIqly%9*tpVdDMzd746?YTAS=*74HQ6=2glG0lMn#U zCdS#qE7b$Qloh7`>B7(zff6k;IW;t!^|Ur{D>7q-I633?eb>HQCTc!GJy@=QMNyNC z=R;PBI2-E0#}+O_ak=BS|XJIrG0%w(~Dp+TE$p>VwA`k|`V3uZ#hl{bt_2Z%{e{h&?8;sqr zhSoJnNXIvyQud?_sB-!iL!JRfhM8%>R6-0C7Y&==;IoYp({co8@d#Tdq;Kp|P$giX zmL@sG(N={TL&@h!()o8=%ayJrU76gUZ2K@+{B`us{j8vdaLm8Wyfi;NJWS5Z%lihn zbp4YfSuzKWqH{CS%7R46&Jbl&$#7j-W|vk2gc^gqMNt_sMcG*CLm!DRR+-=@UiKtf zjHssl#0G@yRe;Pw}5$H~_BBG{-TidG4d_@7m<0K6-9 zvUCLx)dhlRDURA6^%o!6K)vC@I${FQ4&o_ym%ik2sFGfurDE~iw=sbNrcCobg7Dg0 zWTDI%f+!otk}+apV*k;mIc%e2-LF)L-60UKfIJM*0la4(YiQwB0UM5S0nM-yntDTy zy_%jFHY(BLv{O6+B{aG0knq^~$%5k~mzv(*qRqZ#WB|+2%_4WJJ3G2(KNdA;OY`i* zG-z+Mt%{{;jq@nSnIX!DQMkRXn)bZb(V{as71he#-scl}zuJ*!nB%3}xqgz(q(bpdN3nTS9t{*jm&>4VuwS_Jx?n4tvmg+)ba zE8z#d*v`tYuc-|mw;CRW6Zf7<6VpAk)eEblN;{d-ix?!dv=!CAVg6*-ri%J?PqOSP z;#l|RUgTkIoVZ?H3r1uc2FgQIEP_LaF4nWS<_=P}?S+tIFS9E*m+*l8oU)oPX|>{VRvH)2 zO{=G&rMGDRyHgV8I(bV`8blomSt#g4g@S#!9#)At0p%5*k&ofrvq1AiLxWZgPn;t(05xk+G-e6GC#ltfLQY z#K%*f1#ZP%fq-!1XJWSt?=aWlND17Zqu1DuHFE?ozq1WY=xCVHCIGO~Uv`6Wb1&DT zI;FAn=zT=hB$yA;%gO)53w&t$cVSI020dV30e0mTfn)TwtxQ|b7ucw(KwPh7qH~0? zyMMG_y5IG3OZ$t9Djnu|WkT&mD+}@FT`u(Ag>mo5Ac(Ghb1=^Qq7^M>LiJ%V6#qWW z(B8qE=;twA?J}{xfHelg%7y>C(a&m8zsTz(aVup5^0y)cZV{p5!YXEfp`~O(eJoCk zWSzIJQjS4|A(;E#3NBv2IG z7JOQA2@X5M{uxyWF{!KG7sJ}vX8z8 zvAuSgP*L*|BLV0(2-;MMKq)jV6HU%4Cd41oYXd}mf`9=}V`Q8rVFPeZ+~(Gyl2q|igKs=sXr9X(}j&OG8$3BK&a2X3jK?sQiZ4V?BkiXlx&1f zpRRpFx*Cq%@dOzc#$h2wg{928x2)|45VFJhsi_m+ZmiYKU0AW5j^^X-^vBXuo12mE zUDdkIHi{YY4>jflh;HX&%^5&#h_`L?e1EWicz%5CTK3b{`l*g9{pB8I@!XshI07`q zXi9?=4C*M|4+cC7J;?IprsJiMfIxDzl#zJiktE|p0To!X_*PV&@bpLD_|l+NtUecW z^o$qFafNo!BiKCcn}UPe;~1|>`32Q}b$=Wm>)o!`at&^(D#R=96vSpgjuvN=*h9>` zSiTiucA&qT@*DH9GdIVwk}wZ!;zd-4V$tZErUyf7Ej3(M-Qj)`2OdU@^t!)3ONWK$ z7QZT4g{n{`ekc{SlPG}z3M76QX2K~3`jFoMGUM1(2{mp%Id)h|5+21uRWSO|%so33 z(J3l1o5Jx?{5K^V&&7-LilgHb9fF}4>T_jR?ARtdiV)OfCU)gex+pBG&>D7v+n`yB z!3tSj7)rQK(y0WX=otBUvDCgU|9~f`pmoM#ppOmq(8BcawYiT0gh}5iy#go8O%5W9 z{AS(9w-cJs5SKvLX&^~3Bpib!MDVc5GT-7|%5p>fAWn8jcUN$M7nH?)W{3ntm2Dks z+GOB6c6Z)WPv00V-EKP zwSD6+TOO&D5OXr4*U@wl(>_voJ=)BCKHYMwIk$!6C`sy6Z1LJKIuP0f z&u>kT%^+F|s%+#5um?m#OzGO^!AljwMJCMpc>{Y*@Q8LJ-Ol|!uIlAk`UmjE8Z_T?*e2Dr3Wt{=78gTws2!G zYtC~5AzTP#d+v4K%3!NK>b@T?6KW;wUlBCYo0iu-Qw5Y`ouX~v$FAVh>%X5F*jT~K zhUMKjDtkS#be2=0xD%&)`-8`4ji2 zjP|Ag84W`}sq^^G^UoW>#-20nTVi|v7hA2yq+o&rIm)II%6>|O0fpnqPuMwnYU|>s6?`}D z=N0Q`+e`Ob+;h#(^x|5Y+#Y<+afChzfl)^V!u`Hb$C?wnp2k@3A8bx}9O-bg5`!_# zus{-7Ze^cP6c(mab#P#>*0R>l|0J6Efq}b9?b73+NRNiHgNdsL4P`%bsQEdMU4sZR zk0O)j>$NDDhmWXY%@vX0`kZ|UU4-XON66nN7Cj0aYH~FTE0u}Ozr;#zUg`xVU!3@6 zd#ORXrLhGeOplOwdB4MfpR78;Wt(wRP|}V*r5s8OI$(59s|JtG;GYY*Y3Ecrz?gKo z(L_AjrVXwJL7~*u&keD$v7!yg6_ROuqfyxF#W_#wJ}x%3KPLm^^^Pp5r5Xn+H8^Ta za;c9z7V_vkjv@W(XrOVte(UCc&W*zQiG0(XgEoVzgU4-ysAWK1&q5Ponb!Al?bRwu zL9&^+j~@lMrabive(IMuA5G3e`{+=CHcZ5`hZ}hWxNTmcPv5YxTAU1tGQ&0Ksp~ND zdfwKUQ#?1HigpTx%6j=CiV6N%K0}+nrK{^D*swmnsLF(88=%M02u$Picy0ait?7y} z1dt-F#{YSHhFC{~FWhIo*iw0c0y(jV7B^LL(H+z8k)Jyxm=;S_*P;tPz>iHrZ7r?N zGK=cYAckV6^%NCEfWrH5_P<&H=7b@!qQEY}-Qb}sCWB8=_dm9MSqyz3`}cMt0$BmY z_@$P|d1<=M#?{rJk`x~>CaM%(B zc8J4*sLe#uy1&5MVfA)q4dTAI-!id+unJiP&ge6RZSlc1%%ptk zqk$eRCG6X&evGKUU?@b=KRB33|2@UWmYmR+FG{-W|AGONK;89pctCBSfpM2$*{AZV}qU|t(rDGm@wu#%L+u5#*aAJ()%_QZfZB%ZzBpP-p)DOtpZD1-YZpDB2kay zITQ;m4s6T+ce~#IrVtU$jh5EJi)gjNu9-aG8BqiGrClbZ&7T_9Qhc>ySn0Fm6Hj<6 z!jdi&8Yk@8U1W%{pWqQm&srA`Ps2++uRW{4$mwqh*g_s8PFSc-cw_p^K-j3Xg10Bz zrXs2srv-S~;*ElevJ<)=E>@{3Q&Rjw^o^|xJJTw`D3-X|+TfnKP{jV5?9Xg+DWQY8 z>$Gn#KB$#6U@#a%BrRMoh}x9&TBV4>JZgQt!}1EE@yg+Z`ad1JyBnN|7w;|g`bF;i zY;T$u%L`HTA057dNAGrFG3x%39AZ9-NAzV-_h?M1JIaDv1SZ2&P@X(SC1uF#CMhn9lhJG7`Hc?2W*HBtWr`yc*2 zIjQfK&|`!}@3T6Ef&0YI*zv@YI%MzX&DTkHi&EpPY8VO5zy?5sFo_15r6!Z_2-n|=S81Pq;lvNT#%*UB^>azw4h(#CKnSV{sN`>v>2FYGR|;e3FbyeWnGJi zw@Pl_dC`8pox77qcL$^;K&jx&hDS8;ILYCV*&`m$?M+)qhDl3hkbkSU{8>zw8)Ckp zMY%)a;Kdu{jLysql9F1P!4hd#eZ)VtY7-`h zczspzv+fG4wFV_KYK?1;@Xud%CVnPsHM!-N4%Q%D7glCeYmKw4Ucl^qhs9rx9OsPu zNK7jUY{%Fw0e2%#9D4JAE2_h^NeVFy7Ug`%6JZ=IOafKS9SuzMY;blP<#s&~0v;8Z zoA@dpiDom@RWN>E{CmE7qu%?|yOZU-Znv{P&AUf?AG<^Q)D#Q`w3D1$Faov%Tzu?TKtEso{9&G zWOdUFp65S}u+#khNVBn?2 zo1LEX8hS(S0bf3aMc&@LI>XD?>*Z!*a&=RK7<@qT?WZ0kGyh5sx!f%RW8G#|q6;?7 zzz+ES4X+{mFn~X6UXCh^;3!nu0OtLmcLPn3e-xTE7881KcTZAAki?Gd?wRWg^y{P_ z5qnus4dTFufPQGeKSx8G5pX|$If|j!f4*H*3gK7#-T^FV4Qs-ie>A?{40k*5 zy&jVt5D0IgOTiOojPzq0_&4iCJX2TYep#=oIYYjniNK*&F(qRrxy+bH=dCymjSsAqidZ z?9aiNuvrcR$zreoR!{n7#L>1-pQSXU$66X2ufoTG9}=GHt|EPJz{;-C!e~yYP>HL` z{PTwP;78udVWhgftf+2ZAlsC{yGbAY7!p9sFQ@5c`Gkn~MWC3wgB? z1XtQIVa@XCntT-qV&^bg3Zqto{+mnS$l@z-KG(r`zT9${EfPlyxkTaNl5a(b4ms(7 za#sHF2@`slKnG;ZLd}@(|MpXso?B!n&;L!@X}Vu$0>ln~nXOMhcNX@*AiW;{qx*6| zkyX?Pvj~Ov=?CU7sW}J8sLw;<1^4oo$LS)7REU+5^vrwt&@AaCqpuEjz3N}Er9BU z1=&_#e*tw0Pa1Mc&*o0OQ^xogt?l1P&*F^U%q698eeoh)s&h;suIajB3dI&%xZEjK zg{&%=R%n60*gySSg+Dzf2+<0vQNuUj#D zoKILHNd63qGlqB4B{UwpQk#YLpBl5k_^RLAQcIvsV(^`7^1GK!VDRR|GL<@%>AxD9@1ArC+I`$6#-`LM*N-XB9%t;ZlU6UiiN4kpi`JvRp6n zqoLvPDYwuH=CG*90NqQ}TUBhRanT*R&}hXN-RaYF<~(-a9syyJ(YhVV4S~58pvb^T zt{$g7JCn!=dRO;dC8>DK$C{$KE$s@{KGLDhB zE65Y~|8uV!vX(HWoX4g>?{kVGQO&OhlrOmhui*Hi2P>=f-)s2^K8Ko!6&{9NE;AAW>9TRq6Pt z|4)@(mOcztA;%&zD?DLTa#OciPJA;`e{eDswCK~Lnbgds$0oXNUW%=q({HBBMO!<+ zyVJwNFX47C$<5-0ix@GAK-nqvf6@d&9OcXVaO{+2JaF_*N5KKom=}obdhTr5`Q7h_ z4e^C{b0H+ol$wAk6ks8-u&ph#+>c0l1)cKy_YZ+rV_iFoXh6g3KG81Zt^7 zGig=KA{`DUn{jV3^Rs33rZp?vMwJ8(d@4NKO0Z5e4L$=Cf*3_ZqctfM13*cZom1Cq zNl;?5VUo*e20d4*Uk4>NIaoB@`IFnrePfuZVuxs#m%T2d&EqZ)y@^S%k)ffV#r*G$ z_?ydmf4AO|iwFL=b+W(bZU6EcmGW>Jc&ge&JY}JWog5)!0*##w2?Qzh@}ti0j?DqB z3#>99{u4|7#Z)OYmYm-bplD|lQXkJ$z9q~vl>So8TC?msnnW=kUzV#MERnF|Vdi`( zu!9Yu-Qa z8#?M22tN#w*Ov4YetF?vmnHXI`2)BUta9Zq6edY<2sT_i*Ps`9d37c2lSi%m6hS(X zYFQiTimo+S0s7VSMEhRmR%@|LqTaxkb@P6^ob@KMzh}C2_Bep>srA zMouzcxprGWmNg8fG8<9C=oJx3D<`o!B4R%X9snHilGk00IjBA%(#@k}j>7L0Rd>257(j{18CwqFKE(lqLJxFKP_tzEh{O~S5D|_D3?-!q zEa>SY|6Yx}Q_+bdu-b0}ZGcSW{i(C=EKFO`De9w&!8HvU(oeViD_3S>!z_UT!|b@N zHK^l|&N56d3-<2iZBpn)hTGz4c;BpH_Kr-5H1A|c z%ZxE=nR<5jn>QIRE{=ImIP8kuZ6t^uLPex$E$sU85 zp|tF;VYaqV+pqdo3wb6u#I1l9?8lwKAB2s`Isqj8j|}ZKtZq$B=DN9MP($7KFE?!J z#>87=3Mo9Y`ombGSoOJpTAGx33aJcI8pED-ja#KFWscPiS4dTpoLIz*@%8uRCGISJ8M*H}NSwLZiT8hhr(eJnTEZX--P+g_39<>Z$FiggD0Dla<%iDKkDqX`~Y&W4} z@L_|9Mf1}Y7IYy#Ef7T!lF7Qs@W>SBR(4F2eGmel152o-1cwXKe?9cR*FT?^kiXoF zP(^ym4CqLd(Ef@j-plk8T;hZ5=Ub-mR8CA|Pcx2@#8&jVoFPYn7H5vN@f>@t|Aw#>W3k3R!KqULV(o#uK3Qd|VQJZ3YCS8(9QR%kQgUb3Gjwkej5y zt>NQepOhd=3;0418ig`V)V!A$X=ITT!7ZU@%8=5eKnK=^Mf@lkHC@c>vXlA=^6F>o zE87k(j_5o7bqKhBcbp%&9CFKESy}NowkNXd^7B7b!2H!o&KqJJNIfmE+{COD{U2O7 zb!I#DnfJS1i?^DMPE3baM~dOx%YIjEz? zj}f%jqrc*Kn|?l>YoJFzj@)dotv8}0mDd3_deHTMSyp5yy^J<``6b2oJdw_Z1CXP2 z*o_%@zq3bm_4^ePH?!q+QrqTvd%tckDga@hdu+Ts4f9qaSBULvRB^2`hE4qnC?ntf zE&0ziUO&IzY-0jm?B7pBeSpOC|0s}CD>T|bzt2UZta*a9j(3N_YVtL8oVBms7q9Ao zQx_PQ%!tbgqfdH-+smzxjS;s}lAnTctbfi5IM5H7fHK)ATvT5{4_Oo^C=7PAyt9Z@s1pUv+o9>b+e+;tZg$>pUZ+E72F-pz;p^7X}fHZEY$i zCnvI>E)~y?$@?vR%Y%6 zTkY4-g?i}ST!a>(AZ}AuJl>I4V$j*5z|71d?JY{YRcx2eW>ki9q)rnVqK<6e)#y|N zLeV@6=(}&|XW&y+Fs);AG!Y-v#Jip>U_o?mLp%J7tqhEJ*$|&nLp1j5)4MIV+|#CY zy0{63`QCsDW2_y!+aRTNy&ax8sBc6XY2yq@ES;$a^o>j%4Jp8|K%IFVg~+ZJ2Bt&1 zYNBvrcWeHmxEjqThV_4@a&~rxv>Ro+gd9t<136!9@Sly+CI2quzqK&$jW(e|?(_S< z2_GIFCYTFLG(!e6eOCYiv~AyguYP_21Q^!0UpRKX-mg>noQr4sjCS1%{;;2q`$dz# zGI!c1zeLLkGMQPiDUBg9T@?Lu@!`d_pC=x-r@d3({DZtAB!AYnS2JgY}3 z^_$-OUu}fXMYp^3Xc&N^nl>y`QX9Z$|JBDcI3Ycxsz1?6C5U13MXWIUdU#u&q~{q| zGVH;EgpxXjDzb`ZW6LzmN)|oIX1zZ1Wc&HA0R;ERAO>@g>asMkdtq2GlTPRSj_BY z1+ipXiUoW39#2Q6?$4k?{RZ9hqf~j2<4CIJe@$P$o%|lLt*-W|LAl|uyWsZtTO)z1 z`10_c&KGIlsY0$GA+GnZ-0SxqDC!S!`5xy_{i|@XRL%r=kVvecb7Fk9?0pe#LiyLo ztiyQ7aX%MP<5iKHfe9TELNs)iXDt0ot&XA9N)m>J`?AwRGW`SjfcGkKWrafRsLiB{-yPl9=~adTPtL(y*e_sjf?Fdcgvn->m5%Uluf^P46!by*Nd)K-+Pt1CSem3LH2 z2s+HLN&*q>h(%pRrJ=J^<)Ztc4iWo9VW;*YzJh`c#z+h2WS4RU+Cz|qcS!Br8`SSs z&=-GXz|nNBKRq)GBms@eP!`OC@To>hWPIe*4Lv<^kbv|>zq6$i#45EYIJMURDFd;@ z$Hy17Ux%OWLwtHGoh?%u^8JGHS`rCAGSqxX|&ld0zP5% zx@)lc+R%C-nSjg+TL<`d1O5^AR0B_3SZ!Q!S=4Ygv8OQRv5**J7W1jW zyQqq2A5V1&{i~3>WeFs!SLaKWqe5b&wFV~Qod?c3gFv7X>dQI6FJXf@|0F)%qWvTh&T! z`CJ0lX^sh`={HEP^nhcx9|&Yd{BOp|=pZ(Q%XPbF!GeZHgt;WK7vZ7&31n%gP;7f)ykwqiIa z_@$633IUidHL7^xG7x4R2D28XiYV>ooCf%@xs#I3p0bb!XVaRofv1T-s_r$f z;w>I>6v#BQnSd2)P9_k2P)O44c2l#!nkL0PNGe1QJ4_d*bTRSMdjmdw>bJpKIPmxiLhJ%UZdQyTh(C+u71=sLb1>2fE+D8iZ21aQU`Nuk5sV>da^(+W7O_Dz0xUa@ufYZ2~mq7TJsGQ z{gMtfR3L(BrDTw=07b9n4S+{Y&CO%awW2!yU%NUkgQaa{dlt|^NTFrU9ws_=CD7vi z9m3DNlRuMZF(R;Vgr+0!`q_@xDDQ8y^U-u~ajT44+OZoBt~4CgTUV3YD9VvI^!$mm zV+T6m(2|ND)W~Cc`*o9mU=Q;rbgLEnu{44C#)3ez!~G3peWSJ?et+{Ob@3+J)|?Cq`T#&M~2OQm-60PFIbO0NW*CQGyq1k1LjrfLa_5S#d^NPvtpezI=PioQ_` z6(t;ez$;u%B+xiyC!$|^TJOi_-rTR;K+XyWC#Qkee86H4p|d@uz_)p~H{#1OccfQ?8q0@}Wf)P!2}*yj>QwcvV$_ zah`3m0a}{w*Bmc~o`phG61ei*vEuF2Cb`nsIO9aBvKc7ICx5p>3F1iHIKwrZv8B*; z#UaX7#2+^?nLbYM74`$jJWe*31I;=DTSFkV!rHOHuIRdIlxGb& z?z#@1zuSACVs&(NsY}0Wa<6O1?Ga21XrlG{fy}P9yniS<{)OumdP^a?=xbj{H26n*o%b)ac+v z{C2yoE=#vg2&kTXcpLN|fd5}Dz~bfrLd*8v15z|d+nv&t*|_OT>6)c?tWkbgEK zSulUXKrnK|MDiK23qlopE86zG-Wj1cCy-7}P3;;I&W56Vm_2iYa(F+iFb)dN8F1te zN^Tg#1_F3;ME%M9A!$KbXZ`Ig>FnRXf}sK-uLVCA%peE=l3TjtdnR1JukWML{~vQ- z)f89Pgo_0yxI4i$5Hz@k3?aY}2u^T!g8Sg^?l8E!yW1eaT_*&W;BqGKSLYX;n^Q#< zT(H;Pt5-kmt2>GrX( z3O%k>1U(+jEVC6xC2x56sbyewMTH6zF2_R!2a8b13QlmEjlgmZ3uk6{tXwpBV061& zIIwN+%E?mV{nR~`Odu~Twar3Pj;bx~6pM(^f&UR^s|wRPDdKS+3PF|C_-xmfo(^|* zRL=tQ=F_@zgyYe(qGjd;b-LBI57eiqwd|kv(xD&N2+lxY03Z;)=H_6 z@~YD<{JP0VG*p%rAcVyobdWn7jINj~P+w2awbX)u4{k*B6gg|^>oE~6K?ex*#^IPB7+mM@Fk4~zo9C2(vfmKh* zY--n4$UTgqeZF_IJyB&&(>q?Mi0fm6Q3y!lgK#u5Q%FoD^z2LjCE89=em23(g<@0Q zwsjJRUo>2P0-GIQrnbq`plQ5xKdP!#l3@2B8gGiY*Ff$)db7_OGv}sXX$0whVb$t1 z!@~M>d7?*$4RJR=^ZG0ccJxtri|mE(ev5gHw0>4JVD1CLLAam>G?eMa@~;9&+_ zGJgo8HypBpUGJlT6*sSH^LbGE53x9ZM9+Hr-kgH}%v`W8v_Vc!d!Wo8hpfLtQd2l| zs8?vi{&tn7-w3AX;k5h^wkYwQ4pxqsHI9@QhJ=x`)to|W+yGJ*Kc*9K=msZG#z3^E z>yNNI`{bGgP3BU2)9Cv8&z~b78+VsV#9&(W;kX`RzugzqZ+4!cQaSB5>#2+d=x&)V|3d7+XZhCII>cPd0On7M4;KT_d3P^W2r zuA{P^O-HfTGUvdIoEOc!FjU54pVIUB}#>;w~QezT-xcfD%?+AjfskEGRnVi z5%r1RbX(SdsL4}43Z9F8g!lb!ZF#zyahzJ(?NC(c86T{1ZCrD_<{9e-J*jVg&sfD{ zD7nDSQ@7L9>r=O#PXf7st%wuu&{fX|hki~yeXe6|{>}rpeT()Hh1N)AQ&p;9p6BYs zotrGIa&_EF+9@;CnWHAF2wR1=?u@&40+7=AznF&c(e}=E@Pg{T6yxx8D+-e zj-}=Wnyzs&J2&eF6p>imJc3F9+)7?Dv|vtFw2iOPEGnY2&~BHL6>968U8}{aXZ4pW z6oz+Jp18~ujs6AvTL?(R_tzu!mW0x=k5lF#zGUrYv8P@T{Zde1`}W@205fSdAFPce zEm=TJgLke|I7oDI+aFx@m4alfR{WID@#;XyHx`)hicUT7Qg%Y{seGi2@QtB^1~ZXl zh-a`nVniCn<323bQ7@YLOz)=kT1WkQH+&c({$v0%W|3bX^k0v1x_zEM!d$J#Zr|$y z>e5e>Re)+E;U(LI={br`V))fGNK?ZTFQ7=H<^=HKB!27k4owu=?`KNr;xZ1v@@aw&NbRIdNF!;0lG!uAUzTTZ$+nWHX{xE zrd9Ra3&IFb-7)&St%P#PgVbgz$|89l)@CV_`#$t>&~%qujsn;8Lk1-}o=7fIGGtP~ zcKzWTg{Oi2IVmC}Db^i5kmNVYa1pk7r9Caroy3{(biI5I1}uW?`T3eE!k>uZJjE)<%m1sK39T8{Z8N?}3gSWQ*OH&6p`QUEOgx#!ST`^Yf$X#~8O` zyfs+D;@OzBHTwzY?$hhm8z3 zjAw^Z#4tlf^X5y^H-wEjj{|UGs>$J15UIII$?Xm5l!NCR79m?|(9t_kLN0GPA19OW z#-{+KwU@iyS}ywqvvAzRaLlUWI=;d06(o)OkZ(^($ zf55CcDFZXKV z`lUbTmCbj*TIBNTWnuF{+oDrX)!K5y`*wZylgA%IBicOir+#rH5|H)9cTb#F8~iB? z_uDzDMZ2em>Zt1H=GUoB?*q17V0a+Dg~(dYH^6A6pn9fHk2FAEslQcz6qZd#-`sYe`l14y(Jv+JtNDq|+~-QLYfNLe+fUfWX_Fnny~ zxSU9mfz`?+xCnT+<&?YFvLz&TS%P;r#Gd4JI>$WUR759ZFt~*;VE1zE58OLBdj%_` zY%HQD!eU+{d;HbBYHuRi3_mc7J(VyqI!H{*O=+q!R3!T(aR;Xs7GGU_YMvsX)#-Y= z)M#7T_+&1Wve}9=4RCmVcL*kQ1#!UlQTrcr6ejNcDBaT_=lkg3b>{#Pe_VH^W@LGB zucOwz*VX;5L8)PquyC2Z&%K}S8@u)(xpbqnNe1%g4 z!}I`TU8E7Ix?i+^i-DQYHDH)!Icbrl&yV@@E?)ZAlg=v$`690o4oO}PJ z-WCxIu#A+sU1n1On|om7LheC(R(r01;j24DA*HILJCpn%3hXKhC&qkO%_18f zH~8f$$L{Sh7wb#TtFyOoIoXE^!~TZULzS*|>^hLO0IDXzQ6Y*hhB4mC#)c7F#$&;W z(dN*cvAAroH-|7VW%-|KjkGMUgI~W=24~5N(MY;z=VN8MW3G}fBvn+1bQ$A9`tqRy zKF^D@Id^H|e5}`7_xQM(>p8*@I#9@VoPxbp#QykJ9(kd{>bD0SYkaIoQb?t6y8FBE zjvfEM?w!8w-6xY5RD=w%0|eZ1E(q%x#2&k zcnv@{h9}XvpDDl9?=~N=m`4^!m0st=)jtxFhprsQ_*Hj5jU~Aviz|O8+(23I?hEM& z!oUjgOfNVV6LVX-hF$niQ(BHGwnoFv$C){a5pOG|a3j+5(L}iOD$@LkW`yU7(LY1h zkgTxqC~w4JlTt&(?>$E6AG?lEpi3;YR)2?6!X~IpR>@mFnRa`$sIqg&f9D^2{hMdn z3+TwM;@7gp9 zy?zv9IZb(ngEiC6etCt}t3N%ffOZ#X4$WQbJL|ZN8!C~7y(wh-bVrJxo_Vu_J|5e> zW_6dNqY)-XmLx*6zgwJYc5Q6lk^8=bf9mYMy>*c?jQJt~o5sCMzCD|CYm!6UO8cGl zw2;e*2+(~v3uZg37GR3sfk<2Y&>*ys zWO#OyMZ}&6c|;S_+5JO%0Urf)$P+VsOnqQW!!B?yGsW%p517oT=M#u8`t{mifQe;O zD94`ai+bF;os$qt`#EZ9j=_-rpn)=sLT2yOIjAl9KVgNE!l~M53$o*ZMD4zDQe2ug5v}I-$SE1BU;=iS<=gjbikh@SH(hpy7^{WMeQsXmD<6r9uT zxNmhfOb@E@#2EB_u*iWLOlVxzt2Z?>$N$Eu<9#fMIXw)?90Elfc%uHj+)vs3EEHk= z$AbcJ)4T0W5hE>DOos9aUox5lc|kXK{OSwf^8pZ0P`pm_kJr6pv%_P2c+V+3MenX>5>R4d&R_%z@B@EyNL{0`-H%gao{zRNbf z-hLzI4EOvK5ZHbf?dspw)Nmw*;=`b4*lbHA*{c!I$~v{G?|Zm2=*r*emJCMHx>(wC zy1yJh8zvFb2v0lAGHP+_oo*jkPl{bTfi-yR1+%4W&H$j23q8Y31Bgl_#?9ZK+#6s2 zvVHHm`AYkdmL^VOv(4S<$<^;CaVQIq9X*@2jR>v6w4LwM1$LFr{fcn;f}I&{@#bT5 z)!1nj)1>!L;L7vwg-Hm`5lBq`rP*wuzVYwYZlxTiU*ROu|@s>Mww-c7ym zvV@s*+V^hP7qam($eqCcTsLUwpd{x^_1Qe-ZZ;#%XU3imwdJl0*oGtDD?R>wt^JP= z{m!ya)Hp0bu5T5^mt6u#e&@d=6eERev^A$BCR*Qt4B_m_M&Gyw51*x)sRygY@~=+Z1Oo21G}tp{-gwkIEL#}R=RgvdvWL0F zjzz3f+VtAoE+|R0ngX|)J(Xi*2Z}d6>9RfzA5!6nXn#Ms3V_8%UzT4leDBVNj&?Sm zR()@;;8weZFiphtsz5Kp;mvSw}wZ>%R z&ti4I=J5AdbZ)UM0+i)W42!CGu_20U9O|q7?^(0+_k5e~*uPZ|>a;W8PN`0dcHaf* z-vuF^bHS>hGkJ2+@EO0Pp5GfRB3*;m<30Ndw*OG;KR>p6y$Za}XS92HZaiO|i7hVr zu0U=(V$R%mJA7Ys^?loiszQsm(pn6BanEI4`m^2}^aXOg>%v3&wwL#B#rNA!TM9}N z`1i0s4f^f}Qquik^0mAr=P3~O#!NFW3t80FJ*VWE9rvMr4>kO_udqkP{kRC>38CwA zdsput@2&!O#GYudE17EBbN(%V70iVNPlyp)&y!mx+&soL)jVf}O27mohon<0TSzi$-e;`C(+ySl-$I`>;3>{Hr2 zre~4ji$CSnm#}caMK209ELu)&|NQV%&wZbeC}L35_boQ|*&(Uu(e6Kz1w#F~tdYNs z6x(Q5>32`nt9KAyw!%EZe~yNk-ecpU-Yy7Rj0DklwaMu7v8D-)>*<<7tBxbgI`bKd zCl&o;mdca&kJR72Xwl4tuUhpt%{uk!bvPkw*e@!2-LVkET9R=aYFN--UgXyE)l5+; z$P18mVIbYOn&B_{R#WlcS3skqRDIfEAc1`XI_x?4>aGXYh`}8y zyp^Hy>aH=^-GX6gZ#@f$@KTdA<(6XcP`uayMi|x9$Sbf)rGYuLkM%tE5h(W3t5z0aJ#g}>{Exe}fp;tUw9h-EPkK0!->X)e}=_ge$-f)U=Qj{oG zVYmE>vNK28!;hPExwMNJk?g0xQGcB=MF!w-Q;sQ;&!_%`ql{XGO8Jkjr)I!5Mf>sn zODczfo2O}d`G4o@9`JGz0YZ5soA3mTQZX|XU;9L zGpH%dtexpEvsCB_2mROuY#x{Qv9JyPxn^Ng)|;f9aVI9c|Gq8YWpeXr@;amYJ_E{1 zla<7msot9;IT_I3FZ~aLCN+5WN(ss;4K};Z$d@I1_0R1vZ~sFWR1&f|bXd3X57GbF zjAskRaA%{TU$sv%liu|H?`U%bzw6l-H>)!j0u#+w;s5eVW)i_S*o?z~hIk4CfvbMf zTK@=?m&N+EGj?Trdt07lb$Ru0IGO!7yQIYE1iGZHZ}4U7IFvbtEoNEHT}SA2N2$3p zNTxUA_0J?EGX{cx_Hys)`tYLv^Z4P>mn8f4LiFtD%p&I1oqAIrS}hI}(8F~ zD&em-q1!%oDhS#B!;%ZuGcRkkSr}f4f}eP$i!hth(%a+x&DADK6!~UPCIQZW=7ZrHtv!GP{-tB7AyX_`N(%hxTTRw|W~13xw1rjg^m?(~o1>hdIg`e@ zGhG;i^>!iMNd@l4!X)eHzdxn(zun1h~K@c+s9Cojx`oEZAM zsK1&?VLzk&>%(>+HW-j_Joi%S7J#K!*ZcSbkRE%A%uB{yLOyn4V&{fw{sp{QI z#{9Q{uB2|ei_PNjdD76lc-Ov;2BHb~%1HcYKNv9m zGhhB=L8Sjb7V}^K3;)Hf%k4_P^Zpye_ZHIq7#VFWaFrs-lQSPCc#`rk?n~jC!p>6cu?tlOErG)NF1b}>Z<2V zhD9n;!%AfZN3{!iN{71NaH+3DY>-50Va)-ExCDdpZ|8Fh8>)tx`nKjt`WQFf%_gU^4B5RJgDki#Pj835NFmO$y(V@~VEE`(r#NE1h~6hSCFHsOImw%(nl% zMeSYhZL`mj>pa)%W%FEH%m5CmBzJ}yMKy9_JbKUS=JQupX#3XZ`dP=RvCV3|&U7l5 zXYtdY-B^`;hEnYrj$FUZz^PSW?;ES~f)?kZ232;6a zH9NBrgj|$k&?98pSQ31ItXC{g5O?|t8EZM}%-r8>kVz0XlO4H{OrHG|t`Wi;vkQo; z?>!f|x%)B8vkL_($}M;Bfv;-;+#gI}V1G$h-UvOHl0NVv`Y<`zpALEsZ^HP8xkDK7 z+X$FyDiJz(W9z6C9Wy5u++z5I<(K=U_mZ5{m zGDDg$F!fY55z!Amcw!4pm?3-Tyy^J|TV9z#k6q15O5c0m7uywQ+^C%$1-|c9znrs- z?9jMi(+0I1I$W$D2Fm_AbI!Joyk4ek0(#!%Jf1lH&CQ{{{Ym9~8lw)hEw3;U`Vb%I zF-n9X?s=9rx%ULI1U-R_c@@DVnF+&)VWHS$(0yTR0a5fIpnc7E=!6cDlckwL{uRh< z1@Ytht3PyC_NL-d!sKt$p~-xGb8Lp1#pjvFDziqk0g|B~4%-2PmO<1AL+Y5EbsI=P zgaSTKg~_%kqd(p?n>f;%qijCVoTvI)c*TYaBPxG9$CF&YO3GiP)HsTv^rA0<_3n3~=pG$a3$bK!dP}Wk>Y_YFVnk9mI1UL=ga4(=5n6YPsr_$MQndJC{ z7ADH8sHhQgk3p@;WIxo$(gY5`7>{7TcEPXGs-p6)llnFL;Xl@OFDc{Tmecvw+EA#; zn_jRBFK=G(n?D{I7Jep^N(}mhXxde-@z)nMuUR^73Hx;pDgQwWBi z1zj3iwi%>C)EqA!`=f$vMQqm?VVDj`+#19ne0&TpTA(;yG~#!-Eis$-SmB($siX6K zY~Ng7o)oA;pO3tsFsz_d2p%%;z_1P;!QoXCmDV12)XR%iYj*N43QomSXEXfL5?jJH zS;0R*B6lhI`ATPmBr#a?rz5*v=e?Yk9m3J%=QJr6zU)`EBh4wku)x~%t#_`cWA{j~ zOl~Y*AkJsB=mpX8Nr_M@Fz_=7vs4hD%WryX@b7=@{+0$Z6ls=Bhj)EAN+ zN2`zC=~6E{`>P%OlU5D&|Bj0!xF>5jT2WxyHCnTvmcdDB3wgAwU5BVqjwh>#U+T+3 zz5;6k?=ahN@fZGtsCx!ZDZxwOnkcE*KChtz%dARa#t9E2q6cx;=x8JY8gwZ@_nbwdM7p&mZc=w!}+T(uTUCB$2Cc{mi z4=6-ZEC{2`k9A^&pT?Ei>+|WqSb=>{eRoC8P|UC(W_G`Ke@L?^$|F{JBh!G_X+a-} z^xUrj0-8h~+76FWsSJrTy2d!a5?+c*51)O{c5FX8FQ~Rf{RO7j`|<8GVJ1m6rDHL^bB~*WR)wt06CpgxWeM5T2Lq+~ZIr712h2;cPTJwXbNvJ$-K<1}PKG>?M*?Lh#wJ=S+wOjH z&LZZI$JNHKRsSF;sD;)3@$WkzfIdF9n~$FBc{IMp94yi6sAur)$NH!%ZbDy3xZ0{g zr0n28P$wz1FRSPr&O1iNkHn>xA2^u-o3K`%V4X)dc}fIh?(B>Exi2FN_1Jz z3S%VhB}+u2BJonj_ZEsL4`?V-1-uR3BMP5uOb5~wy$Mh`U&J{~CNS_x&#GaTq+Cer zr#U5Qusmwbp;be0T4UjolWSH&q`3Lu5k~BUtsH9bp6C7TN(HCp}?B~wFm@wkE zyaM8ceF?qDuUgQa21K6-2A#yeEVKxm}rXh2;BONVwN;@%_Su^TjuLACWB%! zZF$92Y{47cErCvN6Z=^Ey^0P|gv4R?KPuD^zRk*OAFTw&u|Ooe z3{K$pu=e46)>(9Z@4`}Ym^ppXR!m(uTJ=XBHT~!67(*r=dWka)gCZcjin7FVuvfDF zC`JhorgYkb**XA+R>_+b)E2C1gUY?t>DARfoIO}!cG1JkgLq1?2Q2l$4&zcyQy;Y=QP_3GIwWI(hZV#yF>Gxq~RiGC`{HEB}H< z;|Ry_cbv@m36Y=JqORUnaF@#dXwZ50md?>ZeKXV=6@!cxfJ4385zzo zw229{6Z4I`%K~iK$-Nz2e{iS#m~NM+o1S3(@fIT0g+s6b6MR;yyBh7Cj$f|5DE3$C8 zC(LH2#uo)6SU*y9;~n}Hz#wD zgwr(eq;BzEjPjmCFmfg;NDj6f>s)O_0#eC>P`^KDS=u9Q+=I^|tiqh&gFe~^) zkN6c%Kjg(D$SI`FXhgXWNbW(yfHz6om!VE?3#3jFSq>CId{`M`wHt!`oS8Ug@$B~M z3d@~Ujxj26(2C1ve|C{a$x|lHl3<6C$MVQ(5qNkbH2I@=w9z+2(qlXDARe6jcSSus zkZ*~jk9dAb;dpS4mD*O5Z9gZ-Ka~05(SfkIGcj<#w8CRZ-hj;PnG`ML1Gg;N=~z&# zuxMo>r^=Uf9Aoc;V?RqJ+R>6)v>dd#x@XS&gF1t8u7sqgv@K>?8GH@99HgC{L7xtJ zSvMXy4cVefG1cCr36xq6TB{et`fPZaqN)JCEbLL~fBQ9miumIToxxk~TFZ#l{?E%i zL!mQxmz)$68WU!`iv7L$u=dmh;Ty7<Ag>}uj^E_%bl#E_}-!GKGDCD z2gD5)$s-9K*>ds4ecPFcW)#lbXzWQRq@=mPt_i{k7~6XX7Nj0KhJ@Ae%B2z3w*FGi zM3ZMJf0O6BRe<|9eLj#xbAp2wsjX^w1zlTB>M3mv(26=%1`N^px4GoZEi_L=qHybDY5c}ZO7znAVKk4@UpW3 zg%n|9i2+GsV!D%wQKv&;YAEe?R${D;8jpkKx1S*PK%MjWdviuMDwsD=w(zS7xiDK7 z86uOOE)Oe zxus4U309sDkVMW&{a#47%Wx)rODQdf>O*wDF8NC^N$ z=N4j9X00W4;X>qJP==D0rXqmGy<1I^>aoyt>Q`{Qj7jL_Pr1m`i%jk8!V*dGK@pf@ zb)yCFNtGp$gCw}rfgKV7f%9?hHWq)#qJ}Ip%v^G-Fq0~kOd~obz9l(nlTKEYS(Jqr zHDPk%J-yd@!sLv?h)J*#{d`lfPvvBL`>Z6WNpp|$`@!$_GD~MEr*_0-U6J_rdKeGU z-{s@U5mvtTubGLN(oJPR04t4_LRt>}?aFsbeRqU(0U~M&tq55APOYMotK*o3bP|LG z4Hc@vz(zD1UNDAXg#4ofslY>+<}^s2}JT4>vxF%YEDyyR?Jm) z`zOSeR4*e|WT*;jSP4zFMtRxTK4scj%?kRxMpPv)x1>BWbzui?0#l*HqGv&6X8z?Gmjpbn4Dvkv}SGV8Gpsoz#k5(Q~0=HHRHvpB|5J&G;dcuSW?RULt zFrOO#ha^8QD&wB|m02vhKO_&l$W?#kra-(W`yZNhb4v-hj+Zkvuj^39B&q3z**j6r z8e{7l&B3KsqNb(Gb_oPQ^g3UJgn{lVv5udAMVcI@0;h`T?bR3ZolxV1XzIeNG4U0U zzc2DFpyS5V_j8gSR>j3V39U&THvSINmsoy1Cti=0zW-x%y*O}TQMjhH^>u%04QwM}J{pSCH? zG@WL*9Pfy$%bX8Z`=*`9_qWbVQz^|k?N$0eNSnLOcLmCGk7?w+uCK`-O>>aC;qP5eM z-iEgN<~i9Xjg^u$)0ae+knFnyZxZ6(u*?UO3?+4Q6UkC+F0P70@zTR~m1GN=YCS;>&2d@qLhP$K_e|egP$#C9#(Qa<1GM zpx1*SQrBoo=?yX1W_IVoBBtKEMb7BBm-e<|t&n(8hpHe4fW@sHHg>a^N@(z$IR=a& z<21kCUL=VlKEMB=CMBzKZ`|x5F>|}aK$O6{q8i{~HLt@%VuiP$PGWN|8B>-P8~zIz z^@lbiXY<$mlJ7D@qzbQ^qMGKPvN0@kMYYeP_Wb3gY!OoGb6OGpMcm0Ai5m;_g}8b= z-5Xhf#Fh9qxEDNnrk&NbbzRX6S$!aV~zE)4EB?TJQ9YW7{(?7^PT+ZGApb(xzxkk z!r6%xlUt7}gZ9HpK6eSUid$MaTG-w$ZdTPkvi%rVMru1|W=C?wTLPdlA7t<{s8m@L z45ha$T();n!vTlRRCsmZ5VR0T{*@3^Yia9 zdK=q+W!H&~*Ff^ZQ6NbHX)NASP*!U+Q!bF9odbC6p}Q@p%4C1WW`7aZuK=}27se%i zS8xXB{n=Ynmz$~L3kKs1=rd#ycuE}$q6;9{l0kM zOPO%{WF!FG8==5(;T}?YLy+PrXF0&1dbijjLhL}O+ zB=85Qk??9LQj|kTo#E{kR03HiQpB4b9#HY%ZND6$Wa8?NUTN$lL&eqQeZmOYW-d7n zyPQasv0>{-v2vaH#-0(ZscPC%sNb|K_>D0tKFifx10@=bAkKV1bF!2D+^M%Wur{ z`R`~1-5i`H+zn)bBc!AK# z&+h>&DguNH%s01eWdLN-#yHemf?tYBgnIFk6P6sHqGdG+HkFVhe?Z3f1coaX&UPq# z7v|u>;hkosV{E+jlzG1h*f$3UPN~VqJLwlc}Mb^^kxUoNd6bStx_1SLFJ?3_{C5Agc zN=Tp_l1i5}=>Y&MA*h}jFz_~|Z+9}8kh+i31K`ch<4Ay<35O0_wby&WaI~Lm^m&so zfwoe~R!kLq10qL+MlK>{ioULKA0lXz#U# z$kL8Rzz|z8Jy@X@*6@8)8LaNF#`Z^$n>UK05&?k4ZpL@Q2uq&Cc>8M_E$vPIKCNmy zb}y2Nw)tWU>lnZRu3*v@ckV1G!vdg4T^cPbA0}8=oQSN*yuCFmKv{GrL5k*fFxwrn zZlOkIwL1LhAd4zy@P%xUH2)VXly0@7Ff;}i%o{Nnu=RMXoF&hX(0IJKw?`(R#qKE; zxIf|8;UZ(4R29WID35Dy`oW163szpz-yf{F!Bq}f2CA_TIYq@#Rq|F|xf6Zex00U` z!u9^W#>6roq)WGVT!ndBwMbQfZ-xEY-7Ate%CJ7}jpCwvnH{PCM2m~V|{1tdD6 z&%E6g44DZf&f&hJ0hLFjeS_853D(ba;wD9Rm*%RADaKeS-XKL6-)j}U?i4V z=t`c&!N7N@S5KPgE zjY0bp`D6>0$AviC0-u0wqfy#VZh>Dv6RIs_KUSRZeM5)(5hsV%SSFu{GxPX>= zMleI=r&2J+lZeTE4)%k;>+3125t`h2cRIA9=@oABjIc?^?%~DL-u2LF53F@`j%v%~ zpi0T3LksQjobD~StxDJwZ20O~w*#y}w0$^}9_tE1Y~ZUIkc4&GS003n7`2O?ZAa($ zCGBs=9Fv0VTAIthl`@$~P(&n&-ezokDBt(!aAMDKCmFDnqYYF=?Pz%)Nfm0O{zFdb zgBAM%2`9xb3hD?|XL08Nk|pQ-X;nZTE*+bb6kIO~ZWW22%$bqP^wRTvb!8?>0Vb;^ zXSd*P(6ebndN?h&^ZCwpz!>efU4wRruQ3QY}Ly$SFzwd$hkv zECf#1?zT_x+Pe{^;0MHHnuz)3=4>g{4#nN-w!u0Q(o;T}*b<5J=4rgu?2V0gTZiK{ zMe1(U%5u2*W@7qy{jroyu8`LS^URcW1=aW=Ct6TlmQ*;I4WYPGsp$tKL*Sb3pfv~G zbAB%fNT+IJIoZLDNp+J#h&v7@CRnt?!u+aJdR8TIRM!Yshc_QqXk7fYQfX~a8g+t9 z7B##|$tX9AFIX{!Bp8)%O%yqi7n<=g+MLHN;{VnHoE}UGH7q!X)_wV<>Eo59OE(}m zc7c6kxu83M0t@PF1Rm$o_I8w#BUig0N=2!sD}6_tVyIBNBan3{p;uVb59i7TrYptp z)~8K01nJx)rE!k_ig4DV9|}%Im?FJN4(?(D(@*whW8#pP{}w>J8mc>VthUiJs2uV< zG-6;1peXnd#}XtLP`zLh>VC`MmtRG|MmKD^TSu4ZFn&w&Zp!`73SNMTZ+0lLX$3!L zIk9R}MR?|&8#Bog>g$wQ*u{=wb5dz{Q2s29fEE9 z`(*pXfL;aZzh<0g^%_$eoS0k`WG_eG*}3%VbbLnZQb(-@j8QeiJv&?|su8KZKSr`` zKbs^$y=DArR|L~A#>xjnlSt+1L*IgS6O@G8qReJsDR(C=;rBUVbN=LhI;<>x*tA3V z3@P+ANp}Npe!JSe`Sc|p!E3uOz;vOeyFnwN&b%+iBz?DW_qW^Dpm#=ym8we^POtwg zS)z4SQG=5XaJ*q1`ME1se|~ zHJ0=?+SQ_M-4U=v8Wf#nt03WaY-)+l{5&&+vEwRC)@V2$T7)q`|ICBKH9+zXbOkHf zSjZ|~moKdV08y#rxjUg2oX|Y_>qog3#yS zh~S8j8=X{x0NMe=F=n?6aI?Z)M<_cckYg=M zp~=as3%xrpf;7E=hiRfGbg9FTOn=(??GBUD72vOENQF`~OG$ttmha{d@&IuaeKZ z=k2AJUK*B=4grY|&C(%V0FEfqfJ$4-y^=f(Ev`w#t{H*0z9#($B`53t|La zx9>8jY8Tmm5x@CVlrZ}TntW&OdYjhnKt<9{bx9- zHyCQy&r_+E=R;Vpc-|A*BCrx%;T&6Q?VzkYWiYO-M z9|~1BAOCev=&%4MHWA{DyY6}Ro_o^|1h~eT?Z2hq;qRZaUBCKCe!3%O)EJlYE^Y+B z_i(_#7s5!+(Rg!CRaEkN8?#W;BA#Plh|B5R`R?Q3n2}vW+GwSX?XK%*)E7Q4+^Z&D zKo#v1MHaFjSeii$ls@}j z^ZNYUIKA0noLKdp5k`PImJn-$;4E{PBw-~=Y0zqD_E)y0?*FpjP_)W1$I%cMYA0Zp zH~$+?0@U^Q^`DHtwCAnZF?*z@f1Pu>5I?m&0h`CjSg z$L)6{_44x$CRn@!55*4i)z8K$q)=S2;^FHI$y>4p?W&`8z3;(wXq6hCG;(;GW$Bf{ z2&vkw^VRsUT0P?t4^zSLyJ?7|i8ufytsY5&#Dozh^JALkF%4N6&5c4p<7aN~+`;~z zMf->3lsabxnRt-~Cn2_Y>aYiQBRwgiTmSsW$3>s!9g#R@hqGuox&u%^II^HW*Cy*; zJ)VAp{Jl8E46}4(v69MveziWeJOx<$*9P@Dsq)IRkviA^gzN^48mc@Tt(!W>#+%-^ z&XJ<%?(h+GP~;}E2hd|jl@Ft9zs;&pF4Rh-N~EGYdAQwk$L0_TPq9qCa;-nqi%WK3 z#S4Mdz9bc7PFSMnkra#4z-eQ`qUxUtAL<$Kt`k~whUW9?;#+Gz;cIA$y`m^ zRDEHbFaUHV+{#R4A3lb+@HQA2j@4}SC=s=F?8mSY49>D59bA@{>wa;`)Jd}L6-Uxf zZE3<72Zy~9`q6Zr@aJdUv^gvS5*}BluKuQiBzL*c5gs!h&6@YMAhmzIs@Jo)08;Fq zx$Uo+bT%-1sGQ^|JbZH4wKd^?Ljz(~Zp8=dx_hlJ5={KAsLkuwUK6FW_5J;0Z!>sV z7z~p9yL~yxTB7&D>7s1I!*LCvC>w#~4fQqQ^s*@42kxVa&T zJ?}7VUbR#J8*!xmo7r#qZQ3zt=@JQAd>q zGv1L6FyV8MO5XqbW-|Jyed#kBMwU?8du(bzK%H82R@~U)cWJDGg)qDSQ-&v|KWQjC zLg{XqC?C5}PE5&)6VHlKw<7RUJ8+6Vv|ifMHvbp3LSJ}d~Py|WCsnKG_-e^J?K27;>UbU_==-x zLR9H%lEaqNRJmg8RJ7rxAjoz+bIgu+kpZr5>c;rDs>HL}Po0nIgl84E;;_ehld)t; zR~q(8UM1#>Li^oV$QN`4ZPe8s<@c_`p9kDP6g2}X1?+fsISe#Cna=TA>~`_OCbVXT zlt=1nsvHy$P-Ig=qaDrlqQnVw3@y+1Z+{};Va1>u@I1Wfwk~0wN!p_JF}Z^`pe|e= zX;m&4Qb0{$l@3pFSw|%X^D91vo2SIf4$8|ICnrY}mQx9R#g$!ildN7Bmsy(8t%qO~ z8iK)PonawD8F|eWC5q6kU17)e>lc=>0=j^oU-G{&hNVyR0G+HPM0+a05DqrisynVD zVjk{{b^3&KI>6o!e}7-@$Cb!AsZ`}iOt<>BDPfQFk~GfKxJF(k;R-|#Z`~Ws`#jPB zbGl+L6iLp@Y4#9PCFj^fs-=4qyS96QGbCt+o3n+47~#fSTq9M^OtOJ8D}Hy;TjouV zv+!QVytxvrhQ2p^z%O8VJaBo-1b%K9`g+}A=3(WL1Jjx%NdD3-b-(4)?nm(ClgXpb zI{xEjk42MMr{QF8{>kEFiT0y<9(eBQ-gbMmGKAqLS1yV(Eq^(%xJ;C0z#aiEk|`qd2(}qZKF%7uArpgS zG~kgr?+lZBohFjKfefoG(-myCadO#i#cp~o8>D$Ww~I$Kcph(ps*Py`RFL;SLo{)K zjp>oe!{pDXI1#vg5-C<;ENBPNRG)GijvPry$FF>kI=pUc({i%`>kRH zE58oFY6PTJNAWk%SxW3J<;Pf$)S0~D0sr$Q!>>&lFx$MZGTa)x_0ksOf}o)-Qqh5 zPpRr`&9mX#;$Kjkw|~!OfQCg&V4}QGUr!}Gwvg-GnXT9VA)`C=q}mG9J+pzNyL&u= zGAwqi1NLW=X(Y0@v`1#jv#-45$QJox(W&6q7VzG+giL*tKKX!mV9?_(j~UE+(7gTRKtbXH_1s}Mrynu zp5Xj^;aX7KV-2azj@PBPu84h=ZA9f()hjHHwei_^#~1Zf^c@tDcgEFOakHO1p+JM8 zjSlDx3si3)6yWeQ)?}^%x1C|+e@4t<6yjY=J;~7a&76%E;fSpsnxO zklrgAE`r($yG8ry;0SC|*Q;`eGX-cKd-Xj1p5=T{b|nrznz*O0X(6{5FCqwIRosq1*NULR5r}~}^q|a$?)Y#shtV$$%RIQlC=RYi@2T-f$ zrJhb*s)A>0ndFb2#0s|q}be8JuoIyIoTiPMi^VQQn^J`we8rR~2 zXL?xME*H}o_bDFS%Yggz2ie-rCGq`41P~tgw17K5k9`TKEU|OS!vAAB1IFLO!`9RG z*&@Jg$vCy8`nS?5UaZWXz$|oXqLcK9y+U@{i|MPX1OG(u1}DeP7k2rF^(%ZF+BYpF z;!-owI_LX;yPUWF>J6(#7rMoYp)SQWvDx?rJS{!s{J427Lb{V+nhw|^eP;fSQWntf z=uo3bOLO2?YP=-=M0b{ZXdBeyrvH6)H~47->$!yHu1OC{{u;aXsk~Xs>rS(kP3j|hb?g&;a=f7jjE@L4) zW4t*LFMShU(?4Q(_uH4A{paa1D=bSsWLWENuRbFFbj6|fN7uuDe$)HcB(;5C6c+;8 zuW!z^=N^{f()oGU=f#~}E{|a6m$U5JL&H!p1_+J5qV_4G=gj`$rrfrqczpQMoAN0D zwHcSKyPS1|W!1O!tq{3QMMd#0Tb^@yq8S;TYI)kceeAC(dFDa&=_mWFdgFb{5YpvY z{`Rl=?E9GJ%0{he^8~>DWXc0Y)%!EpL{^{WxYjfd_Pfe46P^9#L{nOfb{n*K+V%i9 zBZRd@{|;&ukbPRK^7{m`DzEqWn2`ZVjGoiY={8jzc5B6#7gVOI`4;S@LRQ zH6mGX>ZNim6@W8D`Sfz?y)Fwu3K&@=Br!|K;?js_c*C;j(!thW9pUWOwH#e;o zB%m4SlAT9y!moXR(=)V#a-&`r3^kKg(cU#9Ma=O;}I0)>MSKBvr!hbSD>pvQEv^!KdwFVFu>lje6-W)kTdT)%9016_H z5=2DVuWA}YuhFe&KeDkj99W6-m&~G&9l=#@M`9*UKCx7_*DEI@f;**RHb?IkUcFXhl=QR~wk_JmT5)hdLb6A-N)`z`@l- zTI+wA#YuV<*IpqHzr3@Q2!t_2ic9%RhCynnV)e&I18vBk4mj(%?RIqf)Xc1N6p$#i zTu4(+-hg%xE2;n4S_@}T_Jcjwrm^JiScrm>2g%h4L!R~U(LkYALXMWlclg1^+O z0W4EZbN6gnPX_`-c}^@k zh-=NA-8Ju1<0KtP#gcZvODIO^ME!yW10kft0yx{>Z{=$GX!q<9`0qNe{PyZ#`gae+ zQNN1Wd!BFzVB6g)2-CR}&|vho|72CYRV)-))}NRs=)o5GbP)Hda)s=dFm}OCq-sJH zi&Bp}UQO(^JOIYXF`3c3;b3Bf)n@#D>d6A-#HAPAhT*1W>j$!{ek zd1NKI+(L2&Vb<$6?S{F8FY&UpYsN}Vy=?J{NY8S(Hm^lHu1}w7~;K(9fs%+D(@ZoQyNb^YI}S7%-|?9 zFSRV^Y)>Yl(?e_sN>n)Vb%8t~UoT|-1uJEog?_*UUiRg}4;503>b(P{z;zj|nsJ86 zoBH;`0)+pe7+ISD8)V(i0EbNx!P?CWxF3N5ichGJ>BPfQpP_ROy?d_vOx!8VJt#RV=zP0J{u zoVE;-31)s$87@^vYks4QiL>fM>J`h?>>Rgn0}g<7dt=R2)TR`cxL-g2k1MqcfL|=I zOMJ|5$>XF0_P;8YACfG>p0bob5)`gTZR#Kh`W`kqy*ftEXEQ@*i(W+C!snDBobb8S z@-N8dhz47*R>1UEeClJ0`-?{&LWYTf2b%NjP|*|}SsM(-T^xBp_LM8Vv_c-ZCr3FF zVU9+Qj46k!g2peQ^Y8s>y)6PBemz?&`Z1edPCJCwCf18KknMqyWR+M%kmem^MpL zp(IT3m`n^1BtTx+c*CMVU}@_B=8A6&B|%A;^2CYL3nTm>>C8TMRa@D=blKh4s zW{72Z$t2Vgw1Z72N_2MGz$jbPSFq3hL^ z;lN-(Ib8WfP<|LY=+m=?khDz-=c9YjZITk_OdSGshNzzIZFjc>ei~l?ca`Tjy;|%D z6YP0Pz-gTmJ&aq>sFy3RS4(Uhmkr54+`U>Vi;m17L$VVqhnVoS`?6F)m|dR8L0fJU z9}Yu)s-j7H!q|NbCeg((;|Mm=tFIQlB6wO_TkP!fXE}*CXD{*v=`kR7-7Qh2w5`@b zF!arkmR;EyLdP{vRtsTBP*D1Rsxdf?4h8@Ed>%7uy&sY8Q6v})=)*w@l5*qQ&5v%$ z5ApsFzYScyR@!g9V&;ylq`vzV`IS~Q(jWOeSNN4b+aZ+eN|*bFX<3e!e|rub5HO8? z`aPhLi&h%7sGy5?ie1I=hj~T6b3%{T1ZKJ27TVcn=28I{4Be|QbA7t|Bb?ky7R*a; z|6DWq0={_*nU(x&y-@vjxe(zT!HB_t(U3Hx8oxB3AloDA-zAO%ku}XwQQ}A>3xIST zsKn&_BwlsJ{k64n>au$8bxOGEgGY?d7^H)vf#m;%fdoautM>@PD+r==AiWJn* zM^AWZVV!{}4nfBT^<6eM6{1)J&KXzOv^L=a!Hcm=+S^0w9I4wvP>KSBu2B^lJ73hB zXxPAFtO`RbvBk`LmEz9k!>Ov?ir>OcFM)!yy;h|c0BhQc{e zurE4dbG%Lq$V@A+0uu(H?j(P8RW^GLKm*W<7;|=IEZNQWArvaRjrQIXftUIzLl=sH zb8Y|a+=Je{5~MpACobi&D9n!Fr8`(9>qe!qdd5uL8yJGbvE$_X^k;&~aKYH~!u-5}b4f>TDW@{v;8-G)XwNy-#=7w;h5$slyTY#W^ z?fc!-+JEA!Ui<7l&Xi^XF+@qqjTm2-xP0`uzp;crj@( zqJYO;MfM6BL-fgs$(<0H>!+XEgC4J%ITYpkQ;>?^@IZZj>Y5Aao{jfHaan?2GV`g3Miado3IQp~ zg+8t0;bflEb88NrnA{Lm)wedJ8Fc{TO95 zv%V6vv@R_GBm-WnNDR?|8rlJtBaP#Ux|u=NjIr=dZL8P^jX6|=9_Xv@tRGwMd`g3v z)7th+e%E=*n4Ku1xgQl@BF<(-S!Cgtk*_@C*=gyi@Rw@bjNxzI%y|BhogUbH{n{W^ zA{HgU)54Wb0bay)*n~|F+AcYDU_#WrKW*TrOJzt&l}n>8){cp`gYM~Lo#pn%k9)Cm zm{o??s^K@86n^E7VImkLVo+yhq-BUhubU2#PEaYVSfoe$rv~E?{s>^g!>U3TGHCT& zcBGd0hp_A|^Lc^Y?BfHKm8y|%6vV(<$~kdg)uX5?qRIaP_0+%AM>6)+=h{{62Il|4 zu6b>Mq^tXK{X->KggC-2L}25L0n8hxHAjwg1uqzM8JbY<`MN4GW*gD2Z)Pe&`vIJ= z?n60VJ^7$qY@H)4|Alb2rqrVffP0xbWC!Ill-TN)GO>TzmHd<^yezrPr9tg26&@r( ze=0$DvdxQR%t{-=4QJkkg?n@r{e*he=CviL&}#_5&RJj5dk#Jlp1tQEb2#MNDLE}+ zOBq18_%NoZ6a8h6O-Lt4+TfAQ5o0&#C+N`A*6fuv1|*oYhV=;!VdJ%zbA@@R}ekXJdCnPYJ(O7)BlT8GU6fTI*-bjQf-h;(9QHDEFJhCK$+ z;o_pzX7wk7pNqVhqPsaS!+Cso_Ub!AzR8OBXJz7!?=Q>apWUtc*NXpwU8a$IlUs?pizQ-SZ8 zfc4*k0=U>r+9c4@({HXHGd2hFo_C#6|Fjw#D|CKJO?Xos_H}-+IAIauZqDv8~BAdRiEgGVmK6F63377e|JWqNZ*%`rC&eeqego9P!zV zu0Fy3!c37WNPUL5lt%I6mzM=2K1h0T;IdO-oJyF8=KWs61tNqZur%p;*ln~cHfu}& zNv=X#`ohTbjM8LVk#_MBMMZ2BO}avkqgP47Q36EYzmspwI~Cvjq2>s16!VqDjT7nX z??YjwBeTRf75O2Rqrb4od9$q^JDznpbb*tM01Zh)!VhAZ`ldbwWi7&?)!EOSSvr$6 z{qo+6--LO!eJ5v7Ux09Mw0P?9)~nMRMxSL<9oD4l4P6ko`H&~CY*Osqh{iBFI!Q}# z2?3ZQ$0F}0<`I1S_jwdkcY#L0UN#=9S0Gn4lu7Pb?D&W*LxYp`he&*3#X3Sx&u6xY zBbxrFAHhC57+jwFQ8imQ2;Doyn)yr5k}YBqHaW6+Z|`VQ(l|${{1w8|+RWX}#GNJI zCQJJ3Bh!BajMcW=p$dA`lyV8OAGQ?%nHE#q@ZqE8c+7>njad1vWII_c)aplbXM5W# z#@dN>w1Hkq9q=r&zA1~3H%{0KCGhL-h(cBx3hd%9nDhyK%G6WOIdM7Ump|XE;G8n; zy=IKTx&VnE@`gZw!7zW`;%Uvns@ST6&sUpGw7%~SQ)VnNM77lDfTeM;Ex>vX2^5I1 zfrYDehyns>XeZzLc9|z}(39TfQ)ukFj5g)>^r6!5#gDnkA}C#U`P4=>@KU}{OO>_h zdiG$N|L}9f|H8qp7BP0wKB=>n+wp9K%i9?Z<`#PMRMDLoS?HlI|Hho z-k%53a+CpRKHaNYKdwN6v)gCO8|NFWl?kB zQ(`vh%xL&A)xkh(%?xM>DW0tgb1{xbYLQGQs3*~ZhNzCzIn_!ry=-L0150e6@5#=? z94ZG-8hVN(K26D;TRy|*+k-AR_i2wFETC%PfQWR9L?9QUC{-=#twLuZA1BIV0&B^| zeOMY80qAW0UmnCd5*kjlyEpjKo{R37=wlgK#f~V3SMC<;YuRxI9&XlGCL|wXg;i!N zs7T0*P0jQuE(~o#n5UGm?jtWC8M1V{Kg6&q!Qwt@x!~NU6yjXZ@xXgQ(By7x$htF2 zL_TRXOF7&KWO(S8I8$akTNtaA&;bOc{O}4&j>-0OFCc`@+$I0U$#*fmA@!(3A4PGU z@X_y)EtVa5Gto&36c>jzT$kH;v)OFDD}i28JWQM9YAr6WctzB=*t4bba4LBf&}uJS zEN_9g!jpr` z9bjKG_hRhIV8y1X*mm5kGsFV|Vd0=t<|Ov8H2k*75i~pfdG(Vz_-oX)PFDgwJ;_DT zn4HZYk^L=f2W@cnzjo!=Ji1hf&gM5Bie@u6T5ky(-#Np(meb>zbLS!7YUt>^TaR(Y)5@7YXpbbQ=V!)4YvG}o$l6^Jzb zO98VNr@VA5_G&UiQBXr0eKRCc1aq-&Ikf4s`zWkF%}yWnSd52Tu@@}s%>F?jh8NoR z-Q)BZEnMmeB%^c)Y%r_b`DbJr$RQNc=yIlYR;;ZnXnVN)FRYdh35-#{YP<_#)5ztG zcy>M9=8JXu^RAr|-yA?5Qf48tW&-b2eDf{qJsdp8X(wLlh5t#O6tg9*7pyD93^b(y z)^(uKx>Kzb%33J6BiFHK_u>?J7(G^+Y;(9Q} zJMM3I?2S(EO1)4(&a1L^v0;#i%WrC2;KFxrs(Uu7AOmsv)@!9l9Z*Y<=)D<9roVe2RZ!`;m4Ad>HTpD$(7 z2V~UL4Bs*wiZtx{7AZ|t!1a9COV(*{Gf=G`>=}xWuy9YV#_Uqa>}g*H`%xt-6z~AB zAdj^>jfKGaQjqq{y^E_WAO3*}CXcnNYjjV&fOxv!_go1CRBD+<~bjotz)s` zsGBMYzFpw3$UBzie4(@%xdKr-uNQB)zh)|vhs`gD$G6tC!ywoZNNixgbk5yNiq`as zD~h;`jlX&h24@>h#Y%2GKsi{T6A`~oEq4e2nCZ=_*Y>%!MWDn7V8cXuOKuEYUeKdC z<^~n}@k^qFTW&5&^Jt{)pD8X5ih7g(eJ<%u88|?lS?+Z~iAPo!Lspl%If?mtm1GDl zc?BrJ+xF(U^lgY++-$dtb0-eq1T)Ld&6a8KyH=2+U-oHNQl&7944T9z6iV2T5WT1< zy|HPQFK)?{j1$1Sy_(G-EBzq|j=|(ZN4K@m=ESm)C~fKX1jmFEv3|CRB+%@|YIf4v zuV)UCCPr!G%Rp8HllNH1!BQ9jA;XtIOma*6!$c0;g5AAj)0-#@zin#C(orjtvW+wG zE%ldd(9a(>wIn_F+rtE!W|++@AuplmJ#m#r4(%J#TmNiBIwim6LoqFg?IlTT*~PWqqdocf1tvl) z>fE|){Z;3A#1^GCDZ_Now~mBK4hCk zlgbv~T4f|Nk(xL_k76SFtb5`9P4937{uuMO#8gb3niHd9vTTZ)+$Ay6fUJBB)BzXR zx-=mjp`Zb6zFCBfd4jE{?QZKYIZ`A&qDV!^-IzE+fFgImC1$ur_{_Sj>mb7YU-t2u zx3BM>qiP@fuwSY>O0Ll!24f}U7g$H>T}e0dH-@gwS_XUDiZ0GQCOl#j)hX~Vr29`& z4crB`ITV!WWzQ$Xy_P>p+IbS2fzwVtvghDn>7y&HtHa>rUcxJgLiTQ0$-2bG!CXI8 z!OVN#Digo(Ay?HP8%J;M9xAQZkv^ z0p6vOIHndg;tsRS4UTu{KL++S_#H7lvaJ{rgsw`-Y%R2qf^V5bMXK|QWmnz6JB$Wb z{EeGvK}kf4HZklv0j7jJ8kwKO#yq5!#x}SLs{XPy(M&TqB+E5*CFn5P!iIkSk`e1< zlXduV-qg~)zJ^FMhp9nqLbWr6qIN-D@+7CCh<_QHt^4Y1v-k4Y|G9ts*kZT(sm`ur z|BzMu+Yq%-{Na_h=*;4Rfb7o(NF@x9C)Bi1~$ zaGSCXs~HNI4l0cX?o;l?tH%x~z1cC>%F}R&K;HcL$$>!=gCdP?LCaVvon%@W4xv-Q zC->c(z?_*BeAACPr#8{ne<^4DB+3rHt2?>A3ua}UX#@O3w`C6?n zAbmx(&OQ9g#YR&Vjs;c(sSWl8ESX-41)-Kt|Bv*?}0Ttxz!$B&K#!ot-C+ zN!Jpea7}5(v+rfi`riQ4ka>OLE60!!WF1koLvaK&Ddw7P0DQCv)xr69kxKMUaaL`G zvN%Ku@}i>urux}6esi3G(scmV>|7~RN!MXDUV?lVQ0!dD)N`0H{I3-rLJ>nH*mXkp zs(z3u7AV*452yO+^rUhcalbklCe!Guj`?15F7V&JkZ^Uw z%jf~7r&D|3=Qrxz@duhKvQ-~Prb@wgOc-b&_u~x-hG}Sh;st%^W_Dj_BM~QExnfBD zRN&Twh_6b{d%=)(m{R)fv-N#zG?^|@TtM|r%fL9YR!oPT)6}YUt&=S_keTt#8&yN? zQRkru0kaI-V!r>Bg5POpDghM;fXd(yaRmRrN-d=f*feu)4D&Ft@1To@69bPp6xWVP zgl*X!8)6#Y?IUX(ev%(GZ(!H=*E+u8FhUJ{-r>!#BdY$6lW#lwB@`FVOmWV*nzwlYJ@cMS!U+p65aJybI08OA+SnA<=|w16sW#-wPt0s?1IQz zr+dy*AJ^^qaGL4=Jz}*=kn?KHILeMV&$lZ|jbw)g#iJ2wucW*ZBH8eMLuVs+; z^`PjO*x;c7RlEV3%L*x%V#rr!W+Hm_Y&jbV6|irMwwRwyfG?2^UsMOdH^#t&zt1&xzB;0I`>^^0{Wvd1MK z4`bb!eOUUZnzoB@btq4zCG) z>^nOe3_iUU{w`DY0`yLw(yQ_qr=G%>JUftB=Xq4uxE!9u5h7dxEqazfMW z6+}ZI79NJu;K0X2K=6d3C1NIOa*G*^6Gm}fq$x@9Y^6iy@pdoVmG%*y<(7}>54@Q% zl_gF7%f1e7w%9God9RJrhg~vIS3^aMRy>#vI)EH~&mL0O81i2VX0jIhLZAGX8b5=rzD*b10uwlM^OaGRHn-9_D|Gb)~HG z#BqG-f{^%TN{{hPBRqJ?vpk+Q%=bEnGbMdRa%rX`!H$%p*d~e@(;WXu1BIL077dU1 zC(^j~z2Ri;WrQ0Z_6yQ!L$j5>FAszM|2(&Ej7#KXdFq^N>XA+AguN)p**|}e5NOhw z1kH`+RC!88madpG^I8jHIVX*Fv}`}FEHJ?aM`Le&Jd#V{_zW!)b`q}q2|BXhz@3kN z%{&t}gEL8I(Gj9jW7MA0^-b)XT{Bg7M6Q>IsubF~1aw^TP@F$e!$B~%?-<`jd@^kR z-IU7~`@Py{b3TXpw6whn|7t#Ame9xkJlYIe8}musFKts)=h8(0>SVR(CZQg_EDIVJ z2Rmz9TjlAVqYZb9bwZvkS6gexfy9yq=ba@(QDsaOG$hJ9c6+@Ris!y2R4%H z>CqSzv5nn1i!6N{4_+@QHaG6!e@kfRHEZw^n*Tl_RJ~_6)juC0piZ3T^=BM~bBk0j z7Of>PS@HFWH5Q#)4vaoWu1OxGl(#kw7FpSeZ^m96))jlh)*t~?o_FACV%y5 z(Lk7JlBtrJ1_l;pLAJG~g6JrL!;pdA^6HkFKUP#pMP~v*Iyd(K|9;RVMusu>aMcN* z1FO3RGuz54m4rKGt}1Aw)7oUe-5~MPKKf6uzQ-)1&WuQQy^~HOku_=2t*;S2E+!3@ zSFwP_oC7kL{-Ot~Vr%hFq+&9%`D1i6w`@^=Xq@35+in1P3!mx_1rr?=-%I>xj4*WH z(6T)a4X6}bcx@lC6b0N&OBcNo1?(uC&_}GGJcNtIfkAhIq>LB1Nb0qlS!q&a4_PS& zTPX**(ut7|;l_)k?VH_dMi&V+_HN`@`8_C?e>IngDm+djI-@yT?G<^?^jv{ga0IQu zrn;*|e9g^jT`*CHo`*IiEkg+Z{G!JUrb}7O#YtNJK|ul11KYuA#Uhf}y@z2-S&f}M zac4|Cr8Q;Qqb^pr03OcRj(6@y)RSJCQEFo!knx1e{Pp9={0nmr9R}se=QMx-3 zy(Z3-cD_iyb~l$l?oupybp2muSSQ0AdrHiM(lY`E z)eD>?_lhaEXvnawh+alUR3)xqJnN_{A(|+5Cnm2fWVNjvR$ZZ zH6aS<@!?V3kb@FLiO-mDroX zwvu15ezpgYf}B3NS~BFy@`{x$-u&AYe$8IaYZcln^~pIHW>^dsPoWuf4A3(fNYisUqHGA8WdwT|6^)E#7$54RrEFTS& zT=NkPH^)wEMT=k07t=j+J*i>{1JKGG0$j5@?~i>_(#e5Mb&8W|n^s1tE75K?misk= zjd=XSiA4fCUec$i&vcm7YJoC6wlm|zYtffOI8~w_w&|JI?%=ZQD|DrG4*Cm%067yI zMB|7%3>2r$t(t?b=>RfJ?Z=b3-(^4dM}xq;NORci*{X$AbC1B5R;lYCNq;um`#nRP z#jmUoWu>{_M8kNj@K6L$J7M6tuPwH34ND>R`gNPh8Lq5v1`QF^bB!OnSDAXe6IDi^ zOPTcH=4pGKC2GW3)0b$v^hLokFx4eyDj?OaQQzTJmDi>vklq$h(KIjao=pskC|bfl z9RO_6s~&J=C3CEs3VJ1(8%Y{fDNY+naC>C;YKj6gTk=l}zQR`d?*sHkjpBTirM{Xg zNS{ZOdHp1f>PFlnbvwj|+|xEftf-S}(@2f;hsr};I5`Pk4)ZmuJEs##2zW0ved-2e zjltIoJ5fJqsxMAKO+9|WM(X_d;HRC8q8yDFX3**t!!2PMUXoK0nB98#s4Ic9Vc7ER zxfYU6Mb*HHL5r#SLqbFOmt~qsMpxVL!RK~s2QOb7K;wF*K{+mJuF*1FS-VE4NC9J9 z>cAoPC`0WYmN=|aiHm8*D1(O~QT?W-Fp@5y76=*9Zz@rZD7}h+scw_F0P}ls3|~@~ zzHtINxmJCoP@Y)M$D9pu^d+c)gs;V>D|k_+yi|`ApnrU>JID*QjR?16&fcaQK=c12 zU?jA|&%V~0?xmb@6kGKH#*HzqzkS%b<|!%TC;T_k`OgG`qNmq*Ods|oh!0c6TV%pj zW42v;oWUEazcVMCl1b}TUS6X`I#UI?7W?F z=JzjcoN-Zhs@}|#3a!`0xe3eo4$W-{a#NrI17r#i0abyL_B!IT4N7}obU*}ED6A|F zu9R!)C$%f2Bkl0KFEPler*cIt7Jp*R#9ZQgYHH)L7q(6=^Z$+o;4cTz&9W-z8yv+ciHA&5-GUs%nfU10&p%ke=OTejcn}+bkq)GO8K*&vL+H z?I(%XhIW?o!S>atbn+v^X3Ht1!p=ryeo6?)15m4wQ4Yklsyoq018GWn|%8 z!MmE*J~0)OVnvCzDAdYl{w~6i(hy)A^K*pJ=-m^7K-8Ve@-n)jeThJhwlNniC$A!h z6PWY!Vzzeu<44t@((~nS6yXN69DUl>)#?!q^4V_5!6>qavHjz}c6aCWxA@#5cL)$8 zG?T=%GRl$6aqI0b;jiw@@pRhpx^qAg|UC5-7$=^^Vds?iYohYhq zQ6iECF)!7R{P~Zu1z+?V;9-x!9>{>-Bik1(C?vk{b5;n)t9)BXTV z?d=$p`I^v&>ipBD8)N5I!Ca{CbXFkce21vbAm$QD6=cXLDwVbeF`)wfZX5$AVo>% z?-{IHX0lz_Ss02#-0#YaOK2F!c&ze-MrOizJrZ|C@yk9O(uAaNix#N%MFvd+Retj+ z-3ySH?P3B*BZr)(W*;4E`0){wYl6-zR4UfxzgH|CleQX(2_&u9S{fOCg|( zX}+0+Odn#z`Q2{8$@5z}w{`NJQ1nx)B&v#-jC1Jk^&XjePKoa)5ofBuQPHbZsR)_W zTxXQ#adpR;BC%DtpC#tuvVJts6%@ojK+nFUBxDl0GR93p32-gXdJ~X@qv7efiTo@#es{im=RZhe@BEY?n=;w})Mxo;gcMSu@PM|VC(>keL%IgZrvg~{T z6|)ZsP^c5}&3JcR4L`~4NDzlAm zKJCjq3}vfob#TYkv?_2Jm4yimRx2YP_lB+f;2mq@Y&r=F6{_BYx{U0d*gYrXN7(OU89o729Z=JKYQ-s4?5V%9fyil)pLc z<`x{&v)IyzJkP$)UwjKEiTOE!Wnjfe!+vz6r&LJkLfjZWElHgh;2bL-A`+P(&-d}_ zIUW`cQV>ss!IW#whXSZ_+12hRyiQX`*4x)fj&WZkea=wm$0(Ntue4pev-xQIG~7P* zLOc=VwLF{lY5GU(BY@_zLM_T`pao+SNc~S&&O$oH_ZaA*yUC#`IKjFFm1a%gSjmzihTJ6L{c6n7CzeN7>VYhKJ=;x@{K(rRi#rTZ5gulg3xvH=idHd*c1a z%qp?G^1q^Rd>1ucEH@4Jc`Ja5TNRB^Emv61(RENgc>#Bh$9N19o}K%DEnRn5Q_a(j zpnyV%h=K?t3L-_MN-qHvRHTSfr58a3LX}Pu1wo|?ib$6xU5pgzMJdvICqR@gNhl_S zl;7q3zC2I<$<3bGGdp+hZgx&ax5-0KOx>exbA9Jw{KwhGvfP3fpzKr5hMvy#B1SB^ zT()KWUd&7nC4N7G@BMotWM5h0)FigEKNN;|%SYY&9C?~aEg`_jjLr%Qfc`CGzeE_# z`#Nrud|#k$QmWEeyn1NJg1}KU+O(g8PhA~-h7V_Kpj~hH+Acnl*9lk9#N4}IQ-x%* zNObQ%hqJc6t5LW5b7I2m{zD!2o^#|y@v}PrbWW%((rJgN3~>25-DXWd7Rub}^0gKk zJNSgHW`|4q)0YNW|xb^SasAn%r!(Bma!EMBUK&l#EqoiIh zn=J~HP6U+pvyKs}RLtlLK>Ep$JzE$Ry51Xp954EsC-h1a3fLlrld<$vY;$zey~w#^ z?|t6O$BT`Wv5Fp#mG!J{K7B?tUPeHSO14NSwZP_9w$Pv68HBSO9X5d4y>oJKYzyh; zZvE#wN=B1jtUW(FXtLI;NBgw486Jcu{yIQjzkD2RF(7)R5bBv)o=lZ`dzuL~U!1A{ zsDOd(^Ib^}-x`QP_6=R>HxM{AQIksO7(jTdAZu~bz-3wlhcAcr)wM?+TK|@!VI$UF zP1OR7*!UKpjsNp4q$UC{a>EcQoc;XK{$g( zBG2QIwL{biUn-)@-s!an1$-?M$QkEwt!G96c`Ft9d`71*>DYmR0bi=TBK=GeHw;O> z)O4_tt_&3F5F{_AJxdqwaiLeF7a1gjrPI~5TAQ>UY8{YqGmmc#Peg@?Ca0c@#V5a-7I z32S4G#>jqeo+TH$&}5YWstULH1Oc30-tO^aGQtqk!26MK2B8DeOEe~uG3g6iJ)4L4 zmM)-Z{Y=$D$Y%Kjb7=0dR9v7YQs(i};F8tHd5&FmFNUT7C&UIDW7h@HjgvlhU9xUF zG?^;Uhd^4r7yR($54k9mdioc8(O)!yrlzJ=2lN`M9^~!7t)A3&ghmGp5CEUy01iQf z#bf_bic?x>^;T9VV2?`oW^@{FsJ;idg&E%buu}6tcPu+5QKomdah}SABrmbLSg|-D ziNrtTU4FO+5>6#BF90PbL99I-fClagU~EW}9Gq24Tu)YhE!~9=pvo*fzTOTX&kSvQ zFj19Bg#Y|O$87@V1xEoG(5rUs!#p+qmzrc3ya_jmKz1yB0h82&2s!iQ_F}5{6#lYe z+aL0LJUr&h!3pU@D}_f`6hnh4W;0l^7x+9@`k1)}k50x%s__&R-DMAQVC-pJh1Q3Agmx}=1BVR;yEIl{$=6Oz)2?a}Eh_Z~w{w254SCkF~{%&ol|HC>0fes$f@}$hUp?J$(_l?>e*hE12W?J2N%HVY(3bK%iuoJ(YY0WY?6)1d1x&65(1RtOS}a z90Sq+V(H}?;e%Q2(zf+`I_U%;2xC!W?5BM>F_8VOE9Z@H#&0=;vkd>6DPZ(W+%jP* zx{}E7!h7iaE*X)bng_b8nMI{sQmoQv<()ru^3)lLOZ-VUU#X_o`I$!zp9Ht*ZW$tP z|A?$eGj!v*H1xwH#M~#9?KVKpms za{^z!O@Xql*8`7*L6v6%q)f(W8M8TUVI2YngB>3gAQin++#(Jy#lfoL)Q+ ze^t+NKX0^%=o}L>^HQfR@MVqXk~`*v{{G>O?2v-{Jx`t=B=xvtRGrd%P;5E_O|+nG zZ$QkD#j4T6KVj>Y-1RE6`mPTDjMLK|PBa@@27RP)nZhrGy9R*P27h`l|& zZ{nx_c!3SAN#2I>iJKOtEsVWrG==Yv$BCa#Rn3{; zuJ9rQV~;Cuu19WVZ~u7eIgW4EjDvxpjI;-u{jQpz{QKf=!}*c4(SBw4c!C}_67x5| z!rnc-nxJ2>T#{6qj5lj(pxgk-sH?|bOUKnA-T4waZ&93tm>Q5Sh6VwL-F;pY&7^5UDqOZGBKEp|3{6mb?I{1E1Zz)m1xuC@lPBNyYxyFW=LJe8jC4Xj~QztCq z=H@njnzOUE^E-~?o9L&zkZ6izUBf$xUvMmI!bUukHO5viKEg?0j%6ba4ozsFoo?tyAWRT|4V*Hv4(wq8S9OYsKQbXWV~(eI}Ju z?F|?H448+fkPX9mBP}JPz^|hPx^{a+QTQj^Vkp#B`*7aAr`GR(5S!rs8AV}dr-AmQ z*P`4~ue2u_YvkF(kaAcjxd%^mok=65_C6}Tbe%nR<5sD3l7i<(&*ArB5%)h|^$_gv z77U;EI37aFefRF1+l7E~^9T>F{Wnxv75gj{HmxMD79E&i-0*aYqwM?CH8J5-{r$MJ zhQeatTon;-{|6gH^sGq~?Mk|V*>=FUdXv@#lX#A-OzXz0DgdCmavs;|n~ z7~M&KdwKXyusmXnV1&9flnQA81BJN1GfJEbN<0VW(fSk@qmYz#l3pAv*iE z1YF$;HAvo)pG+lGoVk$Q^gGjk-zv%0X7W$7RIKvc5o?-9|VZ zlto?eq@_)(NMa@Rcu*UdSqn|)(22R!c-$*rjA@RYiNrhehR_44V2$LS#kg9|kY8H0 z``Dn(#fXN1ngY1WGY*i9!uJ+8N|CXkyBhswAxaX8`o;p{SBhOWIsed?t&yrt1=bJQ9%2Cn+OSL`gs+R~_fl?>?)pZo7l^3jr;S zq)+}jt5PV7S;`*I)^zp!9dBs&Pg1~rM(YA^hB-Qxk!3q87%_G=oR48w$Vkn$Umy+O zQLL?=kx%-J1~Q&CJGZIHJ{$Uuh$7D8M$IFBxrxL~mYRn@2{Q(+J~1}j4qR>@^cNZv z-BIE1Rf>^KTm5`Q3E<-Q@U0vbNx{gEem0e8rj#u z(G4HnC^h6}l07*V~0He5k^f*xjaCB^#Ev zjtdy~hSFzO;Dg5a^2Vs=eA}@o0rBnv4Q;Q9tTy+mwK=z?E&*FS0z_yNW6pH`I|Hg`{@|@Rq1_Q zY8ti^#_wYmWO?jslXSNU(Q3inT<&})7J^B%Y zX$rGHENY+XCsmwN9SExA;@D=1G*%(NXYzM9mAU7>eUb+~p$> zWEV!qK=obNvodAPuPHl+NT1@6bA$!q++4O$=yxJr2uZj_s*EE=?>f@9?MtMoa8##uyl|wvSQAtjHhw69>(p#=2$^^ zrJzS0JOv^gxyyrJ0v=|6iMj9@cHUAdHPgJZk!K}vvS~3zyAy~0En7D2p~zFn-ZLn3 zx`prAmGU)KzRv1G7!Q1Prn~RY(E*A0E(!#xo zRlWEqk*ItzOM`Q4kjAO@Vc}%n)EngaJn}^WFxaS+=yeZ=_+B_)Nmfa<`-yolkZ(oJ zfPjoQMU!qv`s!?;|La9iqn|qTR>6JkV$=4Ca_a`U%&B}cTFy*>d}lE?WwU=;>{+{V zwu>C_6I(@{z-b1lSM9%+e$dl#-V$bBb*2*Me&BpAC--Ej=4y{yP#~iRDf%LVb&y3* zn)k>}xwO5^#7~^J`CQaD(O;)M%50?6m^ZfKCEn?4Sea?AQS?!lG9gtefC)Y_)RsSA z%r0z@*~zogQnJ2c{}&sizWIzs#@{P~hPn69!C-XhW`F)YG0>2q>A9ajHqcY27C*H( zRlNc_=pr}$l?$;#&Cs5-pl}=M_x$m#*mT|O+CtvFe5U)4@5ARNXPF1jpI)+L19cIY zyj}kp7#Lhk?L86yy8DHzkr(3Z7a+2KBy~h*1TxkKQR@eFV$9`guZy?_UK`cSfY@u+Op;nP4|5J0=W2VsoV#;YS*_rb zY5~o!1l8=1aG#D`B)!=$8LX&%`SL{m=#xn%=UFFb{yuo08p!6_C0!Fq&d0!V>%CD? z<^_Qzw*jV4mlWf~!Cpd1TDkW?5HIH?&u_^-V-&6Vpqs_8_W5OdUy*)@s0#S2($ctO~&1|kXR#k7W;oC+Cpd#R&( z)<_++s0>-p56b3M9SX@6CSqmZ~iJ=_}jpvTi$*jQ4^4AMQ%`f)gVP*FusxxNrv+%y|6plKSq zGQCQ~L9oJaczQyHlrkaK%7xrcfi_i^{)X2RIbY2bax*O|3mM%sVa?}|14FFYs0%B^*p9buo zx>6WdeVX<1ozN|d>S3?tov09-Trioj|f6p#Cmu}_zy=9L6r-i1jGFuq6vr}_No^h#@oF6qPV%%Tz-4p zQE}7`m`yY4p1Wk-08BV~fCtCK#M5m3e4kcN3%VIO zfXqdOK9obB0YRO|dHcz0J=TGP;lEP95CV~IHL;+ef8ox(dQLU5{z@LYKEO9jbR49V lT{pWMS4gp0tiY{HhcMb#y@Yp>4j|xlN7v|Pg^t6E{{d0o`py6V literal 0 HcmV?d00001 diff --git a/nebula.dme b/nebula.dme index 5cffd8b32162..34cdd86342d3 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1033,7 +1033,6 @@ #include "code\game\objects\effects\landmarks.dm" #include "code\game\objects\effects\landmarks_endgame.dm" #include "code\game\objects\effects\landmarks_latejoin.dm" -#include "code\game\objects\effects\mines.dm" #include "code\game\objects\effects\misc.dm" #include "code\game\objects\effects\overlays.dm" #include "code\game\objects\effects\portals.dm" @@ -1055,6 +1054,18 @@ #include "code\game\objects\effects\decals\Cleanable\misc.dm" #include "code\game\objects\effects\decals\Cleanable\robots.dm" #include "code\game\objects\effects\decals\Cleanable\tracks.dm" +#include "code\game\objects\effects\mines\_mine.dm" +#include "code\game\objects\effects\mines\_mine_payload.dm" +#include "code\game\objects\effects\mines\mine_assembly.dm" +#include "code\game\objects\effects\mines\mine_emp.dm" +#include "code\game\objects\effects\mines\mine_frag.dm" +#include "code\game\objects\effects\mines\mine_incendiary.dm" +#include "code\game\objects\effects\mines\mine_kick.dm" +#include "code\game\objects\effects\mines\mine_napalm.dm" +#include "code\game\objects\effects\mines\mine_radiation.dm" +#include "code\game\objects\effects\mines\mine_sleeping.dm" +#include "code\game\objects\effects\mines\mine_stun.dm" +#include "code\game\objects\effects\mines\mine_training.dm" #include "code\game\objects\effects\spawners\bombspawner.dm" #include "code\game\objects\effects\spawners\gibspawner.dm" #include "code\game\objects\items\__item.dm" @@ -1403,6 +1414,7 @@ #include "code\game\objects\structures\bookcase.dm" #include "code\game\objects\structures\catwalk.dm" #include "code\game\objects\structures\charge_pylon.dm" +#include "code\game\objects\structures\cliffs.dm" #include "code\game\objects\structures\coathanger.dm" #include "code\game\objects\structures\compost.dm" #include "code\game\objects\structures\crematorium.dm" diff --git a/sound/effects/break_stone.ogg b/sound/effects/break_stone.ogg new file mode 100644 index 0000000000000000000000000000000000000000..711fd50d482cb6d493396ede7776635e03a5c797 GIT binary patch literal 35871 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%0AGq0*XTUW6+!AZa-u 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 Lc;pP=H_#^l)@fHG literal 0 HcmV?d00001 From 4f7af9ba529879184cf223c5d21983d1ef21a73a Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 30 Dec 2025 09:22:01 +1100 Subject: [PATCH 0951/1331] Automatic changelog generation for PR #5213 [ci skip] --- html/changelogs/AutoChangeLog-pr-5213.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5213.yml diff --git a/html/changelogs/AutoChangeLog-pr-5213.yml b/html/changelogs/AutoChangeLog-pr-5213.yml new file mode 100644 index 000000000000..88516f2dbf7d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5213.yml @@ -0,0 +1,5 @@ +author: Penelope Haze +changes: + - {tweak: Iron can now optionally be used instead of aluminum when filling + sealant tanks for sealant guns.} +delete-after: true From 6ffb3075c6708d71502c3dd29b231c6c74b737ce Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 30 Dec 2025 01:01:56 +0000 Subject: [PATCH 0952/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-5213.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5213.yml diff --git a/html/changelog.html b/html/changelog.html index c8d067f1305b..6e4661c68847 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->

    [name_plural]
    " #undef ANTAG_COLUMNS - var/list/misc_roles = list("Botany Roles", "Graffiti") //Other roles (BLUE, because I have no idea what other color to make this) jobs += "" - jobs += "" - for(var/entry in misc_roles) + jobs += "" + for(var/entry in misc_jobban_roles) if(jobban_isbanned(M, entry)) jobs += "" else diff --git a/code/modules/submaps/submap_join.dm b/code/modules/submaps/submap_join.dm index 4a38fc4568cf..6698813bdd5e 100644 --- a/code/modules/submaps/submap_join.dm +++ b/code/modules/submaps/submap_join.dm @@ -29,10 +29,6 @@ to_chat(joining, SPAN_WARNING("You are banned from playing offstation roles.")) return FALSE - if(job.is_semi_antagonist && jobban_isbanned(joining, /decl/special_role/provocateur)) - to_chat(joining, SPAN_WARNING("You are banned from playing semi-antagonist roles.")) - return FALSE - if(job.is_restricted(joining.client.prefs, joining)) return FALSE diff --git a/mods/species/ascent/ascent.dm b/mods/species/ascent/ascent.dm index b0dcf2940b35..5678832ba712 100644 --- a/mods/species/ascent/ascent.dm +++ b/mods/species/ascent/ascent.dm @@ -4,8 +4,10 @@ #define BODY_EQUIP_FLAG_ALATE BITFLAG(4) #define BODY_EQUIP_FLAG_GYNE BITFLAG(5) -#define BP_SYSTEM_CONTROLLER "system controller" -#define BP_EGG "egg sac" +#define BP_SYSTEM_CONTROLLER "system controller" +#define BP_EGG "egg sac" + +#define MANTID_NYMPH_BAN "Mantid Nymph" #define MANTIDIFY(_thing, _name, _desc) \ ##_thing/ascent/name = _name; \ @@ -14,3 +16,7 @@ /decl/modpack/ascent name = "The Ascent" + +/decl/modpack/ascent/pre_initialize() + global.misc_jobban_roles += MANTID_NYMPH_BAN + . = ..() \ No newline at end of file diff --git a/mods/species/ascent/mobs/insectoid_egg.dm b/mods/species/ascent/mobs/insectoid_egg.dm index 5a9fba0cd993..f66efcbc97db 100644 --- a/mods/species/ascent/mobs/insectoid_egg.dm +++ b/mods/species/ascent/mobs/insectoid_egg.dm @@ -2,7 +2,7 @@ var/global/default_gyne /decl/ghosttrap/kharmaani_egg name = "mantid nymph" - ban_checks = list(/decl/special_role/provocateur) + ban_checks = list(MANTID_NYMPH_BAN) ghost_trap_message = "They are hatching from a kharmaan egg now." /decl/ghosttrap/kharmaani_egg/forced(var/mob/user) diff --git a/nebula.dme b/nebula.dme index 6e5ed0bad9c2..f5360b0f6894 100644 --- a/nebula.dme +++ b/nebula.dme @@ -776,7 +776,6 @@ #include "code\game\antagonist\antagonist_print.dm" #include "code\game\antagonist\antagonist_update.dm" #include "code\game\antagonist\outsider\ert.dm" -#include "code\game\antagonist\station\provocateur.dm" #include "code\game\area\area_abstract.dm" #include "code\game\area\area_access.dm" #include "code\game\area\area_fishing.dm" From a4a86a09a28d65f2eca97add0cd7d3ebeceee753 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 15:02:15 -0500 Subject: [PATCH 0956/1331] Move 'helping hands' out of augments module --- code/{modules/augment => game/objects/items}/helping_hands.dm | 0 nebula.dme | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename code/{modules/augment => game/objects/items}/helping_hands.dm (100%) diff --git a/code/modules/augment/helping_hands.dm b/code/game/objects/items/helping_hands.dm similarity index 100% rename from code/modules/augment/helping_hands.dm rename to code/game/objects/items/helping_hands.dm diff --git a/nebula.dme b/nebula.dme index 6e5ed0bad9c2..abb0ae344eab 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1077,6 +1077,7 @@ #include "code\game\objects\items\documents.dm" #include "code\game\objects\items\fleece.dm" #include "code\game\objects\items\glassjar.dm" +#include "code\game\objects\items\helping_hands.dm" #include "code\game\objects\items\holosign_creator.dm" #include "code\game\objects\items\horseshoe.dm" #include "code\game\objects\items\hourglass.dm" @@ -1780,7 +1781,6 @@ #include "code\modules\atmospherics\components\unary\vent_scrubber.dm" #include "code\modules\augment\active.dm" #include "code\modules\augment\augment.dm" -#include "code\modules\augment\helping_hands.dm" #include "code\modules\augment\simple.dm" #include "code\modules\augment\active\armblades.dm" #include "code\modules\augment\active\cyberbrain.dm" From fd05aa71841683711c71829adede9ca57b7ded0a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 16:34:21 -0500 Subject: [PATCH 0957/1331] Move augments into a modpack --- code/__defines/mobs.dm | 15 -------- code/_onclick/hud/action.dm | 3 -- code/game/objects/structures/mineral_bath.dm | 7 +++- code/modules/augment/passive/armor.dm | 12 ------ .../bodytype/bodytype_prosthetic_models.dm | 7 +--- .../preference_setup/loadout/_defines.dm | 6 --- .../preference_setup/loadout/loadout.dm | 7 ++++ .../definitions/modifiers_nanoswarm.dm | 34 ----------------- .../ailments/faults/fault_locking_thumbs.dm | 20 +++++----- .../faults/fault_visual_impairment.dm | 5 +-- .../organs/external/_external_damage.dm | 32 +++++++--------- code/modules/organs/external/diagnostics.dm | 3 -- code/modules/surgery/organs_internal.dm | 14 ++++--- code/modules/surgery/robotics.dm | 8 ++-- maps/exodus/exodus.dm | 1 + maps/ministation/ministation.dm | 1 + maps/modpack_testing/modpack_testing.dm | 1 + maps/tradeship/tradeship.dm | 1 + mods/content/augments/_augments.dm | 13 +++++++ mods/content/augments/_augments.dme | 24 ++++++++++++ .../content/augments}/active.dm | 3 ++ .../content/augments}/active/armblades.dm | 2 +- .../content/augments}/active/cyberbrain.dm | 0 .../content/augments}/active/polytool.dm | 0 .../augments}/active/tool/engineering.dm | 12 +++--- .../content/augments}/active/tool/surgical.dm | 0 .../content/augments/augment_loadout.dm | 11 ++++-- .../content/augments/augment_organ.dm | 12 +++++- mods/content/augments/bodytype_augment.dm | 4 ++ .../content/augments}/designs_augments.dm | 0 .../content/augments/icons}/augment.dmi | Bin .../content/augments/icons}/augment_tools.dmi | Bin .../content/augments/icons}/welder_finger.dmi | Bin mods/content/augments/overrides.dm | 32 ++++++++++++++++ mods/content/augments/passive/armor.dm | 26 +++++++++++++ .../content/augments}/passive/boost.dm | 0 .../content/augments}/passive/boost/muscle.dm | 0 .../content/augments}/passive/boost/reflex.dm | 0 .../augments}/passive/boost/shooting.dm | 0 .../content/augments}/passive/nanoaura.dm | 36 ++++++++++++++++++ .../content/augments}/simple.dm | 0 .../_integrated_electronics.dme | 1 - .../fabricator_designs.dm | 3 -- mods/~compatibility/patches/circuits.dm | 4 ++ .../patches/circuits/augment_circuits.dm} | 6 ++- nebula.dme | 18 --------- 46 files changed, 227 insertions(+), 157 deletions(-) delete mode 100644 code/modules/augment/passive/armor.dm delete mode 100644 code/modules/client/preference_setup/loadout/_defines.dm delete mode 100644 code/modules/mob_modifiers/definitions/modifiers_nanoswarm.dm create mode 100644 mods/content/augments/_augments.dm create mode 100644 mods/content/augments/_augments.dme rename {code/modules/augment => mods/content/augments}/active.dm (87%) rename {code/modules/augment => mods/content/augments}/active/armblades.dm (97%) rename {code/modules/augment => mods/content/augments}/active/cyberbrain.dm (100%) rename {code/modules/augment => mods/content/augments}/active/polytool.dm (100%) rename {code/modules/augment => mods/content/augments}/active/tool/engineering.dm (84%) rename {code/modules/augment => mods/content/augments}/active/tool/surgical.dm (100%) rename code/modules/client/preference_setup/loadout/lists/augmentations.dm => mods/content/augments/augment_loadout.dm (79%) rename code/modules/augment/augment.dm => mods/content/augments/augment_organ.dm (85%) create mode 100644 mods/content/augments/bodytype_augment.dm rename {code/modules/fabrication/designs/robotics => mods/content/augments}/designs_augments.dm (100%) rename {icons/obj => mods/content/augments/icons}/augment.dmi (100%) rename {icons/obj => mods/content/augments/icons}/augment_tools.dmi (100%) rename {icons/obj/items/tool/welders => mods/content/augments/icons}/welder_finger.dmi (100%) create mode 100644 mods/content/augments/overrides.dm create mode 100644 mods/content/augments/passive/armor.dm rename {code/modules/augment => mods/content/augments}/passive/boost.dm (100%) rename {code/modules/augment => mods/content/augments}/passive/boost/muscle.dm (100%) rename {code/modules/augment => mods/content/augments}/passive/boost/reflex.dm (100%) rename {code/modules/augment => mods/content/augments}/passive/boost/shooting.dm (100%) rename {code/modules/augment => mods/content/augments}/passive/nanoaura.dm (58%) rename {code/modules/augment => mods/content/augments}/simple.dm (100%) rename mods/{content/integrated_electronics/assemblies/circuit_augment.dm => ~compatibility/patches/circuits/augment_circuits.dm} (90%) diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 75c9ac6ef8ce..77b1dae70dd5 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -160,21 +160,6 @@ // Robo Organs. #define BP_VOICE "vocal synthesiser" -//Augmetations -#define BP_AUGMENT_R_ARM "right arm augment" -#define BP_AUGMENT_L_ARM "left arm augment" -#define BP_AUGMENT_R_HAND "right hand augment" -#define BP_AUGMENT_L_HAND "left hand augment" -#define BP_AUGMENT_R_LEG "right leg augment" -#define BP_AUGMENT_L_LEG "left leg augment" -#define BP_AUGMENT_CHEST_ARMOUR "chest armor augment" -#define BP_AUGMENT_CHEST_ACTIVE "active chest augment" -#define BP_AUGMENT_HEAD "head augment" - -//Augment flags -#define AUGMENTATION_MECHANIC 1 -#define AUGMENTATION_ORGANIC 2 - // Prosthetic helpers. #define BP_IS_PROSTHETIC(org) (!QDELETED(org) && (org.organ_properties & ORGAN_PROP_PROSTHETIC)) #define BP_IS_ROBOTIC(org) (!QDELETED(org) && (org.bodytype?.is_robotic)) diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm index e6599e5ac2b7..70cc620cb206 100644 --- a/code/_onclick/hud/action.dm +++ b/code/_onclick/hud/action.dm @@ -162,9 +162,6 @@ if(istype(O)) O.refresh_action_button() -/datum/action/item_action/organ/augment - button_icon = 'icons/obj/augment.dmi' - #undef AB_WEST_OFFSET #undef AB_NORTH_OFFSET #undef AB_MAX_COLUMNS \ No newline at end of file diff --git a/code/game/objects/structures/mineral_bath.dm b/code/game/objects/structures/mineral_bath.dm index 1ed4d581c726..1ff9947a566c 100644 --- a/code/game/objects/structures/mineral_bath.dm +++ b/code/game/objects/structures/mineral_bath.dm @@ -137,7 +137,7 @@ for(var/obj/item/organ/external/limb in occupant.get_external_organs()) if(BP_IS_PROSTHETIC(limb)) for(var/obj/implanted_object in limb.implants) - if(!istype(implanted_object,/obj/item/implant) && !istype(implanted_object,/obj/item/organ/internal/augment) && prob(25)) // We don't want to remove REAL implants. Just shrapnel etc. + if(!should_dissolve_implant(implanted_object)) // We don't want to remove REAL implants. Just shrapnel etc. LAZYREMOVE(limb.implants, implanted_object) to_chat(occupant, SPAN_NOTICE("The mineral-rich bath dissolves the [implanted_object.name].")) qdel(implanted_object) @@ -149,3 +149,8 @@ limb.status |= ORGAN_BRITTLE to_chat(occupant, SPAN_WARNING("It feels a bit brittle, though...")) break + +/obj/structure/mineral_bath/proc/should_dissolve_implant(obj/implanted_object) + if(istype(implanted_object, /obj/item/implant)) + return FALSE + return prob(25) diff --git a/code/modules/augment/passive/armor.dm b/code/modules/augment/passive/armor.dm deleted file mode 100644 index 306de5710645..000000000000 --- a/code/modules/augment/passive/armor.dm +++ /dev/null @@ -1,12 +0,0 @@ -/obj/item/organ/internal/augment/armor - name = "subdermal armor" - allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) - icon_state = "armor-chest" - desc = "A flexible composite mesh designed to prevent tearing and puncturing of underlying tissue." - material = /decl/material/solid/metal/steel - origin_tech = @'{"materials":4,"engineering":2,"biotech":3}' - var/brute_mult = 0.8 - var/burn_mult = 1 - -/obj/item/organ/internal/augment/armor/reset_matter() - matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) \ No newline at end of file diff --git a/code/modules/bodytype/bodytype_prosthetic_models.dm b/code/modules/bodytype/bodytype_prosthetic_models.dm index d7156bf6bdc2..6d6f47ff139e 100644 --- a/code/modules/bodytype/bodytype_prosthetic_models.dm +++ b/code/modules/bodytype/bodytype_prosthetic_models.dm @@ -19,9 +19,4 @@ required_map_tech = MAP_TECH_LEVEL_MEDIEVAL uid = "bodytype_prosthetic_wooden" -DEFINE_ROBOLIMB_MODEL_TRAITS(/decl/bodytype/prosthetic/wooden, pirate, 0, "wooden") - -// Dummy/stub prosthetic type for augment implants. -/decl/bodytype/prosthetic/augment - name = "Augment" - uid = "bodytype_prosthetic_augment" +DEFINE_ROBOLIMB_MODEL_TRAITS(/decl/bodytype/prosthetic/wooden, pirate, 0, "wooden") \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/_defines.dm b/code/modules/client/preference_setup/loadout/_defines.dm deleted file mode 100644 index c98f5c4d3134..000000000000 --- a/code/modules/client/preference_setup/loadout/_defines.dm +++ /dev/null @@ -1,6 +0,0 @@ -#define GEAR_HAS_COLOR_SELECTION BITFLAG(0) -#define GEAR_HAS_TYPE_SELECTION BITFLAG(1) -#define GEAR_HAS_SUBTYPE_SELECTION BITFLAG(2) -#define GEAR_HAS_CUSTOM_SELECTION BITFLAG(3) -#define GEAR_NO_EQUIP BITFLAG(4) -#define GEAR_NO_FINGERPRINTS BITFLAG(5) diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index ceb5eaaeac52..d8b2da85e07e 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -406,6 +406,13 @@ var/list/allowed_branches /// Skills required to spawn with this item. var/list/allowed_skills + // The various valid values for loadout_flags. + var/const/GEAR_HAS_COLOR_SELECTION = BITFLAG(0) + var/const/GEAR_HAS_TYPE_SELECTION = BITFLAG(1) + var/const/GEAR_HAS_SUBTYPE_SELECTION = BITFLAG(2) + var/const/GEAR_HAS_CUSTOM_SELECTION = BITFLAG(3) + var/const/GEAR_NO_EQUIP = BITFLAG(4) + var/const/GEAR_NO_FINGERPRINTS = BITFLAG(5) /// Special tweaks in new var/loadout_flags /// Special tweak in New diff --git a/code/modules/mob_modifiers/definitions/modifiers_nanoswarm.dm b/code/modules/mob_modifiers/definitions/modifiers_nanoswarm.dm deleted file mode 100644 index ddb6d6023eae..000000000000 --- a/code/modules/mob_modifiers/definitions/modifiers_nanoswarm.dm +++ /dev/null @@ -1,34 +0,0 @@ -//This handy augment protects you to a degree, keeping it online after critical damage however is bad -/decl/mob_modifier/nanoswarm - name = "Defensive Nanoswarm" - desc = "You are surrounded by nanomachines that harden in response to projectiles." - hud_icon_state = "nanomachines" - on_add_message_1p = SPAN_NOTICE("Your skin tingles as the nanites spread over your body.") - on_end_message_1p = SPAN_WARNING("The nanites dissolve!") - -/decl/mob_modifier/nanoswarm/on_modifier_datum_added(mob/living/_owner, decl/mob_modifier/modifier) - . = ..() - playsound(_owner.loc,'sound/weapons/flash.ogg',35,1) - -/decl/mob_modifier/nanoswarm/check_modifiers_block_attack(mob/living/_owner, list/modifiers, attack_type, atom/movable/attacker, additional_data) - if(attack_type != MM_ATTACK_TYPE_PROJECTILE) - return ..() - - var/obj/item/organ/internal/augment/active/nanounit/unit - for(var/datum/mob_modifier/modifier in modifiers) - var/obj/item/organ/internal/augment/active/nanounit/implant = modifier.source?.resolve() - if(istype(implant) && implant.active && implant.charges >= 0) // active with 0 charges means it's about to critically fail. - unit = implant - break - - if(!istype(unit)) - return ..() - - _owner.visible_message(SPAN_WARNING("The nanomachines harden as a response to physical trauma!")) - playsound(_owner, 'sound/effects/basscannon.ogg',35,1) - unit.charges-- - if(unit.charges <= 0) - to_chat(_owner, SPAN_DANGER("Warning: Critical damage threshold passed. Shut down unit to avoid further damage.")) - else - unit.catastrophic_failure() - return MM_ATTACK_RESULT_BLOCKED|MM_ATTACK_RESULT_DEFLECTED diff --git a/code/modules/organs/ailments/faults/fault_locking_thumbs.dm b/code/modules/organs/ailments/faults/fault_locking_thumbs.dm index c689d9c477db..61678c157ead 100644 --- a/code/modules/organs/ailments/faults/fault_locking_thumbs.dm +++ b/code/modules/organs/ailments/faults/fault_locking_thumbs.dm @@ -5,20 +5,18 @@ BP_L_ARM, BP_L_HAND, BP_R_ARM, - BP_R_HAND, - BP_AUGMENT_R_ARM, - BP_AUGMENT_L_ARM, - BP_AUGMENT_R_HAND, - BP_AUGMENT_L_HAND + BP_R_HAND ) +/datum/ailment/fault/locking_thumbs/proc/resolve_tag_to_slot(organ_tag) + switch(organ_tag) + if(BP_L_ARM, BP_L_HAND) + return BP_L_HAND + if(BP_R_ARM, BP_R_HAND) + return BP_R_HAND + /datum/ailment/fault/locking_thumbs/on_ailment_event() - var/slot = null - switch (organ.organ_tag) - if (BP_L_ARM, BP_L_HAND, BP_AUGMENT_L_HAND, BP_AUGMENT_L_ARM) - slot = BP_L_HAND - if (BP_R_ARM, BP_R_HAND, BP_AUGMENT_R_HAND, BP_AUGMENT_R_ARM) - slot = BP_R_HAND + var/slot = resolve_tag_to_slot(organ.organ_tag) var/obj/item/thing = organ.owner.get_equipped_item(slot) if(thing && organ.owner.try_unequip(thing)) var/decl/pronouns/pronouns = organ.owner.get_pronouns() diff --git a/code/modules/organs/ailments/faults/fault_visual_impairment.dm b/code/modules/organs/ailments/faults/fault_visual_impairment.dm index 3d54995fc284..c7b1c8fe87ef 100644 --- a/code/modules/organs/ailments/faults/fault_visual_impairment.dm +++ b/code/modules/organs/ailments/faults/fault_visual_impairment.dm @@ -1,10 +1,9 @@ /datum/ailment/fault/visual_impair name = "visual impairment" applies_to_organ = list( - BP_HEAD, - BP_AUGMENT_HEAD + BP_HEAD, ) - + /datum/ailment/fault/visual_impair/New() ..() if(organ?.owner) diff --git a/code/modules/organs/external/_external_damage.dm b/code/modules/organs/external/_external_damage.dm index 6ca145d43361..2431326554db 100644 --- a/code/modules/organs/external/_external_damage.dm +++ b/code/modules/organs/external/_external_damage.dm @@ -11,8 +11,11 @@ if(!owner) return ..() - var/brute = damage_type == BRUTE ? round(damage * get_brute_mod(damage_flags), 0.1) : 0 - var/burn = damage_type == BURN ? round(damage * get_burn_mod(damage_flags), 0.1) : 0 + var/final_brute_mod = get_brute_mod(damage_flags) + (0.2 * burn_dam/max_damage) // extra brute taken if you have burn damage. why? ask whoever originally coded it. + var/final_burn_mod = get_burn_mod(damage_flags) + + var/brute = damage_type == BRUTE ? round(damage * final_brute_mod, 0.1) : 0 + var/burn = damage_type == BURN ? round(damage * final_burn_mod, 0.1) : 0 if((brute <= 0) && (burn <= 0)) return 0 @@ -318,29 +321,20 @@ return FALSE /obj/item/organ/external/proc/get_brute_mod(var/damage_flags) - var/obj/item/organ/internal/augment/armor/A = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) - var/B = 1 - if(A) - B = A.brute_mult + . = 1 if(!BP_IS_PROSTHETIC(src)) - B *= species.get_brute_mod(owner) - var/blunt = !(damage_flags & DAM_EDGE|DAM_SHARP) - if(blunt && BP_IS_BRITTLE(src)) - B *= 1.5 + . *= species.get_brute_mod(owner) + if(!(damage_flags & DAM_EDGE|DAM_SHARP) && BP_IS_BRITTLE(src)) + . *= 1.5 if(BP_IS_CRYSTAL(src)) - B *= 0.8 - return B + (0.2 * burn_dam/max_damage) //burns make you take more brute damage + . *= 0.8 /obj/item/organ/external/proc/get_burn_mod(var/damage_flags) - var/obj/item/organ/internal/augment/armor/A = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) - var/B = 1 - if(A) - B = A.burn_mult + . = 1 if(!BP_IS_PROSTHETIC(src)) - B *= species.get_burn_mod(owner) + . *= species.get_burn_mod(owner) if(BP_IS_CRYSTAL(src)) - B *= 0.1 - return B + . *= 0.1 //organs can come off in three cases //1. If the damage source is edge_eligible and the brute damage dealt exceeds the edge threshold, then the organ is cut off. diff --git a/code/modules/organs/external/diagnostics.dm b/code/modules/organs/external/diagnostics.dm index b78c313b71c7..d7047a14aa82 100644 --- a/code/modules/organs/external/diagnostics.dm +++ b/code/modules/organs/external/diagnostics.dm @@ -107,9 +107,6 @@ unknown_body++ if(unknown_body) . += "Unknown body present" - for(var/obj/item/organ/internal/augment/aug in internal_organs) - if(istype(aug) && aug.known) - . += "[capitalize(aug.name)] implanted" var/obj/item/organ/internal/lungs/L = locate() in src if( L && L.is_bruised()) . += "Lung ruptured" diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 1ed731ee4546..4d25ab85a850 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -328,6 +328,9 @@ else return ..() +/obj/item/organ/internal/proc/get_attachment_failure_reason(obj/item/organ/external/affected, robotic = FALSE) + return FALSE + /decl/surgery_step/internal/attach_organ/pre_surgery_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/list/attachable_organs @@ -342,7 +345,7 @@ if(!LAZYLEN(attachable_organs)) return FALSE - var/obj/item/organ/organ_to_replace = show_radial_menu(user, tool, attachable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) + var/obj/item/organ/internal/organ_to_replace = show_radial_menu(user, tool, attachable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) if(!organ_to_replace) return FALSE @@ -350,11 +353,10 @@ to_chat(user, SPAN_WARNING("You can't find anywhere to attach \the [organ_to_replace] to!")) return FALSE - if(istype(organ_to_replace, /obj/item/organ/internal/augment)) - var/obj/item/organ/internal/augment/A = organ_to_replace - if(!(A.augment_flags & AUGMENTATION_ORGANIC)) - to_chat(user, SPAN_WARNING("\The [A] cannot function within a non-robotic limb.")) - return FALSE + var/attach_failure_reason = organ_to_replace.get_attachment_failure_reason(affected, robotic = FALSE) // if this returns FALSE, it can attach + if(attach_failure_reason) + to_chat(user, attach_failure_reason) + return FALSE var/decl/species/species = target.get_species() if(!species) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 07dd1eb2d65d..ce4bd511bd65 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -463,12 +463,12 @@ var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) radial_button.name = "Reattach \the [I]" LAZYSET(removable_organs, I.organ_tag, radial_button) - var/organ_to_replace = show_radial_menu(user, tool, removable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) + var/obj/item/organ/internal/organ_to_replace = show_radial_menu(user, tool, removable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) if(!organ_to_replace) return FALSE - var/obj/item/organ/internal/augment/A = organ_to_replace - if(istype(A) && !(A.augment_flags & AUGMENTATION_MECHANIC)) - to_chat(user, SPAN_WARNING("\The [A] cannot function within a robotic limb.")) + var/attach_failure_reason = organ_to_replace.get_attachment_failure_reason(affected, robotic = TRUE) // if this returns FALSE, it can attach + if(attach_failure_reason) + to_chat(user, attach_failure_reason) return FALSE return organ_to_replace diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 4308072cd477..43dcd862a404 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -3,6 +3,7 @@ #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/blob/_blob.dme" diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index f4aafdce3206..19b9c46bc302 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -20,6 +20,7 @@ Twice... #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/blob/_blob.dme" #include "../../mods/content/corporate/_corporate.dme" diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 505bf42e8948..590290547609 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -7,6 +7,7 @@ #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/biomods/_biomods.dme" diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 542a6dcb22f4..d7c622f2a55f 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -16,6 +16,7 @@ #include "../../mods/content/plant_dissection/_plant_dissection.dme" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/blob/_blob.dme" diff --git a/mods/content/augments/_augments.dm b/mods/content/augments/_augments.dm new file mode 100644 index 000000000000..702d94917fae --- /dev/null +++ b/mods/content/augments/_augments.dm @@ -0,0 +1,13 @@ +//Augmentation organ tag defines +#define BP_AUGMENT_R_ARM "right arm augment" +#define BP_AUGMENT_L_ARM "left arm augment" +#define BP_AUGMENT_R_HAND "right hand augment" +#define BP_AUGMENT_L_HAND "left hand augment" +#define BP_AUGMENT_R_LEG "right leg augment" +#define BP_AUGMENT_L_LEG "left leg augment" +#define BP_AUGMENT_CHEST_ARMOUR "chest armor augment" +#define BP_AUGMENT_CHEST_ACTIVE "active chest augment" +#define BP_AUGMENT_HEAD "head augment" + +/decl/modpack/augments + name = "Augmentation Content" diff --git a/mods/content/augments/_augments.dme b/mods/content/augments/_augments.dme new file mode 100644 index 000000000000..ec0fba60a729 --- /dev/null +++ b/mods/content/augments/_augments.dme @@ -0,0 +1,24 @@ +#ifndef CONTENT_PACK_AUGMENTS +#define CONTENT_PACK_AUGMENTS +// BEGIN_INCLUDE +#include "_augments.dm" +#include "active.dm" +#include "augment_loadout.dm" +#include "augment_organ.dm" +#include "bodytype_augment.dm" +#include "designs_augments.dm" +#include "overrides.dm" +#include "simple.dm" +#include "active\armblades.dm" +#include "active\cyberbrain.dm" +#include "active\polytool.dm" +#include "active\tool\engineering.dm" +#include "active\tool\surgical.dm" +#include "passive\armor.dm" +#include "passive\boost.dm" +#include "passive\nanoaura.dm" +#include "passive\boost\muscle.dm" +#include "passive\boost\reflex.dm" +#include "passive\boost\shooting.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/code/modules/augment/active.dm b/mods/content/augments/active.dm similarity index 87% rename from code/modules/augment/active.dm rename to mods/content/augments/active.dm index e338d1e763c8..773215a8fa69 100644 --- a/code/modules/augment/active.dm +++ b/mods/content/augments/active.dm @@ -22,3 +22,6 @@ if(. && istype(action)) action.button_icon_state = icon_state action.button?.update_icon() + +/datum/action/item_action/organ/augment + button_icon = 'mods/content/augments/icons/augment.dmi' \ No newline at end of file diff --git a/code/modules/augment/active/armblades.dm b/mods/content/augments/active/armblades.dm similarity index 97% rename from code/modules/augment/active/armblades.dm rename to mods/content/augments/active/armblades.dm index 8e40dc7c43e3..38d043f9a32e 100644 --- a/code/modules/augment/active/armblades.dm +++ b/mods/content/augments/active/armblades.dm @@ -2,7 +2,7 @@ icon_state = "armblade" item_state = null name = "armblade" - icon = 'icons/obj/augment.dmi' + icon = 'mods/content/augments/icons/augment.dmi' desc = "A handy utility blade for the discerning augmentee. Warranty void if used for cutting." base_parry_chance = 30 sharp = TRUE diff --git a/code/modules/augment/active/cyberbrain.dm b/mods/content/augments/active/cyberbrain.dm similarity index 100% rename from code/modules/augment/active/cyberbrain.dm rename to mods/content/augments/active/cyberbrain.dm diff --git a/code/modules/augment/active/polytool.dm b/mods/content/augments/active/polytool.dm similarity index 100% rename from code/modules/augment/active/polytool.dm rename to mods/content/augments/active/polytool.dm diff --git a/code/modules/augment/active/tool/engineering.dm b/mods/content/augments/active/tool/engineering.dm similarity index 84% rename from code/modules/augment/active/tool/engineering.dm rename to mods/content/augments/active/tool/engineering.dm index 20fec8cfe1a6..2aff3eedd2a0 100644 --- a/code/modules/augment/active/tool/engineering.dm +++ b/mods/content/augments/active/tool/engineering.dm @@ -19,13 +19,13 @@ /obj/item/weldingtool/finger name = "digital welder" desc = "A precise, high quality welding tool." - icon = 'icons/obj/items/tool/welders/welder_finger.dmi' + icon = 'mods/content/augments/icons/welder_finger.dmi' /obj/item/wirecutters/finger name = "digital splicer" desc = "A small embedded cutter in your finger." icon_state = "wirecutter_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/wirecutters/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -35,7 +35,7 @@ name = "digital screwdriver" desc = "A nifty power tool at your literal fingertips." icon_state = "screwdriver_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/screwdriver/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -45,7 +45,7 @@ name = "digital prybar" desc = "A somewhat awkward to use prybar. It doubles as a bottle opener." icon_state = "prybar_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/crowbar/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -55,7 +55,7 @@ name = "digital wrench" desc = "A rotating wrench. Don't get your hair caught in it." icon_state = "wrench_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/wrench/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -65,4 +65,4 @@ name = "digital multitool" desc = "A multitool inside of a multitool. Doubletool?" icon_state = "multitool_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' diff --git a/code/modules/augment/active/tool/surgical.dm b/mods/content/augments/active/tool/surgical.dm similarity index 100% rename from code/modules/augment/active/tool/surgical.dm rename to mods/content/augments/active/tool/surgical.dm diff --git a/code/modules/client/preference_setup/loadout/lists/augmentations.dm b/mods/content/augments/augment_loadout.dm similarity index 79% rename from code/modules/client/preference_setup/loadout/lists/augmentations.dm rename to mods/content/augments/augment_loadout.dm index e2f387cd6ec2..3fd9d8efa984 100644 --- a/code/modules/client/preference_setup/loadout/lists/augmentations.dm +++ b/mods/content/augments/augment_loadout.dm @@ -27,9 +27,14 @@ if(!istype(organ_to_implant_into)) return ..() - if(augment_flags == AUGMENTATION_MECHANIC && !BP_IS_PROSTHETIC(organ_to_implant_into)) - to_chat(user, SPAN_DANGER("Your [organ_to_implant_into.name] is not prosthetic, and therefore \the [src] can not be installed!")) - return ..() + if(BP_IS_PROSTHETIC(organ_to_implant_into)) + if(!(augment_flags & AUGMENTATION_MECHANIC)) + to_chat(user, SPAN_DANGER("Your [organ_to_implant_into.name] is not organic, and therefore \the [src] can not be installed!")) + return ..() + else + if(!(augment_flags & AUGMENTATION_ORGANIC)) + to_chat(user, SPAN_DANGER("Your [organ_to_implant_into.name] is not prosthetic, and therefore \the [src] can not be installed!")) + return ..() user.add_organ(src, organ_to_implant_into) to_chat(user, SPAN_NOTICE("Your [organ_to_implant_into.name] has been replaced with \the [src].")) diff --git a/code/modules/augment/augment.dm b/mods/content/augments/augment_organ.dm similarity index 85% rename from code/modules/augment/augment.dm rename to mods/content/augments/augment_organ.dm index 63105dc6e02b..8f097a8874bd 100644 --- a/code/modules/augment/augment.dm +++ b/mods/content/augments/augment_organ.dm @@ -1,7 +1,7 @@ /obj/item/organ/internal/augment name = "embedded augment" desc = "An embedded augment." - icon = 'icons/obj/augment.dmi' + icon = 'mods/content/augments/icons/augment.dmi' w_class = ITEM_SIZE_TINY // Need to be tiny to fit inside limbs. //By default these fit on both flesh and robotic organs and are robotic organ_properties = ORGAN_PROP_PROSTHETIC @@ -12,6 +12,8 @@ var/descriptor = "" var/known = TRUE + var/const/AUGMENTATION_MECHANIC = BITFLAG(0) + var/const/AUGMENTATION_ORGANIC = BITFLAG(1) var/augment_flags = AUGMENTATION_MECHANIC | AUGMENTATION_ORGANIC var/list/allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM) @@ -36,6 +38,14 @@ parent_organ = affected.organ_tag update_parent_organ() +/obj/item/organ/internal/augment/get_attachment_failure_reason(obj/item/organ/external/affected, robotic = FALSE) + if(robotic) + if(!(augment_flags & AUGMENTATION_ORGANIC)) + return SPAN_WARNING("\The [src] cannot function within a non-robotic limb.") + else if(!(augment_flags & AUGMENTATION_MECHANIC)) + return SPAN_WARNING("\The [src] cannot function within a robotic limb.") + return ..() + /obj/item/organ/internal/augment/proc/update_parent_organ() //This tries to match a parent organ to an augment slot //This is intended to match the possible positions to a parent organ diff --git a/mods/content/augments/bodytype_augment.dm b/mods/content/augments/bodytype_augment.dm new file mode 100644 index 000000000000..4fd09ed6a0ed --- /dev/null +++ b/mods/content/augments/bodytype_augment.dm @@ -0,0 +1,4 @@ +// Dummy/stub prosthetic type for augment implants. +/decl/bodytype/prosthetic/augment + name = "Augment" + uid = "bodytype_prosthetic_augment" diff --git a/code/modules/fabrication/designs/robotics/designs_augments.dm b/mods/content/augments/designs_augments.dm similarity index 100% rename from code/modules/fabrication/designs/robotics/designs_augments.dm rename to mods/content/augments/designs_augments.dm diff --git a/icons/obj/augment.dmi b/mods/content/augments/icons/augment.dmi similarity index 100% rename from icons/obj/augment.dmi rename to mods/content/augments/icons/augment.dmi diff --git a/icons/obj/augment_tools.dmi b/mods/content/augments/icons/augment_tools.dmi similarity index 100% rename from icons/obj/augment_tools.dmi rename to mods/content/augments/icons/augment_tools.dmi diff --git a/icons/obj/items/tool/welders/welder_finger.dmi b/mods/content/augments/icons/welder_finger.dmi similarity index 100% rename from icons/obj/items/tool/welders/welder_finger.dmi rename to mods/content/augments/icons/welder_finger.dmi diff --git a/mods/content/augments/overrides.dm b/mods/content/augments/overrides.dm new file mode 100644 index 000000000000..296d4c337067 --- /dev/null +++ b/mods/content/augments/overrides.dm @@ -0,0 +1,32 @@ + +/obj/structure/mineral_bath/should_dissolve_implant(obj/implanted_object) + if(istype(implanted_object, /obj/item/organ/internal/augment)) + return FALSE + return ..() + +// Cause arm and hand augments to trigger fault ailments. +/datum/ailment/fault/locking_thumbs/New(obj/item/organ/_organ) + var/static/did_injection = FALSE + if(!did_injection) + did_injection = TRUE + applies_to_organ |= list( + BP_AUGMENT_R_ARM, + BP_AUGMENT_L_ARM, + BP_AUGMENT_R_HAND, + BP_AUGMENT_L_HAND + ) + . = ..() + +/datum/ailment/fault/locking_thumbs/resolve_tag_to_slot(organ_tag) + switch(organ_tag) + if(BP_AUGMENT_L_ARM, BP_AUGMENT_L_HAND) + return BP_L_HAND + if(BP_AUGMENT_R_ARM, BP_AUGMENT_R_HAND) + return BP_R_HAND + return ..() + +// Add augments to scan results +/obj/item/organ/external/get_scan_results() + for(var/obj/item/organ/internal/augment/aug in internal_organs) + if(istype(aug) && aug.known) + . += "[capitalize(aug.name)] implanted" \ No newline at end of file diff --git a/mods/content/augments/passive/armor.dm b/mods/content/augments/passive/armor.dm new file mode 100644 index 000000000000..2dff1139cba4 --- /dev/null +++ b/mods/content/augments/passive/armor.dm @@ -0,0 +1,26 @@ +/obj/item/organ/internal/augment/armor + name = "subdermal armor" + allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) + icon_state = "armor-chest" + desc = "A flexible composite mesh designed to prevent tearing and puncturing of underlying tissue." + material = /decl/material/solid/metal/steel + origin_tech = @'{"materials":4,"engineering":2,"biotech":3}' + var/brute_mult = 0.8 + var/burn_mult = 1 + +/obj/item/organ/internal/augment/armor/reset_matter() + matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) + +// override to add armor augment damage mods +/obj/item/organ/external/get_brute_mod(var/damage_flags) + . = ..() + var/obj/item/organ/internal/augment/armor/armor_augment = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) + if(armor_augment) + . *= armor_augment.brute_mult + +// override to add armor augment damage mods +/obj/item/organ/external/get_burn_mod(var/damage_flags) + . = ..() + var/obj/item/organ/internal/augment/armor/armor_augment = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) + if(armor_augment) + . *= armor_augment.burn_mult \ No newline at end of file diff --git a/code/modules/augment/passive/boost.dm b/mods/content/augments/passive/boost.dm similarity index 100% rename from code/modules/augment/passive/boost.dm rename to mods/content/augments/passive/boost.dm diff --git a/code/modules/augment/passive/boost/muscle.dm b/mods/content/augments/passive/boost/muscle.dm similarity index 100% rename from code/modules/augment/passive/boost/muscle.dm rename to mods/content/augments/passive/boost/muscle.dm diff --git a/code/modules/augment/passive/boost/reflex.dm b/mods/content/augments/passive/boost/reflex.dm similarity index 100% rename from code/modules/augment/passive/boost/reflex.dm rename to mods/content/augments/passive/boost/reflex.dm diff --git a/code/modules/augment/passive/boost/shooting.dm b/mods/content/augments/passive/boost/shooting.dm similarity index 100% rename from code/modules/augment/passive/boost/shooting.dm rename to mods/content/augments/passive/boost/shooting.dm diff --git a/code/modules/augment/passive/nanoaura.dm b/mods/content/augments/passive/nanoaura.dm similarity index 58% rename from code/modules/augment/passive/nanoaura.dm rename to mods/content/augments/passive/nanoaura.dm index 751ed5541abc..e22f8331cd63 100644 --- a/code/modules/augment/passive/nanoaura.dm +++ b/mods/content/augments/passive/nanoaura.dm @@ -1,3 +1,4 @@ +//This handy augment protects you to a degree, keeping it online after critical damage however is bad /obj/item/organ/internal/augment/active/nanounit name = "nanite MCU" allowed_organs = list(BP_AUGMENT_CHEST_ACTIVE) @@ -55,3 +56,38 @@ if(owner && modifier_archetype) owner.remove_mob_modifier(modifier_archetype, source = src) . = ..() + +// The modifier applied by the above implant. +/decl/mob_modifier/nanoswarm + name = "Defensive Nanoswarm" + desc = "You are surrounded by nanomachines that harden in response to projectiles." + hud_icon_state = "nanomachines" + on_add_message_1p = SPAN_NOTICE("Your skin tingles as the nanites spread over your body.") + on_end_message_1p = SPAN_WARNING("The nanites dissolve!") + +/decl/mob_modifier/nanoswarm/on_modifier_datum_added(mob/living/_owner, decl/mob_modifier/modifier) + . = ..() + playsound(_owner.loc,'sound/weapons/flash.ogg',35,1) + +/decl/mob_modifier/nanoswarm/check_modifiers_block_attack(mob/living/_owner, list/modifiers, attack_type, atom/movable/attacker, additional_data) + if(attack_type != MM_ATTACK_TYPE_PROJECTILE) + return ..() + + var/obj/item/organ/internal/augment/active/nanounit/unit + for(var/datum/mob_modifier/modifier in modifiers) + var/obj/item/organ/internal/augment/active/nanounit/implant = modifier.source?.resolve() + if(istype(implant) && implant.active && implant.charges >= 0) // active with 0 charges means it's about to critically fail. + unit = implant + break + + if(!istype(unit)) + return ..() + + _owner.visible_message(SPAN_WARNING("The nanomachines harden as a response to physical trauma!")) + playsound(_owner, 'sound/effects/basscannon.ogg',35,1) + unit.charges-- + if(unit.charges <= 0) + to_chat(_owner, SPAN_DANGER("Warning: Critical damage threshold passed. Shut down unit to avoid further damage.")) + else + unit.catastrophic_failure() + return MM_ATTACK_RESULT_BLOCKED|MM_ATTACK_RESULT_DEFLECTED diff --git a/code/modules/augment/simple.dm b/mods/content/augments/simple.dm similarity index 100% rename from code/modules/augment/simple.dm rename to mods/content/augments/simple.dm diff --git a/mods/content/integrated_electronics/_integrated_electronics.dme b/mods/content/integrated_electronics/_integrated_electronics.dme index 7a5c25290367..725bb52ed49e 100644 --- a/mods/content/integrated_electronics/_integrated_electronics.dme +++ b/mods/content/integrated_electronics/_integrated_electronics.dme @@ -10,7 +10,6 @@ #include "random.dm" #include "toggle_circuits_secret.dm" #include "assemblies\_assemblies.dm" -#include "assemblies\circuit_augment.dm" #include "components\_integrated_circuit.dm" #include "components\access.dm" #include "components\arithmetic.dm" diff --git a/mods/content/integrated_electronics/fabricator_designs.dm b/mods/content/integrated_electronics/fabricator_designs.dm index 2018e9d5bae0..0b6dc3289f5a 100644 --- a/mods/content/integrated_electronics/fabricator_designs.dm +++ b/mods/content/integrated_electronics/fabricator_designs.dm @@ -15,6 +15,3 @@ /datum/fabricator_recipe/protolathe/integrated_printer_upgrade_clone path = /obj/item/disk/integrated_circuit/upgrade/clone - -/datum/fabricator_recipe/robotics/augment/circuit - path = /obj/item/organ/internal/augment/active/simple/circuit diff --git a/mods/~compatibility/patches/circuits.dm b/mods/~compatibility/patches/circuits.dm index 457c3b4f855f..afa15ae3f36c 100644 --- a/mods/~compatibility/patches/circuits.dm +++ b/mods/~compatibility/patches/circuits.dm @@ -5,4 +5,8 @@ // Add circuit items to dungeon loot. #ifdef MODPACK_DUNGEON_LOOT #include "circuits/loot_circuits.dm" +#endif +// Add augment assembly for circuits. +#ifdef CONTENT_PACK_AUGMENTS +#include "circuits/augment_circuits.dm" #endif \ No newline at end of file diff --git a/mods/content/integrated_electronics/assemblies/circuit_augment.dm b/mods/~compatibility/patches/circuits/augment_circuits.dm similarity index 90% rename from mods/content/integrated_electronics/assemblies/circuit_augment.dm rename to mods/~compatibility/patches/circuits/augment_circuits.dm index cc3b522aa6ee..842d89dad232 100644 --- a/mods/content/integrated_electronics/assemblies/circuit_augment.dm +++ b/mods/~compatibility/patches/circuits/augment_circuits.dm @@ -35,4 +35,8 @@ holding.canremove = 0 playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) return TRUE - return ..() \ No newline at end of file + return ..() + +// And add a fabricator design +/datum/fabricator_recipe/robotics/augment/circuit + path = /obj/item/organ/internal/augment/active/simple/circuit \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index abb0ae344eab..7a8e35932e63 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1779,20 +1779,6 @@ #include "code\modules\atmospherics\components\unary\unary_base.dm" #include "code\modules\atmospherics\components\unary\vent_pump.dm" #include "code\modules\atmospherics\components\unary\vent_scrubber.dm" -#include "code\modules\augment\active.dm" -#include "code\modules\augment\augment.dm" -#include "code\modules\augment\simple.dm" -#include "code\modules\augment\active\armblades.dm" -#include "code\modules\augment\active\cyberbrain.dm" -#include "code\modules\augment\active\polytool.dm" -#include "code\modules\augment\active\tool\engineering.dm" -#include "code\modules\augment\active\tool\surgical.dm" -#include "code\modules\augment\passive\armor.dm" -#include "code\modules\augment\passive\boost.dm" -#include "code\modules\augment\passive\nanoaura.dm" -#include "code\modules\augment\passive\boost\muscle.dm" -#include "code\modules\augment\passive\boost\reflex.dm" -#include "code\modules\augment\passive\boost\shooting.dm" #include "code\modules\backgrounds\_background.dm" #include "code\modules\backgrounds\background_categories.dm" #include "code\modules\backgrounds\citizenship\_citizenship.dm" @@ -1898,11 +1884,9 @@ #include "code\modules\client\preference_setup\global\05_settings.dm" #include "code\modules\client\preference_setup\global\preferences.dm" #include "code\modules\client\preference_setup\global\prefixes.dm" -#include "code\modules\client\preference_setup\loadout\_defines.dm" #include "code\modules\client\preference_setup\loadout\gear_tweaks.dm" #include "code\modules\client\preference_setup\loadout\loadout.dm" #include "code\modules\client\preference_setup\loadout\lists\accessories.dm" -#include "code\modules\client\preference_setup\loadout\lists\augmentations.dm" #include "code\modules\client\preference_setup\loadout\lists\clothing.dm" #include "code\modules\client\preference_setup\loadout\lists\earwear.dm" #include "code\modules\client\preference_setup\loadout\lists\eyegear.dm" @@ -2402,7 +2386,6 @@ #include "code\modules\fabrication\designs\protolathe\designs_weapons.dm" #include "code\modules\fabrication\designs\replicator\designs_food.dm" #include "code\modules\fabrication\designs\robotics\_designs_robotics.dm" -#include "code\modules\fabrication\designs\robotics\designs_augments.dm" #include "code\modules\fabrication\designs\robotics\designs_misc.dm" #include "code\modules\fabrication\designs\robotics\designs_organs.dm" #include "code\modules\fabrication\designs\robotics\designs_prosthetics.dm" @@ -3111,7 +3094,6 @@ #include "code\modules\mob_modifiers\definitions\modifiers_cloaked.dm" #include "code\modules\mob_modifiers\definitions\modifiers_light.dm" #include "code\modules\mob_modifiers\definitions\modifiers_mech_shields.dm" -#include "code\modules\mob_modifiers\definitions\modifiers_nanoswarm.dm" #include "code\modules\mob_modifiers\definitions\modifiers_object.dm" #include "code\modules\mob_modifiers\definitions\modifiers_prone.dm" #include "code\modules\mob_modifiers\definitions\modifiers_regeneration.dm" From 8d1a8dd178a5bab516bd35beaecc0957054f8a5b Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 30 Dec 2025 22:04:14 +1100 Subject: [PATCH 0958/1331] Automatic changelog generation for PR #5223 [ci skip] --- html/changelogs/AutoChangeLog-pr-5223.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5223.yml diff --git a/html/changelogs/AutoChangeLog-pr-5223.yml b/html/changelogs/AutoChangeLog-pr-5223.yml new file mode 100644 index 000000000000..4a934dba46d3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5223.yml @@ -0,0 +1,5 @@ +author: Penelope Haze +changes: + - {admin: 'Kharmaan nymph jobbans now use the "Mantid Nymph" role in the jobban + menu, rather than the Semi-Antagonist role.'} +delete-after: true From e563b3ffe5313b18a3a7edb4602fe87f8ba4a6c1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 20 Dec 2025 22:15:25 -0500 Subject: [PATCH 0959/1331] Make power debug objects not use the power subtype --- code/modules/power/debug_items.dm | 30 ++++++++++--------- code/modules/power/power.dm | 2 ++ maps/example/example-1.dmm | 2 +- maps/example/example-2.dmm | 2 +- maps/example/example-3.dmm | 2 +- mods/species/ascent/machines/ship_machines.dm | 12 ++++---- 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/code/modules/power/debug_items.dm b/code/modules/power/debug_items.dm index dcb428ebdf8e..35a6efc12070 100644 --- a/code/modules/power/debug_items.dm +++ b/code/modules/power/debug_items.dm @@ -1,17 +1,18 @@ -/obj/machinery/power/debug_items - abstract_type = /obj/machinery/power/debug_items +/obj/machinery/debug_items + abstract_type = /obj/machinery/debug_items icon = 'icons/obj/power.dmi' icon_state = "tracker" anchored = TRUE density = TRUE var/show_extended_information = 1 // Set to 0 to disable extra information on examining (for example, when used on admin events) -/obj/machinery/power/debug_items/examined_by(mob/user) +/obj/machinery/debug_items/examined_by(mob/user) . = ..() if(show_extended_information) show_info(user) -/obj/machinery/power/debug_items/proc/show_info(var/mob/user) +/obj/machinery/debug_items/proc/show_info(var/mob/user) + var/datum/powernet/powernet = get_powernet() if(!powernet) to_chat(user, "This device is not connected to a powernet.") return @@ -25,42 +26,43 @@ // An infinite power generator. Adds energy to connected cable. -/obj/machinery/power/debug_items/infinite_generator +/obj/machinery/debug_items/infinite_generator name = "Fractal Energy Reactor" desc = "An experimental power generator" var/power_generation_rate = 1000000 -/obj/machinery/power/debug_items/infinite_generator/Process() - add_avail(power_generation_rate) +/obj/machinery/debug_items/infinite_generator/Process() + generate_power(power_generation_rate) -/obj/machinery/power/debug_items/infinite_generator/show_info(var/mob/user) +/obj/machinery/debug_items/infinite_generator/show_info(var/mob/user) ..() to_chat(user, "Generator is providing [num2text(power_generation_rate, 20)] W") // A cable powersink, without the explosion/network alarms normal powersink causes. -/obj/machinery/power/debug_items/infinite_cable_powersink +/obj/machinery/debug_items/infinite_cable_powersink name = "Null Point Core" desc = "An experimental device that disperses energy, used for grid testing purposes." var/power_usage_rate = 0 var/last_used = 0 -/obj/machinery/power/debug_items/infinite_cable_powersink/Process() - last_used = draw_power(power_usage_rate) +/obj/machinery/debug_items/infinite_cable_powersink/Process() + var/datum/powernet/powernet = get_powernet() + last_used = powernet.draw_power(power_usage_rate) -/obj/machinery/power/debug_items/infinite_cable_powersink/show_info(var/mob/user) +/obj/machinery/debug_items/infinite_cable_powersink/show_info(var/mob/user) ..() to_chat(user, "Power sink is demanding [num2text(power_usage_rate, 20)] W") to_chat(user, "[num2text(last_used, 20)] W was actually used last tick") -/obj/machinery/power/debug_items/infinite_apc_powersink +/obj/machinery/debug_items/infinite_apc_powersink name = "APC Dummy Load" desc = "A dummy load that connects to an APC, used for load testing purposes." use_power = POWER_USE_ACTIVE active_power_usage = 0 -/obj/machinery/power/debug_items/infinite_apc_powersink/show_info(var/mob/user) +/obj/machinery/debug_items/infinite_apc_powersink/show_info(var/mob/user) ..() to_chat(user, "Dummy load is using [num2text(active_power_usage, 20)] W") to_chat(user, "Powered: [!(stat & NOPOWER) ? "YES" : "NO"]") diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index ff6d5274e982..8cf99bdbeb20 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -2,6 +2,8 @@ // POWER MACHINERY BASE CLASS // This subtype is for machinery which needs to be directly referenced by its parent powernet during powernet processing. // This subtype does not encompass all power generating machinery, or machinery that needs to draw from a powernet in general. +// If you try using this, make sure you can't just use get_powernet() instead. Ideally either the powernet needs to know about it (APCs) +// or we need to run logic when we connect or disconnect from a powernet (solar panels/trackers). ////////////////////////////// ///////////////////////////// diff --git a/maps/example/example-1.dmm b/maps/example/example-1.dmm index 129c5b08e972..500a16664c7c 100644 --- a/maps/example/example-1.dmm +++ b/maps/example/example-1.dmm @@ -334,7 +334,7 @@ /turf/floor/tiled/dark/monotile, /area/example/first) "qR" = ( -/obj/machinery/power/debug_items/infinite_generator, +/obj/machinery/debug_items/infinite_generator, /obj/structure/cable/yellow, /turf/floor, /area/example/first) diff --git a/maps/example/example-2.dmm b/maps/example/example-2.dmm index f1bf69ddda70..51b091fc0d04 100644 --- a/maps/example/example-2.dmm +++ b/maps/example/example-2.dmm @@ -436,7 +436,7 @@ /turf/floor/tiled/steel_grid, /area/example/second) "Ci" = ( -/obj/machinery/power/debug_items/infinite_generator, +/obj/machinery/debug_items/infinite_generator, /obj/structure/cable/yellow, /turf/floor/tiled/white, /area/example/second) diff --git a/maps/example/example-3.dmm b/maps/example/example-3.dmm index 7dda2d9ff4ca..018cb124270b 100644 --- a/maps/example/example-3.dmm +++ b/maps/example/example-3.dmm @@ -198,7 +198,7 @@ /turf/floor/tiled/steel_grid, /area/example/third) "GA" = ( -/obj/machinery/power/debug_items/infinite_generator, +/obj/machinery/debug_items/infinite_generator, /obj/structure/cable/yellow{ icon_state = "0-2" }, diff --git a/mods/species/ascent/machines/ship_machines.dm b/mods/species/ascent/machines/ship_machines.dm index 447467987067..b761299d5138 100644 --- a/mods/species/ascent/machines/ship_machines.dm +++ b/mods/species/ascent/machines/ship_machines.dm @@ -149,7 +149,7 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") // This is an absolutely stupid machine. Basically the same as the debug one with some alterations. // It is a placeholder for a proper reactor setup (probably a RUST descendant) -/obj/machinery/power/ascent_reactor +/obj/machinery/ascent_reactor name = "mantid fusion stack" desc = "A tall, gleaming assemblage of advanced alien machinery. It hums and crackles with restrained power." icon = 'icons/obj/machines/power/fusion_core.dmi' @@ -159,7 +159,7 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") var/output_power = 9000 KILOWATTS var/image/field_image -/obj/machinery/power/ascent_reactor/attack_hand(mob/user) +/obj/machinery/ascent_reactor/attack_hand(mob/user) if(!user.check_dexterity(DEXTERITY_COMPLEX_TOOLS, TRUE)) return ..() if(ishuman(user)) @@ -175,7 +175,7 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") update_icon() return TRUE -/obj/machinery/power/ascent_reactor/on_update_icon() +/obj/machinery/ascent_reactor/on_update_icon() . = ..() if(!field_image) @@ -198,13 +198,13 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") set_light(0) icon_state = "core0" -/obj/machinery/power/ascent_reactor/Initialize() +/obj/machinery/ascent_reactor/Initialize() . = ..() update_icon() -/obj/machinery/power/ascent_reactor/Process() +/obj/machinery/ascent_reactor/Process() if(on) - add_avail(output_power) + generate_power(output_power) /obj/machinery/power/smes/buildable/power_shuttle/ascent name = "mantid battery" From fcf694b6c216e25415fcf9eda77a1f714f72ec19 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 20 Dec 2025 20:18:56 -0500 Subject: [PATCH 0960/1331] Clean up cable/power helper procs --- code/modules/power/cable.dm | 16 ++++++++-------- code/modules/power/power.dm | 33 ++++++++++----------------------- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index b18e5bd16812..4db554905561 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -418,9 +418,9 @@ var/global/list/obj/structure/cable/all_cables = list() . += C if(cable_dir & (cable_dir - 1)) // Diagonal, check for /\/\/\ style cables along cardinal directions for(var/pair in list(NORTH|SOUTH, EAST|WEST)) - T = get_step_resolving_mimic(src, cable_dir & pair) + T = get_step_resolving_mimic(src, cable_dir & pair) // move either vertically or horizontally if(T) - var/req_dir = cable_dir ^ pair + var/req_dir = cable_dir ^ pair // flip along the direction we moved, so if we're NORTHEAST we want a cable to our east that's NORTHWEST for(var/obj/structure/cable/C in T) if(C.d1 == req_dir || C.d2 == req_dir) . += C @@ -452,10 +452,10 @@ var/global/list/obj/structure/cable/all_cables = list() var/turf/T1 = loc if(!T1) return - var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet - if(powerlist.len>0) + var/list/cablelist = cable_list(T1, src, 0) //find the other cables that ended in the centre of the turf, with or without a powernet + if(length(cablelist)) var/datum/powernet/PN = new() - propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable + propagate_network(cablelist[1],PN) //propagates the new powernet beginning at the source cable if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables qdel(PN) @@ -467,12 +467,12 @@ var/global/list/obj/structure/cable/all_cables = list() if(!T1) return if(d1) T1 = get_zstep_resolving_mimic(T1, d1) - P_list = power_list(T1, src, turn(d1,180),0,cable_only = 1) // what adjacently joins on to cut cable... + P_list = cable_list(T1, src, d1) // what adjacently joins on to cut cable... - P_list += power_list(loc, src, d1, 0, cable_only = 1)//... and on turf + P_list += cable_list(loc, src, d1)//... and on turf - if(P_list.len == 0)//if nothing in both list, then the cable was a lone cable, just delete it and its powernet + if(!length(P_list))//if nothing in both list, then the cable was a lone cable, just delete it and its powernet powernet.remove_cable(src) for(var/obj/machinery/power/P in T1)//check if it was powering a machine diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index ff6d5274e982..ebae7df01a30 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -100,30 +100,17 @@ // GLOBAL PROCS for powernets handling ////////////////////////////////////////// - -// returns a list of all power-related objects (nodes, cable, junctions) in turf, -// excluding source, that match the direction d -// if unmarked==1, only return those with no powernet -/proc/power_list(var/turf/T, var/source, var/d, var/unmarked=0, var/cable_only = 0) +/// Returns all cables in target_turf matching target_direction, excluding excluded_cable. +/// If only_no_powernet is TRUE, only cables with no powernet will be returned. +/proc/cable_list(var/turf/target_turf, var/obj/structure/cable/excluded_cable = null, var/target_direction, only_no_powernet = FALSE) . = list() - - var/reverse = d ? global.reverse_dir[d] : 0 - for(var/AM in T) - if(AM == source) continue //we don't want to return source - - if(!cable_only && istype(AM,/obj/machinery/power)) - var/obj/machinery/power/P = AM - if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet - if(d == 0) - . += P - - else if(istype(AM,/obj/structure/cable)) - var/obj/structure/cable/C = AM - - if(!unmarked || !C.powernet) - if(C.d1 == d || C.d2 == d || C.d1 == reverse || C.d2 == reverse ) - . += C - return . + var/reverse_direction = target_direction ? global.reverse_dir[target_direction] : 0 + for(var/obj/structure/cable/other_cable in target_turf) + if(other_cable == excluded_cable) + continue + if(!only_no_powernet || !other_cable.powernet) + if(other_cable.d1 == target_direction || other_cable.d2 == target_direction || other_cable.d1 == reverse_direction || other_cable.d2 == reverse_direction) + . += other_cable //remove the old powernet and replace it with a new one throughout the network. /proc/propagate_network(var/obj/structure/cable/cable, var/datum/powernet/PN) From 9936c12c095d9de2d826a695b8a01b5b8c24ffcf Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 20 Dec 2025 20:49:18 -0500 Subject: [PATCH 0961/1331] Clean up length checks in powernet code --- code/modules/admin/verbs/atmosdebug.dm | 12 ++++++------ code/modules/power/powernet.dm | 7 +++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index 19916f831e9d..2c7947e4a9b0 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -7,14 +7,14 @@ SSstatistics.add_field_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! for (var/datum/powernet/PN in SSmachines.powernets) - if (!PN.nodes || !PN.nodes.len) + if (!LAZYLEN(PN.nodes)) if(PN.cables && (PN.cables.len > 1)) var/obj/structure/cable/C = PN.cables[1] var/area/A = get_area(C.loc) to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [A?.proper_name]") - if (!PN.cables || (PN.cables.len < 10)) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - var/area/A = get_area(C.loc) - to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [A?.proper_name]") + var/cable_count = LAZYLEN(PN.cables) + if (cable_count > 1 && cable_count < 10) + var/obj/structure/cable/C = PN.cables[1] + var/area/A = get_area(C.loc) + to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [A?.proper_name]") diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 6220dc117d20..6727118ba2f8 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -101,10 +101,9 @@ if(problem > 0) problem = max(problem - 1, 0) - if(LAZYLEN(nodes)) // Added to fix a bad list bug -- TLE - for(var/obj/machinery/power/terminal/term in nodes) - if( istype( term.master_machine(), /obj/machinery/apc ) ) - numapc++ + for(var/obj/machinery/power/terminal/term in nodes) + if( istype( term.master_machine(), /obj/machinery/apc ) ) + numapc++ netexcess = avail - load From bfbca927d7c478cf39bd1b5297e6db29e394355a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 20 Dec 2025 22:33:51 -0500 Subject: [PATCH 0962/1331] Avoid unnecessary list allocation in powernet code --- code/modules/power/cable.dm | 18 +++++++++--------- code/modules/power/power.dm | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 4db554905561..c1ad6452b563 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -452,10 +452,10 @@ var/global/list/obj/structure/cable/all_cables = list() var/turf/T1 = loc if(!T1) return - var/list/cablelist = cable_list(T1, src, 0) //find the other cables that ended in the centre of the turf, with or without a powernet - if(length(cablelist)) + var/obj/structure/cable/other_cable = get_matching_cable(T1, src, 0) // find a cable to start a replacement network from, if it exists + if(other_cable) var/datum/powernet/PN = new() - propagate_network(cablelist[1],PN) //propagates the new powernet beginning at the source cable + propagate_network(other_cable,PN) //propagates the new powernet beginning at the source cable if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables qdel(PN) @@ -463,16 +463,16 @@ var/global/list/obj/structure/cable/all_cables = list() // cut the cable's powernet at this cable and updates the powergrid /obj/structure/cable/proc/cut_cable_from_powernet() var/turf/T1 = loc - var/list/P_list + var/obj/structure/cable/other_cable if(!T1) return if(d1) T1 = get_zstep_resolving_mimic(T1, d1) - P_list = cable_list(T1, src, d1) // what adjacently joins on to cut cable... + other_cable = get_matching_cable(T1, src, d1) // check our adjacent turf for connecting cables first + if(!other_cable) + other_cable = get_matching_cable(loc, src, d1) // and fall back to our own turf if we don't find one - P_list += cable_list(loc, src, d1)//... and on turf - - if(!length(P_list))//if nothing in both list, then the cable was a lone cable, just delete it and its powernet + if(!other_cable) // if we didn't find another cable, then the cable was a lone cable, just delete it and its powernet powernet.remove_cable(src) for(var/obj/machinery/power/P in T1)//check if it was powering a machine @@ -485,7 +485,7 @@ var/global/list/obj/structure/cable/all_cables = list() powernet.remove_cable(src) //remove the cut cable from its powernet var/datum/powernet/newPN = new()// creates a new powernet... - propagate_network(P_list[1], newPN)//... and propagates it to the other side of the cable + propagate_network(other_cable, newPN)//... and propagates it to the other side of the cable // Disconnect machines connected to nodes if(d1 == 0) // if we cut a node (O-X) cable diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index ebae7df01a30..43fcffe4c681 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -102,15 +102,24 @@ /// Returns all cables in target_turf matching target_direction, excluding excluded_cable. /// If only_no_powernet is TRUE, only cables with no powernet will be returned. -/proc/cable_list(var/turf/target_turf, var/obj/structure/cable/excluded_cable = null, var/target_direction, only_no_powernet = FALSE) +/// Unused, because get_maching_cable or get_connected_cables is usually preferable, but kept just in case. +/proc/cable_list(var/turf/target_turf, var/obj/structure/cable/excluded_cable = null, var/target_direction) . = list() var/reverse_direction = target_direction ? global.reverse_dir[target_direction] : 0 for(var/obj/structure/cable/other_cable in target_turf) if(other_cable == excluded_cable) continue - if(!only_no_powernet || !other_cable.powernet) - if(other_cable.d1 == target_direction || other_cable.d2 == target_direction || other_cable.d1 == reverse_direction || other_cable.d2 == reverse_direction) - . += other_cable + if(other_cable.d1 == target_direction || other_cable.d2 == target_direction || other_cable.d1 == reverse_direction || other_cable.d2 == reverse_direction) + . += other_cable + +/// Like cable_list, but only returns the first cable, since that's all most uses of it check. +/proc/get_matching_cable(var/turf/target_turf, var/obj/structure/cable/excluded_cable = null, var/target_direction) + var/reverse_direction = target_direction ? global.reverse_dir[target_direction] : 0 + for(var/obj/structure/cable/other_cable in target_turf) + if(other_cable == excluded_cable) + continue + if(other_cable.d1 == target_direction || other_cable.d2 == target_direction || other_cable.d1 == reverse_direction || other_cable.d2 == reverse_direction) + return other_cable //remove the old powernet and replace it with a new one throughout the network. /proc/propagate_network(var/obj/structure/cable/cable, var/datum/powernet/PN) From aad616bcadc1f06a90a6c56ba40f0697364facfb Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 21 Jan 2025 12:29:58 -0500 Subject: [PATCH 0963/1331] Add support for saltwater turfs --- code/controllers/subsystems/fluids.dm | 5 +++- code/game/turfs/floors/_floor.dm | 9 +++++-- .../turfs/floors/subtypes/floor_concrete.dm | 4 ++++ .../turfs/floors/subtypes/floor_natural.dm | 24 +++++++++++++++++++ code/game/turfs/open/_open.dm | 5 ++++ code/game/turfs/turf.dm | 8 +++++++ code/game/turfs/unsimulated/mask.dm | 5 ++++ code/game/turfs/walls/wall_natural.dm | 4 ++++ code/modules/fluids/fluid_flood.dm | 2 ++ code/modules/multiz/turf_mimic_edge.dm | 4 ++++ code/modules/reagents/Chemistry-Holder.dm | 10 ++++++++ 11 files changed, 77 insertions(+), 3 deletions(-) diff --git a/code/controllers/subsystems/fluids.dm b/code/controllers/subsystems/fluids.dm index b05b4ed11f8a..e0248091c4ea 100644 --- a/code/controllers/subsystems/fluids.dm +++ b/code/controllers/subsystems/fluids.dm @@ -71,7 +71,10 @@ SUBSYSTEM_DEF(fluids) continue checked_targets[neighbor] = TRUE flooded_a_neighbor = TRUE - neighbor.add_to_reagents(current_fluid_holder.flooded, FLUID_MAX_DEPTH) + if(current_fluid_holder.contaminant_reagent_type && current_fluid_holder.contaminant_proportion) + neighbor.add_to_reagents_contaminated(current_fluid_holder.flooded, FLUID_MAX_DEPTH, contaminant_type = current_fluid_holder.contaminant_reagent_type, contaminant_proportion = current_fluid_holder.contaminant_proportion) + else + neighbor.add_to_reagents(current_fluid_holder.flooded, FLUID_MAX_DEPTH) if(!flooded_a_neighbor) REMOVE_ACTIVE_FLUID_SOURCE(current_fluid_holder) diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 3982c3dab82d..515dcaca81dd 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -11,7 +11,7 @@ zone_membership_candidate = TRUE open_turf_type = /turf/open/airless - // Reagent to use to fill the turf. + /// Reagent to use to refill trenches to capacity automatically. var/fill_reagent_type var/can_engrave = TRUE @@ -69,7 +69,12 @@ var/my_height = get_physical_height() if(fill_reagent_type && my_height < 0 && (!reagents || !QDELING(reagents)) && REAGENT_TOTAL_VOLUME(reagents) < abs(my_height)) var/reagents_to_add = abs(my_height) - REAGENT_TOTAL_VOLUME(reagents) - add_to_reagents(fill_reagent_type, reagents_to_add, phase = MAT_PHASE_LIQUID) + var/contaminant_to_add = 0 + if(contaminant_reagent_type) + contaminant_to_add = CHEMS_QUANTIZE(reagents_to_add * contaminant_proportion) + add_to_reagents(fill_reagent_type, reagents_to_add - contaminant_to_add, phase = MAT_PHASE_LIQUID, defer_update = !!contaminant_to_add) + if(contaminant_to_add) + add_to_reagents(contaminant_reagent_type, contaminant_to_add, phase = MAT_PHASE_LIQUID) /turf/floor/can_climb_from_below(var/mob/climber) return TRUE diff --git a/code/game/turfs/floors/subtypes/floor_concrete.dm b/code/game/turfs/floors/subtypes/floor_concrete.dm index 60303ef1ee79..f52e66144503 100644 --- a/code/game/turfs/floors/subtypes/floor_concrete.dm +++ b/code/game/turfs/floors/subtypes/floor_concrete.dm @@ -13,6 +13,10 @@ flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER +/turf/floor/concrete/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/floor/concrete/reinforced name = "reinforced concrete" icon_state = "hexacrete" diff --git a/code/game/turfs/floors/subtypes/floor_natural.dm b/code/game/turfs/floors/subtypes/floor_natural.dm index ae7350535805..43d7b34ac45e 100644 --- a/code/game/turfs/floors/subtypes/floor_natural.dm +++ b/code/game/turfs/floors/subtypes/floor_natural.dm @@ -85,13 +85,25 @@ height = -(FLUID_SHALLOW) fill_reagent_type = /decl/material/liquid/water +/turf/floor/mud/water/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/floor/mud/water/deep color = COLOR_BLUE height = -(FLUID_DEEP) +/turf/floor/mud/water/deep/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/mud/flooded flooded = /decl/material/liquid/water +/turf/floor/mud/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/dry name = "dry mud" icon = 'icons/turf/flooring/seafloor.dmi' @@ -117,10 +129,18 @@ height = -(FLUID_SHALLOW) fill_reagent_type = /decl/material/liquid/water +/turf/floor/rock/sand/water/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/rock/sand/water/deep color = COLOR_BLUE height = -(FLUID_DEEP) +/turf/floor/rock/sand/water/deep/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/seafloor name = "sea floor" icon = 'icons/turf/flooring/seafloor.dmi' @@ -131,6 +151,10 @@ flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER +/turf/floor/seafloor/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/shrouded name = "packed sand" icon = 'icons/turf/flooring/shrouded.dmi' diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index abbcfecc695a..186c84cb2642 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -27,6 +27,11 @@ name = "open water" flooded = /decl/material/liquid/water +/turf/open/flooded/salt + name = "open saltwater" // alt. ver: open ocean? + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/open/Entered(var/atom/movable/mover, var/atom/oldloc) ..() mover.fall(oldloc) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 33828543cd5d..fdc9684e137f 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -38,6 +38,14 @@ var/footstep_type var/open_turf_type = /turf/open // Which open turf type to use by default above this turf in a multiz context. Overridden by area. + // If you ever need to refill or flood a turf with more than two reagents, this should be rewritten entirely. + // The reason it's written like this is to avoid creating a new list for every turf with contaminants + // and that should still hold up even if you have turfs with three or more liquids in the mixture. + /// Reagent to contaminate refilled or flooded reagents. + var/contaminant_reagent_type + /// What fraction of the refilled/flooded liquid should be the contaminant? If zero, no contaminant is added. + var/contaminant_proportion + var/tmp/changing_turf var/tmp/prev_type // Previous type of the turf, prior to turf translation. diff --git a/code/game/turfs/unsimulated/mask.dm b/code/game/turfs/unsimulated/mask.dm index 1b82356696eb..a380cfa054dc 100644 --- a/code/game/turfs/unsimulated/mask.dm +++ b/code/game/turfs/unsimulated/mask.dm @@ -9,6 +9,11 @@ icon_state = "rockvault" color = COLOR_SILVER +// Why do these exist? Are they just for typechecks when generating random maps? Does the flooding code even run for unsim turfs? /turf/unsimulated/mask/flooded flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER + +/turf/unsimulated/mask/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt \ No newline at end of file diff --git a/code/game/turfs/walls/wall_natural.dm b/code/game/turfs/walls/wall_natural.dm index 4710e89e5626..8b8fdab59083 100644 --- a/code/game/turfs/walls/wall_natural.dm +++ b/code/game/turfs/walls/wall_natural.dm @@ -20,6 +20,10 @@ var/global/_wall_chisel_skill = SKILL_CONSTRUCTION flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER +/turf/wall/natural/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/wall/natural/get_paint_examine_message() return SPAN_NOTICE("It has been noticeably discoloured by the elements.") diff --git a/code/modules/fluids/fluid_flood.dm b/code/modules/fluids/fluid_flood.dm index 47a48f0f5e95..7b3f2a333840 100644 --- a/code/modules/fluids/fluid_flood.dm +++ b/code/modules/fluids/fluid_flood.dm @@ -1,5 +1,7 @@ // Permaflood overlay. var/global/list/flood_type_overlay_cache = list() +// TODO: does this need to also take contaminant type as an argument? flooding contaminants are totally untested +// also, do flooded turfs even apply fluid_act and touch effects? /proc/get_flood_overlay(fluid_type) if(!ispath(fluid_type, /decl/material)) return null diff --git a/code/modules/multiz/turf_mimic_edge.dm b/code/modules/multiz/turf_mimic_edge.dm index df31a9f6bdfc..512b1f7fd629 100644 --- a/code/modules/multiz/turf_mimic_edge.dm +++ b/code/modules/multiz/turf_mimic_edge.dm @@ -208,6 +208,10 @@ /turf/mimic_edge/transition/flooded flooded = /decl/material/liquid/water +/turf/mimic_edge/transition/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + //////////////////////////////// // Loop Edges //////////////////////////////// diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 629091fd1096..a9c135a0b29c 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -10,6 +10,16 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /atom/proc/remove_any_reagents(amount = 1, defer_update = FALSE, removed_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID), skip_reagents = null) return reagents?.remove_any(amount, defer_update, removed_phases, skip_reagents) +/// Adds reagents, but contaminated. A fraction of `amount` is replaced with `contaminant_type` according to `contaminant_proportion`. +/// Handles null contaminant_type and zero contaminant_proportion, but it's probably faster to check before you call this. +/atom/proc/add_to_reagents_contaminated(reagent_type, amount, data, contaminant_type = null, contaminant_proportion = 0, safety = FALSE, defer_update = FALSE, phase = null) + var/contaminant_to_add = 0 + if(contaminant_type) + contaminant_to_add = CHEMS_QUANTIZE(amount * contaminant_proportion) + add_to_reagents(reagent_type, amount - contaminant_to_add, data, safety = safety, defer_update = !!contaminant_to_add, phase = MAT_PHASE_LIQUID) + if(contaminant_to_add) + add_to_reagents(contaminant_type, contaminant_to_add, phase = MAT_PHASE_LIQUID) + /atom/proc/get_reagent_space() if(!REAGENT_MAXIMUM_VOLUME(reagents)) return 0 From c999bdb6dc95d6e4d896f60186100a335bd51417 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 26 Sep 2025 13:37:09 -0400 Subject: [PATCH 0964/1331] Add flavor strings to flooded turfs --- code/game/turfs/floors/_floor.dm | 5 +++++ code/game/turfs/floors/floor_icon.dm | 3 +++ 2 files changed, 8 insertions(+) diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 515dcaca81dd..6739729434c2 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -76,6 +76,11 @@ if(contaminant_to_add) add_to_reagents(contaminant_reagent_type, contaminant_to_add, phase = MAT_PHASE_LIQUID) +/turf/floor/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(check_fluid_depth(FLUID_SHALLOW)) + . += SPAN_NOTICE("It has a pool of [get_fluid_name()].") + /turf/floor/can_climb_from_below(var/mob/climber) return TRUE diff --git a/code/game/turfs/floors/floor_icon.dm b/code/game/turfs/floors/floor_icon.dm index 2b799313b9b1..9e9fac1ac326 100644 --- a/code/game/turfs/floors/floor_icon.dm +++ b/code/game/turfs/floors/floor_icon.dm @@ -128,6 +128,9 @@ else SetName(initial(name)) desc = initial(desc) + // do this once name and desc have been updated + if(check_fluid_depth(FLUID_SHALLOW)) + SetName(get_fluid_name()) // just entirely overwrite name, but keep desc /turf/floor/proc/update_floor_icon() var/decl/flooring/use_flooring = get_topmost_flooring() From eecb58cc26261b666f7f6cac7676290cc3b284f6 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 26 Sep 2025 13:37:22 -0400 Subject: [PATCH 0965/1331] Readd missing update_turf_strings call --- code/game/turfs/floors/floor_icon.dm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/game/turfs/floors/floor_icon.dm b/code/game/turfs/floors/floor_icon.dm index 9e9fac1ac326..531fa45d1250 100644 --- a/code/game/turfs/floors/floor_icon.dm +++ b/code/game/turfs/floors/floor_icon.dm @@ -123,8 +123,7 @@ /turf/floor/proc/update_floor_strings() var/decl/flooring/flooring = get_topmost_flooring() if(istype(flooring)) - SetName(flooring.name) - desc = flooring.desc + flooring.update_turf_strings(src) else SetName(initial(name)) desc = initial(desc) From 89f529aad510545372bbfef6b61fdc955a09833b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 26 Sep 2025 13:43:45 -0400 Subject: [PATCH 0966/1331] Fix get_fluid_name() for flooded turfs --- code/game/turfs/turf_fluids.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index fe248f689773..21227d330c38 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -75,8 +75,8 @@ . = (get_fluid_depth() >= min) /turf/proc/get_fluid_name() - var/decl/material/mat = reagents?.get_primary_reagent_decl() - return mat.get_reagent_name(reagents, MAT_PHASE_LIQUID) || "liquid" + var/decl/material/mat = reagents?.get_primary_reagent_decl() || RESOLVE_TO_DECL(flooded) + return mat?.get_reagent_name(reagents, MAT_PHASE_LIQUID) || "liquid" /turf/get_fluid_depth() if(is_flooded(absolute=1)) From 75af494ac8aa5eee5dc2260cf9f6773e0e5a20cc Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 26 Sep 2025 14:18:18 -0400 Subject: [PATCH 0967/1331] Fix base rock and path turfs runtiming in init --- code/game/turfs/flooring/flooring_path.dm | 2 +- code/game/turfs/flooring/flooring_rock.dm | 2 +- code/game/turfs/floors/subtypes/floor_path.dm | 4 +++- code/game/turfs/floors/subtypes/floor_rock.dm | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/code/game/turfs/flooring/flooring_path.dm b/code/game/turfs/flooring/flooring_path.dm index 6a934d5c5eb3..1f08b1719eb9 100644 --- a/code/game/turfs/flooring/flooring_path.dm +++ b/code/game/turfs/flooring/flooring_path.dm @@ -16,7 +16,7 @@ var/paver_noun = "stones" /decl/flooring/path/update_turf_strings(turf/floor/target) - var/decl/material/floor_material = target?.get_material() + var/decl/material/floor_material = RESOLVE_TO_DECL(target?.get_material()) ASSERT(floor_material?.adjective_name) ASSERT(paver_noun) target.SetName("[floor_material.adjective_name] [name]") diff --git a/code/game/turfs/flooring/flooring_rock.dm b/code/game/turfs/flooring/flooring_rock.dm index 13836a0a7da7..ba6caee762a7 100644 --- a/code/game/turfs/flooring/flooring_rock.dm +++ b/code/game/turfs/flooring/flooring_rock.dm @@ -10,7 +10,7 @@ uid = "floor_reinf_shuttle_rock" /decl/flooring/rock/update_turf_strings(turf/floor/target) - var/decl/material/turf_material = target?.get_material() + var/decl/material/turf_material = RESOLVE_TO_DECL(target?.get_material()) ASSERT(turf_material?.adjective_name) target.SetName("[turf_material.adjective_name] [name]") target.desc = "An expanse of bare [turf_material.solid_name]." diff --git a/code/game/turfs/floors/subtypes/floor_path.dm b/code/game/turfs/floors/subtypes/floor_path.dm index b67e06bf1b70..bded9334f69b 100644 --- a/code/game/turfs/floors/subtypes/floor_path.dm +++ b/code/game/turfs/floors/subtypes/floor_path.dm @@ -11,8 +11,10 @@ _base_flooring = /decl/flooring/dirt /turf/floor/path/Initialize(mapload, no_update_icon) + // Take advantage of the set_turf_materials call in ..() + // to avoid doing pointless work + material ||= get_strata_material_type() || /decl/material/solid/stone/sandstone . = ..() - set_turf_materials(material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) if(mapload && is_outside() && prob(20)) var/image/moss = image('icons/effects/decals/plant_remains.dmi', "leafy_bits", DECAL_LAYER) moss.pixel_x = rand(-6, 6) diff --git a/code/game/turfs/floors/subtypes/floor_rock.dm b/code/game/turfs/floors/subtypes/floor_rock.dm index a0e1194fda70..704fd1ad5946 100644 --- a/code/game/turfs/floors/subtypes/floor_rock.dm +++ b/code/game/turfs/floors/subtypes/floor_rock.dm @@ -5,8 +5,9 @@ _base_flooring = /decl/flooring/rock /turf/floor/rock/Initialize(mapload, no_update_icon) + // Take advantage of the set_turf_materials call in ..() + material ||= get_strata_material_type() || /decl/material/solid/stone/sandstone . = ..() - set_turf_materials(material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) /turf/floor/rock/volcanic name = "volcanic floor" From f0ad1a4c7acfbd0e2aa7b84eac9a8fc8e5f4a203 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 30 Dec 2025 14:27:06 -0500 Subject: [PATCH 0968/1331] Add a proc to allow custom vendor slogans --- code/game/machinery/vending/_vending.dm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index 0587601e9378..5fa543120f98 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -404,6 +404,11 @@ SSnano.update_uis(src) +/// Used to get a slogan to say in Process(). +/// If you want to do things like non-constant token replacement you can do that in an override. +/obj/machinery/vending/proc/get_slogan() + return pick(slogan_list) + /obj/machinery/vending/Process() if(stat & (BROKEN|NOPOWER)) return @@ -415,8 +420,8 @@ seconds_electrified-- //Pitch to the people! Really sell it! - if(((last_slogan + slogan_delay) <= world.time) && (slogan_list.len > 0) && (!shut_up) && prob(5)) - var/slogan = pick(slogan_list) + if(((last_slogan + slogan_delay) <= world.time) && length(slogan_list) && (!shut_up) && prob(5)) + var/slogan = get_slogan() speak(slogan) last_slogan = world.time From d786fd141a71e0e52f62bb1843fb6d296b309b36 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 18:52:43 -0500 Subject: [PATCH 0969/1331] Make maint_all_access defined on map --- code/game/jobs/access.dm | 2 +- maps/~mapsystem/maps.dm | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index 20cd9d5a395b..4acbcff8b72f 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -66,7 +66,7 @@ if(!istype(supplied_access, /list)) return FALSE - if(maint_all_access) + if(global.using_map.maint_all_access) // todo: movable -> loc -> area -> map datum, to allow separate maps? supplied_access = supplied_access.Copy() supplied_access |= access_maint_tunnels diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index 04ce37b278c6..3a161bc41eab 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -207,6 +207,9 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable var/default_ui_style + /// Is maint currently all-access? + var/maint_all_access = FALSE + /datum/map/New() ..() default_ui_style ||= DEFAULT_UI_STYLE @@ -475,11 +478,11 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable return /datum/map/proc/make_maint_all_access(var/radstorm = 0) - maint_all_access = 1 + maint_all_access = TRUE priority_announcement.Announce("The maintenance access requirement has been revoked on all maintenance airlocks.", "Attention!") /datum/map/proc/revoke_maint_all_access(var/radstorm = 0) - maint_all_access = 0 + maint_all_access = FALSE priority_announcement.Announce("The maintenance access requirement has been readded on all maintenance airlocks.", "Attention!") /datum/map/proc/show_titlescreen(client/C) From e3b546ddbfef4eff78969009b7b53c0a5a7542be Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sun, 28 Dec 2025 18:55:22 -0500 Subject: [PATCH 0970/1331] Make keycard auth device use decls --- .../security_levels/keycard_authentication.dm | 76 ++++----------- .../keycard_authentication_events.dm | 96 +++++++++++++++++++ nebula.dme | 1 + 3 files changed, 115 insertions(+), 58 deletions(-) create mode 100644 code/modules/security_levels/keycard_authentication_events.dm diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm index eca9534b53ce..cb91e3c505c1 100644 --- a/code/modules/security_levels/keycard_authentication.dm +++ b/code/modules/security_levels/keycard_authentication.dm @@ -65,6 +65,13 @@ interact(user) return TRUE +/obj/machinery/keycard_auth/proc/get_event_options(mob/user) + . = list() + for(var/decl/keycard_auth_event/event in global.decls_repository.get_decls_of_subtype_unassociated(/decl/keycard_auth_event)) + if(!event.is_available(src, user)) + continue + . += event.get_option(src, user) + /obj/machinery/keycard_auth/interact(mob/user) user.set_machine(src) @@ -74,27 +81,12 @@ dat += "


    " if(screen == 1) - dat += "Select an event to trigger:" + var/list/options = get_event_options(user) + dat += "Select an event to trigger:
    • [options.Join("
    • ")]
    " show_browser(user, dat, "window=keycard_auth;size=500x250") if(screen == 2) - dat += "Please swipe your card to authorize the following event: [event]" + var/decl/keycard_auth_event/real_event = global.decls_repository.get_decl_by_id(event) + dat += "Please swipe your card to authorize the following event: [real_event.get_link_text()]" dat += "

    Back" show_browser(user, dat, "window=keycard_auth;size=500x250") return @@ -144,8 +136,10 @@ /obj/machinery/keycard_auth/proc/broadcast_check() if(confirmed) confirmed = 0 + var/decl/keycard_auth_event/real_event = global.decls_repository.get_decl_by_id(event) + var/cached_name = real_event.get_link_text() // because triggering the event can change the name trigger_event(event) - log_and_message_admins("triggered and [key_name(event_confirmed_by)] confirmed event [event]", event_triggered_by || usr) + log_and_message_admins("triggered and [key_name(event_confirmed_by)] confirmed event [cached_name]", event_triggered_by || usr) reset() /obj/machinery/keycard_auth/proc/receive_request(var/obj/machinery/keycard_auth/source, obj/item/card/id/ID) @@ -166,45 +160,11 @@ busy = 0 /obj/machinery/keycard_auth/proc/trigger_event() - switch(event) - if("Red alert") - var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) - security_state.stored_security_level = security_state.current_security_level - security_state.set_security_level(security_state.high_security_level) - SSstatistics.add_field("alert_keycard_auth_red",1) - if("Revert alert") - var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) - security_state.set_security_level(security_state.stored_security_level) - SSstatistics.add_field("alert_keycard_revert_red",1) - if("Grant Emergency Maintenance Access") - global.using_map.make_maint_all_access() - SSstatistics.add_field("alert_keycard_auth_maintGrant",1) - if("Revoke Emergency Maintenance Access") - global.using_map.revoke_maint_all_access() - SSstatistics.add_field("alert_keycard_auth_maintRevoke",1) - if("Emergency Response Team") - if(is_ert_blocked()) - visible_message(SPAN_WARNING("\The [src] blinks and displays a message: All emergency response teams are dispatched and can not be called at this time."), range=2) - return - - trigger_armed_response_team(1) - SSstatistics.add_field("alert_keycard_auth_ert",1) - if("Grant Nuclear Authorization Code") - var/obj/machinery/nuclearbomb/nuke = locate(/obj/machinery/nuclearbomb/station) in SSmachines.machinery - if(nuke) - visible_message(SPAN_WARNING("\The [src] blinks and displays a message: The nuclear authorization code is [nuke.r_code]"), range=2) - else - visible_message(SPAN_WARNING("\The [src] blinks and displays a message: No self-destruct terminal found."), range=2) - SSstatistics.add_field("alert_keycard_auth_nukecode",1) - -/obj/machinery/keycard_auth/proc/is_ert_blocked() - if(get_config_value(/decl/config/toggle/ert_admin_call_only)) - return TRUE - return SSticker.mode?.ert_disabled + var/decl/keycard_auth_event/the_event = decls_repository.get_decl_by_id(event) + if(the_event) + the_event.on_event(src) /obj/machinery/keycard_auth/update_directional_offset(force = FALSE) if(!force && (!length(directional_offset) || !is_wall_mounted())) //Check if the thing is actually mapped onto a table or something return - . = ..() - -var/global/maint_all_access = 0 \ No newline at end of file + . = ..() \ No newline at end of file diff --git a/code/modules/security_levels/keycard_authentication_events.dm b/code/modules/security_levels/keycard_authentication_events.dm new file mode 100644 index 000000000000..76b36018a0b1 --- /dev/null +++ b/code/modules/security_levels/keycard_authentication_events.dm @@ -0,0 +1,96 @@ +/decl/keycard_auth_event + abstract_type = /decl/keycard_auth_event + decl_flags = DECL_FLAG_MANDATORY_UID + var/name = "Abstract Keycard Authentication Event" + +/decl/keycard_auth_event/proc/get_link_text(obj/machinery/keycard_auth/auth, mob/user) + return name + +/decl/keycard_auth_event/proc/is_available(obj/machinery/keycard_auth/auth, mob/user) + return TRUE + +/decl/keycard_auth_event/proc/get_option(obj/machinery/keycard_auth/auth, mob/user) + SHOULD_NOT_OVERRIDE(TRUE) + var/fail_reason = get_failure_reason(user) + if(fail_reason) + return fail_reason + return "[get_link_text(auth, user)]" + +/decl/keycard_auth_event/proc/get_failure_reason(obj/machinery/keycard_auth/auth, mob/user) + return + +/decl/keycard_auth_event/proc/on_event(obj/machinery/keycard_auth/auth) + return + +/decl/keycard_auth_event/high_security + name = "Toggle High Security Level" + uid = "keycard_event_toggle_high_security" + +/decl/keycard_auth_event/high_security/get_failure_reason(obj/machinery/keycard_auth/auth, mob/user) + var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) + if(security_state.current_security_level == security_state.severe_security_level) + return "Cannot modify the alert level at this time: [security_state.severe_security_level.name] engaged." + +/decl/keycard_auth_event/high_security/get_link_text(obj/machinery/keycard_auth/auth, mob/user) + var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) + if(security_state.current_security_level == security_state.high_security_level) // toggle! + return "Disengage [security_state.high_security_level.name]" + else + return "Engage [security_state.high_security_level.name]" + +/decl/keycard_auth_event/high_security/on_event(obj/machinery/keycard_auth/auth, mob/user) + var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) + if(security_state.current_security_level == security_state.high_security_level) + security_state.set_security_level(security_state.stored_security_level) + SSstatistics.add_field("alert_keycard_revert_red",1) + else + security_state.stored_security_level = security_state.current_security_level + security_state.set_security_level(security_state.high_security_level) + SSstatistics.add_field("alert_keycard_auth_red",1) + +/decl/keycard_auth_event/call_ert + name = "Emergency Response Team" + uid = "keycard_event_call_ert" + +/decl/keycard_auth_event/call_ert/is_available(obj/machinery/keycard_auth/auth, mob/user) + if(get_config_value(/decl/config/toggle/ert_admin_call_only)) + return FALSE + return TRUE + +/decl/keycard_auth_event/call_ert/on_event(obj/machinery/keycard_auth/auth) + if(!SSticker.mode || SSticker.mode.ert_disabled) // disabled by mode + auth.visible_message(SPAN_WARNING("\The [src] blinks and displays a message: All emergency response teams are dispatched and can not be called at this time."), range=2) + return + + trigger_armed_response_team(1) + SSstatistics.add_field("alert_keycard_auth_ert",1) + +/decl/keycard_auth_event/maintenance_access + name = "Toggle Emergency Maintenance Access" + uid = "keycard_event_maintenance_access" + +/decl/keycard_auth_event/maintenance_access/get_link_text(obj/machinery/keycard_auth/auth, mob/user) + if(global.using_map.maint_all_access) + return "Revoke Emergency Maintenance Access" + else + return "Grant Emergency Maintenance Access" + +/decl/keycard_auth_event/maintenance_access/on_event(obj/machinery/keycard_auth/auth) + if(global.using_map.maint_all_access) + global.using_map.revoke_maint_all_access() + SSstatistics.add_field("alert_keycard_auth_maintRevoke",1) + else + global.using_map.make_maint_all_access() + SSstatistics.add_field("alert_keycard_auth_maintGrant",1) + +/decl/keycard_auth_event/nuke_code + name = "Grant Nuclear Authorization Code" + uid = "keycard_event_nuke_code" + +/decl/keycard_auth_event/nuke_code/on_event(obj/machinery/keycard_auth/auth) + var/obj/machinery/nuclearbomb/nuke = locate(/obj/machinery/nuclearbomb/station) in SSmachines.machinery + if(nuke) + auth.visible_message(SPAN_WARNING("\The [src] blinks and displays a message: The nuclear authorization code is [nuke.r_code]"), range=2) + else + auth.visible_message(SPAN_WARNING("\The [src] blinks and displays a message: No self-destruct terminal found."), range=2) + SSstatistics.add_field("alert_keycard_auth_nukecode",1) \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index 97f30cde7b26..6740b3302927 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3748,6 +3748,7 @@ #include "code\modules\security_levels\_security_level.dm" #include "code\modules\security_levels\alarm_appearance.dm" #include "code\modules\security_levels\keycard_authentication.dm" +#include "code\modules\security_levels\keycard_authentication_events.dm" #include "code\modules\security_levels\security_levels.dm" #include "code\modules\security_levels\security_state.dm" #include "code\modules\shield_generators\floor_diffuser.dm" From 886719904720748b6aaf8692602c0ba8d8db82b9 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 31 Dec 2025 11:22:48 +1100 Subject: [PATCH 0971/1331] Removes now-unneeded drake marking override. --- mods/~compatibility/patches/drakes/fantasy_drakes.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/mods/~compatibility/patches/drakes/fantasy_drakes.dm b/mods/~compatibility/patches/drakes/fantasy_drakes.dm index f2f20a7a00f3..7c9279184ba2 100644 --- a/mods/~compatibility/patches/drakes/fantasy_drakes.dm +++ b/mods/~compatibility/patches/drakes/fantasy_drakes.dm @@ -6,9 +6,6 @@ They are commonly found living in caves or burrows bordering grassland or forest, and while they prefer to hunt deer or rabbits, they will sometimes attack travellers if pickings are slim enough. \ While they are not domesticated, they can be habituated and trained as working animals if captured young enough." -/decl/sprite_accessory/marking/grafadreka - species_allowed = list("Meredrake") - /decl/language/grafadreka desc = "Hiss hiss, feed me rabbits." From b4f6be058b0ca5a5882fa1a42cfa8d527d66f1fe Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 30 Dec 2025 14:32:03 -0500 Subject: [PATCH 0972/1331] Add map migration --- tools/map_migrations/5320_power_debug.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tools/map_migrations/5320_power_debug.txt diff --git a/tools/map_migrations/5320_power_debug.txt b/tools/map_migrations/5320_power_debug.txt new file mode 100644 index 000000000000..b5a1d738e495 --- /dev/null +++ b/tools/map_migrations/5320_power_debug.txt @@ -0,0 +1,2 @@ +/obj/machinery/power/debug_items/@SUBTYPES : /obj/machinery/debug_items/@SUBTYPES{@OLD} +/obj/machinery/power/ascent_reactor/@SUBTYPES : /obj/machinery/ascent_reactor/@SUBTYPES{@OLD} \ No newline at end of file From 092d7c1ec11e914c4a2ebfcf9ede3454ca95c490 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Wed, 31 Dec 2025 11:39:15 +1100 Subject: [PATCH 0973/1331] Prevents tile removal sound on non-tile floor removal. --- code/game/turfs/flooring/_flooring.dm | 4 +++- code/game/turfs/flooring/flooring_reinforced.dm | 1 + code/game/turfs/flooring/flooring_tiled.dm | 3 +++ code/game/turfs/floors/subtypes/floor_reinforced.dm | 8 ++++---- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index f5f8083873f8..a93566d69e38 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -19,6 +19,7 @@ var/global/list/flooring_cache = list() var/color = COLOR_WHITE var/footstep_type = /decl/footsteps/plating var/growth_value = 0 + var/deconstruct_sound var/neighbour_type @@ -295,7 +296,8 @@ var/global/list/flooring_cache = list() return TRUE to_chat(user, SPAN_NOTICE("You remove the [get_surface_descriptor()] with \the [item].")) floor.remove_flooring(floor.get_topmost_flooring(), place_product = TRUE) - playsound(floor, 'sound/items/Deconstruct.ogg', 80, 1) + if(deconstruct_sound) + playsound(floor, deconstruct_sound, 80, 1) return TRUE if(constructed) diff --git a/code/game/turfs/flooring/flooring_reinforced.dm b/code/game/turfs/flooring/flooring_reinforced.dm index 38234d29ce8e..a48566d08613 100644 --- a/code/game/turfs/flooring/flooring_reinforced.dm +++ b/code/game/turfs/flooring/flooring_reinforced.dm @@ -24,6 +24,7 @@ "broken4" ) uid = "floor_reinf" + deconstruct_sound = 'sound/items/Deconstruct.ogg' /decl/flooring/reinforced/circuit name = "processing strata" diff --git a/code/game/turfs/flooring/flooring_tiled.dm b/code/game/turfs/flooring/flooring_tiled.dm index e2b4af68c879..4b762fffeef1 100644 --- a/code/game/turfs/flooring/flooring_tiled.dm +++ b/code/game/turfs/flooring/flooring_tiled.dm @@ -15,6 +15,8 @@ space_smooth = SMOOTH_ALL constructed = TRUE gender = NEUTER + deconstruct_sound = 'sound/items/Deconstruct.ogg' + burned_states = list( "burned0", "burned1" @@ -87,6 +89,7 @@ color = null build_type = null uid = "floor_tiled_new" + deconstruct_sound = 'sound/items/Deconstruct.ogg' /decl/flooring/tiling/new_tile/cargo_one icon_base = "cargo_one_full" diff --git a/code/game/turfs/floors/subtypes/floor_reinforced.dm b/code/game/turfs/floors/subtypes/floor_reinforced.dm index 5ea377aa3961..551786ed357b 100644 --- a/code/game/turfs/floors/subtypes/floor_reinforced.dm +++ b/code/game/turfs/floors/subtypes/floor_reinforced.dm @@ -1,8 +1,8 @@ /turf/floor/reinforced - name = "reinforced floor" - icon = 'icons/turf/flooring/tiles.dmi' - icon_state = "reinforced" - _flooring = /decl/flooring/reinforced + name = "reinforced floor" + icon = 'icons/turf/flooring/tiles.dmi' + icon_state = "reinforced" + _flooring = /decl/flooring/reinforced /turf/floor/reinforced/airless initial_gas = null From afcafb9523450011dc17673f5b1f058ffab040be Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Wed, 31 Dec 2025 01:03:19 +0000 Subject: [PATCH 0974/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-5223.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5223.yml diff --git a/html/changelog.html b/html/changelog.html index 6e4661c68847..2a129d1019d2 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->

    +

    31 December 2025

    +

    Penelope Haze updated:

    +
      +
    • Kharmaan nymph jobbans now use the "Mantid Nymph" role in the jobban menu, rather than the Semi-Antagonist role.
    • +
    +

    30 December 2025

    Penelope Haze updated:

    Other Roles
    Other Roles
    [entry]
    From 0ee162656a4e48648a901113e2c4699d58691409 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 4 Jan 2026 21:41:23 +1100 Subject: [PATCH 1002/1331] Ported/reindexed parkas from Polaris. --- .../preference_setup/loadout/lists/suits.dm | 6 ++++++ .../clothing/suits/jackets/wintercoat.dm | 19 ++++++++++++++++++ icons/clothing/head/hood_parka.dmi | Bin 0 -> 643 bytes icons/clothing/suits/wintercoat/parka.dmi | Bin 0 -> 1833 bytes 4 files changed, 25 insertions(+) create mode 100644 icons/clothing/head/hood_parka.dmi create mode 100644 icons/clothing/suits/wintercoat/parka.dmi diff --git a/code/modules/client/preference_setup/loadout/lists/suits.dm b/code/modules/client/preference_setup/loadout/lists/suits.dm index 071c2a139cab..4b17b6a6a316 100644 --- a/code/modules/client/preference_setup/loadout/lists/suits.dm +++ b/code/modules/client/preference_setup/loadout/lists/suits.dm @@ -64,6 +64,12 @@ path = /obj/item/clothing/suit/jacket/winter uid = "gear_suit_winter_coat" +/decl/loadout_option/suit/parka + name = "parka" + path = /obj/item/clothing/suit/jacket/winter/parka + loadout_flags = GEAR_HAS_COLOR_SELECTION + uid = "gear_suit_parka" + /decl/loadout_option/suit/track name = "track jacket selection" path = /obj/item/clothing/suit/toggle/track diff --git a/code/modules/clothing/suits/jackets/wintercoat.dm b/code/modules/clothing/suits/jackets/wintercoat.dm index b34c74a2d43c..ae70473b1823 100644 --- a/code/modules/clothing/suits/jackets/wintercoat.dm +++ b/code/modules/clothing/suits/jackets/wintercoat.dm @@ -127,3 +127,22 @@ /obj/item/clothing/head/winterhood/mining icon = 'icons/clothing/head/hood_winter_mining.dmi' + +/obj/item/clothing/suit/jacket/winter/parka + name = "parka" + desc = "A heavy fur-lined jacket designed to keep you extra warm in sub-zero conditions." + hood = /obj/item/clothing/head/winterhood/parka + icon = 'icons/clothing/suits/wintercoat/parka.dmi' + markings_color = "#a5975c" + markings_state_modifier = "-lining" + paint_color = COLOR_SILVER + +/obj/item/clothing/head/winterhood/parka + name = "parka hood" + desc = "A heavy fur-lined jacket hood.." + icon = 'icons/clothing/head/hood_parka.dmi' + markings_color = "#a5975c" + markings_state_modifier = "-lining" + +/obj/item/clothing/suit/jacket/winter/parka/purple + paint_color = COLOR_PURPLE diff --git a/icons/clothing/head/hood_parka.dmi b/icons/clothing/head/hood_parka.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7b006dea42458c9093f4b5a1265a620f227e8b05 GIT binary patch literal 643 zcmV-}0(||6P)X(bU7z`(#SEkghogi!zh00DGTPE!Ct=GbNc005MFR9JLG zWpiV4X>fFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TB zGg33tGfE(w;*!LYR3K9+qck@$FF!LyAt^tlQnxrKza%~*H8Dkri!&v&s2F4n7iU^g zVs0vkZAeg`Zcb)iW?ni$?dADJIY^cm;!%ZRB&uR%1y?^8u-5^6%}t$Mko?~O00DDJ zL_t(oh3%NVPQx%1g`G&v-2Aw6Y$Zr+1YUqv?p_x%u=Nqxcn*dRep_5oDL&UIRt6+c z@26xqj(zWOGUUiY{1LLU1f4%UqVBe9nBVMLn4s+u2D-8#+S1|Z9azY;vL@`1`RW!Nj<4aKa$`^6SQHWp$cwF@gY1my1MMBaj{Hc%f!y!GK^H## z?g4Kx_dctAQGF#AEpKyJlHR$2RKEQ$o8)!9v&g%*b+|?VHV2pXD zrWuA|nE%Vi`?C4DJD{>noeG{58Z?u^lR^)o dICwHQ#}~U4F~G~K#NPk_002ovPDHLkV1me{7$yJ! literal 0 HcmV?d00001 diff --git a/icons/clothing/suits/wintercoat/parka.dmi b/icons/clothing/suits/wintercoat/parka.dmi new file mode 100644 index 0000000000000000000000000000000000000000..acc4af648701fa3f55703c3e28e8d5dffcf34d70 GIT binary patch literal 1833 zcmV+^2iEwBP)V=-0C=3GQ_Bj%Fc3T^UlH1K#YeARq(yqLzaW*wW+BZ=l3M(Gs|BS+Fllc) zFf%(lBr!YW$D(A}si1dZETCq4maf*6bikeL!EQ^56i`by0P89z`Z4OzC>~M5!Pp*e zD7l)d5yL!YB?>g?+LMeop#!L4<`U62^ots99uoh<_gy2~rqT$UU64N#tI(Zo3+}5+ zgLIRuf#PwMGsMf*Pb-^o`cjXdFJx{Je~e<5m)ZNHJ^?OYCxo*iOxyqf1?oveK~#90 z?V7=Jn>ZAQ1z~-*1o(`I$$5$6o)KnyUd-rjH{G1~w*UTur?KCA!Xg@IraP;zb!T3r zA5R$4@I_|K^J@}t?o`hvFP}4)dM<%%lys=)H+*TD(xIMB+%(N${qcG>?q?V^&73;i zHcjqO_jgfKXQ-}7Ju&{+9ISQG{DJC7y_9IleORkDHO z^|?{s;JB(3DjoA-+Mdr^z$vE_gX5cIlfv;;|7S)%)q9@q_1*h6?b?KQmT|{?(8{ju zTG`&2AGF0jN%xj<$9yQHF86IN?gT8H zz7q912lb5P*>DljC8YYL4m<{a_LxYy`2zE=XU^60wXDw-(le6p=_j1PdWn98>3LWF zb*-aPXvx3B{8!GkjH~PX%^K<%$q!)PqxzNVr?-=*WByb8tNIz z8&Lh~>z4Cd9dX&pBU-R5L@M4N1P47^13ja81FBy`y@NW|NMSaVqPfm5x;4-x3_$qu7RGJc>>ihQ^#u2NOTSR)05jYvV#p3%Gk)i2p~sIL1v4>#;@2+**1Go)uUZ$S0Sx#6xlI5njDC3TAGdTqX? z0c_Y-NY7~Afa;gx0@bzT^bEOx*N|7rmN%e!HC9(NP{FdY<~<&d=U0I0T-emOE?V@6 z6r}ifea<>ab*|tksmt>BTlo;j96eCD>R=#;zc)g@E8#OusAG|XQw(xh!XHmGKi#lj zJ=(lVcYF9Ag}OZ9#41J&Nq&;Je?7qMxkBqlQ1@q;;Y5hekme^b&M@vebcTho{utgh z&SyBd7TTVZyAsz4NiZcqLzIDZ`>EJ74^lz_X?T8Kq!vT_Y7Yg-csOu4u*Y^U3`x=M83DIFAf1w<0tWmZ=vrk>x)Bx{YGz^KauY(?2Chb z;qAdFMf$b0FHSaG+82A@Gv89^drSM`;Cu88iSI4zix+=m25_h#ejd$V!%z1g_>-h5nrZ|?r0tMAc|b27yOP*3{inD4!!B@Vs_JCKia zvIBbn!;hB)9KRgx=9^=_XZj*qGWs|tW9J?CK8h~?W#9!5|J>Fz;(KGh$nLXxdm~35 z<9!WdzBlHJmWFPO`QDf>F4u6(_h>`ld$SqyJ)18sfa!a7U!2W|ep&VHKiJUlJ-aW? zX2kcbzBrpP-@`Mg0D3$g&;O8r{{Feg56fr!gaG;Gd?X7p*#F1t2KLR_7`tyjUN@+3 z&cxVXWOTof`sPeNaE6QaGxrx6y@u2`=lUMqUo@ckLv^TNZTcR0(3iybh%Xuh3NI+A z6kdSgd#`AT7yAR>dej$9urHF2dye@Yaj;<&u4$T+b=1xGJRXn7j2MEy$-F+`~_A%EpwB|nok{Ng3!}$-e!k;&F z_C1oHBuwyaeOO;VdO1!&ebMoE6t5?8ylUz#!U)`UgR6V00000NkvXXu0mjf3s|+| literal 0 HcmV?d00001 From a36bb85e73108b31c861c342f2000a3610b2acd7 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Sun, 4 Jan 2026 21:46:08 +1100 Subject: [PATCH 1003/1331] Added very basic alt title decl structure. --- code/game/jobs/alt_titles.dm | 23 +++++++++++++++++++++++ nebula.dme | 1 + 2 files changed, 24 insertions(+) create mode 100644 code/game/jobs/alt_titles.dm diff --git a/code/game/jobs/alt_titles.dm b/code/game/jobs/alt_titles.dm new file mode 100644 index 000000000000..cea4dafc8378 --- /dev/null +++ b/code/game/jobs/alt_titles.dm @@ -0,0 +1,23 @@ +/decl/alt_title + abstract_type = /decl/alt_title + var/name + var/desc + var/outfit + +/decl/alt_title/validate() + . = ..() + if(!name) + . += "missing name" + if(!desc) + . += "missing desc" + if(!ispath(outfit, /decl/outfit)) + . += "missing or invalid outfit: [outfit || "NULL"]" + +/datum/job/New() + if(length(alt_titles)) + for(var/title in alt_titles) + if(ispath(title, /decl/alt_title)) + var/decl/alt_title/title_data = GET_DECL(title) + alt_titles -= title + alt_titles[title_data.name] = title_data.outfit + ..() diff --git a/nebula.dme b/nebula.dme index 48ab5484c7c3..3b9260878da0 100644 --- a/nebula.dme +++ b/nebula.dme @@ -805,6 +805,7 @@ #include "code\game\jobs\_access_defs.dm" #include "code\game\jobs\access.dm" #include "code\game\jobs\access_datum.dm" +#include "code\game\jobs\alt_titles.dm" #include "code\game\jobs\server_whitelist.dm" #include "code\game\jobs\job\_job.dm" #include "code\game\machinery\ai_slipper.dm" From 6f369bba98775abeed9a33c53accff51e81ccfc6 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Mon, 5 Jan 2026 13:20:43 +1100 Subject: [PATCH 1004/1331] Automatic changelog generation for PR #5241 [ci skip] --- html/changelogs/AutoChangeLog-pr-5241.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5241.yml diff --git a/html/changelogs/AutoChangeLog-pr-5241.yml b/html/changelogs/AutoChangeLog-pr-5241.yml new file mode 100644 index 000000000000..78c044967529 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5241.yml @@ -0,0 +1,5 @@ +author: MistakeNot4892 +changes: + - {tweak: 'Internal code for neo-avian markings has been reworked, check your sprite + accessories.'} +delete-after: true From 13965a7a72a2570b11c3dd3c19a3128cf3da7d92 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 5 Jan 2026 17:58:53 -0500 Subject: [PATCH 1005/1331] Optimize nanoui merge_templates --- code/modules/client/asset_cache.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 67bb71649839..691812c23018 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -221,12 +221,14 @@ var/global/template_file_name = "all_templates.json" /// Handles adding a directory's templates to the compiled templates list. /datum/asset/nanoui/proc/merge_templates(use_dir) PRIVATE_PROC(TRUE) + var/static/regex/whitespace = new(@"[\n\t]+", "g") var/list/templates = flist(use_dir) for(var/filename in templates) if(copytext(filename, length(filename)) != "/") - templates[filename] = replacetext(replacetext(file2text(use_dir + filename), "\n", ""), "\t", "") + templates[filename] = whitespace.Replace(file2text(use_dir + filename), "") else templates -= filename + CHECK_TICK return templates /datum/asset/nanoui/send(client, uncommon) From 7a239d13354e0943d888cc94ef1fc056bd4e0495 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 5 Jan 2026 19:19:09 -0500 Subject: [PATCH 1006/1331] Make extensions use an alist --- code/datums/datum.dm | 7 ++++--- code/datums/extensions/extensions.dm | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 881b69bb4e36..bbc6bd7a5f2c 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -42,10 +42,11 @@ qdel(timer) if(extensions) - for(var/expansion_key in extensions) - var/list/extension = extensions[expansion_key] + var/list/extension_list + for(var/expansion_key, extension in extensions) if(islist(extension)) - extension.Cut() + extension_list = extension + extension_list.Cut() else qdel(extension) extensions = null diff --git a/code/datums/extensions/extensions.dm b/code/datums/extensions/extensions.dm index 53985ff5862d..ab28746a5581 100644 --- a/code/datums/extensions/extensions.dm +++ b/code/datums/extensions/extensions.dm @@ -38,7 +38,8 @@ source.PopulateClone(src) /datum - var/list/datum/extension/extensions + /// A lazy alist() keyed by extension base type. Values are either a list of extension init arguments for lazy-loaded extensions, or an extension datum. + var/alist/extensions //Variadic - Additional positional arguments can be given. Named arguments might not work so well /proc/set_extension(var/datum/source, var/datum/extension/extension_type) @@ -49,8 +50,7 @@ CRASH("Invalid base type: Expected /datum/extension, was [log_info_line(extension_base_type)]") if(!ispath(extension_type, extension_base_type)) CRASH("Invalid extension type: Expected [extension_base_type], was [log_info_line(extension_type)]") - if(!source.extensions) - source.extensions = list() + A_LAZYINITLIST(source.extensions) var/datum/extension/existing_extension = source.extensions[extension_base_type] if(istype(existing_extension)) qdel(existing_extension) @@ -115,7 +115,7 @@ return if(!islist(source.extensions[base_type])) qdel(source.extensions[base_type]) - LAZYREMOVE(source.extensions, base_type) + A_LAZYREMOVE(source.extensions, base_type) ///Copy the extension instance on the 'source' and put it on the 'destination'. /proc/copy_extension(var/datum/source, var/datum/destination, var/base_type) From bab64088e503dff50c1da64ef02430bff1ac6aa4 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 5 Jan 2026 19:19:52 -0500 Subject: [PATCH 1007/1331] Add a subset of lazylist macros for alists --- code/__defines/lists.dm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/__defines/lists.dm b/code/__defines/lists.dm index c29a24a164d7..9935dab6b5c7 100644 --- a/code/__defines/lists.dm +++ b/code/__defines/lists.dm @@ -32,6 +32,14 @@ // Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression. #define SANITIZE_LIST(L) ( islist(L) ? L : list() ) +// The above but for alists. Prefixed with A_ because inserting "A" randomly in the name just made it confusing +#define A_LAZYINITLIST(AL) if (!AL) { AL = alist(); } +#define A_UNSETEMPTY(AL) if(!length(AL)) { AL = null; } +#define A_LAZYREMOVE(AL, I) if(AL) { AL -= I; A_UNSETEMPTY(AL) } +#define A_LAZYSET(AL, A, I) if(!AL) { AL = alist(); } AL[A] = I; +#define A_LAZYCLEARLIST(AL) if(AL) { AL.Cut(); AL = null; } +#define A_LAZYLEN(AL) length(AL) + /// Passed into BINARY_INSERT to compare keys #define COMPARE_KEY __BIN_LIST[__BIN_MID] /// Passed into BINARY_INSERT to compare values From 4ef02532c445470c4b24501b831fb938dc93e4f5 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 6 Jan 2026 01:03:04 +0000 Subject: [PATCH 1008/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-5241.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5241.yml diff --git a/html/changelog.html b/html/changelog.html index cfb65991d92f..fb311db0cf29 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    06 January 2026

    +

    MistakeNot4892 updated:

    +
      +
    • Internal code for neo-avian markings has been reworked, check your sprite accessories.
    • +
    +

    02 January 2026

    Sutures updated:

    From e1624d97361b312d94bc2040937dc0193b77695c Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Wed, 4 Mar 2026 01:14:16 +0000 Subject: [PATCH 1156/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 4dea7670967f..bf211e520c01 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -69,12 +69,6 @@

    Sutures updated:

    • The equip hotkey can now place items into backpacks and wallets.
    - -

    31 December 2025

    -

    Penelope Haze updated:

    -
      -
    • Kharmaan nymph jobbans now use the "Mantid Nymph" role in the jobban menu, rather than the Semi-Antagonist role.
    • -
    From 40c3ff60389bd9146baa71198815f01413bd60a5 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 6 Mar 2026 01:19:47 +0000 Subject: [PATCH 1157/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index bf211e520c01..24729b5cd40e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -63,12 +63,6 @@

    MistakeNot4892 updated:

    • Internal code for neo-avian markings has been reworked, check your sprite accessories.
    - -

    02 January 2026

    -

    Sutures updated:

    -
      -
    • The equip hotkey can now place items into backpacks and wallets.
    • -
    From 318927812abbc6d94873435ed9cac0da2869ee82 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Tue, 10 Mar 2026 01:11:35 +0000 Subject: [PATCH 1158/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 24729b5cd40e..432fdcc9b8b1 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,12 +57,6 @@

    Penelope Haze updated:

    • Certain supply packs that give a random selection are now priced more fairly.
    - -

    06 January 2026

    -

    MistakeNot4892 updated:

    -
      -
    • Internal code for neo-avian markings has been reworked, check your sprite accessories.
    • -
    From 23c21fee3bdac29687f1c509ddb70f5466a7822a Mon Sep 17 00:00:00 2001 From: sypsoti Date: Tue, 10 Mar 2026 05:58:06 -0500 Subject: [PATCH 1159/1331] fixes random spawner --- code/game/objects/random/random.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index b10416f63713..3a111afaccac 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -27,7 +27,7 @@ if(!type_to_spawn) return - for(var/atom/A as anything in create_instance(type_to_spawn)) + for(var/atom/A as anything in create_instance(type_to_spawn, loc)) if(pixel_x || pixel_y) A.default_pixel_x = pixel_x A.default_pixel_y = pixel_y From ef6e4fe3e730f1fb447ebafddd40d0bb1e1edd7a Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 14 Mar 2026 01:13:50 +0000 Subject: [PATCH 1160/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 432fdcc9b8b1..2aecab0a3e31 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -51,12 +51,6 @@ -->
    - -

    10 January 2026

    -

    Penelope Haze updated:

    -
      -
    • Certain supply packs that give a random selection are now priced more fairly.
    • -
    From 4e4ab9b31404c035e639db0885dba9c3256a924d Mon Sep 17 00:00:00 2001 From: sypsoti Date: Fri, 13 Mar 2026 22:33:32 -0500 Subject: [PATCH 1161/1331] switching to stable --- .../modules/modular_computers/networking/machinery/telecomms.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/modular_computers/networking/machinery/telecomms.dm b/code/modules/modular_computers/networking/machinery/telecomms.dm index 93f7f7cd4914..8ff24e38a591 100644 --- a/code/modules/modular_computers/networking/machinery/telecomms.dm +++ b/code/modules/modular_computers/networking/machinery/telecomms.dm @@ -190,7 +190,7 @@ var/global/list/telecomms_hubs = list() listeners[ghost_listener] = TRUE for(var/mob/listener in listeners) - listener.hear_radio(message, message_verb, speaking, formatted_msg, " ", "", speaker, message_compression, vname = send_name, vsource = (listeners[listener] ? send_overmap_object.name : null)) + listener.hear_radio(message, message_verb, speaking, formatted_msg, " ", "", speaker, message_compression, vname = send_name, vsource = (listeners[listener] ? send_overmap_object?.name : null)) if(!chain_transmit) return From ae8d8743701ac9fc3183e3d68c4e98c8fca8653a Mon Sep 17 00:00:00 2001 From: Typhin Date: Fri, 13 Mar 2026 22:55:47 -0500 Subject: [PATCH 1162/1331] Garlic Oil now treats infections Adds `affect_blood_on_ingest` to garlic oil, allowing it to be an antibiotic without requiring it to be injected. --- code/modules/reagents/chems/chems_drinks.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/reagents/chems/chems_drinks.dm b/code/modules/reagents/chems/chems_drinks.dm index a00cf2beaf94..2be58a63f7a5 100644 --- a/code/modules/reagents/chems/chems_drinks.dm +++ b/code/modules/reagents/chems/chems_drinks.dm @@ -185,6 +185,7 @@ color = "#eeddcc" uid = "chem_drink_garlic" antibiotic_strength = 0.65 + affect_blood_on_ingest = TRUE glass_name = "garlic oil" glass_desc = "A potion of guaranteed bad breath." From 99d393a92d2bfc9e3c3e68f405c29371868bafca Mon Sep 17 00:00:00 2001 From: Typhin Date: Sat, 21 Mar 2026 23:21:05 -0500 Subject: [PATCH 1163/1331] Prevent Garlic Oil from being poison Tags garlic oil as `injectable_nutrition`. Because this causes `adjust_mob_nutrition()` to get called twice, nutriment/hydration factors are cut in half to compensate. --- code/modules/reagents/chems/chems_drinks.dm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/modules/reagents/chems/chems_drinks.dm b/code/modules/reagents/chems/chems_drinks.dm index 2be58a63f7a5..7f4f56bcac08 100644 --- a/code/modules/reagents/chems/chems_drinks.dm +++ b/code/modules/reagents/chems/chems_drinks.dm @@ -18,7 +18,8 @@ /decl/material/liquid/drink/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() - M.take_damage(removed, TOX) // Probably not a good idea; not very deadly though + if(!injectable_nutrition) + M.take_damage(removed, TOX) // Probably not a good idea; not very deadly though /decl/material/liquid/drink/affect_ingest(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() @@ -181,11 +182,13 @@ name = "garlic oil" lore_text = "A strong-smelling, pungent oil pressed from garlic cloves. It has some antibiotic properties, and can help with infections." taste_description = "bad breath" - nutriment_factor = 1 + nutriment_factor = 0.5 // Injectable Nutrition flag causes it to be digested twice + hydration_factor = 3 // Cut in half from 6 so double digestion gives normal amount color = "#eeddcc" uid = "chem_drink_garlic" antibiotic_strength = 0.65 affect_blood_on_ingest = TRUE + injectable_nutrition = TRUE glass_name = "garlic oil" glass_desc = "A potion of guaranteed bad breath." From 2d3c67fd397d6fa8427d020beecfd52bd401eca7 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Thu, 26 Mar 2026 17:33:19 +1100 Subject: [PATCH 1164/1331] Automatic changelog generation for PR #5314 [ci skip] --- html/changelogs/AutoChangeLog-pr-5314.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5314.yml diff --git a/html/changelogs/AutoChangeLog-pr-5314.yml b/html/changelogs/AutoChangeLog-pr-5314.yml new file mode 100644 index 000000000000..c04e82e76418 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5314.yml @@ -0,0 +1,4 @@ +author: Typhin +changes: + - {tweak: Prevented Garlic Oil from dealing TOX damage} +delete-after: true From 37b98aa2f9650e55510d491ca9b66617957f55f5 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 27 Mar 2026 01:24:38 +0000 Subject: [PATCH 1165/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-5314.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5314.yml diff --git a/html/changelog.html b/html/changelog.html index 2aecab0a3e31..796f54f9fd81 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -51,6 +51,12 @@ -->
    + +

    27 March 2026

    +

    Typhin updated:

    +
      +
    • Prevented Garlic Oil from dealing TOX damage
    • +
    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 8e915de101ab..83328d67f5a5 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15067,3 +15067,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Penelope Haze: - balance: Certain supply packs that give a random selection are now priced more fairly. +2026-03-27: + Typhin: + - tweak: Prevented Garlic Oil from dealing TOX damage diff --git a/html/changelogs/AutoChangeLog-pr-5314.yml b/html/changelogs/AutoChangeLog-pr-5314.yml deleted file mode 100644 index c04e82e76418..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5314.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Typhin -changes: - - {tweak: Prevented Garlic Oil from dealing TOX damage} -delete-after: true From a66c5e6b0f69e4c80af4886a6931d7a48251dd97 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 30 Mar 2026 14:46:52 -0400 Subject: [PATCH 1166/1331] Fix accessories vanishing when clothes are equipped --- code/modules/clothing/_clothing.dm | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index 6e00b3e04f18..6b7b48dca0f3 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -219,12 +219,11 @@ if(should_use_combined_accessory_appearance()) var/image/I = get_mob_overlay(ismob(loc) ? loc : null, get_fallback_slot()) if(I?.icon) // Null or invisible overlay, we don't want to make our clothing invisible just because it has an accessory. - I.plane = plane - I.layer = layer - I.color = color - I.alpha = alpha - I.name = name - appearance = I + icon = I.icon + icon_state = I.icon_state + underlays = I.underlays.Copy() + // we need to use the managed overlays system or else they will end up overwritten + set_overlays(I.overlays.Copy()) set_dir(SOUTH) update_clothing_icon() return From 5a939580c2dfc0e6d6bb653a6406c0076b721724 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 30 Mar 2026 14:48:57 -0400 Subject: [PATCH 1167/1331] Fix issues with hood material, coloration, and storage interactions --- code/modules/clothing/suits/_suit_hood.dm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/code/modules/clothing/suits/_suit_hood.dm b/code/modules/clothing/suits/_suit_hood.dm index 3aaaba4e5f7d..f9d99d17b607 100644 --- a/code/modules/clothing/suits/_suit_hood.dm +++ b/code/modules/clothing/suits/_suit_hood.dm @@ -16,9 +16,18 @@ if(. && istype(hood)) hood.set_color(new_color) +// i don't think this needs to be a get_stored_inventory override instead since it should never be separated currently +// but that may change in the future +/obj/item/clothing/suit/get_contained_external_atoms() + . = ..() + if(istype(hood)) // this is considered a component rather than a contained item + . -= hood + /obj/item/clothing/suit/Initialize() if(ispath(hood)) - hood = new hood(src) + hood = new hood(src, material) + if(paint_color) + hood.set_color(paint_color) return ..() /obj/item/clothing/suit/Destroy() From fb26e82b334eb5efa51a20e3bdeb4b67a45df347 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Thu, 2 Apr 2026 13:22:59 +1100 Subject: [PATCH 1168/1331] Fixes runtime with hood override. --- code/modules/clothing/_clothing.dm | 15 ++++++++------- code/modules/clothing/suits/_suit_hood.dm | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index 6b7b48dca0f3..f32b0b3ee2a2 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -217,18 +217,19 @@ // Clothing does not generally align with each other's world icons, so we just use the mob overlay in this case. if(should_use_combined_accessory_appearance()) - var/image/I = get_mob_overlay(ismob(loc) ? loc : null, get_fallback_slot()) - if(I?.icon) // Null or invisible overlay, we don't want to make our clothing invisible just because it has an accessory. - icon = I.icon - icon_state = I.icon_state - underlays = I.underlays.Copy() + var/image/overlay_image = get_mob_overlay(ismob(loc) ? loc : null, get_fallback_slot()) + if(overlay_image?.icon) // Null or invisible overlay, we don't want to make our clothing invisible just because it has an accessory. + icon = overlay_image.icon + icon_state = overlay_image.icon_state + underlays = overlay_image.underlays.Copy() // we need to use the managed overlays system or else they will end up overwritten - set_overlays(I.overlays.Copy()) + set_overlays(overlay_image.overlays.Copy()) set_dir(SOUTH) update_clothing_icon() return - icon_state = JOINTEXT(list(get_world_inventory_state(), get_clothing_state_modifier())) + set_icon(initial(icon)) // this is not going to work correctly for custom icons + set_icon_state(JOINTEXT(list(get_world_inventory_state(), get_clothing_state_modifier()))) if(markings_state_modifier && markings_color) add_overlay(mutable_appearance(icon, "[icon_state][markings_state_modifier]", markings_color)) update_clothing_icon() diff --git a/code/modules/clothing/suits/_suit_hood.dm b/code/modules/clothing/suits/_suit_hood.dm index f9d99d17b607..1ac5049bb6eb 100644 --- a/code/modules/clothing/suits/_suit_hood.dm +++ b/code/modules/clothing/suits/_suit_hood.dm @@ -20,7 +20,7 @@ // but that may change in the future /obj/item/clothing/suit/get_contained_external_atoms() . = ..() - if(istype(hood)) // this is considered a component rather than a contained item + if(islist(.) && istype(hood)) // this is considered a component rather than a contained item . -= hood /obj/item/clothing/suit/Initialize() From 06adb217fb0a15d3880997a1b582a85745361f22 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Feb 2026 19:55:38 -0500 Subject: [PATCH 1169/1331] Fix gun aiming accuracy bonus --- code/modules/projectiles/gun.dm | 3 +-- code/modules/projectiles/targeting/targeting_overlay.dm | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 153d6a39e691..598dd9d8cf77 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -75,7 +75,6 @@ var/selector_sound = 'sound/weapons/guns/selector.ogg' //aiming system stuff - var/tmp/list/mob/living/aim_targets //List of who yer targeting. var/tmp/last_safety_check = -INFINITY var/safety_state = 1 var/has_safety = TRUE @@ -490,7 +489,7 @@ disp_mod += 0.5 //accuracy bonus from aiming - if (aim_targets && (target in aim_targets)) + if (user.aiming?.aiming_at == target) //If you aim at someone beforehead, it'll hit more often. //Kinda balanced by fact you need like 2 seconds to aim //As opposed to no-delay pew pew diff --git a/code/modules/projectiles/targeting/targeting_overlay.dm b/code/modules/projectiles/targeting/targeting_overlay.dm index 5d5b1f9ba654..0c7ce3cde0e4 100644 --- a/code/modules/projectiles/targeting/targeting_overlay.dm +++ b/code/modules/projectiles/targeting/targeting_overlay.dm @@ -76,6 +76,7 @@ to_chat(owner, aim_message) if(aiming_at) to_chat(aiming_at, "You are [message].") + /obj/aiming_overlay/Process() if(!owner) qdel(src) From 3f695104e68702e7e2e4033d34beefed7008e0b2 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:21:52 -0500 Subject: [PATCH 1170/1331] Fix missing length check in crew records code --- .../modular_computers/file_system/programs/generic/records.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/modular_computers/file_system/programs/generic/records.dm b/code/modules/modular_computers/file_system/programs/generic/records.dm index 4d79e385dd3a..2b5084582ade 100644 --- a/code/modules/modular_computers/file_system/programs/generic/records.dm +++ b/code/modules/modular_computers/file_system/programs/generic/records.dm @@ -98,7 +98,7 @@ to_chat(usr, SPAN_WARNING("Network error.")) return var/list/accesses = get_access(usr) - if(!network.get_mainframes_by_role(MF_ROLE_CREW_RECORDS, accesses)) + if(!length(network.get_mainframes_by_role(MF_ROLE_CREW_RECORDS, accesses))) to_chat(usr, SPAN_WARNING("You may not have access to generate new crew records, or there may not be a crew record mainframe active on the network.")) return active_record = new/datum/computer_file/report/crew_record() From d27478fc0472ac3c3cdb99f00aa8760ba90d59a8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 9 Feb 2026 01:45:17 -0500 Subject: [PATCH 1171/1331] Fix bad pixel offset code copypasta --- code/modules/hydroponics/trays/tray_soil.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/hydroponics/trays/tray_soil.dm b/code/modules/hydroponics/trays/tray_soil.dm index 5d7e341558c8..37d9df8cd244 100644 --- a/code/modules/hydroponics/trays/tray_soil.dm +++ b/code/modules/hydroponics/trays/tray_soil.dm @@ -168,7 +168,7 @@ plant_health = seed.get_trait(TRAIT_ENDURANCE) if(isnull(default_pixel_y)) default_pixel_y = rand(-12,12) - if(isnull(default_pixel_y)) + if(isnull(default_pixel_x)) default_pixel_x = rand(-12,12) reset_offsets(0) if(seed) From 3e9d18d6420d41b2ca4c36d8bafdf9210e99ace3 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:21:14 -0500 Subject: [PATCH 1172/1331] Improve material stratum picking for levels/planets --- code/modules/geology/_strata.dm | 10 +++++++++- code/modules/geology/strata_igneous.dm | 4 ++-- code/modules/geology/strata_metamorphic.dm | 2 +- code/modules/geology/strata_permafrost.dm | 1 + code/modules/geology/strata_sedimentary.dm | 2 +- maps/shaded_hills/levels/strata.dm | 2 +- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/code/modules/geology/_strata.dm b/code/modules/geology/_strata.dm index 24acb2d824d6..411da879d3d7 100644 --- a/code/modules/geology/_strata.dm +++ b/code/modules/geology/_strata.dm @@ -3,15 +3,23 @@ var/list/base_materials var/list/ores_sparse var/list/ores_rich - var/default_strata_candidate = FALSE + var/const/STRATA_RANDOM_NEVER = 0 + var/const/STRATA_RANDOM_PLANET = BITFLAG(0) + var/const/STRATA_RANDOM_LEVEL = BITFLAG(1) + var/const/STRATA_RANDOM_ANY = STRATA_RANDOM_PLANET | STRATA_RANDOM_LEVEL + var/default_strata_candidate = STRATA_RANDOM_NEVER var/maximum_temperature = INFINITY /decl/strata/proc/is_valid_exoplanet_strata(var/datum/planetoid_data/planet) + if(!(default_strata_candidate & STRATA_RANDOM_PLANET)) + return FALSE if(istype(planet.atmosphere)) return planet.atmosphere.temperature <= maximum_temperature return TCMB <= maximum_temperature /decl/strata/proc/is_valid_level_stratum(datum/level_data/level_data) + if(!(default_strata_candidate & STRATA_RANDOM_LEVEL)) + return FALSE var/temperature_to_check = istype(level_data.exterior_atmosphere) ? level_data.exterior_atmosphere.temperature : level_data.exterior_atmos_temp return (temperature_to_check || TCMB) <= maximum_temperature diff --git a/code/modules/geology/strata_igneous.dm b/code/modules/geology/strata_igneous.dm index 7c6a77e9e0ed..9ee954525214 100644 --- a/code/modules/geology/strata_igneous.dm +++ b/code/modules/geology/strata_igneous.dm @@ -1,12 +1,12 @@ /decl/strata/igneous name = "igneous rock" base_materials = list(/decl/material/solid/stone/basalt) - default_strata_candidate = TRUE + default_strata_candidate = STRATA_RANDOM_ANY ores_rich = list( /decl/material/solid/gemstone/diamond, /decl/material/solid/quartz, /decl/material/solid/graphite, - /decl/material/solid/densegraphite, + /decl/material/solid/densegraphite, /decl/material/solid/metal/gold, /decl/material/solid/quartz, /decl/material/solid/metal/platinum, diff --git a/code/modules/geology/strata_metamorphic.dm b/code/modules/geology/strata_metamorphic.dm index a1eeced227a0..aefa322cfbc8 100644 --- a/code/modules/geology/strata_metamorphic.dm +++ b/code/modules/geology/strata_metamorphic.dm @@ -1,7 +1,7 @@ /decl/strata/metamorphic name = "metamorphic rock" base_materials = list(/decl/material/solid/stone/marble) - default_strata_candidate = TRUE + default_strata_candidate = STRATA_RANDOM_ANY ores_rich = list( /decl/material/solid/quartz, /decl/material/solid/graphite, diff --git a/code/modules/geology/strata_permafrost.dm b/code/modules/geology/strata_permafrost.dm index ee157faa20d8..994587ba5cc0 100644 --- a/code/modules/geology/strata_permafrost.dm +++ b/code/modules/geology/strata_permafrost.dm @@ -18,4 +18,5 @@ /decl/material/solid/ice/hydrate/krypton, /decl/material/solid/ice/hydrate/xenon, ) + default_strata_candidate = STRATA_RANDOM_PLANET maximum_temperature = T0C diff --git a/code/modules/geology/strata_sedimentary.dm b/code/modules/geology/strata_sedimentary.dm index 27f229ce298c..0e502aa7135b 100644 --- a/code/modules/geology/strata_sedimentary.dm +++ b/code/modules/geology/strata_sedimentary.dm @@ -1,7 +1,7 @@ /decl/strata/sedimentary name = "sedimentary rock" base_materials = list(/decl/material/solid/stone/sandstone) - default_strata_candidate = TRUE + default_strata_candidate = STRATA_RANDOM_ANY ores_rich = list( /decl/material/solid/pitchblende, /decl/material/solid/pyrite, diff --git a/maps/shaded_hills/levels/strata.dm b/maps/shaded_hills/levels/strata.dm index 54781d0f5dca..7ea08f8512ee 100644 --- a/maps/shaded_hills/levels/strata.dm +++ b/maps/shaded_hills/levels/strata.dm @@ -2,7 +2,7 @@ /decl/strata/shaded_hills name = "mountainous rock" base_materials = list(/decl/material/solid/stone/basalt) - default_strata_candidate = FALSE + default_strata_candidate = STRATA_RANDOM_NEVER // this is for a specific map ores_sparse = list( /decl/material/solid/quartz, /decl/material/solid/graphite, From e067bf81eb19886b1350683d0d8c7a7c2da9132e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:22:08 -0500 Subject: [PATCH 1173/1331] Implement salvage do_rotation var --- code/modules/salvage/salvage.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/salvage/salvage.dm b/code/modules/salvage/salvage.dm index ecf3196f8834..24801642b5d2 100644 --- a/code/modules/salvage/salvage.dm +++ b/code/modules/salvage/salvage.dm @@ -14,7 +14,8 @@ if(!ispath(salvaged_type, /obj/item)) return INITIALIZE_HINT_QDEL // TODO: grab partial initial matter from the salvage type. - icon_rotation = rand(-45, 45) + if(do_rotation) + icon_rotation = rand(-45, 45) name = "[pick("busted", "broken", "shattered", "scrapped")] [salvaged_type::name]" w_class = salvaged_type::w_class From d7cdf958fd9118122979ef2fa0f25471fd7ab901 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 12:24:39 -0500 Subject: [PATCH 1174/1331] Reimplement air alarm remote_control variable --- code/game/area/areas.dm | 8 +-- code/game/machinery/alarm.dm | 50 +++++++++++-------- .../programs/engineering/atmos_control.dm | 4 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index e2441adeb536..82d9e47fde06 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -37,7 +37,7 @@ var/global/list/areas = list() /// Disables constructing or using APCs in this area. var/always_unpowered = FALSE - var/atmosalm = 0 + var/atmosalm = /obj/machinery/alarm::DANGER_NONE var/power_equip = 1 // Status var/power_light = 1 var/power_environ = 1 @@ -193,7 +193,7 @@ var/global/list/areas = list() return cameras /area/proc/atmosalert(danger_level, var/alarm_source) - if (danger_level == 0) + if (danger_level == /obj/machinery/alarm::DANGER_NONE) atmosphere_alarm.clearAlarm(src, alarm_source) else atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) @@ -204,10 +204,10 @@ var/global/list/areas = list() danger_level = max(danger_level, AA.danger_level) if(danger_level != atmosalm) - if (danger_level < 1 && atmosalm >= 1) + if (danger_level < /obj/machinery/alarm::DANGER_WARN && atmosalm >= /obj/machinery/alarm::DANGER_WARN) //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise air_doors_open() - else if (danger_level >= 2 && atmosalm < 2) + else if (danger_level >= /obj/machinery/alarm::DANGER_DANGER && atmosalm < /obj/machinery/alarm::DANGER_DANGER) air_doors_close() atmosalm = danger_level diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 5b72753a9e6d..0747694c3039 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -65,15 +65,18 @@ directional_offset = @'{"NORTH":{"y":-21}, "SOUTH":{"y":21}, "EAST":{"x":-21}, "WEST":{"x":21}}' var/alarm_id = null - var/breach_detection = 1 // Whether to use automatic breach detection or not + var/breach_detection = TRUE // Whether to use automatic breach detection or not var/frequency = 1439 var/alarm_frequency = 1437 - var/remote_control = 0 - var/rcon_setting = 2 + /// If TRUE, remote controllers like the atmos control computer can control the air alarm; if FALSE, they can only view it. + /// Typically auto-set by rcon_setting. + var/remote_control = FALSE + /// On RCON_AUTO, remote control is enabled when danger_level is DANGER_DANGER. + var/rcon_setting = RCON_AUTO var/rcon_remote_override_access = list(access_ce) - var/locked = 1 - var/aidisabled = 0 - var/shorted = 0 + var/locked = TRUE + var/aidisabled = FALSE + var/shorted = FALSE var/mode = AALARM_MODE_SCRUBBING var/screen = AALARM_SCREEN_MAIN @@ -89,14 +92,17 @@ var/list/TLV = list() // stands for Threshold Limit Value, since it handles exposure amounts var/list/trace_gas = list() //list of other gases that this air alarm is able to detect - var/danger_level = 0 - var/pressure_dangerlevel = 0 - var/oxygen_dangerlevel = 0 - var/co2_dangerlevel = 0 - var/temperature_dangerlevel = 0 - var/other_dangerlevel = 0 + var/const/DANGER_NONE = 0 + var/const/DANGER_WARN = 1 + var/const/DANGER_DANGER = 2 // danger, danger, circuits ready + var/danger_level = DANGER_NONE + var/pressure_dangerlevel = DANGER_NONE + var/oxygen_dangerlevel = DANGER_NONE + var/co2_dangerlevel = DANGER_NONE + var/temperature_dangerlevel = DANGER_NONE + var/other_dangerlevel = DANGER_NONE var/environment_type = /decl/environment_data - var/report_danger_level = 1 + var/report_danger_level = TRUE /obj/machinery/alarm/cold target_temperature = T0C+4 @@ -215,14 +221,14 @@ //atmos computer remote controll stuff switch(rcon_setting) if(RCON_NO) - remote_control = 0 + remote_control = FALSE if(RCON_AUTO) - if(danger_level == 2) - remote_control = 1 + if(danger_level == DANGER_DANGER) + remote_control = TRUE else - remote_control = 0 + remote_control = FALSE if(RCON_YES) - remote_control = 1 + remote_control = TRUE return @@ -318,10 +324,10 @@ /obj/machinery/alarm/proc/get_danger_level(var/current_value, var/list/danger_levels) if((current_value >= danger_levels[4] && danger_levels[4] > 0) || current_value <= danger_levels[1]) - return 2 + return DANGER_DANGER if((current_value > danger_levels[3] && danger_levels[3] > 0) || current_value < danger_levels[2]) - return 1 - return 0 + return DANGER_WARN + return DANGER_NONE /obj/machinery/alarm/on_update_icon() // Broken or deconstructed states @@ -477,7 +483,7 @@ /obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = global.default_topic_state) var/data[0] var/remote_connection = istype(state, /datum/topic_state/remote) // Remote connection means we're non-adjacent/connecting from another computer - var/remote_access = remote_connection && CanInteract(user, state) // Remote access means we also have the privilege to alter the air alarm. + var/remote_access = remote_control && remote_connection && CanInteract(user, state) // Remote access means we also have the privilege to alter the air alarm. data["locked"] = locked && !issilicon(user) data["remote_connection"] = remote_connection diff --git a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm index c1e691ad90e1..995fc28cc69a 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm @@ -80,9 +80,9 @@ continue var/danger_level = max(alarm.danger_level, alarm.alarm_area.atmosalm) - if(danger_level == 2) + if(danger_level == /obj/machinery/alarm::DANGER_DANGER) alarmsAlert[++alarmsAlert.len] = list("name" = alarm_name, "ref"= "\ref[alarm]", "danger" = danger_level) - else if(danger_level == 1) + else if(danger_level == /obj/machinery/alarm::DANGER_WARN) alarmsDanger[++alarmsDanger.len] = list("name" = alarm_name, "ref"= "\ref[alarm]", "danger" = danger_level) else alarms[++alarms.len] = list("name" = alarm_name, "ref"= "\ref[alarm]", "danger" = danger_level) From ce6ad2c9003cfe1d827a2280c246e2a35c40f2c4 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 12:26:25 -0500 Subject: [PATCH 1175/1331] Replace MIDNIGHT_ROLLOVER with (1 DAY) in REALTIMEOFDAY --- code/__defines/_tick.dm | 2 +- code/__defines/misc.dm | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/code/__defines/_tick.dm b/code/__defines/_tick.dm index 2c33329a612f..deb6e555a84f 100644 --- a/code/__defines/_tick.dm +++ b/code/__defines/_tick.dm @@ -30,5 +30,5 @@ //time of day but automatically adjusts to the server going into the next day within the same round. //for when you need a reliable time number that doesn't depend on byond time. -#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK)) +#define REALTIMEOFDAY (world.timeofday + ((1 DAY) * MIDNIGHT_ROLLOVER_CHECK)) #define MIDNIGHT_ROLLOVER_CHECK ( global.rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : global.midnight_rollovers ) diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index faba544b1bab..3721efb82c0b 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -184,8 +184,6 @@ #define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 -#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day - //Error handler defines #define ERROR_USEFUL_LEN 2 From aa1297ebcb7e3beb37797f9813d20d9174332f79 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Feb 2026 19:58:37 -0500 Subject: [PATCH 1176/1331] Implement magnetic gun ammo string --- code/modules/projectiles/guns/magnetic/magnetic.dm | 7 +++++-- code/modules/projectiles/guns/magnetic/magnetic_railgun.dm | 6 +++--- mods/species/ascent/items/guns.dm | 5 +++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/code/modules/projectiles/guns/magnetic/magnetic.dm b/code/modules/projectiles/guns/magnetic/magnetic.dm index 43478d115947..359d016f9148 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic.dm @@ -83,9 +83,9 @@ if(mag.remaining) add_overlay("[icon_state]_ammo") -/obj/item/gun/magnetic/proc/show_ammo(var/mob/user) +/obj/item/gun/magnetic/proc/get_ammo_string(mob/user, distance) if(loaded) - to_chat(user, "It has \a [loaded] loaded.") + return SPAN_NOTICE("It has \a [loaded] loaded.") /obj/item/gun/magnetic/get_examine_strings(mob/user, distance, infix, suffix) . = ..() @@ -97,6 +97,9 @@ . += SPAN_NOTICE("The capacitor charge indicator is [SPAN_ORANGE("amber")].") else . += SPAN_NOTICE("The capacitor charge indicator is [SPAN_GREEN("green")].") + var/ammo_string = get_ammo_string(user, distance) + if(ammo_string) + . += ammo_string /obj/item/gun/magnetic/attackby(var/obj/item/used_item, var/mob/user) diff --git a/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm b/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm index 30bb71cea69e..127b3ff7ca36 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm @@ -33,12 +33,12 @@ // Not going to check type repeatedly, if you code or varedit // load_type and get runtime errors, don't come crying to me. -/obj/item/gun/magnetic/railgun/show_ammo(var/mob/user) +/obj/item/gun/magnetic/railgun/get_ammo_string(mob/user, distance) var/obj/item/rcd_ammo/ammo = loaded if (ammo) - to_chat(user, "There are [ammo.remaining] shot\s remaining in \the [loaded].") + return SPAN_NOTICE("There [ammo.remaining == 1 ? "is" : "are"] [ammo.remaining] shot\s remaining in \the [loaded].") else - to_chat(user, "There is nothing loaded.") + return SPAN_NOTICE("There is nothing loaded.") /obj/item/gun/magnetic/railgun/check_ammo() var/obj/item/rcd_ammo/ammo = loaded diff --git a/mods/species/ascent/items/guns.dm b/mods/species/ascent/items/guns.dm index 99a76ce53514..e9e42dd733a4 100644 --- a/mods/species/ascent/items/guns.dm +++ b/mods/species/ascent/items/guns.dm @@ -60,9 +60,10 @@ if(isrobot(loc) || istype(loc, /obj/item/rig_module)) return loc.get_cell() -/obj/item/gun/magnetic/railgun/flechette/ascent/show_ammo(var/mob/user) +/obj/item/gun/magnetic/railgun/flechette/ascent/get_ammo_string(mob/user, distance) var/obj/item/cell/cell = get_cell() - to_chat(user, "There are [cell ? floor(cell.charge/charge_per_shot) : 0] shot\s remaining.") + var/shots_left = cell ? floor(cell.charge/charge_per_shot) : 0 + return SPAN_NOTICE("There [shots_left == 1 ? "is" : "are"] [shots_left] shot\s remaining.") /obj/item/gun/magnetic/railgun/flechette/ascent/check_ammo() var/obj/item/cell/cell = get_cell() From 1b4aabee0dbd2c998a43195c9a70984a1bd320c3 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 12:24:52 -0500 Subject: [PATCH 1177/1331] Remove defunct machinery uid and gl_uid vars --- code/game/machinery/_machines_base/machinery.dm | 8 -------- 1 file changed, 8 deletions(-) diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index eb27136f261c..bf6673bab4ce 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -31,12 +31,6 @@ Class Variables: panel_open (num) Whether the panel is open - uid (num) - Unique id of machine across all machines. - - gl_uid (global num) - Next uid value in sequence - stat (bitflag) Machine status bit flags. Possible bit flags: @@ -112,9 +106,7 @@ Class Procs: var/list/uncreated_component_parts = list(/obj/item/stock_parts/power/apc) /// null - no max. list(type part = number max). var/list/maximum_component_parts = list(/obj/item/stock_parts = 10) - var/uid var/panel_open = FALSE - var/static/gl_uid = 1 /// Can the machine be interacted with while de-powered. var/interact_offline = FALSE /// sound played on successful interface use From ee563efa65d5f1c63ea8fbedf783c6f66506ab3b Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:13:37 -0500 Subject: [PATCH 1178/1331] Remove pointless ToTicks procs --- code/_helpers/game.dm | 6 ------ code/game/machinery/doors/airlock.dm | 14 +++++++------- code/modules/alarm/alarm.dm | 2 +- code/modules/mob/living/silicon/silicon.dm | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index f084ad999bbb..94a6ab1bf7d7 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -401,12 +401,6 @@ temps[direction] = rstats return temps -/proc/MinutesToTicks(var/minutes) - return SecondsToTicks(60 * minutes) - -/proc/SecondsToTicks(var/seconds) - return seconds * 10 - /proc/window_flash(var/client_or_usr) if (!client_or_usr) return diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index b19b2f20f189..88a240fc9c25 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -175,11 +175,11 @@ About the new airlock wires panel: return src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) || src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2) /obj/machinery/door/airlock/proc/loseMainPower() - main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + (1 MINUTE) // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running if(backup_power_lost_until == -1 && !backupPowerCablesCut()) - backup_power_lost_until = world.time + SecondsToTicks(10) + backup_power_lost_until = world.time + (10 SECONDS) // Disable electricity if required if(electrified_until && isAllPowerLoss()) @@ -188,7 +188,7 @@ About the new airlock wires panel: update_icon() /obj/machinery/door/airlock/proc/loseBackupPower() - backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + (1 MINUTE) // Disable electricity if required if(electrified_until && isAllPowerLoss()) @@ -231,7 +231,7 @@ About the new airlock wires panel: else shockedby += text("\[[time_stamp()]\] - EMP)") message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." - src.electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) + src.electrified_until = duration == -1 ? -1 : world.time + (duration SECONDS) . = 1 if(feedback && message) @@ -939,8 +939,8 @@ About the new airlock wires panel: if(AM.blocks_airlock()) if(world.time > next_beep_at) playsound(src.loc, close_failure_blocked, 30, 0, -3) - next_beep_at = world.time + SecondsToTicks(10) - close_door_at = world.time + 6 + next_beep_at = world.time + (10 SECONDS) + close_door_at = world.time + (0.6 SECONDS) return FALSE for(var/turf/turf in locs) @@ -1082,7 +1082,7 @@ About the new airlock wires panel: spawn(0) open() if(prob(40/severity)) - var/duration = SecondsToTicks(30 / severity) + var/duration = (30 SECONDS) / severity if(electrified_until > -1 && (duration + world.time) > electrified_until) electrify(duration) ..() diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm index 61ae60e38c6c..eaa513f0a589 100644 --- a/code/modules/alarm/alarm.dm +++ b/code/modules/alarm/alarm.dm @@ -57,7 +57,7 @@ sources_assoc[source] = AS // Currently only non-0 durations can be altered (normal alarms VS EMP blasts) if(AS.duration) - duration = SecondsToTicks(duration) + duration = duration SECONDS AS.duration = duration AS.severity = severity diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index b5508d6d2217..ceed4ab9ed06 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -271,7 +271,7 @@ if(!(alarm.alarm_z() in SSmapping.get_connected_levels(my_z))) return // Didn't actually hear it as far as we're concerned. if(!next_alarm_notice) - next_alarm_notice = world.time + SecondsToTicks(10) + next_alarm_notice = world.time + (10 SECONDS) var/list/alarms = queued_alarms[alarm_handler] if(was_raised) From 4ca2c45058797f5d243846a9879a6036b577375d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:13:57 -0500 Subject: [PATCH 1179/1331] Remove unused beam overlay effect --- code/game/objects/effects/overlays.dm | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 38cd105e6d01..146baea3704e 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -4,16 +4,6 @@ /obj/effect/overlay/singularity_pull() return -/obj/effect/overlay/beam//Not actually a projectile, just an effect. - name="beam" - icon='icons/effects/beam.dmi' - icon_state= "b_beam" - var/tmp/atom/BeamSource - -/obj/effect/overlay/beam/Initialize() - . = ..() - QDEL_IN(src, 1 SECOND) - /obj/effect/overlay/palmtree_r name = "Palm tree" icon = 'icons/misc/beach2.dmi' From f367ff825892c0a553bd08614a5e3c9bbcff9cd3 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:14:57 -0500 Subject: [PATCH 1180/1331] Remove redundant and unused canister helpers --- code/game/machinery/atmoalter/canister.dm | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index bb0801f80b5b..0fc2b9e72776 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -139,7 +139,7 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) if(get_port()) add_overlay("can-connector") - var/tank_pressure = return_pressure() + var/tank_pressure = air_contents?.return_pressure() if(tank_pressure < 10) add_overlay("can-o0") else if(tank_pressure < ONE_ATMOSPHERE) @@ -205,18 +205,6 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) if(holding) holding.update_icon() -/obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = return_air() - if(GM?.total_volume>0) - return GM.temperature - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = return_air() - if(GM?.total_volume>0) - return GM.return_pressure() - return 0 - /obj/machinery/portable_atmospherics/canister/bullet_act(var/obj/item/projectile/Proj) if(!(Proj.atom_damage_type == BRUTE || Proj.atom_damage_type == BURN)) return @@ -258,8 +246,8 @@ EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) data["name"] = name data["canLabel"] = can_label ? 1 : 0 data["portConnected"] = get_port() ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["tankPressure"] = round(air_contents.return_pressure()) + data["releasePressure"] = round(release_pressure) data["minReleasePressure"] = round(0.1 ATM) data["maxReleasePressure"] = round(10 ATM) data["valveOpen"] = valve_open ? 1 : 0 From 8719a8c3f2ddaed2f53d3b2900248ffbb534a9bd Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:16:11 -0500 Subject: [PATCH 1181/1331] Remove defunct can_have_gravity area var --- code/game/area/areas.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index e2441adeb536..fafe3ecbb300 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -48,8 +48,6 @@ var/global/list/areas = list() var/oneoff_light = 0 var/oneoff_environ = 0 var/has_gravity = TRUE - /// If FALSE, this area is unable to have its gravity overridden by a gravity generator. Used on /area/space. - var/can_have_gravity = TRUE var/air_doors_activated = FALSE var/obj/machinery/apc/apc From 1591e3233779ef0b93e3fed425d248fb9ef408d1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:18:17 -0500 Subject: [PATCH 1182/1331] Remove defunct equipped_robot helper --- code/game/objects/items/__item.dm | 4 ---- code/game/objects/items/devices/gps.dm | 4 ---- 2 files changed, 8 deletions(-) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index d9164b23c5e2..2a8d149f734c 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -711,10 +711,6 @@ RAISE_EVENT(/decl/observ/mob_equipped, user, src, slot) RAISE_EVENT(/decl/observ/item_equipped, src, user, slot) -// As above but for items being equipped to an active module on a robot. -/obj/item/proc/equipped_robot(var/mob/user) - return - //the mob M is attempting to equip this item into the slot passed through as 'slot'. Return 1 if it can do this and 0 if it can't. //Set disable_warning to 1 if you wish it to not give you outputs. //Set ignore_equipped to 1 if you wish to ignore covering checks etc. when this item is already equipped. diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index fa29f4316653..5413be88195c 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -100,10 +100,6 @@ var/global/list/all_gps_units = list() if(holder?.client && _compass) holder.client.screen -= _compass -/obj/item/gps/equipped_robot() - . = ..() - update_holder() - /obj/item/gps/equipped() . = ..() update_holder() From ce68a2e09e5436ed06ff0b4ce6e367da0af8c419 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:18:53 -0500 Subject: [PATCH 1183/1331] Remove unused try_teleport method --- code/modules/mob/mob_helpers.dm | 37 --------------------------------- 1 file changed, 37 deletions(-) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 7a546009a86c..872835bafbdd 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -562,43 +562,6 @@ english_only - whether to use traditional english letters only (for use in NanoU /mob/proc/ssd_check() return !client && !teleop && (last_ckey || !ai) -/mob/proc/try_teleport(var/area/thearea) - if(istype(thearea, /list)) - var/list/area_list = thearea - thearea = area_list[1] - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea)) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(buckled) - buckled = null - - var/attempt = null - var/success = 0 - var/turf/end - var/candidates = L.Copy() - while(L.len) - attempt = pick(L) - success = Move(attempt) - if(!success) - L.Remove(attempt) - else - end = attempt - break - - if(!success) - end = pick(candidates) - forceMove(end) - - return end - //Tries to find the mob's email. /proc/find_email(real_name) for(var/mob/mob in global.living_mob_list_) From 0d406f29798434b3add52c36fba145f6947e19be Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:22:59 -0500 Subject: [PATCH 1184/1331] Remove unused military rank grade variable --- code/datums/mil_ranks.dm | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/code/datums/mil_ranks.dm b/code/datums/mil_ranks.dm index 29a14bc5e985..b907d46f6102 100644 --- a/code/datums/mil_ranks.dm +++ b/code/datums/mil_ranks.dm @@ -69,11 +69,7 @@ */ /datum/mil_rank var/name = "Unknown" - var/name_short // Abbreviation of the name. Should be null if the - // rank doesn't usually serve as a prefix to the individual's name. - var/list/accessory //type of accesory that will be equipped by job code with this rank - var/sort_order = 0 // A numerical equivalent of the rank used to indicate its order when compared to other datums: eg e-1 = 1, o-1 = 11 - -//Returns short designation (yes shorter than name_short), like E1, O3 etc. -/datum/mil_rank/proc/grade() - return sort_order \ No newline at end of file + /// Abbreviation of the name. Should be null if the rank doesn't usually serve as a prefix to the individual's name. + var/name_short + ///type of accesory that will be equipped by job code with this rank + var/list/accessory From 334454ffd959cee7ec74f7b4efb5298e45b0ed59 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 13 Feb 2026 11:51:35 -0500 Subject: [PATCH 1185/1331] Remove unused tail hair variables --- code/modules/sprite_accessories/tails/_accessory_tail.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/sprite_accessories/tails/_accessory_tail.dm b/code/modules/sprite_accessories/tails/_accessory_tail.dm index 031e7ac40793..44d3c41facb1 100644 --- a/code/modules/sprite_accessories/tails/_accessory_tail.dm +++ b/code/modules/sprite_accessories/tails/_accessory_tail.dm @@ -35,8 +35,6 @@ color_blend = ICON_MULTIPLY var/icon_animation_states - var/hair_state - var/hair_blend = ICON_ADD /decl/sprite_accessory/tail/none name = "Default Tail" From 090f1473fcab45b1d1a7cfa6a0de9f8e4627a338 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 9 Feb 2026 01:46:07 -0500 Subject: [PATCH 1186/1331] Simplify some conditionals --- code/game/machinery/flasher.dm | 7 ++----- code/game/machinery/oxygen_pump.dm | 17 ++++++----------- code/game/objects/structures/window.dm | 1 - code/modules/materials/material_metabolism.dm | 2 -- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index edd7f125381e..cc28f19f2c22 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -38,11 +38,8 @@ /obj/machinery/flasher/attackby(obj/item/used_item, mob/user) if(IS_WIRECUTTER(used_item)) add_fingerprint(user, 0, used_item) - src.disable = !src.disable - if (src.disable) - user.visible_message("[user] has disconnected \the [src]'s flashbulb!", "You disconnect \the [src]'s flashbulb!") - if (!src.disable) - user.visible_message("[user] has connected \the [src]'s flashbulb!", "You connect \the [src]'s flashbulb!") + disable = !disable + user.visible_message(SPAN_WARNING("\The [user] has [disable ? "disconnected" : "reconnected"] \the [src]'s flashbulb!"), SPAN_WARNING("You [disable ? "disconnect" : "reconnect"] \the [src]'s flashbulb!")) return TRUE else return ..() diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 7482f522af6c..1f8f099fd5c9 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -195,22 +195,17 @@ data["releasePressure"] = 0 data["defaultReleasePressure"] = 0 data["maxReleasePressure"] = 0 - data["maskConnected"] = 0 - data["tankInstalled"] = 0 + data["tankInstalled"] = FALSE // this is the data which will be sent to the ui - if(tank) - data["tankPressure"] = round(tank.air_contents.return_pressure() ? tank.air_contents.return_pressure() : 0) + else + var/tank_pressure = tank.air_contents.return_pressure() + data["tankPressure"] = round(tank_pressure ? tank_pressure : 0) data["releasePressure"] = round(tank.distribute_pressure ? tank.distribute_pressure : 0) data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - data["maskConnected"] = 0 - data["tankInstalled"] = 1 - - if(!breather) - data["maskConnected"] = 0 - if(breather) - data["maskConnected"] = 1 + data["tankInstalled"] = TRUE + data["maskConnected"] = !!breather // update the ui if it exists, returns null if no ui is passed/found ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index f128e44b16f6..eaea88c34e70 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -404,7 +404,6 @@ . = ..(user) if(reinf_material) . += SPAN_NOTICE("It is reinforced with the [reinf_material.solid_name] lattice.") - if (reinf_material) switch (construction_state) if (CONSTRUCTION_STATE_NO_FRAME) . += SPAN_WARNING("The window is not in the frame.") diff --git a/code/modules/materials/material_metabolism.dm b/code/modules/materials/material_metabolism.dm index 71714cf27fb7..dcbed9dec06f 100644 --- a/code/modules/materials/material_metabolism.dm +++ b/code/modules/materials/material_metabolism.dm @@ -87,8 +87,6 @@ removed = inhale_met if(!removed) removed = metabolism - if(!removed) - removed = metabolism removed = M.get_adjusted_metabolism(removed) //adjust effective amounts - removed, dose, and max_dose - for mob size From f84d8b4412ec63af7e32680a2a916f94b4295e88 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Feb 2026 20:04:36 -0500 Subject: [PATCH 1187/1331] Remove redundant conditional checks --- mods/content/supermatter/structures/supermatter_crystal.dm | 7 ++----- mods/species/serpentid/datum/species.dm | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/mods/content/supermatter/structures/supermatter_crystal.dm b/mods/content/supermatter/structures/supermatter_crystal.dm index 8bc697cd896d..284f6d4e6954 100644 --- a/mods/content/supermatter/structures/supermatter_crystal.dm +++ b/mods/content/supermatter/structures/supermatter_crystal.dm @@ -539,11 +539,8 @@ var/global/list/supermatter_delam_accent_sounds = list( var/effect = max(0, min(200, power * config_hallucination_power * sqrt( 1 / max(1,get_dist(subject, src)))) ) subject.adjust_hallucination(effect, 0.25 * effect) - if(power) - var/size_calc = max((power / 200), 1) //this needs to be a decently small value, but not TOO small. - animate_filter("outline", list(size = size_calc)) - if(!power) - animate_filter("outline", list(size = 0)) + var/size_calc = power ? max(power / 200, 1) : 0 //this needs to be a decently small value if we have power, but not TOO small. + animate_filter("outline", list(size = size_calc)) color = color_matrix_contrast(Interpolate(1, 5, clamp( (damage - emergency_point) / (explosion_point - emergency_point), 0, 1))) diff --git a/mods/species/serpentid/datum/species.dm b/mods/species/serpentid/datum/species.dm index f01a3f4c746f..9e3e5aed99d5 100644 --- a/mods/species/serpentid/datum/species.dm +++ b/mods/species/serpentid/datum/species.dm @@ -126,8 +126,7 @@ if(!part) image_key += "0" continue - if(part) - image_key += "[part.bodytype.get_icon_cache_uid(part.owner)]" + image_key += "[part.bodytype.get_icon_cache_uid(part.owner)]" if(!BP_IS_PROSTHETIC(part) && (part.status & ORGAN_DEAD)) image_key += "2" else From 9a7e685ce92835210324f49974cafcefcb78cbc9 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 10 Feb 2026 20:06:01 -0500 Subject: [PATCH 1188/1331] Simplify more conditional logic --- code/game/machinery/atmo_control.dm | 5 +--- code/game/machinery/bodyscanner_console.dm | 5 +--- code/modules/admin/verbs/adminhelp.dm | 6 +---- code/modules/admin/verbs/debug.dm | 7 +----- .../spacesuits/rig/modules/computer.dm | 5 +--- code/modules/hydroponics/seed_machines.dm | 18 ++++----------- .../mob/living/silicon/robot/component.dm | 8 +++---- code/modules/power/generator.dm | 5 +--- .../modules/projectiles/projectile/bullets.dm | 4 ++-- code/modules/recycling/disposal.dm | 5 +--- code/modules/turbolift/turbolift.dm | 5 +--- .../components/logic.dm | 23 ++++++++----------- 12 files changed, 29 insertions(+), 67 deletions(-) diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index 65ecb25cd2e3..e498a6c87391 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -59,10 +59,7 @@ /obj/machinery/computer/air_control/proc/get_console_data() if(sensor_info) var/list/temp = list() - if(input_tag || output_tag) - data["control"] = 1 - else - data["control"] = 0 + data["control"] = !!(input_tag || output_tag) if(!sensor_name && sensor_tag) temp += list("long_name" = sensor_tag) diff --git a/code/game/machinery/bodyscanner_console.dm b/code/game/machinery/bodyscanner_console.dm index f8864ab712e3..90f5d54f7c77 100644 --- a/code/game/machinery/bodyscanner_console.dm +++ b/code/game/machinery/bodyscanner_console.dm @@ -58,10 +58,7 @@ /obj/machinery/body_scanconsole/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(connected && connected.occupant) data["scanEnabled"] = TRUE - if(ishuman(connected.occupant)) - data["isCompatible"] = TRUE - else - data["isCompatible"] = FALSE + data["isCompatible"] = ishuman(connected.occupant) else data["scanEnabled"] = FALSE diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 77017eee5f6c..dbce3b0808b5 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -44,11 +44,7 @@ var/global/list/adminhelp_ignored_words = list("unknown","the","a","an","of","mo msg += "[original_word]
    (CL) " continue else - var/mob/found = ckeys[word] - if(!found) - found = surnames[word] - if(!found) - found = forenames[word] + var/mob/found = ckeys[word] || surnames[word] || forenames[word] if(found) if(!(found in mobs_found)) mobs_found += found diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index e39ed6b84fa2..480a29e13010 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -452,12 +452,7 @@ if (!theme) theme = /datum/exoplanet_theme - var/daycycle = alert("Should the planet have a day-night cycle?","Day Night Cycle", "Yes", "No") - - if (daycycle == "Yes") - daycycle = TRUE - else - daycycle = FALSE + var/daycycle = alert("Should the planet have a day-night cycle?","Day Night Cycle", "Yes", "No") == "Yes" var/last_chance = alert("Spawn exoplanet?", "Final Confirmation", "Yes", "Cancel") diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index 68570144c141..673ecb204be0 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -66,10 +66,7 @@ /obj/item/rig_module/ai_container/Process() if(integrated_ai) var/obj/item/rig/rig = get_rig() - if(rig && rig.ai_override_enabled) - integrated_ai.get_rig_stats = 1 - else - integrated_ai.get_rig_stats = 0 + integrated_ai.get_rig_stats = rig?.ai_override_enabled /mob/living/Stat() . = ..() diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm index af1d79e8cbd5..055fd84cd2ed 100644 --- a/code/modules/hydroponics/seed_machines.dm +++ b/code/modules/hydroponics/seed_machines.dm @@ -126,25 +126,17 @@ data["geneMasks"] = SSplants.gene_masked_list data["activity"] = active data["degradation"] = degradation - - if(loaded_disk) - data["disk"] = 1 - else - data["disk"] = 0 - - if(seed) - data["loaded"] = "[seed.name]" - else - data["loaded"] = 0 + data["disk"] = !!loaded_disk + data["loaded"] = seed?.name || FALSE if(genetics) - data["hasGenetics"] = 1 + data["hasGenetics"] = TRUE data["sourceName"] = genetics.display_name if(!genetics.roundstart) data["sourceName"] += " (variety #[genetics.uid])" else - data["hasGenetics"] = 0 - data["sourceName"] = 0 + data["hasGenetics"] = FALSE + data["sourceName"] = FALSE ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 45a09beca266..f6d49a565a17 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -74,13 +74,13 @@ return (installed == 1) && (brute_damage + burn_damage < max_damage) && (!idle_usage || powered) /datum/robot_component/proc/update_power_state() - if(toggled == 0) - powered = 0 + if(!toggled) + powered = FALSE return if(owner.cell_use_power(idle_usage)) - powered = 1 + powered = TRUE else - powered = 0 + powered = FALSE // ARMOUR // Protects the cyborg from damage. Usually first module to be hit diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 01ab115896a3..af50b411ee56 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -208,10 +208,7 @@ data["secondaryOutletPressure"] = circ2.air2.return_pressure() data["secondaryOutletTemperature"] = circ2.air2.temperature - if(circ1 && circ2) - data["circConnected"] = 1 - else - data["circConnected"] = 0 + data["circConnected"] = !!(circ1 && circ2) // update the ui if it exists, returns null if no ui is passed/found diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index e94f5926a344..ec1c968a8859 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -47,9 +47,9 @@ /obj/item/projectile/bullet/attack_mob(var/mob/target_mob, var/distance, var/miss_modifier) if(penetrating > 0 && damage > 20 && prob(damage)) - mob_passthrough_check = 1 + mob_passthrough_check = TRUE else - mob_passthrough_check = 0 + mob_passthrough_check = FALSE . = ..() if(. == 1 && isliving(target_mob)) var/mob/living/squish = target_mob diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 49527c33cdbb..32ed9077eebb 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -273,10 +273,7 @@ var/global/list/diversion_junctions = list() return TOPIC_HANDLED if(href_list["pump"]) - if(text2num(href_list["pump"])) - mode = 1 - else - mode = 0 + mode = !!text2num(href_list["pump"]) update_icon() . = TOPIC_REFRESH diff --git a/code/modules/turbolift/turbolift.dm b/code/modules/turbolift/turbolift.dm index f343dce1d988..5619388fdf9d 100644 --- a/code/modules/turbolift/turbolift.dm +++ b/code/modules/turbolift/turbolift.dm @@ -85,10 +85,7 @@ return 0 target_floor = queued_floors[1] queued_floors -= target_floor - if(current_floor_index < floors.Find(target_floor)) - moving_upwards = 1 - else - moving_upwards = 0 + moving_upwards = (current_floor_index < floors.Find(target_floor)) if(doors_are_open()) if(!doors_closing) diff --git a/mods/content/integrated_electronics/components/logic.dm b/mods/content/integrated_electronics/components/logic.dm index ee09979abced..65445d8995a7 100644 --- a/mods/content/integrated_electronics/components/logic.dm +++ b/mods/content/integrated_electronics/components/logic.dm @@ -123,21 +123,18 @@ outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/lstate=FALSE + var/latch_state = FALSE /obj/item/integrated_circuit/logic/binary/gdlatch/do_work() - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/B = inputs[2] - var/datum/integrated_io/O = outputs[1] - var/datum/integrated_io/Q = outputs[2] - if(B.data) - if(A.data) - lstate=TRUE - else - lstate=FALSE - - O.data = lstate ? TRUE : FALSE - Q.data = !lstate ? TRUE : FALSE + var/datum/integrated_io/data_pin = inputs[1] + var/datum/integrated_io/load_pin = inputs[2] + var/datum/integrated_io/Q_pin = outputs[1] + var/datum/integrated_io/Qnot_pin = outputs[2] + if(load_pin.data) + latch_state = !!data_pin.data + + Q_pin.data = latch_state + Qnot_pin.data = !latch_state if(get_pin_data(IC_OUTPUT, 1)) activate_pin(2) else From db906d3b0fe4f557dfdb00eb78cbfe66725d4db8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:15:53 -0500 Subject: [PATCH 1189/1331] Make stack recipe cache use an alist --- code/modules/crafting/stack_recipes/_recipe_getter.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/crafting/stack_recipes/_recipe_getter.dm b/code/modules/crafting/stack_recipes/_recipe_getter.dm index 41bfffc954e8..cf79001263d0 100644 --- a/code/modules/crafting/stack_recipes/_recipe_getter.dm +++ b/code/modules/crafting/stack_recipes/_recipe_getter.dm @@ -16,7 +16,6 @@ /* * Recipe retrieval proc. */ -var/global/list/cached_recipes = list() /proc/get_stack_recipes(decl/material/mat, decl/material/reinf_mat, stack_type, tool_type, flat = FALSE) // No recipes for holograms or fluids. @@ -25,8 +24,9 @@ var/global/list/cached_recipes = list() #ifndef UNIT_TEST // key creation is SLOW and in unit testing almost every call to this will be a cache fail // Check if we've cached this before. + var/static/alist/cached_recipes = alist() var/key = jointext(list((mat?.name || "base"), (reinf_mat?.name || "base"), (stack_type || "base"), (tool_type || "base")), "-") - . = global.cached_recipes[key] + . = cached_recipes[key] #endif if(!.) @@ -49,5 +49,5 @@ var/global/list/cached_recipes = list() for(var/group_name in grouped_recipes) . += new /datum/stack_recipe_list(group_name, grouped_recipes[group_name]) #ifndef UNIT_TEST // associative list insertion is SLOW and in unit testing almost every call to this will be a cache fail - global.cached_recipes[key] = . + cached_recipes[key] = . #endif From c3df19812adcbf38901ebebb31acaed963c0f6fd Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:19:27 -0500 Subject: [PATCH 1190/1331] Reimplement active throwforce on energy blade --- code/game/objects/items/weapons/melee/energy.dm | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 58437b40dcb6..791857f636af 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -28,7 +28,6 @@ var/active = FALSE var/active_parry_chance = 15 - var/active_throwforce = 20 var/active_armour_pen = 50 var/active_edge = 1 var/active_sharp = 1 @@ -36,6 +35,7 @@ var/active_hitsound = 'sound/weapons/blade1.ogg' var/active_sound = 'sound/weapons/saberon.ogg' VAR_PROTECTED/_active_base_attack_force = 30 + VAR_PROTECTED/_active_thrown_force_multiplier = 2/3 var/inactive_sound = 'sound/weapons/saberoff.ogg' @@ -93,9 +93,10 @@ obj_flags |= OBJ_FLAG_NO_STORAGE set_sharp(active_sharp) set_edge(active_edge) - base_parry_chance = active_parry_chance - armor_penetration = active_armour_pen - hitsound = active_hitsound + base_parry_chance = active_parry_chance + armor_penetration = active_armour_pen + hitsound = active_hitsound + _thrown_force_multiplier = _active_thrown_force_multiplier w_class = max(w_class, ITEM_SIZE_NORMAL) slot_flags &= ~SLOT_POCKET @@ -107,9 +108,10 @@ obj_flags &= ~OBJ_FLAG_NO_STORAGE set_sharp(initial(sharp)) set_edge(initial(edge)) - base_parry_chance = initial(base_parry_chance) - armor_penetration = initial(armor_penetration) - hitsound = initial(hitsound) + base_parry_chance = initial(base_parry_chance) + armor_penetration = initial(armor_penetration) + hitsound = initial(hitsound) + _thrown_force_multiplier = initial(_thrown_force_multiplier) w_class = initial(w_class) slot_flags = initial(slot_flags) From ed03e36eeccd73d3fb2e610f9f8b9f49a640e6e6 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 12 Feb 2026 13:20:34 -0500 Subject: [PATCH 1191/1331] Replace fire alarm timeofday usage with REALTIMEOFDAY --- code/game/machinery/alarm.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 5b72753a9e6d..df9f77c2bc29 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -923,14 +923,14 @@ FIRE ALARM if(src.timing) if(src.time > 0) - src.time = src.time - ((world.timeofday - last_process)/10) + src.time = src.time - ((REALTIMEOFDAY - last_process)/(1 SECOND)) else src.alarm() src.time = 0 src.timing = 0 STOP_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) src.updateDialog() - last_process = world.timeofday + last_process = REALTIMEOFDAY if(locate(/obj/fire) in loc) alarm() @@ -988,7 +988,7 @@ FIRE ALARM . = TOPIC_REFRESH else if (href_list["time"]) src.timing = text2num(href_list["time"]) - last_process = world.timeofday + last_process = REALTIMEOFDAY START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) . = TOPIC_REFRESH else if (href_list["tp"]) From 3693c356d29013f94f96490e4f1c7bd7e31edac8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:27:54 -0400 Subject: [PATCH 1192/1331] Move geothermal generators into their own module --- .../{power => }/geothermal/_geothermal.dm | 106 +----------------- .../modules/geothermal/_geothermal_geysers.dm | 70 ++++++++++++ .../geothermal/_geothermal_particles.dm | 22 ++++ .../geothermal/geothermal_circuit.dm | 0 .../geothermal/geothermal_extension.dm | 0 nebula.dme | 8 +- 6 files changed, 103 insertions(+), 103 deletions(-) rename code/modules/{power => }/geothermal/_geothermal.dm (64%) create mode 100644 code/modules/geothermal/_geothermal_geysers.dm create mode 100644 code/modules/geothermal/_geothermal_particles.dm rename code/modules/{power => }/geothermal/geothermal_circuit.dm (100%) rename code/modules/{power => }/geothermal/geothermal_extension.dm (100%) diff --git a/code/modules/power/geothermal/_geothermal.dm b/code/modules/geothermal/_geothermal.dm similarity index 64% rename from code/modules/power/geothermal/_geothermal.dm rename to code/modules/geothermal/_geothermal.dm index 138a38cd1cf4..632dc84e1e4b 100644 --- a/code/modules/power/geothermal/_geothermal.dm +++ b/code/modules/geothermal/_geothermal.dm @@ -1,103 +1,3 @@ -var/global/const/GEOTHERMAL_EFFICIENCY_MOD = 0.2 -var/global/const/GEOTHERMAL_PRESSURE_LOSS = 0.3 -var/global/const/GEOTHERMAL_PRESSURE_CONSUMED_PER_TICK = 0.05 -var/global/const/GEOTHERMAL_PRESSURE_TO_POWER = 800 -var/global/const/MAX_GEOTHERMAL_PRESSURE = 12000 - -////////////////////////////////////////////////////////////////////// -// Geyser Steam Particle Emitter -////////////////////////////////////////////////////////////////////// - -///Particle emitter that emits a ~64 pixels by ~192 pixels high column of steam while active. -/particles/geyser_steam - icon_state = "smallsmoke" - icon = 'icons/effects/effects.dmi' - width = WORLD_ICON_SIZE * 2 //Particles expand a bit as they climb, so need a bit of space on the width - height = WORLD_ICON_SIZE * 6 //Needs to be really tall, because particles stop being drawn outside of the canvas. - count = 64 - spawning = 5 - lifespan = generator("num", 1 SECOND, 2.5 SECONDS, LINEAR_RAND) - fade = 3 SECONDS - fadein = 0.25 SECONDS - grow = 0.1 - velocity = generator("vector", list(0, 0), list(0, 0.2)) - position = generator("circle", -6, 6, NORMAL_RAND) - gravity = list(0, 0.40) - scale = generator("vector", list(0.3, 0.3), list(1,1), NORMAL_RAND) - rotation = generator("num", -45, 45) - spin = generator("num", -20, 20) - -////////////////////////////////////////////////////////////////////// -// Geyser Object -////////////////////////////////////////////////////////////////////// - -/// A prop that periodically emit steam spouts and can have a geothermal generator placed on top to generate power. -/obj/effect/geyser - name = "geothermal vent" - desc = "A vent leading to an underground geothermally heated reservoir, which periodically spews superheated liquid." - icon = 'icons/effects/geyser.dmi' - icon_state = "geyser" - anchored = TRUE - layer = TURF_LAYER + 0.01 - level = LEVEL_BELOW_PLATING // Goes under floor/plating - /// The particle emitter that will generate the steam column effect for this geyser - var/particles/geyser_steam/steamfx - -/obj/effect/geyser/Initialize(ml) - . = ..() - if(prob(50)) - var/matrix/M = matrix() - M.Scale(-1, 1) - transform = M - set_extension(src, /datum/extension/geothermal_vent) - steamfx = new //Prepare our FX - -///Async proc that enables the particle emitter for the steam column for a few seconds -/obj/effect/geyser/proc/do_spout() - set waitfor = FALSE - particles = steamfx - particles.spawning = initial(particles.spawning) - sleep(6 SECONDS) - particles.spawning = 0 - -/obj/effect/geyser/explosion_act(severity) - . = ..() - if(!QDELETED(src) && prob(100 - (25 * severity))) - physically_destroyed() - -/obj/effect/geyser/hide(hide) - var/datum/extension/geothermal_vent/E = get_extension(src, /datum/extension/geothermal_vent) - if(istype(E)) - E.set_obstructed(hide) - . = ..() - update_icon() - -////////////////////////////////////////////////////////////////////// -// Underwater Geyser Variant -////////////////////////////////////////////////////////////////////// - -/obj/effect/geyser/underwater - desc = "A crack in the ocean floor that occasionally vents gouts of superheated water and steam." - -/obj/effect/geyser/underwater/Initialize(ml) - . = ..() - if(!loc) - return INITIALIZE_HINT_QDEL - for(var/turf/floor/seafloor/T in RANGE_TURFS(loc, 5)) - var/dist = get_dist(loc, T)-1 - if(prob(100 - (dist * 20))) - if(prob(25)) - T = T.ChangeTurf(/turf/floor/clay) - else - T = T.ChangeTurf(/turf/floor/mud) - if(prob(50 - (dist * 10))) - new /obj/random/seaweed(T) - -/obj/effect/geyser/underwater/do_spout() - set waitfor = FALSE - var/turf/T = get_turf(src) - T.show_bubbles() - ////////////////////////////////////////////////////////////////////// // Geothermal Generator ////////////////////////////////////////////////////////////////////// @@ -131,6 +31,12 @@ var/global/const/MAX_GEOTHERMAL_PRESSURE = 12000 var/tmp/list/neighbor_connectors var/tmp/list/neighbor_connectors_glow + var/const/GEOTHERMAL_EFFICIENCY_MOD = 0.2 + var/const/GEOTHERMAL_PRESSURE_LOSS = 0.3 + var/const/GEOTHERMAL_PRESSURE_CONSUMED_PER_TICK = 0.05 + var/const/GEOTHERMAL_PRESSURE_TO_POWER = 800 + var/const/MAX_GEOTHERMAL_PRESSURE = 12000 + //#TODO: Maybe should cache neighbors and put listeners on them? /obj/machinery/geothermal/Initialize(mapload, d, populate_parts = TRUE) diff --git a/code/modules/geothermal/_geothermal_geysers.dm b/code/modules/geothermal/_geothermal_geysers.dm new file mode 100644 index 000000000000..54c4bc09e7ee --- /dev/null +++ b/code/modules/geothermal/_geothermal_geysers.dm @@ -0,0 +1,70 @@ +////////////////////////////////////////////////////////////////////// +// Geyser Object +////////////////////////////////////////////////////////////////////// + +/// A prop that periodically emit steam spouts and can have a geothermal generator placed on top to generate power. +/obj/effect/geyser + name = "geothermal vent" + desc = "A vent leading to an underground geothermally heated reservoir, which periodically spews superheated liquid." + icon = 'icons/effects/geyser.dmi' + icon_state = "geyser" + anchored = TRUE + layer = TURF_LAYER + 0.01 + level = LEVEL_BELOW_PLATING // Goes under floor/plating + /// The particle emitter that will generate the steam column effect for this geyser + var/particles/geyser_steam/steamfx + +/obj/effect/geyser/Initialize(ml) + . = ..() + if(prob(50)) + var/matrix/M = matrix() + M.Scale(-1, 1) + transform = M + set_extension(src, /datum/extension/geothermal_vent) + steamfx = new //Prepare our FX + +///Async proc that enables the particle emitter for the steam column for a few seconds +/obj/effect/geyser/proc/do_spout() + set waitfor = FALSE + particles = steamfx + particles.spawning = initial(particles.spawning) + sleep(6 SECONDS) + particles.spawning = 0 + +/obj/effect/geyser/explosion_act(severity) + . = ..() + if(!QDELETED(src) && prob(100 - (25 * severity))) + physically_destroyed() + +/obj/effect/geyser/hide(hide) + var/datum/extension/geothermal_vent/E = get_extension(src, /datum/extension/geothermal_vent) + if(istype(E)) + E.set_obstructed(hide) + . = ..() + update_icon() + +////////////////////////////////////////////////////////////////////// +// Underwater Geyser Variant +////////////////////////////////////////////////////////////////////// + +/obj/effect/geyser/underwater + desc = "A crack in the ocean floor that occasionally vents gouts of superheated water and steam." + +/obj/effect/geyser/underwater/Initialize(ml) + . = ..() + if(!loc) + return INITIALIZE_HINT_QDEL + for(var/turf/floor/seafloor/T in RANGE_TURFS(loc, 5)) + var/dist = get_dist(loc, T)-1 + if(prob(100 - (dist * 20))) + if(prob(25)) + T = T.ChangeTurf(/turf/floor/clay) + else + T = T.ChangeTurf(/turf/floor/mud) + if(prob(50 - (dist * 10))) + new /obj/random/seaweed(T) + +/obj/effect/geyser/underwater/do_spout() + set waitfor = FALSE + var/turf/T = get_turf(src) + T.show_bubbles() diff --git a/code/modules/geothermal/_geothermal_particles.dm b/code/modules/geothermal/_geothermal_particles.dm new file mode 100644 index 000000000000..d1b8423f02c2 --- /dev/null +++ b/code/modules/geothermal/_geothermal_particles.dm @@ -0,0 +1,22 @@ +////////////////////////////////////////////////////////////////////// +// Geyser Steam Particle Emitter +////////////////////////////////////////////////////////////////////// + +///Particle emitter that emits a ~64 pixels by ~192 pixels high column of steam while active. +/particles/geyser_steam + icon_state = "smallsmoke" + icon = 'icons/effects/effects.dmi' + width = WORLD_ICON_SIZE * 2 //Particles expand a bit as they climb, so need a bit of space on the width + height = WORLD_ICON_SIZE * 6 //Needs to be really tall, because particles stop being drawn outside of the canvas. + count = 64 + spawning = 5 + lifespan = generator("num", 1 SECOND, 2.5 SECONDS, LINEAR_RAND) + fade = 3 SECONDS + fadein = 0.25 SECONDS + grow = 0.1 + velocity = generator("vector", list(0, 0), list(0, 0.2)) + position = generator("circle", -6, 6, NORMAL_RAND) + gravity = list(0, 0.40) + scale = generator("vector", list(0.3, 0.3), list(1,1), NORMAL_RAND) + rotation = generator("num", -45, 45) + spin = generator("num", -20, 20) \ No newline at end of file diff --git a/code/modules/power/geothermal/geothermal_circuit.dm b/code/modules/geothermal/geothermal_circuit.dm similarity index 100% rename from code/modules/power/geothermal/geothermal_circuit.dm rename to code/modules/geothermal/geothermal_circuit.dm diff --git a/code/modules/power/geothermal/geothermal_extension.dm b/code/modules/geothermal/geothermal_extension.dm similarity index 100% rename from code/modules/power/geothermal/geothermal_extension.dm rename to code/modules/geothermal/geothermal_extension.dm diff --git a/nebula.dme b/nebula.dme index 34f8a2ff2835..57a119a1c766 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2549,6 +2549,11 @@ #include "code\modules\geology\strata_metamorphic.dm" #include "code\modules\geology\strata_permafrost.dm" #include "code\modules\geology\strata_sedimentary.dm" +#include "code\modules\geothermal\_geothermal.dm" +#include "code\modules\geothermal\_geothermal_geysers.dm" +#include "code\modules\geothermal\_geothermal_particles.dm" +#include "code\modules\geothermal\geothermal_circuit.dm" +#include "code\modules\geothermal\geothermal_extension.dm" #include "code\modules\ghosttrap\trap.dm" #include "code\modules\goals\_goal.dm" #include "code\modules\goals\goal_ambition.dm" @@ -3484,9 +3489,6 @@ #include "code\modules\power\fusion\core\core_field.dm" #include "code\modules\power\fusion\fuel_injector\fuel_injector.dm" #include "code\modules\power\fusion\gyrotron\gyrotron.dm" -#include "code\modules\power\geothermal\_geothermal.dm" -#include "code\modules\power\geothermal\geothermal_circuit.dm" -#include "code\modules\power\geothermal\geothermal_extension.dm" #include "code\modules\power\singularity\collector.dm" #include "code\modules\power\singularity\containment_field.dm" #include "code\modules\power\singularity\emitter.dm" From 40bfc24fc542a97fa9b223ee5090af759eb923e1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:28:39 -0400 Subject: [PATCH 1193/1331] Move fuel assembly and compressor into its own module --- code/modules/{power => }/fuel_assembly/fuel_assembly.dm | 0 code/modules/{power => }/fuel_assembly/fuel_compressor.dm | 0 nebula.dme | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename code/modules/{power => }/fuel_assembly/fuel_assembly.dm (100%) rename code/modules/{power => }/fuel_assembly/fuel_compressor.dm (100%) diff --git a/code/modules/power/fuel_assembly/fuel_assembly.dm b/code/modules/fuel_assembly/fuel_assembly.dm similarity index 100% rename from code/modules/power/fuel_assembly/fuel_assembly.dm rename to code/modules/fuel_assembly/fuel_assembly.dm diff --git a/code/modules/power/fuel_assembly/fuel_compressor.dm b/code/modules/fuel_assembly/fuel_compressor.dm similarity index 100% rename from code/modules/power/fuel_assembly/fuel_compressor.dm rename to code/modules/fuel_assembly/fuel_compressor.dm diff --git a/nebula.dme b/nebula.dme index 34f8a2ff2835..4786090875a6 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2475,6 +2475,8 @@ #include "code\modules\food\utensils\utensil_hybrid.dm" #include "code\modules\food\utensils\utensil_knife.dm" #include "code\modules\food\utensils\utensil_spoon.dm" +#include "code\modules\fuel_assembly\fuel_assembly.dm" +#include "code\modules\fuel_assembly\fuel_compressor.dm" #include "code\modules\games\boardgame.dm" #include "code\modules\games\cardemon.dm" #include "code\modules\games\cards.dm" @@ -3469,8 +3471,6 @@ #include "code\modules\power\fission\core.dm" #include "code\modules\power\fission\core_control.dm" #include "code\modules\power\fission\fission_circuits.dm" -#include "code\modules\power\fuel_assembly\fuel_assembly.dm" -#include "code\modules\power\fuel_assembly\fuel_compressor.dm" #include "code\modules\power\fusion\_setup.dm" #include "code\modules\power\fusion\fusion_circuits.dm" #include "code\modules\power\fusion\fusion_particle_catcher.dm" From 1a2b29c944346870fd8030d240cf17e543d80f19 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 20 Dec 2025 22:09:57 -0500 Subject: [PATCH 1194/1331] Reorganize power module into subfolders --- code/modules/power/{ => cable}/cable.dm | 423 ------------------ code/modules/power/cable/cable_coil.dm | 422 +++++++++++++++++ code/modules/power/{ => cable}/heavycable.dm | 0 code/modules/power/cell/_cell.dm | 117 +++++ .../power/{cell.dm => cell/cell_types.dm} | 120 ----- code/modules/power/solar/solar_control.dm | 215 +++++++++ .../power/{solar.dm => solar/solar_panel.dm} | 216 --------- code/modules/power/{ => solar}/tracker.dm | 1 - nebula.dme | 13 +- 9 files changed, 762 insertions(+), 765 deletions(-) rename code/modules/power/{ => cable}/cable.dm (56%) create mode 100644 code/modules/power/cable/cable_coil.dm rename code/modules/power/{ => cable}/heavycable.dm (100%) create mode 100644 code/modules/power/cell/_cell.dm rename code/modules/power/{cell.dm => cell/cell_types.dm} (68%) create mode 100644 code/modules/power/solar/solar_control.dm rename code/modules/power/{solar.dm => solar/solar_panel.dm} (58%) rename code/modules/power/{ => solar}/tracker.dm (99%) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable/cable.dm similarity index 56% rename from code/modules/power/cable.dm rename to code/modules/power/cable/cable.dm index 7067ecc7974b..00cc9a0e2d83 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable/cable.dm @@ -494,426 +494,3 @@ var/global/list/obj/structure/cable/all_cables = list() P.disconnect_from_network() //remove from current network powernet = null // And finally null the powernet var. - -/////////////////////////////////////////////// -// The cable coil object, used for laying cable -/////////////////////////////////////////////// - -//////////////////////////////// -// Definitions -//////////////////////////////// - -#define MAXCOIL 30 - -/obj/item/stack/cable_coil - name = "multipurpose cable coil" - icon = 'icons/obj/items/cable_coil.dmi' - icon_state = ICON_STATE_WORLD - randpixel = 2 - amount = MAXCOIL - max_amount = MAXCOIL - color = COLOR_MAROON - paint_color = COLOR_MAROON - desc = "A coil of wiring, suitable for both delicate electronics and heavy-duty power supply." - singular_name = "length of cable" - plural_name = "lengths of cable" - w_class = ITEM_SIZE_NORMAL - throw_speed = 2 - throw_range = 5 - material = /decl/material/solid/metal/copper - matter = list( - /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/organic/plastic = MATTER_AMOUNT_TRACE - ) - obj_flags = OBJ_FLAG_CONDUCTIBLE - slot_flags = SLOT_LOWER_BODY - item_state = "coil" - attack_verb = list("whipped", "lashed", "disciplined", "flogged") - stack_merge_type = /obj/item/stack/cable_coil - matter_multiplier = 0.15 - /// Whether or not this cable coil can even have a color in the first place. - var/can_have_color = TRUE - /// The type of cable structure produced when laying down this cable. - /// src.cable_type::cable_type should equal stack_merge_type, ideally - var/cable_type = /obj/structure/cable - -/obj/item/stack/cable_coil/single - amount = 1 - -/obj/item/stack/cable_coil/cyborg - name = "cable coil synthesizer" - desc = "A device that makes cable." - gender = NEUTER - matter = null - uses_charge = 1 - charge_costs = list(1) - max_health = ITEM_HEALTH_NO_DAMAGE - is_spawnable_type = FALSE - -/obj/item/stack/cable_coil/Initialize(mapload, c_length, var/param_color = null) - . = ..(mapload, c_length) - set_extension(src, /datum/extension/tool/variable/simple, list( - TOOL_CABLECOIL = TOOL_QUALITY_DEFAULT, - TOOL_SUTURES = TOOL_QUALITY_MEDIOCRE - )) - if (can_have_color && param_color) // It should be red by default, so only recolor it if parameter was specified. - set_color(param_color) - update_icon() - update_wclass() - -/////////////////////////////////// -// General procedures -/////////////////////////////////// - -//you can use wires to heal robotics -/obj/item/stack/cable_coil/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - var/obj/item/organ/external/affecting = istype(target) && GET_EXTERNAL_ORGAN(target, user?.get_target_zone()) - if(affecting && user.check_intent(I_FLAG_HELP)) - if(!affecting.is_robotic()) - to_chat(user, SPAN_WARNING("\The [target]'s [affecting.name] is not robotic. \The [src] cannot repair it.")) - else if(BP_IS_BRITTLE(affecting)) - to_chat(user, SPAN_WARNING("\The [target]'s [affecting.name] is hard and brittle. \The [src] cannot repair it.")) - else - var/use_amt = min(src.amount, ceil(affecting.burn_dam/3), 5) - if(can_use(use_amt) && affecting.robo_repair(3*use_amt, BURN, "some damaged wiring", src, user)) - use(use_amt) - return TRUE - return ..() - -/obj/item/stack/cable_coil/on_update_icon() - . = ..() - if (!paint_color && can_have_color) - var/list/possible_cable_colours = get_global_cable_colors() - set_color(possible_cable_colours[pick(possible_cable_colours)]) - if(amount == 1) - icon_state = "coil1" - SetName("cable piece") - else if(amount == 2) - icon_state = "coil2" - SetName("cable piece") - else if(amount > 2 && amount != max_amount) - icon_state = "coil" - SetName(initial(name)) - else - icon_state = "coil-max" - SetName(initial(name)) - -/obj/item/stack/cable_coil/proc/set_cable_color(var/selected_color, var/user) - if(!selected_color || !can_have_color) - return - - var/list/possible_cable_colours = get_global_cable_colors() - var/final_color = possible_cable_colours[selected_color] - if(!final_color) - selected_color = "Red" - final_color = possible_cable_colours[selected_color] - set_color(final_color) - to_chat(user, SPAN_NOTICE("You change \the [src]'s color to [lowertext(selected_color)].")) - -/obj/item/stack/cable_coil/proc/update_wclass() - if(amount == 1) - w_class = ITEM_SIZE_TINY - else - w_class = ITEM_SIZE_SMALL - -/obj/item/stack/cable_coil/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(distance > 1) - return - if(get_amount() == 1) - . += "\A [singular_name]." - else if(get_amount() == 2) - . += "Two [plural_name]." - else - . += "A coil of power cable. There are [get_amount()] [plural_name] in the coil." - -/obj/item/stack/cable_coil/verb/make_restraint() - set name = "Make Cable Restraints" - set category = "Object" - var/mob/M = usr - - if(ishuman(M) && !M.incapacitated()) - if(!isturf(usr.loc)) return - if(!src.use(15)) - to_chat(usr, SPAN_WARNING("You need at least 15 [plural_name] to make restraints!")) - return - var/obj/item/handcuffs/cable/B = new /obj/item/handcuffs/cable(usr.loc) - B.set_color(color) - to_chat(usr, SPAN_NOTICE("You wind some [plural_name] together to make some restraints.")) - else - to_chat(usr, SPAN_NOTICE("You cannot do that.")) - -/obj/item/stack/cable_coil/cyborg/verb/set_colour() - set name = "Change Colour" - set category = "Object" - - var/selected_type = input("Pick new colour.", "Cable Colour", null, null) as null|anything in get_global_cable_colors() - set_cable_color(selected_type, usr) - -// Items usable on a cable coil : -// - Wirecutters : cut them duh ! -// - Cable coil : merge cables -/obj/item/stack/cable_coil/can_merge_stacks(var/obj/item/stack/other) - return !other || (istype(other) && other.color == color) - -/obj/item/stack/cable_coil/cyborg/can_merge_stacks(var/obj/item/stack/other) - return TRUE - -/obj/item/stack/cable_coil/transfer_to(obj/item/stack/cable_coil/coil) - if(!istype(coil)) - return 0 - if(!(can_merge_stacks(coil) || coil.can_merge_stacks(src))) - return 0 - - return ..() - -/////////////////////////////////////////////// -// Cable laying procedures -////////////////////////////////////////////// - -// called when cable_coil is clicked on a turf -/obj/item/stack/cable_coil/proc/turf_place(turf/F, mob/user) - if(!isturf(user.loc)) - return - - if(get_amount() < 1) // Out of cable - to_chat(user, SPAN_WARNING("There is no [plural_name] left.")) - return - - if(get_dist(F,user) > 1) // Too far - to_chat(user, SPAN_WARNING("You can't lay cable at a place that far away.")) - return - - if(!F.is_plating()) // Ff floor is intact, complain - to_chat(user, SPAN_WARNING("You can't lay cable there unless the floor tiles are removed.")) - return - - var/dirn - if(user.loc == F) - dirn = user.dir // if laying on the tile we're on, lay in the direction we're facing - else - dirn = get_dir(F, user) - - var/end_dir = 0 - if(istype(F) && F.is_open()) - if(!can_use(2)) - to_chat(user, SPAN_WARNING("You don't have enough [plural_name] to do this!")) - return - end_dir = DOWN - - for(var/obj/structure/cable/LC in F) - if((LC.d1 == dirn && LC.d2 == end_dir ) || ( LC.d2 == dirn && LC.d1 == end_dir)) - to_chat(user, SPAN_WARNING("There's already a cable at that position.")) - return - - put_cable(F, user, end_dir, dirn) - if(end_dir == DOWN) - put_cable(GetBelow(F), user, UP, 0) - return TRUE - -// called when cable_coil is click on an installed obj/cable -// or click on a turf that already contains a "node" cable -/obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user) - var/turf/U = user.loc - if(!isturf(U)) - return - - var/turf/T = C.loc - - if(!isturf(T) || !T.is_plating()) // sanity checks, also stop use interacting with T-scanner revealed cable - return - - if(get_dist(C, user) > 1) // make sure it's close enough - to_chat(user, SPAN_WARNING("You can't lay cable at a place that far away.")) - return - - if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing - return turf_place(T,user) - - var/dirn = get_dir(C, user) - - // one end of the clicked cable is pointing towards us - if(C.d1 == dirn || C.d2 == dirn) - if(!U.is_plating()) // can't place a cable if the floor is complete - to_chat(user, SPAN_WARNING("You can't lay cable there unless the floor tiles are removed.")) - return - else - // cable is pointing at us, we're standing on an open tile - // so create a stub pointing at the clicked cable on our tile - - var/fdirn = turn(dirn, 180) // the opposite direction - - for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already - if(LC.d1 == fdirn || LC.d2 == fdirn) - to_chat(user, SPAN_WARNING("There's already a cable at that position.")) - return - put_cable(U,user,0,fdirn) - return TRUE - - // exisiting cable doesn't point at our position, so see if it's a stub - else if(C.d1 == 0) - // if so, make it a full cable pointing from it's old direction to our dirn - var/nd1 = C.d2 // these will be the new directions - var/nd2 = dirn - - - if(nd1 > nd2) // swap directions to match icons/states - nd1 = dirn - nd2 = C.d2 - - - for(var/obj/structure/cable/LC in T) // check to make sure there's no matching cable - if(LC == C) // skip the cable we're interacting with - continue - if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction - to_chat(user, SPAN_WARNING("There's already a cable at that position.")) - return - - - C.cableColor(color) - - C.d1 = nd1 - C.d2 = nd2 - - C.add_fingerprint() - C.update_icon() - - - C.mergeConnectedNetworks(C.d1) //merge the powernets... - C.mergeConnectedNetworks(C.d2) //...in the two new cable directions - C.mergeConnectedNetworksOnTurf() - - if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d1) - - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) - - use(1) - - if (C.shock(user, 50)) - if (prob(50)) //fail - new/obj/item/stack/cable_coil(C.loc, 2, C.color) - qdel(C) - return - - C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. - return TRUE - - else if(C.d1 == UP) //Special cases for zcables, since they behave weirdly - . = turf_place(T, user) - if(.) - to_chat(user, SPAN_NOTICE("You connect the cable hanging from the ceiling.")) - return . - -/obj/item/stack/cable_coil/proc/put_cable(turf/F, mob/user, d1, d2) - if(!istype(F)) - return FALSE - - var/obj/structure/cable/C = new cable_type(F) - C.cableColor(color) - C.d1 = d1 - C.d2 = d2 - C.add_fingerprint(user) - C.update_icon() - - //create a new powernet with the cable, if needed it will be merged later - var/datum/powernet/PN = new() - PN.add_cable(C) - - C.mergeConnectedNetworks(C.d1) //merge the powernets... - C.mergeConnectedNetworks(C.d2) //...in the two new cable directions - C.mergeConnectedNetworksOnTurf() - - if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d1) - - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) - - . = use(1) - if (C.shock(user, 50)) - if (prob(50)) //fail - new/obj/item/stack/cable_coil(C.loc, 1, C.color) - qdel(C) - return FALSE - -////////////////////////////// -// Misc. -///////////////////////////// -/obj/item/stack/cable_coil/five - amount = 5 - -/obj/item/stack/cable_coil/cut - item_state = "coil2" - -/obj/item/stack/cable_coil/cut/Initialize() - . = ..() - src.amount = rand(1,2) - update_icon() - update_wclass() - -/obj/item/stack/cable_coil/yellow - color = COLOR_AMBER - paint_color = COLOR_AMBER - -/obj/item/stack/cable_coil/blue - color = COLOR_CYAN_BLUE - paint_color = COLOR_CYAN_BLUE - -/obj/item/stack/cable_coil/green - color = COLOR_GREEN - paint_color = COLOR_GREEN - -/obj/item/stack/cable_coil/pink - color = COLOR_PURPLE - paint_color = COLOR_PURPLE - -/obj/item/stack/cable_coil/orange - color = COLOR_ORANGE - paint_color = COLOR_ORANGE - -/obj/item/stack/cable_coil/cyan - color = COLOR_SKY_BLUE - paint_color = COLOR_SKY_BLUE - -/obj/item/stack/cable_coil/white - color = COLOR_SILVER - paint_color = COLOR_SILVER - -/obj/item/stack/cable_coil/lime - color = COLOR_LIME - paint_color = COLOR_LIME - -/obj/item/stack/cable_coil/random/Initialize(mapload, c_length, param_color) - var/list/possible_cable_colours = get_global_cable_colors() - set_color(possible_cable_colours[pick(possible_cable_colours)]) - . = ..() - -// Produces cable coil from a rig power cell. -/obj/item/stack/cable_coil/fabricator - name = "cable fabricator" - var/cost_per_cable = 10 - -/obj/item/stack/cable_coil/fabricator/split(var/tamount, var/force=FALSE) - return - -/obj/item/stack/cable_coil/fabricator/get_cell() - if(istype(loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = loc - return module.get_cell() - if(isrobot(loc)) - var/mob/living/silicon/robot/robot = loc - return robot.get_cell() - -/obj/item/stack/cable_coil/fabricator/use(var/used) - var/obj/item/cell/cell = get_cell() - return cell?.use(used * cost_per_cable) - -/obj/item/stack/cable_coil/fabricator/get_amount() - var/obj/item/cell/cell = get_cell() - . = (cell ? floor(cell.charge / cost_per_cable) : 0) - -/obj/item/stack/cable_coil/fabricator/get_max_amount() - var/obj/item/cell/cell = get_cell() - . = (cell ? floor(cell.maxcharge / cost_per_cable) : 0) diff --git a/code/modules/power/cable/cable_coil.dm b/code/modules/power/cable/cable_coil.dm new file mode 100644 index 000000000000..613395954b66 --- /dev/null +++ b/code/modules/power/cable/cable_coil.dm @@ -0,0 +1,422 @@ +/////////////////////////////////////////////// +// The cable coil object, used for laying cable +/////////////////////////////////////////////// + +//////////////////////////////// +// Definitions +//////////////////////////////// + +#define MAXCOIL 30 + +/obj/item/stack/cable_coil + name = "multipurpose cable coil" + icon = 'icons/obj/items/cable_coil.dmi' + icon_state = ICON_STATE_WORLD + randpixel = 2 + amount = MAXCOIL + max_amount = MAXCOIL + color = COLOR_MAROON + paint_color = COLOR_MAROON + desc = "A coil of wiring, suitable for both delicate electronics and heavy-duty power supply." + singular_name = "length of cable" + plural_name = "lengths of cable" + w_class = ITEM_SIZE_NORMAL + throw_speed = 2 + throw_range = 5 + material = /decl/material/solid/metal/copper + matter = list( + /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/organic/plastic = MATTER_AMOUNT_TRACE + ) + obj_flags = OBJ_FLAG_CONDUCTIBLE + slot_flags = SLOT_LOWER_BODY + item_state = "coil" + attack_verb = list("whipped", "lashed", "disciplined", "flogged") + stack_merge_type = /obj/item/stack/cable_coil + matter_multiplier = 0.15 + /// Whether or not this cable coil can even have a color in the first place. + var/can_have_color = TRUE + /// The type of cable structure produced when laying down this cable. + /// src.cable_type::cable_type should equal stack_merge_type, ideally + var/cable_type = /obj/structure/cable + +/obj/item/stack/cable_coil/single + amount = 1 + +/obj/item/stack/cable_coil/cyborg + name = "cable coil synthesizer" + desc = "A device that makes cable." + gender = NEUTER + matter = null + uses_charge = 1 + charge_costs = list(1) + max_health = ITEM_HEALTH_NO_DAMAGE + is_spawnable_type = FALSE + +/obj/item/stack/cable_coil/Initialize(mapload, c_length, var/param_color = null) + . = ..(mapload, c_length) + set_extension(src, /datum/extension/tool/variable/simple, list( + TOOL_CABLECOIL = TOOL_QUALITY_DEFAULT, + TOOL_SUTURES = TOOL_QUALITY_MEDIOCRE + )) + if (can_have_color && param_color) // It should be red by default, so only recolor it if parameter was specified. + set_color(param_color) + update_icon() + update_wclass() + +/////////////////////////////////// +// General procedures +/////////////////////////////////// + +//you can use wires to heal robotics +/obj/item/stack/cable_coil/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) + var/obj/item/organ/external/affecting = istype(target) && GET_EXTERNAL_ORGAN(target, user?.get_target_zone()) + if(affecting && user.check_intent(I_FLAG_HELP)) + if(!affecting.is_robotic()) + to_chat(user, SPAN_WARNING("\The [target]'s [affecting.name] is not robotic. \The [src] cannot repair it.")) + else if(BP_IS_BRITTLE(affecting)) + to_chat(user, SPAN_WARNING("\The [target]'s [affecting.name] is hard and brittle. \The [src] cannot repair it.")) + else + var/use_amt = min(src.amount, ceil(affecting.burn_dam/3), 5) + if(can_use(use_amt) && affecting.robo_repair(3*use_amt, BURN, "some damaged wiring", src, user)) + use(use_amt) + return TRUE + return ..() + +/obj/item/stack/cable_coil/on_update_icon() + . = ..() + if (!paint_color && can_have_color) + var/list/possible_cable_colours = get_global_cable_colors() + set_color(possible_cable_colours[pick(possible_cable_colours)]) + if(amount == 1) + icon_state = "coil1" + SetName("cable piece") + else if(amount == 2) + icon_state = "coil2" + SetName("cable piece") + else if(amount > 2 && amount != max_amount) + icon_state = "coil" + SetName(initial(name)) + else + icon_state = "coil-max" + SetName(initial(name)) + +/obj/item/stack/cable_coil/proc/set_cable_color(var/selected_color, var/user) + if(!selected_color || !can_have_color) + return + + var/list/possible_cable_colours = get_global_cable_colors() + var/final_color = possible_cable_colours[selected_color] + if(!final_color) + selected_color = "Red" + final_color = possible_cable_colours[selected_color] + set_color(final_color) + to_chat(user, SPAN_NOTICE("You change \the [src]'s color to [lowertext(selected_color)].")) + +/obj/item/stack/cable_coil/proc/update_wclass() + if(amount == 1) + w_class = ITEM_SIZE_TINY + else + w_class = ITEM_SIZE_SMALL + +/obj/item/stack/cable_coil/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(distance > 1) + return + if(get_amount() == 1) + . += "\A [singular_name]." + else if(get_amount() == 2) + . += "Two [plural_name]." + else + . += "A coil of power cable. There are [get_amount()] [plural_name] in the coil." + +/obj/item/stack/cable_coil/verb/make_restraint() + set name = "Make Cable Restraints" + set category = "Object" + var/mob/M = usr + + if(ishuman(M) && !M.incapacitated()) + if(!isturf(usr.loc)) return + if(!src.use(15)) + to_chat(usr, SPAN_WARNING("You need at least 15 [plural_name] of cable to make restraints!")) + return + var/obj/item/handcuffs/cable/B = new /obj/item/handcuffs/cable(usr.loc) + B.set_color(color) + to_chat(usr, SPAN_NOTICE("You wind some [plural_name] of cable together to make some restraints.")) + else + to_chat(usr, SPAN_NOTICE("You cannot do that.")) + +/obj/item/stack/cable_coil/cyborg/verb/set_colour() + set name = "Change Colour" + set category = "Object" + + var/selected_type = input("Pick new colour.", "Cable Colour", null, null) as null|anything in get_global_cable_colors() + set_cable_color(selected_type, usr) + +// Items usable on a cable coil : +// - Wirecutters : cut them duh ! +// - Cable coil : merge cables +/obj/item/stack/cable_coil/can_merge_stacks(var/obj/item/stack/other) + return !other || (istype(other) && other.color == color) + +/obj/item/stack/cable_coil/cyborg/can_merge_stacks(var/obj/item/stack/other) + return TRUE + +/obj/item/stack/cable_coil/transfer_to(obj/item/stack/cable_coil/coil) + if(!istype(coil)) + return 0 + if(!(can_merge_stacks(coil) || coil.can_merge_stacks(src))) + return 0 + + return ..() + +/////////////////////////////////////////////// +// Cable laying procedures +////////////////////////////////////////////// + +// called when cable_coil is clicked on a turf +/obj/item/stack/cable_coil/proc/turf_place(turf/F, mob/user) + if(!isturf(user.loc)) + return + + if(get_amount() < 1) // Out of cable + to_chat(user, SPAN_WARNING("There is no [plural_name] left.")) + return + + if(get_dist(F,user) > 1) // Too far + to_chat(user, SPAN_WARNING("You can't lay cable at a place that far away.")) + return + + if(!F.is_plating()) // Ff floor is intact, complain + to_chat(user, SPAN_WARNING("You can't lay cable there unless the floor tiles are removed.")) + return + + var/dirn + if(user.loc == F) + dirn = user.dir // if laying on the tile we're on, lay in the direction we're facing + else + dirn = get_dir(F, user) + + var/end_dir = 0 + if(istype(F) && F.is_open()) + if(!can_use(2)) + to_chat(user, SPAN_WARNING("You don't have enough [plural_name] of cable to do this!")) + return + end_dir = DOWN + + for(var/obj/structure/cable/LC in F) + if((LC.d1 == dirn && LC.d2 == end_dir ) || ( LC.d2 == dirn && LC.d1 == end_dir)) + to_chat(user, SPAN_WARNING("There's already a cable at that position.")) + return + + put_cable(F, user, end_dir, dirn) + if(end_dir == DOWN) + put_cable(GetBelow(F), user, UP, 0) + return TRUE + +// called when cable_coil is click on an installed obj/cable +// or click on a turf that already contains a "node" cable +/obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user) + var/turf/U = user.loc + if(!isturf(U)) + return + + var/turf/T = C.loc + + if(!isturf(T) || !T.is_plating()) // sanity checks, also stop use interacting with T-scanner revealed cable + return + + if(get_dist(C, user) > 1) // make sure it's close enough + to_chat(user, SPAN_WARNING("You can't lay cable at a place that far away.")) + return + + if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing + return turf_place(T,user) + + var/dirn = get_dir(C, user) + + // one end of the clicked cable is pointing towards us + if(C.d1 == dirn || C.d2 == dirn) + if(!U.is_plating()) // can't place a cable if the floor is complete + to_chat(user, SPAN_WARNING("You can't lay cable there unless the floor tiles are removed.")) + return + else + // cable is pointing at us, we're standing on an open tile + // so create a stub pointing at the clicked cable on our tile + + var/fdirn = turn(dirn, 180) // the opposite direction + + for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already + if(LC.d1 == fdirn || LC.d2 == fdirn) + to_chat(user, SPAN_WARNING("There's already a cable at that position.")) + return + put_cable(U,user,0,fdirn) + return TRUE + + // exisiting cable doesn't point at our position, so see if it's a stub + else if(C.d1 == 0) + // if so, make it a full cable pointing from it's old direction to our dirn + var/nd1 = C.d2 // these will be the new directions + var/nd2 = dirn + + + if(nd1 > nd2) // swap directions to match icons/states + nd1 = dirn + nd2 = C.d2 + + + for(var/obj/structure/cable/LC in T) // check to make sure there's no matching cable + if(LC == C) // skip the cable we're interacting with + continue + if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction + to_chat(user, SPAN_WARNING("There's already a cable at that position.")) + return + + + C.cableColor(color) + + C.d1 = nd1 + C.d2 = nd2 + + C.add_fingerprint() + C.update_icon() + + + C.mergeConnectedNetworks(C.d1) //merge the powernets... + C.mergeConnectedNetworks(C.d2) //...in the two new cable directions + C.mergeConnectedNetworksOnTurf() + + if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d1) + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + + use(1) + + if (C.shock(user, 50)) + if (prob(50)) //fail + new/obj/item/stack/cable_coil(C.loc, 2, C.color) + qdel(C) + return + + C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. + return TRUE + + else if(C.d1 == UP) //Special cases for zcables, since they behave weirdly + . = turf_place(T, user) + if(.) + to_chat(user, SPAN_NOTICE("You connect the cable hanging from the ceiling.")) + return . + +/obj/item/stack/cable_coil/proc/put_cable(turf/F, mob/user, d1, d2) + if(!istype(F)) + return FALSE + + var/obj/structure/cable/C = new cable_type(F) + C.cableColor(color) + C.d1 = d1 + C.d2 = d2 + C.add_fingerprint(user) + C.update_icon() + + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/PN = new() + PN.add_cable(C) + + C.mergeConnectedNetworks(C.d1) //merge the powernets... + C.mergeConnectedNetworks(C.d2) //...in the two new cable directions + C.mergeConnectedNetworksOnTurf() + + if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d1) + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + + . = use(1) + if (C.shock(user, 50)) + if (prob(50)) //fail + new/obj/item/stack/cable_coil(C.loc, 1, C.color) + qdel(C) + return FALSE + +////////////////////////////// +// Misc. +///////////////////////////// +/obj/item/stack/cable_coil/five + amount = 5 + +/obj/item/stack/cable_coil/cut + item_state = "coil2" + +/obj/item/stack/cable_coil/cut/Initialize() + . = ..() + src.amount = rand(1,2) + update_icon() + update_wclass() + +/obj/item/stack/cable_coil/yellow + color = COLOR_AMBER + paint_color = COLOR_AMBER + +/obj/item/stack/cable_coil/blue + color = COLOR_CYAN_BLUE + paint_color = COLOR_CYAN_BLUE + +/obj/item/stack/cable_coil/green + color = COLOR_GREEN + paint_color = COLOR_GREEN + +/obj/item/stack/cable_coil/pink + color = COLOR_PURPLE + paint_color = COLOR_PURPLE + +/obj/item/stack/cable_coil/orange + color = COLOR_ORANGE + paint_color = COLOR_ORANGE + +/obj/item/stack/cable_coil/cyan + color = COLOR_SKY_BLUE + paint_color = COLOR_SKY_BLUE + +/obj/item/stack/cable_coil/white + color = COLOR_SILVER + paint_color = COLOR_SILVER + +/obj/item/stack/cable_coil/lime + color = COLOR_LIME + paint_color = COLOR_LIME + +/obj/item/stack/cable_coil/random/Initialize(mapload, c_length, param_color) + var/list/possible_cable_colours = get_global_cable_colors() + set_color(possible_cable_colours[pick(possible_cable_colours)]) + . = ..() + +// Produces cable coil from a rig power cell. +/obj/item/stack/cable_coil/fabricator + name = "cable fabricator" + var/cost_per_cable = 10 + +/obj/item/stack/cable_coil/fabricator/split(var/tamount, var/force=FALSE) + return + +/obj/item/stack/cable_coil/fabricator/get_cell() + if(istype(loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = loc + return module.get_cell() + if(isrobot(loc)) + var/mob/living/silicon/robot/robot = loc + return robot.get_cell() + +/obj/item/stack/cable_coil/fabricator/use(var/used) + var/obj/item/cell/cell = get_cell() + return cell?.use(used * cost_per_cable) + +/obj/item/stack/cable_coil/fabricator/get_amount() + var/obj/item/cell/cell = get_cell() + . = (cell ? floor(cell.charge / cost_per_cable) : 0) + +/obj/item/stack/cable_coil/fabricator/get_max_amount() + var/obj/item/cell/cell = get_cell() + . = (cell ? floor(cell.maxcharge / cost_per_cable) : 0) diff --git a/code/modules/power/heavycable.dm b/code/modules/power/cable/heavycable.dm similarity index 100% rename from code/modules/power/heavycable.dm rename to code/modules/power/cable/heavycable.dm diff --git a/code/modules/power/cell/_cell.dm b/code/modules/power/cell/_cell.dm new file mode 100644 index 000000000000..b51e7a6258af --- /dev/null +++ b/code/modules/power/cell/_cell.dm @@ -0,0 +1,117 @@ +// Power Cells +/obj/item/cell + name = "power cell" + desc = "A rechargeable electrochemical power cell." + icon = 'icons/obj/power.dmi' + icon_state = "cell" + item_state = "cell" + origin_tech = @'{"powerstorage":1}' + throw_speed = 3 + throw_range = 5 + w_class = ITEM_SIZE_NORMAL + material = /decl/material/solid/metal/steel + matter = list( + /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/organic/plastic = MATTER_AMOUNT_TRACE + ) + var/charge // Current charge + var/maxcharge = 1000 // Capacity in Wh + +/obj/item/cell/Initialize() + . = ..() + if(isnull(charge)) + charge = maxcharge + update_icon() + +/obj/item/cell/drain_power(var/drain_check, var/surge, var/power = 0) + + if(drain_check) + return 1 + + if(charge <= 0) + return 0 + + var/cell_amt = power * CELLRATE + + return use(cell_amt) / CELLRATE + +/obj/item/cell/on_update_icon() + . = ..() + var/overlay_state = null + switch(percent()) + if(95 to 100) + overlay_state = "cell-o2" + if(25 to 95) + overlay_state = "cell-o1" + if(0.05 to 25) + overlay_state = "cell-o0" + if(overlay_state) + add_overlay(overlay_state) + +/obj/item/cell/proc/percent() // return % charge of cell + return maxcharge && (100.0*charge/maxcharge) + +/obj/item/cell/proc/fully_charged() + return (charge == maxcharge) + +// checks if the power cell is able to provide the specified amount of charge +/obj/item/cell/proc/check_charge(var/amount) + return (charge >= amount) + +// use power from a cell, returns the amount actually used +/obj/item/cell/proc/use(var/amount) + var/used = min(charge, amount) + charge -= used + update_icon() + return used + +// Checks if the specified amount can be provided. If it can, it removes the amount +// from the cell and returns 1. Otherwise does nothing and returns 0. +/obj/item/cell/proc/checked_use(var/amount) + if(!check_charge(amount)) + return 0 + use(amount) + return 1 + +/obj/item/cell/proc/give(var/amount) + var/amount_used = min(maxcharge-charge,amount) + charge += amount_used + update_icon() + return amount_used + +/obj/item/cell/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + . += "The label states it's capacity is [maxcharge] Wh." + . += "The charge meter reads [round(src.percent(), 0.1)]%." + +/obj/item/cell/emp_act(severity) + // remove this if EMPs are ever rebalanced so that they don't instantly drain borg cells + // todo: containers (partially) shielding contents? + if(isrobot(loc)) + var/mob/living/silicon/robot/robot = loc + severity *= robot.cell_emp_mult + + // Lose 1/2, 1/4, 1/6 of the current charge per hit or 1/4, 1/8, 1/12 of the max charge per hit, whichever is highest + charge -= max(charge / (2 * severity), maxcharge/(4 * severity)) + if (charge < 0) + charge = 0 + ..() + + +/obj/item/cell/proc/get_electrocute_damage() + switch (charge) + if (1000000 to INFINITY) + return min(rand(50,160),rand(50,160)) + if (200000 to 1000000-1) + return min(rand(25,80),rand(25,80)) + if (100000 to 200000-1)//Ave powernet + return min(rand(20,60),rand(20,60)) + if (50000 to 100000-1) + return min(rand(15,40),rand(15,40)) + if (1000 to 50000-1) + return min(rand(10,20),rand(10,20)) + else + return 0 + +/obj/item/cell/get_cell() + return src //no shit Sherlock \ No newline at end of file diff --git a/code/modules/power/cell.dm b/code/modules/power/cell/cell_types.dm similarity index 68% rename from code/modules/power/cell.dm rename to code/modules/power/cell/cell_types.dm index d9e93ff2da18..c3afc0b8a3f1 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell/cell_types.dm @@ -1,123 +1,3 @@ -// Power Cells -/obj/item/cell - name = "power cell" - desc = "A rechargeable electrochemical power cell." - icon = 'icons/obj/power.dmi' - icon_state = "cell" - item_state = "cell" - origin_tech = @'{"powerstorage":1}' - throw_speed = 3 - throw_range = 5 - w_class = ITEM_SIZE_NORMAL - material = /decl/material/solid/metal/steel - matter = list( - /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/organic/plastic = MATTER_AMOUNT_TRACE - ) - var/charge // Current charge - var/maxcharge = 1000 // Capacity in Wh - -/obj/item/cell/Initialize() - . = ..() - if(isnull(charge)) - charge = maxcharge - update_icon() - -/obj/item/cell/drain_power(var/drain_check, var/surge, var/power = 0) - - if(drain_check) - return 1 - - if(charge <= 0) - return 0 - - var/cell_amt = power * CELLRATE - - return use(cell_amt) / CELLRATE - -/obj/item/cell/on_update_icon() - . = ..() - var/overlay_state = null - switch(percent()) - if(95 to 100) - overlay_state = "cell-o2" - if(25 to 95) - overlay_state = "cell-o1" - if(0.05 to 25) - overlay_state = "cell-o0" - if(overlay_state) - add_overlay(overlay_state) - -/obj/item/cell/proc/percent() // return % charge of cell - return maxcharge && (100.0*charge/maxcharge) - -/obj/item/cell/proc/fully_charged() - return (charge == maxcharge) - -// checks if the power cell is able to provide the specified amount of charge -/obj/item/cell/proc/check_charge(var/amount) - return (charge >= amount) - -// use power from a cell, returns the amount actually used -/obj/item/cell/proc/use(var/amount) - var/used = min(charge, amount) - charge -= used - update_icon() - return used - -// Checks if the specified amount can be provided. If it can, it removes the amount -// from the cell and returns 1. Otherwise does nothing and returns 0. -/obj/item/cell/proc/checked_use(var/amount) - if(!check_charge(amount)) - return 0 - use(amount) - return 1 - -/obj/item/cell/proc/give(var/amount) - var/amount_used = min(maxcharge-charge,amount) - charge += amount_used - update_icon() - return amount_used - -/obj/item/cell/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - . += "The label states it's capacity is [maxcharge] Wh." - . += "The charge meter reads [round(src.percent(), 0.1)]%." - -/obj/item/cell/emp_act(severity) - // remove this if EMPs are ever rebalanced so that they don't instantly drain borg cells - // todo: containers (partially) shielding contents? - if(isrobot(loc)) - var/mob/living/silicon/robot/robot = loc - severity *= robot.cell_emp_mult - - // Lose 1/2, 1/4, 1/6 of the current charge per hit or 1/4, 1/8, 1/12 of the max charge per hit, whichever is highest - charge -= max(charge / (2 * severity), maxcharge/(4 * severity)) - if (charge < 0) - charge = 0 - ..() - - -/obj/item/cell/proc/get_electrocute_damage() - switch (charge) - if (1000000 to INFINITY) - return min(rand(50,160),rand(50,160)) - if (200000 to 1000000-1) - return min(rand(25,80),rand(25,80)) - if (100000 to 200000-1)//Ave powernet - return min(rand(20,60),rand(20,60)) - if (50000 to 100000-1) - return min(rand(15,40),rand(15,40)) - if (1000 to 50000-1) - return min(rand(10,20),rand(10,20)) - else - return 0 - -/obj/item/cell/get_cell() - return src //no shit Sherlock - -// SUBTYPES BELOW - // Smaller variant, used by energy guns and similar small devices. /obj/item/cell/device name = "device power cell" diff --git a/code/modules/power/solar/solar_control.dm b/code/modules/power/solar/solar_control.dm new file mode 100644 index 000000000000..865534212a95 --- /dev/null +++ b/code/modules/power/solar/solar_control.dm @@ -0,0 +1,215 @@ +// +// Solar Control Computer +// + +/obj/machinery/power/solar_control + name = "solar panel control" + desc = "A controller for solar panel arrays." + icon = 'icons/obj/computer.dmi' + icon_state = "solar" + anchored = TRUE + density = TRUE + use_power = POWER_USE_IDLE + idle_power_usage = 250 + construct_state = /decl/machine_construction/default/panel_closed/computer + base_type = /obj/machinery/power/solar_control + frame_type = /obj/machinery/constructable_frame/computerframe/deconstruct + var/cdir = 0 + var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) + var/gen = 0 + var/lastgen = 0 + var/track = 0 // 0= off 1=timed 2=auto (tracker) + var/trackrate = 600 // 300-900 seconds + var/nexttime = 0 // time for a panel to rotate of 1° in manual tracking + var/obj/machinery/power/tracker/connected_tracker = null + var/list/connected_panels = list() + +/obj/machinery/power/solar_control/drain_power() + return -1 + +/obj/machinery/power/solar_control/Destroy() + for(var/obj/machinery/power/solar/M in connected_panels) + M.unset_control() + if(connected_tracker) + connected_tracker.unset_control() + return ..() + +/obj/machinery/power/solar_control/disconnect_from_network() + ..() + solars_list.Remove(src) + +/obj/machinery/power/solar_control/connect_to_network() + var/to_return = ..() + if(powernet) //if connected and not already in solar_list... + solars_list |= src //... add it + return to_return + +//search for unconnected panels and trackers in the computer powernet and connect them +/obj/machinery/power/solar_control/proc/search_for_connected() + if(powernet) + for(var/obj/machinery/power/M in powernet.nodes) + if(istype(M, /obj/machinery/power/solar)) + var/obj/machinery/power/solar/S = M + if(!S.control) //i.e unconnected + if(S.set_control(src)) + connected_panels |= S + else if(istype(M, /obj/machinery/power/tracker)) + if(!connected_tracker) //if there's already a tracker connected to the computer don't add another + var/obj/machinery/power/tracker/T = M + if(!T.control) //i.e unconnected + if(T.set_control(src)) + connected_tracker = T + +//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly +/obj/machinery/power/solar_control/proc/update() + if(stat & (NOPOWER | BROKEN)) + return + + switch(track) + if(1) + if(trackrate) //we're manual tracking. If we set a rotation speed... + cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) + if(2) // auto-tracking + var/datum/sun/sun = get_best_sun() + if(connected_tracker && sun) + connected_tracker.set_angle(sun.angle) + + set_panels(cdir) + updateDialog() + +/obj/machinery/power/solar_control/Initialize() + . = ..() + if(!connect_to_network()) return + set_panels(cdir) + +/obj/machinery/power/solar_control/on_update_icon() + if(stat & BROKEN) + icon_state = "broken" + return + if(stat & NOPOWER) + icon_state = "c_unpowered" + return + icon_state = "solar" + +/obj/machinery/power/solar_control/interface_interact(mob/user) + interact(user) + return TRUE + +/obj/machinery/power/solar_control/interact(mob/user) + + var/datum/sun/sun = get_best_sun() + var/t = "Generated power : [round(lastgen)] W
    " + t += "Star Orientation: [sun?.angle || 0]° ([angle2text(sun?.angle || 0)])
    " + t += "Array Orientation: [rate_control(src,"cdir","[cdir]°",1,15)] ([angle2text(cdir)])
    " + t += "Tracking:
    " + switch(track) + if(0) + t += "Off Timed Auto
    " + if(1) + t += "Off Timed Auto
    " + if(2) + t += "Off Timed Auto
    " + + t += "Tracking Rate: [rate_control(src,"tdir","[trackrate] deg/h ([trackrate<0 ? "CCW" : "CW"])",1,30,180)]

    " + + t += "Connected devices:
    " + + t += "Search for devices
    " + t += "Solar panels : [connected_panels.len] connected
    " + t += "Solar tracker : [connected_tracker ? "Found" : "Not found"]

    " + + t += "Close" + + var/datum/browser/written_digital/popup = new(user, "solar", name) + popup.set_content(t) + popup.open() + +/obj/machinery/power/solar_control/Process() + lastgen = gen + gen = 0 + + if(stat & (NOPOWER | BROKEN)) + return + + if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list + if(connected_tracker.powernet != powernet) + connected_tracker.unset_control() + + if(track==1 && trackrate) //manual tracking and set a rotation speed + if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1°... + targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it + nexttime += 36000/abs(trackrate) //reset the counter for the next 1° + + updateDialog() + +/obj/machinery/power/solar_control/Topic(href, href_list) + . = ..() + if(. == TOPIC_CLOSE) + close_browser(usr, "window=solcon") + +/obj/machinery/power/solar_control/OnTopic(mob/user, href_list) + if((. = ..())) + return + if(href_list["close"]) + return TOPIC_CLOSE + + if(href_list["rate control"]) + if(href_list["cdir"]) + cdir = clamp((360+cdir+text2num(href_list["cdir"]))%360, 0, 359) + targetdir = cdir + if(track == 2) //manual update, so losing auto-tracking + track = 0 + addtimer(CALLBACK(src, PROC_REF(set_panels), cdir), 1) + if(href_list["tdir"]) + trackrate = clamp(trackrate+text2num(href_list["tdir"]), -7200, 7200) + if(trackrate) nexttime = world.time + 36000/abs(trackrate) + return TOPIC_REFRESH + + if(href_list["track"]) + track = text2num(href_list["track"]) + if(track == 2) + var/datum/sun/sun = get_best_sun() + if(connected_tracker && sun) + connected_tracker.set_angle(sun.angle) + set_panels(cdir) + else if (track == 1) //begin manual tracking + targetdir = cdir + if(trackrate) nexttime = world.time + 36000/abs(trackrate) + set_panels(targetdir) + return TOPIC_REFRESH + + if(href_list["search_connected"]) + search_for_connected() + var/datum/sun/sun = get_best_sun() + if(connected_tracker && track == 2 && sun) + connected_tracker.set_angle(sun.angle) + set_panels(cdir) + return TOPIC_REFRESH + +//rotates the panel to the passed angle +/obj/machinery/power/solar_control/proc/set_panels(var/cdir) + for(var/obj/machinery/power/solar/S in connected_panels) + S.adir = cdir //instantly rotates the panel + S.occlusion()//and + S.update_icon() //update it + update_icon() + +/obj/machinery/power/solar_control/explosion_act(severity) + . = ..() + if(.) + if(severity == 1) + physically_destroyed() + else if((severity == 2 && prob(50)) || (severity == 3 && prob(25))) + set_broken(TRUE) + +// Used for mapping in solar array which automatically starts itself (telecomms, for example) +/obj/machinery/power/solar_control/autostart + track = 2 // Auto tracking mode + +/obj/machinery/power/solar_control/autostart/Initialize() + search_for_connected() + var/datum/sun/sun = get_best_sun() + if(connected_tracker && track == 2 && sun) + connected_tracker.set_angle(sun.angle) + set_panels(cdir) + . = ..() \ No newline at end of file diff --git a/code/modules/power/solar.dm b/code/modules/power/solar/solar_panel.dm similarity index 58% rename from code/modules/power/solar.dm rename to code/modules/power/solar/solar_panel.dm index 29c458486a8b..a5dbc706a1e1 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar/solar_panel.dm @@ -282,222 +282,6 @@ var/global/list/solars_list = list() return TRUE return ..() -// -// Solar Control Computer -// - -/obj/machinery/power/solar_control - name = "solar panel control" - desc = "A controller for solar panel arrays." - icon = 'icons/obj/computer.dmi' - icon_state = "solar" - anchored = TRUE - density = TRUE - use_power = POWER_USE_IDLE - idle_power_usage = 250 - construct_state = /decl/machine_construction/default/panel_closed/computer - base_type = /obj/machinery/power/solar_control - frame_type = /obj/machinery/constructable_frame/computerframe/deconstruct - var/cdir = 0 - var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) - var/gen = 0 - var/lastgen = 0 - var/track = 0 // 0= off 1=timed 2=auto (tracker) - var/trackrate = 600 // 300-900 seconds - var/nexttime = 0 // time for a panel to rotate of 1° in manual tracking - var/obj/machinery/power/tracker/connected_tracker = null - var/list/connected_panels = list() - -/obj/machinery/power/solar_control/drain_power() - return -1 - -/obj/machinery/power/solar_control/Destroy() - for(var/obj/machinery/power/solar/M in connected_panels) - M.unset_control() - if(connected_tracker) - connected_tracker.unset_control() - return ..() - -/obj/machinery/power/solar_control/disconnect_from_network() - ..() - solars_list.Remove(src) - -/obj/machinery/power/solar_control/connect_to_network() - var/to_return = ..() - if(powernet) //if connected and not already in solar_list... - solars_list |= src //... add it - return to_return - -//search for unconnected panels and trackers in the computer powernet and connect them -/obj/machinery/power/solar_control/proc/search_for_connected() - if(powernet) - for(var/obj/machinery/power/M in powernet.nodes) - if(istype(M, /obj/machinery/power/solar)) - var/obj/machinery/power/solar/S = M - if(!S.control) //i.e unconnected - if(S.set_control(src)) - connected_panels |= S - else if(istype(M, /obj/machinery/power/tracker)) - if(!connected_tracker) //if there's already a tracker connected to the computer don't add another - var/obj/machinery/power/tracker/T = M - if(!T.control) //i.e unconnected - if(T.set_control(src)) - connected_tracker = T - -//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly -/obj/machinery/power/solar_control/proc/update() - if(stat & (NOPOWER | BROKEN)) - return - - switch(track) - if(1) - if(trackrate) //we're manual tracking. If we set a rotation speed... - cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) - if(2) // auto-tracking - var/datum/sun/sun = get_best_sun() - if(connected_tracker && sun) - connected_tracker.set_angle(sun.angle) - - set_panels(cdir) - updateDialog() - -/obj/machinery/power/solar_control/Initialize() - . = ..() - if(!connect_to_network()) return - set_panels(cdir) - -/obj/machinery/power/solar_control/on_update_icon() - if(stat & BROKEN) - icon_state = "broken" - return - if(stat & NOPOWER) - icon_state = "c_unpowered" - return - icon_state = "solar" - -/obj/machinery/power/solar_control/interface_interact(mob/user) - interact(user) - return TRUE - -/obj/machinery/power/solar_control/interact(mob/user) - - var/datum/sun/sun = get_best_sun() - var/t = "Generated power : [round(lastgen)] W
    " - t += "Star Orientation: [sun?.angle || 0]° ([angle2text(sun?.angle || 0)])
    " - t += "Array Orientation: [rate_control(src,"cdir","[cdir]°",1,15)] ([angle2text(cdir)])
    " - t += "Tracking:
    " - switch(track) - if(0) - t += "Off Timed Auto
    " - if(1) - t += "Off Timed Auto
    " - if(2) - t += "Off Timed Auto
    " - - t += "Tracking Rate: [rate_control(src,"tdir","[trackrate] deg/h ([trackrate<0 ? "CCW" : "CW"])",1,30,180)]

    " - - t += "Connected devices:
    " - - t += "Search for devices
    " - t += "Solar panels : [connected_panels.len] connected
    " - t += "Solar tracker : [connected_tracker ? "Found" : "Not found"]

    " - - t += "Close" - - var/datum/browser/written_digital/popup = new(user, "solar", name) - popup.set_content(t) - popup.open() - -/obj/machinery/power/solar_control/Process() - lastgen = gen - gen = 0 - - if(stat & (NOPOWER | BROKEN)) - return - - if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list - if(connected_tracker.powernet != powernet) - connected_tracker.unset_control() - - if(track==1 && trackrate) //manual tracking and set a rotation speed - if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1°... - targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it - nexttime += 36000/abs(trackrate) //reset the counter for the next 1° - - updateDialog() - -/obj/machinery/power/solar_control/Topic(href, href_list) - . = ..() - if(. == TOPIC_CLOSE) - close_browser(usr, "window=solcon") - -/obj/machinery/power/solar_control/OnTopic(mob/user, href_list) - if((. = ..())) - return - if(href_list["close"]) - return TOPIC_CLOSE - - if(href_list["rate control"]) - if(href_list["cdir"]) - cdir = clamp((360+cdir+text2num(href_list["cdir"]))%360, 0, 359) - targetdir = cdir - if(track == 2) //manual update, so losing auto-tracking - track = 0 - addtimer(CALLBACK(src, PROC_REF(set_panels), cdir), 1) - if(href_list["tdir"]) - trackrate = clamp(trackrate+text2num(href_list["tdir"]), -7200, 7200) - if(trackrate) nexttime = world.time + 36000/abs(trackrate) - return TOPIC_REFRESH - - if(href_list["track"]) - track = text2num(href_list["track"]) - if(track == 2) - var/datum/sun/sun = get_best_sun() - if(connected_tracker && sun) - connected_tracker.set_angle(sun.angle) - set_panels(cdir) - else if (track == 1) //begin manual tracking - targetdir = cdir - if(trackrate) nexttime = world.time + 36000/abs(trackrate) - set_panels(targetdir) - return TOPIC_REFRESH - - if(href_list["search_connected"]) - search_for_connected() - var/datum/sun/sun = get_best_sun() - if(connected_tracker && track == 2 && sun) - connected_tracker.set_angle(sun.angle) - set_panels(cdir) - return TOPIC_REFRESH - -//rotates the panel to the passed angle -/obj/machinery/power/solar_control/proc/set_panels(var/cdir) - for(var/obj/machinery/power/solar/S in connected_panels) - S.adir = cdir //instantly rotates the panel - S.occlusion()//and - S.update_icon() //update it - update_icon() - -/obj/machinery/power/solar_control/explosion_act(severity) - . = ..() - if(.) - if(severity == 1) - physically_destroyed() - else if((severity == 2 && prob(50)) || (severity == 3 && prob(25))) - set_broken(TRUE) - -// Used for mapping in solar array which automatically starts itself (telecomms, for example) -/obj/machinery/power/solar_control/autostart - track = 2 // Auto tracking mode - -/obj/machinery/power/solar_control/autostart/Initialize() - search_for_connected() - var/datum/sun/sun = get_best_sun() - if(connected_tracker && track == 2 && sun) - connected_tracker.set_angle(sun.angle) - set_panels(cdir) - . = ..() - // // MISC // diff --git a/code/modules/power/tracker.dm b/code/modules/power/solar/tracker.dm similarity index 99% rename from code/modules/power/tracker.dm rename to code/modules/power/solar/tracker.dm index b6f3bfb0274d..8c5417f3b235 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/solar/tracker.dm @@ -75,7 +75,6 @@ // Tracker Electronic /obj/item/tracker_electronics - name = "solar tracker electronics" icon = 'icons/obj/doors/door_assembly.dmi' icon_state = "door_electronics" diff --git a/nebula.dme b/nebula.dme index 34f8a2ff2835..40b3ba3070da 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3445,11 +3445,8 @@ #include "code\modules\power\admin_setup_engine.dm" #include "code\modules\power\batteryrack.dm" #include "code\modules\power\breaker_box.dm" -#include "code\modules\power\cable.dm" -#include "code\modules\power\cell.dm" #include "code\modules\power\debug_items.dm" #include "code\modules\power\generator.dm" -#include "code\modules\power\heavycable.dm" #include "code\modules\power\port_gen.dm" #include "code\modules\power\power.dm" #include "code\modules\power\powernet.dm" @@ -3457,15 +3454,18 @@ #include "code\modules\power\smes.dm" #include "code\modules\power\smes_construction.dm" #include "code\modules\power\smes_presets.dm" -#include "code\modules\power\solar.dm" #include "code\modules\power\stirling.dm" #include "code\modules\power\terminal.dm" -#include "code\modules\power\tracker.dm" #include "code\modules\power\turbine.dm" #include "code\modules\power\apc\_apc.dm" #include "code\modules\power\apc\apc_frame.dm" #include "code\modules\power\apc\apc_presets.dm" #include "code\modules\power\apc\apc_wires.dm" +#include "code\modules\power\cable\cable.dm" +#include "code\modules\power\cable\cable_coil.dm" +#include "code\modules\power\cable\heavycable.dm" +#include "code\modules\power\cell\_cell.dm" +#include "code\modules\power\cell\cell_types.dm" #include "code\modules\power\fission\core.dm" #include "code\modules\power\fission\core_control.dm" #include "code\modules\power\fission\fission_circuits.dm" @@ -3501,6 +3501,9 @@ #include "code\modules\power\singularity\particle_accelerator\particle_control.dm" #include "code\modules\power\singularity\particle_accelerator\particle_emitter.dm" #include "code\modules\power\singularity\particle_accelerator\particle_power.dm" +#include "code\modules\power\solar\solar_control.dm" +#include "code\modules\power\solar\solar_panel.dm" +#include "code\modules\power\solar\tracker.dm" #include "code\modules\projectiles\ammunition.dm" #include "code\modules\projectiles\gun.dm" #include "code\modules\projectiles\projectile.dm" From ab23d3bb5e92f5fb0803bc08f96277996c8c4eba Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Thu, 9 Apr 2026 16:25:06 +1000 Subject: [PATCH 1195/1331] Cherrypicking strut removal changes from dev due to stable gameplay issues. --- code/game/machinery/constructable_frame.dm | 2 +- code/game/objects/items/stacks/rods.dm | 6 --- code/game/objects/random/subtypes/misc.dm | 4 +- .../crates_lockers/closets/syndicate.dm | 2 +- code/game/objects/structures/girders.dm | 2 +- code/game/objects/structures/racks.dm | 2 +- code/game/objects/structures/railing.dm | 2 +- .../stool_bed_chair_nest_sofa/bed.dm | 2 +- code/game/objects/structures/tables.dm | 2 +- code/game/objects/structures/wall_frame.dm | 2 +- code/modules/butchery/butchery_data_misc.dm | 2 +- code/modules/butchery/butchery_hook.dm | 2 +- .../crafting/stack_recipes/recipes_struts.dm | 32 ++++++++-------- .../designs/general/designs_general.dm | 38 ------------------- .../materials/material_sheets_mapping.dm | 5 +++ code/modules/materials/material_stack_misc.dm | 21 ---------- .../robot/flying/module_flying_repair.dm | 2 - .../robot/modules/module_engineering.dm | 2 - .../robot/modules/module_maintenance_drone.dm | 2 - maps/antag_spawn/mercenary/mercenary_base.dmm | 8 ++-- maps/away/bearcat/bearcat-1.dmm | 2 +- maps/away/bearcat/bearcat-2.dmm | 14 +++---- maps/away/casino/casino.dmm | 2 +- maps/away/liberia/liberia.dmm | 8 ++-- .../lost_supply_base/lost_supply_base.dmm | 6 +-- maps/away/magshield/magshield.dmm | 12 +++--- maps/exodus/exodus-1.dmm | 4 +- maps/exodus/exodus-2.dmm | 18 +++------ maps/ministation/ministation-0.dmm | 10 ++--- .../exoplanet_ruins/marooned/marooned.dmm | 4 +- .../exoplanet_ruins/playablecolony/colony.dmm | 8 ++-- maps/tradeship/tradeship-1.dmm | 2 +- maps/tradeship/tradeship-3.dmm | 2 +- .../exoplanet_ruins/oldpod/oldpod.dmm | 8 ++-- mods/content/dungeon_loot/subtypes/maint.dm | 2 +- .../government/away_sites/icarus/icarus-1.dmm | 14 +++---- mods/gamemodes/heist/heist_base.dmm | 10 ++--- mods/species/ascent/mobs/drone.dm | 2 - tools/map_migrations/5345_strut_removal.txt | 3 ++ 39 files changed, 99 insertions(+), 172 deletions(-) create mode 100644 tools/map_migrations/5345_strut_removal.txt diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 11c41dca7caf..df5391cc06d8 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -19,7 +19,7 @@ update_icon() /obj/machinery/constructable_frame/dismantle() - SSmaterials.create_object(/decl/material/solid/metal/steel, loc, 5, object_type = /obj/item/stack/material/strut) + SSmaterials.create_object(/decl/material/solid/metal/steel, loc, 5, object_type = /obj/item/stack/material/rods) qdel(src) return TRUE diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index 86363f815e9c..0f6544daaaab 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -25,12 +25,6 @@ . = ..() . += "narrow" -/obj/item/stack/material/rods/ten - amount = 10 - -/obj/item/stack/material/rods/fifty - amount = 50 - /obj/item/stack/material/rods/cyborg name = "metal rod synthesizer" desc = "A device that makes metal rods." diff --git a/code/game/objects/random/subtypes/misc.dm b/code/game/objects/random/subtypes/misc.dm index 8ed2df78bc34..004ad4de08ce 100644 --- a/code/game/objects/random/subtypes/misc.dm +++ b/code/game/objects/random/subtypes/misc.dm @@ -211,8 +211,8 @@ /obj/item/stack/material/plank/mapped/wood/fifty, /obj/item/stack/material/cardstock/mapped/cardboard/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, - /obj/item/stack/material/rods/ten, - /obj/item/stack/material/rods/fifty + /obj/item/stack/material/rods/mapped/steel/ten, + /obj/item/stack/material/rods/mapped/steel/fifty ) return spawnable_choices diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index 798d8f65c49f..6d3bec631ee9 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -128,7 +128,7 @@ /obj/item/stack/material/puck/mapped/uranium/fifty, /obj/item/stack/material/gemstone/mapped/diamond/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, - /obj/item/stack/material/rods/fifty + /obj/item/stack/material/rods/mapped/steel/fifty ) for(var/i = 0, i < 2, i++) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index d4aed89ecdcc..83ebf5a68237 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -10,7 +10,7 @@ tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) max_health = 100 parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/cover = 50 var/prepped_for_fakewall diff --git a/code/game/objects/structures/racks.dm b/code/game/objects/structures/racks.dm index 7f1c77404ec0..3ade37d94fe8 100644 --- a/code/game/objects/structures/racks.dm +++ b/code/game/objects/structures/racks.dm @@ -10,7 +10,7 @@ atom_flags = ATOM_FLAG_CLIMBABLE throwpass = TRUE parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods density = TRUE anchored = TRUE structure_flags = STRUCTURE_FLAG_SURFACE diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index 01e7a84e68a5..43a9647ce54d 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -14,7 +14,7 @@ material_alteration = MAT_FLAG_ALTERATION_ALL max_health = 100 parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/broken = FALSE var/neighbor_status = 0 diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm index 519dae2ab6e7..2fa897ecf250 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm @@ -22,7 +22,7 @@ material_alteration = MAT_FLAG_ALTERATION_ALL tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods user_comfort = 1 obj_flags = OBJ_FLAG_SUPPORT_MOB var/base_icon = "bed" diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index da377b255045..db6c7fad45ac 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -19,7 +19,7 @@ tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods structure_flags = STRUCTURE_FLAG_SURFACE can_support_butchery = TRUE diff --git a/code/game/objects/structures/wall_frame.dm b/code/game/objects/structures/wall_frame.dm index 850fc4d7c580..3aaf589d466e 100644 --- a/code/game/objects/structures/wall_frame.dm +++ b/code/game/objects/structures/wall_frame.dm @@ -18,7 +18,7 @@ tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) max_health = 40 parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/stripe_color var/list/connections var/list/other_connections diff --git a/code/modules/butchery/butchery_data_misc.dm b/code/modules/butchery/butchery_data_misc.dm index 60aa3f2e6ff9..54720ce3b742 100644 --- a/code/modules/butchery/butchery_data_misc.dm +++ b/code/modules/butchery/butchery_data_misc.dm @@ -4,7 +4,7 @@ meat_type = /obj/item/stack/material/rods bone_material = /decl/material/solid/metal/titanium - bone_type = /obj/item/stack/material/strut + bone_type = /obj/item/stack/material/rods skin_material = /decl/material/solid/metal/aluminium skin_type = /obj/item/stack/material/sheet/shiny diff --git a/code/modules/butchery/butchery_hook.dm b/code/modules/butchery/butchery_hook.dm index 5d92a0eb4b44..40d7e2466783 100644 --- a/code/modules/butchery/butchery_hook.dm +++ b/code/modules/butchery/butchery_hook.dm @@ -19,7 +19,7 @@ ) tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) parts_amount = 2 - parts_type = /obj/item/stack/material/strut + parts_type = /obj/item/stack/material/rods var/mob/living/occupant var/occupant_state = CARCASS_EMPTY diff --git a/code/modules/crafting/stack_recipes/recipes_struts.dm b/code/modules/crafting/stack_recipes/recipes_struts.dm index fced999502f2..e6fe0f9b67f9 100644 --- a/code/modules/crafting/stack_recipes/recipes_struts.dm +++ b/code/modules/crafting/stack_recipes/recipes_struts.dm @@ -1,7 +1,7 @@ -/decl/stack_recipe/strut - abstract_type = /decl/stack_recipe/strut +/decl/stack_recipe/rods + abstract_type = /decl/stack_recipe/rods craft_stack_types = list( - /obj/item/stack/material/strut, + /obj/item/stack/material/rods, /obj/item/stack/material/bone ) one_per_turf = TRUE @@ -10,64 +10,64 @@ available_to_map_tech_level = MAP_TECH_LEVEL_MEDIEVAL category = "structures" -/decl/stack_recipe/strut/stick +/decl/stack_recipe/rods/stick result_type = /obj/item/stick one_per_turf = FALSE on_floor = FALSE difficulty = MAT_VALUE_EASY_DIY category = "items" -/decl/stack_recipe/strut/stick/staff +/decl/stack_recipe/rods/stick/staff result_type = /obj/item/staff difficulty = MAT_VALUE_NORMAL_DIY -/decl/stack_recipe/strut/stick/cane +/decl/stack_recipe/rods/stick/cane result_type = /obj/item/cane difficulty = MAT_VALUE_NORMAL_DIY -/decl/stack_recipe/strut/railing +/decl/stack_recipe/rods/railing result_type = /obj/structure/railing -/decl/stack_recipe/strut/ladder +/decl/stack_recipe/rods/ladder result_type = /obj/structure/ladder on_floor = FALSE required_wall_support_value = 10 -/decl/stack_recipe/strut/girder +/decl/stack_recipe/rods/girder result_type = /obj/structure/girder required_wall_support_value = 10 req_amount = 5 * SHEET_MATERIAL_AMOUNT // Arbitrary value since girders return weird matter values. available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/strut/wall_frame +/decl/stack_recipe/rods/wall_frame result_type = /obj/structure/wall_frame available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/strut/table_frame +/decl/stack_recipe/rods/table_frame result_type = /obj/structure/table/frame category = "furniture" -/decl/stack_recipe/strut/rack +/decl/stack_recipe/rods/rack result_type = /obj/structure/rack category = "furniture" -/decl/stack_recipe/strut/butcher_hook +/decl/stack_recipe/rods/butcher_hook result_type = /obj/structure/meat_hook one_per_turf = TRUE difficulty = MAT_VALUE_NORMAL_DIY category = "furniture" -/decl/stack_recipe/strut/bed +/decl/stack_recipe/rods/bed result_type = /obj/structure/bed required_integrity = 50 required_min_hardness = MAT_VALUE_FLEXIBLE + 10 category = "furniture" -/decl/stack_recipe/strut/machine +/decl/stack_recipe/rods/machine result_type = /obj/machinery/constructable_frame/machine_frame req_amount = 5 * SHEET_MATERIAL_AMOUNT // Arbitrary value since machines don't handle matter properly yet. required_material = /decl/material/solid/metal/steel available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/strut/machine/spawn_result(mob/user, location, amount, decl/material/mat, decl/material/reinf_mat, paint_color, spent_type, spent_amount = 1) +/decl/stack_recipe/rods/machine/spawn_result(mob/user, location, amount, decl/material/mat, decl/material/reinf_mat, paint_color, spent_type, spent_amount = 1) return ..(user, location, amount, null, null, paint_color, spent_type, spent_amount) diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index 47eebe2b4542..e0d3c4c21a0a 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -116,44 +116,6 @@ /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)/2) ) -/datum/fabricator_recipe/struts - name = "strut, steel" - path = /obj/item/stack/material/strut/mapped/steel - -/datum/fabricator_recipe/struts/get_resources() - resources = list( - /decl/material/solid/metal/steel = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - -/datum/fabricator_recipe/struts/plastic - name = "strut, plastic" - path = /obj/item/stack/material/strut/mapped/plastic - -/datum/fabricator_recipe/struts/plastic/get_resources() - resources = list( - /decl/material/solid/organic/plastic = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - -/datum/fabricator_recipe/struts/aluminium - name = "strut, aluminium" - path = /obj/item/stack/material/strut/mapped/aluminium - fabricator_types = list(FABRICATOR_CLASS_INDUSTRIAL) - -/datum/fabricator_recipe/struts/aluminium/get_resources() - resources = list( - /decl/material/solid/metal/aluminium = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - -/datum/fabricator_recipe/struts/titanium - name = "strut, titanium" - path = /obj/item/stack/material/strut/mapped/titanium - fabricator_types = list(FABRICATOR_CLASS_INDUSTRIAL) - -/datum/fabricator_recipe/struts/titanium/get_resources() - resources = list( - /decl/material/solid/metal/titanium = ceil((SHEET_MATERIAL_AMOUNT * FABRICATOR_EXTRA_COST_FACTOR)), - ) - /datum/fabricator_recipe/umbrella path = /obj/item/umbrella diff --git a/code/modules/materials/material_sheets_mapping.dm b/code/modules/materials/material_sheets_mapping.dm index d852c202c26f..d7f80277bcd3 100644 --- a/code/modules/materials/material_sheets_mapping.dm +++ b/code/modules/materials/material_sheets_mapping.dm @@ -114,6 +114,11 @@ STACK_SUBTYPES(plastic, "plastic", solid/organic/pl STACK_SUBTYPES(aluminium, "aluminium", solid/metal/aluminium, strut, null) STACK_SUBTYPES(titanium, "titanium", solid/metal/titanium, strut, null) +STACK_SUBTYPES(steel, "steel", solid/metal/steel, rods, null) +STACK_SUBTYPES(plastic, "plastic", solid/organic/plastic, rods, null) +STACK_SUBTYPES(aluminium, "aluminium", solid/metal/aluminium, rods, null) +STACK_SUBTYPES(titanium, "titanium", solid/metal/titanium, rods, null) + STACK_SUBTYPES(cotton, "cotton", solid/organic/cloth, thread, null) STACK_SUBTYPES(dried_gut, "dried gut", solid/organic/leather/gut, thread, null) diff --git a/code/modules/materials/material_stack_misc.dm b/code/modules/materials/material_stack_misc.dm index 9ed1861aab88..837678955369 100644 --- a/code/modules/materials/material_stack_misc.dm +++ b/code/modules/materials/material_stack_misc.dm @@ -154,24 +154,3 @@ material = /decl/material/solid/organic/plantmatter/grass/dry drying_wetness = null dried_type = null - -/obj/item/stack/material/strut - name = "struts" - singular_name = "strut" - plural_name = "struts" - icon_state = "sheet-strut" - plural_icon_state = "sheet-strut-mult" - max_icon_state = "sheet-strut-max" - stack_merge_type = /obj/item/stack/material/strut - crafting_stack_type = /obj/item/stack/material/strut - -/obj/item/stack/material/strut/cyborg - name = "metal strut synthesizer" - desc = "A device that makes metal strut." - gender = NEUTER - matter = null - uses_charge = 1 - charge_costs = list(500) - material = /decl/material/solid/metal/steel - max_health = ITEM_HEALTH_NO_DAMAGE - is_spawnable_type = FALSE diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm index 2711d4981b8b..8a060e833c2f 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm @@ -34,7 +34,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -70,7 +69,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm index 9d3b3c6bfb97..24acaa74926f 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm @@ -48,7 +48,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -88,7 +87,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm index c36f632ff369..8a6d482d6bcd 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_maintenance_drone.dm @@ -32,7 +32,6 @@ /obj/item/matter_decompiler, /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -85,7 +84,6 @@ for(var/thing in list( /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/maps/antag_spawn/mercenary/mercenary_base.dmm b/maps/antag_spawn/mercenary/mercenary_base.dmm index 86f6f092e57a..f75023f8eb7d 100644 --- a/maps/antag_spawn/mercenary/mercenary_base.dmm +++ b/maps/antag_spawn/mercenary/mercenary_base.dmm @@ -1209,7 +1209,7 @@ /obj/item/stack/material/sheet/reinforced/mapped/plasteel/ten, /obj/item/stack/material/ingot/mapped/copper/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/pane/mapped/rglass/fifty, /obj/item/stack/material/pane/mapped/rborosilicate/ten, /obj/structure/cable{ @@ -2751,9 +2751,9 @@ /obj/item/stack/material/sheet/mapped/steel/ten, /obj/item/stack/material/sheet/mapped/steel/ten, /obj/item/stack/material/sheet/mapped/steel/ten, -/obj/item/stack/material/rods/ten, -/obj/item/stack/material/rods/ten, -/obj/item/stack/material/rods/ten, +/obj/item/stack/material/rods/mapped/steel/ten, +/obj/item/stack/material/rods/mapped/steel/ten, +/obj/item/stack/material/rods/mapped/steel/ten, /obj/item/stack/material/pane/mapped/glass/ten, /obj/item/stack/material/pane/mapped/glass/ten, /obj/item/stack/material/pane/mapped/glass/ten, diff --git a/maps/away/bearcat/bearcat-1.dmm b/maps/away/bearcat/bearcat-1.dmm index 7b990dda9af0..eff2b18e4e04 100644 --- a/maps/away/bearcat/bearcat-1.dmm +++ b/maps/away/bearcat/bearcat-1.dmm @@ -2384,7 +2384,7 @@ /obj/item/stack/tape_roll/duct_tape, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/panel/mapped/plastic/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 62ededfcaf0f..0b0071ed32fd 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -4058,7 +4058,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "hM" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/structure/lattice, /turf/space, /area/ship/scrap/maintenance/atmos) @@ -4542,13 +4542,13 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "iO" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /mob/living/simple_animal/hostile/carp, /turf/space, /area/ship/scrap/maintenance/atmos) "iP" = ( /obj/item/stack/material/sheet/mapped/steel, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/ship/scrap/maintenance/atmos) "iQ" = ( @@ -4686,7 +4686,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "jc" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/structure/lattice, /turf/space, /area/ship/scrap/maintenance/power) @@ -4861,7 +4861,7 @@ /turf/wall, /area/ship/scrap/maintenance/power) "jB" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "jC" = ( @@ -4939,7 +4939,7 @@ /area/ship/scrap/maintenance/engine/aft) "jN" = ( /obj/item/stack/material/sheet/mapped/steel, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/structure/lattice, /turf/space, /area/ship/scrap/maintenance/engine/aft) @@ -5003,7 +5003,7 @@ /turf/floor/plating/airless, /area/ship/scrap/maintenance/engine/aft) "jX" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/ship/scrap/maintenance/engine/aft) "jY" = ( diff --git a/maps/away/casino/casino.dmm b/maps/away/casino/casino.dmm index b1d54db6ab7c..adebd3f43c9e 100644 --- a/maps/away/casino/casino.dmm +++ b/maps/away/casino/casino.dmm @@ -152,7 +152,7 @@ /area/casino/casino_bridge) "aw" = ( /obj/item/wirecutters, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "ax" = ( diff --git a/maps/away/liberia/liberia.dmm b/maps/away/liberia/liberia.dmm index 83a7106382f2..d3a1d6658d03 100644 --- a/maps/away/liberia/liberia.dmm +++ b/maps/away/liberia/liberia.dmm @@ -2340,8 +2340,8 @@ /obj/item/stack/material/pane/mapped/glass/fifty, /obj/item/stack/material/pane/mapped/glass/fifty, /obj/item/stack/material/sheet/reinforced/mapped/fiberglass/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/forty, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, @@ -7523,8 +7523,8 @@ /obj/structure/closet/crate, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/panel/mapped/plastic/fifty, /obj/item/stack/material/sheet/reinforced/mapped/titanium/fifty, /obj/item/stack/material/sheet/reinforced/mapped/ocp/fifty, diff --git a/maps/away/lost_supply_base/lost_supply_base.dmm b/maps/away/lost_supply_base/lost_supply_base.dmm index 5acfba27ea2a..55d862015336 100644 --- a/maps/away/lost_supply_base/lost_supply_base.dmm +++ b/maps/away/lost_supply_base/lost_supply_base.dmm @@ -117,14 +117,14 @@ /turf/floor/plating/airless, /area/space) "aq" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "ar" = ( /obj/structure/cable/yellow{ icon_state = "4-8" }, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel, /turf/floor/plating/airless, /area/space) @@ -760,7 +760,7 @@ /turf/floor/plating/airless, /area/lost_supply_base) "cf" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/item/stack/material/sheet/mapped/steel, /turf/floor/plating/airless, /area/lost_supply_base) diff --git a/maps/away/magshield/magshield.dmm b/maps/away/magshield/magshield.dmm index d9da2ebbfdb1..b45e75903196 100644 --- a/maps/away/magshield/magshield.dmm +++ b/maps/away/magshield/magshield.dmm @@ -106,7 +106,7 @@ /turf/floor/plating/airless, /area/space) "ao" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "ap" = ( @@ -529,7 +529,7 @@ /turf/space, /area/magshield/smes_storage) "bF" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/magshield/smes_storage) "bG" = ( @@ -1265,7 +1265,7 @@ /turf/floor/plating/airless, /area/magshield/north) "dF" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/floor/plating/airless, /area/magshield/north) "dG" = ( @@ -2750,11 +2750,11 @@ /turf/floor/carpet/blue, /area/magshield/west) "ic" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/floor/tiled/dark, /area/magshield/west) "id" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/magshield/west) "ie" = ( @@ -3769,7 +3769,7 @@ /turf/floor/tiled, /area/magshield/south) "lg" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/magshield/south) "lh" = ( diff --git a/maps/exodus/exodus-1.dmm b/maps/exodus/exodus-1.dmm index 030d5c21bbae..b26f56f69c62 100644 --- a/maps/exodus/exodus-1.dmm +++ b/maps/exodus/exodus-1.dmm @@ -2424,7 +2424,7 @@ /turf/floor/plating, /area/exodus/maintenance/sub/central) "gJ" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/space, /area/space) "gK" = ( @@ -4114,7 +4114,7 @@ /turf/floor/plating, /area/exodus/maintenance/sub/aft) "kQ" = ( -/obj/item/stack/material/rods{ +/obj/item/stack/material/rods/mapped/steel{ amount = 6 }, /turf/space, diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 5c0062593c76..734bab7372ae 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -12749,9 +12749,7 @@ /obj/item/stack/material/pane/mapped/rglass{ amount = 50 }, -/obj/item/stack/material/rods{ - amount = 50 - }, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/effect/floor_decal/industrial/hatch/yellow, /turf/floor/tiled/steel_grid, /area/exodus/ai_monitored/storage/eva) @@ -31610,9 +31608,7 @@ /turf/floor/tiled/white, /area/exodus/research/lab) "boW" = ( -/obj/item/stack/material/rods{ - amount = 50 - }, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stock_parts/circuitboard/airlock_electronics, /obj/item/stock_parts/circuitboard/airlock_electronics, /obj/item/cell/high, @@ -58793,7 +58789,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/starboard) "ctg" = ( -/obj/item/stack/material/rods{ +/obj/item/stack/material/rods/mapped/steel{ amount = 6 }, /turf/space, @@ -58839,9 +58835,7 @@ /turf/floor/plating, /area/exodus/engineering/engine_room) "cts" = ( -/obj/item/stack/material/rods{ - amount = 10 - }, +/obj/item/stack/material/rods/mapped/steel/ten, /obj/structure/lattice, /turf/space, /area/space) @@ -62056,9 +62050,7 @@ /turf/floor/plating, /area/exodus/engineering/engine_room) "cHK" = ( -/obj/item/stack/material/rods{ - amount = 10 - }, +/obj/item/stack/material/rods/mapped/steel/ten, /turf/space, /area/space) "cHL" = ( diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 2f87aab690dd..e9be521e3b37 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -422,7 +422,7 @@ /turf/floor/tiled, /area/ministation/cargo) "cd" = ( -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /turf/floor/plating/airless, /area/space) "ce" = ( @@ -4268,8 +4268,8 @@ /obj/structure/closet/crate, /obj/item/stack/material/sheet/shiny/mapped/aluminium/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, /obj/item/stack/material/pane/mapped/glass/fifty, @@ -4755,7 +4755,7 @@ /turf/floor/tiled/monotile, /area/ministation/atmospherics) "va" = ( -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/pane/mapped/glass/fifty, /obj/item/stock_parts/circuitboard/airlock_electronics, /obj/item/stock_parts/circuitboard/airlock_electronics, @@ -8636,7 +8636,7 @@ /obj/structure/table, /obj/item/folder/yellow, /obj/item/clothing/head/earmuffs, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/cash/scavbucks, /turf/floor/tiled, /area/ministation/engine) diff --git a/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm b/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm index 29714944a166..13485f43f126 100644 --- a/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm +++ b/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm @@ -3,11 +3,11 @@ /turf/template_noop, /area/template_noop) "ac" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/template_noop, /area/template_noop) "ad" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/item/stack/material/sheet/reinforced/mapped/ocp, /turf/template_noop, /area/template_noop) diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index 51089ce8d4b0..1cf08cd2dc4e 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -7421,10 +7421,10 @@ /obj/structure/railing/mapped, /obj/effect/floor_decal/industrial/warning/dust, /obj/structure/rack, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/tool/pickaxe, /obj/item/tool/pickaxe, /obj/item/tool/shovel, diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index a758ba9410b4..e0a0f73ffbcc 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -3283,7 +3283,7 @@ /obj/item/stack/tape_roll/duct_tape, /obj/item/stack/material/sheet/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, -/obj/item/stack/material/rods/fifty, +/obj/item/stack/material/rods/mapped/steel/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, /obj/item/stack/material/panel/mapped/plastic/fifty, /obj/item/stack/material/sheet/mapped/steel/fifty, diff --git a/maps/tradeship/tradeship-3.dmm b/maps/tradeship/tradeship-3.dmm index e666e1f3cece..8af1d43d792e 100644 --- a/maps/tradeship/tradeship-3.dmm +++ b/maps/tradeship/tradeship-3.dmm @@ -870,7 +870,7 @@ /turf/floor/tiled/techfloor/grid, /area/ship/trade/comms) "yM" = ( -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /obj/machinery/atmospherics/unary/vent_scrubber/on, /turf/floor/plating, /area/ship/trade/bridge_unused) diff --git a/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm b/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm index 366a84d6ba3f..1edbe07fd68b 100644 --- a/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm +++ b/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm @@ -297,10 +297,10 @@ /obj/machinery/door/firedoor, /obj/structure/wall_frame/hull, /obj/structure/grille, -/obj/item/stack/material/rods, -/obj/item/stack/material/rods, -/obj/item/stack/material/rods, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, +/obj/item/stack/material/rods/mapped/steel, +/obj/item/stack/material/rods/mapped/steel, +/obj/item/stack/material/rods/mapped/steel, /obj/item/shard, /obj/item/shard, /obj/item/shard, diff --git a/mods/content/dungeon_loot/subtypes/maint.dm b/mods/content/dungeon_loot/subtypes/maint.dm index 1063d1d165f8..cea9732ac81c 100644 --- a/mods/content/dungeon_loot/subtypes/maint.dm +++ b/mods/content/dungeon_loot/subtypes/maint.dm @@ -146,7 +146,7 @@ /obj/item/food/old/hotdog, /obj/item/food/old/pizza, /obj/item/ammo_casing, - /obj/item/stack/material/rods/ten, + /obj/item/stack/material/rods/mapped/steel/ten, /obj/item/stack/material/sheet/mapped/steel/five, /obj/item/stack/material/cardstock/mapped/cardboard/five, /obj/item/poster, diff --git a/mods/content/government/away_sites/icarus/icarus-1.dmm b/mods/content/government/away_sites/icarus/icarus-1.dmm index e42c91b348e3..ea85c81b5b57 100644 --- a/mods/content/government/away_sites/icarus/icarus-1.dmm +++ b/mods/content/government/away_sites/icarus/icarus-1.dmm @@ -1442,7 +1442,7 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "eR" = ( @@ -1492,7 +1492,7 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "eW" = ( @@ -1518,7 +1518,7 @@ /area/icarus/open) "eZ" = ( /obj/effect/decal/cleanable/dirt/visible, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fa" = ( @@ -1589,7 +1589,7 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fh" = ( @@ -1604,13 +1604,13 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fj" = ( /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fk" = ( @@ -1669,7 +1669,7 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/item/stack/material/rods, +/obj/item/stack/material/rods/mapped/steel, /turf/unsimulated/floor/sand, /area/icarus/open) "fq" = ( diff --git a/mods/gamemodes/heist/heist_base.dmm b/mods/gamemodes/heist/heist_base.dmm index 03fe3d7afdb7..17f4b84050e4 100644 --- a/mods/gamemodes/heist/heist_base.dmm +++ b/mods/gamemodes/heist/heist_base.dmm @@ -737,7 +737,7 @@ /obj/machinery/door/blast/regular/open{ id_tag = "SkipjackShuttersNorth"; name = "Blast Doors"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /obj/effect/paint/brown, @@ -1104,7 +1104,7 @@ dir = 4; id_tag = "SkipjackShuttersWest"; name = "Skipjack Shutters"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /obj/effect/paint/brown, @@ -1131,7 +1131,7 @@ dir = 8; id_tag = "SkipjackShuttersEast"; name = "Skipjack Shutters"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /obj/effect/paint/brown, @@ -1149,7 +1149,7 @@ "dB" = ( /obj/structure/rack, /obj/item/gun/launcher/bow/crossbow/powered, -/obj/item/stack/material/rods/ten, +/obj/item/stack/material/rods/mapped/steel/ten, /obj/machinery/light/small{ dir = 8 }, @@ -1203,7 +1203,7 @@ dir = 4; id_tag = "SkipjackShuttersWest"; name = "Skipjack Shutters"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /turf/floor/plating, diff --git a/mods/species/ascent/mobs/drone.dm b/mods/species/ascent/mobs/drone.dm index f2f94d499dea..2294585ee151 100644 --- a/mods/species/ascent/mobs/drone.dm +++ b/mods/species/ascent/mobs/drone.dm @@ -35,7 +35,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass, @@ -96,7 +95,6 @@ /obj/item/stack/material/cyborg/steel, /obj/item/stack/material/cyborg/aluminium, /obj/item/stack/material/rods/cyborg, - /obj/item/stack/material/strut/cyborg, /obj/item/stack/tile/floor/cyborg, /obj/item/stack/tile/roof/cyborg, /obj/item/stack/material/cyborg/glass/reinforced diff --git a/tools/map_migrations/5345_strut_removal.txt b/tools/map_migrations/5345_strut_removal.txt new file mode 100644 index 000000000000..9c52c9ba3cdf --- /dev/null +++ b/tools/map_migrations/5345_strut_removal.txt @@ -0,0 +1,3 @@ +/obj/item/stack/material/rods/ten : /obj/item/stack/material/rods/mapped/steel/ten{@OLD} +/obj/item/stack/material/rods/fifty : /obj/item/stack/material/rods/mapped/steel/fifty{@OLD} +/obj/item/stack/material/rods : /obj/item/stack/material/rods/mapped/steel{@OLD} From b7db107794c748a7fefcd554e3d85cb2b66c00f0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Apr 2026 19:13:39 -0400 Subject: [PATCH 1196/1331] Avoid queueing turfs for ambience during init --- code/controllers/subsystems/ambience.dm | 13 ++++++++++++- code/game/area/areas.dm | 5 +++-- code/game/objects/structures/flora/tree.dm | 4 ++-- code/game/turfs/space/space.dm | 3 ++- code/game/turfs/turf.dm | 6 ++++-- code/modules/multiz/level_data.dm | 2 +- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/code/controllers/subsystems/ambience.dm b/code/controllers/subsystems/ambience.dm index 8f4b462e2050..55bd35f9f164 100644 --- a/code/controllers/subsystems/ambience.dm +++ b/code/controllers/subsystems/ambience.dm @@ -4,12 +4,23 @@ SUBSYSTEM_DEF(ambience) priority = SS_PRIORITY_LIGHTING init_order = SS_INIT_LIGHTING runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT // Copied from icon update subsystem. - flags = SS_NO_INIT var/list/queued = list() /datum/controller/subsystem/ambience/stat_entry() ..("P:[length(queued)]") +/datum/controller/subsystem/ambience/Initialize(start_timeofday) + for(var/datum/level_data/the_level as anything in SSmapping.levels_by_z) + // this may actually be faster than adding them to the queue. TBD. + for(var/turf/target_turf as anything in block(the_level.level_inner_min_x, the_level.level_inner_min_y, the_level.level_z, the_level.level_inner_max_x, the_level.level_inner_max_y, the_level.level_z)) + if(target_turf.ambience_queued) // Somehow wound up queued, handle it then. + continue + if(!target_turf.simulated) + continue + target_turf.update_ambient_light_from_z_or_area() + // also flush the queue prior to roundstart + fire(no_mc_tick = TRUE) + /datum/controller/subsystem/ambience/fire(resumed = FALSE, no_mc_tick = FALSE) var/list/curr = queued while (curr.len) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index b2858a7308e0..b481b506d1c6 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -173,8 +173,9 @@ var/global/list/areas = list() if(T.is_outside() != old_outside) T.update_weather() - AMBIENCE_QUEUE_TURF(T) - else if(A.interior_ambient_light_modifier != old_area_ambience) + if(SSambience.initialized) // if not initialized, we'll loop over all turfs anyway + AMBIENCE_QUEUE_TURF(T) + else if(A.interior_ambient_light_modifier != old_area_ambience && SSambience.initialized) AMBIENCE_QUEUE_TURF(T) /turf/proc/update_registrations_on_adjacent_area_change() diff --git a/code/game/objects/structures/flora/tree.dm b/code/game/objects/structures/flora/tree.dm index 13dcf346bcc1..57c59edea804 100644 --- a/code/game/objects/structures/flora/tree.dm +++ b/code/game/objects/structures/flora/tree.dm @@ -37,7 +37,7 @@ /obj/structure/flora/tree/Initialize(ml, _mat, _reinf_mat) . = ..() - if(!ml && protects_against_weather) + if(!ml && protects_against_weather && SSambience.initialized) for(var/turf/T as anything in RANGE_TURFS(src, 1)) AMBIENCE_QUEUE_TURF(T) @@ -45,7 +45,7 @@ /obj/structure/flora/tree/Destroy() var/list/turfs_to_update = RANGE_TURFS(src, 1) . = ..() - if(protects_against_weather) + if(protects_against_weather && SSambience.initialized) for(var/turf/T in turfs_to_update) AMBIENCE_QUEUE_TURF(T) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index ff76d205ad23..eb63e73c3ccf 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -25,7 +25,8 @@ atom_flags |= ATOM_FLAG_INITIALIZED _earliest_type ||= type - AMBIENCE_QUEUE_TURF(src) + if(SSambience.initialized) // if not initialized, we'll loop over all turfs anyway + AMBIENCE_QUEUE_TURF(src) //We might be an edge if(y == world.maxy || forced_dirs & NORTH) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index fdacc8c04d43..b677b30f83c7 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -121,7 +121,8 @@ // we don't care about volume because turfs always create a maximum volume holder on reagent add. FINALIZE_REAGENTS_SERDE(reagents) - AMBIENCE_QUEUE_TURF(src) + if(SSambience.initialized) // if not initialized, we'll loop over all turfs anyway + AMBIENCE_QUEUE_TURF(src) if (opacity) has_opaque_atom = TRUE @@ -636,7 +637,8 @@ state_was_modified() is_outside = new_outside update_external_atmos_participation() - AMBIENCE_QUEUE_TURF(src) + if(SSambience.initialized) // if not initialized, we'll loop over all turfs anyway + AMBIENCE_QUEUE_TURF(src) if(!skip_weather_update) update_weather() diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index 9250c0dfea4c..73f2054e5696 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -906,7 +906,7 @@ INITIALIZE_IMMEDIATE(/obj/abstract/level_data_spawner) return TRUE /datum/level_data/proc/update_turf_ambience() - if(SSatoms.atom_init_stage >= INITIALIZATION_INNEW_REGULAR) + if(SSambience.initialized) // our turfs will update themselves later anyway for(var/turf/level_turf as anything in block(level_inner_min_x, level_inner_min_y, level_z, level_inner_max_x, level_inner_max_y, level_z)) level_turf.update_ambient_light_from_z_or_area() // AMBIENCE_QUEUE_TURF(level_turf) - seems to be less consistent CHECK_TICK From 6a9c40a4a0f2540d577a9f7f2d67cb6511c638f0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 11 Apr 2026 12:49:44 -0400 Subject: [PATCH 1197/1331] Add codex dump to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3d638d224bdc..185303ed487c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.lk *.backup *.before +codex/ data/ dmdoc/ cfg/ From 4d67b2a581a9ee2101664d0ed86f8067f875ffc1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:49:53 -0400 Subject: [PATCH 1198/1331] Move SMES code into its own module --- .../items/circuitboards/machinery/power.dm | 24 ---------- code/modules/codex/entries/machinery.dm | 6 --- .../imprinter/designs_misc_circuits.dm | 6 --- code/modules/{power/smes.dm => smes/_smes.dm} | 0 .../batteryrack}/batteryrack.dm | 0 .../smes/batteryrack/batteryrack_circuit.dm | 14 ++++++ .../smes_buildable.dm} | 47 ------------------- code/modules/smes/smes_circuit.dm | 15 ++++++ code/modules/smes/smes_codex.dm | 5 ++ code/modules/smes/smes_parts.dm | 46 ++++++++++++++++++ code/modules/{power => smes}/smes_presets.dm | 2 +- .../smes.dm => modules/smes/smes_wires.dm} | 20 ++++---- nebula.dme | 14 ++++-- 13 files changed, 100 insertions(+), 99 deletions(-) rename code/modules/{power/smes.dm => smes/_smes.dm} (100%) rename code/modules/{power => smes/batteryrack}/batteryrack.dm (100%) create mode 100644 code/modules/smes/batteryrack/batteryrack_circuit.dm rename code/modules/{power/smes_construction.dm => smes/smes_buildable.dm} (85%) create mode 100644 code/modules/smes/smes_circuit.dm create mode 100644 code/modules/smes/smes_codex.dm create mode 100644 code/modules/smes/smes_parts.dm rename code/modules/{power => smes}/smes_presets.dm (93%) rename code/{datums/wires/smes.dm => modules/smes/smes_wires.dm} (82%) diff --git a/code/game/objects/items/circuitboards/machinery/power.dm b/code/game/objects/items/circuitboards/machinery/power.dm index b580ee6ff641..a32eac00fff0 100644 --- a/code/game/objects/items/circuitboards/machinery/power.dm +++ b/code/game/objects/items/circuitboards/machinery/power.dm @@ -1,27 +1,3 @@ -/obj/item/stock_parts/circuitboard/smes - name = "circuitboard (superconductive magnetic energy storage)" - build_path = /obj/machinery/power/smes/buildable - board_type = "machine" - origin_tech = @'{"powerstorage":6,"engineering":4}' - req_components = list(/obj/item/stock_parts/smes_coil = 1, /obj/item/stack/cable_coil = 30) - additional_spawn_components = list( - /obj/item/stock_parts/console_screen = 1, - /obj/item/stock_parts/keyboard = 1, - /obj/item/stock_parts/shielding/electric = 1 - ) - -/obj/item/stock_parts/circuitboard/batteryrack - name = "circuitboard (battery rack PSU)" - build_path = /obj/machinery/power/smes/batteryrack - board_type = "machine" - origin_tech = @'{"powerstorage":3,"engineering":2}' - req_components = list(/obj/item/stock_parts/capacitor/ = 3, /obj/item/stock_parts/matter_bin/ = 1) - additional_spawn_components = list( - /obj/item/stock_parts/console_screen = 1, - /obj/item/stock_parts/keyboard = 1, - /obj/item/stock_parts/power/apc/buildable = 1 - ) - /obj/item/stock_parts/circuitboard/recharger name = "circuitboard (recharger)" build_path = /obj/machinery/recharger diff --git a/code/modules/codex/entries/machinery.dm b/code/modules/codex/entries/machinery.dm index 62f34de3efeb..0d60980ef1e0 100644 --- a/code/modules/codex/entries/machinery.dm +++ b/code/modules/codex/entries/machinery.dm @@ -123,12 +123,6 @@ disambiguator = "machine" available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/datum/codex_entry/smes - associated_paths = list(/obj/machinery/power/smes) - mechanics_text = "It's a big battery. An important part of the power network that ensures that you still have power when your generators eventually explode. Maximum input and output settings are available.
    The lights on the front show the status of the SMES: The column on the left shows stored power, a blinking red light at top right shows that it's on but receiving no power, blinking yellow shows that the SMES is charging, and the little light on the middle right shows whether it's outputting power or not.
    A floor terminal puts power into the SMES, and power is output to a wire underneath." - disambiguator = "machine" - available_to_map_tech_level = MAP_TECH_LEVEL_SPACE - /datum/codex_entry/vending associated_paths = list(/obj/machinery/vending) associated_strings = list("vending machine", "vendor") diff --git a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm index 7f85bd5fe3ae..474b8c3e4099 100644 --- a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm +++ b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm @@ -191,12 +191,6 @@ /datum/fabricator_recipe/imprinter/circuit/pacmanpotato path = /obj/item/stock_parts/circuitboard/pacman/super/potato -/datum/fabricator_recipe/imprinter/circuit/batteryrack - path = /obj/item/stock_parts/circuitboard/batteryrack - -/datum/fabricator_recipe/imprinter/circuit/smes_cell - path = /obj/item/stock_parts/circuitboard/smes - /datum/fabricator_recipe/imprinter/circuit/alerts path = /obj/item/stock_parts/circuitboard/stationalert diff --git a/code/modules/power/smes.dm b/code/modules/smes/_smes.dm similarity index 100% rename from code/modules/power/smes.dm rename to code/modules/smes/_smes.dm diff --git a/code/modules/power/batteryrack.dm b/code/modules/smes/batteryrack/batteryrack.dm similarity index 100% rename from code/modules/power/batteryrack.dm rename to code/modules/smes/batteryrack/batteryrack.dm diff --git a/code/modules/smes/batteryrack/batteryrack_circuit.dm b/code/modules/smes/batteryrack/batteryrack_circuit.dm new file mode 100644 index 000000000000..47a5ded16b43 --- /dev/null +++ b/code/modules/smes/batteryrack/batteryrack_circuit.dm @@ -0,0 +1,14 @@ +/obj/item/stock_parts/circuitboard/batteryrack + name = "circuitboard (battery rack PSU)" + build_path = /obj/machinery/power/smes/batteryrack + board_type = "machine" + origin_tech = @'{"powerstorage":3,"engineering":2}' + req_components = list(/obj/item/stock_parts/capacitor = 3, /obj/item/stock_parts/matter_bin = 1) + additional_spawn_components = list( + /obj/item/stock_parts/console_screen = 1, + /obj/item/stock_parts/keyboard = 1, + /obj/item/stock_parts/power/apc/buildable = 1 + ) + +/datum/fabricator_recipe/imprinter/circuit/batteryrack + path = /obj/item/stock_parts/circuitboard/batteryrack \ No newline at end of file diff --git a/code/modules/power/smes_construction.dm b/code/modules/smes/smes_buildable.dm similarity index 85% rename from code/modules/power/smes_construction.dm rename to code/modules/smes/smes_buildable.dm index cb78bef66777..3aaccff8bbd9 100644 --- a/code/modules/power/smes_construction.dm +++ b/code/modules/smes/smes_buildable.dm @@ -5,53 +5,6 @@ // This is subtype of SMES that should be normally used. It can be constructed, deconstructed and hacked. // It also supports RCON System which allows you to operate it remotely, if properly set. -//MAGNETIC COILS - These things actually store and transmit power within the SMES. Different types have different -/obj/item/stock_parts/smes_coil - name = "superconductive magnetic coil" - desc = "Standard superconductive magnetic coil with average capacity and I/O rating." - icon = 'icons/obj/items/stock_parts/stock_parts.dmi' - icon_state = "smes_coil" - w_class = ITEM_SIZE_LARGE // It's LARGE (backpack size) - origin_tech = @'{"materials":7,"powerstorage":7,"engineering":5}' - base_type = /obj/item/stock_parts/smes_coil - part_flags = PART_FLAG_HAND_REMOVE - material = /decl/material/solid/metal/steel - matter = list( - /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE, - /decl/material/solid/metal/silver = MATTER_AMOUNT_TRACE, - /decl/material/solid/metal/aluminium = MATTER_AMOUNT_TRACE - ) - - var/ChargeCapacity = 50 KILOWATTS - var/IOCapacity = 250 KILOWATTS - -// 20% Charge Capacity, 60% I/O Capacity. Used for substation/outpost SMESs. -/obj/item/stock_parts/smes_coil/weak - name = "basic superconductive magnetic coil" - desc = "Cheaper model of standard superconductive magnetic coil. It's capacity and I/O rating are considerably lower." - icon_state = "smes_coil_weak" - ChargeCapacity = 10 KILOWATTS - IOCapacity = 150 KILOWATTS - -// 500% Charge Capacity, 40% I/O Capacity. Holds a lot of energy, but charges slowly if not combined with other coils. Ideal for backup storage. -/obj/item/stock_parts/smes_coil/super_capacity - name = "superconductive capacitance coil" - desc = "Specialised version of standard superconductive magnetic coil. This one has significantly stronger containment field, allowing for significantly larger power storage. Its IO rating is much lower, however." - icon_state = "smes_coil_capacitance" - ChargeCapacity = 250 KILOWATTS - IOCapacity = 100 KILOWATTS - rating = 2 - -// 40% Charge Capacity, 500% I/O Capacity. Technically turns SMES into large super capacitor. Ideal for shields. -/obj/item/stock_parts/smes_coil/super_io - name = "superconductive transmission coil" - desc = "Specialised version of standard superconductive magnetic coil. While this one won't store almost any power, it rapidly transfers current, making it useful in systems which require large throughput." - icon_state = "smes_coil_transmission" - ChargeCapacity = 20 KILOWATTS - IOCapacity = 1.25 MEGAWATTS - rating = 2 - // DEPRECATED // These are used on individual outposts as backup should power line be cut, or engineering outpost lost power. // 1M Charge, 150K I/O diff --git a/code/modules/smes/smes_circuit.dm b/code/modules/smes/smes_circuit.dm new file mode 100644 index 000000000000..66abf1a7cf26 --- /dev/null +++ b/code/modules/smes/smes_circuit.dm @@ -0,0 +1,15 @@ +/obj/item/stock_parts/circuitboard/smes + name = "circuitboard (superconductive magnetic energy storage)" + build_path = /obj/machinery/power/smes/buildable + board_type = "machine" + origin_tech = @'{"powerstorage":6,"engineering":4}' + req_components = list(/obj/item/stock_parts/smes_coil = 1, /obj/item/stack/cable_coil = 30) + additional_spawn_components = list( + /obj/item/stock_parts/console_screen = 1, + /obj/item/stock_parts/keyboard = 1, + /obj/item/stock_parts/shielding/electric = 1 + ) + +// And the fabricator recipe for the circuit +/datum/fabricator_recipe/imprinter/circuit/smes + path = /obj/item/stock_parts/circuitboard/smes \ No newline at end of file diff --git a/code/modules/smes/smes_codex.dm b/code/modules/smes/smes_codex.dm new file mode 100644 index 000000000000..bc10e415bf44 --- /dev/null +++ b/code/modules/smes/smes_codex.dm @@ -0,0 +1,5 @@ +/datum/codex_entry/smes + associated_paths = list(/obj/machinery/power/smes) + mechanics_text = "It's a big battery. An important part of the power network that ensures that you still have power when your generators eventually explode. Maximum input and output settings are available.
    The lights on the front show the status of the SMES: The column on the left shows stored power, a blinking red light at top right shows that it's on but receiving no power, blinking yellow shows that the SMES is charging, and the little light on the middle right shows whether it's outputting power or not.
    A floor terminal puts power into the SMES, and power is output to a wire underneath." + disambiguator = "machine" + available_to_map_tech_level = MAP_TECH_LEVEL_SPACE \ No newline at end of file diff --git a/code/modules/smes/smes_parts.dm b/code/modules/smes/smes_parts.dm new file mode 100644 index 000000000000..f67cd561a3c9 --- /dev/null +++ b/code/modules/smes/smes_parts.dm @@ -0,0 +1,46 @@ +//MAGNETIC COILS - These things actually store and transmit power within the SMES. Different types have different +/obj/item/stock_parts/smes_coil + name = "superconductive magnetic coil" + desc = "Standard superconductive magnetic coil with average capacity and I/O rating." + icon = 'icons/obj/items/stock_parts/stock_parts.dmi' + icon_state = "smes_coil" + w_class = ITEM_SIZE_LARGE // It's LARGE (backpack size) + origin_tech = @'{"materials":7,"powerstorage":7,"engineering":5}' + base_type = /obj/item/stock_parts/smes_coil + part_flags = PART_FLAG_HAND_REMOVE + material = /decl/material/solid/metal/steel + matter = list( + /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE, + /decl/material/solid/metal/silver = MATTER_AMOUNT_TRACE, + /decl/material/solid/metal/aluminium = MATTER_AMOUNT_TRACE + ) + + var/ChargeCapacity = 50 KILOWATTS + var/IOCapacity = 250 KILOWATTS + +// 20% Charge Capacity, 60% I/O Capacity. Used for substation/outpost SMESs. +/obj/item/stock_parts/smes_coil/weak + name = "basic superconductive magnetic coil" + desc = "Cheaper model of standard superconductive magnetic coil. It's capacity and I/O rating are considerably lower." + icon_state = "smes_coil_weak" + ChargeCapacity = 10 KILOWATTS + IOCapacity = 150 KILOWATTS + +// 500% Charge Capacity, 40% I/O Capacity. Holds a lot of energy, but charges slowly if not combined with other coils. Ideal for backup storage. +/obj/item/stock_parts/smes_coil/super_capacity + name = "superconductive capacitance coil" + desc = "Specialised version of standard superconductive magnetic coil. This one has significantly stronger containment field, allowing for significantly larger power storage. Its IO rating is much lower, however." + icon_state = "smes_coil_capacitance" + ChargeCapacity = 250 KILOWATTS + IOCapacity = 100 KILOWATTS + rating = 2 + +// 40% Charge Capacity, 500% I/O Capacity. Technically turns SMES into large super capacitor. Ideal for shields. +/obj/item/stock_parts/smes_coil/super_io + name = "superconductive transmission coil" + desc = "Specialised version of standard superconductive magnetic coil. While this one won't store almost any power, it rapidly transfers current, making it useful in systems which require large throughput." + icon_state = "smes_coil_transmission" + ChargeCapacity = 20 KILOWATTS + IOCapacity = 1.25 MEGAWATTS + rating = 2 \ No newline at end of file diff --git a/code/modules/power/smes_presets.dm b/code/modules/smes/smes_presets.dm similarity index 93% rename from code/modules/power/smes_presets.dm rename to code/modules/smes/smes_presets.dm index 39d638616c0c..7c7752e3d1f3 100644 --- a/code/modules/power/smes_presets.dm +++ b/code/modules/smes/smes_presets.dm @@ -1,4 +1,4 @@ -/obj/machinery/power/smes/buildable/preset/ +/obj/machinery/power/smes/buildable/preset var/_fully_charged = FALSE var/_input_maxed = FALSE var/_input_on = FALSE diff --git a/code/datums/wires/smes.dm b/code/modules/smes/smes_wires.dm similarity index 82% rename from code/datums/wires/smes.dm rename to code/modules/smes/smes_wires.dm index 7cc90b2d664a..5512d7922596 100644 --- a/code/datums/wires/smes.dm +++ b/code/modules/smes/smes_wires.dm @@ -8,17 +8,17 @@ new /datum/wire_description(SMES_WIRE_GROUNDING, "This wire appeas to connect directly to the floor.", SKILL_EXPERT), new /datum/wire_description(SMES_WIRE_FAILSAFES, "This wire appears to connect to a failsafe mechanism.") ) + /// Remote control (AI and consoles), cut to disable + var/const/SMES_WIRE_RCON = BITFLAG(0) + /// Input wire, cut to disable input, pulse to disable for 60s + var/const/SMES_WIRE_INPUT = BITFLAG(1) + /// Output wire, cut to disable output, pulse to disable for 60s + var/const/SMES_WIRE_OUTPUT = BITFLAG(2) + /// Cut to quickly discharge causing sparks, pulse to only create few sparks + var/const/SMES_WIRE_GROUNDING = BITFLAG(3) + /// Cut to disable failsafes, mend to reenable + var/const/SMES_WIRE_FAILSAFES = BITFLAG(4) -/// Remote control (AI and consoles), cut to disable -var/global/const/SMES_WIRE_RCON = BITFLAG(0) -/// Input wire, cut to disable input, pulse to disable for 60s -var/global/const/SMES_WIRE_INPUT = BITFLAG(1) -/// Output wire, cut to disable output, pulse to disable for 60s -var/global/const/SMES_WIRE_OUTPUT = BITFLAG(2) -/// Cut to quickly discharge causing sparks, pulse to only create few sparks -var/global/const/SMES_WIRE_GROUNDING = BITFLAG(3) -/// Cut to disable failsafes, mend to reenable -var/global/const/SMES_WIRE_FAILSAFES = BITFLAG(4) /datum/wires/smes/CanUse(var/mob/living/user) var/obj/machinery/power/smes/buildable/storage = holder diff --git a/nebula.dme b/nebula.dme index 0a8163cbf910..62dead7c9918 100644 --- a/nebula.dme +++ b/nebula.dme @@ -742,7 +742,6 @@ #include "code\datums\wires\robot.dm" #include "code\datums\wires\shield_generator.dm" #include "code\datums\wires\smartfridge.dm" -#include "code\datums\wires\smes.dm" #include "code\datums\wires\suit_cycler.dm" #include "code\datums\wires\vending.dm" #include "code\datums\wires\wire_description.dm" @@ -3450,7 +3449,6 @@ #include "code\modules\posture\posture_mob.dm" #include "code\modules\posture\posture_subtypes.dm" #include "code\modules\power\admin_setup_engine.dm" -#include "code\modules\power\batteryrack.dm" #include "code\modules\power\breaker_box.dm" #include "code\modules\power\debug_items.dm" #include "code\modules\power\generator.dm" @@ -3458,9 +3456,6 @@ #include "code\modules\power\power.dm" #include "code\modules\power\powernet.dm" #include "code\modules\power\powernet_sensor.dm" -#include "code\modules\power\smes.dm" -#include "code\modules\power\smes_construction.dm" -#include "code\modules\power\smes_presets.dm" #include "code\modules\power\stirling.dm" #include "code\modules\power\terminal.dm" #include "code\modules\power\turbine.dm" @@ -3823,6 +3818,15 @@ #include "code\modules\shuttles\shuttle_specops.dm" #include "code\modules\shuttles\shuttle_supply.dm" #include "code\modules\shuttles\shuttles_multi.dm" +#include "code\modules\smes\_smes.dm" +#include "code\modules\smes\smes_buildable.dm" +#include "code\modules\smes\smes_circuit.dm" +#include "code\modules\smes\smes_codex.dm" +#include "code\modules\smes\smes_parts.dm" +#include "code\modules\smes\smes_presets.dm" +#include "code\modules\smes\smes_wires.dm" +#include "code\modules\smes\batteryrack\batteryrack.dm" +#include "code\modules\smes\batteryrack\batteryrack_circuit.dm" #include "code\modules\species\species.dm" #include "code\modules\species\species_allergies.dm" #include "code\modules\species\species_attack.dm" From bb89102660f67e84fdded78e372cd03aa26d6d95 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:31:17 -0400 Subject: [PATCH 1199/1331] Move fission power into its own module --- code/modules/{power => }/fission/core.dm | 0 code/modules/{power => }/fission/core_control.dm | 0 code/modules/{power => }/fission/fission_circuits.dm | 0 nebula.dme | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename code/modules/{power => }/fission/core.dm (100%) rename code/modules/{power => }/fission/core_control.dm (100%) rename code/modules/{power => }/fission/fission_circuits.dm (100%) diff --git a/code/modules/power/fission/core.dm b/code/modules/fission/core.dm similarity index 100% rename from code/modules/power/fission/core.dm rename to code/modules/fission/core.dm diff --git a/code/modules/power/fission/core_control.dm b/code/modules/fission/core_control.dm similarity index 100% rename from code/modules/power/fission/core_control.dm rename to code/modules/fission/core_control.dm diff --git a/code/modules/power/fission/fission_circuits.dm b/code/modules/fission/fission_circuits.dm similarity index 100% rename from code/modules/power/fission/fission_circuits.dm rename to code/modules/fission/fission_circuits.dm diff --git a/nebula.dme b/nebula.dme index 0a8163cbf910..161336dbe49c 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2440,6 +2440,9 @@ #include "code\modules\fishing\bait.dm" #include "code\modules\fishing\fishing_line.dm" #include "code\modules\fishing\fishing_rod.dm" +#include "code\modules\fission\core.dm" +#include "code\modules\fission\core_control.dm" +#include "code\modules\fission\fission_circuits.dm" #include "code\modules\fluids\_fluid.dm" #include "code\modules\fluids\fluid_flood.dm" #include "code\modules\fluids\fluid_mapped.dm" @@ -3473,9 +3476,6 @@ #include "code\modules\power\cable\heavycable.dm" #include "code\modules\power\cell\_cell.dm" #include "code\modules\power\cell\cell_types.dm" -#include "code\modules\power\fission\core.dm" -#include "code\modules\power\fission\core_control.dm" -#include "code\modules\power\fission\fission_circuits.dm" #include "code\modules\power\fusion\_setup.dm" #include "code\modules\power\fusion\fusion_circuits.dm" #include "code\modules\power\fusion\fusion_particle_catcher.dm" From ab74726c89ddce9197b55a612ed82f6fd8283c3e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Oct 2025 17:17:50 -0400 Subject: [PATCH 1200/1331] Move thermoelectric generator into its own module --- .../items/circuitboards/machinery/power.dm | 29 ------------------- .../circulator.dm | 0 .../generator.dm | 0 .../thermoelectric_generator/teg_circuits.dm | 27 +++++++++++++++++ nebula.dme | 5 ++-- 5 files changed, 30 insertions(+), 31 deletions(-) rename code/modules/{atmospherics/components/binary_devices => thermoelectric_generator}/circulator.dm (100%) rename code/modules/{power => thermoelectric_generator}/generator.dm (100%) create mode 100644 code/modules/thermoelectric_generator/teg_circuits.dm diff --git a/code/game/objects/items/circuitboards/machinery/power.dm b/code/game/objects/items/circuitboards/machinery/power.dm index b580ee6ff641..135a1493f670 100644 --- a/code/game/objects/items/circuitboards/machinery/power.dm +++ b/code/game/objects/items/circuitboards/machinery/power.dm @@ -99,35 +99,6 @@ /obj/item/stock_parts/keyboard = 1, /obj/item/stock_parts/power/apc/buildable = 1 ) - -/obj/item/stock_parts/circuitboard/teg_turbine - name = "circuitboard (thermoelectric generator turbine)" - build_path = /obj/machinery/atmospherics/binary/circulator - board_type = "machine" - origin_tech = @'{"powerstorage":4,"engineering":4}' - req_components = list( - /obj/item/stock_parts/manipulator = 3, - /obj/item/stock_parts/matter_bin = 3 - ) - additional_spawn_components = list( - /obj/item/stock_parts/power/apc/buildable = 1 - ) - -/obj/item/stock_parts/circuitboard/teg_turbine/motor - name = "circuitboard (thermoelectric generator motor)" - build_path = /obj/machinery/generator - board_type = "machine" - origin_tech = @'{"powerstorage":4,"engineering":4}' - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/capacitor = 4 - ) - additional_spawn_components = list( - /obj/item/stock_parts/console_screen = 1, - /obj/item/stock_parts/keyboard = 1, - /obj/item/stock_parts/power/apc/buildable = 1 - ) - /obj/item/stock_parts/circuitboard/breaker name = "circuitboard (breaker box)" build_path = /obj/machinery/power/breakerbox diff --git a/code/modules/atmospherics/components/binary_devices/circulator.dm b/code/modules/thermoelectric_generator/circulator.dm similarity index 100% rename from code/modules/atmospherics/components/binary_devices/circulator.dm rename to code/modules/thermoelectric_generator/circulator.dm diff --git a/code/modules/power/generator.dm b/code/modules/thermoelectric_generator/generator.dm similarity index 100% rename from code/modules/power/generator.dm rename to code/modules/thermoelectric_generator/generator.dm diff --git a/code/modules/thermoelectric_generator/teg_circuits.dm b/code/modules/thermoelectric_generator/teg_circuits.dm new file mode 100644 index 000000000000..d3c03c163d02 --- /dev/null +++ b/code/modules/thermoelectric_generator/teg_circuits.dm @@ -0,0 +1,27 @@ +/obj/item/stock_parts/circuitboard/teg_turbine + name = "circuitboard (thermoelectric generator turbine)" + build_path = /obj/machinery/atmospherics/binary/circulator + board_type = "machine" + origin_tech = @'{"powerstorage":4,"engineering":4}' + req_components = list( + /obj/item/stock_parts/manipulator = 3, + /obj/item/stock_parts/matter_bin = 3 + ) + additional_spawn_components = list( + /obj/item/stock_parts/power/apc/buildable = 1 + ) + +/obj/item/stock_parts/circuitboard/teg_turbine/motor + name = "circuitboard (thermoelectric generator motor)" + build_path = /obj/machinery/generator + board_type = "machine" + origin_tech = @'{"powerstorage":4,"engineering":4}' + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/capacitor = 4 + ) + additional_spawn_components = list( + /obj/item/stock_parts/console_screen = 1, + /obj/item/stock_parts/keyboard = 1, + /obj/item/stock_parts/power/apc/buildable = 1 + ) \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index 0a8163cbf910..9b3c0b159acf 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1797,7 +1797,6 @@ #include "code\modules\atmospherics\components\tvalve.dm" #include "code\modules\atmospherics\components\valve.dm" #include "code\modules\atmospherics\components\binary_devices\binary_atmos_base.dm" -#include "code\modules\atmospherics\components\binary_devices\circulator.dm" #include "code\modules\atmospherics\components\binary_devices\oxyregenerator.dm" #include "code\modules\atmospherics\components\binary_devices\passive_gate.dm" #include "code\modules\atmospherics\components\binary_devices\pipeturbine.dm" @@ -3453,7 +3452,6 @@ #include "code\modules\power\batteryrack.dm" #include "code\modules\power\breaker_box.dm" #include "code\modules\power\debug_items.dm" -#include "code\modules\power\generator.dm" #include "code\modules\power\port_gen.dm" #include "code\modules\power\power.dm" #include "code\modules\power\powernet.dm" @@ -3911,6 +3909,9 @@ #include "code\modules\synthesized_instruments\real_instruments\Synthesizer\synthesizer.dm" #include "code\modules\synthesized_instruments\real_instruments\Trumpet\trumpet.dm" #include "code\modules\synthesized_instruments\real_instruments\Violin\violin.dm" +#include "code\modules\thermoelectric_generator\circulator.dm" +#include "code\modules\thermoelectric_generator\generator.dm" +#include "code\modules\thermoelectric_generator\teg_circuits.dm" #include "code\modules\tools\tool.dm" #include "code\modules\tools\tool_serde.dm" #include "code\modules\tools\archetypes\_tool_defines.dm" From ed6dd873c6c429690231f163bd5bfb0ce34a1d84 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 10 Apr 2026 13:34:38 -0400 Subject: [PATCH 1201/1331] Fix missing return statements in modpack-friendly getters --- code/game/objects/items/weapons/secrets_disk.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/game/objects/items/weapons/secrets_disk.dm b/code/game/objects/items/weapons/secrets_disk.dm index 70a6c4036b17..381dd15a1533 100644 --- a/code/game/objects/items/weapons/secrets_disk.dm +++ b/code/game/objects/items/weapons/secrets_disk.dm @@ -18,6 +18,7 @@ "gamma", "delta", "epsilon", "zeta", "theta", "lambda", "omicron", "sigma", "tau", "upsilon", "omega", "echelon", "prism", "calypso", "bernoulli", "harmony", "nyx", "fresnel" ) + return codenames /obj/item/disk/secret_project/proc/get_secret_project_types() var/static/list/types = list( @@ -41,6 +42,7 @@ "that is extremely powerful", "which is highly efficient", "which is incredibly compact", "created by aliens", "that runs off of an exotic form of matter", "that runs off of hydrogen gas", "that just looks really cool" ) + return descriptors /obj/item/disk/secret_project/Initialize() . = ..() From 599a4c9deda62f7eb191ab5f6974b479e939ed1f Mon Sep 17 00:00:00 2001 From: MelNye Date: Sat, 18 Apr 2026 03:01:30 +0100 Subject: [PATCH 1202/1331] shotgun fix --- code/modules/projectiles/projectile/pellets.dm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/modules/projectiles/projectile/pellets.dm b/code/modules/projectiles/projectile/pellets.dm index c4a244c802ac..64efccc1af85 100644 --- a/code/modules/projectiles/projectile/pellets.dm +++ b/code/modules/projectiles/projectile/pellets.dm @@ -16,6 +16,16 @@ var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance return max(pellets - pellet_loss, 1) +/obj/item/projectile/bullet/pellet/fire(angle, atom/direct_target) + if(direct_target) + for (var/i in 1 to pellets) + var/old_zone = def_zone + def_zone = ran_zone(def_zone, base_spread, direct_target) + direct_target.bullet_act(src, def_zone) + def_zone = old_zone + return + ..() + /obj/item/projectile/bullet/pellet/attack_mob(var/mob/target_mob, var/distance, var/miss_modifier) if (pellets < 0) return 1 From 24eb71b137e8af39e1e7868b0d68138eb14d9949 Mon Sep 17 00:00:00 2001 From: MelNye Date: Fri, 17 Apr 2026 20:01:12 +0100 Subject: [PATCH 1203/1331] MistakeNot's open turf fix --- code/game/turfs/turf.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index cec61c3db3ef..03e9bb6affdb 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -351,7 +351,7 @@ var/old_height = old_turf.get_physical_height() + old_turf.reagents?.total_volume var/current_height = get_physical_height() + reagents?.total_volume if(abs(current_height - old_height) > FLUID_SHALLOW) - if(current_height > old_height) + if(current_height > old_height && !is_open() && !old_turf?.is_open()) // check is_open() due to open turf depth stuff. return 0 if(istype(mover_mob) && MOVING_DELIBERATELY(mover_mob)) to_chat(mover_mob, SPAN_WARNING("You refrain from stepping over the edge; it looks like a steep drop down to \the [src].")) From 58a957b89db2d24f82f9af965a2d960d6c3e15c1 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:30:39 -0400 Subject: [PATCH 1204/1331] Move fusion power into its own module --- code/modules/{power => }/fusion/_setup.dm | 0 .../{power => }/fusion/consoles/_consoles.dm | 0 .../fusion/consoles/core_control.dm | 0 .../fusion/consoles/gyrotron_control.dm | 0 .../fusion/consoles/injector_control.dm | 0 code/modules/{power => }/fusion/core/_core.dm | 0 .../{power => }/fusion/core/core_field.dm | 0 .../fusion/fuel_injector/fuel_injector.dm | 0 .../{power => }/fusion/fusion_circuits.dm | 0 .../fusion/fusion_particle_catcher.dm | 0 .../{power => }/fusion/fusion_reactions.dm | 0 .../{power => }/fusion/gyrotron/gyrotron.dm | 0 .../{power => }/fusion/kinetic_harvester.dm | 0 nebula.dme | 26 +++++++++---------- 14 files changed, 13 insertions(+), 13 deletions(-) rename code/modules/{power => }/fusion/_setup.dm (100%) rename code/modules/{power => }/fusion/consoles/_consoles.dm (100%) rename code/modules/{power => }/fusion/consoles/core_control.dm (100%) rename code/modules/{power => }/fusion/consoles/gyrotron_control.dm (100%) rename code/modules/{power => }/fusion/consoles/injector_control.dm (100%) rename code/modules/{power => }/fusion/core/_core.dm (100%) rename code/modules/{power => }/fusion/core/core_field.dm (100%) rename code/modules/{power => }/fusion/fuel_injector/fuel_injector.dm (100%) rename code/modules/{power => }/fusion/fusion_circuits.dm (100%) rename code/modules/{power => }/fusion/fusion_particle_catcher.dm (100%) rename code/modules/{power => }/fusion/fusion_reactions.dm (100%) rename code/modules/{power => }/fusion/gyrotron/gyrotron.dm (100%) rename code/modules/{power => }/fusion/kinetic_harvester.dm (100%) diff --git a/code/modules/power/fusion/_setup.dm b/code/modules/fusion/_setup.dm similarity index 100% rename from code/modules/power/fusion/_setup.dm rename to code/modules/fusion/_setup.dm diff --git a/code/modules/power/fusion/consoles/_consoles.dm b/code/modules/fusion/consoles/_consoles.dm similarity index 100% rename from code/modules/power/fusion/consoles/_consoles.dm rename to code/modules/fusion/consoles/_consoles.dm diff --git a/code/modules/power/fusion/consoles/core_control.dm b/code/modules/fusion/consoles/core_control.dm similarity index 100% rename from code/modules/power/fusion/consoles/core_control.dm rename to code/modules/fusion/consoles/core_control.dm diff --git a/code/modules/power/fusion/consoles/gyrotron_control.dm b/code/modules/fusion/consoles/gyrotron_control.dm similarity index 100% rename from code/modules/power/fusion/consoles/gyrotron_control.dm rename to code/modules/fusion/consoles/gyrotron_control.dm diff --git a/code/modules/power/fusion/consoles/injector_control.dm b/code/modules/fusion/consoles/injector_control.dm similarity index 100% rename from code/modules/power/fusion/consoles/injector_control.dm rename to code/modules/fusion/consoles/injector_control.dm diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/fusion/core/_core.dm similarity index 100% rename from code/modules/power/fusion/core/_core.dm rename to code/modules/fusion/core/_core.dm diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/fusion/core/core_field.dm similarity index 100% rename from code/modules/power/fusion/core/core_field.dm rename to code/modules/fusion/core/core_field.dm diff --git a/code/modules/power/fusion/fuel_injector/fuel_injector.dm b/code/modules/fusion/fuel_injector/fuel_injector.dm similarity index 100% rename from code/modules/power/fusion/fuel_injector/fuel_injector.dm rename to code/modules/fusion/fuel_injector/fuel_injector.dm diff --git a/code/modules/power/fusion/fusion_circuits.dm b/code/modules/fusion/fusion_circuits.dm similarity index 100% rename from code/modules/power/fusion/fusion_circuits.dm rename to code/modules/fusion/fusion_circuits.dm diff --git a/code/modules/power/fusion/fusion_particle_catcher.dm b/code/modules/fusion/fusion_particle_catcher.dm similarity index 100% rename from code/modules/power/fusion/fusion_particle_catcher.dm rename to code/modules/fusion/fusion_particle_catcher.dm diff --git a/code/modules/power/fusion/fusion_reactions.dm b/code/modules/fusion/fusion_reactions.dm similarity index 100% rename from code/modules/power/fusion/fusion_reactions.dm rename to code/modules/fusion/fusion_reactions.dm diff --git a/code/modules/power/fusion/gyrotron/gyrotron.dm b/code/modules/fusion/gyrotron/gyrotron.dm similarity index 100% rename from code/modules/power/fusion/gyrotron/gyrotron.dm rename to code/modules/fusion/gyrotron/gyrotron.dm diff --git a/code/modules/power/fusion/kinetic_harvester.dm b/code/modules/fusion/kinetic_harvester.dm similarity index 100% rename from code/modules/power/fusion/kinetic_harvester.dm rename to code/modules/fusion/kinetic_harvester.dm diff --git a/nebula.dme b/nebula.dme index aec57b7069ae..b1b6588dddfc 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2478,6 +2478,19 @@ #include "code\modules\food\utensils\utensil_spoon.dm" #include "code\modules\fuel_assembly\fuel_assembly.dm" #include "code\modules\fuel_assembly\fuel_compressor.dm" +#include "code\modules\fusion\_setup.dm" +#include "code\modules\fusion\fusion_circuits.dm" +#include "code\modules\fusion\fusion_particle_catcher.dm" +#include "code\modules\fusion\fusion_reactions.dm" +#include "code\modules\fusion\kinetic_harvester.dm" +#include "code\modules\fusion\consoles\_consoles.dm" +#include "code\modules\fusion\consoles\core_control.dm" +#include "code\modules\fusion\consoles\gyrotron_control.dm" +#include "code\modules\fusion\consoles\injector_control.dm" +#include "code\modules\fusion\core\_core.dm" +#include "code\modules\fusion\core\core_field.dm" +#include "code\modules\fusion\fuel_injector\fuel_injector.dm" +#include "code\modules\fusion\gyrotron\gyrotron.dm" #include "code\modules\games\boardgame.dm" #include "code\modules\games\cardemon.dm" #include "code\modules\games\cards.dm" @@ -3469,19 +3482,6 @@ #include "code\modules\power\cable\heavycable.dm" #include "code\modules\power\cell\_cell.dm" #include "code\modules\power\cell\cell_types.dm" -#include "code\modules\power\fusion\_setup.dm" -#include "code\modules\power\fusion\fusion_circuits.dm" -#include "code\modules\power\fusion\fusion_particle_catcher.dm" -#include "code\modules\power\fusion\fusion_reactions.dm" -#include "code\modules\power\fusion\kinetic_harvester.dm" -#include "code\modules\power\fusion\consoles\_consoles.dm" -#include "code\modules\power\fusion\consoles\core_control.dm" -#include "code\modules\power\fusion\consoles\gyrotron_control.dm" -#include "code\modules\power\fusion\consoles\injector_control.dm" -#include "code\modules\power\fusion\core\_core.dm" -#include "code\modules\power\fusion\core\core_field.dm" -#include "code\modules\power\fusion\fuel_injector\fuel_injector.dm" -#include "code\modules\power\fusion\gyrotron\gyrotron.dm" #include "code\modules\power\singularity\collector.dm" #include "code\modules\power\singularity\containment_field.dm" #include "code\modules\power\singularity\emitter.dm" From 04a85403b2aa7a4c49010cd2bd6bba4f7e00fe8c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Oct 2025 17:16:12 -0400 Subject: [PATCH 1205/1331] Move turbines into their own module --- .../items/circuitboards/machinery/power.dm | 48 ------------------- .../turbine.dm => turbines/largeturbine.dm} | 0 .../smallturbine.dm} | 0 code/modules/turbines/turbine_circuits.dm | 48 +++++++++++++++++++ nebula.dme | 5 +- 5 files changed, 51 insertions(+), 50 deletions(-) rename code/modules/{power/turbine.dm => turbines/largeturbine.dm} (100%) rename code/modules/{atmospherics/components/binary_devices/pipeturbine.dm => turbines/smallturbine.dm} (100%) create mode 100644 code/modules/turbines/turbine_circuits.dm diff --git a/code/game/objects/items/circuitboards/machinery/power.dm b/code/game/objects/items/circuitboards/machinery/power.dm index ff0eafc2d370..d8cdc9faab6c 100644 --- a/code/game/objects/items/circuitboards/machinery/power.dm +++ b/code/game/objects/items/circuitboards/machinery/power.dm @@ -27,54 +27,6 @@ /obj/item/stock_parts/capacitor = 6 ) // The apc part is to supply upkeep power, so it charges the battery instead of draining it. Capacitors make things go faster. -/obj/item/stock_parts/circuitboard/turbine - name = "circuitboard (small turbine)" - build_path = /obj/machinery/atmospherics/pipeturbine - board_type = "machine" - origin_tech = @'{"powerstorage":4,"engineering":4}' - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/matter_bin = 2 - ) - additional_spawn_components = list() - -/obj/item/stock_parts/circuitboard/turbine/motor - name = "circuitboard (small turbine motor)" - build_path = /obj/machinery/turbinemotor - board_type = "machine" - origin_tech = @'{"powerstorage":4,"engineering":4}' - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/capacitor = 4 - ) - -/obj/item/stock_parts/circuitboard/big_turbine - name = "circuitboard (large turbine compressor)" - build_path = /obj/machinery/compressor - board_type = "machine" - origin_tech = @'{"powerstorage":4,"engineering":4}' - req_components = list( - /obj/item/stock_parts/manipulator = 3, - /obj/item/stock_parts/matter_bin = 3 - ) - additional_spawn_components = list( - /obj/item/stock_parts/power/apc/buildable = 1 - ) - -/obj/item/stock_parts/circuitboard/big_turbine/center - name = "circuitboard (large turbine motor)" - build_path = /obj/machinery/turbine - board_type = "machine" - origin_tech = @'{"powerstorage":4,"engineering":4}' - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/capacitor = 4 - ) - additional_spawn_components = list( - /obj/item/stock_parts/console_screen = 1, - /obj/item/stock_parts/keyboard = 1, - /obj/item/stock_parts/power/apc/buildable = 1 - ) /obj/item/stock_parts/circuitboard/breaker name = "circuitboard (breaker box)" build_path = /obj/machinery/power/breakerbox diff --git a/code/modules/power/turbine.dm b/code/modules/turbines/largeturbine.dm similarity index 100% rename from code/modules/power/turbine.dm rename to code/modules/turbines/largeturbine.dm diff --git a/code/modules/atmospherics/components/binary_devices/pipeturbine.dm b/code/modules/turbines/smallturbine.dm similarity index 100% rename from code/modules/atmospherics/components/binary_devices/pipeturbine.dm rename to code/modules/turbines/smallturbine.dm diff --git a/code/modules/turbines/turbine_circuits.dm b/code/modules/turbines/turbine_circuits.dm new file mode 100644 index 000000000000..22875b256666 --- /dev/null +++ b/code/modules/turbines/turbine_circuits.dm @@ -0,0 +1,48 @@ +/obj/item/stock_parts/circuitboard/turbine + name = "circuitboard (small turbine)" + build_path = /obj/machinery/atmospherics/pipeturbine + board_type = "machine" + origin_tech = @'{"powerstorage":4,"engineering":4}' + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/matter_bin = 2 + ) + additional_spawn_components = list() + +/obj/item/stock_parts/circuitboard/turbine/motor + name = "circuitboard (small turbine motor)" + build_path = /obj/machinery/turbinemotor + board_type = "machine" + origin_tech = @'{"powerstorage":4,"engineering":4}' + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/capacitor = 4 + ) + +/obj/item/stock_parts/circuitboard/big_turbine + name = "circuitboard (large turbine compressor)" + build_path = /obj/machinery/compressor + board_type = "machine" + origin_tech = @'{"powerstorage":4,"engineering":4}' + req_components = list( + /obj/item/stock_parts/manipulator = 3, + /obj/item/stock_parts/matter_bin = 3 + ) + additional_spawn_components = list( + /obj/item/stock_parts/power/apc/buildable = 1 + ) + +/obj/item/stock_parts/circuitboard/big_turbine/center + name = "circuitboard (large turbine motor)" + build_path = /obj/machinery/turbine + board_type = "machine" + origin_tech = @'{"powerstorage":4,"engineering":4}' + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/capacitor = 4 + ) + additional_spawn_components = list( + /obj/item/stock_parts/console_screen = 1, + /obj/item/stock_parts/keyboard = 1, + /obj/item/stock_parts/power/apc/buildable = 1 + ) \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index b1b6588dddfc..0d5310bb53d0 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1798,7 +1798,6 @@ #include "code\modules\atmospherics\components\binary_devices\binary_atmos_base.dm" #include "code\modules\atmospherics\components\binary_devices\oxyregenerator.dm" #include "code\modules\atmospherics\components\binary_devices\passive_gate.dm" -#include "code\modules\atmospherics\components\binary_devices\pipeturbine.dm" #include "code\modules\atmospherics\components\binary_devices\pump.dm" #include "code\modules\atmospherics\components\binary_devices\volume_pump.dm" #include "code\modules\atmospherics\components\omni_devices\_omni_extras.dm" @@ -3472,7 +3471,6 @@ #include "code\modules\power\powernet_sensor.dm" #include "code\modules\power\stirling.dm" #include "code\modules\power\terminal.dm" -#include "code\modules\power\turbine.dm" #include "code\modules\power\apc\_apc.dm" #include "code\modules\power\apc\apc_frame.dm" #include "code\modules\power\apc\apc_presets.dm" @@ -3941,6 +3939,9 @@ #include "code\modules\tools\subtypes\shovel.dm" #include "code\modules\tools\subtypes\xenoarchaeology_picks.dm" #include "code\modules\tooltip\tooltip.dm" +#include "code\modules\turbines\largeturbine.dm" +#include "code\modules\turbines\smallturbine.dm" +#include "code\modules\turbines\turbine_circuits.dm" #include "code\modules\turbolift\turbolift.dm" #include "code\modules\turbolift\turbolift_areas.dm" #include "code\modules\turbolift\turbolift_console.dm" From 10ee122ead8ce4d903fcd9c5a502e75c2fec07a9 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:53:41 -0400 Subject: [PATCH 1206/1331] Move stirling generator to binary devices folder --- .../components/binary_devices}/stirling.dm | 0 nebula.dme | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename code/modules/{power => atmospherics/components/binary_devices}/stirling.dm (100%) diff --git a/code/modules/power/stirling.dm b/code/modules/atmospherics/components/binary_devices/stirling.dm similarity index 100% rename from code/modules/power/stirling.dm rename to code/modules/atmospherics/components/binary_devices/stirling.dm diff --git a/nebula.dme b/nebula.dme index b1b6588dddfc..20bed13f4c5f 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1800,6 +1800,7 @@ #include "code\modules\atmospherics\components\binary_devices\passive_gate.dm" #include "code\modules\atmospherics\components\binary_devices\pipeturbine.dm" #include "code\modules\atmospherics\components\binary_devices\pump.dm" +#include "code\modules\atmospherics\components\binary_devices\stirling.dm" #include "code\modules\atmospherics\components\binary_devices\volume_pump.dm" #include "code\modules\atmospherics\components\omni_devices\_omni_extras.dm" #include "code\modules\atmospherics\components\omni_devices\filter.dm" @@ -3470,7 +3471,6 @@ #include "code\modules\power\power.dm" #include "code\modules\power\powernet.dm" #include "code\modules\power\powernet_sensor.dm" -#include "code\modules\power\stirling.dm" #include "code\modules\power\terminal.dm" #include "code\modules\power\turbine.dm" #include "code\modules\power\apc\_apc.dm" From 948ec7654206921198dc4172df1bf141e424bf48 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 18:59:08 -0400 Subject: [PATCH 1207/1331] Fix incorrect organ typepaths --- code/modules/organs/internal/eyes.dm | 2 +- mods/species/ascent/datum/species_bodytypes.dm | 2 +- mods/species/serpentid/mobs/bodyparts_serpentid.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index 27d1be46f409..1c68dc0c422c 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -39,7 +39,7 @@ verbs |= /obj/item/organ/internal/eyes/proc/change_eye_color_verb verbs |= /obj/item/organ/internal/eyes/proc/toggle_eye_glow -/obj/item/organ/external/eyes/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE) +/obj/item/organ/internal/eyes/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE) . = ..() if(. && owner && !skip_owner_update) owner.update_eyes() diff --git a/mods/species/ascent/datum/species_bodytypes.dm b/mods/species/ascent/datum/species_bodytypes.dm index 87a8462b0f7d..08c43405e51d 100644 --- a/mods/species/ascent/datum/species_bodytypes.dm +++ b/mods/species/ascent/datum/species_bodytypes.dm @@ -7,7 +7,7 @@ has_limbs = list( BP_CHEST = list("path" = /obj/item/organ/external/chest/insectoid), BP_GROIN = list("path" = /obj/item/organ/external/groin/insectoid/mantid), - BP_HEAD = list("path" = /obj/item/organ/external/head/insectoid), + BP_HEAD = list("path" = /obj/item/organ/external/head/insectoid/mantid), BP_L_ARM = list("path" = /obj/item/organ/external/arm/insectoid), BP_L_HAND = list("path" = /obj/item/organ/external/hand/insectoid), BP_R_ARM = list("path" = /obj/item/organ/external/arm/right/insectoid), diff --git a/mods/species/serpentid/mobs/bodyparts_serpentid.dm b/mods/species/serpentid/mobs/bodyparts_serpentid.dm index 636ac9fdbd0d..103fbdbb6eb6 100644 --- a/mods/species/serpentid/mobs/bodyparts_serpentid.dm +++ b/mods/species/serpentid/mobs/bodyparts_serpentid.dm @@ -43,7 +43,7 @@ /obj/item/organ/internal/eyes/insectoid/serpentid/proc/remove_shield() owner.clear_fullscreen("eyeshield") -/obj/item/organ/internal/eyes/serpentid/Initialize() +/obj/item/organ/internal/eyes/insectoid/serpentid/Initialize() . = ..() if(owner) color = owner.get_eye_colour() From d168ad74c998ef49bc135225f5cf1d09174d2a3f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:01:47 -0400 Subject: [PATCH 1208/1331] Remove unused and defunct code --- code/_helpers/game.dm | 21 ------ code/datums/position_point_vector.dm | 24 ------- code/game/machinery/computer/shuttle.dm | 70 ------------------- code/game/objects/effects/force_portal.dm | 68 ------------------ code/game/objects/items/devices/uplink.dm | 3 - code/game/objects/items/weapons/melee/misc.dm | 12 ---- code/game/turfs/unsimulated/walls.dm | 17 ----- code/modules/merchant/merchant_guns.dm | 2 +- code/modules/projectiles/gun.dm | 2 +- .../projectiles/guns/energy/lasertag.dm | 8 +-- code/modules/projectiles/projectile/beams.dm | 26 ++----- code/modules/projectiles/projectile/energy.dm | 10 --- code/modules/projectiles/projectile/force.dm | 3 - .../modules/projectiles/projectile/special.dm | 31 -------- code/modules/shield_generators/shield.dm | 4 -- mods/gamemodes/cult/_cult.dme | 1 - mods/gamemodes/cult/cultify/de-cultify.dm | 7 -- nebula.dme | 2 - 18 files changed, 12 insertions(+), 299 deletions(-) delete mode 100644 code/game/machinery/computer/shuttle.dm delete mode 100644 code/game/objects/effects/force_portal.dm delete mode 100644 mods/gamemodes/cult/cultify/de-cultify.dm diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 94a6ab1bf7d7..2f2a441bafd9 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -271,27 +271,6 @@ return M return null -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ - var/power_x, var/power_y, var/dest_x, var/dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - /proc/MixColors(const/list/colors) switch(length(colors)) if(1) diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm index e33f91b83dd7..226bcfa37898 100644 --- a/code/datums/position_point_vector.dm +++ b/code/datums/position_point_vector.dm @@ -190,27 +190,3 @@ /datum/point/vector/proc/on_z_change() return - -/datum/point/vector/processed //pixel_speed is per decisecond. - var/last_process = 0 - var/last_move = 0 - var/paused = FALSE - -/datum/point/vector/processed/Destroy() - STOP_PROCESSING(SSprojectiles, src) - return ..() - -/datum/point/vector/processed/proc/start() - last_process = world.time - last_move = world.time - START_PROCESSING(SSprojectiles, src) - -/datum/point/vector/processed/Process() - if(paused) - last_move += world.time - last_process - last_process = world.time - return - var/needed_time = world.time - last_move - last_process = world.time - last_move = world.time - increment(needed_time / SSprojectiles.wait) diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm deleted file mode 100644 index a63d6d3b2ead..000000000000 --- a/code/game/machinery/computer/shuttle.dm +++ /dev/null @@ -1,70 +0,0 @@ -/obj/machinery/computer/shuttle - name = "Shuttle" - desc = "For shuttle control." - icon_keyboard = "tech_key" - icon_screen = "shuttle" - light_color = "#00ffff" - construct_state = null - var/auth_need = 3.0 - var/list/authorized = list( ) - - -/obj/machinery/computer/shuttle/attackby(var/obj/item/used_item, var/mob/user) - if(stat & (BROKEN|NOPOWER)) return TRUE - - var/datum/evacuation_controller/shuttle/evac_control = SSevac.evacuation_controller - if(!istype(evac_control)) - to_chat(user, "This console should not be in use on this map. Please report this to a developer.") - return TRUE - - if(!istype(used_item, /obj/item/card)) // don't try to get an ID card if we're an emag - used_item = used_item.GetIdCard() // handles stored IDs in modcomps and similar - - if ((!istype(used_item, /obj/item/card) || evac_control.has_evacuated() || !user)) - return FALSE - - if (istype(used_item, /obj/item/card/id)) - var/obj/item/card/id/id_card = used_item - if(!LAZYISIN(id_card.access, access_bridge)) //doesn't have this access - to_chat(user, "The access level of [id_card.registered_name]\'s card is not high enough.") - return TRUE - - var/choice = alert(user, "Would you like to (un)authorize a shortened launch time? [auth_need - authorized.len] authorization\s are still needed. Use abort to cancel all authorizations.", "Shuttle Launch", "Authorize", "Repeal", "Abort") - if(evac_control.is_prepared() && user.get_active_held_item() != used_item) - return TRUE - switch(choice) - if("Authorize") - src.authorized -= id_card.registered_name - src.authorized += id_card.registered_name - if (src.auth_need - src.authorized.len > 0) - message_admins("[key_name_admin(user)] has authorized early shuttle launch") - log_game("[user.ckey] has authorized early shuttle launch") - to_world("Alert: [auth_need - authorized.len] authorizations needed until shuttle is launched early") - else - message_admins("[key_name_admin(user)] has launched the shuttle") - log_game("[user.ckey] has launched the shuttle early") - to_world("Alert: Shuttle launch time shortened to 10 seconds!") - evac_control.set_launch_time(world.time+100) - //src.authorized = null - qdel(src.authorized) - src.authorized = list( ) - - if("Repeal") - src.authorized -= id_card.registered_name - to_world("Alert: [auth_need - authorized.len] authorizations needed until shuttle is launched early") - - if("Abort") - to_world("All authorizations to shortening time for shuttle launch have been revoked!") - src.authorized.len = 0 - src.authorized = list( ) - return TRUE - - else if (istype(used_item, /obj/item/card/emag) && !emagged) - var/choice = alert(user, "Would you like to launch the shuttle?","Shuttle control", "Launch", "Cancel") - - if(!emagged && !evac_control.is_prepared() && user.get_active_held_item() == used_item && choice == "Launch") - to_world("Alert: Shuttle launch time shortened to 10 seconds!") - evac_control.set_launch_time(world.time+100) - emagged = 1 - return TRUE - return FALSE diff --git a/code/game/objects/effects/force_portal.dm b/code/game/objects/effects/force_portal.dm deleted file mode 100644 index dd0020bcd149..000000000000 --- a/code/game/objects/effects/force_portal.dm +++ /dev/null @@ -1,68 +0,0 @@ -/obj/effect/force_portal - name = "portal" - desc = "Like looking into a mirror." - icon = 'icons/effects/portal.dmi' - icon_state = "portal" - blend_mode = BLEND_SUBTRACT - density = TRUE - anchored = TRUE - var/boom_time = 1 - -/obj/effect/force_portal/Initialize() - . = ..() - boom_time = world.time + 30 SECONDS - START_PROCESSING(SSobj, src) - -/obj/effect/force_portal/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/effect/force_portal/Process() - if(boom_time && boom_time < world.time) - boom() - boom_time = 0 - -/obj/effect/force_portal/proc/boom() - set waitfor = FALSE - var/turf/my_turf = get_turf(src) - if(!my_turf) - qdel(src) - return - var/list/possible_turfs = getcircle(my_turf, 5) - while(length(contents)) - var/target = pick(possible_turfs) - possible_turfs -= target - var/atom/movable/picked = pick(contents) - picked.dropInto(loc) - if(istype(picked, /obj/item/projectile)) - var/obj/item/projectile/P = picked - P.launch(target) - playsound(src, P.fire_sound ? P.fire_sound : 'sound/effects/teleport.ogg', 60, 1) - else - picked.throw_at(target, 5, 10) - playsound(src,'sound/effects/teleport.ogg',60,1) - sleep(1) - qdel(src) - -/obj/effect/force_portal/onDropInto(var/atom/movable/AM) - boom_time -= 1 SECOND - src.visible_message("\The [src] sucks in \the [AM]!") - if(!ismob(AM)) - var/obj/O = AM - if(O.w_class <= ITEM_SIZE_SMALL) - return //Dont spam for small stuff - playsound(src,'sound/effects/teleport.ogg',40,1) - return - -/obj/effect/force_portal/Bumped(var/atom/movable/AM) - AM.dropInto(src) - -/obj/effect/force_portal/bullet_act(var/obj/item/projectile/P) - var/atom/movable/AM = new P.type() - if(istype(P, /obj/item/projectile/bullet/pellet)) - var/obj/item/projectile/bullet/pellet/old_pellet = P - var/obj/item/projectile/bullet/pellet/new_pellet = AM - new_pellet.pellets = old_pellet.pellets - AM.dropInto(src) - P.forceMove(null) - qdel(P) \ No newline at end of file diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm index 2d54bf5b1bfa..2bdd134af9b9 100644 --- a/code/game/objects/items/devices/uplink.dm +++ b/code/game/objects/items/devices/uplink.dm @@ -243,6 +243,3 @@ if(!hidden_uplink && user.mind) hidden_uplink = new(src, user.mind, DEFAULT_TELECRYSTAL_AMOUNT) . = ..() - -/obj/item/uplink/contained/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/uistate = global.contained_topic_state) - return ..() diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/weapons/melee/misc.dm index a1b4938bacf0..c58582c527fb 100644 --- a/code/game/objects/items/weapons/melee/misc.dm +++ b/code/game/objects/items/weapons/melee/misc.dm @@ -13,18 +13,6 @@ material = /decl/material/solid/organic/leather _base_attack_force = 10 -/obj/item/whip/abyssal - name = "abyssal whip" - desc = "A weapon from the abyss. Requires 70 attack to wield." - icon_state = "whip" - item_state = "whip" - obj_flags = OBJ_FLAG_CONDUCTIBLE - slot_flags = SLOT_LOWER_BODY - w_class = ITEM_SIZE_NORMAL - origin_tech = @'{"combat":4}' - attack_verb = list("flicked", "whipped", "lashed") - _base_attack_force = 16 //max hit with 60 strength and no equipment. Duel Arena no No forfeit - Snapshot - /obj/item/whip/tail name = "drake's tail" desc = "The tail of a large scaled creature, obviously evolved as some kind of whipping weapon. It's razor sharp and incredibly tough, though relatively lightweight." diff --git a/code/game/turfs/unsimulated/walls.dm b/code/game/turfs/unsimulated/walls.dm index 24193a5e8059..58def3c3da51 100644 --- a/code/game/turfs/unsimulated/walls.dm +++ b/code/game/turfs/unsimulated/walls.dm @@ -6,23 +6,6 @@ density = TRUE turf_flags = TURF_IS_HOLOMAP_OBSTACLE -/turf/unsimulated/wall/fakeglass - name = "window" - icon_state = "fakewindows" - opacity = FALSE - -/turf/unsimulated/wall/fakeglass/alt - icon_state = "fakewindows2" - -/turf/unsimulated/wall/other - icon_state = "r_wall" - -/turf/unsimulated/wall/cult - name = "cult wall" - desc = "Hideous images dance beneath the surface." - icon = 'icons/turf/walls/cult.dmi' - icon_state = "preview" - /turf/unsimulated/wall/airlock name = "Facility Access" desc = "A secure airlock. Doesn't look like you can get through easily." diff --git a/code/modules/merchant/merchant_guns.dm b/code/modules/merchant/merchant_guns.dm index 939a570aab1f..35fd816f649c 100644 --- a/code/modules/merchant/merchant_guns.dm +++ b/code/modules/merchant/merchant_guns.dm @@ -8,7 +8,7 @@ desc = "This carbine works just as well as a normal carbine. Most of the time." //removed reference to Dogan, since only the merchant is likely to know who that is. /obj/item/gun/energy/laser/dogan/consume_next_projectile() - projectile_type = pick(/obj/item/projectile/beam/midlaser, /obj/item/projectile/beam/lastertag/red, /obj/item/projectile/beam) + projectile_type = pick(/obj/item/projectile/beam/midlaser, /obj/item/projectile/beam/lasertag/red, /obj/item/projectile/beam) return ..() /obj/item/gun/projectile/automatic/smg/usi diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 2e1513d80610..3e61a8a50011 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -579,7 +579,7 @@ playsound(user, shot_sound, 10, 1) else playsound(user, shot_sound, 50, 1) - if(istype(in_chamber, /obj/item/projectile/beam/lastertag)) + if(istype(in_chamber, /obj/item/projectile/beam/lasertag)) user.show_message("You feel rather silly, trying to commit suicide with a toy.") mouthshoot = 0 return diff --git a/code/modules/projectiles/guns/energy/lasertag.dm b/code/modules/projectiles/guns/energy/lasertag.dm index fd6af12baa06..d5214a044ac6 100644 --- a/code/modules/projectiles/guns/energy/lasertag.dm +++ b/code/modules/projectiles/guns/energy/lasertag.dm @@ -7,21 +7,21 @@ origin_tech = @'{"combat":1,"magnets":2}' self_recharge = 1 material = /decl/material/solid/metal/steel - projectile_type = /obj/item/projectile/beam/lastertag/blue + projectile_type = /obj/item/projectile/beam/lasertag/omni var/required_vest /obj/item/gun/energy/lasertag/special_check(var/mob/living/human/M) - if(ishuman(M) && !istype(M.get_equipped_item(slot_wear_suit_str), required_vest)) + if(ishuman(M) && required_vest && !istype(M.get_equipped_item(slot_wear_suit_str), required_vest)) to_chat(M, SPAN_WARNING("You need to be wearing your laser tag vest!")) return FALSE return ..() /obj/item/gun/energy/lasertag/blue color = COLOR_SKY_BLUE - projectile_type = /obj/item/projectile/beam/lastertag/blue + projectile_type = /obj/item/projectile/beam/lasertag/blue required_vest = /obj/item/clothing/suit/bluetag /obj/item/gun/energy/lasertag/red color = COLOR_RED - projectile_type = /obj/item/projectile/beam/lastertag/red + projectile_type = /obj/item/projectile/beam/lasertag/red required_vest = /obj/item/clothing/suit/redtag diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 1c8b74eeb566..191fcd8a80d0 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -132,16 +132,6 @@ tracer_type = /obj/effect/projectile/tracer/pulse impact_type = /obj/effect/projectile/impact/pulse -/obj/item/projectile/beam/pulse/destroy - name = "destroyer pulse" - damage = 100 //badmins be badmins I don't give a fuck - armor_penetration = 100 - -/obj/item/projectile/beam/pulse/destroy/on_hit(var/atom/target, var/blocked = 0) - if(isturf(target)) - target.explosion_act(2) - ..() - /obj/item/projectile/beam/emitter name = "emitter beam" icon_state = "emitter" @@ -152,7 +142,7 @@ tracer_type = /obj/effect/projectile/tracer/emitter impact_type = /obj/effect/projectile/impact/emitter -/obj/item/projectile/beam/lastertag/blue +/obj/item/projectile/beam/lasertag/blue name = "lasertag beam" icon_state = "bluelaser" pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GLASS | PASS_FLAG_GRILLE @@ -164,14 +154,14 @@ tracer_type = /obj/effect/projectile/tracer/laser/blue impact_type = /obj/effect/projectile/impact/laser/blue -/obj/item/projectile/beam/lastertag/blue/on_hit(var/atom/target, var/blocked = 0) +/obj/item/projectile/beam/lasertag/blue/on_hit(var/atom/target, var/blocked = 0) if(ishuman(target)) var/mob/living/human/M = target if(istype(M.get_equipped_item(slot_wear_suit_str), /obj/item/clothing/suit/redtag)) SET_STATUS_MAX(M, STAT_WEAK, 5) return 1 -/obj/item/projectile/beam/lastertag/red +/obj/item/projectile/beam/lasertag/red name = "lasertag beam" icon_state = "laser" pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GLASS | PASS_FLAG_GRILLE @@ -179,14 +169,14 @@ no_attack_log = 1 atom_damage_type = BURN -/obj/item/projectile/beam/lastertag/red/on_hit(var/atom/target, var/blocked = 0) +/obj/item/projectile/beam/lasertag/red/on_hit(var/atom/target, var/blocked = 0) if(ishuman(target)) var/mob/living/human/M = target if(istype(M.get_equipped_item(slot_wear_suit_str), /obj/item/clothing/suit/bluetag)) SET_STATUS_MAX(M, STAT_WEAK, 5) return 1 -/obj/item/projectile/beam/lastertag/omni//A laser tag bolt that stuns EVERYONE +/obj/item/projectile/beam/lasertag/omni//A laser tag bolt that stuns EVERYONE name = "lasertag beam" icon_state = "omnilaser" pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GLASS | PASS_FLAG_GRILLE @@ -197,7 +187,7 @@ tracer_type = /obj/effect/projectile/tracer/cult impact_type = /obj/effect/projectile/impact/cult -/obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0) +/obj/item/projectile/beam/lasertag/omni/on_hit(var/atom/target, var/blocked = 0) if(ishuman(target)) var/mob/living/human/M = target var/obj/item/suit = M.get_equipped_item(slot_wear_suit_str) @@ -246,10 +236,6 @@ atom_damage_type = ELECTROCUTE fire_sound='sound/weapons/pulse.ogg' -/obj/item/projectile/beam/stun/shock/heavy - name = "heavy shock beam" - damage = 30 - /obj/item/projectile/beam/plasmacutter name = "plasma arc" icon_state = "omnilaser" diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index e08ffc8d1f01..fa8271f37608 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -96,9 +96,6 @@ eyeblur = 1//Some feedback that you've been hit step_delay = 0.7 -/obj/item/projectile/energy/electrode/green - icon_state = "spark_green" - /obj/item/projectile/energy/electrode/stunshot agony = 80 damage = 3 @@ -133,13 +130,6 @@ damage = 20 agony = 60 -/obj/item/projectile/energy/neurotoxin - name = "neuro" - icon_state = "neurotoxin" - damage = 5 - atom_damage_type = TOX - weaken = 5 - /obj/item/projectile/energy/radiation name = "radiation bolt" icon_state = "energy" diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm index e0b8ea2745f9..0ef2ae15da93 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/force.dm @@ -8,9 +8,6 @@ impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser hitsound_non_mob = 'sound/weapons/searwall.ogg' -/obj/item/projectile/forcebolt/strong - name = "force bolt" - /obj/item/projectile/forcebolt/on_hit(var/atom/movable/target, var/blocked = 0) if(istype(target) && isturf(target.loc)) var/throwdir = get_dir(firer,target) diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index d7ee296a8f14..bc400d016091 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -63,28 +63,6 @@ M.bodytemperature = firing_temperature return 1 -/obj/item/projectile/meteor - name = "meteor" - icon = 'icons/obj/meteor.dmi' - icon_state = "smallf" - damage = 0 - atom_damage_type = BRUTE - nodamage = 1 - -/obj/item/projectile/meteor/Bump(var/atom/A, forced=0) - if(!istype(A)) - return - if(A == firer) - forceMove(A.loc) - return - A.explosion_act(2) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - for(var/mob/M in range(10, src)) - if(!M.stat && !isAI(M)) - shake_camera(M, 3, 1) - qdel(src) - return TRUE - /obj/item/projectile/energy/floramut name = "alpha somatoray" icon_state = "energy" @@ -148,15 +126,6 @@ else M.show_message(SPAN_NOTICE("The radiation beam dissipates harmlessly through your body.")) - -/obj/item/projectile/beam/mindflayer - name = "flayer ray" - -/obj/item/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) - if(ishuman(target)) - var/mob/living/human/M = target - ADJ_STATUS(M, STAT_CONFUSE, rand(5,8)) - /obj/item/projectile/chameleon name = "bullet" icon_state = "bullet" diff --git a/code/modules/shield_generators/shield.dm b/code/modules/shield_generators/shield.dm index 5ad2f51439df..8a894621b40a 100644 --- a/code/modules/shield_generators/shield.dm +++ b/code/modules/shield_generators/shield.dm @@ -299,10 +299,6 @@ /obj/item/projectile/beam/can_pass_shield(var/obj/machinery/shield_generator/gen) return !gen.check_flag(MODEFLAG_PHOTONIC) -// Beams -/obj/item/projectile/ship_munition/energy/can_pass_shield(var/obj/machinery/shield_generator/gen) - return !gen.check_flag(MODEFLAG_PHOTONIC) - // Shield on-impact logic here. This is called only if the object is actually blocked by the field (can_pass_shield applies first) /atom/movable/proc/shield_impact(var/obj/effect/shield/S) return diff --git a/mods/gamemodes/cult/_cult.dme b/mods/gamemodes/cult/_cult.dme index 88b4aa4b8af3..50cb6db01f6d 100644 --- a/mods/gamemodes/cult/_cult.dme +++ b/mods/gamemodes/cult/_cult.dme @@ -25,7 +25,6 @@ #include "abilities\construct.dm" #include "abilities\harvest.dm" #include "abilities\shade.dm" -#include "cultify\de-cultify.dm" #include "cultify\defile.dm" #include "cultify\mob.dm" #include "cultify\turf.dm" diff --git a/mods/gamemodes/cult/cultify/de-cultify.dm b/mods/gamemodes/cult/cultify/de-cultify.dm deleted file mode 100644 index 32c0738df02f..000000000000 --- a/mods/gamemodes/cult/cultify/de-cultify.dm +++ /dev/null @@ -1,7 +0,0 @@ -/turf/unsimulated/wall/cult/nullrod_act(mob/user, obj/item/nullrod/rod) - user.visible_message( - SPAN_NOTICE("\The [user] touches \the [src] with \the [rod], and it shifts."), - SPAN_NOTICE("You touch \the [src] with \the [rod], and it shifts.") - ) - ChangeTurf(/turf/unsimulated/wall) - return TRUE \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index b1b6588dddfc..8ac7cad0bcbb 100644 --- a/nebula.dme +++ b/nebula.dme @@ -940,7 +940,6 @@ #include "code\game\machinery\computer\Operating.dm" #include "code\game\machinery\computer\prisoner.dm" #include "code\game\machinery\computer\robot.dm" -#include "code\game\machinery\computer\shuttle.dm" #include "code\game\machinery\computer\station_alert.dm" #include "code\game\machinery\doors\_door.dm" #include "code\game\machinery\doors\airlock.dm" @@ -1029,7 +1028,6 @@ #include "code\game\objects\effects\fadeout.dm" #include "code\game\objects\effects\fake_fire.dm" #include "code\game\objects\effects\footprints.dm" -#include "code\game\objects\effects\force_portal.dm" #include "code\game\objects\effects\gateway.dm" #include "code\game\objects\effects\gibspawner.dm" #include "code\game\objects\effects\item_pickup_ghost.dm" From caf24faee1940fa829e3647ffe882cbe290af3ff Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:02:22 -0400 Subject: [PATCH 1209/1331] Make tank fragmentation use big tank fragments --- code/game/objects/items/weapons/tanks/tanks.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 9af831affc7d..73ae28e1dcc2 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -436,7 +436,7 @@ var/global/list/global/tank_gauge_cache = list() ) var/num_fragments = round(rand(8,10) * sqrt(strength * mult)) - fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 7,/obj/item/projectile/bullet/pellet/fragment/tank = 2,/obj/item/projectile/bullet/pellet/fragment/strong = 1)) + fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 7, /obj/item/projectile/bullet/pellet/fragment/tank = 2, /obj/item/projectile/bullet/pellet/fragment/tank/big = 1)) if(istype(loc, /obj/item/transfer_valve)) var/obj/item/transfer_valve/TTV = loc @@ -466,7 +466,7 @@ var/global/list/global/tank_gauge_cache = list() var/mult = (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles var/num_fragments = round(rand(6,8) * sqrt(strength * mult)) //Less chunks, but bigger - fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 1,/obj/item/projectile/bullet/pellet/fragment/tank = 5,/obj/item/projectile/bullet/pellet/fragment/strong = 4)) + fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 1,/obj/item/projectile/bullet/pellet/fragment/tank = 5, /obj/item/projectile/bullet/pellet/fragment/tank/big = 4)) if(istype(loc, /obj/item/transfer_valve)) var/obj/item/transfer_valve/TTV = loc From 78fbcc7c29dc5fc3422252460b0faf7a4370773a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:02:34 -0400 Subject: [PATCH 1210/1331] Give ninja rigsuit ninja modules --- mods/gamemodes/ninja/objects/rigsuit.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/gamemodes/ninja/objects/rigsuit.dm b/mods/gamemodes/ninja/objects/rigsuit.dm index 7a49651656a8..9417de4059af 100644 --- a/mods/gamemodes/ninja/objects/rigsuit.dm +++ b/mods/gamemodes/ninja/objects/rigsuit.dm @@ -27,11 +27,11 @@ initial_modules = list( /obj/item/rig_module/teleporter, /obj/item/rig_module/stealth_field, - /obj/item/rig_module/mounted/energy_blade, + /obj/item/rig_module/mounted/energy_blade/ninja, /obj/item/rig_module/vision, /obj/item/rig_module/voice, /obj/item/rig_module/fabricator/energy_net, - /obj/item/rig_module/chem_dispenser/combat, + /obj/item/rig_module/chem_dispenser/ninja, /obj/item/rig_module/grenade_launcher/ninja, /obj/item/rig_module/ai_container, /obj/item/rig_module/power_sink, From dd74bda1c5830a2989549b6a0873ea3461df6a4d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:02:48 -0400 Subject: [PATCH 1211/1331] Fix oak floor tile recipes --- .../modules/crafting/stack_recipes/recipes_stacks.dm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/code/modules/crafting/stack_recipes/recipes_stacks.dm b/code/modules/crafting/stack_recipes/recipes_stacks.dm index 5b45b2c90f42..a37f344a89f1 100644 --- a/code/modules/crafting/stack_recipes/recipes_stacks.dm +++ b/code/modules/crafting/stack_recipes/recipes_stacks.dm @@ -6,7 +6,10 @@ category = "tiling" /decl/stack_recipe/tile/wood - result_type = /obj/item/stack/tile/wood + abstract_type = /decl/stack_recipe/tile/wood + +/decl/stack_recipe/tile/wood/oak + result_type = /obj/item/stack/tile/wood/oak required_material = /decl/material/solid/organic/wood/oak /decl/stack_recipe/tile/wood/mahogany @@ -26,9 +29,14 @@ result_type = /obj/item/stack/tile/wood/walnut required_material = /decl/material/solid/organic/wood/walnut -/decl/stack_recipe/tile/wood/mahogany/rough +/decl/stack_recipe/tile/wood/oak/rough crafting_extra_cost_factor = 2 // wasteful but easy difficulty = MAT_VALUE_EASY_DIY + result_type = /obj/item/stack/tile/wood/rough/oak + +/decl/stack_recipe/tile/wood/mahogany/rough + crafting_extra_cost_factor = 2 + difficulty = MAT_VALUE_EASY_DIY result_type = /obj/item/stack/tile/wood/rough/mahogany /decl/stack_recipe/tile/wood/maple/rough From ec0f4694b66eefd29718aeecbc6f790fe8619a34 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:02:58 -0400 Subject: [PATCH 1212/1331] Fix suspension generator typo --- code/modules/xenoarcheaology/machinery/suspension_generator.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/xenoarcheaology/machinery/suspension_generator.dm b/code/modules/xenoarcheaology/machinery/suspension_generator.dm index 3a2cc181ec82..4895427f4983 100644 --- a/code/modules/xenoarcheaology/machinery/suspension_generator.dm +++ b/code/modules/xenoarcheaology/machinery/suspension_generator.dm @@ -1,6 +1,6 @@ /obj/machinery/suspension_gen name = "suspension field generator" - desc = "Multi-phase mobile suspension field generator MK II \"Steadfast\". It has stubby legs bolted up against it's body for stabilising." + desc = "Multi-phase mobile suspension field generator MK II \"Steadfast\". It has stubby legs bolted up against its body for stabilising." icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "suspension2" density = TRUE From edd3fb6b9b8cde3426c8c7ed98fbccbe817c39ec Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:03:20 -0400 Subject: [PATCH 1213/1331] Add a few typehints --- code/game/atom_material.dm | 2 +- code/modules/mob/mob.dm | 6 +++--- code/modules/organs/organ.dm | 2 +- code/modules/pronouns/_pronouns.dm | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/game/atom_material.dm b/code/game/atom_material.dm index 91bfa983ba44..db77ce9f3109 100644 --- a/code/game/atom_material.dm +++ b/code/game/atom_material.dm @@ -1,6 +1,6 @@ //Returns the material the object is made of, if applicable. //Will we ever need to return more than one value here? Or should we just return the "dominant" material. -/atom/proc/get_material() +/atom/proc/get_material() as /decl/material RETURN_TYPE(/decl/material) return material diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 286b28d1689a..8d3083ecbf9d 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1191,17 +1191,17 @@ var/global/const/ACTION_DANGER_ALL = 2 return FALSE return TRUE -/mob/proc/get_species() +/mob/proc/get_species() as /decl/species RETURN_TYPE(/decl/species) return -/mob/proc/get_bodytype() +/mob/proc/get_bodytype() as /decl/bodytype RETURN_TYPE(/decl/bodytype) // Bit of a stub for now, but should return the bodytype specific // to the slot and organ being checked in the future instead of // always using the mob root bodytype. -/mob/proc/get_equipment_bodytype(slot, bodypart) +/mob/proc/get_equipment_bodytype(slot, bodypart) as /decl/bodytype RETURN_TYPE(/decl/bodytype) var/decl/bodytype/root_bodytype = get_bodytype() return root_bodytype?.resolve_to_equipment_bodytype(src) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index c255185fed0f..9e02e45da629 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -701,6 +701,6 @@ var/global/list/ailment_reference_cache = list() appearance_bodytype = new_bodytype return TRUE -/obj/item/organ/proc/get_organ_appearance_bodytype() +/obj/item/organ/proc/get_organ_appearance_bodytype() as /decl/bodytype RETURN_TYPE(/decl/bodytype) return appearance_bodytype || bodytype diff --git a/code/modules/pronouns/_pronouns.dm b/code/modules/pronouns/_pronouns.dm index 174e9edb8c03..ba0a180f43c2 100644 --- a/code/modules/pronouns/_pronouns.dm +++ b/code/modules/pronouns/_pronouns.dm @@ -35,7 +35,7 @@ /decl/pronouns/proc/get_message_for_being_kicked_in_the_dick() return "Oh god, that hurt!" -/proc/get_pronouns_by_gender(var/gender) +/proc/get_pronouns_by_gender(var/gender) as /decl/pronouns switch(gender) if(PLURAL) . = /decl/pronouns @@ -56,7 +56,7 @@ . = GET_DECL(.) || GET_DECL(/decl/pronouns) // Atom helpers. -/atom/proc/get_pronouns(var/ignore_coverings) +/atom/proc/get_pronouns(var/ignore_coverings) as /decl/pronouns RETURN_TYPE(/decl/pronouns) . = get_pronouns_by_gender(gender) From 801473388359a0610877be74306a516bffd9fa04 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:25:44 -0400 Subject: [PATCH 1214/1331] Fix harddels when atoms with storage undergo EarlyDestroy --- code/game/atoms_init.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm index 74a53a1d715c..4c5d7124b885 100644 --- a/code/game/atoms_init.dm +++ b/code/game/atoms_init.dm @@ -95,6 +95,8 @@ // Called if an atom is deleted before it initializes. Only call Destroy in this if you know what you're doing. /atom/proc/EarlyDestroy(force = FALSE) + // since this is set up in New, we have to make sure it's cleared in EarlyDestroy too + QDEL_NULL(storage) return QDEL_HINT_QUEUE From ad988db1b495e12aa609b1b9a47208a2a31d3059 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 19:52:31 -0400 Subject: [PATCH 1215/1331] Fix incorrect typepath in effect emitter code --- code/game/objects/effects/map_effect/interval/effect_emitter.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/effects/map_effect/interval/effect_emitter.dm b/code/game/objects/effects/map_effect/interval/effect_emitter.dm index 51f3216b480a..ed5f6d400ddd 100644 --- a/code/game/objects/effects/map_effect/interval/effect_emitter.dm +++ b/code/game/objects/effects/map_effect/interval/effect_emitter.dm @@ -17,7 +17,7 @@ effect_system.set_up(effect_amount, effect_cardinals_only, src.loc, effect_forced_dir) return ..() -/obj/abstract/map_effect/interval/effect_emitter/interval/Destroy() +/obj/abstract/map_effect/interval/effect_emitter/Destroy() QDEL_NULL(effect_system) return ..() From cf1cc1c79a7f65fe4458c0085e80fd65796630e8 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 20:10:29 -0400 Subject: [PATCH 1216/1331] Remove more defunct code --- code/game/machinery/Beacon.dm | 45 ------------------ code/game/objects/effects/decals/misc.dm | 6 --- code/game/objects/effects/misc.dm | 9 ---- code/game/objects/items/robot/robot_items.dm | 8 ---- .../game/objects/items/weapons/clothingbag.dm | 19 -------- .../objects/items/weapons/gift_wrappaper.dm | 23 --------- .../hydroponics/spreading/spreading.dm | 16 ------- code/modules/lights/light_bulbs_tubes.dm | 8 ---- code/modules/mechs/interface/_interface.dm | 3 -- code/modules/recycling/conveyor2.dm | 3 -- code/modules/shieldgen/emergency_shield.dm | 9 ---- icons/obj/lighting.dmi | Bin 27080 -> 23496 bytes nebula.dme | 2 - 13 files changed, 151 deletions(-) delete mode 100644 code/game/machinery/Beacon.dm delete mode 100644 code/game/objects/items/weapons/clothingbag.dm diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm deleted file mode 100644 index 64a47117c6b7..000000000000 --- a/code/game/machinery/Beacon.dm +++ /dev/null @@ -1,45 +0,0 @@ -/obj/machinery/tracking_beacon - icon = 'icons/obj/objects.dmi' - icon_state = "floor_beaconf" - name = "tracking beacon" - desc = "A device that uses zero-point energy to create a permanent tracking beacon." - level = LEVEL_BELOW_PLATING - anchored = TRUE - idle_power_usage = 0 - var/obj/item/radio/beacon/beacon - -/obj/machinery/tracking_beacon/Initialize() - . = ..() - var/turf/T = get_turf(src) - if(T) - beacon = new /obj/item/radio/beacon(T) - beacon.set_invisibility(INVISIBILITY_MAXIMUM) - hide(!T?.is_plating()) - -/obj/machinery/tracking_beacon/Destroy() - QDEL_NULL(beacon) - . = ..() - - // update the invisibility and icon -/obj/machinery/tracking_beacon/hide(var/intact) - set_invisibility(intact ? 101 : 0) - update_icon() - - // update the icon_state -/obj/machinery/tracking_beacon/on_update_icon() - var/state="floor_beacon" - - if(invisibility) - icon_state = "[state]f" - - else - icon_state = "[state]" - -/obj/machinery/tracking_beacon/Process() - var/turf/my_turf = get_turf(src) - if(!beacon && my_turf) - beacon = new /obj/item/radio/beacon(my_turf) - beacon.set_invisibility(INVISIBILITY_MAXIMUM) - if(beacon && beacon.loc != my_turf) - beacon.forceMove(my_turf) - update_icon() \ No newline at end of file diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm index ac3664d7559a..aafa972a6ac1 100644 --- a/code/game/objects/effects/decals/misc.dm +++ b/code/game/objects/effects/decals/misc.dm @@ -6,9 +6,3 @@ layer = POINTER_LAYER anchored = TRUE mouse_opacity = MOUSE_OPACITY_UNCLICKABLE - -// Used for spray that you spray at walls, tables, hydrovats etc -/obj/effect/decal/spraystill - density = FALSE - anchored = TRUE - layer = PROJECTILE_LAYER diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index 805288d6b365..a93a6b2faec0 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -1,12 +1,3 @@ -//The effect when you wrap a dead body in gift wrap -/obj/effect/spresent - name = "strange present" - desc = "It's a ... present?" - icon = 'icons/obj/items/gift_wrapped.dmi' - icon_state = "strangepresent" - density = TRUE - anchored = FALSE - /obj/effect/stop var/victim = null icon_state = "empty" diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index f2a7ee2d6d44..d364fa4a15a9 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -2,14 +2,6 @@ /obj/item/borg max_health = ITEM_HEALTH_NO_DAMAGE -/********************************************************************** - Cyborg Spec Items -***********************************************************************/ -/obj/item/borg/overdrive - name = "overdrive" - icon = 'icons/obj/signs/warnings.dmi' - icon_state = "shock-large" - /********************************************************************** HUD/SIGHT things ***********************************************************************/ diff --git a/code/game/objects/items/weapons/clothingbag.dm b/code/game/objects/items/weapons/clothingbag.dm deleted file mode 100644 index 74216775a58e..000000000000 --- a/code/game/objects/items/weapons/clothingbag.dm +++ /dev/null @@ -1,19 +0,0 @@ -/obj/item/clothingbag - name = "clothing bag" - desc = "A cheap plastic bag that contains a fresh set of clothes." - icon = 'icons/obj/items/storage/trashbag.dmi' - icon_state = "trashbag3" - material = /decl/material/solid/organic/plastic - - var/icon_used = "trashbag0" - var/opened = 0 - -/obj/item/clothingbag/attack_self(mob/user) - if(!opened) - user.visible_message("\The [user] tears open \the [src]!", "You tear open \the [src]!") - opened = 1 - icon_state = icon_used - for(var/obj/item in contents) - item.dropInto(loc) - else - to_chat(user, "\The [src.name] is already ripped open and is now completely useless!") diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm index 331c51c130ee..3831b2b50c92 100644 --- a/code/game/objects/items/weapons/gift_wrappaper.dm +++ b/code/game/objects/items/weapons/gift_wrappaper.dm @@ -28,29 +28,6 @@ if(!QDELETED(src)) qdel(src) -// this can't be made, should it just be removed? -// should it be made into a subtype of closet? of living statue? -/obj/effect/spresent/relaymove(mob/user) - if (user.stat) - return - to_chat(user, "You can't move.") - -/obj/effect/spresent/attackby(obj/item/used_item, mob/user) - if(!IS_WIRECUTTER(used_item)) - to_chat(user, "I need wirecutters for that.") - return TRUE - - to_chat(user, "You cut open the present.") - - for(var/mob/M in src) //Should only be one but whatever. - M.dropInto(loc) - if (M.client) - M.client.eye = M.client.mob - M.client.perspective = MOB_PERSPECTIVE - - qdel(src) - return TRUE - /obj/item/a_gift/attack_self(mob/M) var/gift_type = pick( /obj/item/wallet, diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 18dc92c3ce02..589125004ed4 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -22,22 +22,6 @@ return log_and_message_admins("Event: Spacevines failed to find a viable turf.") -/obj/effect/dead_plant - anchored = TRUE - opacity = FALSE - density = FALSE - color = DEAD_PLANT_COLOUR - -/obj/effect/dead_plant/attack_hand() - SHOULD_CALL_PARENT(FALSE) - qdel(src) - return TRUE - -/obj/effect/dead_plant/attackby() - ..() - qdel(src) - return TRUE // if we're deleted we can't do any further interactions - /obj/effect/vine name = "vine" anchored = TRUE diff --git a/code/modules/lights/light_bulbs_tubes.dm b/code/modules/lights/light_bulbs_tubes.dm index 403bf7f4662b..496ca639c1c4 100644 --- a/code/modules/lights/light_bulbs_tubes.dm +++ b/code/modules/lights/light_bulbs_tubes.dm @@ -92,14 +92,6 @@ ..() shatter() -/obj/item/light/bulb/fire - name = "fire bulb" - desc = "A replacement fire bulb." - icon_state = "fbulb" - base_state = "fbulb" - item_state = "egg4" - material = /decl/material/solid/glass - // update the icon state and description of the light /obj/item/light/on_update_icon() . = ..() diff --git a/code/modules/mechs/interface/_interface.dm b/code/modules/mechs/interface/_interface.dm index f9090602be10..f2985acc7324 100644 --- a/code/modules/mechs/interface/_interface.dm +++ b/code/modules/mechs/interface/_interface.dm @@ -20,9 +20,6 @@ if(client) client.screen |= hud_elements -/obj/screen/zone_selector/exosuit - requires_ui_style = FALSE - /mob/living/exosuit/initialize_hud() if(!LAZYLEN(hud_elements)) var/i = 1 diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index ccfd214b9623..d39f9ee06ea0 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -18,9 +18,6 @@ var/global/list/all_conveyor_switches = list() var/movedir // the actual direction to move stuff in var/list/affecting // the list of all items that will be moved this ptick -/obj/machinery/conveyor/centcom_auto - id_tag = "round_end_belt" - // create a conveyor /obj/machinery/conveyor/Initialize(mapload, newdir, on = 0) . = ..(mapload) diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index 149e2d54001f..2ae5885d1e11 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -30,15 +30,6 @@ check_failure() return QDELING(src) // return true if deleted, false otherwise -/obj/machinery/shield/malfai - name = "emergency forcefield" - desc = "A weak forcefield which seems to be projected by the emergency atmosphere containment field." - max_health = 100 // Half health, it's not suposed to resist much. - -/obj/machinery/shield/malfai/Process() - current_health -= 0.5 // Slowly lose integrity over time - check_failure() - /obj/machinery/shield/proc/check_failure() if (current_health <= 0) visible_message(SPAN_NOTICE("\The [src] dissipates!")) diff --git a/icons/obj/lighting.dmi b/icons/obj/lighting.dmi index bc8876bdf83fa68c3e7e00971037806d7047a819..e6794d72c013d09c8a1a9b7fa9476f677e4ea98b 100644 GIT binary patch literal 23496 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 literal 27080 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 G5C01f^r^J~ diff --git a/nebula.dme b/nebula.dme index 8ac7cad0bcbb..7dbc5863ea1a 100644 --- a/nebula.dme +++ b/nebula.dme @@ -819,7 +819,6 @@ #include "code\game\machinery\air_sensor.dm" #include "code\game\machinery\alarm.dm" #include "code\game\machinery\atmo_control.dm" -#include "code\game\machinery\Beacon.dm" #include "code\game\machinery\biogenerator.dm" #include "code\game\machinery\bodyscanner.dm" #include "code\game\machinery\bodyscanner_console.dm" @@ -1283,7 +1282,6 @@ #include "code\game\objects\items\weapons\cane.dm" #include "code\game\objects\items\weapons\cards_ids.dm" #include "code\game\objects\items\weapons\cards_ids_syndicate.dm" -#include "code\game\objects\items\weapons\clothingbag.dm" #include "code\game\objects\items\weapons\clown_items.dm" #include "code\game\objects\items\weapons\cosmetics.dm" #include "code\game\objects\items\weapons\defib.dm" From fd3594e9e6538c3517b6c6da27bdd1a7b2bf6732 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 20:13:17 -0400 Subject: [PATCH 1217/1331] Repath 'wood' doors after their actual wood type --- code/game/objects/structures/doors/_door.dm | 19 ++++++++++--------- maps/away/casino/casino.dmm | 6 +++--- .../deserted_lab/deserted_lab.dmm | 2 +- .../exoplanet_ruins/lodge/lodge.dmm | 2 +- maps/tradeship/tradeship-0.dmm | 2 +- mods/gamemodes/ninja/maps/ninja_base.dmm | 2 +- tools/map_migrations/5353_doors.txt | 3 +++ 7 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 tools/map_migrations/5353_doors.txt diff --git a/code/game/objects/structures/doors/_door.dm b/code/game/objects/structures/doors/_door.dm index ff6989638376..586450e6d93e 100644 --- a/code/game/objects/structures/doors/_door.dm +++ b/code/game/objects/structures/doors/_door.dm @@ -255,7 +255,7 @@ /obj/structure/door/diamond material = /decl/material/solid/gemstone/diamond -/obj/structure/door/wood +/obj/structure/door/oak material = /decl/material/solid/organic/wood/oak color = /decl/material/solid/organic/wood/oak::color @@ -275,17 +275,18 @@ material = /decl/material/solid/organic/wood/walnut color = /decl/material/solid/organic/wood/walnut::color -/obj/structure/door/wood/saloon - material = /decl/material/solid/organic/wood/oak - opacity = FALSE - -/obj/structure/door/wood/saloon/ebony +/obj/structure/door/ebony material = /decl/material/solid/organic/wood/ebony color = /decl/material/solid/organic/wood/ebony::color -/obj/structure/door/wood/saloon/walnut - material = /decl/material/solid/organic/wood/walnut - color = /decl/material/solid/organic/wood/walnut::color +/obj/structure/door/oak/saloon + opacity = FALSE + +/obj/structure/door/ebony/saloon + opacity = FALSE + +/obj/structure/door/walnut/saloon + opacity = FALSE /obj/structure/door/glass material = /decl/material/solid/glass diff --git a/maps/away/casino/casino.dmm b/maps/away/casino/casino.dmm index 64901cc1070a..537e0de1732d 100644 --- a/maps/away/casino/casino.dmm +++ b/maps/away/casino/casino.dmm @@ -3787,7 +3787,7 @@ /area/casino/casino_mainfloor) "kQ" = ( /obj/machinery/door/firedoor, -/obj/structure/door/wood{ +/obj/structure/door/oak{ name = "VIP Private room" }, /obj/structure/cable{ @@ -3802,7 +3802,7 @@ /area/casino/casino_private_vip) "kS" = ( /obj/machinery/door/firedoor, -/obj/structure/door/wood{ +/obj/structure/door/oak{ name = "Private room 1" }, /obj/structure/cable{ @@ -3817,7 +3817,7 @@ /area/casino/casino_private1) "kU" = ( /obj/machinery/door/firedoor, -/obj/structure/door/wood{ +/obj/structure/door/oak{ name = "Private room 2" }, /obj/structure/cable{ diff --git a/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm b/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm index 39a8a4743f78..126caa29c4a4 100644 --- a/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm +++ b/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm @@ -120,7 +120,7 @@ /turf/floor/tiled/white, /area/template_noop) "az" = ( -/obj/structure/door/wood, +/obj/structure/door/oak, /turf/floor/carpet/blue2, /area/template_noop) "aA" = ( diff --git a/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm b/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm index a98e2f8fec67..1844aa27807f 100644 --- a/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm +++ b/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm @@ -185,7 +185,7 @@ /turf/floor/wood, /area/template_noop) "H" = ( -/obj/structure/door/wood, +/obj/structure/door/oak, /turf/floor/wood, /area/template_noop) "I" = ( diff --git a/maps/tradeship/tradeship-0.dmm b/maps/tradeship/tradeship-0.dmm index 64ebd190f204..09bbc00b7958 100644 --- a/maps/tradeship/tradeship-0.dmm +++ b/maps/tradeship/tradeship-0.dmm @@ -27,7 +27,7 @@ /turf/floor, /area/ship/trade/undercomms) "ad" = ( -/obj/structure/door/wood{ +/obj/structure/door/oak{ name = "captain's quarters" }, /obj/structure/cable{ diff --git a/mods/gamemodes/ninja/maps/ninja_base.dmm b/mods/gamemodes/ninja/maps/ninja_base.dmm index 05709304e93f..0c0e63774e1d 100644 --- a/mods/gamemodes/ninja/maps/ninja_base.dmm +++ b/mods/gamemodes/ninja/maps/ninja_base.dmm @@ -306,7 +306,7 @@ /turf/unsimulated/floor/white, /area/map_template/ninja_dojo/dojo) "bh" = ( -/obj/structure/door/wood, +/obj/structure/door/oak, /turf/unsimulated/floor/wood, /area/map_template/ninja_dojo/dojo) "bi" = ( diff --git a/tools/map_migrations/5353_doors.txt b/tools/map_migrations/5353_doors.txt new file mode 100644 index 000000000000..5dbfb3f79e62 --- /dev/null +++ b/tools/map_migrations/5353_doors.txt @@ -0,0 +1,3 @@ +# REPATH DOORS +/obj/structure/door/wood/saloon/@SUBTYPES : /obj/structure/door/@SUBTYPES/saloon{@OLD} +/obj/structure/door/wood/@SUBTYPES : /obj/structure/door/oak/@SUBTYPES{@OLD} From ff533f0c141e773249c07c7f52de38b07c8b1461 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 20:19:17 -0400 Subject: [PATCH 1218/1331] Remove defunct projectile vars --- code/modules/projectiles/projectile.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index d36ec1783cc9..79e4a4315ccf 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -78,9 +78,6 @@ var/fire_sound var/fire_sound_vol = 50 var/fire_sound_vol_silenced = 10 - var/miss_sounds - var/ricochet_sounds - var/list/impact_sounds //for different categories, IMPACT_MEAT etc var/shrapnel_type = /obj/item/shard/shrapnel var/vacuum_traversal = 1 //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum. From 64f071612eab700f721161045268abd4390815ca Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 20:19:37 -0400 Subject: [PATCH 1219/1331] Remove defunct intent_height var --- code/_onclick/hud/screen/screen_intent.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/_onclick/hud/screen/screen_intent.dm b/code/_onclick/hud/screen/screen_intent.dm index f3fba3b8563f..54aafe35ddce 100644 --- a/code/_onclick/hud/screen/screen_intent.dm +++ b/code/_onclick/hud/screen/screen_intent.dm @@ -70,7 +70,6 @@ requires_ui_style = FALSE apply_screen_overlay = FALSE var/intent_width = 16 - var/intent_height = 16 var/list/intent_selectors /obj/screen/intent/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) From 4e9707e7cc99cad050f4a0b47a451464220b5f67 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 6 May 2026 20:19:47 -0400 Subject: [PATCH 1220/1331] Remove defunct structure salvage work_skill var --- code/modules/salvage/structure.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/modules/salvage/structure.dm b/code/modules/salvage/structure.dm index 1d71f206157a..987d7775431f 100644 --- a/code/modules/salvage/structure.dm +++ b/code/modules/salvage/structure.dm @@ -4,7 +4,6 @@ tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT material = /decl/material/solid/metal/steel var/frame_type = /obj/machinery/constructable_frame/machine_frame - var/work_skill = SKILL_CONSTRUCTION /obj/structure/salvage/proc/get_salvageable_components() return From e2be5e8918147c7a563e608e38b4315ac0e14c6b Mon Sep 17 00:00:00 2001 From: Typhin Date: Fri, 8 May 2026 20:59:09 -0500 Subject: [PATCH 1221/1331] Fix turret control panels Fixes Turret Control Panels to initialize late, so it can search areas after they initialize and populate `global.areas`. --- code/game/machinery/turret_control.dm | 6 ++++++ maps/ministation/ministation-0.dmm | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm index 141619bd14f7..38dd0d8e1939 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -49,6 +49,10 @@ . = ..() /obj/machinery/turretid/Initialize() + . = ..() + return INITIALIZE_HINT_LATELOAD // Because areas initialize AFTER these! + +/obj/machinery/turretid/LateInitialize() if(!control_area) control_area = get_area(src) else if(istext(control_area)) @@ -56,6 +60,8 @@ if(A.name && A.name==control_area) control_area = A break + else if(ispath(control_area)) + control_area = locate(control_area) in global.areas if(control_area) var/area/A = control_area diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index e9be521e3b37..cda0d8a9e6a1 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -10255,7 +10255,7 @@ dir = 1 }, /obj/machinery/turretid/stun{ - control_area = "\improper AI Upload Chamber"; + control_area = /area/ministation/ai_core; id_tag = "aihome"; pixel_y = -24; dir = 1 @@ -13371,7 +13371,6 @@ /obj/machinery/turretid/stun{ name = "AI Upload turret control"; id_tag = "upload"; - control_area = "\improper AI Upload Chamber"; pixel_y = -24; dir = 1 }, From e079488c98c86b4ba3e690f960ee2d88fd166bae Mon Sep 17 00:00:00 2001 From: Typhin Date: Fri, 8 May 2026 23:26:33 -0500 Subject: [PATCH 1222/1331] Heat Exchange pipes can now radiate to space at half efficiency on non-space turfs --- code/modules/atmospherics/he_pipes.dm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/modules/atmospherics/he_pipes.dm b/code/modules/atmospherics/he_pipes.dm index aeb607af1eb1..c06096efa928 100644 --- a/code/modules/atmospherics/he_pipes.dm +++ b/code/modules/atmospherics/he_pipes.dm @@ -62,6 +62,7 @@ else var/turf/turf = loc var/datum/gas_mixture/pipe_air = return_air() + var/heat_radiated = FALSE if(istype(loc, /turf/space)) parent.radiate_heat_to_space(surface, 1) else if(istype(turf) && turf.simulated) @@ -71,8 +72,13 @@ environment_temperature = pipe_turf.temperature else var/datum/gas_mixture/environment = pipe_turf.return_air() + if(environment.get_total_moles() == 0) // We're in a vacuum + if(loc.is_outside()) // But we're outside, so we're in space + parent.radiate_heat_to_space(surface, 0.5) // Radiate out at half efficiency + // Else, we're inside, no gas means no heat capacity. Nothing to do, heat was dealt with either way + heat_radiated = TRUE // Skip temperature_interact() either way environment_temperature = environment.temperature - if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) + if(!heat_radiated && abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) parent.temperature_interact(pipe_turf, volume, thermal_conductivity) if(buckled_mob) From d432d690e3ded5cec64051ecd79d99b2732327a6 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 9 May 2026 10:18:19 -0400 Subject: [PATCH 1223/1331] Fix nutriment being HAAAAARDEEEEEEER THAN STEEEEL --- code/modules/reagents/chems/chems_nutriment.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index e1b478c93fab..929c4307d091 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -15,6 +15,7 @@ affect_blood_on_ingest = 0 affect_blood_on_inhale = 0 opacity = 1.0 // liquids are half transparent by default; meat and etc should not be transparent + hardness = MAT_VALUE_SOFT // same softness as meat // Technically a room-temperature solid, but saves // repathing it to /solid all over the codebase. From 7cdb8811616f6b286901bac41bbc3c1bf9b9dfb0 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 1 Jan 2026 17:20:54 -0500 Subject: [PATCH 1224/1331] Make gases use alist --- code/__defines/_byond_version_compat.dm | 4 +- code/__defines/math_physics.dm | 2 +- code/_helpers/lists.dm | 7 + code/game/machinery/air_sensor.dm | 6 +- .../atmoalter/portable_atmospherics.dm | 6 +- .../objects/effects/spawners/bombspawner.dm | 4 +- .../game/objects/items/weapons/tanks/tanks.dm | 6 +- code/game/objects/structures/fires.dm | 4 +- .../turfs/floors/subtypes/floor_circuit.dm | 2 +- .../turfs/floors/subtypes/floor_reinforced.dm | 16 +- code/game/turfs/turf.dm | 2 +- code/game/turfs/turf_enter.dm | 6 +- code/modules/ZAS/Diagnostic.dm | 4 +- code/modules/ZAS/Fire.dm | 64 +++---- code/modules/ZAS/Turf.dm | 2 +- code/modules/ZAS/Zone.dm | 16 +- code/modules/admin/verbs/debug.dm | 4 +- code/modules/atmospherics/atmos_primitives.dm | 146 ++++++-------- .../components/unary/unary_base.dm | 6 +- code/modules/fusion/core/core_field.dm | 10 +- .../random_exoplanet/flora_generator.dm | 6 +- code/modules/materials/_materials.dm | 3 + code/modules/mechs/components/body.dm | 4 +- code/modules/mob/living/human/life.dm | 6 +- .../living/silicon/pai/software_modules.dm | 6 +- .../living/simple_animal/_simple_animal.dm | 7 +- code/modules/multiz/level_data.dm | 8 +- code/modules/organs/internal/lungs.dm | 6 +- .../modules/overmap/planetoids/_planetoids.dm | 6 +- .../overmap/planetoids/planetoid_skybox.dm | 6 +- .../ships/device_types/gas_thruster.dm | 6 +- code/modules/power/singularity/collector.dm | 7 +- code/modules/scanners/gas.dm | 8 +- code/modules/xgm/xgm_gas_mixture.dm | 178 +++++++++--------- code/unit_tests/atmospherics_tests.dm | 20 +- maps/away/bearcat/bearcat.dm | 8 +- maps/planets/test_planet/test_planet.dm | 2 +- .../exoplanet_ruins/lodge/lodge.dm | 2 +- maps/shaded_hills/levels/_levels.dm | 2 +- .../ruins/ec_old_crash/ec_old_crash.dm | 4 +- mods/species/ascent/turfs/ship.dm | 4 +- 41 files changed, 281 insertions(+), 335 deletions(-) diff --git a/code/__defines/_byond_version_compat.dm b/code/__defines/_byond_version_compat.dm index 61d61853a893..8e843d66d713 100644 --- a/code/__defines/_byond_version_compat.dm +++ b/code/__defines/_byond_version_compat.dm @@ -1,5 +1,5 @@ -#define REQUIRED_DM_VERSION 515 +#define REQUIRED_DM_VERSION 516 #if DM_VERSION < REQUIRED_DM_VERSION -#warn Nebula is not tested on BYOND versions older than 515. The code may not compile, and if it does compile it may have severe problems. +#warn Nebula is not tested on BYOND versions older than 516. The code may not compile, and if it does compile it may have severe problems. #endif \ No newline at end of file diff --git a/code/__defines/math_physics.dm b/code/__defines/math_physics.dm index 4edf99ad88af..64dd03e8c1d3 100644 --- a/code/__defines/math_physics.dm +++ b/code/__defines/math_physics.dm @@ -27,7 +27,7 @@ #define ATM *ONE_ATMOSPHERE #define ATMOS_PRECISION 0.0001 -#define QUANTIZE(variable) (round(variable, ATMOS_PRECISION)) +#define QUANTIZE(variable) (NONUNIT_FLOOR(variable, ATMOS_PRECISION)) #define INFINITY 1.#INF diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm index a39367a43233..0a0c554c5fb0 100644 --- a/code/_helpers/lists.dm +++ b/code/_helpers/lists.dm @@ -272,6 +272,13 @@ Checks if a list has the same entries and values as an element of big. else .[key] = call(merge_method)(.[key], b_value) +// Picks a key in an alist. This is awful but hey, what can you do? +/proc/apick(alist/target_alist) + var/index = rand(1, length(target_alist)) + for(var/key in target_alist) + if(--index == 0) + return key + //Pretends to pick an element based on its weight but really just seems to pick a random element. /proc/pickweight(list/target_list) var/total = 0 diff --git a/code/game/machinery/air_sensor.dm b/code/game/machinery/air_sensor.dm index bfbb9236ed3e..6a4774c0759b 100644 --- a/code/game/machinery/air_sensor.dm +++ b/code/game/machinery/air_sensor.dm @@ -46,9 +46,9 @@ if(total_moles <= 0) return . = list() - for(var/gas in air_sample.gas) - var/decl/material/mat = GET_DECL(gas) - var/gaspercent = round(air_sample.gas[gas]*100/total_moles,0.01) + for(var/gas_type, gas_amount in air_sample.gas) + var/decl/material/mat = GET_DECL(gas_type) + var/gaspercent = round(gas_amount*100/total_moles,0.01) var/gas_list = list("symbol" = mat.gas_symbol_html, "percent" = gaspercent) . += list(gas_list) diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 3de7c00f4368..b788ebfe1e8a 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -12,9 +12,9 @@ /obj/machinery/portable_atmospherics/get_single_monetary_worth() . = ..() - for(var/gas in air_contents?.gas) - var/decl/material/gas_data = GET_DECL(gas) - . += gas_data.get_value() * air_contents.gas[gas] * GAS_WORTH_MULTIPLIER + for(var/gas_type, gas_amount in air_contents?.gas) + var/decl/material/gas_data = GET_DECL(gas_type) + . += gas_data.get_value() * gas_amount * GAS_WORTH_MULTIPLIER . = max(1, round(.)) /obj/machinery/portable_atmospherics/Initialize() diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index 9528d9d7b556..1716a5662e97 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -75,7 +75,7 @@ OT.master = V PT.valve_welded = TRUE - PT.air_contents.gas = list() + PT.air_contents.gas = alist() PT.air_contents.gas[accelerant_type] = accelerant_amount PT.air_contents.gas[filler_type] = filler_amount PT.air_contents.total_moles = accelerant_amount + filler_amount @@ -83,7 +83,7 @@ PT.air_contents.update_values() OT.valve_welded = TRUE - OT.air_contents.gas = list() + OT.air_contents.gas = alist() OT.air_contents.gas[oxidizer_type] = oxidizer_amount OT.air_contents.total_moles = oxidizer_amount OT.air_contents.temperature = FLAMMABLE_GAS_MINIMUM_BURN_TEMPERATURE+1 diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 73ae28e1dcc2..b191b55e56da 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -80,9 +80,9 @@ var/global/list/global/tank_gauge_cache = list() /obj/item/tank/get_single_monetary_worth() . = ..() - for(var/gas in air_contents?.gas) - var/decl/material/gas_data = GET_DECL(gas) - . += gas_data.get_value() * air_contents.gas[gas] * GAS_WORTH_MULTIPLIER + for(var/gas_type, gas_amount in air_contents?.gas) + var/decl/material/gas_data = GET_DECL(gas_type) + . += gas_data.get_value() * gas_amount * GAS_WORTH_MULTIPLIER . = max(1, round(.)) /obj/item/tank/get_examine_strings(mob/user, distance, infix, suffix) diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index b05b4cfba864..a92571c8a8f0 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -151,8 +151,8 @@ /obj/structure/fire_source/proc/check_atmos() var/datum/gas_mixture/GM = loc?.return_air() - for(var/g in GM?.gas) - var/decl/material/oxidizer = GET_DECL(g) + for(var/gas_type in GM?.gas) + var/decl/material/oxidizer = GET_DECL(gas_type) if(oxidizer.gas_flags & XGM_GAS_OXIDIZER) return TRUE diff --git a/code/game/turfs/floors/subtypes/floor_circuit.dm b/code/game/turfs/floors/subtypes/floor_circuit.dm index 77bb38e13a45..2737352de969 100644 --- a/code/game/turfs/floors/subtypes/floor_circuit.dm +++ b/code/game/turfs/floors/subtypes/floor_circuit.dm @@ -29,7 +29,7 @@ temperature = TCMB /turf/floor/greengrid/nitrogen - initial_gas = list(/decl/material/gas/nitrogen = MOLES_N2STANDARD) + initial_gas = alist(/decl/material/gas/nitrogen = MOLES_N2STANDARD) /turf/floor/blackgrid name = "mainframe floor" diff --git a/code/game/turfs/floors/subtypes/floor_reinforced.dm b/code/game/turfs/floors/subtypes/floor_reinforced.dm index 551786ed357b..a322fb767ce0 100644 --- a/code/game/turfs/floors/subtypes/floor_reinforced.dm +++ b/code/game/turfs/floors/subtypes/floor_reinforced.dm @@ -8,32 +8,32 @@ initial_gas = null /turf/floor/reinforced/airmix - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = MOLES_O2ATMOS, /decl/material/gas/nitrogen = MOLES_N2ATMOS ) /turf/floor/reinforced/nitrogen - initial_gas = list(/decl/material/gas/nitrogen = ATMOSTANK_NITROGEN) + initial_gas = alist(/decl/material/gas/nitrogen = ATMOSTANK_NITROGEN) /turf/floor/reinforced/hydrogen - initial_gas = list(/decl/material/gas/hydrogen = ATMOSTANK_HYDROGEN) + initial_gas = alist(/decl/material/gas/hydrogen = ATMOSTANK_HYDROGEN) /turf/floor/reinforced/oxygen - initial_gas = list(/decl/material/gas/oxygen = ATMOSTANK_OXYGEN) + initial_gas = alist(/decl/material/gas/oxygen = ATMOSTANK_OXYGEN) /turf/floor/reinforced/nitrogen/engine name = "engine floor" - initial_gas = list(/decl/material/gas/nitrogen = MOLES_N2STANDARD) + initial_gas = alist(/decl/material/gas/nitrogen = MOLES_N2STANDARD) /turf/floor/reinforced/hydrogen/fuel - initial_gas = list(/decl/material/gas/hydrogen = ATMOSTANK_HYDROGEN_FUEL) + initial_gas = alist(/decl/material/gas/hydrogen = ATMOSTANK_HYDROGEN_FUEL) /turf/floor/reinforced/carbon_dioxide - initial_gas = list(/decl/material/gas/carbon_dioxide = ATMOSTANK_CO2) + initial_gas = alist(/decl/material/gas/carbon_dioxide = ATMOSTANK_CO2) /turf/floor/reinforced/n20 - initial_gas = list(/decl/material/gas/nitrous_oxide = ATMOSTANK_NITROUSOXIDE) + initial_gas = alist(/decl/material/gas/nitrous_oxide = ATMOSTANK_NITROUSOXIDE) /turf/floor/reinforced/airless name = "vacuum floor" diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index b677b30f83c7..28171b7c2394 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -18,7 +18,7 @@ var/turf_flags /// Either a mapping of material decls to mol amounts, or a reserved initial gas define like GAS_STANDARD_AIRMIX. - var/list/initial_gas + var/alist/initial_gas //Properties for airtight tiles (/wall) var/thermal_conductivity = 0.05 diff --git a/code/game/turfs/turf_enter.dm b/code/game/turfs/turf_enter.dm index cb1849868ab6..f419ff75cc63 100644 --- a/code/game/turfs/turf_enter.dm +++ b/code/game/turfs/turf_enter.dm @@ -59,9 +59,9 @@ var/datum/gas_mixture/env = return_air(1) if(!env) return - for(var/g in env.gas) - var/decl/material/mat = GET_DECL(g) - if((mat.gas_flags & XGM_GAS_CONTAMINANT) && env.gas[g] > mat.gas_overlay_limit + 1) + for(var/gas_type, gas_amount in env.gas) + var/decl/material/mat = GET_DECL(gas_type) + if((mat.gas_flags & XGM_GAS_CONTAMINANT) && gas_amount > mat.gas_overlay_limit + 1) I.contaminate() break diff --git a/code/modules/ZAS/Diagnostic.dm b/code/modules/ZAS/Diagnostic.dm index 71641a66eef1..4892aa15e51b 100644 --- a/code/modules/ZAS/Diagnostic.dm +++ b/code/modules/ZAS/Diagnostic.dm @@ -10,8 +10,8 @@ to_chat(mob, "ZONE: No zone here.") var/datum/gas_mixture/mix = T.return_air() to_chat(mob, "ZONE: [mix.return_pressure()] kPa [mix.temperature] k") - for(var/g in mix.gas) - to_chat(mob, "ZONE GASES: [g]: [mix.gas[g]]\n") + for(var/gas_type, gas_amount in mix.gas) + to_chat(mob, "ZONE GASES: [gas_type]: [gas_amount]\n") /client/proc/Test_ZAS_Connection(var/turf/T) set category = "Debug" diff --git a/code/modules/ZAS/Fire.dm b/code/modules/ZAS/Fire.dm index 8e68d92a22f9..d6365ddce8df 100644 --- a/code/modules/ZAS/Fire.dm +++ b/code/modules/ZAS/Fire.dm @@ -187,12 +187,12 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin var/total_oxidizers = 0 //*** Get the fuel and oxidizer amounts - for(var/g in gas) - var/decl/material/mat = GET_DECL(g) + for(var/gas_type, gas_amount in gas) + var/decl/material/mat = GET_DECL(gas_type) if(mat.gas_flags & XGM_GAS_FUEL) - total_fuel += gas[g] + total_fuel += gas_amount if(mat.gas_flags & XGM_GAS_OXIDIZER) - total_oxidizers += gas[g] + total_oxidizers += gas_amount total_fuel *= group_multiplier total_oxidizers *= group_multiplier @@ -233,9 +233,9 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin //remove_by_flag() and adjust_gas() handle the group_multiplier for us. remove_by_flag(XGM_GAS_OXIDIZER, used_oxidizers) var/datum/gas_mixture/burned_fuel = remove_by_flag(XGM_GAS_FUEL, used_fuel) - for(var/g in burned_fuel.gas) - var/decl/material/mat = GET_DECL(g) - mat.add_burn_product(src, burned_fuel.gas[g]) + for(var/gas_type, gas_amount in burned_fuel.gas) + var/decl/material/mat = GET_DECL(gas_type) + mat.add_burn_product(src, gas_amount) //calculate the energy produced by the reaction and then set the new temperature of the mix temperature = (starting_energy + vsc.fire_fuel_energy_release * used_fuel) / heat_capacity() @@ -249,44 +249,32 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin return firelevel /datum/gas_mixture/proc/check_recombustibility() + var/const/HAS_OXIDIZER = BITFLAG(0) + var/const/HAS_FUEL = BITFLAG(1) . = 0 - for(var/g in gas) - if(gas[g] >= 0.1) - var/decl/material/gas = GET_DECL(g) + for(var/gas_type, gas_amount in gas) + if(gas_amount >= 0.1) + var/decl/material/gas = GET_DECL(gas_type) if(gas.gas_flags & XGM_GAS_OXIDIZER) - . = 1 - break - - if(!.) - return 0 - - . = 0 - for(var/g in gas) - if(gas[g] >= 0.1) - var/decl/material/gas = GET_DECL(g) - if(gas.gas_flags & XGM_GAS_OXIDIZER) - . = 1 - break + . |= HAS_OXIDIZER + if(gas.gas_flags & XGM_GAS_FUEL) + . |= HAS_FUEL + if(. == (HAS_OXIDIZER|HAS_FUEL)) + return TRUE /datum/gas_mixture/proc/check_combustibility() + var/const/HAS_OXIDIZER = BITFLAG(0) + var/const/HAS_FUEL = BITFLAG(1) . = 0 - for(var/g in gas) - if(QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1) - var/decl/material/gas = GET_DECL(g) + for(var/gas_type, gas_amount in gas) + if(QUANTIZE(gas_amount * vsc.fire_consuption_rate) >= 0.1) + var/decl/material/gas = GET_DECL(gas_type) if(gas.gas_flags & XGM_GAS_OXIDIZER) - . = 1 - break - - if(!.) - return 0 - - . = 0 - for(var/g in gas) - if(QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1) - var/decl/material/gas = GET_DECL(g) + . |= HAS_OXIDIZER if(gas.gas_flags & XGM_GAS_FUEL) - . = 1 - break + . |= HAS_FUEL + if(. == (HAS_OXIDIZER|HAS_FUEL)) + return TRUE //returns a value between 0 and vsc.fire_firelevel_multiplier /datum/gas_mixture/proc/calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, gas_volume) diff --git a/code/modules/ZAS/Turf.dm b/code/modules/ZAS/Turf.dm index 016d69b4c53e..95f4c9c6f34e 100644 --- a/code/modules/ZAS/Turf.dm +++ b/code/modules/ZAS/Turf.dm @@ -223,7 +223,7 @@ return TRUE return FALSE -var/global/list/STANDARD_AIRMIX = list( +var/global/alist/STANDARD_AIRMIX = alist( /decl/material/gas/oxygen = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD ) diff --git a/code/modules/ZAS/Zone.dm b/code/modules/ZAS/Zone.dm index cac84cb69bf5..10fab6f56161 100644 --- a/code/modules/ZAS/Zone.dm +++ b/code/modules/ZAS/Zone.dm @@ -180,26 +180,26 @@ Class Procs: /zone/proc/handle_condensation() set waitfor = FALSE condensing = TRUE - for(var/g in air.gas) - var/decl/material/mat = GET_DECL(g) + for(var/gas_type, gas_amount in air.gas) + var/decl/material/mat = GET_DECL(gas_type) if(!isnull(mat.gas_condensation_point) && (air.temperature <= mat.gas_condensation_point)) var/condensation_area = air.group_multiplier / length(air.gas) while(condensation_area > 0 && length(contents)) condensation_area-- var/turf/flooding = pick(contents) - var/condense_amt = min(air.gas[g], rand(1,3)) + var/condense_amt = min(gas_amount, rand(1,3)) if(condense_amt < 1) break - air.adjust_gas(g, -condense_amt) - flooding.add_to_reagents(g, condense_amt * REAGENT_UNITS_PER_GAS_MOLE) + air.adjust_gas(gas_type, -condense_amt) + flooding.add_to_reagents(gas_type, condense_amt * REAGENT_UNITS_PER_GAS_MOLE) CHECK_TICK condensing = FALSE /zone/proc/dbg_data(mob/M) to_chat(M, name) - for(var/g in air.gas) - var/decl/material/mat = GET_DECL(g) - to_chat(M, "[capitalize(mat.gas_name)]: [air.gas[g]]") + for(var/gas_type, gas_amount in air.gas) + var/decl/material/mat = GET_DECL(gas_type) + to_chat(M, "[capitalize(mat.gas_name)]: [gas_amount]") to_chat(M, "P: [air.return_pressure()] kPa V: [air.total_volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)") to_chat(M, "O2 per N2: [(air.gas[/decl/material/gas/nitrogen] ? air.gas[/decl/material/gas/oxygen]/air.gas[/decl/material/gas/nitrogen] : "N/A")] Moles: [air.total_moles]") to_chat(M, "Simulated: [contents.len] ([air.group_multiplier])") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 480a29e13010..c9b324c94e9d 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -16,8 +16,8 @@ var/t = "Coordinates: [T.x],[T.y],[T.z]\n" t += "Temperature: [env.temperature]\n" t += "Pressure: [env.return_pressure()]kPa\n" - for(var/g in env.gas) - t += "[g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.total_volume]kPa\n" + for(var/gas_type, gas_amount in env.gas) + t += "[gas_type]: [gas_amount] / [gas_amount * R_IDEAL_GAS_EQUATION * env.temperature / env.total_volume]kPa\n" usr.show_message(t, 1) SSstatistics.add_field_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/atmospherics/atmos_primitives.dm b/code/modules/atmospherics/atmos_primitives.dm index 6a09490b3251..13a0044acfc6 100644 --- a/code/modules/atmospherics/atmos_primitives.dm +++ b/code/modules/atmospherics/atmos_primitives.dm @@ -110,27 +110,28 @@ if (source.total_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing return -1 - filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &= + // this takes the associated values of the left side, e.g. source.gas + filtering = source.gas & filtering //only filter gasses that are actually there. DO NOT USE &= //Determine the specific power of each filterable gas type, and the total amount of filterable gas (gasses selected to be scrubbed) - var/total_filterable_moles = 0 //the total amount of filterable gas - var/list/specific_power_gas = list() //the power required to remove one mole of pure gas, for each gas type - for (var/g in filtering) - if (source.gas[g] < MINIMUM_MOLES_TO_FILTER) + var/total_filterable_moles = 0 //the total amount of filterable gas + var/alist/specific_power_gas = alist() //the power required to remove one mole of pure gas, for each gas type + ///the power required to scrub one mol of input gas + var/power_per_mol = 0 + for (var/gas_type, gas_amount in filtering) + if (gas_amount < MINIMUM_MOLES_TO_FILTER) continue - var/specific_power = calculate_specific_power_gas(g, source, sink)/ATMOS_FILTER_EFFICIENCY - specific_power_gas[g] = specific_power - total_filterable_moles += source.gas[g] + var/specific_power = calculate_specific_power_gas(gas_type, source, sink)/ATMOS_FILTER_EFFICIENCY + specific_power_gas[gas_type] = specific_power + power_per_mol += gas_amount * specific_power + total_filterable_moles += gas_amount if (total_filterable_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing return -1 //now that we know the total amount of filterable gas, we can calculate the amount of power needed to scrub one mole of gas - var/total_specific_power = 0 //the power required to remove one mole of filterable gas - for (var/g in filtering) - var/ratio = source.gas[g]/total_filterable_moles //this converts the specific power per mole of pure gas to specific power per mole of scrubbed gas - total_specific_power += specific_power_gas[g]*ratio + power_per_mol /= total_filterable_moles //this converts the specific power per mole of pure gas to specific power per mole of scrubbed gas //Figure out how much of each gas to filter if (isnull(total_transfer_moles)) @@ -139,8 +140,8 @@ total_transfer_moles = min(total_transfer_moles, total_filterable_moles) //limit transfer_moles based on available power - if (!isnull(available_power) && total_specific_power > 0) - total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power) + if (!isnull(available_power) && power_per_mol > 0) + total_transfer_moles = min(total_transfer_moles, available_power/power_per_mol) if (total_transfer_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing return -1 @@ -154,16 +155,15 @@ P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here var/power_draw = 0 - for (var/g in filtering) - var/transfer_moles = source.gas[g] + for (var/gas_type, gas_amount in filtering) //filter gas in proportion to the mole ratio - transfer_moles = min(transfer_moles, total_transfer_moles*(source.gas[g]/total_filterable_moles)) + var/transfer_moles = min(gas_amount, total_transfer_moles*(gas_amount/total_filterable_moles)) //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop. - source.adjust_gas(g, -transfer_moles, update=0) - sink.adjust_gas_temp(g, transfer_moles, source.temperature, update=0) + source.adjust_gas(gas_type, -transfer_moles, update=0) + sink.adjust_gas_temp(gas_type, transfer_moles, source.temperature, update=0) - power_draw += specific_power_gas[g]*transfer_moles + power_draw += specific_power_gas[gas_type]*transfer_moles //Remix the resulting gases sink.update_values() @@ -181,25 +181,16 @@ if (source.total_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing return -1 - filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &= + filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &= - var/total_specific_power = 0 //the power required to remove one mole of input gas - var/total_filterable_moles = 0 //the total amount of filterable gas - var/total_unfilterable_moles = 0 //the total amount of non-filterable gas - var/list/specific_power_gas = list() //the power required to remove one mole of pure gas, for each gas type - for (var/g in source.gas) - if (source.gas[g] < MINIMUM_MOLES_TO_FILTER) + var/power_per_mol = 0 + for (var/gas_type, gas_amount in source.gas) + if (gas_amount < MINIMUM_MOLES_TO_FILTER) continue + //the power required to move all the moles of this gas from source to sink + power_per_mol += gas_amount * calculate_specific_power_gas(gas_type, source, (gas_type in filtering) ? sink_filtered : sink_clean)/ATMOS_FILTER_EFFICIENCY - if (g in filtering) - specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_filtered)/ATMOS_FILTER_EFFICIENCY - total_filterable_moles += source.gas[g] - else - specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_clean)/ATMOS_FILTER_EFFICIENCY - total_unfilterable_moles += source.gas[g] - - var/ratio = source.gas[g]/source.total_moles //converts the specific power per mole of pure gas to specific power per mole of input gas mix - total_specific_power += specific_power_gas[g]*ratio + power_per_mol /= source.total_moles //converts the sum of specific powers per mole of pure gas to specific power per mole of input gas mix //Figure out how much of each gas to filter if (isnull(total_transfer_moles)) @@ -208,8 +199,8 @@ total_transfer_moles = min(total_transfer_moles, source.total_moles) //limit transfer_moles based on available power - if (!isnull(available_power) && total_specific_power > 0) - total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power) + if (!isnull(available_power) && power_per_mol > 0) + total_transfer_moles = min(total_transfer_moles, available_power/power_per_mol) if (total_transfer_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing return -1 @@ -223,28 +214,21 @@ P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.total_volume //group_multiplier gets divided out here var/datum/gas_mixture/removed = source.remove(total_transfer_moles) - if (!removed) //Just in case + if (!removed?.total_moles) //Just in case return -1 - var/filtered_power_used = 0 //power used to move filterable gas to sink_filtered - var/unfiltered_power_used = 0 //power used to move unfilterable gas to sink_clean - for (var/g in removed.gas) - var/power_used = specific_power_gas[g]*removed.gas[g] - - if (g in filtering) - //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop. - sink_filtered.adjust_gas_temp(g, removed.gas[g], removed.temperature, update=0) - removed.adjust_gas(g, -removed.gas[g], update=0) - filtered_power_used += power_used - else - unfiltered_power_used += power_used + // total power draw + . = power_per_mol * removed.total_moles + for (var/gas_type, gas_amount in removed.gas & filtering) // only the filtered gases + //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop. + sink_filtered.adjust_gas_temp(gas_type, gas_amount, removed.temperature, update=0) + removed.adjust_gas(gas_type, -gas_amount, update=0) sink_filtered.update_values() removed.update_values() - sink_clean.merge(removed) - return filtered_power_used + unfiltered_power_used + return . //For omni devices. Instead filtering is an associative list mapping gasids to gas mixtures. //I don't like the copypasta, but I decided to keep both versions of gas filtering as filter_gas is slightly faster (doesn't create as many temporary lists, doesn't call update_values() as much) @@ -253,26 +237,15 @@ if (source.total_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing return -1 - filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &= + filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &= - var/total_specific_power = 0 //the power required to remove one mole of input gas - var/total_filterable_moles = 0 //the total amount of filterable gas - var/total_unfilterable_moles = 0 //the total amount of non-filterable gas - var/list/specific_power_gas = list() //the power required to remove one mole of pure gas, for each gas type - for (var/g in source.gas) - if (source.gas[g] < MINIMUM_MOLES_TO_FILTER) + var/power_per_mol = 0 + for (var/gas_type, gas_amount in source.gas) + if (gas_amount < MINIMUM_MOLES_TO_FILTER) continue - - if (g in filtering) - var/datum/gas_mixture/sink_filtered = filtering[g] - specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_filtered)/ATMOS_FILTER_EFFICIENCY - total_filterable_moles += source.gas[g] - else - specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_clean)/ATMOS_FILTER_EFFICIENCY - total_unfilterable_moles += source.gas[g] - - var/ratio = source.gas[g]/source.total_moles //converts the specific power per mole of pure gas to specific power per mole of input gas mix - total_specific_power += specific_power_gas[g]*ratio + power_per_mol += gas_amount * (calculate_specific_power_gas(gas_type, source, filtering[gas_type] || sink_clean)/ATMOS_FILTER_EFFICIENCY) + //converts the sum of specific powers per mole of pure gas to specific power per mole of input gas mix + power_per_mol /= source.total_moles //Figure out how much of each gas to filter if (isnull(total_transfer_moles)) @@ -281,8 +254,8 @@ total_transfer_moles = min(total_transfer_moles, source.total_moles) //limit transfer_moles based on available power - if (!isnull(available_power) && total_specific_power > 0) - total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power) + if (!isnull(available_power) && power_per_mol > 0) + total_transfer_moles = min(total_transfer_moles, available_power/power_per_mol) if (total_transfer_moles < MINIMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing return -1 @@ -299,27 +272,14 @@ if (!removed) //Just in case return -1 - var/list/filtered_power_used = list() //power used to move filterable gas to the filtered gas mixes - var/unfiltered_power_used = 0 //power used to move unfilterable gas to sink_clean - for (var/g in removed.gas) - var/power_used = specific_power_gas[g]*removed.gas[g] - - if (g in filtering) - var/datum/gas_mixture/sink_filtered = filtering[g] - //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop. - sink_filtered.adjust_gas_temp(g, removed.gas[g], removed.temperature, update=1) - removed.adjust_gas(g, -removed.gas[g], update=0) - if (power_used) - filtered_power_used[sink_filtered] = power_used - else - unfiltered_power_used += power_used - + // for some reason we used to separate filtered and unfiltered power but then we just added it back up anyway?? + var/power_draw = power_per_mol * removed.total_moles + for (var/gas_type, gas_amount in removed.gas & filtering) // keys in both with the values of the former + var/datum/gas_mixture/sink_filtered = filtering[gas_type] + //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop. + sink_filtered.adjust_gas_temp(gas_type, gas_amount, removed.temperature, update=1) + removed.adjust_gas(gas_type, -gas_amount, update=FALSE) removed.update_values() - - var/power_draw = unfiltered_power_used - for (var/datum/gas_mixture/sink_filtered in filtered_power_used) - power_draw += filtered_power_used[sink_filtered] - sink_clean.merge(removed) return power_draw diff --git a/code/modules/atmospherics/components/unary/unary_base.dm b/code/modules/atmospherics/components/unary/unary_base.dm index 6cbe3cb184b6..21734409daa8 100644 --- a/code/modules/atmospherics/components/unary/unary_base.dm +++ b/code/modules/atmospherics/components/unary/unary_base.dm @@ -12,9 +12,9 @@ /obj/machinery/atmospherics/unary/get_single_monetary_worth() . = ..() - for(var/gas in air_contents?.gas) - var/decl/material/gas_data = GET_DECL(gas) - . += gas_data.get_value() * air_contents.gas[gas] * GAS_WORTH_MULTIPLIER + for(var/gas_type, gas_amount in air_contents?.gas) + var/decl/material/gas_data = GET_DECL(gas_type) + . += gas_data.get_value() * gas_amount * GAS_WORTH_MULTIPLIER . = max(1, round(.)) /obj/machinery/atmospherics/unary/Initialize() diff --git a/code/modules/fusion/core/core_field.dm b/code/modules/fusion/core/core_field.dm index f9c366bc4960..fdd171b839c2 100644 --- a/code/modules/fusion/core/core_field.dm +++ b/code/modules/fusion/core/core_field.dm @@ -100,11 +100,11 @@ var/datum/gas_mixture/uptake_gas = owned_core.loc.return_air() if(uptake_gas) uptake_gas = uptake_gas.remove_by_flag(MAT_FLAG_FUSION_FUEL, rand(50,100), TRUE) - if(uptake_gas && uptake_gas.total_moles) - for(var/gasname in uptake_gas.gas) - if(uptake_gas.gas[gasname]*10 > reactants[gasname]) - AddParticles(gasname, uptake_gas.gas[gasname]*10) - uptake_gas.adjust_gas(gasname, -(uptake_gas.gas[gasname]), update=FALSE) + if(uptake_gas?.total_moles) + for(var/gas_type, gas_amount in uptake_gas.gas) + if(gas_amount*10 > reactants[gas_type]) + AddParticles(gas_type, gas_amount*10) + uptake_gas.adjust_gas(gas_type, -gas_amount, update=FALSE) added_particles = TRUE if(added_particles) uptake_gas.update_values() diff --git a/code/modules/maps/template_types/random_exoplanet/flora_generator.dm b/code/modules/maps/template_types/random_exoplanet/flora_generator.dm index 75c97b70ffc0..1ee8cf4f83cf 100644 --- a/code/modules/maps/template_types/random_exoplanet/flora_generator.dm +++ b/code/modules/maps/template_types/random_exoplanet/flora_generator.dm @@ -71,9 +71,9 @@ S.exude_gasses -= exuded_gases_exclusions if(length(atmos.gas)) if(S.consume_gasses) - S.consume_gasses = list(pick(atmos.gas)) // ensure that if the plant consumes a gas, the atmosphere will have it - for(var/g in atmos.gas) - var/decl/material/mat = GET_DECL(g) + S.consume_gasses = list(apick(atmos.gas)) // ensure that if the plant consumes a gas, the atmosphere will have it + for(var/gas_type in atmos.gas) + var/decl/material/mat = GET_DECL(gas_type) if(mat.gas_flags & XGM_GAS_CONTAMINANT) S.set_trait(TRAIT_TOXINS_TOLERANCE, rand(10,15)) if(prob(50)) diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index ada5e9a86c73..e515e82710ae 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -391,6 +391,9 @@ var/global/list/materials_by_gas_symbol = list() else if(isnull(ignition_point) || (new_temperature_damage_threshold > ignition_point)) temperature_damage_threshold = new_temperature_damage_threshold + global.cached_specific_heat[type] = gas_specific_heat + global.cached_molar_mass[type] = molar_mass + if(!shard_icon) shard_icon = shard_name if(!burn_armor) diff --git a/code/modules/mechs/components/body.dm b/code/modules/mechs/components/body.dm index 90890403e105..83869776a9c4 100644 --- a/code/modules/mechs/components/body.dm +++ b/code/modules/mechs/components/body.dm @@ -57,8 +57,8 @@ var/datum/gas_mixture/air = loc.return_air() if(air) //Essentially at this point its like we created a vacuum, but realistically making a bottle doesnt actually increase volume of a room and neither should a mech - for(var/g in air.gas) - cockpit.gas[g] = (air.gas[g] / air.total_volume) * cockpit.total_volume + for(var/gas_type, gas_amount in air.gas) + cockpit.gas[gas_type] = (gas_amount / air.total_volume) * cockpit.total_volume cockpit.temperature = air.temperature cockpit.update_values() diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm index 21aa9577627e..b5588d76bde8 100644 --- a/code/modules/mob/living/human/life.dm +++ b/code/modules/mob/living/human/life.dm @@ -157,9 +157,9 @@ var/adjusted_pressure = calculate_affecting_pressure(pressure) //Check for contaminants before anything else because we don't want to skip it. - for(var/g in environment.gas) - var/decl/material/mat = GET_DECL(g) - if((mat.gas_flags & XGM_GAS_CONTAMINANT) && environment.gas[g] > mat.gas_overlay_limit + 1) + for(var/gas_type, gas_amount in environment.gas) + var/decl/material/mat = GET_DECL(gas_type) + if((mat.gas_flags & XGM_GAS_CONTAMINANT) && gas_amount > mat.gas_overlay_limit + 1) handle_contaminants() break diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index b113096f13be..53c6a4859bfe 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -234,11 +234,11 @@ var/t_moles = env.total_moles var/gases[0] - for(var/g in env.gas) + for(var/gas_type, gas_amount in env.gas) var/gas[0] - var/decl/material/mat = GET_DECL(g) + var/decl/material/mat = GET_DECL(gas_type) gas["name"] = capitalize(mat.gas_name) - gas["percent"] = round((env.gas[g] / t_moles) * 100) + gas["percent"] = round((gas_amount / t_moles) * 100) gases[++gases.len] = gas data["gas"] = gases diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index 7b62adcc2ed0..7c30f1cfcc6d 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -496,12 +496,11 @@ var/global/list/simplemob_icon_bitflag_cache = list() if(!level_data.exterior_atmosphere) return - for(var/gas in level_data.exterior_atmosphere.gas) - var/gas_amt = level_data.exterior_atmosphere.gas[gas] + for(var/gas_type, gas_amt in level_data.exterior_atmosphere.gas) if(min_gas) - min_gas[gas] = round(gas_amt * 0.5) + min_gas[gas_type] = round(gas_amt * 0.5) if(max_gas) - min_gas[gas] = round(gas_amt * 1.5) + min_gas[gas_type] = round(gas_amt * 1.5) // Simple filler bodytype so animals get offsets for their inventory slots. /decl/bodytype/animal diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index 73f2054e5696..1090bb6e6a22 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -410,11 +410,11 @@ exterior_atmosphere.update_values() //Might as well update exterior_atmosphere.check_tile_graphic() return - var/list/exterior_atmos_composition = exterior_atmosphere + var/alist/exterior_atmos_composition = exterior_atmosphere exterior_atmosphere = new - if(islist(exterior_atmos_composition)) - for(var/gas in exterior_atmos_composition) - exterior_atmosphere.adjust_gas(gas, exterior_atmos_composition[gas], FALSE) + if(istype(exterior_atmos_composition, /alist)) + for(var/gas, gas_amount in exterior_atmos_composition) + exterior_atmosphere.adjust_gas(gas, gas_amount, FALSE) exterior_atmosphere.temperature = exterior_atmos_temp exterior_atmosphere.update_values() exterior_atmosphere.check_tile_graphic() diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index aeea20125422..934534fc6323 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -210,9 +210,9 @@ if(!failed_inhale) // Enough gas to tell we're being poisoned via chemical burns or whatever. var/poison_total = 0 if(poison_types) - for(var/gname in breath.gas) - if(poison_types[gname]) - poison_total += breath.gas[gname] + for(var/gas_type, gas_amount in breath.gas) + if(poison_types[gas_type]) + poison_total += gas_amount if(((poison_total/breath.total_moles)*breath_pressure) > safe_toxins_max) SET_HUD_ALERT(owner, HUD_TOX, 1) diff --git a/code/modules/overmap/planetoids/_planetoids.dm b/code/modules/overmap/planetoids/_planetoids.dm index 16ea48dfe92b..f28b871ef26b 100644 --- a/code/modules/overmap/planetoids/_planetoids.dm +++ b/code/modules/overmap/planetoids/_planetoids.dm @@ -56,9 +56,9 @@ if(atmosphere) if(user.skill_check(SKILL_SCIENCE, SKILL_EXPERT) || user.skill_check(SKILL_ATMOS, SKILL_EXPERT)) var/list/gases = list() - for(var/g in atmosphere.gas) - if(atmosphere.gas[g] > atmosphere.total_moles * 0.05) - var/decl/material/mat = GET_DECL(g) + for(var/gas_type, gas_amount in atmosphere.gas) + if(gas_amount > atmosphere.total_moles * 0.05) + var/decl/material/mat = GET_DECL(gas_type) gases += mat.gas_name . += "Atmosphere composition: [english_list(gases)]
    " var/inaccuracy = rand(8,12)/10 diff --git a/code/modules/overmap/planetoids/planetoid_skybox.dm b/code/modules/overmap/planetoids/planetoid_skybox.dm index d9528a9bfc38..f0b30e4bc25b 100644 --- a/code/modules/overmap/planetoids/planetoid_skybox.dm +++ b/code/modules/overmap/planetoids/planetoid_skybox.dm @@ -27,10 +27,8 @@ var/list/colors = list() for(var/lvl in map_z) var/datum/level_data/level_data = SSmapping.levels_by_z[lvl] - ///#TODO: Check if the z-level is visible from space - for(var/g in level_data.exterior_atmosphere?.gas) - var/decl/material/mat = GET_DECL(g) - colors += mat.color + if(level_data.exterior_atmosphere) + colors += level_data.exterior_atmosphere.get_overall_color() if(length(colors)) return MixColors(colors) diff --git a/code/modules/overmap/ships/device_types/gas_thruster.dm b/code/modules/overmap/ships/device_types/gas_thruster.dm index 1f09c65b3187..1c9c32381827 100644 --- a/code/modules/overmap/ships/device_types/gas_thruster.dm +++ b/code/modules/overmap/ships/device_types/gas_thruster.dm @@ -74,12 +74,12 @@ if(!propellant || !length(propellant.gas) || !propellant.total_moles) return 0.01 // Divide by zero protection. - for(var/mat in propellant.gas) + for(var/mat, amt in propellant.gas) var/decl/material/gas/G = GET_DECL(mat) // 0.08 chosen to get the RATIO of the specific heat, we don't have cV/cP here, so this is a rough approximate. var/ratio = (G.gas_specific_heat / 25) + 0.8// These numbers are meaningless, just magic numbers to calibrate range. - ratio_specific_heat += ratio * (propellant.gas[mat] / propellant.total_moles) - ratio_specific_heat = ratio_specific_heat / length(propellant.gas) + ratio_specific_heat += ratio * (amt / propellant.total_moles) + ratio_specific_heat /= length(propellant.gas) if(ratio_specific_heat == 0 || ratio_specific_heat == 1) // rare case of avoiding a divide by zero error. ratio_specific_heat += 0.01 diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index cd70fdbc44a5..b271e18090ea 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -64,7 +64,7 @@ var/global/list/rad_collectors = list() receive_pulse(12.5*(last_rads/max_rads)/(0.3+(last_rads/max_rads))) if(loaded_tank) - if(loaded_tank.air_contents.gas[/decl/material/gas/hydrogen] == 0) + if(!loaded_tank.air_contents.get_gas(/decl/material/gas/hydrogen)) investigate_log("out of fuel.","singulo") eject() else @@ -85,7 +85,8 @@ var/global/list/rad_collectors = list() toggle_power() user.visible_message("[user.name] turns \the [src] [active? "on":"off"].", \ "You turn \the [src] [active? "on":"off"].") - investigate_log("turned [active?"on":"off"] by [user.key]. [loaded_tank?"Fuel: [round(loaded_tank.air_contents.gas[/decl/material/gas/hydrogen]/0.29)]%":"It is empty"].","singulo") + // WHY DOES THIS DIVIDE BY 0.29 + investigate_log("turned [active?"on":"off"] by [user.key]. [loaded_tank?"Fuel: [round(loaded_tank.air_contents.get_gas(/decl/material/gas/hydrogen)/0.29)]%":"It is empty"].","singulo") else to_chat(user, SPAN_WARNING("The controls are locked!")) @@ -177,7 +178,7 @@ var/global/list/rad_collectors = list() /obj/machinery/rad_collector/proc/receive_pulse(var/pulse_strength) if(loaded_tank && active) var/power_produced = 0 - power_produced = min(100*loaded_tank.air_contents.gas[/decl/material/gas/hydrogen]*pulse_strength*pulse_coeff,max_power) + power_produced = min(100*loaded_tank.air_contents.get_gas(/decl/material/gas/hydrogen)*pulse_strength*pulse_coeff,max_power) generate_power(power_produced) last_power_new = power_produced return diff --git a/code/modules/scanners/gas.dm b/code/modules/scanners/gas.dm index 397ea46d74bf..3637da7d02e4 100644 --- a/code/modules/scanners/gas.dm +++ b/code/modules/scanners/gas.dm @@ -57,14 +57,14 @@ . += "Pressure: [round(pressure,0.01)] kPa" var/perGas_add_string = "" - for(var/mix in mixture.gas) - var/percentage = round(mixture.gas[mix]/total_moles * 100, 0.01) + for(var/gas_type, gas_moles in mixture.gas) + var/percentage = round(gas_moles/total_moles * 100, 0.01) if(!percentage) continue - var/decl/material/mat = GET_DECL(mix) + var/decl/material/mat = GET_DECL(gas_type) switch(mode) if(MV_MODE) - perGas_add_string = ", Moles: [round(mixture.gas[mix], 0.01)]" + perGas_add_string = ", Moles: [round(gas_moles, 0.01)]" if(MAT_TRAIT_MODE) var/list/traits = list() if(mat.gas_flags & XGM_GAS_FUEL) diff --git a/code/modules/xgm/xgm_gas_mixture.dm b/code/modules/xgm/xgm_gas_mixture.dm index d88010d37564..59b19c52794e 100644 --- a/code/modules/xgm/xgm_gas_mixture.dm +++ b/code/modules/xgm/xgm_gas_mixture.dm @@ -1,7 +1,16 @@ +// These variables are used to speed up certain calculations by using dot products. +var/global/alist/cached_specific_heat = alist() +var/global/alist/cached_molar_mass = alist() +var/global/alist/cached_mat_r = alist() +var/global/alist/cached_mat_g = alist() +var/global/alist/cached_mat_b = alist() +var/global/alist/cached_mat_a = alist() +var/global/alist/cached_mat_color_weight = alist() + /datum/gas_mixture //Associative list of gas moles. //Gases with 0 moles are not tracked and are pruned by update_values() - var/list/gas = list() + var/alist/gas = alist() //Temperature in Kelvin of this gas mix. var/temperature = 0 @@ -86,11 +95,12 @@ temperature = (giver.temperature*giver_heat_capacity + temperature*self_heat_capacity)/combined_heat_capacity if((group_multiplier != 1)||(giver.group_multiplier != 1)) - for(var/g in giver.gas) - gas[g] += giver.gas[g] * giver.group_multiplier / group_multiplier + var/scale_factor = giver.group_multiplier / group_multiplier + for(var/gas_type, gas_amount in giver.gas) + gas[gas_type] += gas_amount * scale_factor else - for(var/g in giver.gas) - gas[g] += giver.gas[g] + for(var/gas_type, gas_amount in giver.gas) + gas[gas_type] += gas_amount update_values() return TRUE @@ -105,11 +115,13 @@ gas.Cut() sharer.gas.Cut() - for(var/g in gas|sharer.gas) - var/comb = gas[g] + sharer.gas[g] - comb /= total_volume + sharer.total_volume - gas[g] = comb * total_volume - sharer.gas[g] = comb * sharer.total_volume + var/scale_factor = total_volume + sharer.total_volume + var/origin_scale_factor = total_volume / scale_factor + var/sharer_scale_factor = sharer.total_volume / scale_factor + for(var/gas_type in gas|sharer.gas) // we can only iterate keys here since merging alists doesn't combine values + var/comb = gas[gas_type] + sharer.gas[gas_type] + gas[gas_type] = comb * origin_scale_factor + sharer.gas[gas_type] = comb * sharer_scale_factor if(our_heatcap + share_heatcap) temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap) @@ -123,12 +135,7 @@ //Returns the heat capacity of the gas mix based on the specific heat of the gases. /datum/gas_mixture/proc/heat_capacity() - . = 0 - for(var/g in gas) - var/decl/material/mat = GET_DECL(g) - . += mat.gas_specific_heat * gas[g] - . *= max(1, group_multiplier) - + return values_dot(gas, global.cached_specific_heat) * max(1, group_multiplier) //Adds or removes thermal energy. Returns the actual thermal energy change, as in the case of removing energy we can't go below TCMB. /datum/gas_mixture/proc/add_thermal_energy(var/thermal_energy) @@ -152,7 +159,6 @@ /datum/gas_mixture/proc/get_thermal_energy_change(var/new_temperature) return heat_capacity()*(max(new_temperature, 0) - temperature) - //Technically vacuum doesn't have a specific entropy. Just use a really big number (infinity would be ideal) here so that it's easy to add gas to vacuum and hard to take gas out. #define SPECIFIC_ENTROPY_VACUUM 150000 @@ -197,13 +203,8 @@ //Updates the total_moles count and trims any empty gases. /datum/gas_mixture/proc/update_values() - total_moles = 0 - for(var/g in gas) - if(gas[g] <= 0) - gas -= g - else - total_moles += gas[g] - + values_cut_under(gas, ATMOS_PRECISION) + total_moles = values_sum(gas) //Mark the cached color for update cached_mix_color = null @@ -222,9 +223,9 @@ var/datum/gas_mixture/removed = new - for(var/g in gas) - removed.gas[g] = QUANTIZE((gas[g] / total_moles) * amount) - gas[g] -= removed.gas[g] / group_multiplier + for(var/gas_type, gas_amount in gas) + removed.gas[gas_type] = QUANTIZE((gas_amount / total_moles) * amount) + gas[gas_type] -= removed.gas[gas_type] / group_multiplier removed.temperature = temperature update_values() @@ -244,9 +245,9 @@ var/datum/gas_mixture/removed = new removed.group_multiplier = out_group_multiplier - for(var/g in gas) - removed.gas[g] = (gas[g] * ratio * group_multiplier / out_group_multiplier) - gas[g] = gas[g] * (1 - ratio) + for(var/gas_type, gas_amount in gas) + removed.gas[gas_type] = (gas_amount * ratio * group_multiplier / out_group_multiplier) + gas[gas_type] = gas_amount * (1 - ratio) removed.temperature = temperature removed.total_volume = total_volume * group_multiplier / out_group_multiplier @@ -269,18 +270,18 @@ return removed var/sum = 0 - for(var/g in gas) - var/decl/material/mat = GET_DECL(g) - var/list/check = mat_flag ? mat.flags : mat.gas_flags + for(var/gas_type, gas_amount in gas) + var/decl/material/mat = GET_DECL(gas_type) + var/check = mat_flag ? mat.flags : mat.gas_flags if(check & flag) - sum += gas[g] + sum += gas_amount - for(var/g in gas) - var/decl/material/mat = GET_DECL(g) - var/list/check = mat_flag ? mat.flags : mat.gas_flags + for(var/gas_type, gas_amount in gas) + var/decl/material/mat = GET_DECL(gas_type) + var/check = mat_flag ? mat.flags : mat.gas_flags if(check & flag) - removed.gas[g] = QUANTIZE((gas[g] / sum) * amount) - gas[g] -= removed.gas[g] / group_multiplier + removed.gas[gas_type] = QUANTIZE((gas_amount / sum) * amount) + gas[gas_type] -= removed.gas[gas_type] / group_multiplier removed.temperature = temperature update_values() @@ -291,10 +292,10 @@ //Returns the amount of gas that has the given flag, in moles /datum/gas_mixture/proc/get_by_flag(flag) . = 0 - for(var/g in gas) - var/decl/material/mat = GET_DECL(g) + for(var/gas_type, gas_amount in gas) + var/decl/material/mat = GET_DECL(gas_type) if(mat.gas_flags & flag) - . += gas[g] + . += gas_amount //Copies gas and temperature from another gas_mixture. /datum/gas_mixture/proc/copy_from(const/datum/gas_mixture/sample) @@ -324,22 +325,22 @@ if(total_moles == 0 && sample.total_moles != 0 || sample.total_moles == 0 && total_moles != 0) return 0 - var/list/marked = list() - for(var/g in gas) - if((abs(gas[g] - sample.gas[g]) > MINIMUM_AIR_TO_SUSPEND) && \ - ((gas[g] < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]) || \ - (gas[g] > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]))) + var/alist/marked = alist() + for(var/gas_type, gas_amount in gas) + if((abs(gas_amount - sample.gas[gas_type]) > MINIMUM_AIR_TO_SUSPEND) && \ + ((gas_amount < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[gas_type]) || \ + (gas_amount > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[gas_type]))) return 0 - marked[g] = 1 + marked[gas_type] = 1 if(abs(return_pressure() - sample.return_pressure()) > MINIMUM_PRESSURE_DIFFERENCE_TO_SUSPEND) return 0 - for(var/g in sample.gas) - if(!marked[g]) - if((abs(gas[g] - sample.gas[g]) > MINIMUM_AIR_TO_SUSPEND) && \ - ((gas[g] < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]) || \ - (gas[g] > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]))) + for(var/sample_type, sample_moles in sample.gas) + if(!marked[sample_type]) + if((abs(gas[sample_type] - sample_moles) > MINIMUM_AIR_TO_SUSPEND) && \ + ((gas[sample_type] < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample_moles) || \ + (gas[sample_type] > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample_moles))) return 0 if(total_moles > MINIMUM_AIR_TO_SUSPEND) @@ -358,13 +359,13 @@ for(var/obj/effect/gas_overlay/O in graphic) if(gas[O.material.type] <= O.material.gas_overlay_limit) LAZYADD(graphic_remove, O) - for(var/g in gas) + for(var/gas_type, gas_amount in gas) + var/decl/material/mat = GET_DECL(gas_type) //Overlay isn't applied for this gas, check if it's valid and needs to be added. - var/decl/material/mat = GET_DECL(g) - if(!isnull(mat.gas_overlay_limit) && gas[g] > mat.gas_overlay_limit) - if(!LAZYACCESS(tile_overlay_cache, g)) - LAZYSET(tile_overlay_cache, g, new /obj/effect/gas_overlay(null, g)) - var/tile_overlay = tile_overlay_cache[g] + if(!isnull(mat.gas_overlay_limit) && gas_amount > mat.gas_overlay_limit) + if(!LAZYACCESS(tile_overlay_cache, gas_type)) + LAZYSET(tile_overlay_cache, gas_type, new /obj/effect/gas_overlay(null, gas_type)) + var/tile_overlay = tile_overlay_cache[gas_type] if(!(tile_overlay in graphic)) LAZYADD(graphic_add, tile_overlay) . = FALSE @@ -413,16 +414,16 @@ var/full_heat_capacity = heat_capacity() var/s_full_heat_capacity = other.heat_capacity() - var/list/avg_gas = list() + var/alist/avg_gas = alist() + var/scale_factor = (size + share_size) + var/self_scale_factor = size / scale_factor + var/other_scale_factor = share_size / scale_factor - for(var/g in gas) - avg_gas[g] += gas[g] * size + for(var/gas_type, gas_moles in gas) + avg_gas[gas_type] += gas_moles * self_scale_factor - for(var/g in other.gas) - avg_gas[g] += other.gas[g] * share_size - - for(var/g in avg_gas) - avg_gas[g] /= (size + share_size) + for(var/gas_type, gas_moles in other.gas) + avg_gas[gas_type] += gas_moles * other_scale_factor var/temp_avg = 0 if(full_heat_capacity + s_full_heat_capacity) @@ -433,10 +434,10 @@ ratio = sharing_lookup_table[connecting_tiles] //WOOT WOOT DO NOT TOUCH THIS. - for(var/g in avg_gas) - gas[g] = max(0, (gas[g] - avg_gas[g]) * (1 - ratio) + avg_gas[g]) + for(var/gas_type, gas_amount in avg_gas) + gas[gas_type] = max(0, (gas[gas_type] - gas_amount) * (1 - ratio) + gas_amount) if(!one_way) - other.gas[g] = max(0, (other.gas[g] - avg_gas[g]) * (1 - ratio) + avg_gas[g]) + other.gas[gas_type] = max(0, (other.gas[gas_type] - gas_amount) * (1 - ratio) + gas_amount) temperature = max(0, (temperature - temp_avg) * (1-ratio) + temp_avg) if(!one_way) @@ -459,14 +460,14 @@ var/total_thermal_energy = 0 var/total_heat_capacity = 0 - var/list/total_gas = list() + var/alist/total_gas = alist() for(var/datum/gas_mixture/gasmix in gases) total_volume += gasmix.total_volume var/temp_heatcap = gasmix.heat_capacity() total_thermal_energy += gasmix.temperature * temp_heatcap total_heat_capacity += temp_heatcap - for(var/g in gasmix.gas) - total_gas[g] += gasmix.gas[g] + for(var/gas_type, gas_amount in gasmix.gas) + total_gas[gas_type] += gas_amount if(total_volume > 0) var/datum/gas_mixture/combined = new(total_volume) @@ -481,8 +482,7 @@ combined.react() //Average out the gases - for(var/g in combined.gas) - combined.gas[g] /= total_volume + combined.divide(total_volume) //Update individual gas_mixtures for(var/datum/gas_mixture/gasmix in gases) @@ -493,10 +493,7 @@ return 1 /datum/gas_mixture/proc/get_mass() - . = 0 - for(var/g in gas) - var/decl/material/mat = GET_DECL(g) - . += gas[g] * mat.molar_mass * group_multiplier + return values_dot(gas, global.cached_molar_mass) * group_multiplier /datum/gas_mixture/proc/specific_mass() var/M = get_total_moles() @@ -507,30 +504,23 @@ ///Returns a color blended from all materials the gas mixture contains /datum/gas_mixture/proc/get_overall_color() if(!cached_mix_color) - if(!LAZYLEN(gas)) + if(!length(gas)) cached_mix_color = "#ffffffff" return cached_mix_color - if(LAZYLEN(gas) == 1) - var/decl/material/G = GET_DECL(gas[1]) - cached_mix_color = G.color + num2hex(G.opacity * 255) + if(length(gas) == 1) + for(var/gas_type in gas) + var/decl/material/G = GET_DECL(gas_type) + cached_mix_color = G.color + num2hex(G.opacity * 255) return cached_mix_color //If we really have to, add up all colors - var/list/colors = list(0, 0, 0, 0) - var/total_color_weight = 0 - - for(var/mat_path in gas) + cached_mix_color = rgb(255,255,255,255) + for(var/mat_path, mat_moles in gas) var/decl/material/G = GET_DECL(mat_path) if(G.color_weight <= 0) continue var/hex = uppertext(G.color) + num2hex(G.opacity * 255) - var/mod = gas[mat_path] * G.color_weight - colors[1] += HEX_RED(hex) * mod - colors[2] += HEX_GREEN(hex) * mod - colors[3] += HEX_BLUE(hex) * mod - colors[4] += HEX_ALPHA(hex) * mod - total_color_weight += mod - cached_mix_color = rgb(colors[1] / total_color_weight, colors[2] / total_color_weight, colors[3] / total_color_weight, colors[4] / total_color_weight) + cached_mix_color = BlendHSV(cached_mix_color, hex, (mat_moles * G.color_weight) / total_moles) return cached_mix_color diff --git a/code/unit_tests/atmospherics_tests.dm b/code/unit_tests/atmospherics_tests.dm index e28fcc06a378..ea7ed3b20eb3 100644 --- a/code/unit_tests/atmospherics_tests.dm +++ b/code/unit_tests/atmospherics_tests.dm @@ -62,7 +62,7 @@ test_cases = list( uphill = list( source = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 5, /decl/material/gas/nitrogen = 10, /decl/material/gas/carbon_dioxide = 5, @@ -72,7 +72,7 @@ temperature = T20C - 5, ), sink = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 10, /decl/material/gas/nitrogen = 20, /decl/material/gas/carbon_dioxide = 10, @@ -84,7 +84,7 @@ ), downhill = list( source = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 10, /decl/material/gas/nitrogen = 20, /decl/material/gas/carbon_dioxide = 10, @@ -94,7 +94,7 @@ temperature = T20C + 5, ), sink = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 5, /decl/material/gas/nitrogen = 10, /decl/material/gas/carbon_dioxide = 5, @@ -106,7 +106,7 @@ ), flat = list( source = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 10, /decl/material/gas/nitrogen = 20, /decl/material/gas/carbon_dioxide = 10, @@ -116,7 +116,7 @@ temperature = T20C, ), sink = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 10, /decl/material/gas/nitrogen = 20, /decl/material/gas/carbon_dioxide = 10, @@ -128,7 +128,7 @@ ), vacuum_sink = list( source = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 10, /decl/material/gas/nitrogen = 20, /decl/material/gas/carbon_dioxide = 10, @@ -138,17 +138,17 @@ temperature = T20C, ), sink = list( - initial_gas = list(), + initial_gas = alist(), temperature = 0, ), ), vacuum_source = list( source = list( - initial_gas = list(), + initial_gas = alist(), temperature = 0, ), sink = list( - initial_gas = list( + initial_gas = alist( /decl/material/gas/oxygen = 10, /decl/material/gas/nitrogen = 20, /decl/material/gas/carbon_dioxide = 10, diff --git a/maps/away/bearcat/bearcat.dm b/maps/away/bearcat/bearcat.dm index dc13ca07d873..c022e64c5308 100644 --- a/maps/away/bearcat/bearcat.dm +++ b/maps/away/bearcat/bearcat.dm @@ -89,16 +89,16 @@ door_color = COLOR_AMBER /turf/floor/usedup - initial_gas = list(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) + initial_gas = alist(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) /turf/floor/tiled/usedup - initial_gas = list(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) + initial_gas = alist(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) /turf/floor/tiled/dark/usedup - initial_gas = list(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) + initial_gas = alist(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) /turf/floor/tiled/white/usedup - initial_gas = list(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) + initial_gas = alist(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) /obj/abstract/landmark/corpse/deadcap name = "Dead Captain" diff --git a/maps/planets/test_planet/test_planet.dm b/maps/planets/test_planet/test_planet.dm index 83c42703e654..9d7d0f324d71 100644 --- a/maps/planets/test_planet/test_planet.dm +++ b/maps/planets/test_planet/test_planet.dm @@ -7,7 +7,7 @@ /datum/gas_mixture/atmos_neutralia temperature = T20C - gas = list( + gas = alist( /decl/material/gas/oxygen = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD, ) diff --git a/maps/random_ruins/exoplanet_ruins/lodge/lodge.dm b/maps/random_ruins/exoplanet_ruins/lodge/lodge.dm index 893ebdb5c648..b92079557e2e 100644 --- a/maps/random_ruins/exoplanet_ruins/lodge/lodge.dm +++ b/maps/random_ruins/exoplanet_ruins/lodge/lodge.dm @@ -7,4 +7,4 @@ template_tags = TEMPLATE_TAG_HUMAN|TEMPLATE_TAG_HABITAT /turf/floor/wood/usedup - initial_gas = list(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) \ No newline at end of file + initial_gas = alist(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD) \ No newline at end of file diff --git a/maps/shaded_hills/levels/_levels.dm b/maps/shaded_hills/levels/_levels.dm index 411714f57c56..cf7081b88e51 100644 --- a/maps/shaded_hills/levels/_levels.dm +++ b/maps/shaded_hills/levels/_levels.dm @@ -9,7 +9,7 @@ ambient_light_level = 1 ambient_light_color = "#f3e6ca" strata = /decl/strata/shaded_hills - exterior_atmosphere = list( + exterior_atmosphere = alist( /decl/material/gas/oxygen = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD ) diff --git a/mods/content/government/ruins/ec_old_crash/ec_old_crash.dm b/mods/content/government/ruins/ec_old_crash/ec_old_crash.dm index bc42e4e157af..4942a9d83b02 100644 --- a/mods/content/government/ruins/ec_old_crash/ec_old_crash.dm +++ b/mods/content/government/ruins/ec_old_crash/ec_old_crash.dm @@ -43,10 +43,10 @@ external_pressure_bound = 0.25 ATM /turf/floor/tiled/lowpressure - initial_gas = list(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD) + initial_gas = alist(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD) /turf/floor/tiled/white/lowpressure - initial_gas = list(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD) + initial_gas = alist(/decl/material/gas/carbon_dioxide = MOLES_O2STANDARD) /obj/item/disk/astrodata name = "astronomical data disk" diff --git a/mods/species/ascent/turfs/ship.dm b/mods/species/ascent/turfs/ship.dm index 320a2d8c30e2..03e991d57619 100644 --- a/mods/species/ascent/turfs/ship.dm +++ b/mods/species/ascent/turfs/ship.dm @@ -43,7 +43,7 @@ _base_flooring = /decl/flooring/plating/ascent icon_state = "curvy" icon = 'icons/turf/flooring/alium.dmi' - initial_gas = list( + initial_gas = alist( /decl/material/gas/methyl_bromide = MOLES_CELLSTANDARD * 0.5, /decl/material/gas/oxygen = MOLES_CELLSTANDARD * 0.5 ) @@ -58,7 +58,7 @@ icon_state = "jaggy" color = COLOR_GRAY40 _flooring = /decl/flooring/tiling_ascent - initial_gas = list( + initial_gas = alist( /decl/material/gas/methyl_bromide = MOLES_CELLSTANDARD * 0.5, /decl/material/gas/oxygen = MOLES_CELLSTANDARD * 0.5 ) From 78ac787f1bfbedac630a9f1d9656270583fc374f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 9 May 2026 10:18:19 -0400 Subject: [PATCH 1225/1331] Fix nutriment being HAAAAARDEEEEEEER THAN STEEEEL --- code/modules/reagents/chems/chems_nutriment.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index b8b7637fd6ff..af6c79f205d2 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -14,6 +14,7 @@ nutriment_factor = 10 affect_blood_on_ingest = 0 affect_blood_on_inhale = 0 + hardness = MAT_VALUE_SOFT // same softness as meat // Technically a room-temperature solid, but saves // repathing it to /solid all over the codebase. From da51ea6028f7ce55b52bec4e3ed4d937903fb213 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 11 May 2026 18:10:46 +1000 Subject: [PATCH 1226/1331] Converting hnoll and kobaloi ears to use the ears/horns accessories. --- code/__defines/mobs.dm | 1 + code/modules/sprite_accessories/_accessory.dm | 33 ++++--- .../cosmetics/_accessory_cosmetics.dm | 1 + .../ears/_accessory_ears.dm | 11 +-- .../horns/_accessory_horns.dm | 1 + .../metadata/_accessory_metadata.dm | 1 + .../metadata/accessory_metadata_color.dm | 6 ++ .../tails/_accessory_tail.dm | 2 +- mods/content/fantasy/_fantasy.dme | 3 + mods/content/fantasy/datum/hnoll/bodytypes.dm | 8 +- mods/content/fantasy/datum/hnoll/ears.dm | 13 +++ mods/content/fantasy/datum/hnoll/markings.dm | 36 -------- .../fantasy/datum/kobaloi/bodytypes.dm | 5 +- mods/content/fantasy/datum/kobaloi/ears.dm | 26 ++++++ mods/content/fantasy/datum/kobaloi/horns.dm | 49 +++++++++++ .../content/fantasy/datum/kobaloi/markings.dm | 82 ------------------ mods/content/fantasy/datum/kobaloi/species.dm | 10 +++ mods/content/fantasy/icons/hnoll/ears.dmi | Bin 0 -> 576 bytes mods/content/fantasy/icons/hnoll/markings.dmi | Bin 6542 -> 5018 bytes mods/content/fantasy/icons/kobaloi/ears.dmi | Bin 0 -> 749 bytes .../fantasy/icons/kobaloi/markings.dmi | Bin 3691 -> 2126 bytes mods/species/drakes/species_bodytypes.dm | 15 ++-- 22 files changed, 151 insertions(+), 152 deletions(-) create mode 100644 mods/content/fantasy/datum/hnoll/ears.dm create mode 100644 mods/content/fantasy/datum/kobaloi/ears.dm create mode 100644 mods/content/fantasy/datum/kobaloi/horns.dm create mode 100644 mods/content/fantasy/icons/hnoll/ears.dmi create mode 100644 mods/content/fantasy/icons/kobaloi/ears.dmi diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 77b1dae70dd5..3838c2a29f2d 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -381,6 +381,7 @@ var/global/list/dexterity_levels = list( // Sprite accessory metadata types for shorter reference. #define SAM_COLOR /decl/sprite_accessory_metadata/color #define SAM_COLOR_INNER /decl/sprite_accessory_metadata/color/alt +#define SAM_COLOR_EXTRA /decl/sprite_accessory_metadata/color/extra #define SAM_GRADIENT /decl/sprite_accessory_metadata/gradient // Helpers for setting mob appearance. They are extremely ugly, hence the helpers. diff --git a/code/modules/sprite_accessories/_accessory.dm b/code/modules/sprite_accessories/_accessory.dm index 7993cfbb561f..9484749376f3 100644 --- a/code/modules/sprite_accessories/_accessory.dm +++ b/code/modules/sprite_accessories/_accessory.dm @@ -77,6 +77,11 @@ var/is_whitelisted /// A set of trait levels to check for. var/list/required_traits + /// A list of metadata types to attempt to apply after initial overlay gen. + var/list/additional_states = list( + (SAM_COLOR_INNER), + (SAM_COLOR_EXTRA) + ) /decl/sprite_accessory/Initialize() . = ..() @@ -205,9 +210,6 @@ accessory_icon = icon(use_icon, use_state) - // Inner overlay and color. - var/inner_color = LAZYACCESS(metadata, SAM_COLOR_INNER) - // Base icon and color. if(!isnull(color_blend)) var/decl/sprite_accessory_metadata/gradient/gradient_metadata = GET_DECL(SAM_GRADIENT) @@ -218,21 +220,28 @@ gradient_icon = null if(gradient_icon) gradient_icon.Blend(accessory_icon, ICON_AND) - if(!isnull(inner_color)) - gradient_icon.Blend(inner_color, color_blend) + var/gradient_color = LAZYACCESS(metadata, SAM_COLOR_INNER) + if(!isnull(gradient_color)) + gradient_icon.Blend(gradient_color, color_blend) var/color = LAZYACCESS(metadata, SAM_COLOR) if(!isnull(color)) accessory_icon.Blend(color, color_blend) if(gradient_icon) accessory_icon.Blend(gradient_icon, ICON_OVERLAY) - if(!isnull(inner_color)) - var/inner_state = "[use_state]_inner" - if(check_state_in_icon(inner_state, use_icon)) - var/icon/inner_icon = icon(use_icon, inner_state) - if(!isnull(color_blend)) - inner_icon.Blend(inner_color, color_blend) - accessory_icon.Blend(inner_icon, ICON_OVERLAY) + // Additional overlays based on metadata (inner and extra) + for(var/extra_metadata_type in additional_states) + var/decl/sprite_accessory_metadata/extra_metadata = RESOLVE_TO_DECL(extra_metadata_type) + if(!istype(extra_metadata) || !extra_metadata.additional_icon_state) + continue + var/extra_color = LAZYACCESS(metadata, SAM_COLOR_EXTRA) + if(!isnull(extra_color)) + var/extra_state = "[use_state][extra_metadata.additional_icon_state]" + if(check_state_in_icon(extra_state, use_icon)) + var/icon/extra_icon = icon(use_icon, extra_state) + if(!isnull(color_blend)) + extra_icon.Blend(extra_color, color_blend) + accessory_icon.Blend(extra_icon, ICON_OVERLAY) // Clip the icon if needed. if(mask_to_bodypart) diff --git a/code/modules/sprite_accessories/cosmetics/_accessory_cosmetics.dm b/code/modules/sprite_accessories/cosmetics/_accessory_cosmetics.dm index 91df82be7f3d..64a414b685cd 100644 --- a/code/modules/sprite_accessories/cosmetics/_accessory_cosmetics.dm +++ b/code/modules/sprite_accessories/cosmetics/_accessory_cosmetics.dm @@ -1,5 +1,6 @@ /decl/sprite_accessory_category/cosmetics name = "Cosmetics" + single_selection = FALSE default_accessory = /decl/sprite_accessory/cosmetics/none base_accessory_type = /decl/sprite_accessory/cosmetics uid = "acc_cat_cosmetics" diff --git a/code/modules/sprite_accessories/ears/_accessory_ears.dm b/code/modules/sprite_accessories/ears/_accessory_ears.dm index bc64873f19a0..c7827c1a8f6d 100644 --- a/code/modules/sprite_accessories/ears/_accessory_ears.dm +++ b/code/modules/sprite_accessories/ears/_accessory_ears.dm @@ -16,7 +16,7 @@ color_blend = ICON_MULTIPLY /decl/sprite_accessory/ears/none - name = "Default Ears" + name = "No Custom Ears" icon_state = "none" uid = "acc_ears_none" bodytypes_allowed = null @@ -28,12 +28,3 @@ body_flags_denied = null grooming_flags = null draw_accessory = FALSE - -/* -// Leaving this in for reference. -/decl/sprite_accessory/ears/debug - name = "Debug Two-Tone Ears" - uid = "acc_ears_debug" - icon_state = "debug" - accessory_metadata_types = list(SAM_COLOR, SAM_COLOR_INNER) -*/ \ No newline at end of file diff --git a/code/modules/sprite_accessories/horns/_accessory_horns.dm b/code/modules/sprite_accessories/horns/_accessory_horns.dm index 4526113a70f8..35386d8c336e 100644 --- a/code/modules/sprite_accessories/horns/_accessory_horns.dm +++ b/code/modules/sprite_accessories/horns/_accessory_horns.dm @@ -3,6 +3,7 @@ base_accessory_type = /decl/sprite_accessory/horns default_accessory = /decl/sprite_accessory/horns/none uid = "acc_cat_horns" + single_selection = FALSE /decl/sprite_accessory/horns hidden_by_gear_slot = slot_head_str diff --git a/code/modules/sprite_accessories/metadata/_accessory_metadata.dm b/code/modules/sprite_accessories/metadata/_accessory_metadata.dm index 94e0d0e3d4d9..98052400f4bc 100644 --- a/code/modules/sprite_accessories/metadata/_accessory_metadata.dm +++ b/code/modules/sprite_accessories/metadata/_accessory_metadata.dm @@ -3,6 +3,7 @@ decl_flags = DECL_FLAG_MANDATORY_UID var/name var/default_value + var/additional_icon_state /decl/sprite_accessory_metadata/proc/get_new_value_for(mob/user, decl/sprite_accessory/accessory_decl, current_value) return diff --git a/code/modules/sprite_accessories/metadata/accessory_metadata_color.dm b/code/modules/sprite_accessories/metadata/accessory_metadata_color.dm index e63739203cbb..b352584b40d2 100644 --- a/code/modules/sprite_accessories/metadata/accessory_metadata_color.dm +++ b/code/modules/sprite_accessories/metadata/accessory_metadata_color.dm @@ -16,3 +16,9 @@ /decl/sprite_accessory_metadata/color/alt name = "Secondary Color" uid = "sa_metadata_color_alt" + additional_icon_state = "_inner" + +/decl/sprite_accessory_metadata/color/extra + name = "Tertiary Color" + uid = "sa_metadata_color_extra" + additional_icon_state = "_extra" diff --git a/code/modules/sprite_accessories/tails/_accessory_tail.dm b/code/modules/sprite_accessories/tails/_accessory_tail.dm index 44d3c41facb1..a9f91eb34d29 100644 --- a/code/modules/sprite_accessories/tails/_accessory_tail.dm +++ b/code/modules/sprite_accessories/tails/_accessory_tail.dm @@ -37,7 +37,7 @@ var/icon_animation_states /decl/sprite_accessory/tail/none - name = "Default Tail" + name = "No Custom Tail" icon_state = "none" uid = "acc_tail_none" bodytypes_allowed = null diff --git a/mods/content/fantasy/_fantasy.dme b/mods/content/fantasy/_fantasy.dme index 39602d52bbc3..a02e6b80683f 100644 --- a/mods/content/fantasy/_fantasy.dme +++ b/mods/content/fantasy/_fantasy.dme @@ -15,6 +15,7 @@ #include "datum\species.dm" #include "datum\hnoll\bodytypes.dm" #include "datum\hnoll\culture.dm" +#include "datum\hnoll\ears.dm" #include "datum\hnoll\language.dm" #include "datum\hnoll\markings.dm" #include "datum\hnoll\organs.dm" @@ -22,6 +23,8 @@ #include "datum\kobaloi\bodytypes.dm" #include "datum\kobaloi\clothing.dm" #include "datum\kobaloi\culture.dm" +#include "datum\kobaloi\ears.dm" +#include "datum\kobaloi\horns.dm" #include "datum\kobaloi\language.dm" #include "datum\kobaloi\markings.dm" #include "datum\kobaloi\organs.dm" diff --git a/mods/content/fantasy/datum/hnoll/bodytypes.dm b/mods/content/fantasy/datum/hnoll/bodytypes.dm index 1edda9e8e998..04d6f5a80860 100644 --- a/mods/content/fantasy/datum/hnoll/bodytypes.dm +++ b/mods/content/fantasy/datum/hnoll/bodytypes.dm @@ -25,7 +25,13 @@ SAC_MARKINGS = list( /decl/sprite_accessory/marking/hnoll/belly = list(SAM_COLOR = "#b6b0a8"), /decl/sprite_accessory/marking/hnoll/spots/body = list(SAM_COLOR = "#46331d"), - /decl/sprite_accessory/marking/hnoll/ears = list(SAM_COLOR = "#46331d") + ), + SAC_EARS = list( + /decl/sprite_accessory/ears/hnoll = list( + SAM_COLOR = "#46331d", + SAM_COLOR_INNER = "#c0a88d", + SAM_COLOR_EXTRA = "#201406" + ) ) ) diff --git a/mods/content/fantasy/datum/hnoll/ears.dm b/mods/content/fantasy/datum/hnoll/ears.dm new file mode 100644 index 000000000000..e03cd248779c --- /dev/null +++ b/mods/content/fantasy/datum/hnoll/ears.dm @@ -0,0 +1,13 @@ +/decl/sprite_accessory/ears/hnoll + name = "Hnoll Wide Ears" + icon_state = "ears_plain" + mask_to_bodypart = FALSE + uid = "acc_marking_hnoll_wideears" + icon = 'mods/content/fantasy/icons/hnoll/ears.dmi' + species_allowed = list(/decl/species/hnoll::uid) + accessory_metadata_types = list(SAM_COLOR, SAM_COLOR_INNER, SAM_COLOR_EXTRA) + +/decl/sprite_accessory/ears/hnoll/narrow + name = "Hnoll Narrow Ears" + icon_state = "ears_narrow" + uid = "acc_marking_hnoll_narrowears" diff --git a/mods/content/fantasy/datum/hnoll/markings.dm b/mods/content/fantasy/datum/hnoll/markings.dm index e897347c5c81..01ca1409a8e6 100644 --- a/mods/content/fantasy/datum/hnoll/markings.dm +++ b/mods/content/fantasy/datum/hnoll/markings.dm @@ -174,42 +174,6 @@ color_blend = ICON_MULTIPLY uid = "acc_marking_hnoll_nose" -/decl/sprite_accessory/marking/hnoll/ears - name = "Hnoll Wide Ears" - icon_state = "ears_plain" - mask_to_bodypart = FALSE - uid = "acc_marking_hnoll_wideears" - -/decl/sprite_accessory/marking/hnoll/ears/wide_inner - name = "Hnoll Wide Ears Interior" - icon_state = "ears_plain_inner" - uid = "acc_marking_hnoll_wideears_inner" - -/decl/sprite_accessory/marking/hnoll/ears/wide_tuft - name = "Hnoll Wide Ears Tuft" - icon_state = "ears_plain_tuft" - uid = "acc_marking_hnoll_wideears_tuft" - -/decl/sprite_accessory/marking/hnoll/ears/narrow - name = "Hnoll Narrow Ears" - icon_state = "ears_narrow" - uid = "acc_marking_hnoll_narrowears" - -/decl/sprite_accessory/marking/hnoll/ears/narrow_inner - name = "Hnoll Narrow Ears Interior" - icon_state = "ears_narrow_inner" - uid = "acc_marking_hnoll_narrowears_inner" - -/decl/sprite_accessory/marking/hnoll/ears/narrow_tuft - name = "Hnoll Narrow Ears Tuft" - icon_state = "ears_narrow_tuft" - uid = "acc_marking_hnoll_narrowears_tuft" - -/decl/sprite_accessory/marking/hnoll/ears/earrings - name = "Hnoll Earrings" - icon_state = "earrings" - uid = "acc_marking_hnoll_earrings" - /decl/sprite_accessory/marking/hnoll/patches name = "Patches (Body)" icon_state = "patches" diff --git a/mods/content/fantasy/datum/kobaloi/bodytypes.dm b/mods/content/fantasy/datum/kobaloi/bodytypes.dm index 72740539a495..6e108582a5af 100644 --- a/mods/content/fantasy/datum/kobaloi/bodytypes.dm +++ b/mods/content/fantasy/datum/kobaloi/bodytypes.dm @@ -22,9 +22,8 @@ BP_TAIL = /obj/item/organ/external/tail/kobaloi ) default_sprite_accessories = list( - SAC_MARKINGS = list( - /decl/sprite_accessory/marking/kobaloi/left_ear = list(SAM_COLOR = "#8f974a"), - /decl/sprite_accessory/marking/kobaloi/right_ear = list(SAM_COLOR = "#8f974a") + SAC_EARS = list( + /decl/sprite_accessory/ears/kobaloi = list(SAM_COLOR = "#8f974a") ) ) eye_low_light_vision_effectiveness = 0.15 diff --git a/mods/content/fantasy/datum/kobaloi/ears.dm b/mods/content/fantasy/datum/kobaloi/ears.dm new file mode 100644 index 000000000000..1f82a13ca21e --- /dev/null +++ b/mods/content/fantasy/datum/kobaloi/ears.dm @@ -0,0 +1,26 @@ +/decl/sprite_accessory/ears/kobaloi + name = "Kobaloi Ears" + icon_state = "ears" + uid = "acc_kobaloi_ears" + icon = 'mods/content/fantasy/icons/kobaloi/ears.dmi' + species_allowed = list(/decl/species/kobaloi::uid) + +/decl/sprite_accessory/ears/kobaloi/floppy + name = "Floppy Kobaloi Ears" + icon_state = "floopy" + uid = "acc_kobaloi_ears_floppy" + +/decl/sprite_accessory/ears/kobaloi/stubs + name = "Kobaloi Ear Stubs" + icon_state = "stubs" + uid = "acc_kobaloi_ear_stubs" + +/decl/sprite_accessory/ears/kobaloi/left_stub + name = "Right Kobaloi Ear With Stub" + icon_state = "left_stub" + uid = "acc_kobaloi_ear_stub_left" + +/decl/sprite_accessory/ears/kobaloi/right_stub + name = "Left Kobaloi Ear With Stub" + icon_state = "right_stub" + uid = "acc_kobaloi_ear_stub_right" diff --git a/mods/content/fantasy/datum/kobaloi/horns.dm b/mods/content/fantasy/datum/kobaloi/horns.dm new file mode 100644 index 000000000000..d6bfc7e08914 --- /dev/null +++ b/mods/content/fantasy/datum/kobaloi/horns.dm @@ -0,0 +1,49 @@ +/decl/sprite_accessory/horns/kobaloi + abstract_type = /decl/sprite_accessory/horns/kobaloi + icon = 'mods/content/fantasy/icons/kobaloi/horns.dmi' + species_allowed = list(/decl/species/kobaloi::uid) + +/decl/sprite_accessory/horns/kobaloi/spikes + name = "Spikes" + icon_state = "spikes" + uid = "acc_kobaloi_spikes" + +/decl/sprite_accessory/horns/kobaloi/left_horn + name = "Left Horn" + icon_state = "left_horn" + uid = "acc_kobaloi_horn_left" + +/decl/sprite_accessory/horns/kobaloi/right_horn + name = "Right Horn" + icon_state = "right_horn" + uid = "acc_kobaloi_horn_right" + +/decl/sprite_accessory/horns/kobaloi/left_broken_horn + name = "Broken Left Horn" + icon_state = "left_broken_horn" + uid = "acc_kobaloi_horn_broken_left" + +/decl/sprite_accessory/horns/kobaloi/right_broken_horn + name = "Broken Right Horn" + icon_state = "right_broken_horn" + uid = "acc_kobaloi_horn_broken_right" + +/decl/sprite_accessory/horns/kobaloi/left_curved_horn + name = "Curved Left Horn" + icon_state = "left_curved_horn" + uid = "acc_kobaloi_horn_curved_left" + +/decl/sprite_accessory/horns/kobaloi/right_curved_horn + name = "Curved Right Horn" + icon_state = "right_curved_horn" + uid = "acc_kobaloi_horn_curved_right" + +/decl/sprite_accessory/horns/kobaloi/left_antler + name = "Left Antler" + icon_state = "left_antler" + uid = "acc_kobaloi_antler_left" + +/decl/sprite_accessory/horns/kobaloi/right_antler + name = "Right Antler" + icon_state = "right_antler" + uid = "acc_kobaloi_antler_right" diff --git a/mods/content/fantasy/datum/kobaloi/markings.dm b/mods/content/fantasy/datum/kobaloi/markings.dm index 23281846c75a..b81bcccbeef4 100644 --- a/mods/content/fantasy/datum/kobaloi/markings.dm +++ b/mods/content/fantasy/datum/kobaloi/markings.dm @@ -8,43 +8,11 @@ color_blend = ICON_MULTIPLY species_allowed = list(/decl/species/kobaloi::uid) body_parts = list(BP_HEAD) - mask_to_bodypart = FALSE - -/decl/sprite_accessory/marking/kobaloi/left_ear - name = "Left Ear" - icon_state = "left_ear" - uid = "acc_kobaloi_ear_left" - -/decl/sprite_accessory/marking/kobaloi/right_ear - name = "Right Ear" - icon_state = "right_ear" - uid = "acc_kobaloi_ear_right" - -/decl/sprite_accessory/marking/kobaloi/left_ear_floopy - name = "Floppy Left Ear" - icon_state = "left_ear_floopy" - uid = "acc_kobaloi_ear_left_floopy" - -/decl/sprite_accessory/marking/kobaloi/right_ear_floopy - name = "Floppy Right Ear" - icon_state = "right_ear_floopy" - uid = "acc_kobaloi_ear_right_floopy" - -/decl/sprite_accessory/marking/kobaloi/left_ear_stub - name = "Left Ear Stub" - icon_state = "left_ear_stub" - uid = "acc_kobaloi_ear_stub_left" - -/decl/sprite_accessory/marking/kobaloi/right_ear_stub - name = "Right Ear Stub" - icon_state = "right_ear_stub" - uid = "acc_kobaloi_ear_stub_right" /decl/sprite_accessory/marking/kobaloi/body name = "Mottling" icon_state = "mottling" uid = "acc_kobaloi_mottling" - mask_to_bodypart = TRUE body_parts = list(BP_CHEST, BP_GROIN, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT) /decl/sprite_accessory/marking/kobaloi/body/stripes @@ -62,59 +30,9 @@ name = "Muzzle" icon_state = "muzzle" uid = "acc_kobaloi_muzzle" - mask_to_bodypart = TRUE body_parts = list(BP_HEAD) /decl/sprite_accessory/marking/kobaloi/face/nose name = "Nose" icon_state = "nose" uid = "acc_kobaloi_nose" - -/decl/sprite_accessory/marking/kobaloi/horns - abstract_type = /decl/sprite_accessory/marking/kobaloi/horns - icon = 'mods/content/fantasy/icons/kobaloi/horns.dmi' - -/decl/sprite_accessory/marking/kobaloi/horns/spikes - name = "Spikes" - icon_state = "spikes" - uid = "acc_kobaloi_spikes" - -/decl/sprite_accessory/marking/kobaloi/horns/left_horn - name = "Left Horn" - icon_state = "left_horn" - uid = "acc_kobaloi_horn_left" - -/decl/sprite_accessory/marking/kobaloi/horns/right_horn - name = "Right Horn" - icon_state = "right_horn" - uid = "acc_kobaloi_horn_right" - -/decl/sprite_accessory/marking/kobaloi/horns/left_broken_horn - name = "Broken Left Horn" - icon_state = "left_broken_horn" - uid = "acc_kobaloi_horn_broken_left" - -/decl/sprite_accessory/marking/kobaloi/horns/right_broken_horn - name = "Broken Right Horn" - icon_state = "right_broken_horn" - uid = "acc_kobaloi_horn_broken_right" - -/decl/sprite_accessory/marking/kobaloi/horns/left_curved_horn - name = "Curved Left Horn" - icon_state = "left_curved_horn" - uid = "acc_kobaloi_horn_curved_left" - -/decl/sprite_accessory/marking/kobaloi/horns/right_curved_horn - name = "Curved Right Horn" - icon_state = "right_curved_horn" - uid = "acc_kobaloi_horn_curved_right" - -/decl/sprite_accessory/marking/kobaloi/horns/left_antler - name = "Left Antler" - icon_state = "left_antler" - uid = "acc_kobaloi_antler_left" - -/decl/sprite_accessory/marking/kobaloi/horns/right_antler - name = "Right Antler" - icon_state = "right_antler" - uid = "acc_kobaloi_antler_right" diff --git a/mods/content/fantasy/datum/kobaloi/species.dm b/mods/content/fantasy/datum/kobaloi/species.dm index c34156ee2a9a..b99b31683e52 100644 --- a/mods/content/fantasy/datum/kobaloi/species.dm +++ b/mods/content/fantasy/datum/kobaloi/species.dm @@ -16,6 +16,16 @@ preview_outfit = /decl/outfit/job/generic/fantasy base_external_prosthetics_model = null + available_accessory_categories = list( + SAC_HAIR, + SAC_FACIAL_HAIR, + SAC_HORNS, + SAC_EARS, + SAC_TAIL, + SAC_COSMETICS, + SAC_MARKINGS + ) + available_background_info = list( /decl/background_category/citizenship = list( /decl/background_detail/citizenship/other diff --git a/mods/content/fantasy/icons/hnoll/ears.dmi b/mods/content/fantasy/icons/hnoll/ears.dmi new file mode 100644 index 0000000000000000000000000000000000000000..f2a31ad3165999764cac707cacfc3e3934ad3c4e GIT binary patch literal 576 zcmV-G0>Ax09%Qx{G50003yNklwOz8&EVRaLSe&*zQV&l$=nYNWLeXO!nRS#T{bh)^7xS5eS4FTE`AwnP<7 z9g=>S{?h7Xt=(gU#VWS$%!aEWX5fKp){fUBF%^S1KB)J{o4CGqOCQ)iR znp3BfEEq>|-8#QaZuTDN7OK{rWlr8~aEMvbaQTCHT1gR=t% z&N7LJh=_>jL2;J3cb4h@_qvR`5c!48#;9@OajW-7+dV8LA|fIp`gfmQAsaP1FA=5y O0000|qq{5EB4tnv({(#s;kJtBSuR?$ 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 EFR7O;X#fBK diff --git a/mods/content/fantasy/icons/kobaloi/ears.dmi b/mods/content/fantasy/icons/kobaloi/ears.dmi new file mode 100644 index 0000000000000000000000000000000000000000..d87b5abbf5fc435f5dcc2088c026ada833acb033 GIT binary patch literal 749 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 A)Bpeg literal 3691 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 B&6xlI diff --git a/mods/species/drakes/species_bodytypes.dm b/mods/species/drakes/species_bodytypes.dm index 9da2dbfd25e4..104eee0ce715 100644 --- a/mods/species/drakes/species_bodytypes.dm +++ b/mods/species/drakes/species_bodytypes.dm @@ -280,13 +280,14 @@ return ..() /decl/sprite_accessory/marking/grafadreka - name = "Drake Spines" - icon = 'mods/species/drakes/icons/markings.dmi' - icon_state = "spines" - uid = "acc_marking_drake_spines" - species_allowed = list(/decl/species/grafadreka::uid) - color_blend = ICON_MULTIPLY - body_parts = list( + name = "Drake Spines" + icon = 'mods/species/drakes/icons/markings.dmi' + icon_state = "spines" + uid = "acc_marking_drake_spines" + species_allowed = list(/decl/species/grafadreka::uid) + color_blend = ICON_MULTIPLY + mask_to_bodypart = FALSE + body_parts = list( BP_CHEST, BP_GROIN, BP_TAIL, From 02ed0b61141841d2f321b04b8af02e2e739a5995 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 12 May 2026 18:12:57 -0400 Subject: [PATCH 1227/1331] Apply traits via mob snapshots in character creation --- code/datums/traits/_traits.dm | 2 ++ code/datums/traits/maluses/amputations.dm | 1 + code/datums/traits/prosthetics/prosthetic_limbs.dm | 1 + code/datums/traits/prosthetics/prosthetic_organs.dm | 1 + .../client/preference_setup/general/03_traits.dm | 12 ++++++++++-- code/modules/mob/mob_snapshot.dm | 6 ++++++ 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/code/datums/traits/_traits.dm b/code/datums/traits/_traits.dm index b3e7103fe9d4..94ce0f5da28d 100644 --- a/code/datums/traits/_traits.dm +++ b/code/datums/traits/_traits.dm @@ -106,6 +106,8 @@ var/available_at_map_tech = MAP_TECH_LEVEL_ANY /// Whether or not a rejuvenation should apply this aspect. var/reapply_on_rejuvenation = FALSE + /// Whether this trait should be copied and applied by mob snapshots. + var/is_heritable = TRUE /// What species can select this trait in chargen? var/list/permitted_species /// What species cannot select this trait in chargen? diff --git a/code/datums/traits/maluses/amputations.dm b/code/datums/traits/maluses/amputations.dm index 8ce9bc5d4186..5b576e2ef5f8 100644 --- a/code/datums/traits/maluses/amputations.dm +++ b/code/datums/traits/maluses/amputations.dm @@ -3,6 +3,7 @@ category = "Missing Limbs" abstract_type = /decl/trait/malus/amputation reapply_on_rejuvenation = TRUE + is_heritable = FALSE var/list/apply_to_limbs var/list/ban_traits_relating_to_limbs diff --git a/code/datums/traits/prosthetics/prosthetic_limbs.dm b/code/datums/traits/prosthetics/prosthetic_limbs.dm index a620ebef2942..ace1236f36d4 100644 --- a/code/datums/traits/prosthetics/prosthetic_limbs.dm +++ b/code/datums/traits/prosthetics/prosthetic_limbs.dm @@ -6,6 +6,7 @@ available_at_map_tech = MAP_TECH_LEVEL_ANY // the base trait must be available so that wooden prostheses are available abstract_type = /decl/trait/prosthetic_limb reapply_on_rejuvenation = TRUE + is_heritable = FALSE var/fullbody_synthetic_only = FALSE var/replace_children = TRUE var/check_bodytype diff --git a/code/datums/traits/prosthetics/prosthetic_organs.dm b/code/datums/traits/prosthetics/prosthetic_organs.dm index 0ae6ce949569..513da35f6e2d 100644 --- a/code/datums/traits/prosthetics/prosthetic_organs.dm +++ b/code/datums/traits/prosthetics/prosthetic_organs.dm @@ -5,6 +5,7 @@ available_at_map_tech = MAP_TECH_LEVEL_SPACE category = "Prosthetic Organs" reapply_on_rejuvenation = TRUE + is_heritable = FALSE var/synthetic_bodytype_restricted = FALSE var/apply_to_organ diff --git a/code/modules/client/preference_setup/general/03_traits.dm b/code/modules/client/preference_setup/general/03_traits.dm index b61d8b02b201..e2a06b282568 100644 --- a/code/modules/client/preference_setup/general/03_traits.dm +++ b/code/modules/client/preference_setup/general/03_traits.dm @@ -39,10 +39,18 @@ else pref.prune_invalid_traits() +/datum/category_item/player_setup_item/traits/populate_mob_snapshot(datum/mob_snapshot/snapshot, is_preview_copy) + for(var/trait_type in pref.traits) + var/decl/trait/trait = GET_DECL(trait_type) + if(trait.is_heritable) + LAZYSET(snapshot.heritable_traits, trait_type, pref.traits[trait_type] || TRAIT_LEVEL_EXISTS) + /datum/category_item/player_setup_item/traits/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) - character.clear_extrinsic_traits() + // apply non-heritable traits now for(var/trait_type in pref.traits) - character.set_trait(trait_type, (pref.traits[trait_type] || TRAIT_LEVEL_EXISTS)) + var/decl/trait/trait = GET_DECL(trait_type) + if(!trait.is_heritable) + character.set_trait(trait_type, (pref.traits[trait_type] || TRAIT_LEVEL_EXISTS)) /datum/category_item/player_setup_item/traits/save_character(datum/pref_record_writer/writer) var/list/trait_ids = list() diff --git a/code/modules/mob/mob_snapshot.dm b/code/modules/mob/mob_snapshot.dm index c876a47857c6..271fade652b2 100644 --- a/code/modules/mob/mob_snapshot.dm +++ b/code/modules/mob/mob_snapshot.dm @@ -16,6 +16,8 @@ var/list/genetic_conditions /// Please find a better way to do this. This is done to add tails if we have the tail accessory selected... var/list/extra_limbs + /// A list of trait levels to add. Should contain only 'heritable' traits... whatever that means. + var/list/heritable_traits /datum/mob_snapshot/New(mob/living/donor, genetic_info_only = FALSE) @@ -74,6 +76,10 @@ else if(istype(root_bodytype) && target.get_bodytype() != root_bodytype) target.set_bodytype(root_bodytype) + // we try to set traits as soon as possible after species set + for(var/target_trait in heritable_traits) + target.set_trait(target_trait, heritable_traits[target_trait]) + target.set_fingerprint(fingerprint) target.set_unique_enzymes(unique_enzymes) target.set_skin_colour(skin_color) From 0f69fe373d742292667b34c7bb4a2d5ea94960a5 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 12 May 2026 18:13:32 -0400 Subject: [PATCH 1228/1331] Fix excessive mannequin regeneration on character slot save --- code/modules/client/preferences.dm | 8 +++++++- code/modules/mob/new_player/preferences_setup.dm | 7 ++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index cdfe05e2c66c..e851c2513908 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -309,6 +309,7 @@ var/global/list/time_prefs_fixed = list() if(href_list["save"]) save_preferences() save_character() + return TRUE // don't regenerate our preview, that's wasteful else if(href_list["reload"]) load_preferences() load_character() @@ -325,6 +326,8 @@ var/global/list/time_prefs_fixed = list() if(isnewplayer(client.mob)) var/mob/new_player/M = client.mob M.show_lobby_menu() + update_setup_window(usr) + return TRUE // don't do a duplicate icon update else if(href_list["resetslot"]) if(real_name != input("This will reset the current slot. Enter the character's full name to confirm.")) @@ -339,12 +342,15 @@ var/global/list/time_prefs_fixed = list() equip_preview_mob ^= text2num(href_list["toggle_preview_value"]) else if(href_list["cycle_bg"]) bgstate = next_in_list(bgstate, global.using_map.char_preview_bgstate_options) + update_preview_icon(redress_mob = FALSE) + return TRUE else return FALSE + // this should get hit for reset, reload, load, slot change, and equipment preview toggle update_preview_icon() update_setup_window(usr) - return 1 + return TRUE /datum/category_item/player_setup_item/records/character_info/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) if(is_preview_copy) diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 5a8b7f89ecbe..b1104cadc6cf 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -109,11 +109,12 @@ mannequin.update_icon() mannequin.compile_overlays() -/datum/preferences/proc/update_preview_icon() +/datum/preferences/proc/update_preview_icon(redress_mob = TRUE) var/mob/living/human/dummy/mannequin/mannequin = get_mannequin(client?.ckey) if(mannequin) - mannequin.delete_inventory(TRUE) - dress_preview_mob(mannequin) + if(redress_mob) + mannequin.delete_inventory(TRUE) + dress_preview_mob(mannequin) update_character_previews(mannequin) /datum/preferences/proc/get_random_name() From bac15440ab961c92956824fd6ddebaf613139744 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 12 May 2026 18:14:06 -0400 Subject: [PATCH 1229/1331] Fix supplied mob snapshot not being used in create_missing_organs --- code/modules/bodytype/_bodytype.dm | 8 +++++--- code/modules/mob/living/human/human.dm | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/code/modules/bodytype/_bodytype.dm b/code/modules/bodytype/_bodytype.dm index 600043bdc11b..03aae1595c27 100644 --- a/code/modules/bodytype/_bodytype.dm +++ b/code/modules/bodytype/_bodytype.dm @@ -639,7 +639,7 @@ var/global/list/bodytypes_by_category = list() set_extension(limb, /datum/extension/armor, natural_armour_values) //fully_replace: If true, all existing organs will be discarded. Useful when doing mob transformations, and not caring about the existing organs -/decl/bodytype/proc/create_missing_organs(mob/living/human/H, fully_replace = FALSE) +/decl/bodytype/proc/create_missing_organs(mob/living/human/H, fully_replace = FALSE, datum/mob_snapshot/snapshot_to_use = null) if(fully_replace) H.delete_organs() @@ -659,8 +659,10 @@ var/global/list/bodytypes_by_category = list() qdel(O) //Create missing limbs - var/datum/mob_snapshot/supplied_data = H.get_mob_snapshot() - supplied_data.root_bodytype = src // This may not have been set on the target mob torso yet. + var/datum/mob_snapshot/supplied_data = snapshot_to_use + if(!supplied_data) + supplied_data = H.get_mob_snapshot() + supplied_data.root_bodytype = src // This may not have been set on the target mob torso yet. for(var/limb_type in has_limbs) if(GET_EXTERNAL_ORGAN(H, limb_type)) //Skip existing diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 00c520947984..739e8771d5ea 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -462,7 +462,7 @@ /mob/proc/set_bodytype(var/decl/bodytype/new_bodytype) return -/mob/living/human/set_bodytype(var/decl/bodytype/new_bodytype) +/mob/living/human/set_bodytype(var/decl/bodytype/new_bodytype, var/datum/mob_snapshot/snapshot_to_use = null) var/decl/bodytype/old_bodytype = get_bodytype() if(ispath(new_bodytype)) @@ -489,7 +489,7 @@ //set_species should not handle the entirety of initing the mob, and should not trigger deep updates //It focuses on setting up species-related data, without force applying them uppon organs and the mob's appearance. // For transforming an existing mob, look at change_species() -/mob/living/human/set_species(var/new_species_uid, var/new_bodytype = null) +/mob/living/human/set_species(var/new_species_uid, var/new_bodytype = null, var/datum/mob_snapshot/snapshot_to_use = null) if(!new_species_uid) CRASH("set_species on mob '[src]' was passed a null species uid!") var/decl/species/new_species = decls_repository.get_decl_by_id(new_species_uid) @@ -518,7 +518,7 @@ //Handle bodytype if(!new_bodytype) new_bodytype = species.get_bodytype_by_pronouns(new_pronouns) - set_bodytype(new_bodytype) + set_bodytype(new_bodytype, snapshot_to_use = snapshot_to_use) available_maneuvers = species.maneuvers.Copy() @@ -984,7 +984,7 @@ else if(!species_uid) species_uid = global.using_map.default_species //Humans cannot exist without a species! - set_species(species_uid, supplied_appearance?.root_bodytype) + set_species(species_uid, supplied_appearance?.root_bodytype, snapshot_to_use = supplied_appearance) var/decl/bodytype/root_bodytype = get_bodytype() // root bodytype is set in set_species ASSERT((!supplied_appearance?.root_bodytype) || (root_bodytype == supplied_appearance.root_bodytype)) if(!get_skin_colour()) From f0906d6cfd25728811b8d7f62a705c54a4bf56fe Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 12 May 2026 18:14:29 -0400 Subject: [PATCH 1230/1331] Fix antlers biomod not being an animal biomod --- mods/content/biomods/ears_animal.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/content/biomods/ears_animal.dm b/mods/content/biomods/ears_animal.dm index 3e39b6567dd1..71785dd8f32f 100644 --- a/mods/content/biomods/ears_animal.dm +++ b/mods/content/biomods/ears_animal.dm @@ -19,7 +19,7 @@ icon_state = "fox" uid = "accessory_ears_fox" -/decl/sprite_accessory/ears/biomods/antlers +/decl/sprite_accessory/ears/biomods/animal/antlers name = "Antlers" icon_state = "antlers" uid = "accessory_ears_antlers" From 12bf7a931519ddb18311faa0c7058a87ce17973d Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 14:08:39 +1000 Subject: [PATCH 1231/1331] Minor hood fixes. --- code/modules/clothing/suits/_suit_hood.dm | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/code/modules/clothing/suits/_suit_hood.dm b/code/modules/clothing/suits/_suit_hood.dm index ba02cf546900..b6d4a6e6dbd7 100644 --- a/code/modules/clothing/suits/_suit_hood.dm +++ b/code/modules/clothing/suits/_suit_hood.dm @@ -3,9 +3,12 @@ /obj/item/clothing/suit/Initialize() if(ispath(hood)) - hood = new hood(src) - hood.paint_color = paint_color - hood.markings_color = markings_color + hood = new hood(src, material) + hood.canremove = FALSE + if(markings_color) + hood.set_markings_color(markings_color) + if(paint_color) + hood.set_color(paint_color) if(isnull(hood.markings_state_modifier)) hood.markings_state_modifier = markings_state_modifier hood.update_icon() @@ -48,18 +51,6 @@ if(length(.) && istype(hood)) // this is considered a component rather than a contained item . -= hood -/obj/item/clothing/suit/Initialize() - if(ispath(hood)) - hood = new hood(src, material) - if(paint_color) - hood.set_color(paint_color) - return ..() - -/obj/item/clothing/suit/Destroy() - if(istype(hood)) - QDEL_NULL(hood) - return ..() - /obj/item/clothing/suit/equipped(mob/user, slot) if(slot != slot_wear_suit_str) remove_hood() From 789ccc5c3466d69e221741c40c5e5200fbab2acb Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 14:25:58 +1000 Subject: [PATCH 1232/1331] Adding a skip_update param to set_color() and set_markings_color(). --- code/_onclick/hud/screen/_screen.dm | 2 +- code/game/atoms.dm | 2 +- code/game/objects/items/__item.dm | 5 +++-- code/game/objects/structures/__structure.dm | 5 +++-- code/game/turfs/walls/_wall.dm | 10 +++++++--- code/modules/atmospherics/pipes.dm | 21 +++++++++++++-------- code/modules/clothing/_clothing.dm | 7 ++++--- code/modules/clothing/suits/_suit_hood.dm | 10 +++++----- code/modules/lights/light_bulbs_tubes.dm | 5 +++-- code/modules/lights/light_fixture_base.dm | 6 +++--- code/modules/mechs/components/frame.dm | 6 +++--- 11 files changed, 46 insertions(+), 33 deletions(-) diff --git a/code/_onclick/hud/screen/_screen.dm b/code/_onclick/hud/screen/_screen.dm index da0c7b5478af..cf3709acd140 100644 --- a/code/_onclick/hud/screen/_screen.dm +++ b/code/_onclick/hud/screen/_screen.dm @@ -71,7 +71,7 @@ /obj/screen/get_color() return color -/obj/screen/set_color(new_color) +/obj/screen/set_color(new_color, skip_update) if(color != new_color) color = new_color return TRUE diff --git a/code/game/atoms.dm b/code/game/atoms.dm index e698467579de..0d0b617fc5a5 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -847,7 +847,7 @@ /* Set the atom colour. This is a stub effectively due to the broad use of direct setting. */ // TODO: implement this everywhere that it should be used instead of direct setting. -/atom/proc/set_color(var/new_color) +/atom/proc/set_color(new_color, skip_update) if(isnull(new_color)) return reset_color() if(color != new_color) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 2a8d149f734c..1bed5d4cf0f6 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -155,13 +155,14 @@ return material.color return initial(color) -/obj/item/set_color(new_color) +/obj/item/set_color(new_color, skip_update) if(new_color == COLOR_WHITE) new_color = null if(paint_color != new_color) paint_color = new_color . = TRUE - refresh_color() + if(!skip_update) + refresh_color() /obj/item/refresh_color() if(paint_color) diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 27239ec9127b..7184dc4797b4 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -24,13 +24,14 @@ return material.color return initial(color) -/obj/structure/set_color(new_color) +/obj/structure/set_color(new_color, skip_update) if(new_color == COLOR_WHITE) new_color = null if(paint_color != new_color) paint_color = new_color . = TRUE - refresh_color() + if(!skip_update) + refresh_color() /obj/structure/refresh_color() if(paint_color) diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 3706a99fcfa1..e73bea10dcb7 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -301,9 +301,13 @@ var/global/list/wall_fullblend_objects = list( addtimer(CALLBACK(wall, TYPE_PROC_REF(/turf/wall, burn), temperature/4), 2) physically_destroyed() -/turf/wall/set_color(new_color) - paint_color = new_color - update_icon() +/turf/wall/set_color(new_color, skip_update) + if(paint_color != new_color) + paint_color = new_color + if(!skip_update) + update_icon() + return TRUE + return FALSE /turf/wall/proc/CheckPenetration(var/base_chance, var/damage) return round(damage/material.integrity*180) diff --git a/code/modules/atmospherics/pipes.dm b/code/modules/atmospherics/pipes.dm index 5d7ee7e8cb1f..f31cd7a5c53e 100644 --- a/code/modules/atmospherics/pipes.dm +++ b/code/modules/atmospherics/pipes.dm @@ -165,9 +165,13 @@ /obj/machinery/atmospherics/get_color() return pipe_color -/obj/machinery/atmospherics/set_color(new_color) - pipe_color = new_color - update_icon() +/obj/machinery/atmospherics/set_color(new_color, skip_update) + if(pipe_color != new_color) + pipe_color = new_color + if(!skip_update) + update_icon() + return TRUE + return FALSE /obj/machinery/atmospherics/pipe/color_cache_name(var/obj/machinery/atmospherics/node) if(istype(src, /obj/machinery/atmospherics/unary/tank)) @@ -183,11 +187,12 @@ else return pipe_color -/obj/machinery/atmospherics/pipe/set_color(new_color) - ..() - //for updating connected atmos device pipes (i.e. vents, manifolds, etc) - for(var/obj/machinery/atmospherics/node as anything in nodes_to_networks) - node.update_icon() +/obj/machinery/atmospherics/pipe/set_color(new_color, skip_update) + . = ..() + if(. && !skip_update) + //for updating connected atmos device pipes (i.e. vents, manifolds, etc) + for(var/obj/machinery/atmospherics/node as anything in nodes_to_networks) + node.update_icon() /obj/machinery/atmospherics/pipe/proc/try_leak() var/missing = FALSE diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index 6ac430b952f5..67483c57d4f8 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -452,11 +452,12 @@ if(get_vitals_sensor()) LAZYADD(., /decl/interaction_handler/clothing_set_sensors) -/obj/item/clothing/proc/set_markings_color(new_color) +/obj/item/clothing/proc/set_markings_color(new_color, skip_update) if(markings_color != new_color) markings_color = new_color - update_icon() - update_clothing_icon() + if(!skip_update) + update_icon() + update_clothing_icon() return TRUE return FALSE diff --git a/code/modules/clothing/suits/_suit_hood.dm b/code/modules/clothing/suits/_suit_hood.dm index b6d4a6e6dbd7..c4227a0cc66e 100644 --- a/code/modules/clothing/suits/_suit_hood.dm +++ b/code/modules/clothing/suits/_suit_hood.dm @@ -6,20 +6,20 @@ hood = new hood(src, material) hood.canremove = FALSE if(markings_color) - hood.set_markings_color(markings_color) + hood.set_markings_color(markings_color, skip_update = TRUE) if(paint_color) - hood.set_color(paint_color) + hood.set_color(paint_color, skip_update = TRUE) if(isnull(hood.markings_state_modifier)) hood.markings_state_modifier = markings_state_modifier hood.update_icon() return ..() -/obj/item/clothing/suit/set_color(new_color) +/obj/item/clothing/suit/set_color(new_color, skip_update) . = ..() if(. && istype(hood)) - hood.set_color(new_color) + hood.set_color(new_color, skip_update) -/obj/item/clothing/suit/set_markings_color(new_color) +/obj/item/clothing/suit/set_markings_color(new_color, skip_update) . = ..() if(istype(hood)) hood.set_markings_color(new_color) diff --git a/code/modules/lights/light_bulbs_tubes.dm b/code/modules/lights/light_bulbs_tubes.dm index 496ca639c1c4..1c70ad67bb80 100644 --- a/code/modules/lights/light_bulbs_tubes.dm +++ b/code/modules/lights/light_bulbs_tubes.dm @@ -31,9 +31,10 @@ /obj/item/light/get_color() return b_color -/obj/item/light/set_color(color) +/obj/item/light/set_color(new_color, skip_update) b_color = isnull(color) ? COLOR_WHITE : color - queue_icon_update() // avoid running update_icon before Initialize + if(!skip_update) + queue_icon_update() // avoid running update_icon before Initialize /obj/item/light/tube name = "light tube" diff --git a/code/modules/lights/light_fixture_base.dm b/code/modules/lights/light_fixture_base.dm index 0f2f5741a626..1afdd5b1552f 100644 --- a/code/modules/lights/light_fixture_base.dm +++ b/code/modules/lights/light_fixture_base.dm @@ -39,9 +39,9 @@ /obj/machinery/light/get_color() return lightbulb?.get_color() -/obj/machinery/light/set_color(color) - . = lightbulb?.set_color(color) - if(.) +/obj/machinery/light/set_color(new_color, skip_update) + . = lightbulb?.set_color(color, skip_update) + if(. && !skip_update) update_light_status(TRUE) update_icon() diff --git a/code/modules/mechs/components/frame.dm b/code/modules/mechs/components/frame.dm index 867a6b831ec7..439d0a28e639 100644 --- a/code/modules/mechs/components/frame.dm +++ b/code/modules/mechs/components/frame.dm @@ -18,12 +18,12 @@ var/is_reinforced = 0 var/set_name -/obj/structure/heavy_vehicle_frame/set_color(new_colour) +/obj/structure/heavy_vehicle_frame/set_color(new_color, skip_update) var/painted_component = FALSE for(var/obj/item/mech_component/comp in list(body, arms, legs, head)) - if(comp.set_color(new_colour)) + if(comp.set_color(new_color, skip_update)) painted_component = TRUE - if(painted_component) + if(!skip_update && painted_component) queue_icon_update() /obj/structure/heavy_vehicle_frame/Destroy() From 3a0ddcd46d7890b561806f8730a9d042f1893b3f Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 16:02:57 +1000 Subject: [PATCH 1233/1331] Closing doors cannot be walked through. --- code/game/machinery/doors/_door.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm index 30ae253804cb..fee36a86822b 100644 --- a/code/game/machinery/doors/_door.dm +++ b/code/game/machinery/doors/_door.dm @@ -465,8 +465,8 @@ close_door_at = 0 do_animate("closing") - sleep(0.5 SECONDS) src.set_density(TRUE) + sleep(0.5 SECONDS) update_nearby_tiles() src.layer = closed_layer From 337669d1f1c3f7835fc726e2f1c44d5156bbaceb Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 17:46:14 +1000 Subject: [PATCH 1234/1331] Prevents pull damage if you are buckled to something (rollerbed, travois, etc.) --- code/modules/mob/mob.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 24a317b46ea0..1b5e70e1c3ee 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -603,10 +603,10 @@ return ..() /mob/proc/pull_damage() - return 0 + return FALSE /mob/living/human/pull_damage() - if(!current_posture.prone|| get_damage(BRUTE) + get_damage(BURN) < 100) + if(buckled || !current_posture.prone || get_damage(BRUTE) + get_damage(BURN) < 100) return FALSE for(var/obj/item/organ/external/e in get_external_organs()) if((e.status & ORGAN_BROKEN) && !e.splinted) From 3111398024d92ddd422176ec265696a44c26e438 Mon Sep 17 00:00:00 2001 From: Typhin Date: Wed, 13 May 2026 03:46:19 -0500 Subject: [PATCH 1235/1331] Update inducers to accept cells Inducers now allow the replacement of cells. --- code/game/objects/items/devices/inducer.dm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/devices/inducer.dm b/code/game/objects/items/devices/inducer.dm index b3bc6d8ae8f4..3cda346feb5d 100644 --- a/code/game/objects/items/devices/inducer.dm +++ b/code/game/objects/items/devices/inducer.dm @@ -47,8 +47,18 @@ return FALSE /obj/item/inducer/attackby(obj/item/W, mob/user) - if(CannotUse(user) || recharge(W, user)) + var/obj/item/cell/my_cell = get_cell() + var/datum/extension/loaded_cell/panel/cell_loaded = get_extension(src, /datum/extension/loaded_cell) + + if(cell_loaded.has_tool_unload_interaction(W)) + return cell_loaded.try_unload(user, W) + + else if(!istype(my_cell) && istype(W, /obj/item/cell)) + return cell_loaded.try_load(user, W) + + else if(CannotUse(user) || recharge(W, user)) return TRUE + return ..() /obj/item/inducer/proc/recharge(atom/A, mob/user) From 25a6e8a6ca1b6d43187f4050d75390b504f89a9c Mon Sep 17 00:00:00 2001 From: Typhin Date: Wed, 13 May 2026 04:06:35 -0500 Subject: [PATCH 1236/1331] Add null checks to cell_loaded Adds null checks just in case a subtype doesn't have a cell extension somehow. --- code/game/objects/items/devices/inducer.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/devices/inducer.dm b/code/game/objects/items/devices/inducer.dm index 3cda346feb5d..8d6e14a0f251 100644 --- a/code/game/objects/items/devices/inducer.dm +++ b/code/game/objects/items/devices/inducer.dm @@ -50,11 +50,11 @@ var/obj/item/cell/my_cell = get_cell() var/datum/extension/loaded_cell/panel/cell_loaded = get_extension(src, /datum/extension/loaded_cell) - if(cell_loaded.has_tool_unload_interaction(W)) + if(cell_loaded?.has_tool_unload_interaction(W)) return cell_loaded.try_unload(user, W) else if(!istype(my_cell) && istype(W, /obj/item/cell)) - return cell_loaded.try_load(user, W) + return cell_loaded?.try_load(user, W) else if(CannotUse(user) || recharge(W, user)) return TRUE From 604a8ea0fc0d46206862b0e92ce592962cfe65af Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 20:58:17 +1000 Subject: [PATCH 1237/1331] Background helpers are now usable on /mob not /mob/living. --- code/modules/mob/living/human/human.dm | 12 +----------- code/modules/mob/mob.dm | 10 ++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 00c520947984..73284f372cbe 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -894,20 +894,10 @@ if(!defer_language_update) update_languages() -/mob/living/proc/get_background_datum_by_flag(background_flag) - var/list/all_categories = global.using_map.get_background_categories() - for(var/cat_type in all_categories) - var/decl/background_category/background_cat = all_categories[cat_type] - if(background_cat.background_flags && (background_cat.background_flags & background_flag)) - return get_background_datum(cat_type) - -/mob/living/proc/get_background_datum(cat_type) - return null - /mob/living/human/get_background_datum(cat_type) . = LAZYACCESS(background_info, cat_type) if(!istype(., /decl/background_detail)) - . = global.using_map.default_background_info[cat_type] + . = ..() PRINT_STACK_TRACE("get_background_datum() tried to return a non-instance value for background category '[cat_type]' - full background list: [json_encode(background_info)] default species culture list: [json_encode(global.using_map.default_background_info)]") /mob/living/human/get_digestion_product() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 8d3083ecbf9d..a6cd829e0474 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1603,3 +1603,13 @@ var/global/const/ACTION_DANGER_ALL = 2 // Returns true if the mob is cloaked, otherwise false /mob/proc/is_cloaked() return FALSE + +/mob/proc/get_background_datum_by_flag(background_flag) + var/list/all_categories = global.using_map.get_background_categories() + for(var/cat_type in all_categories) + var/decl/background_category/background_cat = all_categories[cat_type] + if(background_cat.background_flags && (background_cat.background_flags & background_flag)) + return get_background_datum(cat_type) + +/mob/proc/get_background_datum(cat_type) + return global.using_map.default_background_info[cat_type] From 4896ef107ba7920eb41620e0cfaeab5a7caf7338 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 20:56:53 +1000 Subject: [PATCH 1238/1331] Tweaks to the ability system and ability targeters. --- .../extensions/abilities/ability_button.dm | 2 +- .../extensions/abilities/ability_decl.dm | 8 +++--- .../extensions/abilities/ability_handler.dm | 6 ++--- .../extensions/abilities/ability_item.dm | 4 +-- .../abilities/ability_projectile.dm | 4 +-- .../extensions/abilities/ability_targeting.dm | 26 +++++++++++++++---- mods/gamemodes/cult/abilities/construct.dm | 9 +++---- mods/gamemodes/cult/abilities/harvest.dm | 4 +-- 8 files changed, 39 insertions(+), 24 deletions(-) diff --git a/code/datums/extensions/abilities/ability_button.dm b/code/datums/extensions/abilities/ability_button.dm index 5ab2d44e66b8..7bebb71a1993 100644 --- a/code/datums/extensions/abilities/ability_button.dm +++ b/code/datums/extensions/abilities/ability_button.dm @@ -65,7 +65,7 @@ /obj/screen/ability/button/handle_click(mob/user, params) if(owning_handler.prepared_ability == ability) owning_handler.cancel_prepared_ability() - else if(ability.use_ability(user, get_turf(user), owning_handler)) // tmp, needs better/multi-step target selection + else if(ability.use_ability(user, user, owning_handler)) // tmp, needs better/multi-step target selection update_icon() addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), ability.get_cooldown_time(ability.get_metadata_for_user(user)) + 1) diff --git a/code/datums/extensions/abilities/ability_decl.dm b/code/datums/extensions/abilities/ability_decl.dm index a3cb3726f89f..1c11a89414d4 100644 --- a/code/datums/extensions/abilities/ability_decl.dm +++ b/code/datums/extensions/abilities/ability_decl.dm @@ -182,7 +182,7 @@ else // Otherwise, just apply to the target directly. - apply_effect(user, target, metadata) + apply_ability_effect(user, target, metadata) if(end_prep_on_cast && handler.prepared_ability == src) handler.cancel_prepared_ability() @@ -325,7 +325,7 @@ return TRUE -/decl/ability/proc/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/proc/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) SHOULD_CALL_PARENT(TRUE) if(use_sound) playsound(get_turf(user), use_sound, use_sound_volume, 1) @@ -339,7 +339,7 @@ show_ability_cast_msg(user, targets, metadata) while(length(targets)) var/target = targets[1] - apply_effect_to(user, target, metadata) + apply_ability_effect_to(user, target, metadata) targets = prune_targets(user, target, targets, metadata) finish_casting(user, hit_target, metadata) @@ -366,7 +366,7 @@ ability_overlay.set_density(FALSE) QDEL_IN(ability_overlay, overlay_lifespan) -/decl/ability/proc/apply_effect_to(mob/user, atom/target, list/metadata) +/decl/ability/proc/apply_ability_effect_to(mob/living/user, atom/target, list/metadata) SHOULD_CALL_PARENT(TRUE) SHOULD_NOT_SLEEP(TRUE) apply_visuals(user, target, metadata) diff --git a/code/datums/extensions/abilities/ability_handler.dm b/code/datums/extensions/abilities/ability_handler.dm index 0b773cb469d5..d81a8bd023b8 100644 --- a/code/datums/extensions/abilities/ability_handler.dm +++ b/code/datums/extensions/abilities/ability_handler.dm @@ -125,7 +125,7 @@ add_screen_element(category_toggle, "toggle", TRUE) toggle_category_visibility(TRUE) -/datum/ability_handler/proc/refresh_element_positioning(row = 1, col = 1) +/datum/ability_handler/proc/refresh_element_positioning(row = 1, col = 0) if(!LAZYLEN(screen_elements)) return 0 var/button_pos = col @@ -134,14 +134,14 @@ for(var/ability in screen_elements) var/obj/screen/element = screen_elements[ability] if(istype(element, /obj/screen/ability/category)) - element.screen_loc = "RIGHT-[col]:-4,TOP-[row]" + element.screen_loc = "RIGHT-[col]:-4,TOP-[row]:-24" else if(!element.invisibility) button_pos++ if((button_pos-col) > 5) button_row++ .++ button_pos = col+1 - element.screen_loc = "RIGHT-[button_pos]:-4,TOP-[button_row]" + element.screen_loc = "RIGHT-[button_pos]:-4,TOP-[button_row]:-24" /datum/ability_handler/proc/toggle_category_visibility(force_state) showing_abilities = isnull(force_state) ? !showing_abilities : force_state diff --git a/code/datums/extensions/abilities/ability_item.dm b/code/datums/extensions/abilities/ability_item.dm index b4838fd41458..666d6d268e36 100644 --- a/code/datums/extensions/abilities/ability_item.dm +++ b/code/datums/extensions/abilities/ability_item.dm @@ -54,8 +54,8 @@ // Fire a projectile if that is how this ability works. ability.fire_projectile_at(user, target, metadata) else - // Otherwise, apply to the target. Range checking etc. will be handled in apply_effect(). - ability.apply_effect(user, target, metadata) + // Otherwise, apply to the target. Range checking etc. will be handled in apply_ability_effect(). + ability.apply_ability_effect(user, target, metadata) // Clean up our item if needed. if(ability.item_end_on_cast) diff --git a/code/datums/extensions/abilities/ability_projectile.dm b/code/datums/extensions/abilities/ability_projectile.dm index 896acba2e95a..5b5f2f10b373 100644 --- a/code/datums/extensions/abilities/ability_projectile.dm +++ b/code/datums/extensions/abilities/ability_projectile.dm @@ -22,10 +22,10 @@ /obj/item/projectile/ability/Bump(var/atom/A, forced=0) if(loc && carried_ability && !expended) - carried_ability.apply_effect(owner, A, ability_metadata, src) + carried_ability.apply_ability_effect(owner, A, ability_metadata, src) return TRUE /obj/item/projectile/ability/on_impact(var/atom/A) if(loc && carried_ability && !expended) - carried_ability.apply_effect(owner, A, ability_metadata, src) + carried_ability.apply_ability_effect(owner, A, ability_metadata, src) return TRUE diff --git a/code/datums/extensions/abilities/ability_targeting.dm b/code/datums/extensions/abilities/ability_targeting.dm index 52f7ee8166c5..e93a59c3a793 100644 --- a/code/datums/extensions/abilities/ability_targeting.dm +++ b/code/datums/extensions/abilities/ability_targeting.dm @@ -72,19 +72,35 @@ return FALSE return TRUE +/decl/ability_targeting/target_self + target_turf = FALSE + +/decl/ability_targeting/target_self/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) + return target == user + +/decl/ability_targeting/target_self/get_affected(mob/user, atom/hit_target, list/metadata, decl/ability/ability, obj/item/projectile/ability/projectile) + return list(user) + /decl/ability_targeting/clear_turf ignore_dense_turfs = TRUE /decl/ability_targeting/clear_turf/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) . = ..() && isturf(target) if(.) - var/turf/target_turf = target - return !target_turf.contains_dense_objects(user) + var/turf/turf_to_target = target + return !turf_to_target.contains_dense_objects(user) + +/decl/ability_targeting/single_atom + target_turf = FALSE + user_is_immune = TRUE + +/decl/ability_targeting/single_atom/can_target_user + user_is_immune = FALSE -/decl/ability_targeting/living_mob - target_turf = FALSE +/decl/ability_targeting/single_atom/get_affected(mob/user, atom/hit_target, list/metadata, decl/ability/ability, obj/item/projectile/ability/projectile) + return list(hit_target) -/decl/ability_targeting/living_mob/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) +/decl/ability_targeting/single_atom/living_mob/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) . = ..() && isliving(target) if(.) var/mob/living/victim = target diff --git a/mods/gamemodes/cult/abilities/construct.dm b/mods/gamemodes/cult/abilities/construct.dm index 60138eaba356..bbf1a5bc0de3 100644 --- a/mods/gamemodes/cult/abilities/construct.dm +++ b/mods/gamemodes/cult/abilities/construct.dm @@ -2,10 +2,9 @@ /decl/ability/cult/construct name = "Artificer" desc = "This spell conjures a construct which may be controlled by shades." - target_selector = /decl/ability_targeting/clear_turf + target_selector = /decl/ability_targeting/clear_turf/construct overlay_icon = 'mods/gamemodes/cult/icons/effects.dmi' overlay_icon_state = "sparkles" - target_selector = /decl/ability_targeting/clear_turf/construct var/summon_type = /obj/structure/constructshell /decl/ability_targeting/clear_turf/construct/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) @@ -14,14 +13,14 @@ return FALSE return ..() && !istype(target, cult_ability.summon_type) && !(locate(cult_ability.summon_type) in target) -/decl/ability/cult/construct/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/cult/construct/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) . = ..() var/turf/target_turf = get_turf(hit_target) if(istype(target_turf)) if(ispath(summon_type, /turf)) target_turf = target_turf.ChangeTurf(summon_type, TRUE, FALSE, TRUE, TRUE, FALSE) if(target_turf) // We reapply effects as target no longer exists. - apply_effect_to(user, target_turf, metadata) + apply_ability_effect_to(user, target_turf, metadata) else if(ispath(summon_type, /atom)) new summon_type(target_turf) @@ -87,7 +86,7 @@ return TRUE return FALSE -/decl/ability/cult/construct/pylon/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/cult/construct/pylon/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) for(var/obj/structure/cult/pylon/P in get_turf(hit_target)) if(P.isbroken) P.repair(user) diff --git a/mods/gamemodes/cult/abilities/harvest.dm b/mods/gamemodes/cult/abilities/harvest.dm index 49569d6a25ef..dc8bfc1002ff 100644 --- a/mods/gamemodes/cult/abilities/harvest.dm +++ b/mods/gamemodes/cult/abilities/harvest.dm @@ -9,7 +9,7 @@ prepare_message_3p_str = "Space around $USER$ begins to bubble and decay as a terrible vista begins to intrude..." prepare_message_1p_str = "You bore through space and time, seeking the essence of the Geometer of Blood." fail_cast_1p_str = "Reality reasserts itself, preventing your return to Nar-Sie." - target_selector = /decl/ability_targeting/living_mob + target_selector = /decl/ability_targeting/single_atom/living_mob /decl/ability/cult/construct/harvest/can_use_ability(mob/user, list/metadata, silent) . = ..() @@ -22,7 +22,7 @@ to_chat(user, SPAN_DANGER("You cannot sense the Geometer of Blood!")) return FALSE -/decl/ability/cult/construct/harvest/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/cult/construct/harvest/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) ..() var/destination = null for(var/obj/effect/narsie/N in global.narsie_list) From c7a729c18d0df3de7a170da41f8f492186366dfd Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 00:03:41 +1000 Subject: [PATCH 1239/1331] Implementing variant fence icons from Doe. --- code/game/objects/structures/fence_types.dm | 49 ++++++ code/game/objects/structures/fences.dm | 140 ++++++++++++++---- .../crafting/stack_recipes/recipes_bricks.dm | 8 + .../crafting/stack_recipes/recipes_logs.dm | 17 ++- .../crafting/stack_recipes/recipes_planks.dm | 8 + .../crafting/stack_recipes/recipes_rods.dm | 16 ++ icons/obj/structures/fence.dmi | Bin 17048 -> 16571 bytes 7 files changed, 207 insertions(+), 31 deletions(-) create mode 100644 code/game/objects/structures/fence_types.dm diff --git a/code/game/objects/structures/fence_types.dm b/code/game/objects/structures/fence_types.dm new file mode 100644 index 000000000000..940709d14a67 --- /dev/null +++ b/code/game/objects/structures/fence_types.dm @@ -0,0 +1,49 @@ +/decl/fence_type + var/name = "chain link fence" + var/desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." + var/corner_state = "corner" + var/straight_state = "straight" + var/post_state = "post" + var/end_state = "end" + var/door_closed_state = "door-opened" + var/door_open_state = "door-closed" + +/decl/fence_type/brick + name = "brick fence" + desc = "A brick fence. Not as effective as a wall, but generally it keeps people out." + corner_state = "corner_stone" + straight_state = "straight_stone" + post_state = "post_stone" + end_state = "end_stone" + door_closed_state = "door_stone-opened" + door_open_state = "door_stone-closed" + +/decl/fence_type/palisade + name = "palisade" + desc = "A tall and imposing palisade with sharpened points atop it." + corner_state = "corner_palisade" + straight_state = "straight_palisade" + post_state = "post_palisade" + end_state = "end_palisade" + door_closed_state = "door_palisade-opened" + door_open_state = "door_palisade-closed" + +/decl/fence_type/stick + name = "stick fence" + desc = "A stick fence. Not as effective as a wall, but generally it keeps people out." + corner_state = "corner_stick" + straight_state = "straight_stick" + post_state = "post_stick" + end_state = "end_stick" + door_closed_state = "door_stick-opened" + door_open_state = "door_stick-closed" + +/decl/fence_type/plank + name = "plank fence" + desc = "A plank fence. Not as effective as a wall, but generally it keeps people out." + corner_state = "corner_plank" + straight_state = "straight_plank" + post_state = "post_plank" + end_state = "end_plank" + door_closed_state = "door_plank-opened" + door_open_state = "door_plank-closed" diff --git a/code/game/objects/structures/fences.dm b/code/game/objects/structures/fences.dm index 1ed302b304a5..66243aac4f0e 100644 --- a/code/game/objects/structures/fences.dm +++ b/code/game/objects/structures/fences.dm @@ -1,5 +1,6 @@ -//Chain link fences -//Sprites ported from /VG/ +// Various fences +// Chain link sprites ported from /VG/ +// Stone, stick, plank and palisade sprites by Doe. #define CUT_TIME 10 SECONDS #define CLIMB_TIME 5 SECONDS @@ -14,24 +15,81 @@ /obj/structure/fence name = "fence" - desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." + desc = "A fence. Not as effective as a wall, but generally it keeps people out." density = TRUE anchored = TRUE - icon = 'icons/obj/structures/fence.dmi' icon_state = "straight" - material = /decl/material/solid/metal/steel material_alteration = MAT_FLAG_ALTERATION_ALL tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT - var/cuttable = TRUE + var/decl/fence_type/fence_data = /decl/fence_type var/hole_size = NO_HOLE /obj/structure/fence/Initialize(mapload) update_cut_status() + if(ispath(fence_data)) + fence_data = GET_DECL(fence_data) + SetName(fence_data.name) + desc = (fence_data.desc) + else if(!istype(fence_data)) + fence_data = null + queue_icon_update() return ..() +/obj/structure/fence/update_icon() + . = ..() + if(!istype(fence_data)) + return + update_fence_icon() + +/obj/structure/fence/proc/update_fence_icon() + + // Find any adjacent fences. + var/static/list/direct_adjacent = list(NORTH, SOUTH, EAST, WEST) + var/connected_dirs = 0 + for(var/check_dir in direct_adjacent) + var/turf/neighbor = get_step_resolving_mimic(get_turf(src), check_dir) + if(!istype(neighbor) || !(locate(/obj/structure/fence) in neighbor)) + continue + connected_dirs |= check_dir + + // End segments. + if(check_dir == NORTH || check_dir == SOUTH || check_dir == EAST || check_dir == WEST) + set_dir(global.reverse_dir[check_dir]) + set_icon_state(fence_data.end_state) + // Straight segments. + else if(check_dir == (NORTH | SOUTH) || check_dir == (EAST | WEST)) + if(check_dir & NORTH) + set_dir(NORTH) + else + set_dir(EAST) + switch(hole_size) + if(MEDIUM_HOLE) + set_icon_state("[fence_data.straight_state]-cut2") + if(LARGE_HOLE) + set_icon_state("[fence_data.straight_state]-cut3") + else + set_icon_state(fence_data.straight_state) + + // Corner segments. + else if(check_dir in global.cornerdirs) + set_icon_state(fence_data.corner_state) + var/static/list/_corner_fence_to_state_mapping = alist( + (NORTHWEST) = SOUTH, + (NORTHEAST) = NORTH, + (SOUTHWEST) = EAST, + (SOUTHEAST) = WEST + ) + set_dir(_corner_fence_to_state_mapping[check_dir]) + + // Junction segments - not currently supported. + + +/obj/structure/fence/proc/is_cuttable() + return icon_state == fence_data.straight_state && hole_size < MAX_HOLE_SIZE + /obj/structure/fence/get_examine_strings(mob/user, distance, infix, suffix) . = ..() switch(hole_size) @@ -45,18 +103,6 @@ if(cuttable && hole_size < MAX_HOLE_SIZE) LAZYADD(., SPAN_SUBTLE("Use wirecutters to [hole_size > NO_HOLE ? "expand the":"cut a"] hole into the fence, allowing passage.")) -/obj/structure/fence/end - icon_state = "end" - cuttable = FALSE - -/obj/structure/fence/corner - icon_state = "corner" - cuttable = FALSE - -/obj/structure/fence/post - icon_state = "post" - cuttable = FALSE - /obj/structure/fence/cut/medium icon_state = "straight-cut2" hole_size = MEDIUM_HOLE @@ -69,7 +115,7 @@ /obj/structure/fence/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) if(mover?.checkpass(PASS_FLAG_TABLE)) return TRUE - if(hole_size == MEDIUM_HOLE && issmall(mover)) + if(hole_size >= MEDIUM_HOLE && issmall(mover)) return TRUE return ..() @@ -119,7 +165,6 @@ if(!cuttable) return density = TRUE - switch(hole_size) if(NO_HOLE) icon_state = initial(icon_state) @@ -130,7 +175,6 @@ density = FALSE //FENCE DOORS - /obj/structure/fence/door name = "fence door" desc = "Not very useful without a real lock." @@ -140,8 +184,16 @@ var/locked = FALSE /obj/structure/fence/door/Initialize(mapload) + . = ..() update_door_status() - return ..() + +/obj/structure/fence/door/update_fence_icon() + if(!istype(fence_data)) + return + if(density) + set_icon_state(fence_data.door_closed_state) + else + set_icon_state(fence_data.door_opened_state) /obj/structure/fence/door/opened icon_state = "door-opened" @@ -173,13 +225,8 @@ playsound(src, 'sound/machines/click.ogg', 100, 1) /obj/structure/fence/door/proc/update_door_status() - switch(open) - if(FALSE) - density = TRUE - icon_state = "door-closed" - if(TRUE) - density = FALSE - icon_state = "door-opened" + density = !open + update_icon() /obj/structure/fence/door/proc/can_open(mob/user) if(locked) @@ -192,4 +239,37 @@ #undef NO_HOLE #undef MEDIUM_HOLE #undef LARGE_HOLE -#undef MAX_HOLE_SIZE \ No newline at end of file +#undef MAX_HOLE_SIZE + +// Mapping/crafting helpers. +/obj/structure/fence/brick + icon_state = /decl/fence_type/brick::straight_state + fence_data = /decl/fence_type/brick + +/obj/structure/fence/door/brick + icon_state = /decl/fence_type/brick::door_state_closed + fence_data = /decl/fence_type/brick + +/obj/structure/fence/palisade + icon_state = /decl/fence_type/palisade::straight_state + fence_data = /decl/fence_type/palisade + +/obj/structure/fence/door/palisade + icon_state = /decl/fence_type/palisade::door_state_closed + fence_data = /decl/fence_type/palisade + +/obj/structure/fence/stick + icon_state = /decl/fence_type/stick::straight_state + fence_data = /decl/fence_type/stick + +/obj/structure/fence/door/stick + icon_state = /decl/fence_type/stick::door_state_closed + fence_data = /decl/fence_type/stick + +/obj/structure/fence/plank + icon_state = /decl/fence_type/plank::straight_state + fence_data = /decl/fence_type/plank + +/obj/structure/fence/door/plank + icon_state = /decl/fence_type/plank::door_state_closed + fence_data = /decl/fence_type/plank diff --git a/code/modules/crafting/stack_recipes/recipes_bricks.dm b/code/modules/crafting/stack_recipes/recipes_bricks.dm index 0c9670fbcd9a..a7dcdbc92fdb 100644 --- a/code/modules/crafting/stack_recipes/recipes_bricks.dm +++ b/code/modules/crafting/stack_recipes/recipes_bricks.dm @@ -101,6 +101,14 @@ name = "pedestal, round" result_type = /obj/structure/pedestal/round +/decl/stack_recipe/bricks/furniture/fence + result_type = /obj/structure/fence/brick + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/bricks/furniture/fence_door + result_type = /obj/structure/fence/door/brick + difficulty = MAT_VALUE_NORMAL_DIY + /decl/stack_recipe/bricks/gravestone result_type = /obj/item/gravemarker/gravestone difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_logs.dm b/code/modules/crafting/stack_recipes/recipes_logs.dm index 02010e3a9252..3dadcadb4248 100644 --- a/code/modules/crafting/stack_recipes/recipes_logs.dm +++ b/code/modules/crafting/stack_recipes/recipes_logs.dm @@ -22,4 +22,19 @@ /decl/stack_recipe/logs/wall_frame result_type = /obj/structure/wall_frame/log - difficulty = MAT_VALUE_HARD_DIY \ No newline at end of file + difficulty = MAT_VALUE_HARD_DIY + +/decl/stack_recipe/logs/furniture + abstract_type = /decl/stack_recipe/logs/furniture + one_per_turf = TRUE + on_floor = TRUE + difficulty = MAT_VALUE_HARD_DIY + category = "furniture" + +/decl/stack_recipe/logs/furniture/fence + result_type = /obj/structure/fence/palisade + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/logs/furniture/fence_door + result_type = /obj/structure/fence/door/palisade + difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index feac5e54b587..f00339f99e11 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -251,3 +251,11 @@ /decl/stack_recipe/planks/furniture/target_stake result_type = /obj/structure/target_stake difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/planks/furniture/fence + result_type = /obj/structure/fence/plank + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/planks/furniture/fence_door + result_type = /obj/structure/fence/door/plank + difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_rods.dm b/code/modules/crafting/stack_recipes/recipes_rods.dm index 4b506210b577..96a92267ab98 100644 --- a/code/modules/crafting/stack_recipes/recipes_rods.dm +++ b/code/modules/crafting/stack_recipes/recipes_rods.dm @@ -77,3 +77,19 @@ result_type = /obj/structure/grille one_per_turf = TRUE difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/rods/furniture + abstract_type = /decl/stack_recipe/rods/furniture + one_per_turf = TRUE + on_floor = TRUE + difficulty = MAT_VALUE_HARD_DIY + category = "furniture" + +/decl/stack_recipe/rods/furniture/fence + result_type = /obj/structure/fence/stick + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/rods/furniture/fence_door + result_type = /obj/structure/fence/door/stick + difficulty = MAT_VALUE_NORMAL_DIY + diff --git a/icons/obj/structures/fence.dmi b/icons/obj/structures/fence.dmi index b3d997a940f8e7fa816ee9017629d0d30b015eae..e75db9717befe2449aec5c3340f77f7f8fa8b7c3 100644 GIT binary patch literal 16571 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 literal 17048 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 X_wY#AofG`23COc2stQH&Mn3-^CG+*z From 36d25506a61c1fe5b425e7afac3ac6132eb1308e Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 20:48:18 +1000 Subject: [PATCH 1240/1331] ChangeArea() is now a /turf proc. --- code/_helpers/serde.dm | 2 +- code/_helpers/turfs.dm | 4 +-- code/game/area/areas.dm | 36 +++++++++---------- code/game/turfs/space/space.dm | 2 +- code/modules/admin/buildmode/mode_areas.dm | 2 +- code/modules/maps/reader.dm | 2 +- .../mob/observer/eye/blueprints_eye.dm | 6 ++-- code/modules/multiz/level_data.dm | 2 +- code/modules/overmap/_overmap.dm | 2 +- code/modules/random_map/random_map.dm | 3 +- code/modules/turbolift/turbolift_map.dm | 4 +-- 11 files changed, 33 insertions(+), 32 deletions(-) diff --git a/code/_helpers/serde.dm b/code/_helpers/serde.dm index 9d50f3fae347..4ff9ff8e3004 100644 --- a/code/_helpers/serde.dm +++ b/code/_helpers/serde.dm @@ -51,7 +51,7 @@ if(!area) area = new load_path(null) instanced_areas[load_path] = area - ChangeArea(spawn_loc, area) + spawn_loc.ChangeArea(area) else if(ispath(load_path, /atom)) created_instance = new load_path(spawn_loc) diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index d74b742f97a5..c39b745f6b03 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -120,9 +120,9 @@ if(target) if(base_area) - ChangeArea(target, get_area(source)) + target.ChangeArea(get_area(source)) . += transport_turf_contents(source, target, ignore_background, translate_air, angle = angle) - ChangeArea(source, base_area) + source.ChangeArea(base_area) else . += transport_turf_contents(source, target, ignore_background, translate_air, angle = angle) //change the old turfs diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index b481b506d1c6..d92c81169bdb 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -137,46 +137,46 @@ var/global/list/areas = list() ..() return QDEL_HINT_HARDDEL -// Changes the area of T to A. Do not do this manually. +// Changes the area of src to A. Do not do this manually. // Area is expected to be a non-null instance. -/proc/ChangeArea(var/turf/T, var/area/A) +/turf/proc/ChangeArea(var/area/A) if(!istype(A)) CRASH("Area change attempt failed: invalid area supplied.") - var/old_outside = T.is_outside() - var/area/old_area = get_area(T) + var/old_outside = is_outside() + var/area/old_area = get_area(src) if(old_area == A) return var/old_area_ambience = old_area?.interior_ambient_light_modifier - A.contents.Add(T) + A.contents.Add(src) if(old_area) - old_area.Exited(T, A) - for(var/atom/movable/AM as anything in T) + old_area.Exited(src, A) + for(var/atom/movable/AM as anything in src) old_area.Exited(AM, A) // Note: this _will_ raise exited events. - A.Entered(T, old_area) - for(var/atom/movable/AM as anything in T) + A.Entered(src, old_area) + for(var/atom/movable/AM as anything in src) A.Entered(AM, old_area) // Note: this will _not_ raise moved or entered events. If you change this, you must also change everything which uses them. - for(var/obj/machinery/M in T) + for(var/obj/machinery/M in src) M.area_changed(old_area, A) // They usually get moved events, but this is the one way an area can change without triggering one. - T.update_registrations_on_adjacent_area_change() + update_registrations_on_adjacent_area_change() for(var/direction in global.cardinal) - var/turf/adjacent_turf = get_step(T, direction) + var/turf/adjacent_turf = get_step(src, direction) if(adjacent_turf) adjacent_turf.update_registrations_on_adjacent_area_change() // Handle updating weather and atmos if the outside status of the turf changed. - if(T.is_outside == OUTSIDE_AREA) - T.update_external_atmos_participation() // Refreshes outside status and adds exterior air to turf air if necessary. + if(is_outside == OUTSIDE_AREA) + update_external_atmos_participation() // Refreshes outside status and adds exterior air to turf air if necessary. - if(T.is_outside() != old_outside) - T.update_weather() + if(is_outside() != old_outside) + update_weather() if(SSambience.initialized) // if not initialized, we'll loop over all turfs anyway - AMBIENCE_QUEUE_TURF(T) + AMBIENCE_QUEUE_TURF(src) else if(A.interior_ambient_light_modifier != old_area_ambience && SSambience.initialized) - AMBIENCE_QUEUE_TURF(T) + AMBIENCE_QUEUE_TURF(src) /turf/proc/update_registrations_on_adjacent_area_change() for(var/obj/machinery/door/firedoor/door in src) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index eb63e73c3ccf..21fce02894d7 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -60,7 +60,7 @@ /turf/space/LateInitialize() if(SSmapping.base_floor_area) var/area/new_area = locate(SSmapping.base_floor_area) || new SSmapping.base_floor_area - ChangeArea(src, new_area) + ChangeArea(new_area) ChangeTurf(SSmapping.base_floor_type) /turf/space/proc/toggle_transit(var/direction) diff --git a/code/modules/admin/buildmode/mode_areas.dm b/code/modules/admin/buildmode/mode_areas.dm index 4df3b7e41797..92366800d9ad 100644 --- a/code/modules/admin/buildmode/mode_areas.dm +++ b/code/modules/admin/buildmode/mode_areas.dm @@ -119,7 +119,7 @@ if(!istype(T) || !istype(area_mode)) return FALSE if (area_mode.selected_area) - ChangeArea(T, area_mode.selected_area) + T.ChangeArea(area_mode.selected_area) to_chat(build_mode.user, SPAN_NOTICE("Set area of turf [T.name] to [area_mode.selected_area.proper_name]")) return TRUE to_chat(build_mode.user, SPAN_WARNING("Pick or create an area first")) diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm index 9f7dff608187..1704be274b26 100644 --- a/code/modules/maps/reader.dm +++ b/code/modules/maps/reader.dm @@ -348,7 +348,7 @@ var/global/dmm_suite/preloader/_preloader = new instance = new atype(null) initialized_areas_by_type[atype] = instance if(crds) - ChangeArea(crds, instance) + crds.ChangeArea(instance) //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect diff --git a/code/modules/mob/observer/eye/blueprints_eye.dm b/code/modules/mob/observer/eye/blueprints_eye.dm index c7bae7b5f9c7..6309e82b166d 100644 --- a/code/modules/mob/observer/eye/blueprints_eye.dm +++ b/code/modules/mob/observer/eye/blueprints_eye.dm @@ -66,7 +66,7 @@ var/area/A = new A.SetName(area_name) for(var/turf/T in selected_turfs) - ChangeArea(T, A) + T.ChangeArea(A) finalize_area(A) remove_selection() // Reset the selection for clarity. @@ -90,7 +90,7 @@ var/datum/level_data/our_level_data = SSmapping.levels_by_z[our_turf.z] var/area/base_area = our_level_data.get_base_area_instance() for(var/turf/T in A.contents) - ChangeArea(T, base_area) + T.ChangeArea(base_area) if(!(locate(/turf) in A)) qdel(A) // uh oh, is this safe? @@ -316,7 +316,7 @@ var/datum/level_data/our_level_data = SSmapping.levels_by_z[our_turf.z] var/area/base_area = our_level_data.get_base_area_instance() for(var/turf/T in A.contents) - ChangeArea(T, base_area) + T.ChangeArea(base_area) if(!(locate(/turf) in A)) qdel(A) // uh oh, is this safe? diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index 73f2054e5696..b9a056cb9b4e 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -232,7 +232,7 @@ if(change_turf) T = T.ChangeTurf(picked_turf) if(change_area) - ChangeArea(T, A) + T.ChangeArea(A) ///Prepare level for being used. Setup borders, lateral z connections, ambient lighting, atmosphere, etc.. /datum/level_data/proc/setup_level_data(var/skip_gen = FALSE) diff --git a/code/modules/overmap/_overmap.dm b/code/modules/overmap/_overmap.dm index 96d9180a2402..690abc887fc5 100644 --- a/code/modules/overmap/_overmap.dm +++ b/code/modules/overmap/_overmap.dm @@ -43,7 +43,7 @@ square = square.ChangeTurf(overmap_edge_type) else square = square.ChangeTurf(overmap_turf_type) - ChangeArea(square, A) + square.ChangeArea(A) /datum/overmap/proc/generate_overmap() testing("Building overmap [name]...") diff --git a/code/modules/random_map/random_map.dm b/code/modules/random_map/random_map.dm index 10c390dfcd47..c4bce943eefb 100644 --- a/code/modules/random_map/random_map.dm +++ b/code/modules/random_map/random_map.dm @@ -175,7 +175,8 @@ var/global/list/map_count = list() . = (newpath && !istype(T, newpath)) ? T.ChangeTurf(newpath) : T get_additional_spawns(map[current_cell], ., get_spawn_dir(x, y)) if(use_area) - ChangeArea(., use_area) + T = . + T.ChangeArea(use_area) /datum/random_map/proc/get_spawn_dir() return 0 diff --git a/code/modules/turbolift/turbolift_map.dm b/code/modules/turbolift/turbolift_map.dm index bbea91dd8cc4..d69a39fd5bb8 100644 --- a/code/modules/turbolift/turbolift_map.dm +++ b/code/modules/turbolift/turbolift_map.dm @@ -186,8 +186,8 @@ INITIALIZE_IMMEDIATE(/obj/abstract/turbolift_spawner) var/area_path = areas_to_use[az] var/area/A = locate(area_path) || new area_path() - for(var/T in floor_turfs) - ChangeArea(T, A) + for(var/turf/T as anything in floor_turfs) + T.ChangeArea(A) cfloor.set_area_ref("\ref[A]") // Place exterior doors. From 247f90c19a9f2070797d744130c3d1fea05ea075 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 22:44:35 +1000 Subject: [PATCH 1241/1331] Implementing fence smoothing and crafting. --- code/game/objects/structures/fence_types.dm | 49 ----- .../{fences.dm => fences/_fences.dm} | 190 +++++++++++------- .../objects/structures/fences/fence_types.dm | 61 ++++++ icons/obj/structures/fence.dmi | Bin 16571 -> 0 bytes icons/obj/structures/fences/brick.dmi | Bin 0 -> 2885 bytes icons/obj/structures/fences/chain.dmi | Bin 0 -> 7160 bytes icons/obj/structures/fences/palisade.dmi | Bin 0 -> 3403 bytes icons/obj/structures/fences/plank.dmi | Bin 0 -> 2297 bytes icons/obj/structures/fences/stick.dmi | Bin 0 -> 2853 bytes nebula.dme | 3 +- 10 files changed, 181 insertions(+), 122 deletions(-) delete mode 100644 code/game/objects/structures/fence_types.dm rename code/game/objects/structures/{fences.dm => fences/_fences.dm} (60%) create mode 100644 code/game/objects/structures/fences/fence_types.dm delete mode 100644 icons/obj/structures/fence.dmi create mode 100755 icons/obj/structures/fences/brick.dmi create mode 100755 icons/obj/structures/fences/chain.dmi create mode 100755 icons/obj/structures/fences/palisade.dmi create mode 100755 icons/obj/structures/fences/plank.dmi create mode 100755 icons/obj/structures/fences/stick.dmi diff --git a/code/game/objects/structures/fence_types.dm b/code/game/objects/structures/fence_types.dm deleted file mode 100644 index 940709d14a67..000000000000 --- a/code/game/objects/structures/fence_types.dm +++ /dev/null @@ -1,49 +0,0 @@ -/decl/fence_type - var/name = "chain link fence" - var/desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." - var/corner_state = "corner" - var/straight_state = "straight" - var/post_state = "post" - var/end_state = "end" - var/door_closed_state = "door-opened" - var/door_open_state = "door-closed" - -/decl/fence_type/brick - name = "brick fence" - desc = "A brick fence. Not as effective as a wall, but generally it keeps people out." - corner_state = "corner_stone" - straight_state = "straight_stone" - post_state = "post_stone" - end_state = "end_stone" - door_closed_state = "door_stone-opened" - door_open_state = "door_stone-closed" - -/decl/fence_type/palisade - name = "palisade" - desc = "A tall and imposing palisade with sharpened points atop it." - corner_state = "corner_palisade" - straight_state = "straight_palisade" - post_state = "post_palisade" - end_state = "end_palisade" - door_closed_state = "door_palisade-opened" - door_open_state = "door_palisade-closed" - -/decl/fence_type/stick - name = "stick fence" - desc = "A stick fence. Not as effective as a wall, but generally it keeps people out." - corner_state = "corner_stick" - straight_state = "straight_stick" - post_state = "post_stick" - end_state = "end_stick" - door_closed_state = "door_stick-opened" - door_open_state = "door_stick-closed" - -/decl/fence_type/plank - name = "plank fence" - desc = "A plank fence. Not as effective as a wall, but generally it keeps people out." - corner_state = "corner_plank" - straight_state = "straight_plank" - post_state = "post_plank" - end_state = "end_plank" - door_closed_state = "door_plank-opened" - door_open_state = "door_plank-closed" diff --git a/code/game/objects/structures/fences.dm b/code/game/objects/structures/fences/_fences.dm similarity index 60% rename from code/game/objects/structures/fences.dm rename to code/game/objects/structures/fences/_fences.dm index 66243aac4f0e..c94d2229caf1 100644 --- a/code/game/objects/structures/fences.dm +++ b/code/game/objects/structures/fences/_fences.dm @@ -3,7 +3,6 @@ // Stone, stick, plank and palisade sprites by Doe. #define CUT_TIME 10 SECONDS -#define CLIMB_TIME 5 SECONDS ///section is intact #define NO_HOLE 0 @@ -14,67 +13,112 @@ #define MAX_HOLE_SIZE LARGE_HOLE /obj/structure/fence - name = "fence" - desc = "A fence. Not as effective as a wall, but generally it keeps people out." - density = TRUE - anchored = TRUE - icon = 'icons/obj/structures/fence.dmi' - icon_state = "straight" - material = /decl/material/solid/metal/steel - material_alteration = MAT_FLAG_ALTERATION_ALL + name = "fence" + desc = "A fence. Not as effective as a wall, but generally it keeps people out." + density = TRUE + anchored = TRUE + icon = /decl/fence_type::fence_icon + icon_state = /decl/fence_type::straight_state + material = /decl/material/solid/metal/steel + atom_flags = ATOM_FLAG_CLIMBABLE + material_alteration = MAT_FLAG_ALTERATION_ALL tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT var/decl/fence_type/fence_data = /decl/fence_type - var/hole_size = NO_HOLE + var/hole_size = NO_HOLE + var/connected_dirs = 0 -/obj/structure/fence/Initialize(mapload) - update_cut_status() +/obj/structure/fence/Destroy() + var/turf/prior_loc = loc + . = ..() + if(istype(prior_loc)) + for(var/check_dir in global.cardinal) + for(var/obj/structure/fence/fence in get_step_resolving_mimic(prior_loc, check_dir)) + fence.update_icon() + +/obj/structure/fence/Initialize(ml, _mat, _reinf_mat) if(ispath(fence_data)) fence_data = GET_DECL(fence_data) - SetName(fence_data.name) - desc = (fence_data.desc) + set_icon(fence_data.fence_icon) else if(!istype(fence_data)) fence_data = null - queue_icon_update() - return ..() + . = ..() + update_cut_status() + if(ml) + queue_icon_update() + else + return INITIALIZE_HINT_LATELOAD -/obj/structure/fence/update_icon() +/obj/structure/fence/LateInitialize() . = ..() - if(!istype(fence_data)) - return - update_fence_icon() + update_icon() + for(var/check_dir in global.cardinal) + var/turf/neighbor = get_step_resolving_mimic(get_turf(src), check_dir) + if(istype(neighbor)) + for(var/obj/structure/fence/fence in neighbor) + if(fence_data == RESOLVE_TO_DECL(fence.fence_data)) + fence.update_icon() -/obj/structure/fence/proc/update_fence_icon() +/obj/structure/fence/update_material_name(override_name) + override_name ||= fence_data.name + . = ..() + +/obj/structure/fence/update_material_desc(override_desc) + override_desc ||= fence_data.desc + . = ..() + +/obj/structure/fence/on_update_icon() + . = ..() + if(istype(fence_data)) + update_fence_connections() + update_fence_icon() + +/obj/structure/fence/proc/is_fencepost() + return FALSE // TODO: detect doors and junctions next to us. +/obj/structure/fence/proc/update_fence_connections() // Find any adjacent fences. - var/static/list/direct_adjacent = list(NORTH, SOUTH, EAST, WEST) - var/connected_dirs = 0 - for(var/check_dir in direct_adjacent) - var/turf/neighbor = get_step_resolving_mimic(get_turf(src), check_dir) - if(!istype(neighbor) || !(locate(/obj/structure/fence) in neighbor)) + connected_dirs = 0 + var/turf/my_turf = get_turf(src) + for(var/check_dir in global.cardinal) + var/turf/neighbor = get_step_resolving_mimic(my_turf, check_dir) + if(!istype(neighbor)) continue - connected_dirs |= check_dir + for(var/obj/structure/fence/fence in neighbor) + if(fence_data == RESOLVE_TO_DECL(fence.fence_data)) + connected_dirs |= check_dir + break + +/obj/structure/fence/proc/update_fence_icon() + + // Standalone segment. + if(!connected_dirs) + set_icon_state(fence_data.single_state) + + // Four-way junction. + else if(connected_dirs == (NORTH|SOUTH|EAST|WEST)) + set_icon_state(fence_data.four_way_state) // End segments. - if(check_dir == NORTH || check_dir == SOUTH || check_dir == EAST || check_dir == WEST) - set_dir(global.reverse_dir[check_dir]) + else if(connected_dirs == NORTH || connected_dirs == SOUTH || connected_dirs == EAST || connected_dirs == WEST) + set_dir(connected_dirs) set_icon_state(fence_data.end_state) + // Straight segments. - else if(check_dir == (NORTH | SOUTH) || check_dir == (EAST | WEST)) - if(check_dir & NORTH) + else if(connected_dirs == (NORTH | SOUTH) || connected_dirs == (EAST | WEST)) + if(connected_dirs & NORTH) set_dir(NORTH) else set_dir(EAST) - switch(hole_size) - if(MEDIUM_HOLE) - set_icon_state("[fence_data.straight_state]-cut2") - if(LARGE_HOLE) - set_icon_state("[fence_data.straight_state]-cut3") - else - set_icon_state(fence_data.straight_state) + if(hole_size > 0) + set_icon_state("[fence_data.straight_state]-cut[hole_size]") + else if(is_fencepost()) + set_icon_state(fence_data.post_state) + else + set_icon_state(fence_data.straight_state) // Corner segments. - else if(check_dir in global.cornerdirs) + else if(connected_dirs in global.cornerdirs) set_icon_state(fence_data.corner_state) var/static/list/_corner_fence_to_state_mapping = alist( (NORTHWEST) = SOUTH, @@ -82,10 +126,15 @@ (SOUTHWEST) = EAST, (SOUTHEAST) = WEST ) - set_dir(_corner_fence_to_state_mapping[check_dir]) - - // Junction segments - not currently supported. + set_dir(_corner_fence_to_state_mapping[connected_dirs]) + // Junction segments. + else + set_icon_state(fence_data.three_way_state) + for(var/check_dir in global.cardinal) + if(!(connected_dirs & check_dir)) + set_dir(check_dir) + break /obj/structure/fence/proc/is_cuttable() return icon_state == fence_data.straight_state && hole_size < MAX_HOLE_SIZE @@ -100,7 +149,7 @@ /obj/structure/fence/get_examine_hints(mob/user, distance, infix, suffix) . = ..() - if(cuttable && hole_size < MAX_HOLE_SIZE) + if(is_cuttable()) LAZYADD(., SPAN_SUBTLE("Use wirecutters to [hole_size > NO_HOLE ? "expand the":"cut a"] hole into the fence, allowing passage.")) /obj/structure/fence/cut/medium @@ -134,10 +183,9 @@ return TRUE return ..() - /obj/structure/fence/attackby(obj/item/used_item, mob/user) if(IS_WIRECUTTER(used_item)) - if(!cuttable) + if(!is_cuttable()) to_chat(user, SPAN_WARNING("This section of the fence can't be cut.")) return TRUE var/current_stage = hole_size @@ -162,7 +210,7 @@ return ..() /obj/structure/fence/proc/update_cut_status() - if(!cuttable) + if(!is_cuttable()) return density = TRUE switch(hole_size) @@ -179,62 +227,60 @@ name = "fence door" desc = "Not very useful without a real lock." icon_state = "door-closed" - cuttable = FALSE - var/open = FALSE - var/locked = FALSE -/obj/structure/fence/door/Initialize(mapload) +/obj/structure/fence/door/update_material_name(override_name) + override_name ||= fence_data.door_name . = ..() - update_door_status() + +/obj/structure/fence/door/update_material_desc(override_desc) + override_desc ||= fence_data.door_desc + . = ..() + return INITIALIZE_HINT_LATELOAD /obj/structure/fence/door/update_fence_icon() if(!istype(fence_data)) return + if((connected_dirs & NORTH) || (connected_dirs & SOUTH)) + set_dir(NORTH) + else + set_dir(EAST) if(density) - set_icon_state(fence_data.door_closed_state) + set_icon_state(fence_data.door_state_closed) else - set_icon_state(fence_data.door_opened_state) + set_icon_state(fence_data.door_state_opened) /obj/structure/fence/door/opened icon_state = "door-opened" - open = TRUE density = TRUE /obj/structure/fence/door/locked desc = "It looks like it has a strong padlock attached." - locked = TRUE + +/obj/structure/fence/door/locked/Initialize(mapload) + lock ||= "[random_id(type, 10000, 99999)]" + . = ..() /obj/structure/fence/door/attack_hand(mob/user, list/params) SHOULD_CALL_PARENT(FALSE) if(can_open(user)) toggle(user) else - to_chat(user, SPAN_WARNING("\The [src] is [!open ? "locked" : "stuck open"].")) + to_chat(user, SPAN_WARNING("\The [src] is [density ? "locked" : "stuck open"].")) return TRUE /obj/structure/fence/door/proc/toggle(mob/user) - switch(open) - if(FALSE) - visible_message(SPAN_NOTICE("\The [user] opens \the [src].")) - open = TRUE - if(TRUE) - visible_message(SPAN_NOTICE("\The [user] closes \the [src].")) - open = FALSE - - update_door_status() + density = !density + if(density) + visible_message(SPAN_NOTICE("\The [user] closes \the [src].")) + else + visible_message(SPAN_NOTICE("\The [user] opens \the [src].")) playsound(src, 'sound/machines/click.ogg', 100, 1) - -/obj/structure/fence/door/proc/update_door_status() - density = !open update_icon() /obj/structure/fence/door/proc/can_open(mob/user) - if(locked) - return FALSE - return TRUE + return !lock || !lock.isLocked() #undef CUT_TIME -#undef CLIMB_TIME #undef NO_HOLE #undef MEDIUM_HOLE diff --git a/code/game/objects/structures/fences/fence_types.dm b/code/game/objects/structures/fences/fence_types.dm new file mode 100644 index 000000000000..bec8e817efd8 --- /dev/null +++ b/code/game/objects/structures/fences/fence_types.dm @@ -0,0 +1,61 @@ +/decl/fence_type + var/name = "chain link fence" + var/desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." + var/door_name = "fence gate" + var/door_desc = "Not very useful without a real lock." + var/fence_icon = 'icons/obj/structures/fences/chain.dmi' + var/single_state = "single" + var/corner_state = "corner" + var/straight_state = "straight" + var/post_state = "post" + var/end_state = "end" + var/three_way_state = "three_way" + var/four_way_state = "four_way" + var/door_state_closed = "door-opened" + var/door_state_opened = "door-closed" + +/decl/fence_type/validate() + . = ..() + + if(!fence_icon) + . += "missing fence icon" + return + + if(!single_state || !check_state_in_icon(single_state, fence_icon)) + . += "missing or invalid single_state '[single_state]' from '[fence_icon]'" + if(!straight_state || !check_state_in_icon(straight_state, fence_icon)) + . += "missing or invalid straight_state '[straight_state]' from '[fence_icon]'" + if(!corner_state || !check_state_in_icon(corner_state, fence_icon)) + . += "missing or invalid corner_state '[corner_state]' from '[fence_icon]'" + if(!post_state || !check_state_in_icon(post_state, fence_icon)) + . += "missing or invalid post_state '[post_state]' from '[fence_icon]'" + if(!end_state || !check_state_in_icon(end_state, fence_icon)) + . += "missing or invalid end_state '[end_state]' from '[fence_icon]'" + if(!door_state_closed || !check_state_in_icon(door_state_closed, fence_icon)) + . += "missing or invalid door_state_closed '[door_state_closed]' from '[fence_icon]'" + if(!door_state_opened || !check_state_in_icon(door_state_opened, fence_icon)) + . += "missing or invalid door_state_opened '[door_state_opened]' from '[fence_icon]'" + if(!three_way_state || !check_state_in_icon(three_way_state, fence_icon)) + . += "missing or invalid three_way_state '[three_way_state]' from '[fence_icon]'" + if(!four_way_state || !check_state_in_icon(four_way_state, fence_icon)) + . += "missing or invalid four_way_state '[four_way_state]' from '[fence_icon]'" + +/decl/fence_type/brick + name = "brick fence" + desc = "A brick fence. Not as effective as a wall, but generally it keeps people out." + fence_icon = 'icons/obj/structures/fences/brick.dmi' + +/decl/fence_type/palisade + name = "palisade" + desc = "A tall and imposing palisade with sharpened points atop it." + fence_icon = 'icons/obj/structures/fences/palisade.dmi' + +/decl/fence_type/stick + name = "stick fence" + desc = "A stick fence. Not as effective as a wall, but generally it keeps people out." + fence_icon = 'icons/obj/structures/fences/stick.dmi' + +/decl/fence_type/plank + name = "plank fence" + desc = "A plank fence. Not as effective as a wall, but generally it keeps people out." + fence_icon = 'icons/obj/structures/fences/plank.dmi' diff --git a/icons/obj/structures/fence.dmi b/icons/obj/structures/fence.dmi deleted file mode 100644 index e75db9717befe2449aec5c3340f77f7f8fa8b7c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16571 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 diff --git a/icons/obj/structures/fences/brick.dmi b/icons/obj/structures/fences/brick.dmi new file mode 100755 index 0000000000000000000000000000000000000000..a9e2f9c17b7d7964af9a58c6e416766d460b0035 GIT binary patch literal 2885 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 OF2LHt-n`z-H}Sumkj_K^ literal 0 HcmV?d00001 diff --git a/icons/obj/structures/fences/chain.dmi b/icons/obj/structures/fences/chain.dmi new file mode 100755 index 0000000000000000000000000000000000000000..109a1aaec4b2be0ac23283ceaa48acec54cd93f1 GIT binary patch literal 7160 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 T0VMb-5BTe@)}7qjX2kyj;_i3A literal 0 HcmV?d00001 diff --git a/icons/obj/structures/fences/palisade.dmi b/icons/obj/structures/fences/palisade.dmi new file mode 100755 index 0000000000000000000000000000000000000000..ad5c81aca43deead7b9e828ad8b4452c847fbbc3 GIT binary patch literal 3403 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 N2z3%;Uuug5{sA~$gzNwS literal 0 HcmV?d00001 diff --git a/icons/obj/structures/fences/stick.dmi b/icons/obj/structures/fences/stick.dmi new file mode 100755 index 0000000000000000000000000000000000000000..c10c06afd293e18765b7c4cd10d4703891e453dd GIT binary patch literal 2853 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 literal 0 HcmV?d00001 diff --git a/nebula.dme b/nebula.dme index e664d65973b8..76e67c95d00a 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1443,7 +1443,6 @@ #include "code\game\objects\structures\drying_rack.dm" #include "code\game\objects\structures\emergency_dispenser.dm" #include "code\game\objects\structures\extinguisher.dm" -#include "code\game\objects\structures\fences.dm" #include "code\game\objects\structures\fireaxe_cabinet.dm" #include "code\game\objects\structures\fires.dm" #include "code\game\objects\structures\fishtanks.dm" @@ -1548,6 +1547,8 @@ #include "code\game\objects\structures\decorations\_decoration.dm" #include "code\game\objects\structures\decorations\gargoyle.dm" #include "code\game\objects\structures\doors\_door.dm" +#include "code\game\objects\structures\fences\_fences.dm" +#include "code\game\objects\structures\fences\fence_types.dm" #include "code\game\objects\structures\flora\_flora.dm" #include "code\game\objects\structures\flora\bush.dm" #include "code\game\objects\structures\flora\grass.dm" From 689e667dd874a72411446565c44322ee50c86320 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 14 May 2026 20:26:31 +1000 Subject: [PATCH 1242/1331] Fixing typo in lock code. --- code/game/objects/structures/__structure.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 27239ec9127b..b3247344e7d2 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -63,7 +63,7 @@ . = ..() update_materials() paint_verb ||= "painted" // fallback for the case of no material - if(lock && !istype(loc)) + if(lock && !istype(lock)) lock = new /datum/lock(src, lock) if(!CanFluidPass()) fluid_update(TRUE) From 1863fd70228fcb8fb4e29b0943abb8b1b4536181 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Thu, 14 May 2026 20:28:16 +1000 Subject: [PATCH 1243/1331] Fixing fence strings. --- .../game/objects/structures/fences/_fences.dm | 55 +++++++----------- .../objects/structures/fences/fence_types.dm | 2 +- icons/obj/structures/fences/brick.dmi | Bin icons/obj/structures/fences/chain.dmi | Bin icons/obj/structures/fences/palisade.dmi | Bin icons/obj/structures/fences/plank.dmi | Bin icons/obj/structures/fences/stick.dmi | Bin 7 files changed, 22 insertions(+), 35 deletions(-) mode change 100755 => 100644 icons/obj/structures/fences/brick.dmi mode change 100755 => 100644 icons/obj/structures/fences/chain.dmi mode change 100755 => 100644 icons/obj/structures/fences/palisade.dmi mode change 100755 => 100644 icons/obj/structures/fences/plank.dmi mode change 100755 => 100644 icons/obj/structures/fences/stick.dmi diff --git a/code/game/objects/structures/fences/_fences.dm b/code/game/objects/structures/fences/_fences.dm index c94d2229caf1..9bc566ec9e88 100644 --- a/code/game/objects/structures/fences/_fences.dm +++ b/code/game/objects/structures/fences/_fences.dm @@ -2,15 +2,6 @@ // Chain link sprites ported from /VG/ // Stone, stick, plank and palisade sprites by Doe. -#define CUT_TIME 10 SECONDS - -///section is intact -#define NO_HOLE 0 -///medium hole in the section - can climb through -#define MEDIUM_HOLE 1 -///large hole in the section - can walk through -#define LARGE_HOLE 2 -#define MAX_HOLE_SIZE LARGE_HOLE /obj/structure/fence name = "fence" @@ -28,6 +19,15 @@ var/hole_size = NO_HOLE var/connected_dirs = 0 + var/const/CUT_TIME = 10 SECONDS + ///section is intact + var/const/NO_HOLE = 0 + ///medium hole in the section - can climb through + var/const/MEDIUM_HOLE = 1 + ///large hole in the section - can walk through + var/const/LARGE_HOLE = 2 + var/const/MAX_HOLE_SIZE = LARGE_HOLE + /obj/structure/fence/Destroy() var/turf/prior_loc = loc . = ..() @@ -224,10 +224,13 @@ //FENCE DOORS /obj/structure/fence/door - name = "fence door" - desc = "Not very useful without a real lock." + name = "fence gate" + desc = "Much like a regular door, but thinner." icon_state = "door-closed" +/obj/structure/fence/door/can_install_lock() + return TRUE + /obj/structure/fence/door/update_material_name(override_name) override_name ||= fence_data.door_name . = ..() @@ -253,40 +256,24 @@ icon_state = "door-opened" density = TRUE -/obj/structure/fence/door/locked - desc = "It looks like it has a strong padlock attached." - /obj/structure/fence/door/locked/Initialize(mapload) - lock ||= "[random_id(type, 10000, 99999)]" + lock ||= "fence key #[random_id(type, 10000, 99999)]" . = ..() /obj/structure/fence/door/attack_hand(mob/user, list/params) SHOULD_CALL_PARENT(FALSE) - if(can_open(user)) - toggle(user) + if(!density || can_open(user)) + density = !density + visible_message(SPAN_NOTICE("\The [user] [density ? "opens" : "closes"] \the [src].")) + playsound(src, 'sound/machines/click.ogg', 100, 1) + update_icon() else - to_chat(user, SPAN_WARNING("\The [src] is [density ? "locked" : "stuck open"].")) + to_chat(user, SPAN_WARNING("\The [src] is locked.")) return TRUE -/obj/structure/fence/door/proc/toggle(mob/user) - density = !density - if(density) - visible_message(SPAN_NOTICE("\The [user] closes \the [src].")) - else - visible_message(SPAN_NOTICE("\The [user] opens \the [src].")) - playsound(src, 'sound/machines/click.ogg', 100, 1) - update_icon() - /obj/structure/fence/door/proc/can_open(mob/user) return !lock || !lock.isLocked() -#undef CUT_TIME - -#undef NO_HOLE -#undef MEDIUM_HOLE -#undef LARGE_HOLE -#undef MAX_HOLE_SIZE - // Mapping/crafting helpers. /obj/structure/fence/brick icon_state = /decl/fence_type/brick::straight_state diff --git a/code/game/objects/structures/fences/fence_types.dm b/code/game/objects/structures/fences/fence_types.dm index bec8e817efd8..029df0390007 100644 --- a/code/game/objects/structures/fences/fence_types.dm +++ b/code/game/objects/structures/fences/fence_types.dm @@ -2,7 +2,7 @@ var/name = "chain link fence" var/desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." var/door_name = "fence gate" - var/door_desc = "Not very useful without a real lock." + var/door_desc = "Much like a regular door, but thinner." var/fence_icon = 'icons/obj/structures/fences/chain.dmi' var/single_state = "single" var/corner_state = "corner" diff --git a/icons/obj/structures/fences/brick.dmi b/icons/obj/structures/fences/brick.dmi old mode 100755 new mode 100644 diff --git a/icons/obj/structures/fences/chain.dmi b/icons/obj/structures/fences/chain.dmi old mode 100755 new mode 100644 diff --git a/icons/obj/structures/fences/palisade.dmi b/icons/obj/structures/fences/palisade.dmi old mode 100755 new mode 100644 diff --git a/icons/obj/structures/fences/plank.dmi b/icons/obj/structures/fences/plank.dmi old mode 100755 new mode 100644 diff --git a/icons/obj/structures/fences/stick.dmi b/icons/obj/structures/fences/stick.dmi old mode 100755 new mode 100644 From cd4d673875a6859694a4e03b0b594cd246412a5c Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 16 May 2026 14:39:07 +1000 Subject: [PATCH 1244/1331] Corrects 'a multiple iron arrow' in embed examine. --- code/modules/mob/living/human/examine.dm | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index 00c79f546190..32bfaad2c934 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -165,12 +165,15 @@ shown_objects += embedlist var/parsedembed[0] for(var/obj/embedded in embedlist) - if(!parsedembed.len || (!parsedembed.Find(embedded.name) && !parsedembed.Find("multiple [embedded.name]"))) - parsedembed.Add(embedded.name) - else if(!parsedembed.Find("multiple [embedded.name]")) - parsedembed.Remove(embedded.name) - parsedembed.Add("multiple "+embedded.name) - wound_flavor_text["[E.name]"] += "The [wound.desc] on [use_his] [E.name] has \a [english_list(parsedembed, and_text = " and a ", comma_text = ", a ")] sticking out of it!
    " + var/single_embed_string = "\a [embedded.name]" + var/plural_embed_string = "multiple [text_make_plural(embedded.name)]" + if(!parsedembed.len || (!parsedembed.Find(single_embed_string) && !parsedembed.Find(plural_embed_string))) + parsedembed.Add(single_embed_string) + else if(!parsedembed.Find(plural_embed_string)) + parsedembed.Remove(single_embed_string) + parsedembed.Add(plural_embed_string) + wound_flavor_text[E.organ_tag] += SPAN_WARNING("The [wound.desc] on [pronouns.his] [E.name] has [english_list(parsedembed, and_text = " and ", comma_text = ", ")] sticking out of it!") + for(var/hidden in hidden_bleeders) wound_flavor_text[hidden] = "[use_He] [use_has] blood soaking through [hidden] around [use_his] [english_list(hidden_bleeders[hidden])]!
    " From 3d4459a04cf565717ad7f594852578dc1ea21462 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 16 May 2026 14:35:06 +1000 Subject: [PATCH 1245/1331] Offsets mob modifers to centre, Prone -> Lying --- code/_onclick/hud/hud_types/_hud.dm | 4 ++-- code/_onclick/hud/screen/screen_mob_modifier.dm | 2 +- code/modules/mob_modifiers/definitions/modifiers_prone.dm | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/_onclick/hud/hud_types/_hud.dm b/code/_onclick/hud/hud_types/_hud.dm index 5a64f04f09a9..1e9b6337df0f 100644 --- a/code/_onclick/hud/hud_types/_hud.dm +++ b/code/_onclick/hud/hud_types/_hud.dm @@ -220,9 +220,9 @@ mymob.remove_mob_modifier(/decl/mob_modifier/restrained, source = mymob) if(mymob.current_posture?.prone) - mymob.add_mob_modifier(/decl/mob_modifier/prone, source = mymob) + mymob.add_mob_modifier(/decl/mob_modifier/lying, source = mymob) else - mymob.remove_mob_modifier(/decl/mob_modifier/prone, source = mymob) + mymob.remove_mob_modifier(/decl/mob_modifier/lying, source = mymob) for(var/obj/screen/elem as anything in hud_elements_update_in_life) elem.update_icon() diff --git a/code/_onclick/hud/screen/screen_mob_modifier.dm b/code/_onclick/hud/screen/screen_mob_modifier.dm index d8d32bd3c586..a229d9d7db4b 100644 --- a/code/_onclick/hud/screen/screen_mob_modifier.dm +++ b/code/_onclick/hud/screen/screen_mob_modifier.dm @@ -1,5 +1,5 @@ /obj/screen/mob_modifiers - screen_loc = "CENTER,TOP" + screen_loc = "CENTER:8,TOP" icon_state = "blank" requires_ui_style = FALSE diff --git a/code/modules/mob_modifiers/definitions/modifiers_prone.dm b/code/modules/mob_modifiers/definitions/modifiers_prone.dm index caf5b30c53b6..14d04ecac121 100644 --- a/code/modules/mob_modifiers/definitions/modifiers_prone.dm +++ b/code/modules/mob_modifiers/definitions/modifiers_prone.dm @@ -1,10 +1,10 @@ -/decl/mob_modifier/prone - name = "Prone" - desc = "You are lying prone and may need to stand up before taking action." +/decl/mob_modifier/lying + name = "Lying" + desc = "You are lying down and may need to stand up before taking action." hud_icon_state = "prone" show_indefinite_duration = FALSE -/decl/mob_modifier/prone/on_modifier_datum_click(mob/living/_owner, decl/mob_modifier/modifier, params) +/decl/mob_modifier/lying/on_modifier_datum_click(mob/living/_owner, decl/mob_modifier/modifier, params) if(_owner.current_posture?.prone) _owner.lay_down() return TRUE From 179cebf278c6d336a418ec2b35f06f1d283ba262 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sat, 16 May 2026 14:19:48 +1000 Subject: [PATCH 1246/1331] Mech UI and code cleanup. --- code/_onclick/hud/screen/screen_exosuit.dm | 4 +- code/modules/mechs/{mech.dm => _mech.dm} | 9 ++- code/modules/mechs/equipment/_equipment.dm | 70 ++++--------------- code/modules/mechs/equipment/combat.dm | 9 +-- .../mechs/equipment/combat_projectile.dm | 24 +++---- code/modules/mechs/equipment/engineering.dm | 3 +- code/modules/mechs/equipment/medical.dm | 3 +- .../modules/mechs/equipment/mounted_system.dm | 41 +++++++++++ code/modules/mechs/equipment/utility.dm | 35 ++++------ code/modules/mechs/interface/_interface.dm | 12 ++-- code/modules/mechs/mech_construction.dm | 13 ++-- code/modules/mechs/mech_interaction.dm | 4 +- code/modules/mob/living/living.dm | 3 + code/modules/mob/mob_intent.dm | 6 ++ nebula.dme | 3 +- 15 files changed, 112 insertions(+), 127 deletions(-) rename code/modules/mechs/{mech.dm => _mech.dm} (98%) create mode 100644 code/modules/mechs/equipment/mounted_system.dm diff --git a/code/_onclick/hud/screen/screen_exosuit.dm b/code/_onclick/hud/screen/screen_exosuit.dm index ad9171ed446d..5aaf6c0c9c8a 100644 --- a/code/_onclick/hud/screen/screen_exosuit.dm +++ b/code/_onclick/hud/screen/screen_exosuit.dm @@ -198,6 +198,7 @@ maptext_x = -16 maptext_width = 64 maptext_y = -8 + screen_loc = "RIGHT-2:28,CENTER+1:22" /obj/screen/exosuit/toggle name = "toggle" @@ -363,9 +364,9 @@ /obj/screen/exosuit/heat name = "heat probe" icon_state = "heatprobe" + screen_loc = "RIGHT-2:28,CENTER+1:12" var/celsius = TRUE var/obj/screen/exosuit/needle/gauge_needle = null - desc = "TEST" /obj/screen/exosuit/heat/Initialize(mapload, mob/_owner, ui_style, ui_color, ui_alpha) . = ..() @@ -417,6 +418,7 @@ /obj/screen/exosuit/health name = "exosuit integrity" icon_state = "health" + screen_loc = "RIGHT-2:28,CENTER:12" /obj/screen/exosuit/health/handle_click(mob/user, params) if(!..()) diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/_mech.dm similarity index 98% rename from code/modules/mechs/mech.dm rename to code/modules/mechs/_mech.dm index 25f9ecbf7873..957fe6f236a4 100644 --- a/code/modules/mechs/mech.dm +++ b/code/modules/mechs/_mech.dm @@ -68,10 +68,10 @@ var/mob/living/current_user = null - -//Pixel projectiles need a client, so we need a way to pass who the last user was for view calcs -/mob/living/proc/get_effective_gunner() - return src +/mob/living/exosuit/carried_mob_intent_changed(mob/user, decl/intent/new_intent) + if(user in pilots) + return set_intent(new_intent) + return ..() /mob/living/exosuit/get_effective_gunner() return current_user @@ -260,4 +260,3 @@ if(current_user) return FALSE return ..() - diff --git a/code/modules/mechs/equipment/_equipment.dm b/code/modules/mechs/equipment/_equipment.dm index ca8273afd3b2..c98994742c58 100644 --- a/code/modules/mechs/equipment/_equipment.dm +++ b/code/modules/mechs/equipment/_equipment.dm @@ -27,26 +27,24 @@ /obj/item/mech_equipment/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params) if(require_adjacent) if(!inrange) - return 0 + return FALSE if (owner && loc == owner && ((user in owner.pilots) || user == owner)) if(target in owner.contents) - return 0 - + return FALSE if(!(owner.get_cell()?.check_charge(active_power_use * CELLRATE))) to_chat(user, SPAN_WARNING("The power indicator flashes briefly as you attempt to use \the [src].")) - return 0 - return 1 - else - return 0 + return FALSE + return TRUE + return FALSE +// Per base attack_self(), return TRUE on an action performed (ie. no further action done) and FALSE to continue action. /obj/item/mech_equipment/attack_self(var/mob/user) - if (owner && loc == owner && ((user in owner.pilots) || user == owner)) - if(!(owner.get_cell()?.check_charge(active_power_use * CELLRATE))) - to_chat(user, SPAN_WARNING("The power indicator flashes briefly as you attempt to use \the [src].")) - return 0 - return 1 - else - return 0 + if(!owner || loc != owner || (!(user in owner.pilots) && user != owner)) + return TRUE + if(!(owner.get_cell()?.check_charge(active_power_use * CELLRATE))) + to_chat(user, SPAN_WARNING("The power indicator flashes briefly as you attempt to use \the [src].")) + return TRUE + return FALSE /obj/item/mech_equipment/get_examine_strings(mob/user, distance, infix, suffix) . = ..() @@ -89,50 +87,6 @@ //Installed equipment shall not be unequiped. return FALSE -/obj/item/mech_equipment/mounted_system - abstract_type = /obj/item/mech_equipment/mounted_system - var/obj/item/holding - -/obj/item/mech_equipment/mounted_system/attack_self(var/mob/user) - . = ..() - if(. && holding) - return holding.attack_self(user) - -/obj/item/mech_equipment/mounted_system/proc/forget_holding() - if(holding) //It'd be strange for this to be called with this var unset - events_repository.unregister(/decl/observ/destroyed, holding, src, PROC_REF(forget_holding)) - holding = null - if(!QDELETED(src)) - qdel(src) - -/obj/item/mech_equipment/mounted_system/Initialize() - . = ..() - if(ispath(holding)) - holding = new holding(src) - events_repository.register(/decl/observ/destroyed, holding, src, PROC_REF(forget_holding)) - if(!istype(holding)) - return - if(!icon_state) - icon = holding.icon - icon_state = holding.icon_state - SetName(holding.name) - desc = "[holding.desc] This one is suitable for installation on an exosuit." - -/obj/item/mech_equipment/mounted_system/Destroy() - events_repository.unregister(/decl/observ/destroyed, holding, src, PROC_REF(forget_holding)) - if(holding) - QDEL_NULL(holding) - . = ..() - -/obj/item/mech_equipment/mounted_system/get_effective_obj() - return (holding ? holding : src) - -/obj/item/mech_equipment/mounted_system/get_hardpoint_status_value() - return (holding ? holding.get_hardpoint_status_value() : null) - -/obj/item/mech_equipment/mounted_system/get_hardpoint_maptext() - return (holding ? holding.get_hardpoint_maptext() : null) - /obj/item/proc/get_hardpoint_status_value() return null diff --git a/code/modules/mechs/equipment/combat.dm b/code/modules/mechs/equipment/combat.dm index 750d69df49de..fa7e758282a8 100644 --- a/code/modules/mechs/equipment/combat.dm +++ b/code/modules/mechs/equipment/combat.dm @@ -79,8 +79,7 @@ . = ..() /obj/item/mech_equipment/shields/attack_self(var/mob/user) - . = ..() - if(.) + if(!(. = ..())) toggle() /obj/item/mech_equipment/shields/proc/stop_damage(var/damage) @@ -246,8 +245,7 @@ do_attack_effect(T, "smash") /obj/item/mech_equipment/ballistic_shield/attack_self(mob/user) - . = ..() - if (.) //FORM A SHIELD WALL! + if (!(. = ..())) //FORM A SHIELD WALL! if (last_max_block + 2 SECONDS < world.time) owner.visible_message(SPAN_WARNING("\The [owner] raises \the [src], locking it in place!"), blind_message = SPAN_WARNING("You hear the whir of motors and scratching metal!")) playsound(src ,'sound/effects/bamf.ogg',35,1) @@ -320,8 +318,7 @@ O.handle_flashed(flash_time, do_stun = FALSE) /obj/item/mech_equipment/flash/attack_self(mob/user) - . = ..() - if(.) + if(!(. = ..())) if(world.time < next_use) to_chat(user, SPAN_WARNING("\The [src] is recharging!")) return diff --git a/code/modules/mechs/equipment/combat_projectile.dm b/code/modules/mechs/equipment/combat_projectile.dm index f71d5a2a5e59..a717954a893f 100644 --- a/code/modules/mechs/equipment/combat_projectile.dm +++ b/code/modules/mechs/equipment/combat_projectile.dm @@ -1,3 +1,11 @@ +/obj/item/mech_equipment/mounted_system/projectile + name = "mounted submachine gun" + icon_state = "mech_ballistic" + holding = /obj/item/gun/projectile/automatic/smg/mech + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_WEAPONS) + origin_tech = @'{"programming":4,"combat":6,"engineering":5}' + /obj/item/mech_equipment/mounted_system/projectile/attackby(var/obj/item/used_item, var/mob/user) var/obj/item/gun/projectile/automatic/A = holding if(!istype(A)) @@ -10,12 +18,6 @@ to_chat(user, SPAN_NOTICE("You load the ammo magazine into \the [src].")) return TRUE -/obj/item/mech_equipment/mounted_system/projectile/attack_self(var/mob/user) - . = ..() - if(. && holding) - var/obj/item/gun/M = holding - return M.switch_firemodes() - /obj/item/gun/projectile/automatic/get_hardpoint_status_value() if(!isnull(ammo_magazine)) return ammo_magazine.get_stored_ammo_count() @@ -26,14 +28,6 @@ return 0 //Weapons below this. -/obj/item/mech_equipment/mounted_system/projectile - name = "mounted submachine gun" - icon_state = "mech_ballistic" - holding = /obj/item/gun/projectile/automatic/smg/mech - restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) - restricted_software = list(MECH_SOFTWARE_WEAPONS) - origin_tech = @'{"programming":4,"combat":6,"engineering":5}' - /obj/item/gun/projectile/automatic/smg/mech magazine_type = /obj/item/ammo_magazine/mech/smg_top allowed_magazines = /obj/item/ammo_magazine/mech/smg_top @@ -167,4 +161,4 @@ gun.set_autofire(over_object, owner, FALSE) return - gun.clear_autofire() \ No newline at end of file + gun.clear_autofire() diff --git a/code/modules/mechs/equipment/engineering.dm b/code/modules/mechs/equipment/engineering.dm index 607226f683e0..00ed16ae5e14 100644 --- a/code/modules/mechs/equipment/engineering.dm +++ b/code/modules/mechs/equipment/engineering.dm @@ -179,8 +179,7 @@ owner.update_icon() /obj/item/mech_equipment/atmos_shields/attack_self(mob/user) - . = ..() - if(.) + if(!(. = ..())) if(active) deactivate() else diff --git a/code/modules/mechs/equipment/medical.dm b/code/modules/mechs/equipment/medical.dm index f18d56ea2e0e..15f415cd6478 100644 --- a/code/modules/mechs/equipment/medical.dm +++ b/code/modules/mechs/equipment/medical.dm @@ -25,8 +25,7 @@ sleeper?.go_out() /obj/item/mech_equipment/sleeper/attack_self(var/mob/user) - . = ..() - if(.) + if(!(. = ..())) sleeper.ui_interact(user) /obj/item/mech_equipment/sleeper/attackby(var/obj/item/used_item, var/mob/user) diff --git a/code/modules/mechs/equipment/mounted_system.dm b/code/modules/mechs/equipment/mounted_system.dm new file mode 100644 index 000000000000..d8a62f70a8fb --- /dev/null +++ b/code/modules/mechs/equipment/mounted_system.dm @@ -0,0 +1,41 @@ +/obj/item/mech_equipment/mounted_system + abstract_type = /obj/item/mech_equipment/mounted_system + var/obj/item/holding + +/obj/item/mech_equipment/mounted_system/attack_self(var/mob/user) + return holding ? holding.attack_self(user) : ..() + +/obj/item/mech_equipment/mounted_system/proc/forget_holding() + if(holding) //It'd be strange for this to be called with this var unset + events_repository.unregister(/decl/observ/destroyed, holding, src, PROC_REF(forget_holding)) + holding = null + if(!QDELETED(src)) + qdel(src) + +/obj/item/mech_equipment/mounted_system/Initialize() + . = ..() + if(ispath(holding)) + holding = new holding(src) + events_repository.register(/decl/observ/destroyed, holding, src, PROC_REF(forget_holding)) + if(!istype(holding)) + return + if(!icon_state) + icon = holding.icon + icon_state = holding.icon_state + SetName(holding.name) + desc = "[holding.desc] This one is suitable for installation on an exosuit." + +/obj/item/mech_equipment/mounted_system/Destroy() + events_repository.unregister(/decl/observ/destroyed, holding, src, PROC_REF(forget_holding)) + if(holding) + QDEL_NULL(holding) + . = ..() + +/obj/item/mech_equipment/mounted_system/get_effective_obj() + return (holding ? holding : src) + +/obj/item/mech_equipment/mounted_system/get_hardpoint_status_value() + return (holding ? holding.get_hardpoint_status_value() : null) + +/obj/item/mech_equipment/mounted_system/get_hardpoint_maptext() + return (holding ? holding.get_hardpoint_maptext() : null) diff --git a/code/modules/mechs/equipment/utility.dm b/code/modules/mechs/equipment/utility.dm index 37b955a8ddfe..81ed2e6b9fe3 100644 --- a/code/modules/mechs/equipment/utility.dm +++ b/code/modules/mechs/equipment/utility.dm @@ -135,8 +135,7 @@ owner.visible_message("[owner] pushes [target] out of the way.") /obj/item/mech_equipment/clamp/attack_self(var/mob/user) - . = ..() - if(.) + if(!(. = ..())) drop_carrying(user, TRUE) /obj/item/mech_equipment/clamp/get_alt_interactions(mob/user) @@ -221,8 +220,7 @@ update_icon() /obj/item/mech_equipment/light/attack_self(var/mob/user) - . = ..() - if(.) + if(!(. = ..())) toggle() to_chat(user, "You switch \the [src] [on ? "on" : "off"].") @@ -276,15 +274,12 @@ else string += "Push" return string - /obj/item/mech_equipment/catapult/attack_self(var/mob/user) - . = ..() - if(.) + if(!(. = ..())) mode = mode == CATAPULT_SINGLE ? CATAPULT_AREA : CATAPULT_SINGLE to_chat(user, SPAN_NOTICE("You set \the [src] to [mode == CATAPULT_SINGLE ? "single" : "multi"]-target mode.")) update_icon() - /obj/item/mech_equipment/catapult/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params) . = ..() if(.) @@ -393,11 +388,9 @@ drill_head = new drill_head(src) /obj/item/mech_equipment/drill/attack_self(var/mob/user) - . = ..() - if(.) - if(drill_head) - owner.visible_message(SPAN_WARNING("[owner] revs the [drill_head], menancingly.")) - playsound(src, 'sound/mecha/mechdrill.ogg', 50, 1) + if(!(. = ..()) && drill_head) + owner.visible_message(SPAN_WARNING("[owner] revs the [drill_head] menancingly.")) + playsound(src, 'sound/mecha/mechdrill.ogg', 50, 1) /obj/item/mech_equipment/drill/get_hardpoint_maptext() if(drill_head) @@ -615,14 +608,11 @@ return FALSE /obj/item/mech_equipment/ionjets/attack_self(mob/user) - . = ..() - if (!.) - return - - if (active) - deactivate() - else - activate() + if(!(. = ..())) + if(active) + deactivate() + else + activate() /obj/item/mech_equipment/ionjets/proc/activate() passive_power_use = activated_passive_power @@ -763,8 +753,7 @@ D.ui_interact(user) /obj/item/mech_equipment/camera/attack_self(mob/user) - . = ..() - if(.) + if(!(. = ..())) if(active) deactivate() else diff --git a/code/modules/mechs/interface/_interface.dm b/code/modules/mechs/interface/_interface.dm index f2985acc7324..a77bc9d53b7a 100644 --- a/code/modules/mechs/interface/_interface.dm +++ b/code/modules/mechs/interface/_interface.dm @@ -25,32 +25,28 @@ var/i = 1 for(var/hardpoint in hardpoints) var/obj/screen/exosuit/hardpoint/H = new(null, src, null, null, null, null, hardpoint) - H.screen_loc = "LEFT:6,TOP-[i]:-16" + H.screen_loc = "LEFT+1:6,TOP-[i]:-16" hud_elements |= H hardpoint_hud_elements[hardpoint] = H i++ if(body && body.pilot_coverage >= 100) additional_hud_elements += /obj/screen/exosuit/toggle/air - i = 0 - var/pos = 7 + i = 16 for(var/additional_hud in additional_hud_elements) var/obj/screen/exosuit/M = new additional_hud(null, src) - M.screen_loc = "LEFT:6,BOTTOM+[pos]:[i]" + M.screen_loc = "LEFT:6,TOP-1:-[i]" hud_elements |= M - i -= M.height + i += M.height hud_health = new /obj/screen/exosuit/health(null, src) - hud_health.screen_loc = "RIGHT-1:28,CENTER-3:11" hud_elements |= hud_health hud_open = locate(/obj/screen/exosuit/toggle/hatch_open) in hud_elements hud_power = new /obj/screen/exosuit/power(null, src) - hud_power.screen_loc = "RIGHT-1:28,CENTER-4:25" hud_elements |= hud_power hud_power_control = locate(/obj/screen/exosuit/toggle/power_control) in hud_elements hud_camera = locate(/obj/screen/exosuit/toggle/camera) in hud_elements hud_heat = new /obj/screen/exosuit/heat(null, src) - hud_heat.screen_loc = "RIGHT-1:28,CENTER-4" hud_elements |= hud_heat refresh_hud() diff --git a/code/modules/mechs/mech_construction.dm b/code/modules/mechs/mech_construction.dm index fb4f5f6b705e..41a213d4893a 100644 --- a/code/modules/mechs/mech_construction.dm +++ b/code/modules/mechs/mech_construction.dm @@ -61,15 +61,19 @@ /mob/living/exosuit/proc/install_system(var/obj/item/system, var/system_hardpoint, var/mob/user) set waitfor = FALSE + if(hardpoints_locked || hardpoints[system_hardpoint]) + to_chat(user, SPAN_WARNING("\The [system] could not be installed in that hardpoint, as it is locked or occupied.")) return FALSE var/obj/item/mech_equipment/ME = system if(istype(ME)) if(ME.restricted_hardpoints && !(system_hardpoint in ME.restricted_hardpoints)) + to_chat(user, SPAN_WARNING("\The [system] could not be installed in that hardpoint, as it is restricted.")) return FALSE if(ME.restricted_software) if(!head || !head.software) + to_chat(user, SPAN_WARNING("\The [system] could not be installed in that hardpoint, as it requires specialist software.")) return FALSE var/found for(var/software in ME.restricted_software) @@ -77,6 +81,7 @@ found = TRUE break if(!found) + to_chat(user, SPAN_WARNING("\The [system] could not be installed in that hardpoint, as it requires specialist software.")) return FALSE else return FALSE @@ -91,10 +96,10 @@ if(!do_after(user, delay, src) || user.get_active_held_item() != system) return FALSE - if(user.try_unequip(system)) - to_chat(user, SPAN_NOTICE("You install \the [system] in \the [src]'s [system_hardpoint].")) - playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1) - else return FALSE + if(!user.try_unequip(system)) + return FALSE + to_chat(user, SPAN_NOTICE("You install \the [system] in \the [src]'s [system_hardpoint].")) + playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1) events_repository.register(/decl/observ/destroyed, system, src, PROC_REF(forget_module)) diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm index 8a8e9b671675..eb900899d021 100644 --- a/code/modules/mechs/mech_interaction.dm +++ b/code/modules/mechs/mech_interaction.dm @@ -349,8 +349,8 @@ var/to_place = input("Where would you like to install it?") as null|anything in (realThing.restricted_hardpoints & free_hardpoints) if(!to_place) to_chat(user, SPAN_WARNING("There is no room to install \the [used_item].")) - else if(!install_system(used_item, to_place, user)) - to_chat(user, SPAN_WARNING("\The [used_item] could not be installed in that hardpoint.")) + else + install_system(used_item, to_place, user) return TRUE // Apply customisation. diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 24bb4de3f4b9..55e94083cb93 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2017,3 +2017,6 @@ default behaviour is: /mob/living/proc/is_tagging_suitable() return !key && !client +//Pixel projectiles need a client, so we need a way to pass who the last user was for view calcs +/mob/living/proc/get_effective_gunner() + return src diff --git a/code/modules/mob/mob_intent.dm b/code/modules/mob/mob_intent.dm index f9271fb64fec..4eeeaaff9ff4 100644 --- a/code/modules/mob/mob_intent.dm +++ b/code/modules/mob/mob_intent.dm @@ -115,10 +115,16 @@ _a_intent = new_intent if(istype(hud_used)) hud_used.refresh_element(HUD_INTENT) + if(ismob(loc)) + var/mob/holder = loc + holder.carried_mob_intent_changed(src, _a_intent) return TRUE return FALSE +/mob/proc/carried_mob_intent_changed(mob/user, decl/intent/new_intent) + return + /mob/proc/get_intent() RETURN_TYPE(/decl/intent) var/list/available_intents = get_available_intents() diff --git a/nebula.dme b/nebula.dme index e664d65973b8..10b0a0e685d6 100644 --- a/nebula.dme +++ b/nebula.dme @@ -2772,8 +2772,8 @@ #include "code\modules\materials\stack_types\material_stack_misc.dm" #include "code\modules\materials\stack_types\material_stack_nail.dm" #include "code\modules\materials\stack_types\material_stack_ore.dm" +#include "code\modules\mechs\_mech.dm" #include "code\modules\mechs\_mech_setup.dm" -#include "code\modules\mechs\mech.dm" #include "code\modules\mechs\mech_construction.dm" #include "code\modules\mechs\mech_damage.dm" #include "code\modules\mechs\mech_damage_immunity.dm" @@ -2796,6 +2796,7 @@ #include "code\modules\mechs\equipment\combat_projectile.dm" #include "code\modules\mechs\equipment\engineering.dm" #include "code\modules\mechs\equipment\medical.dm" +#include "code\modules\mechs\equipment\mounted_system.dm" #include "code\modules\mechs\equipment\utility.dm" #include "code\modules\mechs\interface\_interface.dm" #include "code\modules\mechs\premade\_premade.dm" From b21f9391f5d902601daed94a9ed7d24279503ae3 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 17 May 2026 00:20:53 +1000 Subject: [PATCH 1247/1331] Removing unused tallness var. --- .../maps/template_types/random_exoplanet/planetoid_data.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm b/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm index 95df86b82f93..5591ee519965 100644 --- a/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm +++ b/code/modules/maps/template_types/random_exoplanet/planetoid_data.dm @@ -16,8 +16,6 @@ var/width ///Preferred height for all the planet z-levels. Null means it's up to each z-levels. Not reliable for telling the height of the levels under this planet. var/height - ///Preferred amount of vertically connected z-levels for this planets. Null means it's up to each z-levels. - var/tallness = 1 ///Topmost level data datum id of the root z stack (ID only, because this datum has an uncontrolled lifetime, and we don't want dangling refs) var/topmost_level_id ///Level data id for the level that's considered to be the planet's surface. In other words, the topmost firm ground level of the root z stack. From 4772d11bff2a17023b9e48b305d51207a485cd71 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 17 May 2026 00:24:49 +1000 Subject: [PATCH 1248/1331] Cleaning up citizenship vars. --- .../backgrounds/citizenship/_citizenship.dm | 14 +++----------- .../backgrounds/citizenship/citizenship_other.dm | 1 - 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/code/modules/backgrounds/citizenship/_citizenship.dm b/code/modules/backgrounds/citizenship/_citizenship.dm index e16cc9455ac7..8ff6186f88a3 100644 --- a/code/modules/backgrounds/citizenship/_citizenship.dm +++ b/code/modules/backgrounds/citizenship/_citizenship.dm @@ -1,18 +1,10 @@ /decl/background_detail/citizenship abstract_type = /decl/background_detail/citizenship category = /decl/background_category/citizenship - var/ruling_body = "Other Faction" - var/capital - var/size_heading = "Systems" - var/size_value - var/founded + var/issuing_body /decl/background_detail/citizenship/get_text_details() . = list() - if(!isnull(capital)) - . += "Capital: [capital]." - if(!isnull(size_value) && !isnull(size_heading)) - . += "Extent: [size_value] [size_heading]." - if(!isnull(founded)) - . += "Founded: [founded]" + if(!isnull(issuing_body)) + . += "Issuing body: [issuing_body]." . += ..() diff --git a/code/modules/backgrounds/citizenship/citizenship_other.dm b/code/modules/backgrounds/citizenship/citizenship_other.dm index cfeed30a29cf..8e9b9ab29408 100644 --- a/code/modules/backgrounds/citizenship/citizenship_other.dm +++ b/code/modules/backgrounds/citizenship/citizenship_other.dm @@ -13,7 +13,6 @@ uid = "stateless" description = "You do not possess any kind of official citizenship." economic_power = 0 - capital = "None" /decl/background_detail/citizenship/synthetic name = "Stateless Drone" From b7ad045f1e12fc38cf821c291f57e1ea115d1abc Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 18 May 2026 12:10:05 +1000 Subject: [PATCH 1249/1331] Walls will be undamageable to certain weapons based on material. --- code/game/turfs/walls/wall_attacks.dm | 12 +++++++----- code/modules/materials/_materials.dm | 3 +++ .../definitions/solids/materials_solid_alien.dm | 1 + .../definitions/solids/materials_solid_exotic.dm | 2 ++ .../definitions/solids/materials_solid_fission.dm | 3 ++- .../definitions/solids/materials_solid_gemstones.dm | 1 + .../definitions/solids/materials_solid_ice.dm | 1 + .../definitions/solids/materials_solid_metal.dm | 1 + .../definitions/solids/materials_solid_mineral.dm | 1 + .../definitions/solids/materials_solid_mundane.dm | 1 + .../definitions/solids/materials_solid_organic.dm | 1 + .../definitions/solids/materials_solid_stone.dm | 1 + .../definitions/solids/materials_solid_wood.dm | 1 + 13 files changed, 23 insertions(+), 6 deletions(-) diff --git a/code/game/turfs/walls/wall_attacks.dm b/code/game/turfs/walls/wall_attacks.dm index 4c567def7856..16d98d068218 100644 --- a/code/game/turfs/walls/wall_attacks.dm +++ b/code/game/turfs/walls/wall_attacks.dm @@ -303,25 +303,27 @@ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.do_attack_animation(src) + + var/damage_threshold = max(2, max(material.wall_damage_threshold, reinf_material?.wall_damage_threshold)) var/material_divisor = max(material.brute_armor, reinf_material?.brute_armor) if(W.atom_damage_type == BURN) material_divisor = max(material.burn_armor, reinf_material?.burn_armor) var/effective_force = round(force / material_divisor) - if(effective_force < 2) - visible_message(SPAN_DANGER("\The [user] [pick(W.attack_verb)] \the [src] with \the [W], but it had no effect!")) + if(effective_force < damage_threshold) + visible_message(SPAN_DANGER("\The [user] has [pick(W.attack_verb)] \the [src] with \the [W], but it has no effect!")) playsound(src, hitsound, 25, 1) return TRUE // Check for a glancing blow. var/dam_prob = max(0, 100 - material.hardness + effective_force + W.armor_penetration) if(!prob(dam_prob)) - visible_message(SPAN_DANGER("\The [user] [pick(W.attack_verb)] \the [src] with \the [W], but it bounced off!")) + visible_message(SPAN_DANGER("\The [user] has [pick(W.attack_verb)] \the [src] with \the [W], but it bounced off!")) playsound(src, hitsound, 25, 1) if(user.skill_fail_prob(SKILL_HAULING, 40, SKILL_ADEPT)) SET_STATUS_MAX(user, STAT_WEAK, 2) visible_message(SPAN_DANGER("\The [user] is knocked back by the force of the blow!")) return TRUE - playsound(src, get_hit_sound(), 50, 1) - visible_message(SPAN_DANGER("\The [user] [pick(W.attack_verb)] \the [src] with \the [W]!")) + visible_message(SPAN_DANGER("\The [user] has [pick(W.attack_verb)] \the [src] with \the [W]!")) + playsound(src, hitsound, 50, 1) take_damage(effective_force) return TRUE \ No newline at end of file diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 65b38e35046d..3b5e519e9fed 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -362,6 +362,9 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /// If an item has a null paint_verb, it automatically sets it based on material. var/paint_verb = "painted" + // Physical attacks against walls must beat this threshold to cause damage. + var/wall_damage_threshold = 2 + // Placeholders for light tiles and rglass. /decl/material/proc/reinforce(var/mob/user, var/obj/item/stack/material/used_stack, var/obj/item/stack/material/target_stack, var/use_sheets = 1) if(!used_stack.can_use(use_sheets)) diff --git a/code/modules/materials/definitions/solids/materials_solid_alien.dm b/code/modules/materials/definitions/solids/materials_solid_alien.dm index 97ff03bfe45f..c36607ddac7f 100644 --- a/code/modules/materials/definitions/solids/materials_solid_alien.dm +++ b/code/modules/materials/definitions/solids/materials_solid_alien.dm @@ -12,6 +12,7 @@ default_solid_form = /obj/item/stack/material/cubes exoplanet_rarity_plant = MAT_RARITY_EXOTIC exoplanet_rarity_gas = MAT_RARITY_NOWHERE + wall_damage_threshold = 20 /decl/material/solid/metal/aliumium/Initialize() icon_base = 'icons/turf/walls/metal.dmi' diff --git a/code/modules/materials/definitions/solids/materials_solid_exotic.dm b/code/modules/materials/definitions/solids/materials_solid_exotic.dm index 1d3448bdfed3..11c4c804bf1d 100644 --- a/code/modules/materials/definitions/solids/materials_solid_exotic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_exotic.dm @@ -30,6 +30,7 @@ default_solid_form = /obj/item/stack/material/segment exoplanet_rarity_plant = MAT_RARITY_EXOTIC exoplanet_rarity_gas = MAT_RARITY_NOWHERE + wall_damage_threshold = 20 /decl/material/solid/exotic_matter name = "exotic matter" @@ -65,3 +66,4 @@ default_solid_form = /obj/item/stack/material/segment exoplanet_rarity_plant = MAT_RARITY_EXOTIC exoplanet_rarity_gas = MAT_RARITY_NOWHERE + wall_damage_threshold = 10 diff --git a/code/modules/materials/definitions/solids/materials_solid_fission.dm b/code/modules/materials/definitions/solids/materials_solid_fission.dm index f68c8c856730..066c788dbc0f 100644 --- a/code/modules/materials/definitions/solids/materials_solid_fission.dm +++ b/code/modules/materials/definitions/solids/materials_solid_fission.dm @@ -27,7 +27,8 @@ fission_heat = 35000 fission_energy = 4000 neutron_absorption = 4 - + wall_damage_threshold = 20 + /decl/material/solid/metal/neptunium // Np-237. name = "neptunium" diff --git a/code/modules/materials/definitions/solids/materials_solid_gemstones.dm b/code/modules/materials/definitions/solids/materials_solid_gemstones.dm index f0718ad4ccfe..46a87745eea5 100644 --- a/code/modules/materials/definitions/solids/materials_solid_gemstones.dm +++ b/code/modules/materials/definitions/solids/materials_solid_gemstones.dm @@ -13,6 +13,7 @@ abstract_type = /decl/material/solid/gemstone sound_manipulate = 'sound/foley/pebblespickup1.ogg' sound_dropped = 'sound/foley/pebblesdrop1.ogg' + wall_damage_threshold = 10 /decl/material/solid/gemstone/diamond name = "diamond" diff --git a/code/modules/materials/definitions/solids/materials_solid_ice.dm b/code/modules/materials/definitions/solids/materials_solid_ice.dm index 34144fe83933..a9ca928102d9 100644 --- a/code/modules/materials/definitions/solids/materials_solid_ice.dm +++ b/code/modules/materials/definitions/solids/materials_solid_ice.dm @@ -20,6 +20,7 @@ heating_products = list( /decl/material/liquid/water = 1 ) + wall_damage_threshold = 5 /decl/material/solid/ice/Initialize() liquid_name ||= "liquid [name]" // avoiding the 'molten ice' issue diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index 5aa1b8acd502..ff248eb40d5f 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -20,6 +20,7 @@ icon_reinf = 'icons/turf/walls/reinforced_metal.dmi' exoplanet_rarity_gas = MAT_RARITY_NOWHERE tensile_strength = 0.8 // metal wire is probably better than plastic? + wall_damage_threshold = 10 /decl/material/solid/metal/uranium name = "uranium" diff --git a/code/modules/materials/definitions/solids/materials_solid_mineral.dm b/code/modules/materials/definitions/solids/materials_solid_mineral.dm index 4317abc5b12d..61b307481087 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mineral.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mineral.dm @@ -19,6 +19,7 @@ ) ore_type_value = ORE_NUCLEAR ore_data_value = 3 + wall_damage_threshold = 5 /decl/material/solid/graphite name = "graphite" diff --git a/code/modules/materials/definitions/solids/materials_solid_mundane.dm b/code/modules/materials/definitions/solids/materials_solid_mundane.dm index 8579088f2f26..bf4f1f31a150 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mundane.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mundane.dm @@ -24,3 +24,4 @@ /decl/material/gas/sulfur_dioxide = 0.05, /decl/material/gas/carbon_dioxide = 0.05 ) + wall_damage_threshold = 10 diff --git a/code/modules/materials/definitions/solids/materials_solid_organic.dm b/code/modules/materials/definitions/solids/materials_solid_organic.dm index c5b9a8960c95..139e9746f923 100644 --- a/code/modules/materials/definitions/solids/materials_solid_organic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_organic.dm @@ -12,6 +12,7 @@ bakes_into_at_temperature = T0C+500 bakes_into_material = /decl/material/solid/carbon */ + wall_damage_threshold = 5 /decl/material/solid/organic/plastic name = "plastic" diff --git a/code/modules/materials/definitions/solids/materials_solid_stone.dm b/code/modules/materials/definitions/solids/materials_solid_stone.dm index 6334b03f1b8e..390728357136 100644 --- a/code/modules/materials/definitions/solids/materials_solid_stone.dm +++ b/code/modules/materials/definitions/solids/materials_solid_stone.dm @@ -23,6 +23,7 @@ ore_result_amount = 4 sound_manipulate = 'sound/foley/rockscrape.ogg' sound_dropped = 'sound/foley/rockscrape.ogg' + wall_damage_threshold = 10 /decl/material/solid/stone/sandstone name = "sandstone" diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index ac5d06a17fec..0695085e3c6e 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -48,6 +48,7 @@ compost_value = 0.2 temperature_burn_milestone_material = /decl/material/solid/organic/wood paint_verb = "stained" + wall_damage_threshold = 8 // Wood is hard but can't really give it an edge. /decl/material/solid/organic/wood/can_hold_edge() From 5d11fd4bc22a633bc51df6e6b6e4756b1744fa64 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Tue, 19 May 2026 13:37:07 +1000 Subject: [PATCH 1250/1331] Consistency pass on wall hitsound. --- code/game/turfs/walls/_wall.dm | 5 ++--- code/game/turfs/walls/wall_attacks.dm | 8 ++++---- code/game/turfs/walls/wall_icon.dm | 1 - .../materials/definitions/solids/materials_solid_metal.dm | 1 + 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 084fb135e175..4fcb5558c716 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -41,7 +41,6 @@ var/global/list/wall_fullblend_objects = list( var/decl/material/reinf_material var/decl/material/girder_material = /decl/material/solid/metal/steel var/construction_stage - var/hitsound = 'sound/weapons/Genhit.ogg' /// A list of connections to walls for each corner, used for icon generation. Can be converted to a list of dirs with corner_states_to_dirs(). var/list/wall_connections /// A list of connections to non-walls for each corner, used for icon generation. Can be converted to a list of dirs with corner_states_to_dirs(). @@ -152,7 +151,7 @@ var/global/list/wall_fullblend_objects = list( . = ..() if(. && density && !ismob(AM)) var/tforce = AM.get_thrown_attack_force() * (TT.speed/THROWFORCE_SPEED_DIVISOR) - playsound(src, hitsound, tforce >= 15 ? 60 : 25, TRUE) + playsound(src, get_hit_sound(), tforce >= 15 ? 60 : 25, TRUE) if(tforce > 0) take_damage(tforce) @@ -323,7 +322,7 @@ var/global/list/wall_fullblend_objects = list( handle_melting() /turf/wall/proc/get_hit_sound() - return 'sound/effects/metalhit.ogg' + return material?.hitsound || 'sound/weapons/Genhit.ogg' // Mapped premade for false walls /turf/wall/false diff --git a/code/game/turfs/walls/wall_attacks.dm b/code/game/turfs/walls/wall_attacks.dm index 16d98d068218..d0e1b0beda6b 100644 --- a/code/game/turfs/walls/wall_attacks.dm +++ b/code/game/turfs/walls/wall_attacks.dm @@ -95,7 +95,7 @@ if (isnull(construction_stage) || !reinf_material) to_chat(user, "You push \the [src], but nothing happens.") - playsound(src, hitsound, 25, 1) + playsound(src, get_hit_sound(), 25, 1) return TRUE /turf/wall/attack_hand(var/mob/user) @@ -311,19 +311,19 @@ var/effective_force = round(force / material_divisor) if(effective_force < damage_threshold) visible_message(SPAN_DANGER("\The [user] has [pick(W.attack_verb)] \the [src] with \the [W], but it has no effect!")) - playsound(src, hitsound, 25, 1) + playsound(src, get_hit_sound(), 25, 1) return TRUE // Check for a glancing blow. var/dam_prob = max(0, 100 - material.hardness + effective_force + W.armor_penetration) if(!prob(dam_prob)) visible_message(SPAN_DANGER("\The [user] has [pick(W.attack_verb)] \the [src] with \the [W], but it bounced off!")) - playsound(src, hitsound, 25, 1) + playsound(src, get_hit_sound(), 25, 1) if(user.skill_fail_prob(SKILL_HAULING, 40, SKILL_ADEPT)) SET_STATUS_MAX(user, STAT_WEAK, 2) visible_message(SPAN_DANGER("\The [user] is knocked back by the force of the blow!")) return TRUE visible_message(SPAN_DANGER("\The [user] has [pick(W.attack_verb)] \the [src] with \the [W]!")) - playsound(src, hitsound, 50, 1) + playsound(src, get_hit_sound(), 50, 1) take_damage(effective_force) return TRUE \ No newline at end of file diff --git a/code/game/turfs/walls/wall_icon.dm b/code/game/turfs/walls/wall_icon.dm index c682d4f266fe..365e76169d39 100644 --- a/code/game/turfs/walls/wall_icon.dm +++ b/code/game/turfs/walls/wall_icon.dm @@ -11,7 +11,6 @@ material = get_default_material() if(material) explosion_resistance = material.explosion_resistance - hitsound = material.hitsound if(reinf_material && reinf_material.explosion_resistance > explosion_resistance) explosion_resistance = reinf_material.explosion_resistance update_strings() diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index ff248eb40d5f..dadcc8fa3954 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -21,6 +21,7 @@ exoplanet_rarity_gas = MAT_RARITY_NOWHERE tensile_strength = 0.8 // metal wire is probably better than plastic? wall_damage_threshold = 10 + hitsound = 'sound/effects/metalhit.ogg' /decl/material/solid/metal/uranium name = "uranium" From 3f9162643992f28878460732035c3e45e4761cfd Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 22 Sep 2025 10:51:08 +1000 Subject: [PATCH 1251/1331] Rewriting beehives and beehavior. --- maps/ministation/ministation-1.dmm | 2 +- mods/content/beekeeping/_beekeeping.dm | 3 +- mods/content/beekeeping/_beekeeping.dme | 11 +- mods/content/beekeeping/closets.dm | 4 +- .../beekeeping/hives/hive_extension.dm | 195 +++++++++++ mods/content/beekeeping/hives/hive_flora.dm | 40 +++ .../beekeeping/{ => hives}/hive_frame.dm | 47 ++- mods/content/beekeeping/hives/hive_queen.dm | 23 ++ .../beekeeping/hives/hive_structure.dm | 83 +++++ mods/content/beekeeping/hives/hive_swarm.dm | 322 ++++++++++++++++++ .../hives/insect_species/_insects.dm | 174 ++++++++++ .../insect_species/insects_pollinators.dm | 26 ++ mods/content/beekeeping/icons/apiary.dmi | Bin 0 -> 345 bytes .../beekeeping/icons/apiary_bees_etc.dmi | Bin 3301 -> 0 bytes mods/content/beekeeping/icons/bee_pack.dmi | Bin 0 -> 366 bytes mods/content/beekeeping/icons/beehive.dmi | Bin 0 -> 319 bytes mods/content/beekeeping/icons/beekeeping.dmi | Bin 5053 -> 0 bytes mods/content/beekeeping/icons/comb.dmi | Bin 0 -> 1077 bytes mods/content/beekeeping/icons/smoker.dmi | Bin 1019 -> 480 bytes mods/content/beekeeping/icons/swarm.dmi | Bin 0 -> 1016 bytes mods/content/beekeeping/items.dm | 40 --- mods/content/beekeeping/materials.dm | 20 ++ mods/content/beekeeping/recipes.dm | 7 +- mods/content/beekeeping/trading.dm | 10 +- 24 files changed, 940 insertions(+), 67 deletions(-) create mode 100644 mods/content/beekeeping/hives/hive_extension.dm create mode 100644 mods/content/beekeeping/hives/hive_flora.dm rename mods/content/beekeeping/{ => hives}/hive_frame.dm (52%) create mode 100644 mods/content/beekeeping/hives/hive_queen.dm create mode 100644 mods/content/beekeeping/hives/hive_structure.dm create mode 100644 mods/content/beekeeping/hives/hive_swarm.dm create mode 100644 mods/content/beekeeping/hives/insect_species/_insects.dm create mode 100644 mods/content/beekeeping/hives/insect_species/insects_pollinators.dm create mode 100644 mods/content/beekeeping/icons/apiary.dmi delete mode 100644 mods/content/beekeeping/icons/apiary_bees_etc.dmi create mode 100644 mods/content/beekeeping/icons/bee_pack.dmi create mode 100644 mods/content/beekeeping/icons/beehive.dmi delete mode 100644 mods/content/beekeeping/icons/beekeeping.dmi create mode 100644 mods/content/beekeeping/icons/comb.dmi create mode 100644 mods/content/beekeeping/icons/swarm.dmi create mode 100644 mods/content/beekeeping/materials.dm diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index fc3ebff786d9..54715c60e4be 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -8744,7 +8744,7 @@ /turf/floor/tiled, /area/ministation/hall/e2) "Oo" = ( -/obj/machinery/beehive, +/obj/structure/apiary/mapped, /turf/floor/fake_grass, /area/ministation/hydro) "Op" = ( diff --git a/mods/content/beekeeping/_beekeeping.dm b/mods/content/beekeeping/_beekeeping.dm index d3b58be746d6..471217ac52d6 100644 --- a/mods/content/beekeeping/_beekeeping.dm +++ b/mods/content/beekeeping/_beekeeping.dm @@ -1,7 +1,6 @@ /decl/modpack/beekeeping - name = "Beekeeping Content" + name = "Beekeeping and Insects Content" /datum/storage/hopper/industrial/centrifuge/New() ..() can_hold |= /obj/item/hive_frame - diff --git a/mods/content/beekeeping/_beekeeping.dme b/mods/content/beekeeping/_beekeeping.dme index 75778704f368..92ee753943b5 100644 --- a/mods/content/beekeeping/_beekeeping.dme +++ b/mods/content/beekeeping/_beekeeping.dme @@ -3,10 +3,17 @@ // BEGIN_INCLUDE #include "_beekeeping.dm" #include "closets.dm" -#include "hive_frame.dm" #include "items.dm" +#include "materials.dm" #include "recipes.dm" #include "trading.dm" -#include "hives\_hive.dm" +#include "hives\hive_extension.dm" +#include "hives\hive_flora.dm" +#include "hives\hive_frame.dm" +#include "hives\hive_queen.dm" +#include "hives\hive_structure.dm" +#include "hives\hive_swarm.dm" +#include "hives\insect_species\_insects.dm" +#include "hives\insect_species\insects_pollinators.dm" // END_INCLUDE #endif diff --git a/mods/content/beekeeping/closets.dm b/mods/content/beekeeping/closets.dm index c39e70dbb457..9caf9bb459c8 100644 --- a/mods/content/beekeeping/closets.dm +++ b/mods/content/beekeeping/closets.dm @@ -1,10 +1,10 @@ /obj/structure/closet/crate/hydroponics/beekeeping name = "beekeeping crate" - desc = "All you need to set up your own beehive." + desc = "All you need to set up your own beehive, except the beehive." /obj/structure/closet/crate/hydroponics/beekeeping/Initialize() . = ..() - new /obj/item/beehive_assembly(src) + new /obj/item/stack/material/plank/mapped/wood/ten new /obj/item/bee_smoker(src) new /obj/item/hive_frame/crafted(src) new /obj/item/hive_frame/crafted(src) diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm new file mode 100644 index 000000000000..d02839f4d40b --- /dev/null +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -0,0 +1,195 @@ +/datum/extension/insect_hive + base_type = /datum/extension/insect_hive + expected_type = /obj/structure + flags = EXTENSION_FLAG_IMMEDIATE + /// The species of insect that made this hive. + var/decl/insect_species/holding_species + /// References to our current swarm effects gathering for the hive. + var/list/swarms + var/current_health = 100 + var/material = 10 + var/raw_reserves = 0 + /// Tracker for the last world.time that a frame was removed. + var/frame_last_removed = 0 + /// Tracker for ticks remaning since we were last smoked. + var/smoked_out = 0 + +/datum/extension/insect_hive/New(datum/holder, _species_decl) + ..() + holding_species = istype(_species_decl, /decl/insect_species) ? _species_decl : GET_DECL(_species_decl) + if(!istype(holding_species)) + CRASH("Insect hive extension instantiated with invalid insect species: '[_species_decl]'.") + START_PROCESSING(SSprocessing, src) + +/datum/extension/insect_hive/Destroy() + STOP_PROCESSING(SSprocessing, src) + if(length(swarms)) + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + swarm.owner = null + swarms = null + var/atom/movable/hive = holder + if(istype(hive) && !QDELETED(hive)) + hive.queue_icon_update() + return ..() + +/datum/extension/insect_hive/Process() + if(smoked_out > 0) + smoked_out-- + return + holding_species.process_hive(src) + create_hive_products() + +/datum/extension/insect_hive/proc/handle_item_interaction(mob/user, obj/item/item) + return FALSE + +/datum/extension/insect_hive/proc/drop_nest(atom/drop_loc) + return + +/datum/extension/insect_hive/proc/get_nest_condition() + switch(current_health) + if(0, 10) + return "dying" + if(10, 30) + return "struggling" + if(30, 60) + return "sickly" + if(60, 90) + return null + return "thriving" + +/datum/extension/insect_hive/proc/get_nest_name() + return holding_species?.nest_name + +/datum/extension/insect_hive/proc/examined(mob/user, show_detail) + var/nest_descriptor = get_nest_condition() + if(nest_descriptor) + to_chat(user, SPAN_NOTICE("It contains \a [nest_descriptor] [get_nest_name()].")) + else + to_chat(user, SPAN_NOTICE("It contains \a [get_nest_name()].")) + +/datum/extension/insect_hive/proc/frame_removed(obj/item/frame) + frame_last_removed = world.time + if(!smoked_out) + for(var/obj/effect/insect_swarm/swarm in swarms) + swarm.swarm_agitation = min(100, swarm.swarm_agitation + 5) + +/datum/extension/insect_hive/proc/try_hand_harvest(mob/user) + return FALSE + +/datum/extension/insect_hive/proc/try_tool_harvest(mob/user, obj/item/tool) + return FALSE + +/datum/extension/insect_hive/proc/swarm_destroyed(obj/effect/insect_swarm/swarm) + return + +/datum/extension/insect_hive/proc/swarm_at_hive() + for(var/atom/movable/swarm as anything in swarms) + if(get_turf(swarm) == get_turf(holder)) + return swarm + +/datum/extension/insect_hive/proc/has_material(amt) + return amt <= material + +/datum/extension/insect_hive/proc/consume_material(amt) + if(has_material(amt)) + material = clamp(material-amt, 0, 100) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/add_material(amt) + material = clamp(material+amt, 0, 100) + return TRUE + +/datum/extension/insect_hive/proc/add_reserves(amt) + raw_reserves = clamp(raw_reserves+amt, 0, 100) + return TRUE + +/datum/extension/insect_hive/proc/has_reserves(amt, raw_reserves_only = TRUE) + if(raw_reserves >= amt) + return TRUE + if(raw_reserves_only) + return FALSE + var/reserve = 0 + for(var/obj/item/frame in holder) + reserve += frame.reagents?.total_volume + if(reserve >= amt) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/consume_reserves(amt, raw_reserves_only = TRUE) + if(!has_reserves(amt, raw_reserves_only)) + return FALSE + if(raw_reserves >= amt) + raw_reserves -= amt + return TRUE + if(raw_reserves_only) + return FALSE + amt -= raw_reserves + raw_reserves = 0 + for(var/obj/item/frame in holder) + if(!frame.reagents?.total_volume) + continue + var/consume = min(amt, frame.reagents.total_volume) + frame.reagents.remove_any(consume) + amt -= consume + if(amt <= 0) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/adjust_health(amt) + current_health = clamp(current_health + amt, 0, 100) + if(current_health <= 0) + var/atom/movable/hive = holder + hive.visible_message(SPAN_DANGER("\The [holding_species.nest_name] sags and collapses.")) + remove_extension(holder, base_type) + +/datum/extension/insect_hive/proc/create_hive_products() + + var/atom/movable/hive = holder + if(!istype(hive) || !holding_species) + return TRUE + + if(!swarm_at_hive()) // nobody home to do the work + return TRUE + + // Naturally build up enough material for a new frame (or repairs). + if(!has_material(20)) + add_material(1) + + // Damaged hives cannot produce combs or honey. + if(current_health < 100) + if(consume_material(5)) + adjust_health(rand(3,5)) + return TRUE + + if(!has_reserves(20)) + return TRUE + + var/list/holder_contents = hive.get_contained_external_atoms() + for(var/obj/item/hive_frame/frame in holder_contents) + if(!frame.reagents || (frame.reagents.total_volume >= frame.reagents.maximum_volume)) + continue + var/fill_cost = REAGENTS_FREE_SPACE(frame.reagents) + if(consume_material(5) && consume_reserves(fill_cost)) + holding_species.fill_hive_frame(frame) + return TRUE + + var/obj/item/native_frame = holding_species.native_frame_type + var/native_frame_size = initial(native_frame.w_class) + var/space_left = hive.storage.max_storage_space + for(var/obj/item/thing in hive.get_stored_inventory()) + space_left -= thing.w_class + if(space_left < native_frame_size) + return + + // Put a timer check on this to avoid a hive filling up with combs the moment you take 2 frames out. + if(world.time > (frame_last_removed + 2 MINUTES) && space_left >= native_frame_size && consume_material(20)) + // Frames start empty, and will be filled next run. + // Native 'frames' (combs) are bigger than crafted ones and aren't reusable. + new native_frame(holder, holding_species.produce_material) + hive.storage.update_ui_after_item_insertion() + +/datum/extension/insect_hive/proc/get_total_swarm_intensity() + . = 0 + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + . += swarm.swarm_intensity diff --git a/mods/content/beekeeping/hives/hive_flora.dm b/mods/content/beekeeping/hives/hive_flora.dm new file mode 100644 index 000000000000..e6091c3610bd --- /dev/null +++ b/mods/content/beekeeping/hives/hive_flora.dm @@ -0,0 +1,40 @@ +/obj/structure/flora + /// Percentage chance of trying to spawn an insect hive here, if appropriate. + var/insect_hive_chance = 20 + +/obj/structure/flora/Initialize(ml, _mat, _reinf_mat) + . = ..() + if(insect_hive_chance && length(get_supported_insects())) + return INITIALIZE_HINT_LATELOAD + +/obj/structure/flora/LateInitialize() + ..() + if(prob(insect_hive_chance) && !has_extension(src, /datum/extension/insect_hive)) + var/list/insects = get_supported_insects() + if(length(insects)) + insects = insects.Copy() // don't mutate the static list. + for(var/species_type in insects) + var/decl/insect_species/species = GET_DECL(species_type) + if(!species.can_spawn_in_flora(src)) + insects -= species_type + if(length(insects)) + set_extension(src, /datum/extension/insect_hive, pickweight(insects)) + update_icon() + +// Insect species that can hive in this flora. +/obj/structure/flora/proc/get_supported_insects() + return + +/obj/structure/flora/tree/get_supported_insects() + var/static/list/_insects = list( + /decl/insect_species/honeybees = 10, + /decl/insect_species/wasps = 1 + ) + return _insects + +/obj/structure/flora/stump/get_supported_insects() + var/static/list/_insects = list( + /decl/insect_species/honeybees = 10, + /decl/insect_species/wasps = 1 + ) + return _insects diff --git a/mods/content/beekeeping/hive_frame.dm b/mods/content/beekeeping/hives/hive_frame.dm similarity index 52% rename from mods/content/beekeeping/hive_frame.dm rename to mods/content/beekeeping/hives/hive_frame.dm index 35ffd429777c..be57c7adf7af 100644 --- a/mods/content/beekeeping/hive_frame.dm +++ b/mods/content/beekeeping/hives/hive_frame.dm @@ -1,7 +1,7 @@ /obj/item/hive_frame - abstract_type = /obj/item/hive_frame - icon_state = ICON_STATE_WORLD - w_class = ITEM_SIZE_SMALL + abstract_type = /obj/item/hive_frame + icon_state = ICON_STATE_WORLD + w_class = ITEM_SIZE_SMALL material_alteration = MAT_FLAG_ALTERATION_ALL chem_volume = 20 var/destroy_on_centrifuge = FALSE @@ -36,20 +36,45 @@ for(var/atom/movable/thing in convert_matter_to_lumps()) thing.dropInto(centrifuge.loc) +/obj/item/hive_frame/honey/populate_reagents() + . = ..() + var/decl/insect_species/bees = GET_DECL(/decl/insect_species/honeybees) + bees.fill_hive_frame(src) + +/obj/item/hive_frame/Move() + var/datum/extension/insect_hive/hive = get_extension(loc, /datum/extension/insect_hive) + . = ..() + if(. && istype(hive) && loc != hive.holder) + hive.frame_removed(src) + // Crafted frame used in apiaries. /obj/item/hive_frame/crafted name = "hive frame" desc = "A wooden frame for insect hives that the workers will fill with products like honey." icon = 'mods/content/beekeeping/icons/frame.dmi' material = /decl/material/solid/organic/wood/oak - material_alteration = MAT_FLAG_ALTERATION_ALL -// TEMP until beewrite redoes hives. -/obj/item/hive_frame/crafted/filled/Initialize() - . = ..() - new /obj/item/stack/material/bar/wax(src) - update_icon() +// Raw version of honeycomb for wild hives. +/obj/item/hive_frame/comb + name = "comb" + icon = 'mods/content/beekeeping/icons/comb.dmi' + material = /decl/material/solid/organic/wax + destroy_on_centrifuge = TRUE + material_alteration = MAT_FLAG_ALTERATION_COLOR + is_spawnable_type = FALSE + w_class = ITEM_SIZE_NORMAL // Larger than crafted frames, because you should use crafted frames in your hive. -/obj/item/hive_frame/crafted/filled/populate_reagents() +/obj/item/hive_frame/comb/Initialize(ml, material_key, decl/insect_species/spawning_hive) . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/honey, REAGENT_MAXIMUM_VOLUME(reagents)) + if(istype(spawning_hive)) + SetName(spawning_hive.native_frame_name) + desc = spawning_hive.native_frame_desc + spawning_hive.fill_hive_frame(src) + +// Comb subtype for mapping and debugging. +/obj/item/hive_frame/comb/honey + is_spawnable_type = TRUE + color = COLOR_GOLD + +/obj/item/hive_frame/comb/honey/Initialize(ml, material_key) + return ..(ml, material_key, GET_DECL(/decl/insect_species/honeybees)) diff --git a/mods/content/beekeeping/hives/hive_queen.dm b/mods/content/beekeeping/hives/hive_queen.dm new file mode 100644 index 000000000000..599b487d243e --- /dev/null +++ b/mods/content/beekeeping/hives/hive_queen.dm @@ -0,0 +1,23 @@ +/obj/item/bee_pack + name = "bee pack" + desc = "Contains a queen bee and some worker bees. Everything you'll need to start a hive!" + icon = 'mods/content/beekeeping/icons/bee_pack.dmi' + material = /decl/material/solid/organic/plastic + var/contains_insects = /decl/insect_species/honeybees + +/obj/item/bee_pack/Initialize() + . = ..() + update_icon() + +/obj/item/bee_pack/on_update_icon() + . = ..() + if(contains_insects) + add_overlay("[icon_state]-full") + else + add_overlay("[icon_state]-empty") + +/obj/item/bee_pack/proc/empty() + SetName("empty [initial(name)]") + desc = "A stasis pack for moving bees. It's empty." + contains_insects = null + update_icon() diff --git a/mods/content/beekeeping/hives/hive_structure.dm b/mods/content/beekeeping/hives/hive_structure.dm new file mode 100644 index 000000000000..f7586ad6eec4 --- /dev/null +++ b/mods/content/beekeeping/hives/hive_structure.dm @@ -0,0 +1,83 @@ +/obj/structure/attackby(obj/item/used_item, mob/user) + if((. = ..())) + return + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive) && hive.handle_item_interaction(user, used_item)) + return TRUE + +/obj/structure/attack_hand(mob/user) + if(has_extension(src, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(hive.try_hand_harvest(user)) + return TRUE + return ..() + +/obj/structure/attackby(obj/item/used_item, mob/user) + if(has_extension(src, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(hive.try_tool_harvest(user, used_item)) + return TRUE + return ..() + +/obj/structure/examined_by(mob/user, distance, infix, suffix) + . = ..() + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + hive.examined(user, (distance <= 1)) + +/obj/structure/dismantle_structure(mob/user) + if(isatom(loc)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + hive.drop_nest(loc) + return ..() + +// 'proper' nest structure for building and mapping +/obj/structure/apiary + name = "apiary" + desc = "An artificial hive for raising insects, like bees, and harvesting products like honey." + icon = 'mods/content/beekeeping/icons/apiary.dmi' + icon_state = ICON_STATE_WORLD + density = TRUE + anchored = TRUE + storage = /datum/storage/apiary + material_alteration = MAT_FLAG_ALTERATION_ALL + material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color + obj_flags = OBJ_FLAG_ANCHORABLE + tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) + +/obj/structure/apiary/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + return air_group || height == 0 || !density || (istype(mover) && mover.checkpass(PASS_FLAG_TABLE)) + +/obj/structure/apiary/attackby(obj/item/used_item, mob/user) + + if(istype(used_item, /obj/item/bee_pack)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + to_chat(user, SPAN_WARNING("\The [src] already contains \a [hive.holding_species.nest_name].")) + return TRUE + var/obj/item/bee_pack/pack = used_item + if(!pack.contains_insects) + to_chat(user, SPAN_WARNING("\The [pack] is empty!")) + return TRUE + user.visible_message(SPAN_NOTICE("\The [user] transfers the contents of \the [pack] into \the [src].")) + set_extension(src, /datum/extension/insect_hive, pack.contains_insects) + pack.empty() + return TRUE + + . = ..() + +/datum/storage/apiary + can_hold = list(/obj/item/hive_frame) + max_w_class = ITEM_SIZE_NORMAL + max_storage_space = ITEM_SIZE_SMALL * 5 // Five regular frames. + +/obj/structure/apiary/mapped/Initialize(ml, _mat, _reinf_mat) + . = ..() + for(var/_ = 1 to 5) + new /obj/item/hive_frame/crafted(src) + +/obj/structure/apiary/mapped/bees/Initialize(ml, _mat, _reinf_mat) + set_extension(src, /datum/extension/insect_hive, /decl/insect_species/honeybees) + . = ..() diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm new file mode 100644 index 000000000000..2e733a96b138 --- /dev/null +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -0,0 +1,322 @@ +/obj/effect/insect_swarm + anchored = TRUE + is_spawnable_type = FALSE + icon_state = "0" + gender = NEUTER + default_pixel_z = 8 + layer = ABOVE_HUMAN_LAYER + pass_flags = PASS_FLAG_TABLE + movement_handlers = list(/datum/movement_handler/delay/insect_swarm) + + /// Current movement target for automove (ie. hive, flowers or victim) + VAR_PRIVATE/atom/move_target + /// Reference to our owning hive. + var/datum/extension/insect_hive/owner + /// Reference to our insect archetype. + var/decl/insect_species/insect_type + /// A counter for disturbances to the hive or this swarm, causes them to sting people. + var/swarm_agitation = 0 + /// Percentage value; if it drops to 0, the swarm will be destroyed. + var/swarm_intensity = 1 + /// if more states are added to swarm.dmi, increase this + var/const/MAX_SWARM_STATE = 6 + /// Cooldown timer for next tick. + VAR_PRIVATE/next_work = 0 + +/datum/movement_handler/delay/insect_swarm + delay = 1 SECOND + +/datum/movement_handler/delay/insect_swarm/DoMove(direction, mob/mover, is_external) + ..() + step(host, direction) + return MOVEMENT_HANDLED + +/obj/effect/insect_swarm/debug/Initialize(mapload) + . = ..(mapload, _insect_type = /decl/insect_species/honeybees) + +/obj/effect/insect_swarm/Initialize(mapload, _insect_type, _hive) + . = ..() + insect_type = istype(_insect_type, /decl/insect_species) ? _insect_type : GET_DECL(_insect_type) + owner = _hive + if(!istype(insect_type)) + PRINT_STACK_TRACE("Insect swarm created with invalid insect type: '[_insect_type]'") + return INITIALIZE_HINT_QDEL + if(!istype(owner)) + PRINT_STACK_TRACE("Insect swarm created with invalid hive: '[owner]'") + return INITIALIZE_HINT_QDEL + color = insect_type.swarm_color + icon = insect_type.swarm_icon + update_swarm() + LAZYDISTINCTADD(owner.swarms, src) + START_PROCESSING(SSobj, src) + +/obj/effect/insect_swarm/Destroy() + if(owner) + owner.swarm_destroyed(src) + LAZYREMOVE(owner.swarms, src) + owner = null + stop_automove() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/insect_swarm/proc/update_swarm() + icon_state = num2text(ceil((swarm_intensity / insect_type.max_swarm_intensity) * MAX_SWARM_STATE)) + if(icon_state == "1") + SetName(insect_type.name_singular) + desc = insect_type.insect_desc + gender = NEUTER + else + SetName(insect_type.name_plural) + desc = insect_type.swarm_desc + gender = PLURAL + + // Some icon variation via transform. + if(prob(75)) + var/matrix/swarm_transform = matrix() + swarm_transform.Turn(pick(90, 180, 270)) + +/obj/effect/insect_swarm/proc/is_agitated() + return QDELETED(owner) || swarm_agitation > 0 + +/obj/effect/insect_swarm/proc/find_sting_target() + for(var/mob/living/victim in view(7, src)) + if(!victim.simulated || victim.stat || victim.current_posture?.prone) + continue + if(victim.isSynthetic()) + continue + return victim + +/obj/effect/insect_swarm/proc/merge(obj/effect/insect_swarm/other_swarm) + + // If we can fit into one swarm, just merge us together. + var/total_intensity = swarm_intensity + other_swarm.swarm_intensity + if(total_intensity <= insect_type.max_swarm_intensity) + swarm_intensity = total_intensity + swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + update_swarm() + qdel(other_swarm) + return + + // Otherwise equalize between swarms. + swarm_intensity = floor(total_intensity / 2) + other_swarm.swarm_intensity = total_intensity - swarm_intensity + swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + other_swarm.swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + update_swarm() + other_swarm.update_swarm() + +/obj/effect/insect_swarm/Move() + . = ..() + // Swarms from the same hive in the same loc merge together. + if(. && loc && !QDELETED(src)) + try_consolidate_swarms() + +/obj/effect/insect_swarm/Process() + + // Swarms on a loc should try to merge if possible. + try_consolidate_swarms() + if(QDELETED(src)) + return + + // Swarms with no hive gradually decay to nothing. + if(!owner) + adjust_swarm_intensity(-(rand(1,3))) + if(QDELETED(src)) + return + + if(!move_target || !(move_target in view(5, src))) + stop_automove() + + // Angry swarms move with purpose. + if(is_agitated()) + swarm_agitation = max(0, swarm_agitation-1) + if(!move_target) + move_target = find_sting_target() + if(move_target) + start_automove(move_target) + return + + // Large swarms split if they aren't agitated. + if(swarm_can_split() && isturf(loc)) + var/turf/our_turf = loc + for(var/turf/swarm_turf as anything in RANGE_TURFS(our_turf, 1)) + if(swarm_turf == loc || !swarm_turf.CanPass(src)) + continue + var/new_intensity = round(swarm_intensity/2) + var/obj/effect/insect_swarm/new_swarm = new type(swarm_turf, insect_type, owner) + new_swarm.swarm_intensity = new_intensity + new_swarm.swarm_agitation = swarm_agitation + new_swarm.update_swarm() + swarm_intensity -= new_intensity + update_swarm() + break + + // Sting people, if we are so inclined. + if(insect_type.sting_amount || insect_type.sting_reagent) + insect_type.try_sting(src, loc) + + // Hive behavior is dictated by the hive. + if(owner) + handle_hive_behavior() + return + + // If we're not agitated and don't have a hive, we probably shouldn't be pathing somewhere. + stop_automove() + + // Idle swarms with no hive just wander around. + if(prob(5)) + SelfMove(pick(global.alldirs)) + +/obj/effect/insect_swarm/proc/is_first_swarm_at_hive() + var/atom/movable/hive = owner?.holder + if(!isturf(hive?.loc) || loc != hive.loc) + return FALSE + if(length(owner?.swarms) == 1) + return TRUE + for(var/obj/effect/insect_swarm/swarm in hive.loc) + if(swarm == src) + return TRUE + if(swarm in owner.swarms) + break + return FALSE + +/obj/effect/insect_swarm/get_automove_target(datum/automove_metadata/metadata) + return move_target + +/obj/effect/insect_swarm/stop_automove() + SHOULD_CALL_PARENT(FALSE) + move_target = null + //. = ..() // TODO work out why they're not automoving + walk(src, 0) + +/obj/effect/insect_swarm/start_automove(target, movement_type, datum/automove_metadata/metadata) + SHOULD_CALL_PARENT(FALSE) + move_target = target + //. = ..() // TODO work out why they're not automoving + if(move_target) + walk_to(src, move_target, 0, 7) + else + walk(src, 0) + +/obj/effect/insect_swarm/proc/handle_hive_behavior() + + var/atom/movable/hive = owner?.holder + if(!isturf(loc)) + // We've just been created; shunt us out onto the turf. + if(loc == hive) + dropInto(hive.loc) + else + return + + // If we are the first (or only) of our owner swarms in the loc, and we aren't needed, we don't move. Hive needs workers. + if(owner?.raw_reserves >= 15) + if(is_first_swarm_at_hive()) + stop_automove() + return + if(!hive_has_swarm() && loc != hive.loc) + start_automove(owner.holder) + return + + do_work() + +/obj/effect/insect_swarm/proc/do_work() + stop_automove() + if(prob(25)) + var/step_dir = pick(global.alldirs) + if(get_dist(owner.holder, get_step(loc, step_dir)) <= 2) + SelfMove(step_dir) + +/obj/effect/insect_swarm/proc/hive_has_swarm() + var/atom/movable/hive = owner?.holder + if(!isturf(hive?.loc)) + return FALSE + for(var/obj/item/swarm as anything in owner.swarms) + if(swarm.loc == hive.loc) + return TRUE + return FALSE + +/obj/effect/insect_swarm/proc/adjust_swarm_intensity(amount) + var/old_intensity = swarm_intensity + swarm_intensity = clamp(swarm_intensity + amount, 0, insect_type.max_swarm_intensity) + if(old_intensity != swarm_intensity) + if(swarm_intensity <= 0) + qdel(src) + else + update_swarm() + +/obj/effect/insect_swarm/proc/can_grow() + // higher swarm intensity is only seen during agitated states when they converge on a victim and merge. + return swarm_intensity < insect_type.max_swarm_growth_intensity + +/obj/effect/insect_swarm/proc/can_merge() + return swarm_intensity < (is_agitated() ? insect_type.max_swarm_intensity : insect_type.max_swarm_growth_intensity) + +/obj/effect/insect_swarm/proc/swarm_can_split() + return !is_agitated() && swarm_intensity > insect_type.max_swarm_growth_intensity + +/obj/effect/insect_swarm/proc/try_consolidate_swarms() + if(!can_merge()) + return + for(var/obj/effect/insect_swarm/other_swarm in loc) + if(other_swarm == src || !other_swarm.can_merge() || other_swarm.owner != owner || other_swarm.insect_type != insect_type) + continue + merge(other_swarm) + return + +/obj/effect/insect_swarm/pollinator + var/pollen = 0 + +/obj/effect/insect_swarm/pollinator/do_work() + + // Have a rest/do some work. + if(world.time < next_work) + return + + // Unload pollen into hive. + if(pollen) + if(loc == get_turf(owner.holder)) + owner.add_reserves(pollen) + pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + else + start_automove(owner.holder) + return + + // Move to flowers. + if(move_target) + if(get_turf(move_target) == loc || !(move_target in view(src, 7))) + move_target = null + stop_automove() + else + start_automove(move_target) + return + + // Harvest from flowers in our loc. + for(var/obj/machinery/portable_atmospherics/hydroponics/flower in loc) + if(!flower.pollen) + continue + if(flower.seed && !flower.dead) + flower.plant_health += rand(3, 5) + flower.check_plant_health() + pollen += flower.pollen + flower.pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + return + + // Find a flower. + var/closest_dist + var/atom/closest_target + for(var/obj/machinery/portable_atmospherics/hydroponics/flower in view(src, 7)) + if(!flower.pollen) + continue + var/next_dist = get_dist(src, closest_target) + if(isnull(closest_dist) || next_dist < closest_dist) + closest_target = flower + closest_dist = next_dist + + if(closest_target) + start_automove(closest_target) + else + start_automove(owner.holder) diff --git a/mods/content/beekeeping/hives/insect_species/_insects.dm b/mods/content/beekeeping/hives/insect_species/_insects.dm new file mode 100644 index 000000000000..013e720ae593 --- /dev/null +++ b/mods/content/beekeeping/hives/insect_species/_insects.dm @@ -0,0 +1,174 @@ +/decl/insect_species + abstract_type = /decl/insect_species + + // Descriptive strings for individual insects and swarms. + var/name_singular + var/name_plural + var/insect_desc + + // Vars for nest description and products. + var/nest_name + var/list/produce_reagents + var/decl/material/produce_material + var/produce_material_amount = 1 + var/native_frame_name = "comb" + var/native_frame_desc = "A wax comb from an insect nest." + var/native_frame_type = /obj/item/hive_frame/comb + + // Visual appearance and behavior of swarms. + var/swarm_desc + var/swarm_color = COLOR_BROWN + var/swarm_icon = 'mods/content/beekeeping/icons/swarm.dmi' + var/swarm_type = /obj/effect/insect_swarm + var/max_swarm_growth_intensity = 50 + var/max_swarm_intensity = 100 + + // Venom delivered by swarms whens stinging a victim. + var/sting_reagent + var/sting_amount + +/decl/insect_species/Initialize() + if(produce_material) + produce_material = GET_DECL(produce_material) + return ..() + +/decl/insect_species/validate() + . = ..() + + if(!name_singular) + . += "no singular name set" + if(!name_plural) + . += "no plural name set" + if(!nest_name) + . += "no nest name set" + if(!insect_desc) + . += "no insect desc set" + + if(swarm_type) + if(!ispath(swarm_type, /obj/effect/insect_swarm)) + . += "invalid swarm path (must be /obj/effect/insect_swarm or subtype): '[swarm_type]'" + if(!swarm_desc) + . += "no swarm description set" + + if(produce_reagents) + if(!length(produce_reagents) || !islist(produce_reagents)) + . += "empty or non-list produce_reagents" + else + var/total = 0 + for(var/reagent in produce_reagents) + if(!ispath(reagent, /decl/material)) + . += "non-material produce_reagents entry '[reagent]'" + continue + var/amt = produce_reagents[reagent] + if(!isnum(amt) || amt <= 0) + . += "non-numerical or 0 produce_reagents value: '[reagent]', '[amt]'" + total += amt + if(total != 1) + . += "produce_reagents weighting does not sum to 1: '[total]'" + + if(produce_material) + if(!isnum(produce_material_amount) || produce_material_amount <= 0) + . += "non-numeric or zero produce amount: '[produce_material_amount]'" + if(!istype(produce_material, /decl/material)) + . += "non-material product material type: '[produce_material]'" + +/decl/insect_species/proc/fill_hive_frame(obj/item/frame) + + if(!istype(frame) || QDELETED(frame)) + return FALSE + + var/frame_space = REAGENTS_FREE_SPACE(frame.reagents) + if(frame_space <= 0) + return FALSE + + if(frame.reagents?.maximum_volume && length(produce_reagents)) + var/reagent_split = max(1, floor(min(REAGENTS_FREE_SPACE(frame.reagents), 20) / length(produce_reagents))) + for(var/reagent in produce_reagents) + frame.reagents.add_reagent(reagent, max(1, (reagent_split * produce_reagents[reagent])), defer_update = TRUE) + frame.reagents.handle_update() + if(produce_material && (frame.material != produce_material) && !(locate(/obj/item/stack/material/lump) in frame)) + for(var/atom/movable/thing in produce_material.create_object(frame, produce_material_amount, /obj/item/stack/material/lump)) + thing.forceMove(frame) + return TRUE + +/decl/insect_species/proc/try_sting(obj/effect/insect_swarm/swarm, atom/loc) + if(!istype(swarm) || QDELETED(swarm) || !istype(loc)) + return FALSE + // If we're agitated, always sting. Otherwise, % chance equal to a quarter of our overall swarm intensity. + if(!swarm.is_agitated() && !prob(max(1, round(swarm.swarm_intensity/4)))) + return FALSE + var/sting_mult = sting_amount * clamp(round(swarm.swarm_intensity/10), 1, 10) + for(var/mob/living/victim in loc) + if(!victim.simulated || victim.stat || victim.current_posture?.prone) + continue + var/datum/reagents/injected_reagents = victim.get_injected_reagents() + var/obj/item/organ/external/affecting = victim.get_organ(pick(global.all_limb_tags)) + if(!affecting || BP_IS_PROSTHETIC(affecting) || BP_IS_CRYSTAL(affecting)) + continue + if(injected_reagents && victim.can_inject(victim, affecting.organ_tag)) + injected_reagents.add_reagent(sting_reagent, sting_mult) + affecting.add_pain(sting_mult) + if(sting_mult <= sting_amount * 2) + to_chat(victim, SPAN_DANGER("You are stung on your [affecting.name] by \a [swarm]!")) + else + to_chat(victim, SPAN_DANGER("You are stung multiple times on your [affecting.name] by \a [swarm]!")) + . = TRUE + +/decl/insect_species/proc/can_spawn_in_flora(var/obj/structure/flora) + + // Territory range. + for(var/obj/structure/flora/plant in view(flora, 7)) + if(has_extension(plant, /datum/extension/insect_hive)) + return FALSE + + // Food source. + for(var/obj/machinery/portable_atmospherics/hydroponics/flower in view(flora, 7)) + if(flower.seed?.produces_pollen) + return TRUE + + return FALSE + +/decl/insect_species/proc/process_hive(datum/extension/insect_hive/hive_metadata) + + // Sanity check. + var/atom/movable/hive = hive_metadata.holder + if(!istype(hive) || !swarm_type || !istype(hive_metadata)) + return + + // Make sure we always have at least one swarm. + if(!length(hive_metadata.swarms)) + new swarm_type(hive, src, hive_metadata) + + // Reduce swarms if we have too many. + var/swarm_intensity = hive_metadata.get_total_swarm_intensity() + if(swarm_intensity > max_swarm_intensity && length(hive_metadata.swarms)) + var/obj/effect/insect_swarm/swarm = hive_metadata.swarms[1] + swarm.adjust_swarm_intensity(-(swarm_intensity-max_swarm_intensity)) + return + + // Try to grow an existing swarm until we're at our max. + if(hive_metadata.has_reserves(5) && length(hive_metadata.swarms)) + for(var/obj/effect/insect_swarm/swarm as anything in hive_metadata.swarms) + if(swarm.can_grow() && hive_metadata.consume_reserves(5)) + swarm.adjust_swarm_intensity(min(max_swarm_growth_intensity-swarm_intensity, rand(3,5))) + return + + // If we have sufficient filled combs, create a new swarm. Otherwise, expand a swarm. + if(hive.loc && hive_metadata.has_reserves(5)) + + var/obj/effect/insect_swarm/swarm + for(var/obj/effect/insect_swarm/check_swarm as anything in hive_metadata.swarms) + if(check_swarm.loc == hive.loc && check_swarm.can_grow()) + swarm = check_swarm + break + + if(!swarm) + var/comb_count = 0 + for(var/obj/item/hive_frame/frame in hive) + if(frame.reagents && frame.reagents.total_volume >= frame.reagents.maximum_volume) + comb_count++ + if(length(hive_metadata.swarms) < comb_count) + swarm = new swarm_type(hive.loc, src, hive_metadata) + + if(!QDELETED(swarm) && istype(swarm) && hive_metadata.consume_reserves(5)) + swarm.adjust_swarm_intensity(min((max_swarm_growth_intensity-swarm_intensity), rand(3,5))) diff --git a/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm new file mode 100644 index 000000000000..0f9b924e62fe --- /dev/null +++ b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm @@ -0,0 +1,26 @@ +/decl/insect_species/honeybees + name_singular = "honeybee" + name_plural = "honeybees" + nest_name = "beehive" + native_frame_name = "honeycomb" + native_frame_desc = "A lattice of hexagonal wax cells usually filled with honey." + native_frame_type = /obj/item/hive_frame/comb + swarm_desc = "A swarm of buzzing honeybees." + insect_desc = "A single buzzing honeybee." + swarm_color = COLOR_GOLD + swarm_type = /obj/effect/insect_swarm/pollinator + sting_reagent = /decl/material/liquid/bee_venom + sting_amount = 1 + produce_reagents = list(/decl/material/liquid/nutriment/honey = 1) + produce_material = /decl/material/solid/organic/wax + +/decl/insect_species/wasps + name_singular = "wasp" + name_plural = "wasps" + nest_name = "wasp hive" + swarm_desc = "A swarm of humming wasps." + insect_desc = "A solitary wasp." + sting_reagent = /decl/material/liquid/cyanide + sting_amount = 5 + swarm_color = COLOR_BRONZE + swarm_type = /obj/effect/insect_swarm/pollinator // tarantula hunter... diff --git a/mods/content/beekeeping/icons/apiary.dmi b/mods/content/beekeeping/icons/apiary.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7ad80a4408f3f53bf80d2991b201ac53255a1200 GIT binary patch literal 345 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 diff --git a/mods/content/beekeeping/icons/bee_pack.dmi b/mods/content/beekeeping/icons/bee_pack.dmi new file mode 100644 index 0000000000000000000000000000000000000000..fe68dad8b4b7b784528b38dea0b402b09a99fae4 GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|exd5LK*8>L*Xr`G?I$Q%3*s){B zB`@dyUyI#M6gxUPfKqQ#KSu*47)yfuf*Bm1-ADs+Dyu>wN?cNllZ!G7N;32F7#J$% z1cwzAm45#cT=4Ph6D@CDt#fD22X6>9xM=*~kW=W z2U{2ZKQ;Y0!?BF;slHbwFC2)R&3N5mGIM5w%7cT8s~euO-DuV0;O1~`mufsSDCboFyt I=akR{0DX&+ApigX literal 0 HcmV?d00001 diff --git a/mods/content/beekeeping/icons/beehive.dmi b/mods/content/beekeeping/icons/beehive.dmi new file mode 100644 index 0000000000000000000000000000000000000000..9b58ec19c1b74072539d0b269117bffdb25863f9 GIT binary patch literal 319 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 LF0}L^{MG*ef&6Bj diff --git a/mods/content/beekeeping/icons/comb.dmi b/mods/content/beekeeping/icons/comb.dmi new file mode 100644 index 0000000000000000000000000000000000000000..edb611c93ae4cfa8c7a3c51219b16f0b8858a17e GIT binary patch literal 1077 zcmV-51j_q~P)V=-0C=2r%DoDJAQ(pB+3zY|YA*U)UBU>5x`#?jp%PT;_8XcS+RlL|Mcegq zgcu}U+6U`Sa5Q;A?6xWzaMTqsqwQwmJ0VU{gCn_}-&I0PSs%br5?lRr=(oQrTih}K z(G=Y%AO=Yf@ZToKT(=aW000AcNkl^8@84&$*)NSoL?)Fc`E- zrP7;3BJqpI-tHCs~&JUa!|4jYhVnX}>lajn~O! zvKoy>yKeS&Re)g_uq+Fjropl-Twh-!6bd00i(xPrAP@*391f$`>*46=2=#jXX^9`_ zvzn%vmSwRwH#Z1{LVw0$vD?95uoege`r&Z6-|O|5!^1;!9{!q}H%XEN01!nHj^lt3 zf^<5KcDoJJG_kd{gLYPb@!?xS)H>PR6df+pJkZvZE zVRv_TyKece=Kw`f#)e@)k|d;3DG)+Xuh(H&76>5_MG=$NCn^?;&o&DvigGr?7YHHU z2R=&(>58IgJ;D!vZ{N8lK}nKe7zXzD_u)7WR8c6N5m%*;tiNr#7r zz`(%3z`!&NX{7)F00DGTPE!Ct=GbNc005JER9JLGWpiV4X>fFDZ*Bkpc$`yKaB_9` z^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGk;1ToZ^zil2jm5sXV_Z zCq;>iGbOXA7|1u|;!G<_%uR)`;i@u9a})FOGgB0j@>42xi*xcz;*(NyN)Y-?h}KsW zpOKiCLXOTHygHQ?T>V_YK>z@Og;3&NQFc85007iUL_t(&f$f+}4#F@DMC%3n7^&d^ zh=Xv0cEJ(2|9=%oS2)N@aHc}4n73-u=E?oZqf8SaJEAE;>*adK3J_IHaK1gTTgXpP zFsovM3s^zgDnJZrt6*n=galFqA0NUTDkv90u2-B;LF~0Uf*4Z%h1gRggb+f0hHT|L2S)=0bii!ZRFPJD0$iyd|1gL}4`WG^R#5KHoZm^|R-g}&gO2i&~yg}^(E z2uvOr64*R|Z4nVd2q8ygECN`;3O3NO2w(+E|C%<80OZ)b0Dm$Nm$EhwRsaA107*qo IM6N<$f`7cmv;Y7A literal 1019 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSv#LTON?cNllZ!G7N;32F7#J$% z%ssuA>yUv&>qE<(UC!3we_qaV4f1OHCg$a}h;zwLg-)^e_ZBwqSg~U+^ZVe(cfMw| z$cXH}J;m0QW6N&a^ws7^9UqiDITKl5nrglCMUrB`+9^#dEsX6_Ib__KcD2>_F?7aFV0|T>+ zr;B4q#hkZybo-PXW!gU0=Z7uYafpMZ{YH=sL!*vo#$~pHn#UHTP1HEo@G2odTTrG! z`a$*4W$LF>#AhwC*SPn5dXGfLB(IMp9|c!UwmHwcYhS4?LrFKo5y1uvCINSbM;s0^ zj2+4hg$Efc-gkdm|7ZWc#D%)|o#meF)puJw@$2u}**ZT8t^+YTOJGB>lN>74Q2TL|7@B5{d1gxT;Ljp48Hp;*A7^Uznz{im04r@ z_w3^udjHSuJk+Ihi$jb3!Cw7KCVQ)VW-Tj<=X_o%SNh@NewH2gHeP-(r?^%1qnW^; z&<~qWoNx9^xBn)(;L~x3&8PdD;@>}3PMGtKi($R`ukQ>MJm7F)QRu@AEW(0|?n_;3 zds_5!r?A%PP0R286fyD(Ymh$WdAF{n#CD3;sltDSUK>;n?t&TQ9Asjmz5J!^WMUEug} zheAxVfl24n^zQO=rM4}88<{{U39A}3!I&%d91o>f`@8E{S5({YoArWOV40VxhkWBx-co9ECzaln6fAg}Pcw5$4JIeu>n;AS^{an^LB{Ts5PVTgY diff --git a/mods/content/beekeeping/icons/swarm.dmi b/mods/content/beekeeping/icons/swarm.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1d8ea6f1b1ae8e237aaaefaf5352ad590422c65c GIT binary patch literal 1016 zcmVV=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 z$v}yVGbOXA7|1r{;!G<_%uR)`;VKNVt1ttKrsgD80+~iSaEQ=mgk75@R&D5-jmfjq zggkAg1VIpP=E)`bg$(EWxzFaRs2vf)T9ilpwumJRE7-|%>5mY;1#v)<|-QZ z?Z|);!<5D>7emjJaG);mUL>xr3`Q>=44Ts^k_1Tb{>d<1-dOSwJhG$OCBi z33*DnTR8cE zW(1R{(<5Yh3|70j(MBpjZ@r9{GxgFa0>h~}7S&HrMK2+v`cpQ@mG|o4U1&7fb_^!> zB7flXLdJQVrgMGKC#latr(he^sia*+p$pqY4;e2r6Myx_-%2A;Q;fjoMKr8!KQs5V z%h7LB_3o0mff5Qc(1(`gXifcPuZIhrpCeRP?3KXMioXYou start assembling \the [src]...") - if(do_after(user, 30, src)) - user.visible_message("\The [user] constructs a beehive.", "You construct a beehive.") - new /obj/machinery/beehive(get_turf(user)) - qdel(src) - /obj/item/bee_smoker name = "bee smoker" desc = "A device used to calm down bees before harvesting honey." @@ -19,29 +5,3 @@ icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_SMALL material = /decl/material/solid/metal/steel - -/obj/item/bee_pack - name = "bee pack" - desc = "Contains a queen bee and some worker bees. Everything you'll need to start a hive!" - icon = 'mods/content/beekeeping/icons/beekeeping.dmi' - icon_state = "beepack" - material = /decl/material/solid/organic/plastic - var/full = 1 - -/obj/item/bee_pack/Initialize() - . = ..() - overlays += "beepack-full" - -/obj/item/bee_pack/proc/empty() - full = 0 - name = "empty bee pack" - desc = "A stasis pack for moving bees. It's empty." - overlays.Cut() - overlays += "beepack-empty" - -/obj/item/bee_pack/proc/fill() - full = initial(full) - SetName(initial(name)) - desc = initial(desc) - overlays.Cut() - overlays += "beepack-full" diff --git a/mods/content/beekeeping/materials.dm b/mods/content/beekeeping/materials.dm new file mode 100644 index 000000000000..496d5f9b394c --- /dev/null +++ b/mods/content/beekeeping/materials.dm @@ -0,0 +1,20 @@ +/decl/material/liquid/bee_venom + name = "bee venom" + uid = "liquid_venom_bee" + lore_text = "An irritant used by bees to drive off predators." + taste_description = "noxious bitterness" + color = "#d7d891" + heating_products = list( + /decl/material/liquid/denatured_toxin = 1 + ) + heating_point = 100 CELSIUS + heating_message = "becomes clear." + taste_mult = 1.2 + metabolism = REM * 0.25 + exoplanet_rarity_plant = MAT_RARITY_UNCOMMON + exoplanet_rarity_gas = MAT_RARITY_EXOTIC + +/decl/material/liquid/bee_venom/affect_blood(mob/living/M, removed, datum/reagents/holder) + . = ..() + if(istype(M)) + M.adjustHalLoss(max(1, ceil(removed * 10))) diff --git a/mods/content/beekeeping/recipes.dm b/mods/content/beekeeping/recipes.dm index 8494617d3477..3d8515113826 100644 --- a/mods/content/beekeeping/recipes.dm +++ b/mods/content/beekeeping/recipes.dm @@ -1,6 +1,5 @@ -/decl/stack_recipe/planks/beehive_assembly - result_type = /obj/item/beehive_assembly - category = "furniture" +/decl/stack_recipe/planks/furniture/apiary + result_type = /obj/structure/apiary /decl/stack_recipe/planks/beehive_frame - result_type = /obj/item/hive_frame/crafted + result_type = /obj/item/hive_frame/crafted diff --git a/mods/content/beekeeping/trading.dm b/mods/content/beekeeping/trading.dm index 45009265f1bf..f79cceb6cc50 100644 --- a/mods/content/beekeeping/trading.dm +++ b/mods/content/beekeeping/trading.dm @@ -1,14 +1,14 @@ /datum/trader/trading_beacon/manufacturing/New() - LAZYSET(possible_trading_items, /obj/item/bee_pack, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/bee_smoker, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/beehive_assembly, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/hive_frame/crafted, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/bee_pack, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/bee_smoker, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/hive_frame/crafted, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/stack/material/plank/mapped/wood/ten, TRADER_THIS_TYPE) ..() /decl/hierarchy/supply_pack/hydroponics/bee_keeper name = "Equipment - Beekeeping" contains = list( - /obj/item/beehive_assembly, + /obj/item/stack/material/plank/mapped/wood/ten, /obj/item/bee_smoker, /obj/item/hive_frame/crafted = 5, /obj/item/bee_pack From 160712f1b2ceb4a83720d86142681e73cec58801 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 22 Sep 2025 11:10:25 +1000 Subject: [PATCH 1252/1331] Reimplementing bee smoking. --- mods/content/beekeeping/closets.dm | 2 +- .../beekeeping/hives/hive_extension.dm | 16 ++++++--- mods/content/beekeeping/hives/hive_swarm.dm | 9 ++++- mods/content/beekeeping/items.dm | 33 +++++++++++++++++-- mods/content/beekeeping/trading.dm | 4 +-- 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/mods/content/beekeeping/closets.dm b/mods/content/beekeeping/closets.dm index 9caf9bb459c8..daf63a942d5c 100644 --- a/mods/content/beekeeping/closets.dm +++ b/mods/content/beekeeping/closets.dm @@ -5,7 +5,7 @@ /obj/structure/closet/crate/hydroponics/beekeeping/Initialize() . = ..() new /obj/item/stack/material/plank/mapped/wood/ten - new /obj/item/bee_smoker(src) + new /obj/item/smoker(src) new /obj/item/hive_frame/crafted(src) new /obj/item/hive_frame/crafted(src) new /obj/item/hive_frame/crafted(src) diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm index d02839f4d40b..576bbfc369f0 100644 --- a/mods/content/beekeeping/hives/hive_extension.dm +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -11,8 +11,8 @@ var/raw_reserves = 0 /// Tracker for the last world.time that a frame was removed. var/frame_last_removed = 0 - /// Tracker for ticks remaning since we were last smoked. - var/smoked_out = 0 + /// Tracker for time that smoke will wear off. + var/smoked_until = 0 /datum/extension/insect_hive/New(datum/holder, _species_decl) ..() @@ -33,8 +33,7 @@ return ..() /datum/extension/insect_hive/Process() - if(smoked_out > 0) - smoked_out-- + if(world.time < smoked_until) return holding_species.process_hive(src) create_hive_products() @@ -69,7 +68,7 @@ /datum/extension/insect_hive/proc/frame_removed(obj/item/frame) frame_last_removed = world.time - if(!smoked_out) + if(world.time >= smoked_until) for(var/obj/effect/insect_swarm/swarm in swarms) swarm.swarm_agitation = min(100, swarm.swarm_agitation + 5) @@ -193,3 +192,10 @@ . = 0 for(var/obj/effect/insect_swarm/swarm as anything in swarms) . += swarm.swarm_intensity + +/datum/extension/insect_hive/proc/smoked_by(mob/user, atom/source, smoke_time = 10 SECONDS) + smoked_until = max(smoked_until, world.time + smoke_time) + // this is a little weird due to telekinetic bee smoking but so it goes + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + swarm.was_smoked(max(0, smoked_until-world.time)) + return TRUE diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm index 2e733a96b138..662e972cd41e 100644 --- a/mods/content/beekeeping/hives/hive_swarm.dm +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -22,6 +22,8 @@ var/const/MAX_SWARM_STATE = 6 /// Cooldown timer for next tick. VAR_PRIVATE/next_work = 0 + /// Time that smoke will wear off. + var/smoked_until = 0 /datum/movement_handler/delay/insect_swarm delay = 1 SECOND @@ -76,7 +78,7 @@ swarm_transform.Turn(pick(90, 180, 270)) /obj/effect/insect_swarm/proc/is_agitated() - return QDELETED(owner) || swarm_agitation > 0 + return QDELETED(owner) || (swarm_agitation > 0 && world.time > smoked_until) /obj/effect/insect_swarm/proc/find_sting_target() for(var/mob/living/victim in view(7, src)) @@ -320,3 +322,8 @@ start_automove(closest_target) else start_automove(owner.holder) + +// TODO: update icon (twitching on ground?) +// TODO: lower agitation +/obj/effect/insect_swarm/proc/was_smoked(smoke_time = 10 SECONDS) + smoked_until = max(smoked_until, world.time + smoke_time) diff --git a/mods/content/beekeeping/items.dm b/mods/content/beekeeping/items.dm index a309ff921a28..1161ae82fd98 100644 --- a/mods/content/beekeeping/items.dm +++ b/mods/content/beekeeping/items.dm @@ -1,7 +1,34 @@ -/obj/item/bee_smoker - name = "bee smoker" - desc = "A device used to calm down bees before harvesting honey." +/obj/item/smoker + name = "smoker" + desc = "A device used to calm insects down before harvesting from a hive." icon = 'mods/content/beekeeping/icons/smoker.dmi' icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_SMALL material = /decl/material/solid/metal/steel + +// TODO: consume reagents or charges? Unnecessary complexity? +/obj/item/smoker/resolve_attackby(atom/A, mob/user, click_params) + + if(!user.check_dexterity(get_required_attack_dexterity(user, A))) + return TRUE + + var/smoked = FALSE + if(has_extension(A, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(A, /datum/extension/insect_hive) + if(hive.smoked_by(user, A)) + smoked = TRUE + + if(!smoked && isturf(A)) + for(var/obj/effect/insect_swarm/swarm in A) + swarm.was_smoked() + smoked = TRUE + + if(smoked) + var/turf/smoked_turf = get_turf(user) + if(smoked_turf) + playsound(smoked_turf, 'sound/effects/refill.ogg', 25, 1) + user.visible_message(SPAN_NOTICE("\The [user] douses \the [A] in smoke from \the [src].")) + new /obj/effect/effect/smoke(smoked_turf, 2 SECONDS) + return TRUE + + return ..() diff --git a/mods/content/beekeeping/trading.dm b/mods/content/beekeeping/trading.dm index f79cceb6cc50..c63d01f412c2 100644 --- a/mods/content/beekeeping/trading.dm +++ b/mods/content/beekeeping/trading.dm @@ -1,6 +1,6 @@ /datum/trader/trading_beacon/manufacturing/New() LAZYSET(possible_trading_items, /obj/item/bee_pack, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/bee_smoker, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/smoker, TRADER_THIS_TYPE) LAZYSET(possible_trading_items, /obj/item/hive_frame/crafted, TRADER_THIS_TYPE) LAZYSET(possible_trading_items, /obj/item/stack/material/plank/mapped/wood/ten, TRADER_THIS_TYPE) ..() @@ -9,7 +9,7 @@ name = "Equipment - Beekeeping" contains = list( /obj/item/stack/material/plank/mapped/wood/ten, - /obj/item/bee_smoker, + /obj/item/smoker, /obj/item/hive_frame/crafted = 5, /obj/item/bee_pack ) From 8f164aeb7d234eae056f72df730cb36ca311136f Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 22 Sep 2025 11:55:47 +1000 Subject: [PATCH 1253/1331] Things can be stored inside dead trees. --- code/game/objects/structures/flora/tree.dm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/game/objects/structures/flora/tree.dm b/code/game/objects/structures/flora/tree.dm index 57c59edea804..12c7df0a8848 100644 --- a/code/game/objects/structures/flora/tree.dm +++ b/code/game/objects/structures/flora/tree.dm @@ -114,6 +114,11 @@ var/global/list/christmas_trees = list() icon_state = "tree_1" protects_against_weather = FALSE stump_type = /obj/structure/flora/stump/tree/dead + storage = /datum/storage/dead_tree + +/datum/storage/dead_tree + max_w_class = ITEM_SIZE_NORMAL + max_storage_space = ITEM_SIZE_SMALL * 5 /obj/structure/flora/tree/dead/random/init_appearance() icon_state = "tree_[rand(1, 6)]" From ac7b7b4849d7cad12b6b577f39b7fe5ed2205d9a Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 22 Sep 2025 13:04:14 +1000 Subject: [PATCH 1254/1331] Implementing hive spawning/destruction logic. --- code/game/objects/structures/flora/plant.dm | 36 +++++++++++++++++-- code/game/objects/structures/flora/stump.dm | 1 + .../beekeeping/hives/hive_extension.dm | 11 ++++-- .../beekeeping/hives/hive_structure.dm | 11 +++--- mods/content/beekeeping/hives/hive_swarm.dm | 33 +++++++++++++---- .../hives/insect_species/_insects.dm | 5 +++ 6 files changed, 81 insertions(+), 16 deletions(-) diff --git a/code/game/objects/structures/flora/plant.dm b/code/game/objects/structures/flora/plant.dm index 9e44f55a2f3a..b17d620d6394 100644 --- a/code/game/objects/structures/flora/plant.dm +++ b/code/game/objects/structures/flora/plant.dm @@ -7,14 +7,20 @@ var/dead = FALSE var/sampled = FALSE var/datum/seed/plant - var/harvestable + var/harvestable = 0 + var/pollen = 0 /obj/structure/flora/plant/large opacity = TRUE density = TRUE -/* Notes for future work moving logic off hydrotrays onto plants themselves: /obj/structure/flora/plant/Process() + if(plant?.produces_pollen <= 0) + return PROCESS_KILL + if(pollen < 10) + pollen += plant.produces_pollen + +/* Notes for future work moving logic off hydrotrays onto plants themselves: // check our immediate environment // ask our environment for available reagents // process the reagents @@ -61,9 +67,13 @@ var/potency = plant.get_trait(TRAIT_POTENCY) set_light(l_range = max(1, round(potency/10)), l_power = clamp(round(potency/30), 0, 1), l_color = plant.get_trait(TRAIT_BIOLUM_COLOUR)) update_icon() - return ..() + . = ..() + if(plant?.produces_pollen && !is_processing) + START_PROCESSING(SSprocessing, src) /obj/structure/flora/plant/Destroy() + if(is_processing) + STOP_PROCESSING(SSprocessing, src) plant = null . = ..() @@ -147,3 +157,23 @@ /obj/structure/flora/plant/random_mushroom/Initialize() plant = pick(get_mushroom_variants()) return ..() + +/obj/structure/flora/plant/random_flower + name = "flower" + color = COLOR_PINK + icon_state = "flower5" + is_spawnable_type = TRUE + +/obj/structure/flora/plant/random_flower/proc/get_flower_variants() + var/static/list/flower_variants + if(isnull(flower_variants)) + flower_variants = list() + for(var/plant in SSplants.seeds) + var/datum/seed/seed = SSplants.seeds[plant] + if(!isnull(seed?.name) && seed.produces_pollen) + flower_variants |= seed.name + return flower_variants + +/obj/structure/flora/plant/random_flower/Initialize() + plant = pick(get_flower_variants()) + return ..() diff --git a/code/game/objects/structures/flora/stump.dm b/code/game/objects/structures/flora/stump.dm index 4911cd3a57e7..f13415593f7c 100644 --- a/code/game/objects/structures/flora/stump.dm +++ b/code/game/objects/structures/flora/stump.dm @@ -4,6 +4,7 @@ /obj/structure/flora/stump name = "stump" hitsound = 'sound/effects/hit_wood.ogg' + storage = /datum/storage/dead_tree var/log_type = /obj/item/stack/material/log /obj/structure/flora/stump/get_material_health_modifier() diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm index 576bbfc369f0..7bd559e74cf4 100644 --- a/mods/content/beekeeping/hives/hive_extension.dm +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -1,3 +1,5 @@ +#define DEFAULT_FRAME_COST 20 + /datum/extension/insect_hive base_type = /datum/extension/insect_hive expected_type = /obj/structure @@ -42,7 +44,12 @@ return FALSE /datum/extension/insect_hive/proc/drop_nest(atom/drop_loc) - return + if(!isatom(drop_loc)) + return + // handle some kind of physical hive dropping here + remove_extension(holder, /datum/extension/insect_hive) + if(!QDELETED(src)) + qdel(src) /datum/extension/insect_hive/proc/get_nest_condition() switch(current_health) @@ -161,7 +168,7 @@ adjust_health(rand(3,5)) return TRUE - if(!has_reserves(20)) + if(!has_reserves(DEFAULT_FRAME_COST)) return TRUE var/list/holder_contents = hive.get_contained_external_atoms() diff --git a/mods/content/beekeeping/hives/hive_structure.dm b/mods/content/beekeeping/hives/hive_structure.dm index f7586ad6eec4..33eb99e67c8d 100644 --- a/mods/content/beekeeping/hives/hive_structure.dm +++ b/mods/content/beekeeping/hives/hive_structure.dm @@ -25,11 +25,14 @@ if(istype(hive)) hive.examined(user, (distance <= 1)) +/atom/physically_destroyed(var/skip_qdel) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + hive?.drop_nest(loc) + return ..() + /obj/structure/dismantle_structure(mob/user) - if(isatom(loc)) - var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) - if(istype(hive)) - hive.drop_nest(loc) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + hive?.drop_nest(loc) return ..() // 'proper' nest structure for building and mapping diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm index 662e972cd41e..3284152915f1 100644 --- a/mods/content/beekeeping/hives/hive_swarm.dm +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -211,7 +211,7 @@ return // If we are the first (or only) of our owner swarms in the loc, and we aren't needed, we don't move. Hive needs workers. - if(owner?.raw_reserves >= 15) + if(owner?.raw_reserves >= DEFAULT_FRAME_COST) if(is_first_swarm_at_hive()) stop_automove() return @@ -307,15 +307,34 @@ stop_automove() return + // Same logic for flora. TODO unify these when seeds are rewritten to be less bespoke. + for(var/obj/structure/flora/plant/flower in loc) + if(!flower.pollen) + continue + pollen += flower.pollen + flower.pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + return + // Find a flower. + var/list/all_potential_targets = list() + for(var/thing in view(src, 7)) + if(istype(thing, /obj/machinery/portable_atmospherics/hydroponics)) + var/obj/machinery/portable_atmospherics/hydroponics/flower = thing + if(flower.pollen) + all_potential_targets += flower + else if(istype(thing, /obj/structure/flora/plant)) + var/obj/structure/flora/plant/flower = thing + if(flower.pollen) + all_potential_targets += flower + var/closest_dist var/atom/closest_target - for(var/obj/machinery/portable_atmospherics/hydroponics/flower in view(src, 7)) - if(!flower.pollen) - continue - var/next_dist = get_dist(src, closest_target) - if(isnull(closest_dist) || next_dist < closest_dist) - closest_target = flower + for(var/atom/thing as anything in shuffle(all_potential_targets)) + var/next_dist = get_dist(src, thing) + if(isnull(closest_target) || next_dist < closest_dist) + closest_target = thing closest_dist = next_dist if(closest_target) diff --git a/mods/content/beekeeping/hives/insect_species/_insects.dm b/mods/content/beekeeping/hives/insect_species/_insects.dm index 013e720ae593..0e3dcac55a51 100644 --- a/mods/content/beekeeping/hives/insect_species/_insects.dm +++ b/mods/content/beekeeping/hives/insect_species/_insects.dm @@ -126,6 +126,11 @@ if(flower.seed?.produces_pollen) return TRUE + for(var/obj/structure/flora/plant/flower in view(flora, 7)) + if(flower.plant?.produces_pollen) + return TRUE + + return FALSE /decl/insect_species/proc/process_hive(datum/extension/insect_hive/hive_metadata) From 13ab3be3a368787ed414ada6ab6ca437e1d71db5 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 22 Sep 2025 14:47:08 +1000 Subject: [PATCH 1255/1331] Adjusting rate of development for hives/hive products. --- code/__defines/misc.dm | 5 ++++ code/game/objects/structures/flora/plant.dm | 6 ++--- .../modules/hydroponics/trays/tray_process.dm | 2 +- code/modules/mob/skills/skillset.dm | 3 ++- mods/content/beekeeping/_beekeeping.dm | 7 ++++++ .../beekeeping/hives/hive_extension.dm | 23 +++++++++++-------- mods/content/beekeeping/hives/hive_flora.dm | 4 ++-- mods/content/beekeeping/hives/hive_frame.dm | 15 ++++++++++-- mods/content/beekeeping/hives/hive_swarm.dm | 20 ++++++++++++---- .../hives/insect_species/_insects.dm | 16 ++++++------- mods/content/beekeeping/items.dm | 2 +- 11 files changed, 69 insertions(+), 34 deletions(-) diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 3721efb82c0b..6a1de9936b74 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -402,3 +402,8 @@ #define MM_ATTACK_RESULT_NONE 0 #define MM_ATTACK_RESULT_DEFLECTED BITFLAG(0) #define MM_ATTACK_RESULT_BLOCKED BITFLAG(1) + +// Effectively a speed modifier for how fast pollen is produced by flowering plants. Pollen per second. +// In theory, one pollen every 5 seconds (at time of writing) +#define POLLEN_PER_SECOND 0.2 +#define POLLEN_PRODUCTION_MULT (POLLEN_PER_SECOND * (SSplants.wait / 10)) diff --git a/code/game/objects/structures/flora/plant.dm b/code/game/objects/structures/flora/plant.dm index b17d620d6394..c56c18864799 100644 --- a/code/game/objects/structures/flora/plant.dm +++ b/code/game/objects/structures/flora/plant.dm @@ -18,7 +18,7 @@ if(plant?.produces_pollen <= 0) return PROCESS_KILL if(pollen < 10) - pollen += plant.produces_pollen + pollen += plant.produces_pollen * POLLEN_PRODUCTION_MULT /* Notes for future work moving logic off hydrotrays onto plants themselves: // check our immediate environment @@ -69,11 +69,11 @@ update_icon() . = ..() if(plant?.produces_pollen && !is_processing) - START_PROCESSING(SSprocessing, src) + START_PROCESSING(SSplants, src) /obj/structure/flora/plant/Destroy() if(is_processing) - STOP_PROCESSING(SSprocessing, src) + STOP_PROCESSING(SSplants, src) plant = null . = ..() diff --git a/code/modules/hydroponics/trays/tray_process.dm b/code/modules/hydroponics/trays/tray_process.dm index d692dc841a51..d6df7e1c6ca8 100644 --- a/code/modules/hydroponics/trays/tray_process.dm +++ b/code/modules/hydroponics/trays/tray_process.dm @@ -66,7 +66,7 @@ mutation_level = 0 if(pollen < 10) - pollen += seed?.produces_pollen + pollen += seed?.produces_pollen * POLLEN_PRODUCTION_MULT // Maintain tray nutrient and water levels. if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS) && seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0 && nutrilevel > 0 && prob(25)) diff --git a/code/modules/mob/skills/skillset.dm b/code/modules/mob/skills/skillset.dm index eef151360637..9da79495a218 100644 --- a/code/modules/mob/skills/skillset.dm +++ b/code/modules/mob/skills/skillset.dm @@ -47,7 +47,8 @@ var/global/list/all_skill_verbs QDEL_NULL(NM) //Clean all nano_modules for simplicity. QDEL_NULL(mob.skillset.NM) QDEL_NULL_LIST(nm_viewing) - QDEL_NULL_LIST(mob.skillset.nm_viewing) + if(mob.skillset) + QDEL_NULL_LIST(mob.skillset.nm_viewing) on_levels_change() //Called when a player is added as an antag and the antag datum processes the skillset. diff --git a/mods/content/beekeeping/_beekeeping.dm b/mods/content/beekeeping/_beekeeping.dm index 471217ac52d6..53ca84daf621 100644 --- a/mods/content/beekeeping/_beekeeping.dm +++ b/mods/content/beekeeping/_beekeeping.dm @@ -1,3 +1,10 @@ +#define FRAME_RESERVE_COST 30 +#define SWARM_AGITATION_PER_FRAME 25 +#define FRAME_MATERIAL_COST 20 +#define SWARM_GROWTH_COST 10 +#define FRAME_FILL_MATERIAL_COST 5 +#define HIVE_REPAIR_MATERIAL_COST 5 + /decl/modpack/beekeeping name = "Beekeeping and Insects Content" diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm index 7bd559e74cf4..0917903fae59 100644 --- a/mods/content/beekeeping/hives/hive_extension.dm +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -1,5 +1,3 @@ -#define DEFAULT_FRAME_COST 20 - /datum/extension/insect_hive base_type = /datum/extension/insect_hive expected_type = /obj/structure @@ -75,9 +73,12 @@ /datum/extension/insect_hive/proc/frame_removed(obj/item/frame) frame_last_removed = world.time - if(world.time >= smoked_until) - for(var/obj/effect/insect_swarm/swarm in swarms) - swarm.swarm_agitation = min(100, swarm.swarm_agitation + 5) + if(world.time >= smoked_until && length(swarms) > 0) + if(isatom(holder)) + var/atom/hive = holder + hive.visible_message(SPAN_DANGER("The buzzing from \the [holder] intensifies.")) + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + swarm.swarm_agitation = min(100, swarm.swarm_agitation + SWARM_AGITATION_PER_FRAME) /datum/extension/insect_hive/proc/try_hand_harvest(mob/user) return FALSE @@ -159,16 +160,16 @@ return TRUE // Naturally build up enough material for a new frame (or repairs). - if(!has_material(20)) + if(!has_material(FRAME_MATERIAL_COST)) add_material(1) // Damaged hives cannot produce combs or honey. if(current_health < 100) - if(consume_material(5)) + if(consume_material(HIVE_REPAIR_MATERIAL_COST)) adjust_health(rand(3,5)) return TRUE - if(!has_reserves(DEFAULT_FRAME_COST)) + if(!has_reserves(FRAME_RESERVE_COST)) return TRUE var/list/holder_contents = hive.get_contained_external_atoms() @@ -176,7 +177,9 @@ if(!frame.reagents || (frame.reagents.total_volume >= frame.reagents.maximum_volume)) continue var/fill_cost = REAGENTS_FREE_SPACE(frame.reagents) - if(consume_material(5) && consume_reserves(fill_cost)) + if(has_material(FRAME_FILL_MATERIAL_COST) && has_reserves(fill_cost)) + consume_material(FRAME_FILL_MATERIAL_COST) + consume_reserves(fill_cost) holding_species.fill_hive_frame(frame) return TRUE @@ -189,7 +192,7 @@ return // Put a timer check on this to avoid a hive filling up with combs the moment you take 2 frames out. - if(world.time > (frame_last_removed + 2 MINUTES) && space_left >= native_frame_size && consume_material(20)) + if(world.time > (frame_last_removed + 2 MINUTES) && space_left >= native_frame_size && consume_material(FRAME_MATERIAL_COST)) // Frames start empty, and will be filled next run. // Native 'frames' (combs) are bigger than crafted ones and aren't reusable. new native_frame(holder, holding_species.produce_material) diff --git a/mods/content/beekeeping/hives/hive_flora.dm b/mods/content/beekeeping/hives/hive_flora.dm index e6091c3610bd..d6bc3a50c34f 100644 --- a/mods/content/beekeeping/hives/hive_flora.dm +++ b/mods/content/beekeeping/hives/hive_flora.dm @@ -28,13 +28,13 @@ /obj/structure/flora/tree/get_supported_insects() var/static/list/_insects = list( /decl/insect_species/honeybees = 10, - /decl/insect_species/wasps = 1 + ///decl/insect_species/wasps = 1 ) return _insects /obj/structure/flora/stump/get_supported_insects() var/static/list/_insects = list( /decl/insect_species/honeybees = 10, - /decl/insect_species/wasps = 1 + ///decl/insect_species/wasps = 1 ) return _insects diff --git a/mods/content/beekeeping/hives/hive_frame.dm b/mods/content/beekeeping/hives/hive_frame.dm index be57c7adf7af..473e667e521a 100644 --- a/mods/content/beekeeping/hives/hive_frame.dm +++ b/mods/content/beekeeping/hives/hive_frame.dm @@ -41,10 +41,21 @@ var/decl/insect_species/bees = GET_DECL(/decl/insect_species/honeybees) bees.fill_hive_frame(src) +/obj/item/hive_frame/forceMove(atom/dest) + var/atom/old_loc = loc + . = ..() + if(. && istype(old_loc)) + check_hive_loc(old_loc) + /obj/item/hive_frame/Move() - var/datum/extension/insect_hive/hive = get_extension(loc, /datum/extension/insect_hive) + var/atom/old_loc = loc . = ..() - if(. && istype(hive) && loc != hive.holder) + if(. && istype(old_loc)) + check_hive_loc(old_loc) + +/obj/item/hive_frame/proc/check_hive_loc(atom/check_loc) + var/datum/extension/insect_hive/hive = get_extension(check_loc, /datum/extension/insect_hive) + if(istype(hive) && loc != hive.holder) hive.frame_removed(src) // Crafted frame used in apiaries. diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm index 3284152915f1..544b7f33406d 100644 --- a/mods/content/beekeeping/hives/hive_swarm.dm +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -78,7 +78,7 @@ swarm_transform.Turn(pick(90, 180, 270)) /obj/effect/insect_swarm/proc/is_agitated() - return QDELETED(owner) || (swarm_agitation > 0 && world.time > smoked_until) + return QDELETED(owner) || (swarm_agitation > 0 && !is_smoked()) /obj/effect/insect_swarm/proc/find_sting_target() for(var/mob/living/victim in view(7, src)) @@ -129,14 +129,21 @@ if(!move_target || !(move_target in view(5, src))) stop_automove() + if(is_smoked()) + return + // Angry swarms move with purpose. if(is_agitated()) swarm_agitation = max(0, swarm_agitation-1) - if(!move_target) - move_target = find_sting_target() + if(!ismob(move_target)) + var/mob/new_move_target = find_sting_target() + if(istype(new_move_target)) + move_target = new_move_target if(move_target) start_automove(move_target) - return + if(insect_type.sting_amount || insect_type.sting_reagent) + insect_type.try_sting(src, loc) + return // Large swarms split if they aren't agitated. if(swarm_can_split() && isturf(loc)) @@ -211,7 +218,7 @@ return // If we are the first (or only) of our owner swarms in the loc, and we aren't needed, we don't move. Hive needs workers. - if(owner?.raw_reserves >= DEFAULT_FRAME_COST) + if(owner?.has_reserves(FRAME_RESERVE_COST)) if(is_first_swarm_at_hive()) stop_automove() return @@ -346,3 +353,6 @@ // TODO: lower agitation /obj/effect/insect_swarm/proc/was_smoked(smoke_time = 10 SECONDS) smoked_until = max(smoked_until, world.time + smoke_time) + +/obj/effect/insect_swarm/proc/is_smoked() + return world.time < smoked_until \ No newline at end of file diff --git a/mods/content/beekeeping/hives/insect_species/_insects.dm b/mods/content/beekeeping/hives/insect_species/_insects.dm index 0e3dcac55a51..37e4481748cd 100644 --- a/mods/content/beekeeping/hives/insect_species/_insects.dm +++ b/mods/content/beekeeping/hives/insect_species/_insects.dm @@ -97,7 +97,8 @@ // If we're agitated, always sting. Otherwise, % chance equal to a quarter of our overall swarm intensity. if(!swarm.is_agitated() && !prob(max(1, round(swarm.swarm_intensity/4)))) return FALSE - var/sting_mult = sting_amount * clamp(round(swarm.swarm_intensity/10), 1, 10) + var/base_sting_chance = (sting_amount * clamp(round(swarm.swarm_intensity/10), 1, 10)) + var/sting_mult = swarm.is_agitated() ? max(base_sting_chance, 65) : base_sting_chance for(var/mob/living/victim in loc) if(!victim.simulated || victim.stat || victim.current_posture?.prone) continue @@ -106,12 +107,9 @@ if(!affecting || BP_IS_PROSTHETIC(affecting) || BP_IS_CRYSTAL(affecting)) continue if(injected_reagents && victim.can_inject(victim, affecting.organ_tag)) + to_chat(victim, SPAN_DANGER("\A [swarm] stings you [sting_mult <= sting_amount * 2 ? "" : "multiple times"] on your [affecting.name]!")) injected_reagents.add_reagent(sting_reagent, sting_mult) affecting.add_pain(sting_mult) - if(sting_mult <= sting_amount * 2) - to_chat(victim, SPAN_DANGER("You are stung on your [affecting.name] by \a [swarm]!")) - else - to_chat(victim, SPAN_DANGER("You are stung multiple times on your [affecting.name] by \a [swarm]!")) . = TRUE /decl/insect_species/proc/can_spawn_in_flora(var/obj/structure/flora) @@ -152,14 +150,14 @@ return // Try to grow an existing swarm until we're at our max. - if(hive_metadata.has_reserves(5) && length(hive_metadata.swarms)) + if(hive_metadata.has_reserves(SWARM_GROWTH_COST) && length(hive_metadata.swarms)) for(var/obj/effect/insect_swarm/swarm as anything in hive_metadata.swarms) - if(swarm.can_grow() && hive_metadata.consume_reserves(5)) + if(swarm.can_grow() && hive_metadata.consume_reserves(SWARM_GROWTH_COST)) swarm.adjust_swarm_intensity(min(max_swarm_growth_intensity-swarm_intensity, rand(3,5))) return // If we have sufficient filled combs, create a new swarm. Otherwise, expand a swarm. - if(hive.loc && hive_metadata.has_reserves(5)) + if(hive.loc && hive_metadata.has_reserves(SWARM_GROWTH_COST)) var/obj/effect/insect_swarm/swarm for(var/obj/effect/insect_swarm/check_swarm as anything in hive_metadata.swarms) @@ -175,5 +173,5 @@ if(length(hive_metadata.swarms) < comb_count) swarm = new swarm_type(hive.loc, src, hive_metadata) - if(!QDELETED(swarm) && istype(swarm) && hive_metadata.consume_reserves(5)) + if(!QDELETED(swarm) && istype(swarm) && hive_metadata.consume_reserves(SWARM_GROWTH_COST)) swarm.adjust_swarm_intensity(min((max_swarm_growth_intensity-swarm_intensity), rand(3,5))) diff --git a/mods/content/beekeeping/items.dm b/mods/content/beekeeping/items.dm index 1161ae82fd98..f520e5085eab 100644 --- a/mods/content/beekeeping/items.dm +++ b/mods/content/beekeeping/items.dm @@ -24,7 +24,7 @@ smoked = TRUE if(smoked) - var/turf/smoked_turf = get_turf(user) + var/turf/smoked_turf = get_turf(A) if(smoked_turf) playsound(smoked_turf, 'sound/effects/refill.ogg', 25, 1) user.visible_message(SPAN_NOTICE("\The [user] douses \the [A] in smoke from \the [src].")) From e9756389598606796c6d92e0f65c05d067b0ff2d Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 22 Sep 2025 21:36:05 +1000 Subject: [PATCH 1256/1331] Commenting out wasps. --- mods/content/beekeeping/hives/hive_extension.dm | 9 ++++++++- mods/content/beekeeping/hives/hive_structure.dm | 2 +- .../hives/insect_species/insects_pollinators.dm | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm index 0917903fae59..c315dd59e2ef 100644 --- a/mods/content/beekeeping/hives/hive_extension.dm +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -80,7 +80,14 @@ for(var/obj/effect/insect_swarm/swarm as anything in swarms) swarm.swarm_agitation = min(100, swarm.swarm_agitation + SWARM_AGITATION_PER_FRAME) -/datum/extension/insect_hive/proc/try_hand_harvest(mob/user) +/datum/extension/insect_hive/proc/try_hand_harvest(mob/user, obj/item/structure) + if(istype(structure) && !structure.storage) + var/obj/item/hive_frame/frame = locate() in structure + if(frame) + frame.dropInto(get_turf(structure)) + if(istype(user)) + user.put_in_hands(frame) + return TRUE return FALSE /datum/extension/insect_hive/proc/try_tool_harvest(mob/user, obj/item/tool) diff --git a/mods/content/beekeeping/hives/hive_structure.dm b/mods/content/beekeeping/hives/hive_structure.dm index 33eb99e67c8d..855f9e42c7b8 100644 --- a/mods/content/beekeeping/hives/hive_structure.dm +++ b/mods/content/beekeeping/hives/hive_structure.dm @@ -8,7 +8,7 @@ /obj/structure/attack_hand(mob/user) if(has_extension(src, /datum/extension/insect_hive)) var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) - if(hive.try_hand_harvest(user)) + if(hive.try_hand_harvest(user, src)) return TRUE return ..() diff --git a/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm index 0f9b924e62fe..83aeb86f8462 100644 --- a/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm +++ b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm @@ -14,6 +14,7 @@ produce_reagents = list(/decl/material/liquid/nutriment/honey = 1) produce_material = /decl/material/solid/organic/wax +/* /decl/insect_species/wasps name_singular = "wasp" name_plural = "wasps" @@ -24,3 +25,4 @@ sting_amount = 5 swarm_color = COLOR_BRONZE swarm_type = /obj/effect/insect_swarm/pollinator // tarantula hunter... +*/ \ No newline at end of file From 815c22139d712e284f63aeb7bd11054334e8f602 Mon Sep 17 00:00:00 2001 From: mistakenot4892 Date: Mon, 13 Oct 2025 10:32:08 +1100 Subject: [PATCH 1257/1331] Addressing some comments on beewrite PR. --- code/__defines/misc.dm | 1 + .../objects/items/devices/chameleonproj.dm | 5 +- code/game/objects/structures/flora/plant.dm | 5 +- .../modules/hydroponics/trays/tray_process.dm | 3 +- code/modules/mob/living/living.dm | 3 + code/modules/mob/mob_automove.dm | 1 - mods/content/beekeeping/hives/_hive.dm | 168 ------------------ .../beekeeping/hives/hive_extension.dm | 8 +- mods/content/beekeeping/hives/hive_swarm.dm | 73 ++++---- .../hives/insect_species/_insects.dm | 16 +- mods/content/beekeeping/icons/swarm.dmi | Bin 1016 -> 1124 bytes 11 files changed, 70 insertions(+), 213 deletions(-) delete mode 100644 mods/content/beekeeping/hives/_hive.dm diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 6a1de9936b74..9fc564c109c7 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -407,3 +407,4 @@ // In theory, one pollen every 5 seconds (at time of writing) #define POLLEN_PER_SECOND 0.2 #define POLLEN_PRODUCTION_MULT (POLLEN_PER_SECOND * (SSplants.wait / 10)) +#define MAX_POLLEN_PER_FLOWER 10 diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index fc743829ceef..83f05a1b7de8 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -102,7 +102,7 @@ density = FALSE anchored = TRUE is_spawnable_type = FALSE - movement_handlers = list(/datum/movement_handler/delay/chameleon_projector) + movement_handlers = list(/datum/movement_handler/delay/chameleon_projector = list(2.5 SECONDS)) var/obj/item/chameleon/master = null /obj/effect/dummy/chameleon/Initialize(mapload, var/obj/item/chameleon/projector) @@ -152,9 +152,6 @@ if(!my_turf.get_supporting_platform() && !(locate(/obj/structure/lattice) in loc)) disrupted() -/datum/movement_handler/delay/chameleon_projector - delay = 2.5 SECONDS - /datum/movement_handler/delay/chameleon_projector/MayMove(mob/mover, is_external) return host.loc?.has_gravity() ? ..() : MOVEMENT_STOP diff --git a/code/game/objects/structures/flora/plant.dm b/code/game/objects/structures/flora/plant.dm index c56c18864799..278a60e8f019 100644 --- a/code/game/objects/structures/flora/plant.dm +++ b/code/game/objects/structures/flora/plant.dm @@ -7,7 +7,7 @@ var/dead = FALSE var/sampled = FALSE var/datum/seed/plant - var/harvestable = 0 + var/harvestable = 0 // Note that this is a counter, not a bool. var/pollen = 0 /obj/structure/flora/plant/large @@ -17,7 +17,7 @@ /obj/structure/flora/plant/Process() if(plant?.produces_pollen <= 0) return PROCESS_KILL - if(pollen < 10) + if(pollen < MAX_POLLEN_PER_FLOWER) pollen += plant.produces_pollen * POLLEN_PRODUCTION_MULT /* Notes for future work moving logic off hydrotrays onto plants themselves: @@ -164,6 +164,7 @@ icon_state = "flower5" is_spawnable_type = TRUE +// Only contains roundstart plants, this is meant to be a mapping helper. /obj/structure/flora/plant/random_flower/proc/get_flower_variants() var/static/list/flower_variants if(isnull(flower_variants)) diff --git a/code/modules/hydroponics/trays/tray_process.dm b/code/modules/hydroponics/trays/tray_process.dm index d6df7e1c6ca8..2b835ea3eb69 100644 --- a/code/modules/hydroponics/trays/tray_process.dm +++ b/code/modules/hydroponics/trays/tray_process.dm @@ -65,8 +65,9 @@ mutate((rand(100) < 15) ? 2 : 1) mutation_level = 0 - if(pollen < 10) + if(pollen < MAX_POLLEN_PER_FLOWER) pollen += seed?.produces_pollen * POLLEN_PRODUCTION_MULT + to_world("\ref[src] has pollen [pollen] ([seed?.produces_pollen] * [POLLEN_PRODUCTION_MULT])") // Maintain tray nutrient and water levels. if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS) && seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0 && nutrilevel > 0 && prob(25)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 55e94083cb93..cc265798515b 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2020,3 +2020,6 @@ default behaviour is: //Pixel projectiles need a client, so we need a way to pass who the last user was for view calcs /mob/living/proc/get_effective_gunner() return src + +/mob/living/proc/is_playing_dead() + return stat || current_posture?.prone || (status_flags & FAKEDEATH) diff --git a/code/modules/mob/mob_automove.dm b/code/modules/mob/mob_automove.dm index f552d4f7c1e6..c74d72c44e97 100644 --- a/code/modules/mob/mob_automove.dm +++ b/code/modules/mob/mob_automove.dm @@ -25,7 +25,6 @@ /mob/failed_automove() ..() stop_automove() - _automove_target = null return FALSE /mob/start_automove(target, movement_type, datum/automove_metadata/metadata) diff --git a/mods/content/beekeeping/hives/_hive.dm b/mods/content/beekeeping/hives/_hive.dm deleted file mode 100644 index 990d148a16f9..000000000000 --- a/mods/content/beekeeping/hives/_hive.dm +++ /dev/null @@ -1,168 +0,0 @@ -/obj/machinery/beehive - name = "apiary" - icon = 'mods/content/beekeeping/icons/beekeeping.dmi' - icon_state = "beehive-0" - desc = "A wooden box designed specifically to house our buzzling buddies. Far more efficient than traditional hives. Just insert a frame and a queen, close it up, and you're good to go!" - density = TRUE - anchored = TRUE - layer = BELOW_OBJ_LAYER - - var/closed = 0 - var/bee_count = 0 // Percent - var/smoked = 0 // Timer - var/honeycombs = 0 // Percent - var/frames = 0 - var/maxFrames = 5 - -/obj/machinery/beehive/Initialize() - . = ..() - update_icon() - -/obj/machinery/beehive/on_update_icon() - overlays.Cut() - icon_state = "beehive-[closed]" - if(closed) - overlays += "lid" - if(frames) - overlays += "empty[frames]" - if(honeycombs >= 100) - overlays += "full[round(honeycombs / 100)]" - if(!smoked) - switch(bee_count) - if(1 to 20) - overlays += "bees1" - if(21 to 40) - overlays += "bees2" - if(41 to 60) - overlays += "bees3" - if(61 to 80) - overlays += "bees4" - if(81 to 100) - overlays += "bees5" - -/obj/machinery/beehive/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(!closed) - . += "The lid is open." - -/obj/machinery/beehive/attackby(var/obj/item/used_item, var/mob/user) - if(IS_CROWBAR(used_item)) - closed = !closed - user.visible_message("\The [user] [closed ? "closes" : "opens"] \the [src].", "You [closed ? "close" : "open"] \the [src].") - update_icon() - return TRUE - else if(IS_WRENCH(used_item)) - anchored = !anchored - user.visible_message("\The [user] [anchored ? "wrenches" : "unwrenches"] \the [src].", "You [anchored ? "wrench" : "unwrench"] \the [src].") - return TRUE - else if(istype(used_item, /obj/item/bee_smoker)) - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before smoking the bees.") - return TRUE - user.visible_message("\The [user] smokes the bees in \the [src].", "You smoke the bees in \the [src].") - smoked = 30 - update_icon() - return TRUE - else if(istype(used_item, /obj/item/hive_frame/crafted)) - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before inserting \the [used_item].") - return TRUE - if(frames >= maxFrames) - to_chat(user, "There is no place for an another frame.") - return TRUE - var/obj/item/hive_frame/crafted/H = used_item - if(REAGENT_TOTAL_VOLUME(H.reagents)) - to_chat(user, "\The [used_item] is full with beeswax and honey, empty it in the extractor first.") - return TRUE - ++frames - user.visible_message("\The [user] loads \the [used_item] into \the [src].", "You load \the [used_item] into \the [src].") - update_icon() - qdel(used_item) - return TRUE - else if(istype(used_item, /obj/item/bee_pack)) - var/obj/item/bee_pack/B = used_item - if(B.full && bee_count) - to_chat(user, "\The [src] already has bees inside.") - return TRUE - if(!B.full && bee_count < 90) - to_chat(user, "\The [src] is not ready to split.") - return TRUE - if(!B.full && !smoked) - to_chat(user, "Smoke \the [src] first!") - return TRUE - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before moving the bees.") - return TRUE - if(B.full) - user.visible_message("\The [user] puts the queen and the bees from \the [used_item] into \the [src].", "You put the queen and the bees from \the [used_item] into \the [src].") - bee_count = 20 - B.empty() - else - user.visible_message("\The [user] puts bees and larvae from \the [src] into \the [used_item].", "You put bees and larvae from \the [src] into \the [used_item].") - bee_count /= 2 - B.fill() - update_icon() - return TRUE - else if(istype(used_item, /obj/item/scanner/plant)) - to_chat(user, "Scan result of \the [src]...") - to_chat(user, "Beehive is [bee_count ? "[round(bee_count)]% full" : "empty"].[bee_count > 90 ? " Colony is ready to split." : ""]") - if(frames) - to_chat(user, "[frames] frames installed, [round(honeycombs / 100)] filled.") - if(honeycombs < frames * 100) - to_chat(user, "Next frame is [round(honeycombs % 100)]% full.") - else - to_chat(user, "No frames installed.") - if(smoked) - to_chat(user, "The hive is smoked.") - return TRUE - else if(IS_SCREWDRIVER(used_item)) - if(bee_count) - to_chat(user, "You can't dismantle \the [src] with these bees inside.") - return TRUE - to_chat(user, "You start dismantling \the [src]...") - playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 30, src)) - user.visible_message("\The [user] dismantles \the [src].", "You dismantle \the [src].") - new /obj/item/beehive_assembly(loc) - qdel(src) - return TRUE - return FALSE // this should probably not be a machine, so don't do any component interactions - -/obj/machinery/beehive/physical_attack_hand(var/mob/user) - if(closed) - return FALSE - . = TRUE - if(honeycombs < 100) - to_chat(user, "There are no filled honeycombs.") - return - if(!smoked && bee_count) - to_chat(user, "The bees won't let you take the honeycombs out like this, smoke them first.") - return - user.visible_message("\The [user] starts taking the honeycombs out of \the [src].", "You start taking the honeycombs out of \the [src]...") - while(honeycombs >= 100 && do_after(user, 30, src)) - new /obj/item/hive_frame/crafted/filled(loc) - honeycombs -= 100 - --frames - update_icon() - if(honeycombs < 100) - to_chat(user, "You take all filled honeycombs out.") - -/obj/machinery/beehive/Process() - if(closed && !smoked && bee_count) - pollinate_flowers() - update_icon() - smoked = max(0, smoked - 1) - if(!smoked && bee_count) - bee_count = min(bee_count * 1.005, 100) - update_icon() - -/obj/machinery/beehive/proc/pollinate_flowers() - var/coef = bee_count / 100 - var/trays = 0 - for(var/obj/machinery/portable_atmospherics/hydroponics/H in view(7, src)) - if(H.seed && !H.dead) - H.plant_health += 0.05 * coef - if(H.pollen >= 1) - H.pollen-- - trays++ - honeycombs = min(honeycombs + 0.1 * coef * min(trays, 5), frames * 100) diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm index c315dd59e2ef..40c44df25054 100644 --- a/mods/content/beekeeping/hives/hive_extension.dm +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -125,7 +125,7 @@ return FALSE var/reserve = 0 for(var/obj/item/frame in holder) - reserve += frame.reagents?.total_volume + reserve += REAGENT_TOTAL_VOLUME(frame.reagents) if(reserve >= amt) return TRUE return FALSE @@ -141,9 +141,9 @@ amt -= raw_reserves raw_reserves = 0 for(var/obj/item/frame in holder) - if(!frame.reagents?.total_volume) + if(!REAGENT_TOTAL_VOLUME(frame.reagents)) continue - var/consume = min(amt, frame.reagents.total_volume) + var/consume = min(amt, REAGENT_TOTAL_VOLUME(frame.reagents)) frame.reagents.remove_any(consume) amt -= consume if(amt <= 0) @@ -181,7 +181,7 @@ var/list/holder_contents = hive.get_contained_external_atoms() for(var/obj/item/hive_frame/frame in holder_contents) - if(!frame.reagents || (frame.reagents.total_volume >= frame.reagents.maximum_volume)) + if(!frame.reagents || (REAGENT_TOTAL_VOLUME(frame.reagents) >= REAGENT_MAXIMUM_VOLUME(frame.reagents))) continue var/fill_cost = REAGENTS_FREE_SPACE(frame.reagents) if(has_material(FRAME_FILL_MATERIAL_COST) && has_reserves(fill_cost)) diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm index 544b7f33406d..084ef4461d84 100644 --- a/mods/content/beekeeping/hives/hive_swarm.dm +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -6,7 +6,7 @@ default_pixel_z = 8 layer = ABOVE_HUMAN_LAYER pass_flags = PASS_FLAG_TABLE - movement_handlers = list(/datum/movement_handler/delay/insect_swarm) + movement_handlers = list(/datum/movement_handler/delay/insect_swarm = list(1 SECOND)) /// Current movement target for automove (ie. hive, flowers or victim) VAR_PRIVATE/atom/move_target @@ -18,16 +18,11 @@ var/swarm_agitation = 0 /// Percentage value; if it drops to 0, the swarm will be destroyed. var/swarm_intensity = 1 - /// if more states are added to swarm.dmi, increase this - var/const/MAX_SWARM_STATE = 6 /// Cooldown timer for next tick. VAR_PRIVATE/next_work = 0 /// Time that smoke will wear off. var/smoked_until = 0 -/datum/movement_handler/delay/insect_swarm - delay = 1 SECOND - /datum/movement_handler/delay/insect_swarm/DoMove(direction, mob/mover, is_external) ..() step(host, direction) @@ -46,8 +41,7 @@ if(!istype(owner)) PRINT_STACK_TRACE("Insect swarm created with invalid hive: '[owner]'") return INITIALIZE_HINT_QDEL - color = insect_type.swarm_color - icon = insect_type.swarm_icon + update_transform() update_swarm() LAZYDISTINCTADD(owner.swarms, src) START_PROCESSING(SSobj, src) @@ -61,9 +55,29 @@ STOP_PROCESSING(SSobj, src) return ..() +// Resolves the current swarm amount to a coarser value used for icon state selection. +/obj/effect/insect_swarm/proc/get_swarm_state() + return ceil((swarm_intensity / insect_type.max_swarm_intensity) * insect_type.max_swarm_state) + +/obj/effect/insect_swarm/on_update_icon() + . = ..() + color = insect_type.swarm_color + icon = insect_type.swarm_icon + icon_state = num2text(get_swarm_state()) + if(is_smoked()) + icon_state = "[icon_state]_smoked" + +/obj/effect/insect_swarm/update_transform() + . = ..() + // Some icon variation via transform. + if(prob(75)) + var/matrix/swarm_transform = transform || matrix() + swarm_transform.Turn(pick(90, 180, 270)) + transform = swarm_transform + /obj/effect/insect_swarm/proc/update_swarm() - icon_state = num2text(ceil((swarm_intensity / insect_type.max_swarm_intensity) * MAX_SWARM_STATE)) - if(icon_state == "1") + update_icon() + if(get_swarm_state() == 1) SetName(insect_type.name_singular) desc = insect_type.insect_desc gender = NEUTER @@ -72,21 +86,13 @@ desc = insect_type.swarm_desc gender = PLURAL - // Some icon variation via transform. - if(prob(75)) - var/matrix/swarm_transform = matrix() - swarm_transform.Turn(pick(90, 180, 270)) - /obj/effect/insect_swarm/proc/is_agitated() return QDELETED(owner) || (swarm_agitation > 0 && !is_smoked()) /obj/effect/insect_swarm/proc/find_sting_target() for(var/mob/living/victim in view(7, src)) - if(!victim.simulated || victim.stat || victim.current_posture?.prone) - continue - if(victim.isSynthetic()) - continue - return victim + if(victim.simulated && !victim.is_playing_dead()) + return victim /obj/effect/insect_swarm/proc/merge(obj/effect/insect_swarm/other_swarm) @@ -189,23 +195,24 @@ break return FALSE +/obj/effect/insect_swarm/failed_automove() + ..() + stop_automove() + return FALSE + /obj/effect/insect_swarm/get_automove_target(datum/automove_metadata/metadata) return move_target /obj/effect/insect_swarm/stop_automove() - SHOULD_CALL_PARENT(FALSE) move_target = null - //. = ..() // TODO work out why they're not automoving - walk(src, 0) + . = ..() + +/obj/effect/insect_swarm/can_do_automated_move(variant_move_delay) + return !is_smoked() /obj/effect/insect_swarm/start_automove(target, movement_type, datum/automove_metadata/metadata) - SHOULD_CALL_PARENT(FALSE) move_target = target - //. = ..() // TODO work out why they're not automoving - if(move_target) - walk_to(src, move_target, 0, 7) - else - walk(src, 0) + . = ..() /obj/effect/insect_swarm/proc/handle_hive_behavior() @@ -272,6 +279,10 @@ merge(other_swarm) return +/obj/effect/insect_swarm/DoMove(direction, mob/mover, is_external) + . = ..() + to_world("swarm tried to move: [.]") + /obj/effect/insect_swarm/pollinator var/pollen = 0 @@ -349,10 +360,10 @@ else start_automove(owner.holder) -// TODO: update icon (twitching on ground?) -// TODO: lower agitation /obj/effect/insect_swarm/proc/was_smoked(smoke_time = 10 SECONDS) smoked_until = max(smoked_until, world.time + smoke_time) + swarm_agitation = round(swarm_agitation * 0.75) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon), TRUE), smoke_time, (TIMER_UNIQUE|TIMER_OVERRIDE)) /obj/effect/insect_swarm/proc/is_smoked() return world.time < smoked_until \ No newline at end of file diff --git a/mods/content/beekeeping/hives/insect_species/_insects.dm b/mods/content/beekeeping/hives/insect_species/_insects.dm index 37e4481748cd..b6902a51a0ab 100644 --- a/mods/content/beekeeping/hives/insect_species/_insects.dm +++ b/mods/content/beekeeping/hives/insect_species/_insects.dm @@ -22,6 +22,7 @@ var/swarm_type = /obj/effect/insect_swarm var/max_swarm_growth_intensity = 50 var/max_swarm_intensity = 100 + var/max_swarm_state = 6 // Venom delivered by swarms whens stinging a victim. var/sting_reagent @@ -72,6 +73,17 @@ if(!istype(produce_material, /decl/material)) . += "non-material product material type: '[produce_material]'" + if(!swarm_icon) + . += "null swarm icon" + else + for(var/i = 0 to max_swarm_state) + var/check_state = num2text(i) + if(!check_state_in_icon(check_state, swarm_icon)) + . += "missing active icon_state '[check_state]'" + check_state = "[check_state]_smoked" + if(!check_state_in_icon(check_state, swarm_icon)) + . += "missing smoked icon_state '[check_state]'" + /decl/insect_species/proc/fill_hive_frame(obj/item/frame) if(!istype(frame) || QDELETED(frame)) @@ -81,7 +93,7 @@ if(frame_space <= 0) return FALSE - if(frame.reagents?.maximum_volume && length(produce_reagents)) + if(REAGENT_MAXIMUM_VOLUME(frame.reagents) && length(produce_reagents)) var/reagent_split = max(1, floor(min(REAGENTS_FREE_SPACE(frame.reagents), 20) / length(produce_reagents))) for(var/reagent in produce_reagents) frame.reagents.add_reagent(reagent, max(1, (reagent_split * produce_reagents[reagent])), defer_update = TRUE) @@ -168,7 +180,7 @@ if(!swarm) var/comb_count = 0 for(var/obj/item/hive_frame/frame in hive) - if(frame.reagents && frame.reagents.total_volume >= frame.reagents.maximum_volume) + if(REAGENT_TOTAL_VOLUME(frame.reagents) >= REAGENT_MAXIMUM_VOLUME(frame.reagents)) comb_count++ if(length(hive_metadata.swarms) < comb_count) swarm = new swarm_type(hive.loc, src, hive_metadata) diff --git a/mods/content/beekeeping/icons/swarm.dmi b/mods/content/beekeeping/icons/swarm.dmi index 1d8ea6f1b1ae8e237aaaefaf5352ad590422c65c..e55d936c1f15331e32068155b7bb6ea87076b676 100644 GIT binary patch delta 940 zcmV;d15^C?2jmEl7Yg760{{R3=mU4sks&Jqw2@7Wf04pFzBo5OJ2i!HuoJ7$h**Wj z#40o)R-q}e3e5;8R90~Ha{;Gc0N-xA%A#`K)&KwlLrFwIRCt{2o9lstAPj&FE|7DA zbOZOF?m(bCd=nJg=KEnqGzlR<(7Ub^1VIqK2W)MC(3+1A0cvf4AP9mW2*Ni4tqu4* zRBHo1e-LVIfN&O>8Lo!wVX9$-AP9mW2)_WeHh|BQb5m+kCZLO19%RDv^0TxN?5r z_O!g=&TDvC?Wx-@GXk9KTL%Z+o2ct91a)T%P5S`2W8?U;4Bb$-b+l7I*h6?rvG>3N z=k|lxnI1eHHOw%Z55%j`T1H$lSEV;qLwRLL6{T*0lQQh6;@IEu2wsufZmyzf+m3V? ze=$ty%yKclcoGie1;LBN)m6ac#e+d}dWd8J5Y-ds zrV_VIrkV0$|AT!#zk)uwvp@ap1_p!8f8=6;>MfQ!mr=$o0>0vDby7}>@mH3!>xlr?HxnL>UzJvCH-LYh>zk7Nb*Xdgw3IMn`K4e`h?P z>A@`ObPt&pgVk;>^oa`4TQB3~LcKJJz<6p-MZNr8$wSD<{*(=J6}&nI7aC2r9;3;< z%0KW0A=5lg{#+Mc+0f^phhU%Nsia*+u?zc54;e2r2ma!PzlBPmt{RcevuIe_eirU& z=e^(O>Rpkzff5Td&`Qm`w|slqf9v5)=XzvJnGJszmQ32O)POih_c;~BY9=+057-Ks zS-V}1t(7Ye`x0{{R3@2_&?ks&JqmXS@2f4P(uT>V_Y5d;9b!D;2Pce3gL z00Q+%L_t(|ob8+2xq~1KfDJB?;{7pj@s%?NE2!bF8!Z!l74R{}_wgE4MY8xP&MP`Pp;d+=#7$FFPAPB-QK(!6v z^W@r;e`*`B!}GVV=STOJ-@0D^gW8jz+6FACF{zwSM%rqWJP-sy5N_tlCHaL6`;XA& zM({Y$1>wu6xCuN^KH|nz2_0oB*!S1LKRRb0q(+XkWySGA^!eWu-OFizhyxuopNUI^ zAK*t;R`q#-$y&*F+(tPvuLnPzA9y^z-f-tNfAm&+?Dory04Mv_Ul*TL^IP2=$lXj#FaL z_&oA*R_{;U0_kiYqFF#IpvVJg_X&AQ(eE}U^#eo!LDRVUJBmBqBhEFA)f5%Vps)>H zuD4$UQ=hXKok}x8f4((1T3a~zfMx`fsM8~4dJI;(xzR={KyST_moxR!C<4Q&e>oP_ zPftZJA*1?JHprFt>fl{yG}(3xCifzL;PXPpd7P$mebFbW&q1eP8`Y_#T}7b_+e8l; zFEbN=^~T>yBT!R}z~)6XtZhFt_q5B=Z&UT|lDL5q3Nz4$mgQ(o{bjF*3!R@MW2|iW zyRc}|?qdVOAT5kqNKNAdvO^}4C9wqkC#|(-GADh!$QIO4k$)9N6-H98v2*|c002ov JPDHLkV1hfkhZq0= From 1d403a0c79848cbb6a19621608e350d64c25082c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 4 May 2026 15:27:32 -0400 Subject: [PATCH 1258/1331] Make alists display properly in VV --- code/modules/admin/view_variables/view_variables.dm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index 19efad19a399..4d248d8255d3 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -175,6 +175,14 @@ var/global/list/view_variables_no_assoc = list("verbs", "contents","screen","ima else if(istype(value, /client)) var/client/C = value vtext = "\ref[C] - [C] ([C.type])" + else if(istype(value, /alist)) + var/alist/AL = value + vtext = "/alist ([AL.len])" + if(!(varname in view_variables_dont_expand) && AL.len > 0 && AL.len < 100) + extra += "
      " + for (var/key, entry in AL) + extra += "
    • [make_view_variables_value(key)] -> [make_view_variables_value(entry)]
    • " + extra += "
    " else if(islist(value)) var/list/L = value vtext = "/list ([L.len])" From dbf67deb82bec1517e4aedd3d4848b445a3dbef4 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 4 May 2026 15:27:47 -0400 Subject: [PATCH 1259/1331] Fix structure plants not generating pollen --- code/game/objects/structures/flora/plant.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/structures/flora/plant.dm b/code/game/objects/structures/flora/plant.dm index 278a60e8f019..f4dfac197bc6 100644 --- a/code/game/objects/structures/flora/plant.dm +++ b/code/game/objects/structures/flora/plant.dm @@ -14,7 +14,7 @@ opacity = TRUE density = TRUE -/obj/structure/flora/plant/Process() +/obj/structure/flora/plant/process_plants() if(plant?.produces_pollen <= 0) return PROCESS_KILL if(pollen < MAX_POLLEN_PER_FLOWER) From fcbdd298273a9be160f502c81b535a7473c6a1ae Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 4 May 2026 15:28:06 -0400 Subject: [PATCH 1260/1331] Fix mob stop_effect movement handler typepath --- code/datums/movement/mob.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm index 996c62150859..ee52cd4ff8c8 100644 --- a/code/datums/movement/mob.dm +++ b/code/datums/movement/mob.dm @@ -120,7 +120,7 @@ next_move += max(0, delay) // Stop effect -/datum/movement_handler/mob/DoMove(direction, mob/mover, is_external) +/datum/movement_handler/mob/stop_effect/DoMove(direction, mob/mover, is_external) if(MayMove(mover, is_external) == MOVEMENT_STOP) return MOVEMENT_HANDLED From c94e3d1362e8c727c424a4d0db527bd4b6d99724 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 4 May 2026 15:28:30 -0400 Subject: [PATCH 1261/1331] Improve and fix automove functionality --- code/datums/movement/automove.dm | 2 +- code/datums/movement/automove_controller.dm | 97 ++++++++++++++------- code/game/atoms_movable.dm | 11 ++- code/modules/mob/mob.dm | 11 ++- code/modules/mob/mob_automove.dm | 2 +- 5 files changed, 84 insertions(+), 39 deletions(-) diff --git a/code/datums/movement/automove.dm b/code/datums/movement/automove.dm index a07d65d0324b..6c3533f758f9 100644 --- a/code/datums/movement/automove.dm +++ b/code/datums/movement/automove.dm @@ -37,4 +37,4 @@ /// Generalized entrypoint for checking CanMove and such on /mob. /atom/movable/proc/can_do_automated_move(variant_move_delay) - return FALSE + return MayMove(src) diff --git a/code/datums/movement/automove_controller.dm b/code/datums/movement/automove_controller.dm index b413d9998497..e593f4315c9e 100644 --- a/code/datums/movement/automove_controller.dm +++ b/code/datums/movement/automove_controller.dm @@ -1,11 +1,32 @@ /// Implements automove logic; can be overridden on mob procs if you want to vary the logic from the below. /decl/automove_controller - var/completion_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom reaches its target. - var/failure_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom fails to move. + // these could be proper bools but i assumed they were set up this way for a reason, like supporting other return values in the future + var/completion_signal = FALSE // Set to PROCESS_KILL if you want movement to stop processing when the atom reaches its target. + var/failure_signal = FALSE // Set to PROCESS_KILL if you want movement to stop processing when the atom fails to move. var/try_avoid_obstacles = TRUE // Will try to move 90 degrees around an obstacle. -/decl/automove_controller/proc/handle_mover(atom/movable/mover, datum/automove_metadata/metadata) +/decl/automove_controller/proc/check_move_completion(atom/movable/mover, datum/automove_metadata/metadata) + // Null target means abandon pathing, regardless of return signals. + var/atom/target = mover.get_automove_target(metadata) + if(!istype(target)) + return TRUE + // Cease automovement if we're already at the target. + var/acceptable_move_dist = isnull(metadata?.acceptable_distance) ? mover.get_acceptable_automove_distance_from_target() : metadata.acceptable_distance + var/current_distance = get_dist(mover, target) + if(metadata?.avoid_target) + return current_distance >= acceptable_move_dist + else + if(get_turf(mover) == get_turf(target)) + return TRUE + if(ismovable(target) && (target.density && mover.density) && mover.Adjacent(target)) + return TRUE + if(current_distance <= acceptable_move_dist) + return TRUE + return FALSE + +/// Return PROCESS_KILL to terminate automovement. Will return completion_signal when the atom reaches its target and failure_signal if it fails to move. +/decl/automove_controller/proc/handle_mover(atom/movable/mover, datum/automove_metadata/metadata) // Cease automovement if we got an invalid mover.. if(!istype(mover)) return PROCESS_KILL @@ -19,48 +40,60 @@ if(ismob(mover)) var/mob/mover_mob = mover if(mover_mob.moving) - return TRUE + return - // Cease automovement if we're already at the target. - var/avoid_target = metadata?.avoid_target - if(!avoid_target && (get_turf(mover) == get_turf(target) || (ismovable(target) && mover.Adjacent(target)))) + if(check_move_completion(mover, metadata)) mover.finished_automove() return completion_signal - // Cease movement if we're close enough to the target. - var/acceptable_move_dist = isnull(metadata?.acceptable_distance) ? mover.get_acceptable_automove_distance_from_target() : metadata.acceptable_distance - if(avoid_target ? (get_dist(mover, target) >= acceptable_move_dist) : (get_dist(mover, target) <= acceptable_move_dist)) - mover.finished_automove() - return completion_signal + // Skip automovement if we aren't allowed to move yet. + // This is for checks that are expected to fail sometimes (movedelay, incapacitation, etc), so we don't send the failure signal when this happens. + if(!mover.can_do_automated_move(metadata?.move_delay)) + return - // Cease automovement if we failed to move a turf. - if(mover.can_do_automated_move(metadata?.move_delay)) - if(avoid_target) - target = get_edge_target_turf(target, get_dir(target, mover)) - - // Note for future coders: SelfMove() only confirms if a handler handled the move, not if the atom moved. - var/old_loc = mover.loc + var/avoid_target = metadata?.avoid_target + if(avoid_target) + target = get_edge_target_turf(target, get_dir(target, mover)) - // Try to move directly. - var/target_dir = get_dir(mover, target) - if(!target_dir) - if(avoid_target) - target_dir = pick(global.cardinal) - else - return TRUE // no idea how we would get into this position + // Note for future coders: SelfMove() only confirms if a handler handled the move, not if the atom moved. + var/old_loc = mover.loc - if(mover.SelfMove(target_dir) && (old_loc != mover.loc)) - mover.handle_post_automoved(old_loc) - return (mover.get_automove_target() == mover.loc) // We may have transitioned to the next step in a path. + // Try to move directly. + var/target_dir = get_dir(mover, target) + if(!target_dir) + if(avoid_target) + target_dir = pick(global.cardinal) + else + return // no idea how we would get into this position + var/old_next_move_time = mover.get_next_move_time() // to reset to later, so obstacle bumps don't let us ignore move delay + if(mover.SelfMove(target_dir) && (old_loc != mover.loc)) + mover.handle_post_automoved(old_loc) + // check if we're done, and if so, return the completion signal + if(check_move_completion(mover, metadata)) + mover.finished_automove() + return completion_signal + return // we moved, so we didn't fail, but we also aren't finished yet + else if(try_avoid_obstacles) // Try to move around any obstacle. var/static/list/_alt_dir_rot = list(45, -45) for(var/alt_dir in shuffle(_alt_dir_rot)) - mover.reset_movement_delay() + mover.set_next_move_time(old_next_move_time) if(mover.SelfMove(turn(target_dir, alt_dir)) && (old_loc != mover.loc)) - return TRUE + mover.handle_post_automoved(old_loc) + // check if we're done, and if so, return the completion signal + if(check_move_completion(mover, metadata)) + mover.finished_automove() + return completion_signal + return // see above; we succeeded on the retry but aren't done moving mover.failed_automove() + return failure_signal + +/decl/automove_controller/stop_on_completion + completion_signal = PROCESS_KILL - return failure_signal +/decl/automove_controller/stop_on_fail_or_completion + completion_signal = PROCESS_KILL + failure_signal = PROCESS_KILL \ No newline at end of file diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 628b5880042e..47c5406dd739 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -583,9 +583,16 @@ throwing = null /atom/movable/proc/reset_movement_delay() + set_next_move_time(world.time) + +/atom/movable/proc/get_next_move_time() + var/datum/movement_handler/delay/delay = locate() in movement_handlers + return delay?.next_move + +/atom/movable/proc/set_next_move_time(new_time) var/datum/movement_handler/delay/delay = locate() in movement_handlers - if(istype(delay)) - delay.next_move = world.time + if(delay) + delay.next_move = new_time /atom/movable/get_affecting_weather() var/turf/my_turf = get_turf(src) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 612a4e888b47..9ddb5ed172ad 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1463,10 +1463,15 @@ var/global/const/ACTION_DANGER_ALL = 2 var/decl/butchery_data/butchery_decl = GET_DECL(butchery_data) . = butchery_decl?.meat_name || name -/mob/reset_movement_delay() +// we change the base type of our delay handler... +/mob/get_next_move_time() var/datum/movement_handler/mob/delay/delay = locate() in movement_handlers - if(istype(delay)) - delay.next_move = world.time + return delay?.next_move + +/mob/set_next_move_time(new_time) + var/datum/movement_handler/mob/delay/delay = locate() in movement_handlers + if(delay) + delay.next_move = new_time /mob/proc/do_attack_windup_checking(atom/target) return TRUE diff --git a/code/modules/mob/mob_automove.dm b/code/modules/mob/mob_automove.dm index c74d72c44e97..543bf5dfd30a 100644 --- a/code/modules/mob/mob_automove.dm +++ b/code/modules/mob/mob_automove.dm @@ -62,4 +62,4 @@ // We do some early checking here to avoid doing the same checks repeatedly by calling SelfMove(). /mob/can_do_automated_move(variant_move_delay) - . = MayMove() && !incapacitated() && (!istype(ai) || ai.can_do_automated_move()) + . = ..() && !incapacitated() && (!istype(ai) || ai.can_do_automated_move()) From 04cc751cc37dfdd5ef33aadb518ee28b64b32dcd Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 4 May 2026 15:28:49 -0400 Subject: [PATCH 1262/1331] Fix bee automove --- mods/content/beekeeping/hives/hive_swarm.dm | 41 +++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm index 084ef4461d84..3e00fd4241b3 100644 --- a/mods/content/beekeeping/hives/hive_swarm.dm +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -195,9 +195,9 @@ break return FALSE -/obj/effect/insect_swarm/failed_automove() +/obj/effect/insect_swarm/finished_automove() ..() - stop_automove() + next_work = world.time // we'be a busy bee, check to for new work once you reach your destination return FALSE /obj/effect/insect_swarm/get_automove_target(datum/automove_metadata/metadata) @@ -208,12 +208,15 @@ . = ..() /obj/effect/insect_swarm/can_do_automated_move(variant_move_delay) - return !is_smoked() + return ..() && !is_smoked() /obj/effect/insect_swarm/start_automove(target, movement_type, datum/automove_metadata/metadata) move_target = target . = ..() +/obj/effect/insect_swarm/get_default_automove_controller_type() + return /decl/automove_controller/stop_on_fail_or_completion + /obj/effect/insect_swarm/proc/handle_hive_behavior() var/atom/movable/hive = owner?.holder @@ -230,7 +233,7 @@ stop_automove() return if(!hive_has_swarm() && loc != hive.loc) - start_automove(owner.holder) + start_automove(hive) return do_work() @@ -279,10 +282,6 @@ merge(other_swarm) return -/obj/effect/insect_swarm/DoMove(direction, mob/mover, is_external) - . = ..() - to_world("swarm tried to move: [.]") - /obj/effect/insect_swarm/pollinator var/pollen = 0 @@ -292,26 +291,28 @@ if(world.time < next_work) return + var/atom/movable/hive = owner?.holder + + // Move to move target (hive or flowers) + if(move_target) + if(!(move_target in view(src, 7))) // no longer able to see our move target + stop_automove() + // don't bail early if we just stopped automove, that would introduce stutter as it'd take one tick to decide what to do next + else + // let us automove, don't restart it + return + // Unload pollen into hive. if(pollen) - if(loc == get_turf(owner.holder)) + if(loc == get_turf(hive)) owner.add_reserves(pollen) pollen = 0 next_work = world.time + 5 SECONDS stop_automove() else - start_automove(owner.holder) + start_automove(hive) return - // Move to flowers. - if(move_target) - if(get_turf(move_target) == loc || !(move_target in view(src, 7))) - move_target = null - stop_automove() - else - start_automove(move_target) - return - // Harvest from flowers in our loc. for(var/obj/machinery/portable_atmospherics/hydroponics/flower in loc) if(!flower.pollen) @@ -358,7 +359,7 @@ if(closest_target) start_automove(closest_target) else - start_automove(owner.holder) + start_automove(hive) /obj/effect/insect_swarm/proc/was_smoked(smoke_time = 10 SECONDS) smoked_until = max(smoked_until, world.time + smoke_time) From 510eed2062ab497c4805359be50472f10c224ed0 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 21:21:27 +1000 Subject: [PATCH 1263/1331] Tweaks to beewrite after testing. --- code/controllers/subsystems/initialization/customitems.dm | 2 ++ code/controllers/subsystems/jobs.dm | 2 +- code/modules/hydroponics/trays/tray_process.dm | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/controllers/subsystems/initialization/customitems.dm b/code/controllers/subsystems/initialization/customitems.dm index d571851c618e..d3bdc56698bd 100644 --- a/code/controllers/subsystems/initialization/customitems.dm +++ b/code/controllers/subsystems/initialization/customitems.dm @@ -49,6 +49,8 @@ SUBSYSTEM_DEF(customitems) //gets the relevant list for the key from the listlist if it exists, check to make sure they are meant to have it and then calls the giving function /datum/controller/subsystem/customitems/proc/equip_custom_items(mob/living/human/M) + if(!istype(M) || !M.ckey) + return var/list/key_list = custom_items_by_ckey[M.ckey] if(!length(key_list)) return diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 886d58c7a9c1..63bc4995beef 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -565,7 +565,7 @@ SUBSYSTEM_DEF(jobs) job.post_equip_job_title(H, alt_title || job_title) - H.client.show_location_blurb(30) + H.client?.show_location_blurb(30) return H diff --git a/code/modules/hydroponics/trays/tray_process.dm b/code/modules/hydroponics/trays/tray_process.dm index 2b835ea3eb69..8491df96a2cb 100644 --- a/code/modules/hydroponics/trays/tray_process.dm +++ b/code/modules/hydroponics/trays/tray_process.dm @@ -67,7 +67,6 @@ if(pollen < MAX_POLLEN_PER_FLOWER) pollen += seed?.produces_pollen * POLLEN_PRODUCTION_MULT - to_world("\ref[src] has pollen [pollen] ([seed?.produces_pollen] * [POLLEN_PRODUCTION_MULT])") // Maintain tray nutrient and water levels. if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS) && seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0 && nutrilevel > 0 && prob(25)) From 00ff4700bc2efcfd7bf478e469149ec09cc0dab9 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 21:33:43 +1000 Subject: [PATCH 1264/1331] Tweaking bee pain and venom amount for stinging. --- code/modules/mob/living/human/human.dm | 8 +++---- code/modules/mob/living/living.dm | 2 +- code/modules/mob/new_player/new_player.dm | 5 +++- .../beekeeping/hives/hive_extension.dm | 2 +- mods/content/beekeeping/hives/hive_swarm.dm | 3 ++- .../hives/insect_species/_insects.dm | 23 ++++++++++++++----- .../insect_species/insects_pollinators.dm | 4 ++-- mods/content/beekeeping/items.dm | 2 +- mods/content/beekeeping/materials.dm | 6 +++-- mods/content/beekeeping/trading.dm | 2 +- 10 files changed, 37 insertions(+), 20 deletions(-) diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 4e60c2cc91d8..5369b1671138 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -688,11 +688,11 @@ if(!affecting) to_chat(user, SPAN_WARNING("\The [src] is missing that limb.")) - return 0 + return FALSE if(BP_IS_PROSTHETIC(affecting)) to_chat(user, SPAN_WARNING("That limb is prosthetic.")) - return 0 + return FALSE . = CAN_INJECT for(var/slot in list(slot_head_str, slot_wear_mask_str, slot_wear_suit_str, slot_w_uniform_str, slot_gloves_str, slot_shoes_str)) @@ -701,8 +701,8 @@ if(istype(C, /obj/item/clothing/suit/space)) . = INJECTION_PORT //it was going to block us, but it's a space suit so it doesn't because it has some kind of port else - to_chat(user, "There is no exposed flesh or thin material on [src]'s [affecting.name] to inject into.") - return 0 + to_chat(user, SPAN_WARNING("There is no exposed flesh or thin material on [src]'s [affecting.name] to inject into.")) + return FALSE /mob/living/human/print_flavor_text(var/shrink = 1) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index cc265798515b..14727789f105 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -271,7 +271,7 @@ default behaviour is: gear_tree |= storage_contents /mob/living/proc/can_inject(var/mob/user, var/target_zone) - return 1 + return TRUE /mob/living/proc/get_organ_target() var/mob/shooter = src diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 0b7f6c091e9f..56fdd28e45bd 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -228,7 +228,10 @@ INITIALIZE_IMMEDIATE(/mob/new_player) if(!character) return 0 - character = SSjobs.equip_job_title(character, job.title, 1) //equips the human + character = SSjobs.equip_job_title(character, job.title, 1) //equips the human + if(!character) + return 0 + SScustomitems.equip_custom_items(character) if(job.do_spawn_special(character, src, TRUE)) //This replaces the AI spawn logic with a proc stub. Refer to silicon.dm for the spawn logic. diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm index 40c44df25054..5e91d1c1c232 100644 --- a/mods/content/beekeeping/hives/hive_extension.dm +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -210,7 +210,7 @@ for(var/obj/effect/insect_swarm/swarm as anything in swarms) . += swarm.swarm_intensity -/datum/extension/insect_hive/proc/smoked_by(mob/user, atom/source, smoke_time = 10 SECONDS) +/datum/extension/insect_hive/proc/smoked_by(mob/user, atom/source, smoke_time = 1 MINUTE) smoked_until = max(smoked_until, world.time + smoke_time) // this is a little weird due to telekinetic bee smoking but so it goes for(var/obj/effect/insect_swarm/swarm as anything in swarms) diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm index 3e00fd4241b3..c05a7d9972d6 100644 --- a/mods/content/beekeeping/hives/hive_swarm.dm +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -361,9 +361,10 @@ else start_automove(hive) -/obj/effect/insect_swarm/proc/was_smoked(smoke_time = 10 SECONDS) +/obj/effect/insect_swarm/proc/was_smoked(smoke_time = 1 MINUTE) smoked_until = max(smoked_until, world.time + smoke_time) swarm_agitation = round(swarm_agitation * 0.75) + update_icon() addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon), TRUE), smoke_time, (TIMER_UNIQUE|TIMER_OVERRIDE)) /obj/effect/insect_swarm/proc/is_smoked() diff --git a/mods/content/beekeeping/hives/insect_species/_insects.dm b/mods/content/beekeeping/hives/insect_species/_insects.dm index b6902a51a0ab..d05460b9a5ef 100644 --- a/mods/content/beekeeping/hives/insect_species/_insects.dm +++ b/mods/content/beekeeping/hives/insect_species/_insects.dm @@ -27,6 +27,8 @@ // Venom delivered by swarms whens stinging a victim. var/sting_reagent var/sting_amount + var/per_sting_reagents = 0.1 + var/per_sting_pain = 1 /decl/insect_species/Initialize() if(produce_material) @@ -110,7 +112,7 @@ if(!swarm.is_agitated() && !prob(max(1, round(swarm.swarm_intensity/4)))) return FALSE var/base_sting_chance = (sting_amount * clamp(round(swarm.swarm_intensity/10), 1, 10)) - var/sting_mult = swarm.is_agitated() ? max(base_sting_chance, 65) : base_sting_chance + var/sting_mult = swarm.is_agitated() ? max(base_sting_chance, 15) : base_sting_chance for(var/mob/living/victim in loc) if(!victim.simulated || victim.stat || victim.current_posture?.prone) continue @@ -118,11 +120,20 @@ var/obj/item/organ/external/affecting = victim.get_organ(pick(global.all_limb_tags)) if(!affecting || BP_IS_PROSTHETIC(affecting) || BP_IS_CRYSTAL(affecting)) continue - if(injected_reagents && victim.can_inject(victim, affecting.organ_tag)) - to_chat(victim, SPAN_DANGER("\A [swarm] stings you [sting_mult <= sting_amount * 2 ? "" : "multiple times"] on your [affecting.name]!")) - injected_reagents.add_reagent(sting_reagent, sting_mult) - affecting.add_pain(sting_mult) - . = TRUE + if(!injected_reagents || !victim.can_inject(null, affecting.organ_tag)) + continue + + to_chat(victim, SPAN_DANGER("\A [swarm] stings you [sting_mult <= sting_amount * 2 ? "" : "multiple times"] on your [affecting.name]!")) + + var/sting_venom = (per_sting_reagents * sting_mult) - REAGENT_VOLUME(injected_reagents, sting_reagent) + if(sting_venom > 0) + injected_reagents.add_reagent(sting_reagent, sting_venom) + + var/sting_pain = (per_sting_pain * sting_mult) - victim.getHalLoss() + if(sting_pain > 0) + affecting.add_pain(sting_pain) + + . = TRUE /decl/insect_species/proc/can_spawn_in_flora(var/obj/structure/flora) diff --git a/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm index 83aeb86f8462..460ca2ef16b8 100644 --- a/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm +++ b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm @@ -10,7 +10,7 @@ swarm_color = COLOR_GOLD swarm_type = /obj/effect/insect_swarm/pollinator sting_reagent = /decl/material/liquid/bee_venom - sting_amount = 1 + sting_amount = 0.2 produce_reagents = list(/decl/material/liquid/nutriment/honey = 1) produce_material = /decl/material/solid/organic/wax @@ -22,7 +22,7 @@ swarm_desc = "A swarm of humming wasps." insect_desc = "A solitary wasp." sting_reagent = /decl/material/liquid/cyanide - sting_amount = 5 + sting_amount = 1 swarm_color = COLOR_BRONZE swarm_type = /obj/effect/insect_swarm/pollinator // tarantula hunter... */ \ No newline at end of file diff --git a/mods/content/beekeeping/items.dm b/mods/content/beekeeping/items.dm index f520e5085eab..0c7774e6f1f6 100644 --- a/mods/content/beekeeping/items.dm +++ b/mods/content/beekeeping/items.dm @@ -20,7 +20,7 @@ if(!smoked && isturf(A)) for(var/obj/effect/insect_swarm/swarm in A) - swarm.was_smoked() + swarm.was_smoked(smoke_time = 1 MINUTE) smoked = TRUE if(smoked) diff --git a/mods/content/beekeeping/materials.dm b/mods/content/beekeeping/materials.dm index 496d5f9b394c..d5fab8c75d27 100644 --- a/mods/content/beekeeping/materials.dm +++ b/mods/content/beekeeping/materials.dm @@ -13,8 +13,10 @@ metabolism = REM * 0.25 exoplanet_rarity_plant = MAT_RARITY_UNCOMMON exoplanet_rarity_gas = MAT_RARITY_EXOTIC + var/pain_mult = 10 + var/pain_threshold = 100 /decl/material/liquid/bee_venom/affect_blood(mob/living/M, removed, datum/reagents/holder) . = ..() - if(istype(M)) - M.adjustHalLoss(max(1, ceil(removed * 10))) + if(istype(M) && M.getHalLoss() < pain_threshold) + M.adjustHalLoss(max(1, ceil(removed * pain_mult))) diff --git a/mods/content/beekeeping/trading.dm b/mods/content/beekeeping/trading.dm index c63d01f412c2..5e8a8dc18b1e 100644 --- a/mods/content/beekeeping/trading.dm +++ b/mods/content/beekeeping/trading.dm @@ -1,6 +1,6 @@ /datum/trader/trading_beacon/manufacturing/New() LAZYSET(possible_trading_items, /obj/item/bee_pack, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/smoker, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/smoker, TRADER_THIS_TYPE) LAZYSET(possible_trading_items, /obj/item/hive_frame/crafted, TRADER_THIS_TYPE) LAZYSET(possible_trading_items, /obj/item/stack/material/plank/mapped/wood/ten, TRADER_THIS_TYPE) ..() From e8b3c908b9eb26eb8f0c6dfe1f163afc0a15c06c Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Fri, 15 May 2026 14:30:35 +1000 Subject: [PATCH 1265/1331] Adding a subsystem to handle clickdragging as mouse events are somewhat unreliable for fine-grained stuff. Adding cursor updates to mousedrag procs in preparation for melee attack PR. --- code/_onclick/MouseDrag.dm | 39 -------- code/_onclick/mouse_drag.dm | 91 +++++++++++++++++++ code/controllers/subsystems/clickdrag.dm | 54 +++++++++++ code/game/objects/items/__item.dm | 24 ++++- code/modules/client/client_procs.dm | 19 ++-- .../clothing/spacesuits/rig/modules/combat.dm | 9 ++ code/modules/mechs/_mech.dm | 31 +++++++ code/modules/mechs/equipment/_equipment.dm | 9 -- .../mechs/equipment/combat_projectile.dm | 53 ----------- .../modules/mechs/equipment/mounted_system.dm | 9 ++ code/modules/mechs/mech_interaction.dm | 46 ---------- code/modules/mob/living/inventory.dm | 18 ++-- code/modules/mob/mob.dm | 4 + code/modules/projectiles/{gun.dm => _gun.dm} | 69 +------------- code/modules/projectiles/autofire.dm | 37 ++++++++ .../projectiles/guns/energy/special.dm | 2 + .../projectiles/guns/launcher/bows/_bow.dm | 45 --------- .../guns/launcher/bows/bow_drawing.dm | 57 ++++++++++++ .../guns/launcher/bows/bow_interaction.dm | 5 +- .../projectiles/guns/launcher/foam_gun.dm | 2 +- .../projectiles/guns/projectile/automatic.dm | 10 +- mods/content/sealant_gun/sealant_gun.dm | 1 + nebula.dme | 6 +- 23 files changed, 355 insertions(+), 285 deletions(-) delete mode 100644 code/_onclick/MouseDrag.dm create mode 100644 code/_onclick/mouse_drag.dm create mode 100644 code/controllers/subsystems/clickdrag.dm rename code/modules/projectiles/{gun.dm => _gun.dm} (92%) create mode 100644 code/modules/projectiles/autofire.dm diff --git a/code/_onclick/MouseDrag.dm b/code/_onclick/MouseDrag.dm deleted file mode 100644 index 6f7e0ee7c3ea..000000000000 --- a/code/_onclick/MouseDrag.dm +++ /dev/null @@ -1,39 +0,0 @@ -//If we intercept it return true else return false -/atom/proc/RelayMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - return FALSE - -/atom/proc/RelayMouseDown(atom/object, location, control, params, mob/user) - return FALSE - -/atom/proc/RelayMouseUp(atom/object, location, control, params, mob/user) - return FALSE - -/mob/proc/OnMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params) - if(loc) - var/atom/A = loc - if(A.RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, src)) - return - - var/obj/item/gun/gun = get_active_held_item() - if(check_intent(I_FLAG_HARM) && istype(over_object) && (isturf(over_object) || isturf(over_object.loc)) && !incapacitated() && istype(gun)) - gun.set_autofire(over_object, src) - -/mob/proc/OnMouseDown(atom/object, location, control, params) - if(loc) - var/atom/A = loc - if(A.RelayMouseDown(object, location, control, params, src)) - return - - var/obj/item/gun/gun = get_active_held_item() - if(check_intent(I_FLAG_HARM) && istype(object) && (isturf(object) || isturf(object.loc)) && !incapacitated() && istype(gun)) - gun.set_autofire(object, src) - -/mob/proc/OnMouseUp(atom/object, location, control, params) - if(loc) - var/atom/A = loc - if(A.RelayMouseUp(object, location, control, params, src)) - return - - var/obj/item/gun/gun = get_active_held_item() - if(istype(gun)) - gun.clear_autofire() diff --git a/code/_onclick/mouse_drag.dm b/code/_onclick/mouse_drag.dm new file mode 100644 index 000000000000..06a79c5a596a --- /dev/null +++ b/code/_onclick/mouse_drag.dm @@ -0,0 +1,91 @@ +/atom/proc/relayed_mouse_down(mob/user, object, location, control, params) + return null + +/atom/proc/relayed_mouse_held(mob/user, atom/target) + return null + +/atom/proc/relayed_mouse_up(mob/user, atom/target) + return null + +/mob/proc/on_mouse_down(object, location, control, params) + + // We are mouse down outside the map or on a screen element, assume it is not relevant. + if(!isatom(object) || istype(object, /obj/screen)) + return FALSE + + // Do not do this for things that are not in the world. + var/atom/atom = object + if(!isturf(atom) && !isturf(atom.loc)) + return FALSE + + // Debounce, we might already be holding. + if(_is_holding_mouse) + return FALSE + + // Ignore right click and middle click currently. + // Might be worth handling these in the future. + var/list/modifiers = params2list(params) + if(modifiers["middle"] || modifiers["right"]) + return FALSE + + // Keep track of when we started holding the mouse down so that we can check if we hold it long enough to start the drag behavior. + _started_mouse_down = world.time + + // Might be inside an exosuit or such that has its own handling for these inputs. + . = loc?.relayed_mouse_down(src, object, location, control, params) + if(isnull(.)) + + // Handle our actual 'drag beginning' logic. + var/obj/item/held = get_active_held_item() + . = istype(held) && held.wielder_mouse_drag_down(src, object, location, control, params) + + if(.) + update_mouse_pointer() + SSclickdrag.active_wielders[src] = TRUE + _is_holding_mouse = TRUE + +/mob/proc/on_mouse_held() + + if(!_is_holding_mouse) + return FALSE + + // Grace period before we start holding (rather than a single click) + if(world.time < _started_mouse_down + MOUSE_DRAG_DELAY) + return TRUE + + var/atom/mouse_over = _last_mouse_over_atom?.resolve() + if(QDELETED(mouse_over) || !istype(mouse_over)) + mouse_over = null + + // Might be inside an exosuit or such that has its own handling for these inputs. + . = loc?.relayed_mouse_held(src, mouse_over) + if(isnull(.)) + var/obj/item/held = get_active_held_item() + . = istype(held) && held.wielder_mouse_drag_held(src, mouse_over) + + if(.) + update_mouse_pointer() + set_dir(get_dir(src, mouse_over)) + else + on_mouse_up() + +/mob/proc/on_mouse_up(remove_from_processing = TRUE) + + if(!_is_holding_mouse) + return FALSE + + // Don't block the follow-up Click() if this wasn't an 'official' drag. + if(world.time >= _started_mouse_down + MOUSE_DRAG_DELAY) + var/atom/mouse_over = _last_mouse_over_atom?.resolve() + if(QDELETED(mouse_over) || !istype(mouse_over)) + mouse_over = null + . = loc?.relayed_mouse_up(src, mouse_over) + if(isnull(.)) + var/obj/item/held = get_active_held_item() + . = istype(held) && held.wielder_mouse_drag_up(src, mouse_over) + + update_mouse_pointer() + _is_holding_mouse = FALSE + SSclickdrag.active_wielders -= src + if(remove_from_processing && length(SSclickdrag.processing_wielders)) + SSclickdrag.processing_wielders -= src diff --git a/code/controllers/subsystems/clickdrag.dm b/code/controllers/subsystems/clickdrag.dm new file mode 100644 index 000000000000..97164a2fa64f --- /dev/null +++ b/code/controllers/subsystems/clickdrag.dm @@ -0,0 +1,54 @@ +SUBSYSTEM_DEF(clickdrag) + name = "Clickdrag" + wait = 1 + flags = SS_TICKER | SS_NO_INIT + + var/tmp/list/active_wielders = list() + var/tmp/active_wielders_copied_yet = FALSE + var/tmp/list/processing_wielders + +/datum/controller/subsystem/clickdrag/stat_entry() + ..("W:[active_wielders.len]") + +/datum/controller/subsystem/clickdrag/fire(resumed = 0) + + if(!resumed) + active_wielders_copied_yet = FALSE + + if(!active_wielders_copied_yet) + active_wielders_copied_yet = TRUE + processing_wielders = active_wielders.Copy() + + var/mob/wielder + var/i = 0 + while(i < processing_wielders.len) + i++ + wielder = processing_wielders[i] + if(!wielder.on_mouse_held()) + wielder.on_mouse_up(remove_from_processing = FALSE) // we will do this via our list iteration anyway + if (MC_TICK_CHECK) + processing_wielders.Cut(1, i+1) + return + processing_wielders.Cut() + +/client + // (BOOL) Flag for whether or not the next Click() should be blocked - Click() is called immediately after MouseUp() which isn't desirable. + VAR_PRIVATE/tmp/_block_next_click = FALSE + +/mob + // (DATUM) Tracker for clickdrag subsystem, + VAR_PRIVATE/tmp/weakref/_last_mouse_over_atom + // (BOOL) Flag for keeping track of if we're already processing or not. + VAR_PRIVATE/tmp/_is_holding_mouse = FALSE + // (INT) Time that we started holding the mouse down. + VAR_PRIVATE/tmp/_started_mouse_down = 0 + // (FLOAT) Delay before a hold is considered a hold rather than a single click. + VAR_PRIVATE/const/MOUSE_DRAG_DELAY = 0.25 SECONDS + +/client/MouseEntered(object,location,control,params) + UNLINT(mob?._last_mouse_over_atom = weakref(object)) + . = ..() + +/client/MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) + UNLINT(mob?._last_mouse_over_atom = weakref(over_object)) + . = ..() diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 1bed5d4cf0f6..dfc848c8b322 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -1353,4 +1353,26 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. qdel(src) /obj/item/proc/pick_attack_verb() - return DEFAULTPICK(attack_verb, attack_verb) || "attacked" // if it's not a list, return itself or just "attacked" \ No newline at end of file + return DEFAULTPICK(attack_verb, attack_verb) || "attacked" // if it's not a list, return itself or just "attacked" + +/obj/item/equipped(mob/user, slot) + if(user?.get_active_held_item() == src) + user.on_mouse_up() + . = ..() + +/obj/item/dropped(mob/user) + if(user?.get_active_held_item() == src) + user.on_mouse_up() + . = ..() + +// Called on initial mouse down event from wielding mob. Return TRUE to begin processing every 1ds. +/obj/item/proc/wielder_mouse_drag_down(mob/user, object, location, control, params) + return FALSE + +// Called every 1ds while mouse is down with an item that returned TRUE to wielder_mouse_drag_down(). Return FALSE to end processing. +/obj/item/proc/wielder_mouse_drag_held(mob/user, atom/target) + return FALSE + +// Called on mouse up event from wielding mob. +/obj/item/proc/wielder_mouse_drag_up(mob/user, atom/target) + return FALSE diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 5a4b8a075400..5631eb2ae6b1 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -430,23 +430,16 @@ var/global/list/localhost_addresses = list( if(world.byond_version >= 511 && byond_version >= 511 && client_fps >= CLIENT_MIN_FPS && client_fps <= CLIENT_MAX_FPS) vars["fps"] = client_fps -/client/MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) - . = ..() - var/mob/living/M = mob - if(istype(M)) - M.OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) - /client/MouseUp(object, location, control, params) . = ..() - var/mob/living/M = mob - if(istype(M)) - M.OnMouseUp(object, location, control, params) + if(mob?.on_mouse_up()) + _block_next_click = TRUE /client/MouseDown(object, location, control, params) . = ..() var/mob/living/M = mob if(istype(M) && !M.in_throw_mode) - M.OnMouseDown(object, location, control, params) + M.on_mouse_down(object, location, control, params) /client/verb/SetWindowIconSize(var/val as num|text) set hidden = 1 @@ -599,6 +592,12 @@ var/global/const/MAX_VIEW = 41 winset(src, "mainwindow.split", "splitter=[pct]") /client/Click(atom/A) + + // Mouse drag safeguard against a trailing Click() called after MouseUp(). + if(_block_next_click) + _block_next_click = FALSE + return + if(!user_acted(src)) return diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 6ba6b837d81c..c8412202284e 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -237,6 +237,15 @@ gun.Fire(target,holder.wearer) return 1 +/obj/item/rig_module/mounted/wielder_mouse_drag_held(mob/user, atom/target) + return istype(gun) ? gun.wielder_mouse_drag_held(user, target) : ..() + +/obj/item/rig_module/mounted/wielder_mouse_drag_up(mob/user, atom/target) + return istype(gun) ? gun.wielder_mouse_drag_up(user, target) : ..() + +/obj/item/rig_module/mounted/wielder_mouse_drag_down(mob/user, object, location, control, params) + return istype(gun) ? gun.wielder_mouse_drag_down(user, object, location, control, params) : ..() + /obj/item/rig_module/mounted/lcannon name = "mounted laser cannon" diff --git a/code/modules/mechs/_mech.dm b/code/modules/mechs/_mech.dm index 957fe6f236a4..c49b2db28d03 100644 --- a/code/modules/mechs/_mech.dm +++ b/code/modules/mechs/_mech.dm @@ -260,3 +260,34 @@ if(current_user) return FALSE return ..() + +// Handling for auto-fire mechanic +/mob/living/exosuit/mob_can_autofire(obj/item/gun/autofiring, atom/autofiring_at) + if(!(autofiring in selected_system)) // Make sure the gun is still selected. + return FALSE + return ..() + +/mob/living/exosuit/proc/relayed_pilot_check(mob/user) + if(!user || incapacitated() || user.incapacitated()) + return FALSE + if(!(user in pilots) && user != src) + return FALSE + if(!selected_system) + return FALSE + return TRUE + +// TODO: make mechs use inventory slots so we can just call on_mouse_foo(). +/mob/living/exosuit/relayed_mouse_down(mob/user, object, location, control, params) + if(!relayed_pilot_check(user)) + return ..() + . = selected_system.wielder_mouse_drag_down(src, object, location, control, params) + +/mob/living/exosuit/relayed_mouse_held(mob/user, atom/target) + if(!relayed_pilot_check(user)) + return ..() + return selected_system.wielder_mouse_drag_held(src, target) + +/mob/living/exosuit/relayed_mouse_up(mob/user, atom/target) + if(!relayed_pilot_check(user)) + return ..() + return selected_system.wielder_mouse_drag_up(src, target) diff --git a/code/modules/mechs/equipment/_equipment.dm b/code/modules/mechs/equipment/_equipment.dm index c98994742c58..7dde002884e4 100644 --- a/code/modules/mechs/equipment/_equipment.dm +++ b/code/modules/mechs/equipment/_equipment.dm @@ -72,15 +72,6 @@ /obj/item/mech_equipment/proc/get_effective_obj() return src -/obj/item/mech_equipment/proc/MouseDragInteraction() - return 0 - -/obj/item/mech_equipment/proc/MouseDownInteraction() - return 0 - -/obj/item/mech_equipment/proc/MouseUpInteraction() - return 0 - /obj/item/mech_equipment/mob_can_unequip(mob/user, slot, disable_warning = FALSE, dropping = FALSE) . = ..() if(. && owner) diff --git a/code/modules/mechs/equipment/combat_projectile.dm b/code/modules/mechs/equipment/combat_projectile.dm index a717954a893f..fa562fcfe1ae 100644 --- a/code/modules/mechs/equipment/combat_projectile.dm +++ b/code/modules/mechs/equipment/combat_projectile.dm @@ -109,56 +109,3 @@ material = /decl/material/solid/metal/steel ammo_type = /obj/item/ammo_casing/rifle max_ammo = 300 - -// Handling for auto-fire mechanic -/mob/living/exosuit/can_autofire(obj/item/gun/autofiring, atom/autofiring_at) - if(autofiring.autofiring_by != src) - return FALSE - var/client/C = current_user ? current_user.client : client - - if(!C || !C.mob || C.mob.incapacitated()) - return FALSE - - if(!(autofiring_at in view(C.view, src))) - return FALSE - if(!(get_dir(src, autofiring_at) & dir)) - return FALSE - if(!(autofiring in selected_system)) // Make sure the gun is still selected. - return FALSE - return TRUE - -/obj/item/mech_equipment/mounted_system/projectile/MouseDownInteraction(atom/object, location, control, params, mob/user) - var/obj/item/gun/gun = holding - if(istype(object) && (isturf(object) || isturf(object.loc)) && istype(gun)) - if(user != src) - if(!user.incapacitated()) - gun.set_autofire(object, owner, FALSE) // Passed gun-firer is still the exosuit since all checks need to be done on the suit. - owner.current_user = user - else - if(!owner.incapacitated()) - gun.set_autofire(object, owner, FALSE) - owner.current_user = null - -/obj/item/mech_equipment/mounted_system/projectile/MouseUpInteraction(atom/object, location, control, params, mob/user) - var/obj/item/gun/gun = holding - if(istype(gun)) - gun.clear_autofire() - if(owner) // In case the owning exosuit has been gibbed etc. - owner.current_user = null - -/obj/item/mech_equipment/mounted_system/projectile/MouseDragInteraction(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - var/obj/item/gun/gun = holding - if(!owner) - gun?.clear_autofire() - return - if(!istype(gun)) - owner?.current_user = null - return - if(istype(over_object) && (isturf(over_object) || isturf(over_object.loc))) - if(user.incapacitated() || (user != owner && user != owner.current_user)) - gun.clear_autofire() - return - gun.set_autofire(over_object, owner, FALSE) - return - - gun.clear_autofire() diff --git a/code/modules/mechs/equipment/mounted_system.dm b/code/modules/mechs/equipment/mounted_system.dm index d8a62f70a8fb..5bf0cce9c3bb 100644 --- a/code/modules/mechs/equipment/mounted_system.dm +++ b/code/modules/mechs/equipment/mounted_system.dm @@ -39,3 +39,12 @@ /obj/item/mech_equipment/mounted_system/get_hardpoint_maptext() return (holding ? holding.get_hardpoint_maptext() : null) + +/obj/item/mech_equipment/mounted_system/wielder_mouse_drag_held(mob/user, atom/target) + return (holding ? holding.wielder_mouse_drag_held(user, target) : ..()) + +/obj/item/mech_equipment/mounted_system/wielder_mouse_drag_up(mob/user, atom/target) + return (holding ? holding.wielder_mouse_drag_up(user, target) : ..()) + +/obj/item/mech_equipment/mounted_system/wielder_mouse_drag_down(mob/user, object, location, control, params) + return (holding ? holding.wielder_mouse_drag_down(user, object, location, control, params) : ..()) diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm index eb900899d021..abc0708cb39e 100644 --- a/code/modules/mechs/mech_interaction.dm +++ b/code/modules/mechs/mech_interaction.dm @@ -9,52 +9,6 @@ return TRUE . = ..() -/mob/living/exosuit/RelayMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - if(user && (user in pilots) && user.loc == src) - return OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, user) - return ..() - -/mob/living/exosuit/OnMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - if(!user || incapacitated() || user.incapacitated()) - return FALSE - - if(!(user in pilots) && user != src) - return FALSE - - //This is handled at active module level really, it is the one who has to know if it's supposed to act - if(selected_system) - return selected_system.MouseDragInteraction(src_object, over_object, src_location, over_location, src_control, over_control, params, user) - -/mob/living/exosuit/RelayMouseDown(atom/object, location, control, params, mob/user) - if(user && (user in pilots) && user.loc == src) - return OnMouseDown(object, location, control, params, user) - return ..() - -/mob/living/exosuit/OnMouseDown(atom/object, location, control, params, mob/user) - if(!user || incapacitated() || user.incapacitated()) - return FALSE - - if(!(user in pilots) && user != src) - return FALSE - - if(selected_system) - return selected_system.MouseDownInteraction(object, location, control, params, user) - -/mob/living/exosuit/RelayMouseUp(atom/object, location, control, params, mob/user) - if(user && (user in pilots) && user.loc == src) - return OnMouseUp(object, location, control, params, user) - return ..() - -/mob/living/exosuit/OnMouseUp(atom/object, location, control, params, mob/user) - if(!user || incapacitated() || user.incapacitated()) - return FALSE - - if(!(user in pilots) && user != src) - return FALSE - - if(selected_system) - return selected_system.MouseUpInteraction(object, location, control, params, user) - /datum/click_handler/default/mech/OnClick(var/atom/A, var/params) var/mob/living/exosuit/E = user.loc if(!istype(E)) diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index c4cc217a30e2..675b6eb423b1 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -67,13 +67,17 @@ /mob/living/select_held_item_slot(var/slot) . = ..() var/last_slot = get_active_held_item_slot() - if(slot != last_slot && (slot in get_held_item_slots())) - _held_item_slot_selected = slot - if(istype(hud_used)) - hud_used.update_hand_elements() - var/obj/item/I = get_active_held_item() - if(istype(I)) - I.on_active_hand() + if(slot == last_slot) + return + on_mouse_up() + if(!(slot in get_held_item_slots())) + return + _held_item_slot_selected = slot + if(istype(hud_used)) + hud_used.update_hand_elements() + var/obj/item/I = get_active_held_item() + if(istype(I)) + I.on_active_hand() // Defer proc for the sake of delimbing root limbs with multiple graspers (serpentid) /mob/living/proc/queue_hand_rebuild() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 612a4e888b47..85a7325d9f32 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1613,3 +1613,7 @@ var/global/const/ACTION_DANGER_ALL = 2 /mob/proc/get_background_datum(cat_type) return global.using_map.default_background_info[cat_type] + +// Check if this mob can full-auto fire a gun at a target. +/mob/proc/mob_can_autofire(obj/item/gun/gun, atom/target) + return TRUE // TODO: dexterity check? That will be handled by the item itself probably. diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/_gun.dm similarity index 92% rename from code/modules/projectiles/gun.dm rename to code/modules/projectiles/_gun.dm index 3e61a8a50011..205e1136bcd7 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/_gun.dm @@ -23,9 +23,10 @@ else settings[propname] = propvalue -/datum/firemode/proc/apply_to(obj/item/gun/gun) +/datum/firemode/proc/apply_firemode_to(obj/item/gun/gun) for(var/propname in settings) - gun.vars[propname] = settings[propname] + if(propname in gun.vars) + gun.vars[propname] = settings[propname] //Parent gun type. Guns are weapons that can be aimed at mobs and act over a distance /obj/item/gun @@ -83,11 +84,6 @@ var/has_safety = TRUE var/safety_icon //overlay to apply to gun based on safety state, if any - var/autofire_enabled = FALSE - var/atom/autofiring_at - var/mob/autofiring_by - var/autofiring_timer - // Spam prevention var/last_fire_message_type var/last_fire_message_time @@ -104,58 +100,12 @@ if(scope_zoom) verbs += /obj/item/gun/proc/scope -/obj/item/gun/Destroy() - // autofire timer is automatically cleaned up - autofiring_at = null - autofiring_by = null - . = ..() - /obj/item/gun/is_held_twohanded(mob/living/wielder) return one_hand_penalty > 0 && ..() /obj/item/gun/preserve_in_cryopod(var/obj/machinery/cryopod/pod) return TRUE -/obj/item/gun/proc/set_autofire(var/atom/fire_at, var/mob/fire_by, var/autoturn = TRUE) - . = TRUE - if(!istype(fire_at) || !istype(fire_by)) - . = FALSE - else if(QDELETED(fire_at) || QDELETED(fire_by) || QDELETED(src)) - . = FALSE - else if(!autofire_enabled) - . = FALSE - if(.) - autofiring_at = fire_at - autofiring_by = fire_by - if(!autofiring_timer) - autofiring_timer = addtimer(CALLBACK(src, PROC_REF(handle_autofire), autoturn), burst_delay, (TIMER_STOPPABLE | TIMER_LOOP | TIMER_UNIQUE | TIMER_OVERRIDE)) - else - clear_autofire() - -/obj/item/gun/proc/clear_autofire() - autofiring_at = null - autofiring_by = null - if(autofiring_timer) - deltimer(autofiring_timer) - autofiring_timer = null - -/obj/item/gun/proc/handle_autofire(autoturn) - set waitfor = FALSE - . = TRUE - if(QDELETED(autofiring_at) || QDELETED(autofiring_by)) - . = FALSE - else if(!autofiring_by.can_autofire(src, autofiring_at)) - . = FALSE - if(!.) - clear_autofire() - else if(can_autofire()) - try_autofire(autoturn) - -/obj/item/gun/proc/try_autofire(autoturn) - if(autoturn) - autofiring_by.set_dir(get_dir(src, autofiring_at)) - Fire(autofiring_at, autofiring_by, null, (get_dist(autofiring_at, autofiring_by) <= 1), FALSE, FALSE) - /obj/item/gun/update_twohanding() if(one_hand_penalty) update_icon() // In case item_state is set somewhere else. @@ -269,7 +219,6 @@ check_accidents(user) update_icon() . = ..() - clear_autofire() /obj/item/gun/proc/Fire(atom/target, atom/movable/firer, clickparams, pointblank = FALSE, reflex = FALSE, set_click_cooldown = TRUE, target_zone = BP_CHEST) if(!firer || !target) @@ -660,7 +609,7 @@ sel_mode = next_mode var/datum/firemode/new_mode = firemodes[sel_mode] - new_mode.apply_to(src) + new_mode.apply_firemode_to(src) playsound(loc, selector_sound, 50, 1) return new_mode @@ -716,9 +665,6 @@ return TRUE return FALSE -/obj/item/gun/proc/can_autofire() - return (autofire_enabled && world.time >= next_fire_time) - /obj/item/gun/proc/check_accidents(mob/living/user, message = "[user] fumbles with \the [src] and it goes off!",skill_path = SKILL_WEAPONS, fail_chance = 20, no_more_fail = SKILL_EXPERT, factor = 2) if(istype(user) && !safety() && user.skill_fail_prob(skill_path, fail_chance, no_more_fail, factor) && special_check(user)) user.visible_message(SPAN_WARNING(message)) @@ -744,13 +690,6 @@ if(M.aiming) M.aiming.toggle_active(FALSE, TRUE) -/mob/proc/can_autofire(var/obj/item/gun/autofiring, var/atom/autofiring_at) - if(!client || !(autofiring_at in view(client.view,src))) - return FALSE - if(get_active_held_item() != autofiring || incapacitated()) - return FALSE - return TRUE - /obj/item/gun/get_quick_interaction_handler(mob/user) return GET_DECL(/decl/interaction_handler/gun/toggle_safety) diff --git a/code/modules/projectiles/autofire.dm b/code/modules/projectiles/autofire.dm new file mode 100644 index 000000000000..5577984a6d7e --- /dev/null +++ b/code/modules/projectiles/autofire.dm @@ -0,0 +1,37 @@ +/obj/item/gun + var/autofire_enabled = FALSE + var/autofire_delay = 0.1 SECOND + var/next_autofire + +/obj/item/gun/proc/gun_can_autofire() + return (autofire_enabled && world.time >= next_fire_time) + +/obj/item/gun/proc/autofire_check(mob/user, atom/target) + if(!gun_can_autofire()) + return FALSE + if(!istype(user)) + return FALSE + if(!user.check_intent(I_FLAG_HARM)) + return FALSE + if(user.incapacitated()) + return FALSE + if(!user.mob_can_autofire(src, target)) + return FALSE + if(!istype(target) || (!isturf(target) && !isturf(target.loc))) + return FALSE + return TRUE + +/obj/item/gun/wielder_mouse_drag_down(mob/user, object, location, control, params) + if(autofire_check(user, object)) + return TRUE + return FALSE + +/obj/item/gun/wielder_mouse_drag_held(mob/user, atom/target) + next_fire_time = world.time // Reset so we aren't held to a timer. + if(!autofire_check(user, target)) + return FALSE + if(world.time < next_autofire) + return TRUE + next_autofire = world.time + autofire_delay + Fire(target, user, null, (get_dist(target, user) <= 1), FALSE, FALSE) + return TRUE diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index d69f9bdef703..1c2b99a45877 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -144,6 +144,8 @@ use_external_power = 1 max_shots = 4 has_safety = FALSE + autofire_enabled = TRUE + autofire_delay = 0.5 SECONDS /obj/item/gun/energy/plasmacutter/proc/slice(var/mob/M = null) var/obj/item/cell/power_supply = get_cell() diff --git a/code/modules/projectiles/guns/launcher/bows/_bow.dm b/code/modules/projectiles/guns/launcher/bows/_bow.dm index d4182d48fddb..5fee0efa549c 100644 --- a/code/modules/projectiles/guns/launcher/bows/_bow.dm +++ b/code/modules/projectiles/guns/launcher/bows/_bow.dm @@ -46,51 +46,6 @@ /// How big is this bow when unstrung? Uses initial w_class if unset. var/unstrung_w_class -/obj/item/gun/launcher/bow/set_autofire(var/atom/fire_at, var/mob/fire_by, var/autoturn = TRUE) - if(!autofire_enabled || autofiring_at) - return ..() - . = ..() - if(ismob(fire_by)) - if(!get_loaded_arrow(fire_by) && fire_by.skill_check(SKILL_WEAPONS, SKILL_ADEPT)) - load_available_ammo(fire_by) - if(check_can_draw(fire_by)) - tension = 0 - next_tension_step = world.time + get_draw_time(fire_by) - fire_by.set_dir(get_dir(fire_by, fire_at)) - show_draw_message(fire_by) - update_icon() - -/obj/item/gun/launcher/bow/try_autofire(autoturn) - if(!autofire_enabled) - return ..() - var/mob/wielder = loc - if(!ismob(wielder) || !check_can_draw(wielder)) - clear_autofire() - else - wielder.set_dir(get_dir(wielder, autofiring_at)) - if(world.time >= next_tension_step && tension < max_tension) - next_tension_step = world.time + get_draw_time(wielder) - tension++ - if(tension == max_tension) - show_max_draw_message(wielder) - else - show_working_draw_message(wielder) - update_icon() - -/obj/item/gun/launcher/bow/clear_autofire() - if(!autofire_enabled) - return ..() - var/mob/living/wielder = loc - if(tension && istype(wielder) && !wielder.incapacitated() && wielder.get_active_held_item() == src && get_loaded_arrow()) - wielder.set_dir(get_dir(wielder, autofiring_at)) - Fire(autofiring_at, autofiring_by, null, (get_dist(autofiring_at, autofiring_by) <= 1), FALSE, FALSE) - . = ..() - if(tension) - if(istype(wielder)) - show_cancel_draw_message(wielder) - tension = 0 - update_icon() - /obj/item/gun/launcher/bow/handle_click_empty(atom/movable/firer) if(check_fire_message_spam("click")) to_chat(firer, SPAN_WARNING("\The [src] has nothing loaded.")) diff --git a/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm b/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm index ccef4f510c15..b3a15416bd0e 100644 --- a/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm +++ b/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm @@ -63,3 +63,60 @@ show_working_draw_message(user) continue_drawing(user) update_icon() + +/obj/item/gun/launcher/bow/wielder_mouse_drag_down(mob/user, object, location, control, params) + if(drawing_bow) + return FALSE + . = ..() + +// Nock an arrow, or continue to draw the string back. +// We do this here so we don't instantly nock an arrow even if this is not a proper drag yet. +// DO NOT CALL PARENT, default full auto behavior is to fire while held. +/obj/item/gun/launcher/bow/wielder_mouse_drag_held(mob/user, atom/target) + + if(!autofire_enabled) + return FALSE + + // High skills mean you automatically nock an arrow before you draw. + if(tension <= 0 && !get_loaded_arrow(user) && user.skill_check(SKILL_WEAPONS, SKILL_ADEPT)) + load_available_ammo(user) + + if(!check_can_draw(user)) + return FALSE + + // Start drawing. + if(!drawing_bow) + drawing_bow = TRUE + tension = 0 + next_tension_step = world.time + get_draw_time(user) + if(user && isatom(target)) + user.set_dir(get_dir(user, target)) + show_draw_message(user) + update_icon() + return TRUE + + // Already drawing - keep drawing. + if(world.time >= next_tension_step && tension < max_tension) + next_tension_step = world.time + get_draw_time(user) + tension++ + if(tension == max_tension) + show_max_draw_message(user) + else + show_working_draw_message(user) + update_icon() + return TRUE + +// Fire! +/obj/item/gun/launcher/bow/wielder_mouse_drag_up(mob/user, atom/target) + if(!autofire_enabled || !istype(target)) + return FALSE + if(tension && istype(user) && !user.incapacitated() && user.get_active_held_item() == src && get_loaded_arrow()) + user.set_dir(get_dir(user, target)) + Fire(target, user, null, (get_dist(target, user) <= 1), FALSE, FALSE) + if(tension) + if(istype(user)) + show_cancel_draw_message(user) + tension = 0 + update_icon() + drawing_bow = FALSE + return TRUE diff --git a/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm b/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm index 14eaf8359812..ac2434a56e7e 100644 --- a/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm +++ b/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm @@ -84,8 +84,9 @@ /obj/item/gun/launcher/bow/proc/relax_tension(mob/user) tension = 0 update_icon() - if(autofire_enabled) - clear_autofire() + // Cancel any drag fire. + if(autofire_enabled && user.get_active_held_item() == src) + user.on_mouse_up() else if(user) show_string_relax_message(user) diff --git a/code/modules/projectiles/guns/launcher/foam_gun.dm b/code/modules/projectiles/guns/launcher/foam_gun.dm index 6a351feeda82..7350c7128ae9 100644 --- a/code/modules/projectiles/guns/launcher/foam_gun.dm +++ b/code/modules/projectiles/guns/launcher/foam_gun.dm @@ -86,7 +86,7 @@ icon = 'icons/obj/guns/foam/machine_gun.dmi' w_class = ITEM_SIZE_NORMAL fire_delay = 0 - autofire_enabled = 1 + autofire_enabled = TRUE one_hand_penalty = 3 max_darts = 30 burst_delay = 1 diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 1614d25961e8..9646296d5ffc 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -149,9 +149,9 @@ burst = 3 burst_accuracy = list(0,-1,-1) dispersion = list(0.0, 0.6, 1.0) + autofire_enabled = TRUE fire_delay = 0 - autofire_enabled = 1 mag_insert_sound = 'sound/weapons/guns/interaction/batrifle_magin.ogg' mag_remove_sound = 'sound/weapons/guns/interaction/batrifle_magout.ogg' @@ -177,8 +177,8 @@ return FALSE return TRUE -/obj/item/gun/projectile/automatic/machine/set_autofire(atom/fire_at, mob/fire_by, autoturn) - if(!special_check(fire_by)) +/obj/item/gun/projectile/automatic/machine/wielder_mouse_drag_down(mob/user, object, location, control, params) + if(!special_check(user)) return FALSE . = ..() if(. && !spin_up_time) @@ -186,7 +186,7 @@ sound_token = play_looping_sound(src, "machine_gun", 'sound/mecha/hydraulic.ogg', volume = 30) spin_up_time = world.time -/obj/item/gun/projectile/automatic/machine/clear_autofire() +/obj/item/gun/projectile/automatic/machine/wielder_mouse_drag_up(mob/user, atom/target) . = ..() spin_up_time = null - QDEL_NULL(sound_token) \ No newline at end of file + QDEL_NULL(sound_token) diff --git a/mods/content/sealant_gun/sealant_gun.dm b/mods/content/sealant_gun/sealant_gun.dm index 23a753cc0102..79e7a251d4f4 100644 --- a/mods/content/sealant_gun/sealant_gun.dm +++ b/mods/content/sealant_gun/sealant_gun.dm @@ -4,6 +4,7 @@ icon = 'mods/content/sealant_gun/icons/sealant_gun.dmi' icon_state = ICON_STATE_WORLD autofire_enabled = TRUE + autofire_delay = 0.5 SECONDS has_safety = FALSE waterproof = TRUE w_class = ITEM_SIZE_GARGANTUAN diff --git a/nebula.dme b/nebula.dme index 92e4d634b7f4..a30d4dfdb403 100644 --- a/nebula.dme +++ b/nebula.dme @@ -176,7 +176,7 @@ #include "code\_onclick\drag_drop.dm" #include "code\_onclick\ghost.dm" #include "code\_onclick\item_attack.dm" -#include "code\_onclick\MouseDrag.dm" +#include "code\_onclick\mouse_drag.dm" #include "code\_onclick\other_mobs.dm" #include "code\_onclick\rig.dm" #include "code\_onclick\hud\_defines.dm" @@ -273,6 +273,7 @@ #include "code\controllers\subsystems\ambience.dm" #include "code\controllers\subsystems\ao.dm" #include "code\controllers\subsystems\atoms.dm" +#include "code\controllers\subsystems\clickdrag.dm" #include "code\controllers\subsystems\configuration.dm" #include "code\controllers\subsystems\daycycle.dm" #include "code\controllers\subsystems\disposals.dm" @@ -3492,8 +3493,9 @@ #include "code\modules\power\solar\solar_control.dm" #include "code\modules\power\solar\solar_panel.dm" #include "code\modules\power\solar\tracker.dm" +#include "code\modules\projectiles\_gun.dm" #include "code\modules\projectiles\ammunition.dm" -#include "code\modules\projectiles\gun.dm" +#include "code\modules\projectiles\autofire.dm" #include "code\modules\projectiles\projectile.dm" #include "code\modules\projectiles\secure.dm" #include "code\modules\projectiles\ammunition\boxes.dm" From 13e4f2c2dc4d8bc6bf671694b2d1745716f5cc90 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 22 May 2026 09:04:48 +1000 Subject: [PATCH 1266/1331] Automatic changelog generation for PR #5373 [ci skip] --- html/changelogs/AutoChangeLog-pr-5373.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5373.yml diff --git a/html/changelogs/AutoChangeLog-pr-5373.yml b/html/changelogs/AutoChangeLog-pr-5373.yml new file mode 100644 index 000000000000..54f648ad7be0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5373.yml @@ -0,0 +1,6 @@ +author: MistakeNot4892 +changes: + - {tweak: Mech plasmacutter is full auto and mech AR full auto should now + work.} + - {tweak: Autofire should be more responsive in general.} +delete-after: true From 31208360e8b26b5d8de8bd56d2c25fa313158c06 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 17 May 2026 19:53:08 +1000 Subject: [PATCH 1267/1331] Temporary commit to make girders craftable with metal sheets. --- code/game/objects/structures/girders.dm | 3 ++- code/modules/crafting/stack_recipes/recipes_steel.dm | 6 ++++++ code/modules/crafting/stack_recipes/recipes_struts.dm | 1 - 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 83ebf5a68237..861e57b76efb 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -9,7 +9,8 @@ material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) max_health = 100 - parts_amount = 2 + material = /decl/material/solid/metal/steel + parts_amount = 5 parts_type = /obj/item/stack/material/rods var/cover = 50 diff --git a/code/modules/crafting/stack_recipes/recipes_steel.dm b/code/modules/crafting/stack_recipes/recipes_steel.dm index 8f15300c3677..5f95cd083414 100644 --- a/code/modules/crafting/stack_recipes/recipes_steel.dm +++ b/code/modules/crafting/stack_recipes/recipes_steel.dm @@ -108,3 +108,9 @@ /decl/stack_recipe/steel/furniture/drill_brace result_type = /obj/structure/drill_brace + +// Temporary recipe until dev merge. +/decl/stack_recipe/steel/girder + result_type = /obj/structure/girder + required_wall_support_value = 10 + available_to_map_tech_level = MAP_TECH_LEVEL_SPACE diff --git a/code/modules/crafting/stack_recipes/recipes_struts.dm b/code/modules/crafting/stack_recipes/recipes_struts.dm index e6fe0f9b67f9..a6692f00ea5a 100644 --- a/code/modules/crafting/stack_recipes/recipes_struts.dm +++ b/code/modules/crafting/stack_recipes/recipes_struts.dm @@ -36,7 +36,6 @@ /decl/stack_recipe/rods/girder result_type = /obj/structure/girder required_wall_support_value = 10 - req_amount = 5 * SHEET_MATERIAL_AMOUNT // Arbitrary value since girders return weird matter values. available_to_map_tech_level = MAP_TECH_LEVEL_SPACE /decl/stack_recipe/rods/wall_frame From 5458f3ae5ec9ae7f16cd02dc48a3d5e9d8aa0703 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 22 May 2026 02:32:07 +0000 Subject: [PATCH 1268/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 7 +++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-5373.yml | 6 ------ 3 files changed, 11 insertions(+), 6 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5373.yml diff --git a/html/changelog.html b/html/changelog.html index 796f54f9fd81..45343c1006a3 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,13 @@ -->
    +

    22 May 2026

    +

    MistakeNot4892 updated:

    +
      +
    • Mech plasmacutter is full auto and mech AR full auto should now work.
    • +
    • Autofire should be more responsive in general.
    • +
    +

    27 March 2026

    Typhin updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 83328d67f5a5..f044af70e159 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15070,3 +15070,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2026-03-27: Typhin: - tweak: Prevented Garlic Oil from dealing TOX damage +2026-05-22: + MistakeNot4892: + - tweak: Mech plasmacutter is full auto and mech AR full auto should now work. + - tweak: Autofire should be more responsive in general. diff --git a/html/changelogs/AutoChangeLog-pr-5373.yml b/html/changelogs/AutoChangeLog-pr-5373.yml deleted file mode 100644 index 54f648ad7be0..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5373.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: MistakeNot4892 -changes: - - {tweak: Mech plasmacutter is full auto and mech AR full auto should now - work.} - - {tweak: Autofire should be more responsive in general.} -delete-after: true From 51dc453c5574d8e54865c21f87bd027ec78db9be Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 27 May 2026 23:46:41 -0400 Subject: [PATCH 1269/1331] Clean up unused/defunct message server code --- code/game/machinery/message_server.dm | 32 +++++---------------------- code/modules/events/ion_storm.dm | 10 --------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/code/game/machinery/message_server.dm b/code/game/machinery/message_server.dm index 9ddf2a4d9f39..8fbc655edc44 100644 --- a/code/game/machinery/message_server.dm +++ b/code/game/machinery/message_server.dm @@ -48,22 +48,10 @@ var/global/list/message_servers = list() active_power_usage = 100 var/list/datum/data_rc_msg/rc_msgs = list() - var/active = 1 + var/active = TRUE var/power_failure = 0 // Reboot timer after power outage var/decryptkey = "password" - /// Spam filtering stuff. Messages having theese tokens will be rejected by server. Case sensitive. - var/list/spamfilter = list( - "You have won", - "your prize", - "male enhancement", - "shitcurity", - "are happy to inform you", - "account number", - "enter your PIN" - ) - var/spamfilter_limit = MESSAGE_SERVER_DEFAULT_SPAM_LIMIT //Maximal amount of tokens - stat_immune = 0 uncreated_component_parts = null construct_state = /decl/machine_construction/default/panel_closed @@ -80,7 +68,7 @@ var/global/list/message_servers = list() /obj/machinery/network/message_server/Process() ..() if(active && (stat & (BROKEN|NOPOWER))) - active = 0 + active = FALSE power_failure = 10 update_icon() return @@ -88,7 +76,7 @@ var/global/list/message_servers = list() return else if(power_failure > 0) if(!(--power_failure)) - active = 1 + active = TRUE update_icon() /obj/machinery/network/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) @@ -99,6 +87,8 @@ var/global/list/message_servers = list() if (stamp) authmsg += "[stamp]
      " . = FALSE + if(!active) + return // message suppressed but still saved on the message server var/datum/extension/network_device/network_device = get_extension(src, /datum/extension/network_device) var/datum/computer_network/network = network_device?.get_network() @@ -129,22 +119,12 @@ var/global/list/message_servers = list() /obj/machinery/network/message_server/interface_interact(mob/user) if(!CanInteract(user, DefaultTopicState())) return FALSE - to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") + to_chat(user, "You toggle message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") active = !active power_failure = 0 update_icon() return TRUE -/obj/machinery/network/message_server/attackby(obj/item/used_item, mob/user) - if (active && !(stat & (BROKEN|NOPOWER)) && (spamfilter_limit < MESSAGE_SERVER_DEFAULT_SPAM_LIMIT*2) && \ - istype(used_item,/obj/item/stock_parts/circuitboard/message_monitor)) - spamfilter_limit += round(MESSAGE_SERVER_DEFAULT_SPAM_LIMIT / 2) - qdel(used_item) - to_chat(user, "You install additional memory and processors into \the [src]. Its filtering capabilities been enhanced.") - return TRUE - else - return ..() - /obj/machinery/network/message_server/on_update_icon() icon_state = initial(icon_state) if(panel_open) diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index 48702089f434..0a7a83f44719 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -108,16 +108,6 @@ S.add_ion_law(law) S.show_laws() - for(var/z in affecting_z) - var/obj/machinery/network/message_server/MS = get_message_server_for_z(z) - if(MS) - MS.spamfilter.Cut() - var/i - for (i = 1, i <= MS.spamfilter_limit, i++) - MS.spamfilter += pick("kitty","HONK","rev","malf","liberty","freedom","drugs", "[global.using_map.station_short]", \ - "admin","ponies","heresy","meow","Pun Pun","monkey","Ian","moron","pizza","message","spam",\ - "director", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno") - /datum/event/ionstorm/tick() if(botEmagChance) for(var/mob/living/bot/bot in global.living_mob_list_) From afa62c7da1b88e77c35ef51698586c0d15130c4e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 27 May 2026 23:47:30 -0400 Subject: [PATCH 1270/1331] Improve requests console UI slightly --- code/game/machinery/requests_console.dm | 2 +- nano/templates/request_console.tmpl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 3452f128f8b0..681270aa8cb2 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -107,7 +107,7 @@ var/global/req_console_information = list() data["message"] = message data["recipient"] = recipient - data["priortiy"] = priority + data["priority"] = priority data["msgStamped"] = msgStamped data["msgVerified"] = msgVerified data["announceAuth"] = announceAuth diff --git a/nano/templates/request_console.tmpl b/nano/templates/request_console.tmpl index 698f7b162c92..ee7fc4f0a2c2 100644 --- a/nano/templates/request_console.tmpl +++ b/nano/templates/request_console.tmpl @@ -66,7 +66,7 @@ Used In File(s): \code\game\machinery\requests_console.dm
      Message sent successfully.
      {{:helper.link('Continue', 'arrowthick-1-e', { 'setScreen' : 0 })}}
      {{else data.screen == 5}} -
      An Error occurred. Message not sent.
      +
      An error occurred and your message could not be sent. Retry in 30 seconds. If the issue persists, contact your system administrator for assistance.
      {{:helper.link('Continue', 'arrowthick-1-e', { 'setScreen' : 0 })}}
      {{else data.screen == 6}}
      @@ -104,9 +104,9 @@ Used In File(s): \code\game\machinery\requests_console.dm
      {{else}} {{if data.newmessagepriority == 1}} -
      There are new messages
      +
      There are new messages.
      {{else data.newmessagepriority == 2}} -
      NEW PRIORITY MESSAGES
      +
      NEW PRIORITY MESSAGE!
      {{/if}}
      {{:helper.link('View Messages', data.newmessagepriority ? 'mail-closed' : 'mail-open', { 'setScreen' : 6 })}}

      From 90e50ebad84cb76928f0e35935f61f9e5df8b948 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 27 May 2026 23:54:14 -0400 Subject: [PATCH 1271/1331] Make the base law upload console an abstract type --- code/game/machinery/computer/law.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 992d2b211b51..6e15e0e1df38 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -1,4 +1,5 @@ /obj/machinery/computer/upload + abstract_type = /obj/machinery/computer/upload name = "unused upload console" icon_keyboard = "rd_key" icon_screen = "command" From 25e3e51677ef73ab1f997c08d4b549e082f3555c Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 29 May 2026 02:14:51 +0000 Subject: [PATCH 1272/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 45343c1006a3..2ee1ce5c883f 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -58,12 +58,6 @@

      MistakeNot4892 updated:

    • Mech plasmacutter is full auto and mech AR full auto should now work.
    • Autofire should be more responsive in general.
    - -

    27 March 2026

    -

    Typhin updated:

    -
      -
    • Prevented Garlic Oil from dealing TOX damage
    • -
    From 6c767b46aace81caaceedac8050945b93e0f11d5 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 29 May 2026 20:09:32 +1000 Subject: [PATCH 1273/1331] Automatic changelog generation for PR #5385 [ci skip] --- html/changelogs/AutoChangeLog-pr-5385.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5385.yml diff --git a/html/changelogs/AutoChangeLog-pr-5385.yml b/html/changelogs/AutoChangeLog-pr-5385.yml new file mode 100644 index 000000000000..ef2f7cadb4b4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5385.yml @@ -0,0 +1,6 @@ +author: Penelope Haze +changes: + - {tweak: Deactivating message passing on the message server now prevents + requests console messages from being received. They will still be logged + for admins and on the in-game message server console.} +delete-after: true From 2f36ff5d010b218eaf6caf84dbb5f8da5fa8d141 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 30 May 2026 02:10:36 +0000 Subject: [PATCH 1274/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 5 +++++ html/changelogs/AutoChangeLog-pr-5385.yml | 6 ------ 3 files changed, 11 insertions(+), 6 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5385.yml diff --git a/html/changelog.html b/html/changelog.html index 2ee1ce5c883f..185b3d9def1e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
    +

    30 May 2026

    +

    Penelope Haze updated:

    +
      +
    • Deactivating message passing on the message server now prevents requests console messages from being received. They will still be logged for admins and on the in-game message server console.
    • +
    +

    22 May 2026

    MistakeNot4892 updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index f044af70e159..2fb6a977c186 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15074,3 +15074,8 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. MistakeNot4892: - tweak: Mech plasmacutter is full auto and mech AR full auto should now work. - tweak: Autofire should be more responsive in general. +2026-05-30: + Penelope Haze: + - tweak: Deactivating message passing on the message server now prevents requests + console messages from being received. They will still be logged for admins and + on the in-game message server console. diff --git a/html/changelogs/AutoChangeLog-pr-5385.yml b/html/changelogs/AutoChangeLog-pr-5385.yml deleted file mode 100644 index ef2f7cadb4b4..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5385.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: Penelope Haze -changes: - - {tweak: Deactivating message passing on the message server now prevents - requests console messages from being received. They will still be logged - for admins and on the in-game message server console.} -delete-after: true From aa1569a3904a5299baf68f8cc425a379560abc1f Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Sat, 20 Dec 2025 20:21:57 -0500 Subject: [PATCH 1275/1331] Make breaker boxes no longer a power subtype --- .../items/circuitboards/machinery/power.dm | 2 +- .../programs/engineering/rcon_console.dm | 12 ++++---- code/modules/power/breaker_box.dm | 28 +++++++++---------- code/modules/power/cable/cable.dm | 2 +- maps/away/bearcat/bearcat-2.dmm | 2 +- maps/away/derelict/derelict-station.dmm | 2 +- maps/exodus/exodus-2.dmm | 14 +++++----- maps/tradeship/tradeship-2.dmm | 2 +- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/code/game/objects/items/circuitboards/machinery/power.dm b/code/game/objects/items/circuitboards/machinery/power.dm index d8cdc9faab6c..7bb30358f80b 100644 --- a/code/game/objects/items/circuitboards/machinery/power.dm +++ b/code/game/objects/items/circuitboards/machinery/power.dm @@ -29,7 +29,7 @@ /obj/item/stock_parts/circuitboard/breaker name = "circuitboard (breaker box)" - build_path = /obj/machinery/power/breakerbox + build_path = /obj/machinery/breakerbox board_type = "machine" origin_tech = @'{"powerstorage":4,"engineering":4}' req_components = list( diff --git a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm index 224d0cdccc59..ca6513aba203 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm @@ -44,7 +44,7 @@ // BREAKER DATA (simplified view) var/list/breakerlist[0] - for(var/obj/machinery/power/breakerbox/BR in known_breakers) + for(var/obj/machinery/breakerbox/BR in known_breakers) breakerlist.Add(list(list( "RCON_tag" = BR.RCon_tag, "enabled" = BR.on @@ -90,8 +90,8 @@ SMES.set_output(outputset) if(href_list["toggle_breaker"]) - var/obj/machinery/power/breakerbox/toggle = null - for(var/obj/machinery/power/breakerbox/breaker in known_breakers) + var/obj/machinery/breakerbox/toggle = null + for(var/obj/machinery/breakerbox/breaker in known_breakers) if(breaker.RCon_tag == href_list["toggle_breaker"]) toggle = breaker if(toggle) @@ -129,7 +129,7 @@ known_SMESs = sortTim(known_SMESs, /proc/cmp_rcon_tag_asc) known_breakers = new /list() - for(var/obj/machinery/power/breakerbox/breaker in SSmachines.machinery) + for(var/obj/machinery/breakerbox/breaker in SSmachines.machinery) if(can_connect_to(breaker)) known_breakers.Add(breaker) @@ -145,6 +145,6 @@ var/obj/machinery/power/smes/buildable/SMES = M return SMES.RCon_tag && SMES.RCon_tag != "NO_TAG" && SMES.RCon - if(istype(M, /obj/machinery/power/breakerbox)) - var/obj/machinery/power/breakerbox/breaker = M + if(istype(M, /obj/machinery/breakerbox)) + var/obj/machinery/breakerbox/breaker = M return breaker.RCon_tag != "NO_TAG" \ No newline at end of file diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index 2cd7d48af023..e7eb827fd599 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -3,7 +3,7 @@ // Requires 5 seconds to toggle and can be toggled once a minute // Used for advanced grid control (read: Substations) -/obj/machinery/power/breakerbox +/obj/machinery/breakerbox name = "breaker box" icon = 'icons/obj/power.dmi' icon_state = "bbox_off" @@ -13,7 +13,7 @@ construct_state = /decl/machine_construction/default/panel_closed stat_immune = 0 uncreated_component_parts = null - base_type = /obj/machinery/power/breakerbox + base_type = /obj/machinery/breakerbox var/icon_state_on = "bbox_on" var/icon_state_off = "bbox_off" @@ -23,27 +23,27 @@ /// If world.time < lock_time, system is locked for interactions. var/lock_time = 0 -/obj/machinery/power/breakerbox/activated +/obj/machinery/breakerbox/activated icon_state = parent_type::icon_state_on // Enabled on server startup. Used in substations to keep them in bypass mode. -/obj/machinery/power/breakerbox/activated/Initialize() +/obj/machinery/breakerbox/activated/Initialize() ..() return INITIALIZE_HINT_LATELOAD -/obj/machinery/power/breakerbox/activated/LateInitialize() +/obj/machinery/breakerbox/activated/LateInitialize() set_state(TRUE) . = ..() -/obj/machinery/power/breakerbox/get_examine_strings(mob/user, distance, infix, suffix) +/obj/machinery/breakerbox/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(on) . += SPAN_GOOD("It seems to be online.") else . += SPAN_WARNING("It seems to be offline.") -/obj/machinery/power/breakerbox/proc/try_toggle_state(mob/living/user, digital = FALSE) - if(lock_time < world.time) +/obj/machinery/breakerbox/proc/try_toggle_state(mob/living/user, digital = FALSE) + if(world.time < lock_time) // maybe rename this unlock_time to make it clearer it's the time it unlocks at to_chat(user, SPAN_WARNING("System locked. Please try again later.")) return TRUE @@ -68,13 +68,13 @@ busy = FALSE return TRUE -/obj/machinery/power/breakerbox/attack_ai(mob/living/silicon/ai/user) +/obj/machinery/breakerbox/attack_ai(mob/living/silicon/ai/user) return try_toggle_state(user, digital = TRUE) -/obj/machinery/power/breakerbox/physical_attack_hand(mob/user) +/obj/machinery/breakerbox/physical_attack_hand(mob/user) return try_toggle_state(user, digital = FALSE) -/obj/machinery/power/breakerbox/attackby(obj/item/used_item, mob/user) +/obj/machinery/breakerbox/attackby(obj/item/used_item, mob/user) if(IS_MULTITOOL(used_item)) var/newtag = input(user, "Enter new RCON tag. Use \"NO_TAG\" to disable RCON or leave empty to cancel.", "SMES RCON system") as text if(!CanPhysicallyInteract(user)) @@ -85,11 +85,11 @@ return TRUE return ..() -/obj/machinery/power/breakerbox/on_update_icon() +/obj/machinery/breakerbox/on_update_icon() . = ..() icon_state = on ? icon_state_on : icon_state_off -/obj/machinery/power/breakerbox/proc/set_state(state) +/obj/machinery/breakerbox/proc/set_state(state) on = state update_icon() if(on) @@ -121,7 +121,7 @@ qdel(C) // Used by RCON to toggle the breaker box. -/obj/machinery/power/breakerbox/proc/auto_toggle() +/obj/machinery/breakerbox/proc/auto_toggle() if(lock_time > world.time) return FALSE // still on cooldown set_state(!on) diff --git a/code/modules/power/cable/cable.dm b/code/modules/power/cable/cable.dm index 00cc9a0e2d83..1b39dda6262d 100644 --- a/code/modules/power/cable/cable.dm +++ b/code/modules/power/cable/cable.dm @@ -44,7 +44,7 @@ var/global/list/obj/structure/cable/all_cables = list() var/d1 var/d2 var/datum/powernet/powernet - var/obj/machinery/power/breakerbox/breaker_box + var/obj/machinery/breakerbox/breaker_box /obj/structure/cable/drain_power(var/drain_check, var/surge, var/amount = 0) diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 64fa7037ab30..d661d6b51347 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -4191,7 +4191,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "ib" = ( -/obj/machinery/power/breakerbox/activated, +/obj/machinery/breakerbox/activated, /obj/structure/cable{ icon_state = "1-2" }, diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index 46a25943bbfc..4f933e1973d4 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -2916,7 +2916,7 @@ /turf/floor/tiled/dark/airless, /area/constructionsite) "kt" = ( -/obj/machinery/power/breakerbox, +/obj/machinery/breakerbox, /turf/floor/plating/airless, /area/constructionsite) "ku" = ( diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index b7aebcc8585b..931f81211199 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -6631,7 +6631,7 @@ /turf/wall/prepainted, /area/exodus/maintenance/substation/security) "anR" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Security Substation Bypass" }, /turf/floor/plating, @@ -13766,7 +13766,7 @@ /turf/floor/plating, /area/exodus/maintenance/substation/civilian_east) "aDv" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Civilian East Substation Bypass" }, /turf/floor/plating, @@ -16993,7 +16993,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 9 }, -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Civilian West Substation Bypass" }, /turf/floor/plating, @@ -24402,7 +24402,7 @@ /turf/floor/plating, /area/exodus/maintenance/locker) "bay" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Medical Substation Bypass" }, /turf/floor/plating, @@ -32916,7 +32916,7 @@ /turf/floor/tiled/white, /area/exodus/medical/exam_room) "bsa" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Command Substation Bypass" }, /obj/machinery/light, @@ -51093,7 +51093,7 @@ /turf/floor/plating, /area/exodus/maintenance/cargo) "ccA" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Research Substation Bypass" }, /turf/floor/plating, @@ -53849,7 +53849,7 @@ /turf/floor/tiled/white/monotile, /area/exodus/medical/surgery2) "cib" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Engineering Substation Bypass" }, /turf/floor/plating, diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 765635bb94f9..63c009143c54 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -8057,7 +8057,7 @@ /turf/wall/titanium, /area/ship/trade/shuttle/rescue) "Za" = ( -/obj/machinery/power/breakerbox/activated, +/obj/machinery/breakerbox/activated, /obj/structure/cable{ icon_state = "1-2" }, From 3373f630c715d56a2f34831601d490224c01f43a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 26 Jun 2026 21:09:52 -0400 Subject: [PATCH 1276/1331] Add map migration for breakerbox repath --- tools/map_migrations/5229_breakerbox.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/map_migrations/5229_breakerbox.txt diff --git a/tools/map_migrations/5229_breakerbox.txt b/tools/map_migrations/5229_breakerbox.txt new file mode 100644 index 000000000000..74f298b033c6 --- /dev/null +++ b/tools/map_migrations/5229_breakerbox.txt @@ -0,0 +1 @@ +/obj/machinery/power/breakerbox/@SUBTYPES : /obj/machinery/breakerbox{@OLD} \ No newline at end of file From ab2f6f04c8668266e235fe4ac0ff0b4e572e004b Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sat, 27 Jun 2026 16:02:54 -0500 Subject: [PATCH 1277/1331] lighting: Rewrite corner z-bleed to explicitly build a stack of corners --- code/__defines/lighting.dm | 5 + code/game/turfs/turf_changing.dm | 2 +- code/modules/lighting/ambient_turf.dm | 17 +- code/modules/lighting/lighting_corner.dm | 366 +++++++++++++---------- code/modules/lighting/lighting_source.dm | 4 +- code/modules/lighting/lighting_turf.dm | 3 +- 6 files changed, 230 insertions(+), 167 deletions(-) diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index d9c934a0fb5f..6e0c142608a4 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -25,6 +25,11 @@ #define TURF_IS_AMBIENT_LIT_UNSAFE(T) (T:ambient_active) #define TURF_IS_AMBIENT_LIT(T) (isturf(T) && TURF_IS_AMBIENT_LIT_UNSAFE(T)) +// The relation of these is important. +#define LIGHTING_CORNER_GENERATE_UP 1 +#define LIGHTING_CORNER_GENERATE_BOTH 0 +#define LIGHTING_CORNER_GENERATE_DOWN -1 + // If I were you I'd leave this alone. #define LIGHTING_BASE_MATRIX \ list \ diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 0dfa901cf988..2b5d18ba136d 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -152,7 +152,7 @@ var/new_z_opacity = z_flags & ZM_ALLOW_LIGHTING if (new_z_opacity != old_z_opacity) for (var/datum/lighting_corner/corn in corners) - corn.rebuild_ztraversal(!new_z_opacity) + corn.generate_z_connections() var/tidlu = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) if ((old_opacity != opacity) || (tidlu != old_dynamic_lighting) || force_lighting_update) diff --git a/code/modules/lighting/ambient_turf.dm b/code/modules/lighting/ambient_turf.dm index 60992f1fcb5d..aeceed3dbd67 100644 --- a/code/modules/lighting/ambient_turf.dm +++ b/code/modules/lighting/ambient_turf.dm @@ -18,7 +18,11 @@ ambient_light = isnull(color) ? ambient_light : color ambient_light_multiplier = isnull(multiplier) ? ambient_light_multiplier : multiplier - update_ambient_light() + // If we haven't initialized our corners yet, do that instead to avoid ambience double-init. + if (!corners || !lighting_corners_initialised) + generate_missing_corners() + else + update_ambient_light() /// Replace one ambient light with another. This is effectively a delta update, but it can be used to pretend that our one channel is doing color blending. /turf/proc/replace_ambient_light(old_color, new_color, old_multiplier, new_multiplier = 0) @@ -79,13 +83,12 @@ ambient_light_old_g += lg ambient_light_old_b += lb - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) - if (!corners || !lighting_corners_initialised) - generate_missing_corners() + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && (!corners || !lighting_corners_initialised)) + generate_missing_corners() - // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. - for (var/datum/lighting_corner/C in corners) - C.update_ambient_lumcount(lr, lg, lb, !update) + // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. + for (var/datum/lighting_corner/C in corners) + C.update_ambient_lumcount(lr, lg, lb, !update) if (!ambient_active) SSlighting.total_ambient_turfs += 1 diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index ea6f99e9a915..f131ac2983b4 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -22,6 +22,11 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/turf/t4 var/t4i + /// If a connection for z-lights exists, the corner above us. + var/datum/lighting_corner/above_corner + /// If a connection for z-lights exists, the corner below us. + var/datum/lighting_corner/below_corner + var/list/datum/light_source/affecting // Light sources affecting us. var/active = FALSE // TRUE if one of our masters has dynamic lighting. @@ -34,7 +39,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/self_g = 0 var/self_b = 0 - // The intensity we're inheriting from the turf below us, if we're a Z-turf. This is a sum of all below turfs in the Z-stack. + // The intensity we're inheriting from the turfs below us, if we're a Z-turf. This is a sum of all below turfs. var/below_r = 0 var/below_g = 0 var/below_b = 0 @@ -44,7 +49,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/ambient_g = 0 var/ambient_b = 0 - // The turf above us' ambient + // The turf above us' ambient values. var/above_ambient_r = 0 var/above_ambient_g = 0 var/above_ambient_b = 0 @@ -61,7 +66,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/cache_b = 0 var/cache_mx = 0 -/datum/lighting_corner/New(turf/new_turf, diagonal, oi) +/datum/lighting_corner/New(turf/new_turf, diagonal, oi, direction = LIGHTING_CORNER_GENERATE_BOTH) SSlighting.total_lighting_corners += 1 var/has_ambience = FALSE @@ -125,9 +130,10 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) has_ambience = TRUE - update_active() if (has_ambience) init_ambient() + generate_z_connections(direction) + update_active() #define OVERLAY_PRESENT(T) (T && T.lighting_overlay) @@ -144,6 +150,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, #define UPDATE_APPARENT(T, CH) T.apparent_##CH = T.self_##CH + T.below_##CH + T.ambient_##CH + T.above_ambient_##CH +// Configure ambient lighting for *just* this corner. This deliberately does not handle Z-propagation, that's managed by generate_z_connections(). /datum/lighting_corner/proc/init_ambient() var/sum_r = 0 var/sum_g = 0 @@ -171,7 +178,182 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, sum_g /= 4 sum_b /= 4 - update_ambient_lumcount(sum_r, sum_g, sum_b) + ambient_r += sum_r + ambient_g += sum_g + ambient_b += sum_b + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + if (!needs_update) + needs_update = TRUE + SSlighting.corner_queue += src + +/datum/lighting_corner/proc/generate_z_connections(direction = LIGHTING_CORNER_GENERATE_BOTH) + /* + ZM_ALLOW_LIGHTING means that a z-turf is lighting-connected to the turf below it. + So: + Upward: check if above and above is ALLOW_LIGHTING + Downward: check if self is ALLOW_LIGHTING and below + + This corner will be shared by all four of its turfs, so it doesn't matter which condition passes. + The above/below corners should be created iff one of the masters is considered dynamically lit, including dynamic promotion. No other condition matters, and light is not + allowed to transmit through a static lit turf, even if it is marked as ZM_ALLOW_LIGHTING. + */ + + // BOTH is 0, so it's true for both conditions. + // Sometimes we need to only generate going upwards (or downwards); if this corner was created by another corner using this proc, then generating downward is invalid and causes infinite recursion. + // We still need to scan downward (or upward) to find the new connection in this case though. + #define GOING_UP (direction > LIGHTING_CORNER_GENERATE_DOWN) + #define GOING_DOWN (direction < LIGHTING_CORNER_GENERATE_UP) + + var/turf/T + + var/datum/lighting_corner/old_above_corner = above_corner + var/datum/lighting_corner/old_below_corner = below_corner + + /* + This brick is responsible for finding the corner that's directly above us, and forcibly generating the corner if it doesn't exist yet. + It's just the same block of code repeated four times (for each master), plus the case of there now being no above corner, but previously having had one. + We also only initialize the one corner we need rather than all four since there's no benefit to initializing them all -- if a true light needs them, it'll make them itself. + */ + if (t1 && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(above_corner = T.corners?[t1i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t1i] + else if (t2 && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(above_corner = T.corners?[t2i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t2i] + else if (t3 && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(above_corner = T.corners?[t3i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t3i] + else if (t4 && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(above_corner = T.corners?[t4i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t4i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t4i], t4i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t4i] + else if (above_corner) // connected -> disconnected transition + /* + The corner directly above us contains the sum of the light that comes from us and everything below us, which is conveniently everything that we need to remove. + We iterate up through the stack removing only the above_corner's light contribution, as to not disturb light sourced from other turfs higher in the stack. + */ + for (var/datum/lighting_corner/corn = above_corner; corn; corn = corn.above_corner) + corn.below_r -= above_corner.below_r + corn.below_g -= above_corner.below_g + corn.below_b -= above_corner.below_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + above_corner.below_corner = null + above_corner = null + + if (!old_above_corner && above_corner) // disconnected -> connected transition + if (!(apparent_r == apparent_g == apparent_b == 0)) + for (var/datum/lighting_corner/corn = above_corner; corn; corn = corn.above_corner) + // We can't just steal the precomputed value from the above like we can in the removal case: our effect on the turf above us is our own self-light plus the light below us. + corn.below_r += src.below_r + src.self_r + corn.below_g += src.below_g + src.self_g + corn.below_b += src.below_b + src.self_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + // As above, so below. The ordering here is a bit different from the above block, check the comment at the top of this proc. + if ((t1?.z_flags & ZM_ALLOW_LIGHTING) && (T = t1.below || GET_BELOW(t1)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(below_corner = T.corners?[t1i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t1i] + else if ((t2?.z_flags & ZM_ALLOW_LIGHTING) && (T = t2.below || GET_BELOW(t2)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(below_corner = T.corners?[t2i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t2i] + else if ((t3?.z_flags & ZM_ALLOW_LIGHTING) && (T = t3.below || GET_BELOW(t3)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(below_corner = T.corners?[t3i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t3i] + else if ((t4?.z_flags & ZM_ALLOW_LIGHTING) && (T = t4.below || GET_BELOW(t4)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (!(below_corner = T.corners?[t4i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t4i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t4i], t4i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t4i] + else if (below_corner) // connected -> disconnected transition + /* + Similar case to above, but not quite the same. + The corner below us' `above_ambient_*` var contins both our contributed light as well as turfs above us, so just subtract that instead of combining both vars manually. + */ + for (var/datum/lighting_corner/corn = below_corner; corn; corn = corn.below_corner) + corn.above_ambient_r -= below_corner.above_ambient_r + corn.above_ambient_g -= below_corner.above_ambient_g + corn.above_ambient_b -= below_corner.above_ambient_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + below_corner.above_corner = null + below_corner = null + + if (!old_below_corner && below_corner) // disconnected -> connected transition + // quick and dirty heuristic to avoid checking a bunch of different vars, it's still valid if we needlessly run this + if (!(apparent_r == apparent_g == apparent_b == 0)) + for (var/datum/lighting_corner/corn = below_corner; corn; corn = corn.below_corner) + // As with above, we can't just steal the precomputed value from our neighbor. Our effect will be the sum effect of turfs above us, plus our effect. + corn.above_ambient_r += src.above_ambient_r + src.ambient_r + corn.above_ambient_g += src.above_ambient_g + src.ambient_g + corn.above_ambient_b += src.above_ambient_b + src.ambient_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + if (above_corner) + ASSERT(x == above_corner.x) + ASSERT(y == above_corner.y) + ASSERT(z == above_corner.z - 1) + + if (below_corner) + ASSERT(x == below_corner.x) + ASSERT(y == below_corner.y) + ASSERT(z == below_corner.z + 1) + +#undef GOING_UP +#undef GOING_DOWN // God that was a mess, now to do the rest of the corner code! Hooray! /datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b, now = FALSE) @@ -186,29 +368,19 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, UPDATE_APPARENT(src, g) UPDATE_APPARENT(src, b) - var/turf/T - var/Ti - // Grab the first master that's a Z-turf, if one exists. - // The above var cannot be relied on due to init ordering, but we can use it if it is set. - if (t1 && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t1i - else if (t2 && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t2i - else if (t3 && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t3i - else if (t4 && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t4i - else // Nothing above us that cares about below light. - T = null - - if (TURF_IS_DYNAMICALLY_LIT(T)) - do - if (!T.corners || !T.corners[Ti]) - T.generate_missing_corners() - - // Above corners never get instant updates; they're less important, so better to avoid risk of lag. - T.corners[Ti].update_below_lumcount(delta_r, delta_g, delta_b) - while ((T = T.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + for (var/datum/lighting_corner/corn = above_corner; corn; corn = corn.above_corner) + corn.below_r += delta_r + corn.below_g += delta_g + corn.below_b += delta_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + // These are always queued, players are far less likely to notice these being a little behind. + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn // This needs to be down here instead of the above if so the lum values are properly updated. if (needs_update) @@ -220,25 +392,6 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, needs_update = TRUE SSlighting.corner_queue += src -/datum/lighting_corner/proc/update_below_lumcount(delta_r, delta_g, delta_b) - if (!(delta_r + delta_g + delta_b)) - return - - below_r += delta_r - below_g += delta_g - below_b += delta_b - - UPDATE_APPARENT(src, r) - UPDATE_APPARENT(src, g) - UPDATE_APPARENT(src, b) - - // This needs to be down here instead of the above if so the lum values are properly updated. - if (needs_update) - return - - needs_update = TRUE - SSlighting.corner_queue += src - /datum/lighting_corner/proc/update_ambient_lumcount(delta_r, delta_g, delta_b, skip_update = FALSE) ambient_r += delta_r @@ -249,46 +402,18 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, UPDATE_APPARENT(src, g) UPDATE_APPARENT(src, b) - var/turf/T - var/Ti - - if (t1) - T = t1 - Ti = t1i - else if (t2) - T = t2 - Ti = t2i - else if (t3) - T = t3 - Ti = t3i - else if (t4) - T = t4 - Ti = t4i - else - // This should be impossible to reach -- how do we exist without at least one master turf? - CRASH("Corner has no masters!") - - var/datum/lighting_corner/below = src - - // We init before Z-Mimic, cannot rely on above/below. - while ((T = GET_BELOW(T)) && ((below.t1?.z_flags | below.t2?.z_flags | below.t3?.z_flags | below.t4?.z_flags) & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) - if (!T.corners || !T.corners[Ti]) - T.generate_missing_corners() + for (var/datum/lighting_corner/corn = below_corner; corn; corn = corn.below_corner) + corn.above_ambient_r += delta_r + corn.above_ambient_g += delta_g + corn.above_ambient_b += delta_b - ASSERT(T.corners?.len) + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) - below = T.corners[Ti] - below.above_ambient_r += delta_r - below.above_ambient_g += delta_g - below.above_ambient_b += delta_b - - UPDATE_APPARENT(below, r) - UPDATE_APPARENT(below, g) - UPDATE_APPARENT(below, b) - - if (!skip_update && !below.needs_update) - below.needs_update = TRUE - SSlighting.corner_queue += below + if (!skip_update && !corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn if (needs_update || skip_update) return @@ -302,7 +427,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/lg = apparent_g var/lb = apparent_b - // Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually. + // Cache these values ahead of time so 4 individual lighting overlays don't all calculate them individually. var/mx = max(lr, lg, lb) // Scale it so 1 is the strongest lum, if it is above 1. . = 1 // factor if (mx > 1) @@ -331,75 +456,6 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, Ov.needs_update = TRUE SSlighting.overlay_queue += Ov -// This is called when our turf's downward Z-opacity changes. -/datum/lighting_corner/proc/rebuild_ztraversal(new_opacity) - /* - If opacity transitions to off: - - Look down stack, removing below lights contributing to us - - Look up stack, updating turfs with our new below lighting value - If opacity transitions to on: - - Look down stack, add below contributors - - Look up stack, updating turfs with our new below lighting value - */ - below_r = below_g = below_b = 0 - var/turf/T = null - var/datum/lighting_corner/Tcorn = src - var/Ti - - if (!new_opacity) - for (;;) - if (Tcorn.t1 && (T = Tcorn.t1.below || GET_BELOW(Tcorn.t1)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t1i - else if (Tcorn.t2 && (T = Tcorn.t2.below || GET_BELOW(Tcorn.t2)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t2i - else if (Tcorn.t3 && (T = Tcorn.t3.below || GET_BELOW(Tcorn.t3)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t3i - else if (Tcorn.t4 && (T = Tcorn.t4.below || GET_BELOW(Tcorn.t4)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t4i - else // Nothing above us that cares about below light. - break - - Tcorn = T.corners[Ti] - below_r += Tcorn.apparent_r - below_g += Tcorn.apparent_g - below_b += Tcorn.apparent_b - - UPDATE_APPARENT(src, r) - UPDATE_APPARENT(src, g) - UPDATE_APPARENT(src, b) - - if (!needs_update) - needs_update = TRUE - SSlighting.corner_queue += src - - T = null - Tcorn = src - - for (;;) - if (Tcorn.t1 && (T = Tcorn.t1.above || GET_ABOVE(Tcorn.t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t1i - else if (Tcorn.t2 && (T = Tcorn.t2.above || GET_ABOVE(Tcorn.t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t2i - else if (Tcorn.t3 && (T = Tcorn.t3.above || GET_ABOVE(Tcorn.t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t3i - else if (Tcorn.t4 && (T = Tcorn.t4.above || GET_ABOVE(Tcorn.t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t4i - else // Nothing above us that cares about below light. - break - - Tcorn = T.corners[Ti] - Tcorn.below_r += apparent_r - Tcorn.below_g += apparent_g - Tcorn.below_b += apparent_b - - UPDATE_APPARENT(Tcorn, r) - UPDATE_APPARENT(Tcorn, g) - UPDATE_APPARENT(Tcorn, b) - - if (!Tcorn.needs_update) - Tcorn.needs_update = TRUE - SSlighting.corner_queue += Tcorn - /datum/lighting_corner/Destroy(force = FALSE) PRINT_STACK_TRACE("Someone [force ? "force-" : ""]deleted a lighting corner.") if (!force) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 84bccc897573..550a467ba8eb 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -358,8 +358,8 @@ continue Tcorners = T.corners - // These checks are inlined from generate_missing_corners. They must be kept in sync. - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) || T.light_source_solo || T.light_source_multi || (T.z_flags & ZM_ALLOW_LIGHTING)) + // These checks are inlined from generate_missing_corners. They must be kept (roughly) in sync. This one intentionally does not check for ambient turfs. + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) if (!T.lighting_corners_initialised) T.lighting_corners_initialised = TRUE diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 5543d1166c1b..f0713689d33f 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -143,8 +143,7 @@ // This is inlined in lighting_source.dm. // Update it too if you change this. /turf/proc/generate_missing_corners() - // If a turf is dynamically lit, has a light source, or mimics lighting, it needs to have corners created. - if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !light_source_solo && !light_source_multi && !(z_flags & ZM_ALLOW_LIGHTING)) + if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !ambient_light) return lighting_corners_initialised = TRUE From 0def104cdc28c05f3fe76d6b5d29f3acc58ec790 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 29 Jun 2026 11:04:48 -0400 Subject: [PATCH 1278/1331] Add "overriding core code" section to modpack readme --- mods/README.md | 99 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/mods/README.md b/mods/README.md index c6b7a62b11f8..7c73056d41bc 100644 --- a/mods/README.md +++ b/mods/README.md @@ -38,8 +38,103 @@ Modpacks have a defined, user-controlled load order, and cross-modpack compatibi ### Enabling Your Modpack Modpacks are enabled on a per-map basis. To activate a modpack, you `#include` the modpack's .dme in a map's .dme file. -### Overriding Stock Code -TODO: Actually write this section. It's distinct from the "How do I write upstream/core code with extension via modpacks in mind?" part because it's the *opposite,* this section is about overriding core code while the other section is about writing core code to be extended. Maybe do a basic explanation of side-overrides and associated footguns here. +### Overriding Core Code +Sometimes a modpack needs to change how existing non-modpack code behaves, rather than just add new content. Because DM lets you extend any type in any file, a modpack can re-declare an existing type and redefine its vars or procs. This is called a *side-override*: normal overrides are created deeper in the type hierarchy on a subtype, while side-overrides exist 'to the side' of the existing override(s) for a type. (Even though it's not on a parent- or child-type, we still call `..()` the "parent call" even inside a side-override.) + +This is the *opposite* of the approach described in "How do I write upstream/core code with extension via modpacks in mind?" below. That section is about writing core code so modpacks can hook into it without touching it; this section is about the cases where you have to touch it anyway. Prefer the extension approach when stock code already offers a hook (a decl subtype to add, a list to append to, a subtype to iterate over). Reach for a side-override only when there's no such entry point. + +By convention, overrides go in a file named `overrides.dm` (or `_overrides.dm` for a focused group, e.g. `living_overrides.dm`) and are `#include`d from the modpack's `.dme` like any other file. Keeping them in clearly-named files makes it obvious at a glance which stock behavior a modpack changes. Cleverly-designed modpacks will define their core code hooks/overrides separate from per-type value overrides, so they can change as little as possible for each type, making changes less brittle. + +#### Overriding a var +The simplest type of override just extends an existing type and changes variable values: + +```dm +// mods/content/fantasy/items/material_overrides.dm +// FRANCE ISN'T REAL +/obj/item/chems/drinks/bottle/champagne + name = "sparkling wine bottle" + +/decl/material/liquid/alcohol/champagne + name = "sparkling wine" + glass_name = "sparkling wine" + glass_desc = "Sparkling white wine, a favourite at noble and merchant parties." + lore_text = "Sparkling white wine, a favourite at noble and merchant parties." +``` + +This is safe and done entirely at compile-time without adding any new code; it just changes the initial value of vars that the existing type already declares. The only conflict risk is two modpacks setting the same var on the same type to different values, in which case the last one loaded wins. Some modpacks may intend this, while others may want to write a compatibility patch (see below). + +#### Overriding a proc +To change behavior, redefine the proc on the existing type. Most overrides should call `..()` so the stock implementation (and any other modpack's override of it) still runs: + +```dm +// mods/content/augments/passive/armor.dm +// override to add armor augment damage mods +/obj/item/organ/external/get_brute_mod(var/damage_flags) + . = ..() // run the stock proc, keep its result + var/obj/item/organ/internal/augment/armor/armor_augment = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) + if(armor_augment) + . *= armor_augment.brute_mult +``` + +You can call `..()` at the start (to modify the result afterward), at the end (to run your logic first), or conditionally (to sometimes short-circuit and sometimes defer to stock): + +```dm +// mods/content/breath_holding/living_overrides.dm +// override to make a held breath take priority +/mob/living/get_breath(obj/item/organ/internal/lungs/lungs) + if(lungs?.holding_breath && lungs.held_breath) + return lungs.held_breath // intentionally skip the stock proc + return ..() +``` + +#### Overriding a static list getter (the injector pattern) +One common pattern in core code is the *static list getter,* used to avoid creating a new list every time the getter is called. This is much more efficient, but is a little more complex to override. Take this getter, for example: + +```dm +// code/game/objects/items/weapons/secrets_disk.dm +/obj/item/disk/secret_project/proc/get_secret_project_nouns() + var/static/list/nouns = list( + "a superluminal artillery cannon", "a fusion engine", "an atmospheric scrubber",\ + "a human cloning pod", "a microwave oven", "a wormhole generator", "a laser carbine", "an energy pistol",\ + "a wormhole", "a teleporter", "a huge mining drill", "a strange spacecraft", "a space station",\ + "a sleek-looking fighter spacecraft", "a ballistic rifle", "an energy sword", "an inanimate carbon rod" + ) + return nouns +``` + +We want to extend this by adding "a supermatter engine" to the list. A naive approach might be like this: + +```dm +//Example code not actually used +/obj/item/disk/secret_project/get_secret_project_nouns() + . = ..() + . += "a supermatter engine" +``` + +This works at first glance, if you call it once. However, because the getter uses a *static list,* it's saved between calls. That means it will be added every time we use the getter, which will quickly add a lot of duplicate entries to the list. Another naive fix for this would be using `|=` to avoid duplicates, but this is expensive because it checks if the item already exists in the list. Wouldn't it be nice to just add it once? + +For this, we use something called an injector, which uses a static var to track whether or not we've run our override before. If we're running it for the first time, we make all our changes to the static list returned by `..()`, and after that we set our tracking variable to ensure we never modify it again: + +```dm +// mods/content/supermatter/overrides/sm_strings.dm +/obj/item/disk/secret_project/get_secret_project_nouns() + var/static/sm_injected = FALSE + if(sm_injected) + return ..() + sm_injected = TRUE + . = ..() + . += "a supermatter engine" + return . +``` + +This also works for removing items from static lists, and may be useful for run-once code in other contexts as well. Another good example is in mods/content/corporate/items/random.dm. + +#### Footguns +- **Multiple side-overrides chain through `..()` in definition order.** Unlike a normal override, which lives on a new subtype deeper in the type tree, a side-override is defined directly on the existing type. If there are several side-overrides of `/mob/living/some_proc()`, they're all kept and chained: `..()` in the last-defined override calls the previous one, and so on down to the first, which then walks up the type tree to the base implementation. The order in which they run depends on the order they're defined in, so don't write a side-override that assumes it runs first, last, or in any particular position relative to another modpack's. (You can generally assume that it will run after the core definition, though.) +- **Extend, don't copy.** It may be easier to copy an existing proc definition, skip the parent call, and make a change somewhere in the middle. This is (almost) always a horrible idea, because you may not even notice something breaks when an update changes the definition you copied. The correct solution is to add it to an override that runs before or after the parent call, and if you *really* need to run it in the middle, consider adding a proc to the core code that your modpack can override (or split the existing proc into two or more). +- **Always call `..()` unless you really mean to break the chain.** Forgetting `..()` drops the base implementation *and* any earlier modpack's side-override, which can break unrelated core features and any other modpack that expected that proc to do its normal job. Only omit it when you genuinely intend to replace the behavior wholesale. +- **Don't depend on load order between modpacks.** A modpack may depend only on itself and stock code. If your override only makes sense when *another* modpack is also enabled, it's a cross-modpack interaction and belongs in `mods/~compatibility` (see below), which is loaded last and therefore modpack load-order agnostic. +- **Side-overrides are the most fragile thing to maintain across upstream changes.** When upstream code renames a proc, changes its signature, or alters what `..()` returns, your override breaks. The fewer side-overrides a modpack has, the less it breaks when upstream code is refactored. **There being no merge conflicts doesn't mean your code wasn't broken by an update!** ### Cross-modpack interactions Sometimes, a modpack that's enabled might need to do something in response to another modpack also being enabled. Compatibility patches allow for this to happen without the modpacks in question requiring a hard dependency on each other. From a4bdd5467725a721001669906517898a11e82c84 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 29 Jun 2026 17:16:50 -0400 Subject: [PATCH 1279/1331] Fix incorrect proc references --- code/game/objects/items/weapons/storage/secure.dm | 4 ++-- code/modules/augment/active/polytool.dm | 6 +++--- code/modules/augment/simple.dm | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index 19eed2008471..45721fdb72b6 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -31,12 +31,12 @@ /obj/item/secure_storage/Initialize(ml, material_key) var/datum/extension/lockable/mylock = get_or_create_extension(src, lock_type) - events_repository.register(/decl/observ/lock_state_changed, mylock, src, /obj/item/secure_storage/proc/on_lock_state_changed) + events_repository.register(/decl/observ/lock_state_changed, mylock, src, PROC_REF(on_lock_state_changed)) . = ..() /obj/item/secure_storage/Destroy() var/datum/extension/lockable/mylock = get_extension(src, lock_type) - events_repository.unregister(/decl/observ/lock_state_changed, mylock, src, /obj/item/secure_storage/proc/on_lock_state_changed) + events_repository.unregister(/decl/observ/lock_state_changed, mylock, src, PROC_REF(on_lock_state_changed)) . = ..() /obj/item/secure_storage/proc/on_lock_state_changed(datum/extension/lockable/L, old_locked, new_locked) diff --git a/code/modules/augment/active/polytool.dm b/code/modules/augment/active/polytool.dm index ad72c0f405b7..09d7ec605d6e 100644 --- a/code/modules/augment/active/polytool.dm +++ b/code/modules/augment/active/polytool.dm @@ -14,9 +14,9 @@ var/obj/item/I = new path (src) I.canremove = FALSE items += I - events_repository.register(/decl/observ/moved, I, src, /obj/item/organ/internal/augment/active/polytool/proc/check_holding) - events_repository.register(/decl/observ/destroyed, I, src, /obj/item/organ/internal/augment/active/polytool/proc/check_holding) - events_repository.register(/decl/observ/item_unequipped, I, src, /obj/item/organ/internal/augment/active/polytool/proc/check_holding) + events_repository.register(/decl/observ/moved, I, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/destroyed, I, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/item_unequipped, I, src, PROC_REF(check_holding)) /obj/item/organ/internal/augment/active/polytool/Destroy() for(var/obj/item/item in items) diff --git a/code/modules/augment/simple.dm b/code/modules/augment/simple.dm index 14e48b5ca457..52410b05c911 100644 --- a/code/modules/augment/simple.dm +++ b/code/modules/augment/simple.dm @@ -12,9 +12,9 @@ holding.canremove = FALSE if(!origin_tech) origin_tech = holding.get_origin_tech() - events_repository.register(/decl/observ/moved, holding, src, /obj/item/organ/internal/augment/active/simple/proc/check_holding) - events_repository.register(/decl/observ/destroyed, holding, src, /obj/item/organ/internal/augment/active/simple/proc/check_holding) - events_repository.register(/decl/observ/item_unequipped, holding, src, /obj/item/organ/internal/augment/active/simple/proc/check_holding) + events_repository.register(/decl/observ/moved, holding, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/destroyed, holding, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/item_unequipped, holding, src, PROC_REF(check_holding)) /obj/item/organ/internal/augment/active/simple/proc/check_holding() From 843f53c78c8b8d5954393eef8cb5a989209b744e Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 29 Jun 2026 17:13:00 -0400 Subject: [PATCH 1280/1331] Add 'writing extensible code' section to modpack readme --- mods/README.md | 163 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/mods/README.md b/mods/README.md index 7c73056d41bc..3d154e8b1e65 100644 --- a/mods/README.md +++ b/mods/README.md @@ -157,7 +157,168 @@ Some modpacks extend other modpacks and make no sense to include on their own, i Modular code on a downstream with an upstream that does frequent refactors and rewrites is inevitably going to break when the upstream codebase does anything. Names change, so do assumptions, and even design directions might diverge so far that reconciling them will be hard or even impossible. There's not really getting around that, but we can at least mitigate it by designing stable interfaces and documenting changes. When upstream code is written with modularity in mind, downstreams have a much easier time adding content. ## How do I write upstream/core code with extension via modpacks in mind? -TODO: Actually write this section. Give examples like `/decl/atmos_grief_fix_step`, `/decl/human_examination`, the cocktails system, etc. Iterating over subtypes of a base type makes it easy for modpacks to add new code. Also maybe address some footguns like trying to make something modular before trying to make it actually work? Could also discuss the open-closed principle I guess, e.g. write code that gets *extended* rather than *modified* (so avoiding side-overrides where possible, etc.). +This is the counterpart of "Overriding Core Code" above. There, a modpack reaches into core code and changes it from the side; here, you're the one writing the core code, and your goal is to leave an *entry point* that modpacks can hook into without ever editing your code. The guiding idea is the open/closed principle: code should be open for extension but closed for modification. Every time a modpack can add a feature by writing a new file instead of side-overriding one of yours, that's one fewer thing that silently breaks when you refactor later. + +The single most useful tool for this is **iterating over decl subtypes.** Define an abstract decl as a hook point, write your core logic to enumerate every subtype of it and call into them, and modpacks extend the system simply by defining a new subtype. Nothing in core needs to know the modpack exists. + +### Pattern: action decls +Take the "fix atmospherics grief" admin tool. Core code defines an abstract decl with a small interface, then enumerates every subtype, sorts them, and calls each: + +```dm +// code/modules/admin/verbs/grief_fixers.dm +/decl/atmos_grief_fix_step + abstract_type = /decl/atmos_grief_fix_step + var/name + +/decl/atmos_grief_fix_step/proc/act() + return + +// ...elsewhere, the verb that runs them all: +var/list/steps = decls_repository.get_decls_of_subtype_unassociated(/decl/atmos_grief_fix_step) +steps = sortTim(steps.Copy(), /proc/cmp_decl_sort_value_asc) +for(var/decl/atmos_grief_fix_step/fix_step as anything in steps) + to_chat(usr, "[fix_step.name].") + fix_step.act() +``` + +A modpack can then add a step without touching any of the above. It just defines a new subtype and the core loop runs it in the specified order: + +```dm +// mods/content/supermatter/datums/sm_grief_fix.dm +/decl/atmos_grief_fix_step/supermatter + name = "Supermatter depowered" + sort_order = 0 + +/decl/atmos_grief_fix_step/supermatter/act() + // Depower the supermatter, as it would quickly blow up once we remove all gases from the pipes. + for(var/obj/structure/supermatter/S in SSsupermatter.processing) + S.power = 0 +``` + +Note the two things that make this clean: `abstract_type` marks the base as not-runnable so the enumeration only picks up real steps, and a `sort_order` var (read by the `cmp_decl_sort_value_asc` comparator) lets each subtype declare where it belongs in the sequence rather than relying on definition or load order. When you design a hook like this, give modpacks an explicit ordering knob instead of leaving order undefined. + +### Pattern: output builder decls +Enumerable decls don't have to *do* something; they can be useful just for a calculation or return value used in base-game code. Human examination text works this way. Base human examination code defines a stub decl whose whole purpose is to be subtyped by modpacks: + +```dm +// code/modules/mob/living/human/human_examine_decl.dm +/decl/human_examination //This is essentially a stub-method for modpacks to be able to add onto the human examination stuff + var/priority = 0 + +/decl/human_examination/proc/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + return +``` + +Core's examine code enumerates these decls (sorted by `priority`) and appends whatever each returns. A modpack adds a line to the examine output by defining a subtype of `/decl/human_examination` and implementing `do_examine()`; see `mods/content/matchmaking/matchmaker.dm` for a working example. + +### Pattern: condition/recipe decls +Similarly to output builder decls (see prior section), the cocktails system (`code/modules/reagents/cocktails.dm`), chemical reaction system (`code\modules\reagents\reactions\_reaction.dm`), and stack recipe system (`code\modules\crafting\stack_recipes\_recipe.dm`) follow a similar idea: a base type that gets enumerated, so modpacks add new recipes by adding subtypes rather than editing a central list. By combining this with other principles, modpacks can extend, remove, or modify existing recipes, cocktails, reactions, etc. without needing to edit them directly. + +### Pattern: events (`/decl/observ`) +The decl patterns above allow core and modpack code to request extensible subtypes representing information, behavior, or conditions. Conversely, events allow modular code to request an update when something particular happens, and they're easily the most versatile tool for writing code that doesn't require side-overrides. When something notable happens, core code raises an event, and anything that cares can register to be notified. The code raising the event has no idea who's listening, and never needs a call added for each new listener. This is exactly what makes it good for modular code: a modpack can react to a core event (or even another modpack's event) without the event-issuing code containing a single reference to the consumer of that event. + +An event is a `/decl/observ` subtype. Defining one is just a declaration plus a doc comment describing the arguments listeners will receive: + +```dm +// code/datums/observation/death.dm +// Raised when: A mob dies. +// Arguments the called proc should expect: +// /mob/dying_mob: the mob that died. +/decl/observ/death + name = "Death" + expected_type = /mob +``` + +Events are typically raised with the `RAISE_EVENT` macro, passing the source as the first argument followed by any event-specific arguments. + +```dm +// code/datums/observation/death.dm +/mob/living/add_to_dead_mob_list() + . = ..() + if(.) + RAISE_EVENT(/decl/observ/death, src) +``` + +A modpack (or any object) hooks in by registering a callback through `events_repository`. The arguments are `(event_type, event_source, listener, proc_to_call)`; the listener's proc receives the event source plus whatever extra args the event documents. Crucially, you must **unregister** when you no longer care (and always before the listener is destroyed), or the listener will be forced to clean them up manually on deletion, which can be slow. This augment registers on the item it's holding and tears the registration down when that item goes away (through another event): + +```dm +// mods/content/augments/simple.dm +/obj/item/organ/internal/augment/active/simple/Initialize() + . = ..() + // ... + events_repository.register(/decl/observ/moved, holding, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/destroyed, holding, src, PROC_REF(check_holding)) + +/obj/item/organ/internal/augment/active/simple/proc/check_holding() + if(QDELETED(holding)) + events_repository.unregister(/decl/observ/moved, holding, src) + events_repository.unregister(/decl/observ/destroyed, holding, src) + holding = null +``` + +Pass `event_source` to register for events from one specific object; use `register_global(event_type, listener, proc_call)` to hear about that event from *every* source. Some high-traffic events forbid this for performance with the `OBSERVATION_NO_GLOBAL_REGISTRATIONS` flag, so check the event's definition; if writing something that may need that level of performance, `raise_event_non_global` can be used instead of `RAISE_EVENT`. + +When you're writing core code, raising an event is the right move whenever you can imagine *someone, someday* wanting to react to something (death, an item moving, a mob examining something) without you knowing who they are or why they want it. It costs one `RAISE_EVENT` line (and the overhead of dispatching events to listeners) and buys almost-unlimited extensibility in modpacks. + +The problem is it's easy to get overeager: every event has a small registration/dispatch cost, so raise them when needed rather than sprinkling them everywhere on the off chance. You can always make an upstream PR to add a new event when it's needed. + +As an aside, those familiar with TGstation's "DCS" system (datum, component, signal) will recognize this as very similar to TG's signals. They do functionally the same thing. + +### Pattern: extensions (`/datum/extension`) +Events let modpacks react to *moments*; extensions let them attach *state and behavior* to an object without subtyping it or piling vars onto its definition. An extension is a separate datum (`/datum/extension`) that hangs off a "holder" datum, keeping a self-contained feature's data and procs encapsulated in its own type instead of smeared across the holder's variable space. This is composition over inheritance: rather than making a new `/obj/item/chems/pill` subtype for "a pill that hides what it contains," you attach an `obfuscated_medication` extension to any pill. + +That separation of concerns is the whole point. The holder doesn't grow a var or a proc for the feature; the feature lives entirely in the extension, can be attached to several unrelated holder types (anything matching its `expected_type`), and can be added or removed at runtime. For a modpack this means adding a self-contained capability to a core object while not touching the core object's *definition* at all. This can even be useful in core code for functionality shared across types whose common ancestor is unacceptably early in the type hierarchy, like `/datum/extension/loaded_cell` (`code\datums\extensions\cell\cell.dm`) or `/datum/extension/padding` (`code\datums\extensions\padding\padding.dm`). + +An extension subtype sets `base_type` (attaching a second extension derived from the same `base_type` replaces the first) and `expected_type` (the holders it's allowed on, enforced at construction): + +```dm +// mods/content/bigpharma/extension.dm +/datum/extension/obfuscated_medication + base_type = /datum/extension/obfuscated_medication + expected_type = /obj/item + flags = EXTENSION_FLAG_IMMEDIATE + var/original_reagent + +/datum/extension/obfuscated_medication/pill + expected_type = /obj/item/chems/pill + +/datum/extension/obfuscated_medication/pill/update_appearance() + var/obj/item/pill = holder // every extension knows its holder + pill.icon_state = get_medication_icon_state_from_reagent_name(original_reagent, "pill", 1, 5) +``` + +You attach an extension to a holder via `set_extension(holder, extension_type, ...)`; any extra arguments are forwarded to the extension's `New()`/`post_construction()`. Then you can retrieve it with `get_extension(holder, base_type)`. By default extensions are lazy-loaded (only instantiated on first `get_extension`); set `EXTENSION_FLAG_IMMEDIATE` if it must exist the moment it's attached. There's also `has_extension()` (a cheap presence check that won't trigger lazy instantiation), `remove_extension()`, and `get_or_create_extension()`: + +```dm +// mods/content/augments/active/cyberbrain.dm +/obj/item/organ/internal/augment/active/cyberbrain/Initialize() + . = ..() + // ... + set_extension(src, /datum/extension/interactive/os/device/implant) + set_extension(src, /datum/extension/assembly/modular_computer/cyberbrain) + // ... + +/obj/item/organ/internal/augment/active/cyberbrain/proc/install_default_hardware() + var/datum/extension/assembly/assembly = get_extension(src, /datum/extension/assembly) + for(var/component_type in default_hardware) + assembly.try_install_component(null, new component_type(src)) +``` + +Note that the cyberbrain above attaches *two* unrelated extensions (a modular-computer assembly extension and the OS extension) to one organ. Each is a distinct concern with its own state, neither knows about the other, and neither required a new organ subtype. + +When you're writing core code, prefer an extension over adding vars/procs to a base type whenever the feature is **optional, self-contained, or only relevant to some instances.** This keeps the base type lean and gives modpacks a clean attachment point. Create a subtype instead when the behavior is intrinsic to what the object *is* rather than an add-on. As with all of these patterns, don't build an extension for something only one type will ever use and that isn't a separable concern; overengineering is the enemy of getting things done. + +As with observation events, those familiar with DCS will note that these are similar to components, with the caveat that explicitly checking for extensions and calling methods on them is perfectly acceptable. You can still avoid it through the use of events, and doing so will often lead to cleaner, more extensible code (after all, if you need to add a hook, chances are something else will too), but it is by no means mandatory or even preferred by all developers. + +### Other hooks worth leaving +- **Append to lists, don't replace them.** If core code builds a list that modpacks might want to add to, expose it (or build it from decl subtypes) so a modpack can contribute an entry. The static-list-getter injector pattern in "Overriding Core Code" above exists precisely *because* a getter didn't leave an easier entry point. Don't make modpacks resort to it if you can offer a cleaner hook. +- **Split a proc to create a hook.** If a modpack would otherwise need to side-override the middle of a long proc (the "Extend, don't copy" footgun), the right fix on the core side is to factor that middle out into its own overridable proc, so modpacks can override the small piece and call `..()`. +- **Add vars to a type for modpacks to fill in.** A core type can carry a var that core logic respects but only modpacks ever set, letting modpacks opt into behavior declaratively. + +### Footguns +- **Don't focus too hard on abstraction before getting something working.** It's tempting to design an elaborate system ahead of time to make implementation easier, but a structure for extension is only useful once you understand what that structure needs to accomplish. Implement a working feature first, *then* focus on the points modpacks might actually want to modify. Time spent abstracting and modularizing a system that's fundamentally broken is time wasted. +- **A stable interface is a promise.** Once modpacks (and downstreams) hook into your decl or proc, renaming it or changing its signature breaks them silently. Treat hook points as a small, deliberate API: keep them narrow, name them clearly, and **document them,** because changing them is more troublesome than changing ordinary internal code. +- **Be aware of subtype ordering.** If the order your subtypes run in matters, give them an explicit ordering var (like `sort_order`/`priority` above). Relying on enumeration or load order makes behavior depend on which modpacks happen to be enabled, which is exactly the kind of fragility this whole approach is meant to avoid. # Contribution Please contribute to this README/guide. It's currently unfinished and doesn't cover a lot of important things. Thanks. \ No newline at end of file From a17df4688a623084c054a002c9b19f423155f00a Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Mon, 29 Jun 2026 23:38:10 -0400 Subject: [PATCH 1281/1331] Fix self_message passed to non-mob visible_message --- .../structures/stool_bed_chair_nest_sofa/bedroll.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/bedroll.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/bedroll.dm index 27d710d9cdb4..1b326dbcfda0 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/bedroll.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/bedroll.dm @@ -61,14 +61,14 @@ /obj/structure/bed/bedroll/show_buckle_message(var/mob/buckled, var/mob/buckling) if(buckled == buckling) - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] climbs into \the [src]."), SPAN_NOTICE("You climb into \the [src]."), SPAN_NOTICE("You hear a rustling sound.") ) else var/decl/pronouns/pronouns = buckled.get_pronouns() - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] [pronouns.is] bundled into \the [src] by \the [buckling]."), SPAN_NOTICE("You are bundled into \the [src] by \the [buckling]."), SPAN_NOTICE("You hear a rustling sound.") @@ -76,13 +76,13 @@ /obj/structure/bed/bedroll/show_unbuckle_message(var/mob/buckled, var/mob/buckling) if(buckled == buckling) - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] climbs out of \the [src]."), SPAN_NOTICE("You climb out of \the [src]."), SPAN_NOTICE("You hear a rustling sound.") ) else - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] was pulled out of \the [src] by \the [buckling]."), SPAN_NOTICE("You were pulled out of \the [src] by \the [buckling]."), SPAN_NOTICE("You hear a rustling sound.") From 014ba380467cf9b7c53d79eb40d0db3b0551481b Mon Sep 17 00:00:00 2001 From: Lohikar Date: Tue, 30 Jun 2026 00:12:32 -0500 Subject: [PATCH 1282/1331] lighting: Fix cryptic comment --- code/__defines/lighting.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index 6e0c142608a4..0662b49a0909 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -25,7 +25,7 @@ #define TURF_IS_AMBIENT_LIT_UNSAFE(T) (T:ambient_active) #define TURF_IS_AMBIENT_LIT(T) (isturf(T) && TURF_IS_AMBIENT_LIT_UNSAFE(T)) -// The relation of these is important. +// These are centered around zero to simplify logic; 'up' is 'is greater than -1', 'down' is 'is less than 0'. 0 matches both conditions. #define LIGHTING_CORNER_GENERATE_UP 1 #define LIGHTING_CORNER_GENERATE_BOTH 0 #define LIGHTING_CORNER_GENERATE_DOWN -1 From fd6f6ff0655e5fdb54abf12a9b14f98c4863429a Mon Sep 17 00:00:00 2001 From: Lohikar Date: Tue, 30 Jun 2026 00:51:57 -0500 Subject: [PATCH 1283/1331] lighting: Fix lights shining onto occluding dyn turfs from static ones (common case: flashlight on space facing wall) --- code/modules/lighting/lighting_source.dm | 12 ++++++++++-- code/modules/lighting/lighting_turf.dm | 9 ++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 550a467ba8eb..32cf79dec8b0 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -357,9 +357,17 @@ if ((DETERMINANT(limit_a_x, limit_a_y, test_x, test_y) > 0) || DETERMINANT(test_x, test_y, limit_b_x, limit_b_y) > 0) continue - Tcorners = T.corners + // If we're shining a light from a static lit turf onto a dynamic lit one, we do actually want to create corners to light that turf. // These checks are inlined from generate_missing_corners. They must be kept (roughly) in sync. This one intentionally does not check for ambient turfs. - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + is_dyn_or_adj = TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) + if (!is_dyn_or_adj) + for (var/turf/Tneigh as anything in RANGE_TURFS(1, T)) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(Tneigh)) + is_dyn_or_adj = TRUE + break + + Tcorners = T.corners + if (is_dyn_or_adj) if (!T.lighting_corners_initialised) T.lighting_corners_initialised = TRUE diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index f0713689d33f..77a57083bb8a 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -143,7 +143,14 @@ // This is inlined in lighting_source.dm. // Update it too if you change this. /turf/proc/generate_missing_corners() - if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !ambient_light) + var/is_dyn = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) || ambient_light + if (!is_dyn) + for (var/turf/Tneigh as anything in RANGE_TURFS(1, src)) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(Tneigh)) + is_dyn = TRUE + break + + if (!is_dyn) return lighting_corners_initialised = TRUE From 7895524ed338ba1a509b1f1cc01b56acc9d946a0 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Tue, 30 Jun 2026 00:54:45 -0500 Subject: [PATCH 1284/1331] lighting: Allow Z-light transmission through static turfs --- code/modules/lighting/lighting_corner.dm | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index f131ac2983b4..36926e35da48 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -198,8 +198,8 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, Downward: check if self is ALLOW_LIGHTING and below This corner will be shared by all four of its turfs, so it doesn't matter which condition passes. - The above/below corners should be created iff one of the masters is considered dynamically lit, including dynamic promotion. No other condition matters, and light is not - allowed to transmit through a static lit turf, even if it is marked as ZM_ALLOW_LIGHTING. + The above/below corners should be created if the master has a Z-connection and is ALLOW_LIGHTING, regardless of if it's actually dynamic. This allows light to shine + through Z-turfs that are themselves not dynamic. */ // BOTH is 0, so it's true for both conditions. @@ -218,25 +218,25 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, It's just the same block of code repeated four times (for each master), plus the case of there now being no above corner, but previously having had one. We also only initialize the one corner we need rather than all four since there's no benefit to initializing them all -- if a true light needs them, it'll make them itself. */ - if (t1 && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if (t1 && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t1i]) && GOING_UP) if (!T.corners) T.corners = new(4) T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_UP) above_corner = T.corners[t1i] - else if (t2 && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + else if (t2 && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t2i]) && GOING_UP) if (!T.corners) T.corners = new(4) T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_UP) above_corner = T.corners[t2i] - else if (t3 && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + else if (t3 && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t3i]) && GOING_UP) if (!T.corners) T.corners = new(4) T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_UP) above_corner = T.corners[t3i] - else if (t4 && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + else if (t4 && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t4i]) && GOING_UP) if (!T.corners) T.corners = new(4) @@ -280,25 +280,25 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, SSlighting.corner_queue += corn // As above, so below. The ordering here is a bit different from the above block, check the comment at the top of this proc. - if ((t1?.z_flags & ZM_ALLOW_LIGHTING) && (T = t1.below || GET_BELOW(t1)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + if ((t1?.z_flags & ZM_ALLOW_LIGHTING) && (T = t1.below || GET_BELOW(t1))) if (!(below_corner = T.corners?[t1i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_DOWN) below_corner = T.corners[t1i] - else if ((t2?.z_flags & ZM_ALLOW_LIGHTING) && (T = t2.below || GET_BELOW(t2)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + else if ((t2?.z_flags & ZM_ALLOW_LIGHTING) && (T = t2.below || GET_BELOW(t2))) if (!(below_corner = T.corners?[t2i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_DOWN) below_corner = T.corners[t2i] - else if ((t3?.z_flags & ZM_ALLOW_LIGHTING) && (T = t3.below || GET_BELOW(t3)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + else if ((t3?.z_flags & ZM_ALLOW_LIGHTING) && (T = t3.below || GET_BELOW(t3))) if (!(below_corner = T.corners?[t3i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_DOWN) below_corner = T.corners[t3i] - else if ((t4?.z_flags & ZM_ALLOW_LIGHTING) && (T = t4.below || GET_BELOW(t4)) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + else if ((t4?.z_flags & ZM_ALLOW_LIGHTING) && (T = t4.below || GET_BELOW(t4))) if (!(below_corner = T.corners?[t4i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) From b3a373c1ce7838e4a6cde5c5c263c2ec6c0d6f8e Mon Sep 17 00:00:00 2001 From: Lohikar Date: Tue, 30 Jun 2026 00:57:37 -0500 Subject: [PATCH 1285/1331] lighting: Fix paste errors --- code/modules/lighting/lighting_source.dm | 3 ++- code/modules/lighting/lighting_turf.dm | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 32cf79dec8b0..911e71de89d1 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -347,6 +347,7 @@ var/test_y var/should_do_wedge = light_angle && !facing_opaque + var/is_dyn_or_adj FOR_DVIEW(T, NONUNIT_CEILING(actual_range, 1), source_turf, 0) do if (should_do_wedge) // Directional lighting coordinate filter. @@ -361,7 +362,7 @@ // These checks are inlined from generate_missing_corners. They must be kept (roughly) in sync. This one intentionally does not check for ambient turfs. is_dyn_or_adj = TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) if (!is_dyn_or_adj) - for (var/turf/Tneigh as anything in RANGE_TURFS(1, T)) + for (var/turf/Tneigh as anything in RANGE_TURFS(T, 1)) if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(Tneigh)) is_dyn_or_adj = TRUE break diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 77a57083bb8a..900edbcc5310 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -145,7 +145,7 @@ /turf/proc/generate_missing_corners() var/is_dyn = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) || ambient_light if (!is_dyn) - for (var/turf/Tneigh as anything in RANGE_TURFS(1, src)) + for (var/turf/Tneigh as anything in RANGE_TURFS(src, 1)) if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(Tneigh)) is_dyn = TRUE break From ac8abcedf1f747079deef5c4f1639479154b51fc Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sun, 5 Jul 2026 20:05:38 -0500 Subject: [PATCH 1286/1331] mapping: Fix ZM z-groups not being (re)generated, minor efficiency --- code/controllers/subsystems/mapping.dm | 22 ++++++++++++++++++---- code/modules/maps/reader.dm | 6 ++++-- code/modules/multiz/map_data.dm | 4 ++-- code/modules/overmap/ships/landable.dm | 5 +++-- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 1eb7ecbbf52f..717518c3c640 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -256,20 +256,34 @@ SUBSYSTEM_DEF(mapping) planetoid_data_by_z.len = world.maxz connected_z_cache.Cut() + SSzcopy.calculate_zstack_limits() + //Update SSWeather's indexed lists, if we can. if(SSweather?.weather_by_z) SSweather.weather_by_z.len = world.maxz +/// This is equivalent to calling `increment_world_z_size()` in a loop, but more efficient. +/datum/controller/subsystem/mapping/proc/bulk_increment_world_z_size(num_z_levels, new_level_type, defer_setup = FALSE) + ASSERT(num_z_levels > 0) + var/old_max = world.maxz + world.maxz += num_z_levels + + reindex_lists() + + if (!new_level_type) + CRASH("Missing z-level data type for z[old_max] through z[old_max + num_z_levels]!") + + for (var/i in 1 to num_z_levels) + var/datum/level_data/level = new new_level_type(old_max + i, defer_setup) + level.initialize_new_level() + /datum/controller/subsystem/mapping/proc/increment_world_z_size(var/new_level_type, var/defer_setup = FALSE) world.maxz++ reindex_lists() - if(SSzcopy.zlev_maximums.len) - SSzcopy.calculate_zstack_limits() if(!new_level_type) - PRINT_STACK_TRACE("Missing z-level data type for z["[world.maxz]"]!") - return + CRASH("Missing z-level data type for z[world.maxz]!") var/datum/level_data/level = new new_level_type(world.maxz, defer_setup) level.initialize_new_level() diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm index 1704be274b26..b3d125edaac8 100644 --- a/code/modules/maps/reader.dm +++ b/code/modules/maps/reader.dm @@ -140,8 +140,10 @@ var/global/dmm_suite/preloader/_preloader = new if(zexpansion && !measureOnly) // don't actually expand the world if we're only measuring bounds if(cropMap) continue - while(world.maxz < zcrd) //create new z_levels if needed. - SSmapping.increment_world_z_size(level_data_type) + var/desired_levels = zcrd - world.maxz + if (desired_levels > 0) //create new z_levels if needed. + SSmapping.bulk_increment_world_z_size(level_data_type) + bounds[MAP_MINX] = min(bounds[MAP_MINX], clamp(xcrdStart, x_lower, x_upper)) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd) diff --git a/code/modules/multiz/map_data.dm b/code/modules/multiz/map_data.dm index 83d4f972daa2..da960afb031b 100644 --- a/code/modules/multiz/map_data.dm +++ b/code/modules/multiz/map_data.dm @@ -18,8 +18,8 @@ INITIALIZE_IMMEDIATE(/obj/abstract/map_data) z_levels.len = i z_levels[i] = src - if (length(SSzcopy.zlev_maximums)) - SSzcopy.calculate_zstack_limits() + SSzcopy.calculate_zstack_limits() + return ..() /obj/abstract/map_data/Destroy(forced) diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm index 93adc8097675..452b8f5b89d8 100644 --- a/code/modules/overmap/ships/landable.dm +++ b/code/modules/overmap/ships/landable.dm @@ -63,9 +63,10 @@ // We autobuild our z levels. /obj/effect/overmap/visitable/ship/landable/find_z_levels() if(!use_mapped_z_levels) + var/initial_z = world.maxz + SSmapping.bulk_increment_world_z_size(multiz + 1, level_type) for(var/i = 0 to multiz) - SSmapping.increment_world_z_size(level_type) - map_z += world.maxz + map_z += initial_z + i + 1 else ..() From ba17a3a264a7fe21feb3739f635eadb5af51140e Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sun, 5 Jul 2026 20:06:26 -0500 Subject: [PATCH 1287/1331] zm: Adjust comment on calculate_zstack_limits() --- code/controllers/subsystems/zcopy.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/controllers/subsystems/zcopy.dm b/code/controllers/subsystems/zcopy.dm index e0dfe3f0b1cb..e6290e993004 100644 --- a/code/controllers/subsystems/zcopy.dm +++ b/code/controllers/subsystems/zcopy.dm @@ -168,7 +168,7 @@ SUBSYSTEM_DEF(zcopy) // Flush the queue. fire(FALSE, TRUE) -// If you add a new Zlevel or change Z-connections, call this. +/// (Re)generate Z-group information. You should run this every time world.maxz (or z-connections) change. ZM's behavior is undefined between resizing the world and calling this proc. /datum/controller/subsystem/zcopy/proc/calculate_zstack_limits() zlev_maximums = new(world.maxz) var/start_zlev = 1 From 1b36e16353e73644d89b6fcf56012c08df51c046 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sun, 5 Jul 2026 23:28:35 -0500 Subject: [PATCH 1288/1331] mapping: don't assume SSzcopy exists --- code/controllers/subsystems/mapping.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 717518c3c640..78253db4d1d9 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -256,7 +256,7 @@ SUBSYSTEM_DEF(mapping) planetoid_data_by_z.len = world.maxz connected_z_cache.Cut() - SSzcopy.calculate_zstack_limits() + SSzcopy?.calculate_zstack_limits() //Update SSWeather's indexed lists, if we can. if(SSweather?.weather_by_z) From 6632034436db2a8c991a5e50e775b31f7e7ed26d Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sun, 5 Jul 2026 23:36:21 -0500 Subject: [PATCH 1289/1331] mapping: fix bad arg to BIWZS --- code/modules/maps/reader.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm index b3d125edaac8..87b26dd61cbf 100644 --- a/code/modules/maps/reader.dm +++ b/code/modules/maps/reader.dm @@ -142,7 +142,7 @@ var/global/dmm_suite/preloader/_preloader = new continue var/desired_levels = zcrd - world.maxz if (desired_levels > 0) //create new z_levels if needed. - SSmapping.bulk_increment_world_z_size(level_data_type) + SSmapping.bulk_increment_world_z_size(desired_levels, level_data_type) bounds[MAP_MINX] = min(bounds[MAP_MINX], clamp(xcrdStart, x_lower, x_upper)) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) From ac2b259415aeb275c75da2d1e8539f9aa570adc1 Mon Sep 17 00:00:00 2001 From: Tetra Zeta Date: Sun, 5 Jul 2026 22:44:51 -0600 Subject: [PATCH 1290/1331] sand resprites iconreplace --- icons/turf/flooring/sand.dmi | Bin 13412 -> 2019 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/turf/flooring/sand.dmi b/icons/turf/flooring/sand.dmi index 20339e3f1bcccea28314ae5db42648b9a1310140..ba1ad8a4c01e0ab98f235bc3b397801707c4fe78 100644 GIT binary patch delta 2017 zcmV<72Ojw3XyXr%7=Hu<0001a;d&kb000$GOjJd{z`(q}!rk4<@9@a||Hp@qs=&a& zXUDyX00001bW%=J06^y0W&i*HrFv9YbVOxyV{&P5bZKvH004NLjgiX=!Y~j;*U49e zb}xMiZrn(VbYXv?gl0?vnm}gM-#1v$m2`IZ4(GryQ>xQ&9)Bm*Uq<v>R-&E{PBmUc@RKwJNzW4^OUxR{NwPF(h00zrRL_t(&f$dsd znwvTdWk?oKdw;?^mf=0Ivw#Gg1;7{fzm+Q)60rF(gt>1~zYaY}9Lq))r*5OBNt%W= zO_elNgCJb;lvKG8%{xP0aGD2Pe!GYk%XU(4?D+3PY=1N4w17FIB@~8b2J@Fp8=M6W zX|c>o2Rr`z5L1RsAYqd*{LwHiCX(*Pr3!dTN2Dn%&(t~1N< zl35JE_z-*kLiV^NtSlGJnMv8EASo4G7KQe#Q51nY{^5rqnj_x1^;ZjLl()spd>E z@b~c}LUd5!YYAFmG3XX!4;bL$gbQ#o1AiYsAZ+Z!aY%N>7OHR_A}eK!3m9U>&@cGF z-+#wP_^lffHaMjC#UZ7%4M|U74%p>j+8^1ECw-*aDOc|yIsJj^b^-&;`XeSeC01R; z<&Iw#!LXnNZYL$K*ag`PhFO2a_~jz1=J%J&6X%?IJym~6WmN!d!h$+B2#}C)Tnm1^ zU+@lL(PC=-TQD`B2;VwxNNUtVwh^CWW`8UpYlpxOucj~V4t(KKMVhLBO5s}KFY4v{ zaD=NVq%AH}VwaSAaU`$y%SEl1KHy?6jyCH*&&coG*sT9NL;u;iF`@VLqW<#?{Rij9 zSnpx|kM!X{|9OV~n{#8V_wYdnACCu%k8vE23GsRN#)RG@!XBspZnrbyG`*eEY=5+d zTKsb}9P2$K9OC!~Q;b2{==kUp;`8o}vECyw(BqVXFt@Y~cT2=RpL!!;n~<&^#EA~{ zUcl%>=Z-*MMB0V$)_dUSy?|l-KktSD+=cUcFGy$#_XZ|qFvW`*>b(-)dM`wH_398C zB)5cDBYV9E)ID0g7qC&WBcZq!V1LxFk^{YmeY4YhAwoIIkUk2e?_j9+NV2qgFG#4} zt%9EfPFieM?}Z4t*KH^lzNGg;gc$D=+P~F!dXF}<)N;j?f!#)g6M7Jt@%LyOOc9Es ze`7)qDoI~ld|P@2p}SM2^IP`noq(0?OBem-MLheIsst3~nNP6_BgpV0cF*!t> z2K%FHEA-$di{n2gj8^EuP4;sPYng4fLJw}T!=JC=3O%^V4u?Np!`liyxXF$y^x!7T zR_MV^X3zir((L))U)s+KJ-E$Q=)p}E|Ah76Hk;6czPn0(mCJg`NIljfa-1bt&E&@4x;}sLlt3Y`8e2_KI zr!h7`N;C<@2Q6tH#yq+NlPZ*9*t`T;_=yl%9Wd7;D0Tzu*4K?g62*M?k9Yj zQ(J@PP6^h#3A4McL8?Vc@Ufc^I3-xu1yF*|{beI^+XpkqE8Nxj{<7)X3?&FnUF|1q z`(T$K*5Yo$VS-ePy9sR{#02dXH_L89yGgJQzBbElLc5E>)?fm-n|BlTyO_QxEBayR zeDK|7a3#%SMOr@$oe%a2QrlV44@2jJeS(G(3{N?9KIkPFe#@crK`+5DVd#9YOR%on z1jGF>bUp~eCivjlV_iQCoex6S2iruSqYVE8YQ0sC%djpg00000NkvXXu0mjfq~6<+ literal 13412 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 From 488389adfa54d885b8bb869e855f766cff28833a Mon Sep 17 00:00:00 2001 From: Tetra Zeta Date: Sun, 5 Jul 2026 23:42:41 -0600 Subject: [PATCH 1291/1331] sand has a color now by default --- code/game/turfs/flooring/flooring_sand.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index 5b4f13501eaa..9d6dad135c73 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -5,6 +5,7 @@ icon = 'icons/turf/flooring/sand.dmi' icon_base = "sand" icon_edge_layer = FLOOR_EDGE_SAND + color = "#ffd076" has_base_range = 4 turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/sand From 3c7e5bdea8c6a476f2758eedaa29b5bd66654629 Mon Sep 17 00:00:00 2001 From: Tetra Zeta Date: Mon, 6 Jul 2026 16:02:43 -0600 Subject: [PATCH 1292/1331] now we actually get the color from our forced_material top-most flooring decl --- code/game/turfs/flooring/flooring_sand.dm | 2 +- code/game/turfs/walls/wall_natural_subtypes.dm | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index 9d6dad135c73..5e057b64eee7 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -5,7 +5,7 @@ icon = 'icons/turf/flooring/sand.dmi' icon_base = "sand" icon_edge_layer = FLOOR_EDGE_SAND - color = "#ffd076" + color = null has_base_range = 4 turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/sand diff --git a/code/game/turfs/walls/wall_natural_subtypes.dm b/code/game/turfs/walls/wall_natural_subtypes.dm index dc072ac25193..549cab71e396 100644 --- a/code/game/turfs/walls/wall_natural_subtypes.dm +++ b/code/game/turfs/walls/wall_natural_subtypes.dm @@ -75,7 +75,6 @@ name = "sand"; \ icon = 'icons/turf/flooring/sand.dmi'; \ icon_state = "sand0"; \ - color = "#ae9e66"; \ _flooring = /decl/flooring/sand; \ } \ /turf/wall/natural/##ID { \ From 2e085deb64c461025e72e1e585d96241a8fa10d4 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sun, 5 Jul 2026 19:00:35 -0400 Subject: [PATCH 1293/1331] Move fishing into a modpack --- code/game/area/area_fishing.dm | 16 ----- code/game/area/areas.dm | 8 --- code/game/objects/items/__item.dm | 13 ---- code/game/turfs/turf.dm | 4 -- .../crafting/stack_recipes/recipes_planks.dm | 3 - .../designs/general/designs_general.dm | 6 -- .../{fishing/bait.dm => hydroponics/worm.dm} | 0 .../random_exoplanet/planet_types/grass.dm | 23 ------- .../random_exoplanet/random_planet_areas.dm | 8 --- code/modules/materials/_materials.dm | 4 +- .../solids/materials_solid_butchery.dm | 4 -- .../solids/materials_solid_organic.dm | 1 - .../guns/launcher/bows/bow_string.dm | 2 +- .../modules/reagents/chems/chems_nutriment.dm | 1 - code/modules/reagents/chems/chems_oil.dm | 1 - icons/obj/{fishing_line.dmi => bowstring.dmi} | Bin maps/modpack_testing/modpack_testing.dm | 1 + maps/shaded_hills/shaded_hills.dm | 1 + mods/content/fishing/_fishing.dm | 2 + mods/content/fishing/_fishing.dme | 13 ++++ mods/content/fishing/area_fishing.dm | 63 ++++++++++++++++++ mods/content/fishing/fishing_bait.dm | 37 ++++++++++ mods/content/fishing/fishing_designs.dm | 5 ++ .../content}/fishing/fishing_line.dm | 2 +- mods/content/fishing/fishing_recipes.dm | 2 + .../content}/fishing/fishing_rod.dm | 4 +- mods/content/fishing/icons/fishing_line.dmi | Bin 0 -> 280 bytes .../content/fishing/icons}/fishing_rod.dmi | Bin .../fishing/icons}/fishing_rod_advanced.dmi | Bin mods/content/fishing/turf_fishing.dm | 3 + nebula.dme | 5 +- 31 files changed, 133 insertions(+), 99 deletions(-) delete mode 100644 code/game/area/area_fishing.dm rename code/modules/{fishing/bait.dm => hydroponics/worm.dm} (100%) rename icons/obj/{fishing_line.dmi => bowstring.dmi} (100%) create mode 100644 mods/content/fishing/_fishing.dm create mode 100644 mods/content/fishing/_fishing.dme create mode 100644 mods/content/fishing/area_fishing.dm create mode 100644 mods/content/fishing/fishing_bait.dm create mode 100644 mods/content/fishing/fishing_designs.dm rename {code/modules => mods/content}/fishing/fishing_line.dm (93%) create mode 100644 mods/content/fishing/fishing_recipes.dm rename {code/modules => mods/content}/fishing/fishing_rod.dm (98%) create mode 100644 mods/content/fishing/icons/fishing_line.dmi rename {icons/obj => mods/content/fishing/icons}/fishing_rod.dmi (100%) rename {icons/obj => mods/content/fishing/icons}/fishing_rod_advanced.dmi (100%) create mode 100644 mods/content/fishing/turf_fishing.dm diff --git a/code/game/area/area_fishing.dm b/code/game/area/area_fishing.dm deleted file mode 100644 index 8370e39c4494..000000000000 --- a/code/game/area/area_fishing.dm +++ /dev/null @@ -1,16 +0,0 @@ -/area - var/fishing_failure_prob = 95 - // Hardcoding the contents of /obj/random/junk to avoid hacks for getting results from /obj/random. - var/list/fishing_results = list( - /obj/item/remains/mouse = 1, - /obj/item/remains/robot = 1, - /obj/item/paper/crumpled = 1, - /obj/item/inflatable/torn = 1, - /obj/item/shard = 1, - /obj/item/hand/missing_card = 1 - ) - -/area/proc/get_fishing_result(turf/origin, obj/item/food/bait) - if(!length(fishing_results) || prob(fishing_failure_prob)) - return null - return pickweight(fishing_results) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index d92c81169bdb..7d31f5eae330 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -98,15 +98,7 @@ var/global/list/areas = list() area_blurb_category = type ..() -/area/proc/get_additional_fishing_results() - return - /area/Initialize() - var/list/additional_fishing_results = get_additional_fishing_results() - if(LAZYLEN(additional_fishing_results)) - LAZYINITLIST(fishing_results) - for(var/fish in additional_fishing_results) - fishing_results[fish] = additional_fishing_results[fish] . = ..() global.areas += src if(!requires_power || !apc) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index dfc848c8b322..fd5e22b082cd 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -1203,19 +1203,6 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /obj/item/proc/has_textile_fibers() return FALSE -// Returns a value used as a multiplier in the fishing delay calc. Higher represents a stronger reduction in fishing time. -#define BAIT_VALUE_CONSTANT 0.1 -/obj/item/proc/get_bait_value() - . = 0 - for(var/mat in matter) - var/decl/material/bait_mat = GET_DECL(mat) - if(bait_mat.fishing_bait_value) - . += MATERIAL_UNITS_TO_REAGENTS_UNITS(matter[mat]) * bait_mat.fishing_bait_value * BAIT_VALUE_CONSTANT - for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) - if(reagent.fishing_bait_value) - . += REAGENT_VOLUME(reagents, reagent) * reagent.fishing_bait_value * BAIT_VALUE_CONSTANT -#undef BAIT_VALUE_CONSTANT - /obj/item/proc/get_storage_cost() //If you want to prevent stuff above a certain w_class from being stored, use max_w_class return BASE_STORAGE_COST(w_class) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 28171b7c2394..8af4b0d3b118 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -845,10 +845,6 @@ /turf/get_color() return paint_color || get_material()?.color || color -/turf/proc/get_fishing_result(obj/item/food/bait) - var/area/A = get_area(src) - return A.get_fishing_result(src, bait) - /turf/get_affecting_weather() return weather diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index f00339f99e11..a7114078dda1 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -22,9 +22,6 @@ difficulty = MAT_VALUE_VERY_HARD_DIY available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/planks/fishing_rod - result_type = /obj/item/fishing_rod - /decl/stack_recipe/planks/stick result_type = /obj/item/stick difficulty = MAT_VALUE_EASY_DIY diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index aef3b2e9a27a..741fc0c2ded8 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -153,12 +153,6 @@ path = /obj/item/stack/tape_roll/duct_tape pass_multiplier_to_product_new = FALSE // they are printed as single items with 32 uses -/datum/fabricator_recipe/fishing_line - path = /obj/item/fishing_line - -/datum/fabricator_recipe/fishing_line_high_quality - path = /obj/item/fishing_line/high_quality - /datum/fabricator_recipe/chipboard // base type is for oak path = /obj/item/stack/material/sheet/mapped/chipboard_oak category = "Textiles" diff --git a/code/modules/fishing/bait.dm b/code/modules/hydroponics/worm.dm similarity index 100% rename from code/modules/fishing/bait.dm rename to code/modules/hydroponics/worm.dm diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm index d6ecd767659b..1c4e55d0dd8d 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm @@ -158,26 +158,3 @@ forced_ambience = list( 'sound/ambience/jungle.ogg' ) - fishing_failure_prob = 10 - // TODO: waterweed? - // Hardcoding the contents of /obj/random/natural_debris to avoid hacks to get results out of /obj/random. - fishing_results = list( - /mob/living/simple_animal/aquatic/fish = 10, - /mob/living/simple_animal/aquatic/fish/grump = 10, - /obj/item/mollusc = 5, - /obj/item/mollusc/barnacle/fished = 5, - /mob/living/simple_animal/aquatic/fish/large = 5, - /mob/living/simple_animal/aquatic/fish/large/bass = 5, - /mob/living/simple_animal/aquatic/fish/large/salmon = 5, - /mob/living/simple_animal/aquatic/fish/large/trout = 5, - /mob/living/simple_animal/aquatic/fish/large/pike = 3, - /mob/living/simple_animal/aquatic/fish/large/javelin = 3, - /obj/item/mollusc/clam/fished/pearl = 3, - /obj/item/trash/mollusc_shell/clam = 2, - /obj/item/trash/mollusc_shell/barnacle = 2, - /obj/item/remains/mouse = 2, - /obj/item/remains/lizard = 2, - /obj/item/stick = 1, - /obj/item/trash/mollusc_shell = 1, - /mob/living/simple_animal/aquatic/fish/large/koi = 1 - ) diff --git a/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm b/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm index 6964c4dbd2f6..ffbdb92912b8 100644 --- a/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm +++ b/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm @@ -13,14 +13,6 @@ area_flags = AREA_FLAG_IS_BACKGROUND | AREA_FLAG_EXTERNAL | AREA_FLAG_HIDE_FROM_HOLOMAP is_outside = OUTSIDE_YES -// Let's make a token effort at making the fish somewhat alien I guess. -/area/exoplanet/get_fishing_result(turf/origin, obj/item/food/bait) - . = ..() - if(ismob(.)) - var/mob/M = . - M.SetName("xeno-[M.name]") - M.set_color(get_random_colour(simple = TRUE)) - ///Spoopy undergrounds /area/exoplanet/underground name = "\improper Planetary mantle" diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 02ab0d63cd48..e2ed9314861d 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -286,9 +286,7 @@ var/global/list/materials_by_gas_symbol = list() /// If set to a material type, stacks of this material will be able to be tanned on a drying rack after being wetted to convert them to tans_to. var/tans_to - /// A multiplier for this material when used in fishing bait. - var/fishing_bait_value = 0 - /// A relative value used only by fishing line at time of commit. + /// A relative value used only by bowstrings and the fishing modpack at time of writing. var/tensile_strength = 0 /// What form does this take if dug out of the ground, if any? diff --git a/code/modules/materials/definitions/solids/materials_solid_butchery.dm b/code/modules/materials/definitions/solids/materials_solid_butchery.dm index fb78922a5f65..846edc4f50d4 100644 --- a/code/modules/materials/definitions/solids/materials_solid_butchery.dm +++ b/code/modules/materials/definitions/solids/materials_solid_butchery.dm @@ -19,7 +19,6 @@ sound_manipulate = 'sound/foley/meat1.ogg' sound_dropped = 'sound/foley/meat2.ogg' hitsound = 'sound/effects/squelch1.ogg' - fishing_bait_value = 1 reagent_overlay = "soup_chunks" nutriment_factor = 10 allergen_flags = ALLERGEN_MEAT @@ -83,7 +82,6 @@ sound_manipulate = 'sound/foley/meat1.ogg' sound_dropped = 'sound/foley/meat2.ogg' hitsound = "punch" - fishing_bait_value = 0.75 tans_to = /decl/material/solid/organic/leather compost_value = 0.8 allergen_flags = ALLERGEN_MEAT @@ -116,7 +114,6 @@ default_solid_form = /obj/item/stack/material/skin/pelt sound_manipulate = 'sound/foley/paperpickup2.ogg' sound_dropped = 'sound/foley/paperpickup1.ogg' - fishing_bait_value = 0 paint_verb = "dyed" /decl/material/solid/organic/skin/fur/gray @@ -178,7 +175,6 @@ default_solid_form = /obj/item/stack/material/skin/feathers sound_manipulate = 'sound/foley/paperpickup2.ogg' sound_dropped = 'sound/foley/paperpickup1.ogg' - fishing_bait_value = 0 /decl/material/solid/organic/skin/feathers/purple color = COLOR_PALE_PURPLE_GRAY diff --git a/code/modules/materials/definitions/solids/materials_solid_organic.dm b/code/modules/materials/definitions/solids/materials_solid_organic.dm index be9b91441d16..4f0461dcba85 100644 --- a/code/modules/materials/definitions/solids/materials_solid_organic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_organic.dm @@ -217,7 +217,6 @@ dug_drop_type = /obj/item/stack/material/slab sound_manipulate = 'sound/foley/paperpickup2.ogg' sound_dropped = 'sound/foley/paperpickup1.ogg' - fishing_bait_value = 0.75 allergen_flags = ALLERGEN_VEGETABLE exoplanet_rarity_plant = MAT_RARITY_MUNDANE diff --git a/code/modules/projectiles/guns/launcher/bows/bow_string.dm b/code/modules/projectiles/guns/launcher/bows/bow_string.dm index 72212cde43b1..173b35670552 100644 --- a/code/modules/projectiles/guns/launcher/bows/bow_string.dm +++ b/code/modules/projectiles/guns/launcher/bows/bow_string.dm @@ -1,6 +1,6 @@ /obj/item/bowstring name = "bowstring" - icon = 'icons/obj/fishing_line.dmi' // works well enough for the time being + icon = 'icons/obj/bowstring.dmi' icon_state = ICON_STATE_WORLD desc = "A flexible length of material used to string bows." material = /decl/material/solid/organic/meat/gut diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index 929c4307d091..e8f8acf5bbd9 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -9,7 +9,6 @@ fruit_descriptor = "nutritious" uid = "chem_nutriment" exoplanet_rarity_gas = MAT_RARITY_NOWHERE // Please, no more animal protein or glowsap or corn oil atmosphere. - fishing_bait_value = 0.65 compost_value = 1 nutriment_factor = 10 affect_blood_on_ingest = 0 diff --git a/code/modules/reagents/chems/chems_oil.dm b/code/modules/reagents/chems/chems_oil.dm index e540efb9844b..4d38583ebe66 100644 --- a/code/modules/reagents/chems/chems_oil.dm +++ b/code/modules/reagents/chems/chems_oil.dm @@ -10,7 +10,6 @@ uid = "chem_oil_lamp" color = "#664330" value = 1.5 - fishing_bait_value = 0 taste_mult = 4 metabolism = REM * 4 exoplanet_rarity_gas = MAT_RARITY_NOWHERE diff --git a/icons/obj/fishing_line.dmi b/icons/obj/bowstring.dmi similarity index 100% rename from icons/obj/fishing_line.dmi rename to icons/obj/bowstring.dmi diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 47e16e7a41be..31f2fc7176fa 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -19,6 +19,7 @@ #include "../../mods/content/corporate/_corporate.dme" #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/fantasy/_fantasy.dme" + #include "../../mods/content/fishing/_fishing.dme" #include "../../mods/content/generic_shuttles/_generic_shuttles.dme" #include "../../mods/content/government/_government.dme" #include "../../mods/content/inertia/_inertia.dme" diff --git a/maps/shaded_hills/shaded_hills.dm b/maps/shaded_hills/shaded_hills.dm index 0d7f2b5f7708..e7487897a103 100644 --- a/maps/shaded_hills/shaded_hills.dm +++ b/maps/shaded_hills/shaded_hills.dm @@ -7,6 +7,7 @@ #include "../../mods/species/drakes/_drakes.dme" // include before _fantasy.dme so overrides work #include "../../mods/content/item_sharpening/_item_sharpening.dme" #include "../../mods/content/fantasy/_fantasy.dme" + #include "../../mods/content/fishing/_fishing.dme" #include "../../mods/content/blacksmithy/_blacksmithy.dme" #include "areas/_areas.dm" diff --git a/mods/content/fishing/_fishing.dm b/mods/content/fishing/_fishing.dm new file mode 100644 index 000000000000..f2f9bc00d277 --- /dev/null +++ b/mods/content/fishing/_fishing.dm @@ -0,0 +1,2 @@ +/decl/modpack/fishing + name = "Fishing Modpack" \ No newline at end of file diff --git a/mods/content/fishing/_fishing.dme b/mods/content/fishing/_fishing.dme new file mode 100644 index 000000000000..c68d52fd9e9a --- /dev/null +++ b/mods/content/fishing/_fishing.dme @@ -0,0 +1,13 @@ +#ifndef CONTENT_PACK_FISHING +#define CONTENT_PACK_FISHING +// BEGIN_INCLUDE +#include "_fishing.dm" +#include "area_fishing.dm" +#include "fishing_bait.dm" +#include "fishing_designs.dm" +#include "fishing_line.dm" +#include "fishing_recipes.dm" +#include "fishing_rod.dm" +#include "turf_fishing.dm" +// END_INCLUDE +#endif diff --git a/mods/content/fishing/area_fishing.dm b/mods/content/fishing/area_fishing.dm new file mode 100644 index 000000000000..5e046d58fbc2 --- /dev/null +++ b/mods/content/fishing/area_fishing.dm @@ -0,0 +1,63 @@ +/area + var/fishing_failure_prob = 95 + // Hardcoding the contents of /obj/random/junk to avoid hacks for getting results from /obj/random. + var/list/fishing_results = list( + /obj/item/remains/mouse = 1, + /obj/item/remains/robot = 1, + /obj/item/paper/crumpled = 1, + /obj/item/inflatable/torn = 1, + /obj/item/shard = 1, + /obj/item/hand/missing_card = 1 + ) + +/area/Initialize() + var/list/additional_fishing_results = get_additional_fishing_results() + if(LAZYLEN(additional_fishing_results)) + LAZYINITLIST(fishing_results) + for(var/fish in additional_fishing_results) + fishing_results[fish] = additional_fishing_results[fish] + . = ..() + +/area/proc/get_additional_fishing_results() + return + +/area/proc/get_fishing_result(turf/origin, obj/item/food/bait) + if(!length(fishing_results) || prob(fishing_failure_prob)) + return null + return pickweight(fishing_results) + +// overrides down here + +// Let's make a token effort at making the fish somewhat alien I guess. +/area/exoplanet/get_fishing_result(turf/origin, obj/item/food/bait) + . = ..() + if(ismob(.)) + var/mob/M = . + M.SetName("xeno-[M.name]") + M.set_color(get_random_colour(simple = TRUE)) + +//Fishing results for the grass exoplanet surface +/area/exoplanet/grass + fishing_failure_prob = 10 + // TODO: waterweed? + // Hardcoding the contents of /obj/random/natural_debris to avoid hacks to get results out of /obj/random. + fishing_results = list( + /mob/living/simple_animal/aquatic/fish = 10, + /mob/living/simple_animal/aquatic/fish/grump = 10, + /obj/item/mollusc = 5, + /obj/item/mollusc/barnacle/fished = 5, + /mob/living/simple_animal/aquatic/fish/large = 5, + /mob/living/simple_animal/aquatic/fish/large/bass = 5, + /mob/living/simple_animal/aquatic/fish/large/salmon = 5, + /mob/living/simple_animal/aquatic/fish/large/trout = 5, + /mob/living/simple_animal/aquatic/fish/large/pike = 3, + /mob/living/simple_animal/aquatic/fish/large/javelin = 3, + /obj/item/mollusc/clam/fished/pearl = 3, + /obj/item/trash/mollusc_shell/clam = 2, + /obj/item/trash/mollusc_shell/barnacle = 2, + /obj/item/remains/mouse = 2, + /obj/item/remains/lizard = 2, + /obj/item/stick = 1, + /obj/item/trash/mollusc_shell = 1, + /mob/living/simple_animal/aquatic/fish/large/koi = 1 + ) \ No newline at end of file diff --git a/mods/content/fishing/fishing_bait.dm b/mods/content/fishing/fishing_bait.dm new file mode 100644 index 000000000000..a0f3db926f63 --- /dev/null +++ b/mods/content/fishing/fishing_bait.dm @@ -0,0 +1,37 @@ +// Returns a value used as a multiplier in the fishing delay calc. Higher represents a stronger reduction in fishing time. +#define BAIT_VALUE_CONSTANT 0.1 +/obj/item/proc/get_bait_value() + . = 0 + for(var/mat in matter) + var/decl/material/bait_mat = GET_DECL(mat) + if(bait_mat.fishing_bait_value) + . += MATERIAL_UNITS_TO_REAGENTS_UNITS(matter[mat]) * bait_mat.fishing_bait_value * BAIT_VALUE_CONSTANT + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) + if(reagent.fishing_bait_value) + . += REAGENT_VOLUME(reagents, reagent) * reagent.fishing_bait_value * BAIT_VALUE_CONSTANT +#undef BAIT_VALUE_CONSTANT + +/decl/material + /// A multiplier for this material when used in fishing bait. + var/fishing_bait_value = 0 + +/decl/material/solid/organic/meat + fishing_bait_value = 1 + +/decl/material/solid/organic/plantmatter + fishing_bait_value = 0.75 + +/decl/material/liquid/nutriment + fishing_bait_value = 0.65 + +/decl/material/liquid/oil + fishing_bait_value = 0 + +/decl/material/solid/organic/skin + fishing_bait_value = 0.75 + +/decl/material/solid/organic/skin/feathers + fishing_bait_value = 0 + +/decl/material/solid/organic/skin/fur + fishing_bait_value = 0 \ No newline at end of file diff --git a/mods/content/fishing/fishing_designs.dm b/mods/content/fishing/fishing_designs.dm new file mode 100644 index 000000000000..9d1a864cbccf --- /dev/null +++ b/mods/content/fishing/fishing_designs.dm @@ -0,0 +1,5 @@ +/datum/fabricator_recipe/fishing_line + path = /obj/item/fishing_line + +/datum/fabricator_recipe/fishing_line_high_quality + path = /obj/item/fishing_line/high_quality \ No newline at end of file diff --git a/code/modules/fishing/fishing_line.dm b/mods/content/fishing/fishing_line.dm similarity index 93% rename from code/modules/fishing/fishing_line.dm rename to mods/content/fishing/fishing_line.dm index 28afa6aa1a2f..7455c8af6b30 100644 --- a/code/modules/fishing/fishing_line.dm +++ b/mods/content/fishing/fishing_line.dm @@ -1,6 +1,6 @@ /obj/item/fishing_line name = "fishing line" - icon = 'icons/obj/fishing_line.dmi' + icon = 'mods/content/fishing/icons/fishing_line.dmi' icon_state = ICON_STATE_WORLD material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_DESC max_health = 100 diff --git a/mods/content/fishing/fishing_recipes.dm b/mods/content/fishing/fishing_recipes.dm new file mode 100644 index 000000000000..405773b34267 --- /dev/null +++ b/mods/content/fishing/fishing_recipes.dm @@ -0,0 +1,2 @@ +/decl/stack_recipe/planks/fishing_rod + result_type = /obj/item/fishing_rod \ No newline at end of file diff --git a/code/modules/fishing/fishing_rod.dm b/mods/content/fishing/fishing_rod.dm similarity index 98% rename from code/modules/fishing/fishing_rod.dm rename to mods/content/fishing/fishing_rod.dm index ae8342c0f677..eabc2f57cb0c 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/mods/content/fishing/fishing_rod.dm @@ -9,7 +9,7 @@ color = /decl/material/solid/organic/wood/oak::color matter = null material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC - icon = 'icons/obj/fishing_rod.dmi' + icon = 'mods/content/fishing/icons/fishing_rod.dmi' icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_LARGE @@ -346,7 +346,7 @@ matter = list( /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT ) - icon = 'icons/obj/fishing_rod_advanced.dmi' + icon = 'mods/content/fishing/icons/fishing_rod_advanced.dmi' material_alteration = MAT_FLAG_ALTERATION_COLOR fishing_rod_quality = 0.2 line = /obj/item/fishing_line/high_quality diff --git a/mods/content/fishing/icons/fishing_line.dmi b/mods/content/fishing/icons/fishing_line.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4824616fa1a6a968e6a4085f1563e3467992f3b1 GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*q$ZVhbyU2)z4*}Q$iB}&5mf8 literal 0 HcmV?d00001 diff --git a/icons/obj/fishing_rod.dmi b/mods/content/fishing/icons/fishing_rod.dmi similarity index 100% rename from icons/obj/fishing_rod.dmi rename to mods/content/fishing/icons/fishing_rod.dmi diff --git a/icons/obj/fishing_rod_advanced.dmi b/mods/content/fishing/icons/fishing_rod_advanced.dmi similarity index 100% rename from icons/obj/fishing_rod_advanced.dmi rename to mods/content/fishing/icons/fishing_rod_advanced.dmi diff --git a/mods/content/fishing/turf_fishing.dm b/mods/content/fishing/turf_fishing.dm new file mode 100644 index 000000000000..cc796cdcb3c0 --- /dev/null +++ b/mods/content/fishing/turf_fishing.dm @@ -0,0 +1,3 @@ +/turf/proc/get_fishing_result(obj/item/food/bait) + var/area/A = get_area(src) + return A.get_fishing_result(src, bait) \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index 68161da9a707..ebf1f8f1a811 100644 --- a/nebula.dme +++ b/nebula.dme @@ -783,7 +783,6 @@ #include "code\game\antagonist\antagonist_update.dm" #include "code\game\area\area_abstract.dm" #include "code\game\area\area_access.dm" -#include "code\game\area\area_fishing.dm" #include "code\game\area\area_power.dm" #include "code\game\area\area_space.dm" #include "code\game\area\areas.dm" @@ -2431,9 +2430,6 @@ #include "code\modules\fabrication\designs\textile\protective.dm" #include "code\modules\fabrication\designs\textile\space.dm" #include "code\modules\fabrication\designs\textile\storage.dm" -#include "code\modules\fishing\bait.dm" -#include "code\modules\fishing\fishing_line.dm" -#include "code\modules\fishing\fishing_rod.dm" #include "code\modules\fission\core.dm" #include "code\modules\fission\core_control.dm" #include "code\modules\fission\fission_circuits.dm" @@ -2616,6 +2612,7 @@ #include "code\modules\hydroponics\seed_mobs.dm" #include "code\modules\hydroponics\seed_packets.dm" #include "code\modules\hydroponics\seed_storage.dm" +#include "code\modules\hydroponics\worm.dm" #include "code\modules\hydroponics\plant_types\seeds_herbs.dm" #include "code\modules\hydroponics\plant_types\seeds_misc.dm" #include "code\modules\hydroponics\spreading\spreading.dm" From 79e2c2ca0e9c445ce0b2c84e81144ac85ba2cf4b Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Wed, 8 Jul 2026 23:46:41 -0400 Subject: [PATCH 1294/1331] Mark a bunch of areas as abstract --- code/game/area/area_abstract.dm | 7 +++++++ .../random_exoplanet/random_planet_areas.dm | 1 + code/modules/turbolift/turbolift_areas.dm | 1 + code/unit_tests/~unit_test_types.dm | 3 +++ maps/away/derelict/derelict_areas.dm | 3 +++ maps/away/errant_pisces/errant_pisces_areas.dm | 1 + maps/away/liberia/liberia_areas.dm | 1 + maps/away/lost_supply_base/lost_supply_base_areas.dm | 6 +----- maps/away/magshield/magshield_areas.dm | 10 ++++------ maps/away/mining/mining_areas.dm | 6 +++++- maps/away/smugglers/smugglers_areas.dm | 9 +++++---- maps/away/unishi/unishi_areas.dm | 5 +++-- maps/away/yacht/yacht_areas.dm | 11 +++++++---- maps/example/example_areas.dm | 2 ++ maps/exodus/exodus_areas.dm | 1 + maps/tradeship/tradeship_areas.dm | 2 -- maps/~mapsystem/maps_unit_testing.dm | 5 ----- .../corporate/away_sites/lar_maria/lar_maria_areas.dm | 1 + mods/content/fantasy/submaps/_submaps.dm | 1 + .../government/away_sites/icarus/icarus_areas.dm | 1 + 20 files changed, 48 insertions(+), 29 deletions(-) diff --git a/code/game/area/area_abstract.dm b/code/game/area/area_abstract.dm index 853b2907a95b..55e8c249a71c 100644 --- a/code/game/area/area_abstract.dm +++ b/code/game/area/area_abstract.dm @@ -1,9 +1,11 @@ /area/hallway + abstract_type = /area/hallway name = "hallway" holomap_color = HOLOMAP_AREACOLOR_HALLWAYS area_start_lit = TRUE /area/maintenance + abstract_type = /area/maintenance area_flags = AREA_FLAG_RAD_SHIELDED sound_env = TUNNEL_ENCLOSED turf_initializer = /decl/turf_initializer/maintenance @@ -12,6 +14,7 @@ holomap_color = HOLOMAP_AREACOLOR_HALLWAYS /area/shuttle + abstract_type = /area/shuttle requires_power = 0 sound_env = SMALL_ENCLOSED base_turf = /turf/space @@ -19,6 +22,10 @@ holomap_color = HOLOMAP_AREACOLOR_CREW /area/ship + abstract_type = /area/ship name = "\improper Generic Ship" ambience = list('sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg') holomap_color = HOLOMAP_AREACOLOR_CREW + +/area/map_template + abstract_type = /area/map_template \ No newline at end of file diff --git a/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm b/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm index 6964c4dbd2f6..2a225bae12c1 100644 --- a/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm +++ b/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm @@ -1,5 +1,6 @@ ///Windy surface /area/exoplanet + // not abstract, this can get instantiated name = "\improper Planetary surface" ambience = list( 'sound/effects/wind/wind_2_1.ogg', diff --git a/code/modules/turbolift/turbolift_areas.dm b/code/modules/turbolift/turbolift_areas.dm index 893f07ab67c9..8874a14f9985 100644 --- a/code/modules/turbolift/turbolift_areas.dm +++ b/code/modules/turbolift/turbolift_areas.dm @@ -1,5 +1,6 @@ // Used for creating the exchange areas. /area/turbolift + abstract_type = /area/turbolift name = "\improper Turbolift" base_turf = /turf/open requires_power = FALSE diff --git a/code/unit_tests/~unit_test_types.dm b/code/unit_tests/~unit_test_types.dm index 71ba1939b5ab..72fa0be9eb7c 100644 --- a/code/unit_tests/~unit_test_types.dm +++ b/code/unit_tests/~unit_test_types.dm @@ -45,6 +45,9 @@ /obj/unit_test/transparent opacity = FALSE +/area/test_area + abstract_type = /area/test_area + /area/test_area/general icon_state = "blue" diff --git a/maps/away/derelict/derelict_areas.dm b/maps/away/derelict/derelict_areas.dm index 3ac3f4cceb78..a07f86784ddf 100644 --- a/maps/away/derelict/derelict_areas.dm +++ b/maps/away/derelict/derelict_areas.dm @@ -1,3 +1,6 @@ +/area/derelict + abstract_type = /area/derelict + /area/derelict/ship name = "\improper Abandoned Ship" icon_state = "yellow" diff --git a/maps/away/errant_pisces/errant_pisces_areas.dm b/maps/away/errant_pisces/errant_pisces_areas.dm index dedee8fc0250..dc6fef3b065f 100644 --- a/maps/away/errant_pisces/errant_pisces_areas.dm +++ b/maps/away/errant_pisces/errant_pisces_areas.dm @@ -1,4 +1,5 @@ /area/errant_pisces + abstract_type = /area/errant_pisces icon = 'maps/away/errant_pisces/icons/areas.dmi' /area/errant_pisces/bow_port diff --git a/maps/away/liberia/liberia_areas.dm b/maps/away/liberia/liberia_areas.dm index de18310846c2..4e14d0cb731f 100644 --- a/maps/away/liberia/liberia_areas.dm +++ b/maps/away/liberia/liberia_areas.dm @@ -1,4 +1,5 @@ /area/liberia + abstract_type = /area/liberia req_access = list(access_merchant) /area/liberia/dockinghall diff --git a/maps/away/lost_supply_base/lost_supply_base_areas.dm b/maps/away/lost_supply_base/lost_supply_base_areas.dm index c33069e5e7a2..2f58f748ba04 100644 --- a/maps/away/lost_supply_base/lost_supply_base_areas.dm +++ b/maps/away/lost_supply_base/lost_supply_base_areas.dm @@ -6,19 +6,15 @@ /area/lost_supply_base/solar name = "\improper Abandoned supply station solars control room" icon_state = "lost_supply_base_solar" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' /area/lost_supply_base/office name = "\improper Abandoned supply station office" icon_state = "lost_supply_base_office" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' /area/lost_supply_base/supply name = "\improper Abandoned supply station supplies room" icon_state = "lost_supply_base_supply" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' /area/lost_supply_base/common name = "\improper Abandoned supply station common area" - icon_state = "lost_supply_base_common" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' \ No newline at end of file + icon_state = "lost_supply_base_common" \ No newline at end of file diff --git a/maps/away/magshield/magshield_areas.dm b/maps/away/magshield/magshield_areas.dm index 85a429168f8b..0ff5d9ace633 100644 --- a/maps/away/magshield/magshield_areas.dm +++ b/maps/away/magshield/magshield_areas.dm @@ -1,29 +1,27 @@ +/area/magshield + abstract_type = /area/magshield + icon = 'magshield_sprites.dmi' + /area/magshield/south name = "Orbital Station South Wing" icon_state = "south" - icon = 'magshield_sprites.dmi' /area/magshield/north name = "Orbital Station North Wing" icon_state = "north" - icon = 'magshield_sprites.dmi' /area/magshield/east name = "Orbital Station East Wing" icon_state = "east" - icon = 'magshield_sprites.dmi' /area/magshield/west name = "Orbital Station West Wing" icon_state = "west" - icon = 'magshield_sprites.dmi' /area/magshield/engine name = "Orbital Station Engine" icon_state = "engine" - icon = 'magshield_sprites.dmi' /area/magshield/smes_storage name = "Orbital Station SMES Battery Room" icon_state = "smes_storage" - icon = 'magshield_sprites.dmi' diff --git a/maps/away/mining/mining_areas.dm b/maps/away/mining/mining_areas.dm index 3dd6da725299..f786fa4c8447 100644 --- a/maps/away/mining/mining_areas.dm +++ b/maps/away/mining/mining_areas.dm @@ -1,5 +1,6 @@ // GENERIC MINING AREAS /area/mine + abstract_type = /area/mine icon_state = "mining" ambience = list('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg') sound_env = ASTEROID @@ -15,9 +16,12 @@ icon_state = "unexplored" // OUTPOSTS +/area/outpost + abstract_type = /area/outpost + icon_state = "dark" + /area/outpost/abandoned name = "Abandoned Outpost" - icon_state = "dark" /area/djstation name = "\improper Listening Post" diff --git a/maps/away/smugglers/smugglers_areas.dm b/maps/away/smugglers/smugglers_areas.dm index 4736cb8a0f07..be6b6277b237 100644 --- a/maps/away/smugglers/smugglers_areas.dm +++ b/maps/away/smugglers/smugglers_areas.dm @@ -1,14 +1,15 @@ +/area/smugglers + abstract_type = /area/smugglers + icon = 'smugglers_sprites.dmi' + /area/smugglers/base name = "\improper Asteroid Base" icon_state = "smgl_base" - icon = 'smugglers_sprites.dmi' /area/smugglers/office name = "\improper Asteroid Base Office" icon_state = "smgl_office" - icon = 'smugglers_sprites.dmi' /area/smugglers/dorms name = "\improper Asteroid Base Rest Area" - icon_state = "smgl_dorms" - icon = 'smugglers_sprites.dmi' \ No newline at end of file + icon_state = "smgl_dorms" \ No newline at end of file diff --git a/maps/away/unishi/unishi_areas.dm b/maps/away/unishi/unishi_areas.dm index b09d7b9fb5e5..f8e1c52698dc 100644 --- a/maps/away/unishi/unishi_areas.dm +++ b/maps/away/unishi/unishi_areas.dm @@ -1,5 +1,6 @@ -/area/unishi/ - icon = 'unishi.dmi' +/area/unishi + abstract_type = /area/unishi + icon = 'unishi.dmi' /area/unishi/bridge name = "\improper Bridge" diff --git a/maps/away/yacht/yacht_areas.dm b/maps/away/yacht/yacht_areas.dm index 92795b8720be..f85268abb671 100644 --- a/maps/away/yacht/yacht_areas.dm +++ b/maps/away/yacht/yacht_areas.dm @@ -1,12 +1,15 @@ +/area/yacht + abstract_type = /area/yacht + icon = 'yacht_icons.dmi' + /area/yacht/bridge name = "\improper Yacht Bridge" icon_state = "bridge" - icon = 'yacht_icons.dmi' + /area/yacht/living name = "\improper Yacht Living" icon_state = "living" - icon = 'yacht_icons.dmi' + /area/yacht/engine name = "\improper Yacht Engine" - icon_state = "engine" - icon = 'yacht_icons.dmi' \ No newline at end of file + icon_state = "engine" \ No newline at end of file diff --git a/maps/example/example_areas.dm b/maps/example/example_areas.dm index 097163dd93f0..39eb70c9cbd6 100644 --- a/maps/example/example_areas.dm +++ b/maps/example/example_areas.dm @@ -1,4 +1,5 @@ /area/example + abstract_type = /area/example holomap_color = HOLOMAP_AREACOLOR_CREW /area/example/first @@ -14,6 +15,7 @@ icon_state = "storage" /area/turbolift/example + abstract_type = /area/turbolift/example name = "\improper Testing Site Elevator" icon_state = "shuttle" requires_power = FALSE diff --git a/maps/exodus/exodus_areas.dm b/maps/exodus/exodus_areas.dm index 336403c6218d..516fb0985449 100644 --- a/maps/exodus/exodus_areas.dm +++ b/maps/exodus/exodus_areas.dm @@ -11,6 +11,7 @@ //Do not remove dots after comments /area/exodus + abstract_type = /area/exodus secure = TRUE holomap_color = HOLOMAP_AREACOLOR_CREW diff --git a/maps/tradeship/tradeship_areas.dm b/maps/tradeship/tradeship_areas.dm index 1d46436626ce..8451df7b1c1e 100644 --- a/maps/tradeship/tradeship_areas.dm +++ b/maps/tradeship/tradeship_areas.dm @@ -10,8 +10,6 @@ /area/ship/trade name = "\improper Tradeship" ambience = list('sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg') - -/area/ship/trade holomap_color = HOLOMAP_AREACOLOR_CREW /area/ship/trade/crew diff --git a/maps/~mapsystem/maps_unit_testing.dm b/maps/~mapsystem/maps_unit_testing.dm index 2ac13dfa9bb6..e90ef2099235 100644 --- a/maps/~mapsystem/maps_unit_testing.dm +++ b/maps/~mapsystem/maps_unit_testing.dm @@ -23,18 +23,13 @@ // These areas are used specifically by code and need to be broken out somehow var/list/area_usage_test_exempted_areas = list( - /area/ship, - /area/hallway, - /area/maintenance, /area/overmap, - /area/shuttle, /area/template_noop ) var/list/area_usage_test_exempted_root_areas = list( /area/map_template, /area/exoplanet, - /area/turbolift ) var/list/area_purity_test_exempt_areas = list() diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm b/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm index 65115dbaf271..7aa337a145e7 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm @@ -1,4 +1,5 @@ /area/lar_maria + abstract_type = /area/lar_maria icon = 'mods/content/corporate/away_sites/lar_maria/lar_maria_sprites.dmi' /////////////////////////////Upper level areas diff --git a/mods/content/fantasy/submaps/_submaps.dm b/mods/content/fantasy/submaps/_submaps.dm index da60d375fd62..b92c691ab148 100644 --- a/mods/content/fantasy/submaps/_submaps.dm +++ b/mods/content/fantasy/submaps/_submaps.dm @@ -47,6 +47,7 @@ area_flags = AREA_FLAG_EXTERNAL | AREA_FLAG_IS_BACKGROUND /area/fantasy/outside/point_of_interest + abstract_type = /area/fantasy/outside/point_of_interest name = "Point Of Interest" description = null area_blurb_category = /area/fantasy/outside/point_of_interest diff --git a/mods/content/government/away_sites/icarus/icarus_areas.dm b/mods/content/government/away_sites/icarus/icarus_areas.dm index 6ebe40524fbf..000d9a7c529e 100644 --- a/mods/content/government/away_sites/icarus/icarus_areas.dm +++ b/mods/content/government/away_sites/icarus/icarus_areas.dm @@ -1,4 +1,5 @@ /area/icarus + abstract_type = /area/icarus icon = 'mods/content/government/away_sites/icarus/icarus_sprites.dmi' /area/icarus/vessel From afa70c0cef329c9df1a73fa838f15bd4ae1c5f29 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Wed, 8 Jul 2026 23:45:06 -0400 Subject: [PATCH 1295/1331] Skip abstract areas in area usage test --- code/unit_tests/area_tests.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/unit_tests/area_tests.dm b/code/unit_tests/area_tests.dm index fafbf4d00d62..fabf408602f8 100644 --- a/code/unit_tests/area_tests.dm +++ b/code/unit_tests/area_tests.dm @@ -82,7 +82,9 @@ /datum/unit_test/areas_shall_be_used/start_test() var/unused_areas = 0 - for(var/area_type in subtypesof(/area)) + for(var/area/area_type as anything in subtypesof(/area)) + if(TYPE_IS_ABSTRACT(area_type)) + continue if(area_type in global.using_map.area_usage_test_exempted_areas) continue if(is_path_in_list(area_type, global.using_map.area_usage_test_exempted_root_areas)) From ae2d45c7f7b84147866ac7d88c6ccf0237a78cf9 Mon Sep 17 00:00:00 2001 From: Tetra Zeta Date: Wed, 8 Jul 2026 23:24:09 -0600 Subject: [PATCH 1296/1331] apply suggestion --- code/game/turfs/flooring/flooring_sand.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index 5e057b64eee7..fabb83aaf047 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -5,7 +5,7 @@ icon = 'icons/turf/flooring/sand.dmi' icon_base = "sand" icon_edge_layer = FLOOR_EDGE_SAND - color = null + color = null // autoset from material has_base_range = 4 turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/sand From 1368dba8347501defa0e611c76024f665aecf347 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Jul 2026 01:06:58 -0400 Subject: [PATCH 1297/1331] Move the holodeck into a modpack --- code/datums/trading/traders/misc.dm | 3 +- .../atmoalter/portable_atmospherics.dm | 2 + code/game/objects/__objs.dm | 2 - code/game/objects/items/books/skill/_skill.dm | 2 + code/game/objects/items/paintkit.dm | 2 + .../items/weapons/grenades/prank_grenades.dm | 17 -- .../items/weapons/grenades/spawnergrenade.dm | 2 +- .../game/objects/items/weapons/tanks/tanks.dm | 2 + code/game/objects/items/weapons/tech_disks.dm | 2 +- .../game/objects/structures/beds/rollerbed.dm | 2 + code/game/objects/structures/racks.dm | 6 - code/game/objects/structures/tables.dm | 20 -- code/game/turfs/flooring/_flooring.dm | 5 +- .../game/turfs/flooring/flooring_holowater.dm | 2 +- code/game/turfs/flooring/flooring_sand.dm | 4 +- code/game/turfs/flooring/flooring_snow.dm | 4 +- .../components/unary/unary_base.dm | 2 + .../crafting/stack_recipes/_recipe_getter.dm | 2 +- code/modules/decoration/decoration_item.dm | 2 + code/modules/economy/_worth.dm | 4 + code/modules/economy/worth_cash.dm | 4 +- code/modules/economy/worth_clothing.dm | 2 +- code/modules/economy/worth_items.dm | 8 +- code/modules/economy/worth_mob.dm | 4 + code/modules/economy/worth_obj.dm | 2 - .../imprinter/designs_misc_circuits.dm | 3 - code/modules/gemstones/_gemstone.dm | 2 + code/modules/hydroponics/seed_packets.dm | 2 + code/modules/materials/_materials.dm | 8 +- .../solids/materials_solid_metal.dm | 4 +- .../solids/materials_solid_organic.dm | 2 +- .../solids/materials_solid_wood.dm | 2 +- .../reagent_containers/food/meat/cubes.dm | 2 + .../research/design_database_analyzer.dm | 6 +- maps/modpack_testing/modpack_testing.dm | 1 + maps/~mapsystem/maps.dm | 9 - mods/content/holodeck/_holodeck.dm | 3 + mods/content/holodeck/_holodeck.dme | 18 ++ mods/content/holodeck/holo_items.dm | 8 + mods/content/holodeck/holo_mobs.dm | 61 ++++++ .../content/holodeck/holo_objects.dm | 200 +++--------------- mods/content/holodeck/holo_racks.dm | 12 ++ mods/content/holodeck/holo_tables.dm | 22 ++ mods/content/holodeck/holo_turfs.dm | 114 ++++++++++ .../holodeck/holodeck_control_circuit.dm | 0 .../holodeck/holodeck_control_console.dm | 1 + mods/content/holodeck/holodeck_designs.dm | 2 + .../content/holodeck/holodeck_programs.dm | 0 mods/content/holodeck/maps_holodeck.dm | 9 + mods/content/holodeck/trader_overrides.dm | 3 + mods/content/xenobiology/slime/items.dm | 2 +- nebula.dme | 4 - 52 files changed, 345 insertions(+), 262 deletions(-) create mode 100644 mods/content/holodeck/_holodeck.dm create mode 100644 mods/content/holodeck/_holodeck.dme create mode 100644 mods/content/holodeck/holo_items.dm create mode 100644 mods/content/holodeck/holo_mobs.dm rename code/modules/holodeck/HolodeckObjects.dm => mods/content/holodeck/holo_objects.dm (58%) create mode 100644 mods/content/holodeck/holo_racks.dm create mode 100644 mods/content/holodeck/holo_tables.dm create mode 100644 mods/content/holodeck/holo_turfs.dm rename code/game/objects/items/circuitboards/computer/holodeckcontrol.dm => mods/content/holodeck/holodeck_control_circuit.dm (100%) rename code/modules/holodeck/HolodeckControl.dm => mods/content/holodeck/holodeck_control_console.dm (99%) create mode 100644 mods/content/holodeck/holodeck_designs.dm rename code/modules/holodeck/HolodeckPrograms.dm => mods/content/holodeck/holodeck_programs.dm (100%) create mode 100644 mods/content/holodeck/maps_holodeck.dm create mode 100644 mods/content/holodeck/trader_overrides.dm diff --git a/code/datums/trading/traders/misc.dm b/code/datums/trading/traders/misc.dm index 6356be83f0d4..13c1828ffef6 100644 --- a/code/datums/trading/traders/misc.dm +++ b/code/datums/trading/traders/misc.dm @@ -119,8 +119,7 @@ /obj/item/chems/spray/waterflower = TRADER_THIS_TYPE, /obj/item/gun/launcher/pneumatic/small = TRADER_THIS_TYPE, /obj/item/gun/projectile/revolver/capgun = TRADER_THIS_TYPE, - /obj/item/clothing/mask/fakemoustache = TRADER_THIS_TYPE, - /obj/item/grenade/spawnergrenade/fake_carp = TRADER_THIS_TYPE + /obj/item/clothing/mask/fakemoustache = TRADER_THIS_TYPE ) /datum/trader/ship/replica_shop diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index b788ebfe1e8a..1b635ce452c6 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -11,6 +11,8 @@ var/start_pressure = ONE_ATMOSPHERE /obj/machinery/portable_atmospherics/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/gas_type, gas_amount in air_contents?.gas) var/decl/material/gas_data = GET_DECL(gas_type) diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index feebd9293847..7ed2505ff8c5 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -21,8 +21,6 @@ var/in_use = FALSE // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! var/armor_penetration = 0 var/anchor_fall = FALSE - /// if the obj is a holographic object spawned by the holodeck - var/holographic = FALSE ///JSON list of directions to x,y offsets to be applied to the object depending on its direction EX: @'{"NORTH":{"x":12,"y":5}, "EAST":{"x":10,"y":50}}' var/directional_offset diff --git a/code/game/objects/items/books/skill/_skill.dm b/code/game/objects/items/books/skill/_skill.dm index 39ed2b3cdfe0..7f940ad6367a 100644 --- a/code/game/objects/items/books/skill/_skill.dm +++ b/code/game/objects/items/books/skill/_skill.dm @@ -113,6 +113,8 @@ Skill books that increase your skills while you activate and hold them limit = 1 // you can only read one book at a time nerd, therefore you can only get one buff at a time /obj/item/book/skill/get_single_monetary_worth() + if(worthless) + return 0 . = max(..(), 200) + (100 * skill_req) /obj/item/book/skill/proc/check_can_read(mob/user) diff --git a/code/game/objects/items/paintkit.dm b/code/game/objects/items/paintkit.dm index 5d8602efb267..27857f5ec34e 100644 --- a/code/game/objects/items/paintkit.dm +++ b/code/game/objects/items/paintkit.dm @@ -11,6 +11,8 @@ var/custom = FALSE /obj/item/kit/get_single_monetary_worth() + if(worthless) + return 0 . = max(round(..()), (custom ? 100 : 750) * uses) // Luxury good, value is entirely artificial. /obj/item/kit/get_examine_strings(mob/user, distance, infix, suffix) diff --git a/code/game/objects/items/weapons/grenades/prank_grenades.dm b/code/game/objects/items/weapons/grenades/prank_grenades.dm index 0a884f850ec0..1a04949c9214 100644 --- a/code/game/objects/items/weapons/grenades/prank_grenades.dm +++ b/code/game/objects/items/weapons/grenades/prank_grenades.dm @@ -4,20 +4,3 @@ /obj/item/grenade/fake/detonate() active = 0 playsound(src.loc, get_sfx("explosion"), 50, 1, 30) - -/obj/item/natural_weapon/bite/fake - _base_attack_force = 0 - -/mob/living/simple_animal/hostile/carp/holodeck/fake - faction = null - natural_weapon = /obj/item/natural_weapon/bite/fake - environment_smash = 0 - ai = /datum/mob_controller/aggressive/carp/fake - -/datum/mob_controller/aggressive/carp/fake - try_destroy_surroundings = FALSE - -/obj/item/grenade/spawnergrenade/fake_carp - origin_tech = @'{"materials":2,"magnets":2,"wormholes":5}' - spawner_type = /mob/living/simple_animal/hostile/carp/holodeck/fake - deliveryamt = 4 diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index a636763ac076..4d15f289bd68 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -6,7 +6,7 @@ var/spawner_type = null // must be an object path var/deliveryamt = 1 // amount of type to deliver -/obj/item/grenade/spawnergrenade/fake_carp/detonate() +/obj/item/grenade/spawnergrenade/detonate() if(spawner_type && deliveryamt) var/turf/T = get_turf(src) playsound(T, 'sound/effects/phasein.ogg', 100, 1) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index b191b55e56da..cef5d34401a3 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -79,6 +79,8 @@ var/global/list/global/tank_gauge_cache = list() . = ..() /obj/item/tank/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/gas_type, gas_amount in air_contents?.gas) var/decl/material/gas_data = GET_DECL(gas_type) diff --git a/code/game/objects/items/weapons/tech_disks.dm b/code/game/objects/items/weapons/tech_disks.dm index c33fceca35b6..0e87df823017 100644 --- a/code/game/objects/items/weapons/tech_disks.dm +++ b/code/game/objects/items/weapons/tech_disks.dm @@ -142,4 +142,4 @@ . += "A tiny indicator on \the [src] shows it holds [data] good explorer point\s." /obj/item/disk/survey/get_base_value() - . = holographic ? 0 : (sqrt(data) * 5) + return sqrt(data) * 5 diff --git a/code/game/objects/structures/beds/rollerbed.dm b/code/game/objects/structures/beds/rollerbed.dm index d7e7f102ec6d..739c85b1f235 100644 --- a/code/game/objects/structures/beds/rollerbed.dm +++ b/code/game/objects/structures/beds/rollerbed.dm @@ -136,6 +136,8 @@ var/structure_form_type = /obj/structure/bed/roller //The deployed form path. /obj/item/roller/get_single_monetary_worth() + if(worthless) + return 0 . = structure_form_type ? atom_info_repository.get_combined_worth_for(structure_form_type) : ..() /obj/item/roller/attack_self(mob/user) diff --git a/code/game/objects/structures/racks.dm b/code/game/objects/structures/racks.dm index 4830025c3535..264000c20204 100644 --- a/code/game/objects/structures/racks.dm +++ b/code/game/objects/structures/racks.dm @@ -44,12 +44,6 @@ auto_align(used_item, click_params) return TRUE -/obj/structure/rack/holorack/dismantle_structure(mob/user) - material = null - reinf_material = null - parts_type = null - . = ..() - /obj/structure/rack/dark color = COLOR_GRAY40 diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index fb9dda2bf289..6ce7d95c8dcd 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -706,26 +706,6 @@ color = "#8f29a3" reinf_material = /decl/material/solid/glass/borosilicate -/obj/structure/table/holotable - icon_state = "holo_preview" - holographic = TRUE - color = COLOR_OFF_WHITE - material = /decl/material/solid/metal/aluminium/holographic - reinf_material = /decl/material/solid/metal/aluminium/holographic - -/obj/structure/table/holo_plastictable - icon_state = "holo_preview" - holographic = TRUE - color = COLOR_OFF_WHITE - material = /decl/material/solid/organic/plastic/holographic - reinf_material = /decl/material/solid/organic/plastic/holographic - -/obj/structure/table/holo_woodentable - holographic = TRUE - icon_state = "holo_preview" - material = /decl/material/solid/organic/wood/holographic - reinf_material = /decl/material/solid/organic/wood/holographic - //wood wood wood /obj/structure/table/wood icon_state = "solid_preview" diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index f30adf01de71..71f162a7fb52 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -89,7 +89,8 @@ var/global/list/flooring_cache = list() var/render_trenches = TRUE var/floor_layer = TURF_LAYER - var/holographic = FALSE + /// If TRUE, this turf cannot be damaged, painted, pried off, etc. + var/visual_only = FALSE var/dirt_color = /decl/material/solid/soil::color var/list/burned_states @@ -105,7 +106,7 @@ var/global/list/flooring_cache = list() if(!istype(force_material)) force_material = null - if(holographic) + if(visual_only) turf_flags = null damage_temperature = INFINITY build_type = null diff --git a/code/game/turfs/flooring/flooring_holowater.dm b/code/game/turfs/flooring/flooring_holowater.dm index 6bfe235d67e1..32f36110eef3 100644 --- a/code/game/turfs/flooring/flooring_holowater.dm +++ b/code/game/turfs/flooring/flooring_holowater.dm @@ -6,6 +6,6 @@ icon_base = "fakewater" has_base_range = null footstep_type = /decl/footsteps/water - holographic = TRUE + visual_only = TRUE constructed = TRUE uid = "floor_water_fake" diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index 5b4f13501eaa..bf59037f2356 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -43,7 +43,7 @@ /decl/flooring/sand/fake name = "holosand" desc = "Uncomfortably coarse and gritty for a hologram." - holographic = TRUE + visual_only = TRUE uid = "floor_sand_fake" /decl/flooring/fake_space @@ -52,7 +52,7 @@ icon = 'icons/turf/flooring/fake_space.dmi' icon_base = "space" has_base_range = 25 - holographic = TRUE + visual_only = TRUE gender = NEUTER uid = "floor_space_fake" diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 72362928c06e..d4922e3d30bb 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -60,7 +60,7 @@ uid = "floor_permafrost" /decl/flooring/permafrost/get_vehicle_transit_delay(obj/vehicle/vehicle) - if(holographic) + if(visual_only) return vehicle::base_speed if(vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE) return 0.8 @@ -69,6 +69,6 @@ /decl/flooring/snow/fake name = "holosnow" desc = "Not quite the same as snow on an entertainment terminal, but close." - holographic = TRUE + visual_only = TRUE uid = "floor_snow_fake" diff --git a/code/modules/atmospherics/components/unary/unary_base.dm b/code/modules/atmospherics/components/unary/unary_base.dm index 21734409daa8..0e452755498e 100644 --- a/code/modules/atmospherics/components/unary/unary_base.dm +++ b/code/modules/atmospherics/components/unary/unary_base.dm @@ -11,6 +11,8 @@ var/controlled = TRUE // if true, report to air alarm, if false, probably in direct contact with something else by radio (e.g. airlocks) /obj/machinery/atmospherics/unary/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/gas_type, gas_amount in air_contents?.gas) var/decl/material/gas_data = GET_DECL(gas_type) diff --git a/code/modules/crafting/stack_recipes/_recipe_getter.dm b/code/modules/crafting/stack_recipes/_recipe_getter.dm index cf79001263d0..7f6896fcb90a 100644 --- a/code/modules/crafting/stack_recipes/_recipe_getter.dm +++ b/code/modules/crafting/stack_recipes/_recipe_getter.dm @@ -19,7 +19,7 @@ /proc/get_stack_recipes(decl/material/mat, decl/material/reinf_mat, stack_type, tool_type, flat = FALSE) // No recipes for holograms or fluids. - if(istype(mat) && (mat.holographic || mat.phase_at_temperature() != MAT_PHASE_SOLID)) + if(istype(mat) && (mat.visual_only || mat.phase_at_temperature() != MAT_PHASE_SOLID)) return list() #ifndef UNIT_TEST // key creation is SLOW and in unit testing almost every call to this will be a cache fail diff --git a/code/modules/decoration/decoration_item.dm b/code/modules/decoration/decoration_item.dm index ae628cf68bf5..034ebbb78717 100644 --- a/code/modules/decoration/decoration_item.dm +++ b/code/modules/decoration/decoration_item.dm @@ -74,6 +74,8 @@ return TRUE /obj/item/get_single_monetary_worth() + if(worthless) + return 0 . = ..() var/base_value = . for(var/decl/item_decoration/decoration as anything in decorations) diff --git a/code/modules/economy/_worth.dm b/code/modules/economy/_worth.dm index f2f476a776b9..cceeb5e0ae74 100644 --- a/code/modules/economy/_worth.dm +++ b/code/modules/economy/_worth.dm @@ -1,4 +1,6 @@ /atom + /// If TRUE, this is worthless. Its contents will still be properly valued by get_contents_monetary_worth(), however. + var/worthless = FALSE var/monetary_worth_multiplier = 1 /atom/proc/get_base_value() @@ -8,6 +10,8 @@ . = monetary_worth_multiplier /atom/proc/get_single_monetary_worth() + if(worthless) + return 0 . = get_base_value() * get_value_multiplier() if(reagents) for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) diff --git a/code/modules/economy/worth_cash.dm b/code/modules/economy/worth_cash.dm index 49d04f24ebdf..84db32d7d5e6 100644 --- a/code/modules/economy/worth_cash.dm +++ b/code/modules/economy/worth_cash.dm @@ -42,7 +42,7 @@ update_from_worth() /obj/item/cash/get_base_value() - . = holographic ? 0 : absolute_worth + return absolute_worth /obj/item/cash/proc/set_currency(var/new_currency) currency = new_currency @@ -245,7 +245,7 @@ . += SPAN_NOTICE("[capitalize(cur.name)] remaining: [floor(loaded_worth / cur.absolute_value)].") /obj/item/charge_stick/get_base_value() - . = holographic ? 0 : loaded_worth + return loaded_worth /obj/item/charge_stick/attackby(var/obj/item/used_item, var/mob/user) var/datum/extension/lockable/lock = get_extension(src, /datum/extension/lockable) diff --git a/code/modules/economy/worth_clothing.dm b/code/modules/economy/worth_clothing.dm index bf1a039887bf..7a0a45548d39 100644 --- a/code/modules/economy/worth_clothing.dm +++ b/code/modules/economy/worth_clothing.dm @@ -1,6 +1,6 @@ /obj/item/clothing/get_base_value() . = max(..(), 10) - if(!holographic && flash_protection > 0) + if(flash_protection > 0) . += flash_protection * 25 /obj/item/clothing/head/collectable/get_value_multiplier() diff --git a/code/modules/economy/worth_items.dm b/code/modules/economy/worth_items.dm index 15998a7521f9..bea9943f41ec 100644 --- a/code/modules/economy/worth_items.dm +++ b/code/modules/economy/worth_items.dm @@ -3,10 +3,6 @@ #define BASE_ARMOUR_WORTH 50 /obj/item/get_base_value() - - if(holographic) - return 0 - . = ..() if(origin_tech) @@ -75,7 +71,7 @@ #undef MUNDANE_ARMOUR_VALUE #undef BASE_ARMOUR_WORTH -/obj/item/organ/get_single_monetary_worth() +/obj/item/organ/get_value_multiplier() . = ..() if(species) - . = round(. * species.rarity_value) + . *= species.rarity_value diff --git a/code/modules/economy/worth_mob.dm b/code/modules/economy/worth_mob.dm index 7102d7c68094..d4bb79d02ecc 100644 --- a/code/modules/economy/worth_mob.dm +++ b/code/modules/economy/worth_mob.dm @@ -5,6 +5,8 @@ . = max(round(.), mob_size) /mob/living/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/atom/movable/organ in get_organs()) . += organ.get_combined_monetary_worth() @@ -14,5 +16,7 @@ . = round(.) /mob/living/get_value_multiplier() + if(worthless) + return 0 var/decl/species/my_species = get_species() . = my_species ? my_species.rarity_value : 1 diff --git a/code/modules/economy/worth_obj.dm b/code/modules/economy/worth_obj.dm index 04b75a13fed0..3e89c43e68c0 100644 --- a/code/modules/economy/worth_obj.dm +++ b/code/modules/economy/worth_obj.dm @@ -18,8 +18,6 @@ . = length(matter) ? ..() : (material?.value || 1) /obj/get_base_value() - if(holographic) - return 0 if(length(matter)) . = 0 for(var/mat in matter) diff --git a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm index 474b8c3e4099..902b95c986f1 100644 --- a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm +++ b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm @@ -83,9 +83,6 @@ /datum/fabricator_recipe/imprinter/circuit/accounts path = /obj/item/stock_parts/circuitboard/account_database -/datum/fabricator_recipe/imprinter/circuit/holo - path = /obj/item/stock_parts/circuitboard/holodeck_control - /datum/fabricator_recipe/imprinter/circuit/aiupload path = /obj/item/stock_parts/circuitboard/aiupload diff --git a/code/modules/gemstones/_gemstone.dm b/code/modules/gemstones/_gemstone.dm index 02d698d4a130..7bee9c244a8c 100644 --- a/code/modules/gemstones/_gemstone.dm +++ b/code/modules/gemstones/_gemstone.dm @@ -35,6 +35,8 @@ var/global/list/_available_gemstone_cuts SetName("[cut.adjective] [material.solid_name]") /obj/item/gemstone/get_single_monetary_worth() + if(worthless) + return 0 . = ..() * cut.worth_multiplier /obj/item/gemstone/attackby(obj/item/used_item, mob/user) diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index c86b81ef2e98..e0a702306898 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -25,6 +25,8 @@ add_to_reagents(/decl/material/liquid/oil/plant, 3) /obj/item/seeds/get_single_monetary_worth() + if(worthless) + return 0 . = seed ? seed.get_monetary_value() : ..() // Used for extracts/seed sampling purposes. diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 02ab0d63cd48..3f4eaa2fbff7 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -278,7 +278,7 @@ var/global/list/materials_by_gas_symbol = list() var/sound_manipulate //Default sound something like a material stack made of this material does when picked up var/sound_dropped //Default sound something like a material stack made of this material does when hitting the ground or placed down - var/holographic // Set to true if this material is fake/visual only. + var/visual_only // Set to true if this material is fake/visual only. Can be used for holograms, placeholders, etc. /// Does high temperature baking change this material into something else? var/bakes_into_material @@ -338,7 +338,7 @@ var/global/list/materials_by_gas_symbol = list() hidden_from_codex = TRUE exoplanet_rarity_plant = MAT_RARITY_NOWHERE exoplanet_rarity_gas = MAT_RARITY_NOWHERE - holographic = TRUE + visual_only = TRUE // Make sure we have a use name and shard icon even if they aren't explicitly set. /decl/material/Initialize() @@ -356,7 +356,7 @@ var/global/list/materials_by_gas_symbol = list() adjective_name ||= use_name // Null/clear a bunch of physical vars as this material is fake. - if(holographic) + if(visual_only) temperature_burn_milestone_material = null can_boil_to_gas = FALSE shard_name = SHARD_NONE @@ -408,7 +408,7 @@ var/global/list/materials_by_gas_symbol = list() global.materials_by_gas_symbol[gas_symbol] = type generate_armor_values() - if(!holographic) + if(!visual_only) var/list/cocktails = decls_repository.get_decls_of_subtype(/decl/cocktail) for(var/ctype in cocktails) var/decl/cocktail/cocktail = cocktails[ctype] diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index d5abbbb03504..19c069e9bc56 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -222,7 +222,7 @@ /decl/material/solid/metal/steel/holographic name = "holographic steel" uid = "solid_holographic_steel" - holographic = TRUE + visual_only = TRUE /decl/material/solid/metal/stainlesssteel name = "stainless steel" @@ -269,7 +269,7 @@ /decl/material/solid/metal/aluminium/holographic name = "holoaluminium" uid = "solid_holographic_aluminium" - holographic = TRUE + visual_only = TRUE /decl/material/solid/metal/plasteel name = "plasteel" diff --git a/code/modules/materials/definitions/solids/materials_solid_organic.dm b/code/modules/materials/definitions/solids/materials_solid_organic.dm index be9b91441d16..1a44d59c2889 100644 --- a/code/modules/materials/definitions/solids/materials_solid_organic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_organic.dm @@ -76,7 +76,7 @@ /decl/material/solid/organic/plastic/holographic name = "holographic plastic" uid = "solid_holographic_plastic" - holographic = TRUE + visual_only = TRUE /decl/material/solid/organic/cardboard name = "cardboard" diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index 0a3b021339ba..9e44c5f6d153 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -82,7 +82,7 @@ uid = "solid_holographic_wood" color = WOOD_COLOR_CHOCOLATE //the very concept of wood should be brown adjective_name = "holowood" - holographic = TRUE + visual_only = TRUE /decl/material/solid/organic/wood/mahogany name = "mahogany" diff --git a/code/modules/reagents/reagent_containers/food/meat/cubes.dm b/code/modules/reagents/reagent_containers/food/meat/cubes.dm index 13f680708778..3dbbd1ab5172 100644 --- a/code/modules/reagents/reagent_containers/food/meat/cubes.dm +++ b/code/modules/reagents/reagent_containers/food/meat/cubes.dm @@ -36,6 +36,8 @@ add_to_reagents(/decl/material/solid/organic/meat, 10) /obj/item/food/animal_cube/get_single_monetary_worth() + if(worthless) + return 0 . = (spawn_type ? round(atom_info_repository.get_combined_worth_for((islist(spawn_type) ? spawn_type[1] : spawn_type)) * 1.25) : 5) if(wrapper_type) . += atom_info_repository.get_combined_worth_for(wrapper_type) diff --git a/code/modules/research/design_database_analyzer.dm b/code/modules/research/design_database_analyzer.dm index 6d57b82a0633..0c1d18a2ad66 100644 --- a/code/modules/research/design_database_analyzer.dm +++ b/code/modules/research/design_database_analyzer.dm @@ -80,6 +80,10 @@ D.ui_interact(user) return TRUE +/// Returns TRUE if used_item can be deconstructed, assuming it meets other criteria (tech level, etc.) +/obj/machinery/destructive_analyzer/proc/can_deconstruct(var/obj/item/used_item) + return TRUE + /obj/machinery/destructive_analyzer/attackby(var/obj/item/used_item, var/mob/user) if(IS_MULTITOOL(used_item) && !user.check_intent(I_FLAG_HARM)) @@ -106,7 +110,7 @@ return TRUE var/list/techlvls = cached_json_decode(tech) - if(!length(techlvls) || used_item.holographic) + if(!length(techlvls) || !can_deconstruct(used_item)) to_chat(user, SPAN_WARNING("You cannot deconstruct this item.")) return TRUE diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 47e16e7a41be..0c458cd98930 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -21,6 +21,7 @@ #include "../../mods/content/fantasy/_fantasy.dme" #include "../../mods/content/generic_shuttles/_generic_shuttles.dme" #include "../../mods/content/government/_government.dme" + #include "../../mods/content/holodeck/_holodeck.dme" #include "../../mods/content/inertia/_inertia.dme" #include "../../mods/content/integrated_electronics/_integrated_electronics.dme" #include "../../mods/content/item_sharpening/_item_sharpening.dme" diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index 5ddba61742de..82100776d7ee 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -74,15 +74,6 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable var/emergency_shuttle_recall_message var/emergency_shuttle_arriving_at_dock_message - var/list/holodeck_programs = list() // map of string ids to /datum/holodeck_program instances - var/list/holodeck_supported_programs = list() // map of maps - first level maps from list-of-programs string id (e.g. "BarPrograms") to another map - // this is in order to support multiple holodeck program listings for different holodecks - // second level maps from program friendly display names ("Picnic Area") to program string ids ("picnicarea") - // as defined in holodeck_programs - var/list/holodeck_restricted_programs = list() // as above... but EVIL! - var/list/holodeck_default_program = list() // map of program list string ids to default program string id - var/list/holodeck_off_program = list() // as above... but for being off i guess - var/allowed_latejoin_spawns = list( /decl/spawnpoint/arrivals ) diff --git a/mods/content/holodeck/_holodeck.dm b/mods/content/holodeck/_holodeck.dm new file mode 100644 index 000000000000..491e56bf7441 --- /dev/null +++ b/mods/content/holodeck/_holodeck.dm @@ -0,0 +1,3 @@ +/decl/modpack/holodeck + name = "Holodecks and Hardlight Holograms" + desc = "Adds holodecks and support for hardlight hologram objects." \ No newline at end of file diff --git a/mods/content/holodeck/_holodeck.dme b/mods/content/holodeck/_holodeck.dme new file mode 100644 index 000000000000..80d4c5be2cdc --- /dev/null +++ b/mods/content/holodeck/_holodeck.dme @@ -0,0 +1,18 @@ +#ifndef CONTENT_PACK_HOLODECK +#define CONTENT_PACK_HOLODECK +// BEGIN_INCLUDE +#include "_holodeck.dm" +#include "holo_items.dm" +#include "holo_mobs.dm" +#include "holo_objects.dm" +#include "holo_racks.dm" +#include "holo_tables.dm" +#include "holo_turfs.dm" +#include "holodeck_control_circuit.dm" +#include "holodeck_control_console.dm" +#include "holodeck_designs.dm" +#include "holodeck_programs.dm" +#include "maps_holodeck.dm" +#include "trader_overrides.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/mods/content/holodeck/holo_items.dm b/mods/content/holodeck/holo_items.dm new file mode 100644 index 000000000000..b62da36bd4f9 --- /dev/null +++ b/mods/content/holodeck/holo_items.dm @@ -0,0 +1,8 @@ +/obj/machinery/destructive_analyzer/can_deconstruct(var/obj/item/used_item) + if(used_item.holographic) + return FALSE + +/obj/item/grenade/spawnergrenade/fake_carp + origin_tech = @'{"materials":2,"magnets":2,"wormholes":5}' + spawner_type = /mob/living/simple_animal/hostile/carp/holodeck/fake + deliveryamt = 4 \ No newline at end of file diff --git a/mods/content/holodeck/holo_mobs.dm b/mods/content/holodeck/holo_mobs.dm new file mode 100644 index 000000000000..a9e914b20f69 --- /dev/null +++ b/mods/content/holodeck/holo_mobs.dm @@ -0,0 +1,61 @@ +//Holocarp + +/mob/living/simple_animal/hostile/carp/holodeck + icon = 'icons/mob/simple_animal/holocarp.dmi' + alpha = 127 + butchery_data = null + worthless = TRUE + +/mob/living/simple_animal/hostile/carp/holodeck/carp_randomify() + return + +/mob/living/simple_animal/hostile/carp/holodeck/on_update_icon() + SHOULD_CALL_PARENT(FALSE) + return + +/mob/living/simple_animal/hostile/carp/holodeck/Initialize() + . = ..() + set_light(2) //hologram lighting + +/mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(var/safe) + if (safe) + faction = MOB_FACTION_NEUTRAL + natural_weapon.set_base_attack_force(0) + environment_smash = 0 + ai?.try_destroy_surroundings = FALSE + else + faction = "carp" + natural_weapon.set_base_attack_force(natural_weapon.get_initial_base_attack_force()) + +/mob/living/simple_animal/hostile/carp/holodeck/gib(do_gibs = TRUE) + SHOULD_CALL_PARENT(FALSE) + if(stat != DEAD) + death(gibbed = TRUE) + if(stat == DEAD) + qdel(src) + return TRUE + return FALSE + +/mob/living/simple_animal/hostile/carp/holodeck/get_death_message(gibbed) + return "fades away..." + +/mob/living/simple_animal/hostile/carp/holodeck/get_self_death_message(gibbed) + return "You have been destroyed." + +/mob/living/simple_animal/hostile/carp/holodeck/death(gibbed) + . = ..() + if(. && !gibbed) + gib() + +// Non-dangerous holocarp +/mob/living/simple_animal/hostile/carp/holodeck/fake + faction = null + natural_weapon = /obj/item/natural_weapon/bite/fake + environment_smash = 0 + ai = /datum/mob_controller/aggressive/carp/fake + +/obj/item/natural_weapon/bite/fake + _base_attack_force = 0 + +/datum/mob_controller/aggressive/carp/fake + try_destroy_surroundings = FALSE \ No newline at end of file diff --git a/code/modules/holodeck/HolodeckObjects.dm b/mods/content/holodeck/holo_objects.dm similarity index 58% rename from code/modules/holodeck/HolodeckObjects.dm rename to mods/content/holodeck/holo_objects.dm index 407b1b196dad..d1125a62467f 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/mods/content/holodeck/holo_objects.dm @@ -3,119 +3,9 @@ // Holographic tables are in code/modules/tables/presets.dm // Holographic racks are in code/modules/tables/rack.dm -/turf/floor/holofloor - thermal_conductivity = 0 - -/turf/floor/holofloor/get_lumcount(var/minlum = 0, var/maxlum = 1) - return 0.8 - -/turf/floor/holofloor/attackby(obj/item/used_item, mob/user) - return TRUE - // HOLOFLOOR DOES NOT GIVE A FUCK - -/turf/floor/holofloor/carpet - name = "brown carpet" - icon = 'icons/turf/flooring/carpet.dmi' - icon_state = "brown" - _flooring = /decl/flooring/carpet - -/turf/floor/holofloor/concrete - name = "brown carpet" - icon = 'icons/turf/flooring/carpet.dmi' - icon_state = "brown" - _flooring = /decl/flooring/carpet - -/turf/floor/holofloor/concrete - name = "floor" - icon = 'icons/turf/flooring/misc.dmi' - icon_state = "concrete" - _flooring = null - -/turf/floor/holofloor/tiled - name = "floor" - icon = 'icons/turf/flooring/tiles.dmi' - icon_state = "steel" - _flooring = /decl/flooring/tiling - -/turf/floor/holofloor/tiled/dark - name = "dark floor" - icon_state = "dark" - _flooring = /decl/flooring/tiling/dark - -/turf/floor/holofloor/tiled/stone - name = "stone floor" - icon_state = "stone" - _flooring = /decl/flooring/tiling/stone - -/turf/floor/holofloor/lino - name = "lino" - icon = 'icons/turf/flooring/linoleum.dmi' - icon_state = "lino" - _flooring = /decl/flooring/linoleum - -/turf/floor/holofloor/wood - name = "wooden floor" - icon = 'icons/turf/flooring/wood.dmi' - icon_state = "wood0" - color = WOOD_COLOR_CHOCOLATE - _flooring = /decl/flooring/wood - -/turf/floor/holofloor/grass - name = "lush grass" - icon = 'icons/turf/flooring/fakegrass.dmi' - icon_state = "grass0" - _flooring = /decl/flooring/grass/fake - -/turf/floor/holofloor/snow - name = "snow" - icon = 'icons/turf/flooring/snow.dmi' - icon_state = "snow0" - _flooring = /decl/flooring/snow/fake - -/turf/floor/holofloor/space - name = "\proper space" - icon = 'icons/turf/flooring/fake_space.dmi' - icon_state = "space0" - _flooring = /decl/flooring/fake_space - -/turf/floor/holofloor/reinforced - name = "reinforced holofloor" - icon = 'icons/turf/flooring/tiles.dmi' - _flooring = /decl/flooring/reinforced - icon_state = "reinforced" - -/turf/floor/holofloor/beach - desc = "Uncomfortably gritty for a hologram." - icon = 'icons/misc/beach.dmi' - _flooring = /decl/flooring/sand/fake - abstract_type = /turf/floor/holofloor/beach - -/turf/floor/holofloor/beach/sand - name = "sand" - icon_state = "desert0" - -/turf/floor/holofloor/beach/coastline - name = "coastline" - icon = 'icons/misc/beach2.dmi' - icon_state = "sandwater" - _flooring = /decl/flooring/sand/fake - -/turf/floor/holofloor/beach/water - name = "water" - icon_state = "seashallow" - _flooring = /decl/flooring/fake_water - -/turf/floor/holofloor/desert - name = "desert sand" - desc = "Uncomfortably gritty for a hologram." - icon = 'icons/turf/flooring/barren.dmi' - icon_state = "barren" - _flooring = /decl/flooring/sand/fake - -/turf/floor/holofloor/desert/Initialize(var/ml) - . = ..() - if(prob(10)) - LAZYADD(decals, image('icons/turf/flooring/decals.dmi', "asteroid[rand(0,9)]")) +/obj + /// if the obj is a holographic object spawned by the holodeck + var/holographic = FALSE /obj/structure/holostool name = "stool" @@ -123,10 +13,18 @@ icon = 'icons/obj/furniture.dmi' icon_state = "stool_padded_preview" anchored = TRUE + worthless = TRUE + holographic = TRUE /obj/item/clothing/gloves/boxing/hologlove name = "boxing gloves" desc = "Because you really needed another excuse to punch your crewmates." + worthless = TRUE + holographic = TRUE + +/obj/structure/window/reinforced/holowindow + worthless = TRUE + holographic = TRUE /obj/structure/window/reinforced/holowindow/full dir = NORTHEAST @@ -148,27 +46,31 @@ // This subtype is deleted when a ready button in the same area is pressed. /obj/structure/window/reinforced/holowindow/disappearing +/obj/machinery/door/window/holowindoor + holographic = TRUE + worthless = TRUE + /obj/machinery/door/window/holowindoor/attackby(obj/item/used_item, mob/user) - if (src.operating == 1) + if (operating) return TRUE - if(src.density && istype(used_item, /obj/item) && !istype(used_item, /obj/item/card)) - playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) + if(density && istype(used_item, /obj/item) && !istype(used_item, /obj/item/card)) + playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) visible_message("\The [src] was hit by \the [used_item].") if(used_item.atom_damage_type == BRUTE || used_item.atom_damage_type == BURN) take_damage(used_item.expend_attack_force(user)) return TRUE - src.add_fingerprint(user) - if (src.allowed(user)) - if (src.density) + add_fingerprint(user) + if (allowed(user)) + if (density) open() else close() return TRUE - else if (src.density) + else if (density) flick("[base_state]deny", src) return TRUE return FALSE @@ -184,14 +86,18 @@ /obj/structure/bed/holobed tool_interaction_flags = 0 holographic = TRUE + worthless = TRUE material = /decl/material/solid/metal/aluminium/holographic /obj/structure/chair/holochair tool_interaction_flags = 0 holographic = TRUE + worthless = TRUE material = /decl/material/solid/metal/aluminium/holographic /obj/item/holo + holographic = TRUE + worthless = TRUE atom_damage_type = PAIN no_attack_log = 1 max_health = ITEM_HEALTH_NO_DAMAGE @@ -258,6 +164,8 @@ anchored = TRUE density = TRUE throwpass = 1 + holographic = TRUE + worthless = TRUE /obj/structure/holohoop/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) if (istype(mover,/obj/item) && mover.throwing) @@ -284,6 +192,8 @@ layer = TABLE_LAYER throwpass = 1 dir = EAST + holographic = TRUE + worthless = TRUE /obj/structure/holonet/end icon_state = "volleynet_end" @@ -315,6 +225,8 @@ idle_power_usage = 2 active_power_usage = 6 power_channel = ENVIRON + holographic = TRUE + worthless = TRUE /obj/machinery/readybutton/attack_ai(mob/living/silicon/ai/user) to_chat(user, "The AI is not to interact with these devices!") @@ -364,51 +276,3 @@ for(var/mob/M in currentarea) to_chat(M, "FIGHT!") - -//Holocarp - -/mob/living/simple_animal/hostile/carp/holodeck - icon = 'icons/mob/simple_animal/holocarp.dmi' - alpha = 127 - butchery_data = null - -/mob/living/simple_animal/hostile/carp/holodeck/carp_randomify() - return - -/mob/living/simple_animal/hostile/carp/holodeck/on_update_icon() - SHOULD_CALL_PARENT(FALSE) - return - -/mob/living/simple_animal/hostile/carp/holodeck/Initialize() - . = ..() - set_light(2) //hologram lighting - -/mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(var/safe) - if (safe) - faction = MOB_FACTION_NEUTRAL - natural_weapon.set_base_attack_force(0) - environment_smash = 0 - ai?.try_destroy_surroundings = FALSE - else - faction = "carp" - natural_weapon.set_base_attack_force(natural_weapon.get_initial_base_attack_force()) - -/mob/living/simple_animal/hostile/carp/holodeck/gib(do_gibs = TRUE) - SHOULD_CALL_PARENT(FALSE) - if(stat != DEAD) - death(gibbed = TRUE) - if(stat == DEAD) - qdel(src) - return TRUE - return FALSE - -/mob/living/simple_animal/hostile/carp/holodeck/get_death_message(gibbed) - return "fades away..." - -/mob/living/simple_animal/hostile/carp/holodeck/get_self_death_message(gibbed) - return "You have been destroyed." - -/mob/living/simple_animal/hostile/carp/holodeck/death(gibbed) - . = ..() - if(. && !gibbed) - gib() diff --git a/mods/content/holodeck/holo_racks.dm b/mods/content/holodeck/holo_racks.dm new file mode 100644 index 000000000000..9745f215b88a --- /dev/null +++ b/mods/content/holodeck/holo_racks.dm @@ -0,0 +1,12 @@ +/obj/structure/rack/holorack + holographic = TRUE + worthless = TRUE + color = COLOR_OFF_WHITE + material = /decl/material/solid/metal/aluminium/holographic + reinf_material = /decl/material/solid/metal/aluminium/holographic + +/obj/structure/rack/holorack/dismantle_structure(mob/user) + material = null + reinf_material = null + parts_type = null + . = ..() \ No newline at end of file diff --git a/mods/content/holodeck/holo_tables.dm b/mods/content/holodeck/holo_tables.dm new file mode 100644 index 000000000000..fe1e52be43cd --- /dev/null +++ b/mods/content/holodeck/holo_tables.dm @@ -0,0 +1,22 @@ +/obj/structure/table/holotable + icon_state = "holo_preview" + holographic = TRUE + worthless = TRUE + color = COLOR_OFF_WHITE + material = /decl/material/solid/metal/aluminium/holographic + reinf_material = /decl/material/solid/metal/aluminium/holographic + +/obj/structure/table/holo_plastictable + icon_state = "holo_preview" + holographic = TRUE + worthless = TRUE + color = COLOR_OFF_WHITE + material = /decl/material/solid/organic/plastic/holographic + reinf_material = /decl/material/solid/organic/plastic/holographic + +/obj/structure/table/holo_woodentable + holographic = TRUE + worthless = TRUE + icon_state = "holo_preview" + material = /decl/material/solid/organic/wood/holographic + reinf_material = /decl/material/solid/organic/wood/holographic \ No newline at end of file diff --git a/mods/content/holodeck/holo_turfs.dm b/mods/content/holodeck/holo_turfs.dm new file mode 100644 index 000000000000..44b28b5fc96a --- /dev/null +++ b/mods/content/holodeck/holo_turfs.dm @@ -0,0 +1,114 @@ +/turf/floor/holofloor + abstract_type = /turf/floor/holofloor + thermal_conductivity = 0 + +/turf/floor/holofloor/get_lumcount(var/minlum = 0, var/maxlum = 1) + return 0.8 + +/turf/floor/holofloor/attackby(obj/item/used_item, mob/user) + return TRUE + // HOLOFLOOR DOES NOT GIVE A FUCK + +/turf/floor/holofloor/carpet + name = "brown carpet" + icon = 'icons/turf/flooring/carpet.dmi' + icon_state = "brown" + _flooring = /decl/flooring/carpet + +/turf/floor/holofloor/concrete + name = "brown carpet" + icon = 'icons/turf/flooring/carpet.dmi' + icon_state = "brown" + _flooring = /decl/flooring/carpet + +/turf/floor/holofloor/concrete + name = "floor" + icon = 'icons/turf/flooring/misc.dmi' + icon_state = "concrete" + _flooring = null + +/turf/floor/holofloor/tiled + name = "floor" + icon = 'icons/turf/flooring/tiles.dmi' + icon_state = "steel" + _flooring = /decl/flooring/tiling + +/turf/floor/holofloor/tiled/dark + name = "dark floor" + icon_state = "dark" + _flooring = /decl/flooring/tiling/dark + +/turf/floor/holofloor/tiled/stone + name = "stone floor" + icon_state = "stone" + _flooring = /decl/flooring/tiling/stone + +/turf/floor/holofloor/lino + name = "lino" + icon = 'icons/turf/flooring/linoleum.dmi' + icon_state = "lino" + _flooring = /decl/flooring/linoleum + +/turf/floor/holofloor/wood + name = "wooden floor" + icon = 'icons/turf/flooring/wood.dmi' + icon_state = "wood0" + color = WOOD_COLOR_CHOCOLATE + _flooring = /decl/flooring/wood + +/turf/floor/holofloor/grass + name = "lush grass" + icon = 'icons/turf/flooring/fakegrass.dmi' + icon_state = "grass0" + _flooring = /decl/flooring/grass/fake + +/turf/floor/holofloor/snow + name = "snow" + icon = 'icons/turf/flooring/snow.dmi' + icon_state = "snow0" + _flooring = /decl/flooring/snow/fake + +/turf/floor/holofloor/space + name = "\proper space" + icon = 'icons/turf/flooring/fake_space.dmi' + icon_state = "space0" + _flooring = /decl/flooring/fake_space + +/turf/floor/holofloor/reinforced + name = "reinforced holofloor" + icon = 'icons/turf/flooring/tiles.dmi' + _flooring = /decl/flooring/reinforced + icon_state = "reinforced" + +/turf/floor/holofloor/beach + desc = "Uncomfortably gritty for a hologram." + icon = 'icons/misc/beach.dmi' + _flooring = /decl/flooring/sand/fake + abstract_type = /turf/floor/holofloor/beach + +/turf/floor/holofloor/beach/sand + name = "sand" + icon_state = "desert0" + +/turf/floor/holofloor/beach/coastline + name = "coastline" + icon = 'icons/misc/beach2.dmi' + icon_state = "sandwater" + _flooring = /decl/flooring/sand/fake + +/turf/floor/holofloor/beach/water + name = "water" + icon_state = "seashallow" + _flooring = /decl/flooring/fake_water + +/turf/floor/holofloor/desert + name = "desert sand" + desc = "Uncomfortably gritty for a hologram." + icon = 'icons/turf/flooring/barren.dmi' + icon_state = "barren" + _flooring = /decl/flooring/sand/fake + +/turf/floor/holofloor/desert/Initialize(var/ml) + . = ..() + if(prob(10)) + LAZYADD(decals, image('icons/turf/flooring/decals.dmi', "asteroid[rand(0,9)]")) \ No newline at end of file diff --git a/code/game/objects/items/circuitboards/computer/holodeckcontrol.dm b/mods/content/holodeck/holodeck_control_circuit.dm similarity index 100% rename from code/game/objects/items/circuitboards/computer/holodeckcontrol.dm rename to mods/content/holodeck/holodeck_control_circuit.dm diff --git a/code/modules/holodeck/HolodeckControl.dm b/mods/content/holodeck/holodeck_control_console.dm similarity index 99% rename from code/modules/holodeck/HolodeckControl.dm rename to mods/content/holodeck/holodeck_control_console.dm index f5db252b01db..80b2597f9c0e 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/mods/content/holodeck/holodeck_control_console.dm @@ -276,6 +276,7 @@ for(var/obj/holo_obj in holographic_objs) holo_obj.alpha *= 0.8 //give holodeck objs a slight transparency holo_obj.holographic = TRUE + holo_obj.worthless = TRUE if(HP.ambience) linkedholodeck.forced_ambience = HP.ambience diff --git a/mods/content/holodeck/holodeck_designs.dm b/mods/content/holodeck/holodeck_designs.dm new file mode 100644 index 000000000000..2160366208db --- /dev/null +++ b/mods/content/holodeck/holodeck_designs.dm @@ -0,0 +1,2 @@ +/datum/fabricator_recipe/imprinter/circuit/holo + path = /obj/item/stock_parts/circuitboard/holodeck_control \ No newline at end of file diff --git a/code/modules/holodeck/HolodeckPrograms.dm b/mods/content/holodeck/holodeck_programs.dm similarity index 100% rename from code/modules/holodeck/HolodeckPrograms.dm rename to mods/content/holodeck/holodeck_programs.dm diff --git a/mods/content/holodeck/maps_holodeck.dm b/mods/content/holodeck/maps_holodeck.dm new file mode 100644 index 000000000000..427fd2af9ba1 --- /dev/null +++ b/mods/content/holodeck/maps_holodeck.dm @@ -0,0 +1,9 @@ +/datum/map + var/list/holodeck_programs = list() // map of string ids to /datum/holodeck_program instances + var/list/holodeck_supported_programs = list() // map of maps - first level maps from list-of-programs string id (e.g. "BarPrograms") to another map + // this is in order to support multiple holodeck program listings for different holodecks + // second level maps from program friendly display names ("Picnic Area") to program string ids ("picnicarea") + // as defined in holodeck_programs + var/list/holodeck_restricted_programs = list() // as above... but EVIL! + var/list/holodeck_default_program = list() // map of program list string ids to default program string id + var/list/holodeck_off_program = list() // as above... but for being off i guess \ No newline at end of file diff --git a/mods/content/holodeck/trader_overrides.dm b/mods/content/holodeck/trader_overrides.dm new file mode 100644 index 000000000000..f3f957c0819e --- /dev/null +++ b/mods/content/holodeck/trader_overrides.dm @@ -0,0 +1,3 @@ +/datum/trader/ship/prank_shop/New() + LAZYSET(possible_trading_items, /obj/item/grenade/spawnergrenade/fake_carp, TRADER_THIS_TYPE) + ..() \ No newline at end of file diff --git a/mods/content/xenobiology/slime/items.dm b/mods/content/xenobiology/slime/items.dm index 26304f3e971b..28c962136637 100644 --- a/mods/content/xenobiology/slime/items.dm +++ b/mods/content/xenobiology/slime/items.dm @@ -15,7 +15,7 @@ var/Uses = 1 // uses before it goes inert var/enhanced = 0 //has it been enhanced before? -/obj/item/slime_extract/get_base_value() +/obj/item/slime_extract/get_value_multiplier() . = ..() * Uses /obj/item/slime_extract/attackby(obj/item/used_item, mob/user) diff --git a/nebula.dme b/nebula.dme index 68161da9a707..e5d014314fbf 100644 --- a/nebula.dme +++ b/nebula.dme @@ -1160,7 +1160,6 @@ #include "code\game\objects\items\circuitboards\wall.dm" #include "code\game\objects\items\circuitboards\computer\air_management.dm" #include "code\game\objects\items\circuitboards\computer\computer.dm" -#include "code\game\objects\items\circuitboards\computer\holodeckcontrol.dm" #include "code\game\objects\items\circuitboards\computer\modular.dm" #include "code\game\objects\items\circuitboards\computer\shuttle.dm" #include "code\game\objects\items\circuitboards\computer\station_alert.dm" @@ -2597,9 +2596,6 @@ #include "code\modules\holidays\holiday_hook.dm" #include "code\modules\holidays\holiday_name.dm" #include "code\modules\holidays\holiday_special.dm" -#include "code\modules\holodeck\HolodeckControl.dm" -#include "code\modules\holodeck\HolodeckObjects.dm" -#include "code\modules\holodeck\HolodeckPrograms.dm" #include "code\modules\holomap\holomap.dm" #include "code\modules\hotloading\_admin.dm" #include "code\modules\hotloading\note.dm" From a9baa0deb5ad67f9100a98eae8568e5d959daaa7 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 9 Jul 2026 12:49:08 -0400 Subject: [PATCH 1298/1331] Remove inappropriate holofloors from maps --- maps/away/derelict/derelict-station.dmm | 2 +- maps/away/mining/mining-signal.dmm | 181 +++++++++--------------- maps/ministation/ministation-2.dmm | 2 +- 3 files changed, 68 insertions(+), 117 deletions(-) diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index 46a25943bbfc..94cb83ccc6a1 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -998,7 +998,7 @@ /turf/floor/plating/airless, /area/constructionsite/hallway/fore) "dw" = ( -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/constructionsite/hallway/fore) "dx" = ( /obj/random/junk, diff --git a/maps/away/mining/mining-signal.dmm b/maps/away/mining/mining-signal.dmm index 7badf05b3232..95d27ad6286b 100644 --- a/maps/away/mining/mining-signal.dmm +++ b/maps/away/mining/mining-signal.dmm @@ -12,28 +12,28 @@ /obj/structure/rack, /obj/random/tech_supply, /obj/random/bomb_supply, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ah" = ( /obj/structure/rack, /obj/random/tech_supply, /obj/random/loot, /obj/random/loot, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ai" = ( /obj/structure/table/steel_reinforced, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aj" = ( /obj/machinery/porta_turret/stationary, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ak" = ( /obj/structure/rack, /obj/item/cell/hyper, /obj/item/cell/hyper, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "am" = ( /obj/structure/rack, @@ -45,7 +45,7 @@ dir = 4 }, /obj/machinery/door/window/brigdoor/southleft, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "an" = ( /obj/machinery/mech_recharger, @@ -53,10 +53,7 @@ /obj/structure/window/reinforced{ dir = 8 }, -/turf/floor/holofloor/tiled/dark, -/area/outpost/abandoned) -"ao" = ( -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ap" = ( /obj/structure/table, @@ -125,23 +122,23 @@ dir = 8; icon_state = "bulb1" }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aE" = ( /obj/random/trash, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aF" = ( /obj/random/technology_scanner, /obj/machinery/porta_turret/stationary, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aG" = ( /obj/machinery/light/small/emergency{ dir = 4; icon_state = "bulb1" }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aH" = ( /turf/floor/plating, @@ -257,7 +254,7 @@ /obj/machinery/light/small{ dir = 1 }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "be" = ( /obj/effect/decal/cleanable/blood, @@ -345,13 +342,6 @@ }, /turf/floor/tiled/white, /area/outpost/abandoned) -"bu" = ( -/obj/effect/decal/cleanable/dirt/visible, -/obj/effect/floor_decal/corner/purple{ - dir = 5 - }, -/turf/floor/tiled/white, -/area/outpost/abandoned) "bv" = ( /obj/effect/decal/cleanable/dirt/visible, /obj/machinery/light/small{ @@ -429,13 +419,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor, /area/outpost/abandoned) -"bE" = ( -/obj/effect/floor_decal/corner/purple{ - dir = 5 - }, -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white, -/area/outpost/abandoned) "bF" = ( /obj/machinery/light/small{ dir = 1 @@ -469,10 +452,6 @@ /obj/effect/gibspawner/human, /turf/floor/tiled/white, /area/outpost/abandoned) -"bL" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white, -/area/outpost/abandoned) "bM" = ( /obj/effect/decal/cleanable/blood, /turf/floor/tiled/white, @@ -626,10 +605,6 @@ }, /turf/floor/tiled/white, /area/outpost/abandoned) -"ck" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor, -/area/outpost/abandoned) "cl" = ( /obj/structure/hygiene/shower{ dir = 8 @@ -799,10 +774,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/tiled/airless, /area/outpost/abandoned) -"cW" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/airless, -/area/outpost/abandoned) "cX" = ( /obj/effect/decal/cleanable/blood, /obj/effect/floor_decal/corner/paleblue{ @@ -900,10 +871,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/carpet, /area/outpost/abandoned) -"dk" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/carpet/broken, -/area/outpost/abandoned) "dl" = ( /obj/effect/floor_decal/spline/fancy/wood{ dir = 6 @@ -1231,10 +1198,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/tiled/white/airless, /area/outpost/abandoned) -"es" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white/airless, -/area/outpost/abandoned) "et" = ( /obj/abstract/landmark/mapped_fluid/fuel, /obj/structure/table, @@ -1477,10 +1440,6 @@ /obj/effect/floor_decal/industrial/warning/cee, /turf/floor/plating, /area/outpost/abandoned) -"fh" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/dark, -/area/outpost/abandoned) "fi" = ( /obj/item/pen, /obj/effect/decal/cleanable/dirt/visible, @@ -1860,10 +1819,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/barren, /area/mine/explored) -"gt" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/barren, -/area/mine/explored) "gu" = ( /obj/effect/decal/cleanable/dirt/visible, /turf/floor/plating, @@ -2420,10 +2375,6 @@ }, /turf/floor/tiled/white, /area/outpost/abandoned) -"HD" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white/airless, -/area/outpost/abandoned) "IX" = ( /obj/machinery/door/firedoor, /obj/machinery/door/blast/regular/open, @@ -11822,7 +11773,7 @@ cu cU af dE -cW +cw dD dW fm @@ -12025,9 +11976,9 @@ cV cG dF dV -es -HD -HD +dW +dW +dW fF cG aa @@ -12223,7 +12174,7 @@ aa af cc cw -cW +cw ds dD dW @@ -13845,7 +13796,7 @@ dI ea dH eV -cW +cw fK af aa @@ -14044,10 +13995,10 @@ aa aa af dJ -cW +cw ez eW -cW +cw fL af aa @@ -14246,10 +14197,10 @@ cy af af dK -cW +cw dH eX -cW +cw fM af af @@ -14449,7 +14400,7 @@ cZ cG dL eb -cW +cw eY ft dH @@ -14852,7 +14803,7 @@ sb db af dN -cW +cw cw eZ fv @@ -15054,10 +15005,10 @@ tb bZ cy dH -cW +cw cw fa -cW +cw fO fY gf @@ -15247,7 +15198,7 @@ az aX az bm -bu +bt bK bQ bX @@ -15453,15 +15404,15 @@ bv bJ bQ bX -ck +by vb dd dt dP dH -cW +cw eZ -cW +cw dH af af @@ -15667,7 +15618,7 @@ cw fQ dH dH -cW +cw gy cw gK @@ -15867,8 +15818,8 @@ eC fc fx cw -cW -cW +cw +cw gm dH gE @@ -16068,7 +16019,7 @@ eg eD fd eD -cW +cw ga ga gn @@ -16076,8 +16027,8 @@ gn gF dH gP -cW -cW +cw +cw gL hl ht @@ -16279,7 +16230,7 @@ af af cy gS -cW +cw gL dH hu @@ -16453,9 +16404,9 @@ aa aa af ag -ao +dT aD -ao +dT ba bh af @@ -16655,9 +16606,9 @@ aa aa af ah -ao -ao -ao +dT +dT +dT bb bi af @@ -16857,13 +16808,13 @@ aa aa af ai -ao +dT aj af af af af -bu +bt bJ bQ af @@ -17059,11 +17010,11 @@ aa aa af aj -ao -ao +dT +dT af -ao -ao +dT +dT af bB bJ @@ -17261,11 +17212,11 @@ aa aa af ak -ao +dT aE aN -ao -ao +dT +dT bn bx bH @@ -17463,14 +17414,14 @@ aa aa af aj -ao -ao +dT +dT af bc -ao +dT af bC -bL +bJ bT bZ co @@ -17485,7 +17436,7 @@ ab ab gb ej -gt +ej fe ab gM @@ -17665,7 +17616,7 @@ aa aa af ai -ao +dT aF af af @@ -17867,8 +17818,8 @@ aa aa af am -ao -ao +dT +dT aO pb bj @@ -18069,7 +18020,7 @@ aa aa af an -ao +dT aG aP be @@ -18081,7 +18032,7 @@ bP bZ cr cL -dk +di dA dm ab @@ -19904,7 +19855,7 @@ cQ dS em eK -fh +eL dT fS af @@ -20106,8 +20057,8 @@ cQ cQ af eL -fh -fh +eL +eL eL af aa @@ -20703,7 +20654,7 @@ aH af bx bN -bL +bJ bU af af @@ -20903,7 +20854,7 @@ aH aH aI bq -bE +bC bJ bJ bP diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index 36d8182f59f5..2d036207cdf4 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -3147,7 +3147,7 @@ /obj/machinery/door/firedoor{ dir = 8 }, -/turf/floor/holofloor/lino, +/turf/floor/lino, /area/ministation/telecomms) "op" = ( /obj/machinery/light/small{ From d0cf608efaa049533d41ad5ca54ee7b71db87819 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 11 Jul 2026 09:03:19 +1000 Subject: [PATCH 1299/1331] Automatic changelog generation for PR #5395 [ci skip] --- html/changelogs/AutoChangeLog-pr-5395.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5395.yml diff --git a/html/changelogs/AutoChangeLog-pr-5395.yml b/html/changelogs/AutoChangeLog-pr-5395.yml new file mode 100644 index 000000000000..9a098d54e4ab --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5395.yml @@ -0,0 +1,4 @@ +author: 'tetra zeta ' +changes: + - {imageadd: sand resprited and converted to greyscale} +delete-after: true From 9850d4cedf20d44c057518ab59ce9dbc16f9e47f Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 11 Jul 2026 01:48:39 +0000 Subject: [PATCH 1300/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-5395.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5395.yml diff --git a/html/changelog.html b/html/changelog.html index 185b3d9def1e..e54c76e6c581 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
      +

      11 July 2026

      +

      tetra zeta updated:

      +
        +
      • sand resprited and converted to greyscale
      • +
      +

      30 May 2026

      Penelope Haze updated:

        diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 2fb6a977c186..4d94674e39a3 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15079,3 +15079,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: Deactivating message passing on the message server now prevents requests console messages from being received. They will still be logged for admins and on the in-game message server console. +2026-07-11: + 'tetra zeta ': + - imageadd: sand resprited and converted to greyscale diff --git a/html/changelogs/AutoChangeLog-pr-5395.yml b/html/changelogs/AutoChangeLog-pr-5395.yml deleted file mode 100644 index 9a098d54e4ab..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5395.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: 'tetra zeta ' -changes: - - {imageadd: sand resprited and converted to greyscale} -delete-after: true From 0d162df64b3487e288dd0c607406fb01a60b8292 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 14 Jul 2026 18:44:36 -0400 Subject: [PATCH 1301/1331] Fix incorrect holostool icon --- mods/content/holodeck/holo_objects.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/content/holodeck/holo_objects.dm b/mods/content/holodeck/holo_objects.dm index d1125a62467f..08f617da4fbd 100644 --- a/mods/content/holodeck/holo_objects.dm +++ b/mods/content/holodeck/holo_objects.dm @@ -10,7 +10,7 @@ /obj/structure/holostool name = "stool" desc = "Apply butt." - icon = 'icons/obj/furniture.dmi' + icon = 'icons/obj/stool.dmi' icon_state = "stool_padded_preview" anchored = TRUE worthless = TRUE From 295db9d592e6e2912c0a4c60edf90e8e074efdff Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 14 Jul 2026 18:44:58 -0400 Subject: [PATCH 1302/1331] Fix holostools being used outside the holodeck --- maps/away/bearcat/bearcat-1.dmm | 18 +++++++++--------- maps/tradeship/tradeship-1.dmm | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/maps/away/bearcat/bearcat-1.dmm b/maps/away/bearcat/bearcat-1.dmm index 534accc465e3..312a07c8266b 100644 --- a/maps/away/bearcat/bearcat-1.dmm +++ b/maps/away/bearcat/bearcat-1.dmm @@ -378,7 +378,7 @@ /area/ship/scrap/gambling) "aW" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -497,7 +497,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 5 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -527,7 +527,7 @@ /turf/floor/usedup, /area/ship/scrap/gambling) "bj" = ( -/obj/structure/holostool, +/obj/item/stool/padded, /obj/item/hand/missing_card, /turf/floor/usedup, /area/ship/scrap/gambling) @@ -553,7 +553,7 @@ /obj/structure/cable{ icon_state = "1-4" }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/item/hand/missing_card, /turf/floor/usedup, /area/ship/scrap/gambling) @@ -701,7 +701,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/item/hand/missing_card, /turf/floor/usedup, /area/ship/scrap/gambling) @@ -801,7 +801,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /turf/floor/tiled/dark/airless, /area/ship/scrap/crew/dorms1) "bO" = ( @@ -1019,7 +1019,7 @@ /area/ship/scrap/crew/dorms2) "cj" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -1473,7 +1473,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 5 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -1687,7 +1687,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /turf/floor/tiled/dark/airless, /area/ship/scrap/crew/dorms3) "dG" = ( diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index 34a51a33bfa2..73454f89be1f 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -2580,7 +2580,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 5 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light{ dir = 1; icon_state = "bulb1" @@ -2720,7 +2720,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/firealarm{ dir = 1; pixel_y = -21 @@ -2847,7 +2847,7 @@ /area/ship/trade/science/fabricaton) "Ib" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light{ dir = 1; icon_state = "bulb1" From dc0da5c7583da724e5409612ed923e8d581f6847 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 14 Jul 2026 18:45:41 -0400 Subject: [PATCH 1303/1331] Fix holofloor in derelict station map --- maps/away/derelict/derelict-station.dmm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index 94cb83ccc6a1..7b09f5b340c2 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -1048,7 +1048,7 @@ /obj/machinery/door/airlock/glass/command{ name = "Bridge" }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/constructionsite/hallway/fore) "dI" = ( /obj/machinery/door/airlock/glass{ From 96f3b825910872e451c6b7849edd4aa8ca2fa24c Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Wed, 15 Jul 2026 11:01:39 -0400 Subject: [PATCH 1304/1331] Fix gitignore not specifying repo root --- .gitignore | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 185303ed487c..83d58a212451 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,10 @@ *.lk *.backup *.before -codex/ -data/ -dmdoc/ -cfg/ +/codex/ +/data/ +/dmdoc/ +/cfg/ build_log.txt use_map stopserver @@ -17,9 +17,9 @@ reboot_called atupdate # ignore config, but not subdirs -!config/*/ -config/* -sql/test_db +/!config/*/ +/config/* +/sql/test_db # misc OS garbage Thumbs.db @@ -27,7 +27,7 @@ Thumbs.db:encryptable .DS_Store # vscode -.vscode/* +/.vscode/* *.code-workspace .history From c0fcf10a6c597164b8f0150ce543f12688c23aa7 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sat, 18 Jul 2026 16:27:11 -0500 Subject: [PATCH 1305/1331] lighting: Ignore zlev transition boundaries for z-stack construction --- code/modules/lighting/lighting_corner.dm | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 36926e35da48..474b5acd6414 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -191,6 +191,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, SSlighting.corner_queue += src /datum/lighting_corner/proc/generate_z_connections(direction = LIGHTING_CORNER_GENERATE_BOTH) + ASSERT(z != null) /* ZM_ALLOW_LIGHTING means that a z-turf is lighting-connected to the turf below it. So: @@ -217,26 +218,30 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, This brick is responsible for finding the corner that's directly above us, and forcibly generating the corner if it doesn't exist yet. It's just the same block of code repeated four times (for each master), plus the case of there now being no above corner, but previously having had one. We also only initialize the one corner we need rather than all four since there's no benefit to initializing them all -- if a true light needs them, it'll make them itself. + + Nebula specific: due to lighting on edges of z-levels wrapping around, this logic needs to exclude masters that are on a different Z-level. + This logic assumes that all (up to) four masters of this corner are equivalent, but this is not true of corners found via z-level transition boundaries. + Turfs with transition corners should have at least one non-transition corner, so we just ignore them. */ - if (t1 && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + if (t1?.z == z && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t1i]) && GOING_UP) if (!T.corners) T.corners = new(4) T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_UP) above_corner = T.corners[t1i] - else if (t2 && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + else if (t2?.z == z && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t2i]) && GOING_UP) if (!T.corners) T.corners = new(4) T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_UP) above_corner = T.corners[t2i] - else if (t3 && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + else if (t3?.z == z && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t3i]) && GOING_UP) if (!T.corners) T.corners = new(4) T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_UP) above_corner = T.corners[t3i] - else if (t4 && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + else if (t4?.z == z && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) if (!(above_corner = T.corners?[t4i]) && GOING_UP) if (!T.corners) T.corners = new(4) @@ -280,25 +285,25 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, SSlighting.corner_queue += corn // As above, so below. The ordering here is a bit different from the above block, check the comment at the top of this proc. - if ((t1?.z_flags & ZM_ALLOW_LIGHTING) && (T = t1.below || GET_BELOW(t1))) + if (t1?.z == z && (t1.z_flags & ZM_ALLOW_LIGHTING) && (T = t1.below || GET_BELOW(t1))) if (!(below_corner = T.corners?[t1i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_DOWN) below_corner = T.corners[t1i] - else if ((t2?.z_flags & ZM_ALLOW_LIGHTING) && (T = t2.below || GET_BELOW(t2))) + else if (t2?.z == z && (t2.z_flags & ZM_ALLOW_LIGHTING) && (T = t2.below || GET_BELOW(t2))) if (!(below_corner = T.corners?[t2i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_DOWN) below_corner = T.corners[t2i] - else if ((t3?.z_flags & ZM_ALLOW_LIGHTING) && (T = t3.below || GET_BELOW(t3))) + else if (t3?.z == z && (t3.z_flags & ZM_ALLOW_LIGHTING) && (T = t3.below || GET_BELOW(t3))) if (!(below_corner = T.corners?[t3i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_DOWN) below_corner = T.corners[t3i] - else if ((t4?.z_flags & ZM_ALLOW_LIGHTING) && (T = t4.below || GET_BELOW(t4))) + else if (t4?.z == z && (t4.z_flags & ZM_ALLOW_LIGHTING) && (T = t4.below || GET_BELOW(t4))) if (!(below_corner = T.corners?[t4i]) && GOING_DOWN) if (!T.corners) T.corners = new(4) From af6caecd33e4b5c641eedce90f76999bd2b0de8f Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sat, 18 Jul 2026 18:15:19 -0400 Subject: [PATCH 1306/1331] Update CI to 516.1685 --- .github/workflows/test.yml | 2 +- install-byond.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f402ce4ca9f..be6edf6ec395 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ on: env: BYOND_MAJOR: "516" - BYOND_MINOR: "1663" + BYOND_MINOR: "1685" SPACEMAN_DMM_VERSION: suite-1.11 jobs: diff --git a/install-byond.sh b/install-byond.sh index 5d60db277127..ae7d1be6973e 100755 --- a/install-byond.sh +++ b/install-byond.sh @@ -9,7 +9,7 @@ else cd "$HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}" echo "Installing DreamMaker to $PWD" #curl "http://www.byond.com/download/build/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -H "User-Agent: NebulaSS13/1.0 Continuous Integration" -o byond.zip - curl "https://spacestation13.github.io/byond-builds/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -H "User-Agent: NebulaSS13/1.0 Continuous Integration" -o byond.zip + curl "https://byond-builds.dm-lang.org/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -H "User-Agent: NebulaSS13/1.0 Continuous Integration" -o byond.zip unzip -o byond.zip cd byond make here From cddcf8f88ae55cf632887619384d0d907877b457 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sat, 18 Jul 2026 22:38:56 -0400 Subject: [PATCH 1307/1331] Add missing libcurl dependency --- .github/workflows/test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be6edf6ec395..dd359273ecca 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,6 +106,11 @@ jobs: with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} + - name: Install Dependencies + run: | + sudo dpkg --add-architecture i386 + sudo apt update || true + sudo apt install -o APT::Immediate-Configure=false curl:i386 - name: Run Tests env: TEST: MAP From 064044947c69a7231c881fb7566adfd17125e100 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Fri, 24 Jul 2026 01:50:56 +0000 Subject: [PATCH 1308/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 7 ------- 1 file changed, 7 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index e54c76e6c581..197d8b4966e9 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -63,13 +63,6 @@

        Penelope Haze updated:

        • Deactivating message passing on the message server now prevents requests console messages from being received. They will still be logged for admins and on the in-game message server console.
        - -

        22 May 2026

        -

        MistakeNot4892 updated:

        -
          -
        • Mech plasmacutter is full auto and mech AR full auto should now work.
        • -
        • Autofire should be more responsive in general.
        • -
      From d736dff04680b601e11a17dd08d7e4776c2dd1d1 Mon Sep 17 00:00:00 2001 From: NebulaSS13Bot Date: Sat, 1 Aug 2026 01:57:02 +0000 Subject: [PATCH 1309/1331] Automatic changelog generation [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 197d8b4966e9..2a482a38ae9e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,12 +57,6 @@

      tetra zeta updated:

      • sand resprited and converted to greyscale
      - -

      30 May 2026

      -

      Penelope Haze updated:

      -
        -
      • Deactivating message passing on the message server now prevents requests console messages from being received. They will still be logged for admins and on the in-game message server console.
      • -
    From 5ecc068a249fb7b2a17a06c27c13401ce6371204 Mon Sep 17 00:00:00 2001 From: Zandario Date: Fri, 31 Jul 2026 23:39:53 -0400 Subject: [PATCH 1310/1331] Fix typo in ore image rotation matrix --- code/modules/materials/stack_types/material_stack_ore.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/materials/stack_types/material_stack_ore.dm b/code/modules/materials/stack_types/material_stack_ore.dm index 391166a8bb14..3bff7ccab9fc 100644 --- a/code/modules/materials/stack_types/material_stack_ore.dm +++ b/code/modules/materials/stack_types/material_stack_ore.dm @@ -56,7 +56,7 @@ //Randomize the orientation and position of each ores in the image var/matrix/M = matrix() M.Translate(rand(-6, 6), rand(-6, 6)) - M.Turn(pick(-72, -58, -45, -27.-5, 0, 0, 0, 0, 0, 27.5, 45, 58, 72)) + M.Turn(pick(-72, -58, -45, -27.5, 0, 0, 0, 0, 0, 27.5, 45, 58, 72)) var/image/oreoverlay = image('icons/obj/materials/ore.dmi', IS) oreoverlay.transform = M scrapboard.overlays += oreoverlay From ea54528cef8df9ca096ceb16be0a61e86292ccf8 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sun, 5 Jul 2026 19:24:44 -0400 Subject: [PATCH 1311/1331] Move turbolifts into a modpack --- code/controllers/subsystems/mapping.dm | 8 +++--- code/controllers/subsystems/misc_late.dm | 9 +++---- code/modules/turbolift/turbolift_turfs.dm | 2 -- maps/example/example_shuttles.dm | 2 +- maps/exodus/exodus_elevator.dm | 2 +- maps/ministation/ministation_shuttles.dm | 2 +- maps/tradeship/tradeship.dm | 2 ++ maps/tradeship/tradeship_shuttles.dm | 23 ------------------ maps/tradeship/tradeship_turbolift.dm | 22 +++++++++++++++++ mods/_modpack.dm | 8 ++++++ mods/content/turbolift/_turbolift.dm | 3 +++ mods/content/turbolift/_turbolift.dme | 14 +++++++++++ .../content/turbolift/icons/door}/door.dmi | Bin .../turbolift/icons/door}/fill_glass.dmi | Bin .../turbolift/icons/door}/fill_steel.dmi | Bin .../turbolift/icons/door}/lights_bolts.dmi | Bin .../turbolift/icons/door}/lights_deny.dmi | Bin .../turbolift/icons/door}/lights_green.dmi | Bin .../content/turbolift/icons}/turbolift.dmi | Bin .../icons}/turbolift_preview_3x3.dmi | Bin .../icons}/turbolift_preview_5x5.dmi | Bin .../icons}/turbolift_preview_nowalls_3x3.dmi | Bin .../icons}/turbolift_preview_nowalls_4x4.dmi | Bin .../content}/turbolift/turbolift.dm | 0 .../content}/turbolift/turbolift_areas.dm | 0 .../content}/turbolift/turbolift_console.dm | 2 +- .../content}/turbolift/turbolift_door.dm | 12 ++++----- .../content}/turbolift/turbolift_floor.dm | 0 mods/content/turbolift/turbolift_init.dm | 16 ++++++++++++ .../content}/turbolift/turbolift_map.dm | 11 ++++++--- mods/content/turbolift/turbolift_turfs.dm | 2 ++ nebula.dme | 7 ------ 32 files changed, 90 insertions(+), 57 deletions(-) delete mode 100644 code/modules/turbolift/turbolift_turfs.dm create mode 100644 maps/tradeship/tradeship_turbolift.dm create mode 100644 mods/content/turbolift/_turbolift.dm create mode 100644 mods/content/turbolift/_turbolift.dme rename {icons/obj/doors/elevator => mods/content/turbolift/icons/door}/door.dmi (100%) rename {icons/obj/doors/elevator => mods/content/turbolift/icons/door}/fill_glass.dmi (100%) rename {icons/obj/doors/elevator => mods/content/turbolift/icons/door}/fill_steel.dmi (100%) rename {icons/obj/doors/elevator => mods/content/turbolift/icons/door}/lights_bolts.dmi (100%) rename {icons/obj/doors/elevator => mods/content/turbolift/icons/door}/lights_deny.dmi (100%) rename {icons/obj/doors/elevator => mods/content/turbolift/icons/door}/lights_green.dmi (100%) rename {icons/obj => mods/content/turbolift/icons}/turbolift.dmi (100%) rename {icons/obj => mods/content/turbolift/icons}/turbolift_preview_3x3.dmi (100%) rename {icons/obj => mods/content/turbolift/icons}/turbolift_preview_5x5.dmi (100%) rename {icons/obj => mods/content/turbolift/icons}/turbolift_preview_nowalls_3x3.dmi (100%) rename {icons/obj => mods/content/turbolift/icons}/turbolift_preview_nowalls_4x4.dmi (100%) rename {code/modules => mods/content}/turbolift/turbolift.dm (100%) rename {code/modules => mods/content}/turbolift/turbolift_areas.dm (100%) rename {code/modules => mods/content}/turbolift/turbolift_console.dm (98%) rename {code/modules => mods/content}/turbolift/turbolift_door.dm (77%) rename {code/modules => mods/content}/turbolift/turbolift_floor.dm (100%) create mode 100644 mods/content/turbolift/turbolift_init.dm rename {code/modules => mods/content}/turbolift/turbolift_map.dm (94%) create mode 100644 mods/content/turbolift/turbolift_turfs.dm diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 78253db4d1d9..90cdbebd8c4a 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -48,8 +48,6 @@ SUBSYSTEM_DEF(mapping) var/base_floor_area /// A list of connected z-levels to avoid repeatedly rebuilding connections var/list/connected_z_cache = list() - /// A list of turbolift holders to initialize. - var/list/turbolifts_to_initialize = list() ///Associative list of planetoid/exoplanet data currently registered. The key is the planetoid id, the value is the planetoid_data datum. var/list/planetoid_data_by_id ///List of all z-levels in the world where the index corresponds to a z-level, and the key at that index is the planetoid_data datum for the associated planet @@ -169,9 +167,9 @@ SUBSYSTEM_DEF(mapping) global.level_persistence_ref_map.Cut() - // Generate turbolifts last, since away sites may have elevators to generate too. - for(var/obj/abstract/turbolift_spawner/turbolift as anything in turbolifts_to_initialize) - turbolift.build_turbolift() + for(var/modpack_name in SSmodpacks.loaded_modpacks) + var/decl/modpack/loaded_modpack = SSmodpacks.loaded_modpacks[modpack_name] + loaded_modpack.on_mapping_pre_finalize() // With levels set up and serde complete (and levels flagged) we can do any remaining level generation. global.using_map.finalize_map_generation() diff --git a/code/controllers/subsystems/misc_late.dm b/code/controllers/subsystems/misc_late.dm index 7f9ceb8ce6ed..45f0efac2384 100644 --- a/code/controllers/subsystems/misc_late.dm +++ b/code/controllers/subsystems/misc_late.dm @@ -3,18 +3,15 @@ SUBSYSTEM_DEF(misc_late) name = "Late Initialization" init_order = SS_INIT_MISC_LATE flags = SS_NO_FIRE - var/list/turbolifts_to_open = list() /datum/controller/subsystem/misc_late/Initialize() var/decl/asset_cache/asset_cache = GET_DECL(/decl/asset_cache) asset_cache.load() - // This is gross but I'm not sure where else to handle it. Sorry. - for(var/datum/turbolift/lift in turbolifts_to_open) - if(!QDELETED(lift)) - lift.open_doors() - turbolifts_to_open.Cut() + for(var/modpack_name in SSmodpacks.loaded_modpacks) + var/decl/modpack/loaded_modpack = SSmodpacks.loaded_modpacks[modpack_name] + loaded_modpack.on_misc_late_init() // Pre-populate the emote list. decls_repository.get_decls_of_type(/decl/emote) diff --git a/code/modules/turbolift/turbolift_turfs.dm b/code/modules/turbolift/turbolift_turfs.dm deleted file mode 100644 index 045790c25297..000000000000 --- a/code/modules/turbolift/turbolift_turfs.dm +++ /dev/null @@ -1,2 +0,0 @@ -/turf/wall/elevator/Initialize(var/ml) - . = ..(ml, /decl/material/solid/metal/alienalloy/elevatorium) diff --git a/maps/example/example_shuttles.dm b/maps/example/example_shuttles.dm index bcc91db25c60..cccd4c24903c 100644 --- a/maps/example/example_shuttles.dm +++ b/maps/example/example_shuttles.dm @@ -31,7 +31,7 @@ /obj/abstract/turbolift_spawner/example name = "Testing Site elevator placeholder" - icon = 'icons/obj/turbolift_preview_nowalls_3x3.dmi' + icon = 'mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi' depth = 3 lift_size_x = 2 lift_size_y = 2 diff --git a/maps/exodus/exodus_elevator.dm b/maps/exodus/exodus_elevator.dm index a455e5ad5bd6..82176c89dc2a 100644 --- a/maps/exodus/exodus_elevator.dm +++ b/maps/exodus/exodus_elevator.dm @@ -23,7 +23,7 @@ /obj/abstract/turbolift_spawner/exodus/engineering name = "Exodus turbolift map placeholder - Engineering" - icon = 'icons/obj/turbolift_preview_3x3.dmi' + icon = 'mods/content/turbolift/icons/turbolift_preview_3x3.dmi' dir = EAST lift_size_x = 4 lift_size_y = 4 diff --git a/maps/ministation/ministation_shuttles.dm b/maps/ministation/ministation_shuttles.dm index af90fe8838ea..a79f5ae62438 100644 --- a/maps/ministation/ministation_shuttles.dm +++ b/maps/ministation/ministation_shuttles.dm @@ -73,7 +73,7 @@ // Essentially a bare platform that moves up and down. /obj/abstract/turbolift_spawner/ministation name = "Tradestation cargo elevator placeholder" -// icon = 'icons/obj/turbolift_preview_nowalls_3x3.dmi' +// icon = 'mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi' depth = 3 lift_size_x = 2 lift_size_y = 2 diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 2101d73bb84f..36be422ad7a2 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -34,6 +34,7 @@ #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" + #include "../../mods/content/turbolift/_turbolift.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/content/exploration/_exploration.dme" @@ -85,6 +86,7 @@ #include "tradeship_overrides.dm" #include "tradeship_shuttles.dm" #include "tradeship_spawnpoints.dm" + #include "tradeship_turbolift.dm" #include "tradeship_unit_testing.dm" #include "tradeship-0.dmm" #include "tradeship-1.dmm" diff --git a/maps/tradeship/tradeship_shuttles.dm b/maps/tradeship/tradeship_shuttles.dm index c309ad625a2d..66b5728ba50c 100644 --- a/maps/tradeship/tradeship_shuttles.dm +++ b/maps/tradeship/tradeship_shuttles.dm @@ -43,26 +43,3 @@ /obj/effect/shuttle_landmark/below_deck_starboardastern name = "Near CSV Tradeship Starboard Astern" landmark_tag = "nav_tradeship_below_starboardastern" - -// Essentially a bare platform that moves up and down. -/obj/abstract/turbolift_spawner/tradeship - name = "Tradeship cargo elevator placeholder" - icon = 'icons/obj/turbolift_preview_nowalls_4x4.dmi' - depth = 4 - lift_size_x = 3 - lift_size_y = 3 - door_type = null - wall_type = null - firedoor_type = null - light_type = null - floor_type = /turf/floor/tiled/steel_grid - button_type = /obj/structure/lift/button/standalone - panel_type = /obj/structure/lift/panel/standalone - areas_to_use = list( - /area/turbolift/tradeship_enclave, - /area/turbolift/tradeship_cargo, - /area/turbolift/tradeship_upper, - /area/turbolift/tradeship_roof - ) - floor_departure_sound = 'sound/effects/lift_heavy_start.ogg' - floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' diff --git a/maps/tradeship/tradeship_turbolift.dm b/maps/tradeship/tradeship_turbolift.dm new file mode 100644 index 000000000000..2b8ecabee462 --- /dev/null +++ b/maps/tradeship/tradeship_turbolift.dm @@ -0,0 +1,22 @@ +// Essentially a bare platform that moves up and down. +/obj/abstract/turbolift_spawner/tradeship + name = "Tradeship cargo elevator placeholder" + icon = 'mods/content/turbolift/icons/turbolift_preview_nowalls_4x4.dmi' + depth = 4 + lift_size_x = 3 + lift_size_y = 3 + door_type = null + wall_type = null + firedoor_type = null + light_type = null + floor_type = /turf/floor/tiled/steel_grid + button_type = /obj/structure/lift/button/standalone + panel_type = /obj/structure/lift/panel/standalone + areas_to_use = list( + /area/turbolift/tradeship_enclave, + /area/turbolift/tradeship_cargo, + /area/turbolift/tradeship_upper, + /area/turbolift/tradeship_roof + ) + floor_departure_sound = 'sound/effects/lift_heavy_start.ogg' + floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' diff --git a/mods/_modpack.dm b/mods/_modpack.dm index 0fb1d5d79089..b9bad3954441 100644 --- a/mods/_modpack.dm +++ b/mods/_modpack.dm @@ -75,6 +75,14 @@ /decl/modpack/proc/on_roundstart() return +/// This runs before `global.using_map.finalize_map_generation()` in SSmapping initialize. +/decl/modpack/proc/on_mapping_pre_finalize() + return + +/// This runs in SSmisc_late Initialize. +/decl/modpack/proc/on_misc_late_init() + return + /decl/modpack/proc/get_membership_perks() return diff --git a/mods/content/turbolift/_turbolift.dm b/mods/content/turbolift/_turbolift.dm new file mode 100644 index 000000000000..794dac19865d --- /dev/null +++ b/mods/content/turbolift/_turbolift.dm @@ -0,0 +1,3 @@ +/decl/modpack/turbolift + name = "Turbolifts" + desc = "Adds elevators and supporting code." \ No newline at end of file diff --git a/mods/content/turbolift/_turbolift.dme b/mods/content/turbolift/_turbolift.dme new file mode 100644 index 000000000000..27c46ca72c84 --- /dev/null +++ b/mods/content/turbolift/_turbolift.dme @@ -0,0 +1,14 @@ +#ifndef MODPACK_TURBOLIFT +#define MODPACK_TURBOLIFT +// BEGIN_INCLUDE +#include "_turbolift.dm" +#include "turbolift.dm" +#include "turbolift_areas.dm" +#include "turbolift_console.dm" +#include "turbolift_door.dm" +#include "turbolift_floor.dm" +#include "turbolift_init.dm" +#include "turbolift_map.dm" +#include "turbolift_turfs.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/icons/obj/doors/elevator/door.dmi b/mods/content/turbolift/icons/door/door.dmi similarity index 100% rename from icons/obj/doors/elevator/door.dmi rename to mods/content/turbolift/icons/door/door.dmi diff --git a/icons/obj/doors/elevator/fill_glass.dmi b/mods/content/turbolift/icons/door/fill_glass.dmi similarity index 100% rename from icons/obj/doors/elevator/fill_glass.dmi rename to mods/content/turbolift/icons/door/fill_glass.dmi diff --git a/icons/obj/doors/elevator/fill_steel.dmi b/mods/content/turbolift/icons/door/fill_steel.dmi similarity index 100% rename from icons/obj/doors/elevator/fill_steel.dmi rename to mods/content/turbolift/icons/door/fill_steel.dmi diff --git a/icons/obj/doors/elevator/lights_bolts.dmi b/mods/content/turbolift/icons/door/lights_bolts.dmi similarity index 100% rename from icons/obj/doors/elevator/lights_bolts.dmi rename to mods/content/turbolift/icons/door/lights_bolts.dmi diff --git a/icons/obj/doors/elevator/lights_deny.dmi b/mods/content/turbolift/icons/door/lights_deny.dmi similarity index 100% rename from icons/obj/doors/elevator/lights_deny.dmi rename to mods/content/turbolift/icons/door/lights_deny.dmi diff --git a/icons/obj/doors/elevator/lights_green.dmi b/mods/content/turbolift/icons/door/lights_green.dmi similarity index 100% rename from icons/obj/doors/elevator/lights_green.dmi rename to mods/content/turbolift/icons/door/lights_green.dmi diff --git a/icons/obj/turbolift.dmi b/mods/content/turbolift/icons/turbolift.dmi similarity index 100% rename from icons/obj/turbolift.dmi rename to mods/content/turbolift/icons/turbolift.dmi diff --git a/icons/obj/turbolift_preview_3x3.dmi b/mods/content/turbolift/icons/turbolift_preview_3x3.dmi similarity index 100% rename from icons/obj/turbolift_preview_3x3.dmi rename to mods/content/turbolift/icons/turbolift_preview_3x3.dmi diff --git a/icons/obj/turbolift_preview_5x5.dmi b/mods/content/turbolift/icons/turbolift_preview_5x5.dmi similarity index 100% rename from icons/obj/turbolift_preview_5x5.dmi rename to mods/content/turbolift/icons/turbolift_preview_5x5.dmi diff --git a/icons/obj/turbolift_preview_nowalls_3x3.dmi b/mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi similarity index 100% rename from icons/obj/turbolift_preview_nowalls_3x3.dmi rename to mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi diff --git a/icons/obj/turbolift_preview_nowalls_4x4.dmi b/mods/content/turbolift/icons/turbolift_preview_nowalls_4x4.dmi similarity index 100% rename from icons/obj/turbolift_preview_nowalls_4x4.dmi rename to mods/content/turbolift/icons/turbolift_preview_nowalls_4x4.dmi diff --git a/code/modules/turbolift/turbolift.dm b/mods/content/turbolift/turbolift.dm similarity index 100% rename from code/modules/turbolift/turbolift.dm rename to mods/content/turbolift/turbolift.dm diff --git a/code/modules/turbolift/turbolift_areas.dm b/mods/content/turbolift/turbolift_areas.dm similarity index 100% rename from code/modules/turbolift/turbolift_areas.dm rename to mods/content/turbolift/turbolift_areas.dm diff --git a/code/modules/turbolift/turbolift_console.dm b/mods/content/turbolift/turbolift_console.dm similarity index 98% rename from code/modules/turbolift/turbolift_console.dm rename to mods/content/turbolift/turbolift_console.dm index 03c3be6b0e44..934603a19047 100644 --- a/code/modules/turbolift/turbolift_console.dm +++ b/mods/content/turbolift/turbolift_console.dm @@ -1,7 +1,7 @@ // Base type, do not use. /obj/structure/lift name = "turbolift control component" - icon = 'icons/obj/turbolift.dmi' + icon = 'mods/content/turbolift/icons/turbolift.dmi' anchored = TRUE density = FALSE layer = ABOVE_OBJ_LAYER diff --git a/code/modules/turbolift/turbolift_door.dm b/mods/content/turbolift/turbolift_door.dm similarity index 77% rename from code/modules/turbolift/turbolift_door.dm rename to mods/content/turbolift/turbolift_door.dm index f1254ef4f35c..faed7d4ea29e 100644 --- a/code/modules/turbolift/turbolift_door.dm +++ b/mods/content/turbolift/turbolift_door.dm @@ -5,12 +5,12 @@ autoclose = 0 glass = 1 airlock_type = "Lift" - icon = 'icons/obj/doors/elevator/door.dmi' - fill_file = 'icons/obj/doors/elevator/fill_steel.dmi' - glass_file = 'icons/obj/doors/elevator/fill_glass.dmi' - bolts_file = 'icons/obj/doors/elevator/lights_bolts.dmi' - deny_file = 'icons/obj/doors/elevator/lights_deny.dmi' - lights_file = 'icons/obj/doors/elevator/lights_green.dmi' + icon = 'mods/content/turbolift/icons/door/door.dmi' + fill_file = 'mods/content/turbolift/icons/door/fill_steel.dmi' + glass_file = 'mods/content/turbolift/icons/door/fill_glass.dmi' + bolts_file = 'mods/content/turbolift/icons/door/lights_bolts.dmi' + deny_file = 'mods/content/turbolift/icons/door/lights_deny.dmi' + lights_file = 'mods/content/turbolift/icons/door/lights_green.dmi' paintable = PAINT_WINDOW_PAINTABLE diff --git a/code/modules/turbolift/turbolift_floor.dm b/mods/content/turbolift/turbolift_floor.dm similarity index 100% rename from code/modules/turbolift/turbolift_floor.dm rename to mods/content/turbolift/turbolift_floor.dm diff --git a/mods/content/turbolift/turbolift_init.dm b/mods/content/turbolift/turbolift_init.dm new file mode 100644 index 000000000000..f5010303d300 --- /dev/null +++ b/mods/content/turbolift/turbolift_init.dm @@ -0,0 +1,16 @@ +/decl/modpack/turbolift + /// A list of turbolift holders to initialize. + var/list/obj/abstract/turbolift_spawner/turbolifts_to_initialize = list() + /// A list of turbolift datums whose currently-selected floor will open on misc-late init. + var/list/datum/turbolift/turbolifts_to_open = list() + +/decl/modpack/turbolift/on_mapping_pre_finalize() + // Generate turbolifts last, since away sites may have elevators to generate too. + for(var/obj/abstract/turbolift_spawner/turbolift as anything in turbolifts_to_initialize) + turbolift.build_turbolift() + +/decl/modpack/turbolift/on_misc_late_init() + for(var/datum/turbolift/lift in turbolifts_to_open) + if(!QDELETED(lift)) + lift.open_doors() + turbolifts_to_open.Cut() diff --git a/code/modules/turbolift/turbolift_map.dm b/mods/content/turbolift/turbolift_map.dm similarity index 94% rename from code/modules/turbolift/turbolift_map.dm rename to mods/content/turbolift/turbolift_map.dm index d69a39fd5bb8..eeed3f1976a5 100644 --- a/code/modules/turbolift/turbolift_map.dm +++ b/mods/content/turbolift/turbolift_map.dm @@ -1,7 +1,7 @@ // Map object. /obj/abstract/turbolift_spawner name = "turbolift map placeholder" - icon = 'icons/obj/turbolift_preview_3x3.dmi' + icon = 'mods/content/turbolift/icons/turbolift_preview_3x3.dmi' dir = SOUTH // Direction of the holder determines the placement of the lift control panel and doors. var/depth = 1 // Number of floors to generate, including the initial floor. var/lift_size_x = 2 // Number of turfs on each axis to generate in addition to the first @@ -31,10 +31,12 @@ INITIALIZE_IMMEDIATE(/obj/abstract/turbolift_spawner) if(SSmapping.initialized) build_turbolift() else - SSmapping.turbolifts_to_initialize += src + var/decl/modpack/turbolift/turbolift_modpack = IMPLIED_DECL + turbolift_modpack.turbolifts_to_initialize += src /obj/abstract/turbolift_spawner/Destroy() - SSmapping.turbolifts_to_initialize -= src + var/decl/modpack/turbolift/turbolift_modpack = IMPLIED_DECL + turbolift_modpack.turbolifts_to_initialize -= src return ..() /obj/abstract/turbolift_spawner/proc/build_turbolift() @@ -254,6 +256,7 @@ INITIALIZE_IMMEDIATE(/obj/abstract/turbolift_spawner) if(SSmisc_late.initialized) lift.open_doors() else - SSmisc_late.turbolifts_to_open += lift + var/decl/modpack/turbolift/turbolift_modpack = IMPLIED_DECL + turbolift_modpack.turbolifts_to_open += lift qdel(src) // We're done. diff --git a/mods/content/turbolift/turbolift_turfs.dm b/mods/content/turbolift/turbolift_turfs.dm new file mode 100644 index 000000000000..4095ad376e6d --- /dev/null +++ b/mods/content/turbolift/turbolift_turfs.dm @@ -0,0 +1,2 @@ +/turf/wall/elevator + material = /decl/material/solid/metal/alienalloy/elevatorium diff --git a/nebula.dme b/nebula.dme index f904c51d0309..4966607f9e09 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3933,13 +3933,6 @@ #include "code\modules\turbines\largeturbine.dm" #include "code\modules\turbines\smallturbine.dm" #include "code\modules\turbines\turbine_circuits.dm" -#include "code\modules\turbolift\turbolift.dm" -#include "code\modules\turbolift\turbolift_areas.dm" -#include "code\modules\turbolift\turbolift_console.dm" -#include "code\modules\turbolift\turbolift_door.dm" -#include "code\modules\turbolift\turbolift_floor.dm" -#include "code\modules\turbolift\turbolift_map.dm" -#include "code\modules\turbolift\turbolift_turfs.dm" #include "code\modules\vehicles\bike.dm" #include "code\modules\vehicles\cargo_train.dm" #include "code\modules\vehicles\cargo_trolley.dm" From 7eb8b505d8c280fd0d9fe37a1a6e5bb250f6c412 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sun, 5 Jul 2026 19:29:59 -0400 Subject: [PATCH 1312/1331] Remove turbolifts from example modpack --- maps/example/example-1.dmm | 78 ++++++-------------- maps/example/example-2.dmm | 36 +++++++--- maps/example/example-3.dmm | 103 +++++++++++++-------------- maps/example/example_areas.dm | 35 --------- maps/example/example_shuttles.dm | 20 ------ maps/example/example_unit_testing.dm | 1 - 6 files changed, 95 insertions(+), 178 deletions(-) diff --git a/maps/example/example-1.dmm b/maps/example/example-1.dmm index 500a16664c7c..77809d5fc129 100644 --- a/maps/example/example-1.dmm +++ b/maps/example/example-1.dmm @@ -141,12 +141,6 @@ "gO" = ( /turf/wall/titanium, /area/shuttle/ferry) -"gT" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 6 - }, -/turf/floor/tiled/steel_grid, -/area/example/first) "he" = ( /obj/effect/floor_decal/industrial/warning{ dir = 4 @@ -225,9 +219,7 @@ dir = 1 }, /obj/structure/ladder, -/obj/effect/floor_decal/industrial/warning{ - dir = 4 - }, +/obj/effect/floor_decal/industrial/warning/full, /turf/floor/tiled/dark/monotile, /area/example/first) "mo" = ( @@ -427,10 +419,6 @@ /obj/machinery/light, /turf/floor/tiled/steel_grid, /area/example/first) -"tA" = ( -/obj/abstract/turbolift_spawner/example, -/turf/floor, -/area/turbolift/example/first) "uD" = ( /obj/machinery/light, /turf/floor, @@ -652,15 +640,6 @@ /obj/machinery/light, /turf/floor/tiled/steel_grid, /area/example/first) -"FZ" = ( -/obj/effect/floor_decal/industrial/warning/corner{ - dir = 1 - }, -/obj/machinery/light{ - dir = 4 - }, -/turf/floor, -/area/example/first) "Gg" = ( /obj/structure/railing/mapped{ dir = 1 @@ -762,15 +741,9 @@ }, /turf/floor/tiled/steel_grid, /area/example/first) -"LM" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 1 - }, -/turf/floor, -/area/example/first) "LP" = ( -/turf/floor/plating, -/area/turbolift/example/first) +/turf/floor/tiled/dark/monotile, +/area/example/first) "LW" = ( /obj/machinery/teleport/station, /turf/floor/tiled/dark/monotile, @@ -810,12 +783,6 @@ /obj/effect/floor_decal/industrial/outline/red, /turf/floor/tiled/dark/monotile, /area/example/first) -"Pp" = ( -/obj/effect/floor_decal/industrial/warning/corner{ - dir = 4 - }, -/turf/floor, -/area/example/first) "Pv" = ( /obj/structure/rack, /obj/item/gun/projectile/shotgun/pump, @@ -871,9 +838,6 @@ }, /turf/floor/tiled/steel_grid, /area/example/first) -"RW" = ( -/turf/floor, -/area/turbolift/example/first) "Ss" = ( /obj/structure/tank_rack/oxygen, /obj/effect/floor_decal/corner/orange/half{ @@ -932,10 +896,10 @@ /turf/floor, /area/example/first) "Wi" = ( -/obj/effect/floor_decal/industrial/warning/fulltile, -/obj/effect/floor_decal/industrial/warning{ - dir = 8 +/obj/effect/floor_decal/corner/orange{ + dir = 6 }, +/obj/effect/floor_decal/industrial/warning, /turf/floor/tiled/steel_grid, /area/example/first) "Wj" = ( @@ -2892,9 +2856,9 @@ XZ XZ XZ mc -xT -gT -Pp +CU +WW +Yp VY kw kw @@ -2946,9 +2910,9 @@ KT oz XZ LP -LP -tA -LM +CU +WW +Yp VY kw kw @@ -3000,9 +2964,9 @@ CU oz XZ LP -LP -RW -LM +CU +WW +Yp on he tt @@ -3054,9 +3018,9 @@ CU oz XZ LP -LP -RW -LM +CU +WW +Yp Yp WF WF @@ -3107,10 +3071,10 @@ Mc CU oz XZ +LP +hA Wi -Wi -Wi -FZ +WS Yp jP HA diff --git a/maps/example/example-2.dmm b/maps/example/example-2.dmm index 51b091fc0d04..6e3749b02a98 100644 --- a/maps/example/example-2.dmm +++ b/maps/example/example-2.dmm @@ -130,6 +130,9 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/structure/railing/mapped{ + dir = 4 + }, /turf/floor/tiled/dark/monotile, /area/example/second) "kf" = ( @@ -495,6 +498,9 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/structure/railing/mapped{ + dir = 4 + }, /turf/floor/tiled/steel_grid, /area/example/second) "Gn" = ( @@ -649,14 +655,24 @@ }, /turf/floor, /area/example/second) +"Vp" = ( +/obj/structure/ladder, +/obj/structure/railing/mapped{ + dir = 4 + }, +/turf/open, +/area/example/second) "VG" = ( /obj/machinery/fabricator/bioprinter, /obj/effect/floor_decal/corner/blue/mono, /turf/floor/tiled/white/monotile, /area/example/second) "VL" = ( +/obj/structure/sign/warning/fall{ + pixel_y = 32 + }, /turf/open, -/area/turbolift/example/second) +/area/example/second) "WW" = ( /obj/effect/floor_decal/corner/mauve/mono, /obj/machinery/recycler, @@ -2588,7 +2604,7 @@ sA XE XE XE -NY +Vp jW FE Gn @@ -2643,8 +2659,8 @@ qV CW XE VL -VL -VL +wP +wP Gn wP wP @@ -2696,9 +2712,9 @@ CW CW CW XE -VL -VL -VL +wP +wP +wP Gn wP wP @@ -2750,9 +2766,9 @@ CW CW CW XE -VL -VL -VL +wP +wP +wP Gn wP wP diff --git a/maps/example/example-3.dmm b/maps/example/example-3.dmm index 018cb124270b..37b7b892fb99 100644 --- a/maps/example/example-3.dmm +++ b/maps/example/example-3.dmm @@ -10,9 +10,6 @@ /obj/effect/floor_decal/industrial/warning/dust, /turf/floor, /area/example/third) -"cj" = ( -/turf/open, -/area/turbolift/example/third) "cw" = ( /obj/machinery/atmospherics/portables_connector, /obj/machinery/portable_atmospherics/canister/empty, @@ -273,10 +270,6 @@ }, /turf/floor, /area/example/third) -"TM" = ( -/obj/structure/catwalk, -/turf/open, -/area/example/third) "TO" = ( /obj/machinery/atmospherics/pipe/simple/hidden, /obj/effect/floor_decal/industrial/warning/dust{ @@ -1818,9 +1811,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -1872,9 +1865,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -1926,9 +1919,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -1980,9 +1973,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2034,9 +2027,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2088,9 +2081,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2142,9 +2135,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2195,11 +2188,11 @@ Ke Ke Ke Ke -TM -TM -TM -TM -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2249,11 +2242,11 @@ Ke Ke Ke Ke -TM -cj -cj -cj -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2303,11 +2296,11 @@ Ke Ke Ke Ke -TM -cj -cj -cj -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2357,11 +2350,11 @@ Ke Ke Ke Ke -TM -cj -cj -cj -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2411,11 +2404,11 @@ Ke Ke Ke Ke -TM Ke Ke Ke -TM +Ke +Ke Ke Ke Ke @@ -2465,11 +2458,11 @@ Ke Ke Ke Ke -TM -TM -TM -TM -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke diff --git a/maps/example/example_areas.dm b/maps/example/example_areas.dm index 39eb70c9cbd6..856a1b9daa29 100644 --- a/maps/example/example_areas.dm +++ b/maps/example/example_areas.dm @@ -14,41 +14,6 @@ name = "\improper Testing Site Third Floor" icon_state = "storage" -/area/turbolift/example - abstract_type = /area/turbolift/example - name = "\improper Testing Site Elevator" - icon_state = "shuttle" - requires_power = FALSE - dynamic_lighting = TRUE - sound_env = STANDARD_STATION - area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED - ambience = list( - 'sound/ambience/ambigen3.ogg', - 'sound/ambience/ambigen4.ogg', - 'sound/ambience/ambigen5.ogg', - 'sound/ambience/ambigen6.ogg', - 'sound/ambience/ambigen7.ogg', - 'sound/ambience/ambigen8.ogg', - 'sound/ambience/ambigen9.ogg', - 'sound/ambience/ambigen10.ogg', - 'sound/ambience/ambigen11.ogg', - 'sound/ambience/ambigen12.ogg' - ) - arrival_sound = null - lift_announce_str = null - - base_turf = /turf/open - -/area/turbolift/example/first - name = "Testing Site First Floor Lift" - base_turf = /turf/floor/plating - -/area/turbolift/example/second - name = "Testing Site Second Floor Lift" - -/area/turbolift/example/third - name = "Testing Site Third Floor Lift" - /area/shuttle/ferry name = "\improper Testing Site Ferry" icon_state = "shuttle" diff --git a/maps/example/example_shuttles.dm b/maps/example/example_shuttles.dm index cccd4c24903c..e78882e4162a 100644 --- a/maps/example/example_shuttles.dm +++ b/maps/example/example_shuttles.dm @@ -29,23 +29,3 @@ ) ceiling_type = /turf/floor/shuttle_ceiling -/obj/abstract/turbolift_spawner/example - name = "Testing Site elevator placeholder" - icon = 'mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi' - depth = 3 - lift_size_x = 2 - lift_size_y = 2 - door_type = null - wall_type = null - firedoor_type = null - light_type = null - floor_type = /turf/floor/tiled/techfloor - button_type = /obj/structure/lift/button/standalone - panel_type = /obj/structure/lift/panel/standalone - areas_to_use = list( - /area/turbolift/example/first, - /area/turbolift/example/second, - /area/turbolift/example/third - ) - floor_departure_sound = 'sound/effects/lift_heavy_start.ogg' - floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' diff --git a/maps/example/example_unit_testing.dm b/maps/example/example_unit_testing.dm index df857c78a41c..906851bd4fb5 100644 --- a/maps/example/example_unit_testing.dm +++ b/maps/example/example_unit_testing.dm @@ -3,7 +3,6 @@ apc_test_exempt_areas = list( /area/space = NO_SCRUBBER|NO_VENT|NO_APC, /area/exoplanet = NO_SCRUBBER|NO_VENT|NO_APC, - /area/turbolift/example = NO_SCRUBBER|NO_VENT|NO_APC, /area/shuttle/ferry = NO_SCRUBBER|NO_VENT|NO_APC ) From 039ff1526f456934782ccfe2649e5d9584deb1d8 Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Sun, 5 Jul 2026 19:32:00 -0400 Subject: [PATCH 1313/1331] Add turbolift modpack to maps --- maps/exodus/exodus.dm | 1 + maps/ministation/ministation.dm | 1 + maps/modpack_testing/modpack_testing.dm | 1 + 3 files changed, 3 insertions(+) diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 3553f269e878..5f5fa25e9fcd 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -24,6 +24,7 @@ #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/content/exploration/_exploration.dme" #include "../../mods/content/tabloids/_tabloids.dme" + #include "../../mods/content/turbolift/_turbolift.dme" #include "../../mods/gamemodes/cult/_cult.dme" #include "../../mods/gamemodes/heist/_heist.dme" diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index cf34155e64f2..5d97d3401df0 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -32,6 +32,7 @@ Twice... #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/turbolift/_turbolift.dme" #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index fdb35649f5c0..6e506a09cf34 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -38,6 +38,7 @@ #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/tabloids/_tabloids.dme" + #include "../../mods/content/turbolift/_turbolift.dme" #include "../../mods/content/undead/_undead.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" From be2adc6732ba805008445a431d2dcdc5e84286ac Mon Sep 17 00:00:00 2001 From: Noelle Lavenza Date: Mon, 6 Jul 2026 11:56:28 -0400 Subject: [PATCH 1314/1331] Fix offset unit test with turbolift modpack unloaded --- code/unit_tests/offset_tests.dm | 1 - mods/content/turbolift/turbolift_console.dm | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/code/unit_tests/offset_tests.dm b/code/unit_tests/offset_tests.dm index b4605420adc7..fb1537227ff2 100644 --- a/code/unit_tests/offset_tests.dm +++ b/code/unit_tests/offset_tests.dm @@ -45,7 +45,6 @@ var/static/list/exception_types = list( /obj/machinery/light, /obj/machinery/camera, - /obj/structure/lift/button/standalone, /obj/structure/hygiene/sink ) diff --git a/mods/content/turbolift/turbolift_console.dm b/mods/content/turbolift/turbolift_console.dm index 934603a19047..4f09b77619fe 100644 --- a/mods/content/turbolift/turbolift_console.dm +++ b/mods/content/turbolift/turbolift_console.dm @@ -72,6 +72,7 @@ update_icon() /obj/structure/lift/button/standalone + directional_offset = null icon_state = "plinth" /obj/structure/lift/button/on_update_icon() From d1c416ae723dc9c33c8fb9e5b89735e11efe7071 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 10 Sep 2025 00:54:42 -0400 Subject: [PATCH 1315/1331] Move singularity engine into its own module --- .../{power => }/singularity/collector.dm | 0 .../singularity/containment_field.dm | 0 .../{power => }/singularity/emitter.dm | 0 .../singularity/field_generator.dm | 0 .../{power => }/singularity/generator.dm | 0 .../particle_accelerator/particle.dm | 0 .../particle_accelerator.dm | 0 .../particle_accelerator/particle_chamber.dm | 0 .../particle_accelerator/particle_control.dm | 0 .../particle_accelerator/particle_emitter.dm | 0 .../particle_accelerator/particle_power.dm | 0 .../{power => }/singularity/singularity.dm | 0 .../singularity/singularity_events.dm | 0 .../singularity/singularity_stages.dm | 0 nebula.dme | 28 +++++++++---------- 15 files changed, 14 insertions(+), 14 deletions(-) rename code/modules/{power => }/singularity/collector.dm (100%) rename code/modules/{power => }/singularity/containment_field.dm (100%) rename code/modules/{power => }/singularity/emitter.dm (100%) rename code/modules/{power => }/singularity/field_generator.dm (100%) rename code/modules/{power => }/singularity/generator.dm (100%) rename code/modules/{power => }/singularity/particle_accelerator/particle.dm (100%) rename code/modules/{power => }/singularity/particle_accelerator/particle_accelerator.dm (100%) rename code/modules/{power => }/singularity/particle_accelerator/particle_chamber.dm (100%) rename code/modules/{power => }/singularity/particle_accelerator/particle_control.dm (100%) rename code/modules/{power => }/singularity/particle_accelerator/particle_emitter.dm (100%) rename code/modules/{power => }/singularity/particle_accelerator/particle_power.dm (100%) rename code/modules/{power => }/singularity/singularity.dm (100%) rename code/modules/{power => }/singularity/singularity_events.dm (100%) rename code/modules/{power => }/singularity/singularity_stages.dm (100%) diff --git a/code/modules/power/singularity/collector.dm b/code/modules/singularity/collector.dm similarity index 100% rename from code/modules/power/singularity/collector.dm rename to code/modules/singularity/collector.dm diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/singularity/containment_field.dm similarity index 100% rename from code/modules/power/singularity/containment_field.dm rename to code/modules/singularity/containment_field.dm diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/singularity/emitter.dm similarity index 100% rename from code/modules/power/singularity/emitter.dm rename to code/modules/singularity/emitter.dm diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/singularity/field_generator.dm similarity index 100% rename from code/modules/power/singularity/field_generator.dm rename to code/modules/singularity/field_generator.dm diff --git a/code/modules/power/singularity/generator.dm b/code/modules/singularity/generator.dm similarity index 100% rename from code/modules/power/singularity/generator.dm rename to code/modules/singularity/generator.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/singularity/particle_accelerator/particle.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle.dm rename to code/modules/singularity/particle_accelerator/particle.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/singularity/particle_accelerator/particle_accelerator.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_accelerator.dm rename to code/modules/singularity/particle_accelerator/particle_accelerator.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_chamber.dm b/code/modules/singularity/particle_accelerator/particle_chamber.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_chamber.dm rename to code/modules/singularity/particle_accelerator/particle_chamber.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/singularity/particle_accelerator/particle_control.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_control.dm rename to code/modules/singularity/particle_accelerator/particle_control.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm b/code/modules/singularity/particle_accelerator/particle_emitter.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_emitter.dm rename to code/modules/singularity/particle_accelerator/particle_emitter.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_power.dm b/code/modules/singularity/particle_accelerator/particle_power.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_power.dm rename to code/modules/singularity/particle_accelerator/particle_power.dm diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/singularity/singularity.dm similarity index 100% rename from code/modules/power/singularity/singularity.dm rename to code/modules/singularity/singularity.dm diff --git a/code/modules/power/singularity/singularity_events.dm b/code/modules/singularity/singularity_events.dm similarity index 100% rename from code/modules/power/singularity/singularity_events.dm rename to code/modules/singularity/singularity_events.dm diff --git a/code/modules/power/singularity/singularity_stages.dm b/code/modules/singularity/singularity_stages.dm similarity index 100% rename from code/modules/power/singularity/singularity_stages.dm rename to code/modules/singularity/singularity_stages.dm diff --git a/nebula.dme b/nebula.dme index f904c51d0309..be70c91e3ece 100644 --- a/nebula.dme +++ b/nebula.dme @@ -3470,20 +3470,6 @@ #include "code\modules\power\cable\heavycable.dm" #include "code\modules\power\cell\_cell.dm" #include "code\modules\power\cell\cell_types.dm" -#include "code\modules\power\singularity\collector.dm" -#include "code\modules\power\singularity\containment_field.dm" -#include "code\modules\power\singularity\emitter.dm" -#include "code\modules\power\singularity\field_generator.dm" -#include "code\modules\power\singularity\generator.dm" -#include "code\modules\power\singularity\singularity.dm" -#include "code\modules\power\singularity\singularity_events.dm" -#include "code\modules\power\singularity\singularity_stages.dm" -#include "code\modules\power\singularity\particle_accelerator\particle.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_accelerator.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_chamber.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_control.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_emitter.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_power.dm" #include "code\modules\power\solar\solar_control.dm" #include "code\modules\power\solar\solar_panel.dm" #include "code\modules\power\solar\tracker.dm" @@ -3805,6 +3791,20 @@ #include "code\modules\shuttles\shuttle_specops.dm" #include "code\modules\shuttles\shuttle_supply.dm" #include "code\modules\shuttles\shuttles_multi.dm" +#include "code\modules\singularity\collector.dm" +#include "code\modules\singularity\containment_field.dm" +#include "code\modules\singularity\emitter.dm" +#include "code\modules\singularity\field_generator.dm" +#include "code\modules\singularity\generator.dm" +#include "code\modules\singularity\singularity.dm" +#include "code\modules\singularity\singularity_events.dm" +#include "code\modules\singularity\singularity_stages.dm" +#include "code\modules\singularity\particle_accelerator\particle.dm" +#include "code\modules\singularity\particle_accelerator\particle_accelerator.dm" +#include "code\modules\singularity\particle_accelerator\particle_chamber.dm" +#include "code\modules\singularity\particle_accelerator\particle_control.dm" +#include "code\modules\singularity\particle_accelerator\particle_emitter.dm" +#include "code\modules\singularity\particle_accelerator\particle_power.dm" #include "code\modules\smes\_smes.dm" #include "code\modules\smes\smes_buildable.dm" #include "code\modules\smes\smes_circuit.dm" From 6f4701490a924e380736b40ba78d171546a6f023 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 7 Aug 2026 15:24:30 -0400 Subject: [PATCH 1316/1331] Move emitters from singulo module to game/machinery --- .../singularity => game/machinery}/emitter.dm | 49 ++++++++++--------- nebula.dme | 2 +- 2 files changed, 27 insertions(+), 24 deletions(-) rename code/{modules/singularity => game/machinery}/emitter.dm (93%) diff --git a/code/modules/singularity/emitter.dm b/code/game/machinery/emitter.dm similarity index 93% rename from code/modules/singularity/emitter.dm rename to code/game/machinery/emitter.dm index 192376cc994f..d46f3e2c2ec4 100644 --- a/code/modules/singularity/emitter.dm +++ b/code/game/machinery/emitter.dm @@ -12,16 +12,19 @@ var/efficiency = 0.3 // Energy efficiency. 30% at this time, so 100kW load means 30kW laser pulses. var/minimum_power = 10 KILOWATTS // The minimum power below which the emitter will turn off; different than the power needed to fire. - var/active = 0 + var/active = FALSE var/fire_delay = 100 var/max_burst_delay = 100 var/min_burst_delay = 20 var/burst_shots = 3 var/last_shot = 0 var/shot_number = 0 - var/state = 0 - var/locked = 0 - var/powered = 0 + var/const/STATE_UNSECURE = 0 + var/const/STATE_BOLTED = 1 + var/const/STATE_WELDED = 2 + var/state = STATE_UNSECURE + var/locked = FALSE + var/powered = FALSE core_skill = SKILL_ENGINES uncreated_component_parts = list( @@ -39,7 +42,7 @@ /obj/machinery/emitter/anchored anchored = TRUE - state = 2 + state = STATE_WELDED /obj/machinery/emitter/Destroy() log_and_message_admins("deleted \the [src]") @@ -62,15 +65,15 @@ if(!istype(user)) user = null // safety, as the proc is publicly available. - if(state == 2) + if(state == STATE_WELDED) if(!locked) - if(active==1) - active = 0 + if(active) + active = FALSE to_chat(user, "You turn off \the [src].") log_and_message_admins("turned off \the [src]", user) investigate_log("turned off by [key_name_admin(user)]","singulo") else - active = 1 + active = TRUE if(user) operator_skill = user.get_skill_value(core_skill) update_efficiency() @@ -99,11 +102,11 @@ /obj/machinery/emitter/Process() if(stat & (BROKEN)) return - if(state != 2) + if(state != STATE_WELDED) active = FALSE update_icon() return - if(((last_shot + fire_delay) <= world.time) && (active == 1)) + if(((last_shot + fire_delay) <= world.time) && active) if(active_power_usage - can_use_power_oneoff(active_power_usage) < minimum_power) powered = FALSE update_icon() @@ -140,21 +143,21 @@ to_chat(user, "Turn off [src] first.") return TRUE switch(state) - if(0) - state = 1 + if(STATE_UNSECURE) + state = STATE_BOLTED playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) user.visible_message("[user.name] secures [src] to the floor.", \ "You secure the external reinforcing bolts to the floor.", \ "You hear a ratchet.") anchored = TRUE - if(1) - state = 0 + if(STATE_BOLTED) + state = STATE_UNSECURE playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) user.visible_message("[user.name] unsecures [src] reinforcing bolts from the floor.", \ "You undo the external reinforcing bolts.", \ "You hear a ratchet.") anchored = FALSE - if(2) + if(STATE_WELDED) to_chat(user, "\The [src] needs to be unwelded from the floor.") return TRUE @@ -164,9 +167,9 @@ to_chat(user, "Turn off [src] first.") return TRUE switch(state) - if(0) + if(STATE_UNSECURE) to_chat(user, "\The [src] needs to be wrenched to the floor.") - if(1) + if(STATE_BOLTED) if (!welder.weld(0,user)) to_chat(user, "You need more welding fuel to complete this task.") return TRUE @@ -177,9 +180,9 @@ if (!do_after(user, 2 SECONDS, src)) return TRUE if(!src || !welder.isOn()) return TRUE - state = 2 + state = STATE_WELDED to_chat(user, "You weld [src] to the floor.") - if(2) + if(STATE_WELDED) if (welder.weld(0,user)) playsound(loc, 'sound/items/Welder2.ogg', 50, 1) user.visible_message("[user.name] starts to cut [src] free from the floor.", \ @@ -188,7 +191,7 @@ if (!do_after(user, 2 SECONDS, src)) return TRUE if(!src || !welder.isOn()) return TRUE - state = 1 + state = STATE_BOLTED to_chat(user, "You cut [src] free from the floor.") else to_chat(user, "You need more welding fuel to complete this task.") @@ -208,8 +211,8 @@ /obj/machinery/emitter/emag_act(var/remaining_charges, var/mob/user) if(!emagged) - locked = 0 - emagged = 1 + locked = FALSE + emagged = TRUE req_access.Cut() user.visible_message("[user.name] emags [src].","You short out the lock.") return 1 diff --git a/nebula.dme b/nebula.dme index be70c91e3ece..a4f48252924b 100644 --- a/nebula.dme +++ b/nebula.dme @@ -834,6 +834,7 @@ #include "code\game\machinery\dehumidifier.dm" #include "code\game\machinery\deployable.dm" #include "code\game\machinery\doppler_array.dm" +#include "code\game\machinery\emitter.dm" #include "code\game\machinery\flasher.dm" #include "code\game\machinery\floodlight.dm" #include "code\game\machinery\floor_light.dm" @@ -3793,7 +3794,6 @@ #include "code\modules\shuttles\shuttles_multi.dm" #include "code\modules\singularity\collector.dm" #include "code\modules\singularity\containment_field.dm" -#include "code\modules\singularity\emitter.dm" #include "code\modules\singularity\field_generator.dm" #include "code\modules\singularity\generator.dm" #include "code\modules\singularity\singularity.dm" From 1114dc46df942dc08554611111369aa70ad3816d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 7 Aug 2026 17:14:48 -0400 Subject: [PATCH 1317/1331] Make emitters use construction states --- .../machine_construction/_construction.dm | 1 + .../machine_construction/emitter.dm | 217 ++++++++++++++++++ code/game/machinery/emitter.dm | 114 +++------ code/modules/fusion/gyrotron/gyrotron.dm | 4 +- maps/away/derelict/derelict-station.dmm | 12 +- maps/exodus/exodus-2.dmm | 6 +- maps/ministation/ministation-0.dmm | 6 +- nebula.dme | 1 + .../5401_emitter_construct_state.txt | 8 + 9 files changed, 264 insertions(+), 105 deletions(-) create mode 100644 code/game/machinery/_machines_base/machine_construction/emitter.dm create mode 100644 tools/map_migrations/5401_emitter_construct_state.txt diff --git a/code/game/machinery/_machines_base/machine_construction/_construction.dm b/code/game/machinery/_machines_base/machine_construction/_construction.dm index 3555ee7173f1..72679df311c5 100644 --- a/code/game/machinery/_machines_base/machine_construction/_construction.dm +++ b/code/game/machinery/_machines_base/machine_construction/_construction.dm @@ -14,6 +14,7 @@ // Called on state transition; can intercept, but must call parent. /obj/machinery/proc/state_transition(var/decl/machine_construction/new_state, var/mob/user) + SHOULD_CALL_PARENT(TRUE) construct_state = new_state // Return a change state define or a fail message to block transition. diff --git a/code/game/machinery/_machines_base/machine_construction/emitter.dm b/code/game/machinery/_machines_base/machine_construction/emitter.dm new file mode 100644 index 000000000000..f1b87c2ad6a4 --- /dev/null +++ b/code/game/machinery/_machines_base/machine_construction/emitter.dm @@ -0,0 +1,217 @@ +// Emitters are not screwed apart like most machines; they are bolted down with a wrench and then welded in place. +// Some subtypes like gyrotrons also use panel_state to gain the usual maintenance hatch on top of that. +// Yes I hate this, yes it should be done differently, no I do not have it in me to do it any other way. +// Maybe we should just bite the bullet and make gyrotrons not emitters?? +/decl/machine_construction/emitter + visible_components = FALSE + /// The state entered when the emitter is fastened down further, if any. + var/down_state + /// The state entered when the emitter is loosened, if any. + var/up_state + /// Whether the emitter is anchored to the floor in this state. + var/anchored = FALSE + // gyrotron stuff below + /// The state entered when the maintenance hatch is toggled via screwdriver. Null if the emitter has no hatch. + var/panel_state + /// Whether the maintenance hatch is open in this state. + var/panel_open = FALSE + +/decl/machine_construction/emitter/state_is_valid(obj/machinery/machine) + return (machine.anchored == anchored) && (machine.panel_open == panel_open) + +/decl/machine_construction/emitter/validate_state(obj/machinery/machine) + . = ..() + if(!.) + if(machine.panel_open != panel_open) + try_change_state(machine, panel_state) + else + try_change_state(machine, machine.anchored ? down_state : up_state) + +// the panel starts open after construction, again taken from /decl/machine_construction/default/panel_closed +/decl/machine_construction/emitter/post_construct(obj/machinery/machine) + if(!panel_state || panel_open) + return + try_change_state(machine, panel_state) + machine.panel_open = TRUE + machine.queue_icon_update() + +/// Handles a wrench applied to the emitter in this state. Return TRUE if the interaction was handled. +/decl/machine_construction/emitter/proc/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + return FALSE + +/// Handles a welding tool applied to the emitter in this state. Return TRUE if the interaction was handled. +/decl/machine_construction/emitter/proc/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + return FALSE + +/decl/machine_construction/emitter/attackby(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + if((. = ..())) + return + if(machine.active) // can't open/close/unweld/etc while operating + to_chat(user, SPAN_WARNING("Turn \the [machine] off first.")) + return TRUE + if(IS_WRENCH(used_item)) + return wrench_interaction(used_item, user, machine) + else if(IS_WELDER(used_item)) + return welder_interaction(used_item, user, machine) + // everything after this is for gyrotrons/etc + if(!panel_state) + return FALSE + // handle this here because otherwise we'd have some nasty code duplication + if(IS_SCREWDRIVER(used_item)) + TRANSFER_STATE(panel_state) + playsound(get_turf(machine), 'sound/items/Screwdriver.ogg', 50, 1) + machine.panel_open = !panel_open + to_chat(user, SPAN_NOTICE("You [machine.panel_open ? "open" : "close"] the maintenance hatch of \the [machine].")) + machine.update_icon() // could be done in a machinery level /state_transition() override but whatever + return TRUE + // sigh. copied from /decl/machine_construction/default/panel_open and /decl/machine_construction/default/panel_closed + // again done this way to avoid duplication because gyrotrons can have any combo of panel + emitter state + if(!panel_open) + // closed panel (taken from panel_closed) + // maybe these should be on the part replacer or something... + // there's so much code duplication between different panel open/closed states and i hate it. + // maybe we just need to separate it out to a separate state machine and let construct state determine if panel state can change + if(istype(used_item, /obj/item/part_replacer)) + var/obj/item/part_replacer/replacer = used_item + if(replacer.remote_interaction) + machine.part_replacement(user, replacer) + for(var/line in machine.get_part_info_strings(user)) + to_chat(user, line) + return TRUE + return FALSE + // open panel (taken from panel_open) + if(IS_CROWBAR(used_item)) + TRANSFER_STATE(/decl/machine_construction/default/deconstructed) + playsound(get_turf(machine), 'sound/items/Crowbar.ogg', 50, 1) + machine.visible_message(SPAN_NOTICE("\The [user] deconstructs \the [machine].")) + machine.dismantle() + return + if(istype(used_item, /obj/item/part_replacer)) + return machine.part_replacement(user, used_item) + if(istype(used_item)) + return machine.part_insertion(user, used_item) + return FALSE + +/decl/machine_construction/emitter/mechanics_info() + . = list() + if(!panel_state) + return + if(panel_open) + . += "Use a screwdriver to close the maintenance hatch." + . += "Use a parts replacer to upgrade some parts." + . += "Use a crowbar to remove the circuit and deconstruct the emitter." + . += "Insert a new part to install it." + else + . += "Use a screwdriver to open the maintenance hatch." + . += "Use a parts replacer to view installed parts." + +/decl/machine_construction/emitter/unsecured + down_state = /decl/machine_construction/emitter/anchored + +/decl/machine_construction/emitter/unsecured/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + TRANSFER_STATE(down_state) + playsound(machine.loc, 'sound/items/Ratchet.ogg', 75, 1) + user.visible_message( + "\The [user] secures \the [machine] to the floor.", + "You secure the external reinforcing bolts to the floor.", + "You hear a ratchet.") + machine.anchored = TRUE + return TRUE + +/decl/machine_construction/emitter/unsecured/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + to_chat(user, SPAN_WARNING("\The [machine] needs to be wrenched to the floor.")) + return TRUE + +/decl/machine_construction/emitter/unsecured/mechanics_info() + . = ..() + . += "Use a wrench to anchor the emitter to the floor." + +/decl/machine_construction/emitter/anchored + anchored = TRUE + down_state = /decl/machine_construction/emitter/welded + up_state = /decl/machine_construction/emitter/unsecured + +/decl/machine_construction/emitter/anchored/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + TRANSFER_STATE(up_state) + playsound(machine.loc, 'sound/items/Ratchet.ogg', 75, 1) + user.visible_message( + "\The [user] unsecures \the [machine]'s reinforcing bolts from the floor.", + "You undo the external reinforcing bolts.", + "You hear a ratchet.") + machine.anchored = FALSE + return TRUE + +/decl/machine_construction/emitter/anchored/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + if(!welder.do_tool_interaction(TOOL_WELDER, user, machine, 2 SECONDS, \ + "welding", \ + "welding", \ + "You fail to weld \the [machine] to the floor.", \ + fuel_expenditure = 1) \ + ) + return TRUE // failed for whatever reason + TRANSFER_STATE(down_state) + return TRUE + +/decl/machine_construction/emitter/anchored/mechanics_info() + . = ..() + . += "Use a wrench to undo the bolts anchoring the emitter to the floor." + . += "Use a welding tool to weld the emitter to the floor, allowing it to fire." + +/decl/machine_construction/emitter/welded + anchored = TRUE + up_state = /decl/machine_construction/emitter/anchored + +/decl/machine_construction/emitter/welded/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + to_chat(user, SPAN_WARNING("\The [machine] needs to be unwelded from the floor.")) + return TRUE + +/decl/machine_construction/emitter/welded/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + if(!welder.do_tool_interaction(TOOL_WELDER, user, machine, 2 SECONDS, \ + "cutting free", \ + "cutting free", \ + "You fail to cut \the [machine] free from the floor.", \ + fuel_expenditure = 1) \ + ) + return TRUE // failed for whatever reason + TRANSFER_STATE(up_state) + return TRUE + +/decl/machine_construction/emitter/welded/mechanics_info() + . = ..() + . += "Use a welding tool to cut the emitter free from the floor." + +// Emitters built from a circuitboard also have a maintenance hatch, giving one state per (bolting, hatch) pair. +/decl/machine_construction/emitter/unsecured/gyrotron + needs_board = "machine" + down_state = /decl/machine_construction/emitter/anchored/gyrotron + panel_state = /decl/machine_construction/emitter/unsecured/gyrotron/panel_open + +/decl/machine_construction/emitter/unsecured/gyrotron/panel_open + panel_open = TRUE + visible_components = TRUE + down_state = /decl/machine_construction/emitter/anchored/gyrotron/panel_open + panel_state = /decl/machine_construction/emitter/unsecured/gyrotron + +/decl/machine_construction/emitter/anchored/gyrotron + needs_board = "machine" + down_state = /decl/machine_construction/emitter/welded/gyrotron + up_state = /decl/machine_construction/emitter/unsecured/gyrotron + panel_state = /decl/machine_construction/emitter/anchored/gyrotron/panel_open + +/decl/machine_construction/emitter/anchored/gyrotron/panel_open + panel_open = TRUE + visible_components = TRUE + down_state = /decl/machine_construction/emitter/welded/gyrotron/panel_open + up_state = /decl/machine_construction/emitter/unsecured/gyrotron/panel_open + panel_state = /decl/machine_construction/emitter/anchored/gyrotron + +/decl/machine_construction/emitter/welded/gyrotron + needs_board = "machine" + up_state = /decl/machine_construction/emitter/anchored/gyrotron + panel_state = /decl/machine_construction/emitter/welded/gyrotron/panel_open + +/decl/machine_construction/emitter/welded/gyrotron/panel_open + panel_open = TRUE + visible_components = TRUE + up_state = /decl/machine_construction/emitter/anchored/gyrotron/panel_open + panel_state = /decl/machine_construction/emitter/welded/gyrotron diff --git a/code/game/machinery/emitter.dm b/code/game/machinery/emitter.dm index d46f3e2c2ec4..13b2543c0985 100644 --- a/code/game/machinery/emitter.dm +++ b/code/game/machinery/emitter.dm @@ -19,13 +19,10 @@ var/burst_shots = 3 var/last_shot = 0 var/shot_number = 0 - var/const/STATE_UNSECURE = 0 - var/const/STATE_BOLTED = 1 - var/const/STATE_WELDED = 2 - var/state = STATE_UNSECURE var/locked = FALSE var/powered = FALSE core_skill = SKILL_ENGINES + construct_state = /decl/machine_construction/emitter/unsecured uncreated_component_parts = list( /obj/item/stock_parts/radio/receiver, @@ -42,7 +39,11 @@ /obj/machinery/emitter/anchored anchored = TRUE - state = STATE_WELDED + construct_state = /decl/machine_construction/emitter/welded + +/// Returns TRUE if the emitter is able to fire based on its construction state (currently checks if welded down). +/obj/machinery/emitter/proc/can_fire() + return istype(construct_state, /decl/machine_construction/emitter/welded) /obj/machinery/emitter/Destroy() log_and_message_admins("deleted \the [src]") @@ -65,29 +66,28 @@ if(!istype(user)) user = null // safety, as the proc is publicly available. - if(state == STATE_WELDED) - if(!locked) - if(active) - active = FALSE - to_chat(user, "You turn off \the [src].") - log_and_message_admins("turned off \the [src]", user) - investigate_log("turned off by [key_name_admin(user)]","singulo") - else - active = TRUE - if(user) - operator_skill = user.get_skill_value(core_skill) - update_efficiency() - to_chat(user, "You turn on \the [src].") - shot_number = 0 - fire_delay = get_initial_fire_delay() - log_and_message_admins("turned on \the [src]", user) - investigate_log("turned on by [key_name_admin(user)]","singulo") - update_icon() + if(!can_fire()) + to_chat(user, SPAN_WARNING("\The [src] needs to be firmly secured to the floor first.")) + return 1 + if(!locked) + if(active) + active = FALSE + to_chat(user, SPAN_NOTICE("You turn off \the [src].")) + log_and_message_admins("turned off \the [src]", user) + investigate_log("turned off by [key_name_admin(user)]","singulo") else - to_chat(user, "The controls are locked!") + active = TRUE + if(user) + operator_skill = user.get_skill_value(core_skill) + update_efficiency() + to_chat(user, SPAN_NOTICE("You turn on \the [src].")) + shot_number = 0 + fire_delay = get_initial_fire_delay() + log_and_message_admins("turned on \the [src]", user) + investigate_log("turned on by [key_name_admin(user)]","singulo") + update_icon() else - to_chat(user, "\The [src] needs to be firmly secured to the floor first.") - return 1 + to_chat(user, SPAN_WARNING("The controls are locked!")) /obj/machinery/emitter/proc/update_efficiency() efficiency = initial(efficiency) @@ -102,7 +102,7 @@ /obj/machinery/emitter/Process() if(stat & (BROKEN)) return - if(state != STATE_WELDED) + if(!can_fire()) active = FALSE update_icon() return @@ -137,66 +137,6 @@ update_icon() /obj/machinery/emitter/attackby(obj/item/used_item, mob/user) - - if(IS_WRENCH(used_item)) - if(active) - to_chat(user, "Turn off [src] first.") - return TRUE - switch(state) - if(STATE_UNSECURE) - state = STATE_BOLTED - playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) - user.visible_message("[user.name] secures [src] to the floor.", \ - "You secure the external reinforcing bolts to the floor.", \ - "You hear a ratchet.") - anchored = TRUE - if(STATE_BOLTED) - state = STATE_UNSECURE - playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) - user.visible_message("[user.name] unsecures [src] reinforcing bolts from the floor.", \ - "You undo the external reinforcing bolts.", \ - "You hear a ratchet.") - anchored = FALSE - if(STATE_WELDED) - to_chat(user, "\The [src] needs to be unwelded from the floor.") - return TRUE - - if(IS_WELDER(used_item)) - var/obj/item/weldingtool/welder = used_item - if(active) - to_chat(user, "Turn off [src] first.") - return TRUE - switch(state) - if(STATE_UNSECURE) - to_chat(user, "\The [src] needs to be wrenched to the floor.") - if(STATE_BOLTED) - if (!welder.weld(0,user)) - to_chat(user, "You need more welding fuel to complete this task.") - return TRUE - playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - user.visible_message("[user.name] starts to weld [src] to the floor.", \ - "You start to weld [src] to the floor.", \ - "You hear welding.") - if (!do_after(user, 2 SECONDS, src)) - return TRUE - if(!src || !welder.isOn()) return TRUE - state = STATE_WELDED - to_chat(user, "You weld [src] to the floor.") - if(STATE_WELDED) - if (welder.weld(0,user)) - playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - user.visible_message("[user.name] starts to cut [src] free from the floor.", \ - "You start to cut [src] free from the floor.", \ - "You hear welding.") - if (!do_after(user, 2 SECONDS, src)) - return TRUE - if(!src || !welder.isOn()) return TRUE - state = STATE_BOLTED - to_chat(user, "You cut [src] free from the floor.") - else - to_chat(user, "You need more welding fuel to complete this task.") - return TRUE - if(istype(used_item, /obj/item/card/id) || istype(used_item, /obj/item/modular_computer)) if(emagged) to_chat(user, "The lock seems to be broken.") diff --git a/code/modules/fusion/gyrotron/gyrotron.dm b/code/modules/fusion/gyrotron/gyrotron.dm index f70c73ffb5d1..9e7f5892920b 100644 --- a/code/modules/fusion/gyrotron/gyrotron.dm +++ b/code/modules/fusion/gyrotron/gyrotron.dm @@ -13,7 +13,7 @@ var/rate = 3 var/mega_energy = 1 - construct_state = /decl/machine_construction/default/panel_closed + construct_state = /decl/machine_construction/emitter/unsecured/gyrotron uncreated_component_parts = list( /obj/item/stock_parts/radio/receiver ) @@ -21,7 +21,7 @@ /obj/machinery/emitter/gyrotron/anchored anchored = TRUE - state = 2 + construct_state = /decl/machine_construction/emitter/welded/gyrotron /obj/machinery/emitter/gyrotron/Initialize() set_extension(src, /datum/extension/local_network_member) diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index 8943ba108f6c..327b778a2921 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -3321,10 +3321,8 @@ /turf/floor/plating/airless, /area/AIsattele) "lZ" = ( -/obj/machinery/emitter{ - anchored = 1; - dir = 4; - state = 2 +/obj/machinery/emitter/anchored{ + dir = 4 }, /turf/floor/plating/airless, /area/constructionsite/engineering) @@ -3333,10 +3331,8 @@ /turf/floor/plating/airless, /area/constructionsite/engineering) "mb" = ( -/obj/machinery/emitter{ - anchored = 1; - dir = 8; - state = 2 +/obj/machinery/emitter/anchored{ + dir = 8 }, /turf/floor/plating/airless, /area/constructionsite/engineering) diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 931f81211199..beecd85bba5d 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -61848,10 +61848,8 @@ /turf/floor/plating, /area/exodus/engineering/engine_room) "cIa" = ( -/obj/machinery/emitter{ - anchored = 1; - id_tag = "EngineEmitter"; - state = 2 +/obj/machinery/emitter/anchored{ + id_tag = "EngineEmitter" }, /obj/structure/cable/cyan, /obj/machinery/power/terminal{ diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index e3f4b0c453f8..5441d9f8411c 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -11078,10 +11078,8 @@ /obj/machinery/power/terminal{ dir = 1 }, -/obj/machinery/emitter{ - anchored = 1; - id_tag = "EngineEmitter"; - state = 2 +/obj/machinery/emitter/anchored{ + id_tag = "EngineEmitter" }, /obj/structure/cable, /turf/floor/plating, diff --git a/nebula.dme b/nebula.dme index a4f48252924b..0113eccdcfa4 100644 --- a/nebula.dme +++ b/nebula.dme @@ -887,6 +887,7 @@ #include "code\game\machinery\_machines_base\machine_construction\blast_doors.dm" #include "code\game\machinery\_machines_base\machine_construction\computer.dm" #include "code\game\machinery\_machines_base\machine_construction\default.dm" +#include "code\game\machinery\_machines_base\machine_construction\emitter.dm" #include "code\game\machinery\_machines_base\machine_construction\frame.dm" #include "code\game\machinery\_machines_base\machine_construction\item_chassis.dm" #include "code\game\machinery\_machines_base\machine_construction\noninteractive.dm" diff --git a/tools/map_migrations/5401_emitter_construct_state.txt b/tools/map_migrations/5401_emitter_construct_state.txt new file mode 100644 index 000000000000..8ce764621f54 --- /dev/null +++ b/tools/map_migrations/5401_emitter_construct_state.txt @@ -0,0 +1,8 @@ +# emitters use construct states instead of a bespoke state var +# handle redundant var sets if present +/obj/machinery/emitter/anchored{state = 2} : @OLD{@OLD; state = @SKIP; anchored = @SKIP} +/obj/machinery/emitter/gyrotron/anchored{state = 2} : @OLD{@OLD; state = @SKIP; anchored = @SKIP} +# remove state/anchored vars, change subtype if needed +/obj/machinery/emitter/@SUBTYPES{state = 2} : /obj/machinery/emitter/@SUBTYPES/anchored{@OLD; state = @SKIP; anchored = @SKIP} +/obj/machinery/emitter/@SUBTYPES{state = 1} : /obj/machinery/emitter/@SUBTYPES{@OLD; state = @SKIP; anchored = @SKIP} +/obj/machinery/emitter/@SUBTYPES{state = 0} : /obj/machinery/emitter/@SUBTYPES{@OLD; state = @SKIP; anchored = @SKIP} From c07844b9dec45c4af9c4da943365500d5a96fd02 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Fri, 7 Aug 2026 17:15:04 -0400 Subject: [PATCH 1318/1331] Rename some emitter procs for my own sanity --- code/game/machinery/emitter.dm | 14 ++++++++------ code/modules/fusion/gyrotron/gyrotron.dm | 9 +++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/code/game/machinery/emitter.dm b/code/game/machinery/emitter.dm index 13b2543c0985..a497a6d2c5f6 100644 --- a/code/game/machinery/emitter.dm +++ b/code/game/machinery/emitter.dm @@ -114,10 +114,10 @@ var/drawn_power = min(active_power_usage, active_power_usage - use_power_oneoff(active_power_usage)) last_shot = world.time if(shot_number < burst_shots) - fire_delay = get_burst_delay() + fire_delay = get_shot_delay() shot_number ++ else - fire_delay = get_rand_burst_delay() + fire_delay = get_burst_delay() shot_number = 0 //need to calculate the power per shot as the emitter doesn't fire continuously. @@ -163,13 +163,15 @@ return ..() /obj/machinery/emitter/proc/get_initial_fire_delay() - return 100 + return 10 SECONDS -/obj/machinery/emitter/proc/get_rand_burst_delay() +/// The number of deciseconds between each burst-fire grouping. +/obj/machinery/emitter/proc/get_burst_delay() return rand(min_burst_delay, max_burst_delay) -/obj/machinery/emitter/proc/get_burst_delay() - return 2 +/// The number of deciseconds between each shot in a burst. +/obj/machinery/emitter/proc/get_shot_delay() + return 0.2 SECONDS /obj/machinery/emitter/proc/get_emitter_beam() return new /obj/item/projectile/beam/emitter(get_turf(src)) diff --git a/code/modules/fusion/gyrotron/gyrotron.dm b/code/modules/fusion/gyrotron/gyrotron.dm index 9e7f5892920b..27fef671223c 100644 --- a/code/modules/fusion/gyrotron/gyrotron.dm +++ b/code/modules/fusion/gyrotron/gyrotron.dm @@ -10,6 +10,7 @@ active_power_usage = GYRO_POWER var/initial_id_tag + /// Time between shots, in SECONDS, NOT DECISECONDS var/rate = 3 var/mega_energy = 1 @@ -39,11 +40,11 @@ change_power_consumption(mega_energy * GYRO_POWER, POWER_USE_ACTIVE) . = ..() -/obj/machinery/emitter/gyrotron/get_rand_burst_delay() - return rate*10 - /obj/machinery/emitter/gyrotron/get_burst_delay() - return rate*10 + return rate SECONDS + +/obj/machinery/emitter/gyrotron/get_shot_delay() + return rate SECONDS /obj/machinery/emitter/gyrotron/get_emitter_beam() var/obj/item/projectile/beam/emitter/beam = ..() From 8c7fa0e92c9590b0eda09ea6e2ed00c4e72a815c Mon Sep 17 00:00:00 2001 From: markoatonc Date: Sun, 9 Aug 2026 05:22:52 +0200 Subject: [PATCH 1319/1331] this time it SHOULD work --- code/modules/clothing/spacesuits/void/void.dm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm index bb33a5fd1743..c24f2d54ad9d 100644 --- a/code/modules/clothing/spacesuits/void/void.dm +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -274,6 +274,17 @@ else if(##equipment_var) {\ playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1) return TRUE + if(istype(W,/obj/item/suit_cooling_unit)) + if(user.get_equipped_slot_for_item(src) == slot_wear_suit_str) + to_chat(user, "You cannot modify \the [src] while it is being worn.") + else if(tank) + to_chat(user, "\The [src] already has an airtank installed.") + else if(user.try_unequip(W, src)) + to_chat(user, "You insert \the [W] into \the [src]'s storage compartment.") + tank = W + playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1) + return TRUE + return ..() /obj/item/clothing/suit/space/void/attack_self() //sole purpose of existence is to toggle the helmet From 936e499c0a1eb68e32f8ae26e6a2b2bb6c676696 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 13 Aug 2026 16:59:13 -0400 Subject: [PATCH 1320/1331] Replace inline parent with parent call in character info OnTopic --- .../client/preference_setup/records/01_character_info.dm | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/code/modules/client/preference_setup/records/01_character_info.dm b/code/modules/client/preference_setup/records/01_character_info.dm index cd2a6b27b5a0..8818af28d2fb 100644 --- a/code/modules/client/preference_setup/records/01_character_info.dm +++ b/code/modules/client/preference_setup/records/01_character_info.dm @@ -31,11 +31,8 @@ /datum/category_item/player_setup_item/records/character_info/OnTopic(var/href,var/list/href_list, var/mob/user) - if (record_key && href_list["set_record"]) - var/new_record = sanitize(input(user,"Enter new [lowertext(name)] here.", CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.records[record_key])) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(new_record) && !jobban_isbanned(user, "Records") && !jobban_isbanned(user, name) && CanUseTopic(user)) - pref.records[record_key] = new_record - return TOPIC_REFRESH + if((. = ..())) // does nothing because it has no records_key + return var/datum/character_information/comments = pref.comments_record_id && SScharacter_info.get_record(pref.comments_record_id, TRUE) if(comments) From 418eb8bf3d144c9780878a8cd266395648f6626d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 13 Aug 2026 17:00:38 -0400 Subject: [PATCH 1321/1331] Add helper for internal pressure difference checks --- code/game/objects/__objs.dm | 17 +++++++++++++++++ .../binary_devices/binary_atmos_base.dm | 6 +----- .../components/portables_connector.dm | 6 +----- code/modules/atmospherics/components/tvalve.dm | 6 +----- .../components/unary/heat_exchanger.dm | 7 +------ .../atmospherics/components/unary/vent_pump.dm | 4 +--- .../components/unary/vent_scrubber.dm | 4 +--- code/modules/atmospherics/components/valve.dm | 6 +----- code/modules/atmospherics/pipes.dm | 8 ++------ 9 files changed, 26 insertions(+), 38 deletions(-) diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 7ed2505ff8c5..6bde88df1887 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -73,6 +73,23 @@ /obj/return_air() return loc?.return_air() +/obj/proc/get_internal_pressure_difference() + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + return int_air.return_pressure()-env_air.return_pressure() + +/// Return TRUE if the internal pressure difference is over `limit`. +/obj/proc/check_internal_pressure_difference_over(limit) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + return (int_air.return_pressure()-env_air.return_pressure()) > limit + +/// Return TRUE if the internal pressure difference is under `limit`. +/obj/proc/check_internal_pressure_difference_under(limit) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + return (int_air.return_pressure()-env_air.return_pressure()) < limit + /obj/proc/updateUsrDialog() if(in_use) var/is_in_use = 0 diff --git a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm index 70ccb935f755..f8bbb5b54bc6 100644 --- a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm +++ b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm @@ -23,11 +23,7 @@ return air1 /obj/machinery/atmospherics/binary/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) // Will only be used if you set the anchorable obj flag. /obj/machinery/atmospherics/binary/wrench_floor_bolts(mob/user, delay = 2 SECONDS, obj/item/tool) diff --git a/code/modules/atmospherics/components/portables_connector.dm b/code/modules/atmospherics/components/portables_connector.dm index d6898b129870..61bc98beb279 100644 --- a/code/modules/atmospherics/components/portables_connector.dm +++ b/code/modules/atmospherics/components/portables_connector.dm @@ -60,11 +60,7 @@ return list(connection.merged_mixture) /obj/machinery/atmospherics/portables_connector/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/portables_connector/cannot_transition_to(state_path, mob/user) if(state_path == /decl/machine_construction/default/deconstructed) diff --git a/code/modules/atmospherics/components/tvalve.dm b/code/modules/atmospherics/components/tvalve.dm index eca39ebd9377..f24062ba123d 100644 --- a/code/modules/atmospherics/components/tvalve.dm +++ b/code/modules/atmospherics/components/tvalve.dm @@ -117,11 +117,7 @@ return null /obj/machinery/atmospherics/tvalve/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /decl/public_access/public_variable/tvalve_state expected_type = /obj/machinery/atmospherics/tvalve diff --git a/code/modules/atmospherics/components/unary/heat_exchanger.dm b/code/modules/atmospherics/components/unary/heat_exchanger.dm index f19c4ecb4f74..6ed50ed3f467 100644 --- a/code/modules/atmospherics/components/unary/heat_exchanger.dm +++ b/code/modules/atmospherics/components/unary/heat_exchanger.dm @@ -76,12 +76,7 @@ partner.update_networks() /obj/machinery/atmospherics/unary/heat_exchanger/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/unary/heat_exchanger/cannot_transition_to(state_path, mob/user) if(state_path == /decl/machine_construction/default/deconstructed) diff --git a/code/modules/atmospherics/components/unary/vent_pump.dm b/code/modules/atmospherics/components/unary/vent_pump.dm index 49fcbbbb2765..9041a5956203 100644 --- a/code/modules/atmospherics/components/unary/vent_pump.dm +++ b/code/modules/atmospherics/components/unary/vent_pump.dm @@ -341,9 +341,7 @@ break if (hidden_pipe_check && isturf(T) && !T.is_plating()) return SPAN_WARNING("You must remove the plating first.") - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) + if (check_internal_pressure_difference_over(2 ATM)) return SPAN_WARNING("You cannot unwrench \the [src], it is too exerted due to internal pressure.") return ..() diff --git a/code/modules/atmospherics/components/unary/vent_scrubber.dm b/code/modules/atmospherics/components/unary/vent_scrubber.dm index 58fdc8f1717e..a22a770b326f 100644 --- a/code/modules/atmospherics/components/unary/vent_scrubber.dm +++ b/code/modules/atmospherics/components/unary/vent_scrubber.dm @@ -197,9 +197,7 @@ break if (hidden_pipe_check && isturf(T) && !T.is_plating()) return SPAN_WARNING("You must remove the plating first.") - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) + if (check_internal_pressure_difference_over(2 ATM)) return SPAN_WARNING("You cannot take this [src] apart, it too exerted due to internal pressure.") return ..() diff --git a/code/modules/atmospherics/components/valve.dm b/code/modules/atmospherics/components/valve.dm index 8b639036d738..5ffd62ff0826 100644 --- a/code/modules/atmospherics/components/valve.dm +++ b/code/modules/atmospherics/components/valve.dm @@ -113,11 +113,7 @@ return null /obj/machinery/atmospherics/valve/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/valve/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/modules/atmospherics/pipes.dm b/code/modules/atmospherics/pipes.dm index f31cd7a5c53e..db37eef0418c 100644 --- a/code/modules/atmospherics/pipes.dm +++ b/code/modules/atmospherics/pipes.dm @@ -135,12 +135,8 @@ . = ..() /obj/machinery/atmospherics/pipe/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + // this uses !over instead of under so that it's <= instead of < + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/pipe/cannot_transition_to(state_path, mob/user) if(state_path == /decl/machine_construction/default/deconstructed) From c953ad257044337b95d2f97562f7e801d05d490c Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 13 Aug 2026 17:05:37 -0400 Subject: [PATCH 1322/1331] Move character info apply code to correct file --- .../client/preference_setup/records/01_character_info.dm | 6 ++++++ code/modules/client/preferences.dm | 7 ------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/code/modules/client/preference_setup/records/01_character_info.dm b/code/modules/client/preference_setup/records/01_character_info.dm index 8818af28d2fb..68cc61b5f7df 100644 --- a/code/modules/client/preference_setup/records/01_character_info.dm +++ b/code/modules/client/preference_setup/records/01_character_info.dm @@ -61,3 +61,9 @@ if(. == TOPIC_REFRESH && istext(pref.comments_record_id) && length(pref.comments_record_id)) SScharacter_info.queue_to_save(pref.comments_record_id) + +/datum/category_item/player_setup_item/records/character_info/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) + if(is_preview_copy) + return + pref.validate_comments_record() // Make sure a record has been generated for this character. + character.comments_record_id = pref.comments_record_id \ No newline at end of file diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index e851c2513908..ad33367976e6 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -352,12 +352,6 @@ var/global/list/time_prefs_fixed = list() update_setup_window(usr) return TRUE -/datum/category_item/player_setup_item/records/character_info/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) - if(is_preview_copy) - return - pref.validate_comments_record() // Make sure a record has been generated for this character. - character.comments_record_id = pref.comments_record_id - /datum/preferences/proc/create_character_from_snapshot(spawn_turf) // Sanitizing rather than saving as someone might still be editing. player_setup.sanitize_setup() @@ -368,7 +362,6 @@ var/global/list/time_prefs_fixed = list() apply_post_snapshot_preferences(character, FALSE) return character - /datum/preferences/proc/copy_to(mob/living/human/character, is_preview_copy = FALSE) apply_snapshot_to_mob(character, is_preview_copy) // this is effectively what create_character_from_snapshot does, but on an existing mob apply_post_snapshot_preferences(character, is_preview_copy) // this is the stuff we need to share From 7c2c25b12337c0594b7d1050f1c0d47c81e1bc43 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 13 Aug 2026 17:09:54 -0400 Subject: [PATCH 1323/1331] Make chemmaster reagent topic links a macro --- code/modules/reagents/Chemistry-Machinery.dm | 27 ++++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index eb852d575ccf..a0bd71190642 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -257,6 +257,15 @@ interact(user) return TRUE +#define REAGENT_TOPIC_LINKS(HOLDER, REAGENT, ACTION) \ +dat += "[REAGENT.use_name], [REAGENT_VOLUME(HOLDER, REAGENT)] Units - ";\ +dat += "(Analyze) ";\ +dat += "(1) ";\ +dat += "(5) ";\ +dat += "(10) ";\ +dat += "(All) ";\ +dat += "(Custom)
    " + /obj/machinery/chem_master/interact(mob/user) user.set_machine(src) if(!(user.client in has_sprites)) @@ -287,30 +296,20 @@ else dat += "Add to buffer:
    " for(var/decl/material/reagent as anything in REAGENT_VOLUMES(R)) - dat += "[reagent.use_name], [REAGENT_VOLUME(R, reagent)] Units - " - dat += "(Analyze) " - dat += "(1) " - dat += "(5) " - dat += "(10) " - dat += "(All) " - dat += "(Custom)
    " + REAGENT_TOPIC_LINKS(R, reagent, "add") dat += "
    Transfer to [(!mode ? "disposal" : "beaker")]:
    " if(REAGENT_TOTAL_VOLUME(reagents)) for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) - dat += "[reagent.use_name], [REAGENT_VOLUME(reagents, reagent)] Units - " - dat += "(Analyze) " - dat += "(1) " - dat += "(5) " - dat += "(10) " - dat += "(All) " - dat += "(Custom)
    " + REAGENT_TOPIC_LINKS(reagents, reagent, "remove") else dat += "Empty
    " dat += extra_options() show_browser(user, strip_improper(JOINTEXT(dat)), "window=chem_master;size=575x400") onclose(user, "chem_master") +#undef REAGENT_TOPIC_LINKS + //Use to add extra stuff to the end of the menu. /obj/machinery/chem_master/proc/extra_options() . = list() From 23ddf52c3189e4abbed96e9b672f479efbd748c0 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 13 May 2026 14:47:08 +1000 Subject: [PATCH 1324/1331] Adding an Interact hotkey. --- code/_helpers/cmp.dm | 5 ++++ code/game/atoms.dm | 3 +++ .../machinery/_machines_base/machinery.dm | 1 + code/game/machinery/doors/_door.dm | 1 + code/game/objects/items/__item.dm | 1 + code/game/objects/structures/doors/_door.dm | 1 + .../game/objects/structures/fences/_fences.dm | 1 + code/game/objects/structures/inflatable.dm | 1 + code/modules/atmospherics/atmospherics.dm | 1 + code/modules/keybindings/binds/mob.dm | 27 +++++++++++++++++++ code/modules/lights/light_fixture_base.dm | 1 + 11 files changed, 43 insertions(+) diff --git a/code/_helpers/cmp.dm b/code/_helpers/cmp.dm index 70bb6dc061fa..7b63e6e8e8e3 100644 --- a/code/_helpers/cmp.dm +++ b/code/_helpers/cmp.dm @@ -98,6 +98,11 @@ /proc/cmp_planelayer(atom/A, atom/B) return (B.plane - A.plane) || (B.layer - A.layer) +/proc/cmp_planelayer_interact_priority(atom/A, atom/B) + if(A.interaction_priority != B.interaction_priority) + return (B.interaction_priority - A.interaction_priority) + return (B.plane - A.plane) || (B.layer - A.layer) + /proc/cmp_currency_denomination_des(var/datum/denomination/A, var/datum/denomination/B) . = B.marked_value - A.marked_value diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 0d0b617fc5a5..f1c8442e2166 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -73,6 +73,9 @@ /// (DATUM) Similar to above, but largely used by /turf/wall, /obj/structure and /obj/item/stack/material var/decl/material/reinf_material + /// (BOOLEAN) Set to TRUE to prioritise this atom when using the interact hotkey. + var/interaction_priority + /atom/proc/get_max_health() return max_health diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index bf6673bab4ce..dbdeb293a6c5 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -81,6 +81,7 @@ Class Procs: ) temperature_sensitive = TRUE abstract_type = /obj/machinery + interaction_priority = TRUE var/stat = 0 var/waterproof = TRUE diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm index 2aef4698196b..53b5ff87862a 100644 --- a/code/game/machinery/doors/_door.dm +++ b/code/game/machinery/doors/_door.dm @@ -15,6 +15,7 @@ uncreated_component_parts = null required_interaction_dexterity = DEXTERITY_SIMPLE_MACHINES max_health = 300 + interaction_priority = TRUE var/can_open_manually = TRUE diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index fd5e22b082cd..db07418a0997 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -5,6 +5,7 @@ pass_flags = PASS_FLAG_TABLE abstract_type = /obj/item temperature_sensitive = TRUE + interaction_priority = TRUE /// Set to prefix name with this string ('woven' for 'woven basket' etc) var/name_prefix diff --git a/code/game/objects/structures/doors/_door.dm b/code/game/objects/structures/doors/_door.dm index 586450e6d93e..3bce4984aaf4 100644 --- a/code/game/objects/structures/doors/_door.dm +++ b/code/game/objects/structures/doors/_door.dm @@ -9,6 +9,7 @@ anchored = TRUE opacity = TRUE structure_flags = STRUCTURE_FLAG_THROWN_DAMAGE + interaction_priority = TRUE var/has_window = FALSE var/changing_state = FALSE var/door_sound_volume = 25 diff --git a/code/game/objects/structures/fences/_fences.dm b/code/game/objects/structures/fences/_fences.dm index 9bc566ec9e88..af0323e27e68 100644 --- a/code/game/objects/structures/fences/_fences.dm +++ b/code/game/objects/structures/fences/_fences.dm @@ -227,6 +227,7 @@ name = "fence gate" desc = "Much like a regular door, but thinner." icon_state = "door-closed" + interaction_priority = TRUE /obj/structure/fence/door/can_install_lock() return TRUE diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index 137ce1cba193..0251001628ed 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -192,6 +192,7 @@ density = TRUE anchored = TRUE opacity = FALSE + interaction_priority = TRUE icon_state = "door_closed" undeploy_path = /obj/item/inflatable/door diff --git a/code/modules/atmospherics/atmospherics.dm b/code/modules/atmospherics/atmospherics.dm index 1d8c5e788fee..78d6fab2615c 100644 --- a/code/modules/atmospherics/atmospherics.dm +++ b/code/modules/atmospherics/atmospherics.dm @@ -14,6 +14,7 @@ Pipelines + Other Objects -> Pipe network idle_power_usage = 0 active_power_usage = 0 power_channel = ENVIRON + interaction_priority = null var/power_rating //the maximum amount of power the machine can use to do work, affects how powerful the machine is, in Watts diff --git a/code/modules/keybindings/binds/mob.dm b/code/modules/keybindings/binds/mob.dm index 73a6bc128a75..b77720b69972 100644 --- a/code/modules/keybindings/binds/mob.dm +++ b/code/modules/keybindings/binds/mob.dm @@ -183,3 +183,30 @@ /datum/keybinding/mob/minimal_hud/down(client/user) user.mob.minimize_hud() return TRUE + +/datum/keybinding/mob/interact + hotkey_keys = list("Enter") + name = "interact" + full_name = "Interact" + description = "Interact with the turf directly in front of you." + +/datum/keybinding/mob/interact/down(client/user) + user.mob.interact_with_facing() + return TRUE + +/mob/proc/interact_with_facing() + + var/turf/facing = get_step(get_turf(src), dir) + if(!istype(facing) || !facing.simulated) + return + + var/list/atoms = facing.get_contained_external_atoms() + // Move non-prioritised atoms to the end of the list (to avoid burning our hands on lights when trying to open a closet) + if(length(atoms) > 1) + atoms = sortTim(atoms, /proc/cmp_planelayer_interact_priority) + LAZYDISTINCTADD(atoms, facing) // Turf is always at the end of the list. + + // Try to click something. + for(var/atom/clickable as anything in atoms) + if(clickable.invisibility <= see_invisible) + return clickable.Click() diff --git a/code/modules/lights/light_fixture_base.dm b/code/modules/lights/light_fixture_base.dm index 1afdd5b1552f..6db79ddaa955 100644 --- a/code/modules/lights/light_fixture_base.dm +++ b/code/modules/lights/light_fixture_base.dm @@ -12,6 +12,7 @@ idle_power_usage = 2 active_power_usage = 20 power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list + interaction_priority = null uncreated_component_parts = list( /obj/item/stock_parts/power/apc From 1f0681f19525662f2ba7f921aa98f6f64693787d Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Tue, 25 Aug 2026 16:02:36 -0400 Subject: [PATCH 1325/1331] Fix combustion checks always returning true --- code/modules/ZAS/Fire.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/ZAS/Fire.dm b/code/modules/ZAS/Fire.dm index d6365ddce8df..fc25263a0169 100644 --- a/code/modules/ZAS/Fire.dm +++ b/code/modules/ZAS/Fire.dm @@ -261,6 +261,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin . |= HAS_FUEL if(. == (HAS_OXIDIZER|HAS_FUEL)) return TRUE + return FALSE /datum/gas_mixture/proc/check_combustibility() var/const/HAS_OXIDIZER = BITFLAG(0) @@ -275,6 +276,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin . |= HAS_FUEL if(. == (HAS_OXIDIZER|HAS_FUEL)) return TRUE + return FALSE //returns a value between 0 and vsc.fire_firelevel_multiplier /datum/gas_mixture/proc/calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, gas_volume) From a005e6d4bf967ff7060c5e8a32ef1c6e49865f67 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 13 Aug 2026 17:55:04 -0400 Subject: [PATCH 1326/1331] Reduce code duplication in sushi attackby --- .../reagents/reagent_containers/food/sushi.dm | 80 +++++++++---------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/code/modules/reagents/reagent_containers/food/sushi.dm b/code/modules/reagents/reagent_containers/food/sushi.dm index e840beb60e33..66632b14ee81 100644 --- a/code/modules/reagents/reagent_containers/food/sushi.dm +++ b/code/modules/reagents/reagent_containers/food/sushi.dm @@ -90,7 +90,7 @@ if(istype(used_item, /obj/item/food/sashimi)) var/obj/item/food/sashimi/other_sashimi = used_item if(slices + other_sashimi.slices > 5) - to_chat(user, "Show some restraint, would you?") + to_chat(user, SPAN_WARNING("Show some restraint, would you?")) return TRUE if(!user.try_unequip(used_item)) return TRUE @@ -104,12 +104,11 @@ // Make sushi. if(istype(used_item, /obj/item/food/boiledrice)) - if(slices > 1) - to_chat(user, "Putting more than one slice of fish on your sushi is just greedy.") - else - if(!user.try_unequip(used_item)) - return TRUE - new /obj/item/food/sushi(get_turf(src), null, TRUE, used_item, src) + if(!user.try_unequip(used_item)) + return TRUE + var/obj/item/food/boiledrice/used_rice = used_item + if(used_rice.try_make_sushi(src, user)) + return TRUE return TRUE . = ..() @@ -126,46 +125,39 @@ // Used for turning rice into sushi. /obj/item/food/boiledrice/attackby(var/obj/item/used_item, var/mob/user) - if((locate(/obj/structure/table) in loc)) - if(istype(used_item, /obj/item/food/sashimi)) - var/obj/item/food/sashimi/sashimi = used_item - if(sashimi.slices > 1) - to_chat(user, "Putting more than one slice of fish on your sushi is just greedy.") - else - new /obj/item/food/sushi(get_turf(src), null, TRUE, src, used_item) - return TRUE - var/static/list/sushi_types = list( + if(try_make_sushi(used_item, user, reference_item = src)) // since we're the thing being clicked, we want to be the reference item + return TRUE + return ..() + +// Used for turning other food into sushi. +/obj/item/food/boiledrice/resolve_attackby(atom/attacked_object, mob/user, click_params) + if(try_make_sushi(attacked_object, user)) + return TRUE + return ..() + +// reference_item is the item whose loc and pixel offsets we're using +/obj/item/food/boiledrice/proc/try_make_sushi(obj/item/food/used_item, mob/user, obj/item/reference_item = used_item) + var/static/list/non_fish_sushi_typecache + if(!non_fish_sushi_typecache) + non_fish_sushi_typecache = typecacheof(list( /obj/item/food/friedegg, /obj/item/food/tofu, /obj/item/food/butchery/cutlet, - /obj/item/food/butchery/cutlet/raw, /obj/item/food/spider, /obj/item/food/butchery/meat/chicken - ) - if(is_type_in_list(used_item, sushi_types)) - new /obj/item/food/sushi(get_turf(src), null, TRUE, src, used_item) + )) + // before you get confused like i did: yes, raw chicken sushi exists + // it's called torisashi and it's... dubiously safe + // raw beef could also be used i guess, like tataki or etc, or carpaccio/crudo instead of sashimi + // you could just check the cooked_food var for non-fish butchery items but i decided against it + if(!used_item || !(locate(/obj/structure/table) in reference_item.loc)) + return FALSE + if(istype(used_item, /obj/item/food/sashimi)) + var/obj/item/food/sashimi/used_sashimi = used_item + if(used_sashimi.slices > 1) + to_chat(user, SPAN_WARNING("Putting more than one slice of fish on your sushi is just greedy.")) return TRUE - . = ..() -// Used for turning other food into sushi. -// TODO: maybe make these resolve_attackby overrides on boiledrice instead? -/obj/item/food/friedegg/attackby(var/obj/item/used_item, var/mob/user) - if((locate(/obj/structure/table) in loc) && istype(used_item, /obj/item/food/boiledrice)) - new /obj/item/food/sushi(get_turf(src), null, TRUE, used_item, src) - return TRUE - . = ..() -/obj/item/food/tofu/attackby(var/obj/item/used_item, var/mob/user) - if((locate(/obj/structure/table) in loc) && istype(used_item, /obj/item/food/boiledrice)) - new /obj/item/food/sushi(get_turf(src), null, TRUE, used_item, src) - return TRUE - . = ..() -/obj/item/food/butchery/cutlet/raw/attackby(var/obj/item/used_item, var/mob/user) - if((locate(/obj/structure/table) in loc) && istype(used_item, /obj/item/food/boiledrice)) - new /obj/item/food/sushi(get_turf(src), null, TRUE, used_item, src) - return TRUE - . = ..() -/obj/item/food/butchery/cutlet/attackby(var/obj/item/used_item, var/mob/user) - if((locate(/obj/structure/table) in loc) && istype(used_item, /obj/item/food/boiledrice)) - new /obj/item/food/sushi(get_turf(src), null, TRUE, used_item, src) - return TRUE - . = ..() -// End non-fish sushi. + else if(!is_type_in_typecache(used_item, non_fish_sushi_typecache)) + return FALSE + new /obj/item/food/sushi(get_turf(reference_item), null, TRUE, src, used_item) + return TRUE From eb49d2c7a171bb9d01c709c9f38179814e6fbdda Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 26 Aug 2026 14:55:52 -0400 Subject: [PATCH 1327/1331] Copy pixel offset from sushi ingredients to sushi --- code/modules/reagents/reagent_containers/food/sushi.dm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/modules/reagents/reagent_containers/food/sushi.dm b/code/modules/reagents/reagent_containers/food/sushi.dm index 66632b14ee81..79370d942e07 100644 --- a/code/modules/reagents/reagent_containers/food/sushi.dm +++ b/code/modules/reagents/reagent_containers/food/sushi.dm @@ -159,5 +159,10 @@ return TRUE else if(!is_type_in_typecache(used_item, non_fish_sushi_typecache)) return FALSE - new /obj/item/food/sushi(get_turf(reference_item), null, TRUE, src, used_item) + var/obj/item/food/sushi/result = new /obj/item/food/sushi(get_turf(reference_item), null, TRUE, src, used_item) + // copy offsets from the item on the table, so it doesn't jump around + // todo: a helper for this that takes into account center_of_mass? + result.pixel_x = reference_item.pixel_x + result.pixel_y = reference_item.pixel_y + result.pixel_z = reference_item.pixel_z return TRUE From 5d025ebd61444ec83c9e3f565f4e0ea56b0a03cb Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Thu, 13 Aug 2026 16:56:04 -0400 Subject: [PATCH 1328/1331] Move plushie interactions to an extension --- code/game/objects/items/plushies.dm | 104 ++++++++++++++++------------ 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/code/game/objects/items/plushies.dm b/code/game/objects/items/plushies.dm index 3d07788f436b..84379e8c7025 100644 --- a/code/game/objects/items/plushies.dm +++ b/code/game/objects/items/plushies.dm @@ -1,3 +1,47 @@ +/datum/extension/plushie + base_type = /datum/extension/plushie + expected_type = /obj + var/phrase + var/phrase_sound + var/phrase_volume + var/phrase_vary + +/datum/extension/plushie/New(datum/holder, phrase, phrase_sound, phrase_volume, phrase_vary) + ..() + src.phrase = phrase + src.phrase_sound = phrase_sound + src.phrase_volume = phrase_volume + src.phrase_vary = phrase_vary + +/datum/extension/plushie/proc/on_attack(mob/user) + var/obj/plush = holder + playsound(plush.loc, 'sound/effects/rustle1.ogg', 100, 1) + if(user.check_intent(I_FLAG_HELP)) + user.visible_message( + SPAN_NOTICE("\The [user] hugs \the [holder]!"), + SPAN_NOTICE("You hug \the [plush]!")) + else if (user.check_intent(I_FLAG_HARM)) + user.visible_message( + SPAN_WARNING("\The [user] punches \the [plush]!"), + SPAN_WARNING("You punch \the [plush]!")) + else if (user.check_intent(I_FLAG_GRAB)) + user.visible_message( + SPAN_WARNING("\The [user] attempts to strangle \the [plush]!"), + SPAN_WARNING("You attempt to strangle \the [plush]!")) + else + user.visible_message( + SPAN_NOTICE("\The [user] pokes \the [plush]."), + SPAN_NOTICE("You poke \the [plush].")) + if(phrase) + plush.audible_message("\The [plush] says, \"[phrase]\"") + on_any_interact() + return TRUE + +/datum/extension/plushie/proc/on_any_interact() + var/obj/plush = holder + if(phrase_sound) + playsound(plush.loc, phrase_sound, phrase_volume, phrase_vary) + //Large plushies. /obj/structure/plushie abstract_type = /obj/structure/plushie @@ -11,31 +55,15 @@ var/phrase_volume = 20 var/phrase_vary = TRUE +/obj/structure/plushie/Initialize(ml, _mat, _reinf_mat) + . = ..() + set_extension(src, /datum/extension/plushie, phrase, phrase_sound, phrase_volume, phrase_vary) + /obj/structure/plushie/attack_hand(mob/user) if(!user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) return ..() - playsound(src.loc, 'sound/effects/rustle1.ogg', 100, 1) - if(user.check_intent(I_FLAG_HELP)) - user.visible_message( - SPAN_NOTICE("\The [user] hugs \the [src]!"), - SPAN_NOTICE("You hug \the [src]!")) - else if (user.check_intent(I_FLAG_HARM)) - user.visible_message( - SPAN_WARNING("\The [user] punches \the [src]!"), - SPAN_WARNING("You punch \the [src]!")) - else if (user.check_intent(I_FLAG_GRAB)) - user.visible_message( - SPAN_WARNING("\The [user] attempts to strangle \the [src]!"), - SPAN_WARNING("You attempt to strangle \the [src]!")) - else - user.visible_message( - SPAN_NOTICE("\The [user] pokes \the [src]."), - SPAN_NOTICE("You poke \the [src].")) - if(phrase) - audible_message("\The [src] says, \"[phrase]\"") - if(phrase_sound) - playsound(loc, phrase_sound, phrase_volume, phrase_vary) - return TRUE + var/datum/extension/plushie/plush_extension = get_extension(src, __IMPLIED_TYPE__) + return plush_extension.on_attack(user) /obj/structure/plushie/ian name = "plush corgi" @@ -74,33 +102,19 @@ var/phrase_volume = 20 var/phrase_vary = TRUE +/obj/item/toy/plushie/Initialize(ml, material_key) + . = ..() + set_extension(src, /datum/extension/plushie, phrase, phrase_sound, phrase_volume, phrase_vary) + /obj/item/toy/plushie/attack_self(mob/user) - if(user.check_intent(I_FLAG_HELP)) - user.visible_message( - SPAN_NOTICE("\The [user] hugs \the [src]!"), - SPAN_NOTICE("You hug \the [src]!")) - else if (user.check_intent(I_FLAG_HARM)) - user.visible_message( - SPAN_WARNING("\The [user] punches \the [src]!"), - SPAN_WARNING("You punch \the [src]!")) - else if (user.check_intent(I_FLAG_GRAB)) - user.visible_message( - SPAN_WARNING("\The [user] attempts to strangle \the [src]!"), - SPAN_WARNING("You attempt to strangle \the [src]!")) - else - user.visible_message( - SPAN_NOTICE("\The [user] pokes \the [src]."), - SPAN_NOTICE("You poke \the [src].")) - if(phrase) - audible_message("\The [src] says, \"[phrase]\"") - if(phrase_sound) - playsound(loc, phrase_sound, phrase_volume, phrase_vary) - return TRUE + var/datum/extension/plushie/plush_extension = get_extension(src, __IMPLIED_TYPE__) + return plush_extension.on_attack(user) /obj/item/toy/plushie/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) . = ..() - if(. && phrase_sound) - playsound(src, phrase_sound, phrase_volume, phrase_vary) + if(.) + var/datum/extension/plushie/plush_extension = get_extension(src, __IMPLIED_TYPE__) + plush_extension.on_any_interact() // Various miscellaneous animal plushies. /obj/item/toy/plushie/deer From d2072c78e319028b87ba1e7e1395332cadee08ba Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 26 Aug 2026 16:45:16 -0400 Subject: [PATCH 1329/1331] Make ChemMaster use UIDs instead of locate --- code/modules/reagents/Chemistry-Machinery.dm | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index a0bd71190642..a5fcff2dbcdd 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -85,7 +85,7 @@ if(beaker) // The custom ones modify our href_list. if (href_list["addcustom"]) - var/decl/material/their_reagent = locate(href_list["addcustom"]) + var/decl/material/their_reagent = decls_repository.get_decl_by_id(href_list["addcustom"]) if(their_reagent) useramount = input("Select the amount to transfer.", 30, useramount) as null|num if(useramount) @@ -98,7 +98,7 @@ else return TOPIC_REFRESH // Tried to move a nonexistent reagent, maybe their UI is stale? else if(href_list["removecustom"]) - var/decl/material/my_reagents = locate(href_list["removecustom"]) + var/decl/material/my_reagents = decls_repository.get_decl_by_id(href_list["removecustom"]) if(my_reagents) useramount = input("Select the amount to transfer.", 30, useramount) as null|num if(useramount) @@ -112,7 +112,7 @@ // DO NOT use else if here, we want these to run even if the custom ones do var/datum/reagents/R = beaker.reagents if (href_list["analyze"]) - var/decl/material/reagent = locate(href_list["analyze"]) + var/decl/material/reagent = decls_repository.get_decl_by_id(href_list["analyze"]) var/dat = get_chem_info(reagent) if(dat && REAGENT_VOLUME(beaker.reagents, reagent.type)) show_browser(user, dat, "window=chem_master;size=575x400") @@ -120,7 +120,7 @@ else if (href_list["add"]) if(href_list["amount"]) - var/decl/material/their_reagent = locate(href_list["add"]) + var/decl/material/their_reagent = decls_repository.get_decl_by_id(href_list["add"]) if(their_reagent) var/mult = 1 var/amount = clamp((text2num(href_list["amount"])), 0, get_remaining_volume()) @@ -135,7 +135,7 @@ else if (href_list["remove"]) if(href_list["amount"]) - var/decl/material/my_reagents = locate(href_list["remove"]) + var/decl/material/my_reagents = decls_repository.get_decl_by_id(href_list["remove"]) if(my_reagents) var/amount = clamp((text2num(href_list["amount"])), 0, 200) var/contaminants = fetch_contaminants(user, reagents, my_reagents) @@ -259,16 +259,17 @@ #define REAGENT_TOPIC_LINKS(HOLDER, REAGENT, ACTION) \ dat += "[REAGENT.use_name], [REAGENT_VOLUME(HOLDER, REAGENT)] Units - ";\ -dat += "(Analyze) ";\ -dat += "(1) ";\ -dat += "(5) ";\ -dat += "(10) ";\ -dat += "(All) ";\ -dat += "(Custom)
    " +dat += "(Analyze) ";\ +dat += "(1) ";\ +dat += "(5) ";\ +dat += "(10) ";\ +dat += "(All) ";\ +dat += "(Custom)
    " /obj/machinery/chem_master/interact(mob/user) user.set_machine(src) if(!(user.client in has_sprites)) + // todo: convert this to use the asset system spawn() has_sprites += user.client for(var/i = 1 to MAX_PILL_SPRITE) From bf9413198812371e94d1db2c7838a22b5dfbacce Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 26 Aug 2026 17:07:08 -0400 Subject: [PATCH 1330/1331] Fix duplicate caffeine intolerance definition --- code/datums/traits/maluses/intolerances.dm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/code/datums/traits/maluses/intolerances.dm b/code/datums/traits/maluses/intolerances.dm index 4c4b096dbd69..71ca9f0af09a 100644 --- a/code/datums/traits/maluses/intolerances.dm +++ b/code/datums/traits/maluses/intolerances.dm @@ -93,12 +93,6 @@ var/global/list/_intolerances_by_flag = list() allergen_flags = ALLERGEN_NUTS uid = "trait_allergy_nuts" -/decl/trait/malus/intolerance/stimulant - name = "Caffeine Intolerance" - description = "You are allergic to or intolerant of caffeine, and can become very ill if you ingest it." - allergen_flags = ALLERGEN_CAFFEINE - uid = "trait_allergy_caffeine" - /decl/trait/malus/intolerance/stimulant name = "Stimulant Intolerance" description = "You are allergic to or intolerant of stimulants, and can become very ill if you ingest them." From a24e830909269d2ef3b1a5b70e4ba18c3738cef3 Mon Sep 17 00:00:00 2001 From: Penelope Haze Date: Wed, 26 Aug 2026 20:51:02 -0400 Subject: [PATCH 1331/1331] Fix unmarked abstract decl types on dev --- code/modules/overmap/events/event.dm | 1 + mods/utility/prometheus_metrics/metric_family.dm | 1 + 2 files changed, 2 insertions(+) diff --git a/code/modules/overmap/events/event.dm b/code/modules/overmap/events/event.dm index d2d63a62c262..36e546ec6127 100644 --- a/code/modules/overmap/events/event.dm +++ b/code/modules/overmap/events/event.dm @@ -253,6 +253,7 @@ //These now are basically only used to spawn hazards. Will be useful when we need to spawn group of moving hazards /decl/overmap_event + abstract_type = /decl/overmap_event var/name = "map event" var/radius = 2 var/count = 6 diff --git a/mods/utility/prometheus_metrics/metric_family.dm b/mods/utility/prometheus_metrics/metric_family.dm index 0054e97a257e..514589807a8f 100644 --- a/mods/utility/prometheus_metrics/metric_family.dm +++ b/mods/utility/prometheus_metrics/metric_family.dm @@ -1,5 +1,6 @@ // Datum used for gathering a set of prometheus metrics. /decl/metric_family + abstract_type = /decl/metric_family var/name = null var/metric_type = null var/help = null
    ") + for(var/i in 1 to length(timestamp)) + var/time = "\[[time2text(timestamp[i] SECONDS,"mm:ss")]\]" + output += "[time]
    -----CUT------
    " + output += "