From b395c253d105b032a99c01c220c2711dd7beec63 Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:43:08 -0500 Subject: [PATCH 01/15] Can't compile yet but ports a ton of shit --- code/__DEFINES/directional.dm | 3 + code/__DEFINES/flags.dm | 2 + code/__DEFINES/icon_smoothing.dm | 235 ++++++ code/__DEFINES/logging.dm | 17 + code/__DEFINES/map_switch.dm | 8 + code/__DEFINES/matrices.dm | 3 + code/__DEFINES/traits.dm | 5 +- code/__DEFINES/turfs.dm | 9 +- code/__HELPERS/icon_smoothing.dm | 795 ++++++++++++------ code/__HELPERS/icons.dm | 3 +- code/__HELPERS/{ => logging}/_logging.dm | 66 +- code/__HELPERS/logging/attack.dm | 64 ++ code/__HELPERS/logging/talk.dm | 15 + code/__HELPERS/unsorted.dm | 15 +- code/_globalvars/bitfields.dm | 8 - code/controllers/subsystem/icon_smooth.dm | 4 +- code/datums/elements/undertile.dm | 84 ++ code/game/{atoms.dm => atom/_atom.dm} | 286 +------ code/game/atom/atom_invisibility.dm | 72 ++ code/game/atom/atom_materials.dm | 34 + code/game/atom/atom_orbit.dm | 18 + code/game/atom/atom_tool_acts.dm | 82 ++ code/game/atoms_movable.dm | 3 + code/game/machinery/Beacon.dm | 13 +- code/game/machinery/deployable.dm | 8 +- code/game/machinery/doors/airlock.dm | 2 +- code/game/machinery/navbeacon.dm | 3 +- .../machinery/telecomms/telecomunications.dm | 3 +- code/game/objects/effects/contraband.dm | 4 +- .../objects/effects/decals/cleanable/misc.dm | 22 +- .../fluid_spread/effects_foam.dm | 3 +- .../fluid_spread/effects_smoke.dm | 2 +- .../objects/items/devices/pressureplates.dm | 2 +- code/game/objects/items/storage/backpack.dm | 1 + code/game/objects/structures.dm | 13 +- code/game/objects/structures/aliens.dm | 27 +- .../structures/beds_chairs/alien_nest.dm | 4 +- code/game/objects/structures/catwalk.dm | 14 +- code/game/objects/structures/false_walls.dm | 135 +-- code/game/objects/structures/grille.dm | 17 +- code/game/objects/structures/lattice.dm | 8 +- code/game/objects/structures/mineral_doors.dm | 12 +- code/game/objects/structures/safe.dm | 4 + code/game/objects/structures/tables_racks.dm | 56 +- code/game/objects/structures/window.dm | 96 +-- code/game/turfs/change_turf.dm | 8 +- code/game/turfs/closed.dm | 30 +- code/game/turfs/open.dm | 28 +- code/game/turfs/openspace/openspace.dm | 2 + code/game/turfs/simulated/chasm.dm | 10 +- code/game/turfs/simulated/floor.dm | 39 +- .../game/turfs/simulated/floor/fancy_floor.dm | 75 +- .../turfs/simulated/floor/mineral_floor.dm | 8 +- code/game/turfs/simulated/floor/plating.dm | 10 +- .../simulated/floor/plating/misc_plating.dm | 39 +- code/game/turfs/simulated/lava.dm | 31 +- code/game/turfs/simulated/minerals.dm | 139 +-- .../turfs/simulated/wall/mineral_walls.dm | 123 ++- code/game/turfs/simulated/wall/misc_walls.dm | 11 +- code/game/turfs/simulated/wall/reinf_walls.dm | 32 +- code/game/turfs/simulated/walls.dm | 16 +- code/game/turfs/space/space.dm | 3 +- code/game/turfs/turf.dm | 40 +- .../clockcult/clock_effects/clock_overlay.dm | 9 +- .../revenant/revenant_abilities.dm | 2 +- .../atmospherics/machinery/other/meter.dm | 2 - .../atmospherics/machinery/pipes/pipes.dm | 4 + .../awaymissions/mission_code/vrhub.dm | 7 +- code/modules/holodeck/turfs.dm | 9 +- code/modules/mining/equipment/survival_pod.dm | 12 +- .../hostile/megafauna/hierophant.dm | 12 +- .../hostile/mining_mobs/elites/elite.dm | 17 +- .../simple_animal/hostile/venus_human_trap.dm | 4 +- code/modules/plumbing/ducts.dm | 1 + code/modules/power/apc.dm | 2 +- code/modules/power/cable.dm | 9 +- code/modules/power/power.dm | 2 +- code/modules/power/smes.dm | 2 +- code/modules/power/terminal.dm | 6 +- .../projectiles/guns/misc/beam_rifle.dm | 2 - .../recycling/disposal/construction.dm | 3 +- code/modules/recycling/disposal/pipe.dm | 5 +- .../ruins/spaceruin_code/hilbertshotel.dm | 3 +- code/modules/shuttle/shuttle_rotate.dm | 4 +- .../spells/spell_types/conjure/_conjure.dm | 8 +- code/modules/unit_tests/focus_only_tests.dm | 3 + yogstation.dme | 14 +- .../objects/structures/bar_stuff/bar_stuff.dm | 3 +- .../game/objects/structures/tables_racks.dm | 5 +- .../code/game/objects/structures/window.dm | 7 +- .../game/turfs/simulated/floor/fancy_floor.dm | 14 +- .../code/game/turfs/simulated/minerals.dm | 6 +- 92 files changed, 2018 insertions(+), 1083 deletions(-) create mode 100644 code/__DEFINES/icon_smoothing.dm create mode 100644 code/__DEFINES/map_switch.dm create mode 100644 code/__DEFINES/matrices.dm rename code/__HELPERS/{ => logging}/_logging.dm (88%) create mode 100644 code/__HELPERS/logging/attack.dm create mode 100644 code/__HELPERS/logging/talk.dm create mode 100644 code/datums/elements/undertile.dm rename code/game/{atoms.dm => atom/_atom.dm} (82%) create mode 100644 code/game/atom/atom_invisibility.dm create mode 100644 code/game/atom/atom_materials.dm create mode 100644 code/game/atom/atom_orbit.dm create mode 100644 code/game/atom/atom_tool_acts.dm diff --git a/code/__DEFINES/directional.dm b/code/__DEFINES/directional.dm index 85d746a43882..1b40d6094633 100644 --- a/code/__DEFINES/directional.dm +++ b/code/__DEFINES/directional.dm @@ -8,3 +8,6 @@ #define TEXT_SOUTH "[SOUTH]" #define TEXT_EAST "[EAST]" #define TEXT_WEST "[WEST]" + +//Better performant than an artisanal proc and more reliable than Turn(). From TGMC. +#define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 92c389063f8a..3820e833457b 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -55,6 +55,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define RAD_CONTAIN_CONTENTS (1<<17) /// Is the thing currently spinning? #define IS_SPINNING_1 (1<<18) +/// Should we use the initial icon for display? Mostly used by overlay only objects +#define HTML_USE_INITAL_ICON_1 (1<<19) //turf-only flags #define NOJAUNT_1 (1<<0) diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm new file mode 100644 index 000000000000..d4503b4e1efc --- /dev/null +++ b/code/__DEFINES/icon_smoothing.dm @@ -0,0 +1,235 @@ +/* smoothing_flags */ +/// Smoothing system in where adjacencies are calculated and used to build an image by mounting each corner at runtime. +#define SMOOTH_CORNERS (1<<0) +/// Smoothing system in where adjacencies are calculated and used to select a pre-baked icon_state, encoded by bitmasking. +#define SMOOTH_BITMASK (1<<1) +/// Atom has diagonal corners, with underlays under them. +#define SMOOTH_DIAGONAL_CORNERS (1<<2) +/// Atom will smooth with the borders of the map. +#define SMOOTH_BORDER (1<<3) +/// Atom is currently queued to smooth. +#define SMOOTH_QUEUED (1<<4) +/// Smooths with objects, and will thus need to scan turfs for contents. +#define SMOOTH_OBJ (1<<5) +/// Uses directional object smoothing, so we care not only about something being on the right turf, but also its direction +/// Changes the meaning of smoothing_junction, instead of representing the directions we are smoothing in +/// it represents the sides of our directional border object that have a neighbor +/// Is incompatible with SMOOTH_CORNERS because border objects don't have corners +#define SMOOTH_BORDER_OBJECT (1<<6) + +DEFINE_BITFIELD(smoothing_flags, list( + "SMOOTH_CORNERS" = SMOOTH_CORNERS, + "SMOOTH_BITMASK" = SMOOTH_BITMASK, + "SMOOTH_DIAGONAL_CORNERS" = SMOOTH_DIAGONAL_CORNERS, + "SMOOTH_BORDER" = SMOOTH_BORDER, + "SMOOTH_QUEUED" = SMOOTH_QUEUED, + "SMOOTH_OBJ" = SMOOTH_OBJ, + "SMOOTH_BORDER_OBJECT" = SMOOTH_BORDER_OBJECT, +)) + +/// Components of a smoothing junction +/// Redefinitions of the diagonal directions so they can be stored in one var without conflicts +#define NORTH_JUNCTION NORTH //(1<<0) +#define SOUTH_JUNCTION SOUTH //(1<<1) +#define EAST_JUNCTION EAST //(1<<2) +#define WEST_JUNCTION WEST //(1<<3) +#define NORTHEAST_JUNCTION (1<<4) +#define SOUTHEAST_JUNCTION (1<<5) +#define SOUTHWEST_JUNCTION (1<<6) +#define NORTHWEST_JUNCTION (1<<7) + +DEFINE_BITFIELD(smoothing_junction, list( + "NORTH_JUNCTION" = NORTH_JUNCTION, + "SOUTH_JUNCTION" = SOUTH_JUNCTION, + "EAST_JUNCTION" = EAST_JUNCTION, + "WEST_JUNCTION" = WEST_JUNCTION, + "NORTHEAST_JUNCTION" = NORTHEAST_JUNCTION, + "SOUTHEAST_JUNCTION" = SOUTHEAST_JUNCTION, + "SOUTHWEST_JUNCTION" = SOUTHWEST_JUNCTION, + "NORTHWEST_JUNCTION" = NORTHWEST_JUNCTION, +)) + +/*smoothing macros*/ + +#define QUEUE_SMOOTH(thing_to_queue) if(thing_to_queue.smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) {SSicon_smooth.add_to_queue(thing_to_queue)} + +#define QUEUE_SMOOTH_NEIGHBORS(thing_to_queue) for(var/atom/atom_neighbor as anything in orange(1, thing_to_queue)) {QUEUE_SMOOTH(atom_neighbor)} + +/**SMOOTHING GROUPS + * Groups of things to smooth with. + * * Contained in the `list/smoothing_groups` variable. + * * Matched with the `list/canSmoothWith` variable to check whether smoothing is possible or not. + */ + +#define S_TURF(num) (#num + ",") + +/* /turf only */ + +#define SMOOTH_GROUP_TURF_OPEN S_TURF(0) ///turf/open +#define SMOOTH_GROUP_TURF_CHASM S_TURF(1) ///turf/open/chasm, /turf/open/floor/fakepit +#define SMOOTH_GROUP_FLOOR_LAVA S_TURF(2) ///turf/open/lava/smooth +#define SMOOTH_GROUP_FLOOR_TRANSPARENT_GLASS S_TURF(3) ///turf/open/floor/glass + +#define SMOOTH_GROUP_OPEN_FLOOR S_TURF(4) ///turf/open/floor + +#define SMOOTH_GROUP_FLOOR_GRASS S_TURF(5) ///turf/open/misc/grass +#define SMOOTH_GROUP_FLOOR_ASH S_TURF(6) ///turf/open/misc/ashplanet/ash +#define SMOOTH_GROUP_FLOOR_ASH_ROCKY S_TURF(7) ///turf/open/misc/ashplanet/rocky +#define SMOOTH_GROUP_FLOOR_ICE S_TURF(8) ///turf/open/misc/ice +#define SMOOTH_GROUP_FLOOR_SNOWED S_TURF(9) ///turf/open/floor/plating/snowed + +#define SMOOTH_GROUP_CARPET S_TURF(10) ///turf/open/floor/carpet +#define SMOOTH_GROUP_CARPET_BLACK S_TURF(11) ///turf/open/floor/carpet/black +#define SMOOTH_GROUP_CARPET_BLUE S_TURF(12) ///turf/open/floor/carpet/blue +#define SMOOTH_GROUP_CARPET_CYAN S_TURF(13) ///turf/open/floor/carpet/cyan +#define SMOOTH_GROUP_CARPET_GREEN S_TURF(14) ///turf/open/floor/carpet/green +#define SMOOTH_GROUP_CARPET_ORANGE S_TURF(15) ///turf/open/floor/carpet/orange +#define SMOOTH_GROUP_CARPET_PURPLE S_TURF(16) ///turf/open/floor/carpet/purple +#define SMOOTH_GROUP_CARPET_RED S_TURF(17) ///turf/open/floor/carpet/red +#define SMOOTH_GROUP_CARPET_ROYAL_BLACK S_TURF(18) ///turf/open/indestructible/carpet/royal/black +#define SMOOTH_GROUP_CARPET_ROYAL_GREEN S_TURF(19) ///turf/open/indestructible/carpet/royal/black +#define SMOOTH_GROUP_CARPET_ROYAL_BLUE S_TURF(20) ///turf/open/indestructible/carpet/royal/blue + +//TODO INCREASE IT ALL BY ONE + +#define SMOOTH_GROUP_CARPET_ROYAL_PURPLE S_TURF(20) ///turf/open/indestructible/carpet/royal/purple +#define SMOOTH_GROUP_CARPET_EXECUTIVE S_TURF(21) ///turf/open/floor/carpet/executive +#define SMOOTH_GROUP_CARPET_STELLAR S_TURF(22) ///turf/open/floor/carpet/stellar +#define SMOOTH_GROUP_CARPET_DONK S_TURF(23) ///turf/open/floor/carpet/donk +#define SMOOTH_GROUP_CARPET_NEON S_TURF(24) //![turf/open/floor/carpet/neon] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON S_TURF(25) //![turf/open/floor/carpet/neon/simple] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_WHITE S_TURF(26) //![turf/open/floor/carpet/neon/simple/white] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLACK S_TURF(27) //![turf/open/floor/carpet/neon/simple/black] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_RED S_TURF(28) //![turf/open/floor/carpet/neon/simple/red] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_ORANGE S_TURF(29) //![turf/open/floor/carpet/neon/simple/orange] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_YELLOW S_TURF(30) //![turf/open/floor/carpet/neon/simple/yellow] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_LIME S_TURF(31) //![turf/open/floor/carpet/neon/simple/lime] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_GREEN S_TURF(32) //![turf/open/floor/carpet/neon/simple/green] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_TEAL S_TURF(33) //![turf/open/floor/carpet/neon/simple/teal] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_CYAN S_TURF(34) //![turf/open/floor/carpet/neon/simple/cyan] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLUE S_TURF(35) //![turf/open/floor/carpet/neon/simple/blue] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PURPLE S_TURF(36) //![turf/open/floor/carpet/neon/simple/purple] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_VIOLET S_TURF(37) //![turf/open/floor/carpet/neon/simple/violet] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PINK S_TURF(38) //![turf/open/floor/carpet/neon/simple/pink] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_NODOTS S_TURF(39) //![turf/open/floor/carpet/neon/simple/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_WHITE_NODOTS S_TURF(40) //![turf/open/floor/carpet/neon/simple/white/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLACK_NODOTS S_TURF(41) //![turf/open/floor/carpet/neon/simple/black/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_RED_NODOTS S_TURF(42) //![turf/open/floor/carpet/neon/simple/red/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_ORANGE_NODOTS S_TURF(43) //![turf/open/floor/carpet/neon/simple/orange/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_YELLOW_NODOTS S_TURF(44) //![turf/open/floor/carpet/neon/simple/yellow/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_LIME_NODOTS S_TURF(45) //![turf/open/floor/carpet/neon/simple/lime/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_GREEN_NODOTS S_TURF(46) //![turf/open/floor/carpet/neon/simple/green/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_TEAL_NODOTS S_TURF(47) //![turf/open/floor/carpet/neon/simple/teal/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_CYAN_NODOTS S_TURF(48) //![turf/open/floor/carpet/neon/simple/cyan/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLUE_NODOTS S_TURF(49) //![turf/open/floor/carpet/neon/simple/blue/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PURPLE_NODOTS S_TURF(50) //![turf/open/floor/carpet/neon/simple/purple/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_VIOLET_NODOTS S_TURF(51) //![turf/open/floor/carpet/neon/simple/violet/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PINK_NODOTS S_TURF(52) //![turf/open/floor/carpet/neon/simple/pink/nodots] +#define SMOOTH_GROUP_BAMBOO_FLOOR S_TURF(53) //![/turf/open/floor/bamboo] +#define SMOOTH_GROUP_BRAZIL S_TURF(54) ///turf/open/indestructible/brazil/lostit + +#define SMOOTH_GROUP_CLOSED_TURFS S_TURF(54) ///turf/closed +#define SMOOTH_GROUP_MATERIAL_WALLS S_TURF(55) ///turf/closed/wall/material +#define SMOOTH_GROUP_SYNDICATE_WALLS S_TURF(56) ///turf/closed/wall/r_wall/syndicate, /turf/closed/indestructible/syndicate +#define SMOOTH_GROUP_HOTEL_WALLS S_TURF(57) ///turf/closed/indestructible/hotelwall +#define SMOOTH_GROUP_MINERAL_WALLS S_TURF(58) ///turf/closed/mineral, /turf/closed/indestructible +#define SMOOTH_GROUP_BOSS_WALLS S_TURF(59) ///turf/closed/indestructible/riveted/boss +#define SMOOTH_GROUP_SURVIVAL_TITANIUM_WALLS S_TURF(60) ///turf/closed/wall/mineral/titanium/survival +#define SMOOTH_GROUP_TURF_OPEN_CLIFF S_TURF(61) ///turf/open/cliff +#define SMOOTH_GROUP_TURF_BALLPIT S_TURF(62) ///turf/open/floor/ballpit + +#define MAX_S_TURF 62 //Always match this value with the one above it. + +#define S_OBJ(num) ("-" + #num + ",") +/* /obj included */ + +#define SMOOTH_GROUP_WALLS S_OBJ(1) ///turf/closed/wall, /obj/structure/falsewall +#define SMOOTH_GROUP_URANIUM_WALLS S_OBJ(2) ///turf/closed/wall/mineral/uranium, /obj/structure/falsewall/uranium +#define SMOOTH_GROUP_GOLD_WALLS S_OBJ(3) ///turf/closed/wall/mineral/gold, /obj/structure/falsewall/gold +#define SMOOTH_GROUP_SILVER_WALLS S_OBJ(4) ///turf/closed/wall/mineral/silver, /obj/structure/falsewall/silver +#define SMOOTH_GROUP_DIAMOND_WALLS S_OBJ(5) ///turf/closed/wall/mineral/diamond, /obj/structure/falsewall/diamond +#define SMOOTH_GROUP_PLASMA_WALLS S_OBJ(6) ///turf/closed/wall/mineral/plasma, /obj/structure/falsewall/plasma +#define SMOOTH_GROUP_BANANIUM_WALLS S_OBJ(7) ///turf/closed/wall/mineral/bananium, /obj/structure/falsewall/bananium +#define SMOOTH_GROUP_SANDSTONE_WALLS S_OBJ(8) ///turf/closed/wall/mineral/sandstone, /obj/structure/falsewall/sandstone +#define SMOOTH_GROUP_WOOD_WALLS S_OBJ(9) ///turf/closed/wall/mineral/wood, /obj/structure/falsewall/wood +#define SMOOTH_GROUP_IRON_WALLS S_OBJ(10) ///turf/closed/wall/mineral/iron, /obj/structure/falsewall/iron +#define SMOOTH_GROUP_ABDUCTOR_WALLS S_OBJ(11) ///turf/closed/wall/mineral/abductor, /obj/structure/falsewall/abductor +#define SMOOTH_GROUP_TITANIUM_WALLS S_OBJ(12) ///turf/closed/wall/mineral/titanium, /obj/structure/falsewall/titanium +#define SMOOTH_GROUP_PLASTITANIUM_WALLS S_OBJ(14) ///turf/closed/wall/mineral/plastitanium, /obj/structure/falsewall/plastitanium +#define SMOOTH_GROUP_SURVIVAL_TITANIUM_POD S_OBJ(15) ///turf/closed/wall/mineral/titanium/survival/pod, /obj/machinery/door/airlock/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod +#define SMOOTH_GROUP_HIERO_WALL S_OBJ(16) ///obj/effect/temp_visual/elite_tumor_wall, /obj/effect/temp_visual/hierophant/wall +#define SMOOTH_GROUP_BAMBOO_WALLS S_TURF(17) //![/turf/closed/wall/mineral/bamboo, /obj/structure/falsewall/bamboo] +#define SMOOTH_GROUP_PLASTINUM_WALLS S_TURF(18) //![turf/closed/indestructible/riveted/plastinum] +#define SMOOTH_GROUP_CLOCKWORK_WALLS S_TURF(19) //![/turf/closed/wall/clockwork, /obj/structure/falsewall/brass] + +#define SMOOTH_GROUP_PAPERFRAME S_OBJ(21) ///obj/structure/window/paperframe, /obj/structure/mineral_door/paperframe + +#define SMOOTH_GROUP_WINDOW_FULLTILE S_OBJ(22) ///turf/closed/indestructible/fakeglass, /obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/plasma/fulltile, /obj/structure/window/reinforced/plasma/fulltile +#define SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE S_OBJ(23) ///obj/structure/window/bronze/fulltile +#define SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM S_OBJ(24) ///turf/closed/indestructible/opsglass, /obj/structure/window/reinforced/plasma/plastitanium +#define SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE S_OBJ(25) ///obj/structure/window/reinforced/shuttle + +#define SMOOTH_GROUP_WINDOW_DIRECTIONAL_TRAM S_OBJ(26) ///obj/structure/tram + +#define SMOOTH_GROUP_LATTICE S_OBJ(31) ///obj/structure/lattice +#define SMOOTH_GROUP_CATWALK S_OBJ(32) ///obj/structure/lattice/catwalk + +#define SMOOTH_GROUP_AIRLOCK S_OBJ(41) ///obj/machinery/door/airlock + +#define SMOOTH_GROUP_INDUSTRIAL_LIFT S_OBJ(46) ///obj/structure/transport/linear +#define SMOOTH_GROUP_TRAM_STRUCTURE S_OBJ(47) //obj/structure/tram + +#define SMOOTH_GROUP_TABLES S_OBJ(51) ///obj/structure/table +#define SMOOTH_GROUP_WOOD_TABLES S_OBJ(52) ///obj/structure/table/wood +#define SMOOTH_GROUP_FANCY_WOOD_TABLES S_OBJ(53) ///obj/structure/table/wood/fancy +#define SMOOTH_GROUP_BRONZE_TABLES S_OBJ(54) ///obj/structure/table/bronze +#define SMOOTH_GROUP_ABDUCTOR_TABLES S_OBJ(55) ///obj/structure/table/abductor +#define SMOOTH_GROUP_GLASS_TABLES S_OBJ(56) ///obj/structure/table/glass +#define SMOOTH_GROUP_BANANIUM_TABLES S_OBJ(57) ///obj/structure/table/glass + +#define SMOOTH_GROUP_ALIEN_NEST S_OBJ(60) ///obj/structure/bed/nest +#define SMOOTH_GROUP_ALIEN_RESIN S_OBJ(61) ///obj/structure/alien/resin +#define SMOOTH_GROUP_ALIEN_WALLS S_OBJ(62) ///obj/structure/alien/resin/wall, /obj/structure/alien/resin/membrane +#define SMOOTH_GROUP_ALIEN_WEEDS S_OBJ(63) ///obj/structure/alien/weeds + +#define SMOOTH_GROUP_SECURITY_BARRICADE S_OBJ(64) ///obj/structure/barricade/security +#define SMOOTH_GROUP_SANDBAGS S_OBJ(65) ///obj/structure/barricade/sandbags + +#define SMOOTH_GROUP_HEDGE_FLUFF S_OBJ(66) ///obj/structure/hedge + +#define SMOOTH_GROUP_SHUTTLE_PARTS S_OBJ(67) ///obj/structure/window/reinforced/shuttle, /obj/structure/window/reinforced/plasma/plastitanium, /turf/closed/indestructible/opsglass, /obj/machinery/power/shuttle_engine + +#define SMOOTH_GROUP_CLEANABLE_DIRT S_OBJ(68) ///obj/effect/decal/cleanable/dirt + +#define SMOOTH_GROUP_GAS_TANK S_OBJ(72) + + +/// Performs the work to set smoothing_groups and canSmoothWith. +/// An inlined function used in both turf/Initialize and atom/Initialize. +#define SETUP_SMOOTHING(...) \ + if (smoothing_groups) { \ + if (PERFORM_ALL_TESTS(focus_only/sorted_smoothing_groups)) { \ + ASSERT_SORTED_SMOOTHING_GROUPS(smoothing_groups); \ + } \ + SET_SMOOTHING_GROUPS(smoothing_groups); \ + } \ +\ + if (canSmoothWith) { \ + if (PERFORM_ALL_TESTS(focus_only/sorted_smoothing_groups)) { \ + ASSERT_SORTED_SMOOTHING_GROUPS(canSmoothWith); \ + } \ + /* S_OBJ is always negative, and we are guaranteed to be sorted. */ \ + if (canSmoothWith[1] == "-") { \ + smoothing_flags |= SMOOTH_OBJ; \ + } \ + SET_SMOOTHING_GROUPS(canSmoothWith); \ + } + +/// Given a smoothing groups variable, will set out to the actual numbers inside it +#define UNWRAP_SMOOTHING_GROUPS(smoothing_groups, out) \ + json_decode("\[[##smoothing_groups]0\]"); \ + ##out.len--; + +#define ASSERT_SORTED_SMOOTHING_GROUPS(smoothing_group_variable) \ + var/list/unwrapped = UNWRAP_SMOOTHING_GROUPS(smoothing_group_variable, unwrapped); \ + assert_sorted(unwrapped, "[#smoothing_group_variable] ([type])"); \ diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 408db118901a..9456a9000ea2 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -55,3 +55,20 @@ #define LOGSRC_CLIENT "Client" #define LOGSRC_MOB "Mob" + +//wrapper macros for easier grepping +#define DIRECT_OUTPUT(A, B) A << B +#define DIRECT_INPUT(A, B) A >> B +#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) +#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) +#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text) +#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) +#define READ_FILE(file, text) DIRECT_INPUT(file, text) +//This is an external call, "true" and "false" are how rust parses out booleans +#ifdef EXTOOLS_LOGGING +#define WRITE_LOG(log, text) extools_log_write(log, text, TRUE) +#define WRITE_LOG_NO_FORMAT(log, text) extools_log_write(log, text, FALSE) +#else +#define WRITE_LOG(log, text) rustg_log_write(log, "\[[worldtime2text()]\] [text]", "true") +#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") +#endif diff --git a/code/__DEFINES/map_switch.dm b/code/__DEFINES/map_switch.dm new file mode 100644 index 000000000000..dbb0059786ee --- /dev/null +++ b/code/__DEFINES/map_switch.dm @@ -0,0 +1,8 @@ +/// Uses the left operator when compiling, uses the right operator when not compiling. +// Currently uses the CBT macro, but if http://www.byond.com/forum/post/2831057 is ever added, +// or if map tools ever agree on a standard, this should switch to use that. +#ifdef CBT +#define MAP_SWITCH(compile_time, map_time) ##compile_time +#else +#define MAP_SWITCH(compile_time, map_time) ##map_time +#endif diff --git a/code/__DEFINES/matrices.dm b/code/__DEFINES/matrices.dm new file mode 100644 index 000000000000..1bb86f9ff478 --- /dev/null +++ b/code/__DEFINES/matrices.dm @@ -0,0 +1,3 @@ +/// Helper macro for creating a matrix at the given offsets. +/// Works at compile time. +#define TRANSLATE_MATRIX(offset_x, offset_y) matrix(1, 0, (offset_x), 0, 1, (offset_y)) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 31bb25afe2e8..35baf4056ada 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -309,7 +309,8 @@ // item traits #define TRAIT_NODROP "nodrop" -#define TRAIT_T_RAY_VISIBLE "t-ray-visible" // Visible on t-ray scanners if the atom/var/level == 1 +/// Visible on t-ray scanners if the atom/var/level == 1 +#define TRAIT_T_RAY_VISIBLE "t-ray-visible" /// Properly wielded two handed item #define TRAIT_WIELDED "wielded" /// The items needs two hands to be carried @@ -384,6 +385,8 @@ #define ELEMENT_TRAIT(source) "element_trait_[source]" /// Trait from [/datum/element/rust]. Its rusty and should be applying a special overlay to denote this. #define TRAIT_RUSTY "rust_trait" +/// Trait from being under the floor in some manner +#define TRAIT_UNDERFLOOR "underfloor" // unique trait sources, still defines #define CLONING_POD_TRAIT "cloning-pod" diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index 549af806ee03..688d80cc9e28 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -3,10 +3,17 @@ #define CHANGETURF_FORCEOP (1<<2) #define CHANGETURF_SKIP (1<<3) // A flag for PlaceOnTop to just instance the new turf instead of calling ChangeTurf. Used for uninitialized turfs NOTHING ELSE #define CHANGETURF_INHERIT_AIR (1<<4) // Inherit air from previous turf. Implies CHANGETURF_IGNORE_AIR -#define CHANGETURF_RECALC_ADJACENT (1<<5) //Immediately recalc adjacent atmos turfs instead of queuing. +#define CHANGETURF_RECALC_ADJACENT (1<<5) //Immediately recalc adjacent atmos turfs instead of queuing.TRAIT_UNDERFLOOR #define CHANGETURF_TRAPDOOR_INDUCED (1<<6) // Caused by a trapdoor, for trapdoor to know that this changeturf was caused by itself ///Returns all currently loaded turfs #define ALL_TURFS(...) block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz)) #define IS_OPAQUE_TURF(turf) (turf.directional_opacity == ALL_CARDINALS) + +/// The pipes, disposals, and wires are hidden +#define UNDERFLOOR_HIDDEN 0 +/// The pipes, disposals, and wires are visible but cannot be interacted with +#define UNDERFLOOR_VISIBLE 1 +/// The pipes, disposals, and wires are visible and can be interacted with +#define UNDERFLOOR_INTERACTABLE 2 diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index 41b9d266c59e..6a1cfd6191a3 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -2,15 +2,16 @@ //generic (by snowflake) tile smoothing code; smooth your icons with this! /* Each tile is divided in 4 corners, each corner has an appearance associated to it; the tile is then overlayed by these 4 appearances - To use this, just set your atom's 'smooth' var to 1. If your atom can be moved/unanchored, set its 'can_be_unanchored' var to 1. + To use this, just set your atom's 'smoothing_flags' var to 1. If your atom can be moved/unanchored, set its 'can_be_unanchored' var to 1. If you don't want your atom's icon to smooth with anything but atoms of the same type, set the list 'canSmoothWith' to null; - Otherwise, put all types you want the atom icon to smooth with in 'canSmoothWith' INCLUDING THE TYPE OF THE ATOM ITSELF. + Otherwise, put all the smoothing groups you want the atom icon to smooth with in 'canSmoothWith', including the group of the atom itself. + Smoothing groups are just shared flags between objects. If one of the 'canSmoothWith' of A matches one of the `smoothing_groups` of B, then A will smooth with B. Each atom has its own icon file with all the possible corner states. See 'smooth_wall.dmi' for a template. DIAGONAL SMOOTHING INSTRUCTIONS To make your atom smooth diagonally you need all the proper icon states (see 'smooth_wall.dmi' for a template) and - to add the 'SMOOTH_DIAGONAL' flag to the atom's smooth var (in addition to either SMOOTH_TRUE or SMOOTH_MORE). + to add the 'SMOOTH_DIAGONAL_CORNERS' flag to the atom's smoothing_flags var (in addition to either SMOOTH_TRUE or SMOOTH_MORE). For turfs, what appears under the diagonal corners depends on the turf that was in the same position previously: if you make a wall on a plating floor, you will see plating under the diagonal wall corner, if it was space, you will see space. @@ -23,298 +24,510 @@ To see an example of a diagonal wall, see '/turf/closed/wall/mineral/titanium' and its subtypes. */ -//Redefinitions of the diagonal directions so they can be stored in one var without conflicts -#define N_NORTH (1<<1) -#define N_SOUTH (1<<2) -#define N_EAST (1<<4) -#define N_WEST (1<<8) -#define N_NORTHEAST (1<<5) -#define N_NORTHWEST (1<<9) -#define N_SOUTHEAST (1<<6) -#define N_SOUTHWEST (1<<10) - -#define SMOOTH_FALSE 0 //not smooth -#define SMOOTH_TRUE (1<<0) //smooths with exact specified types or just itself -#define SMOOTH_MORE (1<<1) //smooths with all subtypes of specified types or just itself (this value can replace SMOOTH_TRUE) -#define SMOOTH_DIAGONAL (1<<2) //if atom should smooth diagonally, this should be present in 'smooth' var -#define SMOOTH_BORDER (1<<3) //atom will smooth with the borders of the map -#define SMOOTH_QUEUED (1<<4) //atom is currently queued to smooth. - -#define NULLTURF_BORDER 123456789 - -#define DEFAULT_UNDERLAY_ICON 'icons/turf/floors.dmi' -#define DEFAULT_UNDERLAY_ICON_STATE "plating" - -/atom/var/smooth = SMOOTH_FALSE -/atom/var/top_left_corner -/atom/var/top_right_corner -/atom/var/bottom_left_corner -/atom/var/bottom_right_corner -/atom/var/list/canSmoothWith = null // TYPE PATHS I CAN SMOOTH WITH~~~~~ If this is null and atom is smooth, it smooths only with itself -/atom/movable/var/can_be_unanchored = FALSE -/turf/var/list/fixed_underlay = null - -/proc/calculate_adjacencies(atom/A) - if(!A.loc) - return 0 - - var/adjacencies = 0 - - var/atom/movable/AM - if(ismovable(A)) - AM = A - if(AM.can_be_unanchored && !AM.anchored) - return 0 +#define NO_ADJ_FOUND 0 +#define ADJ_FOUND 1 +#define NULLTURF_BORDER 2 - for(var/direction in GLOB.cardinals) - AM = find_type_in_direction(A, direction) - if(AM == NULLTURF_BORDER) - if((A.smooth & SMOOTH_BORDER)) - adjacencies |= 1 << direction - else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) ) - adjacencies |= 1 << direction - - if(adjacencies & N_NORTH) - if(adjacencies & N_WEST) - AM = find_type_in_direction(A, NORTHWEST) - if(AM == NULLTURF_BORDER) - if((A.smooth & SMOOTH_BORDER)) - adjacencies |= N_NORTHWEST - else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) ) - adjacencies |= N_NORTHWEST - if(adjacencies & N_EAST) - AM = find_type_in_direction(A, NORTHEAST) - if(AM == NULLTURF_BORDER) - if((A.smooth & SMOOTH_BORDER)) - adjacencies |= N_NORTHEAST - else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) ) - adjacencies |= N_NORTHEAST - - if(adjacencies & N_SOUTH) - if(adjacencies & N_WEST) - AM = find_type_in_direction(A, SOUTHWEST) - if(AM == NULLTURF_BORDER) - if((A.smooth & SMOOTH_BORDER)) - adjacencies |= N_SOUTHWEST - else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) ) - adjacencies |= N_SOUTHWEST - if(adjacencies & N_EAST) - AM = find_type_in_direction(A, SOUTHEAST) - if(AM == NULLTURF_BORDER) - if((A.smooth & SMOOTH_BORDER)) - adjacencies |= N_SOUTHEAST - else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) ) - adjacencies |= N_SOUTHEAST - - return adjacencies - -//do not use, use queue_smooth(atom) -/proc/smooth_icon(atom/A) - if(!A || !A.smooth) - return - A.smooth &= ~SMOOTH_QUEUED - if (!A.z) - return - if(QDELETED(A)) +GLOBAL_LIST_INIT(adjacent_direction_lookup, generate_adjacent_directions()) + +/* Attempting to mirror the below + * Each 3x3 grid is a tile, with each X representing a direction a border object could be in IN said grid + * Directions marked with A are acceptable smoothing targets, M is the example direction + * The example given here is of a northfacing border object +xxx AxA xxx +xxx AxA xxx +xxx AxA xxx + +AAA MMM AAA +xxx AxA xxx +xxx AxA xxx + +xxx xxx xxx +xxx xxx xxx +xxx xxx xxx +*/ +/// Encodes connectivity between border objects +/// Returns a list accessable by a border object's dir, the direction between it and a target, and a target +/// Said list will return the direction the two objects connect, if any exists (if the target isn't a border object and the direction is fine, return the inverse of the direction in use) +/proc/generate_adjacent_directions() + // Have to hold all conventional dir pairs, so we size to the largest + // We don't HAVE diagonal border objects, so I'm gonna pretend they'll never exist + + // You might be like, lemon, can't we use GLOB.cardinals/GLOB.alldirs here + // No, they aren't loaded yet. life is pain + var/list/cardinals = list(NORTH, SOUTH, EAST, WEST) + var/list/alldirs = cardinals + list(NORTH|EAST, SOUTH|EAST, NORTH|WEST, SOUTH|WEST) + var/largest_cardinal = max(cardinals) + var/largest_dir = max(alldirs) + + var/list/direction_map = new /list(largest_cardinal) + for(var/dir in cardinals) + var/left = turn(dir, 90) + var/right = turn(dir, -90) + var/opposite = REVERSE_DIR(dir) + // Need to encode diagonals here because it's possible, even if it is always false + var/list/acceptable_adjacents = new /list(largest_dir) + // Alright, what directions are acceptable to us + for(var/connectable_dir in (cardinals + NONE)) + // And what border objects INSIDE those directions are alright + var/list/smoothable_dirs = new /list(largest_cardinal + 1) // + 1 because we need to provide space for NONE to be a valid index + // None is fine, we want to smooth with things on our own turf + // We'll do the two dirs to our left and right + // They connect.. "below" us and on their side + if(connectable_dir == NONE) + smoothable_dirs[left] = dir_to_junction(opposite | left) + smoothable_dirs[right] = dir_to_junction(opposite | right) + // If it's to our right or left we'll include just the dir matching ours + // Left edge touches only our left side, and so on + else if (connectable_dir == left) + smoothable_dirs[dir] = left + else if (connectable_dir == right) + smoothable_dirs[dir] = right + // If it's straight on we'll include our direction as a link + // Then include the two edges on the other side as diagonals + else if(connectable_dir == dir) + smoothable_dirs[opposite] = dir + smoothable_dirs[left] = dir_to_junction(dir | left) + smoothable_dirs[right] = dir_to_junction(dir | right) + // otherwise, go HOME, I don't want to encode anything for you + else + continue + acceptable_adjacents[connectable_dir + 1] = smoothable_dirs + direction_map[dir] = acceptable_adjacents + return direction_map + +/// Are two atoms border adjacent, takes a border object, something to compare against, and the direction between A and B +/// Returns the way in which the first thing is adjacent to the second +#define CAN_DIAGONAL_SMOOTH(border_obj, target, direction) (\ + (target.smoothing_flags & SMOOTH_BORDER_OBJECT) ? \ + GLOB.adjacent_direction_lookup[border_obj.dir][direction + 1]?[target.dir] : \ + (GLOB.adjacent_direction_lookup[border_obj.dir][direction + 1]) ? REVERSE_DIR(direction) : NONE \ + ) + +#define DEFAULT_UNDERLAY_ICON 'icons/turf/floors.dmi' +#define DEFAULT_UNDERLAY_ICON_STATE "plating" + + +///Scans all adjacent turfs to find targets to smooth with. +/atom/proc/calculate_adjacencies() + . = NONE + + if(!loc) return - if(A.smooth & (SMOOTH_TRUE | SMOOTH_MORE)) - var/adjacencies = calculate_adjacencies(A) - if(A.smooth & SMOOTH_DIAGONAL) - A.diagonal_smooth(adjacencies) + for(var/direction in GLOB.cardinals) + switch(find_type_in_direction(direction)) + if(NULLTURF_BORDER) + if((smoothing_flags & SMOOTH_BORDER)) + . |= direction //BYOND and smooth dirs are the same for cardinals + if(ADJ_FOUND) + . |= direction //BYOND and smooth dirs are the same for cardinals + + if(. & NORTH_JUNCTION) + if(. & WEST_JUNCTION) + switch(find_type_in_direction(NORTHWEST)) + if(NULLTURF_BORDER) + if((smoothing_flags & SMOOTH_BORDER)) + . |= NORTHWEST_JUNCTION + if(ADJ_FOUND) + . |= NORTHWEST_JUNCTION + + if(. & EAST_JUNCTION) + switch(find_type_in_direction(NORTHEAST)) + if(NULLTURF_BORDER) + if((smoothing_flags & SMOOTH_BORDER)) + . |= NORTHEAST_JUNCTION + if(ADJ_FOUND) + . |= NORTHEAST_JUNCTION + + if(. & SOUTH_JUNCTION) + if(. & WEST_JUNCTION) + switch(find_type_in_direction(SOUTHWEST)) + if(NULLTURF_BORDER) + if((smoothing_flags & SMOOTH_BORDER)) + . |= SOUTHWEST_JUNCTION + if(ADJ_FOUND) + . |= SOUTHWEST_JUNCTION + + if(. & EAST_JUNCTION) + switch(find_type_in_direction(SOUTHEAST)) + if(NULLTURF_BORDER) + if((smoothing_flags & SMOOTH_BORDER)) + . |= SOUTHEAST_JUNCTION + if(ADJ_FOUND) + . |= SOUTHEAST_JUNCTION + + +/atom/movable/calculate_adjacencies() + if(can_be_unanchored && !anchored) + return NONE + return ..() + + +///do not use, use QUEUE_SMOOTH(atom) +/atom/proc/smooth_icon() + smoothing_flags &= ~SMOOTH_QUEUED + flags_1 |= HTML_USE_INITAL_ICON_1 + if (!z) + CRASH("[type] called smooth_icon() without being on a z-level") + if(smoothing_flags & SMOOTH_CORNERS) + if(smoothing_flags & SMOOTH_DIAGONAL_CORNERS) + corners_diagonal_smooth(calculate_adjacencies()) else - cardinal_smooth(A, adjacencies) + corners_cardinal_smooth(calculate_adjacencies()) + else if(smoothing_flags & SMOOTH_BITMASK) + bitmask_smooth() + else + CRASH("smooth_icon called for [src] with smoothing_flags == [smoothing_flags]") + SEND_SIGNAL(src, COMSIG_ATOM_SMOOTHED_ICON) -/atom/proc/diagonal_smooth(adjacencies) +// As a rule, movables will most always care about smoothing changes +// Turfs on the other hand, don't, so we don't do the update for THEM unless they explicitly request it +/atom/movable/smooth_icon() + . = ..() + update_appearance(~UPDATE_SMOOTHING) + +/atom/proc/corners_diagonal_smooth(adjacencies) switch(adjacencies) - if(N_NORTH|N_WEST) + if(NORTH_JUNCTION|WEST_JUNCTION) replace_smooth_overlays("d-se","d-se-0") - if(N_NORTH|N_EAST) + if(NORTH_JUNCTION|EAST_JUNCTION) replace_smooth_overlays("d-sw","d-sw-0") - if(N_SOUTH|N_WEST) + if(SOUTH_JUNCTION|WEST_JUNCTION) replace_smooth_overlays("d-ne","d-ne-0") - if(N_SOUTH|N_EAST) + if(SOUTH_JUNCTION|EAST_JUNCTION) replace_smooth_overlays("d-nw","d-nw-0") - if(N_NORTH|N_WEST|N_NORTHWEST) + if(NORTH_JUNCTION|WEST_JUNCTION|NORTHWEST_JUNCTION) replace_smooth_overlays("d-se","d-se-1") - if(N_NORTH|N_EAST|N_NORTHEAST) + if(NORTH_JUNCTION|EAST_JUNCTION|NORTHEAST_JUNCTION) replace_smooth_overlays("d-sw","d-sw-1") - if(N_SOUTH|N_WEST|N_SOUTHWEST) + if(SOUTH_JUNCTION|WEST_JUNCTION|SOUTHWEST_JUNCTION) replace_smooth_overlays("d-ne","d-ne-1") - if(N_SOUTH|N_EAST|N_SOUTHEAST) + if(SOUTH_JUNCTION|EAST_JUNCTION|SOUTHEAST_JUNCTION) replace_smooth_overlays("d-nw","d-nw-1") else - cardinal_smooth(src, adjacencies) - return + corners_cardinal_smooth(adjacencies) + return FALSE icon_state = "" - return adjacencies - -//only walls should have a need to handle underlays -/turf/closed/wall/diagonal_smooth(adjacencies) - adjacencies = reverse_ndir(..()) - if(adjacencies) - var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, plane = FLOOR_PLANE) - var/list/U = list(underlay_appearance) - if(fixed_underlay) - if(fixed_underlay["space"]) - underlay_appearance.icon = 'icons/turf/space.dmi' - underlay_appearance.icon_state = SPACE_ICON_STATE - underlay_appearance.plane = PLANE_SPACE - else - underlay_appearance.icon = fixed_underlay["icon"] - underlay_appearance.icon_state = fixed_underlay["icon_state"] - else - var/turned_adjacency = turn(adjacencies, 180) - var/turf/T = get_step(src, turned_adjacency) - if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) - T = get_step(src, turn(adjacencies, 135)) - if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) - T = get_step(src, turn(adjacencies, 225)) - //if all else fails, ask our own turf - if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency) && !get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) - underlay_appearance.icon = DEFAULT_UNDERLAY_ICON - underlay_appearance.icon_state = DEFAULT_UNDERLAY_ICON_STATE - underlays = U - - // Drop posters which were previously placed on this wall. - for(var/obj/structure/sign/poster/P in src) - P.roll_and_drop(src) - - -/proc/cardinal_smooth(atom/A, adjacencies) + return TRUE + + +/atom/proc/corners_cardinal_smooth(adjacencies) + var/mutable_appearance/temp_ma + //NW CORNER var/nw = "1-i" - if((adjacencies & N_NORTH) && (adjacencies & N_WEST)) - if(adjacencies & N_NORTHWEST) + if((adjacencies & NORTH_JUNCTION) && (adjacencies & WEST_JUNCTION)) + if(adjacencies & NORTHWEST_JUNCTION) nw = "1-f" else nw = "1-nw" else - if(adjacencies & N_NORTH) + if(adjacencies & NORTH_JUNCTION) nw = "1-n" - else if(adjacencies & N_WEST) + else if(adjacencies & WEST_JUNCTION) nw = "1-w" + temp_ma = mutable_appearance(icon, nw) + nw = temp_ma.appearance //NE CORNER var/ne = "2-i" - if((adjacencies & N_NORTH) && (adjacencies & N_EAST)) - if(adjacencies & N_NORTHEAST) + if((adjacencies & NORTH_JUNCTION) && (adjacencies & EAST_JUNCTION)) + if(adjacencies & NORTHEAST_JUNCTION) ne = "2-f" else ne = "2-ne" else - if(adjacencies & N_NORTH) + if(adjacencies & NORTH_JUNCTION) ne = "2-n" - else if(adjacencies & N_EAST) + else if(adjacencies & EAST_JUNCTION) ne = "2-e" + temp_ma = mutable_appearance(icon, ne) + ne = temp_ma.appearance //SW CORNER var/sw = "3-i" - if((adjacencies & N_SOUTH) && (adjacencies & N_WEST)) - if(adjacencies & N_SOUTHWEST) + if((adjacencies & SOUTH_JUNCTION) && (adjacencies & WEST_JUNCTION)) + if(adjacencies & SOUTHWEST_JUNCTION) sw = "3-f" else sw = "3-sw" else - if(adjacencies & N_SOUTH) + if(adjacencies & SOUTH_JUNCTION) sw = "3-s" - else if(adjacencies & N_WEST) + else if(adjacencies & WEST_JUNCTION) sw = "3-w" + temp_ma = mutable_appearance(icon, sw) + sw = temp_ma.appearance //SE CORNER var/se = "4-i" - if((adjacencies & N_SOUTH) && (adjacencies & N_EAST)) - if(adjacencies & N_SOUTHEAST) + if((adjacencies & SOUTH_JUNCTION) && (adjacencies & EAST_JUNCTION)) + if(adjacencies & SOUTHEAST_JUNCTION) se = "4-f" else se = "4-se" else - if(adjacencies & N_SOUTH) + if(adjacencies & SOUTH_JUNCTION) se = "4-s" - else if(adjacencies & N_EAST) + else if(adjacencies & EAST_JUNCTION) se = "4-e" + temp_ma = mutable_appearance(icon, se) + se = temp_ma.appearance + + var/list/new_overlays - var/list/New + if(top_left_corner != nw) + cut_overlay(top_left_corner) + top_left_corner = nw + LAZYADD(new_overlays, nw) - if(A.top_left_corner != nw) - A.cut_overlay(A.top_left_corner) - A.top_left_corner = nw - LAZYADD(New, nw) + if(top_right_corner != ne) + cut_overlay(top_right_corner) + top_right_corner = ne + LAZYADD(new_overlays, ne) - if(A.top_right_corner != ne) - A.cut_overlay(A.top_right_corner) - A.top_right_corner = ne - LAZYADD(New, ne) + if(bottom_right_corner != sw) + cut_overlay(bottom_right_corner) + bottom_right_corner = sw + LAZYADD(new_overlays, sw) - if(A.bottom_right_corner != sw) - A.cut_overlay(A.bottom_right_corner) - A.bottom_right_corner = sw - LAZYADD(New, sw) + if(bottom_left_corner != se) + cut_overlay(bottom_left_corner) + bottom_left_corner = se + LAZYADD(new_overlays, se) - if(A.bottom_left_corner != se) - A.cut_overlay(A.bottom_left_corner) - A.bottom_left_corner = se - LAZYADD(New, se) + if(new_overlays) + add_overlay(new_overlays) - if(New) - A.add_overlay(New) -/proc/find_type_in_direction(atom/source, direction) - var/turf/target_turf = get_step(source, direction) +///Scans direction to find targets to smooth with. +/atom/proc/find_type_in_direction(direction) + var/turf/target_turf = get_step(src, direction) if(!target_turf) return NULLTURF_BORDER var/area/target_area = get_area(target_turf) - var/area/source_area = get_area(source) - if(source_area.canSmoothWithAreas && !is_type_in_typecache(target_area, source_area.canSmoothWithAreas)) - return null - if(target_area.canSmoothWithAreas && !is_type_in_typecache(source_area, target_area.canSmoothWithAreas)) - return null - - if(source.canSmoothWith) - var/atom/A - if(source.smooth & SMOOTH_MORE) - for(var/a_type in source.canSmoothWith) - if( istype(target_turf, a_type) ) - return target_turf - A = locate(a_type) in target_turf - if(A) - return A - return null - - for(var/a_type in source.canSmoothWith) - if(a_type == target_turf.type) - return target_turf - A = locate(a_type) in target_turf - if(A && A.type == a_type) - return A - return null - else - if(isturf(source)) - return source.type == target_turf.type ? target_turf : null - var/atom/A = locate(source.type) in target_turf - return A && A.type == source.type ? A : null + var/area/source_area = get_area(src) + if((source_area.area_limited_icon_smoothing && !istype(target_area, source_area.area_limited_icon_smoothing)) || (target_area.area_limited_icon_smoothing && !istype(source_area, target_area.area_limited_icon_smoothing))) + return NO_ADJ_FOUND + + if(isnull(canSmoothWith)) //special case in which it will only smooth with itself + if(isturf(src)) + return (type == target_turf.type) ? ADJ_FOUND : NO_ADJ_FOUND + var/atom/matching_obj = locate(type) in target_turf + return (matching_obj && matching_obj.type == type) ? ADJ_FOUND : NO_ADJ_FOUND + + if(!isnull(target_turf.smoothing_groups)) + for(var/target in canSmoothWith) + if(!(canSmoothWith[target] & target_turf.smoothing_groups[target])) + continue + return ADJ_FOUND + + if(smoothing_flags & SMOOTH_OBJ) + for(var/atom/movable/thing as anything in target_turf) + if(!thing.anchored || isnull(thing.smoothing_groups)) + continue + for(var/target in canSmoothWith) + if(!(canSmoothWith[target] & thing.smoothing_groups[target])) + continue + return ADJ_FOUND + + return NO_ADJ_FOUND + +/** + * Basic smoothing proc. The atom checks for adjacent directions to smooth with and changes the icon_state based on that. + * + * Returns the previous smoothing_junction state so the previous state can be compared with the new one after the proc ends, and see the changes, if any. + * +*/ +/atom/proc/bitmask_smooth() + var/new_junction = NONE + + // cache for sanic speed + var/canSmoothWith = src.canSmoothWith + + var/smooth_border = (smoothing_flags & SMOOTH_BORDER) + var/smooth_obj = (smoothing_flags & SMOOTH_OBJ) + var/border_object_smoothing = (smoothing_flags & SMOOTH_BORDER_OBJECT) + + // Did you know you can pass defines into other defines? very handy, lets take advantage of it here to allow 0 cost variation + #define SEARCH_ADJ_IN_DIR(direction, direction_flag, ADJ_FOUND, WORLD_BORDER, BORDER_CHECK) \ + do { \ + var/turf/neighbor = get_step(src, direction); \ + if(neighbor && ##BORDER_CHECK(neighbor, direction)) { \ + var/neighbor_smoothing_groups = neighbor.smoothing_groups; \ + if(neighbor_smoothing_groups) { \ + for(var/target in canSmoothWith) { \ + if(canSmoothWith[target] & neighbor_smoothing_groups[target]) { \ + ##ADJ_FOUND(neighbor, direction, direction_flag); \ + } \ + } \ + } \ + if(smooth_obj) { \ + for(var/atom/movable/thing as anything in neighbor) { \ + var/thing_smoothing_groups = thing.smoothing_groups; \ + if(!thing.anchored || isnull(thing_smoothing_groups) || !##BORDER_CHECK(thing, direction)) { \ + continue; \ + }; \ + for(var/target in canSmoothWith) { \ + if(canSmoothWith[target] & thing_smoothing_groups[target]) { \ + ##ADJ_FOUND(thing, direction, direction_flag); \ + } \ + } \ + } \ + } \ + } else if (smooth_border) { \ + ##WORLD_BORDER(null, direction, direction_flag); \ + } \ + } while(FALSE) \ + + #define BITMASK_FOUND(target, direction, direction_flag) \ + new_junction |= direction_flag; \ + break set_adj_in_dir; \ + /// Check that non border objects use to smooth against border objects + /// Returns true if the smooth is acceptable, FALSE otherwise + #define BITMASK_ON_BORDER_CHECK(target, direction) (!(target.smoothing_flags & SMOOTH_BORDER_OBJECT) || CAN_DIAGONAL_SMOOTH(target, src, REVERSE_DIR(direction))) + + #define BORDER_FOUND(target, direction, direction_flag) new_junction |= CAN_DIAGONAL_SMOOTH(src, target, direction) + // Border objects require an object as context, so we need a dummy. I'm sorry + #define WORLD_BORDER_FOUND(target, direction, direction_flag) \ + var/static/atom/dummy; \ + if(!dummy) { \ + dummy = new(); \ + dummy.smoothing_flags &= ~SMOOTH_BORDER_OBJECT; \ + } \ + BORDER_FOUND(dummy, direction, direction_flag); + // Handle handle border on border checks. no-op, we handle this check inside CAN_DIAGONAL_SMOOTH + #define BORDER_ON_BORDER_CHECK(target, direction) (TRUE) + + // We're building 2 different types of smoothing searches here + // One for standard bitmask smoothing (We provide a label so our macro can eary exit, as it wants to do) + #define SET_ADJ_IN_DIR(direction, direction_flag) do { set_adj_in_dir: { SEARCH_ADJ_IN_DIR(direction, direction_flag, BITMASK_FOUND, BITMASK_FOUND, BITMASK_ON_BORDER_CHECK) }} while(FALSE) + // and another for border object work (Doesn't early exit because we can hit more then one direction by checking the same turf) + #define SET_BORDER_ADJ_IN_DIR(direction) SEARCH_ADJ_IN_DIR(direction, direction, BORDER_FOUND, WORLD_BORDER_FOUND, BORDER_ON_BORDER_CHECK) + + // Let's go over all our cardinals + if(border_object_smoothing) + SET_BORDER_ADJ_IN_DIR(NORTH) + SET_BORDER_ADJ_IN_DIR(SOUTH) + SET_BORDER_ADJ_IN_DIR(EAST) + SET_BORDER_ADJ_IN_DIR(WEST) + // We want to check against stuff in our own turf + SET_BORDER_ADJ_IN_DIR(NONE) + // Border objects don't do diagonals, so GO HOME + set_smoothed_icon_state(new_junction) + return + + SET_ADJ_IN_DIR(NORTH, NORTH) + SET_ADJ_IN_DIR(SOUTH, SOUTH) + SET_ADJ_IN_DIR(EAST, EAST) + SET_ADJ_IN_DIR(WEST, WEST) + + // If there's nothing going on already + if(!(new_junction & (NORTH|SOUTH)) || !(new_junction & (EAST|WEST))) + set_smoothed_icon_state(new_junction) + return + + if(new_junction & NORTH_JUNCTION) + if(new_junction & WEST_JUNCTION) + SET_ADJ_IN_DIR(NORTHWEST, NORTHWEST_JUNCTION) + + if(new_junction & EAST_JUNCTION) + SET_ADJ_IN_DIR(NORTHEAST, NORTHEAST_JUNCTION) + + if(new_junction & SOUTH_JUNCTION) + if(new_junction & WEST_JUNCTION) + SET_ADJ_IN_DIR(SOUTHWEST, SOUTHWEST_JUNCTION) + + if(new_junction & EAST_JUNCTION) + SET_ADJ_IN_DIR(SOUTHEAST, SOUTHEAST_JUNCTION) + + set_smoothed_icon_state(new_junction) + + #undef SET_BORDER_ADJ_IN_DIR + #undef SET_ADJ_IN_DIR + #undef BORDER_ON_BORDER_CHECK + #undef WORLD_BORDER_FOUND + #undef BORDER_FOUND + #undef BITMASK_ON_BORDER_CHECK + #undef BITMASK_FOUND + #undef SEARCH_ADJ_IN_DIR + +///Changes the icon state based on the new junction bitmask +/atom/proc/set_smoothed_icon_state(new_junction) + . = smoothing_junction + smoothing_junction = new_junction + icon_state = "[base_icon_state]-[smoothing_junction]" + + +/turf/closed/set_smoothed_icon_state(new_junction) + // Avoid calling ..() here to avoid setting icon_state twice, which is expensive given how hot this proc is + var/old_junction = smoothing_junction + smoothing_junction = new_junction + + if (!(smoothing_flags & SMOOTH_DIAGONAL_CORNERS)) + icon_state = "[base_icon_state]-[smoothing_junction]" + return + + switch(new_junction) + if( + NORTH_JUNCTION|WEST_JUNCTION, + NORTH_JUNCTION|EAST_JUNCTION, + SOUTH_JUNCTION|WEST_JUNCTION, + SOUTH_JUNCTION|EAST_JUNCTION, + NORTH_JUNCTION|WEST_JUNCTION|NORTHWEST_JUNCTION, + NORTH_JUNCTION|EAST_JUNCTION|NORTHEAST_JUNCTION, + SOUTH_JUNCTION|WEST_JUNCTION|SOUTHWEST_JUNCTION, + SOUTH_JUNCTION|EAST_JUNCTION|SOUTHEAST_JUNCTION, + ) + icon_state = "[base_icon_state]-[smoothing_junction]-d" + if(new_junction == old_junction || fixed_underlay) // Mutable underlays? + return + + var/junction_dir = reverse_ndir(smoothing_junction) + var/turned_adjacency = REVERSE_DIR(junction_dir) + var/turf/neighbor_turf = get_step(src, turned_adjacency & (NORTH|SOUTH)) + var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, offset_spokesman = src, plane = FLOOR_PLANE) + if(!neighbor_turf.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) + neighbor_turf = get_step(src, turned_adjacency & (EAST|WEST)) + + if(!neighbor_turf.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) + neighbor_turf = get_step(src, turned_adjacency) + + if(!neighbor_turf.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) + if(!get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) //if all else fails, ask our own turf + underlay_appearance.icon = DEFAULT_UNDERLAY_ICON + underlay_appearance.icon_state = DEFAULT_UNDERLAY_ICON_STATE + underlays += underlay_appearance + else + icon_state = "[base_icon_state]-[smoothing_junction]" + +/turf/open/floor/set_smoothed_icon_state(new_junction) + if(broken || burnt) + return + return ..() + //Icon smoothing helpers /proc/smooth_zlevel(zlevel, now = FALSE) - var/list/away_turfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)) - for(var/V in away_turfs) - var/turf/T = V - if(T.smooth) + var/list/away_turfs = Z_TURFS(zlevel) + for(var/turf/turf_to_smooth as anything in away_turfs) + if(turf_to_smooth.smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) if(now) - smooth_icon(T) + turf_to_smooth.smooth_icon() else - queue_smooth(T) - for(var/R in T) - var/atom/A = R - if(A.smooth) + QUEUE_SMOOTH(turf_to_smooth) + for(var/atom/movable/movable_to_smooth as anything in turf_to_smooth) + if(movable_to_smooth.smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) if(now) - smooth_icon(A) + movable_to_smooth.smooth_icon() else - queue_smooth(A) + QUEUE_SMOOTH(movable_to_smooth) + /atom/proc/clear_smooth_overlays() cut_overlay(top_left_corner) @@ -326,71 +539,109 @@ cut_overlay(bottom_left_corner) bottom_left_corner = null +/// Internal: Takes icon states as text to replace smoothing corner overlays /atom/proc/replace_smooth_overlays(nw, ne, sw, se) clear_smooth_overlays() - var/list/O = list() + var/mutable_appearance/temp_ma + + temp_ma = mutable_appearance(icon, nw) + nw = temp_ma.appearance + + temp_ma = mutable_appearance(icon, ne) + ne = temp_ma.appearance + + temp_ma = mutable_appearance(icon, sw) + sw = temp_ma.appearance + + temp_ma = mutable_appearance(icon, se) + se = temp_ma.appearance + + var/list/new_overlays = list() + top_left_corner = nw - O += nw + new_overlays += nw + top_right_corner = ne - O += ne + new_overlays += ne + bottom_left_corner = sw - O += sw + new_overlays += sw + bottom_right_corner = se - O += se - add_overlay(O) + new_overlays += se + + add_overlay(new_overlays) + +/// Takes a direction, turns it into all the junctions that contain it +/proc/dir_to_all_junctions(dir) + var/handback = NONE + if(dir & NORTH) + handback |= NORTH_JUNCTION | NORTHEAST_JUNCTION | NORTHWEST_JUNCTION + if(dir & SOUTH) + handback |= SOUTH_JUNCTION | SOUTHEAST_JUNCTION | SOUTHWEST_JUNCTION + if(dir & EAST) + handback |= EAST_JUNCTION | SOUTHEAST_JUNCTION | NORTHEAST_JUNCTION + if(dir & WEST) + handback |= WEST_JUNCTION | NORTHWEST_JUNCTION | SOUTHWEST_JUNCTION + return handback + +/proc/dir_to_junction(dir) + switch(dir) + if(NORTH) + return NORTH_JUNCTION + if(SOUTH) + return SOUTH_JUNCTION + if(WEST) + return WEST_JUNCTION + if(EAST) + return EAST_JUNCTION + if(NORTHWEST) + return NORTHWEST_JUNCTION + if(NORTHEAST) + return NORTHEAST_JUNCTION + if(SOUTHEAST) + return SOUTHEAST_JUNCTION + if(SOUTHWEST) + return SOUTHWEST_JUNCTION + else + return NONE /proc/reverse_ndir(ndir) switch(ndir) - if(N_NORTH) + if(NORTH_JUNCTION) return NORTH - if(N_SOUTH) + if(SOUTH_JUNCTION) return SOUTH - if(N_WEST) + if(WEST_JUNCTION) return WEST - if(N_EAST) + if(EAST_JUNCTION) return EAST - if(N_NORTHWEST) + if(NORTHWEST_JUNCTION) return NORTHWEST - if(N_NORTHEAST) + if(NORTHEAST_JUNCTION) return NORTHEAST - if(N_SOUTHEAST) + if(SOUTHEAST_JUNCTION) return SOUTHEAST - if(N_SOUTHWEST) + if(SOUTHWEST_JUNCTION) return SOUTHWEST - if(N_NORTH|N_WEST) + if(NORTH_JUNCTION | WEST_JUNCTION) return NORTHWEST - if(N_NORTH|N_EAST) + if(NORTH_JUNCTION | EAST_JUNCTION) return NORTHEAST - if(N_SOUTH|N_WEST) + if(SOUTH_JUNCTION | WEST_JUNCTION) return SOUTHWEST - if(N_SOUTH|N_EAST) + if(SOUTH_JUNCTION | EAST_JUNCTION) return SOUTHEAST - if(N_NORTH|N_WEST|N_NORTHWEST) + if(NORTH_JUNCTION | WEST_JUNCTION | NORTHWEST_JUNCTION) return NORTHWEST - if(N_NORTH|N_EAST|N_NORTHEAST) + if(NORTH_JUNCTION | EAST_JUNCTION | NORTHEAST_JUNCTION) return NORTHEAST - if(N_SOUTH|N_WEST|N_SOUTHWEST) + if(SOUTH_JUNCTION | WEST_JUNCTION | SOUTHWEST_JUNCTION) return SOUTHWEST - if(N_SOUTH|N_EAST|N_SOUTHEAST) + if(SOUTH_JUNCTION | EAST_JUNCTION | SOUTHEAST_JUNCTION) return SOUTHEAST else - return 0 - -//SSicon_smooth -/proc/queue_smooth_neighbors(atom/A) - for(var/V in orange(1,A)) - var/atom/T = V - if(T.smooth) - queue_smooth(T) - -//SSicon_smooth -/proc/queue_smooth(atom/A) - if(!A.smooth || A.smooth & SMOOTH_QUEUED) - return - - SSicon_smooth.smooth_queue += A - SSicon_smooth.can_fire = 1 - A.smooth |= SMOOTH_QUEUED + return NONE //Example smooth wall @@ -398,5 +649,23 @@ name = "smooth wall" icon = 'icons/turf/smooth_wall.dmi' icon_state = "smooth" - smooth = SMOOTH_TRUE|SMOOTH_DIAGONAL|SMOOTH_BORDER + smoothing_flags = SMOOTH_CORNERS|SMOOTH_DIAGONAL_CORNERS|SMOOTH_BORDER + smoothing_groups = null canSmoothWith = null + +#undef NORTH_JUNCTION +#undef SOUTH_JUNCTION +#undef EAST_JUNCTION +#undef WEST_JUNCTION +#undef NORTHEAST_JUNCTION +#undef NORTHWEST_JUNCTION +#undef SOUTHEAST_JUNCTION +#undef SOUTHWEST_JUNCTION + +#undef NO_ADJ_FOUND +#undef ADJ_FOUND +#undef NULLTURF_BORDER + +#undef DEFAULT_UNDERLAY_ICON +#undef DEFAULT_UNDERLAY_ICON_STATE +#undef CAN_DIAGONAL_SMOOTH diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 91ee06e97e2e..a4044c5c373b 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1134,7 +1134,8 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico I = A.icon if (isnull(icon_state)) icon_state = A.icon_state - if (!(icon_state in icon_states(I, 1))) + //Despite casting to atom, this code path supports mutable appearances, so let's be nice to them + if(isnull(icon_state) || (isatom(thing) && A.flags_1 & HTML_USE_INITAL_ICON_1)) icon_state = initial(A.icon_state) if (isnull(dir)) dir = initial(A.dir) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/logging/_logging.dm similarity index 88% rename from code/__HELPERS/_logging.dm rename to code/__HELPERS/logging/_logging.dm index d678e57e513d..9701deeaa52f 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/logging/_logging.dm @@ -1,20 +1,3 @@ -//wrapper macros for easier grepping -#define DIRECT_OUTPUT(A, B) A << B -#define DIRECT_INPUT(A, B) A >> B -#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) -#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) -#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text) -#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) -#define READ_FILE(file, text) DIRECT_INPUT(file, text) -//This is an external call, "true" and "false" are how rust parses out booleans -#ifdef EXTOOLS_LOGGING -#define WRITE_LOG(log, text) extools_log_write(log, text, TRUE) -#define WRITE_LOG_NO_FORMAT(log, text) extools_log_write(log, text, FALSE) -#else -#define WRITE_LOG(log, text) rustg_log_write(log, "\[[worldtime2text()]\] [text]", "true") -#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") -#endif - //print a warning message to world.log #define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].") /proc/warning(msg) @@ -361,3 +344,52 @@ return "([AREACOORD(T)])" else if(A.loc) return "(UNKNOWN (?, ?, ?))" + +/// Generic logging helper +/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE) + if(!log_globally) + return + + var/log_text = "[key_name(src)] [message] [loc_name(src)]" + switch(message_type) + if(LOG_ATTACK) + log_attack(log_text) + if(LOG_SAY) + log_say(log_text) + if(LOG_WHISPER) + log_whisper(log_text) + if(LOG_EMOTE) + log_emote(log_text) + if(LOG_DSAY) + log_dsay(log_text) + if(LOG_PDA) + log_pda(log_text) + if(LOG_CHAT) + log_chat(log_text) + if(LOG_COMMENT) + log_comment(log_text) + if(LOG_TELECOMMS) + log_telecomms(log_text) + if(LOG_NTSL) + log_ntsl(log_text) + if(LOG_OOC) + log_ooc(log_text) + if(LOG_LOOC) // yogs - LOOC log + log_looc(log_text) // yogs - LOOC log + if(LOG_DONATOR) // yogs - Donator log + log_donator(log_text) // yogs - Donator log + if(LOG_ADMIN) + log_admin(log_text) + if(LOG_ADMIN_PRIVATE) + log_admin_private(log_text) + if(LOG_ASAY) + log_adminsay(log_text) + if(LOG_OWNERSHIP) + log_game(log_text) + if(LOG_GAME) + log_game(log_text) + if(LOG_MECHA) + log_mecha(log_text) + else + stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") + log_game(log_text) diff --git a/code/__HELPERS/logging/attack.dm b/code/__HELPERS/logging/attack.dm new file mode 100644 index 000000000000..9340c22d0eee --- /dev/null +++ b/code/__HELPERS/logging/attack.dm @@ -0,0 +1,64 @@ +/** + * Log a combat message in the attack log + * + * 1 argument is the actor performing the action + * 2 argument is the target of the action + * 3 is a verb describing the action (e.g. punched, throwed, kicked, etc.) + * 4 is a tool with which the action was made (usually an item) + * 5 is any additional text, which will be appended to the rest of the log line + */ +/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) + var/ssource = key_name(user) + var/starget = key_name(target) + + var/mob/living/living_target = target + var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" + + var/sobject = "" + if(object) + sobject = " with [object]" + var/saddition = "" + if(addition) + saddition = " [addition]" + + var/postfix = "[sobject][saddition][hp]" + + var/message = "has [what_done] [starget][postfix]" + user.log_message(message, LOG_ATTACK, color="red") + + if(user != target) + var/reverse_message = "has been [what_done] by [ssource][postfix]" + target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) + +/** + * log_wound() is for when someone is *attacked* and suffers a wound. Note that this only captures wounds from damage, so smites/forced wounds aren't logged, as well as demotions like cuts scabbing over + * + * Note that this has no info on the attack that dealt the wound: information about where damage came from isn't passed to the bodypart's damaged proc. When in doubt, check the attack log for attacks at that same time + * TODO later: Add logging for healed wounds, though that will require some rewriting of healing code to prevent admin heals from spamming the logs. Not high priority + * + * Arguments: + * * victim- The guy who got wounded + * * suffered_wound- The wound, already applied, that we're logging. It has to already be attached so we can get the limb from it + * * dealt_damage- How much damage is associated with the attack that dealt with this wound. + * * dealt_wound_bonus- The wound_bonus, if one was specified, of the wounding attack + * * dealt_bare_wound_bonus- The bare_wound_bonus, if one was specified *and applied*, of the wounding attack. Not shown if armor was present + * * base_roll- Base wounding ability of an attack is a random number from 1 to (dealt_damage ** WOUND_DAMAGE_EXPONENT). This is the number that was rolled in there, before mods + */ +/proc/log_wound(atom/victim, datum/wound/suffered_wound, dealt_damage, dealt_wound_bonus, dealt_bare_wound_bonus, base_roll) + if(QDELETED(victim) || !suffered_wound) + return + var/message = "has suffered: [suffered_wound][suffered_wound.limb ? " to [suffered_wound.limb.name]" : null]"// maybe indicate if it's a promote/demote? + + if(dealt_damage) + message += " | Damage: [dealt_damage]" + // The base roll is useful since it can show how lucky someone got with the given attack. For example, dealing a cut + if(base_roll) + message += "(rolled [base_roll]/[dealt_damage ** WOUND_DAMAGE_EXPONENT])" + + if(dealt_wound_bonus) + message += " | WB: [dealt_wound_bonus]" + + if(dealt_bare_wound_bonus) + message += " | BWB: [dealt_bare_wound_bonus]" + + victim.log_message(message, LOG_ATTACK, color="blue") diff --git a/code/__HELPERS/logging/talk.dm b/code/__HELPERS/logging/talk.dm new file mode 100644 index 000000000000..5f63b3a3ced6 --- /dev/null +++ b/code/__HELPERS/logging/talk.dm @@ -0,0 +1,15 @@ +/// Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) +/atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null) + var/prefix = tag ? "([tag]) " : "" + var/suffix = forced_by ? " FORCED by [forced_by]" : "" + log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally) + +/// Helper for logging of messages with only one sender and receiver +/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) + if(!tag) + stack_trace("Unspecified tag for private message") + tag = "UNKNOWN" + + source.log_talk(message, message_type, tag="[tag] to [key_name(target)]") + if(source != target) + target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index d346e7fae044..38bfcf2dbd8a 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -34,15 +34,12 @@ /proc/Get_Pixel_Angle(y, x)//for getting the angle when animating something's pixel_x and pixel_y if(!y) - return (x>=0)?90:270 - .=arctan(x/y) - if(y<0) - .+=180 - else if(x<0) - .+=360 - -//Better performant than an artisanal proc and more reliable than Turn(). From TGMC. -#define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) + return (x >= 0) ? 90 : 270 + . = arctan(x / y) + if(y < 0) + . += 180 + else if(x < 0) + . += 360 //Returns location. Returns null if no location was found. /proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = FALSE, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 2123f6a32d20..7aa8761ec224 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -198,14 +198,6 @@ DEFINE_BITFIELD(tesla_flags, list( "TESLA_MACHINE_EXPLOSIVE" = TESLA_MACHINE_EXPLOSIVE, )) -DEFINE_BITFIELD(smooth, list( - "SMOOTH_TRUE" = SMOOTH_TRUE, - "SMOOTH_MORE" = SMOOTH_MORE, - "SMOOTH_DIAGONAL" = SMOOTH_DIAGONAL, - "SMOOTH_BORDER" = SMOOTH_BORDER, - "SMOOTH_QUEUED" = SMOOTH_QUEUED, -)) - DEFINE_BITFIELD(car_traits, list( "CAN_KIDNAP" = CAN_KIDNAP, )) diff --git a/code/controllers/subsystem/icon_smooth.dm b/code/controllers/subsystem/icon_smooth.dm index c5d02cf9439c..338f50b2021c 100644 --- a/code/controllers/subsystem/icon_smooth.dm +++ b/code/controllers/subsystem/icon_smooth.dm @@ -16,7 +16,7 @@ SUBSYSTEM_DEF(icon_smooth) var/atom/A = cached[cached.len] cached.len-- if (A.flags_1 & INITIALIZED_1) - smooth_icon(A) + A.smooth_icon() else deferred += A if (MC_TICK_CHECK) @@ -38,7 +38,7 @@ SUBSYSTEM_DEF(icon_smooth) var/atom/A = V if(!A || A.z <= 2) continue - smooth_icon(A) + A.smooth_icon() CHECK_TICK return SS_INIT_SUCCESS diff --git a/code/datums/elements/undertile.dm b/code/datums/elements/undertile.dm new file mode 100644 index 000000000000..b540e0e3ae1d --- /dev/null +++ b/code/datums/elements/undertile.dm @@ -0,0 +1,84 @@ +/// The alpha we give to stuff under tiles, if they want it +#define ALPHA_UNDERTILE 128 + +///Add to an object if you want to be able to be hidden under tiles +/datum/element/undertile + element_flags = ELEMENT_BESPOKE | COMPONENT_DUPE_HIGHLANDER + argument_hash_start_idx = 2 + + ///the invisiblity trait applied, like TRAIT_T_RAY_VISIBLE + var/invisibility_trait + ///level of invisibility applied when under a tile. Could be INVISIBILITY_OBSERVER if you still want it to be visible to ghosts + var/invisibility_level + ///an overlay for the tile if we wish to apply that + var/tile_overlay + ///whether we use alpha or not. TRUE uses ALPHA_UNDERTILE because otherwise we have 200 different instances of this element for different alphas + var/use_alpha + ///We will switch between anchored and unanchored. for stuff like satchels that shouldn't be pullable under tiles but are otherwise unanchored + var/use_anchor + +/datum/element/undertile/Attach(datum/target, invisibility_trait, invisibility_level = INVISIBILITY_MAXIMUM, tile_overlay, use_alpha = TRUE, use_anchor = FALSE) + . = ..() + + if(!ismovable(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_OBJ_HIDE, PROC_REF(hide)) + + src.invisibility_trait = invisibility_trait + src.invisibility_level = invisibility_level + src.tile_overlay = tile_overlay + src.use_alpha = use_alpha + src.use_anchor = use_anchor + +///called when a tile has been covered or uncovered +/datum/element/undertile/proc/hide(atom/movable/source, underfloor_accessibility) + SIGNAL_HANDLER + + if(underfloor_accessibility < UNDERFLOOR_VISIBLE) + source.SetInvisibility(invisibility_level, id=type) + else + source.RemoveInvisibility(type) + + var/turf/T = get_turf(source) + + if(underfloor_accessibility < UNDERFLOOR_INTERACTABLE) + source.plane = FLOOR_PLANE // We do this so that turfs that allow you to see what's underneath them don't have to be on the game plane (which causes ambient occlusion weirdness) + ADD_TRAIT(source, TRAIT_UNDERFLOOR, REF(src)) + + if(tile_overlay) + T.add_overlay(tile_overlay) + + if(use_anchor) + source.set_anchored(TRUE) + + if(underfloor_accessibility < UNDERFLOOR_VISIBLE) + if(use_alpha) + source.alpha = ALPHA_UNDERTILE + + if(invisibility_trait) + ADD_TRAIT(source, invisibility_trait, ELEMENT_TRAIT(type)) + + else + source.plane = initial(source.plane) + REMOVE_TRAIT(source, TRAIT_UNDERFLOOR, REF(src)) + + if(invisibility_trait) + REMOVE_TRAIT(source, invisibility_trait, ELEMENT_TRAIT(type)) + + if(tile_overlay) + T.overlays -= tile_overlay + + if(use_alpha) + source.alpha = initial(source.alpha) + + if(use_anchor) + source.set_anchored(FALSE) + +/datum/element/undertile/Detach(atom/movable/source, visibility_trait, invisibility_level = INVISIBILITY_MAXIMUM) + . = ..() + + hide(source, UNDERFLOOR_INTERACTABLE) + source.RemoveInvisibility(type) + +#undef ALPHA_UNDERTILE diff --git a/code/game/atoms.dm b/code/game/atom/_atom.dm similarity index 82% rename from code/game/atoms.dm rename to code/game/atom/_atom.dm index 191525de75e4..f7c5b8b8a29b 100644 --- a/code/game/atoms.dm +++ b/code/game/atom/_atom.dm @@ -7,7 +7,6 @@ /atom layer = TURF_LAYER plane = GAME_PLANE - var/level = 2 ///If non-null, overrides a/an/some in all cases var/article @@ -119,6 +118,23 @@ ///Mobs that are currently do_after'ing this atom, to be cleared from on Destroy() var/list/targeted_by + ///Icon-smoothing behavior. + var/smoothing_flags = NONE + ///What directions this is currently smoothing with. IMPORTANT: This uses the smoothing direction flags as defined in icon_smoothing.dm, instead of the BYOND flags. + var/smoothing_junction = null //This starts as null for us to know when it's first set, but after that it will hold a 8-bit mask ranging from 0 to 255. + ///Smoothing variable + var/top_left_corner + ///Smoothing variable + var/top_right_corner + ///Smoothing variable + var/bottom_left_corner + ///Smoothing variable + var/bottom_right_corner + ///What smoothing groups does this atom belongs to, to match canSmoothWith. If null, nobody can smooth with it. Must be sorted. + var/list/smoothing_groups = null + ///List of smoothing groups this atom can smooth with. If this is null and atom is smooth, it smooths only with itself. Must be sorted. + var/list/canSmoothWith = null + var/atom/orbit_target //Reference to atom being orbited /** * Called when an atom is created in byond (built in engine proc) @@ -1287,89 +1303,6 @@ /atom/proc/return_temperature() return -/** - * Tool behavior procedure. Redirects to tool-specific procs by default. - * - * You can override it to catch all tool interactions, for use in complex deconstruction procs. - * - * Must return parent proc ..() in the end if overridden - */ -/atom/proc/tool_act(mob/living/user, obj/item/tool, tool_type) - var/act_result - var/signal_result - - signal_result = SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT(tool_type), user, tool) - if(signal_result & COMPONENT_BLOCK_TOOL_ATTACK) // The COMSIG_ATOM_TOOL_ACT signal is blocking the act - return TOOL_ACT_SIGNAL_BLOCKING - if(QDELETED(tool)) - return TRUE - - switch(tool_type) - if(TOOL_CROWBAR) - act_result = crowbar_act(user, tool) - if(TOOL_MULTITOOL) - act_result = multitool_act(user, tool) - if(TOOL_SCREWDRIVER) - act_result = screwdriver_act(user, tool) - if(TOOL_WRENCH) - act_result = wrench_act(user, tool) - if(TOOL_WIRECUTTER) - act_result = wirecutter_act(user, tool) - if(TOOL_WELDER) - act_result = welder_act(user, tool) - if(TOOL_ANALYZER) - act_result = analyzer_act(user, tool) - if(!act_result) - return - - if(. && tool.toolspeed < 1) //nice tool bro - SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "nice_tool", /datum/mood_event/nice_tool) - - // A tooltype_act has completed successfully -// log_tool("[key_name(user)] used [tool] on [src] at [AREACOORD(src)]") - SEND_SIGNAL(tool, COMSIG_TOOL_ATOM_ACTED_PRIMARY(tool_type), src) - return TOOL_ACT_TOOLTYPE_SUCCESS - - -//! Tool-specific behavior procs. To be overridden in subtypes. -/// - -///Crowbar act -/atom/proc/crowbar_act(mob/living/user, obj/item/I) - return - -///Multitool act -/atom/proc/multitool_act(mob/living/user, obj/item/I) - return - -///Check if the multitool has an item in it's data buffer -/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE) - if(!istype(I, /obj/item/multitool)) - if(user && !silent) - to_chat(user, span_warning("[I] has no data buffer!")) - return FALSE - return TRUE - -///Screwdriver act -/atom/proc/screwdriver_act(mob/living/user, obj/item/I) - SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER), user, I) - -///Wrench act -/atom/proc/wrench_act(mob/living/user, obj/item/I) - return - -///Wirecutter act -/atom/proc/wirecutter_act(mob/living/user, obj/item/I) - return - -///Welder act -/atom/proc/welder_act(mob/living/user, obj/item/I) - return - -///Analyzer act -/atom/proc/analyzer_act(mob/living/user, obj/item/I) - return - ///Generate a tag for this atom /atom/proc/GenerateTag() return @@ -1378,137 +1311,6 @@ /atom/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE) return -/// Generic logging helper -/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE) - if(!log_globally) - return - - var/log_text = "[key_name(src)] [message] [loc_name(src)]" - switch(message_type) - if(LOG_ATTACK) - log_attack(log_text) - if(LOG_SAY) - log_say(log_text) - if(LOG_WHISPER) - log_whisper(log_text) - if(LOG_EMOTE) - log_emote(log_text) - if(LOG_DSAY) - log_dsay(log_text) - if(LOG_PDA) - log_pda(log_text) - if(LOG_CHAT) - log_chat(log_text) - if(LOG_COMMENT) - log_comment(log_text) - if(LOG_TELECOMMS) - log_telecomms(log_text) - if(LOG_NTSL) - log_ntsl(log_text) - if(LOG_OOC) - log_ooc(log_text) - if(LOG_LOOC) // yogs - LOOC log - log_looc(log_text) // yogs - LOOC log - if(LOG_DONATOR) // yogs - Donator log - log_donator(log_text) // yogs - Donator log - if(LOG_ADMIN) - log_admin(log_text) - if(LOG_ADMIN_PRIVATE) - log_admin_private(log_text) - if(LOG_ASAY) - log_adminsay(log_text) - if(LOG_OWNERSHIP) - log_game(log_text) - if(LOG_GAME) - log_game(log_text) - if(LOG_MECHA) - log_mecha(log_text) - else - stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") - log_game(log_text) - -/// Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) -/atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null) - var/prefix = tag ? "([tag]) " : "" - var/suffix = forced_by ? " FORCED by [forced_by]" : "" - log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally) - -/// Helper for logging of messages with only one sender and receiver -/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) - if(!tag) - stack_trace("Unspecified tag for private message") - tag = "UNKNOWN" - - source.log_talk(message, message_type, tag="[tag] to [key_name(target)]") - if(source != target) - target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE) - -/** - * Log a combat message in the attack log - * - * 1 argument is the actor performing the action - * 2 argument is the target of the action - * 3 is a verb describing the action (e.g. punched, throwed, kicked, etc.) - * 4 is a tool with which the action was made (usually an item) - * 5 is any additional text, which will be appended to the rest of the log line - */ -/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) - var/ssource = key_name(user) - var/starget = key_name(target) - - var/mob/living/living_target = target - var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" - - var/sobject = "" - if(object) - sobject = " with [object]" - var/saddition = "" - if(addition) - saddition = " [addition]" - - var/postfix = "[sobject][saddition][hp]" - - var/message = "has [what_done] [starget][postfix]" - user.log_message(message, LOG_ATTACK, color="red") - - if(user != target) - var/reverse_message = "has been [what_done] by [ssource][postfix]" - target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) - -/** - * log_wound() is for when someone is *attacked* and suffers a wound. Note that this only captures wounds from damage, so smites/forced wounds aren't logged, as well as demotions like cuts scabbing over - * - * Note that this has no info on the attack that dealt the wound: information about where damage came from isn't passed to the bodypart's damaged proc. When in doubt, check the attack log for attacks at that same time - * TODO later: Add logging for healed wounds, though that will require some rewriting of healing code to prevent admin heals from spamming the logs. Not high priority - * - * Arguments: - * * victim- The guy who got wounded - * * suffered_wound- The wound, already applied, that we're logging. It has to already be attached so we can get the limb from it - * * dealt_damage- How much damage is associated with the attack that dealt with this wound. - * * dealt_wound_bonus- The wound_bonus, if one was specified, of the wounding attack - * * dealt_bare_wound_bonus- The bare_wound_bonus, if one was specified *and applied*, of the wounding attack. Not shown if armor was present - * * base_roll- Base wounding ability of an attack is a random number from 1 to (dealt_damage ** WOUND_DAMAGE_EXPONENT). This is the number that was rolled in there, before mods - */ -/proc/log_wound(atom/victim, datum/wound/suffered_wound, dealt_damage, dealt_wound_bonus, dealt_bare_wound_bonus, base_roll) - if(QDELETED(victim) || !suffered_wound) - return - var/message = "has suffered: [suffered_wound][suffered_wound.limb ? " to [suffered_wound.limb.name]" : null]"// maybe indicate if it's a promote/demote? - - if(dealt_damage) - message += " | Damage: [dealt_damage]" - // The base roll is useful since it can show how lucky someone got with the given attack. For example, dealing a cut - if(base_roll) - message += "(rolled [base_roll]/[dealt_damage ** WOUND_DAMAGE_EXPONENT])" - - if(dealt_wound_bonus) - message += " | WB: [dealt_wound_bonus]" - - if(dealt_bare_wound_bonus) - message += " | BWB: [dealt_bare_wound_bonus]" - - victim.log_message(message, LOG_ATTACK, color="blue") - - /atom/movable/proc/add_filter(name,priority,list/params) if(!filter_data) filter_data = list() @@ -1550,25 +1352,6 @@ /atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) return FALSE -/**Returns the material composition of the atom. - * - * Used when recycling items, specifically to turn alloys back into their component mats. - * - * Exists because I'd need to add a way to un-alloy alloys or otherwise deal - * with people converting the entire stations material supply into alloys. - * - * Arguments: - * - flags: A set of flags determining how exactly the materials are broken down. - */ -/atom/proc/get_material_composition(breakdown_flags=NONE) - . = list() - var/list/cached_materials = custom_materials - for(var/mat in cached_materials) - var/datum/material/material = getmaterialref(mat) - var/list/material_comp = material.return_composition(cached_materials[material], breakdown_flags) - for(var/comp_mat in material_comp) - .[comp_mat] += material_comp[comp_mat] - ///Setter for the `density` variable to append behavior related to its changing. /atom/proc/set_density(new_value) SHOULD_CALL_PARENT(TRUE) @@ -1600,41 +1383,6 @@ /atom/proc/setClosed() return -/** - * Recursive getter method to return a list of all ghosts orbitting this atom - * - * This will work fine without manually passing arguments. - */ -/atom/proc/get_all_orbiters(list/processed, source = TRUE) - var/list/output = list() - if (!processed) - processed = list() - if (src in processed) - return output - if (!source) - output += src - processed += src - for (var/o in orbiters?.orbiters) - var/atom/atom_orbiter = o - output += atom_orbiter.get_all_orbiters(processed, source = FALSE) - return output - -///Sets the custom materials for an item. -/atom/proc/set_custom_materials(list/materials, multiplier = 1) - if(custom_materials) //Only runs if custom materials existed at first. Should usually be the case but check anyways - for(var/i in custom_materials) - var/datum/material/custom_material = i - custom_material.on_removed(src, material_flags) //Remove the current materials - - custom_materials = list() //Reset the list - - for(var/x in materials) - var/datum/material/custom_material = x - - - custom_material.on_applied(src, materials[custom_material] * multiplier, material_flags) - custom_materials[custom_material] += materials[x] * multiplier - ///Passes Stat Browser Panel clicks to the game and calls client click on an atom /atom/Topic(href, list/href_list) . = ..() diff --git a/code/game/atom/atom_invisibility.dm b/code/game/atom/atom_invisibility.dm new file mode 100644 index 000000000000..53fbe895817c --- /dev/null +++ b/code/game/atom/atom_invisibility.dm @@ -0,0 +1,72 @@ +// Index defines +#define INVISIBILITY_VALUE 1 +#define INVISIBILITY_PRIORITY 2 + +/atom + VAR_PRIVATE/list/invisibility_sources + VAR_PRIVATE/current_invisibility_priority = -INFINITY + +/atom/proc/RecalculateInvisibility() + PRIVATE_PROC(TRUE) + + if(!invisibility_sources) + current_invisibility_priority = -INFINITY + invisibility = initial(invisibility) + return + + var/highest_priority + var/list/highest_priority_invisibility_data + for(var/entry in invisibility_sources) + var/list/priority_data + if(islist(entry)) + priority_data = entry + else + priority_data = invisibility_sources[entry] + + var/priority = priority_data[INVISIBILITY_PRIORITY] + if(highest_priority > priority) // In the case of equal priorities, we use the last thing in the list so that more recent changes apply first + continue + + highest_priority = priority + highest_priority_invisibility_data = priority_data + + current_invisibility_priority = highest_priority + invisibility = highest_priority_invisibility_data[INVISIBILITY_VALUE] + +/** + * Sets invisibility according to priority. + * If you want to be able to undo the value you set back to what it would be otherwise, + * you should provide an id here and remove it using RemoveInvisibility(id) + */ +/atom/proc/SetInvisibility(desired_value, id, priority=0) + if(!invisibility_sources) + invisibility_sources = list() + + if(id) + invisibility_sources[id] = list(desired_value, priority) + else + invisibility_sources += list(list(desired_value, priority)) + + if(current_invisibility_priority > priority) + return + + RecalculateInvisibility() + +/// Removes the specified invisibility source from the tracker +/atom/proc/RemoveInvisibility(id) + if(!invisibility_sources) + return + + var/list/priority_data = invisibility_sources[id] + invisibility_sources -= id + + if(length(invisibility_sources) == 0) + invisibility_sources = null + + if(current_invisibility_priority > priority_data[INVISIBILITY_PRIORITY]) + return + + RecalculateInvisibility() + +#undef INVISIBILITY_VALUE +#undef INVISIBILITY_PRIORITY diff --git a/code/game/atom/atom_materials.dm b/code/game/atom/atom_materials.dm new file mode 100644 index 000000000000..e4536e5ede5c --- /dev/null +++ b/code/game/atom/atom_materials.dm @@ -0,0 +1,34 @@ +///Sets the custom materials for an item. +/atom/proc/set_custom_materials(list/materials, multiplier = 1) + if(custom_materials) //Only runs if custom materials existed at first. Should usually be the case but check anyways + for(var/i in custom_materials) + var/datum/material/custom_material = i + custom_material.on_removed(src, material_flags) //Remove the current materials + + custom_materials = list() //Reset the list + + for(var/x in materials) + var/datum/material/custom_material = x + + + custom_material.on_applied(src, materials[custom_material] * multiplier, material_flags) + custom_materials[custom_material] += materials[x] * multiplier + +/**Returns the material composition of the atom. + * + * Used when recycling items, specifically to turn alloys back into their component mats. + * + * Exists because I'd need to add a way to un-alloy alloys or otherwise deal + * with people converting the entire stations material supply into alloys. + * + * Arguments: + * - flags: A set of flags determining how exactly the materials are broken down. + */ +/atom/proc/get_material_composition(breakdown_flags=NONE) + . = list() + var/list/cached_materials = custom_materials + for(var/mat in cached_materials) + var/datum/material/material = getmaterialref(mat) + var/list/material_comp = material.return_composition(cached_materials[material], breakdown_flags) + for(var/comp_mat in material_comp) + .[comp_mat] += material_comp[comp_mat] diff --git a/code/game/atom/atom_orbit.dm b/code/game/atom/atom_orbit.dm new file mode 100644 index 000000000000..6b7a337164f3 --- /dev/null +++ b/code/game/atom/atom_orbit.dm @@ -0,0 +1,18 @@ +/** + * Recursive getter method to return a list of all ghosts orbitting this atom + * + * This will work fine without manually passing arguments. + */ +/atom/proc/get_all_orbiters(list/processed, source = TRUE) + var/list/output = list() + if (!processed) + processed = list() + if (src in processed) + return output + if (!source) + output += src + processed += src + for (var/o in orbiters?.orbiters) + var/atom/atom_orbiter = o + output += atom_orbiter.get_all_orbiters(processed, source = FALSE) + return output diff --git a/code/game/atom/atom_tool_acts.dm b/code/game/atom/atom_tool_acts.dm new file mode 100644 index 000000000000..f706ea21ebd7 --- /dev/null +++ b/code/game/atom/atom_tool_acts.dm @@ -0,0 +1,82 @@ +/** + * Tool behavior procedure. Redirects to tool-specific procs by default. + * + * You can override it to catch all tool interactions, for use in complex deconstruction procs. + * + * Must return parent proc ..() in the end if overridden + */ +/atom/proc/tool_act(mob/living/user, obj/item/tool, tool_type) + var/act_result + var/signal_result + + signal_result = SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT(tool_type), user, tool) + if(signal_result & COMPONENT_BLOCK_TOOL_ATTACK) // The COMSIG_ATOM_TOOL_ACT signal is blocking the act + return TOOL_ACT_SIGNAL_BLOCKING + if(QDELETED(tool)) + return TRUE + + switch(tool_type) + if(TOOL_CROWBAR) + act_result = crowbar_act(user, tool) + if(TOOL_MULTITOOL) + act_result = multitool_act(user, tool) + if(TOOL_SCREWDRIVER) + act_result = screwdriver_act(user, tool) + if(TOOL_WRENCH) + act_result = wrench_act(user, tool) + if(TOOL_WIRECUTTER) + act_result = wirecutter_act(user, tool) + if(TOOL_WELDER) + act_result = welder_act(user, tool) + if(TOOL_ANALYZER) + act_result = analyzer_act(user, tool) + if(!act_result) + return + + if(. && tool.toolspeed < 1) //nice tool bro + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "nice_tool", /datum/mood_event/nice_tool) + + // A tooltype_act has completed successfully +// log_tool("[key_name(user)] used [tool] on [src] at [AREACOORD(src)]") + SEND_SIGNAL(tool, COMSIG_TOOL_ATOM_ACTED_PRIMARY(tool_type), src) + return TOOL_ACT_TOOLTYPE_SUCCESS + + +//! Tool-specific behavior procs. To be overridden in subtypes. +/// + +///Crowbar act +/atom/proc/crowbar_act(mob/living/user, obj/item/I) + return + +///Multitool act +/atom/proc/multitool_act(mob/living/user, obj/item/I) + return + +///Check if the multitool has an item in it's data buffer +/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE) + if(!istype(I, /obj/item/multitool)) + if(user && !silent) + to_chat(user, span_warning("[I] has no data buffer!")) + return FALSE + return TRUE + +///Screwdriver act +/atom/proc/screwdriver_act(mob/living/user, obj/item/I) + SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER), user, I) + +///Wrench act +/atom/proc/wrench_act(mob/living/user, obj/item/I) + return + +///Wirecutter act +/atom/proc/wirecutter_act(mob/living/user, obj/item/I) + return + +///Welder act +/atom/proc/welder_act(mob/living/user, obj/item/I) + return + +///Analyzer act +/atom/proc/analyzer_act(mob/living/user, obj/item/I) + return diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 99dc8e01778b..f1fe41b3dfc5 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -50,6 +50,9 @@ ///Internal holder for emissive blocker object, do not use directly use blocks_emissive var/atom/movable/emissive_blocker/em_block + ///Used for the calculate_adjacencies proc for icon smoothing. + var/can_be_unanchored = FALSE + /// The degree of thermal insulation that mobs in list/contents have from the external environment, between 0 and 1 var/contents_thermal_insulation = 0 /// The degree of pressure protection that mobs in list/contents have from the external environment, between 0 and 1 diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index fd481d935f78..f921a9adf129 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -1,10 +1,8 @@ /obj/machinery/bluespace_beacon - icon = 'icons/obj/objects.dmi' icon_state = "floor_beaconf" name = "bluespace gigabeacon" desc = "A device that draws power from bluespace and creates a permanent tracking beacon." - level = 1 // underfloor layer = LOW_OBJ_LAYER use_power = IDLE_POWER_USE idle_power_usage = 0 @@ -14,19 +12,14 @@ . = ..() var/turf/T = loc Beacon = new(T) - Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.SetInvisibility(INVISIBILITY_MAXIMUM) - hide(T.intact) + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) /obj/machinery/bluespace_beacon/Destroy() QDEL_NULL(Beacon) return ..() -// update the invisibility and icon -/obj/machinery/bluespace_beacon/hide(intact) - invisibility = intact ? INVISIBILITY_MAXIMUM : 0 - updateicon() - // update the icon_state /obj/machinery/bluespace_beacon/proc/updateicon() var/state="floor_beacon" @@ -41,7 +34,7 @@ if(!Beacon) var/turf/T = loc Beacon = new(T) - Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.SetInvisibility(INVISIBILITY_MAXIMUM) else if (Beacon.loc != loc) Beacon.forceMove(loc) diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index f89e535290a8..1875ad8524df 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -118,13 +118,15 @@ name = "sandbags" desc = "Bags of sand. Self explanatory." icon = 'icons/obj/smooth_structures/sandbags.dmi' - icon_state = "sandbags" + icon_state = "sandbags-0" + base_icon_state = "sandbags" max_integrity = 280 proj_pass_rate = 20 pass_flags = LETPASSTHROW bar_material = SAND - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/barricade/sandbags, /turf/closed/wall, /turf/closed/wall/r_wall, /obj/structure/falsewall, /obj/structure/falsewall/reinforced, /turf/closed/wall/rust, /turf/closed/wall/r_wall/rust, /obj/structure/barricade/security) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SANDBAGS + canSmoothWith = SMOOTH_GROUP_SANDBAGS + SMOOTH_GROUP_SECURITY_BARRICADE + SMOOTH_GROUP_WALLS /obj/structure/barricade/sandbags/Initialize(mapload) . = ..() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index fcb931fa2f4a..7fa0e2fc3b70 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -151,7 +151,7 @@ ///Whether wires should all cut themselves when this door is broken. var/cut_wires_on_break = TRUE - flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1 + flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1 | HTML_USE_INITAL_ICON_1 rad_insulation = RAD_MEDIUM_INSULATION var/static/list/airlock_overlays = list() diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 6e14252d83cd..f26d53db9c00 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -27,7 +27,6 @@ set_codes() var/turf/T = loc - hide(T.intact) if(codes["patrol"]) if(!GLOB.navbeacons["[z]"]) GLOB.navbeacons["[z]"] = list() @@ -35,6 +34,8 @@ if(codes["delivery"]) GLOB.deliverybeacons += src GLOB.deliverybeacontags += location + + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) /obj/machinery/navbeacon/Destroy() if (GLOB.navbeacons["[z]"]) diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index 2d536cadb7f6..a9d31fb3f18b 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -31,7 +31,8 @@ GLOBAL_LIST_EMPTY(telecomms_list) var/on = TRUE var/toggled = TRUE // Is it toggled on var/long_range_link = FALSE // Can you link it across Z levels or on the otherside of the map? (Relay & Hub) - var/hide = FALSE // Is it a hidden machine? + /// Is it a hidden machine? + var/hide = FALSE var/generates_heat = TRUE //yogs turn off tcomms generating heat var/heatoutput = 2500 //yogs modify power output per trafic removed(usual heat capacity of the air in server room is 1600J/K) diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm index f06d0b4be554..d0540c13aebf 100644 --- a/code/game/objects/effects/contraband.dm +++ b/code/game/objects/effects/contraband.dm @@ -130,7 +130,7 @@ return // Deny placing posters on currently-diagonal walls, although the wall may change in the future. - if (smooth & SMOOTH_DIAGONAL) + if (smoothing_flags & SMOOTH_DIAGONAL_CORNERS) for (var/O in overlays) var/image/I = O if(copytext(I.icon_state, 1, 3) == "d-") //3 == length("d-") + 1 @@ -169,7 +169,7 @@ /turf/closed/proc/place_borg_poster(obj/item/wantedposterposter/P, mob/user) // Deny placing posters on currently-diagonal walls, although the wall may change in the future. - if (smooth & SMOOTH_DIAGONAL) + if (smoothing_flags & SMOOTH_DIAGONAL_CORNERS) for (var/O in overlays) var/image/I = O if (copytext(I.icon_state, 1, 3) == "d-") diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index b3260fd51c58..7eef9e32d3e1 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -48,23 +48,25 @@ /obj/effect/decal/cleanable/dirt name = "dirt" desc = "Someone should clean that up." - icon_state = "dirt" - canSmoothWith = list(/obj/effect/decal/cleanable/dirt, /turf/closed/wall, /obj/structure/falsewall) - smooth = SMOOTH_FALSE + icon_state = "dirt-flat-0" + base_icon_state = "dirt" mouse_opacity = MOUSE_OPACITY_TRANSPARENT + smoothing_flags = NONE + smoothing_groups = SMOOTH_GROUP_CLEANABLE_DIRT + canSmoothWith = SMOOTH_GROUP_CLEANABLE_DIRT + SMOOTH_GROUP_WALLS /obj/effect/decal/cleanable/dirt/Initialize(mapload) . = ..() var/turf/T = get_turf(src) if(T.tiled_dirt) - smooth = SMOOTH_MORE - icon = 'icons/effects/dirt.dmi' - icon_state = "" - queue_smooth(src) - queue_smooth_neighbors(src) + smoothing_flags = SMOOTH_BITMASK + QUEUE_SMOOTH(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) /obj/effect/decal/cleanable/dirt/Destroy() - queue_smooth_neighbors(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) return ..() /obj/effect/decal/cleanable/dirt/dust @@ -241,4 +243,4 @@ /obj/effect/decal/cleanable/dirt_siding/corner name = "dirt corner" - icon_state = "dirt_side_corner" \ No newline at end of file + icon_state = "dirt_side_corner" diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm index ca003178d8da..5621a07d5f1a 100644 --- a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm +++ b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm @@ -88,8 +88,7 @@ if(object == src) continue if(isturf(object.loc)) - var/turf/turf = object.loc - if(turf.intact && object.level == 1) //hidden under the floor + if(turf_location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(object, TRAIT_T_RAY_VISIBLE)) continue reagents.reaction(object, TOUCH|VAPOR, fraction) diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm index 030d78c8d201..585fe1433621 100644 --- a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm @@ -370,7 +370,7 @@ for(var/atom/movable/thing as anything in location) if(thing == src) continue - if(location.intact && thing.level == 1) //hidden under the floor + if(location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(thing, TRAIT_T_RAY_VISIBLE)) continue reagents.reaction(thing, TOUCH, fraction) diff --git a/code/game/objects/items/devices/pressureplates.dm b/code/game/objects/items/devices/pressureplates.dm index 516b1d299030..d6ec1ef6c373 100644 --- a/code/game/objects/items/devices/pressureplates.dm +++ b/code/game/objects/items/devices/pressureplates.dm @@ -32,7 +32,7 @@ sigdev.code = roundstart_signaller_code sigdev.frequency = roundstart_signaller_freq if(isopenturf(loc)) - hide(TRUE) + AddElement(/datum/element/undertile, tile_overlay = tile_overlay, use_anchor = TRUE) /obj/item/pressure_plate/Crossed(atom/movable/AM) . = ..() diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index cd41be37a51b..9d8244d815de 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -319,6 +319,7 @@ /obj/item/storage/backpack/satchel/flat/Initialize(mapload) . = ..() + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE, INVISIBILITY_OBSERVER, use_anchor = TRUE) var/datum/component/storage/STR = GetComponent(/datum/component/storage) STR.max_combined_w_class = 15 STR.set_holdable(null, list(/obj/item/storage/backpack/satchel/flat)) //muh recursive backpacks) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 9dd4ff9c89a1..0cd9211a2323 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -12,16 +12,17 @@ if (!armor) armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 50, ACID = 50) . = ..() - if(smooth) - queue_smooth(src) - queue_smooth_neighbors(src) - icon_state = "" + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH(src) + QUEUE_SMOOTH_NEIGHBORS(src) + if(smoothing_flags & SMOOTH_CORNERS) + icon_state = "" GLOB.cameranet.updateVisibility(src) /obj/structure/Destroy() GLOB.cameranet.updateVisibility(src) - if(smooth) - queue_smooth_neighbors(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) return ..() /obj/structure/CanAllowThrough(atom/movable/mover, turf/target) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 8837107f9cb7..3de6b5a2b975 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -53,15 +53,16 @@ name = "resin" desc = "Looks like some kind of thick resin." icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi' - icon_state = "smooth" + icon_state = "resin_wall-0" + base_icon_state = "resin_wall" density = TRUE opacity = TRUE anchored = TRUE - canSmoothWith = list(/obj/structure/alien/resin) max_integrity = 200 - smooth = SMOOTH_TRUE - var/resintype = null CanAtmosPass = ATMOS_PASS_DENSITY + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_ALIEN_RESIN + canSmoothWith = SMOOTH_GROUP_ALIEN_RESIN /obj/structure/alien/resin/Initialize(mapload) @@ -77,9 +78,11 @@ name = "resin wall" desc = "Thick resin solidified into a wall." icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi' - icon_state = "smooth" //same as resin, but consistency ho! + icon_state = "resin_wall-0" + base_icon_state = "resin_wall" resintype = "wall" - canSmoothWith = list(/obj/structure/alien/resin/wall, /obj/structure/alien/resin/membrane) + smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_ALIEN_RESIN + canSmoothWith = SMOOTH_GROUP_ALIEN_WALLS /obj/structure/alien/resin/wall/BlockThermalConductivity() return 1 @@ -88,11 +91,13 @@ name = "resin membrane" desc = "Resin just thin enough to let light pass through." icon = 'icons/obj/smooth_structures/alien/resin_membrane.dmi' - icon_state = "smooth" + icon_state = "resin_membrane-0" + base_icon_state = "resin_membrane" opacity = FALSE max_integrity = 160 resintype = "membrane" - canSmoothWith = list(/obj/structure/alien/resin/wall, /obj/structure/alien/resin/membrane) + smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_ALIEN_RESIN + canSmoothWith = SMOOTH_GROUP_ALIEN_WALLS /obj/structure/alien/resin/attack_paw(mob/user) return attack_hand(user) @@ -113,8 +118,10 @@ plane = FLOOR_PLANE icon_state = "weeds" max_integrity = 15 - canSmoothWith = list(/obj/structure/alien/weeds, /turf/closed/wall) - smooth = SMOOTH_MORE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_ALIEN_WEEDS + SMOOTH_GROUP_ALIEN_RESIN + canSmoothWith = SMOOTH_GROUP_ALIEN_WEEDS + SMOOTH_GROUP_WALLS + var/last_expand = 0 //last world.time this weed expanded var/growth_cooldown_low = 150 var/growth_cooldown_high = 200 diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm index 0b6c2335a185..53970143aebe 100644 --- a/code/game/objects/structures/beds_chairs/alien_nest.dm +++ b/code/game/objects/structures/beds_chairs/alien_nest.dm @@ -6,7 +6,9 @@ icon = 'icons/obj/smooth_structures/alien/nest.dmi' icon_state = "nest" max_integrity = 120 - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_ALIEN_NEST + canSmoothWith = SMOOTH_GROUP_ALIEN_NEST can_be_unanchored = FALSE canSmoothWith = null buildstacktype = null diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 0cf459dc0c7f..f38b820b249b 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -4,8 +4,9 @@ icon = 'icons/obj/smooth_structures/catwalk.dmi' icon_state = "catwalk" number_of_rods = 2 - smooth = SMOOTH_TRUE - canSmoothWith = null + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CATWALK + SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_OPEN_FLOOR + canSmoothWith = SMOOTH_GROUP_CATWALK obj_flags = CAN_BE_HIT /obj/structure/lattice/catwalk/Initialize(mapload) @@ -49,12 +50,9 @@ /obj/structure/lattice/catwalk/clockwork name = "clockwork catwalk" icon = 'icons/obj/smooth_structures/catwalk_clockwork.dmi' - canSmoothWith = list(/obj/structure/lattice, - /turf/open/floor, - /turf/open/indestructible/clock_spawn_room, - /turf/closed/wall, - /obj/structure/falsewall) - smooth = SMOOTH_MORE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CATWALK + SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_OPEN_FLOOR + canSmoothWith = SMOOTH_GROUP_CATWALK /obj/structure/lattice/catwalk/clockwork/Initialize(mapload) . = ..() diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index 3ca29103d44a..5ae78e1c9da2 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -11,21 +11,13 @@ density = TRUE opacity = TRUE max_integrity = 100 - - canSmoothWith = list( - /turf/closed/wall, - /turf/closed/wall/r_wall, - /obj/structure/falsewall, - /obj/structure/falsewall/brass, - /obj/structure/falsewall/reinforced, - /turf/closed/wall/rust, - /turf/closed/wall/r_wall/rust, - /turf/closed/wall/clockwork) - smooth = SMOOTH_TRUE - can_be_unanchored = FALSE - CanAtmosPass = ATMOS_PASS_DENSITY flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1 + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_WALLS + CanAtmosPass = ATMOS_PASS_DENSITY rad_insulation = RAD_MEDIUM_INSULATION + var/mineral = /obj/item/stack/sheet/metal var/mineral_amount = 2 var/walltype = /turf/closed/wall @@ -70,22 +62,24 @@ update_appearance(UPDATE_ICON) air_update_turf() -/obj/structure/falsewall/update_icon_state() +/obj/structure/falsewall/update_icon(updates=ALL)//Calling icon_update will refresh the smoothwalls if it's closed, otherwise it will make sure the icon is correct if it's open . = ..() + if(!density || !(updates & UPDATE_SMOOTHING)) + return + if(opening) - if(density) - icon_state = "fwall_opening" - smooth = SMOOTH_FALSE - clear_smooth_overlays() - else - icon_state = "fwall_closing" + smoothing_flags = NONE + clear_smooth_overlays() else - if(density) - icon_state = initial(icon_state) - smooth = SMOOTH_TRUE - queue_smooth(src) - else - icon_state = "fwall_open" + smoothing_flags = SMOOTH_BITMASK + QUEUE_SMOOTH(src) + +/obj/structure/falsewall/update_icon_state() + if(opening) + icon_state = "fwall_[density ? "opening" : "closing"]" + return ..() + icon_state = density ? "[base_icon_state]-[smoothing_junction]" : "fwall_open" + return ..() /obj/structure/falsewall/proc/ChangeToWall(delete = 1) var/turf/T = get_turf(src) @@ -183,12 +177,15 @@ name = "uranium wall" desc = "A wall with uranium plating. This is probably a bad idea." icon = 'icons/turf/walls/uranium_wall.dmi' - icon_state = "uranium" + icon_state = "uranium_wall-0" + base_icon_state = "uranium_wall" mineral = /obj/item/stack/sheet/mineral/uranium walltype = /turf/closed/wall/mineral/uranium + smoothing_groups = SMOOTH_GROUP_URANIUM_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_URANIUM_WALLS + var/active = null var/last_event = 0 - canSmoothWith = list(/obj/structure/falsewall/uranium, /turf/closed/wall/mineral/uranium) /obj/structure/falsewall/uranium/attackby(obj/item/W, mob/user, params) radiate() @@ -217,38 +214,50 @@ name = "gold wall" desc = "A wall with gold plating. Swag!" icon = 'icons/turf/walls/gold_wall.dmi' - icon_state = "gold" + icon_state = "gold_wall-0" + base_icon_state = "gold_wall" mineral = /obj/item/stack/sheet/mineral/gold walltype = /turf/closed/wall/mineral/gold - canSmoothWith = list(/obj/structure/falsewall/gold, /turf/closed/wall/mineral/gold) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_GOLD_WALLS /obj/structure/falsewall/silver name = "silver wall" desc = "A wall with silver plating. Shiny." icon = 'icons/turf/walls/silver_wall.dmi' - icon_state = "silver" + icon_state = "silver_wall-0" + base_icon_state = "silver_wall" mineral = /obj/item/stack/sheet/mineral/silver walltype = /turf/closed/wall/mineral/silver - canSmoothWith = list(/obj/structure/falsewall/silver, /turf/closed/wall/mineral/silver) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SILVER_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_SILVER_WALLS /obj/structure/falsewall/diamond name = "diamond wall" desc = "A wall with diamond plating. You monster." icon = 'icons/turf/walls/diamond_wall.dmi' - icon_state = "diamond" + icon_state = "diamond_wall-0" + base_icon_state = "diamond_wall" mineral = /obj/item/stack/sheet/mineral/diamond walltype = /turf/closed/wall/mineral/diamond - canSmoothWith = list(/obj/structure/falsewall/diamond, /turf/closed/wall/mineral/diamond) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_DIAMOND_WALLS max_integrity = 800 /obj/structure/falsewall/plasma name = "plasma wall" desc = "A wall with plasma plating. This is definitely a bad idea." icon = 'icons/turf/walls/plasma_wall.dmi' - icon_state = "plasma" + icon_state = "plasma_wall-0" + base_icon_state = "plasma_wall" mineral = /obj/item/stack/sheet/mineral/plasma walltype = /turf/closed/wall/mineral/plasma - canSmoothWith = list(/obj/structure/falsewall/plasma, /turf/closed/wall/mineral/plasma) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_PLASMA_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_PLASMA_WALLS /obj/structure/falsewall/plasma/attackby(obj/item/W, mob/user, params) if(W.is_hot() > 300) @@ -273,10 +282,13 @@ name = "bananium wall" desc = "A wall with bananium plating. Honk!" icon = 'icons/turf/walls/bananium_wall.dmi' - icon_state = "bananium" + icon_state = "bananium_wall-0" + base_icon_state = "bananium_wall" mineral = /obj/item/stack/sheet/mineral/bananium walltype = /turf/closed/wall/mineral/bananium - canSmoothWith = list(/obj/structure/falsewall/bananium, /turf/closed/wall/mineral/bananium) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_BANANIUM_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_BANANIUM_WALLS /obj/structure/falsewall/bananium/honk_act() return FALSE @@ -288,44 +300,56 @@ icon_state = "sandstone" mineral = /obj/item/stack/sheet/mineral/sandstone walltype = /turf/closed/wall/mineral/sandstone - canSmoothWith = list(/obj/structure/falsewall/sandstone, /turf/closed/wall/mineral/sandstone) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_SANDSTONE_WALLS /obj/structure/falsewall/wood name = "wooden wall" desc = "A wall with wooden plating. Stiff." icon = 'icons/turf/walls/wood_wall.dmi' - icon_state = "wood" + icon_state = "wood_wall-0" + base_icon_state = "wood_wall" mineral = /obj/item/stack/sheet/mineral/wood walltype = /turf/closed/wall/mineral/wood - canSmoothWith = list(/obj/structure/falsewall/wood, /turf/closed/wall/mineral/wood) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_WOOD_WALLS /obj/structure/falsewall/bamboo name = "bamboo wall" desc = "A wall with bamboo finish. Zen." icon = 'icons/turf/walls/bamboo_wall.dmi' - icon_state = "bamboo" mineral = /obj/item/stack/sheet/mineral/bamboo walltype = /turf/closed/wall/mineral/bamboo - canSmoothWith = list(/obj/structure/falsewall/bamboo, /turf/closed/wall/mineral/bamboo) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_BAMBOO_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_BAMBOO_WALLS /obj/structure/falsewall/iron name = "rough metal wall" desc = "A wall with rough metal plating." icon = 'icons/turf/walls/iron_wall.dmi' - icon_state = "iron" + icon_state = "iron_wall-0" + base_icon_state = "iron_wall" mineral = /obj/item/stack/rods mineral_amount = 2 walltype = /turf/closed/wall/mineral/iron - canSmoothWith = list(/obj/structure/falsewall/iron, /turf/closed/wall/mineral/iron) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_IRON_WALLS /obj/structure/falsewall/abductor name = "alien wall" desc = "A wall with alien alloy plating." icon = 'icons/turf/walls/abductor_wall.dmi' - icon_state = "abductor" + icon_state = "abductor_wall-0" + base_icon_state = "abductor_wall" mineral = /obj/item/stack/sheet/mineral/abductor walltype = /turf/closed/wall/mineral/abductor - canSmoothWith = list(/obj/structure/falsewall/abductor, /turf/closed/wall/mineral/abductor) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS /obj/structure/falsewall/titanium name = "wall" @@ -334,8 +358,9 @@ icon_state = "shuttle" mineral = /obj/item/stack/sheet/mineral/titanium walltype = /turf/closed/wall/mineral/titanium - smooth = SMOOTH_MORE - canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_TITANIUM_WALLS /obj/structure/falsewall/plastitanium name = "wall" @@ -344,20 +369,24 @@ icon_state = "shuttle" mineral = /obj/item/stack/sheet/mineral/plastitanium walltype = /turf/closed/wall/mineral/plastitanium - smooth = SMOOTH_MORE - canSmoothWith = list(/turf/closed/wall/mineral/plastitanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS /obj/structure/falsewall/brass name = "clockwork wall" desc = "A huge chunk of warm metal. The clanging of machinery emanates from within." icon = 'icons/turf/walls/clockwork_wall.dmi' - icon_state = "clockwork_wall" + icon_state = "clockwork_wall-0" + base_icon_state = "clockwork_wall" resistance_flags = FIRE_PROOF | ACID_PROOF mineral_amount = 1 - canSmoothWith = list(/obj/effect/clockwork/overlay/wall, /obj/structure/falsewall/brass) girder_type = /obj/structure/destructible/clockwork/wall_gear/displaced walltype = /turf/closed/wall/clockwork mineral = /obj/item/stack/tile/brass + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CLOCKWORK_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_CLOCKWORK_WALLS /obj/structure/falsewall/brass/New(loc) ..() diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 57547fbedd0a..494ece9155b0 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -11,9 +11,7 @@ max_integrity = 50 integrity_failure = 20 appearance_flags = KEEP_TOGETHER - smooth = SMOOTH_TRUE can_be_unanchored = TRUE - canSmoothWith = list(/obj/structure/grille, /obj/structure/grille/broken) var/holes = 0 //bitflag var/rods_type = /obj/item/stack/rods var/rods_amount = 2 @@ -41,6 +39,14 @@ update_appearance(UPDATE_ICON) +/obj/structure/grille/update_appearance(updates) + if(QDELETED(src) || broken) + return + + . = ..() + if((updates & UPDATE_SMOOTHING) && (smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK))) + QUEUE_SMOOTH(src) + /obj/structure/grille/update_icon(updates=ALL) . = ..() if(QDELETED(src)) @@ -160,8 +166,6 @@ setAnchored(!anchored) user.visible_message(span_notice("[user] [anchored ? "fastens" : "unfastens"] [src]."), \ span_notice("You [anchored ? "fasten [src] to" : "unfasten [src] from"] the floor.")) - queue_smooth(src) - queue_smooth_neighbors(src) return else if(istype(W, /obj/item/stack/rods) && broken) var/obj/item/stack/rods/R = W @@ -258,6 +262,8 @@ if(!in_range(src, user))//To prevent TK and mech users from getting shocked return FALSE var/turf/T = get_turf(src) + if(T.overfloor_placed)//cant be a floor in the way! + return FALSE var/obj/structure/cable/C = T.get_cable_node() if(C) if(electrocute_mob(user, C, src, 1, TRUE)) @@ -281,6 +287,8 @@ var/obj/O = AM if(O.throwforce != 0)//don't want to let people spam tesla bolts, this way it will break after time var/turf/T = get_turf(src) + if(T.overfloor_placed) + return FALSE var/obj/structure/cable/C = T.get_cable_node() if(C) playsound(src, 'sound/magic/lightningshock.ogg', 100, 1, extrarange = 5) @@ -312,7 +320,6 @@ name = "cog grille" desc = "A strangely-shaped grille." broken_type = /obj/structure/grille/ratvar/broken - smooth = SMOOTH_FALSE /obj/structure/grille/ratvar/Initialize(mapload) . = ..() diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index 12e7af0c4aa6..43ddfe40434c 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -9,12 +9,10 @@ max_integrity = 50 layer = LATTICE_LAYER //under pipes plane = FLOOR_PLANE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_LATTICE + canSmoothWith = SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_OPEN_FLOOR var/number_of_rods = 1 - canSmoothWith = list(/obj/structure/lattice, - /turf/open/floor, - /turf/closed/wall, - /obj/structure/falsewall) - smooth = SMOOTH_MORE // flags = CONDUCT_1 /obj/structure/lattice/examine(mob/user) diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index c05798db10ab..027bf49a36df 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -304,7 +304,13 @@ /obj/structure/mineral_door/paperframe/Initialize(mapload) . = ..() - queue_smooth_neighbors(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) + +/obj/structure/mineral_door/paperframe/Destroy() + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) + return ..() /obj/structure/mineral_door/paperframe/examine(mob/user) . = ..() @@ -334,7 +340,3 @@ return TRUE return ..() - -/obj/structure/mineral_door/paperframe/Destroy() - queue_smooth_neighbors(src) - return ..() diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index 132df55d8472..0c2b3f61e0d1 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -242,5 +242,9 @@ FLOOR SAFES density = FALSE layer = LOW_OBJ_LAYER +/obj/structure/safe/floor/Initialize(mapload) + . = ..() + AddElement(/datum/element/undertile) + #undef SOUND_CHANCE #undef BROKEN_THRESHOLD diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index eba7e22e45ea..e7dc2d5b409c 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -21,6 +21,11 @@ anchored = TRUE layer = TABLE_LAYER pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.") + max_integrity = 100 + integrity_failure = 30 + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TABLES + canSmoothWith = SMOOTH_GROUP_TABLES var/frame = /obj/structure/table_frame var/framestack = /obj/item/stack/rods var/buildstack = /obj/item/stack/sheet/metal @@ -28,10 +33,6 @@ var/buildstackamount = 1 var/framestackamount = 2 var/deconstruction_ready = 1 - max_integrity = 100 - integrity_failure = 30 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/table, /obj/structure/table/reinforced) /obj/structure/table/Initialize(mapload) . = ..() @@ -79,9 +80,9 @@ /obj/structure/table/update_icon(updates=ALL) . = ..() - if(smooth) - queue_smooth(src) - queue_smooth_neighbors(src) + if((updates & UPDATE_SMOOTHING) && (smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK))) + QUEUE_SMOOTH(src) + QUEUE_SMOOTH_NEIGHBORS(src) /obj/structure/table/narsie_act() var/atom/A = loc @@ -331,15 +332,15 @@ name = "wooden table" desc = "Do not apply fire to this. Rumour says it burns easily." icon = 'icons/obj/smooth_structures/wood_table.dmi' - icon_state = "wood_table" + icon_state = "wood_table-0" + base_icon_state = "wood_table" frame = /obj/structure/table_frame/wood framestack = /obj/item/stack/sheet/mineral/wood buildstack = /obj/item/stack/sheet/mineral/wood resistance_flags = FLAMMABLE max_integrity = 70 - canSmoothWith = list(/obj/structure/table/wood, - /obj/structure/table/wood/poker, - /obj/structure/table/wood/bar) + smoothing_groups = SMOOTH_GROUP_WOOD_TABLES //Don't smooth with SMOOTH_GROUP_TABLES + canSmoothWith = SMOOTH_GROUP_WOOD_TABLES /obj/structure/table/wood/narsie_act(total_override = TRUE) if(!total_override) @@ -360,19 +361,12 @@ desc = "A standard metal table frame covered with an amazingly fancy, patterned cloth." icon = 'icons/obj/structures.dmi' icon_state = "fancy_table" + base_icon_state = "fancy_table" frame = /obj/structure/table_frame framestack = /obj/item/stack/rods buildstack = /obj/item/stack/tile/carpet - canSmoothWith = list(/obj/structure/table/wood/fancy, - /obj/structure/table/wood/fancy/black, - /obj/structure/table/wood/fancy/exoticblue, - /obj/structure/table/wood/fancy/cyan, - /obj/structure/table/wood/fancy/exoticgreen, - /obj/structure/table/wood/fancy/orange, - /obj/structure/table/wood/fancy/exoticpurple, - /obj/structure/table/wood/fancy/red, - /obj/structure/table/wood/fancy/royalblack, - /obj/structure/table/wood/fancy/royalblue) + smoothing_groups = SMOOTH_GROUP_FANCY_WOOD_TABLES //Don't smooth with SMOOTH_GROUP_TABLES or SMOOTH_GROUP_WOOD_TABLES + canSmoothWith = SMOOTH_GROUP_FANCY_WOOD_TABLES var/smooth_icon = 'icons/obj/smooth_structures/fancy_table.dmi' // see Initialize(mapload) /obj/structure/table/wood/fancy/Initialize(mapload) @@ -434,10 +428,10 @@ name = "reinforced table" desc = "A reinforced version of the four legged table." icon = 'icons/obj/smooth_structures/reinforced_table.dmi' - icon_state = "r_table" + icon_state = "reinforced_table-0" + base_icon_state = "reinforced_table" deconstruction_ready = 0 buildstack = /obj/item/stack/sheet/plasteel - canSmoothWith = list(/obj/structure/table/reinforced, /obj/structure/table) max_integrity = 200 integrity_failure = 50 armor = list(MELEE = 10, BULLET = 30, LASER = 30, ENERGY = 100, BOMB = 20, BIO = 0, RAD = 0, FIRE = 80, ACID = 70) @@ -470,14 +464,16 @@ name = "brass table" desc = "A solid, slightly beveled brass table." icon = 'icons/obj/smooth_structures/brass_table.dmi' - icon_state = "brass_table" + icon_state = "brass_table-0" + base_icon_state = "brass_table" resistance_flags = FIRE_PROOF | ACID_PROOF frame = /obj/structure/table_frame/brass framestack = /obj/item/stack/tile/brass buildstack = /obj/item/stack/tile/brass framestackamount = 1 buildstackamount = 1 - canSmoothWith = list(/obj/structure/table/reinforced/brass, /obj/structure/table/bronze) + smoothing_groups = SMOOTH_GROUP_BRONZE_TABLES //Don't smooth with SMOOTH_GROUP_TABLES + canSmoothWith = SMOOTH_GROUP_BRONZE_TABLES /obj/structure/table/reinforced/brass/Initialize(mapload) . = ..() @@ -506,10 +502,12 @@ name = "bronze table" desc = "A solid table made out of bronze." icon = 'icons/obj/smooth_structures/brass_table.dmi' - icon_state = "brass_table" + icon_state = "brass_table-0" + base_icon_state = "brass_table" resistance_flags = FIRE_PROOF | ACID_PROOF buildstack = /obj/item/stack/tile/bronze - canSmoothWith = list(/obj/structure/table/reinforced/brass, /obj/structure/table/bronze) + smoothing_groups = SMOOTH_GROUP_BRONZE_TABLES //Don't smooth with SMOOTH_GROUP_TABLES + canSmoothWith = SMOOTH_GROUP_BRONZE_TABLES /obj/structure/table/bronze/tablepush(mob/living/user, mob/living/pushed_mob) ..() @@ -525,7 +523,9 @@ icon = 'icons/obj/surgery.dmi' icon_state = "optable" buildstack = /obj/item/stack/sheet/mineral/silver - smooth = SMOOTH_FALSE + smoothing_flags = NONE + smoothing_groups = null + canSmoothWith = null can_buckle = TRUE buckle_requires_restraints = TRUE diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index d4a01fd2a175..8981b4ce4e45 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -22,7 +22,6 @@ var/fulltile = FALSE var/glass_type = /obj/item/stack/sheet/glass var/glass_amount = 1 - var/mutable_appearance/crack_overlay var/real_explosion_block //ignore this, just use explosion_block var/breaksound = "shatter" var/hitsound = 'sound/effects/Glasshit.ogg' @@ -325,28 +324,24 @@ //This proc is used to update the icons of nearby windows. /obj/structure/window/proc/update_nearby_icons() - update_appearance(UPDATE_ICON) - if(smooth) - queue_smooth_neighbors(src) + update_appearance() + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) //merges adjacent full-tile windows into one /obj/structure/window/update_overlays() . = ..() - if(!QDELETED(src)) - if(!fulltile) - return - - var/ratio = obj_integrity / max_integrity - ratio = CEILING(ratio*4, 1) * 25 + if(QDELETED(src) || !fulltile) + return - if(smooth) - queue_smooth(src) + if((updates & UPDATE_SMOOTHING) && (smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK))) + QUEUE_SMOOTH(src) - cut_overlay(crack_overlay) - if(ratio > 75) - return - crack_overlay = mutable_appearance('icons/obj/structures.dmi', "damage[ratio]", -(layer+0.1)) - . += crack_overlay + var/ratio = obj_integrity / max_integrity + ratio = CEILING(ratio*4, 1) * 25 + if(ratio > 75) + return + . += mutable_appearance('icons/obj/structures.dmi', "damage[ratio]", -(layer+0.1)) /obj/structure/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) @@ -631,8 +626,9 @@ max_integrity = 50 fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/plasma/fulltile, /obj/structure/window/plasma/reinforced/fulltile) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE glass_amount = 2 /obj/structure/window/fulltile/unanchored @@ -646,8 +642,9 @@ max_integrity = 300 fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/plasma/fulltile, /obj/structure/window/plasma/reinforced/fulltile) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE glass_amount = 2 /obj/structure/window/plasma/fulltile/unanchored @@ -662,7 +659,9 @@ max_integrity = 1000 fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE glass_amount = 2 /obj/structure/window/plasma/reinforced/fulltile/unanchored @@ -671,14 +670,16 @@ /obj/structure/window/reinforced/fulltile icon = 'icons/obj/smooth_structures/reinforced_window.dmi' - icon_state = "r_window" + icon_state = "reinforced_window-0" + base_icon_state = "reinforced_window" dir = FULLTILE_WINDOW_DIR max_integrity = 150 fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE state = RWINDOW_SECURE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/plasma/fulltile, /obj/structure/window/plasma/reinforced/fulltile) level = 3 glass_amount = 2 @@ -692,16 +693,17 @@ dir = FULLTILE_WINDOW_DIR fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/plasma/fulltile, /obj/structure/window/plasma/reinforced/fulltile) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE level = 3 glass_amount = 2 /obj/structure/window/reinforced/fulltile/ice icon = 'icons/obj/smooth_structures/rice_window.dmi' - icon_state = "ice_window" + icon_state = "rice_window-0" + base_icon_state = "rice_window" max_integrity = 150 - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/plasma/fulltile, /obj/structure/window/plasma/reinforced/fulltile) level = 3 glass_amount = 2 @@ -725,8 +727,9 @@ reinf = TRUE heat_resistance = 1600 armor = list(MELEE = 50, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 50, BIO = 100, RAD = 100, FIRE = 80, ACID = 100) - smooth = SMOOTH_TRUE - canSmoothWith = null + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE explosion_block = 3 level = 3 glass_type = /obj/item/stack/sheet/titaniumglass @@ -755,8 +758,9 @@ reinf = TRUE heat_resistance = 1600 armor = list(MELEE = 40, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100) - smooth = SMOOTH_TRUE - canSmoothWith = null + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM explosion_block = 3 damage_deflection = 21 //The same as reinforced plasma windows. level = 3 @@ -828,8 +832,9 @@ /obj/structure/window/reinforced/clockwork/fulltile icon_state = "clockwork_window" - smooth = SMOOTH_TRUE - canSmoothWith = null + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE + SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 dir = FULLTILE_WINDOW_DIR @@ -862,8 +867,9 @@ max_integrity = 15 fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/paperframe, /obj/structure/mineral_door/paperframe) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_PAPERFRAME + canSmoothWith = SMOOTH_GROUP_PAPERFRAME glass_amount = 2 glass_type = /obj/item/stack/sheet/paperframes heat_resistance = 233 @@ -906,26 +912,14 @@ user.visible_message(span_danger("[user] tears a hole in [src].")) update_appearance(UPDATE_ICON) -/obj/structure/window/paperframe/update_overlays() - . = ..() - if(obj_integrity < max_integrity) - cut_overlay(paper) - . += torn - set_opacity(FALSE) - else - cut_overlay(torn) - . += paper - set_opacity(TRUE) - queue_smooth(src) - /obj/structure/window/paperframe/update_appearance(updates) . = ..() set_opacity(obj_integrity >= max_integrity) /obj/structure/window/paperframe/update_icon(updates=ALL) . = ..() - if((updates & UPDATE_SMOOTHING) && (smooth & (SMOOTH_TRUE))) - queue_smooth(src) + if((updates & UPDATE_SMOOTHING) && (smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK))) + QUEUE_SMOOTH(src) /obj/structure/window/paperframe/update_overlays() . = ..() diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 9c110801ed2a..42e0702d2148 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -137,6 +137,12 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( for(var/turf/open/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm S.update_starlight() + + // only queue for smoothing if SSatom initialized us, and we'd be changing smoothing state + if(flags_1 & INITIALIZED_1) + QUEUE_SMOOTH_NEIGHBORS(src) + QUEUE_SMOOTH(src) + SSdemo.mark_turf(W) return W @@ -304,8 +310,6 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( for(var/obj/machinery/door/firedoor/FD in T) FD.CalculateAffectingAreas() - queue_smooth_neighbors(src) - HandleTurfChange(src) /turf/open/AfterChange(flags) diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index 114bcd368b51..8ecdb50ce129 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -54,7 +54,7 @@ icon = 'icons/turf/walls/sandstone_wall.dmi' icon_state = "sandstone" baseturfs = /turf/closed/indestructible/sandstone - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_BITMASK /turf/closed/indestructible/oldshuttle/corner icon_state = "corner" @@ -82,16 +82,21 @@ /turf/closed/indestructible/riveted icon = 'icons/turf/walls/riveted.dmi' icon_state = "riveted" - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS /turf/closed/indestructible/syndicate icon = 'icons/turf/walls/plastitanium_wall.dmi' icon_state = "map-shuttle" - smooth = SMOOTH_MORE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_SYNDICATE_WALLS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS /turf/closed/indestructible/riveted/uranium icon = 'icons/turf/walls/uranium_wall.dmi' icon_state = "uranium" + smoothing_flags = SMOOTH_BITMASK /turf/closed/indestructible/abductor icon_state = "alien1" @@ -101,10 +106,12 @@ /turf/closed/indestructible/fakeglass name = "window" + icon = 'icons/obj/smooth_structures/reinforced_window.dmi' icon_state = "fake_window" opacity = FALSE - smooth = SMOOTH_TRUE - icon = 'icons/obj/smooth_structures/reinforced_window.dmi' + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE /turf/closed/indestructible/fakeglass/Initialize(mapload) . = ..() @@ -114,10 +121,12 @@ /turf/closed/indestructible/opsglass name = "window" + icon = 'icons/obj/smooth_structures/plastitanium_window.dmi' icon_state = "plastitanium_window" opacity = FALSE - smooth = SMOOTH_TRUE - icon = 'icons/obj/smooth_structures/plastitanium_window.dmi' + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM /turf/closed/indestructible/opsglass/Initialize(mapload) . = ..() @@ -190,8 +199,11 @@ name = "necropolis wall" desc = "A thick, seemingly indestructible stone wall." icon = 'icons/turf/walls/boss_wall.dmi' - icon_state = "wall" - canSmoothWith = list(/turf/closed/indestructible/riveted/boss, /turf/closed/indestructible/riveted/boss/see_through) + icon_state = "boss_wall-0" + base_icon_state = "boss_wall" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_BOSS_WALLS + canSmoothWith = SMOOTH_GROUP_BOSS_WALLS explosion_block = 50 baseturfs = /turf/closed/indestructible/riveted/boss diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index 82203455e9de..08be586b0c78 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -59,6 +59,8 @@ /turf/open/indestructible/plating name = "plating" icon_state = "plating" + overfloor_placed = FALSE + underfloor_accessibility = UNDERFLOOR_INTERACTABLE footstep = FOOTSTEP_PLATING barefootstep = FOOTSTEP_HARD_BAREFOOT clawfootstep = FOOTSTEP_HARD_CLAW @@ -103,8 +105,6 @@ desc = "Soft velvet carpeting. Feels good between your toes." icon = 'icons/turf/floors/carpet.dmi' icon_state = "carpet" - smooth = SMOOTH_TRUE - canSmoothWith = list(/turf/open/indestructible/carpet) flags_1 = NONE bullet_bounce_sound = null footstep = FOOTSTEP_CARPET @@ -112,6 +112,9 @@ clawfootstep = FOOTSTEP_CARPET_BAREFOOT heavyfootstep = FOOTSTEP_GENERIC_HEAVY tiled_dirt = FALSE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET + canSmoothWith = SMOOTH_GROUP_CARPET /turf/open/indestructible/carpet/black icon = 'icons/turf/floors/carpet_black.dmi' @@ -145,9 +148,11 @@ name = "carpet" desc = "Soft velvet carpeting. Feels good between your toes." icon = 'icons/turf/floors/carpet_royalblue.dmi' - icon_state = "carpet" - smooth = SMOOTH_TRUE - canSmoothWith = list(/turf/open/indestructible/carpet/royal) + icon_state = "carpet-255" + base_icon_state = "carpet" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET + canSmoothWith = SMOOTH_GROUP_CARPET flags_1 = NONE bullet_bounce_sound = null footstep = FOOTSTEP_CARPET @@ -159,14 +164,20 @@ /turf/open/indestructible/carpet/royal/black icon = 'icons/turf/floors/carpet_royalblack.dmi' icon_state = "carpet" + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_ROYAL_BLACK + canSmoothWith = SMOOTH_GROUP_CARPET_ROYAL_BLACK /turf/open/indestructible/carpet/royal/green icon = 'icons/turf/floors/carpet_exoticgreen.dmi' icon_state = "carpet" + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_ROYAL_GREEN + canSmoothWith = SMOOTH_GROUP_CARPET_ROYAL_GREEN /turf/open/indestructible/carpet/royal/purple icon = 'icons/turf/floors/carpet_exoticpurple.dmi' icon_state = "carpet" + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_ROYAL_PURPLE + canSmoothWith = SMOOTH_GROUP_CARPET_ROYAL_PURPLE /turf/open/indestructible/grass name = "grass patch" @@ -337,7 +348,7 @@ icon = 'icons/turf/floors/hierophant_floor.dmi' initial_gas_mix = LAVALAND_DEFAULT_ATMOS baseturfs = /turf/open/indestructible/hierophant - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_CORNERS tiled_dirt = FALSE /turf/open/indestructible/hierophant/two @@ -453,10 +464,11 @@ icon_state = "necro[rand(2,3)]" /turf/open/indestructible/brazil/lostit - smooth = SMOOTH_TRUE | SMOOTH_BORDER | SMOOTH_MORE - canSmoothWith = list(/turf/open/indestructible/brazil/lostit) icon = 'yogstation/icons/turf/floors/ballpit_smooth.dmi' icon_state = "smooth" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_BRAZIL + canSmoothWith = SMOOTH_GROUP_BRAZIL /turf/open/indestructible/wiki light_range = 2 diff --git a/code/game/turfs/openspace/openspace.dm b/code/game/turfs/openspace/openspace.dm index 5a06c4bc2234..e6f1914a01c8 100644 --- a/code/game/turfs/openspace/openspace.dm +++ b/code/game/turfs/openspace/openspace.dm @@ -3,6 +3,8 @@ desc = "Watch your step!" icon_state = "grey" baseturfs = /turf/open/openspace + overfloor_placed = FALSE + underfloor_accessibility = UNDERFLOOR_INTERACTABLE CanAtmosPassVertical = ATMOS_PASS_YES flags_1 = NO_RUST //mouse_opacity = MOUSE_OPACITY_TRANSPARENT diff --git a/code/game/turfs/simulated/chasm.dm b/code/game/turfs/simulated/chasm.dm index 482a7a9a7a7e..391a751f4d67 100644 --- a/code/game/turfs/simulated/chasm.dm +++ b/code/game/turfs/simulated/chasm.dm @@ -2,11 +2,13 @@ /turf/open/chasm name = "chasm" desc = "Watch your step." - baseturfs = /turf/open/chasm - smooth = SMOOTH_TRUE | SMOOTH_BORDER | SMOOTH_MORE icon = 'icons/turf/floors/chasms.dmi' - icon_state = "smooth" - canSmoothWith = list(/turf/open/floor/fakepit, /turf/open/chasm) + icon_state = "chasms-255" + base_icon_state = "chasms" + baseturfs = /turf/open/chasm + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_TURF_CHASM + canSmoothWith = SMOOTH_GROUP_TURF_CHASM density = TRUE //This will prevent hostile mobs from pathing into chasms, while the canpass override will still let it function like an open turf bullet_bounce_sound = null //abandon all hope ye who enter diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index 676b6d1bc154..78e1f3d209ac 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -11,20 +11,23 @@ clawfootstep = FOOTSTEP_HARD_CLAW heavyfootstep = FOOTSTEP_GENERIC_HEAVY + overfloor_placed = TRUE + + thermal_conductivity = 0.040 + heat_capacity = 10000 + + tiled_dirt = TRUE + var/icon_state_regular_floor = "floor" //used to remember what icon state the tile should have by default var/icon_regular_floor = 'icons/turf/floors.dmi' //used to remember what icon the tile should have by default var/icon_plating = "plating" - thermal_conductivity = 0.040 - heat_capacity = 10000 - intact = 1 + var/broken = 0 var/burnt = 0 var/floor_tile = null //tile that this floor drops var/list/broken_states var/list/burnt_states - tiled_dirt = TRUE - /turf/open/floor/Initialize(mapload) if (!broken_states) @@ -110,8 +113,7 @@ /turf/open/floor/is_shielded() for(var/obj/structure/A in contents) - if(A.level == 3) - return 1 + return 1 /turf/open/floor/blob_act(obj/structure/blob/B) return @@ -168,20 +170,25 @@ W.update_appearance(UPDATE_ICON) return W -/turf/open/floor/attackby(obj/item/C, mob/user, params) - if(!C || !user) - return 1 - if(..()) - return 1 - if(intact && istype(C, /obj/item/stack/tile)) - try_replace_tile(C, user, params) - return 0 +/turf/open/floor/attackby(obj/item/object, mob/living/user, params) + if(!object || !user) + return TRUE + . = ..() + if(.) + return . + if(overfloor_placed && istype(object, /obj/item/stack/tile)) + try_replace_tile(object, user, params) + return TRUE + if(underfloor_accessibility >= UNDERFLOOR_INTERACTABLE && istype(object, /obj/item/stack/tile)) + try_replace_tile(object, user, params) + return FALSE /turf/open/floor/crowbar_act(mob/living/user, obj/item/I) if(istype(I,/obj/item/jawsoflife/jimmy)) to_chat(user,"The [I] cannot pry tiles.") return - return intact ? pry_tile(I, user) : FALSE + if(overfloor_placed && pry_tile(I, user)) + return TRUE /turf/open/floor/proc/try_replace_tile(obj/item/stack/tile/T, mob/user, params) if(T.turf_type == type) diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index c6ec1637b061..626efd2584bd 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -223,8 +223,9 @@ icon_state = "bamboo" floor_tile = /obj/item/stack/tile/bamboo broken_states = list("damaged") - smooth = SMOOTH_TRUE - canSmoothWith = list(/turf/open/floor/bamboo) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_BAMBOO_FLOOR + canSmoothWith = SMOOTH_GROUP_BAMBOO_FLOOR flags_1 = NONE footstep = FOOTSTEP_WOOD barefootstep = FOOTSTEP_WOOD_BAREFOOT @@ -338,8 +339,9 @@ icon_state = "carpet" floor_tile = /obj/item/stack/tile/carpet broken_states = list("damaged") - smooth = SMOOTH_TRUE - canSmoothWith = list(/turf/open/floor/carpet) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET + canSmoothWith = SMOOTH_GROUP_CARPET flags_1 = NONE bullet_bounce_sound = null footstep = FOOTSTEP_CARPET @@ -359,15 +361,15 @@ /turf/open/floor/carpet/update_icon(updates=ALL) . = ..() - if(!.) - return 0 + if(!. || !(updates & UPDATE_SMOOTHING)) + return if(!broken && !burnt) - if(smooth) - queue_smooth(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH(src) else make_plating() - if(smooth) - queue_smooth_neighbors(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) /turf/open/floor/carpet/broken icon_state = "damaged" @@ -375,8 +377,11 @@ /turf/open/floor/carpet/black icon = 'icons/turf/floors/carpet_black.dmi' + icon_state = "carpet_black-255" + base_icon_state = "carpet_black" floor_tile = /obj/item/stack/tile/carpet/black - canSmoothWith = list(/turf/open/floor/carpet/black) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_BLACK + canSmoothWith = SMOOTH_GROUP_CARPET_BLACK /turf/open/floor/carpet/black/broken icon_state = "damaged" @@ -384,8 +389,11 @@ /turf/open/floor/carpet/exoticblue icon = 'icons/turf/floors/carpet_exoticblue.dmi' + icon_state = "carpet_blue-255" + base_icon_state = "carpet_blue" floor_tile = /obj/item/stack/tile/carpet/exoticblue - canSmoothWith = list(/turf/open/floor/carpet/exoticblue) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_BLUE + canSmoothWith = SMOOTH_GROUP_CARPET_BLUE /turf/open/floor/carpet/exoticblue/broken icon_state = "damaged" @@ -393,8 +401,11 @@ /turf/open/floor/carpet/cyan icon = 'icons/turf/floors/carpet_cyan.dmi' + icon_state = "carpet_cyan-255" + base_icon_state = "carpet_cyan" floor_tile = /obj/item/stack/tile/carpet/cyan - canSmoothWith = list(/turf/open/floor/carpet/cyan) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_CYAN + canSmoothWith = SMOOTH_GROUP_CARPET_CYAN /turf/open/floor/carpet/cyan/broken icon_state = "damaged" @@ -402,8 +413,11 @@ /turf/open/floor/carpet/exoticgreen icon = 'icons/turf/floors/carpet_exoticgreen.dmi' + icon_state = "carpet_green-255" + base_icon_state = "carpet_green" floor_tile = /obj/item/stack/tile/carpet/exoticgreen - canSmoothWith = list(/turf/open/floor/carpet/exoticgreen) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_GREEN + canSmoothWith = SMOOTH_GROUP_CARPET_GREEN /turf/open/floor/carpet/exoticgreen/broken icon_state = "damaged" @@ -411,8 +425,11 @@ /turf/open/floor/carpet/orange icon = 'icons/turf/floors/carpet_orange.dmi' + icon_state = "carpet_orange-255" + base_icon_state = "carpet_orange" floor_tile = /obj/item/stack/tile/carpet/orange - canSmoothWith = list(/turf/open/floor/carpet/orange) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_ORANGE + canSmoothWith = SMOOTH_GROUP_CARPET_ORANGE /turf/open/floor/carpet/orange/broken icon_state = "damaged" @@ -420,8 +437,11 @@ /turf/open/floor/carpet/exoticpurple icon = 'icons/turf/floors/carpet_exoticpurple.dmi' + icon_state = "carpet_purple-255" + base_icon_state = "carpet_purple" floor_tile = /obj/item/stack/tile/carpet/exoticpurple - canSmoothWith = list(/turf/open/floor/carpet/exoticpurple) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_PURPLE + canSmoothWith = SMOOTH_GROUP_CARPET_PURPLE /turf/open/floor/carpet/exoticpurple/broken icon_state = "damaged" @@ -429,8 +449,11 @@ /turf/open/floor/carpet/red icon = 'icons/turf/floors/carpet_red.dmi' + icon_state = "carpet_red-255" + base_icon_state = "carpet_red" floor_tile = /obj/item/stack/tile/carpet/red - canSmoothWith = list(/turf/open/floor/carpet/red) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_RED + canSmoothWith = SMOOTH_GROUP_CARPET_RED /turf/open/floor/carpet/red/broken icon_state = "damaged" @@ -438,8 +461,11 @@ /turf/open/floor/carpet/royalblack icon = 'icons/turf/floors/carpet_royalblack.dmi' + icon_state = "carpet_royalblack-255" + base_icon_state = "carpet_royalblack" floor_tile = /obj/item/stack/tile/carpet/royalblack - canSmoothWith = list(/turf/open/floor/carpet/royalblack) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_ROYAL_BLACK + canSmoothWith = SMOOTH_GROUP_CARPET_ROYAL_BLACK /turf/open/floor/carpet/royalblack/broken icon_state = "damaged" @@ -447,8 +473,11 @@ /turf/open/floor/carpet/royalblue icon = 'icons/turf/floors/carpet_royalblue.dmi' + icon_state = "carpet_royalblue-255" + base_icon_state = "carpet_royalblue" floor_tile = /obj/item/stack/tile/carpet/royalblue - canSmoothWith = list(/turf/open/floor/carpet/royalblue) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_ROYAL_BLUE + canSmoothWith = SMOOTH_GROUP_CARPET_ROYAL_BLUE /turf/open/floor/carpet/royalblue/broken icon_state = "damaged" @@ -477,10 +506,12 @@ /turf/open/floor/fakepit desc = "A clever illusion designed to look like a bottomless pit." - smooth = SMOOTH_TRUE | SMOOTH_BORDER | SMOOTH_MORE - canSmoothWith = list(/turf/open/floor/fakepit) icon = 'icons/turf/floors/Chasms.dmi' - icon_state = "smooth" + icon_state = "chasms-0" + base_icon_state = "chasms" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_TURF_CHASM + canSmoothWith = SMOOTH_GROUP_TURF_CHASM tiled_dirt = FALSE /turf/open/floor/fakepit/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) diff --git a/code/game/turfs/simulated/floor/mineral_floor.dm b/code/game/turfs/simulated/floor/mineral_floor.dm index d13d418043dc..e1d29e00f9e8 100644 --- a/code/game/turfs/simulated/floor/mineral_floor.dm +++ b/code/game/turfs/simulated/floor/mineral_floor.dm @@ -196,10 +196,12 @@ /turf/open/floor/mineral/plastitanium/red/brig/fakepit name = "brig chasm" desc = "A place for very naughy criminals." - smooth = SMOOTH_TRUE | SMOOTH_BORDER | SMOOTH_MORE - canSmoothWith = list(/turf/open/floor/mineral/plastitanium/red/brig/fakepit) icon = 'icons/turf/floors/Chasms.dmi' - icon_state = "smooth" + icon_state = "chasms-0" + base_icon_state = "chasms" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_TURF_CHASM + canSmoothWith = SMOOTH_GROUP_TURF_CHASM tiled_dirt = FALSE //BANANIUM diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index 5fdd9fa14468..973c922c0cf0 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -10,7 +10,8 @@ /turf/open/floor/plating name = "plating" icon_state = "plating" - intact = FALSE + overfloor_placed = FALSE + underfloor_accessibility = UNDERFLOOR_INTERACTABLE baseturfs = /turf/baseturf_bottom footstep = FOOTSTEP_PLATING barefootstep = FOOTSTEP_HARD_BAREFOOT @@ -87,10 +88,9 @@ else if(istype(C, /obj/item/stack/tile) && !locate(/obj/structure/lattice/catwalk, src)) if(!broken && !burnt) for(var/obj/O in src) - if(O.level == 1) //ex. pipes laid underneath a tile - for(var/M in O.buckled_mobs) - to_chat(user, span_warning("Someone is buckled to \the [O]! Unbuckle [M] to move \him out of the way.")) - return + for(var/M in O.buckled_mobs) + to_chat(user, span_warning("Someone is buckled to \the [O]! Unbuckle [M] to move \him out of the way.")) + return var/obj/item/stack/tile/W = C if(!W.use(1)) return diff --git a/code/game/turfs/simulated/floor/plating/misc_plating.dm b/code/game/turfs/simulated/floor/plating/misc_plating.dm index c3cae2da310b..fad5d6cbee5a 100644 --- a/code/game/turfs/simulated/floor/plating/misc_plating.dm +++ b/code/game/turfs/simulated/floor/plating/misc_plating.dm @@ -39,8 +39,7 @@ gender = PLURAL name = "ash" icon_state = "ash" - smooth = SMOOTH_MORE|SMOOTH_BORDER - var/smooth_icon = 'icons/turf/floors/ash.dmi' + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER desc = "The ground is covered in volcanic ash." baseturfs = /turf/open/floor/plating/ashplanet/wateryrock //I assume this will be a chasm eventually, once this becomes an actual surface initial_gas_mix = LAVALAND_DEFAULT_ATMOS @@ -52,13 +51,16 @@ heavyfootstep = FOOTSTEP_GENERIC_HEAVY tiled_dirt = FALSE + var/smooth_icon = 'icons/turf/floors/ash.dmi' + /turf/open/floor/plating/ashplanet/Initialize(mapload) - if(smooth) + . = ..() + if(smoothing_flags & SMOOTH_BITMASK) var/matrix/M = new M.Translate(-4, -4) transform = M icon = smooth_icon - . = ..() + icon_state = "[icon_state]-[smoothing_junction]" /turf/open/floor/plating/ashplanet/try_replace_tile(obj/item/stack/tile/T, mob/user, params) return @@ -70,7 +72,8 @@ return /turf/open/floor/plating/ashplanet/ash - canSmoothWith = list(/turf/open/floor/plating/ashplanet/ash, /turf/closed) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_ASH + canSmoothWith = SMOOTH_GROUP_FLOOR_ASH + SMOOTH_GROUP_CLOSED_TURFS layer = HIGH_TURF_LAYER slowdown = 1 @@ -78,9 +81,11 @@ gender = PLURAL name = "rocky ground" icon_state = "rockyash" - smooth_icon = 'icons/turf/floors/rocky_ash.dmi' + base_icon_state = "rocky_ash" + icon = MAP_SWITCH('icons/turf/floors/rocky_ash.dmi', 'icons/turf/mining.dmi') layer = MID_TURF_LAYER - canSmoothWith = list(/turf/open/floor/plating/ashplanet/rocky, /turf/closed) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_ASH_ROCKY + canSmoothWith = SMOOTH_GROUP_FLOOR_ASH_ROCKY + SMOOTH_GROUP_CLOSED_TURFS footstep = FOOTSTEP_FLOOR barefootstep = FOOTSTEP_HARD_BAREFOOT clawfootstep = FOOTSTEP_HARD_CLAW @@ -89,8 +94,8 @@ /turf/open/floor/plating/ashplanet/wateryrock gender = PLURAL name = "wet rocky ground" - smooth = null icon_state = "wateryrock" + smoothing_flags = NONE slowdown = 2 footstep = FOOTSTEP_FLOOR barefootstep = FOOTSTEP_HARD_BAREFOOT @@ -196,9 +201,11 @@ return /turf/open/floor/plating/ice/smooth - icon_state = "smooth" - smooth = SMOOTH_MORE | SMOOTH_BORDER - canSmoothWith = list(/turf/open/floor/plating/ice/smooth, /turf/open/floor/plating/ice) + icon_state = "ice_turf-255" + base_icon_state = "ice_turf" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_ICE + canSmoothWith = SMOOTH_GROUP_FLOOR_ICE /turf/open/floor/plating/ice/colder initial_temperature = 140 @@ -237,11 +244,13 @@ initial_gas_mix = "o2=0;n2=82;plasma=24;TEMP=120" /turf/open/floor/plating/snowed/smoothed - smooth = SMOOTH_MORE | SMOOTH_BORDER - canSmoothWith = list(/turf/open/floor/plating/snowed/smoothed, /turf/open/floor/plating/snowed) - planetary_atmos = TRUE icon = 'icons/turf/floors/snow_turf.dmi' - icon_state = "smooth" + icon_state = "snow_turf-0" + base_icon_state = "snow_turf" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_SNOWED + canSmoothWith = SMOOTH_GROUP_FLOOR_SNOWED + planetary_atmos = TRUE /turf/open/floor/plating/snowed/colder initial_temperature = 140 diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index 0a24d89e0e9e..da13fa2a09eb 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -17,6 +17,24 @@ clawfootstep = FOOTSTEP_LAVA heavyfootstep = FOOTSTEP_LAVA + /// The icon state that covers the lava bits of our turf + var/mask_state = "lava-lightmask" + +/turf/open/lava/update_overlays() + . = ..() + // We need a light overlay here because not every lava turf casts light, only the edge ones + var/mutable_appearance/light = mutable_appearance(mask_icon, mask_state, LIGHTING_PRIMARY_LAYER, src, LIGHTING_PLANE) + light.color = light_color + light.blend_mode = BLEND_ADD + . += light + // Mask away our light underlay, so things don't double stack + // This does mean if our light underlay DOESN'T look like the light we emit things will be wrong + // But that's rare, and I'm ok with that, quartering our light source count is useful + var/mutable_appearance/light_mask = mutable_appearance(mask_icon, mask_state, LIGHTING_MASK_LAYER, src, LIGHTING_PLANE) + light_mask.blend_mode = BLEND_MULTIPLY + light_mask.color = list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0) + . += light_mask + /turf/open/lava/ex_act(severity, target) contents_explosion(severity, target) @@ -163,9 +181,16 @@ name = "lava" baseturfs = /turf/open/lava/smooth icon = 'icons/turf/floors/lava.dmi' - icon_state = "unsmooth" - smooth = SMOOTH_MORE | SMOOTH_BORDER - canSmoothWith = list(/turf/open/lava/smooth) + icon_state = "lava-255" + mask_state = "lava-255" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_LAVA + canSmoothWith = SMOOTH_GROUP_FLOOR_LAVA + +/turf/open/lava/smooth_icon() + . = ..() + mask_state = icon_state + update_appearance(~UPDATE_SMOOTHING) /turf/open/lava/smooth/lava_land_surface initial_gas_mix = LAVALAND_DEFAULT_ATMOS diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index b9e8d6521e16..bf740ed64582 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -2,10 +2,15 @@ /turf/closed/mineral //wall piece name = "rock" - icon = 'icons/turf/mining.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') icon_state = "rock" - var/smooth_icon = 'icons/turf/smoothrocks.dmi' - smooth = SMOOTH_MORE|SMOOTH_BORDER + + // This is static + // Done like this to avoid needing to make it dynamic and save cpu time + // 4 to the left, 4 down + transform = MAP_SWITCH(TRANSLATE_MATRIX(-4, -4), matrix()) + + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1 | NO_RUST canSmoothWith = null baseturfs = /turf/open/floor/plating/asteroid/airless @@ -25,12 +30,13 @@ var/hardness = 1 //how hard the material is, we'll have to have more powerful stuff if we want to blast harder materials. /turf/closed/mineral/Initialize(mapload) - if (!canSmoothWith) - canSmoothWith = list(/turf/closed/mineral, /turf/closed/indestructible) - var/matrix/M = new - M.Translate(-4, -4) - transform = M - icon = smooth_icon + var/static/list/smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MINERAL_WALLS + var/static/list/canSmoothWith = SMOOTH_GROUP_MINERAL_WALLS + + // The cost of the list() being in the type def is very large for something as common as minerals + src.smoothing_groups = smoothing_groups + src.canSmoothWith = canSmoothWith + . = ..() if (mineralType && mineralAmt && spread && spreadChance) for(var/dir in GLOB.cardinals) @@ -46,7 +52,6 @@ return TRUE return ..() - /turf/closed/mineral/attackby(obj/item/I, mob/user, params) if (!user.IsAdvancedToolUser()) to_chat(usr, span_warning("You don't have the dexterity to do this!")) @@ -184,6 +189,9 @@ M.baseturfs = src.baseturfs src = M M.levelupdate() + else + src = T + T.levelupdate() /turf/closed/mineral/random/high_chance icon_state = "rock_highchance" @@ -205,11 +213,10 @@ /turf/closed/mineral/random/high_chance/snow name = "snowy mountainside" - icon = 'icons/turf/mining.dmi' - smooth_icon = 'icons/turf/walls/mountain_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/mountain_wall.dmi', 'icons/turf/mining.dmi') icon_state = "mountainrock" - smooth = SMOOTH_MORE|SMOOTH_BORDER - canSmoothWith = list (/turf/closed) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS defer_change = TRUE environment_type = "snow" turf_type = /turf/open/floor/plating/asteroid/snow/icemoon @@ -263,7 +270,7 @@ /turf/closed/mineral/random/volcanic/hard name = "hardened basalt" icon_state = "rock_hard" - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') mineralChance = 15 hardness = 2 @@ -274,7 +281,7 @@ /turf/closed/mineral/random/volcanic/hard/harder name = "granite" icon_state = "rock" - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') color = "#eb9877" mineralChance = 20 hardness = 3 @@ -285,11 +292,10 @@ /turf/closed/mineral/random/snow name = "snowy mountainside" - icon = 'icons/turf/mining.dmi' - smooth_icon = 'icons/turf/walls/mountain_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/mountain_wall.dmi', 'icons/turf/mining.dmi') icon_state = "mountainrock" - smooth = SMOOTH_MORE|SMOOTH_BORDER - canSmoothWith = list (/turf/closed) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS defer_change = TRUE environment_type = "snow" turf_type = /turf/open/floor/plating/asteroid/snow/icemoon @@ -354,11 +360,11 @@ defer_change = TRUE /turf/closed/mineral/iron/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/iron/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 @@ -366,7 +372,7 @@ /turf/closed/mineral/iron/ice environment_type = "snow_cavern" icon_state = "icerock_iron" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -396,11 +402,11 @@ defer_change = TRUE /turf/closed/mineral/uranium/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/uranium/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 @@ -408,7 +414,7 @@ /turf/closed/mineral/uranium/ice environment_type = "snow_cavern" icon_state = "icerock_Uranium" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -437,11 +443,11 @@ defer_change = TRUE /turf/closed/mineral/diamond/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/diamond/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 @@ -449,7 +455,7 @@ /turf/closed/mineral/diamond/ice environment_type = "snow_cavern" icon_state = "icerock_diamond" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -478,11 +484,11 @@ defer_change = TRUE /turf/closed/mineral/gold/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/gold/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 @@ -490,7 +496,7 @@ /turf/closed/mineral/gold/ice environment_type = "snow_cavern" icon_state = "icerock_gold" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -519,11 +525,11 @@ defer_change = TRUE /turf/closed/mineral/silver/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/silver/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 @@ -531,7 +537,7 @@ /turf/closed/mineral/silver/ice environment_type = "snow_cavern" icon_state = "icerock_silver" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -560,11 +566,11 @@ defer_change = TRUE /turf/closed/mineral/titanium/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/titanium/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 @@ -572,7 +578,7 @@ /turf/closed/mineral/titanium/ice environment_type = "snow_cavern" icon_state = "icerock_titanium" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -602,11 +608,11 @@ defer_change = TRUE /turf/closed/mineral/plasma/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/plasma/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 @@ -614,7 +620,7 @@ /turf/closed/mineral/plasma/ice environment_type = "snow_cavern" icon_state = "icerock_plasma" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -639,7 +645,7 @@ /turf/closed/mineral/bananium/ice environment_type = "snow_cavern" icon_state = "icerock_Bananium" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -658,11 +664,11 @@ defer_change = TRUE /turf/closed/mineral/bananium/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/bananium/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 3 color = "#eb9877" hardness = 3 @@ -682,11 +688,11 @@ defer_change = TRUE /turf/closed/mineral/bscrystal/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/bscrystal/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 3 color = "#eb9877" hardness = 3 @@ -694,7 +700,7 @@ /turf/closed/mineral/bscrystal/ice environment_type = "snow_cavern" icon_state = "icerock_BScrystal" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -722,21 +728,20 @@ defer_change = TRUE /turf/closed/mineral/volcanic/lava_land_surface/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/volcanic/lava_land_surface/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') color = "#eb9877" hardness = 3 /turf/closed/mineral/ash_rock //wall piece name = "rock" - icon = 'icons/turf/mining.dmi' - smooth_icon = 'icons/turf/walls/rock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/rock_wall.dmi', 'icons/turf/mining.dmi') icon_state = "rock2" - smooth = SMOOTH_MORE|SMOOTH_BORDER - canSmoothWith = list (/turf/closed) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS baseturfs = /turf/open/floor/plating/ashplanet/wateryrock initial_gas_mix = LAVALAND_DEFAULT_ATMOS environment_type = "waste" @@ -750,11 +755,10 @@ /turf/closed/mineral/snowmountain name = "snowy mountainside" - icon = 'icons/turf/mining.dmi' - smooth_icon = 'icons/turf/walls/mountain_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/mountain_wall.dmi', 'icons/turf/mining.dmi') icon_state = "mountainrock" - smooth = SMOOTH_MORE|SMOOTH_BORDER - canSmoothWith = list (/turf/closed) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS baseturfs = /turf/open/floor/plating/asteroid/snow initial_gas_mix = FROZEN_ATMOS environment_type = "snow" @@ -768,11 +772,10 @@ /turf/closed/mineral/snowmountain/cavern name = "ice cavern rock" - icon = 'icons/turf/mining.dmi' - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') icon_state = "icerock" - smooth = SMOOTH_MORE|SMOOTH_BORDER - canSmoothWith = list (/turf/closed) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS baseturfs = /turf/open/floor/plating/asteroid/snow/ice environment_type = "snow_cavern" turf_type = /turf/open/floor/plating/asteroid/snow/ice @@ -893,18 +896,18 @@ defer_change = TRUE /turf/closed/mineral/gibtonite/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/gibtonite/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') color = "#eb9877" hardness = 3 /turf/closed/mineral/gibtonite/ice environment_type = "snow_cavern" icon_state = "icerock_Gibtonite" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS @@ -937,11 +940,11 @@ defer_change = TRUE /turf/closed/mineral/magmite/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/magmite/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') color = "#eb9877" hardness = 3 @@ -960,11 +963,11 @@ /turf/closed/mineral/gem/volcanic/hard mineralAmt = 2 - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/gem/volcanic/hard/harder mineralAmt = 3 - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') color = "#eb9877" hardness = 3 diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm index 5636dce78a18..95e80aaa101c 100644 --- a/code/game/turfs/simulated/wall/mineral_walls.dm +++ b/code/game/turfs/simulated/wall/mineral_walls.dm @@ -2,45 +2,57 @@ name = "mineral wall" desc = "This shouldn't exist" icon_state = "" + smoothing_flags = SMOOTH_BITMASK + canSmoothWith = null + var/last_event = 0 var/active = null - canSmoothWith = null - smooth = SMOOTH_TRUE /turf/closed/wall/mineral/gold name = "gold wall" desc = "A wall with gold plating. Swag!" icon = 'icons/turf/walls/gold_wall.dmi' - icon_state = "gold" + icon_state = "gold_wall-0" + base_icon_state = "gold_wall" sheet_type = /obj/item/stack/sheet/mineral/gold explosion_block = 0 //gold is a soft metal you dingus. - canSmoothWith = list(/turf/closed/wall/mineral/gold, /obj/structure/falsewall/gold) + smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_GOLD_WALLS /turf/closed/wall/mineral/silver name = "silver wall" desc = "A wall with silver plating. Shiny!" icon = 'icons/turf/walls/silver_wall.dmi' - icon_state = "silver" + icon_state = "silver_wall-0" + base_icon_state = "silver_wall" sheet_type = /obj/item/stack/sheet/mineral/silver - canSmoothWith = list(/turf/closed/wall/mineral/silver, /obj/structure/falsewall/silver) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SILVER_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SILVER_WALLS /turf/closed/wall/mineral/diamond name = "diamond wall" desc = "A wall with diamond plating. You monster." icon = 'icons/turf/walls/diamond_wall.dmi' - icon_state = "diamond" + icon_state = "diamond_wall-0" + base_icon_state = "diamond_wall" sheet_type = /obj/item/stack/sheet/mineral/diamond slicing_duration = 400 //diamond wall takes twice as much time to slice explosion_block = 3 - canSmoothWith = list(/turf/closed/wall/mineral/diamond, /obj/structure/falsewall/diamond) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_DIAMOND_WALLS /turf/closed/wall/mineral/bananium name = "bananium wall" desc = "A wall with bananium plating. Honk!" icon = 'icons/turf/walls/bananium_wall.dmi' - icon_state = "bananium" + icon_state = "bananium_wall-0" + base_icon_state = "bananium_wall" sheet_type = /obj/item/stack/sheet/mineral/bananium - canSmoothWith = list(/turf/closed/wall/mineral/bananium, /obj/structure/falsewall/bananium) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_BANANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_BANANIUM_WALLS /turf/closed/wall/mineral/bananium/honk_act() return FALSE @@ -49,19 +61,25 @@ name = "sandstone wall" desc = "A wall with sandstone plating. Rough." icon = 'icons/turf/walls/sandstone_wall.dmi' - icon_state = "sandstone" + icon_state = "sandstone_wall-0" + base_icon_state = "sandstone_wall" sheet_type = /obj/item/stack/sheet/mineral/sandstone explosion_block = 0 - canSmoothWith = list(/turf/closed/wall/mineral/sandstone, /obj/structure/falsewall/sandstone) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SANDSTONE_WALLS /turf/closed/wall/mineral/uranium article = "a" name = "uranium wall" desc = "A wall with uranium plating. This is probably a bad idea." icon = 'icons/turf/walls/uranium_wall.dmi' - icon_state = "uranium" + icon_state = "uranium_wall-0" + base_icon_state = "uranium_wall" sheet_type = /obj/item/stack/sheet/mineral/uranium - canSmoothWith = list(/turf/closed/wall/mineral/uranium, /obj/structure/falsewall/uranium) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_URANIUM_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_URANIUM_WALLS /turf/closed/wall/mineral/uranium/proc/radiate() if(!active) @@ -91,10 +109,13 @@ name = "plasma wall" desc = "A wall with plasma plating. This is definitely a bad idea." icon = 'icons/turf/walls/plasma_wall.dmi' - icon_state = "plasma" + icon_state = "plasma_wall-0" + base_icon_state = "plasma_wall" sheet_type = /obj/item/stack/sheet/mineral/plasma thermal_conductivity = 0.04 - canSmoothWith = list(/turf/closed/wall/mineral/plasma, /obj/structure/falsewall/plasma) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_PLASMA_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_PLASMA_WALLS rad_insulation = RAD_FULL_INSULATION /turf/closed/wall/mineral/plasma/attackby(obj/item/W, mob/user, params) @@ -130,20 +151,25 @@ name = "wooden wall" desc = "A wall with wooden plating. Stiff." icon = 'icons/turf/walls/wood_wall.dmi' - icon_state = "wood" + icon_state = "wood_wall-0" + base_icon_state = "wood_wall" sheet_type = /obj/item/stack/sheet/mineral/wood hardness = 70 explosion_block = 0 - canSmoothWith = list(/turf/closed/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/closed/wall/mineral/wood/nonmetal) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_WOOD_WALLS /turf/closed/wall/mineral/bamboo name = "bamboo wall" desc = "A wall with a bamboo finish." icon = 'icons/turf/walls/bamboo_wall.dmi' - icon_state = "bamboo" + icon_state = "wall-0" sheet_type = /obj/item/stack/sheet/mineral/bamboo hardness = 60 - canSmoothWith = list(/turf/closed/wall/mineral/bamboo, /obj/structure/falsewall/bamboo) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_BAMBOO_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_BAMBOO_WALLS /turf/closed/wall/mineral/wood/attackby(obj/item/W, mob/user) if(W.is_sharp() && W.force) @@ -159,22 +185,25 @@ desc = "A solidly wooden wall. It's a bit weaker than a wall made with metal." girder_type = /obj/structure/barricade/wooden hardness = 50 - canSmoothWith = list(/turf/closed/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/closed/wall/mineral/wood/nonmetal) /turf/closed/wall/mineral/iron name = "rough metal wall" desc = "A wall with rough metal plating." icon = 'icons/turf/walls/iron_wall.dmi' - icon_state = "iron" + icon_state = "iron_wall-0" + base_icon_state = "iron_wall" sheet_type = /obj/item/stack/rods sheet_amount = 5 - canSmoothWith = list(/turf/closed/wall/mineral/iron, /obj/structure/falsewall/iron) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_IRON_WALLS /turf/closed/wall/mineral/snow name = "packed snow wall" desc = "A wall made of densely packed snow blocks." icon = 'icons/turf/walls/snow_wall.dmi' - icon_state = "snow" + icon_state = "snow_wall-0" + base_icon_state = "snow_wall" hardness = 80 explosion_block = 0 slicing_duration = 30 @@ -188,12 +217,14 @@ name = "alien wall" desc = "A wall with alien alloy plating." icon = 'icons/turf/walls/abductor_wall.dmi' - icon_state = "abductor" - smooth = SMOOTH_TRUE|SMOOTH_DIAGONAL + icon_state = "abductor_wall-0" + base_icon_state = "abductor_wall" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS + smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS sheet_type = /obj/item/stack/sheet/mineral/abductor slicing_duration = 400 //alien wall takes twice as much time to slice explosion_block = 3 - canSmoothWith = list(/turf/closed/wall/mineral/abductor, /obj/structure/falsewall/abductor) /////////////////////Titanium walls///////////////////// @@ -201,21 +232,24 @@ name = "wall" desc = "A light-weight titanium wall used in shuttles." icon = 'icons/turf/walls/shuttle_wall.dmi' - icon_state = "map-shuttle" + icon_state = "shuttle_wall-0" + base_icon_state = "shuttle_wall" explosion_block = 3 flags_1 = CAN_BE_DIRTY_1 | CHECK_RICOCHET_1 sheet_type = /obj/item/stack/sheet/mineral/titanium - smooth = SMOOTH_MORE|SMOOTH_DIAGONAL - canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS + smoothing_groups = SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_TITANIUM_WALLS /turf/closed/wall/mineral/titanium/nodiagonal - smooth = SMOOTH_MORE icon_state = "map-shuttle_nd" + base_icon_state = "shuttle_wall" + smoothing_flags = SMOOTH_BITMASK /turf/closed/wall/mineral/titanium/nosmooth icon = 'icons/turf/shuttle.dmi' icon_state = "wall" - smooth = SMOOTH_FALSE + smoothing_flags = NONE /turf/closed/wall/mineral/titanium/overspace icon_state = "map-overspace" @@ -248,15 +282,17 @@ name = "pod wall" desc = "An easily-compressable wall used for temporary shelter." icon = 'icons/turf/walls/survival_pod_walls.dmi' - icon_state = "smooth" - smooth = SMOOTH_MORE|SMOOTH_DIAGONAL - canSmoothWith = list(/turf/closed/wall/mineral/titanium/survival, /obj/machinery/door/airlock, /obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/shuttle, /obj/structure/shuttle/engine) + icon_state = "survival_pod_walls-0" + base_icon_state = "survival_pod_walls" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_WINDOW_FULLTILE + SMOOTH_GROUP_TITANIUM_WALLS /turf/closed/wall/mineral/titanium/survival/nodiagonal - smooth = SMOOTH_MORE + smoothing_flags = SMOOTH_BITMASK /turf/closed/wall/mineral/titanium/survival/pod - canSmoothWith = list(/turf/closed/wall/mineral/titanium/survival, /obj/machinery/door/airlock/survival_pod, /obj/structure/window/shuttle/survival_pod) + smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD /////////////////////Plastitanium walls///////////////////// @@ -264,21 +300,24 @@ name = "wall" desc = "A durable wall made of an alloy of plasma and titanium." icon = 'icons/turf/walls/plastitanium_wall.dmi' - icon_state = "map-shuttle" + icon_state = "plastitanium_wall-0" + base_icon_state = "plastitanium_wall" explosion_block = 4 sheet_type = /obj/item/stack/sheet/mineral/plastitanium - smooth = SMOOTH_MORE|SMOOTH_DIAGONAL - canSmoothWith = list(/turf/closed/wall/mineral/plastitanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/plastitanium, /obj/structure/shuttle/engine, /obj/structure/falsewall/plastitanium) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS + smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS rad_insulation = RAD_FULL_INSULATION /turf/closed/wall/mineral/plastitanium/nodiagonal - smooth = SMOOTH_MORE icon_state = "map-shuttle_nd" + base_icon_state = "plastitanium_wall" + smoothing_flags = SMOOTH_BITMASK /turf/closed/wall/mineral/plastitanium/nosmooth icon = 'icons/turf/shuttle.dmi' icon_state = "wall" - smooth = SMOOTH_FALSE + smoothing_flags = NONE /turf/closed/wall/mineral/plastitanium/overspace icon_state = "map-overspace" diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm index 3f6374a3668b..5e5cb5de0ba5 100644 --- a/code/game/turfs/simulated/wall/misc_walls.dm +++ b/code/game/turfs/simulated/wall/misc_walls.dm @@ -2,9 +2,10 @@ name = "runed metal wall" desc = "A cold metal wall engraved with indecipherable symbols. Studying them causes your head to pound." icon = 'icons/turf/walls/cult_wall.dmi' - icon_state = "cult" + icon_state = "cult_wall-0" + base_icon_state = "cult_wall" + smoothing_flags = SMOOTH_BITMASK canSmoothWith = null - smooth = SMOOTH_MORE sheet_type = /obj/item/stack/sheet/runed_metal sheet_amount = 1 girder_type = /obj/structure/girder/cult @@ -49,6 +50,8 @@ /turf/closed/wall/clockwork name = "clockwork wall" desc = "A huge chunk of warm metal. The clanging of machinery emanates from within." + icon_state = "clockwork_wall-0" + base_icon_state = "clockwork_wall" explosion_block = 3 hardness = 5 slicing_duration = 150 @@ -56,6 +59,10 @@ sheet_amount = 1 girder_type = /obj/structure/destructible/clockwork/wall_gear baseturfs = /turf/open/floor/clockwork/reebe + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CLOCKWORK_WALLS + SMOOTH_GROUP_WALLS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_CLOCKWORK_WALLS + var/heated var/obj/effect/clockwork/overlay/wall/realappearance diff --git a/code/game/turfs/simulated/wall/reinf_walls.dm b/code/game/turfs/simulated/wall/reinf_walls.dm index 94e94e57333f..72db8935f8b0 100644 --- a/code/game/turfs/simulated/wall/reinf_walls.dm +++ b/code/game/turfs/simulated/wall/reinf_walls.dm @@ -194,14 +194,21 @@ /turf/closed/wall/r_wall/update_icon(updates=ALL) . = ..() if(d_state != INTACT) - smooth = SMOOTH_FALSE - clear_smooth_overlays() + smoothing_flags = NONE + return + if (!(updates & UPDATE_SMOOTHING)) + return + smoothing_flags = SMOOTH_BITMASK + QUEUE_SMOOTH_NEIGHBORS(src) + QUEUE_SMOOTH(src) + +// We don't react to smoothing changing here because this else exists only to "revert" intact changes +/turf/closed/wall/r_wall/update_icon_state() + if(d_state != INTACT) icon_state = "r_wall-[d_state]" else - smooth = SMOOTH_TRUE - queue_smooth_neighbors(src) - queue_smooth(src) - icon_state = "r_wall" + icon_state = "[base_icon_state]-[smoothing_junction]" + return ..() /turf/closed/wall/r_wall/singularity_pull(S, current_size) if(current_size >= STAGE_FIVE) @@ -228,23 +235,26 @@ name = "hull" desc = "The armored hull of an ominous looking ship." icon = 'icons/turf/walls/plastitanium_wall.dmi' - icon_state = "map-shuttle" + icon_state = "plastitanium_wall-0" + base_icon_state = "plastitanium_wall" explosion_block = 20 sheet_type = /obj/item/stack/sheet/mineral/plastitanium - smooth = SMOOTH_MORE|SMOOTH_DIAGONAL - canSmoothWith = list(/turf/closed/wall/r_wall/syndicate, /turf/closed/wall/mineral/plastitanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/plastitanium, /obj/structure/shuttle/engine, /obj/structure/falsewall/plastitanium) + smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS + smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_SYNDICATE_WALLS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS /turf/closed/wall/r_wall/syndicate/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) return FALSE /turf/closed/wall/r_wall/syndicate/nodiagonal - smooth = SMOOTH_MORE icon_state = "map-shuttle_nd" + base_icon_state = "plastitanium_wall" + smoothing_flags = SMOOTH_BITMASK /turf/closed/wall/r_wall/syndicate/nosmooth icon = 'icons/turf/shuttle.dmi' icon_state = "wall" - smooth = SMOOTH_FALSE + smoothing_flags = NONE /turf/closed/wall/r_wall/syndicate/overspace icon_state = "map-overspace" diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index aff1b3ae03f4..04b083241531 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -16,24 +16,16 @@ pipe_astar_cost = 50 /* nich really doesn't like pipes that go through walls */\ ) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_WALLS + var/hardness = 30 //lower numbers are harder. Used to determine the probability of a hulk smashing through. var/slicing_duration = 200 //default time taken to slice the wall var/sheet_type = /obj/item/stack/sheet/metal var/sheet_amount = 2 var/girder_type = /obj/structure/girder - canSmoothWith = list( - /turf/closed/wall, - /turf/closed/wall/r_wall, - /obj/structure/falsewall, - /obj/structure/falsewall/brass, - /obj/structure/falsewall/reinforced, - /turf/closed/wall/explosive, - /turf/closed/wall/rust, - /turf/closed/wall/r_wall/rust, - /turf/closed/wall/clockwork) - smooth = SMOOTH_TRUE - var/list/dent_decals /turf/closed/wall/Initialize(mapload) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 253cf793e805..6ffa1922438b 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -2,7 +2,8 @@ icon = 'icons/turf/space.dmi' icon_state = "0" name = "\proper space" - intact = 0 + overfloor_placed = FALSE + underfloor_accessibility = UNDERFLOOR_INTERACTABLE initial_temperature = TCMB thermal_conductivity = 0 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 0d676cd092d3..2c48b953c4c3 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -7,8 +7,6 @@ GLOBAL_LIST_EMPTY(station_turfs) var/dynamic_lighting = DYNAMIC_LIGHTING_ENABLED - var/intact = 1 - // baseturfs can be either a list or a single turf type. // In class definition like here it should always be a single type. // A list will be created in initialization that figures out the baseturf's baseturf etc. @@ -22,6 +20,11 @@ GLOBAL_LIST_EMPTY(station_turfs) var/blocks_air = FALSE + /// If there's a tile over a basic floor that can be ripped out + var/overfloor_placed = FALSE + /// How accessible underfloor pieces such as wires, pipes, etc are on this turf. Can be HIDDEN, VISIBLE, or INTERACTABLE. + var/underfloor_accessibility = UNDERFLOOR_HIDDEN + flags_1 = CAN_BE_DIRTY_1 var/list/image/blueprint_data //for the station blueprints, images of objects eg: pipes @@ -39,6 +42,9 @@ GLOBAL_LIST_EMPTY(station_turfs) var/tiled_dirt = FALSE // use smooth tiled dirt decal + ///Icon-smoothing variable to map a diagonal wall corner with a fixed underlay. + var/list/fixed_underlay = null + ///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component. var/dynamic_lumcount = 0 @@ -75,8 +81,12 @@ GLOBAL_LIST_EMPTY(station_turfs) assemble_baseturfs() levelupdate() - if(smooth) - queue_smooth(src) + + SETUP_SMOOTHING() + + if (smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH(src) + visibilityChanged() for(var/atom/movable/AM in src) @@ -362,14 +372,12 @@ GLOBAL_LIST_EMPTY(station_turfs) /turf/proc/levelupdate() for(var/obj/O in src) - if(O.level == 1 && (O.flags_1 & INITIALIZED_1)) - O.hide(src.intact) + if(O.flags_1 & INITIALIZED_1) + SEND_SIGNAL(O, COMSIG_OBJ_HIDE, underfloor_accessibility) // override for space turfs, since they should never hide anything /turf/open/space/levelupdate() - for(var/obj/O in src) - if(O.level == 1 && (O.flags_1 & INITIALIZED_1)) - O.hide(0) + return // Removes all signs of lattice on the pos of the turf -Donkieyo /turf/proc/RemoveLattice() @@ -474,12 +482,10 @@ GLOBAL_LIST_EMPTY(station_turfs) //////////////////////////////////////////////////// /turf/singularity_act() - if(intact) - for(var/obj/O in contents) //this is for deleting things like wires contained in the turf - if(O.level != 1) - continue - if(O.invisibility == INVISIBILITY_MAXIMUM) - O.singularity_act() + if(underfloor_accessibility < UNDERFLOOR_INTERACTABLE) + for(var/obj/on_top in contents) //this is for deleting things like wires contained in the turf + if(HAS_TRAIT(on_top, TRAIT_T_RAY_VISIBLE)) + on_top.singularity_act() ScrapeAway(flags = CHANGETURF_INHERIT_AIR) return(2) @@ -487,7 +493,7 @@ GLOBAL_LIST_EMPTY(station_turfs) return TRUE /turf/proc/can_lay_cable() - return can_have_cabling() & !intact + return can_have_cabling() && underfloor_accessibility >= UNDERFLOOR_INTERACTABLE /turf/proc/visibilityChanged() GLOB.cameranet.updateVisibility(src) @@ -562,7 +568,7 @@ GLOBAL_LIST_EMPTY(station_turfs) acid_type = /obj/effect/acid/alien var/has_acid_effect = FALSE for(var/obj/O in src) - if(intact && O.level == 1) //hidden under the floor + if(intact && O.location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) //hidden under the floor continue if(istype(O, acid_type)) var/obj/effect/acid/A = O diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_overlay.dm b/code/modules/antagonists/clockcult/clock_effects/clock_overlay.dm index 5707fd0a9fef..b585b0e82dd1 100644 --- a/code/modules/antagonists/clockcult/clock_effects/clock_overlay.dm +++ b/code/modules/antagonists/clockcult/clock_effects/clock_overlay.dm @@ -29,17 +29,16 @@ name = "clockwork wall" icon = 'icons/turf/walls/clockwork_wall.dmi' icon_state = "clockwork_wall" - canSmoothWith = list(/obj/effect/clockwork/overlay/wall, /obj/structure/falsewall/brass) - smooth = SMOOTH_TRUE layer = CLOSED_TURF_LAYER + smoothing_flags = SMOOTH_BITMASK + canSmoothWith = null /obj/effect/clockwork/overlay/wall/Initialize(mapload) . = ..() - queue_smooth_neighbors(src) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(queue_smooth), src), 1) + QUEUE_SMOOTH_NEIGHBORS(src) /obj/effect/clockwork/overlay/wall/Destroy() - queue_smooth_neighbors(src) + QUEUE_SMOOTH_NEIGHBORS(src) return ..() /obj/effect/clockwork/overlay/floor diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm index 1253d94a738a..815ade06d3dd 100644 --- a/code/modules/antagonists/revenant/revenant_abilities.dm +++ b/code/modules/antagonists/revenant/revenant_abilities.dm @@ -268,7 +268,7 @@ if(!isplatingturf(victim) && !istype(victim, /turf/open/floor/engine/cult) && isfloorturf(victim) && prob(15)) var/turf/open/floor/floor = victim - if(floor.floor_tile) + if(floor.overfloor_placed && floor.floor_tile) new floor.floor_tile(floor) floor.broken = 0 floor.burnt = 0 diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index 3e511d438366..cf791d594a39 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -49,8 +49,6 @@ for(var/obj/machinery/atmospherics/pipe/pipe in loc) if(pipe.piping_layer == target_layer) candidate = pipe - if(pipe.level == 2) - break if(candidate) target = candidate setAttachLayer(candidate.piping_layer) diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm index e14ef7818dab..927e0a3ef46d 100644 --- a/code/modules/atmospherics/machinery/pipes/pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/pipes.dm @@ -18,6 +18,10 @@ ),\ ) +/obj/machinery/atmospherics/pipe/Initialize(mapload) + . = ..() + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) //if changing this, change the subtypes RemoveElements too, because thats how bespoke works + /obj/machinery/atmospherics/pipe/New(mapload) add_atom_colour(pipe_color, FIXED_COLOUR_PRIORITY) volume = 35 * device_type diff --git a/code/modules/awaymissions/mission_code/vrhub.dm b/code/modules/awaymissions/mission_code/vrhub.dm index a69bf9ba0f99..a2e3ec95fe83 100644 --- a/code/modules/awaymissions/mission_code/vrhub.dm +++ b/code/modules/awaymissions/mission_code/vrhub.dm @@ -155,5 +155,8 @@ name = "rough metal wall" desc = "A wall with rough metal plating." icon = 'icons/turf/walls/iron_wall.dmi' - icon_state = "iron" - canSmoothWith = list(/turf/closed/indestructible/iron) + icon_state = "iron_wall-0" + base_icon_state = "iron_wall" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_IRON_WALLS diff --git a/code/modules/holodeck/turfs.dm b/code/modules/holodeck/turfs.dm index 1ac4884134ef..c8194c73af82 100644 --- a/code/modules/holodeck/turfs.dm +++ b/code/modules/holodeck/turfs.dm @@ -110,8 +110,9 @@ icon = 'icons/turf/floors/carpet.dmi' icon_state = "carpet" floor_tile = /obj/item/stack/tile/carpet - smooth = SMOOTH_TRUE - canSmoothWith = null + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET + canSmoothWith = SMOOTH_GROUP_CARPET bullet_bounce_sound = null tiled_dirt = FALSE @@ -123,8 +124,8 @@ . = ..() if(!.) return FALSE - if(intact) - queue_smooth(src) + if((updates & UPDATE_SMOOTHING) && overfloor_placed && smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH(src) /turf/open/floor/holofloor/wood icon_state = "wood" diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index 9669eacfe860..31ad560e6bd6 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -83,9 +83,11 @@ /obj/structure/window/shuttle/survival_pod name = "pod window" icon = 'icons/obj/smooth_structures/pod_window.dmi' - icon_state = "smooth" - smooth = SMOOTH_MORE - canSmoothWith = list(/turf/closed/wall/mineral/titanium/survival, /obj/machinery/door/airlock/survival_pod, /obj/structure/window/shuttle/survival_pod) + icon_state = "pod_window-0" + base_icon_state = "pod_window" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD /obj/structure/window/shuttle/survival_pod/spawner/north dir = NORTH @@ -131,7 +133,9 @@ /obj/structure/table/survival_pod icon = 'icons/obj/lavaland/survival_pod.dmi' icon_state = "table" - smooth = SMOOTH_FALSE + smoothing_flags = NONE + smoothing_groups = null + canSmoothWith = null //Sleeper /obj/machinery/sleeper/survival_pod diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index edb852181af4..862c0c31063c 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -516,15 +516,19 @@ Difficulty: Hard icon_state = "wall" light_range = MINIMUM_USEFUL_LIGHT_RANGE duration = 100 - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_HIERO_WALL + canSmoothWith = SMOOTH_GROUP_HIERO_WALL /obj/effect/temp_visual/hierophant/wall/Initialize(mapload, new_caster) . = ..() - queue_smooth_neighbors(src) - queue_smooth(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) + QUEUE_SMOOTH(src) /obj/effect/temp_visual/hierophant/wall/Destroy() - queue_smooth_neighbors(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) return ..() /obj/effect/temp_visual/hierophant/wall/CanAllowThrough(atom/movable/mover, turf/target) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 496b7338074f..926a51372c92 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -321,21 +321,26 @@ obj/structure/elite_tumor/proc/onEliteWon() icon = 'icons/turf/walls/hierophant_wall_temp.dmi' icon_state = "wall" duration = 50 - smooth = SMOOTH_TRUE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_HIERO_WALL + canSmoothWith = SMOOTH_GROUP_HIERO_WALL layer = BELOW_MOB_LAYER - var/mob/living/carbon/human/activator = null - var/mob/living/simple_animal/hostile/asteroid/elite/ourelite = null color = rgb(255,0,0) light_range = MINIMUM_USEFUL_LIGHT_RANGE light_color = LIGHT_COLOR_RED + var/mob/living/carbon/human/activator = null + var/mob/living/simple_animal/hostile/asteroid/elite/ourelite = null + /obj/effect/temp_visual/elite_tumor_wall/Initialize(mapload, new_caster) . = ..() - queue_smooth_neighbors(src) - queue_smooth(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) + QUEUE_SMOOTH(src) /obj/effect/temp_visual/elite_tumor_wall/Destroy() - queue_smooth_neighbors(src) + if(smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH_NEIGHBORS(src) activator = null ourelite = null return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm index 4ed3901312fe..70ca436a4350 100644 --- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm +++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm @@ -15,8 +15,8 @@ icon_state = "flower_bud" layer = SPACEVINE_MOB_LAYER opacity = FALSE - canSmoothWith = list() - smooth = SMOOTH_FALSE + canSmoothWith = null + smoothing_flags = NONE /// The amount of time it takes to create a venus human trap, in deciseconds var/growth_time = 1200 /// Used by countdown to check time, this is when the timer will complete and the venus trap will spawn. diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index 94d793dc5bde..4460fac89aaf 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -47,6 +47,7 @@ All the important duct code: qdel(src) //replace with dropping or something if(active) attempt_connect() + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) /obj/machinery/duct/proc/attempt_connect() reset_connects(0) //All connects are gathered here again eitherway, we might aswell reset it so they properly update when reconnecting diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 215bfb7fab95..214e864f1259 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -630,7 +630,7 @@ var/turf/host_turf = get_turf(src) if(!host_turf) CRASH("attackby on APC when it's not on a turf") - if (host_turf.intact) + if(host_turf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) to_chat(user, span_warning("You must remove the floor plating in front of the APC first!")) return else if (terminal) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index b3f8caafb3de..86e647815690 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -84,8 +84,7 @@ By design, d1 is the smallest direction and d2 is the highest d2 = text2num( copytext( icon_state, dash+1 ) ) var/turf/T = get_turf(src) // hide if turf is not intact - if(level==1) - hide(T.intact) + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) GLOB.cable_list += src //add it to the global cable list var/list/cable_colors = GLOB.cable_colors @@ -131,7 +130,7 @@ By design, d1 is the smallest direction and d2 is the highest /obj/structure/cable/proc/handlecable(obj/item/W, mob/user, params) var/turf/T = get_turf(src) - if(T.intact) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) return if(W.tool_behaviour == TOOL_WIRECUTTER) if (shock(user, 50)) @@ -614,8 +613,8 @@ By design, d1 is the smallest direction and d2 is the highest if(!isturf(user.loc)) return - if(!isturf(T) || T.intact || !T.can_have_cabling()) - to_chat(user, span_warning("You can only lay cables on top of exterior catwalks and plating!")) + if(!isturf(T) || T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !T.can_have_cabling()) + to_chat(user, span_warning("You can only lay cables on catwalks and plating!")) return if(get_amount() < 1) // Out of cable diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 91ba0b8c813f..3935249d42fd 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -156,7 +156,7 @@ if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/coil = W var/turf/T = user.loc - if(T.intact || !isfloorturf(T)) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !isfloorturf(T)) return if(get_dist(src, user) > 1) return diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index c17a460347bb..33a0bf6eeac7 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -116,7 +116,7 @@ return var/turf/T = get_turf(user) - if (T.intact) //is the floor plating removed ? + if (T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) //can we get to the underfloor? to_chat(user, span_warning("You must first remove the floor plating!")) return diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index 50b70d1d1834..9e8e451fae02 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -14,9 +14,7 @@ /obj/machinery/power/terminal/Initialize(mapload) . = ..() - var/turf/T = get_turf(src) - if(level == 1) - hide(T.intact) + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE, use_alpha = TRUE) /obj/machinery/power/terminal/Destroy() if(master) @@ -50,7 +48,7 @@ /obj/machinery/power/terminal/proc/dismantle(mob/living/user, obj/item/I) if(isturf(loc)) var/turf/T = loc - if(T.intact) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) to_chat(user, span_warning("You must first expose the power terminal!")) return diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index d6dadf6ccf50..c2fd1de84a8c 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -458,8 +458,6 @@ new /obj/effect/hotspot(T) for(var/obj/O in range(aoe_structure_range, epicenter)) if(!isitem(O)) - if(O.level == 1) //Please don't break underfloor items! - continue O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, LASER, FALSE) /obj/projectile/beam/beam_rifle/proc/check_pierce(atom/target) diff --git a/code/modules/recycling/disposal/construction.dm b/code/modules/recycling/disposal/construction.dm index 42d680720e3f..c27e613b5c6b 100644 --- a/code/modules/recycling/disposal/construction.dm +++ b/code/modules/recycling/disposal/construction.dm @@ -31,6 +31,7 @@ var/datum/component/simple_rotation/rotcomp = AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_FLIP | ROTATION_VERBS, null, CALLBACK(src, PROC_REF(can_be_rotated)), CALLBACK(src, PROC_REF(after_rot))) if(flip) rotcomp.BaseRot(null,ROTATION_FLIP) + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) update_appearance(UPDATE_ICON) @@ -115,7 +116,7 @@ var/ispipe = is_pipe() // Indicates if we should change the level of this pipe var/turf/T = get_turf(src) - if(T.intact && isfloorturf(T)) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && isfloorturf(T)) to_chat(user, span_warning("You can only attach the [pipename] if the floor plating is removed!")) return TRUE diff --git a/code/modules/recycling/disposal/pipe.dm b/code/modules/recycling/disposal/pipe.dm index 4b6baf096ccb..8e7bc3937d48 100644 --- a/code/modules/recycling/disposal/pipe.dm +++ b/code/modules/recycling/disposal/pipe.dm @@ -34,13 +34,14 @@ if(initialize_dirs != DISP_DIR_NONE) dpdir = dir - if(initialize_dirs & DISP_DIR_LEFT) dpdir |= turn(dir, 90) if(initialize_dirs & DISP_DIR_RIGHT) dpdir |= turn(dir, -90) if(initialize_dirs & DISP_DIR_FLIP) dpdir |= turn(dir, 180) + + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) update() // pipe is deleted @@ -97,7 +98,7 @@ var/eject_range = 5 var/turf/open/floor/floorturf - if(isfloorturf(T)) //intact floor, pop the tile + if(isfloorturf(T) && T.overfloor_placed) // pop the tile if present floorturf = T floorturf.remove_tile() diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm index 00e9c80bd5ae..f608f35c61c6 100644 --- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm @@ -198,7 +198,8 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337) name = "hotel wall" desc = "A wall designed to protect the security of the hotel's guests." icon_state = "hotelwall" - canSmoothWith = list(/turf/closed/indestructible/hotelwall) + smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_HOTEL_WALLS + canSmoothWith = SMOOTH_GROUP_HOTEL_WALLS explosion_block = INFINITY /turf/open/indestructible/hotelwood diff --git a/code/modules/shuttle/shuttle_rotate.dm b/code/modules/shuttle/shuttle_rotate.dm index 958c9aa5c5ab..af9401994adb 100644 --- a/code/modules/shuttle/shuttle_rotate.dm +++ b/code/modules/shuttle/shuttle_rotate.dm @@ -12,8 +12,8 @@ If ever any of these procs are useful for non-shuttles, rename it to proc/rotate setDir(angle2dir(rotation+dir2angle(dir))) //resmooth if need be. - if(smooth && (params & ROTATE_SMOOTH)) - queue_smooth(src) + if(params & ROTATE_SMOOTH && smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)) + QUEUE_SMOOTH(src) //rotate the pixel offsets too. if((pixel_x || pixel_y) && (params & ROTATE_OFFSET)) diff --git a/code/modules/spells/spell_types/conjure/_conjure.dm b/code/modules/spells/spell_types/conjure/_conjure.dm index 2e49ce063280..f4b7b7d2c17f 100644 --- a/code/modules/spells/spell_types/conjure/_conjure.dm +++ b/code/modules/spells/spell_types/conjure/_conjure.dm @@ -34,7 +34,11 @@ to_summon_in -= spawn_place if(ispath(summoned_object_type, /turf)) - spawn_place.ChangeTurf(summoned_object_type, flags = CHANGETURF_INHERIT_AIR) + if (spawn_place.overfloor_placed) + spawn_place.ChangeTurf(summoned_object_type, flags = CHANGETURF_INHERIT_AIR) + else + spawn_place.PlaceOnTop(summoned_object_type, flags = CHANGETURF_INHERIT_AIR) + return else var/atom/summoned_object = new summoned_object_type(spawn_place) @@ -47,4 +51,4 @@ /// Called on atoms summoned after they are created, allows extra variable editing and such of created objects /datum/action/cooldown/spell/conjure/proc/post_summon(atom/summoned_object, atom/cast_on) - return \ No newline at end of file + return diff --git a/code/modules/unit_tests/focus_only_tests.dm b/code/modules/unit_tests/focus_only_tests.dm index 426a236ab4bd..89c702ca2313 100644 --- a/code/modules/unit_tests/focus_only_tests.dm +++ b/code/modules/unit_tests/focus_only_tests.dm @@ -11,3 +11,6 @@ /// Checks that every overlay passed into build_appearance_list exists in the icon ///datum/unit_test/focus_only/invalid_overlays + +/// Checks that smoothing_groups and canSmoothWith are properly sorted in /atom/Initialize +/datum/unit_test/focus_only/sorted_smoothing_groups diff --git a/yogstation.dme b/yogstation.dme index 8c2967dc5e4c..e800a2e89f5c 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -71,6 +71,7 @@ #include "code\__DEFINES\footsteps.dm" #include "code\__DEFINES\forensics.dm" #include "code\__DEFINES\hud.dm" +#include "code\__DEFINES\icon_smoothing.dm" #include "code\__DEFINES\instruments.dm" #include "code\__DEFINES\interaction_flags.dm" #include "code\__DEFINES\inventory.dm" @@ -82,9 +83,11 @@ #include "code\__DEFINES\logging.dm" #include "code\__DEFINES\machines.dm" #include "code\__DEFINES\magic.dm" +#include "code\__DEFINES\map_switch.dm" #include "code\__DEFINES\maps.dm" #include "code\__DEFINES\materials.dm" #include "code\__DEFINES\maths.dm" +#include "code\__DEFINES\matrices.dm" #include "code\__DEFINES\MC.dm" #include "code\__DEFINES\melee.dm" #include "code\__DEFINES\menu.dm" @@ -204,7 +207,6 @@ #include "code\__DEFINES\{yogs_defines}\wires.dm" #include "code\__HELPERS\_extools_api.dm" #include "code\__HELPERS\_lists.dm" -#include "code\__HELPERS\_logging.dm" #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\animations.dm" #include "code\__HELPERS\areas.dm" @@ -250,6 +252,9 @@ #include "code\__HELPERS\verbs.dm" #include "code\__HELPERS\view.dm" #include "code\__HELPERS\weakref.dm" +#include "code\__HELPERS\logging\_logging.dm" +#include "code\__HELPERS\logging\attack.dm" +#include "code\__HELPERS\logging\talk.dm" #include "code\__HELPERS\sorts\__main.dm" #include "code\__HELPERS\sorts\InsertSort.dm" #include "code\__HELPERS\sorts\MergeSort.dm" @@ -674,6 +679,7 @@ #include "code\datums\elements\regenerator.dm" #include "code\datums\elements\rust.dm" #include "code\datums\elements\squish.dm" +#include "code\datums\elements\undertile.dm" #include "code\datums\elements\update_icon_blocker.dm" #include "code\datums\helper_datums\events.dm" #include "code\datums\helper_datums\getrev.dm" @@ -816,7 +822,6 @@ #include "code\datums\wounds\slash.dm" #include "code\datums\wounds\scars\_scars.dm" #include "code\game\alternate_appearance.dm" -#include "code\game\atoms.dm" #include "code\game\atoms_movable.dm" #include "code\game\communications.dm" #include "code\game\data_huds.dm" @@ -838,6 +843,11 @@ #include "code\game\area\areas\ruins\lavaland.dm" #include "code\game\area\areas\ruins\space.dm" #include "code\game\area\areas\ruins\templates.dm" +#include "code\game\atom\_atom.dm" +#include "code\game\atom\atom_invisibility.dm" +#include "code\game\atom\atom_materials.dm" +#include "code\game\atom\atom_orbit.dm" +#include "code\game\atom\atom_tool_acts.dm" #include "code\game\gamemodes\events.dm" #include "code\game\gamemodes\game_mode.dm" #include "code\game\gamemodes\objective.dm" diff --git a/yogstation/code/game/objects/structures/bar_stuff/bar_stuff.dm b/yogstation/code/game/objects/structures/bar_stuff/bar_stuff.dm index 9cf4a684cbf4..a516fed1e226 100644 --- a/yogstation/code/game/objects/structures/bar_stuff/bar_stuff.dm +++ b/yogstation/code/game/objects/structures/bar_stuff/bar_stuff.dm @@ -49,7 +49,8 @@ turf/open/floor/plasteel/ameridiner desc = "A counter with a red and black motif." icon = 'yogstation/icons/obj/smooth_structures/ameritable.dmi' icon_state = "table" - smooth = SMOOTH_FALSE + smoothing_flags = NONE + smoothing_groups = null canSmoothWith = null /obj/structure/table/american/end diff --git a/yogstation/code/game/objects/structures/tables_racks.dm b/yogstation/code/game/objects/structures/tables_racks.dm index 01d65350de98..ef7917503157 100644 --- a/yogstation/code/game/objects/structures/tables_racks.dm +++ b/yogstation/code/game/objects/structures/tables_racks.dm @@ -9,7 +9,8 @@ buildstack = /obj/item/stack/sheet/mineral/bananium framestackamount = 1 buildstackamount = 1 - canSmoothWith = list(/obj/structure/table/bananium) + smoothing_groups = SMOOTH_GROUP_BANANIUM_TABLES //Don't smooth with SMOOTH_GROUP_TABLES + canSmoothWith = SMOOTH_GROUP_BANANIUM_TABLES var/spam_flag = 0 /obj/structure/table/bananium/attackby(obj/item/W, mob/user, params) @@ -44,4 +45,4 @@ icon_state = "minibar_left" /obj/structure/rack/skeletal/right - icon_state = "minibar_right" \ No newline at end of file + icon_state = "minibar_right" diff --git a/yogstation/code/game/objects/structures/window.dm b/yogstation/code/game/objects/structures/window.dm index dc3bee66c1e1..2441012f083e 100644 --- a/yogstation/code/game/objects/structures/window.dm +++ b/yogstation/code/game/objects/structures/window.dm @@ -50,9 +50,10 @@ max_integrity = 50 fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/plasma/fulltile, /obj/structure/window/plasma/reinforced/fulltile, /obj/structure/window/bananium/fulltile) + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE glass_amount = 2 /obj/structure/window/bananium/fulltile/unanchored - anchored = FALSE \ No newline at end of file + anchored = FALSE diff --git a/yogstation/code/game/turfs/simulated/floor/fancy_floor.dm b/yogstation/code/game/turfs/simulated/floor/fancy_floor.dm index 72f5c3483f84..c9e42ab68e0e 100644 --- a/yogstation/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/yogstation/code/game/turfs/simulated/floor/fancy_floor.dm @@ -1,24 +1,28 @@ /turf/open/floor/ballpit desc = "A bunch of balls compressed together into a tile. Fun for the whole family!" - smooth = SMOOTH_TRUE | SMOOTH_BORDER | SMOOTH_MORE - canSmoothWith = list(/turf/open/floor/ballpit) icon = 'yogstation/icons/turf/floors/ballpit_smooth.dmi' icon_state = "smooth" + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER + smoothing_groups = SMOOTH_GROUP_TURF_BALLPIT + canSmoothWith = SMOOTH_GROUP_TURF_BALLPIT /turf/open/floor/carpet/purple icon = 'goon/icons/turfs/carpet_purple.dmi' floor_tile = /obj/item/stack/tile/carpet/purple - canSmoothWith = list(/turf/open/floor/carpet/purple) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_PURPLE + canSmoothWith = SMOOTH_GROUP_CARPET_PURPLE /turf/open/floor/carpet/green icon = 'goon/icons/turfs/carpet_green.dmi' floor_tile = /obj/item/stack/tile/carpet/green - canSmoothWith = list(/turf/open/floor/carpet/green) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_GREEN + canSmoothWith = SMOOTH_GROUP_CARPET_GREEN /turf/open/floor/carpet/blue icon = 'goon/icons/turfs/carpet_blue.dmi' floor_tile = /obj/item/stack/tile/carpet/blue - canSmoothWith = list(/turf/open/floor/carpet/blue) + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_CARPET_BLUE + canSmoothWith = SMOOTH_GROUP_CARPET_BLUE /turf/open/floor/plasteel/stairs/goon icon = 'goon/icons/turfs/floors.dmi' diff --git a/yogstation/code/game/turfs/simulated/minerals.dm b/yogstation/code/game/turfs/simulated/minerals.dm index 6cb7132a317d..55595332209f 100644 --- a/yogstation/code/game/turfs/simulated/minerals.dm +++ b/yogstation/code/game/turfs/simulated/minerals.dm @@ -13,18 +13,18 @@ defer_change = 1 /turf/closed/mineral/dilithium/volcanic/hard - smooth_icon = 'icons/turf/smoothrocks_hard.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks_hard.dmi', 'icons/turf/mining.dmi') hardness = 2 /turf/closed/mineral/dilithium/volcanic/hard/harder - smooth_icon = 'icons/turf/smoothrocks.dmi' + icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') mineralAmt = 5 color = "#eb9877" hardness = 3 /turf/closed/mineral/dilithium/ice environment_type = "snow_cavern" - smooth_icon = 'icons/turf/walls/icerock_wall.dmi' + icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi') turf_type = /turf/open/floor/plating/asteroid/snow/ice baseturfs = /turf/open/floor/plating/asteroid/snow/ice initial_gas_mix = FROZEN_ATMOS From 558feacf2505b8e8be55a7f37207086aa22cc4a2 Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:44:05 -0500 Subject: [PATCH 02/15] L --- code/game/machinery/magnet.dm | 1 + code/game/objects/objs.dm | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 1c5e2c5b055c..ef5c5de0d74e 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -29,6 +29,7 @@ /obj/machinery/magnetic_module/Initialize(mapload) ..() + AddElement(/datum/element/undertile, tile_overlay = tile_overlay, use_anchor = TRUE) var/turf/T = loc hide(T.intact) center = T diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 686438b8b836..95933f1c03a7 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -226,9 +226,6 @@ if(istype(M) && M.client && M.machine == src) src.attack_self(M) -/obj/proc/hide(h) - return - /obj/singularity_pull(S, current_size) ..() if(!anchored || current_size >= STAGE_FIVE) From 19aab784e350ff253d5fdcef2ebf85409681830c Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 01:52:18 -0500 Subject: [PATCH 03/15] the game opens ig --- _maps/map_files/generic/CentCom.dmm | 30 ++----- code/__DEFINES/subsystems.dm | 4 +- code/__DEFINES/traits.dm | 2 +- code/__HELPERS/_lists.dm | 12 +++ code/__HELPERS/bitflag_lists.dm | 28 +++++++ code/controllers/subsystem/atoms.dm | 63 +++++++++------ code/controllers/subsystem/fire_burning.dm | 2 +- code/controllers/subsystem/icon_smooth.dm | 74 +++++++++++++----- code/controllers/subsystem/minor_mapping.dm | 19 +++-- code/datums/elements/undertile.dm | 4 +- code/game/area/areas.dm | 8 +- code/game/area/areas/shuttles.dm | 16 ++-- code/game/machinery/magnet.dm | 12 +-- code/game/machinery/navbeacon.dm | 13 +-- code/game/machinery/pipe/construction.dm | 3 +- code/game/mecha/equipment/tools/work_tools.dm | 2 +- .../fluid_spread/effects_foam.dm | 2 +- code/game/objects/items/RCL.dm | 6 +- code/game/objects/items/devices/powersink.dm | 2 +- .../objects/items/devices/pressureplates.dm | 28 +++---- code/game/objects/items/devices/scanners.dm | 5 +- code/game/objects/items/puzzle_pieces.dm | 6 ++ code/game/objects/items/storage/backpack.dm | 13 --- code/game/objects/obj_defense.dm | 6 +- code/game/objects/structures/aliens.dm | 1 + code/game/objects/structures/tables_racks.dm | 30 +++---- code/game/objects/structures/window.dm | 8 +- .../simulated/floor/plating/misc_plating.dm | 31 ++++---- code/game/turfs/simulated/lava.dm | 4 + code/game/turfs/turf.dm | 6 +- code/modules/antagonists/blob/blob_report.dm | 8 +- .../atmospherics/machinery/atmosmachinery.dm | 15 ++-- .../binary_devices/binary_devices.dm | 4 - .../components/binary_devices/dp_vent_pump.dm | 2 +- .../machinery/components/components_base.dm | 22 ++++-- .../unary_devices/outlet_injector.dm | 2 +- .../components/unary_devices/passive_vent.dm | 1 + .../unary_devices/portables_connector.dm | 4 +- .../components/unary_devices/unary_devices.dm | 4 - .../components/unary_devices/vent_pump.dm | 2 +- .../components/unary_devices/vent_scrubber.dm | 2 +- .../machinery/pipes/heat_exchange/he_pipes.dm | 13 ++- .../machinery/pipes/layermanifold.dm | 3 - .../atmospherics/machinery/pipes/mapping.dm | 8 +- .../atmospherics/machinery/pipes/pipes.dm | 16 ---- code/modules/holodeck/computer.dm | 2 +- code/modules/mapping/reader.dm | 11 +-- code/modules/mob/dead/observer/observer.dm | 7 +- .../living/simple_animal/friendly/mouse.dm | 2 +- .../mob/living/simple_animal/hostile/rat.dm | 2 +- code/modules/power/cable.dm | 21 ++--- code/modules/power/terminal.dm | 10 --- .../recycling/disposal/construction.dm | 10 --- code/modules/recycling/disposal/pipe.dm | 12 --- code/modules/shuttle/on_move.dm | 23 ------ icons/turf/floors/rocky_ash.dmi | Bin 3004 -> 8601 bytes yogstation.dme | 1 + .../atmospherics/machinery/pipes/bluespace.dm | 5 +- 58 files changed, 292 insertions(+), 360 deletions(-) create mode 100644 code/__HELPERS/bitflag_lists.dm diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 60a5237310a9..d5cb76726efa 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -6861,11 +6861,7 @@ /area/centcom/testchamber) "apy" = ( /obj/machinery/smartfridge, -/turf/closed/indestructible{ - icon = 'icons/turf/walls/wood_wall.dmi'; - icon_state = "wood"; - smooth = 1 - }, +/turf/closed/indestructible/rock/wood, /area/centcom/holding) "apz" = ( /obj/structure/rack, @@ -13119,11 +13115,7 @@ /area/yogs/infiltrator_base/outside) "aBV" = ( /obj/machinery/chem_dispenser/drinks/beer, -/turf/closed/indestructible{ - icon = 'icons/turf/walls/wood_wall.dmi'; - icon_state = "wood"; - smooth = 1 - }, +/turf/closed/indestructible/rock/wood, /area/centcom/holding) "aBW" = ( /obj/machinery/atmospherics/components/binary/pump/on{ @@ -14652,11 +14644,7 @@ /area/wizard_station) "aED" = ( /obj/machinery/vending/boozeomat, -/turf/closed/indestructible{ - icon = 'icons/turf/walls/wood_wall.dmi'; - icon_state = "wood"; - smooth = 1 - }, +/turf/closed/indestructible/rock/wood, /area/centcom/holding) "aEE" = ( /obj/structure/closet/crate/bin, @@ -19174,11 +19162,7 @@ }, /area/holodeck/rec_center/medical) "aNd" = ( -/turf/closed/indestructible{ - icon = 'icons/turf/walls/wood_wall.dmi'; - icon_state = "wood"; - smooth = 1 - }, +/turf/closed/indestructible/rock/wood, /area/centcom/holding) "aNe" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ @@ -21031,11 +21015,7 @@ /area/centcom/testchamber) "aQT" = ( /obj/machinery/chem_dispenser/drinks, -/turf/closed/indestructible{ - icon = 'icons/turf/walls/wood_wall.dmi'; - icon_state = "wood"; - smooth = 1 - }, +/turf/closed/indestructible/rock/wood, /area/centcom/holding) "aQU" = ( /obj/structure/table/reinforced, diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 944f6ef36c98..b92b3a398214 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -188,11 +188,11 @@ #define FIRE_PRIORITY_THROWING 25 #define FIRE_PRIORITY_SPACEDRIFT 30 #define FIRE_PRIORITY_FIELDS 30 -#define FIRE_PRIOTITY_SMOOTHING 35 +#define FIRE_PRIORITY_SMOOTHING 35 #define FIRE_PRIORITY_NETWORKS 40 #define FIRE_PRIORITY_OBJ 40 #define FIRE_PRIORITY_ACID 40 -#define FIRE_PRIOTITY_BURNING 40 +#define FIRE_PRIORITY_BURNING 40 #define FIRE_PRIORITY_DEFAULT 50 #define FIRE_PRIORITY_PARALLAX 65 #define FIRE_PRIORITY_INSTRUMENTS 80 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 35baf4056ada..0fb442556932 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -309,7 +309,7 @@ // item traits #define TRAIT_NODROP "nodrop" -/// Visible on t-ray scanners if the atom/var/level == 1 +/// Visible on t-ray scanners. #define TRAIT_T_RAY_VISIBLE "t-ray-visible" /// Properly wielded two handed item #define TRAIT_WIELDED "wielded" diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index d5cd95b2db6b..4324f060a4d2 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -737,3 +737,15 @@ ///sort any value in a list /proc/sort_list(list/list_to_sort, cmp=/proc/cmp_text_asc) return sortTim(list_to_sort.Copy(), cmp) + +/// Runtimes if the passed in list is not sorted +/proc/assert_sorted(list/list, name, cmp = GLOBAL_PROC_REF(cmp_numeric_asc)) + var/last_value = list[1] + + for (var/index in 2 to list.len) + var/value = list[index] + + if (call(cmp)(value, last_value) < 0) + stack_trace("[name] is not sorted. value at [index] ([value]) is in the wrong place compared to the previous value of [last_value] (when compared to by [cmp])") + + last_value = value diff --git a/code/__HELPERS/bitflag_lists.dm b/code/__HELPERS/bitflag_lists.dm new file mode 100644 index 000000000000..48e35bd6b913 --- /dev/null +++ b/code/__HELPERS/bitflag_lists.dm @@ -0,0 +1,28 @@ +GLOBAL_LIST_EMPTY(bitflag_lists) + +/** + * System for storing bitflags past the 24 limit, making use of an associative list. + * + * Macro converts a list of integers into an associative list of bitflag entries for quicker comparison. + * Example: list(0, 4, 26, 32)) => list( "0" = ( (1<<0) | (1<<4) ), "1" = ( (1<<2) | (1<<8) ) ) + * Lists are cached into a global list of lists to avoid identical duplicates. + * This system makes value comparisons faster than pairing every element of one list with every element of the other for evaluation. + * + * Arguments: + * * target - List of integers. + */ +#define SET_SMOOTHING_GROUPS(target) \ + do { \ + var/txt_signature = target; \ + if(isnull((target = GLOB.bitflag_lists[txt_signature]))) { \ + var/list/new_bitflag_list = list(); \ + var/list/decoded = UNWRAP_SMOOTHING_GROUPS(txt_signature, decoded); \ + for(var/value in decoded) { \ + if (value < 0) { \ + value = MAX_S_TURF + 1 + abs(value); \ + } \ + new_bitflag_list["[round(value / 24)]"] |= (1 << (value % 24)); \ + }; \ + target = GLOB.bitflag_lists[txt_signature] = new_bitflag_list; \ + }; \ + } while (FALSE) diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index e7e6275cd8bc..c4d493d2dff9 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -1,3 +1,5 @@ +#define SUBSYSTEM_INIT_SOURCE "subsystem init" + #define BAD_INIT_QDEL_BEFORE 1 #define BAD_INIT_DIDNT_INIT 2 #define BAD_INIT_SLEPT 4 @@ -9,9 +11,10 @@ SUBSYSTEM_DEF(atoms) flags = SS_NO_FIRE loading_points = 30 SECONDS // Yogs -- smarter loading times - var/old_initialized - /// A count of how many initalize changes we've made. We want to prevent old_initialize being overriden by some other value, breaking init code - var/initialized_changed = 0 + /// A stack of list(source, desired initialized state) + /// We read the source of init changes from the last entry, and assert that all changes will come with a reset + var/list/initialized_state = list() + var/base_initialized var/list/late_loaders = list() @@ -39,11 +42,11 @@ SUBSYSTEM_DEF(atoms) if(initialized == INITIALIZATION_INSSATOMS) return - set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD) + set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, SUBSYSTEM_INIT_SOURCE) // This may look a bit odd, but if the actual atom creation runtimes for some reason, we absolutely need to set initialized BACK CreateAtoms(atoms) - clear_tracked_initalize() + clear_tracked_initalize(SUBSYSTEM_INIT_SOURCE) if(late_loaders.len) for(var/I in 1 to late_loaders.len) @@ -136,31 +139,41 @@ SUBSYSTEM_DEF(atoms) return qdeleted || QDELING(A) -/datum/controller/subsystem/atoms/proc/map_loader_begin() - set_tracked_initalized(INITIALIZATION_INSSATOMS) - -/datum/controller/subsystem/atoms/proc/map_loader_stop() - clear_tracked_initalize() - -/// Use this to set initialized to prevent error states where old_initialized is overriden. It keeps happening and it's cheesing me off -/datum/controller/subsystem/atoms/proc/set_tracked_initalized(value) - if(!initialized_changed) - old_initialized = initialized - initialized = value - else - stack_trace("We started maploading while we were already maploading. You doing something odd?") - initialized_changed += 1 +/datum/controller/subsystem/atoms/proc/map_loader_begin(source) + set_tracked_initalized(INITIALIZATION_INSSATOMS, source) + +/datum/controller/subsystem/atoms/proc/map_loader_stop(source) + clear_tracked_initalize(source) + +/// Use this to set initialized to prevent error states where the old initialized is overriden, and we end up losing all context +/// Accepts a state and a source, the most recent state is used, sources exist to prevent overriding old values accidentially +/datum/controller/subsystem/atoms/proc/set_tracked_initalized(state, source) + if(!length(initialized_state)) + base_initialized = initialized + initialized_state += list(list(source, state)) + initialized = state + +/datum/controller/subsystem/atoms/proc/clear_tracked_initalize(source) + for(var/i in length(initialized_state) to 1) + if(initialized_state[i][1] == source) + initialized_state.Cut(i, i+1) + break + + if(!length(initialized_state)) + initialized = base_initialized + base_initialized = INITIALIZATION_INNEW_REGULAR + return + initialized = initialized_state[length(initialized_state)][2] -/datum/controller/subsystem/atoms/proc/clear_tracked_initalize() - initialized_changed -= 1 - if(!initialized_changed) - initialized = old_initialized +/// Returns TRUE if anything is currently being initialized +/datum/controller/subsystem/atoms/proc/initializing_something() + return length(initialized_state) > 1 /datum/controller/subsystem/atoms/Recover() initialized = SSatoms.initialized if(initialized == INITIALIZATION_INNEW_MAPLOAD) InitializeAtoms() - old_initialized = SSatoms.old_initialized + initialized_state = SSatoms.initialized_state BadInitializeCalls = SSatoms.BadInitializeCalls /datum/controller/subsystem/atoms/proc/setupGenetics() @@ -217,3 +230,5 @@ SUBSYSTEM_DEF(atoms) #undef BAD_INIT_DIDNT_INIT #undef BAD_INIT_SLEPT #undef BAD_INIT_NO_HINT + +#undef SUBSYSTEM_INIT_SOURCE diff --git a/code/controllers/subsystem/fire_burning.dm b/code/controllers/subsystem/fire_burning.dm index 2a4d48f14f26..df138faf013e 100644 --- a/code/controllers/subsystem/fire_burning.dm +++ b/code/controllers/subsystem/fire_burning.dm @@ -1,6 +1,6 @@ SUBSYSTEM_DEF(fire_burning) name = "Fire Burning" - priority = FIRE_PRIOTITY_BURNING + priority = FIRE_PRIORITY_BURNING flags = SS_NO_INIT|SS_BACKGROUND runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME diff --git a/code/controllers/subsystem/icon_smooth.dm b/code/controllers/subsystem/icon_smooth.dm index 338f50b2021c..4d5f3069eca6 100644 --- a/code/controllers/subsystem/icon_smooth.dm +++ b/code/controllers/subsystem/icon_smooth.dm @@ -2,43 +2,77 @@ SUBSYSTEM_DEF(icon_smooth) name = "Icon Smoothing" init_order = INIT_ORDER_ICON_SMOOTHING wait = 1 - priority = FIRE_PRIOTITY_SMOOTHING + priority = FIRE_PRIORITY_SMOOTHING flags = SS_TICKER - loading_points = 3.3 SECONDS // Yogs -- loading times - + ///Blueprints assemble an image of what pipes/manifolds/wires look like on initialization, and thus should be taken after everything's been smoothed + var/list/blueprint_queue = list() var/list/smooth_queue = list() var/list/deferred = list() /datum/controller/subsystem/icon_smooth/fire() - var/list/cached = smooth_queue - while(cached.len) - var/atom/A = cached[cached.len] - cached.len-- - if (A.flags_1 & INITIALIZED_1) - A.smooth_icon() + // We do not want to smooth icons of atoms whose neighbors are not initialized yet, + // this causes runtimes. + // Icon smoothing SS runs after atoms, so this only happens for something like shuttles. + // This kind of map loading shouldn't take too long, so the delay is not a problem. + if (SSatoms.initializing_something()) + return + + var/list/smooth_queue_cache = smooth_queue + while(length(smooth_queue_cache)) + var/atom/smoothing_atom = smooth_queue_cache[length(smooth_queue_cache)] + smooth_queue_cache.len-- + if(QDELETED(smoothing_atom) || !(smoothing_atom.smoothing_flags & SMOOTH_QUEUED)) + continue + if(smoothing_atom.flags_1 & INITIALIZED_1) + smoothing_atom.smooth_icon() else - deferred += A + deferred += smoothing_atom if (MC_TICK_CHECK) return - if (!cached.len) + if (!length(smooth_queue_cache)) if (deferred.len) smooth_queue = deferred - deferred = cached + deferred = smooth_queue_cache else - can_fire = 0 + can_fire = FALSE /datum/controller/subsystem/icon_smooth/Initialize() - smooth_zlevel(1,TRUE) - smooth_zlevel(2,TRUE) - var/queue = smooth_queue + var/list/queue = smooth_queue smooth_queue = list() - for(var/V in queue) - var/atom/A = V - if(!A || A.z <= 2) + + while(length(queue)) + var/atom/smoothing_atom = queue[length(queue)] + queue.len-- + if(QDELETED(smoothing_atom) || !(smoothing_atom.smoothing_flags & SMOOTH_QUEUED) || !smoothing_atom.z) continue - A.smooth_icon() + smoothing_atom.smooth_icon() CHECK_TICK + queue = blueprint_queue + blueprint_queue = null + + for(var/atom/movable/movable_item as anything in queue) + if(!isturf(movable_item.loc)) + continue + var/turf/item_loc = movable_item.loc + item_loc.add_blueprints(movable_item) + return SS_INIT_SUCCESS + + +/datum/controller/subsystem/icon_smooth/proc/add_to_queue(atom/thing) + if(thing.smoothing_flags & SMOOTH_QUEUED) + return + thing.smoothing_flags |= SMOOTH_QUEUED + smooth_queue += thing + if(!can_fire) + can_fire = TRUE + +/datum/controller/subsystem/icon_smooth/proc/remove_from_queues(atom/thing) + thing.smoothing_flags &= ~SMOOTH_QUEUED + smooth_queue -= thing + if(blueprint_queue) + blueprint_queue -= thing + deferred -= thing diff --git a/code/controllers/subsystem/minor_mapping.dm b/code/controllers/subsystem/minor_mapping.dm index d5d385170877..cac72a1d00d3 100644 --- a/code/controllers/subsystem/minor_mapping.dm +++ b/code/controllers/subsystem/minor_mapping.dm @@ -37,14 +37,21 @@ SUBSYSTEM_DEF(minor_mapping) num_mice -= 1 M = null -/datum/controller/subsystem/minor_mapping/proc/place_satchels(amount=10) +/datum/controller/subsystem/minor_mapping/proc/place_satchels(satchel_amount = 10) var/list/turfs = find_satchel_suitable_turfs() + ///List of areas where satchels should not be placed. + var/list/blacklisted_area_types = list( + /area/holodeck, + ) - while(turfs.len && amount > 0) - var/turf/T = pick_n_take(turfs) - var/obj/item/storage/backpack/satchel/flat/S = new(T) - S.hide(intact=TRUE) - amount-- + while(turfs.len && satchel_amount > 0) + var/turf/turf = pick_n_take(turfs) + if(is_type_in_list(get_area(turf), blacklisted_area_types)) + continue + var/obj/item/storage/backpack/satchel/flat/flat_satchel = new(turf) + + SEND_SIGNAL(flat_satchel, COMSIG_OBJ_HIDE, turf.underfloor_accessibility) + satchel_amount-- /proc/find_exposed_wires() diff --git a/code/datums/elements/undertile.dm b/code/datums/elements/undertile.dm index b540e0e3ae1d..de055bb0bd75 100644 --- a/code/datums/elements/undertile.dm +++ b/code/datums/elements/undertile.dm @@ -50,7 +50,7 @@ T.add_overlay(tile_overlay) if(use_anchor) - source.set_anchored(TRUE) + source.anchored = TRUE if(underfloor_accessibility < UNDERFLOOR_VISIBLE) if(use_alpha) @@ -73,7 +73,7 @@ source.alpha = initial(source.alpha) if(use_anchor) - source.set_anchored(FALSE) + source.anchored = FALSE /datum/element/undertile/Detach(atom/movable/source, visibility_trait, invisibility_level = INVISIBILITY_MAXIMUM) . = ..() diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index afab00c7f397..efbd67433b71 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -4,7 +4,6 @@ * A grouping of tiles into a logical space, mostly used by map editors */ /area - level = null name = "Space" icon = 'icons/turf/areas.dmi' icon_state = "unknown" @@ -115,11 +114,13 @@ var/list/cameras var/list/firealarms var/list/airalarms + + ///Typepath to limit the areas (subtypes included) that atoms in this area can smooth with. Used for shuttles. + var/area/area_limited_icon_smoothing + var/firedoors_last_closed_on = 0 /// Can the Xenobio management console transverse this area by default? var/xenobiology_compatible = FALSE - /// typecache to limit the areas that atoms in this area can smooth with, used for shuttles IIRC - var/list/canSmoothWithAreas var/minimap_color = null // if null, chooses random one @@ -197,7 +198,6 @@ GLOBAL_LIST_EMPTY(teleportlocs) layer = AREA_LAYER uid = ++global_uid map_name = name // Save the initial (the name set in the map) name of the area. - canSmoothWithAreas = typecacheof(canSmoothWithAreas) add_delta_areas() diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm index 7d7bdffa26eb..ca3186d59540 100644 --- a/code/game/area/areas/shuttles.dm +++ b/code/game/area/areas/shuttles.dm @@ -11,15 +11,12 @@ icon_state = "shuttle" // Loading the same shuttle map at a different time will produce distinct area instances. unique = FALSE - ///list of miners & their mining points from gems to be given once all exports are processed, used by supply shuttles - var/list/gem_payout = list() + area_limited_icon_smoothing = /area/shuttle lighting_colour_tube = "#fff0dd" lighting_colour_bulb = "#ffe1c1" -/area/shuttle/Initialize(mapload) - if(!canSmoothWithAreas) - canSmoothWithAreas = type - . = ..() + ///list of miners & their mining points from gems to be given once all exports are processed, used by supply shuttles + var/list/gem_payout = list() /area/shuttle/PlaceOnTopReact(list/new_baseturfs, turf/fake_turf_type, flags) . = ..() @@ -36,7 +33,7 @@ name = "Syndicate Infiltrator" blob_allowed = FALSE ambience_index = AMBIENCE_DANGER - canSmoothWithAreas = /area/shuttle/syndicate + area_limited_icon_smoothing = /area/shuttle/syndicate /area/shuttle/syndicate/bridge name = "Syndicate Infiltrator Control" @@ -61,7 +58,6 @@ name = "Pirate Shuttle" blob_allowed = FALSE requires_power = TRUE - canSmoothWithAreas = /area/shuttle/pirate ////////////////////////////Bounty Hunter Shuttles//////////////////////////// @@ -69,7 +65,6 @@ name = "Hunter Shuttle" dynamic_lighting = DYNAMIC_LIGHTING_DISABLED blob_allowed = FALSE - canSmoothWithAreas = /area/shuttle/hunter ////////////////////////////White Ship//////////////////////////// @@ -77,7 +72,7 @@ name = "Abandoned Ship" blob_allowed = FALSE requires_power = TRUE - canSmoothWithAreas = /area/shuttle/abandoned + area_limited_icon_smoothing = /area/shuttle/abandoned /area/shuttle/abandoned/bridge name = "Abandoned Ship Bridge" @@ -149,6 +144,7 @@ /area/shuttle/escape name = "Emergency Shuttle" + area_limited_icon_smoothing = /area/shuttle/escape /area/shuttle/escape/backup name = "Backup Emergency Shuttle" diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index ef5c5de0d74e..b25a7c47d683 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -9,7 +9,6 @@ icon_state = "floor_magnet-f" name = "electromagnetic generator" desc = "A device that uses station power to create points of magnetic energy." - level = 1 // underfloor layer = LOW_OBJ_LAYER use_power = IDLE_POWER_USE idle_power_usage = 50 @@ -29,10 +28,8 @@ /obj/machinery/magnetic_module/Initialize(mapload) ..() - AddElement(/datum/element/undertile, tile_overlay = tile_overlay, use_anchor = TRUE) - var/turf/T = loc - hide(T.intact) - center = T + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) + center = loc SSradio.add_object(src, freq, RADIO_MAGNETS) return INITIALIZE_HINT_LATELOAD @@ -44,11 +41,6 @@ center = null return ..() -// update the invisibility and icon -/obj/machinery/magnetic_module/hide(intact) - invisibility = intact ? INVISIBILITY_MAXIMUM : 0 - update_appearance(UPDATE_ICON) - // update the icon_state /obj/machinery/magnetic_module/update_icon_state() . = ..() diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index f26d53db9c00..a43b57a85e8d 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -7,7 +7,6 @@ icon_state = "navbeacon0-f" name = "navigation beacon" desc = "A radio beacon used for bot navigation." - level = 1 // underfloor layer = UNDER_CATWALK max_integrity = 500 armor = list(MELEE = 70, BULLET = 70, LASER = 70, ENERGY = 70, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 80) @@ -26,7 +25,6 @@ set_codes() - var/turf/T = loc if(codes["patrol"]) if(!GLOB.navbeacons["[z]"]) GLOB.navbeacons["[z]"] = list() @@ -68,13 +66,6 @@ else codes[e] = "1" - -// called when turf state changes -// hide the object if turf is intact -/obj/machinery/navbeacon/hide(intact) - invisibility = intact ? INVISIBILITY_MAXIMUM : 0 - update_appearance(UPDATE_ICON) - // update the icon_state /obj/machinery/navbeacon/update_icon_state() . = ..() @@ -89,7 +80,7 @@ /obj/machinery/navbeacon/attackby(obj/item/I, mob/user, params) var/turf/T = loc - if(T.intact) + if(T.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE) return // prevent intraction when T-scanner revealed if(I.tool_behaviour == TOOL_SCREWDRIVER) @@ -122,7 +113,7 @@ . = ..() var/ai = isAI(user) var/turf/T = loc - if(T.intact) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) return // prevent intraction when T-scanner revealed if(!open && !ai) // can't alter controls if not open, unless you're an AI diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 33c9676d86dc..ad609496b776 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -18,8 +18,9 @@ Buildable meters icon_state = "simple" item_state = "buildpipe" w_class = WEIGHT_CLASS_NORMAL - level = 2 + ///Piping layer that we are going to be on var/piping_layer = PIPING_LAYER_DEFAULT + ///Type of pipe-object made, selected from the RPD var/RPD_type var/disposable = TRUE // yogs diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index 2fe788ba5dd0..855df49cacb3 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -470,7 +470,7 @@ if(!T.broken && !T.burnt) new T.floor_tile(T) T.make_plating() - return !new_turf.intact + return new_turf.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE /obj/item/mecha_parts/mecha_equipment/cable_layer/proc/layCable(turf/new_turf) if(equip_ready || !istype(new_turf) || !dismantle_floor(new_turf)) diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm index 5621a07d5f1a..8e013e9e167f 100644 --- a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm +++ b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm @@ -88,7 +88,7 @@ if(object == src) continue if(isturf(object.loc)) - if(turf_location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(object, TRAIT_T_RAY_VISIBLE)) + if(location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(object, TRAIT_T_RAY_VISIBLE)) continue reagents.reaction(object, TOUCH|VAPOR, fraction) diff --git a/code/game/objects/items/RCL.dm b/code/game/objects/items/RCL.dm index 23c6ccc93454..deaa3a2a02b1 100644 --- a/code/game/objects/items/RCL.dm +++ b/code/game/objects/items/RCL.dm @@ -190,7 +190,7 @@ if(last) if(get_dist(last, user) == 1) //hacky, but it works var/turf/T = get_turf(user) - if(T.intact || !T.can_have_cabling()) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !T.can_have_cabling()) last = null return if(get_dir(last, user) == last.d2) @@ -215,7 +215,7 @@ return T = get_turf(user) - if(T.intact || !T.can_have_cabling()) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !T.can_have_cabling()) return for(var/obj/structure/cable/C in T) @@ -275,7 +275,7 @@ return var/turf/T = get_turf(user) - if(T.intact || !T.can_have_cabling()) + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !T.can_have_cabling()) return loaded.color = colors[current_color_index] diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 7135a929bc4e..ce10148b852f 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -64,7 +64,7 @@ if(I.tool_behaviour == TOOL_SCREWDRIVER) if(mode == DISCONNECTED) var/turf/T = loc - if(isturf(T) && !T.intact) + if(isturf(T) && T.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE) attached = locate() in T if(!attached) to_chat(user, span_warning("This device must be placed over an exposed, powered cable node!")) diff --git a/code/game/objects/items/devices/pressureplates.dm b/code/game/objects/items/devices/pressureplates.dm index d6ec1ef6c373..84414419d2fd 100644 --- a/code/game/objects/items/devices/pressureplates.dm +++ b/code/game/objects/items/devices/pressureplates.dm @@ -5,7 +5,6 @@ icon = 'icons/obj/puzzle_small.dmi' item_state = "flash" icon_state = "pressureplate" - level = 1 layer = LOW_OBJ_LAYER var/trigger_mob = TRUE var/trigger_item = FALSE @@ -23,6 +22,7 @@ var/can_trigger = TRUE var/trigger_delay = 10 var/protected = FALSE + var/undertile_pressureplate = TRUE /obj/item/pressure_plate/Initialize(mapload) . = ..() @@ -31,8 +31,10 @@ sigdev = new sigdev.code = roundstart_signaller_code sigdev.frequency = roundstart_signaller_freq - if(isopenturf(loc)) - AddElement(/datum/element/undertile, tile_overlay = tile_overlay, use_anchor = TRUE) + + if(undertile_pressureplate) + AddElement(/datum/element/undertile, tile_overlay = tile_overlay, use_anchor = TRUE) + RegisterSignal(src, COMSIG_OBJ_HIDE, PROC_REF(ToggleActive)) /obj/item/pressure_plate/Crossed(atom/movable/AM) . = ..() @@ -77,20 +79,8 @@ else to_chat(user, span_notice("You turn [src] off.")) -/obj/item/pressure_plate/hide(yes) - if(yes) - invisibility = INVISIBILITY_MAXIMUM - anchored = TRUE - icon_state = null - active = TRUE - can_trigger = TRUE - if(tile_overlay) - loc.add_overlay(tile_overlay) - else - invisibility = initial(invisibility) - anchored = FALSE - icon_state = initial(icon_state) - active = FALSE - if(tile_overlay) - loc.overlays -= tile_overlay +///Called from COMSIG_OBJ_HIDE to toggle the active part, because yeah im not making a special exception on the element to support it +/obj/item/pressure_plate/proc/ToggleActive(datum/source, underfloor_accessibility) + SIGNAL_HANDLER + active = underfloor_accessibility < UNDERFLOOR_VISIBLE diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 39633ed98f4d..4950ae7cd3da 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -69,10 +69,7 @@ GENE SCANNER return var/list/t_ray_images = list() for(var/obj/O in orange(distance, viewer) ) - if(O.level != 1) - continue - - if(O.invisibility == INVISIBILITY_MAXIMUM || HAS_TRAIT(O, TRAIT_T_RAY_VISIBLE)) + if(HAS_TRAIT(O, TRAIT_T_RAY_VISIBLE)) var/image/I = new(loc = get_turf(O)) var/mutable_appearance/MA = new(O) MA.alpha = 128 diff --git a/code/game/objects/items/puzzle_pieces.dm b/code/game/objects/items/puzzle_pieces.dm index 977744dfae41..b098d3320d53 100644 --- a/code/game/objects/items/puzzle_pieces.dm +++ b/code/game/objects/items/puzzle_pieces.dm @@ -129,9 +129,15 @@ trigger_delay = 10 protected = TRUE resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF + undertile_pressureplate = FALSE var/reward = /obj/item/reagent_containers/food/snacks/cookie var/claimed = FALSE +/obj/item/pressure_plate/hologrid/Initialize(mapload) + . = ..() + if(undertile_pressureplate) + AddElement(/datum/element/undertile, tile_overlay = tile_overlay, use_anchor = FALSE) //we remove use_anchor here, so it ALWAYS stays anchored + /obj/item/pressure_plate/hologrid/examine(mob/user) . = ..() if(claimed) diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 9d8244d815de..39ef72ac0864 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -315,7 +315,6 @@ desc = "A very slim satchel that can easily fit into tight spaces." icon_state = "satchel-flat" w_class = WEIGHT_CLASS_NORMAL //Can fit in backpacks itself. - level = 1 /obj/item/storage/backpack/satchel/flat/Initialize(mapload) . = ..() @@ -324,18 +323,6 @@ STR.max_combined_w_class = 15 STR.set_holdable(null, list(/obj/item/storage/backpack/satchel/flat)) //muh recursive backpacks) -/obj/item/storage/backpack/satchel/flat/hide(intact) - if(intact) - invisibility = INVISIBILITY_OBSERVER - anchored = TRUE //otherwise you can start pulling, cover it, and drag around an invisible backpack. - icon_state = "[initial(icon_state)]2" - ADD_TRAIT(src, TRAIT_T_RAY_VISIBLE, TRAIT_GENERIC) - else - invisibility = initial(invisibility) - anchored = FALSE - icon_state = initial(icon_state) - REMOVE_TRAIT(src, TRAIT_T_RAY_VISIBLE, TRAIT_GENERIC) - /obj/item/storage/backpack/satchel/flat/PopulateContents() var/datum/supply_pack/costumes_toys/randomised/contraband/C = new for(var/i in 1 to 2) diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 9b0f45b5b393..00e8c4dde120 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -102,7 +102,7 @@ /obj/blob_act(obj/structure/blob/B) if(isturf(loc)) var/turf/T = loc - if(T.intact && level == 1) //the blob doesn't destroy thing below the floor + if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(src, TRAIT_T_RAY_VISIBLE)) return take_damage(400, BRUTE, MELEE, 0, get_dir(src, B)) @@ -215,8 +215,8 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e ///Called when the obj is exposed to fire. /obj/fire_act(exposed_temperature, exposed_volume) if(isturf(loc)) - var/turf/T = loc - if(T.intact && level == 1) //fire can't damage things hidden below the floor. + var/turf/our_turf = loc + if(our_turf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(src, TRAIT_T_RAY_VISIBLE)) return if(exposed_temperature && !(resistance_flags & FIRE_PROOF)) take_damage(clamp(0.02 * exposed_temperature, 0, 20), BURN, FIRE, 0) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 3de6b5a2b975..0b9f71cc914b 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -63,6 +63,7 @@ smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_ALIEN_RESIN canSmoothWith = SMOOTH_GROUP_ALIEN_RESIN + var/resintype = null /obj/structure/alien/resin/Initialize(mapload) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index e7dc2d5b409c..6299ee2a76fb 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -359,7 +359,7 @@ /obj/structure/table/wood/fancy name = "fancy table" desc = "A standard metal table frame covered with an amazingly fancy, patterned cloth." - icon = 'icons/obj/structures.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table.dmi', 'icons/obj/structures.dmi') icon_state = "fancy_table" base_icon_state = "fancy_table" frame = /obj/structure/table_frame @@ -367,60 +367,52 @@ buildstack = /obj/item/stack/tile/carpet smoothing_groups = SMOOTH_GROUP_FANCY_WOOD_TABLES //Don't smooth with SMOOTH_GROUP_TABLES or SMOOTH_GROUP_WOOD_TABLES canSmoothWith = SMOOTH_GROUP_FANCY_WOOD_TABLES - var/smooth_icon = 'icons/obj/smooth_structures/fancy_table.dmi' // see Initialize(mapload) - -/obj/structure/table/wood/fancy/Initialize(mapload) - . = ..() - // Needs to be set dynamically because table smooth sprites are 32x34, - // which the editor treats as a two-tile-tall object. The sprites are that - // size so that the north/south corners look nice - examine the detail on - // the sprites in the editor to see why. - icon = smooth_icon /obj/structure/table/wood/fancy/black icon_state = "fancy_table_black" buildstack = /obj/item/stack/tile/carpet/black - smooth_icon = 'icons/obj/smooth_structures/fancy_table_black.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_black.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/exoticblue icon_state = "fancy_table_exoticblue" buildstack = /obj/item/stack/tile/carpet/exoticblue - smooth_icon = 'icons/obj/smooth_structures/fancy_table_exoticblue.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_exoticblue.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/cyan icon_state = "fancy_table_cyan" buildstack = /obj/item/stack/tile/carpet/cyan - smooth_icon = 'icons/obj/smooth_structures/fancy_table_cyan.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_cyan.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/exoticgreen icon_state = "fancy_table_exoticgreen" buildstack = /obj/item/stack/tile/carpet/exoticgreen - smooth_icon = 'icons/obj/smooth_structures/fancy_table_exoticgreen.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_exoticgreen.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/orange icon_state = "fancy_table_orange" buildstack = /obj/item/stack/tile/carpet/orange - smooth_icon = 'icons/obj/smooth_structures/fancy_table_orange.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_orange.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/exoticpurple icon_state = "fancy_table_exoticpurple" buildstack = /obj/item/stack/tile/carpet/exoticpurple - smooth_icon = 'icons/obj/smooth_structures/fancy_table_exoticpurple.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_exoticpurple.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/red icon_state = "fancy_table_red" buildstack = /obj/item/stack/tile/carpet/red - smooth_icon = 'icons/obj/smooth_structures/fancy_table_red.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_red.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/royalblack icon_state = "fancy_table_royalblack" buildstack = /obj/item/stack/tile/carpet/royalblack - smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblack.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_royalblack.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/royalblue icon_state = "fancy_table_royalblue" buildstack = /obj/item/stack/tile/carpet/royalblue - smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblue.dmi' + icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_royalblue.dmi', 'icons/obj/structures.dmi') + /* * Reinforced tables */ diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 8981b4ce4e45..f2c72e76b83e 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -329,7 +329,7 @@ QUEUE_SMOOTH_NEIGHBORS(src) //merges adjacent full-tile windows into one -/obj/structure/window/update_overlays() +/obj/structure/window/update_overlays(updates=ALL) . = ..() if(QDELETED(src) || !fulltile) return @@ -680,7 +680,6 @@ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE state = RWINDOW_SECURE - level = 3 glass_amount = 2 /obj/structure/window/reinforced/fulltile/unanchored @@ -696,7 +695,6 @@ smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE - level = 3 glass_amount = 2 /obj/structure/window/reinforced/fulltile/ice @@ -704,7 +702,6 @@ icon_state = "rice_window-0" base_icon_state = "rice_window" max_integrity = 150 - level = 3 glass_amount = 2 /obj/structure/window/reinforced/fulltile/bronze @@ -731,7 +728,6 @@ smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE explosion_block = 3 - level = 3 glass_type = /obj/item/stack/sheet/titaniumglass glass_amount = 2 @@ -763,7 +759,6 @@ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM explosion_block = 3 damage_deflection = 21 //The same as reinforced plasma windows. - level = 3 glass_type = /obj/item/stack/sheet/plastitaniumglass glass_amount = 2 rad_insulation = RAD_FULL_INSULATION @@ -839,7 +834,6 @@ flags_1 = PREVENT_CLICK_UNDER_1 dir = FULLTILE_WINDOW_DIR max_integrity = 600 - level = 3 glass_amount = 2 /obj/structure/window/reinforced/clockwork/spawnDebris(location) diff --git a/code/game/turfs/simulated/floor/plating/misc_plating.dm b/code/game/turfs/simulated/floor/plating/misc_plating.dm index fad5d6cbee5a..0136e9796db3 100644 --- a/code/game/turfs/simulated/floor/plating/misc_plating.dm +++ b/code/game/turfs/simulated/floor/plating/misc_plating.dm @@ -35,12 +35,13 @@ /turf/open/floor/plating/ashplanet - icon = 'icons/turf/mining.dmi' - gender = PLURAL name = "ash" + desc = "The ground is covered in volcanic ash." icon_state = "ash" + base_icon_state = "ash" + icon = MAP_SWITCH('icons/turf/floors/ash.dmi', 'icons/turf/mining.dmi') smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER - desc = "The ground is covered in volcanic ash." + gender = PLURAL baseturfs = /turf/open/floor/plating/ashplanet/wateryrock //I assume this will be a chasm eventually, once this becomes an actual surface initial_gas_mix = LAVALAND_DEFAULT_ATMOS planetary_atmos = TRUE @@ -51,16 +52,14 @@ heavyfootstep = FOOTSTEP_GENERIC_HEAVY tiled_dirt = FALSE - var/smooth_icon = 'icons/turf/floors/ash.dmi' - /turf/open/floor/plating/ashplanet/Initialize(mapload) . = ..() - if(smoothing_flags & SMOOTH_BITMASK) - var/matrix/M = new - M.Translate(-4, -4) - transform = M - icon = smooth_icon - icon_state = "[icon_state]-[smoothing_junction]" + if(!(smoothing_flags & SMOOTH_BITMASK)) + return + var/matrix/M = new + M.Translate(-4, -4) + transform = M + icon_state = "[base_icon_state]-[smoothing_junction]" /turf/open/floor/plating/ashplanet/try_replace_tile(obj/item/stack/tile/T, mob/user, params) return @@ -80,7 +79,7 @@ /turf/open/floor/plating/ashplanet/rocky gender = PLURAL name = "rocky ground" - icon_state = "rockyash" + icon_state = "rocky_ash" base_icon_state = "rocky_ash" icon = MAP_SWITCH('icons/turf/floors/rocky_ash.dmi', 'icons/turf/mining.dmi') layer = MID_TURF_LAYER @@ -94,7 +93,8 @@ /turf/open/floor/plating/ashplanet/wateryrock gender = PLURAL name = "wet rocky ground" - icon_state = "wateryrock" + icon_state = "watery_rock" + base_icon_state = "watery_rock" smoothing_flags = NONE slowdown = 2 footstep = FOOTSTEP_FLOOR @@ -103,9 +103,8 @@ heavyfootstep = FOOTSTEP_GENERIC_HEAVY /turf/open/floor/plating/ashplanet/wateryrock/Initialize(mapload) - icon_state = "[icon_state][rand(1, 9)]" - . = ..() - + icon_state = "[base_icon_state][rand(1, 9)]" + return ..() /turf/open/floor/plating/beach name = "beach" diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index da13fa2a09eb..42d75d1e52bc 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -17,9 +17,12 @@ clawfootstep = FOOTSTEP_LAVA heavyfootstep = FOOTSTEP_LAVA + /// The icon that covers the lava bits of our turf + var/mask_icon = 'icons/turf/floors.dmi' /// The icon state that covers the lava bits of our turf var/mask_state = "lava-lightmask" +/* //to add when emissives are added :thumbsup: /turf/open/lava/update_overlays() . = ..() // We need a light overlay here because not every lava turf casts light, only the edge ones @@ -34,6 +37,7 @@ light_mask.blend_mode = BLEND_MULTIPLY light_mask.color = list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0) . += light_mask +*/ /turf/open/lava/ex_act(severity, target) contents_explosion(severity, target) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 2c48b953c4c3..10896ec5b30c 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -2,7 +2,6 @@ GLOBAL_LIST_EMPTY(station_turfs) /turf icon = 'icons/turf/floors.dmi' - level = 1 luminosity = 1 var/dynamic_lighting = DYNAMIC_LIGHTING_ENABLED @@ -568,11 +567,12 @@ GLOBAL_LIST_EMPTY(station_turfs) acid_type = /obj/effect/acid/alien var/has_acid_effect = FALSE for(var/obj/O in src) - if(intact && O.location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) //hidden under the floor + var/turf/loc = get_turf(O) + if(loc.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) //hidden under the floor continue if(istype(O, acid_type)) var/obj/effect/acid/A = O - A.acid_level = min(A.level + acid_volume * acidpwr, 12000)//capping acid level to limit power of the acid + A.acid_level = min(acid_volume * acidpwr, 12000)//capping acid level to limit power of the acid has_acid_effect = 1 continue O.acid_act(acidpwr, acid_volume) diff --git a/code/modules/antagonists/blob/blob_report.dm b/code/modules/antagonists/blob/blob_report.dm index f0d46888c2f6..d8ee7857d635 100644 --- a/code/modules/antagonists/blob/blob_report.dm +++ b/code/modules/antagonists/blob/blob_report.dm @@ -29,15 +29,11 @@ floor += 1 if(iswallturf(T)) - var/turf/closed/wall/TW = T - if(TW.intact) - wall += 2 - else - wall += 1 + wall += 1 if(istype(T, /turf/closed/wall/r_wall)) var/turf/closed/wall/r_wall/TRW = T - if(TRW.intact) + if(TRW.d_state == INTACT) r_wall += 2 else r_wall += 1 diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index 73f2b391ca04..ad3d4dbf857f 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -7,9 +7,6 @@ // Pipes -> Pipelines // Pipelines + Other Objects -> Pipe network -#define PIPE_VISIBLE_LEVEL 2 -#define PIPE_HIDDEN_LEVEL 1 - GLOBAL_LIST_EMPTY(iconsetids) GLOBAL_LIST_EMPTY(pipeimages) @@ -33,6 +30,9 @@ GLOBAL_LIST_EMPTY(pipeimages) var/piping_layer = PIPING_LAYER_DEFAULT var/pipe_flags = NONE + ///This only works on pipes, because they have 1000 subtypes wich need to be visible and invisible under tiles, so we track this here + var/hide = TRUE + var/image/pipe_vision_img = null var/device_type = 0 @@ -64,6 +64,8 @@ GLOBAL_LIST_EMPTY(pipeimages) /obj/machinery/atmospherics/Initialize(mapload) if(init_processing) SSair_machinery.start_processing_machine(src) + if(hide) + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) //if changing this, change the subtypes RemoveElements too, because thats how bespoke works return ..() /obj/machinery/atmospherics/Destroy() @@ -216,11 +218,6 @@ GLOBAL_LIST_EMPTY(pipeimages) if(!can_unwrench(user)) return ..() - var/turf/T = get_turf(src) - if (level==1 && isturf(T) && T.intact) - to_chat(user, span_warning("You must remove the plating first!")) - return TRUE - var/datum/gas_mixture/int_air = return_air() var/datum/gas_mixture/env_air = loc.return_air() add_fingerprint(user) @@ -319,8 +316,6 @@ GLOBAL_LIST_EMPTY(pipeimages) add_atom_colour(obj_color, FIXED_COLOUR_PRIORITY) pipe_color = obj_color set_piping_layer(set_layer) - var/turf/T = get_turf(src) - level = T.intact ? 2 : 1 atmos_init() var/list/nodes = pipeline_expansion() for(var/obj/machinery/atmospherics/A in nodes) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/binary_devices.dm b/code/modules/atmospherics/machinery/components/binary_devices/binary_devices.dm index 5f3741f24519..8733af600244 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/binary_devices.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/binary_devices.dm @@ -13,10 +13,6 @@ if(EAST, WEST) initialize_directions = EAST|WEST -/obj/machinery/atmospherics/components/binary/hide(intact) - update_appearance(UPDATE_ICON) - ..() - /obj/machinery/atmospherics/components/binary/get_node_connects() return list(turn(dir, 180), dir) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/dp_vent_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/dp_vent_pump.dm index d82bde6274b8..6e4e519edf5b 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/dp_vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/dp_vent_pump.dm @@ -13,8 +13,8 @@ name = "dual-port air vent" desc = "Has a valve and pump attached to it. There are two ports." + hide = TRUE - level = 1 var/frequency = 0 var/id = null var/datum/radio_frequency/radio_connection diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm index 2f1863532f74..c4c17760d6bd 100644 --- a/code/modules/atmospherics/machinery/components/components_base.dm +++ b/code/modules/atmospherics/machinery/components/components_base.dm @@ -2,18 +2,26 @@ // On top of that, now people can add component-speciic procs/vars if they want! /obj/machinery/atmospherics/components - var/welded = FALSE //Used on pumps and scrubbers - var/showpipe = TRUE - var/shift_underlay_only = TRUE //Layering only shifts underlay? - - var/update_parents_after_rebuild = FALSE + hide = FALSE + layer = GAS_PUMP_LAYER + ///Is the component welded? + var/welded = FALSE + ///Should the component should show the pipe underneath it? + var/showpipe = TRUE + ///When the component is on a non default layer should we shift everything? Or just the underlay pipe + var/shift_underlay_only = TRUE + ///Stores the parent pipeline, used in components var/list/datum/pipeline/parents + ///If this is queued for a rebuild this var signifies whether parents should be updated after it's done + var/update_parents_after_rebuild = FALSE + ///Stores the gasmix for each node, used in components var/list/datum/gas_mixture/airs - var/startingvolume = 200 - + ///Handles whether the custom reconcilation handling should be used var/custom_reconcilation = FALSE + var/startingvolume = 200 + /obj/machinery/atmospherics/components/New() parents = new(device_type) airs = new(device_type) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm index 40d7ebc0af8c..6bedbe6f1c9a 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm @@ -7,6 +7,7 @@ use_power = IDLE_POWER_USE can_unwrench = TRUE shift_underlay_only = FALSE + hide = TRUE resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF //really helpful in building gas chambers for xenomorphs @@ -18,7 +19,6 @@ var/id = null var/datum/radio_frequency/radio_connection - level = 1 layer = GAS_SCRUBBER_LAYER pipe_state = "injector" diff --git a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm index 8a44fb2bcb61..4b9e5121aa63 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm @@ -4,6 +4,7 @@ desc = "It is an open vent." can_unwrench = TRUE layer = GAS_SCRUBBER_LAYER + hide = TRUE shift_underlay_only = FALSE pipe_state = "pvent" diff --git a/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm b/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm index a5d1fe5b0a29..177c281d79e4 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm @@ -4,8 +4,8 @@ desc = "For connecting portables devices related to atmospherics control." can_unwrench = TRUE use_power = NO_POWER_USE - level = 0 layer = GAS_FILTER_LAYER + hide = TRUE shift_underlay_only = FALSE pipe_flags = PIPING_ONE_PER_TURF pipe_state = "connector" @@ -63,8 +63,8 @@ icon_state = "connector_map-4" /obj/machinery/atmospherics/components/unary/portables_connector/visible - level = 3 piping_layer = 3 + hide = FALSE /obj/machinery/atmospherics/components/unary/portables_connector/visible/layer2 piping_layer = 2 diff --git a/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm b/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm index b61cc02b516e..ae12b69d8928 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm @@ -20,10 +20,6 @@ ..() update_appearance(UPDATE_ICON) -/obj/machinery/atmospherics/components/unary/hide(intact) - update_appearance(UPDATE_ICON) - ..(intact) - /obj/machinery/atmospherics/components/unary/proc/assign_uid_vents() uid = num2text(gl_uid++) return uid diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm index 18c8cc194bfb..fced240852cc 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm @@ -14,8 +14,8 @@ use_power = IDLE_POWER_USE can_unwrench = TRUE welded = FALSE - level = 1 layer = GAS_SCRUBBER_LAYER + hide = TRUE shift_underlay_only = FALSE showpipe = FALSE diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm index 4631397bb504..5a1d640884d5 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -11,8 +11,8 @@ active_power_usage = 60 can_unwrench = TRUE welded = FALSE - level = 1 layer = GAS_SCRUBBER_LAYER + hide = TRUE shift_underlay_only = FALSE showpipe = FALSE diff --git a/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm b/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm index 43721f5de7b1..457a8c174e54 100644 --- a/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm @@ -1,11 +1,11 @@ /obj/machinery/atmospherics/pipe/heat_exchanging - level = 2 - var/minimum_temperature_difference = 20 - var/thermal_conductivity = WINDOW_HEAT_TRANSFER_COEFFICIENT color = "#404040" buckle_lying = -1 - var/icon_temperature = T20C //stop small changes in temperature causing icon refresh resistance_flags = LAVA_PROOF | FIRE_PROOF + hide = FALSE + var/minimum_temperature_difference = 20 + var/thermal_conductivity = WINDOW_HEAT_TRANSFER_COEFFICIENT + var/icon_temperature = T20C //stop small changes in temperature causing icon refresh /obj/machinery/atmospherics/pipe/heat_exchanging/Initialize(mapload) . = ..() @@ -14,10 +14,7 @@ /obj/machinery/atmospherics/pipe/heat_exchanging/is_connectable(obj/machinery/atmospherics/pipe/heat_exchanging/target, given_layer, HE_type_check = TRUE) if(istype(target, /obj/machinery/atmospherics/pipe/heat_exchanging) != HE_type_check) return FALSE - . = ..() - -/obj/machinery/atmospherics/pipe/heat_exchanging/hide() - return + return ..() /obj/machinery/atmospherics/pipe/heat_exchanging/process_atmos() var/environment_temperature = 0 diff --git a/code/modules/atmospherics/machinery/pipes/layermanifold.dm b/code/modules/atmospherics/machinery/pipes/layermanifold.dm index d979f42c53a0..c2c11020b4ec 100644 --- a/code/modules/atmospherics/machinery/pipes/layermanifold.dm +++ b/code/modules/atmospherics/machinery/pipes/layermanifold.dm @@ -106,8 +106,6 @@ /obj/machinery/atmospherics/pipe/layer_manifold/atmos_init() normalize_cardinal_directions() findAllConnections() - var/turf/T = loc // hide if turf is not intact - hide(T.intact) /obj/machinery/atmospherics/pipe/layer_manifold/set_piping_layer() piping_layer = PIPING_LAYER_DEFAULT @@ -141,5 +139,4 @@ to_chat(user, "You align yourself with the [user.ventcrawl_layer]\th output.") /obj/machinery/atmospherics/pipe/layer_manifold/visible - level = PIPE_VISIBLE_LEVEL layer = GAS_PIPE_VISIBLE_LAYER diff --git a/code/modules/atmospherics/machinery/pipes/mapping.dm b/code/modules/atmospherics/machinery/pipes/mapping.dm index 887f7788b2b3..61ea729cf538 100644 --- a/code/modules/atmospherics/machinery/pipes/mapping.dm +++ b/code/modules/atmospherics/machinery/pipes/mapping.dm @@ -5,9 +5,9 @@ pipe_color = Color; \ color = Color; \ } \ - ##Fulltype/visible { \ - level = PIPE_VISIBLE_LEVEL; \ - layer = GAS_PIPE_VISIBLE_LAYER; \ + ##Fulltype/visible { \ + hide = FALSE; \ + layer = GAS_PIPE_VISIBLE_LAYER; \ FASTDMM_PROP(pipe_group = "atmos-[piping_layer]-"+Type+"-visible");\ } \ ##Fulltype/visible/layer2 { \ @@ -19,7 +19,7 @@ icon_state = Iconbase + "-4"; \ } \ ##Fulltype/hidden { \ - level = PIPE_HIDDEN_LEVEL; \ + hide = TRUE; \ FASTDMM_PROP(pipe_group = "atmos-[piping_layer]-"+Type+"-hidden");\ } \ ##Fulltype/hidden/layer2 { \ diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm index 927e0a3ef46d..c904e40ebd4f 100644 --- a/code/modules/atmospherics/machinery/pipes/pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/pipes.dm @@ -2,8 +2,6 @@ var/datum/gas_mixture/air_temporary //used when reconstructing a pipeline that broke var/volume = 0 - level = 1 - use_power = NO_POWER_USE can_unwrench = 1 var/datum/pipeline/parent = null @@ -18,10 +16,6 @@ ),\ ) -/obj/machinery/atmospherics/pipe/Initialize(mapload) - . = ..() - AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) //if changing this, change the subtypes RemoveElements too, because thats how bespoke works - /obj/machinery/atmospherics/pipe/New(mapload) add_atom_colour(pipe_color, FIXED_COLOUR_PRIORITY) volume = 35 * device_type @@ -42,16 +36,6 @@ parent = new return list(parent) -/obj/machinery/atmospherics/pipe/atmos_init() - var/turf/T = loc // hide if turf is not intact - hide(T.intact) - ..() - -/obj/machinery/atmospherics/pipe/hide(i) - if(level == 1 && isturf(loc)) - invisibility = i ? INVISIBILITY_MAXIMUM : 0 - update_appearance(UPDATE_ICON) - /obj/machinery/atmospherics/pipe/proc/releaseAirToTurf() if(air_temporary) var/turf/T = loc diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm index 8d2fc26e966d..fda9e32c3c5e 100644 --- a/code/modules/holodeck/computer.dm +++ b/code/modules/holodeck/computer.dm @@ -224,7 +224,7 @@ /obj/machinery/computer/holodeck/proc/floorcheck() for(var/turf/T in linked) - if(!T.intact || isspaceturf(T)) + if(T.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE || isspaceturf(T)) return FALSE return TRUE diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index 6a604037a7a7..50dc28ac4194 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -250,9 +250,9 @@ #define MAPLOADING_CHECK_TICK \ if(TICK_CHECK) { \ - SSatoms.map_loader_stop(); \ + SSatoms.map_loader_stop(REF(src)); \ stoplag(); \ - SSatoms.map_loader_begin(); \ + SSatoms.map_loader_begin(REF(src)); \ } // Do not call except via load() above. @@ -260,7 +260,7 @@ PRIVATE_PROC(TRUE) // Tell ss atoms that we're doing maploading // We'll have to account for this in the following tick_checks so it doesn't overflow - SSatoms.map_loader_begin() + SSatoms.map_loader_begin(REF(src)) // Loading used to be done in this proc // We make the assumption that if the inner procs runtime, we WANT to do cleanup on them, but we should stil tell our parents we failed @@ -273,7 +273,7 @@ sucessful = _dmm_load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, new_z) // And we are done lads, call it off - SSatoms.map_loader_stop() + SSatoms.map_loader_stop(REF(src)) if(new_z) for(var/z_index in bounds[MAP_MINZ] to bounds[MAP_MAXZ]) @@ -404,7 +404,7 @@ var/list/cache = modelCache[gset.gridLines[i]] if(!cache) - SSatoms.map_loader_stop() + SSatoms.map_loader_stop(REF(src)) CRASH("Undefined model key in DMM: [gset.gridLines[i]]") build_coordinate(cache, locate(true_xcrd, ycrd, zcrd), no_afterchange, placeOnTop, new_z) @@ -945,6 +945,7 @@ GLOBAL_LIST_EMPTY(map_model_default) /datum/parsed_map/Destroy() ..() + SSatoms.map_loader_stop(REF(src)) // Just in case, I don't want to double up here if(turf_blacklist) turf_blacklist.Cut() parsed_bounds.Cut() diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 6f2f8e73ed36..8aa2bff2d42e 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -1020,10 +1020,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/list/t_ray_images = list() var/static/list/stored_t_ray_images = list() for(var/obj/O in orange(client.view, src) ) - if(O.level != 1) - continue - - if(O.invisibility == INVISIBILITY_MAXIMUM) + if(HAS_TRAIT(O, TRAIT_T_RAY_VISIBLE)) var/image/I = new(loc = get_turf(O)) var/mutable_appearance/MA = new(O) MA.alpha = 128 @@ -1031,7 +1028,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp I.appearance = MA t_ray_images += I stored_t_ray_images += t_ray_images - if(t_ray_images.len) + if(length(t_ray_images)) if(t_ray_view) client.images += t_ray_images else diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 0db366f360e3..020f212798ec 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -103,7 +103,7 @@ GLOBAL_VAR_INIT(mouse_killed, 0) /mob/living/simple_animal/mouse/handle_automated_action() if(prob(chew_probability)) var/turf/open/floor/F = get_turf(src) - if(istype(F) && !F.intact) + if(istype(F) && !F.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE) var/obj/structure/cable/C = locate() in F if(C && prob(15)) if(C.avail()) diff --git a/code/modules/mob/living/simple_animal/hostile/rat.dm b/code/modules/mob/living/simple_animal/hostile/rat.dm index a99c17ffc4eb..63785ab2dc26 100644 --- a/code/modules/mob/living/simple_animal/hostile/rat.dm +++ b/code/modules/mob/living/simple_animal/hostile/rat.dm @@ -81,7 +81,7 @@ if (!mind) if(prob(40)) var/turf/open/floor/F = get_turf(src) - if(istype(F) && !F.intact) + if(istype(F) && F.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) var/obj/structure/cable/C = locate() in F if(C && prob(15)) if(C.avail()) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 86e647815690..6747f04952d6 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -27,7 +27,6 @@ By design, d1 is the smallest direction and d2 is the highest desc = "A flexible, superconducting insulated cable for heavy-duty power transfer." icon = 'icons/obj/power_cond/cables.dmi' icon_state = "0-1" - level = 1 //is underfloor layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER anchored = TRUE obj_flags = CAN_BE_HIT | ON_BLUEPRINTS @@ -83,7 +82,6 @@ By design, d1 is the smallest direction and d2 is the highest d1 = text2num( copytext( icon_state, 1, dash ) ) d2 = text2num( copytext( icon_state, dash+1 ) ) - var/turf/T = get_turf(src) // hide if turf is not intact AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) GLOB.cable_list += src //add it to the global cable list @@ -115,13 +113,6 @@ By design, d1 is the smallest direction and d2 is the highest // General procedures /////////////////////////////////// -//If underfloor, hide the cable -/obj/structure/cable/hide(i) - - if(level == 1 && isturf(loc)) - invisibility = i ? INVISIBILITY_MAXIMUM : 0 - update_appearance(UPDATE_ICON) - /obj/structure/cable/update_icon(updates=ALL) . = ..() icon_state = "[d1]-[d2]" @@ -675,14 +666,18 @@ By design, d1 is the smallest direction and d2 is the highest var/turf/T = C.loc - if(!isturf(T) || T.intact) // sanity checks, also stop use interacting with T-scanner revealed cable + if(!isturf(T) || T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !T.can_have_cabling()) + to_chat(user, span_warning("You can only lay cables on catwalks and plating!")) + return + + if(get_amount() < 1) // Out of cable + to_chat(user, span_warning("There is no cable left!")) return - if(get_dist(C, user) > 1) // make sure it's close enough + 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 && !forceddir) //if clicked on the turf we're standing on and a direction wasn't supplied, try to put a cable in the direction we're facing place_turf(T,user) return @@ -697,7 +692,7 @@ By design, d1 is the smallest direction and d2 is the highest if (showerror) to_chat(user, span_warning("You can only lay cables on catwalks and plating!")) return - if(U.intact) //can't place a cable if it's a plating with a tile on it + if(U.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) //can't place a cable if it's a plating with a tile on it to_chat(user, span_warning("You can't lay cable there unless the floor tiles are removed!")) return else diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index 9e8e451fae02..d94ad80fc17f 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -7,7 +7,6 @@ name = "terminal" icon_state = "term" desc = "It's an underfloor wiring terminal for power equipment." - level = 1 layer = WIRE_TERMINAL_LAYER //a bit above wires var/obj/machinery/power/master = null @@ -22,15 +21,6 @@ master = null return ..() -/obj/machinery/power/terminal/hide(i) - if(i) - invisibility = INVISIBILITY_MAXIMUM - icon_state = "term-f" - else - invisibility = 0 - icon_state = "term" - - /obj/machinery/power/proc/can_terminal_dismantle() . = FALSE diff --git a/code/modules/recycling/disposal/construction.dm b/code/modules/recycling/disposal/construction.dm index c27e613b5c6b..ca3b71f99003 100644 --- a/code/modules/recycling/disposal/construction.dm +++ b/code/modules/recycling/disposal/construction.dm @@ -9,7 +9,6 @@ anchored = FALSE density = FALSE pressure_resistance = 5*ONE_ATMOSPHERE - level = 2 max_integrity = 200 var/obj/pipe_type = /obj/structure/disposalpipe/segment var/pipename @@ -47,10 +46,8 @@ if(is_pipe()) icon_state = "con[icon_state]" if(anchored) - level = initial(pipe_type.level) layer = initial(pipe_type.layer) else - level = initial(level) layer = initial(layer) else if(ispath(pipe_type, /obj/machinery/disposal/bin)) @@ -60,13 +57,6 @@ else icon_state = "condisposal" - -// hide called by levelupdate if turf intact status changes -// change visibility status and force update of icon -/obj/structure/disposalconstruct/hide(intact) - invisibility = (intact && level==1) ? INVISIBILITY_MAXIMUM: 0 // hide if floor is intact - update_appearance(UPDATE_ICON) - /obj/structure/disposalconstruct/proc/get_disposal_dir() if(!is_pipe()) return NONE diff --git a/code/modules/recycling/disposal/pipe.dm b/code/modules/recycling/disposal/pipe.dm index 8e7bc3937d48..32c30dc8552a 100644 --- a/code/modules/recycling/disposal/pipe.dm +++ b/code/modules/recycling/disposal/pipe.dm @@ -7,7 +7,6 @@ anchored = TRUE density = FALSE obj_flags = CAN_BE_HIT | ON_BLUEPRINTS - level = 1 // underfloor only dir = NONE // dir will contain dominant direction for junction pipes max_integrity = 200 armor = list(MELEE = 25, BULLET = 10, LASER = 10, ENERGY = 100, BOMB = 0, BIO = 100, RAD = 100, FIRE = 90, ACID = 30) @@ -42,7 +41,6 @@ dpdir |= turn(dir, 180) AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) - update() // pipe is deleted // ensure if holder is present, it is expelled @@ -81,16 +79,6 @@ H.forceMove(get_turf(src)) return null -// update the icon_state to reflect hidden status -/obj/structure/disposalpipe/proc/update() - var/turf/T = get_turf(src) - hide(T.intact && !isspaceturf(T)) // space never hides pipes - -// hide called by levelupdate if turf intact status changes -// change visibility status and force update of icon -/obj/structure/disposalpipe/hide(intact) - invisibility = intact ? INVISIBILITY_MAXIMUM: 0 // hide if floor is intact - // expel the held objects into a turf // called when there is a break in the pipe /obj/structure/disposalpipe/proc/expel(obj/structure/disposalholder/H, turf/T, direction) diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 2a02cd5503a7..4b259f7f3a46 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -253,11 +253,6 @@ All ShuttleMove procs go here // atmosinit() calls update_appearance(UPDATE_ICON), so we don't need to call it update_appearance(UPDATE_ICON) -/obj/machinery/atmospherics/pipe/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) - . = ..() - var/turf/T = loc - hide(T.intact) - /obj/machinery/navbeacon/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock) . = ..() GLOB.navbeacons["[z]"] -= src @@ -265,8 +260,6 @@ All ShuttleMove procs go here /obj/machinery/navbeacon/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) . = ..() - var/turf/T = loc - hide(T.intact) if(codes["patrol"]) if(!GLOB.navbeacons["[z]"]) GLOB.navbeacons["[z]"] = list() @@ -275,12 +268,6 @@ All ShuttleMove procs go here GLOB.deliverybeacons += src GLOB.deliverybeacontags += location -/obj/machinery/power/terminal/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) - . = ..() - var/turf/T = src.loc - if(level==1) - hide(T.intact) - /************************************Item move procs************************************/ /obj/item/storage/pod/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) @@ -336,16 +323,6 @@ All ShuttleMove procs go here if(. & MOVE_AREA) . |= MOVE_CONTENTS -/obj/structure/disposalpipe/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) - . = ..() - update() - -/obj/structure/cable/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) - . = ..() - var/turf/T = loc - if(level==1) - hide(T.intact) - /obj/structure/shuttle/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock) . = ..() if(. & MOVE_AREA) diff --git a/icons/turf/floors/rocky_ash.dmi b/icons/turf/floors/rocky_ash.dmi index 2a57edfc53a404cb9986043ce3d1642f67aee273..d0c12fed2a87e44a35ea8acb0a8dcd7cf5153479 100644 GIT binary patch literal 8601 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 A3;+NC literal 3004 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 aSBpyW+-BH05UcvBLj>#y@+ Date: Wed, 6 Dec 2023 02:01:12 -0500 Subject: [PATCH 04/15] extra shit --- .../IceRuins/icemoon_surface_syndicate.dmm | 3 +- .../StationRuins/BoxStation/engine_sm.dmm | 3 +- _maps/RandomZLevels/moonoutpost19.dmm | 3 +- _maps/RandomZLevels/undergroundoutpost45.dmm | 40 +++----- .../AsteroidStation/AsteroidStation.dmm | 3 +- _maps/map_files/GaxStation/GaxStation.dmm | 3 +- _maps/map_files/YogStation/YogStation.dmm | 10 +- code/__DEFINES/icon_smoothing.dm | 97 +++++++++---------- code/game/atom/_atom.dm | 3 - code/game/objects/structures/lattice.dm | 3 +- 10 files changed, 70 insertions(+), 98 deletions(-) diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_syndicate.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_syndicate.dmm index 4422f1676409..6a62784416f4 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_syndicate.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_syndicate.dmm @@ -1697,8 +1697,7 @@ /area/ruin/syndicate_icemoon/medical) "lY" = ( /obj/machinery/atmospherics/pipe/layer_manifold{ - dir = 4; - level = 2 + dir = 4 }, /turf/closed/wall/mineral/plastitanium/nodiagonal, /area/ruin/syndicate_icemoon/engine) diff --git a/_maps/RandomRuins/StationRuins/BoxStation/engine_sm.dmm b/_maps/RandomRuins/StationRuins/BoxStation/engine_sm.dmm index 733a2ef2a261..2be707c2207d 100644 --- a/_maps/RandomRuins/StationRuins/BoxStation/engine_sm.dmm +++ b/_maps/RandomRuins/StationRuins/BoxStation/engine_sm.dmm @@ -1246,8 +1246,7 @@ "dI" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/layer_manifold{ - dir = 4; - level = 2 + dir = 4 }, /turf/open/floor/engine, /area/engine/engineering) diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm index 1e0cf6ce459a..b2faff225898 100644 --- a/_maps/RandomZLevels/moonoutpost19.dmm +++ b/_maps/RandomZLevels/moonoutpost19.dmm @@ -2291,8 +2291,7 @@ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/manifold{ - dir = 4; - level = 2 + dir = 4 }, /turf/open/floor/plasteel/white, /area/awaymission/moonoutpost19/research) diff --git a/_maps/RandomZLevels/undergroundoutpost45.dmm b/_maps/RandomZLevels/undergroundoutpost45.dmm index 3bb3fb1b1440..0d42897eb659 100644 --- a/_maps/RandomZLevels/undergroundoutpost45.dmm +++ b/_maps/RandomZLevels/undergroundoutpost45.dmm @@ -611,8 +611,7 @@ /area/awaymission/undergroundoutpost45/central) "bB" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 5; - level = 1 + dir = 5 }, /obj/effect/turf_decal/tile/neutral{ dir = 4 @@ -637,8 +636,7 @@ /area/awaymission/undergroundoutpost45/central) "bD" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 10; - level = 1 + dir = 10 }, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -796,8 +794,7 @@ /area/awaymission/undergroundoutpost45/central) "bV" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 10; - level = 1 + dir = 10 }, /turf/closed/wall, /area/awaymission/undergroundoutpost45/central) @@ -1189,8 +1186,7 @@ /area/awaymission/undergroundoutpost45/central) "cM" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 5; - level = 2 + dir = 5 }, /turf/open/floor/plating{ heat_capacity = 1e+006 @@ -1274,8 +1270,7 @@ /area/awaymission/undergroundoutpost45/central) "cV" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 10; - level = 1 + dir = 10 }, /turf/closed/wall/rust, /area/awaymission/undergroundoutpost45/central) @@ -3014,15 +3009,13 @@ /area/awaymission/undergroundoutpost45/central) "gc" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 5; - level = 1 + dir = 5 }, /turf/closed/wall/r_wall, /area/awaymission/undergroundoutpost45/central) "gd" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 10; - level = 1 + dir = 10 }, /turf/closed/wall/r_wall, /area/awaymission/undergroundoutpost45/central) @@ -3917,8 +3910,7 @@ /area/awaymission/undergroundoutpost45/crew_quarters) "hU" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4; - level = 2 + dir = 4 }, /obj/structure/table, /obj/item/book/manual/chef_recipes, @@ -3930,8 +3922,7 @@ "hV" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4; - level = 2 + dir = 4 }, /turf/open/floor/plating{ heat_capacity = 1e+006 @@ -4490,8 +4481,7 @@ /area/awaymission/undergroundoutpost45/gateway) "iX" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 10; - level = 1 + dir = 10 }, /turf/open/floor/plasteel/white{ heat_capacity = 1e+006 @@ -9779,8 +9769,7 @@ dir = 4 }, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4; - level = 2 + dir = 4 }, /obj/machinery/door/airlock/maintenance{ name = "Research Maintenance"; @@ -11090,8 +11079,7 @@ /area/awaymission/undergroundoutpost45/engineering) "tK" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 5; - level = 1 + dir = 5 }, /turf/closed/wall/rust, /area/awaymission/undergroundoutpost45/research) @@ -11744,7 +11732,6 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/machinery/computer/atmos_control{ dir = 4; - level = 3; name = "Distribution and Waste Monitor"; sensors = list("UO45_air_sensor" = "Mixed Air Supply Tank", "UO45_distro_meter" = "Distribution Loop", "UO45_waste_meter" = "Waste Loop") }, @@ -11758,8 +11745,7 @@ /area/awaymission/undergroundoutpost45/engineering) "uV" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 5; - level = 1 + dir = 5 }, /obj/structure/chair/office/dark{ dir = 8 diff --git a/_maps/map_files/AsteroidStation/AsteroidStation.dmm b/_maps/map_files/AsteroidStation/AsteroidStation.dmm index e21554301c0c..9ab34cd54dc4 100644 --- a/_maps/map_files/AsteroidStation/AsteroidStation.dmm +++ b/_maps/map_files/AsteroidStation/AsteroidStation.dmm @@ -74962,8 +74962,7 @@ /area/tcommsat/server) "wJw" = ( /obj/machinery/atmospherics/pipe/layer_manifold{ - dir = 4; - level = 2 + dir = 4 }, /turf/closed/wall, /area/engine/atmos/storage) diff --git a/_maps/map_files/GaxStation/GaxStation.dmm b/_maps/map_files/GaxStation/GaxStation.dmm index eb04435ea5f7..e04f23cdf1d5 100644 --- a/_maps/map_files/GaxStation/GaxStation.dmm +++ b/_maps/map_files/GaxStation/GaxStation.dmm @@ -14329,8 +14329,7 @@ /area/hallway/secondary/entry) "gRV" = ( /obj/machinery/atmospherics/pipe/layer_manifold{ - dir = 4; - level = 2 + dir = 4 }, /turf/closed/wall/r_wall, /area/engine/atmos/distro) diff --git a/_maps/map_files/YogStation/YogStation.dmm b/_maps/map_files/YogStation/YogStation.dmm index c460a527c8c9..4d0f7e801bd6 100644 --- a/_maps/map_files/YogStation/YogStation.dmm +++ b/_maps/map_files/YogStation/YogStation.dmm @@ -3175,9 +3175,7 @@ /turf/open/floor/plasteel, /area/security/processing) "axJ" = ( -/obj/machinery/atmospherics/pipe/layer_manifold{ - level = 2 - }, +/obj/machinery/atmospherics/pipe/layer_manifold, /turf/closed/wall/r_wall, /area/engine/atmos/mix) "axL" = ( @@ -37026,8 +37024,7 @@ "kRZ" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ dir = 6; - layer = 2.35; - level = 1 + layer = 2.35 }, /turf/closed/wall, /area/science/mixing) @@ -54097,8 +54094,7 @@ /area/security/brig) "rrF" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ - layer = 2.35; - level = 1 + layer = 2.35 }, /turf/closed/wall, /area/science/mixing) diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm index d4503b4e1efc..05844512da3d 100644 --- a/code/__DEFINES/icon_smoothing.dm +++ b/code/__DEFINES/icon_smoothing.dm @@ -89,56 +89,53 @@ DEFINE_BITFIELD(smoothing_junction, list( #define SMOOTH_GROUP_CARPET_ROYAL_BLACK S_TURF(18) ///turf/open/indestructible/carpet/royal/black #define SMOOTH_GROUP_CARPET_ROYAL_GREEN S_TURF(19) ///turf/open/indestructible/carpet/royal/black #define SMOOTH_GROUP_CARPET_ROYAL_BLUE S_TURF(20) ///turf/open/indestructible/carpet/royal/blue - -//TODO INCREASE IT ALL BY ONE - -#define SMOOTH_GROUP_CARPET_ROYAL_PURPLE S_TURF(20) ///turf/open/indestructible/carpet/royal/purple -#define SMOOTH_GROUP_CARPET_EXECUTIVE S_TURF(21) ///turf/open/floor/carpet/executive -#define SMOOTH_GROUP_CARPET_STELLAR S_TURF(22) ///turf/open/floor/carpet/stellar -#define SMOOTH_GROUP_CARPET_DONK S_TURF(23) ///turf/open/floor/carpet/donk -#define SMOOTH_GROUP_CARPET_NEON S_TURF(24) //![turf/open/floor/carpet/neon] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON S_TURF(25) //![turf/open/floor/carpet/neon/simple] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_WHITE S_TURF(26) //![turf/open/floor/carpet/neon/simple/white] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLACK S_TURF(27) //![turf/open/floor/carpet/neon/simple/black] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_RED S_TURF(28) //![turf/open/floor/carpet/neon/simple/red] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_ORANGE S_TURF(29) //![turf/open/floor/carpet/neon/simple/orange] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_YELLOW S_TURF(30) //![turf/open/floor/carpet/neon/simple/yellow] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_LIME S_TURF(31) //![turf/open/floor/carpet/neon/simple/lime] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_GREEN S_TURF(32) //![turf/open/floor/carpet/neon/simple/green] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_TEAL S_TURF(33) //![turf/open/floor/carpet/neon/simple/teal] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_CYAN S_TURF(34) //![turf/open/floor/carpet/neon/simple/cyan] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLUE S_TURF(35) //![turf/open/floor/carpet/neon/simple/blue] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PURPLE S_TURF(36) //![turf/open/floor/carpet/neon/simple/purple] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_VIOLET S_TURF(37) //![turf/open/floor/carpet/neon/simple/violet] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PINK S_TURF(38) //![turf/open/floor/carpet/neon/simple/pink] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_NODOTS S_TURF(39) //![turf/open/floor/carpet/neon/simple/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_WHITE_NODOTS S_TURF(40) //![turf/open/floor/carpet/neon/simple/white/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLACK_NODOTS S_TURF(41) //![turf/open/floor/carpet/neon/simple/black/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_RED_NODOTS S_TURF(42) //![turf/open/floor/carpet/neon/simple/red/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_ORANGE_NODOTS S_TURF(43) //![turf/open/floor/carpet/neon/simple/orange/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_YELLOW_NODOTS S_TURF(44) //![turf/open/floor/carpet/neon/simple/yellow/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_LIME_NODOTS S_TURF(45) //![turf/open/floor/carpet/neon/simple/lime/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_GREEN_NODOTS S_TURF(46) //![turf/open/floor/carpet/neon/simple/green/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_TEAL_NODOTS S_TURF(47) //![turf/open/floor/carpet/neon/simple/teal/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_CYAN_NODOTS S_TURF(48) //![turf/open/floor/carpet/neon/simple/cyan/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLUE_NODOTS S_TURF(49) //![turf/open/floor/carpet/neon/simple/blue/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PURPLE_NODOTS S_TURF(50) //![turf/open/floor/carpet/neon/simple/purple/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_VIOLET_NODOTS S_TURF(51) //![turf/open/floor/carpet/neon/simple/violet/nodots] -#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PINK_NODOTS S_TURF(52) //![turf/open/floor/carpet/neon/simple/pink/nodots] -#define SMOOTH_GROUP_BAMBOO_FLOOR S_TURF(53) //![/turf/open/floor/bamboo] -#define SMOOTH_GROUP_BRAZIL S_TURF(54) ///turf/open/indestructible/brazil/lostit - -#define SMOOTH_GROUP_CLOSED_TURFS S_TURF(54) ///turf/closed -#define SMOOTH_GROUP_MATERIAL_WALLS S_TURF(55) ///turf/closed/wall/material -#define SMOOTH_GROUP_SYNDICATE_WALLS S_TURF(56) ///turf/closed/wall/r_wall/syndicate, /turf/closed/indestructible/syndicate -#define SMOOTH_GROUP_HOTEL_WALLS S_TURF(57) ///turf/closed/indestructible/hotelwall -#define SMOOTH_GROUP_MINERAL_WALLS S_TURF(58) ///turf/closed/mineral, /turf/closed/indestructible -#define SMOOTH_GROUP_BOSS_WALLS S_TURF(59) ///turf/closed/indestructible/riveted/boss -#define SMOOTH_GROUP_SURVIVAL_TITANIUM_WALLS S_TURF(60) ///turf/closed/wall/mineral/titanium/survival -#define SMOOTH_GROUP_TURF_OPEN_CLIFF S_TURF(61) ///turf/open/cliff -#define SMOOTH_GROUP_TURF_BALLPIT S_TURF(62) ///turf/open/floor/ballpit - -#define MAX_S_TURF 62 //Always match this value with the one above it. +#define SMOOTH_GROUP_CARPET_ROYAL_PURPLE S_TURF(21) ///turf/open/indestructible/carpet/royal/purple +#define SMOOTH_GROUP_CARPET_EXECUTIVE S_TURF(22) ///turf/open/floor/carpet/executive +#define SMOOTH_GROUP_CARPET_STELLAR S_TURF(23) ///turf/open/floor/carpet/stellar +#define SMOOTH_GROUP_CARPET_DONK S_TURF(24) ///turf/open/floor/carpet/donk +#define SMOOTH_GROUP_CARPET_NEON S_TURF(25) //![turf/open/floor/carpet/neon] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON S_TURF(26) //![turf/open/floor/carpet/neon/simple] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_WHITE S_TURF(27) //![turf/open/floor/carpet/neon/simple/white] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLACK S_TURF(28) //![turf/open/floor/carpet/neon/simple/black] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_RED S_TURF(29) //![turf/open/floor/carpet/neon/simple/red] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_ORANGE S_TURF(30) //![turf/open/floor/carpet/neon/simple/orange] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_YELLOW S_TURF(31) //![turf/open/floor/carpet/neon/simple/yellow] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_LIME S_TURF(32) //![turf/open/floor/carpet/neon/simple/lime] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_GREEN S_TURF(33) //![turf/open/floor/carpet/neon/simple/green] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_TEAL S_TURF(34) //![turf/open/floor/carpet/neon/simple/teal] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_CYAN S_TURF(35) //![turf/open/floor/carpet/neon/simple/cyan] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLUE S_TURF(36) //![turf/open/floor/carpet/neon/simple/blue] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PURPLE S_TURF(37) //![turf/open/floor/carpet/neon/simple/purple] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_VIOLET S_TURF(38) //![turf/open/floor/carpet/neon/simple/violet] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PINK S_TURF(39) //![turf/open/floor/carpet/neon/simple/pink] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_NODOTS S_TURF(40) //![turf/open/floor/carpet/neon/simple/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_WHITE_NODOTS S_TURF(41) //![turf/open/floor/carpet/neon/simple/white/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLACK_NODOTS S_TURF(42) //![turf/open/floor/carpet/neon/simple/black/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_RED_NODOTS S_TURF(43) //![turf/open/floor/carpet/neon/simple/red/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_ORANGE_NODOTS S_TURF(44) //![turf/open/floor/carpet/neon/simple/orange/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_YELLOW_NODOTS S_TURF(45) //![turf/open/floor/carpet/neon/simple/yellow/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_LIME_NODOTS S_TURF(46) //![turf/open/floor/carpet/neon/simple/lime/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_GREEN_NODOTS S_TURF(47) //![turf/open/floor/carpet/neon/simple/green/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_TEAL_NODOTS S_TURF(48) //![turf/open/floor/carpet/neon/simple/teal/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_CYAN_NODOTS S_TURF(49) //![turf/open/floor/carpet/neon/simple/cyan/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_BLUE_NODOTS S_TURF(50) //![turf/open/floor/carpet/neon/simple/blue/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PURPLE_NODOTS S_TURF(51) //![turf/open/floor/carpet/neon/simple/purple/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_VIOLET_NODOTS S_TURF(52) //![turf/open/floor/carpet/neon/simple/violet/nodots] +#define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PINK_NODOTS S_TURF(53) //![turf/open/floor/carpet/neon/simple/pink/nodots] +#define SMOOTH_GROUP_BAMBOO_FLOOR S_TURF(54) //![/turf/open/floor/bamboo] +#define SMOOTH_GROUP_BRAZIL S_TURF(55) ///turf/open/indestructible/brazil/lostit + +#define SMOOTH_GROUP_CLOSED_TURFS S_TURF(56) ///turf/closed +#define SMOOTH_GROUP_MATERIAL_WALLS S_TURF(57) ///turf/closed/wall/material +#define SMOOTH_GROUP_SYNDICATE_WALLS S_TURF(58) ///turf/closed/wall/r_wall/syndicate, /turf/closed/indestructible/syndicate +#define SMOOTH_GROUP_HOTEL_WALLS S_TURF(59) ///turf/closed/indestructible/hotelwall +#define SMOOTH_GROUP_MINERAL_WALLS S_TURF(60) ///turf/closed/mineral, /turf/closed/indestructible +#define SMOOTH_GROUP_BOSS_WALLS S_TURF(61) ///turf/closed/indestructible/riveted/boss +#define SMOOTH_GROUP_SURVIVAL_TITANIUM_WALLS S_TURF(62) ///turf/closed/wall/mineral/titanium/survival +#define SMOOTH_GROUP_TURF_OPEN_CLIFF S_TURF(63) ///turf/open/cliff +#define SMOOTH_GROUP_TURF_BALLPIT S_TURF(64) ///turf/open/floor/ballpit + +#define MAX_S_TURF 64 //Always match this value with the one above it. #define S_OBJ(num) ("-" + #num + ",") /* /obj included */ diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index f7c5b8b8a29b..91d5c1de82e6 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -209,9 +209,6 @@ if (light_system == STATIC_LIGHT && light_power && light_range) update_light() - if (canSmoothWith) - canSmoothWith = typelist("canSmoothWith", canSmoothWith) - if(custom_materials && custom_materials.len) var/temp_list = list() for(var/i in custom_materials) diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index 43ddfe40434c..6c2175fcf068 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -2,7 +2,8 @@ name = "lattice" desc = "A lightweight support lattice. These hold our station together." icon = 'icons/obj/smooth_structures/lattice.dmi' - icon_state = "lattice" + icon_state = "lattice-255" + base_icon_state = "lattice" density = FALSE anchored = TRUE armor = list(MELEE = 50, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 50) From 868313131923f13c1a6bd1f186f3567ec3632562 Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 03:35:55 -0500 Subject: [PATCH 05/15] fixes --- code/__HELPERS/icon_smoothing.dm | 8 +++-- code/datums/mutable_appearance.dm | 33 ++++++++++++------- code/game/atom/_atom.dm | 1 + code/game/objects/structures/aliens.dm | 32 ++++++++++++------ .../structures/beds_chairs/alien_nest.dm | 4 +-- code/game/objects/structures/catwalk.dm | 3 +- code/game/objects/structures/false_walls.dm | 6 ++-- code/game/objects/structures/lattice.dm | 1 - code/game/turfs/simulated/wall/reinf_walls.dm | 3 +- code/game/turfs/simulated/walls.dm | 3 +- 10 files changed, 62 insertions(+), 32 deletions(-) diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index 6a1cfd6191a3..09a07b8a2342 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -406,7 +406,11 @@ xxx xxx xxx // We're building 2 different types of smoothing searches here // One for standard bitmask smoothing (We provide a label so our macro can eary exit, as it wants to do) - #define SET_ADJ_IN_DIR(direction, direction_flag) do { set_adj_in_dir: { SEARCH_ADJ_IN_DIR(direction, direction_flag, BITMASK_FOUND, BITMASK_FOUND, BITMASK_ON_BORDER_CHECK) }} while(FALSE) + #define SET_ADJ_IN_DIR(direction, direction_flag) \ + do { set_adj_in_dir: { \ + SEARCH_ADJ_IN_DIR(direction, direction_flag, BITMASK_FOUND, BITMASK_FOUND, BITMASK_ON_BORDER_CHECK) \ + }} \ + while(FALSE) // and another for border object work (Doesn't early exit because we can hit more then one direction by checking the same turf) #define SET_BORDER_ADJ_IN_DIR(direction) SEARCH_ADJ_IN_DIR(direction, direction, BORDER_FOUND, WORLD_BORDER_FOUND, BORDER_ON_BORDER_CHECK) @@ -491,7 +495,7 @@ xxx xxx xxx var/junction_dir = reverse_ndir(smoothing_junction) var/turned_adjacency = REVERSE_DIR(junction_dir) var/turf/neighbor_turf = get_step(src, turned_adjacency & (NORTH|SOUTH)) - var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, offset_spokesman = src, plane = FLOOR_PLANE) + var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, plane = FLOOR_PLANE) if(!neighbor_turf.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency)) neighbor_turf = get_step(src, turned_adjacency & (EAST|WEST)) diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index f248da2a7323..eeb022a49352 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -4,18 +4,27 @@ // Mutable appearances are children of images, just so you know. -/mutable_appearance/New() +// Mutable appearances erase template vars on new, because they accept an appearance to copy as an arg +// If we have nothin to copy, we set the float plane +/mutable_appearance/New(mutable_appearance/to_copy) ..() - plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE - // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var + if(!to_copy) + plane = FLOAT_PLANE -// Helper similar to image() +/** Helper similar to image() + * + * icon - Our appearance's icon + * icon_state - Our appearance's icon state + * layer - Our appearance's layer + * plane - The plane to use for the appearance. If this is not FLOAT_PLANE we require context for the offset to use + * alpha - Our appearance's alpha + * appearance_flags - Our appearance's appearance_flags +**/ /proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE) - var/mutable_appearance/MA = new() - MA.icon = icon - MA.icon_state = icon_state - MA.layer = layer - MA.plane = plane - MA.alpha = alpha - MA.appearance_flags |= appearance_flags - return MA + var/mutable_appearance/appearance = new() + appearance.icon = icon + appearance.icon_state = icon_state + appearance.layer = layer + appearance.alpha = alpha + appearance.appearance_flags |= appearance_flags + return appearance diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index 91d5c1de82e6..a8a3d6215c37 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -136,6 +136,7 @@ var/list/canSmoothWith = null var/atom/orbit_target //Reference to atom being orbited + /** * Called when an atom is created in byond (built in engine proc) * diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 0b9f71cc914b..8778c7d18ab2 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -139,16 +139,24 @@ /turf/open/chasm, /turf/open/lava)) + set_base_icon() last_expand = world.time + rand(growth_cooldown_low, growth_cooldown_high) - if(icon == initial(icon)) - switch(rand(1,3)) - if(1) - icon = 'icons/obj/smooth_structures/alien/weeds1.dmi' - if(2) - icon = 'icons/obj/smooth_structures/alien/weeds2.dmi' - if(3) - icon = 'icons/obj/smooth_structures/alien/weeds3.dmi' + +///Randomizes the weeds' starting icon, gets redefined by children for them not to share the behavior. +/obj/structure/alien/weeds/proc/set_base_icon() + . = base_icon_state + switch(rand(1,3)) + if(1) + icon = 'icons/obj/smooth_structures/alien/weeds1.dmi' + base_icon_state = "weeds1" + if(2) + icon = 'icons/obj/smooth_structures/alien/weeds2.dmi' + base_icon_state = "weeds2" + if(3) + icon = 'icons/obj/smooth_structures/alien/weeds3.dmi' + base_icon_state = "weeds3" + set_smoothed_icon_state(smoothing_junction) /obj/structure/alien/weeds/Click(atom/A) var/turf/T = loc @@ -183,14 +191,15 @@ /obj/structure/alien/weeds/node name = "glowing resin" desc = "Blue bioluminescence shines from beneath the surface." - icon_state = "weednode" + icon = 'icons/obj/smooth_structures/alien/weednode.dmi' + icon_state = "weednode-0" + base_icon_state = "weednode" light_color = LIGHT_COLOR_BLUE light_power = 0.5 var/lon_range = 4 var/node_range = NODERANGE /obj/structure/alien/weeds/node/Initialize(mapload) - icon = 'icons/obj/smooth_structures/alien/weednode.dmi' . = ..() set_light(lon_range) var/obj/structure/alien/weeds/W = locate(/obj/structure/alien/weeds) in loc @@ -208,6 +217,9 @@ if(W.expand()) W.last_expand = world.time + rand(growth_cooldown_low, growth_cooldown_high) +/obj/structure/alien/weeds/node/set_base_icon() + return //No icon randomization at init. The node's icon is already well defined. + #undef NODERANGE diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm index 53970143aebe..dd18b16b135c 100644 --- a/code/game/objects/structures/beds_chairs/alien_nest.dm +++ b/code/game/objects/structures/beds_chairs/alien_nest.dm @@ -4,13 +4,13 @@ name = "alien nest" desc = "It's a gruesome pile of thick, sticky resin shaped like a nest." icon = 'icons/obj/smooth_structures/alien/nest.dmi' - icon_state = "nest" + icon_state = "nest-0" + base_icon_state = "nest" max_integrity = 120 smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_ALIEN_NEST canSmoothWith = SMOOTH_GROUP_ALIEN_NEST can_be_unanchored = FALSE - canSmoothWith = null buildstacktype = null flags_1 = NODECONSTRUCT_1 bolts = FALSE diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index f38b820b249b..f9ba10381828 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -2,7 +2,8 @@ name = "catwalk" desc = "A catwalk for easier EVA maneuvering and cable placement." icon = 'icons/obj/smooth_structures/catwalk.dmi' - icon_state = "catwalk" + icon_state = "catwalk-0" + base_icon_state = "catwalk" number_of_rods = 2 smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_CATWALK + SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_OPEN_FLOOR diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index 5ae78e1c9da2..ed25a37e1eed 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -6,7 +6,8 @@ desc = "A huge chunk of metal used to separate rooms." anchored = TRUE icon = 'icons/turf/walls/wall.dmi' - icon_state = "wall" + icon_state = "wall-0" + base_icon_state = "wall" layer = CLOSED_TURF_LAYER density = TRUE opacity = TRUE @@ -157,7 +158,8 @@ name = "reinforced wall" desc = "A huge chunk of reinforced metal used to separate rooms." icon = 'icons/turf/walls/reinforced_wall.dmi' - icon_state = "r_wall" + icon_state = "reinforced_wall-0" + base_icon_state = "reinforced_wall" walltype = /turf/closed/wall/r_wall mineral = /obj/item/stack/sheet/plasteel diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index 6c2175fcf068..4d73a9dad331 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -74,7 +74,6 @@ icon = 'icons/obj/smooth_structures/lattice_clockwork.dmi' /obj/structure/lattice/clockwork/Initialize(mapload) - canSmoothWith += /turf/open/indestructible/clock_spawn_room //list overrides are a terrible thing . = ..() ratvar_act() if(is_reebe(z)) diff --git a/code/game/turfs/simulated/wall/reinf_walls.dm b/code/game/turfs/simulated/wall/reinf_walls.dm index 72db8935f8b0..46c34c1b5581 100644 --- a/code/game/turfs/simulated/wall/reinf_walls.dm +++ b/code/game/turfs/simulated/wall/reinf_walls.dm @@ -2,7 +2,8 @@ name = "reinforced wall" desc = "A huge chunk of reinforced metal used to separate rooms." icon = 'icons/turf/walls/reinforced_wall.dmi' - icon_state = "r_wall" + icon_state = "reinforced_wall-0" + base_icon_state = "reinforced_wall" opacity = TRUE density = TRUE diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 04b083241531..0467013da8d5 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -4,7 +4,8 @@ name = "wall" desc = "A huge chunk of metal used to separate rooms." icon = 'icons/turf/walls/wall.dmi' - icon_state = "wall" + icon_state = "wall-0" + base_icon_state = "wall" explosion_block = 1 thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT From 39f7a9d155926d8c05e1bf2bb7e1493baee69c20 Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 03:54:00 -0500 Subject: [PATCH 06/15] signals and smooth queue --- .../dcs/signals/signals_atom/signals_atom_movement.dm | 5 +++++ code/game/atom/_atom.dm | 9 +++++++++ code/game/turfs/simulated/walls.dm | 1 + 3 files changed, 15 insertions(+) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm index 2aeb70768075..3746bdc2100e 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm @@ -34,8 +34,13 @@ #define COMSIG_ATOM_RELAYMOVE "atom_relaymove" ///prevents the "you cannot move while buckled! message" #define COMSIG_BLOCK_RELAYMOVE (1<<0) +/// From base of atom/setDir(): (old_dir, new_dir). Called before the direction changes +#define COMSIG_ATOM_PRE_DIR_CHANGE "atom_pre_face_atom" + #define COMPONENT_ATOM_BLOCK_DIR_CHANGE (1<<0) ///from base of atom/setDir(): (old_dir, new_dir). Called before the direction changes. #define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" +///from base of atom/setDir(): (old_dir, new_dir). Called after the direction changes. +#define COMSIG_ATOM_POST_DIR_CHANGE "atom_dir_change" ///from base of atom/setShift(): (dir). Called before the shift changes. #define COMSIG_ATOM_SHIFT_CHANGE "atom_shift_change" /// from /datum/component/singularity/proc/can_move(), as well as /obj/energy_ball/proc/can_move() diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index a8a3d6215c37..88d27eb52bf5 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -271,6 +271,9 @@ QDEL_NULL(light) if (length(light_sources)) light_sources.Cut() + + if(smoothing_flags & SMOOTH_QUEUED) + SSicon_smooth.remove_from_queues(src) return ..() @@ -992,8 +995,14 @@ */ /atom/proc/setDir(newdir) SHOULD_CALL_PARENT(TRUE) + if (SEND_SIGNAL(src, COMSIG_ATOM_PRE_DIR_CHANGE, dir, newdir) & COMPONENT_ATOM_BLOCK_DIR_CHANGE) + newdir = dir + return SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) dir = newdir + SEND_SIGNAL(src, COMSIG_ATOM_POST_DIR_CHANGE, dir, newdir) + if(smoothing_flags & SMOOTH_BORDER_OBJECT) + QUEUE_SMOOTH_NEIGHBORS(src) /** * Used to change the pixel shift of an atom diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 0467013da8d5..012626e355f1 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -75,6 +75,7 @@ P.roll_and_drop(src) ScrapeAway() + QUEUE_SMOOTH_NEIGHBORS(src) /turf/closed/wall/proc/break_wall() new sheet_type(src, sheet_amount) From f64bece50379846b2030a5696b360eac4dfae8c7 Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 05:02:00 -0500 Subject: [PATCH 07/15] look ma im going horribly out of scope --- code/__HELPERS/_string_lists.dm | 4 +- code/controllers/subsystem/overlays.dm | 12 +-- code/datums/components/archaeology.dm | 2 +- code/game/turfs/simulated/floor.dm | 3 + code/game/turfs/simulated/minerals.dm | 25 +++++- code/game/turfs/simulated/wall/misc_walls.dm | 17 ++++ code/game/turfs/simulated/walls.dm | 8 +- code/game/turfs/turf.dm | 3 - code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/baseturfs.dm | 76 ++++++++++++++++++ code/modules/unit_tests/focus_only_tests.dm | 6 +- icons/obj/smooth_structures/catwalk.dmi | Bin 2149 -> 3692 bytes icons/obj/smooth_structures/lattice.dmi | Bin 1104 -> 2113 bytes .../smooth_structures/reinforced_window.dmi | Bin 9138 -> 13410 bytes icons/obj/smooth_structures/window.dmi | Bin 5063 -> 7919 bytes icons/turf/walls/red_wall.dmi | Bin 0 -> 3787 bytes icons/turf/walls/reinforced_rock.dmi | Bin 0 -> 5077 bytes icons/turf/walls/reinforced_wall.dmi | Bin 2574 -> 15211 bytes icons/turf/walls/wall.dmi | Bin 1567 -> 2871 bytes 19 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 code/modules/unit_tests/baseturfs.dm create mode 100644 icons/turf/walls/red_wall.dmi create mode 100644 icons/turf/walls/reinforced_rock.dmi diff --git a/code/__HELPERS/_string_lists.dm b/code/__HELPERS/_string_lists.dm index 3b8acba15d16..5ad2a6493cd9 100644 --- a/code/__HELPERS/_string_lists.dm +++ b/code/__HELPERS/_string_lists.dm @@ -13,7 +13,7 @@ GLOBAL_VAR(string_filename_current_key) if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename])) var/response = pick(GLOB.string_cache[filename][key]) var/regex/r = regex("@pick\\((\\D+?)\\)", "g") - response = r.Replace(response, /proc/strings_subkey_lookup) + response = r.Replace(response, GLOBAL_PROC_REF(strings_subkey_lookup)) return response else CRASH("strings list not found: [directory]/[filename], index=[key]") @@ -39,4 +39,4 @@ GLOBAL_VAR(string_filename_current_key) if(fexists("[directory]/[filename]")) GLOB.string_cache[filename] = json_load("[directory]/[filename]") else - CRASH("file not found: [directory]/[filename]") \ No newline at end of file + CRASH("file not found: [directory]/[filename]") diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index 11cc15426c29..99dbe5d1eba8 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -47,12 +47,12 @@ SUBSYSTEM_DEF(overlays) continue if (istext(overlay)) // This is too expensive to run normally but running it during CI is a good test - // if (PERFORM_ALL_TESTS(focus_only/invalid_overlays)) - // var/list/icon_states_available = icon_states(icon) - // if(!(overlay in icon_states_available)) - // var/icon_file = "[icon]" || "Unknown Generated Icon" - // stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].") - // continue + if (PERFORM_ALL_TESTS(focus_only/invalid_overlays)) + var/list/icon_states_available = icon_states(icon) + if(!(overlay in icon_states_available)) + var/icon_file = "[icon]" || "Unknown Generated Icon" + stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].") + continue var/index = build_overlays.Find(overlay) build_overlays[index] = iconstate2appearance(icon, overlay) diff --git a/code/datums/components/archaeology.dm b/code/datums/components/archaeology.dm index b32a679f61b8..79edc0a94a69 100644 --- a/code/datums/components/archaeology.dm +++ b/code/datums/components/archaeology.dm @@ -56,7 +56,7 @@ if(isopenturf(OT)) if(OT.postdig_icon_change) - if(istype(OT, /turf/open/floor/plating/asteroid/) && !OT.postdig_icon) + if(istype(OT, /turf/open/floor/plating/asteroid) && !OT.postdig_icon) var/turf/open/floor/plating/asteroid/AOT = parent AOT.icon_plating = "[AOT.environment_type]_dug" AOT.icon_state = "[AOT.environment_type]_dug" diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index 78e1f3d209ac..d5a97ccf059b 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -11,6 +11,9 @@ clawfootstep = FOOTSTEP_HARD_CLAW heavyfootstep = FOOTSTEP_GENERIC_HEAVY + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_OPEN_FLOOR + canSmoothWith = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_OPEN_FLOOR + overfloor_placed = TRUE thermal_conductivity = 0.040 diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index bf740ed64582..6d8d875540dc 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -12,13 +12,13 @@ smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1 | NO_RUST - canSmoothWith = null baseturfs = /turf/open/floor/plating/asteroid/airless initial_gas_mix = AIRLESS_ATMOS opacity = TRUE density = TRUE layer = EDGED_TURF_LAYER initial_temperature = TCMB + var/environment_type = "asteroid" var/turf/open/floor/plating/turf_type = /turf/open/floor/plating/asteroid/airless var/mineralType = null @@ -971,3 +971,26 @@ icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') color = "#eb9877" hardness = 3 + +//yoo RED ROCK RED ROCK + +/turf/closed/mineral/asteroid + name = "iron rock" + icon = MAP_SWITCH('icons/turf/walls/red_wall.dmi', 'icons/turf/mining.dmi') + icon_state = "redrock" + base_icon_state = "red_wall" + +/turf/closed/mineral/random/stationside/asteroid + name = "iron rock" + icon = MAP_SWITCH('icons/turf/walls/red_wall.dmi', 'icons/turf/mining.dmi') + base_icon_state = "red_wall" + +/turf/closed/mineral/random/stationside/asteroid/porus + name = "porous iron rock" + desc = "This rock is filled with pockets of breathable air." + baseturfs = /turf/open/floor/plating/asteroid + +/turf/closed/mineral/asteroid/porous + name = "porous rock" + desc = "This rock is filled with pockets of breathable air." + baseturfs = /turf/open/floor/plating/asteroid diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm index 5e5cb5de0ba5..654656630fb1 100644 --- a/code/game/turfs/simulated/wall/misc_walls.dm +++ b/code/game/turfs/simulated/wall/misc_walls.dm @@ -194,3 +194,20 @@ var/obj/item/bombcore/large/bombcore = new(get_turf(src)) bombcore.detonate() ..() + +/turf/closed/wall/rock + name = "reinforced rock" + desc = "It has metal struts that need to be welded away before it can be mined." + icon = 'icons/turf/walls/reinforced_rock.dmi' + icon_state = "porous_rock-0" + base_icon_state = "porous_rock" + flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1 | NO_RUST + sheet_amount = 1 + hardness = 50 + girder_type = null + decon_type = /turf/closed/mineral/asteroid + +/turf/closed/wall/rock/porous + name = "reinforced porous rock" + desc = "This rock is filled with pockets of breathable air. It has metal struts to protect it from mining." + decon_type = /turf/closed/mineral/asteroid/porous diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 012626e355f1..537fe7fa5b3f 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -26,6 +26,8 @@ var/sheet_type = /obj/item/stack/sheet/metal var/sheet_amount = 2 var/girder_type = /obj/structure/girder + /// A turf that will replace this turf when this turf is destroyed + var/decon_type var/list/dent_decals @@ -73,8 +75,10 @@ if(istype(O, /obj/structure/sign/poster)) var/obj/structure/sign/poster/P = O P.roll_and_drop(src) - - ScrapeAway() + if(decon_type) + ChangeTurf(decon_type, flags = CHANGETURF_INHERIT_AIR) + else + ScrapeAway() QUEUE_SMOOTH_NEIGHBORS(src) /turf/closed/wall/proc/break_wall() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 10896ec5b30c..3121733f6fb9 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -317,9 +317,6 @@ GLOBAL_LIST_EMPTY(station_turfs) if(!AM.zfalling) zFall(AM) -/turf/proc/is_plasteel_floor() - return FALSE - // A proc in case it needs to be recreated or badmins want to change the baseturfs /turf/proc/assemble_baseturfs(turf/fake_baseturf_type) var/static/list/created_baseturf_lists = list() diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index af3d3c365116..2db748e1f0cc 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -65,6 +65,7 @@ #define TRAIT_SOURCE_UNIT_TESTS "unit_tests" #include "anchored_mobs.dm" +#include "baseturfs.dm" #include "component_tests.dm" #include "dragon_expiration.dm" #include "dynamic_ruleset_sanity.dm" diff --git a/code/modules/unit_tests/baseturfs.dm b/code/modules/unit_tests/baseturfs.dm new file mode 100644 index 000000000000..918db53bb90d --- /dev/null +++ b/code/modules/unit_tests/baseturfs.dm @@ -0,0 +1,76 @@ +#define EXPECTED_FLOOR_TYPE /turf/open/floor/plasteel +// Do this instead of just ChangeTurf to guarantee that baseturfs is completely default on-init behavior +#define RESET_TO_EXPECTED(turf) \ + turf.ChangeTurf(EXPECTED_FLOOR_TYPE);\ + turf.assemble_baseturfs(initial(turf.baseturfs)) + +/// Validates that unmodified baseturfs tear down properly +/datum/unit_test/baseturfs_unmodified_scrape + +/datum/unit_test/baseturfs_unmodified_scrape/Run() + // What this is specifically doesn't matter, just as long as the test is built for it + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "run_loc_floor_bottom_left should be an iron floor") + + RESET_TO_EXPECTED(run_loc_floor_bottom_left) + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/floor/plating, "Iron floors should scrape away to plating") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/space, "Plating should scrape away to space") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/space, "Space should scrape away to space") + +/datum/unit_test/baseturfs_unmodified_scrape/Destroy() + RESET_TO_EXPECTED(run_loc_floor_bottom_left) + return ..() + +/// Validates that specially placed baseturfs tear down properly +/datum/unit_test/baseturfs_placed_on_top + +/datum/unit_test/baseturfs_placed_on_top/Run() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "run_loc_floor_bottom_left should be a plasteel floor") + + // Do this instead of just ChangeTurf to guarantee that baseturfs is completely default on-init behavior + RESET_TO_EXPECTED(run_loc_floor_bottom_left) + + run_loc_floor_bottom_left.PlaceOnTop(/turf/closed/wall/rock) + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/closed/wall/rock, "Rock wall should've been placed on top") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "Rock wall should've been scraped off, back into the expected type") + +/datum/unit_test/baseturfs_placed_on_top/Destroy() + RESET_TO_EXPECTED(run_loc_floor_bottom_left) + return ..() + +/// Validates that specially placed baseturfs BELOW tear down properly +/datum/unit_test/baseturfs_placed_on_bottom + +/datum/unit_test/baseturfs_placed_on_bottom/Run() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "run_loc_floor_bottom_left should be a plasteel floor") + + // Do this instead of just ChangeTurf to guarantee that baseturfs is completely default on-init behavior + RESET_TO_EXPECTED(run_loc_floor_bottom_left) + + run_loc_floor_bottom_left.PlaceOnBottom(/turf/closed/wall/rock) + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "PlaceOnBottom shouldn't have changed turf") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/floor/plating, "Plasteel floors should scrape away to plating") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/space, "Plating should've scraped off to space") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/closed/wall/rock, "Space should've scraped down to a rock wall") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/floor/plating, "Rock wall should've scraped down back to plating (because it's a wall)") + +/datum/unit_test/baseturfs_placed_on_bottom/Destroy() + RESET_TO_EXPECTED(run_loc_floor_bottom_left) + return ..() + +#undef RESET_TO_EXPECTED +#undef EXPECTED_FLOOR_TYPE diff --git a/code/modules/unit_tests/focus_only_tests.dm b/code/modules/unit_tests/focus_only_tests.dm index 89c702ca2313..2d48294cbaa9 100644 --- a/code/modules/unit_tests/focus_only_tests.dm +++ b/code/modules/unit_tests/focus_only_tests.dm @@ -6,11 +6,11 @@ /// and you will only test the check for invalid overlays in appearance building. /datum/unit_test/focus_only +/// Checks that every overlay passed into build_appearance_list exists in the icon +/datum/unit_test/focus_only/invalid_overlays + /// Checks that every created emissive has a valid icon_state /datum/unit_test/focus_only/multiple_space_initialization -/// Checks that every overlay passed into build_appearance_list exists in the icon -///datum/unit_test/focus_only/invalid_overlays - /// Checks that smoothing_groups and canSmoothWith are properly sorted in /atom/Initialize /datum/unit_test/focus_only/sorted_smoothing_groups diff --git a/icons/obj/smooth_structures/catwalk.dmi b/icons/obj/smooth_structures/catwalk.dmi index 4b71fb507316716348e933692ae426dfe6167955..0920cc571f1e59d36ac1cb734d755280df6b0163 100644 GIT binary patch literal 3692 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 Thj#GaM+DBn$-eZE&-H%;AO4vW literal 2149 zcmZWreK-?p8{Z6Tk|C+2HZr7WIU-*hp%KN2gVdN(S-z5;LfZ_lucY}poQRpv)WLB` zzQT%lm5*5=$wJu}Ml%~4@A6*P`_KEwbKlQ%KfmX9U-$jo*L~e7Zs#$(6f_h70KhIM zNBav>UG#J9keABTfg6TWRTJ&uO|ZWfarHL-76Bg?3IIetPV28#cnw!en7l}v%6ylj zfyLgZ2RKgHYPgqG$^9`Sw3Kb$GjQiPQFF7`bp8nli;>%N(8CoQZh=%Ux^2Vj| z{c%*08@0m2%X*V)czir&C zqLN`&6XU6V+(=}=c{Z$~Hs&zL8xRm9UOjdrixuVLTZU?rd$4QIsee7&1l{OjNaZbB zLcGWS{Ox2+mbEQ52k(tx=Z~Nf1GF%gT#T}){pf|Yknn;$T6Ia~r#kP!V1=&|P9NOu z&;szqHxS;*q^gX4nuk+)0@a@?;W#%nH{X0;clq)n6YPTIfplxkzJbn43cKR2yAOhO z{OM3f^+9kjfYh;C1RYger<_d}wF?$LnJu(W<}F8Bq4$68SbdE*x3Zdl&%C$Q&@xLf zh$?)`8KBV(JsQ>#;YHA9(w`KB)k^D;36k5Y-D#Lw%8?T~hmJ82-1tzKE)KF@^eGA9 z&5hSSfk^C(M>NWkIu>5b9-E|doaxX;&>*-Qe_?TNHH zlZ({%Ya>3|+LW{`Z&(l9Ag<0o+Jrn=riX?-n`UX>Cj8(}of;#)Nqzg$vydWS;Fd}; z4Z)HDVjE}aU{jzH6#ARxQ@t#R3E4*yGK#Dw>4V-EbO-h)z30BT1=K-S2`Rp!5;|VB zJ>YXEyEuQ&ORq>I2@K5Z92jDkKVQEz3$*5Aexc}+)vr_zg-yyh;)+mkyt))_4{iMF z9k5GMwL&TUNuhy08YpCeJKZhG!^DoSjXC60$-s5|OD@vI_2yo;Bh!^4B&o&UZI>ucFuf^a`$1l2UeXcJYpUwC&_Bnsl|3EE)&nPd~L_}T>H{X#pdD)#rOX)d11X6)va}=&2;wxo1#H* z0MrrY7JpX~IJjH_b>xCF8w0d&dqvlm3wXok7?InAu;gNA&G>W~=YRlM!KzI5Xfv?2 zH@F%x;)9vQqVu%L5;Dw~cH*r7?bnr~^zM9;UbPQ~&ZJ$N6ixTuI+abZD)>PfvbLD0 zYOMyMV9GCH=TfEazEA=6(6X);vdNOH{f3*5G=!^+#O1~5s1YcF2-u+>2|2^Zu%@tR zjYyedQ3-yz8kUxzI!{8=?n>NVfhoAyTpS;j_mk?sncBYonKIZ}F+lm)lIr~SVV4!~ zK6(4Gh7%L1yB$ejbZULN)pIthHuG%z>R$j+hw066e zv0urhchnDqhQL9IA~9b0VhCiUBIJ%)@G`cMp3yezID`Ko2f?z_*5nmn!M2oa%M?F2 z3eFKo5>Q2%x~TLXb`ya~hd!yOg_>w8T@o)ZLKZ-Gl$4Za>OKZf8{{&1!mqrM8oKi> z!apB_b@fUPs8phdJzIQT%n;$Tt;Dt4WaH)|XX-=K>I5z*648j=9>dv)D84wTP-D&;89<`~U#8buCxB%t82r(x;aW+{={Iz?82WV3C46#(BxtpFk0X6a+6?mEMK_Q<-q zmRzc=m1WJ$l&Sn4CRnGytH!r4MzSuqyJOP6b`54&$JRMax_U-BHvpU*&f8a?#>M>y Da0fTN diff --git a/icons/obj/smooth_structures/lattice.dmi b/icons/obj/smooth_structures/lattice.dmi index 5adab0caf1beb71a2ae71ad199fa1fac986b2e1a..d9223ded775fb68f515c9e3a009ed4a113d51556 100644 GIT binary patch literal 2113 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 literal 9138 zcmb7~1yCJ9w5A6O7uUcgxRYRk0KwheEx0=bcP{P_B)BAKaCe8`dU2QF!7T*6OLptM z-B-15tF~%pdZzkx&rH|f=X8JFk;;lv7^rVh0RX^|krr2dNx%I&L9bq}(UX?UFG;<( zy0)vhxr?c@wUev0qXPhVX2nj{p!QK<4_xTv{*tzki>+j#rR1imQiivoE_|h)v2ccc z@VjZPCw@CXK%i);l7l5Bz>noZ+Dv-mG%FiAmyKQ$ShqH~U{F;!Tj9+J7W|-)L9#{_R$XD} zw(8#5CraX5-UvN8A#Nl5Op&*Png{)7Z}v z?2uhkEo(Td<&v8>8RgRET8AJ@_d3zzIU9q%?67|%N)#3q#ocS6OqZd9W8ZqEGp6}T?hSA-)v$%&$UE9~$!f>W@B!G2)0%!DTfbOzCj8Dv znX1saR@_6hM7fJj`*KhBED=G|vGet*!b5%fS;2m3>Jk7jd zuPNu87>eIS8jjUL%<^e+cv7%yyp%1d>F7hU{zEC!0N4Rmyppbf&s6pSj~Up+lJ(Y~ zjBplC?(bPQ@NaZ#z&DG5qaZ)O7=4xai3WC%P%JJr{OWB%s6xOdoG?X!VxXWj4+>|rdPK8t~sY|?K0`t*H8Z2H{ zC*0d8e$Uzxh!Evr*Y(k^-c9}S;a#u^PD@(e+`ZL_v4|-N6f7mo04>KXZJI-E^1J6% zurHw2;*>M}+d{jbe%yIjq&i@RtKg|^aohfDcoP4v%WH z{@7`zUsbhdqKejMdyM-tRi!ju^K>_f%Ry^j!V}KgYd0IHZ?iY;f%f8 zshSnHN}8uJsNu14>5-jus;(P9~ta9U;lAR4)FGn!qUKLw@=1d(yzIO>DY2~c z@$!hEAWdLn2|V|V2G75SB@SxI+n0_c3*S1;FAP&TQQAlQID&=GLPg@P$on~~Yd3{> z=X=qs(_j}S@))}S-ZSK=cLrqxE!jg1jC05yPrFf{LSo4MGvf^!6k}uKXy&i=+)8cY z4@Ma}sI(r*U*)glDqeHqINrG<0jutez^1HTi06XCJU}tJPXJIjtk4G)+b`%gg4do4 z^75$k%$a3QTld$cn*8?(Yh!UQhb-rH{R4jJFU?FBYYMt|otR{;rnZuws(RV>ex5<@ z*l$5TTX!DC%2f+a!t2RhZG=8=qCGsp$=);tT+R(y<}P21J1u$%Ru0{*hxa6<4N77i z697h%+`>J@Vq4M28s}XA0!`TO3y+Eyy(I?H!M#U{*ow=a{nV*X#HUH?im6cT zSJgf;)7zl6ivKfR|C=J$oD*sA_PY~Xw{wXjv3z~bFeQ~)`|zPvVg@WVlvtX-gaGfP zgT(G$F8g=llv|Av^sS3q|a3J4e@J$-}HIOKrJB!0NK}Zh4NmO(Hn+P7i)htixl@3cr%n$#dMfK ztkKb?Q&7_KP6a>qCda8|lna?XkslxQ#->#*Jgi-{Ykp+WcldT_u(`f8N1O zzUwX#oMd29f4Aq(1T>#*k;acBzjJ;_q*&xnBe6qJY+>Y&^s;N-J|HrjzdLu7bhKWoD zL40#pgg4_)8?Z^K2?8Z~??}O=4S9%5H0;_n|7_!~6CnH)UOf(1jH#uocNbcFr+1o8 zW4ZHvCoUkxP2aGY5D)nl)Zes03=g^RxQ*JEE9NhR)F{2SdzxU~fg-ep#_NSU5!nc) z`Zy$VIV`|8gd^qbqZHy7G)&-jZ0UwNe@kw2YK_J1+yABs@BP*dboS2kv_%hD)%mqm z*n@B?B9>pRGFG8gs>DVj#+?`hMiF>|(GN*NmaH|sas+;KUqYxYfvze|I|mSG1K zMu%D;AD1e|1X)hOI#^i2J=Gvr?9KtZsbbTcwrGmMcdE{)@?d#|Q zhVmLq^)}`EoD{*n0Mv5gv?ONw(9{6HtM0s+$ts_Q=p}2j35K8URvB=7t>}S_CN0H%p4$I$`hAn!=NdC z21~D68bm9y2ZH) zDY|FgV~rVvfmwuSu$tT1^dLp#PG0}2KSb8cVIqRd@bBIoPQ?E9^4JMpU;LMA`qy7L z-)rxySiHIYa$Y@#{J}>vUKc~hT>FWr+bQ}kXWN5lA=uLv)(GtLQhf6%qYw&r1=U@u zQbi+dv&+^@BazL`tn2)SuQ7m;#61VA&qugVM}>)WBLG6dA%|d!kk6u6!{z3R7BueP zFK(|K1P9aJ&CDx3;+~yFIp8gdJ0{GlTrLIMuhbG6X|{*c5qv$ssl$6*#vt}u$0_+z zQ^V+Bb*=9bT*^h-v|B(6Y5b_067&~BTT8txT{#?AXfF^v^r;RbUhw%C5fJRM)BnjA z&|Ls+WTtDm$gwp$dv!@J1gk1N^Scd@(m1;#9(}il&9mastK9YyadVdzz^WF-hn-P2 zQq%Vs{dlG`8q^(rYLY7ri}kS+>f9*1D3mGW9nP-GGacv;y06Yg*xj-9{)oLt8Bg3C z#>;Y(z~qh_@z2qM$H{@ zqjlgA$m2EM1N={OpBAz@s#Raw#4$Hu?;&-tHrjLvt0$E#_iVf8u1k5q{d&|N#KlIS z2B8ZxL7n=kEDx6fj(RJMC&q^KqS7Kj0lgppTjcjIXB~QeDmm9z7oYEFH+{kOT~M$e zY>e!`FOde%!G`~@+FqgC&9w!+5o9|roAB>(tMP45l2_MSj`|2dvEzc)%eVClCOVaz z@8-5fOI9ouC_W&d!37q*_%f;myDvA&(ldNVC;M`@;?3MfbT0}>?RsdkTsruUdYV7q z11?L&Tp_y;Ah5RR9Z*s#|+uK`_%|Ps{EPV=Wcg_6mS}?$W5O4wdyOX9s&YR#MDOarr$E~Y5Kj_Ys^U^ zhfO+5t(@<8*(0*(g}+2m$qYwsLHl=nBeb!}`d^1(gIgoRKMsZ(l8Y&U@Lm-`*maIK z&+O9J;NEMbd%DNt{*5yl4o)p3ibM3Q8H@hA=@U)Hr(;x8pKix4&u*;Eb}oNqQbEJ! zcAsuzmfD8S0CdO4U$mDEZLl1ldk@F?<+|Fwd4%aWfhz0(dFOAYuDXdF3Msf+(j5;5 z+rgzpG8uG+3TWsp^Q(~#A$oVH9Gs1FI1h{9FMk?Q0D)^bUl5FLWmI#o5wC)~< zrXU?#7gf1XjGqOm2c%uTLvTROPk#6B*ONjG)c^jtiTtVcHrTG+sI})0v7&9P)g(&_ zFjfB-Oy?SN$b~l9`0Gp_Ar9$e`)0ZCB6R?(tl&Ln|M3Jdi&&* zY6Ba5de$#}viRp;|5LgDO>vY@MyYq)uEo@g$8o!4xsEM(1aN}?glhLBq=W>58^3Jj z`iLceOuT-}OWv{7THQ~nWAi9XEn(}U9-z$u%AuxMmU`=9#**>pH=3g z4N?BT(VDAh^j~qKz0w|QKIR3fn-}3{ew(3$21bs@Ws<)22=?~&3=9mymS><^)otaC zsfeH;_zGQs7e7w~r5(K&#nJSw8 z%yFHc0<%{2>N!4AS2e#M9j3!$L z2*;g9TSyQqz8_hNsFdle>iDv=b{$CpSW%Dw^5niVhbyl6n1JpKPT=ULibaO+fG`MJ z^vR|_;!za?a6MQ)5B!+@_M=~z!YlJv!0Lsp_oMV{x>w6xI5>v`1F#Dn^-eVupjtlG zbZ#K_2d^`FE0q{IY4?4mLr+bEVl7K#J z5CB>$DFvnj;k2=n_u+S|Tvw_+7Kjh*cIsg$(Nc0J*R;?zAu+H3ta zFjb;);XexC;2HV{|8)DPNP)m4z=thk$y}aqC6dy%_naJ<6AQriy*tih8 z29X6fW&QrrAkoEbHIbsh9-OG4V>@mQl!UiB*uaj3g=j<+Qu>?l3628G0&4D6Ea)~; zl6gTfotTzrHC++q6g0%O3c0)rPfwKBV(=lR6E;dl28mWt9LN~Qw!t*>zl;k=W01Za zC}=)W6a|4OjbgThO>?e$<*F`{Ra&)xmPYHbZ0y1{)XFjv01 z$RKk!i##OYQADQdh)4x9qLzDcWpt9DG9@U64Jhd7Ahjm4g%qHR*VhB^)eRhkD_r=L zLL=~^AWizOBCdnqS_)sU#kviR$@dLt91caLMJE_+j)wP>2LGAByl&tydRnH!H}9t$ z{IJlnEA_gUbidEZmg&`}BN5*VW$=fcu*@tJECAk^gHLov7eDmGj%3=fl2=4SgR~9> z9%+V0A*CBDw|;uojtFo+UG3N4*a=mHeLLLK?P)4i#~6O+bH5~XSy?W6lOA>7l4Rm4 z3?-71C{EeEa;^x}$&189f}|y7qQ&BJe-~LGvf!cr^YPX=Yc~ITC)wxEpTP%bVu~gt zX```LhD;e5#Ku>2d7Yg&Dk>^qa&jrv0zCQpzLUt_t$d6r62aN|1u8Bs03#q*(GTk zGSf{41gQ!gB)fvn3^fKR{E-Cca$gX0mw2`c5q|VgKYqo$lr0w8o9&1JZg|m13peGC z3eDz|nFVVbIcAk2ItJjS$BL<`lXzcYBv*Y3t!cEthQ6ZG?O7oKXQqd8amK~fSVNq^ zWJE~hyTJj?SiFVc>@0C7Gi-WG_%E(uTf=V`w{fw-zmXC8H>W_L?^b+E$m~2p<@JaM z`N2~KQ=w-uGTNJ_s%UKON3_Ws?t}^EI%$_bk4)QOqLrV~y-s`Dn(XeEP2u#_ z8nl%P`d)8c$d%1M&@*~SV%!%eG;YydYmo;sCI@ZKa_h-h|0pRJwzFUf0u-6E_BYET z#>XU}8kz4Y>!M|0YcpNqzweK}f8?TC5TgJQfND@c6EQ53H}WijGo;YuZSHA7ZZ{o8 zp*&5-rgNd5UK9zQmKFdqHpZkJhv4XvoxsY>i2+o%*5d=!RJQ#@hmj~KmIaet0XcWK zmf7zH9hwlv&Jl9t6-CxiTI_zU=~DsiY8!9>cm;N!2NPv;Yn~R3jC;PYMga!iZD79` zmA3_us0I+)aS^v755DK%k5N_{6z!}$WdR_gob+RO_Vg2fS394dKTPj$2u=1r3`z-i zJJkkY1K0GUA^2@EQy!#`!1ICULuNChn^|Hx$yj`J{ zI^y^SKK6vzhaDlZX-srz@CC`ZD)^K^IHE)50ut=dZSB8zw*Hu4)X?tF{J;FzjEcvh zd;3lwKDf}=<`aSc4(cyt(Og<{R6p7Nv;X=_so{RDSjgR$HNZD~P`RKE` zCpMf%F)o$W`g(&33R{RmRO?B~e-2>Kf(4<@lAU)vtG-I^e@GntF+Dr4?~a~31+DP< zq!r`B17`J}SYCS?N|p%fwxlX8H|xi`@P)?Is`}ktO|slmnLVDb_+FQ~^(-DpM{^2j z9d2Aj{o`_LTomiZ5<88yHSXqAG;%cVPj^aA&HM7&`ns*v-N2rA>rD7uw9HvGc7en) zr9%f9Bi@?TZ&IlrRcI3j;xDt;_mAR+cyI4kwakE7`zCn6q-mqPaZbH`qsABIA3POR zOB%XtK?0mt8e2+an!tlk4ga$`H5dmN3?JA+FLLwUf05dhur7*@9FBMO zbF%kn6Qk*1X~@KPl*Y5KT3))%?$9U5#4)^EZAsV>m_*Ckuq>EWm;B91DWyRwQe(2~ z-v=KyE>b|0ebc9k@Dr2JH}FX?2N$}QI(o<^x|JO~eA3?Iq!mHfO7hL_Z`ObtDjCKN zCLRWl@BHNH|CP>eLv9}-r^)Ih{OZA|7V!!&Pe(fsgPFHf32)28`kWwfxS)DJCZGiA>rf}r{beD;v5^^OL@ z-Vznhnny?^CZjW#<6ug=XFaAOB{x6N#z%H=rhikgS~($caY@yl+mM=;mMxPzIP(ou z@zS+}&m|NjSedNrt60s5+@^FaZ{Ki8vOLW2{=BgoFJp0^S=O(d zOc@J)saM}z`>bQ;3fuVkLL9%b1vRdVl&z{qKLnhs-B@C+cbK!G-u>BDD$Cc`H)$Y3ki?go)pDHxdg47C)cmgKZpN0j}Dhu;om` z%J^J2zwg)MxsN_HsBaKcN&B|drp%B6d;GPJPy2u54AXYUyDogco~WXpaspKZBTeys z5^{?cwa0u-=jB?A$xKx>dvmjy16=IH*mK3ty+w@Hfx^A_oxOswzKx=Wjg6@4>=-M>u1S!U68+2I)NJ^OZ z*tL82o83^B(5Y!?VMq3bSG_TlDKzd|TMGp_I;g0|4+9|Mmi0=}2rw&@%S$?JGii zGVUdl4TWy$!{3$dWE!^d(Kv;SMqTNGDC_;KB& zs1y{#)hiOp`l81Eyj+!rk@AE_O-s<}H3UOn{$<51$jq+z-iRxHWrS3eSm@d*B*2}( zJ)RKb#;sb{o}tZ~Q`3BaYAFLbbu=_?-M?HqTGDb2ok@|N4rD2q)+xRT2T_emK;Ro* zR@xYleE##s|Ir3xkTFmO+rNi2-?gDTCxIA%(C7F<-=yo)z-ccE2>gH0eYmkZxVmXs z71+G3w2ns5bBe{W5L<}MG-SCo3`wk^4+EPY`o)Ztdd^MR&R}+)i@AU?$+TL+3ulU` z6mvnWVQ_QtLK31Xyl|mtD^y?dcF`AQD$E;bXEkexgmX%#U8y7%Fp&5q~wJgb}=rD9JE*Lsp7UUDfJ7F!^INGm#Rr{n;m4(q39YriB;&9Xppk zR@o?qp(?v~oF*lkC9NbqT@4jIvWmEvjWLSv1&WhIGaC*%We@}SLT*SqA=WB~vgR8i zwC1yu*D9!#*UlLmuQsaun53YfsJHZ;^xL<=<9G;PQLE#yOg!yylk%T7KDOede7M?L zBFn^@+z>ceM?k+X{3Bt3(VES1~bT^nBXZ dQVIYguZ)M)6I7C>yllb&WF!>DD@Bch{s)5gnBjLKeO(3=ZTJknqw-7O@2)hydk!Qjctdp7uYy-yuZ z8wG66`af$oNBgl3SsPgMf#Ck23aEL_FU_k9=2zQW9c89@JbTH=RRgg- zosU)?En2hkYno3Df@o;CYqZtxmp_^( z>ogbt+csw}^0F<3#Z2g{3j8oJo44oDP`#H@r}!sy*YU#Z%Jb`gSY7$V`_5%@4_;y5 zbg~=Eu#3~rW<5VO?y=W;;8U`p>+CeA1P|_*yAvE4=C)flgtWiz%p>jvrkrEBP8qwmucZ?AtCnHI&)Na zd}qSwqF5ucr51h8{yxcO-*j~sBQwcsQY&mw()s?LMlhr6rO)IX z6plT%IgZ9i=N@Z#f^p#cmD+|+%$tD0+MAYPj$hQuEQid%QFJu8CitnMysXnHDP7n^ z<##zN>DQ49!7*O>t8KHEOkp%7FOzgC16^PE#B$Kc z`Xag6ATd*W{dB(qNvzSbQFKOhzhV=`etl%7XNacL zNl;_eVMkka+|O&gOvezGBx@6fZYk^f7Uh>RJ+9-6XPWo&lJ07u`6=m6QmB9nJ%`qvF%xi8<`)gsS(U|l`)X880B(< z16P$%3O*?M?Msu$2b@JV)FF9=??)!3=)Mn=K}0hPwfRAW4NM;>JeS_Q(+faF_$Pk> z&JI|a51hBLo!y_~B{&Ek&CXKof|H{oM({n^>Y?AEXr9tuI}(pN;029w^gh;Y^U^!<2eRbV2QR_T*6x{oz!fyi z^0KJO-{{rrM{YV24=RaIlGE(52zQdUZz5j6ni7x;TNXzjFB=;fi&ntUrFV|^3Lrv% z+zv4oXYA%WN*t6Q9z6H> z_z6nyd$;pINU;oUQUAa$8M%xRgkKCA2%sT;HoU{Qi_7fvgK!~dHZcC|%d!w#x3g(F zBLM%e?l7LMi?tn2hecw{t1l%H33Hhz*Dl4IgPNF#1UwDMmU;0#&1}*&3kDeh+z3wK?o)Bp~!^}(phJK4QXisD2!L?9$&k4Ct zUi(D&0tUx7E$<1~DWq5>p6}A?-D6UX8i%gg<9%MYatHM%ZZO2lG*ISV#|W^GghW!x zy@&^CD0lJ((vS~HIHRI4y&rQ!z=!t}x6EY2VVklv{NxmI27%DX#dFiVSEXYxNi9P~ zcWHYxhA+GnEo}3EDIZNexh;uN?QW4kE9lLL@4zyK5Bx%LIhaPZ{<}f2-B+&Qtv$Kd zTNf$ja{-70<=B!N(@%T3u<_FCPTVJXjQVZJ^-L(wa^jN&ivJ@C_%wfQ$McQm*XKZA zQ)^G|XfR)ckQ>effC+*TUNty3hI=R>0RPY(_n%595TBu@Jr0{&k}4 z){~+-cM~hCW)ElIE&NR5O)w42jWK@Nka?=tO_b~1OqRc`O90wTb^7*$`LrKU?^P)p z`zqY)43_1@%NDlH#amv1#Po8yp}Vk4GL3#*g?oNw##5gqyx8&G`o?%$;1$-q$nIkQ zsCE-&AQxGG*=>`~I6Stuq*NL@fKULo(Z}Fa8TTjM_l5y2kYapEGN`kHa#FR8oXL46 zyu{59f?U`ZjD>!(i8@t7ik}M6!vsXO1t`5@Z4~=hzZ2AOub87`MCWe!u4Icz*OP)fgkcNt7fF2dv@wm*0AmItq3z5H+37&OK%NE9$&l9AQ^qz4TMu zO|zQ-HK7K4VRaH%9G6_*1Ok!i;xKK0=>uMqlM;Bf4#LqvU4Mz;wB#bdXXHC(U zx_1lgXN4d2#9TM5Lz;5LYLm>(+J=}N2DUz|J}MxwlBFLowAgI z5_7x)+Kz)$Uz%Y$!ZQQ0zEY*h!ET`U`S6cFm(s3K`n~TO5l_G7ub$$}*#w`RQDVgX zbn87M!b5SRf%!uJIR9*vgWr#KY$E*7)r9X86FJ1p&s@2Dnv7kbBv5wY;DRBh`t3w` zVW*+Xq<)bbL2xcC*D#bHJoQx&rgDqX8^TeXm&c~Xln`NDWu#WmX8k8dvG9Vw^gzf) z2eMAe519 zBb_JPD_&JzA9aR&jCA4`F^3V9{j%BXtLcU|DtaGz4ZmHaY__)cj)7H4ZCbFFXIQM| zs-YzXKnxWiy2sx;-s)2I-_>>sXAsD}juRwg>Ni?E7E9IOjM&J@MKcyWck5SaYzv{KO ztL4OQm8*T3(J=iOxz29zHcnjHBkFKNZ8h(Kb*v?z-o&JBfCM$qo*P_ zTK(kZ?&_mmz|Dnf&B+R!+m9Y4SX?uKsl;CMa@nEwDIE395G>S7K!Z}QFvaZFLwxmf zX{x=?N1n{&P=N^l1yphbB+#Gee6*374XpyC^pil;otY0DdvTKrm&7TYjUW+4UatVS zWER{d5ASQ^=~n}76?#B>$@AKn??LmDc*x7fMGg$UG^O;hBs( zxD%bC8ximmm5t>i(8|a}-+m1)y*_wZTybwrR`5;|=q58EKMuY(7nXBC`vso;!*H8E zA)6n}$s+9}%J^a{o2$t&p*Mb|q3Xag*@+lTjLsWn`qMZQ`<`n8=pqTXbqpLHf0Dci zkjeq_CBXX(NTD;W`b)ufV~L*|Br44D`(vb-qQ>yz`n`OX3(l?eKN<|dYS(+(fz)04 zcTri6YWyN#ditVS0674sFzCh-tl!5MZs#p-ug6cFCQPC{nffue!Soh^Qp>mJH1W*$ zUe9!uzBg-|2j_GF>0YKE!p-^p8*)h?@7pOF_}YorNkkWBL7UrS8MW9Cw=&_|9`fRC}XJ`@B36zB$AIxzJ;@*EbrPQ$gWboI0H2%QkdP)JMl@rr-YA;hULW z&j{HAF>?#8-t+fGasA4dCSqK{?|u`T93B$aPjobahJ0bO5^`jJx`(rgF7jYp1{Yj` z)Btb>;msr=7%0UFM#JA>zhGgvW zWep$|1iH`$ZOZ@Pp8rqWxPMgij#M)C{%Uli4OII6I+9FPxUx)o{sE6q=-I40UkMzB zsSgu^x;yrR8;Rbwqx6Nh;2EIJ@#Iz{lm15I%!czc(}(m{F-2}z{W>*}48aD4ujTl|-zuWOmT6eTmA z_kR#9!RB)ih>c_IoDX?Ah=xZN%AwkZa&lwr(1O*2NTmLC=g1*q;EvhU{uWA-%oNy2 z^3KjioV?TRqO1;d5UUG0vyn3w5o<1o-<5-0Quhqa(72K*tgas^PJDW{?$A;si>uQx zfI1Qff6LaX(#y7FDxYF9Ob3Y-6I@6A;+NaIp7i}2xzU&|4r(5TXYjy9N2pHdPA|f` z&XNLu{5b$*b%AOBs$RE~qQE;6+NuCu)6Wr}GKZJyN?J<_Z`Bf!4r2yOJH$XApBj1E zcq0iDAs1{~v-iXN!#}qwNRNKiKFO80RV1TC=y1Cvw8gI&Bqi``4WcqQJWn^ES61u* z8;B+PZO8=%ZzmGexRj#rfo&<0W6ea{Kq?yoL&@FD|3+?<&mxvXN1VRvZ{aP#_AR8) zd@)L8byYTGMZpOtq|N(T?>d_e^h53F3z+6nA!Owv*!5CyUXNk=FN{ygZLDBl6^K;> zZxX$E>PqBF$oj5zRqDT&EELcBjUsB|76dLP?+srg5Tp?&>tNrY1X|4<6XEx-{wXfJ zmRuWD3NZ5K_C#~cX#GcZp|S8;%_yQWPHAwlQ1=i1_O}(2-UBB5ZvfY*A>o)T{I~3X z@JpG=ojm>o^IvsdB~{+`+!s7JYmYq0cP;vAn7)GXf&9zsMiUd7`i?q)yx0XnOF*NN zO?Vox9I!4B0BXvqD}N){)WYIY3bwx#d@~3<;}27&Dpr7uVh~$YzG#uzlDzyZ<|fPU zQjuG?!QJUACC5|KZJ5pge53toRZyxK;%mJsyB!RFxzM0zej`Qa!kC#^&63&{*X2N- z!Gqs@54+({83PvVt&TwXD zLaXv43p5@!wWMH2^8HY?01nc5PnWRZjg!F*n8IopYGCr!aLwdMDl%Y1%?h9ULchaD`Kv%Nz8EaYA2{w(`5Wsl99pW1mqGOs6f3TL4ZZmqucwMSl!5u~qC_?FG*U$Ale?Z2#&r<+IK41;mwE!3dZX zH|(1ThQ5IZZvEXjsjF8dSap1%vT7j60f2J7@~paMK4suKC99xG!<|G>?g!5 ztSFsLP62^|7Y~P8LM{$YUEJ+NgtSqhhg=xqDYOFgvW}0T&M@H>ZW9&NBVo`RkcZWb zb$Y~?!gl*$h+57xDvxom6U4^!x+pIO6aDO;#tmhsmyl6f5N_z(fG;Vy<7*+Mp6$3hmmm$C^W0~ z87*-fOd09t^%!geR3K*x_%UZVgKha%rbl3k3S=1ol_lRJxqJH@64J&vWokjdwS6=j zl_Y8IRL`uzT0&}*a)~F__OrR+)&&Q&_%+Oz$!*Wa;9PD>b|gPI?+n=LoW&+yUmkp# z`bLpd$_!K4#8CdkcI&lB#5&gDyO@zm!M}zms%55&hiLLi%3<@}H2SX-lpS=uBA0vB zVuC3+uk+zhyV21lQ?ZyzdOIa8j7g#Fv3G%1MC!%yQ^6svD0{DZ1MjhnNYAm1gy(ml zOVxwzhk|*X?_M0EWQzP%mpfLc>Kz>EJ<{Hxw-ib1sV)NzKCIHq6@{sAXY39xDWN5~ zxcd{~d&Riw`mANZo0~HO-`nznG)M6Vn$P#yo$GIC&EVHn!byhwoseyh}gx z#!6=)n&)!tv_I)XC%5ih6Ym5Tg?T#sbk)pL6o0IXSsj+ZD4~<|;0}x7uY_8AzFIBU zPe0F{Dyg{b+J(l-oG3F0MXk?@UOO}7>G%Z~;Gek8-(lUmM4f>ave)Kiv)t~zGhR|o zA%qBV_m<9!*_Zi(Zzp=qz@LA-k?}IP;)l_b-Pn>hhugPOk`#%9A!|7alc$8)biStF z@PqknQS0uq#LJAdht7{>8OzH@pC}~`BSf}(o?Q?g+|6$Wd7Q7D%J3C?Ft@KD`sho1 zQc^^~^kcke*En^!Bc~n^3y1gbs8kupetx9WC1~(YJAjyu@F3D%YX0=|Oolk>mGP6! z=9L}RC~wG#mny3VEj)t_< zvlTrICMPbTb+?x_n;K=bwuXNX;U4)CcRZrc z5sw9zt{qFo+e8oE%|dKYm)_a|)!5RJF_M4we^q41O6g=sBVkm2dE|+|uw+l7Q`O?7 zb>O1sZoY*`kOuYy*Rp2ygRQtRCfUB@XM9Tb2P7AE-P|E*+GOItH(Hn*Cys^6tlGX( zDn+$L*%g>Y*dttWLfij}%G1UWhD6d7^F8PU{%J__|3{CX7z_T$cvoL@o`9-o8q56e zt+6P#c9M%c(OtoQddInTfL{-V;PA>1pdPiT>+3Qz6wp1=>cj^wL}7BdG1;%)|1bhi z7Qv+7o8r9CxXR4b+WR`4^Mjn(cT_s@0ygk0P^G0XrUk~0wLRB3M&WM?xxgVjM9M^c zb4jdejo7JwwKMt_CUtQL$$tJj>!s|+T;0DztV>2RZU8H)vP<&GQeeAWEUK1#&0ZdT zxDf*I=1LEk1$W+-NMsEEYFI3BPu#3H_2r3mM`&T(f_i7dsxy84r!i_ZYJTCCGj07| zHXsurcFU?{a7&>^KF22Br>z}0Ygl`+tZm24N|!{vh74Gw3Z7tnadJI)i2Z+eM zd8Bfl)a}!zf%D%znxI?XG|(LqKRx;m6*SNI>|&gs#RqBbR(IC?Td{1+q1-B0ONtXX&sJ!;k$h#vrN>b92;Mvgt#;+Y$BDev(Ba4W{94hx)Y))! zi%iO#kG>b&<>yj2^;{=4+S_{yhD1uo89=A$Lz7NqgR6+_wG$IXIK5&$c>Fu#^-_JI+i6EHP8Vf%+JG+ByfXIa=B{kQcqkVjpJ0f{aD|xrt+8}8C zoHsSCq!x0v=*8Xy-bPX5<5u!uEm@G4Oa#F;#|zYsv!vGl^}Md;&4xa{{k|tSr6;;< zfxmZ$(wPh-jXY{`Ed|{aTx=$nJ|u+(@ls1AY*;|w3xxPl(S+KHJ#Mju9ENQcZ&8n0 zfOdVr`sF%{3YiC|uf-=g-A%r;Wdk>>g0*q&6R52;YU|Cby@TqPN8$Tq)ltyDGDZ#g z7dwu^dc=dH&43jPsCmFbHOL-&r<5ArC7%Bku5SBR^Z(SVQ96rQ1dK$v0^j_G>PjCk zwNQC2{O}SlI>~xlzm1K$&;2AqgTB{2I`47*W)H9^{scJZb63p=ylAF+u)}OWOl5|U zP4Wfc>Th!p(%j#9++rip=GSWoUEd|p*4`|Ts5BWJ=Nk8Ixt6@xl~B_lQHf#nbX}by g-OBOm5ukvI3t7dg**$5gC)hOFcMa6bRqew6AN93eV*mgE literal 5063 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 literal 0 HcmV?d00001 diff --git a/icons/turf/walls/reinforced_rock.dmi b/icons/turf/walls/reinforced_rock.dmi new file mode 100644 index 0000000000000000000000000000000000000000..f2621f5a86382e552a724f090fed08574cc47b34 GIT binary patch literal 5077 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 MZfs8qs@i z!(f#l{he0A^r?f2Qw^Stkl(AHF?AY~#2fj|_G)Rc8WATS5;3nC!` zj$9Kitq1;j?5l6&sr=l-+TGsO)854y1oBBQH|TJ9D?%&713CQFTHlpowB7O8jgQR> zW*IMDq2$2*TOHRHwySgFh}WD}Y%%7JOA1$OmfRNE({*F$vPOB|oiN2i63Sdn;y#+m#iOocIA%HB5vgIsoG*oR((a2v^;L~DfII)TOfsY zc!mGZC&En&4^Nvm?$OUKwXHqhACA{djx6yIgn`3poak}8y1zgm4$vcIMSY+2-Hd4u zrblh-@DBk>O9mvt@o(Qg@2wT=-Ob!P{59*xsi~o?Dvw-_*pRo%T(iUmzqzk<@#0u> zXZo9*w5`O+K&JH*t+<|-kM@#o_N%Gg4(4`#IwO2xaZ@PrHgC2N3F4Y!o?y0I>Xk&B z=g-%MPR%@kMcqi%2(n}lA;$+D_{tQGZ#S{bTQ=l!kbnYgYa)OjyStIfoH_l6 zsv$}9XNYwK>I7$x>M2BfA>xb#CQiE|l<}&K%3Ml9+<04PjiDa>!mv=kABQ~X{!7@TM?_h@-K~ z88@$)n3(Xc9PL(QqpKU5qZBx@KeDm06tyK=rcm}YD)zhhqY~K+vRASu5L5_}rHlCr zaOF@~Mh5GdG$k~PN+PX%xkH3}uWLCaErSr$<$041zf;6)zi!Iu;KKjFgT>3s%NN^s z{B_r>)3RZC=U89#RrBlv7;i1QI|)K%hl=5cv(&WVyo$$$+&}?A9PR5!FS@zqKwc78 zKN*W>2BOyq!SQMYOUQsiS}~HXb*Z~)gMs{Sm!+OSW0&6z+7ipj^)mPJ3&Pu04VRY? zx})1Fe6Vu!Fi8H-KZ{g}DGrj!mX+?=8rNb7-LV|r zVkb1l{_Ka8PKwJDS$;q`BDG9V#Jzf4{8LExQ!-$EEBD%E8 z^=XM~4Gaz4JUz$s%1+RI2(fg5sAXk5$L(#igo7g3J@hADFSKlOzYaVED(&1bJ+_~< zd$`;qDAp-TO=98fMF+fI!JDkHwUr{dpD^pIV5`4eocot(9<9t;+%1>Dhl&c-`bxJF z6lK`WD$Kq@XZ0`0TeBx|ZFZ2)fFv-C8{c zPGL6>`7wBU@H*&g^8qxLVjO=fgc0682J^u99)pS?5V(x6EflxGK#fe>Vs8+AXMfr+ z>PH?vN0^H(ZX>(93zbfqCv2vV%QY_c_tBle+Y+T(7{o${AQoF$PO!??B4aLX5Xw|y zn~BrPS^Rs_(j5Z>As&8cvXIkDEQg14?@olDyjf=aF6`rNV z-a>_y!@fYsmNzC#u`WT7NZkkG;jni{m9sq#b!8N>ikCBPG;EC-lXpK~e4Kf!5W1Bm z17C9R35t3?jjNb&FnVULQfCl6x~Q$udo*$y6Eu#sfAQ>ty|1npqWHCbYwRB2!(AjuKq0?LY`;;1ldgrH%H3vtcHw3 z`EtkLG9n944j=dAN8iIWF=gAcVHPC7l1n!_81}2lZKIZ|dK_!6(YfwIgcxd^1zicjtFNzr*Khk2 z_T+OaISVE6NU{F?=g;ah4EmmrXWM-7XZzx0ATE+M4iyAurg3X*SN_ee+Ij9u9nlea zC(Pqf-D)3ejB(1!5QI!69cC7IrBx2%_xT;Vy826m#;x3s73GzsZ4FaSUQ#PYyIEcX zMX>yy#qT|~t{?Cd z<0^XsDdp5 zjs}+U4Y~y1cTN27pOdKE1AEBf++@Kd22iXnoMvq6)xhB3E3{zR;Me`rhRILyL-XN8 zRrDu{&1JrK!m~UN&-0Dr#Ea%h%5LMNLW~S zj=G_hs0jHe(mZXD2tGtC-xK4>jJ=h7iTc1QRC~4yO-#^{19iEvC@Tg`FN6C(M;7yv zEJ!(LPo+3OrB}`{XaM;?eVya7p`ICXW5tgd)x|3QtU=fLnt4c%r;H&WrsfWPaXRzz zY7;?Npa zDB_OrePEspCR=ER;Y);WroNwK1pkfYziEs}L&V1IoCCghj~VN==0LX!vAsw9&T0kq z{A~u`i6%zO4rke@d@?7Gu{szVe2-GJK{9~o(QMZP&j$}nA9kfE!*{nU;d@48Doc2L z6ug@>^czy~?Z3}`{S^B5PrX}W&ts5|F=P)hYiIdx9h6j*Rac{)B*II`j^aztje3dlp;zZ%r;QTn~=GBNyL5oXFEkc5d=1pO6HV`sxpNHAkelt+x zBdmx`TX%KC!FDl1{bY~Q(2VO!vcPrqJKsEOnPnBIAT1TKNMVHN#25>^Vm zI|>HmoxKAKW?hhGyrr0uqkyXs%7$b`Ju(537ZSEe?8;KXWoyGx(9HsSnW4_@!v)>XiR91BZ3^W1 zznF@ZpgqN9Ld1yFmaDW9ARhS2O?v&Ny$3p;|rcrw0ek^sORD@ z6b4;XG(NeV^fTsR>)1S~2Tr!laKUT64)Ou9N8h)GVESVSn`ENs1g=&g0TR5R4i05S zg?wEdjSK;@%=P9b54lRi@tn0};%+YdXU2Nv(WUIemDsRdbCb=<8tiHyjY|+a2oa)m zF>m0H)s4Ol8!FKKc=-Q;R|@0}wP%;$>gH*l`F=g-s}nrwd}#G(0WUCq+lvL`Ua)xN zB9R7gFEgZcG1BFn!_)aY>qX7fPS|ULH&AeNIEe@))?AAGH?dDT+F%mSh_xJwNi)Ua ziz`fN5K)HVZ|aw1d={A2|~(u`4Xe4<)z7>Or$)y%K2ag>~*Wj zsuuUvkK2Rc=Y}SwDtZP4459vQOl2C-la)2O^rOb2&011R$iMlvw+o=<{Ur z38O>QDZ(VipN|OSBg>}O=fcA%g23M(d`A9)yUv$YD7kdmWy*;T3TOXfT7h|Jcb92* zy)JZsm2NXx%NS=11M_C^J{)3A1QwJpU%te`AHiV;S+Es9j#lncwWL4>u-osLozIRP z4)ThQ^in`XJ|XVtQ?W>C*)oIj-*{brk4mzua(#W?IiQT`g2QN+SnfoD6y@Io`ZBWp z-8s}@Q|f~%S?7w!?|v5ogLP|aI9ryybdOqamzqnWN#HnC=|+}|7L9hAdeuKky5X)R zVes0|?+6nedV@)v)N0qj5X3jO4ijuM@gKsS=sapipmN^(Qd_>KWB;hbw&s-#Luf~< z4~xg7UoLMxe>DR`=%#O_=c7*ElxMh6bFBU8HU@R^M(#|y!jUOGj!I~5hsJI*?AZPd+<{zTN1@%4I531 zYuSU(#{2T>(jB4!s+rOsu=S!feM2-@)YiV@Hlc)9(i$SXKTfil6I90VrPg zUnssuoJV(Ef!_w-u{HN<$|{cz7%wm&A8TI@E5+1eRgV%Vj`N`nJ@`Nfo0l@tYuGFF z4>W4nqVnD1{{_hDt&xZh;*^FS*Re|*g39! z;k5A=TfWY0O#3iTmv21CK4$P`J%o+%>IN&b-w4`$V_j=|UJZbh@9k#R3V_6WK}O=g zQShkQeFPWx8xIH(ctDP88RMAe2V+2DJo~Z!r7jB&UL9(2^)q%P;4zW=HN(X{`v<+0KaurdXJq2 zS=iYfp}UP|`DwO?GE|j4!DV^P_coz?mjhQ>2w}ReLTN?;i@8gAe)qhP)fhLC{RPZ9 z%U51Jr!9}v!>yopDVvv`8qovNiod_VXu`qu9!n(OYx8YUzZ`E3Zdt_Lh@$Z*M$zK# z4j1uJb7=Ejiwu^{S8j^b)Z|1rNbznbMp|vzJUft?AJ1Sd@Z+Z%P$BwUTcEoVhzhl& zg|jiN8kW+_Zo|?s#yP8!-Sagj951De%jrfo?}9jJDY=q|8uWyy8Bhe1UjaE55SM#| z_#mTo$<-x0EBiTWJG0-|<=fOuQ5`XyAGe3bg#Y33YZoseZ;**=+qq7+g0-chJ#Xiy zta(Te^jTR@MP*i3`KC*gf<;E+y1CEFZ2=G>mb(k;wfW5Do-=1H z;xHM|-V>Vr)2?N_*L*lK%tMU_9fx;Zo#gQ@z21qkx?{QfByKuXA=bk05FEXm?^|A(g5b-hiAm|Uxxse5eXoeSnNv0t`Z zAAJ}}G$JSO^lUY3%Cn&z5P**ZK``bLI=^T5%baF|jo^Rcm^hrp9Sy|-`d#Pwe!SxF z-y}2U;529geh8IK(U;dNoyN-( zKoZsX1Ub5y@?XY5VXhl3X_wetNd+5ghyP6G>!0Rk&stLsoD%J5SX#`!DUk3l>D_yXInympOF+_u^M z6=Fywey7IWWUL^&25y;}-!M~m|!K$7%TyUFb|J?kO+fR8o)-hH8LITTA`v0A)AvxEAA3@J!Pi&xM#%lYS)uAl8nShG1{5F24SdAt zrDWoQ1hGQVNPOh{%n*PX4q0ReqDGt`+W*qpc6Imm`B5!%ffz)4o=PGtn{D_n{8$F? z!-e)z>#rnBaF-j9ZF(|+s7b>B@&%}|_+H!TVy09lI&kdv)GzyvaD*cpexGd(`Cc7oBn~@C6lYoSjSPL7x3>1Uma3s5Ze{}6M?wEG z|9oyf<}1SrX$+c)NdL5>4LF%99@7%VLrB}*h*ztf)Gp4iHqtn%&coV>N&Bq5vT$JL z02P?w>B>K(sC&AJF`pM^UGXty7UU8s2Y+1KgO;1`GdcF!))Zu*4K7x#wQb|T#)FM9 zfqfW&lx1#TKOm4@2aj*PYBa5gg~#t_zgq#5g(!q*D?*k&oWFS$PNVFV6;d7OkSg`=R&&pgklCCjM<2Lfoj0UmE(CzoRHa z#*zXKBTNtT2Lepih`VdBPnUxL<(SGy9|3>C*e$f{BUAi|*BrV~BoCfvQT~y?@0y zSAww_^fFfO-Iv{EWWL8j*}3cG-4#mrkfvB)z0She+WOL^{LHQ}^gS=Z9Jur!%JJ@{ za>VJFSX_qO3H{@nJNx?$ehQ-of1dcC=>Pc}@2`h!?0{1N+K+BJ4)C>90nEG0cKaR& zs2rM@LCt&>087#g|LTBUAP`tCCF~5X#GVQL!lyvs10#OxeABE-&b#BgOaMS##_>K% zQw9F%p3BLp-yC7uG2%3Xn4oCr?abUzJ|3oBg!KWyZOjy|5e%^XAUqpl}g z@wUFxsGRtEcv!tQ37pnyf;929UdJkmj%pcp>haR{UqpwO z|Dp{qOgTYZ>-N9f94Bt#Yu}?dmlcSr}bD zk7nt((!C5K(A%$;D+rWVA0gg@jD_IOpIgWmmN!}ew`{wsHW_H^yd7L9c$hNyBq#Cx zpsoA>*TyKmC?}_)d_fKx7(tb@zo|sW3%wbg$hKOO1ADs&A`68L3m%Fi60FIl*7`TRz6fY+V;F%M{}P zwdRbstF+X497f@zW%V?zi(~bchC6ETr2oh=5e85R=dO1lV=jg!iKRrZ-@rYoLuKt% zpQp5FTA|7j2{J~TS+)aB1UsvSrep<@2uFpQ%0R#P{#5}aa~B+U0iQmHlb&_5W3&6$ zE5{`xVMaYgP_7oqUKblVl`K(7Ak#7MSty%D5dL*#vI$Z`XusbXzUfwXf(pSIH`_R+oisCbs^V&0{ z*T1{Du?2q0f4xzbO~&WIV&QwZs;cT6F0PYKQu)*f77_yT(+e~<_SXV;=KHyxadjyc ziE*AVpJ>nHrJsda#!Ia9@NuNPtCLUG5OKqFIRkT(sSq<;+HVs1DNSn^Z+Z|p5n3A5|0Q{q>DBD~Wj_V$Y5c1Xb==DEL<;|P9;(nvoVWhh zWEh9H8=NYXotT*6@J|B}ll@1l4K=;(^4 zP`Oy)eSPe8tn?dSbo9@s1s-$tAq`89rL~7%HL(J;+AsgDr7fq)VqBM%l(aOMo@4=m zxEinajT(S?z|cAo3LgQu%%2Fk3I7>u{@stm_Vu43z9f40bb1j)ms*l4iz^B}B6aXD z-%l2t`niG-C17=F5*jfQ=Egea5ABHcO2f_FE$Y2Z0ozOdFMSEHKvCjL7WfHZf&ZSn z0u=ps_8N`qG5ZeXp^2-{^&Q2fSShxqj^3E;6zzS3B_ z0}Qk1G*97AJBGdCCCTag&d{JK)UXWEhe6wE4^Ll=F7x%xF>=_Cx4h*l(gWX{r+`Sa zsRO!Ca0ekeY$l>Zt5Z5z-B_EKax(4Yq#ykc2hRlObPGSMGtl$)xaPB@mxYP$&%}pK zgtAnH(w>r^*p9i?SpeX|NLdcO)4IITkcXv^J*NmFxOIH)Zzqu!85cA7#72y9-1Frd zGi2+aZB3`5$kw0M_K^2*KxitCAVZ0G7~{t~I1*i(~A6VJc&>lSi?-e5Is#_QOF1z;7Ew6D?zZPn!R zrvo<38iFmJqKaWn7%#w>N_u|d36QttzN$mMqIyDl=)v-F(01}DSV=}5C30ULyZvsM zP;tZf+5<$6w_HA3>$aEn%4#w!=DS=6;Px4l+UM&AuKV6M;ghiAXGUse%?rtjJc`rO zJXq+!|8SGp9n5a{LySCk`-)OM^uu~*BwA#k=S!+?*7UL{ch*Xs%J^>fJ46F$?7}Il z^m_B`oSYZ@vkuM^#aP%0X6{t@h{sOv!xZy=z7IWf?7tO~d5Q^>rbB9lWeIga@4p#eQ{F9CJRG2 zGc>tqNf&?Ss+#R@o}%;4uFStVHM@_&UhGf9;P%kzwKdyG**l2mxJsC9@&dAwE15%Rv5;Vh3sPD1qHV600UifGf* z*zNNoJMK(%dLM?nn24emCPGYiF1q$a9ie1mV`I-%x{pzqria}C6%OrR4brlN1~H;; z+>yZ#1eGL`0(u2l<+2&*HTL~)KkObWb*}rQ4@C)cTrv1nist_QNtY^|MX%V&7@ea{ zGL$9Xv$l3?W&YX1gMf#133^)cptACEOM?-9p+t4H!qL!ka4iNz{ZA>eD)|y|Ak&J%GP8%3qW1R$WKq2&4K66|<`#=Olx!d0qlgDR|3Enhcs z7AA`U-syuyOSfT8Od4mMf#fY5y(;W;e#}QnMMZIh&(72V)W>M^j*XL{*3w)Kmb#*1 z`?*^&-xx_lnq+|f37fZQd!8bu^P5TDlM)WZP-toz7tar%$*(e-@B16GGP7t4!Lx_) zIb*ad5&Xyiz|aH4)YJZgF5&!S$Q4;kO-;?&$+XL1p*-da9JA|km<&e~UQWk7s6Exn zRLzUCz2^Ocb$J|PuZ`~E)$nw*jfM)8F)vv@Lz7)v4DNv`F7DO%Dfq5? znEELkby)O>#hY70bU*kij2pjPfLMHi(H@^P&+d6GA5G6=>s|1rqO*<_zR%vm`?B+% z6Hd!TkylGI++SH6$cUO2fsPY>k7Uz^PckNEDFRPj^GNqo7)h};;~Y+x@q8Rgt^StF; zjH!wss6<}{%`oc~Hj>mZfMrlqhBc#$^+^G@Lm1Mw!$dMMs^#n*_376S{4ba%=7q@+ zN=CmM_&?N#igUuUW*n$Oly*hgzjk+rZk=fh!wm5W{99H_2vSRx=xn!s;&Vcr^QRkZ zS5(i*T!q=fcu6kZYgrC11m7-r)2zW*=QbSOkfkB|Yi`a!5dK?+BW-XZcDg`cLJ+zs zL!ZB2cKlk}_aa}Gl27G4zlSF#PKMn93Tirgw4A$=JokzHn+p$wDgOfp?&kVyHdBt_ zxk2qXws%QmMQ8vZ($mue$Ov3tzm8Dj4g%J{E`Yh9J^<9MY+9$UQi7^zl6(Vt6fo9T z(51Duc>FP9#)a(%_gNqnPqU{+=zy}sZ{giny_nsNHvDrxp{{pS_b$suD#NZbq_S@! z<%W)clCj1QPNj-ThIW?k{4*h1S`AIkiG9NPrb5=1mWp~M%JAnP4BFwyu~tsr9=i|6 znIAfjJ~v2Fvuu;?slRJ_0U9(^`cJAe@a`Wm zyuX#yd-`Ys$e7TO6XksPbA6Mjw<0xpFOM68S(4>O9g7w9%JLQ9P69uGN$|3$+)8^{ z=wS2t8G#g~H0y(Ips*{BYhzKQ`-VI;t8zP~+@SxRzXTISXP+2(9@+taskKeRVZWE2 z6p%e1`&2OJ7apjVC2wCA^aCoTb>DPXgKSmk0B-_a*wa`1$a>@QSYi-Cup(&7ZKd}} zegCPuHe4FI$y(udbmvc+lT@I;epLNS8I!3%@(@?@;1-jNa9VG`N@oe0N=2V~g^8qjBa?SNKDL0tET>H*bHEsn1A6{*=4*BYC+LLkO zD^>-n2Q9WWQ>*`r1|*0xB0PU_oCaTwQMnF?QPu6knD2!nCq#%zhM2_FLhRKAa-_`BGY(J&AJGMJlp5zz>I#Uc+mG#8oUX~v+3hC`1+D?T)vR+tbMJ)}$ z$@b&qez8M?PS-ip>tvOcAWKrG5LZJd-Wj0u>A#1H?&jN9hZK}0 zZ2n8BJt2Z`t%R<;XbVwB%33s2!F%wNU8AqRV8daABXG~3qxe497s!sPInqZ9aBe3$ z1bTMN+nVXA>ljhnjT?PO^@L z1m7r)QOel}WIY=`<4$-)i$G-tXW7iZ^O33eczf57KSW9`7+KM~u=#aeijV&AbVpF& z{$tk}`Of=VEUNN(uBV%n>M!DGDl(VGf0EO!G23N)S!ky3{yI)RGe=w$I=@uX7Ve6o z8#zfXYB6m$%%VGhl;g1!-T;LD+xwrG$yBA$A{Sh8Sr;rZ2#MIB*=ZA9UfLtOA^h7w z;iQR;L&N*avKbNr{XhKgRE&Id(e4F@IN6+;@~b_T>ay?+OTxpYbtC)qr9Br-YSj8I zi5<>9QD(fy&SS9*w6hYwhCB`wm2-I)#P=1BTfpr5-6N~kQhkCuSXX+jZAzcC*ZPki zwbzACo;u%fKVj63g>$}f#5ZrS%FyPeO|XLm#hchbV}s|{w~azU{zerrVKx_H!L;T% zF4d3jEMfiqkGm4ETX5;HDKa!!(4XB6;=EQ!~yigXp;;xm2aHcV+Icc{p=Kc*9B3 z^7K3)mG!Ojn}`u+W>TuTZPN@|(&yCo4AM~2lEgyR@@6iTZ2q&CZ5;jCSM+Ot-eY(y-RCJ*a+4b8O+EwV@kmjA>foV6i`Ed zFcV|-#G-0waM5g-C?5XR`6XL~gwRb(`nlVYkj~f#5A($=NJ9uH;Frvk(I@g_yu8;7*+Lz3M{r6L(vqBo6 zkq28h_a;okMFqJhc8qw_bHC>eOW|nFy70SKy9-0ITr1D`8XNFtPK6FDzJ5ZixscF} z7JVU4Gk=hyeOF{8HXPuj%+mPfyNqYX#8Q4D!R=qKY$bA<&!1R(9pOFhxR(Bl)pa+@ z$1SyIAeauXe_i2-nlQ6#I_gRK#3#D}Tu{#o%?}S6ZnTvy{1||B^0*PDZOXilv-;i! z3%>8c_qKA)&AT9?bnEIFmt>G2^4WJF<>eFV9H92?n+|i%Sc1uS7kx|bx`k;R-breM z(d%X9!7k2KSFXVf-NnXCGJ3XJyo%FAGQTS7E}pyWXyuG3~fGJpY)xG9maR#kXYE!8TEb zym@5dvOZB0tG83`Kdwr&Qqy+K%pii_vf1XQV*1 zG3B+T7c+w99qVl&HSX63QH&z>yzlpKEBFA_>}2wZbHN$-?@^~I^s-fE=j{Vn)^KF( zs&bBYbbc-|Eom4{RM%y-$Kj_JLz#6w{v(^7_l1rZ7cKA0@S~=^=id}ciL}-h#Q!=V(3lSos!Vb_LEW}}A-jNg#5;fv_-)UkVadjS>V6Pqze1LF;Pk=G1M3F_2bUj->Qc#A!2^9+Rxc?d};Vkab%D4 zNj}nRT00~v<=6#*6J1;zr3KJE|^=U z`N(IzB?g`~&VdDer~=iuMXMYLmIU(Nc9x`MCUc#rsM@$6JZ$AEk8T@L_n3OZ)T6g#)>7 zhc}}NWCK&Rbc@C#t1w$0ANFUMT8RK>dbn1SJF3eHsHzd~)r|%|+6`LLz+D=(O5I|P z3{7uwIm%{MnIZmI@EfE(YJLsN7Wk$*0FL4}x;NBmZPJmZPr67f{fh-$HfC`66-D|} z@~9DM&$OvTJJWV8D6S)V{l$uHP~%{@Yg^nx2v7KzQnG;x37Rxyw%fu(ukxV&Q$7I+ zhHxj4(y!ps)Y~n^+}L#-mS^@|v{H3=n=A((486?$640*9%*>#B!W}@Y`R=)tP2iuS zk~!Yr5!!)2SvG{%E(kRr2+0Pc?5Le6jDGVib(QirLdtU1jJ_U?y!ih5g`0$)auRa+ z+s`0LBf?RecUR6ApTJdKuhCWP?03-1+6keFbz`n68xpJpUEvmDD%yv4?Pd^>itDUz z4%{aggmk4hslAfHLR5dhA?xBvGD6}}k`g#JKyoCk7ZmCxS6>gEIUVV2P& z;5W6nxzWz0Z_$wKnvP}``s^iOt}k-L?(OEz)Zs4}TsLx4<~2`A4OL8$9%;y@_>W4@TmiomyPnBPWPgWnN zwl_!OqW1Be;!VG|*R!ZR204H-mctV)%ia#{JGn+;O{b*_E<9*&oT?YQkUROB^A(@< zvQHi67C-d5Qab~qtyxcJ;Mv#`BA+DC@RMF%+EP1Vm8ZYEs_0ktQj)q0d9Y+V1MBTM zv3*YXe2drV@SLTVCEm6;*OnZqB|8X z1U_8jqh#VsZ+=`Pp;;xwvV6w`f9XQtV?&0|u{-4}Ljsvoy0nK#smaR~UFCPI^-4D} zXx(CQ(GW27jq1awBfDIi+=HSXvqz#l&gRyuTM(qrlZo%-gKX8=KGc9o_z4H*?kgWn z$mn9FV(DQjyL(qh!h{AgkBpiUl+JF4b5#VXH-#Drz8xzkN->%CS?9CfGzahCH?dO# zFe1y$(L7txpGzM#erv1$%E2bZO0H<0u&&X0Gkbk010o81w>t7Tn*9cST=SC9TmwPl zry~m#!!KLLZJu_{Q^^mc{jRdG;PZ7E<0$yo)lzDqWUXNV|Jcdv%tn5ckfe1EsW-o9 znOs|=xuzM4G5OlOTq8BFsA&zlsDk}v9;r9!2hTcoK1Y);SzhB`eabS!w;brfuH<;F zfwR)GA!ITADA;>^SZ!!2#nN?qPv||)^r2&U1l&s&&n>71t;*UC+r#WFnalR zQO4ei$41rOjjn&XG^Nyb!CfE+eK*3S2lp*ww3$S8#>dQx#<(x%c$sf)fAH%N>%#am zy&Da{cNVx;q3Ay4bTMCUP`V5T0eeT_?I<$8D_|2EzFmC}R_F329+=WHK zKFi;LG6d+ED#gAIp@W4C22V=ydKj6v5zjkQ+`+{uy1MB!L|($wsB^$uB`~mE?fdf@ zVB3jVj9Sgqw=~Qf+Q;llQ@sDs<8XLHFArd>L)EjOPQheYfom9#12+(XFgyz*g*a)D8Buzc^1{6t={23>Yo4J zOF|M`9@f~W94|2KDzUAy8bSe9#djJd{^7;Ezvbe}(-c(K4Di`OtCKyj>1pX%hc)qO z1=z1TG!ddMWxuodwCQcg>hm)Cs1#|9?Aooj9VmD^RjRJdSAzG)2mXc-|zOl8kC)0WKsX>3cUQvyq4Swyr^@sk89L7N`o)buZ1~( zT8{f0C)>H%!qG$8swiQYjq9t1w`5`L6;Fs9lgXr_uM|aiSQvgR8p4uBrIKQ+g)GK} zmS$n3zs{d9^oAJPrOSMcK7Db<>dAH)rMu$?28Uf4XgDFRcY(){F>sJ@<`EXJ16e5* zZoZg+A^Xs3IY2w7*m86wHl-o_mg9H9A2;?s=YH&4dde9&HFD+aX>f{n#;1{wJx*(+ z^c^M^UbZ^^`XN0Mamx8Y(uSZX)e$jMrzx*%qFYW`aLYKf!eyxk-60Nazp{tltxN9* RY@!7{dZ?*fp=1&Ce*g-YlT82s literal 2574 zcmZ{md05if8pjcF!8FtqZE`oWu#ys7Py<{L_a(*5tu!@rOiP(86a<^W%rU2CGPhB4 zOv}k7OS8OyWiFXvBSvP3xhv(G3-U`Y&Arb&&;8^6an5@_&wJkY`~BlQ=bS5kK5l9% z->HB=AT@WQOMonn$_7VSUN$2l%O+(Z-p?!0mBZnn(dfXyKwDc|3UJ(QNwl2n4-^#(o?O;6ShmQXct!6^zW(KN@lO5Q@8mzm zENm<-^RqMram=>z*+b?w=wpIiPb$y<%eegyQ6o7k+r1x=akeX_AsUUiplUZ@3_as` z%;uaX%9-rnD^85!D07IM<_8NIAyc1s|AJ^11lq50$p6KZX*CPvh_OAI@(O*trX(#{5ot zrd;H%UQ?%gpN7Q7tz#xgf+6+N+fUG22@U|ozisI3jqvEbEH$s@VQ;;U0GI0@&0S`W ziE4)P?bUT(ST;VG2?G4ua$7sE(&5nLt$uLi*2N)SGi?V}%Q9*^BHdJR}#b3!8HxH`@&Nk`7m_VBPAIdRA4p;K2qtz5~L(on9jm z`w+UeY}+i!*OQTDH&B);T9FfXG4!F6?CQHG-T?xuTkwl{V=E?Vq;?}QC9eN4zCq>i zM3+)pTyQ3ZVgHmV*zsX#|EJ$^CHSS^tweT&AbbHZ550Z;qS?ev8i64>-us&9cX-== z=}lZ6|NdXIjnaHq;fZZc8J0i%8G5sj;}KvU^%HchvF93I*CB9xt}@HTi2l`PMQ;`C&J^ zwNMmYZtbaWB=w?Oi-X>kDA~2*+^DAuvGIN6eRp03D0Q<_rkVD=Kw?k0SpQARzO=2- z8q*OI9o@ZBd1&gyg$c0yWN?A>&$^wZoP2Rie;C!x40)Qm6V;O9{hvjFqaReYh)q!p zpVc5MWKArT;@zlj5%?T{2jodqbam0}aTZgF(6{h7?jzlQ#eP+E4RZd@;9_PNc37!7 zUXquW=ysbm$9bQSMKTHQlV3oW>RB45{hr z7GaHVoIx>Koi4hzJHOhjf@bCOwFgDvdkJYof%d6s=H}M(+%%dgW%{^K>?A0Ri-T>B zn4M{U=5y_fAdZ)sTWUBbfhkG0wOWi88T8`TJv=+*EF5`DO=BptUM92S#8?yol~>Rd_|MWTLGx%Ba*T zb+WU|_6iJSIp6R$u7m#%?gyrnc{>T;%t0=xD>_k?ulpq#{*PX&;y1h*)ULs@#tTq% ztacbtflr0i{QyTVJ%Y}?E$ZQKs)cIwMOd)yz)O!Lkv)IX4xE`vu+V(`o?pe(uzk(n zQxlr7M;HaEYnweKOPt6o@79qM9vz>Q%dx8b zWK|3EQ+q0|eT;G6A;oN*i~BCM**{K?d}lB%s67^V#8_=8mIlT=!X_Y4i@}Y_T|wNW zcKECB!A)vbp?>2cm61-qQq4~S;OW3%eVy!T4V!e0c;Se>Xl%`^zjLW1&!NGzs1L2Eui*I8j#m}YTD1XrgzeM< zX_0$-zPE8N6omX(c?dvb+T$WQR>4TG_) zY|Hmh)T7-10P!}ritO;N)w)?`@OQM@gVVhi^k|W< z6{W+)Q`2wxA5#Wr&))=d&B>V<+6~zoH=_fV$jVw&NI$l*E^`L%)({Z80iiHpw57-L z{V0MPHCby zM4qYi^-=%6+tYVL9Y3PG96}z*P?9PQJq3g9wON*{Jsr*SnYB4?J#JcQz O1#)-wajA1U!T2|ImfiCJ diff --git a/icons/turf/walls/wall.dmi b/icons/turf/walls/wall.dmi index 5df919097cac674b9acda380097ea61274bb63ad..651227fe8de21c4cbc5c30a99b9be907371bb5bb 100644 GIT binary patch literal 2871 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 From 78786dee02d6fa4b58a9e93d9327623d36b5680e Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 05:23:14 -0500 Subject: [PATCH 08/15] fixes chasms --- code/__HELPERS/icon_smoothing.dm | 1 + code/game/objects/structures/window.dm | 3 ++- .../game/turfs/simulated/floor/fancy_floor.dm | 2 +- .../turfs/simulated/floor/mineral_floor.dm | 2 +- code/modules/flufftext/Hallucination.dm | 2 +- icons/turf/floors/chasms.dmi | Bin 2078 -> 3329 bytes 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index 09a07b8a2342..c2b3a7f86056 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -653,6 +653,7 @@ xxx xxx xxx name = "smooth wall" icon = 'icons/turf/smooth_wall.dmi' icon_state = "smooth" + base_icon_state = "smooth" smoothing_flags = SMOOTH_CORNERS|SMOOTH_DIAGONAL_CORNERS|SMOOTH_BORDER smoothing_groups = null canSmoothWith = null diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index f2c72e76b83e..6d26a188e3f1 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -621,7 +621,8 @@ /obj/structure/window/fulltile icon = 'icons/obj/smooth_structures/window.dmi' - icon_state = "window" + icon_state = "window-0" + base_icon_state = "window" dir = FULLTILE_WINDOW_DIR max_integrity = 50 fulltile = TRUE diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index 626efd2584bd..e1a2c1b9174d 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -506,7 +506,7 @@ /turf/open/floor/fakepit desc = "A clever illusion designed to look like a bottomless pit." - icon = 'icons/turf/floors/Chasms.dmi' + icon = 'icons/turf/floors/chasms.dmi' icon_state = "chasms-0" base_icon_state = "chasms" smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER diff --git a/code/game/turfs/simulated/floor/mineral_floor.dm b/code/game/turfs/simulated/floor/mineral_floor.dm index e1d29e00f9e8..a53f6eb2e7f8 100644 --- a/code/game/turfs/simulated/floor/mineral_floor.dm +++ b/code/game/turfs/simulated/floor/mineral_floor.dm @@ -196,7 +196,7 @@ /turf/open/floor/mineral/plastitanium/red/brig/fakepit name = "brig chasm" desc = "A place for very naughy criminals." - icon = 'icons/turf/floors/Chasms.dmi' + icon = 'icons/turf/floors/chasms.dmi' icon_state = "chasms-0" base_icon_state = "chasms" smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 8ede48819396..caab0a55f498 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -1120,7 +1120,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( name = "chasm" /obj/effect/hallucination/danger/chasm/show_icon() - image = image('icons/turf/floors/Chasms.dmi',src,"smooth",TURF_LAYER) + image = image('icons/turf/floors/chasms.dmi',src,"smooth",TURF_LAYER) if(target.client) target.client.images += image diff --git a/icons/turf/floors/chasms.dmi b/icons/turf/floors/chasms.dmi index cc4feb2e032cf2ac2494acde4b5dc432e3fae25e..2e93b5391120cfb7936249cf244dec9f5031baf2 100644 GIT binary patch literal 3329 zcmZu!c{rQt`qnW+t4ZTb7pX1P%aoaF6Gc;LNHyrDmRf5GN(rXc(8QL}%p?@Gl_<5B z+D5e)dqQJtsid{EwkokiR0T~*B%E+&uHWxm*ZJdnpZmF=`+lD9y}tMTpkr2@-Y> z?uNOw)P=bZUXo-z+{OWCd^^M=*e4L@6W}i;g@2r$Zgk)tOy1=E*F(TcJu|0Dt#NRy zyV~7j_g{W@UGweYHBawbs~_rSe$o-4h*=yDLbfZWan6%%3=G`?i^GJ`wrc z)Wl_Mve~Ec_p(Q$0A-=-t*XGV46;*S}QyuoJw(#)J&D}!=!vh`$ zA664^GNni!(SxvF8Q|n+> zG}$CHf|S?Ap?41{cl{w~E}k6lR#~8X{P~o9aEoQXLEZSEYvgPeep4yJGs6_>Xwk1$ zR{WN*a`TyyXecxwA>PXl4S&}3*7{N&`?{lw%2_+_&e@1mFp=Nd(E!dW4u9>#EosUg zvptZg{7uHz1BYp$rgv-62z@8Tcj#2H&=BxfQc1BHWot9wc`+%2}DulTXeb#F|^J( zgQ}!~jteTFJ|NqzSE>S|gtx8M^j~AKiH$+r*22K$u0*x2&Q+a0zX|>e$dM*YZR$YK z0aHb$Ac)IbufNOgl4oDEPq&+~pyTVm#tao9`r9iCde5RT3-dL1+252dI%9PJ;Ullg zsWpudP6)uTSh0q!`^B}373YMGjlY?=`0bIf%4*RO%EiKT!`~zgz@rU|mEdHsLSp$s zYAtHQvNx`OK)VkyWcEXlKv8C4=Z#F@z)d3i|2GuJgX3JGP zTK6bWdTA#6fMjuEaO*yMKl);``Y+B@kZ$R$ldQ0>UPWf~RXy3u6jPsY({>1qHL2qkxp0d19?jXDPgrLX@o`$3Xg2M|>`%D5mj^JkB=J~=H z1>v;_)>7l=MJ(_jC-oaH_51FScsp;>UQkBI_3e9X{V4NKR;pTY*;_w!hb-z7O$pWN z{z3(-T&GD{P&qfV-(0mXb{Wa(y1sq9DZ1F%F&n-;EAiaY>*$2i~DTaFco1OTrXIP$yV08n zf`*dBOObm;VaTUKo_lGl09uHeW>89hSXDYz|5wu56nb!R<_$6Cufb^&LLeWySpBjqp7xL0;bVv$H$%ZFWJ%JmJECqa5dbNi z8{Izxf3VJx&k@v;jo#ELC^H*H8-y>@K5$gYh)`|yac|N$fd=;P%sKI72+Kd(tb38N zFE{&>d|}v{2f`6$wz4Dp*2x^pZHb}mc#79@{u?KmMd5Dw!+J>Y@zDcZ# zf&2@984UlQBhbvRUUImcD%s=P?h&XwfD270GB&GXH-*w`EDk1t+ywG^_8{Y}{hMp{dtc6;mwdR! zaJT!AJH!h5MfuZ{M-7eN3`tvyu+x~%J6Du;C8Q`kn8P|PqWgrNPJ6epa%ErwAQJdl~?f0J!`B{cj^RNt(L?hH5n zJ)dEQN#MD?py6FmeKVbIK*pc~bdhosoSb^z4W$7rWm3YlBM{378bV={C;s^{33aF^z;N=9tX_)XnTh0hBWdy2(~~+JY!1lr7`?7duE2ag;$YRFj&F=; z_~mN^%Z%pd#*LF)4<}$ASdY34Jg#qGB{|R3t3>sjC`*}%125fb0a})Zy*fxn^<(NME&Onm!&G zU|1X3!~68p_U*Y%9m5s1ZW2{QeFWi2z>a$tycbcayI|i|?`zOTGQ0iSR&U=f@RNW` zhBJ6rJCPvhp(uk+Y(RA;b84$ZQ%W@0K*3&u4=1JUmNg^8m=-uCX=uWh4YsgT|ovVy7U9UH3M|yc`*3Ox*p)c)K=@%%1~h)G65Rz>!=@UoC2M?bbxOvrb6?kQ6$kq(~!MLn7VMml)m zr6EV68Bo_F|A=;(9p7y-{2yQJrV(8m@|UY#hd7^D2FW=3GMYsn1jPe_P#jmTWI_Yv vE0oVTjvfaIsY3S@_iD&j5Fg#YynW@})qnwoSEZ646{#yG=EhYQ-R}JxXpE=M literal 2078 zcmZ`)YdDna8h*d|jLFy^GEOy%K{mRQLum;~IhJH-oJy;ia-3QjIb<29kBQ15tYl50 zhFVcMq*6*4+7cZ^#vx)zl2hXpW;E8=?dw|m$KF4l`+n~Cx$o!s`JQ)maYXRz@c;m1 z?;g7Y66UU!6f8l`%qdd|sku74+jF^GJRYyCtgNG>V`OAxVPUZ%u}L1Y4v>g9On2{aJO41>Poe>PcZ+YqOD)0@)3h1DDRLa{oYv1`rw0VA^smCPR zP>4Z<7xp5dwCs>(-%0buHP%{QDZXKJOE;>I_}<#Xf+5XU!L2=G$)=xnd*$u#w7U5K z$rSp{R=Bn+=OKnRSCs;NCseER>uzeR?Ke)QWzU__=nImOfc|4era=L&u1R64`KztW zOtv;h2l|t)qciA0NYhgDw%J2yaSb^=n^ss&oM<)Mvf?qWWqAtU_8DuxGr#c-#j?mh-;A;A4HrE1xBdzjftk8sfx^qFfHTe$|`^9I^v1g7x6~DCk+mYqlNpwYW1bbze(~K zLHq>Ts2lj0Uj|L#%gB*gf(KD7g}fx|gLqAwlG=LpBZDfe0Wj=@dey&UC{Sax>-o$| z%j*lJRatm996>_~ltp@=iR0-rF-J{CkZ>jDA3mp!20NHg;1FzA7?ST7Ws(xGR%P8s zC#dR_2k<@hOIO9Rri<`6cyI{J^hs(bfNJ$soS&-^SOTWgcOIg#e2Q+ z>~T_w697By^4Ny!1?MMk4kDO4afM0d(Y^T}+P7vSx}|*&-N4{4$~5=x;1-$Zt;C=W z-X>5OD=vk{>$tUMYv&m!82RPdMqse@UhQGxjIbBODqymb-5jo5?i3!=#euv^AB4|y zF-*QO&9jc4mI9lg+-iv4)H}417MXX^QiCppqI*wEyZ%o6An)BB{La-HVpj*k7EuS< zr6V!mk@UBYIBp3krTLhHt!liCV8;_^H~05#m&kZDa8CZ64^ffABAP1W>U0T0*G733dsX-ne)hJ6lWQo_ss; zDl*^nte`;9QVvfU0V)*+_siiNe<@liP@_76?x&vLYQze_l-MP zP3M=pF3c*u4=BS(MbE)!L{p6vP;}i{kwY5T z{ARszMdk%%8MLV!9&)<+FcTQ5mv3`Lrp&EU8a%O8A8H1g#{dfZq_aM_RDW&_J9mHF zKL!=y0Tt)X1GJ1elhkqFlnFkWKhQx&N)1~nVXPkUx!d8?L_sUqp8wbb+9Yd+sas<}@(Z$!Iby628juIYxd+eP*63}Z6ki+2QcQw!}v zgpeOgo)0gQfOrwviLSQt;Nc>kBg|Nsj64$|=bG?}eNmUXSjH@(%pj=h5PPj&-4Kt+ zh61}3w0V&2y$VM$n8n5iCO=aY|D4ph0kC=XScUDs6VC{56;V#@ z=-wbT`$ob6(4qRzeayna|KbW;vguE#Q2X@g`DECYdgjNp;A&8e9!aR(K}g5_v`Pq$ zQ@du~?io~UWMkD90>)BZ$)<6I>Z`NwS?6Li52#mvND;6Z+iaK>bqke}6VBeXR~tXM{r`JH!RJ<2+&|EElUVmT+j^VE`zfSV|(<{zsBgd~FVO z(WC=cpi%6>$QECl!Bs9dFwl6>N-6WY9=FJMc-CsUDRiYya!`s*TMfVbzT#QaHoLN= z=6h7!U{h#pn}EqWOf`Mlui2%Fl$*Vnx38IRN%|x#>}P&z2wfsGYPQIXmUNzbP?@m`YXQmDR;&&$1l-$1^zJULXxZw=h2bJy*uYyZp4mV+AHeVbn)9Q jFWpH>x$}D-^HV&~zjVtZks6;W`M<$ldl$Ph8(QLDG6BqK From 85b0a32f2c6138f17591df05cb28f1aadf617a27 Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 05:35:25 -0500 Subject: [PATCH 09/15] this fixed nothing --- code/game/turfs/simulated/floor/plating/asteroid.dm | 3 +++ code/game/turfs/simulated/lava.dm | 1 + code/game/turfs/turf.dm | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm index 4f3960ac2d02..eab099daa196 100644 --- a/code/game/turfs/simulated/floor/plating/asteroid.dm +++ b/code/game/turfs/simulated/floor/plating/asteroid.dm @@ -7,6 +7,7 @@ baseturfs = /turf/open/floor/plating/asteroid icon = 'icons/turf/floors.dmi' icon_state = "asteroid" + base_icon_state = "asteroid" icon_plating = "asteroid" postdig_icon_change = TRUE footstep = FOOTSTEP_SAND @@ -97,6 +98,7 @@ baseturfs = /turf/open/floor/plating/asteroid/basalt icon = 'icons/turf/floors.dmi' icon_state = "basalt" + base_icon_state = "basalt" icon_plating = "basalt" environment_type = "basalt" floor_variance = 15 @@ -146,6 +148,7 @@ icon = 'icons/turf/snow.dmi' baseturfs = /turf/open/floor/plating/asteroid/snow icon_state = "snow" + base_icon_state = "snow" icon_plating = "snow" initial_gas_mix = FROZEN_ATMOS environment_type = "snow" diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index 42d75d1e52bc..55b554a113e0 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -187,6 +187,7 @@ icon = 'icons/turf/floors/lava.dmi' icon_state = "lava-255" mask_state = "lava-255" + base_icon_state = "lava" smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_LAVA canSmoothWith = SMOOTH_GROUP_FLOOR_LAVA diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 3121733f6fb9..6cd10564bdfb 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -551,8 +551,11 @@ GLOBAL_LIST_EMPTY(station_turfs) /turf/proc/add_blueprints_preround(atom/movable/AM) - if(!SSticker.HasRoundStarted()) - add_blueprints(AM) + if(!SSicon_smooth.initialized) + if(AM.layer == WIRE_LAYER) //wires connect to adjacent positions after its parent init, meaning we need to wait (in this case, until smoothing) to take its image + SSicon_smooth.blueprint_queue += AM + else + add_blueprints(AM) /turf/proc/is_transition_turf() return From c12b970daea94cea903685255482f9d355ba777a Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 6 Dec 2023 05:58:32 -0500 Subject: [PATCH 10/15] ??? --- code/game/objects/structures/tables_racks.dm | 21 ++++++++++++++---- .../game/turfs/simulated/floor/fancy_floor.dm | 2 +- icons/obj/smooth_structures/poker_table.dmi | Bin 789 -> 4825 bytes .../smooth_structures/reinforced_table.dmi | Bin 842 -> 4855 bytes icons/obj/smooth_structures/table.dmi | Bin 670 -> 1144 bytes icons/obj/smooth_structures/wood_table.dmi | Bin 670 -> 3703 bytes icons/turf/floors/ash.dmi | Bin 2721 -> 3817 bytes icons/turf/floors/bamboo_mat.dmi | Bin 5327 -> 3843 bytes icons/turf/floors/carpet.dmi | Bin 3405 -> 3481 bytes icons/turf/floors/carpet_black.dmi | Bin 1828 -> 1907 bytes icons/turf/floors/carpet_blue.dmi | Bin 0 -> 3273 bytes icons/turf/floors/carpet_cyan.dmi | Bin 2675 -> 2883 bytes icons/turf/floors/carpet_donk.dmi | Bin 0 -> 2842 bytes icons/turf/floors/carpet_executive.dmi | Bin 0 -> 2535 bytes icons/turf/floors/carpet_green.dmi | Bin 0 -> 4382 bytes icons/turf/floors/carpet_neon_simple.dmi | Bin 0 -> 12520 bytes icons/turf/floors/carpet_orange.dmi | Bin 7950 -> 3293 bytes icons/turf/floors/carpet_purple.dmi | Bin 0 -> 2427 bytes icons/turf/floors/carpet_red.dmi | Bin 8171 -> 3011 bytes icons/turf/floors/carpet_royalblack.dmi | Bin 7139 -> 2967 bytes icons/turf/floors/carpet_royalblue.dmi | Bin 3522 -> 3133 bytes icons/turf/floors/carpet_stellar.dmi | Bin 0 -> 3176 bytes icons/turf/floors/catwalk_plating.dmi | Bin 0 -> 4353 bytes icons/turf/floors/glass.dmi | Bin 0 -> 8083 bytes icons/turf/floors/grass.dmi | Bin 0 -> 5205 bytes icons/turf/floors/ice_turf.dmi | Bin 3120 -> 4712 bytes icons/turf/floors/icechasms.dmi | Bin 3080 -> 4129 bytes icons/turf/floors/junglechasm.dmi | Bin 2461 -> 3648 bytes icons/turf/floors/junglegrass.dmi | Bin 0 -> 5209 bytes icons/turf/floors/lava.dmi | Bin 7740 -> 9697 bytes icons/turf/floors/lava_mask.dmi | Bin 0 -> 1628 bytes icons/turf/floors/plasma_glass.dmi | Bin 0 -> 2695 bytes icons/turf/floors/reinf_glass.dmi | Bin 0 -> 4257 bytes icons/turf/floors/reinf_plasma_glass.dmi | Bin 0 -> 4047 bytes icons/turf/floors/rocky_ash.dmi | Bin 8601 -> 3881 bytes icons/turf/floors/snow_turf.dmi | Bin 10907 -> 5159 bytes 36 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 icons/turf/floors/carpet_blue.dmi create mode 100644 icons/turf/floors/carpet_donk.dmi create mode 100644 icons/turf/floors/carpet_executive.dmi create mode 100644 icons/turf/floors/carpet_green.dmi create mode 100644 icons/turf/floors/carpet_neon_simple.dmi create mode 100644 icons/turf/floors/carpet_purple.dmi create mode 100644 icons/turf/floors/carpet_stellar.dmi create mode 100644 icons/turf/floors/catwalk_plating.dmi create mode 100644 icons/turf/floors/glass.dmi create mode 100644 icons/turf/floors/grass.dmi create mode 100644 icons/turf/floors/junglegrass.dmi create mode 100644 icons/turf/floors/lava_mask.dmi create mode 100644 icons/turf/floors/plasma_glass.dmi create mode 100644 icons/turf/floors/reinf_glass.dmi create mode 100644 icons/turf/floors/reinf_plasma_glass.dmi diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 6299ee2a76fb..3349cf16ee1e 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -16,7 +16,8 @@ name = "table" desc = "A square piece of metal standing on four metal legs. It can not move." icon = 'icons/obj/smooth_structures/table.dmi' - icon_state = "table" + icon_state = "table-0" + base_icon_state = "table" density = TRUE anchored = TRUE layer = TABLE_LAYER @@ -254,9 +255,11 @@ name = "glass table" desc = "What did I say about leaning on the glass tables? Now you need surgery." icon = 'icons/obj/smooth_structures/glass_table.dmi' - icon_state = "glass_table" + icon_state = "glass_table-0" + base_icon_state = "glass_table" buildstack = /obj/item/stack/sheet/glass - canSmoothWith = null + smoothing_groups = SMOOTH_GROUP_GLASS_TABLES + canSmoothWith = SMOOTH_GROUP_GLASS_TABLES max_integrity = 70 resistance_flags = ACID_PROOF armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 100) @@ -350,7 +353,8 @@ name = "gambling table" desc = "A seedy table for seedy dealings in seedy places." icon = 'icons/obj/smooth_structures/poker_table.dmi' - icon_state = "poker_table" + icon_state = "poker_table-0" + base_icon_state = "poker_table" buildstack = /obj/item/stack/tile/carpet /obj/structure/table/wood/poker/narsie_act() @@ -370,46 +374,55 @@ /obj/structure/table/wood/fancy/black icon_state = "fancy_table_black" + base_icon_state = "fancy_table_black" buildstack = /obj/item/stack/tile/carpet/black icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_black.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/exoticblue icon_state = "fancy_table_exoticblue" + base_icon_state = "fancy_table_exoticblue" buildstack = /obj/item/stack/tile/carpet/exoticblue icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_exoticblue.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/cyan icon_state = "fancy_table_cyan" + base_icon_state = "fancy_table_cyan" buildstack = /obj/item/stack/tile/carpet/cyan icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_cyan.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/exoticgreen icon_state = "fancy_table_exoticgreen" + base_icon_state = "fancy_table_exoticgreen" buildstack = /obj/item/stack/tile/carpet/exoticgreen icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_exoticgreen.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/orange icon_state = "fancy_table_orange" + base_icon_state = "fancy_table_orange" buildstack = /obj/item/stack/tile/carpet/orange icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_orange.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/exoticpurple icon_state = "fancy_table_exoticpurple" + base_icon_state = "fancy_table_exoticpurple" buildstack = /obj/item/stack/tile/carpet/exoticpurple icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_exoticpurple.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/red icon_state = "fancy_table_red" + base_icon_state = "fancy_table_red" buildstack = /obj/item/stack/tile/carpet/red icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_red.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/royalblack icon_state = "fancy_table_royalblack" + base_icon_state = "fancy_table_royalblack" buildstack = /obj/item/stack/tile/carpet/royalblack icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_royalblack.dmi', 'icons/obj/structures.dmi') /obj/structure/table/wood/fancy/royalblue icon_state = "fancy_table_royalblue" + base_icon_state = "fancy_table_royalblue" buildstack = /obj/item/stack/tile/carpet/royalblue icon = MAP_SWITCH('icons/obj/smooth_structures/fancy_table_royalblue.dmi', 'icons/obj/structures.dmi') diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index e1a2c1b9174d..96b94613a3f8 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -336,7 +336,7 @@ name = "carpet" desc = "Soft velvet carpeting. Feels good between your toes." icon = 'icons/turf/floors/carpet.dmi' - icon_state = "carpet" + icon_state = "carpet-255" floor_tile = /obj/item/stack/tile/carpet broken_states = list("damaged") smoothing_flags = SMOOTH_BITMASK diff --git a/icons/obj/smooth_structures/poker_table.dmi b/icons/obj/smooth_structures/poker_table.dmi index 2e16a5468dcb055dd39c5deb743625272e5d9766..d0b70822771fb5edc117137dfe59ea4b2ad44160 100644 GIT binary patch literal 4825 zcmcgwX;c&0x;>SU1QH;j8D%gaiZmjlA`XzKfD;Nhf}lwY5^QNfv5_Gm0Yp%-qwSSh ziQp`dD1!q+0yy1^8bF1BOiqA;CP2cFkkl=_Yu)?aTkpqx>#g^GRGn4d+220jsZ)E` zsX7_BWt|@091j4{+pykeE7JDQ9@<#sY`*x{_ee9Hv_1H!&;GdZBL`xS9*8*vKtj=l zoI3q97o!Y@(&)@9^FyI!x=z@AmY_Kh`l9XR#y@`R zAolq#xL#48F*fDF>ByFvVv_kru1y}SOdLGKiXZxM{>EY=O7f-^#^uV@LRSjQ!)*FV+ z(L$NV@3UF7`s5xhhWg^d?^9o2C%fAwe)q~{4eokzuhlX6yVvBU{Fh0S$qNhTJyN!f zU*F~ARmrUCd~|R6$oQwvWGAu}mmi~!8GrYZo5S@eEG*U}k7o+uQhzB`E$(EWoF^(n zyS!mX!@Lq)6N>))$Z~U4LB`%sVD4Myo64|1p4uca9>4}Waq@YpW%>$8;g}nH;zQ)q zJCe7KvH}CN=bME7gUe00C5a{~^5}pfV7Jv?_YyXDvQh3;pA)t9$uYg&VLx3_VM>(M zvw^^#$}TpM!09+M#6C0qi}D2DHvQrUmk(yVpztM3zFto_QKXB@d;QEs;MF%|6vU*Z(&1ZYuv%m?d-U;?$i_%rpdZ7>1^ibBsU zCr%w`8~nS3o;uk!X}$_9n3n(3kO zeyRb7r8Llx8(%IvRI@^&Y}HIsX`3y1oB-jJ^l71N;+u2MGpRV#)X9lE*)T?XI3-_8 z2wY7$0#qoZ`5U~48Tawf?b5WAEAT;5eQ1Z2X-a*B`o*=w&eAY6C91416(L1?&IkT# zT{u}nyHcqrRZj*_$)2`6K2xnT=u|}0C{%IMiJptiF)oTyoWL1Gvq+y|r@gT3X^sB1 zlq&cHksS2_s~ha}&ezLMb8rF$@(@mmDX8(zlCzH&W42Mc*aCfWuTjB$2wj%=2xVoM z)!9*Vt>a&WUtn#O2?@DYOEZXG9*5D`e)8!2Y)`vLhx&MDz`05G;;@kANM8@Hn_7{l z%-57soSl?biq6CXa4edD9s#7O!7Js2An+Up*GV1wr zSj(|$;&joo{e2y;EUB~e5Qs~oX1P?Fu5<`})}L7$Ssd%AKi!qdeD(3J^dLi@H#-Z- zV9*kOwLhE*X}L69TIG?+{I=G6@NlQ1+8x(I0HJ-7&p}v61aJ%+yVLDQh#l{ymSSBh zIj0Cb&09(-JON4wpmd((Mfb`;cvPK!2^VS@>>H69ncKWppsY#a3Bwv)>z1|@UxxQY z$YaV_QFg%W+>R{gd4weg*G@i)`}U*;?lU{-{@4}EEgA+Y6y@PaJ7WdvC_pSC^((AL(}cVIh}q7G3Df1^88^ckRj zh>Lb15)}x-&w1|>I=s~4fv59W6)8Ikbf{3aFQT)M(R!wyH!zmx zHdp^)xt0>Zq=^*|+erX?S;Xt(!+ZXh@P8d8WGI_mxS>HO5i zLgDDa0a^rCeweiXsP?}AcTxBCX;0_G*1p0q;p*G(34%?1CN3+qR47rKSSECPsdd&s z^!t?HFCmgKG#PZCOnh*>aua#bhVz`1jR=wbz<-lFW7 z&;fpQl64X=^XS!!)}GTJYUOBF#R7LFSBcWaJnJqt8Hn18^UHP2)WOKSH5gggtzPq*`stXdt8;({i1E%A@5a|?M&vWN5cfc^68+qbEKo2qzX?(6=Ymjn zyhO8z^9lO@2*En97%L}!drrjMyN&Z#7shl)qh-q$pi;D_9@+aB-vQ@XUU=gHv5Z{Q z&#cL+1QV$Phj(hxzp~>#evb$D8P|z6uQp^o9cDOj6tq-_ak0EFifzF4ZC=t)a4&s# zo^rx;Gyzv-F{mW@g)8$3{VF~+W169q9)Q9s4%kf%csa5xKyaqZ$C1Vk%A;0!r3t-< z?Z>SZb)MvS!H#f85&erG+Pdrb@8kDZJJY|aEVP$M&pr%#Bjhe({2#mGn;YBZx#hSX& zci(qjNdNkdwHiWYv7e1=@55e;i39oSU4Rw3*}4*`U|sYR_3acN=pb6qU!Qob{yjRX zuOI><&NJ;ls|n#9o0th&dj+Xh=%`5u^6Y;eKEK+q@Fv_JD0lCMr67z zW9X7sJ$oW%Uw?wj*_()|N6oY)%L?hu3m&m4akNdzpp(!~*mu5FYCbGn1KbtuR0=^j zg+=AH3SU}Gt>{A29l@zvR6o8~3q{3T^XBZ;w`gUnp3Cuc{Ze4hIbZsJ77)py$BUb|EQ_&q zspVpQ>sr{+56biAz{=qc8wO;F4=uK$#={}mC>ItOHEj*T9kE3HGsXi>1Q5JQ)XpbS zMPDhWgTOUne*l~ilWdO**O4ekz+IJipAqK^I%^lb`(|FXRx~RfbgCJy_}PJ3HkXHU z>307ygj!><@F!Q3S-;9}pRFYj`P`&N z!WU-5jgw(c9NqCc8(kJcG)KK_ltSo}qM^srQOy_d&CEPhuu}9Zz42>b65|UaZEr2~ zLg`vYaxVq=)jp6>S{BM)%K#IoJrB<#`4~}cvg|o)NI@XOy5zqDY{Z^rdGw4ZKWKDI z1f5D5)SWR?DwZBbXFZMCBdkoE_`EO|z92>b-kxOFf_qM3VRNPFvON!{mP}f;Ue?v)BTpbMHhb0Z9&g*^0Oq3VC%|>afKd)I(%YL0kG^S4$U#6gDE5BzWy1Doq zW(wKY6X=+`s%L`3Telb!C2GbHSJX-JS4RR7YdK<4_z_L1Vx2Xo zIpf-o54;qB?{6YJKheXk(r9A@+WG29le2n1Tpdq0a8pSaTU*4#txDS=L_YhYD1a;q zk#}zY)`xOg^jeD~|MmGB#PK>vfRSUS+ z_XXZWP(Oo|p%H+(omt0myd~!YJB$dc@1RBET`Mb&-*IIPjHhn}E+fA}Moc3l-JVmD ziOMqqm_T_TIFH!QskyTNBWmkf$RBYY`y_kL2E?CyiL=yr-q2-)kqQ}k{rkx`%&6T? zUc)Azyd+BF*+BvqbAnTfcF0DW1DxPR-h2*&;_k5-T+2&AH!c z{xvB(jHqjXAYlY_O&_fs+T{!R?*>ASF)AuE{YA+v{s1(Ca^OrWcLag+CL(7ZDv$r4 e!uejDJ`|jjn6DFYc>(gT3T*J*;#0APp7wW~*5;M~ literal 789 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 U<>q`c{Qv*}07*qoM6N<$f*wDAF#rGn diff --git a/icons/obj/smooth_structures/table.dmi b/icons/obj/smooth_structures/table.dmi index ffd88a84fc7aacfa166c1b32e9415aca155202a4..3b15d2a27dd5474dcba569b753d6a192daf95c56 100644 GIT binary patch literal 1144 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 literal 670 zcmV;P0%84$P)005u_0{{R3dEt5<0000LP)t-sz`(#Y zAQNFT6ogABbwD6cDG153d0~2h2dGBLXz21qidIvP%<>HP%N4%vsK1+Ry^Ci@N@YP?Adv` z%nKW@Gkx2XeGKfx)Zs|U=1p;Rkpm^p`iCp~l8Y|3j-BAXqr~`>>n12!lYZHK{xt*8 zNT5+bBX|Hj?MCtpc-oEN8{n~kr+r5Ock25fpn!-nLdu9~1xW2i0*wNiEk<)I`u_Y& z@7c7B-~aLfYr@(oFdPwv0004bNkl;ls`LGOQ< zsmZiiW;n*U9jNd9bjg3=kl| zSwQ0j>Kg)_0?b=@d9Rv4Y0j~LL7Ejc(<0ByYjx^dE*}Q)Ie*p$0gFyz{e@ewDf#o{!Feyo(Veb_nK!7&^d*4CP4&Z=b&eHy1Sufi$P`!sxNipZB zcR4Dl&5HJLuzOHA*xiR59KR3e1A9juAV7csX9u+xsBZ{xSnzl~*a#l4Wg5ZbU5-}p z$ZH0@2j%>LeK;^Uasr8mw?q<;Kk?T@;<#xG5FkK+0Nue%;sVk+#RCk%dD=Lwbh0{a%=VWk}8cmMzZ07*qoM6N<$ Ef|%zdVgLXD diff --git a/icons/turf/floors/ash.dmi b/icons/turf/floors/ash.dmi index 718cd120194e9437c4187d634de0121fe3d184ec..1ebe8d713970c69dbbe056bf7e4d928d0cd04c86 100644 GIT binary patch literal 3817 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 HY1DrLn*bv# diff --git a/icons/turf/floors/carpet.dmi b/icons/turf/floors/carpet.dmi index 52208611a4766c9876a8c3036ff94031900a02ee..1af03a1df8234593dae6bdc97abe510489857cea 100644 GIT binary patch literal 3481 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 JwVi+Re*iZ%>J9(^ literal 3405 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ö>#>}hWS&hVc>cu;g&3v9c!;(4 z4XBR0AhO9Ip#>fnIbypDGzz)rD0$}*k^(W=L%aZv(($GQK4(+ zu;s$i=^ri*y_^4PgWp(Od|NGVhs~<1Raxv@WL@M`@dZ^$UCT9*N&TxpSG~M{ zTVzYgi2L{AGR&^C8zW1Cf?ox%$4@t2{k~kc0-k|r-0NL8?hLJWXYS-s`P;7&iYt`v zIhT5X-p@FX^e+A*_txzsp9zSx5hpl;MFHPf0#-zy z5!p%_OFF@s#7&YdHq4x=Xy*Z`xd<|D`1uE{&8u>tMQU%9yzE{2`i$a*BLh&i4HGboF(32Bzhw zl44ebDuM;r3vFpKB3NVLss`EZUZyR_-g1%AcO~1hvfjhbwblwIPv)gQ81G1KMOT4R zJ5l)z2IbB%c=Z0@aX{;|cj8CKRS%0E%Vwa4c%e?!GtA`R4_Qe4b6?FGUrlT3zo166 z7k1Q-(|UZOOqS{!F~_Bugn6!zg_53gftv}ff9u~~a~I^{Yxfrr9+a(Ownaun*+;cL z7Ok158-th@2V{^>_z8Y&^Z-A?lmt%!}NXmZz DCb8ZD diff --git a/icons/turf/floors/carpet_black.dmi b/icons/turf/floors/carpet_black.dmi index d8cbc1c45342499ed093405ffa66cef5c9ac33af..e2fea9085b2303c8e0128ede042e5bd08fb56ac8 100644 GIT binary patch literal 1907 zcmV-(2aNcMP)TYI^_x00DGTPE!Ct=GbNc008`YR9JLGWpiV4X>fFDZ*Bkp zc$}4%%MOAt5JlJYD;ivDn6_A1xG@^zLVm%ZRv|tEjsCtwwz@dGHrysNbMD+Q(u>u4 zQ|je*qh6uyn#WzwwVPxrc{j&c_ZMqaQitYT$Kle&Du(`;9;)^k;%w~lk_#~H0m5t!5T^G(%N8g_ewOrP0}b0mv-2k}5~;u6Ddr)kr@Io(^w4X$wfWP# z5|`I1vO!#@`O9&GkAWUWr^(3a#%}Cfy z13AwlLd^&}m^0L4eIp3xAqeJ|GmYoxhLID3 zXnuVEgitP=vmYEDB|$hZ>aODb7s5+6z;8eJmBRv-e^2sX?=J|`OD*XK`!1g&mQQNC z<^9DR!T^OIR0~)>AzadO4j~%+)S?R{c|GK(6hEXbJV64M$HL{hR443l#C=(w6B+}n z)@nF}*@#PZDPfBr+sz3P)FkMMt|Yv?`Fb@Weh9nX)OdjU29z+pozw!6)NPj%p^gzY z*#OU`o@#qj#|S6?CYP1r#Y}%b%JnbPB$D;e_CydO&5V*p#YZ`qHoogBl zB&sE>@(QKLKNn$@-zz=-F@!+^%!hSG7$m@aSRap2x}6BYjp!F);^!g+SE3nV;^zW{ z?akLIA@g&LP}Hk>h^&8@h46C$!rWVe&iaQK($6u%rQX(A|1c*U{!>Ee>#TpsqU85X zi>!Z$A^lv0kX?djoteD}v;HBAlGT76|1*T*d5qBM2UZdGFc8#UKTr$r_zJm{XlJb z!{d=~_II;CM6FMeozITX<`A!8P-6t|k*n84({7Wm5zV7OZ4p{|@0uvI7sDV7=bw9fOdyK3Vq`mS z&eyEIf)F<)q?*>35DI^%YF1xE*k66k>dOd+VE3?t*EI1tWcRRx*EDg$jXE(eLYttn z6LTO!4CW;&>+K!1%nw%9ABfPnGi64w{+S7lJ5y!^>rF!RgHiv->L)_x2TlJMtPjm0 z`a#z_#_R1lL_etczlwT~@OwZ)dxW8Sn{cPF3DFO-Z6v|^0}<{Nzn-wo2rDmL4oi5! zZ+3061=!*1kP!QUOqWHQ5fV@6_6uWi2EwJ^7u2DU%UDX7_XC-(ii8jb$8=RBgfJMU zR}*fsQ~gCK33oo#{6a#teosQR{!oN#&ouO}w>%%=+A|IP`!pHh#n+Tjt9J?Kexac$ zp;m7bl0Bm|jCzL<`-ygcQ4hV|nZsR9H0~Vk65gk@bjt;XT7M|QUE)ay+4l!=zbMOl z%glr3kZl-k{ItG574w3SeSZ-5i?Y1ee_ZyLx`e&&54HWNn4byh_XlzRDa(`oqugI= zA+Y_X*8Wt?PlUrym4sTb{if`mHxd^8O@v6^>(1@+mp{B^=1BbSa-X01z}zPu%588f4F82tNMp4=CH1R txQdYI?FotAoUrPK>5Fjl&dlKm{{e*z6(K%}Dy#qi002ovPDHLkV1ipru#x}( literal 1828 zcmV+<2iy3GP)Y*2)u)y|tPgIU*q*7)mXgJpIHW~ZlnmBghqx{w>S)tg z2<{3>jPoIFf|B0r(XR8yRDh9yQGgMA0($aB@&NSYjo=H=V}YLh9R=J;?}G$QNKrtN z0@Bn2NqHjyqX2W!{%&KI89nN&HQ>4M9MALZi+6u<)c)7qk~LlZ{;Mxyx&C_HNaDKy z00neOL_t(|ob8;6cH1fth8s<3-5AMEnz(7*I$e_6-tGNguLx~`m9k0$KJ$}fTN#Y4 z`49{k@v6Gg(<_U7^ShP@exDEH=fAG>WGlcoziWBm_xUh>J`so%5g-DQA_BviFvfrZ zfUN+LMPy_wI|2-RJOC^|0`jcD?g5#r%8vjDuY#KgL`KB}kxER!%>$Vd2=qXtk`aL3 z2etzG^9LfJmxuQG@gBsM(u%ozFkveet&1VR`g-Y-U$9EnlSfNw#Td8|XnV|D34omd zWJ-X6&ouzadh%$E1U$p>>#S@@w09!-=TSOpMgD0?Nm!k;|p1}TvCYT@*h!hbZ0+A}7Ky)}x1R_P( z01=235g-DQA_7DpQppG~KMy2dxl06!C&2tXkbLECECKK(;M!_AmKRQ7Sb*e9z;%I> zV|nq{fb0pt#RH)90vQk(&jpzK>U9Rr9&;%#9RXJlWJw^@1CdHc!0#R!A`n{MAFhEw z4@3(4^U(=L3*CdUn)(qR0_pmZXk88gzq_C^Bmm6)4i1*CABmPm09*(_UIfN)-0vDl z7LG@2jAi}$0p>aeUtB|7#u`&cYpBauW9n!PoD_g&x0|5%r;KJpytdu+x-OFd-||kIvIx|TeBSM=x@l^Wcwq#ZMm*kZ zo7<-Qt4kx$+*Vh7!<+F8N+VDU0`E6m)*ty-9D(*4xZ@)Loxm`_G50UB*;gdc+_sO~ z_Pg)LDs(qk8K`-u{flh&H3-}vd-cg+x(x3558_g%&fKRECI(;bUzW2fW-WxiD1zsP1^o`6W- z`q-^MzL5t{ZXUK0FwZpJ<1^{0kKX!Y{b;P$YMLQ1Z;zf`c1sVZw(&fD>W-deOL{as ztoV4o6`HlLLqN1Wec#m`iC*BZM>h}WK!87s$Dg%~9)5KH`*kvNF{NIsyWiOCD-byH z)3JT-I$wT2p9`kn|F!*xZ1&{|G;IWZ-@ZDu;Sb;coXt1vPh{>tWV5e8;HC{g{}!(v z+vtHe`|VsVruiJsd*T=Dt6T#j463))^Ile=y60hf*a(+h?d!BcmAHQYsbvH$#{(aO zN|{Y7^#j95_`dogpMM3FGMCos2cG08a@W4vx33O`5qQnp2<|&?U%!`eL}>(M{I>0n zj+?tg{$Gzk^Y8Wbf8){rcZvMJ9)YXs-J4`y#~`p+3ioIxDLao6fzlEYz)nN}J56gH z0_W$k?bCXn!1;M>ySmyWXnr2suB>$ko1e$F8w9%KGe3`QKU;0mC+FE=Y`e1FCv1Km z+kQy|v@>>|9mck+t&nS z<*qrer`$hiaa|69m*VZ*MFg;Q9)IewDfaiX>T(F^zXBFW0H)OlS?&L0lSTm4=RydW z`vVQ*fnvhxF%~t(1KI5VV-t;uu{ARq&|*{U?`IVez}8GZto6m%-_NSj3H$~}3$04r SfVy@70000gjFoxj$y!_gTJo&YAC-IWws~yS)qz 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 Iw;WFR52v)#{{R30 literal 0 HcmV?d00001 diff --git a/icons/turf/floors/carpet_cyan.dmi b/icons/turf/floors/carpet_cyan.dmi index feca351ca925de74d85304b194a01b5658bf2b9e..85e053a5a6de40cefec4a6d7f08ecf812fcc809c 100644 GIT binary patch literal 2883 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 literal 2675 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 T^+Ec<0|3-L4EJW&aL#`Lg&v*> diff --git a/icons/turf/floors/carpet_donk.dmi b/icons/turf/floors/carpet_donk.dmi new file mode 100644 index 0000000000000000000000000000000000000000..04c4148d2bc5a62d89c95530dae729a8b9ce443f GIT binary patch literal 2842 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 literal 0 HcmV?d00001 diff --git a/icons/turf/floors/carpet_executive.dmi b/icons/turf/floors/carpet_executive.dmi new file mode 100644 index 0000000000000000000000000000000000000000..2c17e542fcd1981558207b7ef7c3fbbb0ebb429c GIT binary patch literal 2535 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 M9Nk~G_w>d80q1#zqyPW_ literal 0 HcmV?d00001 diff --git a/icons/turf/floors/carpet_orange.dmi b/icons/turf/floors/carpet_orange.dmi index ddf239b63b20e5df880c98bf49242783c2c6b711..4f0b2078fca38ac8abc90f9da781d86a25d412b1 100644 GIT binary patch literal 3293 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 C;Ol?@ literal 7950 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 K|BJeH`2PV8C9)s@ diff --git a/icons/turf/floors/carpet_purple.dmi b/icons/turf/floors/carpet_purple.dmi new file mode 100644 index 0000000000000000000000000000000000000000..f07ce75d33406485a9a3c3ec45a7c46b108f1db2 GIT binary patch literal 2427 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 FzX5sU({%s< literal 0 HcmV?d00001 diff --git a/icons/turf/floors/carpet_red.dmi b/icons/turf/floors/carpet_red.dmi index 926655688ed27b84d065d3bbffd9d1dfd24b7461..eb0527f430dc4a4044ba9be0806e3ba1682a7e7e 100644 GIT binary patch literal 3011 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 literal 8171 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 BcO?J- diff --git a/icons/turf/floors/carpet_royalblue.dmi b/icons/turf/floors/carpet_royalblue.dmi index 841e49e957b69b02f0e7853edfe0f6679a8327cc..1027b0e19f58e736b3df8cdd71fe0af5ddc4a246 100644 GIT binary patch literal 3133 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 diff --git a/icons/turf/floors/carpet_stellar.dmi b/icons/turf/floors/carpet_stellar.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6ba816784261c0920a47e8ff19776980ca3bcd05 GIT binary patch literal 3176 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 literal 0 HcmV?d00001 diff --git a/icons/turf/floors/catwalk_plating.dmi b/icons/turf/floors/catwalk_plating.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b49c46564de508d0ddc8fd050c50655d6ffe6352 GIT binary patch literal 4353 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 qg;w^;-I5+q8oTZz8c0T}3+=oafZhWsB3o^Wdb literal 0 HcmV?d00001 diff --git a/icons/turf/floors/glass.dmi b/icons/turf/floors/glass.dmi new file mode 100644 index 0000000000000000000000000000000000000000..ef9da477681ed2c69e460516cb7e36b22db55490 GIT binary patch literal 8083 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 AmjD0& literal 0 HcmV?d00001 diff --git a/icons/turf/floors/ice_turf.dmi b/icons/turf/floors/ice_turf.dmi index 8751a37216260e4efe32f76f51969bceb941b569..61574645759c989ad26e6403f2332562da1558f2 100644 GIT binary patch literal 4712 zcmaJ_X;>3i+jgOb*DX{O7u0|jL1nR31QZz&DN+SgY^4Q>iXe!}jtU{ErWSBRtH`Ft zN)>S-Y_bR>vIxkkqJ#t_l0a6-HknK^+1`om`@P?f@A`gZ@;vu*-}iaWnUgcQCVOw7 z-@Mt&W>1?oZJz(Go%_-6&!&(0O!Vpm77k3CHp6W1o&&yUx_3y;A7?70qA4*~tpepz z5u}ttpmb8mnG$jMoFTPZjc8PASUEb>%^Mq1$ixbnXq45Xh7=09cuG8uAg~$+AxJjK z{|qT*T12hYYP4FlPKONizSLBAOI(VHoC% zxRnA}2`LUT;ou=yE|-Kb;VFrzS^&!xQUwH73e=EBeP9T#5WotBR4x;OO4+_4^}Zok zB;-g@9}!mxO7{=JXnzm{YE0e2GO2h{Dptd=OePkK#$gp$B~VGFB9T-yDH!98v-sn~ zFeKkQ1P?R1Sp%J7!PqFP7luLJ7()fg5dYW0V{MvVp*a9IX}p3h+^(KS*?G-{=3 zz2s7{R2<5Le`LZ!Ay)-MYKH;2Saa7^?<27v6q|d5?(d7&N;2}UD387v zom3_^?CXCE$%z9=Q)_b9h0AZT#*SWH$Fguag@hx?yN8W-`(x_vGh{#Br;a~ zm@~4XY%Ja2Ylm~yJnr>75=D9zi>*Vl?g0124ti?U$g;B3k+$qscLVm4Rh5N^e)GL?! zma^ktyqdjwW}iL8405#ERy7>f?z<$(E!4}OsDJ7i9?E`O)LlnSnRnw`ilgjya>mC5 zt?FKa-we2lceA+8PA=kBjde!?+15)zw9gk0`r}q`s%D;s%Qcud%Yh9oVvDWhUp4xG zlSK8Z;(%7)@SM8E(Nj0QUh{4Whaa?wzt;9`u{OsQ1&JytkrguO^ z*A;`}Rg|+m%);zm`*(b|v<&E5D8bYG6|ual?l>~*_aJT>Or}K?tCPu@E}jdhJ|idh zemc|8MX+Mj;*NR0`YmoP_;VPW_|}|d9eO&npr6B1k!ceiG&b)EVI`vm_hIIt4JU^F zj>jK7yl5>upz$nA00c!NlH`ST?4)a4L`5FMFNwG}787^7PAzhg>}HdEf+O*-20MbXB(frI$s9t@4`FZ>ei^ zrNmQG+!OJEp4CBs$>3ht2?+={eHn!;sfLn@( zkjoTiu|8BC^l{H2_GDV`DKHW(ttL6}UCKx?K%Ic?5Vy(c3{#BXB&n|nY2V$Gqi%hz zdkxt`WIsdiqk!GqRfL+4NW%hfqo%cu7%ySxTN`M_OKB%a2agihkjz!&S#J5($+_Au z6rXZjbsV>4yYkG#iQ~%5!$5RF4_sok{`|q)ly|rb`uoi4Ad3H3?mxx7Zi2^D>Ia>p z!D12vS+-cNZEVr)F(su--Ls(FLdt901>*Gz$Z2jzyAUeHCcMSYXB8DKxps9x6r{Il z^9I}nsp-xaBDfzAT7=JXB{?>V&hU#rc_)O#!`RM8y!lt6QtS5ehTJO*J=h&6;Zw{= zO|Hl0z-ItRw!rA`+tz(dJ`{QF>P=Dq7sF`tVRL-WEtK&DfK80U{i?^?Wk2_%;IG<$ zq-edfT*R2w#ODLTbjOG63u;Fzr|!c6U)>8czQyh!{0uCS$Ciz{rLEOlwP7QGx7(be z9c0x=Fkmh}3pi+8op+Ki?IMn+W#QVRZ0^*2zv%3VjHm*O9EeGS%Ka z?h%Gr124i)-wS6S`@5Q?tw}l9h1ixA4B+!m{h)Um(?*@1|Do14(1!l~S8akXQNk)U=OV*Xwi2Ds&k zy2Yr>s@{u`m2Gh^0ox?oB8K2VN!51ekNv$55e4$ELAp~I={m_cQ~AK{SSzNV-HqE) z%RL|Vgi^E{LVp|2NH?`bnW5M{sm$xA>0|Fz)@RHz&s7tNWe>|JhxJ8aMnzFsV*zbK zv^p+hxH!%YK?1F4A^3|W&rM|=0$m%B67o`<>XwDE(rX!{oTA)*@l(ZLChN;`I6Q+L zA*C-f!wp#uy$xHcK8+u8a@o8^+LGPQ3NB##Q7(32<@6~`&k-HMwHC~<4QtI(&bK;Tx zEZyNjQ@)nkp?LN!J?7SCAV0;_LY7v##4^?sCU+T}*Y<5eM{62^*;^X#U7lx@$7k@?Kkx!3+^$2mHgv$U`0q_ zljJw!*%#~av744{1nNHXzX9^E`bqL)O(UL@#wD?ij?8nIBf*`DbIY3K9|Q8U{TQ_t ztzgD=c6U07w|Pm*+|ZCU>~jT4tF#_W>;=yzz|1t~JlIgQZ8){%VAMv!x;SD++GPGX zDmPc`($`UZG#x^nO+2Zd#%Iyu;*%l{CmQDI*WA$GnZ(%Ms{x!^3Al0izP1~JMqeHI z{^oNJ-mmlG5+`*wEZhzQb}K2X2 zU}nCid58YUAI8=`+)*CpAO~CV14wy6dSSDc&_?s$eesj?!Tfq#*kd zpP!&LZ06W6V^`Y|lee|q)P25ze_D8k7C9Rpb_(YBF?>AVVf*yId?bAHdnuT05u_q- zL~Tl3XpNN36m$7P;H-f0gy$x)&fqtk8Wua^>9gd=0CzCwc53)Qw~i2179SM07}|(#m3KsxD5OpwsWYb*{AJgm@g;Zi=}`n=|osP z5u^Y0eMIW7NZC?@0|0jk$DATIRUUHLPZD9?8L(V<_Tzq{XXA@rd!M*fsZ(zt>l?_} zOaOQ+foGTY{|+$6fEUjwOMw!4bO`q%(mXcnogs)RU9_JReO0(HzztW3Z70d7C36SJe~pNyZR#`uZ%>o*NzttN~Mb>X%UJ|m?i(@@M<$t(&tb~K@) z8squ~+VPV)ek26!rB4QE@YL5xbEO{7gzQ&F?jZ5vNa!Yt)Y2=(Y!<9PB*o=f>JKvw z)Ja4u2`(9KEwQA!WDv`h$m#_6t@=0zdf1P>eTbdvXGCCbz^tu zpI`v`q$y48G)7Lm)jUG&6O%fP6XR%nEE|8hhrh@qxrs{tup>HsM>)6&bq+T^gGbM#u>+)wGEBS z73miJp_J`Jr*=D1UY-vTt0Z}dEjy`B+{*@MmrPx_l`*fx>%5Nj9ZeurBz8(8<)m{W zlwcQ+FYqC{6dAk2O!92&CG#~+a}6%B88b95_PdY6iE6+Btvp|d;>!)#F8k~F_1UPR z%1BnA!~eiV&s2f&J^`gyqV!7SZ@ME&fA$akGD?4n(r-|J5WVJ43%?A*;T3(@i|!H- z6B|!-X!{6|%GdBScZBItAn|_#8W1Bk0l6r{8D%W@#4k)Y9QWpDuIqPk-il9NhJ!+f zKw!=m;J`Uz0#oy#-pd&0Py81@^GUx;d;j8G#A#APd3^`>#EZUUEB=7QubfY^LM@LH zzfGk*9*H~BZ`?`2UAlw6a@S>925=rQVz(GAi*oLofOS*Y41@h<+*W!>9nNt?dIaaI zZEn%8RPC!arGL7Gl>Z;_e^|&;M;6v<3NFvtuRDB$h}AxBGfv3Uhy3geE;;O0uXX5d z1>ade>oG?5_{Ej4Gv>MCnwp>J@Ph>9tfvan&!F7;8TvOMjhsTk?TjmzfMmhE!3?5B zQ}fGy>;tzDSmUjnCH4ZcMh}1DV^Alpi9DNsrYspHd0zy#9^7IoO3=X56pW(5w*NQy d#5PXXzIxGIcY7igeLFDC-#2h)*^a{({tbYGvVZ^p literal 3120 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 literal 3080 zcmX9=c|6qH8$L5*W*EyTJ98Q0TC&XTa+PQpOLB2Xjcbia8CTZqi7!fNaCI+AS!PnA z4W$q<>6SH3B}-W*`xct94P)jihGpJsk0{|gTkYN!a@OvE6n;>jcFlBLthWXUtx9c`WO)X z{?C~cKP9G;j;T;(+H5yBP~xNb!H@&%qC|DKD<{te9dz;a5 z37EF+p*oJw@~~s z(DaTu%7d*4^gk*1u6x_D87`pilS^PXQ{J+ia^ZgCDont9iHouKjj$U`I0R6FCa};>g5u(tsWH_SqOFQ;LGWCsa;naFhmh64FW(A+>$45fuUjbAQz5v>1+L4n4MM z2FMje><0zmNYE-_wu?*`6a%|bSl}mp&?nw=a+efPsOXdf&Ldh0c{o|bL}Dl;vJqKu z7g;X9uz`+Jzd>_7VN+tPks~NJ#luxin~uTaQ1S>%g?rYk=s9*~o?1A95Zfa(qwTU5 zzHNam>Fx7hwskP^GX+raRkvTG7~=I-4@ofD=oGHjddiSw=4e6U5&k_XS9-%C_mq0A zKMF9iGX~f7sYiqXdX3^;#@U+UMiUDm_`JpCck|J~)BAO&X&q`SMP4EmiEhI9?!74{ zQH8lghpm@letn`xd49r|c}!(9XH2DdIHjw*LxFwzn(Vo0Qf=YA$6@dab5mx;9($kn zTAgCRkf?BhIdBzg`92=+E%tyhcDqW?x1WY7F@ymE@A+{i6r@#^p8YRrW8{I$FFKW#v*U4hJ^x#9EKInCpr>WZs-C86BUc{rvJ&ZMk7DE8^&L zL%+WDTW$6KC_iuUu9}KWdRWjSS;|S=Qrd5Nr6Okyy%< z&#?>^hbBIE>)w~YN90|pyw|p8GsOkn;tzj~|4rFFDdPRf?*&h~N5UY(PmeJXmlRy~|D8(;JjD8J1$^FnBr^bIKEd5zt@JK1Mw#WnS0sHh*b zn(pqNJA=)M^U-$6y-%j{#<^x0@cRzO$BiH8RKEH-^hcltG4t->W5yLWXS?_VjB<6Y zTE4PvxIB>)Uqpc(VAoV#p0YW5D~-;2&@IKW_TKV142Q9kwK-sI?Q{RoA)~{qgPi*% zeG%gy!WPQSxrEV69OHt$PsKuE$4C919{Bm}o55BWA=fT^mrt+L-94bca21g4;9Vwp z_K0=;SI={1Y?HgHJFgGJ3pR9~W>8_r7j7Y`(gXoG%@PR@sXqV&v6|3#-R5ABdkw+L zZiEekFvygt!hQ-nO~#8-V~|HDB&(h_!k*>tY!(MF#0!UD?x%bd=(p*PIH*`%Cr2Mw zkXa3L@^INXaYEG^V~;1);xI9_eGeVxU68+i6p$Y4m|5b`gu?+w9{Um_$}(X(fd_)G z>W3z2mnXD7@>QI-cVv&{R121+k^@jLx{Q`Dx2tV zj!Zr8(khlcu01O|%fWtAW48a%BePVu^x_;aC0;$BSdvU#1EEQ`RGE`%o;c=pok=OPh`}e-c687G*?_#JDK&PeW-x`8m1W(_0c#)gdVDr8-PrzCyq4b zFZu@Sn7FI(4l#}Smpy8dB86{iQ==^Yf&d6j+u_OOpXAl>J}kQ!bxB&6mKXgE;YO$z zFIAopErS3G3Fde*3Iy;+k>sleu7NsWl^!E_BE9u3xH&7jBWC13Krdy@v~rUhnit!l zH1$1FYowkGkR<=GWU!=FtVEtV+9>S_YQFqqz&2%FZO&RE&EK^^-LT96%qCnYVxsg6 z_O*x;(Lfoc0oo)jJi;GfA?Kc+H)}8iNISVc3F>B*@|RFmw6eFbzZ!U|QcziG?HY-l z%(|40po}xp5n%AbDtMN#pVpdTZT+YXuUJ450a>s9R@247S!E}R`r#%k6v53@_iZ&*(K&L;k1obboLM*0b%$_5tYq(r)pG1SNUbd zK$M^KeP~bI0{u2-g|7>3W|#J@(LBOb^1VutEZ-g(gqVjXbB%TY1-(MYvj<-3VM)4@ zWdPpndxm-t)*7p9m7nZ(RD|p|V)R=8Q?Zj8WLuTl`wESDVkNSz9ntwGbm_3#6zK^~ z9@es5REuKO-UupwCKwJrZht2v1P4a=T2+LvDE-LXKLw>8@PTgyIA zVmy1%4#(T_$BBOCt)Yu8!gu30Hechs2h&ut_({Kbbn!P4_kSNkyIboq4d3)tsUM*R zxgGvVegF++eW+o8{Yo%mRik7E#%~KN`X5K%TqT9p$BOV|Smku7(I=qC9)7mk8I1GF z%K2Vl;EMAnN9BUos9=G$N^v)|ZT900gs}w=>2oVxjKIn|D<-@BdDly!_(w zXlvv(cE7&S)D5H*O+B&S1&T~kyM_UY{^z3BuPS+vYTPTVTcUXktDK(V+_g= z6Jv=fyNEGj7|W1lW{hRVZ0~UDocCPsm-FFye#?FT?)!H=*Z=?FNwmIbA}t{=AtE9o zZDxAjRv5=OpPf5|@7W!ppM*iq`obk+VN^eMR9pSz(H|8K$;s}KmICW&b!)0ng}}BT z+lzK0XJ?4Fgas?&l0(pWk3jc(UI9T~{(d4NVOhVYSIWd_?l<<_kdy9~zvhINQr~q$ zaSD`hrAhqQ{SD4O$6#G2i|&B%^@iu_D;Ofk`~?W`Rc<3UJSd`c^teqQP7k#jRQaoM z@NLYhu4{ayd`nQWf6r0`pr}rshFviz-tsijdN;cGH(q(JoH$QQ@Z2po>9nB1 z2V6;hns3hZa}*Jg`fPUI=u%i#{d1>#=sKBrL(u?jTI%t!7soh;#y{T=JO>cDBF6+Z zr#0ETDXp646ts_U{CD>^R89Nu9{9A*8?3RgD<7%DbbSiFmGz3?o-H=IUOF&_Q6S>9 zD#;f_sMiXGe7MT!BW2m5fa z*SZ2~q?<(}YfKj{h6ct5c5>jLWik|*6 zaPNnU-i`-mZ~F#04F&ID*75ryUI(60VA4KWEUwYYK5eoL5U5=vm&J}2K0NemneGL|mFLGD$VZ3E7`D&n`%9X~!OgvS_0(uh}KAKHntB_0@5mUWr6|6wu zL>~0}j91{QZG@bB@Z5`J>5bghVAbL)wZ#SNc?n**Y zvX_rpLpq`^I990Cgya0B6EKI8RRV{A^IGUD+{8je>p4(&0oNX3C_!p9fwlDP)~1CF zUyzjcfn6s`=qDm9U3+o(XuG@k#xx2V3Wvi{Z%FB5*>*48U_T%qQ_YGP0IS)Bzi-DCW0!zxGjaQ%N6<%``L^cq&>@J~UXlE|cSQ_(xo;oX5*)TZE) z6}1VYlF^~WNPkziY^EYLykcli6AjfEYl|z79mSSw_JpS}4IB`Sxy#@&g$06zM&eG z7_!3u8tt`12|J@hO-q303~F=OFF>grJ>U~kNuDOYbz{86LfS2DaQdoSlw>PoqA)%d zP?9J_;wY36tRKwQ4*GztHlW_%;yn(vmBlu5)77>o_&=5JzFP1qB7vqV7%>ZcSFDNOcK}xupTkh;w&Utjvf_u z$uiIcMC0b@2W0J)46^PuwEbdAoEYdv7bA}nroKE`$*oM`dqlQ_?in0l^|k(tj)mk} zv7sr{BM=~2ety<IIzWCeNPX@FjK5_gu*lb!p{ zJmng8CMI~Q<@FY5!MdyApv*VkGI&a#JY{0~y*j{VID|Wx2)+^745Hb+7aLDkR1qH- z&h01;O=@lcU$C{yc1T@Mr40YKElEl4ESL~sK2!Q9CfqZ}z3a1Z%TV+$)EylLX0E7Z z;Pk<+|CEo3Tem^(g?p7SSp#xfQFC&S3iRyc{9$<LQs~uum3m<=HIYNwv&gq!xOZg0^V~&8^EZX zCm+kE|&#Cmmoz z57GL9_3&Y7kds6A1oxuSx$9~g&Q5w?Ppz^mY?uv7}91i^LG{hs~+P!pbfQ%24J#OX{r zKo3~Jvih3S3hh8D0v>UdS5VkVXo{FB|M7+=qjy<}5W8Ps#$$$we^)gCHUo10vPi1zQjR;i%~fp7i;`F*e*>QX&eh9Gk|5awri-;l@)Mn6RhCYr0Xzb zZ4cgRB8+w}uy8i#J(Rl%RLy*SjdR6~%@1{N;bt)Y^HrT*MRbKcAW%a0H;X(tW9ubN_5)A{38^9DeP zF%Z|wh>?mw(5cYOHfR$k7E;DEJ{pFgizc;cxDIdCVJgD9C>RJ3r2!hQoAMOfg<}lq z+I2AoGcW5!2jut?&xrd@#gn>>Vz6x~yrzC;Uh=p8Qh0wU)z0@JmNEu1fM`Vc3P}%O z2s-!OEcS0o64h=!W>prR;1eTwi$4jx#!ynDE!3HMC1@$ zg#uwKZc@?&H+5DR^cX--Y1M7&H{}QwN((bjb$B|Ebp-rmoo)R*KvU zTGoM|0=2`0*#7R^;3#R31Wafw$+89)Xi!>|XXsjR6`Wf$a=4w7#LXR#_cFMUEf)MP zv_x`MHZVc>M*)F>5i0L;QOJ+XmrU|BfUWR~p&1EWVT7`wjsRKk1r7dlZZ2Gg!F-8$ e2ANt~hrl7_kMKIgC&HgR5i{e9=P~E5$NU$;NyNpd+l za!SZ0Wj4Anq1=gvShD5vm|+{+nV#2E=k>f^&mZ5<_w)I@FQ4!CkMHaCy-GN^7pjI( z0{{T(?uPeP?CjO03|7Rvxzjd^4N2JV>%wNUEjO9$wA|`wXKS(%xnaGwCR9VNV6xfe zp(SMs8tAO|K_3A3xj$S{v{QZk&*H<*gpebm&qhR@0su-5qpMu4(MppxY9EvGzFn&; z^jycG$)JrFE;{)jk7+l%NF{63KcR+B+ApbMjr>wV&O|tPkq*l$w2sVvLbaZHH^5J_ z;qCFuInaxFPzgy=+PJw!+ZKHvCWNnqBhez$ zr76)=tabz`xD-ncD{)*l)$4iTz29}Ol_mC)=I-}h97IT+*Fr|WdCsrxjELCdy5h@*QVLV;C4rRK+M$>u(H+~)FZ6@Xq)QHK10A)P`5*erfvD^#M+Tv z9)(lxu~C_4>}lJ7nJ4-H9|n)sW+oj|pJ=$6-ph@^l+1&It9BB*00cF<_A}5oxOJ3g zrXT;s%~9vz^$&Ctb6{ePeUmr*vwqN$XZU+(!h}&=5Jh*L>~hMgzv{-Jut3Usa8dhZ)rAnF$_w?@OEqJxtUr%pYT~WRWezLD12gwq{Th zBj@Bv@Y4*?n;YjVG>zV@WAFA*p;!Nka%UXpx zHW1tuk6MV`EZyE>wxP1sL&8S%t-XLU2UNDauFM3gEtLI>swlc$f&7LY53P^tK9qQG zKRyTcd{g^|>glP}xm?T_FrWT@(+z1`&RzU%hjT1*#c!(EUfTjpOh#h1{nG-7>}81XfpLXfnEy9IGo#%FlGm+q9~Sf zZ=A}$;(8+(8#3l^+|(QBBHYyJiV7;(e?vvHI?DfA<)xm+Q0c`rjg!z2rWxowY1h5B zfzD5rFoygM!r?+mOSUp*LTMESDzbLFs#8m z-6ZduKQ31DHEp`Nut{z>DK!on7?k~4T_eWEk?^qKbh)~u{+MBsF;HzMX64UZYc{YE z%W=Y7slVRd5iWOJez6|?JRyR^S=FP5c$uVbL_ztvVdr7JTL4>*T*S<~Cr|?~X0R$4 z5TZ;#5?4$uvrez#4BdjCmm-C55QTAhrE#z~4j$JmoQ7M)+;*w6#z`5`4)Um>MXS;? zn+DSaoy}+s-6#{1Ko^rUrj0fA0GB34^lM4Xm|Q}){XH| zUYG^=k*DYN*Ec@EBUMjqO=zfd8(`_W`@5C$Af;1XdOV3okJ%ceD6YC=neFVtE zv&Wq)FT9NF(4-Ceg?EspNe&Lsy{`gIUv9q@I{f>F^&xE>;Z_N{>2mtHK<)a`X@qER zv7H3HeMoPX)Vifis9ub9VtT=%hxKN!#HgH)2d(0p7e-#fg=$}ymH0OaQ5m2M`nfAV z*jhc{AIbs9+a&ofMEX4vZbggFXu(h*4RuNmESkim8|meo_}#MruzJF7N9V~@MXtVi z!QT}r2e}6E{UC!M#3a@4qw#IVKSr%d#qapMzF(};ZJ5tZCjTv;+aoa%V?XN=Jn)FR*NV=|O-2;_L>rOcA>lAuw%1^bfa2z6ySU(s|KWxi)Vcp(9iOR<>*fwO);Eivxaj@$Mgv4|-~J7K&!IED+C zUOFd!Yj;3I0f}NzxAw;SE_lM9XK zDspM+gtypKk@68*pp5>=Yy_MkgeyQ~URnZ5uyzUHErSC**($|6!)B_IC2pG>*Q-4X zYWXIG5;&_;2$1sk^1n_>Lw0#N_30wzNSd0Kpwx^4c58=B#-QYv-ce1+X40Wd_#$RM zQ!B>O?L}HHgiKSk7Y=1tD3x-PL$#~WqkP5^#%&4X7lqGNcTC_&Umjs?3LYN;kW0sf`xD+bAb+C672Rdmm(MX13CpKHqZal-d7G{T#*elU3=^rSQm(2HQnlQ8H{VfJgge6Qkac1bR{0y|627?& zntUq*O3?hre(hU(^hCW|j%M+%Q3aXS9wHZ}s3p5dQoyD1TD{}EqL^e@>X-kEg+Ht> zt|8h#^(A#5ev19@$c)%`(<8jARKRQ4$%n$U_*$ zQc0+XC&EyodSok;(D$44yx()Y$9ufr_s1OfeO=dipXYg9_kGMCzqCV6b|O$YC=U;h zh&|4Rz{9h{i~Hl{12PP3X&SHyA98fX0;^Nx5orq*)Dbgfwu=f7?}#Ee9pA%55MdeG#NNHHx|0SbNZzt zd1vEO-=MndibXQg*gW-H7c*9(?Q>Q}$C=Svw-FV8;7-zCE8joxL5gs$!b|0J-<(xJ z#?8IO%or%2W=2(VhyR38r(c0=r?PK>>{Djwk=Kex_n-M(;@@pxQ1a&Yz%?&@gM{cJ z9TPsS4q#_)&G%tM|uN1iok&Ia8V^ z?09%!YxXu6*O=nJiak>mj>z^5MD;w?3?5B+;}6oss5}o!m;$NG^;}qvDCM_#Ij2l`B*J799tdKmQncb-K=ba{x2f_Hfoi=!>G5!n&|2dM!kY-Ir+oUEb*Wj|s73 zoZhvGXwJa_*;1&`$6c3t*KLo*%&|gzm%g#YHWpfz>KXKmo=Fq=kId@|!(hpTrOR9O z3~_SdRoZEGfRo0RAyg)<^r*_)uHYJ>(t=s^)1b_y)0@6zvRvh8NR1E&MVmLA@Vwb( z74~Q9B7Ey>4tNCer|381nsUOZO>^9=)3sJyuWI+5rK~}fwcf~voVW6c=kP|>CF*=6 z27wbh=l})xiwQ!y?H#5BS9^^^w5)>~yfjofx$uq3aM^j@jRH2>Ue76vnPsDTO+39% zaf52QJN@}d@57YSe&D$|%?_6<@5g*ejB|^*Q3^RZ(Z40dIF7=knZr2gL!Yni-Z)-7 zEu`UXyGurtJ;31i{(U9nS~%C7+xbA&~5v<6cBuZl7ot@cb(GVVDdb zJ};}`{((Od+DP+;=4b1)FKgJB{-y_w z=Z-k!e4IPDgzkKAT%H>83NqY?+_>z(WuI+;|G3~wX9ll}#E#HypWdNdc6s2cK}UKr z2r>6V)c7fx?E7P!)1wEKNx6_MWt!k(zWYat9$EfA><>WxCgsN7eYDTFHhPl|-mix_ z&&6J&Pw#)9au0qcTSo~#xZg=nw&wskjpp;jg- z#J0^Q%voR9&La7v)irvDj=;s;?TFuN9!%4ubHJrHPFPS{>zwz#k;&GxH~vUb*h=~< z)KO-JvP-G*fZ_AJoL5nDSP16^)o-RtMyJ2%H1A`aZH=C!fZx71M9-yGAM(Az9<&E* z{?r^h>NEWL(nQ=RomY@H^A6(@@m@`{eMHg6T^eT|93H*xHa#dbN9*i^j7EDghg(g_ z4sIl0ovu~bn3krXWrZN)BkP+n3%-HA&av2+c7tY0R;UcAn_?Oc*cJtAO`v<8?z`?j zvUf`(R?v$Y;Sj1b)8y0(p5$2LF2}f^RC_KpGqU10b9=17d(T3ta?F?aSG@Nm@ZOur zzogT?_Nls-)7@$4G|XAiIuW_kd?(x9*4yl$ev7L{Y7ut#TZl#7gK7MR;(q(^Rfl32 zx^!4Zr#X5V((0ixg+5qTFJAWT?ET!P*-E7SqFRE%=rOi>cxs;m?AUEdt91iI1XDb0 zlXQ}0veTS4lW|kxdidMWnyC}e6n}>gl!%5eaI=a>dpk^?SJjecOl*ErO=Ei*sr(jn zRYz>;LQ{ZhUyc#;%AqrHkocQGD=c@GweZ?sYsivf3a!+U%;y-<((0-4f-N_e^+-JX zExsuDj@loby&ZaHrzA*mJI7Psed5n$wD!0Ub$qU?RPoJ6-3XOLkFgdzjW4E6*d9iU z%pz}EiWts{qDsINzQ^wyzSKW#hluMW9Dee(L`Eb0bcS8mc|wB%%Ro4~U4`~0CbU7E z-DVq8R9hMSLr&*7^@{LaklZOdozmt4mCT#be-ZW8CUzBDuI5T+9Sa<=6|?-Bq9M9# z>y_AK3q1Ilf_MHv+W68N-L}Po%|{1H%}>of8?cDwlrFxX_+FB-zA1T&_d?PP=$qeD z#Zz@V4Rtv0EI#nnOBQA!HTouf{gqh$ znYQJw{I(O?cDS+yKAT!E1y+$|?2_loP0Kw^Vkb6zeVgJsPVO^ywkCI+g$+R$Y*r-3 zYu`oZzXm@azPYve$*;IOe0a<@ymSsvkEC7rr0^U{PuN)p#*8 zOwBbgP>(7wp2oN)`Pdz`hs*OvjAeYOG6uT;K`y1zwkc8XcP%97k7{1z`80A^pBfI+ zqs4?F)X!18FzM|cM5Uz9DvTqib`Yj~rO3A>7DeCT&mTQOjE6t&a(Vnf1p_VQ4hYBI z-(@nJR5fB3Bk#bh! zZ60Je!yT|QTJAHWNc5a1Q7#AB#lasSFFV2zFYCZUV5wMkrmHxDO`l`6D_>}8)ZndX zmrFGJZX=FBw>vO-+nTI($Kyd{iA<4rFJaVBj|n@Ax1x+yn|cf*OOEHOQAW+-lDsGf zV2A}J-c}h>g>xQTNgNocSC=acvFJ_#X}irOnfch0nhump^HEptmZV3k;xIxc%YA_G z%fnzHkE$0!(G`~&;et|J7p-_tGr;*`eXggZCRpeLkpgnVawWtO^Wr}NoKxY0FeOlb zn{hkf^QAjjXgw;)t0@u3WGdda(iK3=vjd%kftj!9o)nYBkfyF2l0e)$bX|G@r%GmL zlFGO~7W+NZ_>n;ufBP*JM8=pWG0MI{Tfr%+sBui{+fX^gyvV}=VBp%qrRA3yWhK_6 z7lXg*y9cT9*w`gdF_g~C+}I&2#N-ungCWEq_Xx0NLF*=o zBMfheBUXccjW`tq9L8Dp{COx2K6)&oD>OicjA>10D6MXB%_*Un+0a#=J674~ZViRDBz0bVj||%oVm5%ySio>DoydYd zW5?pPb`hb>NY%gr1Kw6nKIuXeS#;!_|57*+miMiA78JW=;>iP$)#NS(rqz1KoFhP#TSAhi7FI)Y(ouF+g{pv%{DDK$fw#ZBrp{AZ0#N=?M6Lw zSv%C~E!H46$Z(=nu)q*J_?_1Sl(EY&?dtM) z_*0hv(u?Wvy)u1M_LGhk@D>|db&wJ(@`@~?iM-z3ryQdns7Bz|9W2+B!__M`Nbw=0 zbChF-9>S0b+q|Qh%AEr(2Fm;0JFAoaXWRP6r zSh(!|dCxR>2mU3u;}E-Sa{Mj%jU>R@&z6H+q(rt`&a~;w1+pgGLHr{-4M8Som}_M# z$D|>MgI4K((Q8LHH=|OX0LC0Dz+UCH#3;ItnctoC{KZ}Bm?yFEUVsu@nC@em?k1%x zQDhx=<#$l5%MDG!3rzaEJq9Q>T{cY(M=oM!R^u>y$h;fR7*|?sLVR}+bX4qIE5*0_ z#P{JjYj`?Ao$#{CVK;Zw@LEkTrX^P*jVmF}l}#7s;*9f@-g);*5Rv@by%3gRTbr<%kjq+YF>~(}NH$CMvk(TK~iupe6`6G-LZ??%cH{t`;t_t%0Xu zG&4g7CLY`^hz4HZtD3ChU{~xN!6o{qw`$*&-dQ&0HJg4g4(PN=UkFd}Tn)~{=_xFP zQ{6-)k;$F1p?Vg7pt5~TkFpW1a_R)f6s>We^KB;Cut;>L_L*c>E^bK@5!S@YCP%0e zHYhCl(0T*ZT^olu28XGgDNNPMp4LD;4yUSgPU9*#5US<4PasJl4Q@$ZO7{H3t;3Z5 zRp$p&b;qZB8E6=C2Xk8JAzB}m`HSlRlKS^LFNGx?8sYa#3g}CiDnQWeP(q2OD25I) zaLynkG7pCvM}u3qKk|2U_Kb9q5^ zA{}+?hCZAl*}PNJ{D;-me~Jv&r#`{Eu9iyz{}S~~?eF)!zkjiG>(@u&73EBrr~Vqm zA1E8$*=l&8jJpjQ1GZhyxZAxGJ6kyhC_m1u~N z#zlw-M4JB7>~XHydDT5L=`FH(ItiwCdrQglN>wxI8{AO;XQ=!itD?Wa+$sS68UCXB zJN#d&zpKjI%-nD;-t9&N_E_~`Q@PHoprbDN!G?FM@J90PFRvHUv_1?KY)%d(E;M@s zSMo!m669PcED)?dN9w|pUJG^CXdV>cGm^|WLi}gi6tM)$`NLn~5aAoz0~1;om}<7EK}nb;OeWvh50kyTK}(&oItA9u?=>zjbMHHFoUKSInb zki6H#uNw>Lul8|#F^xDsr)GF3T@OR`T)dv&UZZLE4B}CoAlAwsK>T(ONFQD-=ca|a z`L#nkQoO7is{sNuXjd%cS#{*g;xZNzkO*>l8uz*U&-hj<)KkmfS%3w3%i4F@Fl z-g$CkmS0<^gX+cGSlu!SoKuS#pO%9UYkxKy(b4t24_aweTUcHv zt$d&OE&43GHh1md`4sn6kF$+@iq@d!Nt3Wz_tCn|*L6Q~3TX_A+<*;<*GQfWF)B%8 z@CO?+VII?0!T7QO6;wR8o)@FegjM~yow?p^AdzR)Z@Ya$d&!ZacBm1^oS;;mPFB08 z31<#jbi(%JGVTH2I~-I{wFap8Au#@fNx>2)B9w-UH+**w7g=7&Q+1RN@-#vesSO#q((dF-)HHg~O$C;Sib CDr5Wr literal 0 HcmV?d00001 diff --git a/icons/turf/floors/lava.dmi b/icons/turf/floors/lava.dmi index e4a276c001d9462cf9d32a12423169e8ad2f8e9d..3b889c9a5f68c17e0ada49b6699e95d3a6bc37d8 100644 GIT binary patch literal 9697 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 literal 7740 zcma)Bc_7r?*C#}lJP2hiOLkJqZYbHaXHDZFYqDftW=4vTrGyk2vTuW|*+$kv_AT3F z-;Kdw%=Uiu{NDF{p5O2NAUsLe#rMNdIN!KACBX-Yvs zsSbQD(@_CEdkr>iz~h#&f%)Cm)>e9YdUkepAt50d85t!drJLs&tk_voh55g#i>??_ zm6x%;#W;dN*UM%O4#CLS=IwM@ z7(2uVx6-`LN|?tH5lxypVy}SzO6ct^TM2>bt(%71R?HV<2*o(}y!`)y{1X z<>3Vat68YU*GZE7P}41!EG=6;H=%0ccJtoLNi8*Qv);kQH44lGfhNMBa$BiLClq5} zyi)AH$m1qI`0l_p9+lI^L?O4+aA4fR#+W5)%~kog7m>o;DpjEIt^Uo z??}IB*0{{i2yCWa6?N^hE9J@9j$?HBapGJ$C8JLVx4=r8$TWqp`yIQ+WpukQ-Z^<< z(VF;>OGj@#2EwSg%1NN*cigAad9l{ZJ$#`^+Ru?*I0Eu7?98<`aK&xzu8Y+ix2Cn> z4jxq1Q!r7c>L=N(vc_yBz1@d2W_=gWHU{*9j*mxME(P6`bMdR#i?HD=3*graPo_X% zwP|aq(w??m(|XKb7P%zmcyVh=ik#*UM)ACj%kb-bIbNtZhSaxtvLhEt`aZo<6{;p(RWBjxQ!z={*c;@< zUsk8$NZb+F7JGLq$|$a3c8@tNG7Lpx=6=?Bc{}Mk^j$zo#bocK?FZu#DiEKgM^Z(v z0b72-RrM-JUt)K%V^thJXhuOEqRk-L-acCMZg*uJbId&u3D6Z=}i_mStB<5$h4 z5d|>HWUHhRrzdtCKexpfIYX^;$(wDgoIS_mrC(UHP+NKo#318O@2c?8#>z1SXY#}u z?m@TBkBR4dlfdsap;csVDq+uL&L%%fVsKxULCM@~By=T2TV8=tX9@yLsrb^`q%eR?B z_T@D2x_oBqpRGZcL@q9;<_xW^cEKLcoulitDYYK_) z$G&#JpLH3NrfeoICYdi~&(unJm%(IO3%c`ICu80h@3V<2vK&#H|s zy_(7h4d~^Ur|CgD474a)>@T|VqUPA%WaZk#*2nnJs@hD6iM=zM@Wh#!N=G3efgZoQ zgOL$)ku?@{*jx8~QV)5Gh<@9tpmF!PqwPjV29TlSyuPtBD|(e@_RZUX9uKujTauim zS|vK+9zj94l{IE6F^5Ja;ZvTf8GdtGimbC;?uFu=KK2P8*|*(qI_#_aZA?w!;Ivs?8^6hi z+S%_D?)i@UiGb~A!&_UaurN;)FM6e0QGP&ibak2{tO^p?l+NGHsvO44qYrQ1Zd{xt zDD?cATjkLTeUEPZ>wR3|Sux$DeOc+Z!0Ev5Mx0Mt&6}{+T!@Ly*1tOHticH)Po7wi zE%0F4JUgEVV;5z&f+mFoSEYbn_+Ep~lYp%PT1+f53tdfO%{&Q{2G@*v}w@rz_lY@ zCIxHpA5i( z>u^NFfuXF&Z_V_ozGYS$B|d0y=~r!BX12??@Xj$0K5@pZjiJ9+ZrMOkP+j&k>N3w> z<>A2s`@LJBX}=}ur*Si1@UZ{()Ly}w7Apq-o?$)hc!`_xX{>pOfWuc!0q6%H>b^1R z4?P@t9^)s>IH$PX_w0SFaGVCPFM|%MbOT^xeWq=Ci4AE8l=eF)uqm*$VK$#Mu_;n+ z6CL?lAMdT#lYzxMWSr8C#rHf!0Nh!@WQLVA!BGaI6u#Y_xGE_Bh%$8Py0h17t*1`G zfyXhLrv1Fc7JpJ7E2`D2&xtq6$wEKD0lJv1HQS=BK3vb%+t_O)L^(4$gtlE}PCdQ_ zb(FcHvdOdwBBk{=2%4)tKQleKYACH1 z!3&l$wumM9;W76>ll$Rrf|0gt4UoV;e8k45b9tcx;M82QtIec1Z0WKX%a;U{4&{ev zw%okMwUzmUK|rP_a%J&3lZ#=b) ziaO(GS^JdD>EJpGgAIPiTjj-&iRySrF(fyI)3BVx!5 zYI;gq#fdJZI!2SGNca?|P-9|KButdjW)T$9@+oawI<6!u+MaRJ<-xSy!(ViJUjiPZ zcr6=LOhTxmt~tdyhGq^n3kOw-9&w4J3X_VY8{y40-PgyBE4EcVPu}&y4A_$Pi zM4&9>4K`#nc!L(@Llxo`x{u>7vu(gWM*?N2FVS9Hc{6zfF{bvpx$J!7Fw(_K4ed?- z(|h)$?GZmy<1(twORLOBJ~2us9_?qVLhDqg?RkOA-^6pS{W{39Ic! zKyI)GSv()G=W#s=z2vbcYhW+wdICv>Jqooc;h|o*V`x8@{IHywt|LZlH5{rUzS@xr z;-hl=mMZdY zrsDJ^SbPf*gjn5M(63NBLfI?9l4KdEU`$rCTFhwOD^+51h~dYOQ+k>wN0}-*0MYp< z@wYMrExFrwf6nZIkK;%E=dwH=!pcWSP$kH~o%JJY680zo*e?!p1H{7XH+;qz7r*S| z-B39ODE{fWi*Psx2ytung3@8)%g)4fEFQWQxo-IXBC3@Yz zx_Oy6K%QUF)0Hq4N$7*p&4gs;wnSwvpp9Vs;e7!*Gh~lh6tFv9-bB3o3*A3bD_M|9 zTm%HRKE2&j;6e_vBGemC45{L~+ZVdC$qoYNcz-vC81MZQo+V)_j(sGHpFifLo&UX@ z^eEj~*dKpM4ieGEQb6-pzJffc-mcz8wp{z^Sf_^RUhLx8@Py2BTMlfGi;#lUF9lq5 ztZTWpm18t}(!rm|t-z%gxXFNOr95YL3U7awV{%vy916i+nu85Cn95lJ$7Nvtn{N+( zk2@at{=C!NQc?b?vl*Dr?4jqlHNmO1qmT!MEF{vJXq~Qn(PZ?jGByUpci(;f98&fv z|IRKqh_7Bj$I%{+`sQf=5_OEBK#N@>bP-+v&7hQj|I+c0;PE{;IAXmKYi0zv3v-h` zGeEzn>|A~#2T`p-fi$SIWJKhI=B$3^8{bd++-QWIfgo54J zbbnrwJb~ohd*tL|kCP#ErLFh8#Hw7~U3JZ0UqjFP#2C&D0sf1rmm7I9hE7jg8C4l5 zMIG5+swe}TQTE2Yo)A8^V__>A^$AqF%q2%XRp4~MT7>JYk>v=9T*wf*WHa^@S~-_q zk=adsYbbNh4uD=>!n#JxJzbq5<4kysJwwwd*)~G3jhiDQ<>bl6fv=E?71E{oG(6 z_UNjf*$V`qHCzX?VswCKofUQ`&3!_>)41@MoH-p z`4a6o^(hnbGA23!><-zZAy<$V$Zm~eaCM`Rp+y0L%>ga z+~SqqP1d|#<@AN_mV$AS1fWJ6m~LANJ)EtK<(JW_r{*&OKeM$d6({(h z&aOR?yUV!z%QWi;EKN~Ug&Cr~9~)C7`N5%%u=+^v^g^(^7_0i@uGbS`Z|GNp*n5T& z@Lp_;E{h&e)wj5-9c(qWFLi(2aaLV4W!^S&&^l(E;0a!SwI zF$|i*6@0YZO30@0G}rrHui>HW9H4|G=iK_c_qq(D`>~ONE)}_uBBKmZs`1!2mq_}M zh43%OOh1!PgPRKfWI>1@^0s31CW9rV_+8-1W=ScrJahDg`~Y50Z$*R@1A(tZ`x0;@ zgH$Ib(2H1mFtU6Vw0VSix=QhY{T2VqJ3d@g@*77O^PdywoG-^QQ1Ae39G(8|hV2~I z76^p5mwX9jCy-}zmE|Z-u@?|xOD6dplpjc=aNszeK*|>uvG|9b-J3_vvHS(7Bj#yu zIq2g0|M!a`*0wJNUT7{Y-C%hB3ipk8(jh@0 zHZ?vlfgk%_PUU)7VFpgyqBAZ~!-&KDP$}3LBUHCc~*vc_ryHg1Z`P{dG==!sJvdpNd2e$iolMQ~};7UNYCAfadv0M$K z2Se7XVRm%vg;!>lMVt!{@BPlti3$qO#4)#^f_ctKPf_)I++sCvQ}8{FR7{S=c;k3D zU1zHHY`6RA!RN9-;3t^~T5|+3T6_vhXEXEVZN^F73>D->ic`+AjQ2q ze|njpdoB&C)Vu5lK*%^ocL`)=l3=oFYM)o>QY;d6=4*9Wmr)1Bp#cz@wbw||C>`1> z4XGBtFf#6S8iBOB+$!@TNpfv$rvWeu37VG=Sc5VkF``d{N+nRMBA=xpU|^<^Z$t16 zBn#W!=}*Yx__PD&ELUrAMXymG<_i!E`HB4d+`8&_r+`$PMA4fXNaV00XzC-;u7s{x zOfeKSbR6Hfc%RskgW(SBE;)f{FUs#Ez%AfGflbAzK)_jcg6{uD@2>;5xq=b2GM8Zi zFpyyg^eTJ=1ibFg|L^y-O$N{qI`rZR#E7_h(&2TAUO8!l5Y=*5M2jYYp&ihK2s9m- zb!~z({{ljQnG?4_QxMLMQ|yHyjN&9SVJ`_YL#^vPatNI^*l_90aXd-^M%Z-C;68tapl>(9@?3dB45+40IqMYFC%@$#Iq5_ z<(MCzO@ON)wB>LFfCDv&nseTdNu$2iX8KqBmh0d!#SOYPdF|&wLGxD3Z>#i?tr~#p zm-)a9l7k#-N$4hF>x=WIumUV@D{_JYUepXJNigwwjY#q zQKwSycacW-3ZW|hMy|IKV48V9z%K3U2l#|2*a<{=5hz~^7UlWwA<=$>uAKFwj|r$C zIbzEQhC2~3g%Wge=m2ln?PWj<@Xw3H{2i`kv#K_P%JJVj^(rLaLHRfu9}h45@d>%d zU8CE!OO8h!Cr}LUk~Cq=N7I?7#hu*5lR-2-60RdD>qd@5=}1(^$2h ze0WQeplSF;%=Sx<*HZGw>&~DfpuEH$%m7LHzEx2Pvb)V7`?PQs$yEDYb$4)4x&err z5w<|h;uxO=WLtL<2zzj}K|G!r0aEQIPUArdN`9XW7f?AIA&z7bd1d1EES22n#h8+J zT0c*1OTq1JzknCB4Rj$Uf7EK-dC*y z@_a~Vf41LYUMjyf(V=(=T66BTDbi?G0mKNwR|LCAx` zmpJu^pduuodp0>x0lHwSN$ProFBU~2BC1ExZwa@H$RgMyNm$YE(T^qHP>;W^DJBFW zq>%YEBm_zX7img1+CW>AOvz|(f@UDnj0MNw5C!XirI0(zhNQzKv3Mb*@djB+{~GD# zBb+`F*^Xf=N4zNQK-)OK9%eMT* zZMNNC#y7b}yl^0v{*vIgIvYp2(Ckt2+mp~#+ zT;D(nR`l=cbqvo)=TSL!c|Yfe98k?V_|BA+lZj~G{{zW=WXT%G?@pN?AfNG>v@x0vm z)JW_seb(RBBVRr^rA{-eBH=6JZ3goN+5{(_4svLqlz7_{qs^8EaEqQIJKZzrL?AzebNWGQ+Hu=U3!T0JP>61ZnCNBAEk4HWLK zZi+Ktd=n18Sx^WAKE5dgx_0g_gxBh7b1Z|C`D2R@;EE3BO9+WP$5J!e@91>pxxT-x zwM}TU)QB0be@Hdn+m|E@9Y6amYU(w{*)f7c%% zYZ@0^9+4!S5%P6>V%8bTVbtUN-{v`$1pCfh>T(54XhO!3sX!mOjG-@+P{9AU6uNf} KH4(S%BmN7FG)6}N diff --git a/icons/turf/floors/lava_mask.dmi b/icons/turf/floors/lava_mask.dmi new file mode 100644 index 0000000000000000000000000000000000000000..aaefebe39deb36acecd43ae4a39f52a9ea381e49 GIT binary patch literal 1628 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 M9@zKL<;1050lw*GEdT%j literal 0 HcmV?d00001 diff --git a/icons/turf/floors/reinf_glass.dmi b/icons/turf/floors/reinf_glass.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a7607cada6a7f69478bb9928ea624775472df96f GIT binary patch literal 4257 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 q+k6pU4kZW9-uYkprWarU literal 0 HcmV?d00001 diff --git a/icons/turf/floors/reinf_plasma_glass.dmi b/icons/turf/floors/reinf_plasma_glass.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1bf4dd41734c898e93a643c642d01df27ca829ca GIT binary patch literal 4047 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 A3;+NC diff --git a/icons/turf/floors/snow_turf.dmi b/icons/turf/floors/snow_turf.dmi index e7fef38602b2aed43320cd9bb5f7c5ecfd5a3592..8b70aa44896fe2d2a90cc83a4d0f6969b253ad55 100644 GIT binary patch literal 5159 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 literal 10907 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 From 4676f1e0d10c8560471b7b6bc854834ddd6b6d5e Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Thu, 7 Dec 2023 08:59:50 -0500 Subject: [PATCH 11/15] fuck it we ball --- code/__DEFINES/dcs/flags.dm | 31 +- code/__DEFINES/dcs/helpers.dm | 7 +- .../signals/signals_atom/signals_atom_main.dm | 2 + code/__DEFINES/dcs/signals/signals_datum.dm | 6 +- code/__DEFINES/dcs/signals/signals_turf.dm | 5 + code/__DEFINES/flags.dm | 5 + code/__DEFINES/traits.dm | 42 +- code/__DEFINES/vv.dm | 2 + code/__HELPERS/cmp.dm | 7 + code/__HELPERS/duplicating.dm | 10 +- code/_compile_options.dm | 2 + code/_globalvars/traits.dm | 42 +- code/controllers/subsystem/dcs.dm | 101 ++++- code/controllers/subsystem/timer.dm | 8 +- code/datums/components/README.md | 128 +----- code/datums/components/_component.dm | 422 ++++++++---------- code/datums/components/crafting/crafting.dm | 2 - code/datums/components/decal.dm | 76 ---- code/datums/components/decals/blood.dm | 39 -- code/datums/components/forensics.dm | 4 +- code/datums/components/rotation.dm | 4 +- code/datums/datum.dm | 70 +-- code/datums/datumvars.dm | 15 +- code/datums/elements/_element.dm | 10 +- code/datums/elements/connect_loc.dm | 2 +- code/datums/elements/decals/_decal.dm | 179 ++++++++ code/datums/elements/decals/blood.dm | 47 ++ code/datums/mind.dm | 2 +- code/datums/signals.dm | 131 ++++++ code/datums/status_effects/buffs/buffs.dm | 3 +- .../objects/effects/decals/cleanable/misc.dm | 2 +- code/game/objects/effects/decals/decal.dm | 2 +- code/game/objects/items/airlock_painter.dm | 4 +- code/game/objects/structures/window.dm | 3 +- code/game/turfs/change_turf.dm | 24 +- code/game/turfs/simulated/wall/reinf_walls.dm | 1 + code/modules/admin/verbs/randomverbs.dm | 46 ++ .../admin/view_variables/topic_basic.dm | 73 ++- .../antagonists/bloodsuckers/vassal/vassal.dm | 2 +- code/modules/antagonists/creep/creep.dm | 2 +- code/modules/antagonists/demon/demons.dm | 2 +- .../antagonists/traitor/datum_traitor.dm | 2 +- code/modules/cargo/supplypod.dm | 12 +- code/modules/mob/living/brain/brain_item.dm | 9 +- code/modules/pool/components/swimming.dm | 4 +- code/modules/pool/pool.dm | 20 +- code/modules/shuttle/on_move.dm | 1 + code/modules/surgery/organs/stomach.dm | 34 +- code/modules/unit_tests/_unit_tests.dm | 11 +- .../unit_tests/dcs_check_list_arguments.dm | 55 +++ .../unit_tests/trait_addition_and_removal.dm | 94 ++++ code/modules/uplink/uplink_devices.dm | 2 +- .../obj/smooth_structures/shuttle_window.dmi | Bin 3738 -> 6254 bytes yogstation.dme | 5 +- yogstation/code/datums/components/crawl.dm | 2 +- .../code/datums/components/storage/storage.dm | 8 +- yogstation/code/datums/components/walks.dm | 2 +- .../game/objects/items/storage/backpack.dm | 7 +- 58 files changed, 1176 insertions(+), 657 deletions(-) delete mode 100644 code/datums/components/decal.dm delete mode 100644 code/datums/components/decals/blood.dm create mode 100644 code/datums/elements/decals/_decal.dm create mode 100644 code/datums/elements/decals/blood.dm create mode 100644 code/datums/signals.dm create mode 100644 code/modules/unit_tests/dcs_check_list_arguments.dm create mode 100644 code/modules/unit_tests/trait_addition_and_removal.dm diff --git a/code/__DEFINES/dcs/flags.dm b/code/__DEFINES/dcs/flags.dm index 916eb247b68b..dbeb0b933a1e 100644 --- a/code/__DEFINES/dcs/flags.dm +++ b/code/__DEFINES/dcs/flags.dm @@ -1,4 +1,4 @@ -/// Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. +/// Return this from `/datum/component/Initialize` or `/datum/component/OnTransfer` or `/datum/component/on_source_add` to have the component be deleted if it's applied to an incorrect type. /// `parent` must not be modified if this is to be returned. /// This will be noted in the runtime logs #define COMPONENT_INCOMPATIBLE 1 @@ -22,19 +22,36 @@ /// Causes all detach arguments to be passed to detach instead of only being used to identify the element /// When this is used your Detach proc should have the same signature as your Attach proc #define ELEMENT_COMPLEX_DETACH (1 << 2) +/** + * Elements with this flag will have their datum lists arguments compared as is, + * without the contents being sorted alpha-numerically first. + * This is good for those elements where the position of the keys matter, like in the case of color matrices. + */ +#define ELEMENT_DONT_SORT_LIST_ARGS (1<<3) +/** + * Elements with this flag will be ignored by the dcs_check_list_arguments test. + * A good example is connect_loc, for which it's pratically undoable unless we force every signal proc to have a different name. + */ +#define ELEMENT_NO_LIST_UNIT_TEST (1<<4) // How multiple components of the exact same type are handled in the same datum /// old component is deleted (default) -#define COMPONENT_DUPE_HIGHLANDER 0 +#define COMPONENT_DUPE_HIGHLANDER 0 /// duplicates allowed -#define COMPONENT_DUPE_ALLOWED 1 +#define COMPONENT_DUPE_ALLOWED 1 /// new component is deleted -#define COMPONENT_DUPE_UNIQUE 2 +#define COMPONENT_DUPE_UNIQUE 2 +/** + * Component uses source tracking to manage adding and removal logic. + * Add a source/spawn to/the component by using AddComponentFrom(source, component_type, args...) + * Removing the last source will automatically remove the component from the parent. + * Arguments will be passed to on_source_add(source, args...); ensure that Initialize and on_source_add have the same signature. + */ +#define COMPONENT_DUPE_SOURCES 3 /// old component is given the initialization args of the new -#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 +#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 /// each component of the same type is consulted as to whether the duplicate should be allowed -#define COMPONENT_DUPE_SELECTIVE 5 - +#define COMPONENT_DUPE_SELECTIVE 5 //Arch #define ARCH_PROB "probability" //Probability for each item diff --git a/code/__DEFINES/dcs/helpers.dm b/code/__DEFINES/dcs/helpers.dm index 12eea34e1560..df6acffd761a 100644 --- a/code/__DEFINES/dcs/helpers.dm +++ b/code/__DEFINES/dcs/helpers.dm @@ -2,7 +2,7 @@ /// The datum hosting the signal is automaticaly added as the first argument /// Returns a bitfield gathered from all registered procs /// Arguments given here are packaged in a list and given to _SendSignal -#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) ) +#define SEND_SIGNAL(target, sigtype, arguments...) ( !target._listen_lookup?[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) ) #define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) ) @@ -12,12 +12,15 @@ /// A wrapper for _AddElement that allows us to pretend we're using normal named arguments #define AddElement(arguments...) _AddElement(list(##arguments)) - /// A wrapper for _RemoveElement that allows us to pretend we're using normal named arguments #define RemoveElement(arguments...) _RemoveElement(list(##arguments)) /// A wrapper for _AddComponent that allows us to pretend we're using normal named arguments #define AddComponent(arguments...) _AddComponent(list(##arguments)) +/// A wrapper for _AddComonent that passes in a source. +/// Necessary if dupe_mode is set to COMPONENT_DUPE_SOURCES. +#define AddComponentFrom(source, arguments...) _AddComponent(list(##arguments), source) + /// A wrapper for _LoadComponent that allows us to pretend we're using normal named arguments #define LoadComponent(arguments...) _LoadComponent(list(##arguments)) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm index 43db37a73bd4..4fc26240b084 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm @@ -43,6 +43,8 @@ #define COMSIG_ATOM_UPDATED_ICON "atom_updated_icon" ///from base of [/atom/proc/smooth_icon]: () #define COMSIG_ATOM_SMOOTHED_ICON "atom_smoothed_icon" +///from [/datum/controller/subsystem/processing/dcs/proc/rotate_decals]: (list/datum/element/decal/rotating) +#define COMSIG_ATOM_DECALS_ROTATING "atom_decals_rotating" ///from base of atom/Entered(): (atom/movable/arrived, atom/old_loc, list/atom/old_locs) #define COMSIG_ATOM_ENTERED "atom_entered" ///from base of atom/movable/Moved(): (atom/movable/arrived, atom/old_loc, list/atom/old_locs) diff --git a/code/__DEFINES/dcs/signals/signals_datum.dm b/code/__DEFINES/dcs/signals/signals_datum.dm index 9b3067bc8a45..b75bc99b7a4f 100644 --- a/code/__DEFINES/dcs/signals/signals_datum.dm +++ b/code/__DEFINES/dcs/signals/signals_datum.dm @@ -7,7 +7,11 @@ #define COMSIG_COMPONENT_REMOVING "component_removing" //! before a component is removed from a datum because of RemoveComponent: (/datum/component) #define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" //! before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation #define COMSIG_PARENT_QDELETING "parent_qdeleting" //! just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called -#define COMSIG_TOPIC "handle_topic" //! generic topic handler (usr, href_list) +/// generic topic handler (usr, href_list) +#define COMSIG_TOPIC "handle_topic" +/// handler for vv_do_topic (usr, href_list) +#define COMSIG_VV_TOPIC "vv_topic" + #define COMPONENT_VV_HANDLED (1<<0) /// fires on the target datum when an element is attached to it (/datum/element) #define COMSIG_ELEMENT_ATTACH "element_attach" diff --git a/code/__DEFINES/dcs/signals/signals_turf.dm b/code/__DEFINES/dcs/signals/signals_turf.dm index 08b1a59d7bc0..9e56fb8a102d 100644 --- a/code/__DEFINES/dcs/signals/signals_turf.dm +++ b/code/__DEFINES/dcs/signals/signals_turf.dm @@ -6,10 +6,15 @@ #define COMSIG_TURF_CHANGE "turf_change" //! from base of turf/ChangeTurf(): (path, list/new_baseturfs, flags, list/transferring_comps) #define COMSIG_TURF_HAS_GRAVITY "turf_has_gravity" //! from base of atom/has_gravity(): (atom/asker, list/forced_gravities) #define COMSIG_TURF_MULTIZ_NEW "turf_multiz_new" //! from base of turf/New(): (turf/source, direction) +///from base of turf/proc/onShuttleMove(): (turf/new_turf) +#define COMSIG_TURF_ON_SHUTTLE_MOVE "turf_on_shuttle_move" #define COMSIG_TURF_AFTER_SHUTTLE_MOVE "turf_after_shuttle_move" //! from base of turf/proc/afterShuttleMove: (turf/new_turf) ///from base of /datum/turf_reservation/proc/Release: (datum/turf_reservation/reservation) #define COMSIG_TURF_RESERVATION_RELEASED "turf_reservation_released" +///from /datum/element/decal/Detach(): (description, cleanable, directional, mutable_appearance/pic) +#define COMSIG_TURF_DECAL_DETACHED "turf_decal_detached" + ///from /datum/element/footstep/prepare_step(): (list/steps) #define COMSIG_TURF_PREPARE_STEP_SOUND "turf_prepare_step_sound" ///from base of datum/thrownthing/finalize(): (turf/turf, atom/movable/thrownthing) when something is thrown and lands on us diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 3820e833457b..92c198dd18c5 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -69,6 +69,11 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define NO_RUINS_1 (1<<18) /// Blocks this turf from being rusted #define NO_RUST (1<<19) +/// If this atom has experienced a decal element "init finished" sourced appearance update +/// We use this to ensure stacked decals don't double up appearance updates for no rasin +/// Flag as an optimization, don't make this a trait without profiling +/// Yes I know this is a stupid flag, no you can't take him from me +#define DECAL_INIT_UPDATE_EXPERIENCED_1 (1<<20) //AREA FLAGS /// If blobs can spawn there and if it counts towards their score. diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 0fb442556932..9e1c49fc6a9c 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -5,13 +5,13 @@ #define ADD_TRAIT(target, trait, source) \ do { \ var/list/_L; \ - if (!target.status_traits) { \ - target.status_traits = list(); \ - _L = target.status_traits; \ + if (!target._status_traits) { \ + target._status_traits = list(); \ + _L = target._status_traits; \ _L[trait] = list(source); \ SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \ } else { \ - _L = target.status_traits; \ + _L = target._status_traits; \ if (_L[trait]) { \ _L[trait] |= list(source); \ } else { \ @@ -22,14 +22,14 @@ } while (0) #define REMOVE_TRAIT(target, trait, sources) \ do { \ - var/list/_L = target.status_traits; \ + var/list/_L = target._status_traits; \ var/list/_S; \ if (sources && !islist(sources)) { \ _S = list(sources); \ } else { \ _S = sources\ }; \ - if (_L && _L[trait]) { \ + if (_L?[trait]) { \ for (var/_T in _L[trait]) { \ if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \ _L[trait] -= _T \ @@ -40,20 +40,20 @@ SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \ }; \ if (!length(_L)) { \ - target.status_traits = null \ + target._status_traits = null \ }; \ } \ } while (0) #define REMOVE_TRAIT_NOT_FROM(target, trait, sources) \ do { \ - var/list/_traits_list = target.status_traits; \ + var/list/_traits_list = target._status_traits; \ var/list/_sources_list; \ if (sources && !islist(sources)) { \ _sources_list = list(sources); \ } else { \ _sources_list = sources\ }; \ - if (_traits_list && _traits_list[trait]) { \ + if (_traits_list?[trait]) { \ for (var/_trait_source in _traits_list[trait]) { \ if (!(_trait_source in _sources_list)) { \ _traits_list[trait] -= _trait_source \ @@ -64,13 +64,13 @@ SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \ }; \ if (!length(_traits_list)) { \ - target.status_traits = null \ + target._status_traits = null \ }; \ } \ } while (0) #define REMOVE_TRAITS_NOT_IN(target, sources) \ do { \ - var/list/_L = target.status_traits; \ + var/list/_L = target._status_traits; \ var/list/_S = sources; \ if (_L) { \ for (var/_T in _L) { \ @@ -81,14 +81,14 @@ }; \ };\ if (!length(_L)) { \ - target.status_traits = null\ + target._status_traits = null\ };\ }\ } while (0) #define REMOVE_TRAITS_IN(target, sources) \ do { \ - var/list/_L = target.status_traits; \ + var/list/_L = target._status_traits; \ var/list/_S = sources; \ if (sources && !islist(sources)) { \ _S = list(sources); \ @@ -104,12 +104,22 @@ }; \ };\ if (!length(_L)) { \ - target.status_traits = null\ + target._status_traits = null\ };\ }\ } while (0) -#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE) -#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE) + +#define HAS_TRAIT(target, trait) (target._status_traits?[trait] ? TRUE : FALSE) +#define HAS_TRAIT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait])) +#define HAS_TRAIT_FROM_ONLY(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait]) && (length(target._status_traits[trait]) == 1)) +#define HAS_TRAIT_NOT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (length(target._status_traits[trait] - source) > 0)) +/// Returns a list of trait sources for this trait. Only useful for wacko cases and internal futzing +/// You should not be using this +#define GET_TRAIT_SOURCES(target, trait) (target._status_traits?[trait] || list()) +/// Returns the amount of sources for a trait. useful if you don't want to have a "thing counter" stuck around all the time +#define COUNT_TRAIT_SOURCES(target, trait) length(GET_TRAIT_SOURCES(target, trait)) +/// A simple helper for checking traits in a mob's mind +#define HAS_MIND_TRAIT(target, trait) (HAS_TRAIT(target, trait) || (target.mind ? HAS_TRAIT(target.mind, trait) : FALSE)) //Remember to update code/datums/traits/ folder if you're adding/removing/renaming traits. diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 7be173cddf12..5d008e22a7d3 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -65,6 +65,8 @@ #define VV_HK_CALLPROC "proc_call" #define VV_HK_MARK "mark" #define VV_HK_ADDCOMPONENT "addcomponent" +#define VV_HK_REMOVECOMPONENT "removecomponent" +#define VV_HK_MASS_REMOVECOMPONENT "massremovecomponent" #define VV_HK_MODIFY_TRAITS "modtraits" #define VV_HK_VIEW_REFERENCES "viewreferences" #define VV_HK_WEAKREF_RESOLVE "weakref_resolve" diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 73118506fcf1..dac2c41b9b30 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -10,6 +10,13 @@ /proc/cmp_text_dsc(a,b) return sorttext(a,b) +/proc/cmp_embed_text_asc(a,b) + if(isdatum(a)) + a = REF(a) + if(isdatum(b)) + b = REF(b) + return sorttext("[b]", "[a]") + /proc/cmp_name_asc(atom/a, atom/b) return sorttext(b.name, a.name) diff --git a/code/__HELPERS/duplicating.dm b/code/__HELPERS/duplicating.dm index 45d065127fe8..a21399e68273 100644 --- a/code/__HELPERS/duplicating.dm +++ b/code/__HELPERS/duplicating.dm @@ -1,19 +1,21 @@ ///List of all vars that will not be copied over when using duplicate_object() GLOBAL_LIST_INIT(duplicate_forbidden_vars, list( + "_active_timers", + "_datum_components", + "_listen_lookup", + "_status_traits", + "_signal_procs", "actions", "active_hud_list", - "active_timers", "AIStatus", "appearance", "area", "atmos_adjacent_turfs", "bodyparts", "ckey", - "comp_lookup", "computer_id", "contents", "cooldowns", - "datum_components", "external_organs", "external_organs_slot", "group", @@ -38,8 +40,6 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars, list( "power_supply", "quirks", "reagents", - "signal_procs", - "status_traits", "stat", "tag", "tgui_shared_states", diff --git a/code/_compile_options.dm b/code/_compile_options.dm index ff9cabf129ae..b296be969328 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -31,6 +31,8 @@ #endif // 1 to use the default behaviour; // 2 for preloading absolutely everything; +#define LOWMEMORYMODE + #ifdef LOWMEMORYMODE #define FORCE_MAP "_maps/runtimestation.json" #endif diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index ebc3d1b0de4c..70449111c7d2 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -3,21 +3,43 @@ Try to keep this in sync with __DEFINES/traits.dm quirks have it's own panel so we don't need them here. */ -GLOBAL_LIST_INIT(traits_by_type, list( - /mob = list(/atom/movable = list( - "TRAIT_MOVE_PHASING" = TRAIT_MOVE_PHASING, +GLOBAL_LIST_INIT(admin_visible_traits, list( + /atom/movable = list( + "TRAIT_ASHSTORM_IMMUNE" = TRAIT_ASHSTORM_IMMUNE, + ), + /atom/movable = list( "TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN, - )))) -/// value -> trait name, generated on use from trait_by_type global -GLOBAL_LIST(trait_name_map) + ), + /mob = list( + "TRAIT_ABDUCTOR_SCIENTIST_TRAINING" = TRAIT_ABDUCTOR_SCIENTIST_TRAINING, + "TRAIT_ANTIMAGIC" = TRAIT_ANTIMAGIC, + "TRAIT_BOMBIMMUNE" = TRAIT_BOMBIMMUNE, + "TRAIT_DEAF" = TRAIT_DEAF, + "TRAIT_HOLY" = TRAIT_HOLY, + "TRAIT_NO_SOUL" = TRAIT_NO_SOUL, + ), + /obj/item = list( + "TRAIT_NODROP" = TRAIT_NODROP, + "TRAIT_T_RAY_VISIBLE" = TRAIT_T_RAY_VISIBLE, + ), + /obj/item/organ/liver = list( + "TRAIT_BALLMER_SCIENTIST" = TRAIT_BALLMER_SCIENTIST, + ), +)) -/proc/generate_trait_name_map() +/// value -> trait name, generated as needed for adminning. +GLOBAL_LIST(admin_trait_name_map) + +/proc/generate_admin_trait_name_map() . = list() - for(var/key in GLOB.traits_by_type) - for(var/tname in GLOB.traits_by_type[key]) - var/val = GLOB.traits_by_type[key][tname] + for(var/key in GLOB.admin_visible_traits) + for(var/tname in GLOB.admin_visible_traits[key]) + var/val = GLOB.admin_visible_traits[key][tname] .[val] = tname + return . + + GLOBAL_LIST_INIT(movement_type_trait_to_flag, list( TRAIT_MOVE_GROUND = GROUND, TRAIT_MOVE_FLYING = FLYING, diff --git a/code/controllers/subsystem/dcs.dm b/code/controllers/subsystem/dcs.dm index f9abd6db3dc6..e130b6dc0ff8 100644 --- a/code/controllers/subsystem/dcs.dm +++ b/code/controllers/subsystem/dcs.dm @@ -5,10 +5,34 @@ PROCESSING_SUBSYSTEM_DEF(dcs) var/list/elements_by_type = list() + /** + * A nested assoc list of bespoke element types (keys) and superlists containing all lists used as arguments (values). + * Inside the superlists, lists that've been sorted alphabetically are keys, while the original unsorted lists are values. + * + * e.g. list( + * /datum/element/first = list(list(A, B, C) = list(B, A, C), list(A, B) = list(A, B)), + * /datum/element/second = list(list(B, C) = list(C, B), list(D) = list(D)), + * ) + * + * Used by the dcs_check_list_arguments unit test. + */ + var/list/arguments_that_are_lists_by_element = list() + /** + * An assoc list of list instances and their sorted counterparts. + * + * e.g. list( + * list(B, A, C) = list(A, B, C), + * list(C, B) = list(B, C), + * ) + * + * Used to make sure each list instance is sorted no more than once, or the unit test won't work. + */ + var/list/sorted_arguments_that_are_lists = list() + /datum/controller/subsystem/processing/dcs/Recover() - comp_lookup = SSdcs.comp_lookup + _listen_lookup = SSdcs._listen_lookup -/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments) +/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments, init_element = TRUE) var/datum/element/eletype = arguments[1] var/element_id = eletype @@ -16,39 +40,70 @@ PROCESSING_SUBSYSTEM_DEF(dcs) CRASH("Attempted to instantiate [eletype] as a /datum/element") if(initial(eletype.element_flags) & ELEMENT_BESPOKE) - element_id = GetIdFromArguments(arguments) + element_id = length(arguments) == 1 ? "[arguments[1]]" : GetIdFromArguments(arguments) . = elements_by_type[element_id] - if(.) + if(. || !init_element) return . = elements_by_type[element_id] = new eletype /**** - * Generates an id for bespoke elements when given the argument list - * Generating the id here is a bit complex because we need to support named arguments - * Named arguments can appear in any order and we need them to appear after ordered arguments - * We assume that no one will pass in a named argument with a value of null - **/ + * Generates an id for bespoke elements when given the argument list + * Generating the id here is a bit complex because we need to support named arguments + * Named arguments can appear in any order and we need them to appear after ordered arguments + * We assume that no one will pass in a named argument with a value of null + **/ /datum/controller/subsystem/processing/dcs/proc/GetIdFromArguments(list/arguments) var/datum/element/eletype = arguments[1] - var/list/fullid = list("[eletype]") - var/list/named_arguments = list() + var/list/fullid = list(eletype) + var/list/named_arguments for(var/i in initial(eletype.argument_hash_start_idx) to length(arguments)) var/key = arguments[i] - var/value + if(istext(key)) - value = arguments[key] - if(!(istext(key) || isnum(key))) - key = ref(key) - key = "[key]" // Key is stringified so numbers dont break things - if(!isnull(value)) - if(!(istext(value) || isnum(value))) - value = ref(value) - named_arguments["[key]"] = value + var/value = arguments[key] + if (isnull(value)) + fullid += key + else + if (!istext(value) && !isnum(value)) + if(PERFORM_ALL_TESTS(dcs_check_list_arguments) && islist(value)) + add_to_arguments_that_are_lists(value, eletype) + value = REF(value) + + if (!named_arguments) + named_arguments = list() + + named_arguments[key] = value + continue + + if (isnum(key)) + fullid += key else - fullid += "[key]" + if(PERFORM_ALL_TESTS(dcs_check_list_arguments) && islist(key)) + add_to_arguments_that_are_lists(key, eletype) + fullid += REF(key) - if(length(named_arguments)) - named_arguments = sortList(named_arguments) + if(named_arguments) + named_arguments = sortTim(named_arguments, GLOBAL_PROC_REF(cmp_text_asc)) fullid += named_arguments + return list2params(fullid) + +/** + * Offloading the first half of the dcs_check_list_arguments here, which is populating the superlist + * with sublists that will be later compared with each other by the dcs_check_list_arguments unit test. + */ +/datum/controller/subsystem/processing/dcs/proc/add_to_arguments_that_are_lists(list/argument, datum/element/element_type) + if(initial(element_type.element_flags) & ELEMENT_NO_LIST_UNIT_TEST) + return + var/list/element_type_superlist = arguments_that_are_lists_by_element[element_type] + if(!element_type_superlist) + arguments_that_are_lists_by_element[element_type] = element_type_superlist = list() + + var/list/sorted_argument = argument + if(!(initial(element_type.element_flags) & ELEMENT_DONT_SORT_LIST_ARGS)) + sorted_argument = sorted_arguments_that_are_lists[argument] + if(!sorted_argument) + sorted_arguments_that_are_lists[argument] = sorted_argument = sortTim(argument.Copy(), GLOBAL_PROC_REF(cmp_embed_text_asc)) + + element_type_superlist[sorted_argument] = argument diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 54df5a5e312e..a056a6c07512 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -429,7 +429,7 @@ SUBSYSTEM_DEF(timer) CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]") if (callBack.object != GLOBAL_PROC && !QDESTROYING(callBack.object)) - LAZYADD(callBack.object.active_timers, src) + LAZYADD(callBack.object._active_timers, src) bucketJoin() @@ -438,9 +438,9 @@ SUBSYSTEM_DEF(timer) if (flags & TIMER_UNIQUE && hash) timer_subsystem.hashes -= hash - if (callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers) - callBack.object.active_timers -= src - UNSETEMPTY(callBack.object.active_timers) + if (callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object._active_timers) + callBack.object._active_timers -= src + UNSETEMPTY(callBack.object._active_timers) callBack = null diff --git a/code/datums/components/README.md b/code/datums/components/README.md index 9db03c5dfc5d..db8bf10a327f 100644 --- a/code/datums/components/README.md +++ b/code/datums/components/README.md @@ -4,130 +4,6 @@ Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SendSignal()` call. Now every component that want's to can also know about this happening. -### In the code +### [HackMD page for an introduction to the system as a whole.](https://hackmd.io/@tgstation/SignalsComponentsElements) -#### Slippery things - -At the time of this writing, every object that is slippery overrides atom/Crossed does some checks, then slips the mob. Instead of all those Crossed overrides they could add a slippery component to all these objects. And have the checks in one proc that is run by the Crossed event - -#### Powercells - -A lot of objects have powercells. The `get_cell()` proc was added to give generic access to the cell var if it had one. This is just a specific use case of `GetComponent()` - -#### Radios - -The radio object as it is should not exist, given that more things use the _concept_ of radios rather than the object itself. The actual function of the radio can exist in a component which all the things that use it (Request consoles, actual radios, the SM shard) can add to themselves. - -#### Standos - -Stands have a lot of procs which mimic mob procs. Rather than inserting hooks for all these procs in overrides, the same can be accomplished with signals - -## API - -### Defines - -1. `COMPONENT_INCOMPATIBLE` Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. `parent` must not be modified if this is to be returned. This will be noted in the runtime logs - -### Vars - -1. `/datum/var/list/datum_components` (private) - * Lazy associated list of type -> component/list of components. -1. `/datum/var/list/comp_lookup` (private) - * Lazy associated list of signal -> registree/list of registrees -1. `/datum/var/list/signal_procs` (private) - * Associated lazy list of signals -> `/datum/callback`s that will be run when the parent datum receives that signal -1. `/datum/var/signal_enabled` (protected, boolean) - * If the datum is signal enabled. If not, it will not react to signals - * `FALSE` by default, set to `TRUE` when a signal is registered -1. `/datum/component/var/dupe_mode` (protected, enum) - * How duplicate component types are handled when added to the datum. - * `COMPONENT_DUPE_HIGHLANDER` (default): Old component will be deleted, new component will first have `/datum/component/proc/InheritComponent(datum/component/old, FALSE)` on it - * `COMPONENT_DUPE_ALLOWED`: The components will be treated as separate, `GetComponent()` will return the first added - * `COMPONENT_DUPE_UNIQUE`: New component will be deleted, old component will first have `/datum/component/proc/InheritComponent(datum/component/new, TRUE)` on it - * `COMPONENT_DUPE_UNIQUE_PASSARGS`: New component will never exist and instead its initialization arguments will be passed on to the old component. -1. `/datum/component/var/dupe_type` (protected, type) - * Definition of a duplicate component type - * `null` means exact match on `type` (default) - * Any other type means that and all subtypes -1. `/datum/component/var/datum/parent` (protected, read-only) - * The datum this component belongs to - * Never `null` in child procs -1. `report_signal_origin` (protected, boolean) - * If `TRUE`, will invoke the callback when signalled with the signal type as the first argument. - * `FALSE` by default. - -### Procs - -1. `/datum/proc/GetComponent(component_type(type)) -> datum/component?` (public, final) - * Returns a reference to a component of component_type if it exists in the datum, null otherwise -1. `/datum/proc/GetComponents(component_type(type)) -> list` (public, final) - * Returns a list of references to all components of component_type that exist in the datum -1. `/datum/proc/GetExactComponent(component_type(type)) -> datum/component?` (public, final) - * Returns a reference to a component whose type MATCHES component_type if that component exists in the datum, null otherwise -1. `SEND_SIGNAL(target, sigtype, ...)` (public, final) - * Use to send signals to target datum - * Extra arguments are to be specified in the signal definition - * Returns a bitflag with signal specific information assembled from all activated components - * Arguments are packaged in a list and handed off to _SendSignal() -1. `/datum/proc/AddComponent(component_type(type), ...) -> datum/component` (public, final) - * Creates an instance of `component_type` in the datum and passes `...` to its `Initialize(mapload)` call - * Sends the `COMSIG_COMPONENT_ADDED` signal to the datum - * All components a datum owns are deleted with the datum - * Returns the component that was created. Or the old component in a dupe situation where `COMPONENT_DUPE_UNIQUE` was set - * If this tries to add an component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it - * Properly handles duplicate situations based on the `dupe_mode` var -1. `/datum/proc/LoadComponent(component_type(type), ...) -> datum/component` (public, final) - * Equivalent to calling `GetComponent(component_type)` where, if the result would be `null`, returns `AddComponent(component_type, ...)` instead -1. `/datum/proc/ComponentActivated(datum/component/C)` (abstract, async) - * Called on a component's `parent` after a signal received causes it to activate. `src` is the parameter - * Will only be called if a component's callback returns `TRUE` -1. `/datum/proc/TakeComponent(datum/component/C)` (public, final) - * Properly transfers ownership of a component from one datum to another - * Signals `COMSIG_COMPONENT_REMOVING` on the parent - * Called on the datum you want to own the component with another datum's component -1. `/datum/proc/_SendSignal(signal, list/arguments)` (private, final) - * Handles most of the actual signaling procedure - * Will runtime if used on datums with an empty component list -1. `/datum/proc/RegisterSignal(datum/target, signal(string/list of strings), proc_ref(type), override(boolean))` (protected, final) - * If signal is a list it will be as if RegisterSignal was called for each of the entries with the same following arguments - * Makes the datum listen for the specified `signal` on it's `parent` datum. - * When that signal is received `proc_ref` will be called on the component, along with associated arguments - * Example proc ref: `PROC_REF(OnEvent)` - * If a previous registration is overwritten by the call, a runtime occurs. Setting `override` to TRUE prevents this - * These callbacks run asyncronously - * Returning `TRUE` from these callbacks will trigger a `TRUE` return from the `SendSignal()` that initiated it -1. `/datum/component/New(datum/parent, ...)` (private, final) - * Runs internal setup for the component - * Extra arguments are passed to `Initialize(mapload)` -1. `/datum/component/Initialize(...)` (abstract, no-sleep) - * Called by `New()` with the same argments excluding `parent` - * Component does not exist in `parent`'s `datum_components` list yet, although `parent` is set and may be used - * Signals will not be received while this function is running - * Component may be deleted after this function completes without being attached - * Do not call `qdel(src)` from this function -1. `/datum/component/Destroy(force(bool), silent(bool))` (virtual, no-sleep) - * Sends the `COMSIG_COMPONENT_REMOVING` signal to the parent datum if the `parent` isn't being qdeleted - * Properly removes the component from `parent` and cleans up references - * Setting `force` makes it not check for and remove the component from the parent - * Setting `silent` deletes the component without sending a `COMSIG_COMPONENT_REMOVING` signal -1. `/datum/component/proc/InheritComponent(datum/component/C, i_am_original(boolean))` (abstract, no-sleep) - * Called on a component when a component of the same type was added to the same parent - * See `/datum/component/var/dupe_mode` - * `C`'s type will always be the same of the called component -1. `/datum/component/proc/AfterComponentActivated()` (abstract, async) - * Called on a component that was activated after it's `parent`'s `ComponentActivated()` is called -1. `/datum/component/proc/OnTransfer(datum/new_parent)` (abstract, no-sleep) - * Called before `new_parent` is assigned to `parent` in `TakeComponent()` - * Allows the component to react to ownership transfers -1. `/datum/component/proc/_RemoveFromParent()` (private, final) - * Clears `parent` and removes the component from it's component list -1. `/datum/component/proc/_JoinParent` (private, final) - * Tries to add the component to it's `parent`s `datum_components` list -1. `/datum/component/proc/RegisterWithParent` (abstract, no-sleep) - * Used to register the signals that should be on the `parent` object - * Use this if you plan on the component transfering between parents -1. `/datum/component/proc/UnregisterFromParent` (abstract, no-sleep) - * Counterpart to `RegisterWithParent()` - * Used to unregister the signals that should only be on the `parent` object - -### See/Define signals and their arguments in __DEFINES\components.dm +### See/Define signals and their arguments in [__DEFINES\components.dm](../../__DEFINES/components.dm) diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 3d201a78c066..e461aa2ee36d 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -10,18 +10,41 @@ * Useful when you want shared behaviour independent of type inheritance */ /datum/component + /** + * Defines how duplicate existing components are handled when added to a datum + * + * See [COMPONENT_DUPE_*][COMPONENT_DUPE_ALLOWED] definitions for available options + */ var/dupe_mode = COMPONENT_DUPE_HIGHLANDER + + /** + * The type to check for duplication + * + * `null` means exact match on `type` (default) + * + * Any other type means that and all subtypes + */ var/dupe_type + + /// The datum this components belongs to var/datum/parent - //only set to true if you are able to properly transfer this component - //At a minimum RegisterWithParent and UnregisterFromParent should be used - //Make sure you also implement PostTransfer for any post transfer handling + + /** + * Only set to true if you are able to properly transfer this component + * + * At a minimum [RegisterWithParent][/datum/component/proc/RegisterWithParent] and [UnregisterFromParent][/datum/component/proc/UnregisterFromParent] should be used + * + * Make sure you also implement [PostTransfer][/datum/component/proc/PostTransfer] for any post transfer handling + */ var/can_transfer = FALSE + /// A lazy list of the sources for this component + var/list/sources + /** * Create a new component. * - * Additional arguments are passed to [Initialize(mapload)][/datum/component/proc/Initialize] + * Additional arguments are passed to [Initialize()][/datum/component/proc/Initialize] * * Arguments: * * datum/P the parent datum this component reacts to signals from @@ -30,8 +53,9 @@ parent = raw_args[1] var/list/arguments = raw_args.Copy(2) if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE) + stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]") qdel(src, TRUE, TRUE) - CRASH("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]") + return _JoinParent(parent) @@ -53,7 +77,7 @@ /datum/component/Destroy(force=FALSE, silent=FALSE) if(!parent) return ..() - if (!force) + if(!force) _RemoveFromParent() if(!silent) SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src) @@ -66,22 +90,22 @@ /datum/component/proc/_JoinParent() var/datum/P = parent //lazy init the parent's dc list - var/list/dc = P.datum_components + var/list/dc = P._datum_components if(!dc) - P.datum_components = dc = list() + P._datum_components = dc = list() //set up the typecache var/our_type = type for(var/I in _GetInverseTypeList(our_type)) var/test = dc[I] - if(test) //already another component of this type here + if(test) //already another component of this type here var/list/components_of_type if(!length(test)) components_of_type = list(test) dc[I] = components_of_type else components_of_type = test - if(I == our_type) //exact match, take priority + if(I == our_type) //exact match, take priority var/inserted = FALSE for(var/J in 1 to components_of_type.len) var/datum/component/C = components_of_type[J] @@ -91,44 +115,48 @@ break if(!inserted) components_of_type += src - else //indirect match, back of the line with ya + else //indirect match, back of the line with ya components_of_type += src - else //only component of this type, no list + else //only component of this type, no list dc[I] = src RegisterWithParent() -/** - * Register the component with the parent object - * - * Use this proc to register with your parent object - * - * Overridable proc that's called when added to a new parent - */ -/datum/component/proc/RegisterWithParent() - return - /** * Internal proc to handle behaviour when being removed from a parent */ /datum/component/proc/_RemoveFromParent() - var/datum/P = parent - var/list/dc = P.datum_components + var/datum/parent = src.parent + var/list/parents_components = parent._datum_components for(var/I in _GetInverseTypeList()) - var/list/components_of_type = dc[I] - if(length(components_of_type)) // + var/list/components_of_type = parents_components[I] + + if(length(components_of_type)) // var/list/subtracted = components_of_type - src - if(subtracted.len == 1) //only 1 guy left - dc[I] = subtracted[1] //make him special + + if(subtracted.len == 1) //only 1 guy left + parents_components[I] = subtracted[1] //make him special else - dc[I] = subtracted - else //just us - dc -= I - if(!dc.len) - P.datum_components = null + parents_components[I] = subtracted + + else //just us + parents_components -= I + + if(!parents_components.len) + parent._datum_components = null UnregisterFromParent() +/** + * Register the component with the parent object + * + * Use this proc to register with your parent object + * + * Overridable proc that's called when added to a new parent + */ +/datum/component/proc/RegisterWithParent() + return + /** * Unregister from our parent object * @@ -141,101 +169,26 @@ return /** - * Register to listen for a signal from the passed in target - * - * This sets up a listening relationship such that when the target object emits a signal - * the source datum this proc is called upon, will receive a callback to the given proctype - * Use PROC_REF(procname), TYPE_PROC_REF(type,procname) or GLOBAL_PROC_REF(procname) macros to validate the passed in proc at compile time. - * PROC_REF for procs defined on current type or it's ancestors, TYPE_PROC_REF for procs defined on unrelated type and GLOBAL_PROC_REF for global procs. - * Return values from procs registered must be a bitfield - * - * Arguments: - * * datum/target The target to listen for signals from - * * signal_type A signal name - * * proctype The proc to call back when the signal is emitted - * * override If a previous registration exists you must explicitly set this + * Called when the component has a new source registered. + * Return COMPONENT_INCOMPATIBLE to signal that the source is incompatible and should not be added */ -/datum/proc/RegisterSignal(datum/target, signal_type, proctype, override = FALSE) - if(QDELETED(src) || QDELETED(target)) - return - - if (islist(signal_type)) - var/static/list/known_failures = list() - var/list/signal_type_list = signal_type - var/message = "([target.type]) is registering [signal_type_list.Join(", ")] as a list, the older method. Change it to RegisterSignals." - - if (!(message in known_failures)) - known_failures[message] = TRUE - stack_trace("[target] [message]") - - RegisterSignals(target, signal_type, proctype, override) - return - - var/list/procs = (signal_procs ||= list()) - var/list/target_procs = (procs[target] ||= list()) - var/list/lookup = (target.comp_lookup ||= list()) - - if(!override && target_procs[signal_type]) - log_signal("[signal_type] overridden. Use override = TRUE to suppress this warning.\nTarget: [target] ([target.type]) Proc: [proctype]") - - target_procs[signal_type] = proctype - var/list/looked_up = lookup[signal_type] - - if(isnull(looked_up)) // Nothing has registered here yet - lookup[signal_type] = src - else if(looked_up == src) // We already registered here - return - else if(!length(looked_up)) // One other thing registered here - lookup[signal_type] = list((looked_up) = TRUE, (src) = TRUE) - else // Many other things have registered here - looked_up[src] = TRUE - -/// Registers multiple signals to the same proc. -/datum/proc/RegisterSignals(datum/target, list/signal_types, proctype, override = FALSE) - for (var/signal_type in signal_types) - RegisterSignal(target, signal_type, proctype) +/datum/component/proc/on_source_add(source, ...) + SHOULD_CALL_PARENT(TRUE) + if(dupe_mode != COMPONENT_DUPE_SOURCES) + return COMPONENT_INCOMPATIBLE + LAZYOR(sources, source) /** - * Stop listening to a given signal from target - * - * Breaks the relationship between target and source datum, removing the callback when the signal fires - * - * Doesn't care if a registration exists or not - * - * Arguments: - * * datum/target Datum to stop listening to signals from - * * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically + * Called when the component has a source removed. + * You probably want to call parent after you do your logic because at the end of this we qdel if we have no sources remaining! */ -/datum/proc/UnregisterSignal(datum/target, sig_type_or_types) - if(!target) - return - var/list/lookup = target.comp_lookup - if(!signal_procs || !signal_procs[target] || !lookup) - return - if(!islist(sig_type_or_types)) - sig_type_or_types = list(sig_type_or_types) - for(var/sig in sig_type_or_types) - switch(length(lookup[sig])) - if(2) - lookup[sig] = (lookup[sig]-src)[1] - if(1) - stack_trace("[target] ([target.type]) somehow has single length list inside comp_lookup") - if(src in lookup[sig]) - lookup -= sig - if(!length(lookup)) - target.comp_lookup = null - break - if(0) - lookup -= sig - if(!length(lookup)) - target.comp_lookup = null - break - else - lookup[sig] -= src - - signal_procs[target] -= sig_type_or_types - if(!signal_procs[target].len) - signal_procs -= target +/datum/component/proc/on_source_remove(source) + SHOULD_CALL_PARENT(TRUE) + if(dupe_mode != COMPONENT_DUPE_SOURCES) + CRASH("Component '[type]' does not use sources but is trying to remove a source") + LAZYREMOVE(sources, source) + if(!LAZYLEN(sources)) + qdel(src) /** * Called on a component when a component of the same type was added to the same parent @@ -247,6 +200,7 @@ /datum/component/proc/InheritComponent(datum/component/C, i_am_original) return + /** * Called on a component when a component of the same type was added to the same parent with [COMPONENT_DUPE_SELECTIVE] * @@ -259,6 +213,7 @@ /datum/component/proc/CheckDupeComponent(datum/component/C, ...) return + /** * Callback Just before this component is transferred * @@ -282,34 +237,12 @@ */ /datum/component/proc/_GetInverseTypeList(our_type = type) //we can do this one simple trick + . = list(our_type) var/current_type = parent_type - . = list(our_type, current_type) //and since most components are root level + 1, this won't even have to run while (current_type != /datum/component) - current_type = type2parent(current_type) . += current_type - -/** - * Internal proc to handle most all of the signaling procedure - * - * Will runtime if used on datums with an empty component list - * - * Use the [SEND_SIGNAL] define instead - */ -/datum/proc/_SendSignal(sigtype, list/arguments) - var/target = comp_lookup[sigtype] - if(!length(target)) - var/datum/listening_datum = target - return NONE | call(listening_datum, listening_datum.signal_procs[src][sigtype])(arglist(arguments)) - . = NONE - // This exists so that even if one of the signal receivers unregisters the signal, - // all the objects that are receiving the signal get the signal this final time. - // AKA: No you can't cancel the signal reception of another object by doing an unregister in the same signal. - var/list/queued_calls = list() - for(var/datum/listening_datum as anything in target) - queued_calls[listening_datum] = listening_datum.signal_procs[src][sigtype] - for(var/datum/listening_datum as anything in queued_calls) - . |= call(listening_datum, queued_calls[listening_datum])(arglist(arguments)) + current_type = type2parent(current_type) // The type arg is casted so initial works, you shouldn't be passing a real instance into this /** @@ -324,7 +257,7 @@ RETURN_TYPE(c_type) if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE) stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]") - var/list/dc = datum_components + var/list/dc = _datum_components if(!dc) return null . = dc[c_type] @@ -341,10 +274,10 @@ * * datum/component/c_type The typepath of the component you want to get a reference to */ /datum/proc/GetExactComponent(datum/component/c_type) - RETURN_TYPE(c_type) + RETURN_TYPE(c_type) if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE) stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]") - var/list/dc = datum_components + var/list/dc = _datum_components if(!dc) return null var/datum/component/C = dc[c_type] @@ -362,12 +295,10 @@ * * c_type The component type path */ /datum/proc/GetComponents(c_type) - var/list/dc = datum_components - if(!dc) - return null - . = dc[c_type] - if(!length(.)) - return list(.) + var/list/components = _datum_components?[c_type] + if(!components) + return list() + return islist(components) ? components : list(components) /** * Creates an instance of `new_type` in the datum and attaches to it as parent @@ -380,70 +311,112 @@ * * Properly handles duplicate situations based on the `dupe_mode` var */ -/datum/proc/_AddComponent(list/raw_args) - var/new_type = raw_args[1] - var/datum/component/nt = new_type - var/dm = initial(nt.dupe_mode) - var/dt = initial(nt.dupe_type) - - var/datum/component/old_comp - var/datum/component/new_comp - - if(ispath(nt)) - if(nt == /datum/component) - CRASH("[nt] attempted instantiation!") - else - new_comp = nt - nt = new_comp.type +/datum/proc/_AddComponent(list/raw_args, source) + var/original_type = raw_args[1] + var/datum/component/component_type = original_type - raw_args[1] = src + if(QDELING(src)) + CRASH("Attempted to add a new component of type \[[component_type]\] to a qdeleting parent of type \[[type]\]!") + + var/datum/component/new_component + + if(!ispath(component_type, /datum/component)) + if(!istype(component_type, /datum/component)) + CRASH("Attempted to instantiate \[[component_type]\] as a component added to parent of type \[[type]\]!") + else + new_component = component_type + component_type = new_component.type + else if(component_type == /datum/component) + CRASH("[component_type] attempted instantiation!") + + var/dupe_mode = initial(component_type.dupe_mode) + var/dupe_type = initial(component_type.dupe_type) + var/uses_sources = (dupe_mode == COMPONENT_DUPE_SOURCES) + if(uses_sources && !source) + CRASH("Attempted to add a sourced component of type '[component_type]' to '[type]' without a source!") + else if(!uses_sources && source) + CRASH("Attempted to add a normal component of type '[component_type]' to '[type]' with a source!") - if(dm != COMPONENT_DUPE_ALLOWED) - if(!dt) - old_comp = GetExactComponent(nt) + var/datum/component/old_component + + raw_args[1] = src + if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES) + if(!dupe_type) + old_component = GetExactComponent(component_type) else - old_comp = GetComponent(dt) - if(old_comp) - switch(dm) + old_component = GetComponent(dupe_type) + + if(old_component) + switch(dupe_mode) if(COMPONENT_DUPE_UNIQUE) - if(!new_comp) - new_comp = new nt(raw_args) - if(!QDELETED(new_comp)) - old_comp.InheritComponent(new_comp, TRUE) - QDEL_NULL(new_comp) + if(!new_component) + new_component = new component_type(raw_args) + if(!QDELETED(new_component)) + old_component.InheritComponent(new_component, TRUE) + QDEL_NULL(new_component) + if(COMPONENT_DUPE_HIGHLANDER) - if(!new_comp) - new_comp = new nt(raw_args) - if(!QDELETED(new_comp)) - new_comp.InheritComponent(old_comp, FALSE) - QDEL_NULL(old_comp) + if(!new_component) + new_component = new component_type(raw_args) + if(!QDELETED(new_component)) + new_component.InheritComponent(old_component, FALSE) + QDEL_NULL(old_component) + if(COMPONENT_DUPE_UNIQUE_PASSARGS) - if(!new_comp) + if(!new_component) var/list/arguments = raw_args.Copy(2) arguments.Insert(1, null, TRUE) - old_comp.InheritComponent(arglist(arguments)) + old_component.InheritComponent(arglist(arguments)) else - old_comp.InheritComponent(new_comp, TRUE) - if(COMPONENT_DUPE_SELECTIVE) - var/list/arguments = raw_args.Copy() - arguments[1] = new_comp - var/make_new_component = TRUE - for(var/datum/component/existing_component as anything in GetComponents(new_type)) - if(existing_component.CheckDupeComponent(arglist(arguments))) - make_new_component = FALSE - QDEL_NULL(new_comp) - break - if(!new_comp && make_new_component) - new_comp = new nt(raw_args) - else if(!new_comp) - new_comp = new nt(raw_args) // There's a valid dupe mode but there's no old component, act like normal - else if(!new_comp) - new_comp = new nt(raw_args) // Dupes are allowed, act like normal - - if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy - SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp) - return new_comp - return old_comp + old_component.InheritComponent(new_component, TRUE) + + if(COMPONENT_DUPE_SOURCES) + if(source in old_component.sources) + return old_component // source already registered, no work to do + + if(old_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE) + stack_trace("incompatible source added to a [old_component.type]. Args: [json_encode(raw_args)]") + return null + + else if(!new_component) + new_component = new component_type(raw_args) // There's a valid dupe mode but there's no old component, act like normal + + else if(dupe_mode == COMPONENT_DUPE_SELECTIVE) + var/list/arguments = raw_args.Copy() + arguments[1] = new_component + var/make_new_component = TRUE + for(var/datum/component/existing_component as anything in GetComponents(original_type)) + if(existing_component.CheckDupeComponent(arglist(arguments))) + make_new_component = FALSE + QDEL_NULL(new_component) + break + if(!new_component && make_new_component) + new_component = new component_type(raw_args) + + else if(dupe_mode == COMPONENT_DUPE_SOURCES) + new_component = new component_type(raw_args) + if(new_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE) + stack_trace("incompatible source added to a [new_component.type]. Args: [json_encode(raw_args)]") + return null + + else if(!new_component) + new_component = new component_type(raw_args) // Dupes are allowed, act like normal + + if(!old_component && !QDELETED(new_component)) // Nothing related to duplicate components happened and the new component is healthy + SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_component) + return new_component + + return old_component + +/** + * Removes a component source from this datum + */ +/datum/proc/RemoveComponentSource(source, datum/component/component_type) + if(ispath(component_type)) + component_type = GetExactComponent(component_type) + if(!component_type) + return + component_type.on_source_remove(source) /** * Get existing component of type, or create it and return a reference to it @@ -463,7 +436,7 @@ * Removes the component from parent, ends up with a null parent * Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does */ -/datum/component/proc/RemoveComponent() +/datum/component/proc/ClearFromParent() if(!parent) return var/datum/old_parent = parent @@ -484,7 +457,7 @@ if(!target || target.parent == src) return if(target.parent) - target.RemoveComponent() + target.ClearFromParent() target.parent = src var/result = target.PostTransfer() switch(result) @@ -505,18 +478,19 @@ * * /datum/target the target to move the components to */ /datum/proc/TransferComponents(datum/target) - var/list/dc = datum_components + var/list/dc = _datum_components if(!dc) return - var/comps = dc[/datum/component] - if(islist(comps)) - for(var/datum/component/I in comps) - if(I.can_transfer) - target.TakeComponent(I) - else - var/datum/component/C = comps - if(C.can_transfer) - target.TakeComponent(comps) + for(var/component_key in dc) + var/component_or_list = dc[component_key] + if(islist(component_or_list)) + for(var/datum/component/I in component_or_list) + if(I.can_transfer) + target.TakeComponent(I) + else + var/datum/component/C = component_or_list + if(C.can_transfer) + target.TakeComponent(C) /** * Return the object that is the host of any UI's that this component has diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index d5cf7fb89133..0a9521646b6f 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -76,8 +76,6 @@ .["other"] = list() .["instances"] = list() for(var/obj/item/item in get_environment(a, blacklist)) - if(item.status_traits && HAS_TRAIT(item, TRAIT_NODROP) || item.flags_1 & HOLOGRAM_1) - continue LAZYADDASSOCLIST(.["instances"], item.type, item) if(isstack(item)) var/obj/item/stack/stack = item diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm deleted file mode 100644 index b601ff1475bb..000000000000 --- a/code/datums/components/decal.dm +++ /dev/null @@ -1,76 +0,0 @@ -/datum/component/decal - dupe_mode = COMPONENT_DUPE_ALLOWED - can_transfer = TRUE - var/cleanable - var/description - var/mutable_appearance/pic - - var/first_dir // This only stores the dir arg from init - -/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=FALSE, _color, _layer=TURF_LAYER, _description, _alpha=255) - if(!isatom(parent) || !generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha)) - return COMPONENT_INCOMPATIBLE - first_dir = _dir - description = _description - cleanable = _cleanable - - apply() - -/datum/component/decal/RegisterWithParent() - if(first_dir) - RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(rotate_react)) - if(cleanable != FALSE) - RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_react)) - if(description) - RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(examine)) - -/datum/component/decal/UnregisterFromParent() - UnregisterSignal(parent, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE)) - -/datum/component/decal/Destroy() - remove() - return ..() - -/datum/component/decal/PreTransfer() - remove() - -/datum/component/decal/PostTransfer() - remove() - apply() - -/datum/component/decal/proc/generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha) - if(!_icon || !_icon_state) - return FALSE - // It has to be made from an image or dir breaks because of a byond bug - var/temp_image = image(_icon, null, _icon_state, _layer, _dir) - pic = new(temp_image) - pic.color = _color - pic.alpha = _alpha - return TRUE - -/datum/component/decal/proc/apply(atom/thing) - var/atom/master = thing || parent - master.add_overlay(pic, TRUE) - if(isitem(master)) - addtimer(CALLBACK(master, TYPE_PROC_REF(/obj/item, update_slot_icon)), 0, TIMER_UNIQUE) - -/datum/component/decal/proc/remove(atom/thing) - var/atom/master = thing || parent - master.cut_overlay(pic, TRUE) - if(isitem(master)) - addtimer(CALLBACK(master, TYPE_PROC_REF(/obj/item, update_slot_icon)), 0, TIMER_UNIQUE) - -/datum/component/decal/proc/rotate_react(datum/source, old_dir, new_dir) - if(old_dir == new_dir) - return - remove() - pic.dir = turn(pic.dir, dir2angle(old_dir) - dir2angle(new_dir)) - apply() - -/datum/component/decal/proc/clean_react(datum/source, clean_types) - if(clean_types & cleanable) - qdel(src) - return TRUE - -/datum/component/decal/proc/examine(datum/source, mob/user, list/examine_list) - examine_list += description diff --git a/code/datums/components/decals/blood.dm b/code/datums/components/decals/blood.dm deleted file mode 100644 index a0a97af3982c..000000000000 --- a/code/datums/components/decals/blood.dm +++ /dev/null @@ -1,39 +0,0 @@ -/datum/component/decal/blood - dupe_mode = COMPONENT_DUPE_UNIQUE - -/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_TYPE_BLOOD, _color, _layer=ABOVE_OBJ_LAYER) - if(!isitem(parent)) - return COMPONENT_INCOMPATIBLE - . = ..() - RegisterSignal(parent, COMSIG_ATOM_GET_EXAMINE_NAME, PROC_REF(get_examine_name)) - -/datum/component/decal/blood/generate_appearance(_icon, _icon_state, _dir, _layer, _color) - var/obj/item/I = parent - if(!_icon) - _icon = 'icons/effects/blood.dmi' - if(!_icon_state) - _icon_state = "itemblood" - var/icon = initial(I.icon) - var/icon_state = initial(I.icon_state) - if(!icon || !icon_state) - // It's something which takes on the look of other items, probably - icon = I.icon - icon_state = I.icon_state - var/static/list/blood_splatter_appearances = list() - //try to find a pre-processed blood-splatter. otherwise, make a new one - var/index = "[REF(icon)]-[icon_state]" - pic = blood_splatter_appearances[index] - - if(!pic) - var/icon/blood_splatter_icon = icon(initial(I.icon), initial(I.icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon(_icon, _icon_state), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - pic = mutable_appearance(blood_splatter_icon, initial(I.icon_state)) - blood_splatter_appearances[index] = pic - return TRUE - -/datum/component/decal/blood/proc/get_examine_name(datum/source, mob/user, list/override) - var/atom/A = parent - override[EXAMINE_POSITION_ARTICLE] = A.gender == PLURAL? "some" : "a" - override[EXAMINE_POSITION_BEFORE] = " blood-stained " - return COMPONENT_EXNAME_CHANGED diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm index b9d618d4092d..ebc47dff7faa 100644 --- a/code/datums/components/forensics.dm +++ b/code/datums/components/forensics.dm @@ -49,7 +49,7 @@ /datum/component/forensics/proc/wipe_blood_DNA() blood_DNA = null if(isitem(parent)) - qdel(parent.GetComponent(/datum/component/decal/blood)) + RemoveElement(/datum/element/decal/blood) return TRUE /datum/component/forensics/proc/wipe_fibers() @@ -193,7 +193,7 @@ return if(!length(blood_DNA)) return - parent.LoadComponent(/datum/component/decal/blood) + parent.AddElement(/datum/element/decal/blood) //yog code for olfaction /datum/component/forensics/proc/wipe_scents() diff --git a/code/datums/components/rotation.dm b/code/datums/components/rotation.dm index e149013a48fa..da158f331644 100644 --- a/code/datums/components/rotation.dm +++ b/code/datums/components/rotation.dm @@ -94,9 +94,9 @@ //Signals + verbs removed via UnRegister . = ..() -/datum/component/simple_rotation/RemoveComponent() +/datum/component/simple_rotation/ClearFromParent() remove_verbs() - . = ..() + return ..() /datum/component/simple_rotation/proc/ExamineMessage(datum/source, mob/user, list/examine_list) if(rotation_flags & ROTATION_ALTCLICK) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index a2d370e2ee68..409ec48731bd 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -19,15 +19,26 @@ var/gc_destroyed /// Active timers with this datum as the target - var/list/active_timers - /// Components attached to this datum - var/list/datum_components - /// Status traits attached to this datum - var/list/status_traits - /// Any datum registered to receive signals from this datum is in this list - var/list/comp_lookup - /// List of callbacks for signal procs - var/list/list/datum/callback/signal_procs + var/list/_active_timers + /// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...)) + var/list/_status_traits + + /** + * Components attached to this datum + * + * Lazy associated list in the structure of `type -> component/list of components` + */ + var/list/_datum_components + + /** + * Any datum registered to receive signals from this datum is in this list + * + * Lazy associated list in the structure of `signal -> registree/list of registrees` + */ + var/list/_listen_lookup + /// Lazy associated list in the structure of `target -> list(signal -> proctype)` that are run when the datum receives that signal + var/list/list/_signal_procs + /// Datum level flags var/datum_flags = NONE @@ -87,24 +98,25 @@ datum_flags &= ~DF_USE_TAG //In case something tries to REF us weak_reference = null //ensure prompt GCing of weakref. - var/list/timers = active_timers - active_timers = null - for(var/thing in timers) - var/datum/timedevent/timer = thing - if (timer.spent) - continue - qdel(timer) + if(_active_timers) + var/list/timers = _active_timers + _active_timers = null + for(var/datum/timedevent/timer as anything in timers) + if (timer.spent && !(timer.flags & TIMER_DELETE_ME)) + continue + qdel(timer) - // Handle components & signals - var/list/dc = datum_components + //BEGIN: ECS SHIT + var/list/dc = _datum_components if(dc) - var/all_components = dc[/datum/component] - if(length(all_components)) - for(var/datum/component/component as anything in all_components) - qdel(component, FALSE, TRUE) - else - var/datum/component/C = all_components - qdel(C, FALSE, TRUE) + for(var/component_key in dc) + var/component_or_list = dc[component_key] + if(islist(component_or_list)) + for(var/datum/component/component as anything in component_or_list) + qdel(component, FALSE, TRUE) + else + var/datum/component/C = component_or_list + qdel(C, FALSE, TRUE) dc.Cut() clear_signal_refs() @@ -114,7 +126,7 @@ ///Only override this if you know what you're doing. You do not know what you're doing ///This is a threat /datum/proc/clear_signal_refs() - var/list/lookup = comp_lookup + var/list/lookup = _listen_lookup if(lookup) for(var/sig in lookup) var/list/comps = lookup[sig] @@ -124,10 +136,10 @@ else var/datum/component/comp = comps comp.UnregisterSignal(src, sig) - comp_lookup = lookup = null + _listen_lookup = lookup = null - for(var/target in signal_procs) - UnregisterSignal(target, signal_procs[target]) + for(var/target in _signal_procs) + UnregisterSignal(target, _signal_procs[target]) #ifdef DATUMVAR_DEBUGGING_MODE /datum/proc/save_vars() diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index c50f7f30f5c9..1f5b7946739b 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -31,17 +31,24 @@ VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete") VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player") VV_DROPDOWN_OPTION(VV_HK_ADDCOMPONENT, "Add Component/Element") - //VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRAITS, "Modify Traits") + VV_DROPDOWN_OPTION(VV_HK_MASS_REMOVECOMPONENT, "Mass Remove Component/Element") + VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRAITS, "Modify Traits") /datum/proc/on_reagent_change(changetype) return -//This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks! -//href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables! -//This proc is for "high level" actions like admin heal/set species/etc/etc. The low level debugging things should go in admin/view_variables/topic_basic.dm incase this runtimes. +/** + * This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks! + * href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables! + * This proc is for "high level" actions like admin heal/set species/etc/etc. The low level debugging things should go in admin/view_variables/topic_basic.dm incase this runtimes. + */ /datum/proc/vv_do_topic(list/href_list) if(!usr || !usr.client || !usr.client.holder || !check_rights(NONE)) return FALSE //This is VV, not to be called by anything else. + if(SEND_SIGNAL(src, COMSIG_VV_TOPIC, usr, href_list) & COMPONENT_VV_HANDLED) + return FALSE + if(href_list[VV_HK_MODIFY_TRAITS]) + usr.client.holder.modify_traits(src) return TRUE /datum/proc/vv_get_header() diff --git a/code/datums/elements/_element.dm b/code/datums/elements/_element.dm index a3ec8be5dddc..4b91bf2caf9f 100644 --- a/code/datums/elements/_element.dm +++ b/code/datums/elements/_element.dm @@ -51,10 +51,10 @@ /// Finds the singleton for the element type given and attaches it to src /datum/proc/_AddElement(list/arguments) if(QDELING(src)) - //linter shits because of the way we init the maps and delete unused assets - //CRASH("We just tried to add an element to a qdeleted datum, something is fucked") - return + CRASH("We just tried to add an element to a qdeleted datum, something is fucked") var/datum/element/ele = SSdcs.GetElement(arguments) + if(!ele) // We couldn't fetch the element, likely because it was not an element. + return // the crash message has already been sent arguments[1] = src if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE) CRASH("Incompatible element [ele.type] was assigned to a [type]! args: [json_encode(args)]") @@ -64,7 +64,9 @@ * You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE] */ /datum/proc/_RemoveElement(list/arguments) - var/datum/element/ele = SSdcs.GetElement(arguments) + var/datum/element/ele = SSdcs.GetElement(arguments, FALSE) + if(!ele) // We couldn't fetch the element, likely because it didn't exist. + return if(ele.element_flags & ELEMENT_COMPLEX_DETACH) arguments[1] = src ele.Detach(arglist(arguments)) diff --git a/code/datums/elements/connect_loc.dm b/code/datums/elements/connect_loc.dm index e6aecbe6aded..32b3b50b783a 100644 --- a/code/datums/elements/connect_loc.dm +++ b/code/datums/elements/connect_loc.dm @@ -1,7 +1,7 @@ /// This element hooks a signal onto the loc the current object is on. /// When the object moves, it will unhook the signal and rehook it to the new object. /datum/element/connect_loc - element_flags = ELEMENT_BESPOKE + element_flags = ELEMENT_BESPOKE|ELEMENT_NO_LIST_UNIT_TEST argument_hash_start_idx = 2 /// An assoc list of signal -> procpath to register to the loc this object is on. diff --git a/code/datums/elements/decals/_decal.dm b/code/datums/elements/decals/_decal.dm new file mode 100644 index 000000000000..3b347fd4e2a5 --- /dev/null +++ b/code/datums/elements/decals/_decal.dm @@ -0,0 +1,179 @@ +/datum/element/decal + element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_DONT_SORT_LIST_ARGS + argument_hash_start_idx = 2 + /// Whether this decal can be cleaned. + var/cleanable + /// A description this decal appends to the target's examine message. + var/description + /// If true this was initialized with no set direction - will follow the parent dir. + var/directional + /// The base icon state that this decal was initialized with. + var/base_icon_state + /// What smoothing junction this was initialized with. + var/smoothing + /// The overlay applied by this decal to the target. + var/mutable_appearance/pic + +/// Remove old decals and apply new decals after rotation as necessary +/datum/controller/subsystem/processing/dcs/proc/rotate_decals(datum/source, old_dir, new_dir) + SIGNAL_HANDLER + + if(old_dir == new_dir) + return + + var/list/datum/element/decal/old_decals = list() //instances + SEND_SIGNAL(source, COMSIG_ATOM_DECALS_ROTATING, old_decals) + + if(!length(old_decals)) + UnregisterSignal(source, COMSIG_ATOM_DIR_CHANGE) + return + + var/list/resulting_decals_params = list() // param lists + for(var/datum/element/decal/rotating as anything in old_decals) + resulting_decals_params += list(rotating.get_rotated_parameters(old_dir,new_dir)) + + //Instead we could generate ids and only remove duplicates to save on churn on four-corners symmetry ? + for(var/datum/element/decal/decal in old_decals) + decal.Detach(source) + + for(var/result in resulting_decals_params) + source.AddElement(/datum/element/decal, result["icon"], result["icon_state"], result["dir"], result["plane"], result["layer"], result["alpha"], result["color"], result["smoothing"], result["cleanable"], result["desc"]) + +/datum/element/decal/proc/get_rotated_parameters(old_dir,new_dir) + var/rotation = 0 + if(directional) //Even when the dirs are the same rotation is coming out as not 0 for some reason + rotation = SIMPLIFY_DEGREES(dir2angle(new_dir)-dir2angle(old_dir)) + new_dir = turn(pic.dir,-rotation) + return list( + "icon" = pic.icon, + "icon_state" = base_icon_state, + "dir" = new_dir, + "plane" = pic.plane, + "layer" = pic.layer, + "alpha" = pic.alpha, + "color" = pic.color, + "smoothing" = smoothing, + "cleanable" = cleanable, + "desc" = description, + ) + +/datum/element/decal/Attach(atom/target, _icon, _icon_state, _dir, _plane=FLOAT_PLANE, _layer=FLOAT_LAYER, _alpha=255, _color, _smoothing, _cleanable=FALSE, _description, mutable_appearance/_pic) + . = ..() + if(!isatom(target)) + return ELEMENT_INCOMPATIBLE + if(_pic) + pic = _pic + else if(!generate_appearance(_icon, _icon_state, _dir, _plane, _layer, _color, _alpha, _smoothing, target)) + return ELEMENT_INCOMPATIBLE + description = _description + cleanable = _cleanable + directional = _dir + base_icon_state = _icon_state + smoothing = _smoothing + + RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(apply_overlay), TRUE) + if(target.flags_1 & INITIALIZED_1) + target.update_appearance(UPDATE_OVERLAYS) //could use some queuing here now maybe. + else + RegisterSignal(target,COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE, PROC_REF(late_update_icon), TRUE) + if(isitem(target)) + INVOKE_ASYNC(target, TYPE_PROC_REF(/obj/item/, update_slot_icon), TRUE) + if(_dir) + RegisterSignal(target, COMSIG_ATOM_DECALS_ROTATING, PROC_REF(shuttle_rotate), TRUE) + SSdcs.RegisterSignal(target, COMSIG_ATOM_DIR_CHANGE, TYPE_PROC_REF(/datum/controller/subsystem/processing/dcs, rotate_decals), override=TRUE) + if(!isnull(_smoothing)) + RegisterSignal(target, COMSIG_ATOM_SMOOTHED_ICON, PROC_REF(smooth_react), TRUE) + if(_cleanable) + RegisterSignal(target, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_react), TRUE) + if(_description) + RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(examine), TRUE) + + RegisterSignal(target, COMSIG_TURF_ON_SHUTTLE_MOVE, PROC_REF(shuttle_move_react), TRUE) + +/** + * ## generate_appearance + * + * If the decal was not given an appearance, it will generate one based on the other given arguments. + * element won't be compatible if it cannot do either + * all args are fed into creating an image, they are byond vars for images you'll recognize in the byond docs + * (except source, source is the object whose appearance we're copying.) + */ +/datum/element/decal/proc/generate_appearance(_icon, _icon_state, _dir, _plane, _layer, _color, _alpha, _smoothing, source) + if(!_icon || !_icon_state) + return FALSE + var/temp_image = image(_icon, null, isnull(_smoothing) ? _icon_state : "[_icon_state]-[_smoothing]", _layer, _dir) + pic = new(temp_image) + pic.plane = _plane + pic.color = _color + pic.alpha = _alpha + return TRUE + +/datum/element/decal/Detach(atom/source) + UnregisterSignal(source, list( + COMSIG_ATOM_DIR_CHANGE, + COMSIG_COMPONENT_CLEAN_ACT, + COMSIG_PARENT_EXAMINE, + COMSIG_ATOM_UPDATE_OVERLAYS, + COMSIG_TURF_ON_SHUTTLE_MOVE, + COMSIG_ATOM_SMOOTHED_ICON, + )) + SSdcs.UnregisterSignal(source, COMSIG_ATOM_DIR_CHANGE) + source.update_appearance(UPDATE_OVERLAYS) + if(isitem(source)) + INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item/, update_slot_icon)) + SEND_SIGNAL(source, COMSIG_TURF_DECAL_DETACHED, description, cleanable, directional, pic) + return ..() + +/datum/element/decal/proc/late_update_icon(atom/source) + SIGNAL_HANDLER + + if(istype(source) && !(source.flags_1 & DECAL_INIT_UPDATE_EXPERIENCED_1)) + source.flags_1 |= DECAL_INIT_UPDATE_EXPERIENCED_1 // I am so sorry, but it saves like 80ms I gotta + source.update_appearance(UPDATE_OVERLAYS) + UnregisterSignal(source, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE) + +/datum/element/decal/proc/apply_overlay(atom/source, list/overlay_list) + SIGNAL_HANDLER + + overlay_list += pic + +/datum/element/decal/proc/clean_react(datum/source, clean_types) + SIGNAL_HANDLER + + if(clean_types & cleanable) + Detach(source) + return COMPONENT_CLEANED + return NONE + +/datum/element/decal/proc/examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + + examine_list += description + +/datum/element/decal/proc/shuttle_move_react(datum/source, turf/new_turf) + SIGNAL_HANDLER + + if(new_turf == source) + return + Detach(source) + new_turf.AddElement(type, pic.icon, base_icon_state, directional, pic.plane, pic.layer, pic.alpha, pic.color, smoothing, cleanable, description) + +/datum/element/decal/proc/shuttle_rotate(datum/source, list/datum/element/decal/rotating) + SIGNAL_HANDLER + rotating += src + +/** + * Reacts to the source atom smoothing. + * + * Arguments: + * - [source][/atom]: The source of the signal and recently smoothed atom. + */ +/datum/element/decal/proc/smooth_react(atom/source) + SIGNAL_HANDLER + var/smoothing_junction = source.smoothing_junction + if(smoothing_junction == smoothing) + return NONE + + Detach(source) + source.AddElement(type, pic.icon, base_icon_state, directional, pic.plane, pic.layer, pic.alpha, pic.color, smoothing_junction, cleanable, description) + return NONE diff --git a/code/datums/elements/decals/blood.dm b/code/datums/elements/decals/blood.dm new file mode 100644 index 000000000000..889ebb12904b --- /dev/null +++ b/code/datums/elements/decals/blood.dm @@ -0,0 +1,47 @@ +/datum/element/decal/blood + +/datum/element/decal/blood/Attach(datum/target, _icon, _icon_state, _dir, _plane, _layer, _alpha, _color, _smoothing, _cleanable=CLEAN_TYPE_BLOOD, _description, mutable_appearance/_pic) + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + + . = ..() + RegisterSignal(target, COMSIG_ATOM_GET_EXAMINE_NAME, PROC_REF(get_examine_name), TRUE) + +/datum/element/decal/blood/Detach(atom/source) + UnregisterSignal(source, COMSIG_ATOM_GET_EXAMINE_NAME) + return ..() + +/datum/element/decal/blood/generate_appearance(_icon, _icon_state, _dir, _plane, _layer, _color, _alpha, _smoothing, source) + var/obj/item/I = source + if(!_icon) + _icon = 'icons/effects/blood.dmi' + if(!_icon_state) + _icon_state = "itemblood" + var/icon = I.icon + var/icon_state = I.icon_state + if(!icon || !icon_state) + // It's something which takes on the look of other items, probably + icon = I.icon + icon_state = I.icon_state + var/static/list/blood_splatter_appearances = list() + //try to find a pre-processed blood-splatter. otherwise, make a new one + var/index = "[REF(icon)]-[icon_state]" + pic = blood_splatter_appearances[index] + + if(!pic) + var/icon/blood_splatter_icon = icon(I.icon, I.icon_state, , 1) //icon of the item that will become splattered + var/icon/blood_icon = icon(_icon, _icon_state) //icon of the blood that we apply + blood_icon.Scale(blood_splatter_icon.Width(), blood_splatter_icon.Height()) + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(blood_icon, ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + pic = mutable_appearance(blood_splatter_icon, I.icon_state) + blood_splatter_appearances[index] = pic + return TRUE + +/datum/element/decal/blood/proc/get_examine_name(datum/source, mob/user, list/override) + SIGNAL_HANDLER + + var/atom/A = source + override[EXAMINE_POSITION_ARTICLE] = A.gender == PLURAL? "some" : "a" + override[EXAMINE_POSITION_BEFORE] = " blood-stained " + return COMPONENT_EXNAME_CHANGED diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 0bc0245972e2..9ccdf913bb23 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -123,7 +123,7 @@ mood_was_enabled = TRUE var/datum/component/mood/c = H.GetComponent(/datum/component/mood) if(c) - c.RemoveComponent() + qdel(c) // Yogs End current.mind = null UnregisterSignal(current, COMSIG_GLOB_MOB_DEATH) diff --git a/code/datums/signals.dm b/code/datums/signals.dm new file mode 100644 index 000000000000..832ad69a2fa9 --- /dev/null +++ b/code/datums/signals.dm @@ -0,0 +1,131 @@ +/** + * Register to listen for a signal from the passed in target + * + * This sets up a listening relationship such that when the target object emits a signal + * the source datum this proc is called upon, will receive a callback to the given proctype + * Use PROC_REF(procname), TYPE_PROC_REF(type,procname) or GLOBAL_PROC_REF(procname) macros to validate the passed in proc at compile time. + * PROC_REF for procs defined on current type or it's ancestors, TYPE_PROC_REF for procs defined on unrelated type and GLOBAL_PROC_REF for global procs. + * Return values from procs registered must be a bitfield + * + * Arguments: + * * datum/target The target to listen for signals from + * * signal_type A signal name + * * proctype The proc to call back when the signal is emitted + * * override If a previous registration exists you must explicitly set this + */ +/datum/proc/RegisterSignal(datum/target, signal_type, proctype, override = FALSE) + if(QDELETED(src) || QDELETED(target)) + return + + if (islist(signal_type)) + var/static/list/known_failures = list() + var/list/signal_type_list = signal_type + var/message = "([target.type]) is registering [signal_type_list.Join(", ")] as a list, the older method. Change it to RegisterSignals." + + if (!(message in known_failures)) + known_failures[message] = TRUE + stack_trace("[target] [message]") + + RegisterSignals(target, signal_type, proctype, override) + return + + var/list/procs = (_signal_procs ||= list()) + var/list/target_procs = (procs[target] ||= list()) + var/list/lookup = (target._listen_lookup ||= list()) + + var/exists = target_procs[signal_type] + target_procs[signal_type] = proctype + + if(exists) + if(!override) + var/override_message = "[signal_type] overridden. Use override = TRUE to suppress this warning.\nTarget: [target] ([target.type]) Proc: [proctype]" + log_signal(override_message) + stack_trace(override_message) + return + + var/list/looked_up = lookup[signal_type] + + if(isnull(looked_up)) // Nothing has registered here yet + lookup[signal_type] = src + else if(!islist(looked_up)) // One other thing registered here + lookup[signal_type] = list(looked_up, src) + else // Many other things have registered here + looked_up += src + +/// Registers multiple signals to the same proc. +/datum/proc/RegisterSignals(datum/target, list/signal_types, proctype, override = FALSE) + for (var/signal_type in signal_types) + RegisterSignal(target, signal_type, proctype, override) + +/** + * Stop listening to a given signal from target + * + * Breaks the relationship between target and source datum, removing the callback when the signal fires + * + * Doesn't care if a registration exists or not + * + * Arguments: + * * datum/target Datum to stop listening to signals from + * * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically + */ +/datum/proc/UnregisterSignal(datum/target, sig_type_or_types) + //Yogs edit: One day we should remove this, but it's too much work to port it all at once and too many snowflake exceptions to fix. + //Sorry, but this check stays for now. + if(!target) + return + //Yogs edit end + var/list/lookup = target._listen_lookup + if(!_signal_procs || !_signal_procs[target] || !lookup) + return + if(!islist(sig_type_or_types)) + sig_type_or_types = list(sig_type_or_types) + for(var/sig in sig_type_or_types) + if(!_signal_procs[target][sig]) + if(!istext(sig)) + stack_trace("We're unregistering with something that isn't a valid signal \[[sig]\], you fucked up") + continue + switch(length(lookup[sig])) + if(2) + lookup[sig] = (lookup[sig]-src)[1] + if(1) + stack_trace("[target] ([target.type]) somehow has single length list inside _listen_lookup") + if(src in lookup[sig]) + lookup -= sig + if(!length(lookup)) + target._listen_lookup = null + break + if(0) + if(lookup[sig] != src) + continue + lookup -= sig + if(!length(lookup)) + target._listen_lookup = null + break + else + lookup[sig] -= src + + _signal_procs[target] -= sig_type_or_types + if(!_signal_procs[target].len) + _signal_procs -= target + +/** + * Internal proc to handle most all of the signaling procedure + * + * Will runtime if used on datums with an empty lookup list + * + * Use the [SEND_SIGNAL] define instead + */ +/datum/proc/_SendSignal(sigtype, list/arguments) + var/target = _listen_lookup[sigtype] + if(!length(target)) + var/datum/listening_datum = target + return NONE | call(listening_datum, listening_datum._signal_procs[src][sigtype])(arglist(arguments)) + . = NONE + // This exists so that even if one of the signal receivers unregisters the signal, + // all the objects that are receiving the signal get the signal this final time. + // AKA: No you can't cancel the signal reception of another object by doing an unregister in the same signal. + var/list/queued_calls = list() + for(var/datum/listening_datum as anything in target) + queued_calls[listening_datum] = listening_datum._signal_procs[src][sigtype] + for(var/datum/listening_datum as anything in queued_calls) + . |= call(listening_datum, queued_calls[listening_datum])(arglist(arguments)) diff --git a/code/datums/status_effects/buffs/buffs.dm b/code/datums/status_effects/buffs/buffs.dm index 112529026ff8..a3774b7437cd 100644 --- a/code/datums/status_effects/buffs/buffs.dm +++ b/code/datums/status_effects/buffs/buffs.dm @@ -743,7 +743,8 @@ /datum/status_effect/holylight_healboost/on_remove() var/datum/component/heal_react/boost/holylight/healing = owner.GetComponent(/datum/component/heal_react/boost/holylight) - healing?.RemoveComponent() + if(healing) + qdel(healing) var/filter = owner.get_filter(HEALBOOST_FILTER) if(filter) animate(filter) diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index 7eef9e32d3e1..4e36645bdeba 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -97,7 +97,7 @@ light_range = 0 update_light() if(R) - R.RemoveComponent() + qdel(R) /obj/effect/decal/cleanable/greenglow/filled/Initialize(mapload) . = ..() diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm index bbdba5ee4fdc..dc4972b30fbf 100644 --- a/code/game/objects/effects/decals/decal.dm +++ b/code/game/objects/effects/decals/decal.dm @@ -41,5 +41,5 @@ var/turf/T = loc if(!istype(T)) //you know this will happen somehow CRASH("Turf decal initialized in an object/nullspace") - T.AddComponent(/datum/component/decal, icon, icon_state, dir, FALSE, color, null, null, alpha) + T.AddElement(/datum/element/decal, icon, icon_state, dir, null, layer, alpha, color, null, FALSE, null) return INITIALIZE_HINT_QDEL diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm index b2e7fb5d761f..d8f3fc908ca0 100644 --- a/code/game/objects/items/airlock_painter.dm +++ b/code/game/objects/items/airlock_painter.dm @@ -439,7 +439,7 @@ * * target - The turf being painted to */ /obj/item/airlock_painter/decal/proc/paint_floor(turf/open/floor/target) - target.AddComponent(/datum/component/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, FALSE, color, null, null, 255) + target.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, null, null, alpha, color, null, FALSE, null) /** * Return the final icon_state for the given decal options @@ -641,4 +641,4 @@ decal_color = rgba_regex.group[1] decal_alpha = text2num(rgba_regex.group[2], 16) - target.AddComponent(/datum/component/decal, 'icons/turf/decals.dmi', source_decal, source_dir, FALSE, decal_color, null, null, decal_alpha) + target.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', source_decal, source_dir, null, null, decal_alpha, decal_color, null, FALSE, null) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 6d26a188e3f1..0044f7796b5a 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -716,7 +716,8 @@ name = "shuttle window" desc = "A reinforced, air-locked pod window." icon = 'icons/obj/smooth_structures/shuttle_window.dmi' - icon_state = "shuttle_window" + icon_state = "shuttle_window-0" + base_icon_state = "shuttle_window" dir = FULLTILE_WINDOW_DIR max_integrity = 100 wtype = "shuttle" diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 42e0702d2148..a3fdfa780022 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -91,18 +91,26 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( var/list/old_baseturfs = baseturfs - var/list/transferring_comps = list() - SEND_SIGNAL(src, COMSIG_TURF_CHANGE, path, new_baseturfs, flags, transferring_comps) - for(var/i in transferring_comps) - var/datum/component/comp = i - comp.RemoveComponent() + var/list/post_change_callbacks = list() + SEND_SIGNAL(src, COMSIG_TURF_CHANGE, path, new_baseturfs, flags, post_change_callbacks) changing_turf = TRUE - qdel(src) //Just get the side effects and call Destroy + qdel(src) //Just get the side effects and call Destroy + //We do this here so anything that doesn't want to persist can clear itself + var/list/old_listen_lookup = _listen_lookup?.Copy() + var/list/old_signal_procs = _signal_procs?.Copy() var/turf/W = new path(src) - for(var/i in transferring_comps) - W.TakeComponent(i) + // WARNING WARNING + // Turfs DO NOT lose their signals when they get replaced, REMEMBER THIS + // It's possible because turfs are fucked, and if you have one in a list and it's replaced with another one, the list ref points to the new turf + if(old_listen_lookup) + LAZYOR(W._listen_lookup, old_listen_lookup) + if(old_signal_procs) + LAZYOR(W._signal_procs, old_signal_procs) + + for(var/datum/callback/callback as anything in post_change_callbacks) + callback.InvokeAsync(W) if(new_baseturfs) W.baseturfs = new_baseturfs diff --git a/code/game/turfs/simulated/wall/reinf_walls.dm b/code/game/turfs/simulated/wall/reinf_walls.dm index 46c34c1b5581..3f36ebade727 100644 --- a/code/game/turfs/simulated/wall/reinf_walls.dm +++ b/code/game/turfs/simulated/wall/reinf_walls.dm @@ -6,6 +6,7 @@ base_icon_state = "reinforced_wall" opacity = TRUE density = TRUE + smoothing_flags = SMOOTH_BITMASK var/d_state = INTACT hardness = 10 diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index a74290108199..b8924ae03639 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1430,6 +1430,52 @@ Traitors and the like can also be revived with the previous role mostly intact. message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]") log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]") +/// Allow admin to add or remove traits of datum +/datum/admins/proc/modify_traits(datum/D) + if(!D) + return + + var/add_or_remove = input("Remove/Add?", "Trait Remove/Add") as null|anything in list("Add","Remove") + if(!add_or_remove) + return + var/list/available_traits = list() + + switch(add_or_remove) + if("Add") + for(var/key in GLOB.admin_visible_traits) + if(istype(D,key)) + available_traits += GLOB.admin_visible_traits[key] + if("Remove") + if(!GLOB.admin_trait_name_map) + GLOB.admin_trait_name_map = generate_admin_trait_name_map() + for(var/trait in D._status_traits) + var/name = GLOB.admin_trait_name_map[trait] || trait + available_traits[name] = trait + + var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in sort_list(available_traits) + if(!chosen_trait) + return + chosen_trait = available_traits[chosen_trait] + + var/source = "adminabuse" + switch(add_or_remove) + if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it. + if(GLOB.movement_type_trait_to_flag[chosen_trait]) //include the required element. + D.AddElement(/datum/element/movetype_handler) + ADD_TRAIT(D,chosen_trait,source) + if("Remove") + var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific") + if(!specific) + return + switch(specific) + if("All") + source = null + if("Specific") + source = input("Source to be removed","Trait Remove/Add") as null|anything in sort_list(GET_TRAIT_SOURCES(D, chosen_trait)) + if(!source) + return + REMOVE_TRAIT(D,chosen_trait,source) + /mob/living/carbon/proc/adminpie(mob/user) var/obj/item/reagent_containers/food/snacks/pie/cream/admin/p = new (get_turf(pick(oview(3,user)))) p.item_flags = UNCATCHABLE diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm index 46c15f28f68c..4e929a7afe25 100644 --- a/code/modules/admin/view_variables/topic_basic.dm +++ b/code/modules/admin/view_variables/topic_basic.dm @@ -54,29 +54,86 @@ if(!check_rights(NONE)) return var/list/names = list() - var/list/componentsubtypes = sortList(subtypesof(/datum/component), /proc/cmp_typepaths_asc) + var/list/componentsubtypes = sort_list(subtypesof(/datum/component), GLOBAL_PROC_REF(cmp_typepaths_asc)) names += "---Components---" names += componentsubtypes names += "---Elements---" - names += sortList(subtypesof(/datum/element), /proc/cmp_typepaths_asc) - var/result = input(usr, "Choose a component/element to add","better know what ur fuckin doin pal") as null|anything in names - if(!usr || !result || result == "---Components---" || result == "---Elements---") + names += sort_list(subtypesof(/datum/element), GLOBAL_PROC_REF(cmp_typepaths_asc)) + + var/result = tgui_input_list(usr, "Choose a component/element to add", "Add Component", names) + if(isnull(result)) + return + if(!usr || result == "---Components---" || result == "---Elements---") return + if(QDELETED(src)) - to_chat(usr, "That thing doesn't exist anymore!") + to_chat(usr, "That thing doesn't exist anymore!", confidential = TRUE) return + + var/add_source + if(ispath(result, /datum/component)) + var/datum/component/comp_path = result + if(initial(comp_path.dupe_mode) == COMPONENT_DUPE_SOURCES) + add_source = tgui_input_text(usr, "Enter a source for the component", "Add Component", "ADMIN-ABUSE") + if(isnull(add_source)) + return + var/list/lst = get_callproc_args() if(!lst) return + var/datumname = "error" lst.Insert(1, result) if(result in componentsubtypes) datumname = "component" - target._AddComponent(lst) + target._AddComponent(lst, add_source) else datumname = "element" target._AddElement(lst) - log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].") - message_admins("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(src)].") + log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(target)].") + message_admins(span_notice("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(target)].")) + if(href_list[VV_HK_REMOVECOMPONENT] || href_list[VV_HK_MASS_REMOVECOMPONENT]) + if(!check_rights(NONE)) + return + var/mass_remove = href_list[VV_HK_MASS_REMOVECOMPONENT] + var/list/components = list() + for(var/datum/component/component in target.GetComponents(/datum/component)) + components += component.type + var/list/names = list() + names += "---Components---" + if(length(components)) + names += sort_list(components, GLOBAL_PROC_REF(cmp_typepaths_asc)) + names += "---Elements---" + // We have to list every element here because there is no way to know what element is on this object without doing some sort of hack. + names += sort_list(subtypesof(/datum/element), GLOBAL_PROC_REF(cmp_typepaths_asc)) + var/path = tgui_input_list(usr, "Choose a component/element to remove. All elements listed here may not be on the datum.", "Remove element", names) + if(isnull(path)) + return + if(!usr || path == "---Components---" || path == "---Elements---") + return + if(QDELETED(src)) + to_chat(usr, "That thing doesn't exist anymore!") + return + var/list/targets_to_remove_from = list(target) + if(mass_remove) + var/method = vv_subtype_prompt(target.type) + targets_to_remove_from = get_all_of_type(target.type, method) + + if(alert(usr, "Are you sure you want to mass-delete [path] on [target.type]?", "Mass Remove Confirmation", "Yes", "No") == "No") + return + + for(var/datum/target_to_remove_from as anything in targets_to_remove_from) + if(ispath(path, /datum/element)) + var/list/lst = get_callproc_args() + if(!lst) + lst = list() + lst.Insert(1, path) + target._RemoveElement(lst) + else + var/list/components_actual = target_to_remove_from.GetComponents(path) + for(var/to_delete in components_actual) + qdel(to_delete) + + message_admins(span_notice("[key_name_admin(usr)] has [mass_remove? "mass" : ""] removed [path] component from [mass_remove? target.type : key_name_admin(target)].")) if(href_list[VV_HK_CALLPROC]) usr.client.callproc_datum(target) diff --git a/code/modules/antagonists/bloodsuckers/vassal/vassal.dm b/code/modules/antagonists/bloodsuckers/vassal/vassal.dm index 3b2cc4f7347a..674f6190c30e 100644 --- a/code/modules/antagonists/bloodsuckers/vassal/vassal.dm +++ b/code/modules/antagonists/bloodsuckers/vassal/vassal.dm @@ -102,7 +102,7 @@ master.special_vassals[special_type] -= src master.vassals -= src owner.enslaved_to = null - for(var/all_status_traits in owner.current.status_traits) + for(var/all_status_traits in owner.current._status_traits) REMOVE_TRAIT(owner.current, all_status_traits, BLOODSUCKER_TRAIT) //Remove Recuperate Power while(powers.len) diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm index 23cbf6779ce4..c586841df3bd 100644 --- a/code/modules/antagonists/creep/creep.dm +++ b/code/modules/antagonists/creep/creep.dm @@ -85,7 +85,7 @@ var/datum/component/C = M.GetComponent(/datum/component/mood) if(C) //we cannot be too sure they may have somehow removed it to_chat(owner, span_danger("Your need for mental fitness vanishes alongside the voices, mood has been disabled.")) - C.RemoveComponent() + qdel(C) /datum/antagonist/obsessed/proc/forge_objectives(datum/mind/obsessionmind) var/list/objectives_left = list("spendtime", "polaroid", "hug") diff --git a/code/modules/antagonists/demon/demons.dm b/code/modules/antagonists/demon/demons.dm index 7310800742c6..b0147f260b40 100644 --- a/code/modules/antagonists/demon/demons.dm +++ b/code/modules/antagonists/demon/demons.dm @@ -177,7 +177,7 @@ /datum/antagonist/sinfuldemon/on_removal() owner.special_role = null owner.current.faction -= "hell" - for(var/all_status_traits in owner.current.status_traits) //removes demon traits + for(var/all_status_traits in owner.current._status_traits) //removes demon traits REMOVE_TRAIT(owner.current, all_status_traits, SINFULDEMON_TRAIT) for(var/datum/action/cooldown/spell/spell in owner.current.actions) if(spell.target == owner) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 877b97da4936..61f086b1e319 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -70,7 +70,7 @@ if(uplink_holder) var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink) if(uplink)//remove uplink so they can't keep using it if admin abuse happens - uplink.RemoveComponent() + qdel(uplink) UnregisterSignal(owner.current, COMSIG_MOVABLE_HEAR) SSticker.mode.traitors -= owner if(!silent && owner.current) diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index 2f491f0ebc5c..48318064622b 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -371,14 +371,10 @@ return FALSE if(istype(obj_to_insert, /obj/effect/supplypod_rubble)) return FALSE - if((obj_to_insert.comp_lookup && obj_to_insert.comp_lookup[COMSIG_OBJ_HIDE]) && reverse_option_list["Underfloor"]) - return TRUE - else if ((obj_to_insert.comp_lookup && obj_to_insert.comp_lookup[COMSIG_OBJ_HIDE]) && !reverse_option_list["Underfloor"]) - return FALSE - if(isProbablyWallMounted(obj_to_insert) && reverse_option_list["Wallmounted"]) - return TRUE - else if (isProbablyWallMounted(obj_to_insert) && !reverse_option_list["Wallmounted"]) - return FALSE + if(HAS_TRAIT(obj_to_insert, TRAIT_UNDERFLOOR)) + return !!reverse_option_list["Underfloor"] + if(isProbablyWallMounted(obj_to_insert)) + return !!reverse_option_list["Wallmounted"] if(!obj_to_insert.anchored && reverse_option_list["Unanchored"]) return TRUE if(obj_to_insert.anchored && !ismecha(obj_to_insert) && reverse_option_list["Anchored"]) //Mecha are anchored but there is a separate option for them diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 7ceb3f3f93b4..52d935f24f3e 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -105,10 +105,11 @@ if(!brainmob.stored_dna) brainmob.stored_dna = new /datum/dna/stored(brainmob) C.dna.copy_dna(brainmob.stored_dna) - if(HAS_TRAIT(L, TRAIT_BADDNA)) - brainmob.status_traits[TRAIT_BADDNA] = L.status_traits[TRAIT_BADDNA] - if(HAS_TRAIT(L, TRAIT_NOCLONE)) // YOU CAN'T ESCAPE - brainmob.status_traits[TRAIT_NOCLONE] = L.status_traits[TRAIT_NOCLONE] + // Hack, fucked dna needs to follow the brain to prevent memes, so we need to copy over the trait sources and shit + for(var/source in GET_TRAIT_SOURCES(L, TRAIT_BADDNA)) + ADD_TRAIT(brainmob, TRAIT_BADDNA, source) + for(var/source in GET_TRAIT_SOURCES(L, TRAIT_NOCLONE)) + ADD_TRAIT(brainmob, TRAIT_NOCLONE, source) var/obj/item/organ/zombie_infection/ZI = L.getorganslot(ORGAN_SLOT_ZOMBIE) if(ZI) brainmob.set_species(ZI.old_species) //For if the brain is cloned diff --git a/code/modules/pool/components/swimming.dm b/code/modules/pool/components/swimming.dm index c61f8a12a204..290e5ee9bfc3 100644 --- a/code/modules/pool/components/swimming.dm +++ b/code/modules/pool/components/swimming.dm @@ -41,7 +41,7 @@ if(istype(C) && C?.dna?.species) component_type = C.dna.species.swimming_component var/mob/M = parent - RemoveComponent() + qdel(src) M.AddComponent(component_type) /datum/component/swimming/proc/try_leave_pool(datum/source, turf/clicked_turf) @@ -56,7 +56,7 @@ if(do_after(parent, 1 SECONDS, clicked_turf)) L.forceMove(clicked_turf) L.visible_message("[parent] climbs out of the pool.") - RemoveComponent() + qdel(src) /datum/component/swimming/UnregisterFromParent() exit_pool() diff --git a/code/modules/pool/pool.dm b/code/modules/pool/pool.dm index d9021beac0c6..c59bbf5c5af6 100644 --- a/code/modules/pool/pool.dm +++ b/code/modules/pool/pool.dm @@ -74,20 +74,22 @@ Place a pool filter somewhere in the pool if you want people to be able to modif . = ..() if(!istype(newloc, /turf/open/indestructible/sound/pool)) var/datum/component/swimming/S = Obj.GetComponent(/datum/component/swimming) //Handling admin TPs here. - S?.RemoveComponent() + if(S) + qdel(S) /turf/open/MouseDrop_T(atom/dropping, mob/user) + . = ..() if(!isliving(user) || !isliving(dropping)) //No I don't want ghosts to be able to dunk people into the pool. return var/atom/movable/AM = dropping var/datum/component/swimming/S = dropping.GetComponent(/datum/component/swimming) - if(S) - if(do_after(user, 1 SECONDS, src)) - S.RemoveComponent() - visible_message("[dropping] climbs out of the pool.") - AM.forceMove(src) - else - . = ..() + if(!S) + return + if(!do_after(user, 1 SECONDS, src)) + return + qdel(S) + visible_message("[dropping] climbs out of the pool.") + AM.forceMove(src) /turf/open/indestructible/sound/pool/MouseDrop_T(atom/dropping, mob/user) if(!isliving(user) || !isliving(dropping)) //No I don't want ghosts to be able to dunk people into the pool. @@ -314,7 +316,7 @@ GLOBAL_LIST_EMPTY(pool_filters) if(S) to_chat(user, "You start to climb out of the pool...") if(do_after(user, 1 SECONDS, src)) - S.RemoveComponent() + qdel(S) visible_message("[user] climbs out of the pool.") if(!reversed) user.forceMove(get_turf(get_step(src, NORTH))) //Ladders shouldn't adjoin another pool section. Ever. diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 4b259f7f3a46..c5200f5843c1 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -58,6 +58,7 @@ All ShuttleMove procs go here CRASH("A turf queued to move via shuttle somehow had no skipover in baseturfs. [src]([type]):[loc]") var/depth = baseturfs.len - shuttle_boundary + 1 newT.CopyOnTop(src, 1, depth, TRUE, CHANGETURF_DEFER_CHANGE) + SEND_SIGNAL(src, COMSIG_TURF_ON_SHUTTLE_MOVE, newT) return TRUE // Called on the new turf after everything has been moved diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index 943b22f41a02..55941be88d2f 100644 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -147,21 +147,22 @@ to_chat(owner, emp_message) charge(amount = owner.nutrition * -0.02 * severity) -/obj/item/organ/stomach/cell/Insert(mob/living/carbon/M, special, drop_if_replaced) +/obj/item/organ/stomach/cell/Insert(mob/living/carbon/stomach_owner, special, drop_if_replaced) . = ..() - if(HAS_TRAIT(M, TRAIT_POWERHUNGRY)) - M.nutrition = stored_charge - RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(charge)) + if(HAS_TRAIT(stomach_owner, TRAIT_POWERHUNGRY)) + stomach_owner.nutrition = stored_charge + RegisterSignal(stomach_owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(charge)) -/obj/item/organ/stomach/cell/Remove(mob/living/carbon/M, special) +/obj/item/organ/stomach/cell/Remove(mob/living/carbon/stomach_owner, special) . = ..() - if(HAS_TRAIT(M, TRAIT_POWERHUNGRY)) - stored_charge = M.nutrition - M.nutrition = 0 + if(HAS_TRAIT(stomach_owner, TRAIT_POWERHUNGRY)) + stored_charge = stomach_owner.nutrition + stomach_owner.nutrition = 0 UnregisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT) - M.dna?.species.handle_digestion(M) // update nutrition stuff + stomach_owner.dna?.species.handle_digestion(stomach_owner) // update nutrition stuff /obj/item/organ/stomach/cell/proc/charge(datum/source, amount, repairs) + SIGNAL_HANDLER if(!HAS_TRAIT(owner, TRAIT_POWERHUNGRY)) return // do nothing in the owner doesn't run on electricity owner.adjust_nutrition(amount/100) // ipcs can't get fat anymore @@ -174,15 +175,16 @@ organ_flags = NONE compatible_biotypes = ALL_NON_ROBOTIC -/obj/item/organ/stomach/cell/ethereal/Insert(mob/living/carbon/M, special = 0) - ..() - RegisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_electrocute)) +/obj/item/organ/stomach/cell/ethereal/Insert(mob/living/carbon/stomach_owner, special = 0) + . = ..() + RegisterSignal(stomach_owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_electrocute)) -/obj/item/organ/stomach/cell/ethereal/Remove(mob/living/carbon/M, special = 0) - UnregisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT) - ..() +/obj/item/organ/stomach/cell/ethereal/Remove(mob/living/carbon/stomach_owner, special = 0) + UnregisterSignal(stomach_owner, COMSIG_LIVING_ELECTROCUTE_ACT) + return ..() /obj/item/organ/stomach/cell/ethereal/proc/on_electrocute(mob/living/victim, shock_damage, obj/source, siemens_coeff = 1, zone = null, tesla_shock = 0, illusion = 0) + SIGNAL_HANDLER if(illusion) return if(!HAS_TRAIT(owner, TRAIT_POWERHUNGRY)) @@ -233,4 +235,4 @@ ..() var/datum/component/crawl/vomit/B = M.GetComponent(/datum/component/crawl/vomit) if(B) - B.RemoveComponent() + qdel(B) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 2db748e1f0cc..ca029b16733c 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -58,8 +58,13 @@ #define TEST_DEFAULT 1 /// After most test steps, used for tests that run long so shorter issues can be noticed faster #define TEST_LONGER 10 -/// This must be the last test to run due to the inherent nature of the test iterating every single tangible atom in the game and qdeleting all of them (while taking long sleeps to make sure the garbage collector fires properly) taking a large amount of time. -#define TEST_CREATE_AND_DESTROY INFINITY +/// This must be the one of last tests to run due to the inherent nature of the test iterating every single tangible atom in the game and qdeleting all of them (while taking long sleeps to make sure the garbage collector fires properly) taking a large amount of time. +#define TEST_CREATE_AND_DESTROY 9001 +/** + * For tests that rely on create and destroy having iterated through every (tangible) atom so they don't have to do something similar. + * Keep in mind tho that create and destroy will absolutely break the test platform, anything that relies on its shape cannot come after it. + */ +#define TEST_AFTER_CREATE_AND_DESTROY INFINITY /// A trait source when adding traits through unit tests #define TRAIT_SOURCE_UNIT_TESTS "unit_tests" @@ -67,6 +72,7 @@ #include "anchored_mobs.dm" #include "baseturfs.dm" #include "component_tests.dm" +#include "dcs_check_list_arguments.dm" #include "dragon_expiration.dm" #include "dynamic_ruleset_sanity.dm" #include "focus_only_tests.dm" @@ -78,6 +84,7 @@ #include "species_whitelists.dm" #include "subsystem_init.dm" #include "timer_sanity.dm" +#include "trait_addition_and_removal.dm" #include "unit_test.dm" #undef TEST_ASSERT diff --git a/code/modules/unit_tests/dcs_check_list_arguments.dm b/code/modules/unit_tests/dcs_check_list_arguments.dm new file mode 100644 index 000000000000..67d7417062b2 --- /dev/null +++ b/code/modules/unit_tests/dcs_check_list_arguments.dm @@ -0,0 +1,55 @@ +/** + * list arguments for bespoke elements are treated as a text ref in the ID, like any other datum. + * Which means that, unless cached, using lists as arguments will lead to multiple instance of the same element + * being created over and over. + * + * Because of how it works, this unit test checks that these list datum args + * do not share similar contents (when rearranged in descending alpha-numerical order), to ensure that + * the least necessary amount of elements is created. So, using static lists may not be enough, + * for example, in the case of two different critters using the death_drops element to drop ectoplasm on death, since, + * despite being static lists, the two are different instances assigned to different mob types. + * + * Most of the time, you won't encounter two different static lists with similar contents used as element args, + * meaning using static lists is accepted. However, should that happen, it's advised to replace the instances + * with various string_x procs: lists, assoc_lists, assoc_nested_lists or numbers_list, depending on the type. + * + * In the case of an element where the position of the contents of each datum list argument is important, + * ELEMENT_DONT_SORT_LIST_ARGS should be added to its flags, to prevent such issues where the contents are similar + * when sorted, but the element instances are not. + * + * In the off-chance the element is not compatible with this unit test (such as for connect_loc et simila), + * you can also use ELEMENT_NO_LIST_UNIT_TEST so that they won't be processed by this unit test at all. + */ +/datum/unit_test/dcs_check_list_arguments + /** + * This unit test requires every (unless ignored) atom to have been created at least once + * for a more accurate search, which is why it's run after create_and_destroy is done running. + */ + priority = TEST_AFTER_CREATE_AND_DESTROY + +/datum/unit_test/dcs_check_list_arguments/Run() + var/we_failed = FALSE + for(var/element_type in SSdcs.arguments_that_are_lists_by_element) + // Keeps track of the lists that shouldn't be compared with again. + var/list/to_ignore = list() + var/list/superlist = SSdcs.arguments_that_are_lists_by_element[element_type] + for(var/list/current as anything in superlist) + to_ignore[current] = TRUE + var/list/bad_lists + for(var/list/compare as anything in superlist) + if(to_ignore[compare]) + continue + if(deep_compare_list(current, compare)) + if(!bad_lists) + bad_lists = list(list(current)) + bad_lists += list(compare) + to_ignore[compare] = TRUE + if(bad_lists) + we_failed = TRUE + //Include the original, unsorted list in the report. It should be easier to find by the contributor. + var/list/unsorted_list = superlist[current] + TEST_FAIL("Found [length(bad_lists)] datum list arguments with similar contents for [element_type]. Contents: [json_encode(unsorted_list)].") + ///Let's avoid sending the same instructions over and over, as it's just going to clutter the CI and confuse someone. + if(we_failed) + TEST_FAIL("Ensure that each list is static or cached. string_lists() (as well as similar procs) is your friend here.\n\ + Check the documentation from dcs_check_list_arguments.dm for more information!") diff --git a/code/modules/unit_tests/trait_addition_and_removal.dm b/code/modules/unit_tests/trait_addition_and_removal.dm new file mode 100644 index 000000000000..273cf102a00a --- /dev/null +++ b/code/modules/unit_tests/trait_addition_and_removal.dm @@ -0,0 +1,94 @@ +/// Simple Unit Test to ensure multiple methods of adding/removing a trait work as intended. +/datum/unit_test/trait_addition_and_removal + +#define TRAIT_UNIT_TEST_MAIN "trait_main" +#define TRAIT_UNIT_TEST_ALT "trait_alternate" +#define TRAIT_UNIT_TEST_A "trait_a" +#define TRAIT_UNIT_TEST_B "trait_b" +#define TRAIT_UNIT_TEST_C "trait_c" +#define UNIT_TEST_SOURCE_MAIN "source_main" +#define UNIT_TEST_SOURCE_ALT "source_alt" +#define UNIT_TEST_SOURCE_A "source_a" +#define UNIT_TEST_SOURCE_B "source_b" + +/datum/unit_test/trait_addition_and_removal/Run() + var/datum/trait_target = allocate(/datum) // traits work on the datum-level, so use a datum to ensure that it'll work on all children + + // We want to ensure that what we add is also removed when we're done with our "block" of tests, if it starts to get messy we might just have to reset the datum between tests to ensure cleanliness + + // The basics, if these fail burn down the codebase + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_A, UNIT_TEST_SOURCE_A) + TEST_ASSERT(HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_A), "Basic trait addition failed for [TRAIT_UNIT_TEST_A], source being [UNIT_TEST_SOURCE_A]!") + + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_B, UNIT_TEST_SOURCE_B) + TEST_ASSERT(HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_B), "Basic trait addition failed for [TRAIT_UNIT_TEST_B], source being [UNIT_TEST_SOURCE_B]!") + TEST_ASSERT(HAS_TRAIT_FROM(trait_target, TRAIT_UNIT_TEST_B, UNIT_TEST_SOURCE_B), "Failed to verify source for [TRAIT_UNIT_TEST_B], expected source being [UNIT_TEST_SOURCE_B]!") + TEST_ASSERT(HAS_TRAIT_NOT_FROM(trait_target, TRAIT_UNIT_TEST_B, UNIT_TEST_SOURCE_A), "Failed to verify source for [TRAIT_UNIT_TEST_B], expected source being [UNIT_TEST_SOURCE_B] but was actually [UNIT_TEST_SOURCE_A]!") + + REMOVE_TRAIT(trait_target, TRAIT_UNIT_TEST_B, UNIT_TEST_SOURCE_B) + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_B), "Basic trait removal failed for [TRAIT_UNIT_TEST_B], source being [UNIT_TEST_SOURCE_B]!") + TEST_ASSERT(HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_A), "Trait [TRAIT_UNIT_TEST_A] was removed when it shouldn't have been!") + REMOVE_TRAIT(trait_target, TRAIT_UNIT_TEST_A, UNIT_TEST_SOURCE_A) + + // Test adding the trait multiple times from different sources + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_MAIN, UNIT_TEST_SOURCE_A) + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_MAIN, UNIT_TEST_SOURCE_B) + TEST_ASSERT(!HAS_TRAIT_FROM_ONLY(trait_target, TRAIT_UNIT_TEST_MAIN, UNIT_TEST_SOURCE_A), "Failed to recognize that [TRAIT_UNIT_TEST_MAIN] was added by both [UNIT_TEST_SOURCE_A] and [UNIT_TEST_SOURCE_B]!") + + // as well as its removal + REMOVE_TRAIT_NOT_FROM(trait_target, TRAIT_UNIT_TEST_MAIN, UNIT_TEST_SOURCE_A) + TEST_ASSERT(!HAS_TRAIT_FROM(trait_target, TRAIT_UNIT_TEST_MAIN, UNIT_TEST_SOURCE_B), "Failed to remove [TRAIT_UNIT_TEST_MAIN] with [UNIT_TEST_SOURCE_B] when removal was expected!") + TEST_ASSERT(HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_MAIN), "[TRAIT_UNIT_TEST_MAIN] was completely removed when it should not have been!") + REMOVE_TRAIT(trait_target, TRAIT_UNIT_TEST_MAIN, UNIT_TEST_SOURCE_A) + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_MAIN), "[TRAIT_UNIT_TEST_MAIN] was not removed when it should have been!") + + // Test adding multiple traits from the same source + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_A, UNIT_TEST_SOURCE_MAIN) + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_B, UNIT_TEST_SOURCE_MAIN) + TEST_ASSERT(HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_B), "Failed to add [TRAIT_UNIT_TEST_B] with common source [UNIT_TEST_SOURCE_MAIN]!") + + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_C, UNIT_TEST_SOURCE_ALT) + REMOVE_TRAITS_NOT_IN(trait_target, UNIT_TEST_SOURCE_MAIN) + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_C), "Failed to remove [TRAIT_UNIT_TEST_C] when expected to be removed with remove_traits_NOT_IN(), was added with source [UNIT_TEST_SOURCE_ALT]!") + + // as well as its removal + REMOVE_TRAITS_IN(trait_target, UNIT_TEST_SOURCE_MAIN) + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_A), "Failed to remove [TRAIT_UNIT_TEST_A] with common source [UNIT_TEST_SOURCE_MAIN]!") + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_B), "Failed to remove [TRAIT_UNIT_TEST_B] with common source [UNIT_TEST_SOURCE_MAIN]!") + + // Lastly, let's ensure that adding/removing traits using lists still works. + var/static/list/standardized_traits_list = list(TRAIT_UNIT_TEST_A, TRAIT_UNIT_TEST_B, TRAIT_UNIT_TEST_C) + trait_target.add_traits(standardized_traits_list, UNIT_TEST_SOURCE_MAIN) + for(var/trait in standardized_traits_list) + TEST_ASSERT(HAS_TRAIT(trait_target, trait), "Failed to add [trait] when using add_traits() using [UNIT_TEST_SOURCE_MAIN]!") + trait_target.remove_traits(standardized_traits_list, UNIT_TEST_SOURCE_MAIN) + for(var/trait in standardized_traits_list) + TEST_ASSERT(!HAS_TRAIT(trait_target, trait), "Failed to remove [trait] when using remove_traits() using [UNIT_TEST_SOURCE_MAIN]!") + + // As well as ensure mixing-and-matching types of trait addition/removal works. + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_A, UNIT_TEST_SOURCE_MAIN) + ADD_TRAIT(trait_target, TRAIT_UNIT_TEST_B, UNIT_TEST_SOURCE_MAIN) + trait_target.remove_traits(standardized_traits_list, UNIT_TEST_SOURCE_MAIN) + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_A), "Failed to remove [TRAIT_UNIT_TEST_A] when using remove_traits() using [UNIT_TEST_SOURCE_MAIN] (was added using ADD_TRAIT())!") + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_B), "Failed to remove [TRAIT_UNIT_TEST_B] when using remove_traits() using [UNIT_TEST_SOURCE_MAIN] (was added using ADD_TRAIT())!") + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_C), "[TRAIT_UNIT_TEST_C] somehow came into existence when using remove_traits() using [UNIT_TEST_SOURCE_MAIN] (what the actual fuck)!") + + trait_target.add_traits(standardized_traits_list, UNIT_TEST_SOURCE_MAIN) + REMOVE_TRAIT(trait_target, TRAIT_UNIT_TEST_A, UNIT_TEST_SOURCE_MAIN) + REMOVE_TRAIT(trait_target, TRAIT_UNIT_TEST_B, UNIT_TEST_SOURCE_MAIN) + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_A), "Failed to remove [TRAIT_UNIT_TEST_A] when using REMOVE_TRAIT() using [UNIT_TEST_SOURCE_MAIN] (was added using add_traits())!") + TEST_ASSERT(!HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_B), "Failed to remove [TRAIT_UNIT_TEST_B] when using REMOVE_TRAIT() using [UNIT_TEST_SOURCE_MAIN] (was added using add_traits())!") + TEST_ASSERT(HAS_TRAIT(trait_target, TRAIT_UNIT_TEST_C), "[TRAIT_UNIT_TEST_C] was unexpectedly removed when using REMOVE_TRAIT() using [UNIT_TEST_SOURCE_MAIN] (was added using add_traits())!") + REMOVE_TRAIT(trait_target, TRAIT_UNIT_TEST_C, UNIT_TEST_SOURCE_MAIN) //just for cleanliness+completeness + + TEST_ASSERT(!length(trait_target._status_traits), "Failed to clean up all status traits at the end of the unit test!") + +#undef TRAIT_UNIT_TEST_MAIN +#undef TRAIT_UNIT_TEST_ALT +#undef TRAIT_UNIT_TEST_A +#undef TRAIT_UNIT_TEST_B +#undef TRAIT_UNIT_TEST_C +#undef UNIT_TEST_SOURCE_MAIN +#undef UNIT_TEST_SOURCE_ALT +#undef UNIT_TEST_SOURCE_A +#undef UNIT_TEST_SOURCE_B diff --git a/code/modules/uplink/uplink_devices.dm b/code/modules/uplink/uplink_devices.dm index d7a4e4f6baa6..5c839e3e09de 100644 --- a/code/modules/uplink/uplink_devices.dm +++ b/code/modules/uplink/uplink_devices.dm @@ -103,7 +103,7 @@ /obj/item/ntuplink/proc/finalize() //if the uplink type has been modified somehow, remove it and replace it var/datum/component/uplink/nanotrasen/uplink = GetComponent(/datum/component/uplink/nanotrasen) if(uplink) - uplink.RemoveComponent() + qdel(uplink) AddComponent(nt_datum, datum_owner, FALSE, TRUE, null, wc_start) /obj/item/ntuplink/official diff --git a/icons/obj/smooth_structures/shuttle_window.dmi b/icons/obj/smooth_structures/shuttle_window.dmi index 67acefd53da0b5514b5081018a3560f32a1421de..9b9891e9d2c8e13a9387dc9257ea4289a534cf91 100644 GIT binary patch literal 6254 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 literal 3738 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 Date: Thu, 7 Dec 2023 09:04:17 -0500 Subject: [PATCH 12/15] Update _lists.dm --- code/__HELPERS/_lists.dm | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 4324f060a4d2..4e49da925c8b 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -749,3 +749,32 @@ stack_trace("[name] is not sorted. value at [index] ([value]) is in the wrong place compared to the previous value of [last_value] (when compared to by [cmp])") last_value = value + +/// Compares 2 lists, returns TRUE if they are the same +/proc/deep_compare_list(list/list_1, list/list_2) + if(!islist(list_1) || !islist(list_2)) + return FALSE + + if(list_1 == list_2) + return TRUE + + if(list_1.len != list_2.len) + return FALSE + + for(var/i in 1 to list_1.len) + var/key_1 = list_1[i] + var/key_2 = list_2[i] + if (islist(key_1) && islist(key_2)) + if(!deep_compare_list(key_1, key_2)) + return FALSE + else if(key_1 != key_2) + return FALSE + if(istext(key_1) || islist(key_1) || ispath(key_1) || isdatum(key_1) || key_1 == world) + var/value_1 = list_1[key_1] + var/value_2 = list_2[key_1] + if (islist(value_1) && islist(value_2)) + if(!deep_compare_list(value_1, value_2)) + return FALSE + else if(value_1 != value_2) + return FALSE + return TRUE From 126b5a0ab63ff98202c42f657b0d0b323e15d3d7 Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:24:57 -0500 Subject: [PATCH 13/15] Update icon_smoothing.dm --- code/__HELPERS/icon_smoothing.dm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index c2b3a7f86056..e820e82df219 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -390,7 +390,9 @@ xxx xxx xxx break set_adj_in_dir; \ /// Check that non border objects use to smooth against border objects /// Returns true if the smooth is acceptable, FALSE otherwise - #define BITMASK_ON_BORDER_CHECK(target, direction) (!(target.smoothing_flags & SMOOTH_BORDER_OBJECT) || CAN_DIAGONAL_SMOOTH(target, src, REVERSE_DIR(direction))) + #define BITMASK_ON_BORDER_CHECK(target, direction) \ + (!(target.smoothing_flags & SMOOTH_BORDER_OBJECT) || CAN_DIAGONAL_SMOOTH(target, src, REVERSE_DIR(direction)) \ + ) #define BORDER_FOUND(target, direction, direction_flag) new_junction |= CAN_DIAGONAL_SMOOTH(src, target, direction) // Border objects require an object as context, so we need a dummy. I'm sorry @@ -411,6 +413,7 @@ xxx xxx xxx SEARCH_ADJ_IN_DIR(direction, direction_flag, BITMASK_FOUND, BITMASK_FOUND, BITMASK_ON_BORDER_CHECK) \ }} \ while(FALSE) + // and another for border object work (Doesn't early exit because we can hit more then one direction by checking the same turf) #define SET_BORDER_ADJ_IN_DIR(direction) SEARCH_ADJ_IN_DIR(direction, direction, BORDER_FOUND, WORLD_BORDER_FOUND, BORDER_ON_BORDER_CHECK) From 0fff429fb1c60f6d2a3a70d4280c2b52af639fae Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:50:43 -0500 Subject: [PATCH 14/15] it now works kinda --- code/__DEFINES/_atoms.dm | 7 + code/__DEFINES/flags.dm | 6 +- code/__DEFINES/subsystems.dm | 29 ++-- code/_compile_options.dm | 2 - code/controllers/subsystem/atoms.dm | 93 ++++------- code/datums/armor.dm | 2 +- code/game/atom/_atom.dm | 107 +------------ code/game/atom/atom_initializing_EXPENSIVE.dm | 151 ++++++++++++++++++ code/modules/mob/mob.dm | 14 +- code/modules/mob/mob_defines.dm | 1 - yogstation.dme | 2 + 11 files changed, 224 insertions(+), 190 deletions(-) create mode 100644 code/__DEFINES/_atoms.dm create mode 100644 code/game/atom/atom_initializing_EXPENSIVE.dm diff --git a/code/__DEFINES/_atoms.dm b/code/__DEFINES/_atoms.dm new file mode 100644 index 000000000000..21ae4ae23b36 --- /dev/null +++ b/code/__DEFINES/_atoms.dm @@ -0,0 +1,7 @@ +#define BAD_INIT_QDEL_BEFORE 1 +#define BAD_INIT_DIDNT_INIT 2 +#define BAD_INIT_SLEPT 4 +#define BAD_INIT_NO_HINT 8 + +#define PROFILE_INIT_ATOM_BEGIN(...) +#define PROFILE_INIT_ATOM_END(...) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 92c198dd18c5..f4c1ce20fa94 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -20,9 +20,9 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768)) // for /datum/var/datum_flags -#define DF_USE_TAG (1<<0) -#define DF_VAR_EDITED (1<<1) -#define DF_ISPROCESSING (1<<2) +#define DF_USE_TAG (1<<0) +#define DF_VAR_EDITED (1<<1) +#define DF_ISPROCESSING (1<<2) //FLAGS BITMASK diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index b92b3a398214..abfbd91b42dd 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -69,9 +69,9 @@ ///New should not call Initialize #define INITIALIZATION_INSSATOMS 0 -///New should call Initialize(mapload, TRUE) +///New should call Initialize(TRUE) #define INITIALIZATION_INNEW_MAPLOAD 2 -///New should call Initialize(mapload, FALSE) +///New should call Initialize(FALSE) #define INITIALIZATION_INNEW_REGULAR 1 //! ### Initialization hints @@ -79,12 +79,12 @@ ///Nothing happens #define INITIALIZE_HINT_NORMAL 0 /** - * call LateInitialize at the end of all atom Initalization - * - * The item will be added to the late_loaders list, this is iterated over after - * initalization of subsystems is complete and calls LateInitalize on the atom - * see [this file for the LateIntialize proc](atom.html#proc/LateInitialize) - */ + * call LateInitialize at the end of all atom Initalization + * + * The item will be added to the late_loaders list, this is iterated over after + * initalization of subsystems is complete and calls LateInitalize on the atom + * see [this file for the LateIntialize proc](atom.html#proc/LateInitialize) + */ #define INITIALIZE_HINT_LATELOAD 1 ///Call qdel on the atom after intialization @@ -92,11 +92,14 @@ ///type and all subtypes should always immediately call Initialize in New() #define INITIALIZE_IMMEDIATE(X) ##X/New(loc, ...){\ - ..();\ - if(!(flags_1 & INITIALIZED_1)) {\ - args[1] = TRUE;\ - SSatoms.InitAtom(src, args);\ - }\ + ..();\ + if(!(flags_1 & INITIALIZED_1)) {\ + var/previous_initialized_value = SSatoms.initialized;\ + SSatoms.initialized = INITIALIZATION_INNEW_MAPLOAD;\ + args[1] = TRUE;\ + SSatoms.InitAtom(src, FALSE, args);\ + SSatoms.initialized = previous_initialized_value;\ + }\ } //! ### SS initialization hints diff --git a/code/_compile_options.dm b/code/_compile_options.dm index b296be969328..ff9cabf129ae 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -31,8 +31,6 @@ #endif // 1 to use the default behaviour; // 2 for preloading absolutely everything; -#define LOWMEMORYMODE - #ifdef LOWMEMORYMODE #define FORCE_MAP "_maps/runtimestation.json" #endif diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index c4d493d2dff9..2e803eb8fb9a 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -1,14 +1,9 @@ #define SUBSYSTEM_INIT_SOURCE "subsystem init" - -#define BAD_INIT_QDEL_BEFORE 1 -#define BAD_INIT_DIDNT_INIT 2 -#define BAD_INIT_SLEPT 4 -#define BAD_INIT_NO_HINT 8 - SUBSYSTEM_DEF(atoms) name = "Atoms" init_order = INIT_ORDER_ATOMS flags = SS_NO_FIRE + loading_points = 30 SECONDS // Yogs -- smarter loading times /// A stack of list(source, desired initialized state) @@ -20,32 +15,34 @@ SUBSYSTEM_DEF(atoms) var/list/BadInitializeCalls = list() - var/init_start_time + ///initAtom() adds the atom its creating to this list iff InitializeAtoms() has been given a list to populate as an argument + var/list/created_atoms /// Atoms that will be deleted once the subsystem is initialized var/list/queued_deletions = list() + var/init_start_time + initialized = INITIALIZATION_INSSATOMS -/datum/controller/subsystem/atoms/Initialize(timeofday) +/datum/controller/subsystem/atoms/Initialize() init_start_time = world.time - GLOB.fire_overlay.appearance_flags = RESET_COLOR setupGenetics() //to set the mutations' sequence initialized = INITIALIZATION_INNEW_MAPLOAD InitializeAtoms() initialized = INITIALIZATION_INNEW_REGULAR - + return SS_INIT_SUCCESS -/datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms) +/datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms, list/atoms_to_return) if(initialized == INITIALIZATION_INSSATOMS) return set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, SUBSYSTEM_INIT_SOURCE) // This may look a bit odd, but if the actual atom creation runtimes for some reason, we absolutely need to set initialized BACK - CreateAtoms(atoms) + CreateAtoms(atoms, atoms_to_return) clear_tracked_initalize(SUBSYSTEM_INIT_SOURCE) if(late_loaders.len) @@ -57,7 +54,11 @@ SUBSYSTEM_DEF(atoms) A.LateInitialize() testing("Late initialized [late_loaders.len] atoms") late_loaders.Cut() - + + if (created_atoms) + atoms_to_return += created_atoms + created_atoms = null + for (var/queued_deletion in queued_deletions) qdel(queued_deletion) @@ -65,7 +66,10 @@ SUBSYSTEM_DEF(atoms) queued_deletions.Cut() /// Actually creates the list of atoms. Exists soley so a runtime in the creation logic doesn't cause initalized to totally break -/datum/controller/subsystem/atoms/proc/CreateAtoms(list/atoms) +/datum/controller/subsystem/atoms/proc/CreateAtoms(list/atoms, list/atoms_to_return = null) + if (atoms_to_return) + LAZYINITLIST(created_atoms) + #ifdef TESTING var/count #endif @@ -81,7 +85,9 @@ SUBSYSTEM_DEF(atoms) var/atom/A = atoms[I] if(!(A.flags_1 & INITIALIZED_1)) CHECK_TICK - InitAtom(A, mapload_arg) + PROFILE_INIT_ATOM_BEGIN() + InitAtom(A, TRUE, mapload_arg) + PROFILE_INIT_ATOM_END(A) else #ifdef TESTING count = 0 @@ -89,7 +95,9 @@ SUBSYSTEM_DEF(atoms) for(var/atom/A as anything in world) if(!(A.flags_1 & INITIALIZED_1)) - InitAtom(A, mapload_arg) + PROFILE_INIT_ATOM_BEGIN() + InitAtom(A, FALSE, mapload_arg) + PROFILE_INIT_ATOM_END(A) #ifdef TESTING ++count #endif @@ -97,48 +105,6 @@ SUBSYSTEM_DEF(atoms) testing("Initialized [count] atoms") -/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments) - var/the_type = A.type - if(QDELING(A)) - // Check init_start_time to not worry about atoms created before the atoms SS that are cleaned up before this - if (A.gc_destroyed > init_start_time) - BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE - return TRUE - - #ifdef UNIT_TESTS - var/start_tick = world.time - #endif - - var/result = A.Initialize(arglist(arguments)) - - #ifdef UNIT_TESTS - if(start_tick != world.time) - BadInitializeCalls[the_type] |= BAD_INIT_SLEPT - #endif - - var/qdeleted = FALSE - - switch(result) - if (INITIALIZE_HINT_NORMAL) - // pass - if (INITIALIZE_HINT_LATELOAD) - if(arguments[1]) //mapload - late_loaders += A - else - A.LateInitialize() - if (INITIALIZE_HINT_QDEL) - qdel(A) - qdeleted = TRUE - else - BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT - - if(!A) //possible harddel - qdeleted = TRUE - else if(!(A.flags_1 & INITIALIZED_1)) - BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT - - return qdeleted || QDELING(A) - /datum/controller/subsystem/atoms/proc/map_loader_begin(source) set_tracked_initalized(INITIALIZATION_INSSATOMS, source) @@ -154,7 +120,9 @@ SUBSYSTEM_DEF(atoms) initialized = state /datum/controller/subsystem/atoms/proc/clear_tracked_initalize(source) - for(var/i in length(initialized_state) to 1) + if(!length(initialized_state)) + return + for(var/i in length(initialized_state) to 1 step -1) if(initialized_state[i][1] == source) initialized_state.Cut(i, i+1) break @@ -211,7 +179,7 @@ SUBSYSTEM_DEF(atoms) if(fails & BAD_INIT_QDEL_BEFORE) . += "- Qdel'd in New()\n" if(fails & BAD_INIT_SLEPT) - . += "- Slept during Initialize(mapload)\n" + . += "- Slept during Initialize()\n" /// Prepares an atom to be deleted once the atoms SS is initialized. /datum/controller/subsystem/atoms/proc/prepare_deletion(atom/target) @@ -226,9 +194,4 @@ SUBSYSTEM_DEF(atoms) if(initlog) text2file(initlog, "[GLOB.log_directory]/initialize.log") -#undef BAD_INIT_QDEL_BEFORE -#undef BAD_INIT_DIDNT_INIT -#undef BAD_INIT_SLEPT -#undef BAD_INIT_NO_HINT - #undef SUBSYSTEM_INIT_SOURCE diff --git a/code/datums/armor.dm b/code/datums/armor.dm index fa995f3490a5..d9b07d7be0f0 100644 --- a/code/datums/armor.dm +++ b/code/datums/armor.dm @@ -6,7 +6,6 @@ . = new /datum/armor(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic, wound, electric) /datum/armor - datum_flags = DF_USE_TAG var/melee var/bullet var/laser @@ -34,6 +33,7 @@ src.wound = wound src.electric = electric tag = ARMORID + GenerateTag() /datum/armor/proc/modifyRating(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0, wound = 0, electric=0) return getArmor(src.melee+melee, src.bullet+bullet, src.laser+laser, src.energy+energy, src.bomb+bomb, src.bio+bio, src.rad+rad, src.fire+fire, src.acid+acid, src.magic+magic, src.wound+wound, src.electric+electric) diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index 88d27eb52bf5..e6f819701a78 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -137,104 +137,6 @@ var/atom/orbit_target //Reference to atom being orbited -/** - * Called when an atom is created in byond (built in engine proc) - * - * Not a lot happens here in SS13 code, as we offload most of the work to the - * [Intialization](atom.html#proc/Initialize) proc, mostly we run the preloader - * if the preloader is being used and then call InitAtom of which the ultimate - * result is that the Intialize proc is called. - * - * We also generate a tag here if the DF_USE_TAG flag is set on the atom - */ -/atom/New(loc, ...) - //atom creation method that preloads variables at creation - if(GLOB.use_preloader && (src.type == GLOB._preloader_path))//in case the instanciated atom is creating other atoms in New() - world.preloader_load(src) - - if(datum_flags & DF_USE_TAG) - GenerateTag() - - var/do_initialize = SSatoms.initialized - if(do_initialize != INITIALIZATION_INSSATOMS) - args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD - if(SSatoms.InitAtom(src, args)) - //we were deleted - return - SSdemo.mark_new(src) - -/** - * The primary method that objects are setup in SS13 with - * - * we don't use New as we have better control over when this is called and we can choose - * to delay calls or hook other logic in and so forth - * - * During roundstart map parsing, atoms are queued for intialization in the base atom/New(), - * After the map has loaded, then Initalize is called on all atoms one by one. NB: this - * is also true for loading map templates as well, so they don't Initalize until all objects - * in the map file are parsed and present in the world - * - * If you're creating an object at any point after SSInit has run then this proc will be - * immediately be called from New. - * - * mapload: This parameter is true if the atom being loaded is either being intialized during - * the Atom subsystem intialization, or if the atom is being loaded from the map template. - * If the item is being created at runtime any time after the Atom subsystem is intialized then - * it's false. - * - * You must always call the parent of this proc, otherwise failures will occur as the item - * will not be seen as initalized (this can lead to all sorts of strange behaviour, like - * the item being completely unclickable) - * - * You must not sleep in this proc, or any subprocs - * - * Any parameters from new are passed through (excluding loc), naturally if you're loading from a map - * there are no other arguments - * - * Must return an [initialization hint](code/__DEFINES/subsystems.html) or a runtime will occur. - * - * Note: the following functions don't call the base for optimization and must copypasta handling: - * * /turf/Initialize - * * /turf/open/space/Initialize - */ -/atom/proc/Initialize(mapload, ...) - SHOULD_CALL_PARENT(TRUE) - if(flags_1 & INITIALIZED_1) - stack_trace("Warning: [src]([type]) initialized multiple times!") - flags_1 |= INITIALIZED_1 - - //atom color stuff - if(color) - add_atom_colour(color, FIXED_COLOUR_PRIORITY) - - if (light_system == STATIC_LIGHT && light_power && light_range) - update_light() - - if(custom_materials && custom_materials.len) - var/temp_list = list() - for(var/i in custom_materials) - var/datum/material/material = getmaterialref(i) || i - temp_list[material] = custom_materials[material] //Get the proper instanced version - - custom_materials = null //Null the list to prepare for applying the materials properly - set_custom_materials(temp_list) - - return INITIALIZE_HINT_NORMAL - -/** - * Late Intialization, for code that should run after all atoms have run Intialization - * - * To have your LateIntialize proc be called, your atoms [Initalization](atom.html#proc/Initialize) - * proc must return the hint - * [INITIALIZE_HINT_LATELOAD](code/__DEFINES/subsystems.html#define/INITIALIZE_HINT_LATELOAD) - * otherwise you will never be called. - * - * useful for doing things like finding other machines on GLOB.machines because you can guarantee - * that all atoms will actually exist in the "WORLD" at this time and that all their Intialization - * code has been run - */ -/atom/proc/LateInitialize() - return /** * Top level of the destroy chain for most atoms @@ -1310,9 +1212,12 @@ /atom/proc/return_temperature() return -///Generate a tag for this atom -/atom/proc/GenerateTag() - return +///Generate a tag for this /datum, if it implements one +///Should be called as early as possible, best would be in New, to avoid weakref mistargets +///Really just don't use this, you don't need it, global lists will do just fine MOST of the time +///We really only use it for mobs to make id'ing people easier +/datum/proc/GenerateTag() + datum_flags |= DF_USE_TAG ///Connect this atom to a shuttle /atom/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE) diff --git a/code/game/atom/atom_initializing_EXPENSIVE.dm b/code/game/atom/atom_initializing_EXPENSIVE.dm new file mode 100644 index 000000000000..a6218922e8be --- /dev/null +++ b/code/game/atom/atom_initializing_EXPENSIVE.dm @@ -0,0 +1,151 @@ +/// Init this specific atom +/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, from_template = FALSE, list/arguments) + var/the_type = A.type + + if(QDELING(A)) + // Check init_start_time to not worry about atoms created before the atoms SS that are cleaned up before this + if (A.gc_destroyed > init_start_time) + BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE + return TRUE + + // This is handled and battle tested by dreamchecker. Limit to UNIT_TESTS just in case that ever fails. + #ifdef UNIT_TESTS + var/start_tick = world.time + #endif + + var/result = A.Initialize(arglist(arguments)) + + #ifdef UNIT_TESTS + if(start_tick != world.time) + BadInitializeCalls[the_type] |= BAD_INIT_SLEPT + #endif + + var/qdeleted = FALSE + + switch(result) + if (INITIALIZE_HINT_NORMAL) + // pass + if(INITIALIZE_HINT_LATELOAD) + if(arguments[1]) //mapload + late_loaders += A + else + A.LateInitialize() + if(INITIALIZE_HINT_QDEL) + qdel(A) + qdeleted = TRUE + else + BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT + + if(!A) //possible harddel + qdeleted = TRUE + else if(!(A.flags_1 & INITIALIZED_1)) + BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT + else + SEND_SIGNAL(A, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE) + if(created_atoms && from_template && ispath(the_type, /atom/movable))//we only want to populate the list with movables + created_atoms += A.get_all_contents() + + return qdeleted || QDELING(A) + +/** + * Called when an atom is created in byond (built in engine proc) + * + * Not a lot happens here in SS13 code, as we offload most of the work to the + * [Intialization][/atom/proc/Initialize] proc, mostly we run the preloader + * if the preloader is being used and then call [InitAtom][/datum/controller/subsystem/atoms/proc/InitAtom] of which the ultimate + * result is that the Intialize proc is called. + * + */ +/atom/New(loc, ...) + //atom creation method that preloads variables at creation + if(GLOB.use_preloader && src.type == GLOB._preloader_path)//in case the instanciated atom is creating other atoms in New() + world.preloader_load(src) + + var/do_initialize = SSatoms.initialized + if(do_initialize != INITIALIZATION_INSSATOMS) + args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD + if(SSatoms.InitAtom(src, FALSE, args)) + //we were deleted + return + //Yogs edit: Demos + SSdemo.mark_new(src) + //Yogs edit ends + +/** + * The primary method that objects are setup in SS13 with + * + * we don't use New as we have better control over when this is called and we can choose + * to delay calls or hook other logic in and so forth + * + * During roundstart map parsing, atoms are queued for intialization in the base atom/New(), + * After the map has loaded, then Initalize is called on all atoms one by one. NB: this + * is also true for loading map templates as well, so they don't Initalize until all objects + * in the map file are parsed and present in the world + * + * If you're creating an object at any point after SSInit has run then this proc will be + * immediately be called from New. + * + * mapload: This parameter is true if the atom being loaded is either being intialized during + * the Atom subsystem intialization, or if the atom is being loaded from the map template. + * If the item is being created at runtime any time after the Atom subsystem is intialized then + * it's false. + * + * The mapload argument occupies the same position as loc when Initialize() is called by New(). + * loc will no longer be needed after it passed New(), and thus it is being overwritten + * with mapload at the end of atom/New() before this proc (atom/Initialize()) is called. + * + * You must always call the parent of this proc, otherwise failures will occur as the item + * will not be seen as initalized (this can lead to all sorts of strange behaviour, like + * the item being completely unclickable) + * + * You must not sleep in this proc, or any subprocs + * + * Any parameters from new are passed through (excluding loc), naturally if you're loading from a map + * there are no other arguments + * + * Must return an [initialization hint][INITIALIZE_HINT_NORMAL] or a runtime will occur. + * + * Note: the following functions don't call the base for optimization and must copypasta handling: + * * [/turf/proc/Initialize] + * * [/turf/open/space/proc/Initialize] + */ +/atom/proc/Initialize(mapload, ...) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(TRUE) + + if(flags_1 & INITIALIZED_1) + stack_trace("Warning: [src]([type]) initialized multiple times!") + flags_1 |= INITIALIZED_1 + + //atom color stuff + if(color) + add_atom_colour(color, FIXED_COLOUR_PRIORITY) + + if (light_system == STATIC_LIGHT && light_power && light_range) + update_light() + + SETUP_SMOOTHING() + + if(custom_materials && custom_materials.len) + var/temp_list = list() + for(var/i in custom_materials) + var/datum/material/material = getmaterialref(i) || i + temp_list[material] = custom_materials[material] //Get the proper instanced version + custom_materials = null //Null the list to prepare for applying the materials properly + set_custom_materials(temp_list) + + return INITIALIZE_HINT_NORMAL + +/** + * Late Intialization, for code that should run after all atoms have run Intialization + * + * To have your LateIntialize proc be called, your atoms [Initalization][/atom/proc/Initialize] + * proc must return the hint + * [INITIALIZE_HINT_LATELOAD] otherwise it will never be called. + * + * useful for doing things like finding other machines on GLOB.machines because you can guarantee + * that all atoms will actually exist in the "WORLD" at this time and that all their Intialization + * code has been run + */ +/atom/proc/LateInitialize() + set waitfor = FALSE diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 399aa3e7786d..455c77749671 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -87,12 +87,18 @@ update_config_movespeed() update_movespeed(TRUE) +/mob/New() + // This needs to happen IMMEDIATELY. I'm sorry :( + GenerateTag() + return ..() + /** - * Generate the tag for this mob - * - * This is simply "mob_"+ a global incrementing counter that goes up for every mob - */ + * Generate the tag for this mob + * + * This is simply "mob_"+ a global incrementing counter that goes up for every mob + */ /mob/GenerateTag() + . = ..() tag = "mob_[next_mob_id++]" /** diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 469261640cb2..82854737eb19 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -7,7 +7,6 @@ * Has a lot of the creature game world logic, such as health etc */ /mob - datum_flags = DF_USE_TAG density = TRUE layer = MOB_LAYER animate_movement = 2 diff --git a/yogstation.dme b/yogstation.dme index 7e4414441c6e..f1e8b345d09d 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -18,6 +18,7 @@ #include "code\_compile_options.dm" #include "code\_debugger.dm" #include "code\world.dm" +#include "code\__DEFINES\_atoms.dm" #include "code\__DEFINES\_auxtools.dm" #include "code\__DEFINES\_bitfields.dm" #include "code\__DEFINES\_click.dm" @@ -846,6 +847,7 @@ #include "code\game\area\areas\ruins\space.dm" #include "code\game\area\areas\ruins\templates.dm" #include "code\game\atom\_atom.dm" +#include "code\game\atom\atom_initializing_EXPENSIVE.dm" #include "code\game\atom\atom_invisibility.dm" #include "code\game\atom\atom_materials.dm" #include "code\game\atom\atom_orbit.dm" From f6d806fac74421188f071255e0d001675ae3297f Mon Sep 17 00:00:00 2001 From: JohnFulpWillard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:59:11 -0500 Subject: [PATCH 15/15] Update atom_initializing_EXPENSIVE.dm --- code/game/atom/atom_initializing_EXPENSIVE.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/atom/atom_initializing_EXPENSIVE.dm b/code/game/atom/atom_initializing_EXPENSIVE.dm index a6218922e8be..d4055267b2ca 100644 --- a/code/game/atom/atom_initializing_EXPENSIVE.dm +++ b/code/game/atom/atom_initializing_EXPENSIVE.dm @@ -110,7 +110,7 @@ * * [/turf/open/space/proc/Initialize] */ /atom/proc/Initialize(mapload, ...) - SHOULD_NOT_SLEEP(TRUE) +// SHOULD_NOT_SLEEP(TRUE) //Yogs edit: This is fucked and needs an in-depth review before commenting back in. But we should eventually. SHOULD_CALL_PARENT(TRUE) if(flags_1 & INITIALIZED_1)