diff --git a/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm b/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm index 9b0765f7f15..14b9fb6f249 100644 --- a/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm +++ b/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm @@ -324,13 +324,12 @@ /obj/structure/fluff/tram_rail{ dir = 1 }, -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_white"; - initial_id = "middle_part_hilbert" - }, /obj/machinery/door/window/survival_pod{ dir = 1 }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_white" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "hQ" = ( @@ -467,13 +466,13 @@ /turf/open/floor/glass, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "kK" = ( -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_white"; - initial_id = "middle_part_hilbert" - }, /obj/structure/window/reinforced/survival_pod{ dir = 4 }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_white" + }, +/obj/effect/landmark/lift_id/hilbert, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "kO" = ( @@ -664,20 +663,18 @@ /area/ruin/space/has_grav/powered/hilbertresearchfacility) "pE" = ( /obj/effect/landmark/tram/middle_part/hilbert, -/obj/structure/industrial_lift/tram/central{ - icon_state = "titanium_white"; - initial_id = "middle_part_hilbert"; - tram_id = "tram_hilbert" - }, -/obj/machinery/computer/tram_controls{ - dir = 8; - tram_id = "tram_hilbert" - }, /obj/machinery/light/floor{ brightness = 2; bulb_colour = "#deefff"; bulb_power = 0.6 }, +/obj/machinery/computer/tram_controls{ + dir = 8; + specific_lift_id = "tram_hilbert" + }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_white" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "pI" = ( @@ -1652,13 +1649,12 @@ /turf/open/floor/mineral/titanium/tiled/purple, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "Kv" = ( -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_white"; - initial_id = "middle_part_hilbert" - }, /obj/structure/window/reinforced/survival_pod{ dir = 8 }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_white" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "KC" = ( @@ -1666,27 +1662,25 @@ /area/ruin/space/has_grav/powered/hilbertresearchfacility) "KF" = ( /obj/structure/fluff/tram_rail, -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_purple"; - initial_id = "middle_part_hilbert" - }, -/obj/structure/shuttle/engine/propulsion/in_wall{ - dir = 8; - pixel_x = -32 - }, /obj/structure/window/reinforced/survival_pod{ dir = 8 }, /obj/structure/window/reinforced/survival_pod, +/obj/structure/shuttle/engine/propulsion/in_wall/tram{ + dir = 8; + pixel_x = -32 + }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_purple" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "KI" = ( /obj/structure/fluff/tram_rail, -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_white"; - initial_id = "middle_part_hilbert" - }, /obj/machinery/door/window/survival_pod, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_white" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "KJ" = ( @@ -1695,18 +1689,17 @@ /area/ruin/unpowered/no_grav) "KK" = ( /obj/structure/fluff/tram_rail, -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_purple"; - initial_id = "middle_part_hilbert" - }, -/obj/structure/shuttle/engine/propulsion/in_wall{ - dir = 4; - pixel_x = 32 - }, /obj/structure/window/reinforced/survival_pod{ dir = 4 }, /obj/structure/window/reinforced/survival_pod, +/obj/structure/shuttle/engine/propulsion/in_wall/tram{ + dir = 4; + pixel_x = 32 + }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_purple" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "KL" = ( @@ -1787,20 +1780,19 @@ /obj/structure/fluff/tram_rail{ dir = 1 }, -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_purple"; - initial_id = "middle_part_hilbert" - }, -/obj/structure/shuttle/engine/propulsion/in_wall{ - dir = 8; - pixel_x = -32 - }, /obj/structure/window/reinforced/survival_pod{ dir = 8 }, /obj/structure/window/reinforced/survival_pod{ dir = 1 }, +/obj/structure/shuttle/engine/propulsion/in_wall/tram{ + dir = 8; + pixel_x = -32 + }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_purple" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "MX" = ( @@ -1994,20 +1986,19 @@ /obj/structure/fluff/tram_rail{ dir = 1 }, -/obj/structure/industrial_lift/tram{ - icon_state = "titanium_purple"; - initial_id = "middle_part_hilbert" - }, -/obj/structure/shuttle/engine/propulsion/in_wall{ - dir = 4; - pixel_x = 32 - }, /obj/structure/window/reinforced/survival_pod{ dir = 4 }, /obj/structure/window/reinforced/survival_pod{ dir = 1 }, +/obj/structure/shuttle/engine/propulsion/in_wall/tram{ + dir = 4; + pixel_x = 32 + }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium_purple" + }, /turf/open/floor/engine, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "RE" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 578f9119595..31ef1f01aeb 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -2890,6 +2890,18 @@ /obj/item/circuitboard/machine/monkey_recycler, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"aYe" = ( +/obj{ + name = "---Merge conflict marker---" + }, +/obj/structure/table/reinforced, +/obj/item/hfr_box/body/fuel_input, +/obj/item/hfr_box/body/interface{ + pixel_x = 6; + pixel_y = 9 + }, +/turf/open/genturf, +/area/icemoon/underground/unexplored/rivers) "aYm" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -3261,6 +3273,22 @@ /obj/machinery/light/directional/north, /turf/open/floor/iron/dark, /area/station/security/execution/education) +"bdH" = ( +/obj{ + name = "---Merge conflict marker---" + }, +/obj/structure/table/reinforced, +/obj/item/stack/sheet/glass/fifty{ + pixel_x = 2; + pixel_y = 0 + }, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/cable_coil{ + pixel_x = -3; + pixel_y = 3 + }, +/turf/open/genturf, +/area/icemoon/underground/unexplored/rivers) "bdK" = ( /turf/open/floor/circuit, /area/station/ai_monitored/turret_protected/ai_upload) @@ -3273,10 +3301,11 @@ /turf/open/floor/plating/icemoon, /area/station/engineering/atmos) "bed" = ( -/obj/structure/industrial_lift{ - id = "publicElevator" - }, /obj/machinery/light/floor, +/obj/structure/industrial_lift, +/obj/effect/landmark/lift_id{ + specific_lift_id = "publicElevator" + }, /turf/open/openspace, /area/station/commons/storage/mining) "ben" = ( @@ -9830,6 +9859,24 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, /turf/closed/wall/r_wall, /area/station/engineering/supermatter) +"dfs" = ( +/obj{ + name = "---Merge conflict marker---" + }, +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 8; + id = "Atmospherics HFR Shutters"; + name = "Atmospherics HFR Shutters" + }, +/obj/effect/turf_decal/trimline/yellow/warning{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/door/firedoor/heavy, +/turf/closed/mineral/random/snow, +/area/icemoon/underground/explored) "dfz" = ( /obj/structure/closet/secure_closet/security/sec, /obj/structure/cable, @@ -13780,6 +13827,24 @@ /obj/structure/flora/bush/lavendergrass/style_random, /turf/open/floor/grass, /area/station/maintenance/starboard/aft) +"ete" = ( +/obj{ + name = "---Merge conflict marker---" + }, +/obj/machinery/atmospherics/components/binary/pump/off{ + dir = 1; + name = "Infiltrate to Exfiltrate" + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/loading_area{ + color = "#439C1E"; + dir = 1 + }, +/obj/effect/turf_decal/trimline/dark_red/warning{ + dir = 1 + }, +/turf/open/genturf, +/area/icemoon/underground/unexplored/rivers) "etw" = ( /obj/effect/turf_decal/stripes/white/line, /obj/effect/decal/cleanable/dirt, @@ -27984,6 +28049,24 @@ }, /turf/open/floor/iron/white/smooth_large, /area/station/medical/chemistry) +"iXW" = ( +/obj{ + name = "---Merge conflict marker---" + }, +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 8; + id = "Atmospherics HFR Shutters"; + name = "Atmospherics HFR Shutters" + }, +/obj/effect/turf_decal/trimline/yellow/warning{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/door/firedoor/heavy, +/turf/open/genturf, +/area/icemoon/underground/unexplored/rivers) "iYb" = ( /turf/closed/wall, /area/station/maintenance/central/greater) @@ -51007,6 +51090,17 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron, /area/station/commons/fitness) +"qnH" = ( +/obj{ + name = "---Merge conflict marker---" + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 8; + name = "Exfiltrate Port" + }, +/turf/open/genturf, +/area/icemoon/underground/unexplored/rivers) "qnU" = ( /obj/structure/extinguisher_cabinet/directional/south, /obj/effect/turf_decal/tile/blue{ @@ -66956,6 +67050,22 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) +"vxk" = ( +/obj{ + name = "---Merge conflict marker---" + }, +/obj/structure/table/reinforced, +/obj/item/hfr_box/body/waste_output{ + pixel_x = 5; + pixel_y = -1 + }, +/obj/item/hfr_box/body/moderator_input{ + pixel_x = -5; + pixel_y = 8 + }, +/obj/item/radio/intercom/directional/west, +/turf/open/genturf, +/area/icemoon/underground/unexplored/rivers) "vxx" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/effect/turf_decal/stripes/line{ @@ -177815,12 +177925,12 @@ nxM xMq tjo tjo +aYe +vxk tjo tjo tjo -tjo -tjo -tjo +bdH tjo tjo tjo @@ -179871,13 +179981,13 @@ qck ilN iDt iDt -xMq -tjo -tjo -tjo -tjo +dfs +iXW +iXW tjo tjo +iXW +iXW tjo tjo tjo @@ -181671,7 +181781,7 @@ gjq nfG iDt tjo -tjo +qnH tjo tjo tjo @@ -182186,7 +182296,7 @@ alM xMq xMq xMq -tjo +ete tjo tjo tjo diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 574a0da14e3..026682942b5 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -176,9 +176,7 @@ /obj/structure/industrial_lift/tram{ icon_state = "titanium_white" }, -/obj/machinery/door/window/right/directional/south{ - id_tag = "tramdoor" - }, +/obj/machinery/door/window/right/tram, /turf/open/openspace, /area/station/hallway/primary/tram/center) "adM" = ( @@ -2325,8 +2323,8 @@ /obj/structure/industrial_lift/tram{ icon_state = "plating" }, -/obj/structure/window/reinforced/shuttle, -/obj/structure/grille, +/obj/structure/grille/tram, +/obj/structure/window/reinforced/shuttle/tram, /turf/open/openspace, /area/station/hallway/primary/tram/center) "aZX" = ( @@ -22762,8 +22760,6 @@ /obj/structure/industrial_lift/tram{ icon_state = "plating" }, -/obj/structure/window/reinforced/shuttle, -/obj/structure/grille, /obj/effect/turf_decal/stripes{ dir = 1 }, @@ -22771,6 +22767,8 @@ /obj/effect/turf_decal/caution/stand_clear/red{ dir = 4 }, +/obj/structure/grille/tram, +/obj/structure/window/reinforced/shuttle/tram, /turf/open/floor/vault, /area/station/hallway/primary/tram/center) "hOh" = ( @@ -25395,11 +25393,11 @@ /obj/structure/industrial_lift/tram{ icon_state = "plating" }, -/obj/structure/window/reinforced/shuttle, -/obj/structure/grille, /obj/structure/fluff/tram_rail{ dir = 1 }, +/obj/structure/grille/tram, +/obj/structure/window/reinforced/shuttle/tram, /turf/open/openspace, /area/station/hallway/primary/tram/center) "iHr" = ( @@ -27210,7 +27208,7 @@ icon_state = "plating" }, /obj/structure/tramwall/titanium, -/obj/structure/shuttle/engine/propulsion/in_wall{ +/obj/structure/shuttle/engine/propulsion/in_wall/tram{ dir = 4; pixel_x = 32 }, @@ -31992,7 +31990,7 @@ icon_state = "plating" }, /obj/structure/tramwall/titanium, -/obj/structure/shuttle/engine/propulsion/in_wall{ +/obj/structure/shuttle/engine/propulsion/in_wall/tram{ dir = 8; pixel_x = -32 }, @@ -35020,9 +35018,7 @@ /obj/structure/industrial_lift/tram{ icon_state = "titanium_white" }, -/obj/machinery/door/window/left/directional/south{ - id_tag = "tramdoor" - }, +/obj/machinery/door/window/left/tram, /turf/open/openspace, /area/station/hallway/primary/tram/center) "lKo" = ( @@ -50120,6 +50116,11 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/secondary/service) +"qMs" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/lattice/catwalk, +/turf/open/floor/plating, +/area/station/maintenance/tram/mid) "qMx" = ( /obj/machinery/door/airlock/external{ name = "Port Docking Bay 3" @@ -50161,10 +50162,10 @@ dir = 1 }, /obj/effect/landmark/tram/middle_part, -/obj/structure/industrial_lift/tram/central{ +/obj/machinery/computer/tram_controls, +/obj/structure/industrial_lift/tram{ icon_state = "titanium" }, -/obj/machinery/computer/tram_controls, /turf/open/floor/vault, /area/station/hallway/primary/tram/center) "qNd" = ( @@ -57516,9 +57517,9 @@ /obj/structure/industrial_lift/tram{ icon_state = "plating" }, -/obj/structure/window/reinforced/shuttle, -/obj/structure/grille, /obj/structure/fluff/tram_rail, +/obj/structure/grille/tram, +/obj/structure/window/reinforced/shuttle/tram, /turf/open/openspace, /area/station/hallway/primary/tram/center) "trr" = ( @@ -60943,9 +60944,7 @@ /obj/structure/industrial_lift/tram{ icon_state = "titanium_white" }, -/obj/machinery/door/window/left/directional/north{ - id_tag = "tramdoor" - }, +/obj/machinery/door/window/left/tram/directional/north, /turf/open/openspace, /area/station/hallway/primary/tram/center) "uyA" = ( @@ -61085,6 +61084,17 @@ /obj/effect/turf_decal/trimline/red/filled/line, /turf/open/floor/iron, /area/station/security/prison) +"uBC" = ( +/obj/effect/turf_decal/stripes, +/obj/effect/turf_decal/stripes{ + dir = 1 + }, +/obj/structure/industrial_lift/tram{ + icon_state = "titanium" + }, +/obj/effect/landmark/lift_id, +/turf/open/floor/vault, +/area/station/hallway/primary/tram/center) "uBH" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 4 @@ -62327,8 +62337,6 @@ /obj/structure/industrial_lift/tram{ icon_state = "plating" }, -/obj/structure/window/reinforced/shuttle, -/obj/structure/grille, /obj/effect/turf_decal/stripes{ dir = 1 }, @@ -62336,6 +62344,8 @@ /obj/effect/turf_decal/caution/stand_clear/red{ dir = 8 }, +/obj/structure/grille/tram, +/obj/structure/window/reinforced/shuttle/tram, /turf/open/floor/vault, /area/station/hallway/primary/tram/center) "uXK" = ( @@ -65633,9 +65643,7 @@ /obj/structure/industrial_lift/tram{ icon_state = "titanium_white" }, -/obj/machinery/door/window/right/directional/north{ - id_tag = "tramdoor" - }, +/obj/machinery/door/window/right/tram/directional/north, /turf/open/openspace, /area/station/hallway/primary/tram/center) "wgJ" = ( @@ -103706,9 +103714,9 @@ rKD cMT iui mgX -mgX -mgX -mgX +qMs +qMs +qMs mgX rCM pcu @@ -168986,7 +168994,7 @@ dhw vSM wgz rPU -sOY +uBC biX adD vIq 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 653d4e6167b..6d946d09f88 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm @@ -45,6 +45,8 @@ #define COMSIG_ATOM_SMOOTHED_ICON "atom_smoothed_icon" ///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) +#define COMSIG_ATOM_ABSTRACT_ENTERED "atom_abstract_entered" /// Sent from the atom that just Entered src. From base of atom/Entered(): (/atom/destination, atom/old_loc, list/atom/old_locs) #define COMSIG_ATOM_ENTERING "atom_entering" ///from base of atom/Exit(): (/atom/movable/leaving, direction) @@ -52,6 +54,8 @@ #define COMPONENT_ATOM_BLOCK_EXIT (1<<0) ///from base of atom/Exited(): (atom/movable/gone, direction) #define COMSIG_ATOM_EXITED "atom_exited" +///from base of atom/movable/Moved(): (atom/movable/gone, direction) +#define COMSIG_ATOM_ABSTRACT_EXITED "atom_abstract_exited" ///from base of atom/Bumped(): (/atom/movable) #define COMSIG_ATOM_BUMPED "atom_bumped" ///from base of atom/handle_atom_del(): (atom/deleted) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index f0503bbff73..498daf9f95c 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -59,6 +59,9 @@ /// from /obj/machinery/atmospherics/components/unary/cryo_cell/set_on(bool): (on) #define COMSIG_CRYO_SET_ON "cryo_set_on" +/// from /obj/proc/make_unfrozen() +#define COMSIG_OBJ_UNFREEZE "obj_unfreeze" + // /obj/machinery/atmospherics/components/binary/valve signals /// from /obj/machinery/atmospherics/components/binary/valve/toggle(): (on) diff --git a/code/__DEFINES/important_recursive_contents.dm b/code/__DEFINES/important_recursive_contents.dm index 24cca713c65..79abb67d183 100644 --- a/code/__DEFINES/important_recursive_contents.dm +++ b/code/__DEFINES/important_recursive_contents.dm @@ -5,3 +5,5 @@ ///the client mobs channel of the important_recursive_contents list, everything in here will be a mob with an attached client ///this is given to both a clients mob, and a clients eye, both point to the clients mob #define RECURSIVE_CONTENTS_CLIENT_MOBS "recursive_contents_client_mobs" +///the parent of storage components currently shown to some client mob get this. gets removed when nothing is viewing the parent +#define RECURSIVE_CONTENTS_ACTIVE_STORAGE "recursive_contents_active_storage" diff --git a/code/__DEFINES/industrial_lift.dm b/code/__DEFINES/industrial_lift.dm new file mode 100644 index 00000000000..9d3052c01f0 --- /dev/null +++ b/code/__DEFINES/industrial_lift.dm @@ -0,0 +1,20 @@ +//Booleans in arguments are confusing, so I made them defines. +///the lift's controls are currently locked from user input +#define LIFT_PLATFORM_LOCKED 1 +///the lift's controls are currently unlocked so user's can direct it +#define LIFT_PLATFORM_UNLOCKED 0 + +//lift_id's +///basic lift_id, goes up and down +#define BASIC_LIFT_ID "base" +///tram lift_id, goes left and right or north and south. maybe one day be able to turn and go up/down as well +#define TRAM_LIFT_ID "tram" +///debug lift_id +#define DEBUG_LIFT_ID "debug" + + +//specific_lift_id's +///the specific_lift_id of the main station tram landmark for tramstation that spawns roundstart. +#define MAIN_STATION_TRAM "main station tram" +///the specific_lift_id of the tram on the hilbert research station +#define HILBERT_TRAM "tram_hilbert" diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index 608f85660c2..e6ffcbae79d 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -9,7 +9,6 @@ #define ON_BLUEPRINTS (1<<5) //Are we visible on the station blueprints at roundstart? #define UNIQUE_RENAME (1<<6) // can you customize the description/name of the thing? #define USES_TGUI (1<<7) //put on things that use tgui on ui_interact instead of custom/old UI. -#define FROZEN (1<<8) #define BLOCK_Z_OUT_DOWN (1<<9) // Should this object block z falling from loc? #define BLOCK_Z_OUT_UP (1<<10) // Should this object block z uprise from loc? #define BLOCK_Z_IN_DOWN (1<<11) // Should this object block z falling from above? diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index ae00cf0ed4a..a6b2e3b85e6 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -488,6 +488,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_AREA_SENSITIVE "area-sensitive" ///every hearing sensitive atom has this trait #define TRAIT_HEARING_SENSITIVE "hearing_sensitive" +///every object that is currently the active storage of some client mob has this trait +#define TRAIT_ACTIVE_STORAGE "active_storage" /// Climbable trait, given and taken by the climbable element when added or removed. Exists to be easily checked via HAS_TRAIT(). #define TRAIT_CLIMBABLE "trait_climbable" @@ -895,5 +897,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Ignores body_parts_covered during the add_fingerprint() proc. Works both on the person and the item in the glove slot. #define TRAIT_FINGERPRINT_PASSTHROUGH "fingerprint_passthrough" +/// this object has been frozen +#define TRAIT_FROZEN "frozen" + /// Currently fishing #define TRAIT_GONE_FISHING "fishing" diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 8ef3ecb07f8..9b60641f864 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1041,23 +1041,9 @@ GLOBAL_LIST_EMPTY(friendly_animal_types) GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0,0,0))) -/obj/proc/make_frozen_visual() - // Used to make the frozen item visuals for Freon. - if(resistance_flags & FREEZE_PROOF) - return - if(!(obj_flags & FROZEN)) - name = "frozen [name]" - add_atom_colour(GLOB.freon_color_matrix, TEMPORARY_COLOUR_PRIORITY) - alpha -= 25 - obj_flags |= FROZEN - //Assumes already frozed /obj/proc/make_unfrozen() - if(obj_flags & FROZEN) - name = replacetext(name, "frozen ", "") - remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, GLOB.freon_color_matrix) - alpha += 25 - obj_flags &= ~FROZEN + SEND_SIGNAL(src, COMSIG_OBJ_UNFREEZE) /// generates a filename for a given asset. /// like generate_asset_name(), except returns the rsc reference and the rsc file hash as well as the asset name (sans extension) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 590962dc358..85b228e3178 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -274,7 +274,6 @@ DEFINE_BITFIELD(obj_flags, list( "CAN_BE_HIT" = CAN_BE_HIT, "DANGEROUS_POSSESSION" = DANGEROUS_POSSESSION, "EMAGGED" = EMAGGED, - "FROZEN" = FROZEN, "IN_USE" = IN_USE, "NO_BUILD" = NO_BUILD, "ON_BLUEPRINTS" = ON_BLUEPRINTS, diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 1f7de75b954..1ba022063ec 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -213,11 +213,13 @@ for(var/atom/target in checking) // will filter out nulls if(closed[target] || isarea(target)) // avoid infinity situations continue - closed[target] = TRUE + if(isturf(target) || isturf(target.loc) || (target in direct_access) || (ismovable(target) && target.flags_1 & IS_ONTOP_1)) //Directly accessible atoms if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks return TRUE + closed[target] = TRUE + if (!target.loc) continue diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm index 8c235bcefb0..9c03ef41b62 100644 --- a/code/controllers/subsystem/explosions.dm +++ b/code/controllers/subsystem/explosions.dm @@ -549,18 +549,18 @@ SUBSYSTEM_DEF(explosions) var/base_shake_amount = sqrt(near_distance / (distance + 1)) if(distance <= round(near_distance + world.view - 2, 1)) // If you are close enough to see the effects of the explosion first-hand (ignoring walls) - listener.playsound_local(epicenter, null, 100, TRUE, frequency, S = near_sound) + listener.playsound_local(epicenter, null, 100, TRUE, frequency, sound_to_use = near_sound) if(base_shake_amount > 0) shake_camera(listener, NEAR_SHAKE_DURATION, clamp(base_shake_amount, 0, NEAR_SHAKE_CAP)) else if(distance < far_distance) // You can hear a far explosion if you are outside the blast radius. Small explosions shouldn't be heard throughout the station. var/far_volume = clamp(far_distance / 2, FAR_LOWER, FAR_UPPER) if(creaking) - listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = creaking_sound, distance_multiplier = 0) + listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, sound_to_use = creaking_sound, distance_multiplier = 0) else if(prob(FAR_SOUND_PROB)) // Sound variety during meteor storm/tesloose/other bad event - listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = far_sound, distance_multiplier = 0) + listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, sound_to_use = far_sound, distance_multiplier = 0) else - listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = echo_sound, distance_multiplier = 0) + listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, sound_to_use = echo_sound, distance_multiplier = 0) if(base_shake_amount || quake_factor) base_shake_amount = max(base_shake_amount, quake_factor * 3, 0) // Devastating explosions rock the station and ground @@ -573,7 +573,7 @@ SUBSYSTEM_DEF(explosions) shake_camera(listener, FAR_SHAKE_DURATION, clamp(quake_factor / 4, 0, FAR_SHAKE_CAP)) else echo_volume = 40 - listener.playsound_local(epicenter, null, echo_volume, TRUE, frequency, S = echo_sound, distance_multiplier = 0) + listener.playsound_local(epicenter, null, echo_volume, TRUE, frequency, sound_to_use = echo_sound, distance_multiplier = 0) if(creaking) // 5 seconds after the bang, the station begins to creak addtimer(CALLBACK(listener, /mob/proc/playsound_local, epicenter, null, rand(FREQ_LOWER, FREQ_UPPER), TRUE, frequency, null, null, FALSE, hull_creaking_sound, 0), CREAK_DELAY) diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 58fbf7ab952..7479845c2e9 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -40,14 +40,21 @@ SUBSYSTEM_DEF(mapping) // Z-manager stuff var/station_start // should only be used for maploading-related tasks var/space_levels_so_far = 0 - ///list of all the z level datums created representing the z levels in the world - var/list/z_list + ///list of all z level datums in the order of their z (z level 1 is at index 1, etc.) + var/list/datum/space_level/z_list + ///list of all z level indices that form multiz connections and whether theyre linked up or down + ///list of lists, inner lists are of the form: list("up or down link direction" = TRUE) + var/list/multiz_levels = list() var/datum/space_level/transit var/datum/space_level/empty_space var/num_of_res_levels = 1 /// True when in the process of adding a new Z-level, global locking var/adding_new_zlevel = FALSE + ///shows the default gravity value for each z level. recalculated when gravity generators change. + ///associative list of the form: list("[z level num]" = max generator gravity in that z level OR the gravity level trait) + var/list/gravity_by_z_level = list() + /datum/controller/subsystem/mapping/New() ..() #ifdef FORCE_MAP @@ -101,8 +108,48 @@ SUBSYSTEM_DEF(mapping) generate_station_area_list() initialize_reserved_level(transit.z_value) SSticker.OnRoundstart(CALLBACK(src, .proc/spawn_maintenance_loot)) + generate_z_level_linkages() + calculate_default_z_level_gravities() + return ..() +/datum/controller/subsystem/mapping/proc/calculate_default_z_level_gravities() + for(var/z_level in 1 to length(z_list)) + calculate_z_level_gravity(z_level) + +/datum/controller/subsystem/mapping/proc/generate_z_level_linkages() + for(var/z_level in 1 to length(z_list)) + generate_linkages_for_z_level(z_level) + +/datum/controller/subsystem/mapping/proc/generate_linkages_for_z_level(z_level) + if(!isnum(z_level) || z_level <= 0) + return FALSE + + if(multiz_levels.len < z_level) + multiz_levels.len = z_level + + var/linked_down = level_trait(z_level, ZTRAIT_DOWN) + var/linked_up = level_trait(z_level, ZTRAIT_UP) + multiz_levels[z_level] = list() + if(linked_down) + multiz_levels[z_level]["[DOWN]"] = TRUE + if(linked_up) + multiz_levels[z_level]["[UP]"] = TRUE + +/datum/controller/subsystem/mapping/proc/calculate_z_level_gravity(z_level_number) + if(!isnum(z_level_number) || z_level_number < 1) + return FALSE + + var/max_gravity = 0 + + for(var/obj/machinery/gravity_generator/main/grav_gen as anything in GLOB.gravity_generators["[z_level_number]"]) + max_gravity = max(grav_gen.setting, max_gravity) + + max_gravity = max_gravity || level_trait(z_level_number, ZTRAIT_GRAVITY) || 0//just to make sure no nulls + gravity_by_z_level["[z_level_number]"] = max_gravity + return max_gravity + + /** * ##setup_ruins * @@ -215,6 +262,7 @@ Used by the AI doomsday and the self-destruct nuke. clearing_reserved_turfs = SSmapping.clearing_reserved_turfs z_list = SSmapping.z_list + multiz_levels = SSmapping.multiz_levels #define INIT_ANNOUNCE(X) to_chat(world, span_boldannounce("[X]")); log_world(X) /datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits, silent = FALSE) diff --git a/code/controllers/subsystem/processing/tramprocess.dm b/code/controllers/subsystem/processing/tramprocess.dm index f72ba30ddee..b497cce8b8c 100644 --- a/code/controllers/subsystem/processing/tramprocess.dm +++ b/code/controllers/subsystem/processing/tramprocess.dm @@ -1,5 +1,15 @@ PROCESSING_SUBSYSTEM_DEF(tramprocess) name = "Tram Process" - wait = 1 + wait = 0.5 /// only used on maps with trams, so only enabled by such. can_fire = FALSE + + ///how much time a tram can take per movement before we notify admins and slow down the tram. in milliseconds + var/max_time = 15 + + ///how many times the tram can move costing over max_time milliseconds before it gets slowed down + var/max_exceeding_moves = 5 + + ///how many times the tram can move costing less than half max_time milliseconds before we speed it back up again. + ///is only used if the tram has been slowed down for exceeding max_time + var/max_cheap_moves = 5 diff --git a/code/controllers/subsystem/spatial_gridmap.dm b/code/controllers/subsystem/spatial_gridmap.dm index 0608140bb7c..b9dc0988840 100644 --- a/code/controllers/subsystem/spatial_gridmap.dm +++ b/code/controllers/subsystem/spatial_gridmap.dm @@ -171,53 +171,6 @@ SUBSYSTEM_DEF(spatial_grid) var/datum/spatial_grid_cell/cell = new(x, y, z_level.z_value) new_cell_grid[y] += cell -///creates number_to_generate new oranges_ear's and adds them to the subsystems list of ears. -///i really fucking hope this never gets called after init :clueless: -/datum/controller/subsystem/spatial_grid/proc/pregenerate_more_oranges_ears(number_to_generate) - for(var/new_ear in 1 to number_to_generate) - pregenerated_oranges_ears += new/mob/oranges_ear(null) - - number_of_oranges_ears = length(pregenerated_oranges_ears) - -///allocate one [/mob/oranges_ear] mob per turf containing atoms_that_need_ears and give them a reference to every listed atom in their turf. -///if an oranges_ear is allocated to a turf that already has an oranges_ear then the second one fails to allocate (and gives the existing one the atom it was assigned to) -/datum/controller/subsystem/spatial_grid/proc/assign_oranges_ears(list/atoms_that_need_ears) - var/input_length = length(atoms_that_need_ears) - - if(input_length > number_of_oranges_ears) - stack_trace("somehow, for some reason, more than the preset generated number of oranges ears was requested. thats fucking [number_of_oranges_ears]. this is not good that should literally never happen") - pregenerate_more_oranges_ears(input_length - number_of_oranges_ears)//im still gonna DO IT but ill complain about it - - . = list() - - ///the next unallocated /mob/oranges_ear that we try to allocate to assigned_atom's turf - var/mob/oranges_ear/current_ear - ///the next atom in atoms_that_need_ears an ear assigned to it - var/atom/assigned_atom - ///the turf loc of the current assigned_atom. turfs are used to track oranges_ears already assigned to one location so we dont allocate more than one - ///because allocating more than one oranges_ear to a given loc wastes view iterations - var/turf/turf_loc - - for(var/current_ear_index in 1 to input_length) - assigned_atom = atoms_that_need_ears[current_ear_index] - - turf_loc = get_turf(assigned_atom) - if(!turf_loc) - continue - - current_ear = pregenerated_oranges_ears[current_ear_index] - - if(turf_loc.assigned_oranges_ear) - turf_loc.assigned_oranges_ear.references += assigned_atom - continue //if theres already an oranges_ear mob at assigned_movable's turf we give assigned_movable to it instead and dont allocate ourselves - - current_ear.references += assigned_atom - - current_ear.loc = turf_loc //normally this is bad, but since this is meant to be as fast as possible we literally just need to exist there for view() to see us - turf_loc.assigned_oranges_ear = current_ear - - . += current_ear - ///adds cells to the grid for every z level when world.maxx or world.maxy is expanded after this subsystem is initialized. hopefully this is never needed. ///because i never tested this. /datum/controller/subsystem/spatial_grid/proc/after_world_bounds_expanded(datum/controller/subsystem/processing/dcs/fucking_dcs, has_expanded_world_maxx, has_expanded_world_maxy) @@ -278,10 +231,6 @@ SUBSYSTEM_DEF(spatial_grid) . = list() - //cache for sanic speeds - var/cells_on_y_axis = src.cells_on_y_axis - var/cells_on_x_axis = src.cells_on_x_axis - //technically THIS list only contains lists, but inside those lists are grid cell datums and we can go without a SINGLE var init if we do this var/list/datum/spatial_grid_cell/grid_level = grids_by_z_level[center_turf.z] @@ -458,6 +407,8 @@ SUBSYSTEM_DEF(spatial_grid) GRID_CELL_SET(intersecting_cell.atmos_contents, new_target) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_ATMOS), new_target) + return intersecting_cell + /** * find the spatial map cell that target used to belong to, then remove the target (and sometimes it's important_recusive_contents) from it. * make sure to provide the turf old_target used to be "in" @@ -584,6 +535,53 @@ SUBSYSTEM_DEF(spatial_grid) message_admins(cell_coords) message_admins("[src] is supposed to only be contained in the cell at indexes ([real_cell.cell_x], [real_cell.cell_y], [real_cell.cell_z]). but is contained at the cells at [cell_coords]") +///creates number_to_generate new oranges_ear's and adds them to the subsystems list of ears. +///i really fucking hope this never gets called after init :clueless: +/datum/controller/subsystem/spatial_grid/proc/pregenerate_more_oranges_ears(number_to_generate) + for(var/new_ear in 1 to number_to_generate) + pregenerated_oranges_ears += new/mob/oranges_ear(null) + + number_of_oranges_ears = length(pregenerated_oranges_ears) + +///allocate one [/mob/oranges_ear] mob per turf containing atoms_that_need_ears and give them a reference to every listed atom in their turf. +///if an oranges_ear is allocated to a turf that already has an oranges_ear then the second one fails to allocate (and gives the existing one the atom it was assigned to) +/datum/controller/subsystem/spatial_grid/proc/assign_oranges_ears(list/atoms_that_need_ears) + var/input_length = length(atoms_that_need_ears) + + if(input_length > number_of_oranges_ears) + stack_trace("somehow, for some reason, more than the preset generated number of oranges ears was requested. thats fucking [number_of_oranges_ears]. this is not good that should literally never happen") + pregenerate_more_oranges_ears(input_length - number_of_oranges_ears)//im still gonna DO IT but ill complain about it + + . = list() + + ///the next unallocated /mob/oranges_ear that we try to allocate to assigned_atom's turf + var/mob/oranges_ear/current_ear + ///the next atom in atoms_that_need_ears an ear assigned to it + var/atom/assigned_atom + ///the turf loc of the current assigned_atom. turfs are used to track oranges_ears already assigned to one location so we dont allocate more than one + ///because allocating more than one oranges_ear to a given loc wastes view iterations + var/turf/turf_loc + + for(var/current_ear_index in 1 to input_length) + assigned_atom = atoms_that_need_ears[current_ear_index] + + turf_loc = get_turf(assigned_atom) + if(!turf_loc) + continue + + current_ear = pregenerated_oranges_ears[current_ear_index] + + if(turf_loc.assigned_oranges_ear) + turf_loc.assigned_oranges_ear.references += assigned_atom + continue //if theres already an oranges_ear mob at assigned_movable's turf we give assigned_movable to it instead and dont allocate ourselves + + current_ear.references += assigned_atom + + current_ear.loc = turf_loc //normally this is bad, but since this is meant to be as fast as possible we literally just need to exist there for view() to see us + turf_loc.assigned_oranges_ear = current_ear + + . += current_ear + ///debug proc for finding how full the cells of src's z level are /atom/proc/find_grid_statistics_for_z_level(insert_clients = 0) var/raw_clients = 0 diff --git a/code/datums/components/bloodysoles.dm b/code/datums/components/bloodysoles.dm index 9268385775f..385f177505e 100644 --- a/code/datums/components/bloodysoles.dm +++ b/code/datums/components/bloodysoles.dm @@ -53,19 +53,42 @@ var/obj/item/parent_item = parent parent_item.update_slot_icon() + +/datum/component/bloodysoles/proc/reset_bloody_shoes() + bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0) + on_changed_bloody_shoes(BLOOD_STATE_NOT_BLOODY) + +///lowers bloody_shoes[index] by adjust_by +/datum/component/bloodysoles/proc/adjust_bloody_shoes(index, adjust_by) + bloody_shoes[index] = max(bloody_shoes[index] - adjust_by, 0) + on_changed_bloody_shoes() + +/datum/component/bloodysoles/proc/set_bloody_shoes(index, new_value) + bloody_shoes[index] = new_value + on_changed_bloody_shoes(index) + +///called whenever the value of bloody_soles changes +/datum/component/bloodysoles/proc/on_changed_bloody_shoes(index) + if(index && index != last_blood_state) + last_blood_state = index + if(!wielder) + return + if(bloody_shoes[last_blood_state] <= BLOOD_FOOTPRINTS_MIN * 2)//need twice that amount to make footprints + UnregisterSignal(wielder, COMSIG_MOVABLE_MOVED) + else + RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, .proc/on_moved, override = TRUE) + /** * Run to equally share the blood between us and a decal */ /datum/component/bloodysoles/proc/share_blood(obj/effect/decal/cleanable/pool) - last_blood_state = pool.blood_state - // Share the blood between our boots and the blood pool - var/total_bloodiness = pool.bloodiness + bloody_shoes[last_blood_state] + var/total_bloodiness = pool.bloodiness + bloody_shoes[pool.blood_state] // We can however be limited by how much blood we can hold var/new_our_bloodiness = min(BLOOD_ITEM_MAX, total_bloodiness / 2) - bloody_shoes[last_blood_state] = new_our_bloodiness + set_bloody_shoes(pool.blood_state, new_our_bloodiness) pool.bloodiness = total_bloodiness - new_our_bloodiness // Give the pool the remaining blood incase we were limited if(HAS_TRAIT(parent_atom, TRAIT_LIGHT_STEP)) //the character is agile enough to don't mess their clothing and hands just from one blood splatter at floor @@ -105,7 +128,8 @@ equipped_slot = slot wielder = equipper - RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, .proc/on_moved) + if(bloody_shoes[last_blood_state] > BLOOD_FOOTPRINTS_MIN * 2) + RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, .proc/on_moved) RegisterSignal(wielder, COMSIG_STEP_ON_BLOOD, .proc/on_step_blood) /** @@ -147,7 +171,7 @@ oldLocFP.update_appearance() else if(find_pool_by_blood_state(oldLocTurf)) // No footprints in the tile we left, but there was some other blood pool there. Add exit footprints on it - bloody_shoes[last_blood_state] -= half_our_blood + adjust_bloody_shoes(last_blood_state, half_our_blood) update_icon() oldLocFP = new(oldLocTurf) @@ -167,7 +191,7 @@ // Create new footprints if(half_our_blood >= BLOOD_FOOTPRINTS_MIN) - bloody_shoes[last_blood_state] -= half_our_blood + adjust_bloody_shoes(last_blood_state, half_our_blood) update_icon() var/obj/effect/decal/cleanable/blood/footprints/FP = new(get_turf(parent_atom)) @@ -213,8 +237,7 @@ if(!(clean_types & CLEAN_TYPE_BLOOD) || last_blood_state == BLOOD_STATE_NOT_BLOODY) return NONE - bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0) - last_blood_state = BLOOD_STATE_NOT_BLOODY + reset_bloody_shoes() update_icon() return COMPONENT_CLEANED @@ -233,7 +256,6 @@ if(!bloody_feet) bloody_feet = mutable_appearance('icons/effects/blood.dmi', "shoeblood", SHOES_LAYER) RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/on_clean) - RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_moved) RegisterSignal(parent, COMSIG_STEP_ON_BLOOD, .proc/on_step_blood) RegisterSignal(parent, COMSIG_CARBON_UNEQUIP_SHOECOVER, .proc/unequip_shoecover) RegisterSignal(parent, COMSIG_CARBON_EQUIP_SHOECOVER, .proc/equip_shoecover) diff --git a/code/datums/components/drift.dm b/code/datums/components/drift.dm index 4d509026a44..d0e4bc6441f 100644 --- a/code/datums/components/drift.dm +++ b/code/datums/components/drift.dm @@ -114,7 +114,6 @@ return var/atom/movable/movable_parent = parent - movable_parent.inertia_moving = FALSE movable_parent.setDir(old_dir) if(movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE)) glide_to_halt(visual_delay) @@ -137,7 +136,7 @@ if(!isturf(movable_parent.loc)) qdel(src) return - if(movable_parent.inertia_moving) //This'll be handled elsewhere + if(movable_parent.inertia_moving) return if(!movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE)) return diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm index 9fd0e0a4f5a..67290d4c58c 100644 --- a/code/datums/components/overlay_lighting.dm +++ b/code/datums/components/overlay_lighting.dm @@ -111,7 +111,6 @@ . = ..() if(directional) RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/on_parent_dir_change) - RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_parent_moved) RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_RANGE, .proc/set_range) RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_POWER, .proc/set_power) RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_COLOR, .proc/set_color) @@ -119,6 +118,7 @@ RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, .proc/on_light_flags_change) RegisterSignal(parent, COMSIG_ATOM_USED_IN_CRAFT, .proc/on_parent_crafted) RegisterSignal(parent, COMSIG_LIGHT_EATER_QUEUE, .proc/on_light_eater) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_parent_moved) var/atom/movable/movable_parent = parent if(movable_parent.light_flags & LIGHT_ATTACHED) overlay_lighting_flags |= LIGHTING_ATTACHED @@ -245,8 +245,9 @@ return if(new_holder != parent && new_holder != parent_attached_to) RegisterSignal(new_holder, COMSIG_PARENT_QDELETING, .proc/on_holder_qdel) - RegisterSignal(new_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved) RegisterSignal(new_holder, COMSIG_LIGHT_EATER_QUEUE, .proc/on_light_eater) + if(overlay_lighting_flags & LIGHTING_ON) + RegisterSignal(new_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved) if(directional) RegisterSignal(new_holder, COMSIG_ATOM_DIR_CHANGE, .proc/on_holder_dir_change) set_direction(new_holder.dir) @@ -423,6 +424,8 @@ cast_directional_light() add_dynamic_lumi() overlay_lighting_flags |= LIGHTING_ON + if(current_holder && current_holder != parent && current_holder != parent_attached_to) + RegisterSignal(current_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved) get_new_turfs() @@ -433,6 +436,8 @@ if(current_holder) remove_dynamic_lumi() overlay_lighting_flags &= ~LIGHTING_ON + if(current_holder) + UnregisterSignal(current_holder, COMSIG_MOVABLE_MOVED) clean_old_turfs() diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index acc8c8d2a0c..0c40efa7dde 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -4,45 +4,66 @@ /datum/component/storage dupe_mode = COMPONENT_DUPE_UNIQUE - var/datum/component/storage/concrete/master //If not null, all actions act on master and this is just an access point. + ///If not null, all actions act on master and this is just an access point. + var/datum/component/storage/concrete/master - var/list/can_hold //if this is set, only items, and their children, will fit - var/list/cant_hold //if this is set, items, and their children, won't fit - var/list/exception_hold //if set, these items will be the exception to the max size of object that can fit. + ///if this is set, only items, and their children, will fit + var/list/can_hold + ///if this is set, items, and their children, won't fit + var/list/cant_hold + ///if set, these items will be the exception to the max size of object that can fit. + var/list/exception_hold /// If set can only contain stuff with this single trait present. var/list/can_hold_trait var/can_hold_description - var/list/mob/is_using //lazy list of mobs looking at the contents of this storage. + ///lazy list of mobs looking at the contents of this storage. + var/list/mob/is_using - var/locked = FALSE //when locked nothing can see inside or use it. + ///when locked nothing can see inside or use it. + var/locked = FALSE - var/max_w_class = WEIGHT_CLASS_SMALL //max size of objects that will fit. - var/max_combined_w_class = 14 //max combined sizes of objects that will fit. - var/max_items = 7 //max number of objects that will fit. + ///max size of objects that will fit. + var/max_w_class = WEIGHT_CLASS_SMALL + ///max combined sizes of objects that will fit. + var/max_combined_w_class = 14 + ///max number of objects that will fit. + var/max_items = 7 var/emp_shielded = FALSE - var/silent = FALSE //whether this makes a message when things are put in. - var/click_gather = FALSE //whether this can be clicked on items to pick it up rather than the other way around. - var/rustle_sound = TRUE //play rustle sound on interact. - var/allow_quick_empty = FALSE //allow empty verb which allows dumping on the floor of everything inside quickly. - var/allow_quick_gather = FALSE //allow toggle mob verb which toggles collecting all items from a tile. + ///whether this makes a message when things are put in. + var/silent = FALSE + ///whether this can be clicked on items to pick it up rather than the other way around. + var/click_gather = FALSE + ///play rustle sound on interact. + var/rustle_sound = TRUE + ///allow empty verb which allows dumping on the floor of everything inside quickly. + var/allow_quick_empty = FALSE + ///allow toggle mob verb which toggles collecting all items from a tile. + var/allow_quick_gather = FALSE var/collection_mode = COLLECT_EVERYTHING - var/insert_preposition = "in" //you put things "in" a bag, but "on" a tray. + ///you put things "in" a bag, but "on" a tray. + var/insert_preposition = "in" - var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number. + ///stack things of the same type and show as a single object with a number. + var/display_numerical_stacking = FALSE - var/atom/movable/screen/storage/boxes //storage display object - var/atom/movable/screen/close/closer //close button object + ///storage display object + var/atom/movable/screen/storage/boxes + ///close button object + var/atom/movable/screen/close/closer - var/allow_big_nesting = FALSE //allow storage objects of the same or greater size. + ///allow storage objects of the same or greater size. + var/allow_big_nesting = FALSE - var/attack_hand_interact = TRUE //interact on attack hand. - var/quickdraw = FALSE //altclick interact + ///interact on attack hand. + var/attack_hand_interact = TRUE + ///altclick interact + var/quickdraw = FALSE var/datum/action/item_action/storage_gather_mode/modeswitch_action @@ -420,6 +441,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) M.client.screen |= closer M.client.screen |= real_location.contents M.set_active_storage(src) + if(ismovable(real_location)) + var/atom/movable/movable_loc = real_location + movable_loc.become_active_storage(src) LAZYOR(is_using, M) RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/mob_deleted) return TRUE @@ -437,6 +461,10 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) if(!M.client) return TRUE var/atom/real_location = real_location() + if(!length(is_using) && ismovable(real_location)) + var/atom/movable/movable_loc = real_location + movable_loc.lose_active_storage(src) + M.client.screen -= boxes M.client.screen -= closer M.client.screen -= real_location.contents diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm index 594ab849e03..7b84f1dabb7 100644 --- a/code/datums/components/wet_floor.dm +++ b/code/datums/components/wet_floor.dm @@ -127,8 +127,7 @@ decrease = max(0, decrease) if((is_wet() & TURF_WET_ICE) && t > T0C) //Ice melts into water! for(var/obj/O in T.contents) - if(O.obj_flags & FROZEN) - O.make_unfrozen() + O.make_unfrozen() add_wet(TURF_WET_WATER, max_time_left()) dry(null, TURF_WET_ICE) dry(null, ALL, FALSE, decrease) diff --git a/code/datums/elements/_element.dm b/code/datums/elements/_element.dm index 30bd98a3268..204a2742011 100644 --- a/code/datums/elements/_element.dm +++ b/code/datums/elements/_element.dm @@ -32,9 +32,9 @@ /// Deactivates the functionality defines by the element on the given datum /datum/element/proc/Detach(datum/source, ...) SIGNAL_HANDLER + SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src) - SHOULD_CALL_PARENT(TRUE) UnregisterSignal(source, COMSIG_PARENT_QDELETING) /datum/element/Destroy(force) diff --git a/code/datums/elements/atmos_sensitive.dm b/code/datums/elements/atmos_sensitive.dm index ca676327e26..c7dd4cf2f7a 100644 --- a/code/datums/elements/atmos_sensitive.dm +++ b/code/datums/elements/atmos_sensitive.dm @@ -22,6 +22,7 @@ /datum/element/atmos_sensitive/Detach(datum/source) var/atom/us = source us.RemoveElement(/datum/element/connect_loc, pass_on) + UnregisterSignal(source, COMSIG_MOVABLE_MOVED) if(us.flags_1 & ATMOS_IS_PROCESSING_1) us.atmos_end() SSair.atom_process -= us diff --git a/code/datums/elements/chewable.dm b/code/datums/elements/chewable.dm index 91e9a0ddac9..a5e10e26d8d 100644 --- a/code/datums/elements/chewable.dm +++ b/code/datums/elements/chewable.dm @@ -30,6 +30,7 @@ RegisterSignal(target, COMSIG_ITEM_EQUIPPED, .proc/on_equipped) /datum/element/chewable/Detach(datum/source, force) + . = ..() processing -= source UnregisterSignal(source, list(COMSIG_ITEM_DROPPED, COMSIG_ITEM_EQUIPPED)) diff --git a/code/datums/elements/footstep.dm b/code/datums/elements/footstep.dm index a529025c967..b9a1ef5df20 100644 --- a/code/datums/elements/footstep.dm +++ b/code/datums/elements/footstep.dm @@ -87,7 +87,7 @@ if(steps % 2) return - if(steps != 0 && !source.has_gravity(turf)) // don't need to step as often when you hop around + if(steps != 0 && !source.has_gravity()) // don't need to step as often when you hop around return return turf @@ -117,10 +117,10 @@ return playsound(source_loc, pick(footstep_sounds[turf_footstep][1]), footstep_sounds[turf_footstep][2] * volume, TRUE, footstep_sounds[turf_footstep][3] + e_range, falloff_distance = 1, vary = sound_vary) -/datum/element/footstep/proc/play_humanstep(mob/living/carbon/human/source, atom/oldloc, direction) +/datum/element/footstep/proc/play_humanstep(mob/living/carbon/human/source, atom/oldloc, direction, forced, list/old_locs, momentum_change) SIGNAL_HANDLER - if (SHOULD_DISABLE_FOOTSTEPS(source)) + if (SHOULD_DISABLE_FOOTSTEPS(source) || !momentum_change) return var/volume_multiplier = 1 @@ -134,21 +134,31 @@ if(!source_loc) return - play_fov_effect(source, 5, "footstep", direction, ignore_self = TRUE) + //cache for sanic speed (lists are references anyways) + var/static/list/footstep_sounds = GLOB.footstep + ///list returned by playsound() filled by client mobs who heard the footstep. given to play_fov_effect() + var/list/heard_clients + if ((source.wear_suit?.body_parts_covered | source.w_uniform?.body_parts_covered | source.shoes?.body_parts_covered) & FEET) // we are wearing shoes - playsound(source_loc, pick(GLOB.footstep[source_loc.footstep][1]), - GLOB.footstep[source_loc.footstep][2] * volume * volume_multiplier, + + heard_clients = playsound(source_loc, pick(footstep_sounds[source_loc.footstep][1]), + footstep_sounds[source_loc.footstep][2] * volume * volume_multiplier, TRUE, - GLOB.footstep[source_loc.footstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary) + footstep_sounds[source_loc.footstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary) else if(source.dna.species.special_step_sounds) - playsound(source_loc, pick(source.dna.species.special_step_sounds), 50, TRUE, falloff_distance = 1, vary = sound_vary) + heard_clients = playsound(source_loc, pick(source.dna.species.special_step_sounds), 50, TRUE, falloff_distance = 1, vary = sound_vary) else - playsound(source_loc, pick(GLOB.barefootstep[source_loc.barefootstep][1]), - GLOB.barefootstep[source_loc.barefootstep][2] * volume * volume_multiplier, + var/static/list/bare_footstep_sounds = GLOB.barefootstep + + heard_clients = playsound(source_loc, pick(bare_footstep_sounds[source_loc.barefootstep][1]), + bare_footstep_sounds[source_loc.barefootstep][2] * volume * volume_multiplier, TRUE, - GLOB.barefootstep[source_loc.barefootstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary) + bare_footstep_sounds[source_loc.barefootstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary) + + if(heard_clients) + play_fov_effect(source, 5, "footstep", direction, ignore_self = TRUE, override_list = heard_clients) ///Prepares a footstep for machine walking diff --git a/code/datums/elements/forced_gravity.dm b/code/datums/elements/forced_gravity.dm index ada16943b86..96889194f2d 100644 --- a/code/datums/elements/forced_gravity.dm +++ b/code/datums/elements/forced_gravity.dm @@ -1,16 +1,18 @@ /datum/element/forced_gravity element_flags = ELEMENT_BESPOKE id_arg_index = 2 + ///the level of gravity we force unto our target var/gravity - var/ignore_space + ///whether we will override the turf if it forces no gravity + var/ignore_turf_gravity -/datum/element/forced_gravity/Attach(datum/target, gravity=1, ignore_space=FALSE) +/datum/element/forced_gravity/Attach(datum/target, gravity=1, ignore_turf_gravity = FALSE) . = ..() if(!isatom(target)) return ELEMENT_INCOMPATIBLE src.gravity = gravity - src.ignore_space = ignore_space + src.ignore_turf_gravity = ignore_turf_gravity RegisterSignal(target, COMSIG_ATOM_HAS_GRAVITY, .proc/gravity_check) if(isturf(target)) @@ -24,10 +26,12 @@ /datum/element/forced_gravity/proc/gravity_check(datum/source, turf/location, list/gravs) SIGNAL_HANDLER - if(!ignore_space && isspaceturf(location)) - return + if(!ignore_turf_gravity && location.force_no_gravity) + return FALSE gravs += gravity + return TRUE + /datum/element/forced_gravity/proc/turf_gravity_check(datum/source, atom/checker, list/gravs) SIGNAL_HANDLER diff --git a/code/datums/elements/frozen.dm b/code/datums/elements/frozen.dm new file mode 100644 index 00000000000..e1cc273774a --- /dev/null +++ b/code/datums/elements/frozen.dm @@ -0,0 +1,47 @@ +///simple element to handle frozen obj's +/datum/element/frozen + element_flags = ELEMENT_DETACH + +/datum/element/frozen/Attach(datum/target) + . = ..() + if(!isobj(target)) + return ELEMENT_INCOMPATIBLE + + var/obj/target_obj = target + if(target_obj.obj_flags & FREEZE_PROOF) + return ELEMENT_INCOMPATIBLE + + target_obj.name = "frozen [target_obj.name]" + target_obj.add_atom_colour(GLOB.freon_color_matrix, TEMPORARY_COLOUR_PRIORITY) + target_obj.alpha -= 25 + + RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/on_moved) + RegisterSignal(target, COMSIG_MOVABLE_POST_THROW, .proc/shatter_on_throw) + RegisterSignal(target, COMSIG_OBJ_UNFREEZE, .proc/on_unfrozen) + +/datum/element/frozen/Detach(datum/source, ...) + var/obj/obj_source = source + obj_source.name = replacetext(obj_source.name, "frozen ", "") + obj_source.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, GLOB.freon_color_matrix) + obj_source.alpha += 25 + . = ..() + +/datum/element/frozen/proc/on_unfrozen(datum/source) + SIGNAL_HANDLER + Detach(source) + +/datum/element/frozen/proc/shatter_on_throw(datum/target) + SIGNAL_HANDLER + var/obj/obj_target = target + obj_target.visible_message(span_danger("[obj_target] shatters into a million pieces!")) + qdel(obj_target) + +/datum/element/frozen/proc/on_moved(datum/target) + SIGNAL_HANDLER + var/obj/obj_target = target + if(!isopenturf(obj_target.loc)) + return + + var/turf/open/turf_loc = obj_target.loc + if(turf_loc.air?.temperature >= T0C)//unfreezes target + Detach(target) diff --git a/code/datums/elements/light_eater.dm b/code/datums/elements/light_eater.dm index 026258122a7..0eb59bbc850 100644 --- a/code/datums/elements/light_eater.dm +++ b/code/datums/elements/light_eater.dm @@ -67,8 +67,7 @@ /datum/element/light_eater/proc/table_buffet(atom/commisary, datum/devourer) . = list() SEND_SIGNAL(commisary, COMSIG_LIGHT_EATER_QUEUE, ., devourer) - for(var/nom in commisary.light_sources) - var/datum/light_source/morsel = nom + for(var/datum/light_source/morsel as anything in commisary.light_sources) .[morsel.source_atom] = TRUE /** diff --git a/code/datums/elements/obj_regen.dm b/code/datums/elements/obj_regen.dm index ef55457c28c..4830d420043 100644 --- a/code/datums/elements/obj_regen.dm +++ b/code/datums/elements/obj_regen.dm @@ -28,6 +28,7 @@ processing |= target /datum/element/obj_regen/Detach(obj/target) + . = ..() UnregisterSignal(target, COMSIG_ATOM_TAKE_DAMAGE) processing -= target if(!length(processing)) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 5970fc4851c..67c6deb77a1 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -403,7 +403,8 @@ GLOBAL_LIST_EMPTY(teleportlocs) /area/Entered(atom/movable/arrived, area/old_area) set waitfor = FALSE SEND_SIGNAL(src, COMSIG_AREA_ENTERED, arrived, old_area) - if(!LAZYACCESS(arrived.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE)) + + if(!arrived.important_recursive_contents?[RECURSIVE_CONTENTS_AREA_SENSITIVE]) return for(var/atom/movable/recipient as anything in arrived.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE]) SEND_SIGNAL(recipient, COMSIG_ENTER_AREA, src) @@ -419,7 +420,18 @@ GLOBAL_LIST_EMPTY(teleportlocs) if(L.client?.prefs.toggles & SOUND_SHIP_AMBIENCE) SEND_SOUND(L, sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ)) +/** + * Called when an atom exits an area + * + * Sends signals COMSIG_AREA_EXITED and COMSIG_EXIT_AREA (to a list of atoms) + */ +/area/Exited(atom/movable/gone, direction) + SEND_SIGNAL(src, COMSIG_AREA_EXITED, gone, direction) + if(!gone.important_recursive_contents?[RECURSIVE_CONTENTS_AREA_SENSITIVE]) + return + for(var/atom/movable/recipient as anything in gone.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE]) + SEND_SIGNAL(recipient, COMSIG_EXIT_AREA, src) ///Divides total beauty in the room by roomsize to allow us to get an average beauty per tile. /area/proc/update_beauty() @@ -431,20 +443,6 @@ GLOBAL_LIST_EMPTY(teleportlocs) return FALSE //Too big beauty = totalbeauty / areasize - -/** - * Called when an atom exits an area - * - * Sends signals COMSIG_AREA_EXITED and COMSIG_EXIT_AREA (to a list of atoms) - */ -/area/Exited(atom/movable/gone, direction) - SEND_SIGNAL(src, COMSIG_AREA_EXITED, gone, direction) - if(!LAZYACCESS(gone.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE)) - return - for(var/atom/movable/recipient as anything in gone.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE]) - SEND_SIGNAL(recipient, COMSIG_EXIT_AREA, src) - - /** * Setup an area (with the given name) * diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 2e72afe785e..8dc93ba4217 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1793,7 +1793,9 @@ * Returns true if this atom has gravity for the passed in turf * * Sends signals [COMSIG_ATOM_HAS_GRAVITY] and [COMSIG_TURF_HAS_GRAVITY], both can force gravity with - * the forced gravity var + * the forced gravity var. + * + * micro-optimized to hell because this proc is very hot, being called several times per movement every movement. * * Gravity situations: * * No gravity if you're not in a turf @@ -1804,39 +1806,28 @@ * * otherwise no gravity */ /atom/proc/has_gravity(turf/gravity_turf) - if(!gravity_turf || !isturf(gravity_turf)) + if(!isturf(gravity_turf)) gravity_turf = get_turf(src) - if(!gravity_turf) - return 0 - - var/list/forced_gravity = list() - SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, forced_gravity) - if(!forced_gravity.len) - SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity) - if(forced_gravity.len) - var/max_grav = forced_gravity[1] - for(var/i in forced_gravity) - max_grav = max(max_grav, i) - return max_grav - - if(isspaceturf(gravity_turf)) // Turf never has gravity - return 0 - if(istype(gravity_turf, /turf/open/openspace)) //openspace in a space area doesn't get gravity - if(istype(get_area(gravity_turf), /area/space)) + if(!gravity_turf)//no gravity in nullspace return 0 - var/area/turf_area = get_area(gravity_turf) - if(turf_area.has_gravity) // Areas which always has gravity - return turf_area.has_gravity - else - // There's a gravity generator on our z level - if(GLOB.gravity_generators["[gravity_turf.z]"]) - var/max_grav = 0 - for(var/obj/machinery/gravity_generator/main/main_grav_gen as anything in GLOB.gravity_generators["[gravity_turf.z]"]) - max_grav = max(main_grav_gen.setting,max_grav) - return max_grav - return SSmapping.level_trait(gravity_turf.z, ZTRAIT_GRAVITY) + //the list isnt created every time as this proc is very hot, its only accessed if anything is actually listening to the signal too + var/static/list/forced_gravity = list() + if(SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, forced_gravity)) + if(!length(forced_gravity)) + SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity) + + var/max_grav = 0 + for(var/i in forced_gravity)//our gravity is the strongest return forced gravity we get + max_grav = max(max_grav, i) + forced_gravity.Cut() + //cut so we can reuse the list, this is ok since forced gravity movers are exceedingly rare compared to all other movement + return max_grav + + var/area/turf_area = gravity_turf.loc + + return !gravity_turf.force_no_gravity && (SSmapping.gravity_by_z_level["[gravity_turf.z]"] || turf_area.has_gravity) /** * Causes effects when the atom gets hit by a rust effect from heretics diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 9d72dfc07a6..927f6069ce8 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -3,8 +3,6 @@ glide_size = 8 appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE - ///how many times a this movable had movement procs called on it since Moved() was last called - var/move_stacks = 0 var/last_move = null var/anchored = FALSE var/move_resist = MOVE_RESIST_DEFAULT @@ -36,8 +34,10 @@ var/pass_flags = NONE /// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour var/generic_canpass = TRUE - var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move - var/atom/movable/moving_from_pull //attempt to resume grab after moving instead of before. + ///0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move + var/moving_diagonally = 0 + ///attempt to resume grab after moving instead of before. + var/atom/movable/moving_from_pull ///Holds information about any movement loops currently running/waiting to run on the movable. Lazy, will be null if nothing's going on var/datum/movement_packet/move_packet var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm @@ -445,8 +445,7 @@ SEND_SIGNAL(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, target) glide_size = target - for(var/m in buckled_mobs) - var/mob/buckled_mob = m + for(var/mob/buckled_mob as anything in buckled_mobs) buckled_mob.set_glide_size(target) /** @@ -456,9 +455,9 @@ */ /atom/movable/proc/abstract_move(atom/new_loc) var/atom/old_loc = loc - move_stacks++ + var/direction = get_dir(old_loc, new_loc) loc = new_loc - Moved(old_loc) + Moved(old_loc, direction) //////////////////////////////////////// // Here's where we rewrite how byond handles movement except slightly different @@ -472,7 +471,7 @@ if(!direction) direction = get_dir(src, newloc) - if(set_dir_on_move && !face_mouse) + if(set_dir_on_move && dir != direction && !face_mouse) // SKYRAT EDIT CHANGE setDir(direction) var/is_multi_tile_object = bound_width > 32 || bound_height > 32 @@ -512,7 +511,6 @@ var/atom/oldloc = loc var/area/oldarea = get_area(oldloc) var/area/newarea = get_area(newloc) - move_stacks++ loc = newloc @@ -547,7 +545,7 @@ return FALSE var/atom/oldloc = loc //Early override for some cases like diagonal movement - if(glide_size_override) + if(glide_size_override && glide_size != glide_size_override) set_glide_size(glide_size_override) if(loc != newloc) @@ -599,12 +597,8 @@ moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) if(moving_diagonally == SECOND_DIAG_STEP) - if(!. && set_dir_on_move && !face_mouse) + if(!. && set_dir_on_move && !face_mouse) // SKYRAT EDIT CHANGE setDir(first_step_dir) - else if (!inertia_moving) - newtonian_move(direct) - if(client_mobs_in_contents) // We're done moving, update our parallax now - update_parallax_contents() moving_diagonally = 0 return @@ -635,12 +629,12 @@ //glide_size strangely enough can change mid movement animation and update correctly while the animation is playing //This means that if you don't override it late like this, it will just be set back by the movement update that's called when you move turfs. - if(glide_size_override) + if(glide_size_override && glide_size != glide_size_override) set_glide_size(glide_size_override) last_move = direct - if(set_dir_on_move && !face_mouse) + if(set_dir_on_move && dir != direct && !face_mouse) // SKYRAT EDIT CHANGE setDir(direct) if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob(s) . = FALSE @@ -665,11 +659,12 @@ * * movement_dir is the direction the movement took place. Can be NONE if it was some sort of teleport. * * The forced flag indicates whether this was a forced move, which skips many checks of regular movement. * * The old_locs is an optional argument, in case the moved movable was present in multiple locations before the movement. + * * momentum_change represents whether this movement is due to a "new" force if TRUE or an already "existing" force if FALSE **/ -/atom/movable/proc/Moved(atom/old_loc, movement_dir, forced = FALSE, list/old_locs) +/atom/movable/proc/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) SHOULD_CALL_PARENT(TRUE) - if (!inertia_moving) + if (!inertia_moving && momentum_change) newtonian_move(movement_dir) // If we ain't moving diagonally right now, update our parallax // We don't do this all the time because diag movements should trigger one call to this, not two @@ -677,14 +672,12 @@ if (!moving_diagonally && client_mobs_in_contents) update_parallax_contents() - move_stacks-- - if(move_stacks > 0) //we want only the first Moved() call in the stack to send this signal, all the other ones have an incorrect old_loc - return - if(move_stacks < 0) - stack_trace("move_stacks is negative in Moved()!") - move_stacks = 0 //setting it to 0 so that we dont get every movable with negative move_stacks runtiming on every movement + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, movement_dir, forced, old_locs, momentum_change) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, movement_dir, forced, old_locs) + if(old_loc) + SEND_SIGNAL(old_loc, COMSIG_ATOM_ABSTRACT_EXITED, src, movement_dir) + if(loc) + SEND_SIGNAL(loc, COMSIG_ATOM_ABSTRACT_ENTERED, src, old_loc, old_locs) var/turf/old_turf = get_turf(old_loc) var/turf/new_turf = get_turf(src) @@ -847,7 +840,6 @@ ///allows this movable to know when it has "entered" another area no matter how many movable atoms its stuffed into, uses important_recursive_contents /atom/movable/proc/become_area_sensitive(trait_source = TRAIT_GENERIC) if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE)) - //RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_AREA_SENSITIVE), .proc/on_area_sensitive_trait_loss) for(var/atom/movable/location as anything in get_nested_locs(src) + src) LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src) ADD_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source) @@ -891,6 +883,24 @@ ASSOC_UNSETEMPTY(recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS) UNSETEMPTY(movable_loc.important_recursive_contents) +///called when this movable becomes the parent of a storage component that is currently being viewed by a player. uses important_recursive_contents +/atom/movable/proc/become_active_storage(datum/component/storage/component_source) + if(!HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE)) + for(var/atom/movable/location as anything in get_nested_locs(src) + src) + LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_ACTIVE_STORAGE, src) + ADD_TRAIT(src, TRAIT_ACTIVE_STORAGE, component_source) + +///called when this movable's storage component is no longer viewed by any players, unsets important_recursive_contents +/atom/movable/proc/lose_active_storage(datum/component/storage/component_source) + if(!HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE)) + return + REMOVE_TRAIT(src, TRAIT_ACTIVE_STORAGE, component_source) + if(HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE)) + return + + for(var/atom/movable/location as anything in get_nested_locs(src) + src) + LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_ACTIVE_STORAGE, src) + ///Sets the anchored var and returns if it was sucessfully changed or not. /atom/movable/proc/set_anchored(anchorvalue) SHOULD_CALL_PARENT(TRUE) @@ -921,12 +931,13 @@ /atom/movable/proc/doMove(atom/destination) . = FALSE - move_stacks++ var/atom/oldloc = loc + var/is_multi_tile = bound_width > world.icon_size || bound_height > world.icon_size if(destination) ///zMove already handles whether a pull from another movable should be broken. if(pulledby && !currently_z_moving) pulledby.stop_pulling() + var/same_loc = oldloc == destination var/area/old_area = get_area(oldloc) var/area/destarea = get_area(destination) @@ -937,23 +948,49 @@ loc = destination if(!same_loc) - if(oldloc) - oldloc.Exited(src, movement_dir) + if(is_multi_tile && isturf(destination)) + var/list/new_locs = block( + destination, + locate( + min(world.maxx, destination.x + ROUND_UP(bound_width / 32)), + min(world.maxy, destination.y + ROUND_UP(bound_height / 32)), + destination.z + ) + ) if(old_area && old_area != destarea) old_area.Exited(src, movement_dir) - destination.Entered(src, oldloc) - if(destarea && old_area != destarea) - destarea.Entered(src, old_area) + for(var/atom/left_loc as anything in locs - new_locs) + left_loc.Exited(src, movement_dir) + + for(var/atom/entering_loc as anything in new_locs - locs) + entering_loc.Entered(src, movement_dir) + + if(old_area && old_area != destarea) + destarea.Entered(src, movement_dir) + else + if(oldloc) + oldloc.Exited(src, movement_dir) + if(old_area && old_area != destarea) + old_area.Exited(src, movement_dir) + destination.Entered(src, oldloc) + if(destarea && old_area != destarea) + destarea.Entered(src, old_area) . = TRUE //If no destination, move the atom into nullspace (don't do this unless you know what you're doing) else . = TRUE - loc = null + if (oldloc) + loc = null var/area/old_area = get_area(oldloc) - oldloc.Exited(src, NONE) + if(is_multi_tile && isturf(oldloc)) + for(var/atom/old_loc as anything in locs) + old_loc.Exited(src, NONE) + else + oldloc.Exited(src, NONE) + if(old_area) old_area.Exited(src, NONE) @@ -979,7 +1016,7 @@ * Called whenever an object moves and by mobs when they attempt to move themselves through space * And when an object or action applies a force on src, see [newtonian_move][/atom/movable/proc/newtonian_move] * - * Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting + * Return FALSE to have src start/keep drifting in a no-grav area and TRUE to stop/not start drifting * * Mobs should return 1 if they should be able to move of their own volition, see [/client/proc/Move] * @@ -988,10 +1025,10 @@ * * continuous_move - If this check is coming from something in the context of already drifting */ /atom/movable/proc/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) - if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE) + if(has_gravity()) return TRUE - if(has_gravity(src)) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE) return TRUE if(pulledby && (pulledby.pulledby != src || moving_from_pull)) @@ -1400,7 +1437,6 @@ log_admin("[key_name(usr)] has added deadchat control to [src]") message_admins(span_notice("[key_name(usr)] has added deadchat control to [src]")) - /** * A wrapper for setDir that should only be able to fail by living mobs. * diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index f548e41d422..04a387f3e22 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -459,7 +459,7 @@ continue if(!(M in rangers)) rangers[M] = TRUE - M.playsound_local(get_turf(M), null, volume, channel = CHANNEL_JUKEBOX, S = song_played, use_reverb = FALSE) + M.playsound_local(get_turf(M), null, volume, channel = CHANNEL_JUKEBOX, sound_to_use = song_played, use_reverb = FALSE) for(var/mob/L in rangers) if(get_dist(src,L) > 10) rangers -= L diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index aa1a6b6fe86..2531db29302 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -104,13 +104,6 @@ span_danger("You [damage_verb] [src] with [attacking_item][damage ? "." : ", without leaving a mark!"]"), null, COMBAT_MESSAGE_RANGE) log_combat(user, src, "attacked", attacking_item) -/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE) - . = ..() - if(obj_flags & FROZEN) - visible_message(span_danger("[src] shatters into a million pieces!")) - qdel(src) - - /obj/assume_air(datum/gas_mixture/giver) if(loc) return loc.assume_air(giver) diff --git a/code/game/objects/structures/industrial_lift.dm b/code/game/objects/structures/industrial_lift.dm deleted file mode 100644 index 2209e12dfe9..00000000000 --- a/code/game/objects/structures/industrial_lift.dm +++ /dev/null @@ -1,620 +0,0 @@ - -//Booleans in arguments are confusing, so I made them defines. -#define LOCKED 1 -#define UNLOCKED 0 - -///Collect and command -/datum/lift_master - var/list/lift_platforms - /// Typepath list of what to ignore smashing through, controls all lifts - var/list/ignored_smashthroughs = list( - /obj/machinery/power/supermatter_crystal, - /obj/structure/holosign - ) - -/datum/lift_master/New(obj/structure/industrial_lift/lift_platform) - Rebuild_lift_plaform(lift_platform) - ignored_smashthroughs = typecacheof(ignored_smashthroughs) - -/datum/lift_master/Destroy() - for(var/l in lift_platforms) - var/obj/structure/industrial_lift/lift_platform = l - lift_platform.lift_master_datum = null - lift_platforms = null - return ..() - - -/datum/lift_master/proc/add_lift_platforms(obj/structure/industrial_lift/new_lift_platform) - if(new_lift_platform in lift_platforms) - return - new_lift_platform.lift_master_datum = src - LAZYADD(lift_platforms, new_lift_platform) - RegisterSignal(new_lift_platform, COMSIG_PARENT_QDELETING, .proc/remove_lift_platforms) - -/datum/lift_master/proc/remove_lift_platforms(obj/structure/industrial_lift/old_lift_platform) - SIGNAL_HANDLER - - if(!(old_lift_platform in lift_platforms)) - return - old_lift_platform.lift_master_datum = null - LAZYREMOVE(lift_platforms, old_lift_platform) - UnregisterSignal(old_lift_platform, COMSIG_PARENT_QDELETING) - -///Collect all bordered platforms -/datum/lift_master/proc/Rebuild_lift_plaform(obj/structure/industrial_lift/base_lift_platform) - add_lift_platforms(base_lift_platform) - var/list/possible_expansions = list(base_lift_platform) - while(possible_expansions.len) - for(var/b in possible_expansions) - var/obj/structure/industrial_lift/borderline = b - var/list/result = borderline.lift_platform_expansion(src) - if(length(result)) - for(var/p in result) - if(lift_platforms.Find(p)) - continue - var/obj/structure/industrial_lift/lift_platform = p - add_lift_platforms(lift_platform) - possible_expansions |= lift_platform - possible_expansions -= borderline - -/** - * Moves the lift UP or DOWN, this is what users invoke with their hand. - * This is a SAFE proc, ensuring every part of the lift moves SANELY. - * It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming. - * Arguments: - * going - UP or DOWN directions, where the lift should go. Keep in mind by this point checks of whether it should go up or down have already been done. - * user - Whomever made the lift movement. - */ -/datum/lift_master/proc/MoveLift(going, mob/user) - set_controls(LOCKED) - for(var/p in lift_platforms) - var/obj/structure/industrial_lift/lift_platform = p - lift_platform.travel(going) - set_controls(UNLOCKED) - -/** - * Moves the lift, this is what users invoke with their hand. - * This is a SAFE proc, ensuring every part of the lift moves SANELY. - * It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming. - */ -/datum/lift_master/proc/MoveLiftHorizontal(going, z, gliding_amount = 8) - var/max_x = 1 - var/max_y = 1 - var/min_x = world.maxx - var/min_y = world.maxy - - - set_controls(LOCKED) - for(var/p in lift_platforms) - var/obj/structure/industrial_lift/lift_platform = p - max_x = max(max_x, lift_platform.x) - max_y = max(max_y, lift_platform.y) - min_x = min(min_x, lift_platform.x) - min_y = min(min_y, lift_platform.y) - - //This must be safe way to border tile to tile move of bordered platforms, that excludes platform overlapping. - if( going & WEST ) - //Go along the X axis from min to max, from left to right - for(var/x in min_x to max_x) - if( going & NORTH ) - //Go along the Y axis from max to min, from up to down - for(var/y in max_y to min_y step -1) - var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) - lift_platform?.travel(going, gliding_amount) - else - //Go along the Y axis from min to max, from down to up - for(var/y in min_y to max_y) - var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) - lift_platform?.travel(going, gliding_amount) - else - //Go along the X axis from max to min, from right to left - for(var/x in max_x to min_x step -1) - if( going & NORTH ) - //Go along the Y axis from max to min, from up to down - for(var/y in max_y to min_y step -1) - var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) - lift_platform?.travel(going, gliding_amount) - else - //Go along the Y axis from min to max, from down to up - for(var/y in min_y to max_y) - var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) - lift_platform?.travel(going, gliding_amount) - set_controls(UNLOCKED) - -///Check destination turfs -/datum/lift_master/proc/Check_lift_move(check_dir) - for(var/l in lift_platforms) - var/obj/structure/industrial_lift/lift_platform = l - var/turf/T = get_step_multiz(lift_platform, check_dir) - if(!T)//the edges of multi-z maps - return FALSE - if(check_dir == UP && !istype(T, /turf/open/openspace)) // We don't want to go through the ceiling! - return FALSE - if(check_dir == DOWN && !istype(get_turf(lift_platform), /turf/open/openspace)) // No going through the floor! - return FALSE - return TRUE - -/** - * Sets all lift parts's controls_locked variable. Used to prevent moving mid movement, or cooldowns. - */ -/datum/lift_master/proc/set_controls(state) - for(var/l in lift_platforms) - var/obj/structure/industrial_lift/lift_platform = l - lift_platform.controls_locked = state - -GLOBAL_LIST_EMPTY(lifts) -/obj/structure/industrial_lift - name = "lift platform" - desc = "A lightweight lift platform. It moves up and down." - icon = 'icons/obj/smooth_structures/catwalk.dmi' - icon_state = "catwalk-0" - base_icon_state = "catwalk" - density = FALSE - anchored = TRUE - armor = list(MELEE = 50, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 80, ACID = 50) - max_integrity = 50 - layer = LATTICE_LAYER //under pipes - plane = FLOOR_PLANE - smoothing_flags = SMOOTH_BITMASK - smoothing_groups = list(SMOOTH_GROUP_INDUSTRIAL_LIFT) - canSmoothWith = list(SMOOTH_GROUP_INDUSTRIAL_LIFT) - obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN - - var/id = null //ONLY SET THIS TO ONE OF THE LIFT'S PARTS. THEY'RE CONNECTED! ONLY ONE NEEDS THE SIGNAL! - var/pass_through_floors = FALSE //if true, the elevator works through floors - var/controls_locked = FALSE //if true, the lift cannot be manually moved. - var/list/atom/movable/lift_load //things to move - var/datum/lift_master/lift_master_datum //control from - -/obj/structure/industrial_lift/Initialize(mapload) - . = ..() - GLOB.lifts.Add(src) - var/static/list/loc_connections = list( - COMSIG_ATOM_EXITED =.proc/UncrossedRemoveItemFromLift, - COMSIG_ATOM_ENTERED = .proc/AddItemOnLift, - COMSIG_ATOM_INITIALIZED_ON = .proc/AddItemOnLift, - ) - AddElement(/datum/element/connect_loc, loc_connections) - RegisterSignal(src, COMSIG_MOVABLE_BUMP, .proc/GracefullyBreak) - - if(!lift_master_datum) - lift_master_datum = new(src) - - -/obj/structure/industrial_lift/proc/UncrossedRemoveItemFromLift(datum/source, atom/movable/gone, direction) - SIGNAL_HANDLER - RemoveItemFromLift(gone) - -/obj/structure/industrial_lift/proc/RemoveItemFromLift(atom/movable/potential_rider) - SIGNAL_HANDLER - if(!(potential_rider in lift_load)) - return - if(isliving(potential_rider) && HAS_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED)) - REMOVE_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT) - LAZYREMOVE(lift_load, potential_rider) - UnregisterSignal(potential_rider, COMSIG_PARENT_QDELETING) - -/obj/structure/industrial_lift/proc/AddItemOnLift(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(istype(AM, /obj/structure/fluff/tram_rail) || AM.invisibility == INVISIBILITY_ABSTRACT) //prevents the tram from stealing things like landmarks - return - if(AM in lift_load) - return - if(isliving(AM) && !HAS_TRAIT(AM, TRAIT_CANNOT_BE_UNBUCKLED)) - ADD_TRAIT(AM, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT) - LAZYADD(lift_load, AM) - RegisterSignal(AM, COMSIG_PARENT_QDELETING, .proc/RemoveItemFromLift) - -/** - * Signal for when the tram runs into a field of which it cannot go through. - * Stops the train's travel fully, sends a message, and destroys the train. - * Arguments: - * bumped_atom - The atom this tram bumped into - */ -/obj/structure/industrial_lift/proc/GracefullyBreak(atom/bumped_atom) - SIGNAL_HANDLER - - if(istype(bumped_atom, /obj/machinery/field)) - return - - bumped_atom.visible_message(span_userdanger("[src] crashes into the field violently!")) - for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_master_datum.lift_platforms) - tram_part.travel_distance = 0 - tram_part.set_travelling(FALSE) - if(prob(15) || locate(/mob/living) in tram_part.lift_load) //always go boom on people on the track - explosion(tram_part, devastation_range = rand(0, 1), heavy_impact_range = 2, light_impact_range = 3) //50% chance of gib - qdel(tram_part) - -/obj/structure/industrial_lift/proc/lift_platform_expansion(datum/lift_master/lift_master_datum) - . = list() - for(var/direction in GLOB.cardinals) - var/obj/structure/industrial_lift/neighbor = locate() in get_step(src, direction) - if(!neighbor) - continue - . += neighbor - -/obj/structure/industrial_lift/proc/travel(going, gliding_amount = 8) - var/list/things_to_move = LAZYCOPY(lift_load) - var/turf/destination - if(!isturf(going)) - destination = get_step_multiz(src, going) - else - destination = going - ///handles any special interactions objects could have with the lift/tram, handled on the item itself - SEND_SIGNAL(destination, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move) - - if(istype(destination, /turf/closed/wall)) - var/turf/closed/wall/C = destination - do_sparks(2, FALSE, C) - C.dismantle_wall(devastated = TRUE) - for(var/mob/M in urange(8, src)) - shake_camera(M, 2, 3) - playsound(C, 'sound/effects/meteorimpact.ogg', 100, TRUE) - - if(going == DOWN) - for(var/mob/living/crushed in destination.contents) - to_chat(crushed, span_userdanger("You are crushed by [src]!")) - crushed.gib(FALSE,FALSE,FALSE)//the nicest kind of gibbing, keeping everything intact. - - else if(going != UP) //can't really crush something upwards - var/atom/throw_target = get_edge_target_turf(src, turn(going, pick(45, -45))) //finds a spot to throw the victim at for daring to be hit by a tram - for(var/obj/structure/victim_structure in destination.contents) - if(QDELETED(victim_structure)) - continue - if(!is_type_in_typecache(victim_structure, lift_master_datum.ignored_smashthroughs) && victim_structure.layer >= LOW_OBJ_LAYER) - if(victim_structure.anchored && initial(victim_structure.anchored) == TRUE) - visible_message(span_danger("[src] smashes through [victim_structure]!")) - victim_structure.deconstruct(FALSE) - else - visible_message(span_danger("[src] violently rams [victim_structure] out of the way!")) - victim_structure.anchored = FALSE - victim_structure.take_damage(rand(20, 25)) - victim_structure.throw_at(throw_target, 200, 4) - for(var/obj/machinery/victim_machine in destination.contents) - if(QDELETED(victim_machine)) - continue - if(is_type_in_typecache(victim_machine, lift_master_datum.ignored_smashthroughs)) - continue - if(istype(victim_machine, /obj/machinery/field)) //graceful break handles this scenario - continue - if(victim_machine.layer >= LOW_OBJ_LAYER) //avoids stuff that is probably flush with the ground - playsound(src, 'sound/effects/bang.ogg', 50, TRUE) - visible_message(span_danger("[src] smashes through [victim_machine]!")) - qdel(victim_machine) - - for(var/mob/living/collided in destination.contents) - if(is_type_in_typecache(collided, lift_master_datum.ignored_smashthroughs)) - continue - to_chat(collided, span_userdanger("[src] collides into you!")) - playsound(src, 'sound/effects/splat.ogg', 50, TRUE) - var/damage = rand(5, 10) - collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_HEAD) - collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_CHEST) - collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_LEG) - collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_LEG) - collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_ARM) - collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_ARM) - - if(QDELETED(collided)) //in case it was a mob that dels on death - continue - var/turf/T = get_turf(src) - T.add_mob_blood(collided) - - collided.throw_at() - //if going EAST, will turn to the NORTHEAST or SOUTHEAST and throw the ran over guy away - var/datum/callback/land_slam = new(collided, /mob/living/.proc/tram_slam_land) - collided.throw_at(throw_target, 200, 4, callback = land_slam) - set_glide_size(gliding_amount) - forceMove(destination) - for(var/atom/movable/thing as anything in things_to_move) - thing.set_glide_size(gliding_amount) //matches the glide size of the moving platform to stop them from jittering on it. - thing.forceMove(destination) - -/obj/structure/industrial_lift/proc/use(mob/living/user) - if(!isliving(user) || !in_range(src, user) || user.combat_mode) - return - - var/list/tool_list = list() - if(lift_master_datum.Check_lift_move(UP)) - tool_list["Up"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH) - if(lift_master_datum.Check_lift_move(DOWN)) - tool_list["Down"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH) - if(!length(tool_list)) - to_chat(user, span_warning("[src] doesn't seem to able to move anywhere!")) - add_fingerprint(user) - return - if(controls_locked) - to_chat(user, span_warning("[src] has its controls locked! It must already be trying to do something!")) - add_fingerprint(user) - return - var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user, src.loc), require_near = TRUE, tooltips = TRUE) - if(!isliving(user) || !in_range(src, user) || user.combat_mode) - return //nice try - switch(result) - if("Up") - // We have to make sure that they don't do illegal actions by not having their radial menu refresh from someone else moving the lift. - if(!lift_master_datum.Check_lift_move(UP)) - to_chat(user, span_warning("[src] doesn't seem to able to move up!")) - add_fingerprint(user) - return - lift_master_datum.MoveLift(UP, user) - show_fluff_message(TRUE, user) - use(user) - if("Down") - if(!lift_master_datum.Check_lift_move(DOWN)) - to_chat(user, span_warning("[src] doesn't seem to able to move down!")) - add_fingerprint(user) - return - lift_master_datum.MoveLift(DOWN, user) - show_fluff_message(FALSE, user) - use(user) - if("Cancel") - return - add_fingerprint(user) - -/** - * Proc to ensure that the radial menu closes when it should. - * Arguments: - * * user - The person that opened the menu. - * * starting_loc - The location of the lift when the menu was opened, used to prevent the menu from being interacted with after the lift was moved by someone else. - * - * Returns: - * * boolean, FALSE if the menu should be closed, TRUE if the menu is clear to stay opened. - */ -/obj/structure/industrial_lift/proc/check_menu(mob/user, starting_loc) - if(user.incapacitated() || !user.Adjacent(src) || starting_loc != src.loc) - return FALSE - return TRUE - -/obj/structure/industrial_lift/attack_hand(mob/user, list/modifiers) - . = ..() - if(.) - return - use(user) - -//ai probably shouldn't get to use lifts but they sure are great for admins to crush people with -/obj/structure/industrial_lift/attack_ghost(mob/user) - . = ..() - if(.) - return - if(isAdminGhostAI(user)) - use(user) - -/obj/structure/industrial_lift/attack_paw(mob/user, list/modifiers) - return use(user) - -/obj/structure/industrial_lift/attackby(obj/item/W, mob/user, params) - return use(user) - -/obj/structure/industrial_lift/attack_robot(mob/living/silicon/robot/R) - if(R.Adjacent(src)) - return use(R) - -/** - * Shows a message indicating that the lift has moved up or down. - * Arguments: - * * going_up - Boolean on whether or not we're going up, to adjust the message appropriately. - * * user - The mob that caused the lift to move, for the visible message. - */ -/obj/structure/industrial_lift/proc/show_fluff_message(going_up, mob/user) - if(going_up) - user.visible_message(span_notice("[user] moves the lift upwards."), span_notice("You move the lift upwards.")) - else - user.visible_message(span_notice("[user] moves the lift downwards."), span_notice("You move the lift downwards.")) - -/obj/structure/industrial_lift/Destroy() - GLOB.lifts.Remove(src) - QDEL_NULL(lift_master_datum) - var/list/border_lift_platforms = lift_platform_expansion() - moveToNullspace() - for(var/border_lift in border_lift_platforms) - lift_master_datum = new(border_lift) - return ..() - -/obj/structure/industrial_lift/debug - name = "transport platform" - desc = "A lightweight platform. It moves in any direction, except up and down." - color = "#5286b9ff" - -/obj/structure/industrial_lift/debug/use(mob/user) - if (!in_range(src, user)) - return -//NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST - var/static/list/tool_list = list( - "NORTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), - "NORTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), - "EAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST), - "SOUTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST), - "SOUTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH), - "SOUTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH), - "WEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST), - "NORTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST) - ) - - var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user, loc), require_near = TRUE, tooltips = FALSE) - if (!in_range(src, user)) - return // nice try - - switch(result) - if("NORTH") - lift_master_datum.MoveLiftHorizontal(NORTH, z) - use(user) - if("NORTHEAST") - lift_master_datum.MoveLiftHorizontal(NORTHEAST, z) - use(user) - if("EAST") - lift_master_datum.MoveLiftHorizontal(EAST, z) - use(user) - if("SOUTHEAST") - lift_master_datum.MoveLiftHorizontal(SOUTHEAST, z) - use(user) - if("SOUTH") - lift_master_datum.MoveLiftHorizontal(SOUTH, z) - use(user) - if("SOUTHWEST") - lift_master_datum.MoveLiftHorizontal(SOUTHWEST, z) - use(user) - if("WEST") - lift_master_datum.MoveLiftHorizontal(WEST, z) - use(user) - if("NORTHWEST") - lift_master_datum.MoveLiftHorizontal(NORTHWEST, z) - use(user) - if("Cancel") - return - - add_fingerprint(user) - -/obj/structure/industrial_lift/tram - name = "tram" - desc = "A tram for traversing the station." - icon = 'icons/turf/floors.dmi' - icon_state = "titanium_yellow" - base_icon_state = null - smoothing_flags = NONE - smoothing_groups = null - canSmoothWith = null - //kind of a centerpiece of the station, so pretty tough to destroy - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - /// Set by the tram control console in late initialize - var/travelling = FALSE - var/travel_distance = 0 - /// For finding the landmark initially - should be the exact same as the landmark's destination id. - var/initial_id = "middle_part" - var/obj/effect/landmark/tram/from_where - var/travel_direction - -GLOBAL_LIST_EMPTY_TYPED(central_trams, /obj/structure/industrial_lift/tram/central) - -/obj/structure/industrial_lift/tram/Initialize(mapload) - . = ..() - return INITIALIZE_HINT_LATELOAD - -/obj/structure/industrial_lift/tram/central - var/tram_id = "tram_station" - -/obj/structure/industrial_lift/tram/central/Initialize(mapload) - . = ..() - if(!SStramprocess.can_fire) - SStramprocess.can_fire = TRUE - GLOB.central_trams += src - -/obj/structure/industrial_lift/tram/central/Destroy() - GLOB.central_trams -= src - return ..() - -/obj/structure/industrial_lift/tram/LateInitialize() - . = ..() - find_our_location() - - -/** - * Finds the location of the tram - * - * The initial_id is assumed to the be the landmark the tram is built on in the map - * and where the tram will set itself to be on roundstart. - * The central tram piece goes further into this by actually checking the contents of the turf its on - * for a tram landmark when it docks anywhere. This assures the tram actually knows where it is after docking, - * even in the worst cast scenario. - */ -/obj/structure/industrial_lift/tram/proc/find_our_location() - for(var/obj/effect/landmark/tram/our_location in GLOB.landmarks_list) - if(our_location.destination_id == initial_id) - from_where = our_location - break - -/obj/structure/industrial_lift/tram/proc/set_travelling(travelling) - if (src.travelling == travelling) - return - - src.travelling = travelling - SEND_SIGNAL(src, COMSIG_TRAM_SET_TRAVELLING, travelling) - -/obj/structure/industrial_lift/tram/use(mob/user) //dont click the floor dingus we use computers now - return - -/obj/structure/industrial_lift/tram/process(delta_time) - if(!travel_distance) - addtimer(CALLBACK(src, .proc/unlock_controls), 3 SECONDS) - return PROCESS_KILL - else - travel_distance-- - lift_master_datum.MoveLiftHorizontal(travel_direction, z, DELAY_TO_GLIDE_SIZE(SStramprocess.wait)) - -/** - * Handles moving the tram - * - * Tells the individual tram parts where to actually go and has an extra safety check - * incase multiple inputs get through, preventing conflicting directions and the tram - * literally ripping itself apart. The proc handles the first move before the subsystem - * takes over to keep moving it in process() - */ -/obj/structure/industrial_lift/tram/proc/tram_travel(obj/effect/landmark/tram/to_where) - if(to_where == from_where) - return - - visible_message(span_notice("[src] has been called to the [to_where]!")) - - lift_master_datum.set_controls(LOCKED) - travel_direction = get_dir(from_where, to_where) - travel_distance = get_dist(from_where, to_where) - //first movement is immediate - for(var/obj/structure/industrial_lift/tram/other_tram_part as anything in lift_master_datum.lift_platforms) //only thing everyone needs to know is the new location. - if(other_tram_part.travelling) //wee woo wee woo there was a double action queued. damn multi tile structs - return //we don't care to undo locked controls, though, as that will resolve itself - SEND_SIGNAL(src, COMSIG_TRAM_TRAVEL, from_where, to_where) - other_tram_part.set_travelling(TRUE) - other_tram_part.from_where = to_where - lift_master_datum.MoveLiftHorizontal(travel_direction, z, DELAY_TO_GLIDE_SIZE(SStramprocess.wait)) - travel_distance-- - - START_PROCESSING(SStramprocess, src) - -/** - * Handles unlocking the tram controls for use after moving - * - * More safety checks to make sure the tram has actually docked properly - * at a location before users are allowed to interact with the tram console again. - * Tram finds its location at this point before fully unlocking controls to the user. - */ -/obj/structure/industrial_lift/tram/proc/unlock_controls() - visible_message(span_notice("[src]'s controls are now unlocked.")) - for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_master_datum.lift_platforms) //only thing everyone needs to know is the new location. - tram_part.set_travelling(FALSE) - lift_master_datum.set_controls(UNLOCKED) - -GLOBAL_LIST_EMPTY(tram_landmarks) - -/obj/effect/landmark/tram - name = "tram destination" //the tram buttons will mention this. - icon_state = "tram" - /// The ID of that particular destination. - var/destination_id - /// The ID of the tram that can travel to use - var/tram_id = "tram_station" - /// Icons for the tgui console to list out for what is at this location - var/list/tgui_icons = list() - -/obj/effect/landmark/tram/Initialize(mapload) - . = ..() - GLOB.tram_landmarks += src - -/obj/effect/landmark/tram/Destroy() - GLOB.tram_landmarks -= src - return ..() - - -/obj/effect/landmark/tram/left_part - name = "West Wing" - destination_id = "left_part" - tgui_icons = list("Arrivals" = "plane-arrival", "Command" = "bullhorn", "Security" = "gavel") - -/obj/effect/landmark/tram/middle_part - name = "Central Wing" - destination_id = "middle_part" - tgui_icons = list("Service" = "cocktail", "Medical" = "plus", "Engineering" = "wrench") - -/obj/effect/landmark/tram/right_part - name = "East Wing" - destination_id = "right_part" - tgui_icons = list("Departures" = "plane-departure", "Cargo" = "box", "Science" = "flask") diff --git a/code/game/sound.dm b/code/game/sound.dm index 8580eaa14da..2e88ae160fe 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -23,120 +23,101 @@ ) environment = SOUND_ENVIRONMENT_NONE //Default to none so sounds without overrides dont get reverb -/*! playsound - -playsound is a proc used to play a 3D sound in a specific range. This uses SOUND_RANGE + extra_range to determine that. - -source - Origin of sound -soundin - Either a file, or a string that can be used to get an SFX -vol - The volume of the sound, excluding falloff and pressure affection. -vary - bool that determines if the sound changes pitch every time it plays -extrarange - modifier for sound range. This gets added on top of SOUND_RANGE -falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. -frequency - playback speed of audio -channel - The channel the sound is played at -pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space) -ignore_walls - Whether or not the sound can pass through walls. -falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range. - -*/ - +/** + * playsound is a proc used to play a 3D sound in a specific range. This uses SOUND_RANGE + extra_range to determine that. + * + * source - Origin of sound. + * soundin - Either a file, or a string that can be used to get an SFX. + * vol - The volume of the sound, excluding falloff and pressure affection. + * vary - bool that determines if the sound changes pitch every time it plays. + * extrarange - modifier for sound range. This gets added on top of SOUND_RANGE. + * falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. + * frequency - playback speed of audio. + * channel - The channel the sound is played at. + * pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space). + * ignore_walls - Whether or not the sound can pass through walls. + * falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range. + */ /proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, use_reverb = TRUE) if(isarea(source)) CRASH("playsound(): source is an area") var/turf/turf_source = get_turf(source) - if (!turf_source) + if (!turf_source || !soundin || !vol) return //allocate a channel if necessary now so its the same for everyone channel = channel || SSsounds.random_available_channel() - // Looping through the player list has the added bonus of working for mobs inside containers var/sound/S = sound(get_sfx(soundin)) var/maxdistance = SOUND_RANGE + extrarange var/source_z = turf_source.z var/list/listeners = SSmobs.clients_by_zlevel[source_z].Copy() + . = list()//output everything that successfully heard the sound + var/turf/above_turf = SSmapping.get_turf_above(turf_source) var/turf/below_turf = SSmapping.get_turf_below(turf_source) - if(!ignore_walls) //these sounds don't carry through walls - listeners = listeners & hearers(maxdistance,turf_source) + if(ignore_walls) - if(above_turf && istransparentturf(above_turf)) - listeners += hearers(maxdistance,above_turf) - - if(below_turf && istransparentturf(turf_source)) - listeners += hearers(maxdistance,below_turf) - - else if(above_turf && istransparentturf(above_turf)) listeners += SSmobs.clients_by_zlevel[above_turf.z] if(below_turf && istransparentturf(turf_source)) listeners += SSmobs.clients_by_zlevel[below_turf.z] - for(var/mob/listening_mob as anything in listeners) - if(get_dist(listening_mob, turf_source) <= maxdistance) - listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) - for(var/mob/listening_mob as anything in SSmobs.dead_players_by_zlevel[source_z]) - if(get_dist(listening_mob, turf_source) <= maxdistance) - listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) + for(var/mob/listening_mob as anything in listeners) + if(get_dist(listening_mob, turf_source) <= maxdistance) + listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) + . += listening_mob -/*! playsound + else //these sounds don't carry through walls + listeners = get_hearers_in_view(maxdistance, turf_source) -playsound_local is a proc used to play a sound directly on a mob from a specific turf. -This is called by playsound to send sounds to players, in which case it also gets the max_distance of that sound. + if(above_turf && istransparentturf(above_turf)) + listeners += get_hearers_in_view(maxdistance, above_turf) -turf_source - Origin of sound -soundin - Either a file, or a string that can be used to get an SFX -vol - The volume of the sound, excluding falloff -vary - bool that determines if the sound changes pitch every time it plays -frequency - playback speed of audio -falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. -channel - The channel the sound is played at -pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space) -max_distance - The peak distance of the sound, if this is a 3D sound -falloff_distance - Distance at which falloff begins, if this is a 3D sound -distance_multiplier - Can be used to multiply the distance at which the sound is heard + if(below_turf && istransparentturf(turf_source)) + listeners += get_hearers_in_view(maxdistance, below_turf) -*/ + for(var/mob/listening_mob in listeners | SSmobs.dead_players_by_zlevel[source_z])//observers always hear through walls + if(get_dist(listening_mob, turf_source) <= maxdistance) + listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) + . += listening_mob -/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE) +/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/sound_to_use, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE) if(!client || !can_hear()) return - if(!S) - S = sound(get_sfx(soundin)) + if(!sound_to_use) + sound_to_use = sound(get_sfx(soundin)) - S.wait = 0 //No queue - S.channel = channel || SSsounds.random_available_channel() - S.volume = vol + sound_to_use.wait = 0 //No queue + sound_to_use.channel = channel || SSsounds.random_available_channel() + sound_to_use.volume = vol if(vary) if(frequency) - S.frequency = frequency + sound_to_use.frequency = frequency else - S.frequency = get_rand_frequency() + sound_to_use.frequency = get_rand_frequency() if(isturf(turf_source)) - var/turf/T = get_turf(src) + var/turf/turf_loc = get_turf(src) //sound volume falloff with distance - var/distance = get_dist(T, turf_source) - - distance *= distance_multiplier + var/distance = get_dist(turf_loc, turf_source) * distance_multiplier if(max_distance) //If theres no max_distance we're not a 3D sound, so no falloff. - S.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * S.volume + sound_to_use.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * sound_to_use.volume //https://www.desmos.com/calculator/sqdfl8ipgf if(pressure_affected) //Atmosphere affects sound var/pressure_factor = 1 - var/datum/gas_mixture/hearer_env = T.return_air() + var/datum/gas_mixture/hearer_env = turf_loc.return_air() var/datum/gas_mixture/source_env = turf_source.return_air() if(hearer_env && source_env) @@ -149,37 +130,35 @@ distance_multiplier - Can be used to multiply the distance at which the sound is if(distance <= 1) pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound - S.volume *= pressure_factor + sound_to_use.volume *= pressure_factor //End Atmosphere affecting sound - if(S.volume <= 0) + if(sound_to_use.volume <= 0) return //No sound - var/dx = turf_source.x - T.x // Hearing from the right/left - S.x = dx * distance_multiplier - var/dz = turf_source.y - T.y // Hearing from infront/behind - S.z = dz * distance_multiplier - var/dy = (turf_source.z - T.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords. - S.y = dy + var/dx = turf_source.x - turf_loc.x // Hearing from the right/left + sound_to_use.x = dx * distance_multiplier + var/dz = turf_source.y - turf_loc.y // Hearing from infront/behind + sound_to_use.z = dz * distance_multiplier + var/dy = (turf_source.z - turf_loc.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords. + sound_to_use.y = dy - S.falloff = max_distance || 1 //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant. + sound_to_use.falloff = max_distance || 1 //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant. // Sounds can't have their own environment. A sound's environment will be: // 1. the mob's // 2. the area's (defaults to SOUND_ENVRIONMENT_NONE) if(sound_environment_override != SOUND_ENVIRONMENT_NONE) - S.environment = sound_environment_override + sound_to_use.environment = sound_environment_override else var/area/A = get_area(src) - S.environment = A.sound_environment + sound_to_use.environment = A.sound_environment - if(use_reverb && S.environment != SOUND_ENVIRONMENT_NONE) //We have reverb, reset our echo setting - //S.echo[3] = 0 //Room setting, 0 means normal reverb //ORIGINAL - //S.echo[4] = 0 //RoomHF setting, 0 means normal reverb. //ORIGINAL - S.echo[3] = -1300 //Room setting, 0 means normal reverb //SKYRAT EDIT CHANGE - S.echo[4] = -1300 //RoomHF setting, 0 means normal reverb. //SKYRAT EDIT CHANGE + if(use_reverb && sound_to_use.environment != SOUND_ENVIRONMENT_NONE) //We have reverb, reset our echo setting + sound_to_use.echo[3] = -1300 //Room setting, 0 means normal reverb //SKYRAT EDIT CHANGE + sound_to_use.echo[4] = -1300 //RoomHF setting, 0 means normal reverb. //SKYRAT EDIT CHANGE - SEND_SOUND(src, S) + SEND_SOUND(src, sound_to_use) /proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, channel = 0, pressure_affected = FALSE, sound/S) if(!S) diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index 9763dcdf063..d0cd6021011 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -1,6 +1,7 @@ /turf/open plane = FLOOR_PLANE - var/slowdown = 0 //negative for faster, positive for slower + ///negative for faster, positive for slower + var/slowdown = 0 var/footstep = null var/barefootstep = null @@ -241,10 +242,9 @@ /turf/open/proc/freeze_turf() for(var/obj/I in contents) - if(I.resistance_flags & FREEZE_PROOF) - continue - if(!(I.obj_flags & FROZEN)) - I.make_frozen_visual() + if(!HAS_TRAIT(I, TRAIT_FROZEN) && !(I.obj_flags & FREEZE_PROOF)) + I.AddElement(/datum/element/frozen) + for(var/mob/living/L in contents) if(L.bodytemperature <= 50) L.apply_status_effect(/datum/status_effect/freon) @@ -258,8 +258,7 @@ M.apply_water() wash(CLEAN_WASH) - for(var/am in src) - var/atom/movable/movable_content = am + for(var/atom/movable/movable_content as anything in src) if(ismopable(movable_content)) // Will have already been washed by the wash call above at this point. continue movable_content.wash(CLEAN_WASH) diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm index 9e0e0805f09..66cd6f96a11 100644 --- a/code/game/turfs/open/openspace.dm +++ b/code/game/turfs/open/openspace.dm @@ -32,6 +32,9 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr . = ..() overlays += GLOB.openspace_backdrop_one_for_all //Special grey square for projecting backdrop darkness filter on it. RegisterSignal(src, COMSIG_ATOM_INITIALIZED_ON, .proc/on_atom_created) + var/area/our_area = loc + if(istype(our_area, /area/space)) + force_no_gravity = TRUE return INITIALIZE_HINT_LATELOAD /turf/open/openspace/LateInitialize() diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm index 74b22b59d46..e5f74afcf44 100644 --- a/code/game/turfs/open/space/space.dm +++ b/code/game/turfs/open/space/space.dm @@ -22,6 +22,8 @@ bullet_bounce_sound = null vis_flags = VIS_INHERIT_ID //when this be added to vis_contents of something it be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf. + force_no_gravity = TRUE + /turf/open/space/basic/New() //Do not convert to Initialize //This is used to optimize the map loader return diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 9e036ee9530..51511c38754 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -74,6 +74,8 @@ GLOBAL_LIST_EMPTY(station_turfs) /// but is now destroyed, this will preserve the value. /// See __DEFINES/construction.dm for RCD_MEMORY_*. var/rcd_memory + ///whether or not this turf forces movables on it to have no gravity (unless they themselves have forced gravity) + var/force_no_gravity = FALSE /// How pathing algorithm will check if this turf is passable by itself (not including content checks). By default it's just density check. /// WARNING: Currently to use a density shortcircuiting this does not support dense turfs with special allow through function @@ -363,8 +365,6 @@ GLOBAL_LIST_EMPTY(station_turfs) var/canPassSelf = CanPass(mover, get_dir(src, mover)) if(canPassSelf || (mover.movement_type & PHASING)) for(var/atom/movable/thing as anything in contents) - if(QDELETED(mover)) - return FALSE //We were deleted, do not attempt to proceed with movement. if(thing == mover || thing == mover.loc) // Multi tile objects and moving out of other objects continue if(!thing.Cross(mover)) @@ -385,14 +385,6 @@ GLOBAL_LIST_EMPTY(station_turfs) return (mover.movement_type & PHASING) return TRUE -/turf/open/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - . = ..() - //melting - if(isobj(arrived) && air && air.temperature > T0C) - var/obj/O = arrived - if(O.obj_flags & FROZEN) - O.make_unfrozen() - // A proc in case it needs to be recreated or badmins want to change the baseturfs /turf/proc/assemble_baseturfs(turf/fake_baseturf_type) var/static/list/created_baseturf_lists = list() diff --git a/code/modules/assembly/doorcontrol.dm b/code/modules/assembly/doorcontrol.dm index b32ffee4ad6..7b254d9e5a2 100644 --- a/code/modules/assembly/doorcontrol.dm +++ b/code/modules/assembly/doorcontrol.dm @@ -185,34 +185,46 @@ if(cooldown) return cooldown = TRUE - var/obj/structure/industrial_lift/lift - for(var/l in GLOB.lifts) - var/obj/structure/industrial_lift/possible_lift = l - if(possible_lift.id != id || possible_lift.z == z || possible_lift.controls_locked) + var/datum/lift_master/lift + for(var/datum/lift_master/possible_match as anything in GLOB.active_lifts_by_type[BASIC_LIFT_ID]) + if(possible_match.specific_lift_id != id || !check_z(possible_match) || possible_match.controls_locked) continue - lift = possible_lift + + lift = possible_match break + if(!lift) addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 2 SECONDS) return - lift.visible_message(span_notice("[src] clinks and whirrs into automated motion, locking controls.")) - lift.lift_master_datum.set_controls(LOCKED) + + var/obj/structure/industrial_lift/target = lift.lift_platforms[1] + var/target_z = target.z + + lift.set_controls(LIFT_PLATFORM_LOCKED) ///The z level to which the elevator should travel var/targetZ = (abs(loc.z)) //The target Z (where the elevator should move to) is not our z level (we are just some assembly in nullspace) but actually the Z level of whatever we are contained in (e.g. elevator button) ///The amount of z levels between the our and targetZ - var/difference = abs(targetZ - lift.z) + var/difference = abs(targetZ - target_z) ///Direction (up/down) needed to go to reach targetZ - var/direction = lift.z < targetZ ? UP : DOWN + var/direction = target_z < targetZ ? UP : DOWN ///How long it will/should take us to reach the target Z level var/travel_duration = FLOOR_TRAVEL_TIME * difference //100 / 2 floors up = 50 seconds on every floor, will always reach destination in the same time addtimer(VARSET_CALLBACK(src, cooldown, FALSE), travel_duration) + for(var/i in 1 to difference) sleep(FLOOR_TRAVEL_TIME)//hey this should be alright... right? if(QDELETED(lift) || QDELETED(src))//elevator control or button gone = don't go up anymore return - lift.lift_master_datum.MoveLift(direction, null) - lift.visible_message(span_notice("[src] clicks, ready to be manually operated again.")) - lift.lift_master_datum.set_controls(UNLOCKED) + lift.MoveLift(direction, null) + lift.set_controls(LIFT_PLATFORM_UNLOCKED) + +///check if any of the lift platforms are already here +/obj/item/assembly/control/elevator/proc/check_z(datum/lift_master/lift) + for(var/obj/structure/industrial_lift/platform as anything in lift.lift_platforms) + if(platform.z == loc.z) + return FALSE + + return TRUE #undef FLOOR_TRAVEL_TIME @@ -221,17 +233,19 @@ desc = "A small device used to bring trams to you." ///for finding the landmark initially - should be the exact same as the landmark's destination id. var/initial_id + ///ID to link to allow us to link to one specific tram in the world + var/specific_lift_id = MAIN_STATION_TRAM ///this is our destination's landmark, so we only have to find it the first time. var/datum/weakref/to_where /obj/item/assembly/control/tram/Initialize(mapload) - . = ..() + ..() return INITIALIZE_HINT_LATELOAD /obj/item/assembly/control/tram/LateInitialize() . = ..() //find where the tram needs to go to (our destination). only needs to happen the first time - for(var/obj/effect/landmark/tram/our_destination as anything in GLOB.tram_landmarks) + for(var/obj/effect/landmark/tram/our_destination as anything in GLOB.tram_landmarks[specific_lift_id]) if(our_destination.destination_id == initial_id) to_where = WEAKREF(our_destination) break @@ -245,29 +259,27 @@ return cooldown = TRUE addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 2 SECONDS) - var/obj/structure/industrial_lift/tram/tram_part - var/turf/current_turf = get_turf(src) - if(!current_turf) - return - for(var/atom/tram as anything in GLOB.central_trams) - if(tram.z != current_turf.z) - continue - tram_part = tram - break - if(!tram_part) + + var/datum/lift_master/tram/tram + for(var/datum/lift_master/tram/possible_match as anything in GLOB.active_lifts_by_type[TRAM_LIFT_ID]) + if(possible_match.specific_lift_id == specific_lift_id) + tram = possible_match + break + + if(!tram) say("The tram is not responding to call signals. Please send a technician to repair the internals of the tram.") return - if(tram_part.travelling) //in use - say("The tram is already travelling to [tram_part.from_where].") + if(tram.travelling) //in use + say("The tram is already travelling to [tram.from_where].") return if(!to_where) return var/obj/effect/landmark/tram/current_location = to_where.resolve() if(!current_location) return - if(tram_part.from_where == current_location) //already here + if(tram.from_where == current_location) //already here say("The tram is already here. Please board the tram and select a destination.") return say("The tram has been called to [current_location.name]. Please wait for its arrival.") - tram_part.tram_travel(current_location) + tram.tram_travel(current_location) diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm index c36f4bbb26c..99196512a50 100644 --- a/code/modules/atmospherics/environmental/LINDA_fire.dm +++ b/code/modules/atmospherics/environmental/LINDA_fire.dm @@ -112,6 +112,7 @@ air_update_turf(FALSE, FALSE) var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = .proc/on_entered, + COMSIG_ATOM_ABSTRACT_ENTERED = .proc/on_entered, ) AddElement(/datum/element/connect_loc, loc_connections) diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index 95ed18a5efe..20f25223287 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -111,8 +111,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) if(volume) // to prevent division by zero var/cached_gases = gases TOTAL_MOLES(cached_gases, .) - . *= R_IDEAL_GAS_EQUATION * temperature / volume - return + return . * R_IDEAL_GAS_EQUATION * temperature / volume return 0 /// Calculate temperature in kelvins @@ -661,7 +660,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) /datum/gas_mixture/proc/gas_pressure_quadratic(a, b, c, lower_limit, upper_limit) var/solution if(!IS_INF_OR_NAN(a) && !IS_INF_OR_NAN(b) && !IS_INF_OR_NAN(c)) - solution = max(SolveQuadratic(a, b, c)) + solution = max(SolveQuadratic(a, b, c)) if(solution > lower_limit && solution < upper_limit) //SolveQuadratic can return empty lists so be careful here return solution stack_trace("Failed to solve pressure quadratic equation. A: [a]. B: [b]. C:[c]. Current value = [solution]. Expected lower limit: [lower_limit]. Expected upper limit: [upper_limit].") diff --git a/code/game/machinery/crossing_signal.dm b/code/modules/industrial_lift/crossing_signal.dm similarity index 88% rename from code/game/machinery/crossing_signal.dm rename to code/modules/industrial_lift/crossing_signal.dm index 80907a59c1d..b7bdc00792f 100644 --- a/code/game/machinery/crossing_signal.dm +++ b/code/modules/industrial_lift/crossing_signal.dm @@ -26,7 +26,7 @@ /// green, amber, or red. var/signal_state = XING_STATE_GREEN /// The ID of the tram we control - var/tram_id = "tram_station" + var/tram_id = MAIN_STATION_TRAM /// Weakref to the tram piece we control var/datum/weakref/tram_ref /// Proximity threshold for amber warning (slow people may be in danger) @@ -42,14 +42,14 @@ . = ..() find_tram() - var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve() + var/datum/lift_master/tram/tram_part = tram_ref?.resolve() if(tram_part) RegisterSignal(tram_part, COMSIG_TRAM_SET_TRAVELLING, .proc/on_tram_travelling) /obj/machinery/crossing_signal/Destroy() . = ..() - var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve() + var/datum/lift_master/tram/tram_part = tram_ref?.resolve() if(tram_part) UnregisterSignal(tram_part, COMSIG_TRAM_SET_TRAVELLING) @@ -67,8 +67,8 @@ * Locates tram parts in the lift global list after everything is done. */ /obj/machinery/crossing_signal/proc/find_tram() - for(var/obj/structure/industrial_lift/tram/central/tram as anything in GLOB.central_trams) - if(tram.tram_id != tram_id) + for(var/datum/lift_master/tram/tram as anything in GLOB.active_lifts_by_type[TRAM_LIFT_ID]) + if(tram.specific_lift_id != tram_id) continue tram_ref = WEAKREF(tram) break @@ -105,10 +105,10 @@ end_processing() /obj/machinery/crossing_signal/process() - var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve() + var/datum/lift_master/tram/tram = tram_ref?.resolve() // Check for stopped states. - if(!tram_part || !is_operational) + if(!tram || !is_operational) // Tram missing, or we lost power. // Tram missing is always safe (green) set_signal_state(XING_STATE_GREEN, force = !is_operational) @@ -116,26 +116,32 @@ use_power(active_power_usage) + var/obj/structure/industrial_lift/tram/tram_part = tram.return_closest_platform_to(src) + + if(QDELETED(tram_part)) + set_signal_state(XING_STATE_GREEN, force = !is_operational) + return PROCESS_KILL + // Everything will be based on position and travel direction var/signal_pos var/tram_pos var/tram_velocity_sign // 1 for positive axis movement, -1 for negative // Try to be agnostic about N-S vs E-W movement - if(tram_part.travel_direction & (NORTH|SOUTH)) + if(tram.travel_direction & (NORTH|SOUTH)) signal_pos = y tram_pos = tram_part.y - tram_velocity_sign = tram_part.travel_direction & NORTH ? 1 : -1 + tram_velocity_sign = tram.travel_direction & NORTH ? 1 : -1 else signal_pos = x tram_pos = tram_part.x - tram_velocity_sign = tram_part.travel_direction & EAST ? 1 : -1 + tram_velocity_sign = tram.travel_direction & EAST ? 1 : -1 // How far away are we? negative if already passed. var/approach_distance = tram_velocity_sign * (signal_pos - tram_pos) // Check for stopped state. // Will kill the process since tram starting up will restart process. - if(!tram_part.travelling) + if(!tram.travelling) // if super close, show red anyway since tram could suddenly start moving if(abs(approach_distance) < red_distance_threshold) set_signal_state(XING_STATE_RED) diff --git a/code/modules/industrial_lift/industrial_lift.dm b/code/modules/industrial_lift/industrial_lift.dm new file mode 100644 index 00000000000..d7b3d9cfbec --- /dev/null +++ b/code/modules/industrial_lift/industrial_lift.dm @@ -0,0 +1,752 @@ +GLOBAL_LIST_EMPTY(lifts) + +/obj/structure/industrial_lift + name = "lift platform" + desc = "A lightweight lift platform. It moves up and down." + icon = 'icons/obj/smooth_structures/catwalk.dmi' + icon_state = "catwalk-0" + base_icon_state = "catwalk" + density = FALSE + anchored = TRUE + armor = list(MELEE = 50, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 80, ACID = 50) + max_integrity = 50 + layer = LATTICE_LAYER //under pipes + plane = FLOOR_PLANE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = list(SMOOTH_GROUP_INDUSTRIAL_LIFT) + canSmoothWith = list(SMOOTH_GROUP_INDUSTRIAL_LIFT) + obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN + appearance_flags = PIXEL_SCALE|KEEP_TOGETHER //no TILE_BOUND since we're potentially multitile + + ///ID used to determine what lift types we can merge with + var/lift_id = BASIC_LIFT_ID + + ///if true, the elevator works through floors + var/pass_through_floors = FALSE + + ///what movables on our platform that we are moving + var/list/atom/movable/lift_load + ///lazylist of weakrefs to the contents we have when we're first created. stored so that admins can clear the tram to its initial state + ///if someone put a bunch of stuff onto it. + var/list/datum/weakref/initial_contents + + ///what glide_size we set our moving contents to. + var/glide_size_override = 8 + ///lazy list of movables inside lift_load who had their glide_size changed since our last movement. + ///used so that we dont have to change the glide_size of every object every movement, which scales to cost more than you'd think + var/list/atom/movable/changed_gliders + + ///master datum that controls our movement. in general /industrial_lift subtypes control moving themselves, and + /// /datum/lift_master instances control moving the entire tram and any behavior associated with that. + var/datum/lift_master/lift_master_datum + ///what subtype of /datum/lift_master to create for itself if no other platform on this tram has created one yet. + ///very important for some behaviors since + var/lift_master_type = /datum/lift_master + + ///how many tiles this platform extends on the x axis + var/width = 1 + ///how many tiles this platform extends on the y axis (north-south not up-down, that would be the z axis) + var/height = 1 + + ///if TRUE, this platform will late initialize and then expand to become a multitile object across all other linked platforms on this z level + var/create_multitile_platform = FALSE + +/obj/structure/industrial_lift/Initialize(mapload) + . = ..() + GLOB.lifts.Add(src) + + set_movement_registrations() + + //since lift_master datums find all connected platforms when an industrial lift first creates it and then + //sets those platforms' lift_master_datum to itself, this check will only evaluate to true once per tram platform + if(!lift_master_datum && lift_master_type) + lift_master_datum = new lift_master_type(src) + return INITIALIZE_HINT_LATELOAD + +/obj/structure/industrial_lift/LateInitialize() + //after everything is initialized the lift master can order everything + lift_master_datum.order_platforms_by_z_level() + +/obj/structure/industrial_lift/Destroy() + GLOB.lifts.Remove(src) + lift_master_datum = null + return ..() + + +///set the movement registrations to our current turf(s) so contents moving out of our tile(s) are removed from our movement lists +/obj/structure/industrial_lift/proc/set_movement_registrations(list/turfs_to_set) + for(var/turf/turf_loc as anything in turfs_to_set || locs) + RegisterSignal(turf_loc, COMSIG_ATOM_EXITED, .proc/UncrossedRemoveItemFromLift) + RegisterSignal(turf_loc, list(COMSIG_ATOM_ENTERED,COMSIG_ATOM_INITIALIZED_ON), .proc/AddItemOnLift) + +///unset our movement registrations from turfs that no longer contain us (or every loc if turfs_to_unset is unspecified) +/obj/structure/industrial_lift/proc/unset_movement_registrations(list/turfs_to_unset) + var/static/list/registrations = list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED, COMSIG_ATOM_INITIALIZED_ON) + for(var/turf/turf_loc as anything in turfs_to_unset || locs) + UnregisterSignal(turf_loc, registrations) + + +/obj/structure/industrial_lift/proc/UncrossedRemoveItemFromLift(datum/source, atom/movable/gone, direction) + SIGNAL_HANDLER + if(!(gone.loc in locs)) + RemoveItemFromLift(gone) + +/obj/structure/industrial_lift/proc/RemoveItemFromLift(atom/movable/potential_rider) + SIGNAL_HANDLER + if(!(potential_rider in lift_load)) + return + if(isliving(potential_rider) && HAS_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED)) + REMOVE_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT) + LAZYREMOVE(lift_load, potential_rider) + LAZYREMOVE(changed_gliders, potential_rider) + UnregisterSignal(potential_rider, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE)) + +/obj/structure/industrial_lift/proc/AddItemOnLift(datum/source, atom/movable/new_lift_contents) + SIGNAL_HANDLER + var/static/list/blacklisted_types = typecacheof(list(/obj/structure/fluff/tram_rail, /obj/effect/decal/cleanable, /obj/structure/industrial_lift, /mob/camera)) + if(is_type_in_typecache(new_lift_contents, blacklisted_types) || new_lift_contents.invisibility == INVISIBILITY_ABSTRACT) //prevents the tram from stealing things like landmarks + return FALSE + if(new_lift_contents in lift_load) + return FALSE + + if(isliving(new_lift_contents) && !HAS_TRAIT(new_lift_contents, TRAIT_CANNOT_BE_UNBUCKLED)) + ADD_TRAIT(new_lift_contents, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT) + LAZYADD(lift_load, new_lift_contents) + RegisterSignal(new_lift_contents, COMSIG_PARENT_QDELETING, .proc/RemoveItemFromLift) + + return TRUE + +///adds everything on our tile that can be added to our lift_load and initial_contents lists when we're created +/obj/structure/industrial_lift/proc/add_initial_contents() + for(var/turf/turf_loc in locs) + for(var/atom/movable/movable_contents as anything in turf_loc) + if(movable_contents == src) + continue + + if(AddItemOnLift(src, movable_contents)) + + var/datum/weakref/new_initial_contents = WEAKREF(movable_contents) + if(!new_initial_contents) + continue + + LAZYADD(initial_contents, new_initial_contents) + +///signal handler for COMSIG_MOVABLE_UPDATE_GLIDE_SIZE: when a movable in lift_load changes its glide_size independently. +///adds that movable to a lazy list, movables in that list have their glide_size updated when the tram next moves +/obj/structure/industrial_lift/proc/on_changed_glide_size(atom/movable/moving_contents, new_glide_size) + SIGNAL_HANDLER + if(new_glide_size != glide_size_override) + LAZYADD(changed_gliders, moving_contents) + + +///make this tram platform multitile, expanding to cover all the tram platforms adjacent to us and deleting them. makes movement more efficient. +///the platform becoming multitile should be in the bottom left corner since thats assumed to be the loc of multitile objects +/obj/structure/industrial_lift/proc/create_multitile_platform(min_x, min_y, max_x, max_y, z) + + if(!(min_x && min_y && max_x && max_y && z)) + for(var/obj/structure/industrial_lift/other_lift as anything in lift_master_datum.lift_platforms) + if(other_lift.z != z) + continue + + min_x = min(min_x, other_lift.x) + max_x = max(max_x, other_lift.x) + + min_y = min(min_y, other_lift.y) + max_y = max(max_y, other_lift.y) + + var/turf/bottom_left_loc = locate(min_x, min_y, z) + var/obj/structure/industrial_lift/loc_corner_lift = locate() in bottom_left_loc + + if(!loc_corner_lift) + stack_trace("no lift in the bottom left corner of a lift level!") + return FALSE + + if(loc_corner_lift != src) + //the loc of a multitile object must always be the lower left corner + return loc_corner_lift.create_multitile_platform() + + width = (max_x - min_x) + 1 + height = (max_y - min_y) + 1 + + ///list of turfs we dont go over. if for whatever reason we encounter an already multitile lift platform + ///we add all of its locs to this list so we dont add that lift platform multiple times as we iterate through its locs + var/list/locs_to_skip = locs.Copy() + + bound_width = bound_width * width + bound_height = bound_height * height + + //multitile movement code assumes our loc is on the lower left corner of our bounding box + + var/first_x = 0 + var/first_y = 0 + + var/last_x = max(max_x - min_x, 0) + var/last_y = max(max_y - min_y, 0) + + for(var/y in first_y to last_y) + + var/y_pixel_offset = world.icon_size * y + + for(var/x in first_x to last_x) + + var/x_pixel_offset = world.icon_size * x + + var/turf/lift_turf = locate(x + min_x, y + min_y, z) + + if(!lift_turf) + continue + + if(lift_turf in locs_to_skip) + continue + + var/obj/structure/industrial_lift/other_lift = locate() in lift_turf + + if(!other_lift) + continue + + locs_to_skip += other_lift.locs.Copy()//make sure we never go over multitile platforms multiple times + + other_lift.pixel_x = x_pixel_offset + other_lift.pixel_y = y_pixel_offset + + overlays += other_lift + + //now we vore all the other lifts connected to us on our z level + for(var/obj/structure/industrial_lift/other_lift in lift_master_datum.lift_platforms) + if(other_lift == src || other_lift.z != z) + continue + + lift_master_datum.lift_platforms -= other_lift + if(other_lift.lift_load) + LAZYOR(lift_load, other_lift.lift_load) + if(other_lift.initial_contents) + LAZYOR(initial_contents, other_lift.initial_contents) + + qdel(other_lift) + + lift_master_datum.multitile_platform = TRUE + + var/turf/old_loc = loc + + forceMove(locate(min_x, min_y, z))//move to the lower left corner + set_movement_registrations(locs - old_loc) + return TRUE + +///returns an unordered list of all lift platforms adjacent to us. used so our lift_master_datum can control all connected platforms. +///includes platforms directly above or below us as well. only includes platforms with an identical lift_id to our own. +/obj/structure/industrial_lift/proc/lift_platform_expansion(datum/lift_master/lift_master_datum) + . = list() + for(var/direction in GLOB.cardinals_multiz) + var/obj/structure/industrial_lift/neighbor = locate() in get_step_multiz(src, direction) + if(!neighbor || neighbor.lift_id != lift_id) + continue + . += neighbor + +///main proc for moving the lift in the direction [going]. handles horizontal and/or vertical movement for multi platformed lifts and multitile lifts. +/obj/structure/industrial_lift/proc/travel(going) + var/list/things_to_move = lift_load + var/turf/destination + if(!isturf(going)) + destination = get_step_multiz(src, going) + else + destination = going + going = get_dir_multiz(loc, going) + + var/x_offset = ROUND_UP(bound_width / 32) - 1 //how many tiles our horizontally farthest edge is from us + var/y_offset = ROUND_UP(bound_height / 32) - 1 //how many tiles our vertically farthest edge is from us + + //the x coordinate of the edge furthest from our future destination, which would be our right hand side + var/back_edge_x = destination.x + x_offset//if we arent multitile this should just be destination.x + var/top_edge_y = destination.y + y_offset + + var/turf/top_right_corner = locate(min(world.maxx, back_edge_x), min(world.maxy, top_edge_y), destination.z) + + var/list/dest_locs = block( + destination, + top_right_corner + ) + + var/list/entering_locs = dest_locs - locs + var/list/exited_locs = locs - dest_locs + + if(going == DOWN) + for(var/turf/dest_turf as anything in entering_locs) + SEND_SIGNAL(dest_turf, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move) + + if(istype(dest_turf, /turf/closed/wall)) + var/turf/closed/wall/C = dest_turf + do_sparks(2, FALSE, C) + C.dismantle_wall(devastated = TRUE) + for(var/mob/M in urange(8, src)) + shake_camera(M, 2, 3) + playsound(C, 'sound/effects/meteorimpact.ogg', 100, TRUE) + + for(var/mob/living/crushed in dest_turf.contents) + to_chat(crushed, span_userdanger("You are crushed by [src]!")) + crushed.gib(FALSE,FALSE,FALSE)//the nicest kind of gibbing, keeping everything intact. + + else if(going == UP) + for(var/turf/dest_turf as anything in entering_locs) + ///handles any special interactions objects could have with the lift/tram, handled on the item itself + SEND_SIGNAL(dest_turf, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move) + + if(istype(dest_turf, /turf/closed/wall)) + var/turf/closed/wall/C = dest_turf + do_sparks(2, FALSE, C) + C.dismantle_wall(devastated = TRUE) + for(var/mob/client_mob in SSspatial_grid.orthogonal_range_search(src, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS, 8)) + shake_camera(client_mob, 2, 3) + playsound(C, 'sound/effects/meteorimpact.ogg', 100, TRUE) + + else + ///potentially finds a spot to throw the victim at for daring to be hit by a tram. is null if we havent found anything to throw + var/atom/throw_target + var/datum/lift_master/tram/our_lift = lift_master_datum + var/collision_lethality = our_lift.collision_lethality + + for(var/turf/dest_turf as anything in entering_locs) + ///handles any special interactions objects could have with the lift/tram, handled on the item itself + SEND_SIGNAL(dest_turf, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move) + + if(istype(dest_turf, /turf/closed/wall)) + var/turf/closed/wall/collided_wall = dest_turf + do_sparks(2, FALSE, collided_wall) + collided_wall.dismantle_wall(devastated = TRUE) + for(var/mob/client_mob in SSspatial_grid.orthogonal_range_search(collided_wall, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS, 8)) + if(get_dist(dest_turf, client_mob) <= 8) + shake_camera(client_mob, 2, 3) + + playsound(collided_wall, 'sound/effects/meteorimpact.ogg', 100, TRUE) + + for(var/obj/structure/victim_structure in dest_turf.contents) + if(QDELING(victim_structure)) + continue + if(!is_type_in_typecache(victim_structure, lift_master_datum.ignored_smashthroughs) && victim_structure.layer >= LOW_OBJ_LAYER) + + if(victim_structure.anchored && initial(victim_structure.anchored) == TRUE) + visible_message(span_danger("[src] smashes through [victim_structure]!")) + victim_structure.deconstruct(FALSE) + + else + if(!throw_target) + throw_target = get_edge_target_turf(src, turn(going, pick(45, -45))) + visible_message(span_danger("[src] violently rams [victim_structure] out of the way!")) + victim_structure.anchored = FALSE + victim_structure.take_damage(rand(20, 25) * collision_lethality) + victim_structure.throw_at(throw_target, 200 * collision_lethality, 4 * collision_lethality) + + for(var/obj/machinery/victim_machine in dest_turf.contents) + if(QDELING(victim_machine)) + continue + if(is_type_in_typecache(victim_machine, lift_master_datum.ignored_smashthroughs)) + continue + if(istype(victim_machine, /obj/machinery/field)) //graceful break handles this scenario + continue + if(victim_machine.layer >= LOW_OBJ_LAYER) //avoids stuff that is probably flush with the ground + playsound(src, 'sound/effects/bang.ogg', 50, TRUE) + visible_message(span_danger("[src] smashes through [victim_machine]!")) + qdel(victim_machine) + + for(var/mob/living/collided in dest_turf.contents) + if(lift_master_datum.ignored_smashthroughs[collided.type]) + continue + to_chat(collided, span_userdanger("[src] collides into you!")) + playsound(src, 'sound/effects/splat.ogg', 50, TRUE) + var/damage = rand(5, 10) * collision_lethality + collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_HEAD) + collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_CHEST) + collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_LEG) + collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_LEG) + collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_ARM) + collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_ARM) + + if(QDELETED(collided)) //in case it was a mob that dels on death + continue + if(!throw_target) + throw_target = get_edge_target_turf(src, turn(going, pick(45, -45))) + + var/turf/T = get_turf(collided) + T.add_mob_blood(collided) + + collided.throw_at() + //if going EAST, will turn to the NORTHEAST or SOUTHEAST and throw the ran over guy away + var/datum/callback/land_slam = new(collided, /mob/living/.proc/tram_slam_land) + collided.throw_at(throw_target, 200 * collision_lethality, 4 * collision_lethality, callback = land_slam) + + unset_movement_registrations(exited_locs) + group_move(things_to_move, going) + set_movement_registrations(entering_locs) + +///move the movers list of movables on our tile to destination if we successfully move there first. +///this is like calling forceMove() on everything in movers and ourselves, except nothing in movers +///has destination.Entered() and origin.Exited() called on them, as only our movement can be perceived. +///none of the movers are able to react to the movement of any other mover, saving a lot of needless processing cost +///and is more sensible. without this, if you and a banana are on the same platform, when that platform moves you will slip +///on the banana even if youre not moving relative to it. +/obj/structure/industrial_lift/proc/group_move(list/atom/movable/movers, movement_direction) + if(movement_direction == NONE) + stack_trace("an industrial lift was told to move to somewhere it already is!") + return FALSE + + var/turf/our_dest = get_step(src, movement_direction) + + var/area/our_area = get_area(src) + var/area/their_area = get_area(our_dest) + var/different_areas = our_area != their_area + var/turf/mover_old_loc + + if(glide_size != glide_size_override) + set_glide_size(glide_size_override) + + forceMove(our_dest) + if(loc != our_dest || QDELETED(src))//check if our movement succeeded, if it didnt then the movers cant be moved + return FALSE + + for(var/atom/movable/mover as anything in changed_gliders) + if(mover.glide_size != glide_size_override) + mover.set_glide_size(glide_size_override) + + LAZYREMOVE(changed_gliders, mover) + + if(different_areas) + for(var/atom/movable/mover as anything in movers) + if(QDELETED(mover)) + movers -= mover + continue + + //we dont need to call Entered() and Exited() for origin and destination here for each mover because + //all of them are considered to be on top of us, so the turfs and anything on them can only perceive us, + //which is why the platform itself uses forceMove() + mover_old_loc = mover.loc + + our_area.Exited(mover, movement_direction) + mover.loc = get_step(mover, movement_direction) + their_area.Entered(mover, movement_direction) + + mover.Moved(mover_old_loc, movement_direction, TRUE, null, FALSE) + + else + for(var/atom/movable/mover as anything in movers) + if(QDELETED(mover)) + movers -= mover + continue + + mover_old_loc = mover.loc + mover.loc = get_step(mover, movement_direction) + + mover.Moved(mover_old_loc, movement_direction, TRUE, null, FALSE) + + return TRUE + +/** + * reset the contents of this lift platform to its original state in case someone put too much shit on it. + * used by an admin via calling reset_lift_contents() on our lift_master_datum. + * + * Arguments: + * * consider_anything_past - number. if > 0 this platform will only handle foreign contents that exceed this number on each of our locs + * * foreign_objects - bool. if true this platform will consider /atom/movable's that arent mobs as part of foreign contents + * * foreign_non_player_mobs - bool. if true we consider mobs that dont have a mind to be foreign + * * consider_player_mobs - bool. if true we consider player mobs to be foreign. only works if foreign_non_player_mobs is true as well + */ +/obj/structure/industrial_lift/proc/reset_contents(consider_anything_past = 0, foreign_objects = TRUE, foreign_non_player_mobs = TRUE, consider_player_mobs = FALSE) + if(!foreign_objects && !foreign_non_player_mobs && !consider_player_mobs) + return FALSE + + consider_anything_past = isnum(consider_anything_past) ? max(consider_anything_past, 0) : 0 + //just in case someone fucks up the arguments + + if(consider_anything_past && length(lift_load) <= consider_anything_past) + return FALSE + + ///list of resolve()'d initial_contents that are still in lift_load + var/list/atom/movable/original_contents = list(src) + + ///list of objects we consider foreign according to the given arguments + var/list/atom/movable/foreign_contents = list() + + + for(var/datum/weakref/initial_contents_ref as anything in initial_contents) + if(!initial_contents_ref) + continue + + var/atom/movable/resolved_contents = initial_contents_ref.resolve() + + if(!resolved_contents) + continue + + if(!(resolved_contents in lift_load)) + continue + + original_contents += resolved_contents + + for(var/turf/turf_loc as anything in locs) + var/list/atom/movable/foreign_contents_in_loc = list() + + for(var/atom/movable/foreign_movable as anything in (turf_loc.contents - original_contents)) + if(foreign_objects && ismovable(foreign_movable) && !ismob(foreign_movable)) + foreign_contents_in_loc += foreign_movable + continue + + if(foreign_non_player_mobs && ismob(foreign_movable)) + var/mob/foreign_mob = foreign_movable + if(consider_player_mobs || !foreign_mob.mind) + foreign_contents_in_loc += foreign_mob + continue + + if(consider_anything_past) + foreign_contents_in_loc.len -= consider_anything_past + //hey cool this works, neat. this takes from the opposite side of the list that youd expect but its easy so idc + //also this means that if you use consider_anything_past then foreign mobs are less likely to be deleted than foreign objects + //because internally the contents list is 2 linked lists of obj contents - mob contents, thus mobs are always last in the order + //when you iterate it. + + foreign_contents += foreign_contents_in_loc + + for(var/atom/movable/contents_to_delete as anything in foreign_contents) + qdel(contents_to_delete) + + return TRUE + +/obj/structure/industrial_lift/proc/use(mob/living/user) + if(!isliving(user) || !in_range(src, user) || user.combat_mode) + return + + var/list/tool_list = list() + if(lift_master_datum.Check_lift_move(UP)) + tool_list["Up"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH) + if(lift_master_datum.Check_lift_move(DOWN)) + tool_list["Down"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH) + if(!length(tool_list)) + to_chat(user, span_warning("[src] doesn't seem to able to move anywhere!")) + add_fingerprint(user) + return + if(lift_master_datum.controls_locked) + to_chat(user, span_warning("[src] has its controls locked! It must already be trying to do something!")) + add_fingerprint(user) + return + var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user, src.loc), require_near = TRUE, tooltips = TRUE) + if(!isliving(user) || !in_range(src, user) || user.combat_mode) + return //nice try + switch(result) + if("Up") + // We have to make sure that they don't do illegal actions by not having their radial menu refresh from someone else moving the lift. + if(!lift_master_datum.Check_lift_move(UP)) + to_chat(user, span_warning("[src] doesn't seem to able to move up!")) + add_fingerprint(user) + return + lift_master_datum.MoveLift(UP, user) + show_fluff_message(TRUE, user) + use(user) + if("Down") + if(!lift_master_datum.Check_lift_move(DOWN)) + to_chat(user, span_warning("[src] doesn't seem to able to move down!")) + add_fingerprint(user) + return + lift_master_datum.MoveLift(DOWN, user) + show_fluff_message(FALSE, user) + use(user) + if("Cancel") + return + add_fingerprint(user) + +/** + * Proc to ensure that the radial menu closes when it should. + * Arguments: + * * user - The person that opened the menu. + * * starting_loc - The location of the lift when the menu was opened, used to prevent the menu from being interacted with after the lift was moved by someone else. + * + * Returns: + * * boolean, FALSE if the menu should be closed, TRUE if the menu is clear to stay opened. + */ +/obj/structure/industrial_lift/proc/check_menu(mob/user, starting_loc) + if(user.incapacitated() || !user.Adjacent(src) || starting_loc != src.loc) + return FALSE + return TRUE + +/obj/structure/industrial_lift/attack_hand(mob/user, list/modifiers) + . = ..() + if(.) + return + use(user) + +//ai probably shouldn't get to use lifts but they sure are great for admins to crush people with +/obj/structure/industrial_lift/attack_ghost(mob/user) + . = ..() + if(.) + return + if(isAdminGhostAI(user)) + use(user) + +/obj/structure/industrial_lift/attack_paw(mob/user, list/modifiers) + return use(user) + +/obj/structure/industrial_lift/attackby(obj/item/W, mob/user, params) + return use(user) + +/obj/structure/industrial_lift/attack_robot(mob/living/silicon/robot/R) + if(R.Adjacent(src)) + return use(R) + +/** + * Shows a message indicating that the lift has moved up or down. + * Arguments: + * * going_up - Boolean on whether or not we're going up, to adjust the message appropriately. + * * user - The mob that caused the lift to move, for the visible message. + */ +/obj/structure/industrial_lift/proc/show_fluff_message(going_up, mob/user) + if(going_up) + user.visible_message(span_notice("[user] moves the lift upwards."), span_notice("You move the lift upwards.")) + else + user.visible_message(span_notice("[user] moves the lift downwards."), span_notice("You move the lift downwards.")) + +/obj/structure/industrial_lift/debug + name = "transport platform" + desc = "A lightweight platform. It moves in any direction, except up and down." + color = "#5286b9ff" + lift_id = DEBUG_LIFT_ID + +/obj/structure/industrial_lift/debug/use(mob/user) + if (!in_range(src, user)) + return +//NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST + var/static/list/tool_list = list( + "NORTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), + "NORTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), + "EAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST), + "SOUTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST), + "SOUTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH), + "SOUTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH), + "WEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST), + "NORTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST) + ) + + var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = FALSE) + if (!in_range(src, user)) + return // nice try + + switch(result) + if("NORTH") + lift_master_datum.MoveLiftHorizontal(NORTH, z) + use(user) + if("NORTHEAST") + lift_master_datum.MoveLiftHorizontal(NORTHEAST, z) + use(user) + if("EAST") + lift_master_datum.MoveLiftHorizontal(EAST, z) + use(user) + if("SOUTHEAST") + lift_master_datum.MoveLiftHorizontal(SOUTHEAST, z) + use(user) + if("SOUTH") + lift_master_datum.MoveLiftHorizontal(SOUTH, z) + use(user) + if("SOUTHWEST") + lift_master_datum.MoveLiftHorizontal(SOUTHWEST, z) + use(user) + if("WEST") + lift_master_datum.MoveLiftHorizontal(WEST, z) + use(user) + if("NORTHWEST") + lift_master_datum.MoveLiftHorizontal(NORTHWEST, z) + use(user) + if("Cancel") + return + + add_fingerprint(user) + +/obj/structure/industrial_lift/tram + name = "tram" + desc = "A tram for tramversing the station." + icon = 'icons/turf/floors.dmi' + icon_state = "titanium_yellow" + base_icon_state = null + smoothing_flags = NONE + smoothing_groups = null + canSmoothWith = null + //kind of a centerpiece of the station, so pretty tough to destroy + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + + lift_id = TRAM_LIFT_ID + lift_master_type = /datum/lift_master/tram + + /// Set by the tram control console in late initialize + var/travelling = FALSE + + //the following are only used to give to the lift_master datum when it's first created + + ///decisecond delay between horizontal movements. cannot make the tram move faster than 1 movement per world.tick_lag. only used to give to the lift_master + var/horizontal_speed = 0.5 + + create_multitile_platform = TRUE + +/obj/structure/industrial_lift/tram/AddItemOnLift(datum/source, atom/movable/AM) + . = ..() + if(travelling) + on_changed_glide_size(AM, AM.glide_size) + +/obj/structure/industrial_lift/tram/proc/set_travelling(travelling) + if (src.travelling == travelling) + return + + for(var/atom/movable/glider as anything in lift_load) + if(travelling) + glider.set_glide_size(glide_size_override) + RegisterSignal(glider, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, .proc/on_changed_glide_size) + else + LAZYREMOVE(changed_gliders, glider) + UnregisterSignal(glider, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE) + + src.travelling = travelling + SEND_SIGNAL(src, COMSIG_TRAM_SET_TRAVELLING, travelling) + +/obj/structure/industrial_lift/tram/use(mob/user) //dont click the floor dingus we use computers now + return + +/obj/structure/industrial_lift/tram/set_currently_z_moving() + return FALSE //trams can never z fall and shouldnt waste any processing time trying to do so + +/** + * Handles unlocking the tram controls for use after moving + * + * More safety checks to make sure the tram has actually docked properly + * at a location before users are allowed to interact with the tram console again. + * Tram finds its location at this point before fully unlocking controls to the user. + */ +/obj/structure/industrial_lift/tram/proc/unlock_controls() + visible_message(span_notice("[src]'s controls are now unlocked.")) + for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_master_datum.lift_platforms) //only thing everyone needs to know is the new location. + tram_part.set_travelling(FALSE) + lift_master_datum.set_controls(LIFT_PLATFORM_UNLOCKED) + +///debug proc to highlight the locs of the tram platform +/obj/structure/industrial_lift/tram/proc/find_dimensions(iterations = 100) + message_admins("num turfs: [length(locs)]") + + var/overlay = /obj/effect/overlay/ai_detect_hud + var/list/turfs = list() + + for(var/turf/our_turf as anything in locs) + new overlay(our_turf) + turfs += our_turf + + addtimer(CALLBACK(src, .proc/clear_turfs, turfs, iterations), 1) + +/obj/structure/industrial_lift/tram/proc/clear_turfs(list/turfs_to_clear, iterations) + for(var/turf/our_old_turf as anything in turfs_to_clear) + var/obj/effect/overlay/ai_detect_hud/hud = locate() in our_old_turf + if(hud) + qdel(hud) + + var/overlay = /obj/effect/overlay/ai_detect_hud + + for(var/turf/our_turf as anything in locs) + new overlay(our_turf) + + iterations-- + + var/list/turfs = list() + for(var/turf/our_turf as anything in locs) + turfs += our_turf + + if(iterations) + addtimer(CALLBACK(src, .proc/clear_turfs, turfs, iterations), 1) diff --git a/code/modules/industrial_lift/lift_master.dm b/code/modules/industrial_lift/lift_master.dm new file mode 100644 index 00000000000..45c2ae3b81a --- /dev/null +++ b/code/modules/industrial_lift/lift_master.dm @@ -0,0 +1,397 @@ +///associative list of the form: list(lift_id = list(all lift_master datums attached to lifts of that type)) +GLOBAL_LIST_EMPTY(active_lifts_by_type) + +///coordinate and control movement across linked industrial_lift's. allows moving large single multitile platforms and many 1 tile platforms. +///also is capable of linking platforms across linked z levels +/datum/lift_master + ///the lift platforms we consider as part of this lift. ordered in order of lowest z level to highest z level after init. + ///(the sorting algorithm sucks btw) + var/list/obj/structure/industrial_lift/lift_platforms + + /// Typepath list of what to ignore smashing through, controls all lifts + var/static/list/ignored_smashthroughs = list( + /obj/machinery/power/supermatter_crystal, + /obj/structure/holosign, + /obj/machinery/field, + ) + + ///whether the lift handled by this lift_master datum is multitile as opposed to nxm platforms per z level + var/multitile_platform = FALSE + + ///taken from our lift platforms. if true we go through each z level of platforms and attempt to make the lowest left corner platform + ///into one giant multitile object the size of all other platforms on that z level. + var/create_multitile_platform = FALSE + + ///lift platforms have already been sorted in order of z level. + var/z_sorted = FALSE + + ///lift_id taken from our base lift platform, used to put us into GLOB.active_lifts_by_type + var/lift_id = BASIC_LIFT_ID + + ///overridable ID string to link control units to this specific lift_master datum. created by placing a lift id landmark object + ///somewhere on the tram, if its anywhere on the tram we'll find it in init and set this to whatever it specifies + var/specific_lift_id + ///what directions we're allowed to move + var/allowed_travel_directions = ALL + + ///if true, the lift cannot be manually moved. + var/controls_locked = FALSE + +/datum/lift_master/New(obj/structure/industrial_lift/lift_platform) + lift_id = lift_platform.lift_id + create_multitile_platform = lift_platform.create_multitile_platform + + Rebuild_lift_plaform(lift_platform) + ignored_smashthroughs = typecacheof(ignored_smashthroughs) + + LAZYADDASSOCLIST(GLOB.active_lifts_by_type, lift_id, src) + + for(var/obj/structure/industrial_lift/lift as anything in lift_platforms) + lift.add_initial_contents() + +/datum/lift_master/Destroy() + for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms) + lift_platform.lift_master_datum = null + lift_platforms = null + + LAZYREMOVEASSOC(GLOB.active_lifts_by_type, lift_id, src) + if(isnull(GLOB.active_lifts_by_type)) + GLOB.active_lifts_by_type = list()//im lazy + + return ..() + +/datum/lift_master/proc/add_lift_platforms(obj/structure/industrial_lift/new_lift_platform) + if(new_lift_platform in lift_platforms) + return + for(var/obj/structure/industrial_lift/other_platform in new_lift_platform.loc) + if(other_platform != new_lift_platform) + stack_trace("there is more than one lift platform on a tile when a lift_master adds it. this causes problems") + qdel(other_platform) + + new_lift_platform.lift_master_datum = src + LAZYADD(lift_platforms, new_lift_platform) + RegisterSignal(new_lift_platform, COMSIG_PARENT_QDELETING, .proc/remove_lift_platforms) + + check_for_landmarks(new_lift_platform) + + if(z_sorted)//make sure we dont lose z ordering if we get additional platforms after init + order_platforms_by_z_level() + +/datum/lift_master/proc/remove_lift_platforms(obj/structure/industrial_lift/old_lift_platform) + SIGNAL_HANDLER + + if(!(old_lift_platform in lift_platforms)) + return + + old_lift_platform.lift_master_datum = null + LAZYREMOVE(lift_platforms, old_lift_platform) + UnregisterSignal(old_lift_platform, COMSIG_PARENT_QDELETING) + if(!length(lift_platforms)) + qdel(src) + +///Collect all bordered platforms via a simple floodfill algorithm. allows multiz trams because its funny +/datum/lift_master/proc/Rebuild_lift_plaform(obj/structure/industrial_lift/base_lift_platform) + add_lift_platforms(base_lift_platform) + var/list/possible_expansions = list(base_lift_platform) + + while(possible_expansions.len) + for(var/obj/structure/industrial_lift/borderline as anything in possible_expansions) + var/list/result = borderline.lift_platform_expansion(src) + if(length(result)) + for(var/obj/structure/industrial_lift/lift_platform as anything in result) + if(lift_platforms.Find(lift_platform)) + continue + + add_lift_platforms(lift_platform) + possible_expansions |= lift_platform + + possible_expansions -= borderline + +///check for any landmarks placed inside the locs of the given lift_platform +/datum/lift_master/proc/check_for_landmarks(obj/structure/industrial_lift/new_lift_platform) + SHOULD_CALL_PARENT(TRUE) + + for(var/turf/platform_loc as anything in new_lift_platform.locs) + var/obj/effect/landmark/lift_id/id_giver = locate() in platform_loc + + if(id_giver) + set_info_from_id_landmark(id_giver) + +///set vars and such given an overriding lift_id landmark +/datum/lift_master/proc/set_info_from_id_landmark(obj/effect/landmark/lift_id/landmark) + SHOULD_CALL_PARENT(TRUE) + + if(!istype(landmark, /obj/effect/landmark/lift_id))//lift_master subtypes can want differnet id's than the base type wants + return + + if(landmark.specific_lift_id) + specific_lift_id = landmark.specific_lift_id + + qdel(landmark) + +///orders the lift platforms in order of lowest z level to highest z level. +/datum/lift_master/proc/order_platforms_by_z_level() + //contains nested lists for every z level in the world. why? because its really easy to sort + var/list/platforms_by_z = list() + platforms_by_z.len = world.maxz + + for(var/z in 1 to world.maxz) + platforms_by_z[z] = list() + + for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms) + if(QDELETED(lift_platform) || !lift_platform.z) + lift_platforms -= lift_platform + continue + + platforms_by_z[lift_platform.z] += lift_platform + + if(create_multitile_platform) + for(var/list/z_list as anything in platforms_by_z) + if(!length(z_list)) + continue + + create_multitile_platform_for_z_level(z_list)//this will subtract all but one platform from the list + + var/list/output = list() + + for(var/list/z_list as anything in platforms_by_z) + output += z_list + + lift_platforms = output + + z_sorted = TRUE + +///goes through all platforms in the given list and finds the one in the lower left corner +/datum/lift_master/proc/create_multitile_platform_for_z_level(list/obj/structure/industrial_lift/platforms_in_z) + var/min_x = INFINITY + var/max_x = 0 + + var/min_y = INFINITY + var/max_y = 0 + + var/z = 0 + + for(var/obj/structure/industrial_lift/lift_to_sort as anything in platforms_in_z) + if(!z) + if(!lift_to_sort.z) + stack_trace("create_multitile_platform_for_z_level() was given a platform in nullspace or not on a turf!") + platforms_in_z -= lift_to_sort + continue + + z = lift_to_sort.z + + if(z != lift_to_sort.z) + stack_trace("create_multitile_platform_for_z_level() was given lifts on different z levels!") + platforms_in_z -= lift_to_sort + continue + + min_x = min(min_x, lift_to_sort.x) + max_x = max(max_x, lift_to_sort.x) + + min_y = min(min_y, lift_to_sort.y) + max_y = max(max_y, lift_to_sort.y) + + var/turf/lower_left_corner_loc = locate(min_x, min_y, z) + if(!lower_left_corner_loc) + CRASH("was unable to find a turf at the lower left corner of this z") + + var/obj/structure/industrial_lift/lower_left_corner_lift = locate() in lower_left_corner_loc + + if(!lower_left_corner_lift) + CRASH("there was no lift in the lower left corner of the given lifts") + + platforms_in_z.Cut() + platforms_in_z += lower_left_corner_lift//we want to change the list given to us not create a new one. so we do this + + lower_left_corner_lift.create_multitile_platform(min_x, min_y, max_x, max_y, z) + +///returns the closest lift to the specified atom, prioritizing lifts on the same z level. used for comparing distance +/datum/lift_master/proc/return_closest_platform_to(atom/comparison, allow_multiple_answers = FALSE) + if(!istype(comparison) || !comparison.z) + return FALSE + + var/list/obj/structure/industrial_lift/candidate_platforms = list() + + for(var/obj/structure/industrial_lift/platform as anything in lift_platforms) + if(platform.z == comparison.z) + candidate_platforms += platform + + var/obj/structure/industrial_lift/winner = candidate_platforms[1] + var/winner_distance = get_dist(comparison, winner) + + var/list/tied_winners = list(winner) + + for(var/obj/structure/industrial_lift/platform_to_sort as anything in candidate_platforms) + var/platform_distance = get_dist(comparison, platform_to_sort) + + if(platform_distance < winner_distance) + winner = platform_to_sort + winner_distance = platform_distance + + if(allow_multiple_answers) + tied_winners = list(winner) + + else if(platform_distance == winner_distance && allow_multiple_answers) + tied_winners += platform_to_sort + + if(allow_multiple_answers) + return tied_winners + + return winner + +///returns all industrial_lifts associated with this tram on the given z level or given atoms z level +/datum/lift_master/proc/get_platforms_on_level(atom/atom_reference_OR_z_level_number) + var/z = atom_reference_OR_z_level_number + if(isatom(atom_reference_OR_z_level_number)) + z = atom_reference_OR_z_level_number.z + + if(!isnum(z) || z < 0 || z > world.maxz) + return null + + var/list/platforms_in_z = list() + + for(var/obj/structure/industrial_lift/lift_to_check as anything in lift_platforms) + if(lift_to_check.z) + platforms_in_z += lift_to_check + + return platforms_in_z + +/** + * Moves the lift UP or DOWN, this is what users invoke with their hand. + * This is a SAFE proc, ensuring every part of the lift moves SANELY. + * It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming. + * Arguments: + * going - UP or DOWN directions, where the lift should go. Keep in mind by this point checks of whether it should go up or down have already been done. + * user - Whomever made the lift movement. + */ +/datum/lift_master/proc/MoveLift(going, mob/user) + set_controls(LIFT_PLATFORM_LOCKED) + //lift_platforms are sorted in order of lowest z to highest z, so going upwards we need to move them in reverse order to not collide + if(going == UP) + var/obj/structure/industrial_lift/platform_to_move + var/current_index = length(lift_platforms) + + while(current_index > 0) + platform_to_move = lift_platforms[current_index] + current_index-- + + platform_to_move.travel(going) + + else if(going == DOWN) + for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms) + lift_platform.travel(going) + set_controls(LIFT_PLATFORM_UNLOCKED) + +/** + * Moves the lift, this is what users invoke with their hand. + * This is a SAFE proc, ensuring every part of the lift moves SANELY. + * It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming. + */ +/datum/lift_master/proc/MoveLiftHorizontal(going) + set_controls(LIFT_PLATFORM_LOCKED) + + if(multitile_platform) + for(var/obj/structure/industrial_lift/platform_to_move as anything in lift_platforms) + platform_to_move.travel(going) + + set_controls(LIFT_PLATFORM_UNLOCKED) + return + + var/max_x = 0 + var/max_y = 0 + var/max_z = 0 + var/min_x = world.maxx + var/min_y = world.maxy + var/min_z = world.maxz + + for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms) + max_z = max(max_z, lift_platform.z) + min_z = min(min_z, lift_platform.z) + + min_x = min(min_x, lift_platform.x) + max_x = max(max_x, lift_platform.x) + //this assumes that all z levels have identical horizontal bounding boxes + //but if youre still using a non multitile tram platform at this point + //then its your own problem. it wont runtime it will jsut be slower than it needs to be if this assumption isnt + //the case + + min_y = min(min_y, lift_platform.y) + max_y = max(max_y, lift_platform.y) + + for(var/z in min_z to max_z) + //This must be safe way to border tile to tile move of bordered platforms, that excludes platform overlapping. + if(going & WEST) + //Go along the X axis from min to max, from left to right + for(var/x in min_x to max_x) + if(going & NORTH) + //Go along the Y axis from max to min, from up to down + for(var/y in max_y to min_y step -1) + var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) + lift_platform?.travel(going) + + else if(going & SOUTH) + //Go along the Y axis from min to max, from down to up + for(var/y in min_y to max_y) + var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) + lift_platform?.travel(going) + + else + for(var/y in min_y to max_y) + var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) + lift_platform?.travel(going) + else + //Go along the X axis from max to min, from right to left + for(var/x in max_x to min_x step -1) + if(going & NORTH) + //Go along the Y axis from max to min, from up to down + for(var/y in max_y to min_y step -1) + var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) + lift_platform?.travel(going) + + else if (going & SOUTH) + for(var/y in min_y to max_y) + var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) + lift_platform?.travel(going) + + else + //Go along the Y axis from min to max, from down to up + for(var/y in min_y to max_y) + var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) + lift_platform?.travel(going) + + set_controls(LIFT_PLATFORM_UNLOCKED) + +///Check destination turfs +/datum/lift_master/proc/Check_lift_move(check_dir) + for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms) + for(var/turf/bound_turf in lift_platform.locs) + var/turf/T = get_step_multiz(lift_platform, check_dir) + if(!T)//the edges of multi-z maps + return FALSE + if(check_dir == UP && !istype(T, /turf/open/openspace)) // We don't want to go through the ceiling! + return FALSE + if(check_dir == DOWN && !istype(get_turf(lift_platform), /turf/open/openspace)) // No going through the floor! + return FALSE + return TRUE + +/** + * Sets all lift parts's controls_locked variable. Used to prevent moving mid movement, or cooldowns. + */ +/datum/lift_master/proc/set_controls(state) + controls_locked = state + +/** + * resets the contents of all platforms to their original state in case someone put a bunch of shit onto the tram. + * intended to be called by admins. passes all arguments to reset_contents() for each of our platforms. + * + * Arguments: + * * consider_anything_past - number. if > 0 our platforms will only handle foreign contents that exceed this number in each of their locs + * * foreign_objects - bool. if true our platforms will consider /atom/movable's that arent mobs as part of foreign contents + * * foreign_non_player_mobs - bool. if true our platforms consider mobs that dont have a mind to be foreign + * * consider_player_mobs - bool. if true our platforms consider player mobs to be foreign. only works if foreign_non_player_mobs is true as well + */ +/datum/lift_master/proc/reset_lift_contents(consider_anything_past = 0, foreign_objects = TRUE, foreign_non_player_mobs = TRUE, consider_player_mobs = FALSE) + for(var/obj/structure/industrial_lift/lift_to_reset in lift_platforms) + lift_to_reset.reset_contents(consider_anything_past, foreign_objects, foreign_non_player_mobs, consider_player_mobs) + + return TRUE diff --git a/code/game/machinery/computer/tram_controls.dm b/code/modules/industrial_lift/tram_controls.dm similarity index 79% rename from code/game/machinery/computer/tram_controls.dm rename to code/modules/industrial_lift/tram_controls.dm index 957547569be..a55ff5b18f4 100644 --- a/code/game/machinery/computer/tram_controls.dm +++ b/code/modules/industrial_lift/tram_controls.dm @@ -6,12 +6,14 @@ circuit = /obj/item/circuitboard/computer/tram_controls flags_1 = NODECONSTRUCT_1 | SUPERMATTER_IGNORES_1 resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - light_color = LIGHT_COLOR_GREEN - ///The ID of the tram we control - var/tram_id = "tram_station" + + light_range = 0 //we dont want to spam SSlighting with source updates every movement + ///Weakref to the tram piece we control var/datum/weakref/tram_ref + var/specific_lift_id = MAIN_STATION_TRAM + /obj/machinery/computer/tram_controls/Initialize(mapload, obj/item/circuitboard/C) . = ..() AddComponent(/datum/component/usb_port, list(/obj/item/circuit_component/tram_controls)) @@ -19,7 +21,6 @@ /obj/machinery/computer/tram_controls/LateInitialize() . = ..() - //find the tram, late so the tram is all... set up so when this is called? i'm seriously stupid and 90% of what i do consists of barely educated guessing :) find_tram() /** @@ -28,19 +29,17 @@ * Locates tram parts in the lift global list after everything is done. */ /obj/machinery/computer/tram_controls/proc/find_tram() - for(var/obj/structure/industrial_lift/tram/central/tram as anything in GLOB.central_trams) - if(tram.tram_id != tram_id) - continue - tram_ref = WEAKREF(tram) - break + for(var/datum/lift_master/lift as anything in GLOB.active_lifts_by_type[TRAM_LIFT_ID]) + if(lift.specific_lift_id == specific_lift_id) + tram_ref = WEAKREF(lift) /obj/machinery/computer/tram_controls/ui_state(mob/user) return GLOB.not_incapacitated_state /obj/machinery/computer/tram_controls/ui_status(mob/user,/datum/tgui/ui) - var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve() + var/datum/lift_master/tram/tram = tram_ref?.resolve() - if(tram_part?.travelling) + if(tram?.travelling) return UI_CLOSE if(!in_range(user, src) && !isobserver(user)) return UI_CLOSE @@ -54,11 +53,11 @@ ui.open() /obj/machinery/computer/tram_controls/ui_data(mob/user) - var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve() + var/datum/lift_master/tram/tram_lift = tram_ref?.resolve() var/list/data = list() - data["moving"] = tram_part?.travelling - data["broken"] = tram_part ? FALSE : TRUE - var/obj/effect/landmark/tram/current_loc = tram_part?.from_where + data["moving"] = tram_lift?.travelling + data["broken"] = tram_lift ? FALSE : TRUE + var/obj/effect/landmark/tram/current_loc = tram_lift?.from_where if(current_loc) data["tram_location"] = current_loc.name return data @@ -77,9 +76,7 @@ */ /obj/machinery/computer/tram_controls/proc/get_destinations() . = list() - for(var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks) - if(destination.tram_id != tram_id) - continue + for(var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks[specific_lift_id]) var/list/this_destination = list() this_destination["name"] = destination.name this_destination["dest_icons"] = destination.tgui_icons @@ -94,9 +91,7 @@ switch (action) if ("send") var/obj/effect/landmark/tram/to_where - for (var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks) - if(destination.tram_id != tram_id) - continue + for (var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks[specific_lift_id]) if(destination.destination_id == params["destination"]) to_where = destination break @@ -108,14 +103,13 @@ /// Attempts to sends the tram to the given destination /obj/machinery/computer/tram_controls/proc/try_send_tram(obj/effect/landmark/tram/to_where) - var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve() + var/datum/lift_master/tram/tram_part = tram_ref?.resolve() if(!tram_part) return FALSE - if(tram_part.travelling) - return FALSE - if(tram_part.controls_locked) // someone else started + if(tram_part.controls_locked) // someone else started already return FALSE tram_part.tram_travel(to_where) + visible_message("The tram has been called to [to_where.name]") return TRUE /obj/item/circuit_component/tram_controls @@ -147,12 +141,12 @@ . = ..() if (istype(shell, /obj/machinery/computer/tram_controls)) computer = shell - var/obj/structure/industrial_lift/tram/central/tram_part = computer.tram_ref?.resolve() + var/datum/lift_master/tram/tram_part = computer.tram_ref?.resolve() RegisterSignal(tram_part, COMSIG_TRAM_SET_TRAVELLING, .proc/on_tram_set_travelling) RegisterSignal(tram_part, COMSIG_TRAM_TRAVEL, .proc/on_tram_travel) /obj/item/circuit_component/tram_controls/unregister_usb_parent(atom/movable/shell) - var/obj/structure/industrial_lift/tram/central/tram_part = computer.tram_ref?.resolve() + var/datum/lift_master/tram/tram_part = computer.tram_ref?.resolve() computer = null UnregisterSignal(tram_part, list(COMSIG_TRAM_SET_TRAVELLING, COMSIG_TRAM_TRAVEL)) return ..() @@ -168,9 +162,7 @@ return var/destination - for(var/obj/effect/landmark/tram/possible_destination as anything in GLOB.tram_landmarks) - if(possible_destination.tram_id != computer.tram_id) - continue + for(var/obj/effect/landmark/tram/possible_destination as anything in GLOB.tram_landmarks[computer.specific_lift_id]) if(possible_destination.name == new_destination.value) destination = possible_destination break diff --git a/code/modules/industrial_lift/tram_landmark.dm b/code/modules/industrial_lift/tram_landmark.dm new file mode 100644 index 00000000000..db855b58a5c --- /dev/null +++ b/code/modules/industrial_lift/tram_landmark.dm @@ -0,0 +1,47 @@ +GLOBAL_LIST_EMPTY(tram_landmarks) + +/obj/effect/landmark/tram + name = "tram destination" //the tram buttons will mention this. + icon_state = "tram" + + ///the id of the tram we're linked to. + var/specific_lift_id = MAIN_STATION_TRAM + /// The ID of that particular destination. + var/destination_id + /// Icons for the tgui console to list out for what is at this location + var/list/tgui_icons = list() + +/obj/effect/landmark/tram/Initialize(mapload) + . = ..() + LAZYADDASSOCLIST(GLOB.tram_landmarks, specific_lift_id, src) + +/obj/effect/landmark/tram/Destroy() + LAZYREMOVEASSOC(GLOB.tram_landmarks, specific_lift_id, src) + return ..() + + +/obj/effect/landmark/tram/left_part + name = "West Wing" + destination_id = "left_part" + tgui_icons = list("Arrivals" = "plane-arrival", "Command" = "bullhorn", "Security" = "gavel") + +/obj/effect/landmark/tram/middle_part + name = "Central Wing" + destination_id = "middle_part" + tgui_icons = list("Service" = "cocktail", "Medical" = "plus", "Engineering" = "wrench") + +/obj/effect/landmark/tram/right_part + name = "East Wing" + destination_id = "right_part" + tgui_icons = list("Departures" = "plane-departure", "Cargo" = "box", "Science" = "flask") + +/** + * lift_id landmarks. used to map in specific_lift_id to trams. when the trams lift_master encounters one on a trams tile + * it sets its specific_lift_id to that landmark. allows you to have multiple trams and multiple controls linking to their specific tram + */ +/obj/effect/landmark/lift_id + name = "lift id setter" + icon_state = "lift_id" + + ///what specific id we give to the tram we're placed on, should explicitely set this if its a subtype, or weird things might happen + var/specific_lift_id = MAIN_STATION_TRAM diff --git a/code/modules/industrial_lift/tram_lift_master.dm b/code/modules/industrial_lift/tram_lift_master.dm new file mode 100644 index 00000000000..573e961a71d --- /dev/null +++ b/code/modules/industrial_lift/tram_lift_master.dm @@ -0,0 +1,174 @@ +/datum/lift_master/tram + + ///whether this tram is traveling across vertical and/or horizontal axis for some distance. not all lifts use this + var/travelling = FALSE + ///if we're travelling, what direction are we going + var/travel_direction = NONE + ///if we're travelling, how far do we have to go + var/travel_distance = 0 + + ///multiplier on how much damage/force the tram imparts on things it hits + var/collision_lethality = 1 + + /// reference to the destination landmark we consider ourselves "at". since we potentially span multiple z levels we dont actually + /// know where on us this platform is. as long as we know THAT its on us we can just move the distance and direction between this + /// and the destination landmark. + var/obj/effect/landmark/tram/from_where + + ///decisecond delay between horizontal movement. cannot make the tram move faster than 1 movement per world.tick_lag. + ///this var is poorly named its actually horizontal movement delay but whatever. + var/horizontal_speed = 0.5 + + ///version of horizontal_speed that gets set in init and is considered our base speed if our lift gets slowed down + var/base_horizontal_speed = 0.5 + + ///the world.time we should next move at. in case our speed is set to less than 1 movement per tick + var/next_move = INFINITY + + ///whether we have been slowed down automatically + var/slowed_down = FALSE + + ///how many times we moved while costing more than SStramprocess.max_time milliseconds per movement. + ///if this exceeds SStramprocess.max_exceeding_moves + var/times_exceeded = 0 + + ///how many times we moved while costing less than 0.5 * SStramprocess.max_time milliseconds per movement + var/times_below = 0 + +/datum/lift_master/tram/New(obj/structure/industrial_lift/tram/lift_platform) + . = ..() + horizontal_speed = lift_platform.horizontal_speed + base_horizontal_speed = lift_platform.horizontal_speed + + check_starting_landmark() + +/datum/lift_master/tram/vv_edit_var(var_name, var_value) + . = ..() + if(var_name == "base_horizontal_speed") + horizontal_speed = max(horizontal_speed, base_horizontal_speed) + +/datum/lift_master/tram/add_lift_platforms(obj/structure/industrial_lift/new_lift_platform) + . = ..() + RegisterSignal(new_lift_platform, COMSIG_MOVABLE_BUMP, .proc/gracefully_break) + +/datum/lift_master/tram/check_for_landmarks(obj/structure/industrial_lift/tram/new_lift_platform) + . = ..() + for(var/turf/platform_loc as anything in new_lift_platform.locs) + var/obj/effect/landmark/tram/initial_destination = locate() in platform_loc + + if(initial_destination) + from_where = initial_destination + +/datum/lift_master/tram/proc/check_starting_landmark() + if(!from_where) + CRASH("a tram lift_master was initialized without any tram landmark to give it direction!") + + SStramprocess.can_fire = TRUE + + return TRUE + +/** + * Signal for when the tram runs into a field of which it cannot go through. + * Stops the train's travel fully, sends a message, and destroys the train. + * Arguments: + * bumped_atom - The atom this tram bumped into + */ +/datum/lift_master/tram/proc/gracefully_break(atom/bumped_atom) + SIGNAL_HANDLER + + if(istype(bumped_atom, /obj/machinery/field)) + return + + travel_distance = 0 + + bumped_atom.visible_message(span_userdanger("[src] crashes into the field violently!")) + for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_platforms) + tram_part.set_travelling(FALSE) + if(prob(15) || locate(/mob/living) in tram_part.lift_load) //always go boom on people on the track + explosion(tram_part, devastation_range = rand(0, 1), heavy_impact_range = 2, light_impact_range = 3) //50% chance of gib + qdel(tram_part) + +/** + * Handles moving the tram + * + * Tells the individual tram parts where to actually go and has an extra safety check + * incase multiple inputs get through, preventing conflicting directions and the tram + * literally ripping itself apart. all of the actual movement is handled by SStramprocess + */ +/datum/lift_master/tram/proc/tram_travel(obj/effect/landmark/tram/to_where) + if(to_where == from_where) + return + + travel_direction = get_dir(from_where, to_where) + travel_distance = get_dist(from_where, to_where) + from_where = to_where + set_travelling(TRUE) + set_controls(LIFT_PLATFORM_LOCKED) + SEND_SIGNAL(src, COMSIG_TRAM_TRAVEL, from_where, to_where) + + for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_platforms) //only thing everyone needs to know is the new location. + if(tram_part.travelling) //wee woo wee woo there was a double action queued. damn multi tile structs + return //we don't care to undo locked controls, though, as that will resolve itself + + tram_part.glide_size_override = DELAY_TO_GLIDE_SIZE(horizontal_speed) + tram_part.set_travelling(TRUE) + + next_move = world.time + horizontal_speed + + START_PROCESSING(SStramprocess, src) + +/datum/lift_master/tram/process(delta_time) + if(!travel_distance) + addtimer(CALLBACK(src, .proc/unlock_controls), 3 SECONDS) + return PROCESS_KILL + else if(world.time >= next_move) + var/start_time = TICK_USAGE + travel_distance-- + + MoveLiftHorizontal(travel_direction) + + var/duration = TICK_USAGE_TO_MS(start_time) + if(slowed_down) + if(duration <= (SStramprocess.max_time / 2)) + times_below++ + else + times_below = 0 + + if(times_below >= SStramprocess.max_cheap_moves) + horizontal_speed = base_horizontal_speed + slowed_down = FALSE + times_below = 0 + + else if(duration > SStramprocess.max_time) + times_exceeded++ + + if(times_exceeded >= SStramprocess.max_exceeding_moves) + message_admins("The tram at [ADMIN_JMP(lift_platforms[1])] is taking more than [SStramprocess.max_time] milliseconds per movement, halving its movement speed. if this continues to be a problem you can call reset_lift_contents() on the trams lift_master_datum to reset it to its original state and clear added objects") + horizontal_speed = base_horizontal_speed * 2 //halves its speed + slowed_down = TRUE + times_exceeded = 0 + else + times_exceeded = max(times_exceeded - 1, 0) + + next_move = world.time + horizontal_speed + +/** + * Handles unlocking the tram controls for use after moving + * + * More safety checks to make sure the tram has actually docked properly + * at a location before users are allowed to interact with the tram console again. + * Tram finds its location at this point before fully unlocking controls to the user. + */ +/datum/lift_master/tram/proc/unlock_controls() + set_travelling(FALSE) + set_controls(LIFT_PLATFORM_UNLOCKED) + for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_platforms) //only thing everyone needs to know is the new location. + tram_part.set_travelling(FALSE) + + +/datum/lift_master/tram/proc/set_travelling(new_travelling) + if(travelling == new_travelling) + return + + travelling = new_travelling + SEND_SIGNAL(src, COMSIG_TRAM_SET_TRAVELLING, travelling) diff --git a/code/modules/industrial_lift/tram_override_objects.dm b/code/modules/industrial_lift/tram_override_objects.dm new file mode 100644 index 00000000000..7a348295a2d --- /dev/null +++ b/code/modules/industrial_lift/tram_override_objects.dm @@ -0,0 +1,39 @@ +/** + * the tram has a few objects mapped onto it at roundstart, by default many of those objects have unwanted properties + * for example grilles and windows have the atmos_sensitive element applied to them, which makes them register to + * themselves moving to re register signals onto the turf via connect_loc. this is bad and dumb since it makes the tram + * more expensive to move. + * + * if you map something on to the tram, make SURE if possible that it doesnt have anythign reacting to its own movement + * it will make the tram more expensive to move and we dont want that because we dont want to return to the days where + * the tram took a third of the tick per movement when its just carrying its default mapped in objects + */ +/obj/structure/grille/tram/Initialize(mapload) + . = ..() + RemoveElement(/datum/element/atmos_sensitive, mapload) + //atmos_sensitive applies connect_loc which 1. reacts to movement in order to 2. unregister and register signals to + //the old and new locs. we dont want that, pretend these grilles and windows are plastic or something idk + +/obj/structure/window/reinforced/shuttle/tram/Initialize(mapload, direct) + . = ..() + RemoveElement(/datum/element/atmos_sensitive, mapload) + +/obj/structure/shuttle/engine/propulsion/in_wall/tram + //if this has opacity, then every movement of the tram causes lighting updates + //DO NOT put something on the tram roundstart that has opacity, it WILL overload SSlighting + opacity = FALSE + +/obj/machinery/door/window/left/tram +/obj/machinery/door/window/right/tram + +/obj/machinery/door/window/left/tram/Initialize(mapload, set_dir, unres_sides) + . = ..() + RemoveElement(/datum/element/atmos_sensitive, mapload) + +/obj/machinery/door/window/right/tram/Initialize(mapload, set_dir, unres_sides) + . = ..() + RemoveElement(/datum/element/atmos_sensitive, mapload) + +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/door/window/left/tram, 0) +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/door/window/right/tram, 0) + diff --git a/code/game/objects/structures/tram_walls.dm b/code/modules/industrial_lift/tram_walls.dm similarity index 99% rename from code/game/objects/structures/tram_walls.dm rename to code/modules/industrial_lift/tram_walls.dm index 092b1d5e2e5..17844dbfa5d 100644 --- a/code/game/objects/structures/tram_walls.dm +++ b/code/modules/industrial_lift/tram_walls.dm @@ -10,7 +10,7 @@ base_icon_state = "wall" layer = LOW_OBJ_LAYER density = TRUE - opacity = TRUE + opacity = FALSE max_integrity = 100 smoothing_flags = SMOOTH_BITMASK smoothing_groups = list(SMOOTH_GROUP_CLOSED_TURFS, SMOOTH_GROUP_WALLS) diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm index dbcdf2a7420..1b7efcad8a0 100644 --- a/code/modules/instruments/songs/play_legacy.dm +++ b/code/modules/instruments/songs/play_legacy.dm @@ -87,5 +87,5 @@ L.apply_status_effect(/datum/status_effect/good_music) if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) continue - M.playsound_local(source, null, volume * using_instrument.volume_multiplier, S = music_played) + M.playsound_local(source, null, volume * using_instrument.volume_multiplier, sound_to_use = music_played) // Could do environment and echo later but not for now diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 4beac5b1dd2..f0487b86b1a 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -25,8 +25,8 @@ #undef NONSENSICAL_VALUE -// Will update the light (duh). -// Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf... +/// Will update the light (duh). +/// Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf... /atom/proc/update_light() set waitfor = FALSE if (QDELETED(src)) @@ -80,13 +80,6 @@ return recalculate_directional_opacity() - -/atom/movable/Moved(atom/OldLoc, Dir) - . = ..() - for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update. - light.source_atom.update_light() - - /atom/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION) return diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index ca600b7d8f2..f9c48fb87ce 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -40,10 +40,10 @@ /datum/light_source/New(atom/owner, atom/top) source_atom = owner // Set our new owner. - LAZYADD(source_atom.light_sources, src) + add_to_light_sources(source_atom.light_sources) top_atom = top if (top_atom != source_atom) - LAZYADD(top_atom.light_sources, src) + add_to_light_sources(top_atom.light_sources) source_turf = top_atom pixel_turf = get_turf_pixel(top_atom) || source_turf @@ -59,10 +59,10 @@ /datum/light_source/Destroy(force) remove_lum() if (source_atom) - LAZYREMOVE(source_atom.light_sources, src) + remove_from_light_sources(source_atom.light_sources) if (top_atom) - LAZYREMOVE(top_atom.light_sources, src) + remove_from_light_sources(top_atom.light_sources) if (needs_update) SSlighting.sources_queue -= src @@ -74,6 +74,35 @@ return ..() +///add this light source to new_atom_host's light_sources list. updating movement registrations as needed +/datum/light_source/proc/add_to_light_sources(atom/new_atom_host) + if(QDELETED(new_atom_host)) + return FALSE + + LAZYADD(new_atom_host.light_sources, src) + if(ismovable(new_atom_host) && new_atom_host == source_atom) + RegisterSignal(new_atom_host, COMSIG_MOVABLE_MOVED, .proc/update_host_lights) + return TRUE + +///remove this light source from old_atom_host's light_sources list, unsetting movement registrations +/datum/light_source/proc/remove_from_light_sources(atom/old_atom_host) + if(QDELETED(old_atom_host)) + return FALSE + + LAZYREMOVE(old_atom_host.light_sources, src) + if(ismovable(old_atom_host) && old_atom_host == source_atom) + UnregisterSignal(old_atom_host, COMSIG_MOVABLE_MOVED) + return TRUE + +///signal handler for when our host atom moves and we need to update our effects +/datum/light_source/proc/update_host_lights(atom/movable/host) + SIGNAL_HANDLER + + if(QDELETED(host)) + return + + host.update_light() + // Yes this doesn't align correctly on anything other than 4 width tabs. // If you want it to go switch everybody to elastic tab stops. // Actually that'd be great if you could! @@ -84,17 +113,17 @@ needs_update = level; \ -// This proc will cause the light source to update the top atom, and add itself to the update queue. +/// This proc will cause the light source to update the top atom, and add itself to the update queue. /datum/light_source/proc/update(atom/new_top_atom) // This top atom is different. if (new_top_atom && new_top_atom != top_atom) if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom. - LAZYREMOVE(top_atom.light_sources, src) + remove_from_light_sources(top_atom.light_sources) top_atom = new_top_atom if (top_atom != source_atom) - LAZYADD(top_atom.light_sources, src) // Add ourselves to the light sources of our new top atom. + add_to_light_sources(top_atom.light_sources) EFFECT_UPDATE(LIGHTING_CHECK_UPDATE) diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm b/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm index 98aa475b8c2..330d3671a50 100644 --- a/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm +++ b/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm @@ -41,8 +41,9 @@ if(hit_object.resistance_flags & FREEZE_PROOF) hit_object.visible_message(span_warning("[hit_object] is freeze-proof!")) return - if(!(hit_object.obj_flags & FROZEN)) - hit_object.make_frozen_visual() + if(HAS_TRAIT(hit_object, TRAIT_FROZEN)) + return + hit_object.AddElement(/datum/element/frozen) else if(isliving(hit_atom)) var/mob/living/hit_mob = hit_atom SSmove_manager.stop_looping(hit_mob) //stops them mid pathing even if they're stunimmune diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm index 7d12dd93c3c..1c6a8fb98e6 100644 --- a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm +++ b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm @@ -187,7 +187,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) for(var/mob/M in GLOB.player_list) if(M.z == z) to_chat(M, span_userdanger("Discordant whispers flood your mind in a thousand voices. Each one speaks your name, over and over. Something horrible has been released.")) - M.playsound_local(T, null, 100, FALSE, 0, FALSE, pressure_affected = FALSE, S = legion_sound) + M.playsound_local(T, null, 100, FALSE, 0, FALSE, pressure_affected = FALSE, sound_to_use = legion_sound) flash_color(M, flash_color = "#FF0000", flash_time = 50) var/mutable_appearance/release_overlay = mutable_appearance('icons/effects/effects.dmi', "legiondoor") notify_ghosts("Legion has been released in the [get_area(src)]!", source = src, alert_overlay = release_overlay, action = NOTIFY_JUMP, flashwindow = FALSE) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm index 5d9b36ff0c0..b8ffceaa6e8 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm @@ -495,19 +495,22 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) else to_chat(user, "No vacated rooms.") +/obj/effect/landmark/lift_id/hilbert + specific_lift_id = HILBERT_TRAM + /obj/effect/landmark/tram/left_part/hilbert + specific_lift_id = HILBERT_TRAM destination_id = "left_part_hilbert" - tram_id = "tram_hilbert" tgui_icons = list("Reception" = "briefcase", "Botany" = "leaf", "Chemistry" = "flask") /obj/effect/landmark/tram/middle_part/hilbert + specific_lift_id = HILBERT_TRAM destination_id = "middle_part_hilbert" - tram_id = "tram_hilbert" tgui_icons = list("Processing" = "cogs", "Xenobiology" = "paw") /obj/effect/landmark/tram/right_part/hilbert + specific_lift_id = HILBERT_TRAM destination_id = "right_part_hilbert" - tram_id = "tram_hilbert" tgui_icons = list("Ordnance" = "bullseye", "Office" = "user", "Dormitories" = "bed") /obj/item/keycard/hilbert diff --git a/code/modules/mapping/space_management/traits.dm b/code/modules/mapping/space_management/traits.dm index b53e2ee332a..4911d5316f1 100644 --- a/code/modules/mapping/space_management/traits.dm +++ b/code/modules/mapping/space_management/traits.dm @@ -57,18 +57,19 @@ /// Attempt to get the turf below the provided one according to Z traits /datum/controller/subsystem/mapping/proc/get_turf_below(turf/T) - if (!T) + if (!T || !initialized) return - var/offset = level_trait(T.z, ZTRAIT_DOWN) + var/offset = multiz_levels[T.z]["[DOWN]"] if (!offset) return - return locate(T.x, T.y, T.z + offset) + return locate(T.x, T.y, T.z - offset) /// Attempt to get the turf above the provided one according to Z traits /datum/controller/subsystem/mapping/proc/get_turf_above(turf/T) - if (!T) + if (!T || !initialized) return - var/offset = level_trait(T.z, ZTRAIT_UP) + + var/offset = multiz_levels[T.z]["[UP]"] if (!offset) return return locate(T.x, T.y, T.z + offset) diff --git a/code/modules/mapping/space_management/zlevel_manager.dm b/code/modules/mapping/space_management/zlevel_manager.dm index 2ad150ebc4b..1e5d2aafc88 100644 --- a/code/modules/mapping/space_management/zlevel_manager.dm +++ b/code/modules/mapping/space_management/zlevel_manager.dm @@ -26,6 +26,8 @@ // TODO: sleep here if the Z level needs to be cleared var/datum/space_level/S = new z_type(new_z, name, traits) z_list += S + generate_linkages_for_z_level(new_z) + calculate_z_level_gravity(new_z) adding_new_zlevel = FALSE SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, S) return S diff --git a/code/modules/meteors/meteors.dm b/code/modules/meteors/meteors.dm index 2e843f2303b..ef7bf322613 100644 --- a/code/modules/meteors/meteors.dm +++ b/code/modules/meteors/meteors.dm @@ -221,7 +221,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust=1)) //for space dust eve continue var/dist = get_dist(M.loc, src.loc) shake_camera(M, dist > 20 ? 2 : 4, dist > 20 ? 1 : 3) - M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) + M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, sound_to_use = meteor_sound) /////////////////////// //Meteor types diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index af1eae47790..f9d73ae1ac5 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -25,11 +25,11 @@ AddComponent(/datum/component/bloodysoles/feet) AddElement(/datum/element/ridable, /datum/component/riding/creature/human) AddElement(/datum/element/strippable, GLOB.strippable_human_items, /mob/living/carbon/human/.proc/should_strip) - GLOB.human_list += src var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = .proc/on_entered, ) AddElement(/datum/element/connect_loc, loc_connections) + GLOB.human_list += src SSopposing_force.give_opfor_button(src) //SKYRAT EDIT - OPFOR SYSTEM /mob/living/carbon/human/proc/setup_human_dna() @@ -98,11 +98,6 @@ . += "Chemical Storage: [changeling.chem_charges]/[changeling.total_chem_storage]" . += "Absorbed DNA: [changeling.absorbed_count]" -// called when something steps onto a human -/mob/living/carbon/human/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - spreadFire(AM) - /mob/living/carbon/human/reset_perspective(atom/new_eye, force_reset = FALSE) if(dna?.species?.prevent_perspective_change && !force_reset) // This is in case a species needs to prevent perspective changes in certain cases, like Dullahans preventing perspective changes when they're looking through their head. update_fullscreen() @@ -411,7 +406,10 @@ to_chat(usr, "Exploitable information: [target_general_records.fields["exploitable_records"]]") //SKYRAT EDIT END - ..() //end of this massive fucking chain. TODO: make the hud chain not spooky. - Yeah, great job doing that. +//called when something steps onto a human +/mob/living/carbon/human/proc/on_entered(datum/source, atom/movable/AM) + SIGNAL_HANDLER + spreadFire(AM) /mob/living/carbon/human/proc/canUseHUD() return (mobility_flags & MOBILITY_USE) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index d6ee7e4e1c1..2e4225cbd7f 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -926,7 +926,7 @@ var/mob/living/L = pulledby L.set_pull_offsets(src, pulledby.grab_state) - if(active_storage && !(CanReach(active_storage.parent,view_only = TRUE))) + if(active_storage && !((active_storage.parent in important_recursive_contents?[RECURSIVE_CONTENTS_ACTIVE_STORAGE]) || CanReach(active_storage.parent,view_only = TRUE))) active_storage.close(src) if(body_position == LYING_DOWN && !buckled && prob(getBruteLoss()*200/maxHealth)) @@ -1464,6 +1464,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) var/datum/status_effect/fire_handler/fire_stacks/fire_status = has_status_effect(/datum/status_effect/fire_handler/fire_stacks) if(!fire_status || !fire_status.on_fire) return + remove_status_effect(/datum/status_effect/fire_handler/fire_stacks) /** diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 1388ffb6e59..6eac0915fad 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -210,3 +210,5 @@ var/native_fov = FOV_180_DEGREES //SKYRAT EDIT CHANGE /// Lazy list of FOV traits that will apply a FOV view when handled. var/list/fov_traits + ///what multiplicative slowdown we get from turfs currently. + var/current_turf_slowdown = 0 diff --git a/code/modules/mob/living/living_fov.dm b/code/modules/mob/living/living_fov.dm index 1bc54a66db7..4812c0bfea9 100644 --- a/code/modules/mob/living/living_fov.dm +++ b/code/modules/mob/living/living_fov.dm @@ -88,11 +88,20 @@ UNSETEMPTY(fov_traits) update_fov() +//did you know you can subtype /image and /mutable_appearance? +/image/fov_image + icon = 'icons/effects/fov/fov_effects.dmi' + layer = FOV_EFFECTS_LAYER + appearance_flags = RESET_COLOR | RESET_TRANSFORM + plane = FULLSCREEN_PLANE + /// Plays a visual effect representing a sound cue for people with vision obstructed by FOV or blindness -/proc/play_fov_effect(atom/center, range, icon_state, dir = SOUTH, ignore_self = FALSE, angle = 0) +/proc/play_fov_effect(atom/center, range, icon_state, dir = SOUTH, ignore_self = FALSE, angle = 0, list/override_list) var/turf/anchor_point = get_turf(center) - var/image/fov_image - for(var/mob/living/living_mob in get_hearers_in_view(range, center)) + var/image/fov_image/fov_image + var/list/clients_shown + + for(var/mob/living/living_mob in override_list || get_hearers_in_view(range, center)) var/client/mob_client = living_mob.client if(!mob_client) continue @@ -101,18 +110,22 @@ if(living_mob.in_fov(center, ignore_self)) continue if(!fov_image) //Make the image once we found one recipient to receive it - fov_image = image(icon = 'icons/effects/fov/fov_effects.dmi', icon_state = icon_state, loc = anchor_point) - fov_image.plane = FULLSCREEN_PLANE - fov_image.layer = FOV_EFFECTS_LAYER + fov_image = new() + fov_image.loc = anchor_point + fov_image.icon_state = icon_state fov_image.dir = dir - fov_image.appearance_flags = RESET_COLOR | RESET_TRANSFORM if(angle) var/matrix/matrix = new matrix.Turn(angle) fov_image.transform = matrix fov_image.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + LAZYADD(clients_shown, mob_client) + mob_client.images += fov_image - addtimer(CALLBACK(GLOBAL_PROC, .proc/remove_image_from_client, fov_image, mob_client), 30) + //when added as an image mutable_appearances act identically. we just make it an MA becuase theyre faster to change appearance + + if(clients_shown) + addtimer(CALLBACK(GLOBAL_PROC, .proc/remove_images_from_clients, fov_image, clients_shown), 30) /atom/movable/screen/fov_blocker icon = 'icons/effects/fov/field_of_view.dmi' diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 8b5ed28aec6..8ffb61b4d93 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -28,9 +28,12 @@ /mob/living/proc/update_turf_movespeed(turf/open/T) if(isopenturf(T)) - add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown, multiplicative_slowdown = T.slowdown) - else + if(T.slowdown != current_turf_slowdown) + add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown, multiplicative_slowdown = T.slowdown) + current_turf_slowdown = T.slowdown + else if(current_turf_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown) + current_turf_slowdown = 0 /mob/living/proc/update_pull_movespeed() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index cc683403ff6..f40050db76e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -38,6 +38,7 @@ if(observers?.len) for(var/mob/dead/observe as anything in observers) observe.reset_perspective(null) + qdel(hud_used) QDEL_LIST(client_colours) ghostize() //False, since we're deleting it currently diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 0560fa3d77d..20238ddb4d1 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -241,3 +241,6 @@ var/thinking_indicator = FALSE /// User is thinking in character. Used to revert to thinking state after stop_typing var/thinking_IC = FALSE + + ///how much gravity is slowing us down + var/gravity_slowdown = 0 diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index d058604e464..0e92ca69035 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -70,9 +70,9 @@ next_move_dir_sub = 0 var/old_move_delay = move_delay move_delay = world.time + world.tick_lag //this is here because Move() can now be called mutiple times per tick - if(!mob || !mob.loc) + if(!direct || !new_loc) return FALSE - if(!new_loc || !direct) + if(!mob?.loc) return FALSE if(mob.notransform) return FALSE //This is sota the goto stop mobs from moving var @@ -125,7 +125,9 @@ //We are now going to move var/add_delay = mob.cached_multiplicative_slowdown - mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay * ( (NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? SQRT_2 : 1 ) )) // set it now in case of pulled objects + var/new_glide_size = DELAY_TO_GLIDE_SIZE(add_delay * ( (NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? SQRT_2 : 1 ) ) + if(mob.glide_size != new_glide_size) + mob.set_glide_size(new_glide_size) // set it now in case of pulled objects //If the move was recent, count using old_move_delay //We want fractional behavior and all if(old_move_delay + world.tick_lag > world.time) @@ -144,10 +146,15 @@ if((direct & (direct - 1)) && mob.loc == new_loc) //moved diagonally successfully add_delay *= SQRT_2 + var/after_glide = 0 if(visual_delay) - mob.set_glide_size(visual_delay) + after_glide = visual_delay else - mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay)) + after_glide = DELAY_TO_GLIDE_SIZE(add_delay) + + if(after_glide != mob.glide_size) + mob.set_glide_size(after_glide) + move_delay += add_delay if(.) // If mob is null here, we deserve the runtime if(mob.throwing) @@ -365,10 +372,12 @@ /// Update the gravity status of this mob /mob/proc/update_gravity(has_gravity, override=FALSE) var/speed_change = max(0, has_gravity - STANDARD_GRAVITY) - if(!speed_change) + if(!speed_change && gravity_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/gravity) - else + gravity_slowdown = 0 + else if(gravity_slowdown != speed_change) add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gravity, multiplicative_slowdown=speed_change) + gravity_slowdown = speed_change //bodypart selection verbs - Cyberboss //8: repeated presses toggles through head - eyes - mouth diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm index da8d5e5578d..24e9beb8fc7 100644 --- a/code/modules/mod/modules/modules_maint.dm +++ b/code/modules/mod/modules/modules_maint.dm @@ -105,7 +105,7 @@ rave_screen = mod.wearer.add_client_colour(/datum/client_colour/rave) rave_screen.update_colour(rainbow_order[rave_number]) if(selection) - SEND_SOUND(mod.wearer, sound(selection.song_path, volume = 50, channel = CHANNEL_JUKEBOX)) + mod.wearer.playsound_local(get_turf(src), null, 50, channel = CHANNEL_JUKEBOX, sound_to_use = sound(selection.song_path), use_reverb = FALSE) /obj/item/mod/module/visor/rave/on_deactivation(display_message = TRUE, deleting = FALSE) . = ..() diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 41300a02847..ab4aec17ce3 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -3,7 +3,8 @@ // Gravity Generator // -GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding new gravity generators to the list, and keying it with the z level. +/// We will keep track of this by adding new gravity generators to the list, and keying it with the z level. +GLOBAL_LIST_EMPTY(gravity_generators) #define POWER_IDLE 0 #define POWER_UP 1 @@ -146,7 +147,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne var/charge_count = 100 var/current_overlay = null var/broken_state = 0 - var/setting = 1 //Gravity value when on + ///Gravity value when on + var/setting = 1 ///Station generator that spawns with gravity turned off. /obj/machinery/gravity_generator/main/station/off @@ -407,7 +409,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne M.update_gravity(M.has_gravity()) if(M.client) shake_camera(M, 15, 1) - M.playsound_local(T, null, 100, 1, 0.5, S = alert_sound) + M.playsound_local(T, null, 100, 1, 0.5, sound_to_use = alert_sound) //SKYRAT EDIT ADDITON BEGIN if(on) priority_announce("GRAVITATIONAL SYSTEMS OPERATIONAL", "Gravity Generator", ANNOUNCER_GRAVGENON) @@ -440,6 +442,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne GLOB.gravity_generators["[z]"] |= src else GLOB.gravity_generators["[z]"] -= src + SSmapping.calculate_z_level_gravity(z) /obj/machinery/gravity_generator/main/proc/change_setting(value) if(value != setting) diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 5b2c22850c9..9a2866dfa1b 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -115,7 +115,7 @@ All ShuttleMove procs go here /atom/movable/proc/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock) return move_mode -// Called on atoms to move the atom to the new location +/// Called on atoms to move the atom to the new location /atom/movable/proc/onShuttleMove(turf/newT, turf/oldT, list/movement_force, move_dir, obj/docking_port/stationary/old_dock, obj/docking_port/mobile/moving_dock) if(newT == oldT) // In case of in place shuttle rotation shenanigans. return diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 526fd875a68..96862680663 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -39,7 +39,7 @@ ///are we registered in SSshuttles? var/registered = FALSE - ///register to SSshuttles +///register to SSshuttles /obj/docking_port/proc/register() if(registered) WARNING("docking_port registered multiple times") @@ -47,7 +47,7 @@ registered = TRUE return - ///unregister from SSshuttles +///unregister from SSshuttles /obj/docking_port/proc/unregister() if(!registered) WARNING("docking_port unregistered multiple times") @@ -57,7 +57,7 @@ /obj/docking_port/proc/Check_id() return - //these objects are indestructible +//these objects are indestructible /obj/docking_port/Destroy(force) // unless you assert that you know what you're doing. Horrible things // may result. @@ -68,7 +68,7 @@ return QDEL_HINT_LETMELIVE /obj/docking_port/has_gravity(turf/T) - return FALSE + return TRUE /obj/docking_port/take_damage() return diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index cee998c2836..07c7e57e026 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -103,6 +103,8 @@ ignore += typesof(/obj/item/toy/cards/cardhand) //Needs a holodeck area linked to it which is not guarenteed to exist and technically is supposed to have a 1:1 relationship with computer anyway. ignore += typesof(/obj/machinery/computer/holodeck) + //runtimes if not paired with a landmark + ignore += typesof(/obj/structure/industrial_lift) var/list/cached_contents = spawn_at.contents.Copy() var/baseturf_count = length(spawn_at.baseturfs) diff --git a/icons/effects/landmarks_static.dmi b/icons/effects/landmarks_static.dmi index 2082afc2fe0..d6a26b050d5 100644 Binary files a/icons/effects/landmarks_static.dmi and b/icons/effects/landmarks_static.dmi differ diff --git a/modular_skyrat/modules/jukebox/code/jukebox_subsystem.dm b/modular_skyrat/modules/jukebox/code/jukebox_subsystem.dm index 85756ccd4f8..d9632be5efa 100644 --- a/modular_skyrat/modules/jukebox/code/jukebox_subsystem.dm +++ b/modular_skyrat/modules/jukebox/code/jukebox_subsystem.dm @@ -40,7 +40,7 @@ SUBSYSTEM_DEF(jukeboxes) if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS)) continue - M.playsound_local(M, null, jukebox.volume, channel = youvegotafreejukebox[2], S = song_to_init) + M.playsound_local(M, null, jukebox.volume, channel = youvegotafreejukebox[2], sound_to_use = song_to_init) return activejukeboxes.len /datum/controller/subsystem/jukeboxes/proc/removejukebox(IDtoremove) @@ -112,6 +112,6 @@ SUBSYSTEM_DEF(jukeboxes) else song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame. - M.playsound_local(currentturf, null, jukebox.volume, channel = jukeinfo[2], S = song_played) + M.playsound_local(currentturf, null, jukebox.volume, channel = jukeinfo[2], sound_to_use = song_played) CHECK_TICK return diff --git a/modular_skyrat/modules/liquids/code/liquid_systems/liquid_interaction.dm b/modular_skyrat/modules/liquids/code/liquid_systems/liquid_interaction.dm index efc02390b3c..e8a52cdd06f 100644 --- a/modular_skyrat/modules/liquids/code/liquid_systems/liquid_interaction.dm +++ b/modular_skyrat/modules/liquids/code/liquid_systems/liquid_interaction.dm @@ -16,6 +16,7 @@ /datum/element/liquids_interaction/Detach(mob/living/target) UnregisterSignal(target, COMSIG_ITEM_AFTERATTACK) + return ..() /datum/element/liquids_interaction/proc/AfterAttack(obj/item/target, atom/target2, mob/user) SIGNAL_HANDLER diff --git a/tgstation.dme b/tgstation.dme index dad38ba31ec..e5b92d04bed 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -90,6 +90,7 @@ #include "code\__DEFINES\icon_smoothing.dm" #include "code\__DEFINES\id_cards.dm" #include "code\__DEFINES\important_recursive_contents.dm" +#include "code\__DEFINES\industrial_lift.dm" #include "code\__DEFINES\injection.dm" #include "code\__DEFINES\instruments.dm" #include "code\__DEFINES\interaction_flags.dm" @@ -1045,6 +1046,7 @@ #include "code\datums\elements\firestacker.dm" #include "code\datums\elements\footstep.dm" #include "code\datums\elements\forced_gravity.dm" +#include "code\datums\elements\frozen.dm" #include "code\datums\elements\haunted.dm" #include "code\datums\elements\honkspam.dm" #include "code\datums\elements\item_fov.dm" @@ -1324,7 +1326,6 @@ #include "code\game\machinery\cell_charger.dm" #include "code\game\machinery\civilian_bounties.dm" #include "code\game\machinery\constructable_frame.dm" -#include "code\game\machinery\crossing_signal.dm" #include "code\game\machinery\dance_machine.dm" #include "code\game\machinery\defibrillator_mount.dm" #include "code\game\machinery\deployable.dm" @@ -1404,7 +1405,6 @@ #include "code\game\machinery\computer\station_alert.dm" #include "code\game\machinery\computer\teleporter.dm" #include "code\game\machinery\computer\terminal.dm" -#include "code\game\machinery\computer\tram_controls.dm" #include "code\game\machinery\computer\warrant.dm" #include "code\game\machinery\computer\arcade\arcade.dm" #include "code\game\machinery\computer\arcade\orion.dm" @@ -1843,7 +1843,6 @@ #include "code\game\objects\structures\headpike.dm" #include "code\game\objects\structures\hivebot.dm" #include "code\game\objects\structures\holosign.dm" -#include "code\game\objects\structures\industrial_lift.dm" #include "code\game\objects\structures\janicart.dm" #include "code\game\objects\structures\kitchen_spike.dm" #include "code\game\objects\structures\ladders.dm" @@ -1873,7 +1872,6 @@ #include "code\game\objects\structures\tank_dispenser.dm" #include "code\game\objects\structures\tank_holder.dm" #include "code\game\objects\structures\training_machine.dm" -#include "code\game\objects\structures\tram_walls.dm" #include "code\game\objects\structures\traps.dm" #include "code\game\objects\structures\votingbox.dm" #include "code\game\objects\structures\watercloset.dm" @@ -3085,6 +3083,14 @@ #include "code\modules\hydroponics\grown\weeds\kudzu.dm" #include "code\modules\hydroponics\grown\weeds\nettle.dm" #include "code\modules\hydroponics\grown\weeds\starthistle.dm" +#include "code\modules\industrial_lift\crossing_signal.dm" +#include "code\modules\industrial_lift\industrial_lift.dm" +#include "code\modules\industrial_lift\lift_master.dm" +#include "code\modules\industrial_lift\tram_controls.dm" +#include "code\modules\industrial_lift\tram_landmark.dm" +#include "code\modules\industrial_lift\tram_lift_master.dm" +#include "code\modules\industrial_lift\tram_override_objects.dm" +#include "code\modules\industrial_lift\tram_walls.dm" #include "code\modules\instruments\items.dm" #include "code\modules\instruments\piano_synth.dm" #include "code\modules\instruments\stationary.dm"