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/_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/_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/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_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/__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/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..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
@@ -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)
@@ -67,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/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm
new file mode 100644
index 000000000000..05844512da3d
--- /dev/null
+++ b/code/__DEFINES/icon_smoothing.dm
@@ -0,0 +1,232 @@
+/* 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
+#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 */
+
+#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/subsystems.dm b/code/__DEFINES/subsystems.dm
index 944f6ef36c98..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
@@ -188,11 +191,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 31bb25afe2e8..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.
@@ -309,7 +319,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.
+#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 +395,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/__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/_lists.dm b/code/__HELPERS/_lists.dm
index d5cd95b2db6b..4e49da925c8b 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -737,3 +737,44 @@
///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
+
+/// 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
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/__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/__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/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm
index 41b9d266c59e..e820e82df219 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,517 @@
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, 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 +546,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 +656,24 @@
name = "smooth wall"
icon = 'icons/turf/smooth_wall.dmi'
icon_state = "smooth"
- smooth = SMOOTH_TRUE|SMOOTH_DIAGONAL|SMOOTH_BORDER
+ base_icon_state = "smooth"
+ 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/_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/atoms.dm b/code/controllers/subsystem/atoms.dm
index e7e6275cd8bc..2e803eb8fb9a 100644
--- a/code/controllers/subsystem/atoms.dm
+++ b/code/controllers/subsystem/atoms.dm
@@ -1,49 +1,49 @@
-#define BAD_INIT_QDEL_BEFORE 1
-#define BAD_INIT_DIDNT_INIT 2
-#define BAD_INIT_SLEPT 4
-#define BAD_INIT_NO_HINT 8
-
+#define SUBSYSTEM_INIT_SOURCE "subsystem init"
SUBSYSTEM_DEF(atoms)
name = "Atoms"
init_order = INIT_ORDER_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()
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)
+ 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()
+ CreateAtoms(atoms, atoms_to_return)
+ clear_tracked_initalize(SUBSYSTEM_INIT_SOURCE)
if(late_loaders.len)
for(var/I in 1 to late_loaders.len)
@@ -54,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)
@@ -62,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
@@ -78,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
@@ -86,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
@@ -94,73 +105,43 @@ 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
+/datum/controller/subsystem/atoms/proc/map_loader_begin(source)
+ set_tracked_initalized(INITIALIZATION_INSSATOMS, source)
- var/result = A.Initialize(arglist(arguments))
+/datum/controller/subsystem/atoms/proc/map_loader_stop(source)
+ clear_tracked_initalize(source)
- #ifdef UNIT_TESTS
- if(start_tick != world.time)
- BadInitializeCalls[the_type] |= BAD_INIT_SLEPT
- #endif
+/// 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
- 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()
- 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/clear_tracked_initalize(source)
+ 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
+
+ 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()
@@ -198,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)
@@ -213,7 +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/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/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 c5d02cf9439c..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)
- smooth_icon(A)
+ // 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
- smooth_icon(A)
+ 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/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/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/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/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/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/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/elements/undertile.dm b/code/datums/elements/undertile.dm
new file mode 100644
index 000000000000..de055bb0bd75
--- /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.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.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/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/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/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/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/atoms.dm b/code/game/atom/_atom.dm
similarity index 74%
rename from code/game/atoms.dm
rename to code/game/atom/_atom.dm
index 191525de75e4..e6f819701a78 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,108 +118,25 @@
///Mobs that are currently do_after'ing this atom, to be cleared from on Destroy()
var/list/targeted_by
- 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)
+ ///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
- 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)
- 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
+ var/atom/orbit_target //Reference to atom being orbited
-/**
- * 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
@@ -257,6 +173,9 @@
QDEL_NULL(light)
if (length(light_sources))
light_sources.Cut()
+
+ if(smoothing_flags & SMOOTH_QUEUED)
+ SSicon_smooth.remove_from_queues(src)
return ..()
@@ -978,8 +897,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
@@ -1287,228 +1212,17 @@
/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
+///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)
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 +1264,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 +1295,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_initializing_EXPENSIVE.dm b/code/game/atom/atom_initializing_EXPENSIVE.dm
new file mode 100644
index 000000000000..d4055267b2ca
--- /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) //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)
+ 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/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/magnet.dm b/code/game/machinery/magnet.dm
index 1c5e2c5b055c..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,9 +28,8 @@
/obj/machinery/magnetic_module/Initialize(mapload)
..()
- 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
@@ -43,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 6e14252d83cd..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,8 +25,6 @@
set_codes()
- var/turf/T = loc
- hide(T.intact)
if(codes["patrol"])
if(!GLOB.navbeacons["[z]"])
GLOB.navbeacons["[z]"] = list()
@@ -35,6 +32,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]"])
@@ -67,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()
. = ..()
@@ -88,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)
@@ -121,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/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/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/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..4e36645bdeba 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
@@ -95,7 +97,7 @@
light_range = 0
update_light()
if(R)
- R.RemoveComponent()
+ qdel(R)
/obj/effect/decal/cleanable/greenglow/filled/Initialize(mapload)
. = ..()
@@ -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/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/effects/effect_system/fluid_spread/effects_foam.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
index ca003178d8da..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,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(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/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/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/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 516b1d299030..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))
- hide(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 cd41be37a51b..39ef72ac0864 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -315,26 +315,14 @@
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)
. = ..()
+ 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)
-/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/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)
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..8778c7d18ab2 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -53,15 +53,17 @@
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
+ var/resintype = null
/obj/structure/alien/resin/Initialize(mapload)
@@ -77,9 +79,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 +92,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 +119,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
@@ -131,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
@@ -175,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
@@ -200,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 0b6c2335a185..dd18b16b135c 100644
--- a/code/game/objects/structures/beds_chairs/alien_nest.dm
+++ b/code/game/objects/structures/beds_chairs/alien_nest.dm
@@ -4,11 +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
- 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
flags_1 = NODECONSTRUCT_1
bolts = FALSE
diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm
index 0cf459dc0c7f..f9ba10381828 100644
--- a/code/game/objects/structures/catwalk.dm
+++ b/code/game/objects/structures/catwalk.dm
@@ -2,10 +2,12 @@
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
- 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 +51,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..ed25a37e1eed 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -6,26 +6,19 @@
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
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 +63,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)
@@ -163,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
@@ -183,12 +179,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 +216,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 +284,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 +302,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 +360,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 +371,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..4d73a9dad331 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -2,19 +2,18 @@
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)
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)
@@ -75,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/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..3349cf16ee1e 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -16,11 +16,17 @@
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
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 +34,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 +81,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
@@ -253,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)
@@ -331,15 +335,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)
@@ -349,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()
@@ -358,75 +363,69 @@
/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
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)
- 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
+ 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
/obj/structure/table/wood/fancy/black
icon_state = "fancy_table_black"
+ base_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"
+ base_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"
+ base_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"
+ base_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"
+ base_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"
+ base_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"
+ base_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"
+ base_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"
+ base_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
*/
@@ -434,10 +433,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 +469,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 +507,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 +528,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..0044f7796b5a 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()
+/obj/structure/window/update_overlays(updates=ALL)
. = ..()
- 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)
@@ -626,13 +621,15 @@
/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
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 +643,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 +660,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,15 +671,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
/obj/structure/window/reinforced/fulltile/unanchored
@@ -692,17 +693,16 @@
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)
- level = 3
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
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
/obj/structure/window/reinforced/fulltile/bronze
@@ -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"
@@ -725,10 +726,10 @@
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
glass_amount = 2
@@ -755,11 +756,11 @@
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
glass_type = /obj/item/stack/sheet/plastitaniumglass
glass_amount = 2
rad_insulation = RAD_FULL_INSULATION
@@ -828,13 +829,13 @@
/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
max_integrity = 600
- level = 3
glass_amount = 2
/obj/structure/window/reinforced/clockwork/spawnDebris(location)
@@ -862,8 +863,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 +908,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..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
@@ -137,6 +145,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 +318,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..d5a97ccf059b 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -11,20 +11,26 @@
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
+ 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 +116,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 +173,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..96b94613a3f8 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
@@ -335,11 +336,12 @@
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")
- 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 = 'icons/turf/floors/chasms.dmi'
+ 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..a53f6eb2e7f8 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 = 'icons/turf/floors/chasms.dmi'
+ 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/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/floor/plating/misc_plating.dm b/code/game/turfs/simulated/floor/plating/misc_plating.dm
index c3cae2da310b..0136e9796db3 100644
--- a/code/game/turfs/simulated/floor/plating/misc_plating.dm
+++ b/code/game/turfs/simulated/floor/plating/misc_plating.dm
@@ -35,13 +35,13 @@
/turf/open/floor/plating/ashplanet
- icon = 'icons/turf/mining.dmi'
- gender = PLURAL
name = "ash"
- icon_state = "ash"
- smooth = SMOOTH_MORE|SMOOTH_BORDER
- var/smooth_icon = 'icons/turf/floors/ash.dmi'
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
+ 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
@@ -53,12 +53,13 @@
tiled_dirt = FALSE
/turf/open/floor/plating/ashplanet/Initialize(mapload)
- if(smooth)
- var/matrix/M = new
- M.Translate(-4, -4)
- transform = M
- icon = smooth_icon
. = ..()
+ 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
@@ -70,17 +71,20 @@
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
/turf/open/floor/plating/ashplanet/rocky
gender = PLURAL
name = "rocky ground"
- icon_state = "rockyash"
- smooth_icon = 'icons/turf/floors/rocky_ash.dmi'
+ 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
- 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 +93,9 @@
/turf/open/floor/plating/ashplanet/wateryrock
gender = PLURAL
name = "wet rocky ground"
- smooth = null
- icon_state = "wateryrock"
+ icon_state = "watery_rock"
+ base_icon_state = "watery_rock"
+ smoothing_flags = NONE
slowdown = 2
footstep = FOOTSTEP_FLOOR
barefootstep = FOOTSTEP_HARD_BAREFOOT
@@ -98,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"
@@ -196,9 +200,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 +243,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..55b554a113e0 100644
--- a/code/game/turfs/simulated/lava.dm
+++ b/code/game/turfs/simulated/lava.dm
@@ -17,6 +17,28 @@
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
+ 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 +185,17 @@
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"
+ 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
+
+/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..6d8d875540dc 100644
--- a/code/game/turfs/simulated/minerals.dm
+++ b/code/game/turfs/simulated/minerals.dm
@@ -2,18 +2,23 @@
/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
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
@@ -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,34 @@
/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
+
+//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/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..654656630fb1 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
@@ -187,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/wall/reinf_walls.dm b/code/game/turfs/simulated/wall/reinf_walls.dm
index 94e94e57333f..3f36ebade727 100644
--- a/code/game/turfs/simulated/wall/reinf_walls.dm
+++ b/code/game/turfs/simulated/wall/reinf_walls.dm
@@ -2,9 +2,11 @@
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
+ smoothing_flags = SMOOTH_BITMASK
var/d_state = INTACT
hardness = 10
@@ -194,14 +196,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 +237,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..537fe7fa5b3f 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
@@ -16,23 +17,17 @@
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
+ /// A turf that will replace this turf when this turf is destroyed
+ var/decon_type
var/list/dent_decals
@@ -80,8 +75,11 @@
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()
new sheet_type(src, sheet_amount)
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..6cd10564bdfb 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -2,13 +2,10 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf
icon = 'icons/turf/floors.dmi'
- level = 1
luminosity = 1
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 +19,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 +41,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 +80,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)
@@ -308,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()
@@ -362,14 +368,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 +478,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 +489,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)
@@ -549,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
@@ -562,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.level == 1) //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/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/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/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/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/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/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/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/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/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/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 e14ef7818dab..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
@@ -38,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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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..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,9 +82,7 @@ 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
- 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
@@ -116,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]"
@@ -131,7 +121,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 +604,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
@@ -676,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
@@ -698,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/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..d94ad80fc17f 100644
--- a/code/modules/power/terminal.dm
+++ b/code/modules/power/terminal.dm
@@ -7,16 +7,13 @@
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
/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)
@@ -24,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
@@ -50,7 +38,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..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
@@ -31,6 +30,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)
@@ -46,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))
@@ -59,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
@@ -115,7 +106,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..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)
@@ -34,14 +33,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)
- update()
+
+ AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE)
// pipe is deleted
// ensure if holder is present, it is expelled
@@ -80,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)
@@ -97,7 +86,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/on_move.dm b/code/modules/shuttle/on_move.dm
index 2a02cd5503a7..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
@@ -253,11 +254,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 +261,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 +269,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 +324,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/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/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 af3d3c365116..ca029b16733c 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -58,14 +58,21 @@
#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"
#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"
@@ -77,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/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/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/focus_only_tests.dm b/code/modules/unit_tests/focus_only_tests.dm
index 426a236ab4bd..2d48294cbaa9 100644
--- a/code/modules/unit_tests/focus_only_tests.dm
+++ b/code/modules/unit_tests/focus_only_tests.dm
@@ -6,8 +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/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/catwalk.dmi b/icons/obj/smooth_structures/catwalk.dmi
index 4b71fb507316..0920cc571f1e 100644
Binary files a/icons/obj/smooth_structures/catwalk.dmi and b/icons/obj/smooth_structures/catwalk.dmi differ
diff --git a/icons/obj/smooth_structures/lattice.dmi b/icons/obj/smooth_structures/lattice.dmi
index 5adab0caf1be..d9223ded775f 100644
Binary files a/icons/obj/smooth_structures/lattice.dmi and b/icons/obj/smooth_structures/lattice.dmi differ
diff --git a/icons/obj/smooth_structures/poker_table.dmi b/icons/obj/smooth_structures/poker_table.dmi
index 2e16a5468dcb..d0b70822771f 100644
Binary files a/icons/obj/smooth_structures/poker_table.dmi and b/icons/obj/smooth_structures/poker_table.dmi differ
diff --git a/icons/obj/smooth_structures/reinforced_table.dmi b/icons/obj/smooth_structures/reinforced_table.dmi
index 85de93594f94..b2867b579945 100644
Binary files a/icons/obj/smooth_structures/reinforced_table.dmi and b/icons/obj/smooth_structures/reinforced_table.dmi differ
diff --git a/icons/obj/smooth_structures/reinforced_window.dmi b/icons/obj/smooth_structures/reinforced_window.dmi
index a1f60f0677ab..a66b648786da 100644
Binary files a/icons/obj/smooth_structures/reinforced_window.dmi and b/icons/obj/smooth_structures/reinforced_window.dmi differ
diff --git a/icons/obj/smooth_structures/shuttle_window.dmi b/icons/obj/smooth_structures/shuttle_window.dmi
index 67acefd53da0..9b9891e9d2c8 100644
Binary files a/icons/obj/smooth_structures/shuttle_window.dmi and b/icons/obj/smooth_structures/shuttle_window.dmi differ
diff --git a/icons/obj/smooth_structures/table.dmi b/icons/obj/smooth_structures/table.dmi
index ffd88a84fc7a..3b15d2a27dd5 100644
Binary files a/icons/obj/smooth_structures/table.dmi and b/icons/obj/smooth_structures/table.dmi differ
diff --git a/icons/obj/smooth_structures/window.dmi b/icons/obj/smooth_structures/window.dmi
index b680e44fa901..4b78390251e0 100644
Binary files a/icons/obj/smooth_structures/window.dmi and b/icons/obj/smooth_structures/window.dmi differ
diff --git a/icons/obj/smooth_structures/wood_table.dmi b/icons/obj/smooth_structures/wood_table.dmi
index 16fb364ad27f..3c72c11c5917 100644
Binary files a/icons/obj/smooth_structures/wood_table.dmi and b/icons/obj/smooth_structures/wood_table.dmi differ
diff --git a/icons/turf/floors/ash.dmi b/icons/turf/floors/ash.dmi
index 718cd120194e..1ebe8d713970 100644
Binary files a/icons/turf/floors/ash.dmi and b/icons/turf/floors/ash.dmi differ
diff --git a/icons/turf/floors/bamboo_mat.dmi b/icons/turf/floors/bamboo_mat.dmi
index e18237b3f63f..1e0b04bc3aa4 100644
Binary files a/icons/turf/floors/bamboo_mat.dmi and b/icons/turf/floors/bamboo_mat.dmi differ
diff --git a/icons/turf/floors/carpet.dmi b/icons/turf/floors/carpet.dmi
index 52208611a476..1af03a1df823 100644
Binary files a/icons/turf/floors/carpet.dmi and b/icons/turf/floors/carpet.dmi differ
diff --git a/icons/turf/floors/carpet_black.dmi b/icons/turf/floors/carpet_black.dmi
index d8cbc1c45342..e2fea9085b23 100644
Binary files a/icons/turf/floors/carpet_black.dmi and b/icons/turf/floors/carpet_black.dmi differ
diff --git a/icons/turf/floors/carpet_blue.dmi b/icons/turf/floors/carpet_blue.dmi
new file mode 100644
index 000000000000..b909b11f359d
Binary files /dev/null and b/icons/turf/floors/carpet_blue.dmi differ
diff --git a/icons/turf/floors/carpet_cyan.dmi b/icons/turf/floors/carpet_cyan.dmi
index feca351ca925..85e053a5a6de 100644
Binary files a/icons/turf/floors/carpet_cyan.dmi and b/icons/turf/floors/carpet_cyan.dmi differ
diff --git a/icons/turf/floors/carpet_donk.dmi b/icons/turf/floors/carpet_donk.dmi
new file mode 100644
index 000000000000..04c4148d2bc5
Binary files /dev/null and b/icons/turf/floors/carpet_donk.dmi differ
diff --git a/icons/turf/floors/carpet_executive.dmi b/icons/turf/floors/carpet_executive.dmi
new file mode 100644
index 000000000000..2c17e542fcd1
Binary files /dev/null and b/icons/turf/floors/carpet_executive.dmi differ
diff --git a/icons/turf/floors/carpet_green.dmi b/icons/turf/floors/carpet_green.dmi
new file mode 100644
index 000000000000..39ef04808748
Binary files /dev/null and b/icons/turf/floors/carpet_green.dmi differ
diff --git a/icons/turf/floors/carpet_neon_simple.dmi b/icons/turf/floors/carpet_neon_simple.dmi
new file mode 100644
index 000000000000..62cb355a2db9
Binary files /dev/null and b/icons/turf/floors/carpet_neon_simple.dmi differ
diff --git a/icons/turf/floors/carpet_orange.dmi b/icons/turf/floors/carpet_orange.dmi
index ddf239b63b20..4f0b2078fca3 100644
Binary files a/icons/turf/floors/carpet_orange.dmi and b/icons/turf/floors/carpet_orange.dmi differ
diff --git a/icons/turf/floors/carpet_purple.dmi b/icons/turf/floors/carpet_purple.dmi
new file mode 100644
index 000000000000..f07ce75d3340
Binary files /dev/null and b/icons/turf/floors/carpet_purple.dmi differ
diff --git a/icons/turf/floors/carpet_red.dmi b/icons/turf/floors/carpet_red.dmi
index 926655688ed2..eb0527f430dc 100644
Binary files a/icons/turf/floors/carpet_red.dmi and b/icons/turf/floors/carpet_red.dmi differ
diff --git a/icons/turf/floors/carpet_royalblack.dmi b/icons/turf/floors/carpet_royalblack.dmi
index bc5cef1cf0eb..0b848ac1afa4 100644
Binary files a/icons/turf/floors/carpet_royalblack.dmi and b/icons/turf/floors/carpet_royalblack.dmi differ
diff --git a/icons/turf/floors/carpet_royalblue.dmi b/icons/turf/floors/carpet_royalblue.dmi
index 841e49e957b6..1027b0e19f58 100644
Binary files a/icons/turf/floors/carpet_royalblue.dmi and b/icons/turf/floors/carpet_royalblue.dmi differ
diff --git a/icons/turf/floors/carpet_stellar.dmi b/icons/turf/floors/carpet_stellar.dmi
new file mode 100644
index 000000000000..6ba816784261
Binary files /dev/null and b/icons/turf/floors/carpet_stellar.dmi differ
diff --git a/icons/turf/floors/catwalk_plating.dmi b/icons/turf/floors/catwalk_plating.dmi
new file mode 100644
index 000000000000..b49c46564de5
Binary files /dev/null and b/icons/turf/floors/catwalk_plating.dmi differ
diff --git a/icons/turf/floors/chasms.dmi b/icons/turf/floors/chasms.dmi
index cc4feb2e032c..2e93b5391120 100644
Binary files a/icons/turf/floors/chasms.dmi and b/icons/turf/floors/chasms.dmi differ
diff --git a/icons/turf/floors/glass.dmi b/icons/turf/floors/glass.dmi
new file mode 100644
index 000000000000..ef9da477681e
Binary files /dev/null and b/icons/turf/floors/glass.dmi differ
diff --git a/icons/turf/floors/grass.dmi b/icons/turf/floors/grass.dmi
new file mode 100644
index 000000000000..2ffb5242562b
Binary files /dev/null and b/icons/turf/floors/grass.dmi differ
diff --git a/icons/turf/floors/ice_turf.dmi b/icons/turf/floors/ice_turf.dmi
index 8751a3721626..61574645759c 100644
Binary files a/icons/turf/floors/ice_turf.dmi and b/icons/turf/floors/ice_turf.dmi differ
diff --git a/icons/turf/floors/icechasms.dmi b/icons/turf/floors/icechasms.dmi
index 97dfc0a2da6b..5585d792ef86 100644
Binary files a/icons/turf/floors/icechasms.dmi and b/icons/turf/floors/icechasms.dmi differ
diff --git a/icons/turf/floors/junglechasm.dmi b/icons/turf/floors/junglechasm.dmi
index 699a3b667cd9..d2ac198865c5 100644
Binary files a/icons/turf/floors/junglechasm.dmi and b/icons/turf/floors/junglechasm.dmi differ
diff --git a/icons/turf/floors/junglegrass.dmi b/icons/turf/floors/junglegrass.dmi
new file mode 100644
index 000000000000..80456e469b11
Binary files /dev/null and b/icons/turf/floors/junglegrass.dmi differ
diff --git a/icons/turf/floors/lava.dmi b/icons/turf/floors/lava.dmi
index e4a276c001d9..3b889c9a5f68 100644
Binary files a/icons/turf/floors/lava.dmi and b/icons/turf/floors/lava.dmi differ
diff --git a/icons/turf/floors/lava_mask.dmi b/icons/turf/floors/lava_mask.dmi
new file mode 100644
index 000000000000..aaefebe39deb
Binary files /dev/null and b/icons/turf/floors/lava_mask.dmi differ
diff --git a/icons/turf/floors/plasma_glass.dmi b/icons/turf/floors/plasma_glass.dmi
new file mode 100644
index 000000000000..83aa755b7bce
Binary files /dev/null and b/icons/turf/floors/plasma_glass.dmi differ
diff --git a/icons/turf/floors/reinf_glass.dmi b/icons/turf/floors/reinf_glass.dmi
new file mode 100644
index 000000000000..a7607cada6a7
Binary files /dev/null and b/icons/turf/floors/reinf_glass.dmi differ
diff --git a/icons/turf/floors/reinf_plasma_glass.dmi b/icons/turf/floors/reinf_plasma_glass.dmi
new file mode 100644
index 000000000000..1bf4dd41734c
Binary files /dev/null and b/icons/turf/floors/reinf_plasma_glass.dmi differ
diff --git a/icons/turf/floors/rocky_ash.dmi b/icons/turf/floors/rocky_ash.dmi
index 2a57edfc53a4..cc6847dc5884 100644
Binary files a/icons/turf/floors/rocky_ash.dmi and b/icons/turf/floors/rocky_ash.dmi differ
diff --git a/icons/turf/floors/snow_turf.dmi b/icons/turf/floors/snow_turf.dmi
index e7fef38602b2..8b70aa44896f 100644
Binary files a/icons/turf/floors/snow_turf.dmi and b/icons/turf/floors/snow_turf.dmi differ
diff --git a/icons/turf/walls/red_wall.dmi b/icons/turf/walls/red_wall.dmi
new file mode 100644
index 000000000000..5eb26cf6a30e
Binary files /dev/null and b/icons/turf/walls/red_wall.dmi differ
diff --git a/icons/turf/walls/reinforced_rock.dmi b/icons/turf/walls/reinforced_rock.dmi
new file mode 100644
index 000000000000..f2621f5a8638
Binary files /dev/null and b/icons/turf/walls/reinforced_rock.dmi differ
diff --git a/icons/turf/walls/reinforced_wall.dmi b/icons/turf/walls/reinforced_wall.dmi
index a6f1bc206665..35d64734e149 100644
Binary files a/icons/turf/walls/reinforced_wall.dmi and b/icons/turf/walls/reinforced_wall.dmi differ
diff --git a/icons/turf/walls/wall.dmi b/icons/turf/walls/wall.dmi
index 5df919097cac..651227fe8de2 100644
Binary files a/icons/turf/walls/wall.dmi and b/icons/turf/walls/wall.dmi differ
diff --git a/yogstation.dme b/yogstation.dme
index 8c2967dc5e4c..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"
@@ -71,6 +72,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 +84,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,11 +208,11 @@
#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"
#include "code\__HELPERS\AStar.dm"
+#include "code\__HELPERS\bitflag_lists.dm"
#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\colors.dm"
#include "code\__HELPERS\config.dm"
@@ -250,6 +254,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"
@@ -457,6 +464,7 @@
#include "code\datums\ruins.dm"
#include "code\datums\saymode.dm"
#include "code\datums\shuttles.dm"
+#include "code\datums\signals.dm"
#include "code\datums\soullink.dm"
#include "code\datums\spawners_menu.dm"
#include "code\datums\verbs.dm"
@@ -512,7 +520,6 @@
#include "code\datums\components\chasm.dm"
#include "code\datums\components\construction.dm"
#include "code\datums\components\curse_of_hunger.dm"
-#include "code\datums\components\decal.dm"
#include "code\datums\components\earhealing.dm"
#include "code\datums\components\earprotection.dm"
#include "code\datums\components\echolocation.dm"
@@ -580,7 +587,6 @@
#include "code\datums\components\crafting\recipes.dm"
#include "code\datums\components\crafting\tailoring.dm"
#include "code\datums\components\crafting\weapons.dm"
-#include "code\datums\components\decals\blood.dm"
#include "code\datums\components\fantasy\_fantasy.dm"
#include "code\datums\components\fantasy\affix.dm"
#include "code\datums\components\fantasy\prefixes.dm"
@@ -674,7 +680,10 @@
#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\elements\decals\_decal.dm"
+#include "code\datums\elements\decals\blood.dm"
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"
#include "code\datums\helper_datums\icon_snapshot.dm"
@@ -816,7 +825,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 +846,12 @@
#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_initializing_EXPENSIVE.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/datums/components/crawl.dm b/yogstation/code/datums/components/crawl.dm
index ba56cffc8a2c..1a00c94bf495 100644
--- a/yogstation/code/datums/components/crawl.dm
+++ b/yogstation/code/datums/components/crawl.dm
@@ -75,7 +75,7 @@
qdel(holder)
holder = null
-/datum/component/crawl/RemoveComponent(del_holder=TRUE)
+/datum/component/crawl/ClearFromParent(del_holder=TRUE)
var/mob/living/M = parent
if(del_holder && holder)
M.forceMove(get_turf(holder))
diff --git a/yogstation/code/datums/components/storage/storage.dm b/yogstation/code/datums/components/storage/storage.dm
index 00fadaa8f31e..28360171756a 100644
--- a/yogstation/code/datums/components/storage/storage.dm
+++ b/yogstation/code/datums/components/storage/storage.dm
@@ -8,7 +8,7 @@
if (.)
SEND_SIGNAL(parent, COMSIG_STORAGE_REMOVED, AM, new_location)
-/datum/component/storage/RemoveComponent() // hey TG you dropped this
+/datum/component/storage/ClearFromParent() // hey TG you dropped this
UnregisterSignal(parent, COMSIG_CONTAINS_STORAGE)
UnregisterSignal(parent, COMSIG_IS_STORAGE_LOCKED)
UnregisterSignal(parent, COMSIG_TRY_STORAGE_SHOW)
@@ -43,12 +43,12 @@
UnregisterSignal(parent, COMSIG_CLICK_ALT)
UnregisterSignal(parent, COMSIG_MOUSEDROP_ONTO)
UnregisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO)
- . = ..()
+ return ..()
-/datum/component/storage/concrete/RemoveComponent()
+/datum/component/storage/concrete/ClearFromParent()
UnregisterSignal(parent, COMSIG_ATOM_CONTENTS_DEL)
UnregisterSignal(parent, COMSIG_OBJ_DECONSTRUCT)
- . = ..()
+ return ..()
// You know, TG created the component system to prevent exactly this problem.
// Turns out they did a shit job. Enjoy the literal copypaste.
diff --git a/yogstation/code/datums/components/walks.dm b/yogstation/code/datums/components/walks.dm
index c8affac3a981..8fb01a6a05fb 100644
--- a/yogstation/code/datums/components/walks.dm
+++ b/yogstation/code/datums/components/walks.dm
@@ -14,7 +14,7 @@
RegisterSignal(parent, COMSIG_MOB_CLIENT_PRE_MOVE, PROC_REF(handle_move))
ADD_TRAIT(parent, TRAIT_SILENT_FOOTSTEPS, WALK_COMPONENT_TRAIT)
-/datum/component/walk/RemoveComponent()
+/datum/component/walk/ClearFromParent()
REMOVE_TRAIT(parent, TRAIT_SILENT_FOOTSTEPS, WALK_COMPONENT_TRAIT)
return ..()
diff --git a/yogstation/code/game/objects/items/storage/backpack.dm b/yogstation/code/game/objects/items/storage/backpack.dm
index 9cc95f1eef4d..2034b9534bcf 100644
--- a/yogstation/code/game/objects/items/storage/backpack.dm
+++ b/yogstation/code/game/objects/items/storage/backpack.dm
@@ -44,7 +44,8 @@
var/obj/item/storage/backpack/holding/twin = new(loc)
var/datum/component/storage/old_other_storage = twin.GetComponent(/datum/component/storage)
- old_other_storage.RemoveComponent()
+ if(old_other_storage)
+ qdel(old_other_storage)
var/datum/component/storage/this_storage = GetComponent(/datum/component/storage)
var/datum/component/storage/twin_storage = twin.AddComponent(/datum/component/storage/bluespace/bag_of_holding, this_storage.master()) // add a slave storage component
twin_storage.allow_big_nesting = TRUE
@@ -85,7 +86,7 @@
var/obj/item/storage/backpack/holding/m_obj = new_master.parent
var/datum/component/storage/m_storage = m_obj.GetComponent(/datum/component/storage)
if(m_storage)
- m_storage.RemoveComponent()
+ qdel(m_storage)
m_storage = m_obj.AddComponent(m_obj.component_type)
m_storage.allow_big_nesting = TRUE
m_storage.max_w_class = WEIGHT_CLASS_GIGANTIC
@@ -95,7 +96,7 @@
slave.change_master(m_storage)
for(var/obj/item/I in src)
I.forceMove(m_obj)
- STR.RemoveComponent()
+ qdel(STR)
if(dump && get_turf(src))
for(var/obj/item/I in src)
I.forceMove(get_turf(src))
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
diff --git a/yogstation/code/modules/atmospherics/machinery/pipes/bluespace.dm b/yogstation/code/modules/atmospherics/machinery/pipes/bluespace.dm
index 8975aaba2ce2..cb2cc75134e0 100644
--- a/yogstation/code/modules/atmospherics/machinery/pipes/bluespace.dm
+++ b/yogstation/code/modules/atmospherics/machinery/pipes/bluespace.dm
@@ -46,15 +46,12 @@ GLOBAL_LIST_EMPTY(bluespace_pipe_networks)
/obj/machinery/atmospherics/pipe/bluespace/pipeline_expansion()
return ..() + GLOB.bluespace_pipe_networks[bluespace_network_name] - src
-/obj/machinery/atmospherics/pipe/bluespace/hide()
- update_appearance(UPDATE_ICON)
-
/obj/machinery/atmospherics/pipe/bluespace/update_icon(updates=ALL)
. = ..()
underlays.Cut()
var/turf/T = loc
- if(level != 2 && !!T.intact)
+ if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE)
plane = FLOOR_PLANE
return //no need to update the pipes if they aren't showing
plane = GAME_PLANE