From 79bad427c81b2d1b55044a15c6320944f04392e0 Mon Sep 17 00:00:00 2001 From: warriorstar-orion Date: Sat, 21 Dec 2024 03:07:44 -0500 Subject: [PATCH] Movement cross/uncross implementation. (#26762) * refactor: Movement cross/uncross implementation. * wrong var name * fix unit tests dropping PDAs into nowhere * Add documentation. * remove unused constants * say which procs are off limits * fix simpleanimal z change runtime * helps not to leave merge conflicts * kill me * fix typecast * fix projectile/table collision * treadmills don't cause MC to crash anymore * connect_loc is appropriate here * fix windoors and teleporters * fix bonfires and clarify docs * fix proximity sensors Tested with sensors in crates, sensors in modsuits Tested new proximity component with firing projectiles at singularity Tested new proximity component with portable flashes Tested new proximity component with facehuggers * lint * fix: polarized access helper false positives * Revert "fix: polarized access helper false positives" This reverts commit 9814f98cf6b3897d14609577b7db4b98a0c4a1ff. * hopefully the right change for mindflayer steam * Changes following cameras * fix glass table collision * appears to fix doorspam * fix ore bags not picking up ore * fix signatures of /Exited * remove debug log * remove duplicate signal registrar * fix emptying bags into locations * I don't trust these nested Move calls * use connect_loc for upgraded resonator fields * use moveToNullspace * fix spiderweb crossing * fix pass checking for windows from a tile off * fix bluespace closet/transparency issues * fix mechs not interacting with doors and probably other things * fix debug * fix telepete * add some docs * stop trying to shoehorn prox monitor into cards * I should make sure things build * kill override signal warning * undef signal * not many prox monitors survive going off like this * small fixes to storage * make moving wormholes respect signals * use correct signals for pulse demon * fix pulse heart too * fix smoke signals * may have fucked singulo projectile swerve * fix singulo projectile arcing * remove duplicate define * just look at it * hopefully last cleanups of incorrect signal usage * fix squeaking * may god have mercy on my soul * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: warriorstar-orion * lewc review * Apply suggestions from code review Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Signed-off-by: warriorstar-orion * burza review * fix bad args for grenade assemblies * Update code/__DEFINES/is_helpers.dm Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: warriorstar-orion --------- Signed-off-by: warriorstar-orion Co-authored-by: DGamerL Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> --- code/__DEFINES/dcs/atom_signals.dm | 6 +- code/__DEFINES/dcs/movable_signals.dm | 17 +- code/__DEFINES/is_helpers.dm | 2 + code/__DEFINES/movement_info.dm | 16 + code/__HELPERS/atom_helpers.dm | 10 + .../subsystem/non_firing/SSatoms.dm | 3 + code/datums/beam.dm | 23 +- code/datums/card_deck_table_tracker.dm | 145 ++++++ code/datums/components/caltrop.dm | 21 +- code/datums/components/connect_containers.dm | 68 +++ code/datums/components/connect_loc_behalf.dm | 67 +++ code/datums/components/connect_range.dm | 114 +++++ code/datums/components/orbiter.dm | 3 +- code/datums/components/proximity_monitor.dm | 451 ------------------ code/datums/components/slippery.dm | 18 +- code/datums/components/squeak.dm | 27 +- code/datums/components/swarming.dm | 16 +- code/datums/datum.dm | 13 +- code/datums/elements/connect_loc.dm | 43 ++ .../proximity/advanced_proximity_monitor.dm | 168 +++++++ code/datums/proximity/proximity_monitor.dm | 89 ++++ .../proximity/singulo_proximity_monitor.dm | 28 ++ code/datums/ruins/bridges/bridges.dm | 10 +- code/datums/spells/spacetime_dist.dm | 12 +- code/game/area/ai_monitored.dm | 2 +- code/game/atoms.dm | 30 +- code/game/atoms_movable.dm | 309 +++++++++--- code/game/gamemodes/cult/cult_structures.dm | 3 - .../miniantags/guardian/types/protector.dm | 2 +- .../miniantags/guardian/types/ranged.dm | 15 +- .../pulsedemon/cross_shock_component.dm | 24 +- .../miniantags/pulsedemon/pulsedemon.dm | 26 +- code/game/machinery/OpTable.dm | 2 +- code/game/machinery/camera/camera.dm | 11 + code/game/machinery/camera/camera_presets.dm | 4 +- code/game/machinery/deployable.dm | 15 +- code/game/machinery/doors/airlock.dm | 12 +- code/game/machinery/doors/airlock_types.dm | 2 +- code/game/machinery/doors/door.dm | 2 +- code/game/machinery/doors/firedoor.dm | 31 +- code/game/machinery/doors/windowdoor.dm | 38 +- code/game/machinery/flasher.dm | 3 +- code/game/machinery/machine_frame.dm | 4 +- code/game/machinery/shieldgen.dm | 4 +- code/game/machinery/tcomms/relay.dm | 2 +- code/game/machinery/tcomms/tcomms_base.dm | 2 +- code/game/machinery/tcomms/tcomms_core.dm | 2 +- code/game/machinery/teleporter.dm | 35 +- code/game/machinery/vendors/vending.dm | 9 +- code/game/mecha/mecha.dm | 22 +- code/game/objects/effects/alien_acid.dm | 14 +- code/game/objects/effects/anomalies.dm | 20 +- .../effects/decals/Cleanable/humans.dm | 12 +- .../decals/Cleanable/misc_cleanables.dm | 12 +- .../objects/effects/decals/Cleanable/tar.dm | 11 +- .../effects/decals/Cleanable/tracks.dm | 117 ++--- code/game/objects/effects/decals/cleanable.dm | 12 +- .../effects/effect_system/effects_foam.dm | 14 +- .../effects/effect_system/effects_smoke.dm | 34 +- code/game/objects/effects/forcefields.dm | 2 +- code/game/objects/effects/mines.dm | 15 +- code/game/objects/effects/portals.dm | 19 +- code/game/objects/effects/spiders.dm | 23 +- code/game/objects/effects/step_triggers.dm | 22 +- code/game/objects/items/stacks/stack.dm | 17 +- code/game/objects/items/toys.dm | 15 +- code/game/objects/items/weapons/caution.dm | 3 +- .../chemical_flamethrower/fire_effect.dm | 20 +- code/game/objects/items/weapons/explosives.dm | 8 +- .../items/weapons/grenades/chem_grenade.dm | 9 +- code/game/objects/items/weapons/legcuffs.dm | 29 +- code/game/objects/items/weapons/shards.dm | 12 +- .../objects/items/weapons/storage/bags.dm | 35 ++ .../items/weapons/storage/briefcase.dm | 2 +- .../items/weapons/storage/storage_base.dm | 8 +- code/game/objects/structures/aliens.dm | 8 +- code/game/objects/structures/coathanger.dm | 2 +- .../structures/crates_lockers/closets.dm | 33 +- code/game/objects/structures/fence.dm | 2 +- code/game/objects/structures/girders.dm | 2 +- code/game/objects/structures/grille.dm | 2 +- code/game/objects/structures/holosigns.dm | 2 +- code/game/objects/structures/inflatable.dm | 4 +- code/game/objects/structures/mineral_doors.dm | 2 +- code/game/objects/structures/morgue.dm | 2 +- code/game/objects/structures/nest.dm | 14 +- code/game/objects/structures/railings.dm | 59 ++- code/game/objects/structures/tables_racks.dm | 57 ++- .../structures/transit_tubes/transit_tube.dm | 2 +- code/game/objects/structures/watercloset.dm | 13 +- .../objects/structures/windoor_assembly.dm | 24 +- code/game/objects/structures/window.dm | 32 +- .../turfs/simulated/floor/asteroid_floors.dm | 4 +- code/game/turfs/simulated/floor/chasm.dm | 6 +- code/game/turfs/simulated/floor/misc_floor.dm | 2 +- code/game/turfs/turf.dm | 76 ++- .../changeling/powers/mutations.dm | 6 +- .../vampire_powers/hemomancer_powers.dm | 2 +- .../vampire/vampire_powers/umbrae_powers.dm | 10 +- code/modules/assembly/assembly.dm | 32 ++ code/modules/assembly/assembly_holder.dm | 14 +- code/modules/assembly/bomb.dm | 11 +- code/modules/assembly/infrared.dm | 12 +- code/modules/assembly/mousetrap.dm | 14 +- code/modules/assembly/proximity.dm | 86 +++- .../atmospherics/environmental/LINDA_fire.dm | 17 +- .../environmental/LINDA_system.dm | 10 +- .../awaymissions/mission_code/beach.dm | 2 +- .../mission_code/ruins/deepstorage.dm | 4 +- .../mission_code/ruins/telecomns.dm | 36 +- .../mission_code/shuttle_shadow.dm | 12 +- code/modules/events/blob/blob_mobs.dm | 2 +- .../events/blob/blob_structures/blob_core.dm | 4 +- .../blob/blob_structures/strong_blob.dm | 2 +- code/modules/events/blob/theblob.dm | 14 +- code/modules/events/brand_intelligence.dm | 5 +- code/modules/events/spacevine.dm | 14 +- code/modules/events/wormholes.dm | 2 +- code/modules/games/cards.dm | 9 +- code/modules/hydroponics/grown/towercap.dm | 15 +- code/modules/hydroponics/hydroponics_tray.dm | 2 +- .../lighting/lighting_emissive_blocker.dm | 4 +- code/modules/lighting/lighting_object.dm | 4 +- code/modules/lighting/lighting_turf.dm | 2 +- code/modules/mining/equipment/resonator.dm | 8 +- code/modules/mining/ores_coins.dm | 28 -- code/modules/mining/satchel_ore_boxdm.dm | 13 +- code/modules/mob/dead/dead.dm | 6 +- .../mob/dead/observer/observer_base.dm | 2 +- code/modules/mob/inventory_procs.dm | 6 +- .../living/carbon/alien/special/facehugger.dm | 11 +- code/modules/mob/living/living.dm | 13 +- .../living/silicon/robot/drone/maint_drone.dm | 2 +- .../mob/living/simple_animal/bot/griefsky.dm | 7 +- .../mob/living/simple_animal/bot/honkbot.dm | 12 +- .../mob/living/simple_animal/bot/mulebot.dm | 11 +- .../mob/living/simple_animal/bot/secbot.dm | 11 +- .../simple_animal/friendly/cockroach.dm | 14 +- .../living/simple_animal/friendly/mouse.dm | 11 +- .../simple_animal/hostile/floorcluwne.dm | 2 +- .../hostile/megafauna/ancient_robot.dm | 14 +- .../hostile/megafauna/bubblegum.dm | 8 +- .../hostile/megafauna/hierophant.dm | 9 +- .../hostile/megafauna/megafauna.dm | 2 +- .../hostile/mining/elites/elite.dm | 7 +- .../hostile/mining/elites/legionnaire.dm | 17 +- .../simple_animal/hostile/syndicate_mobs.dm | 2 +- .../hostile/terror_spiders/actions.dm | 13 +- .../hostile/terror_spiders/mother.dm | 2 +- .../simple_animal/hostile/venus_human_trap.dm | 7 +- .../mob/living/simple_animal/simple_animal.dm | 6 +- code/modules/mob/mob_movement.dm | 2 +- .../engines/singularity/containment_field.dm | 19 +- .../particle_accelerator/particle.dm | 12 +- .../power/engines/singularity/singularity.dm | 48 +- .../power/engines/supermatter/supermatter.dm | 2 +- .../power/engines/tesla/energy_ball.dm | 16 +- code/modules/power/generators/treadmill.dm | 41 +- code/modules/power/lights.dm | 12 +- .../projectile/special_projectiles.dm | 4 +- code/modules/projectiles/projectile_base.dm | 20 +- .../reagent_containers/glass_containers.dm | 11 +- .../reagents/reagent_containers/syringes.dm | 10 +- code/modules/reagents/reagent_dispenser.dm | 11 +- code/modules/recycling/conveyor2.dm | 10 +- code/modules/recycling/disposal.dm | 2 +- code/modules/recycling/sortingmachinery.dm | 2 +- .../ruins/lavalandruin_code/sin_ruins.dm | 2 +- .../ruins/objects_and_mobs/necropolis_gate.dm | 19 +- code/modules/shuttle/on_move.dm | 2 +- code/modules/surgery/organs/organ.dm | 9 +- code/modules/vehicle/vehicle.dm | 2 +- docs/references/movement_signals.md | 140 ++++++ mkdocs.yml | 1 + paradise.dme | 11 +- 175 files changed, 2491 insertions(+), 1322 deletions(-) create mode 100644 code/__DEFINES/movement_info.dm create mode 100644 code/__HELPERS/atom_helpers.dm create mode 100644 code/datums/card_deck_table_tracker.dm create mode 100644 code/datums/components/connect_containers.dm create mode 100644 code/datums/components/connect_loc_behalf.dm create mode 100644 code/datums/components/connect_range.dm delete mode 100644 code/datums/components/proximity_monitor.dm create mode 100644 code/datums/elements/connect_loc.dm create mode 100644 code/datums/proximity/advanced_proximity_monitor.dm create mode 100644 code/datums/proximity/proximity_monitor.dm create mode 100644 code/datums/proximity/singulo_proximity_monitor.dm create mode 100644 docs/references/movement_signals.md diff --git a/code/__DEFINES/dcs/atom_signals.dm b/code/__DEFINES/dcs/atom_signals.dm index d1dbbf2ee1a..294e5bd5aac 100644 --- a/code/__DEFINES/dcs/atom_signals.dm +++ b/code/__DEFINES/dcs/atom_signals.dm @@ -6,6 +6,8 @@ // /atom +//from SSatoms InitAtom - Only if the atom was not deleted or failed initialization and has a loc +#define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON "atom_init_success_on" // from SSatoms InitAtom - Only if the atom was not deleted or failed initialization #define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE "atom_init_success" ///from base of atom/attack_hulk(): (/mob/living/carbon/human) @@ -38,12 +40,12 @@ #define COMSIG_ATOM_UPDATE_OVERLAYS "atom_update_overlays" ///from base of [/atom/proc/update_icon]: (signalOut, did_anything) #define COMSIG_ATOM_UPDATED_ICON "atom_updated_icon" -///from base of atom/Entered(): (atom/movable/entering, /atom) +///from base of atom/Entered(): (atom/movable/entered, /atom) #define COMSIG_ATOM_ENTERED "atom_entered" ///from base of atom/Exit(): (/atom/movable/exiting, /atom/newloc) #define COMSIG_ATOM_EXIT "atom_exit" #define COMPONENT_ATOM_BLOCK_EXIT (1<<0) -///from base of atom/Exited(): (atom/movable/exiting, atom/newloc) +///from base of atom/Exited(): (atom/movable/exiting, direction) #define COMSIG_ATOM_EXITED "atom_exited" ///from base of atom/ex_act(): (severity, target) #define COMSIG_ATOM_EX_ACT "atom_ex_act" diff --git a/code/__DEFINES/dcs/movable_signals.dm b/code/__DEFINES/dcs/movable_signals.dm index 2de0ca3879d..7aa2beca933 100644 --- a/code/__DEFINES/dcs/movable_signals.dm +++ b/code/__DEFINES/dcs/movable_signals.dm @@ -4,21 +4,22 @@ * All signals send the source datum of the signal as the first argument */ +///from base of atom/movable/Moved(): (/atom) +#define COMSIG_MOVABLE_PRE_MOVE "movable_pre_move" + #define COMPONENT_MOVABLE_BLOCK_PRE_MOVE (1<<0) ///from base of atom/movable/Moved(): (/atom, dir) #define COMSIG_MOVABLE_MOVED "movable_moved" ///from base of atom/movable/Cross(): (/atom/movable) -#define COMSIG_MOVABLE_CROSS "movable_cross" -///from base of atom/movable/Crossed(): (/atom/movable) -#define COMSIG_MOVABLE_CROSSED "movable_crossed" -///when we cross over something (calling Crossed() on that atom) -#define COMSIG_CROSSED_MOVABLE "crossed_movable" +#define COMSIG_MOVABLE_CHECK_CROSS "movable_cross" + #define COMPONENT_BLOCK_CROSS (1<<0) +///from base of atom/movable/Move(): (/atom/movable) +#define COMSIG_MOVABLE_CHECK_CROSS_OVER "movable_cross_over" ///from base of atom/movable/Uncross(): (/atom/movable) #define COMSIG_MOVABLE_UNCROSS "movable_uncross" #define COMPONENT_MOVABLE_BLOCK_UNCROSS (1<<0) -///from base of atom/movable/Uncrossed(): (/atom/movable) -#define COMSIG_MOVABLE_UNCROSSED "movable_uncrossed" ///from base of atom/movable/Bump(): (/atom) #define COMSIG_MOVABLE_BUMP "movable_bump" + #define COMPONENT_INTERCEPT_BUMPED (1<<0) ///from base of atom/movable/throw_impact(): (/atom/hit_atom, /datum/thrownthing/throwingdatum) #define COMSIG_MOVABLE_IMPACT "movable_impact" #define COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH (1<<0) //if true, flip if the impact will push what it hits @@ -41,7 +42,7 @@ #define COMSIG_MOVABLE_THROW_LANDED "movable_throw_landed" ///from base of atom/movable/shove_impact(): (mob/living/target, mob/living/attacker) #define COMSIG_MOVABLE_SHOVE_IMPACT "movable_shove_impact" -///from base of atom/movable/onTransitZ(): (old_z, new_z) +///from base of atom/movable/on_changed_z_level(): (turf/old_turf, turf/new_turf) #define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit" /// Called just before something gets untilted diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index a6df2f62741..e524369c445 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -151,3 +151,5 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define ispassmeteorturf(A) (is_type_in_typecache(A, GLOB.turfs_pass_meteor)) #define is_screen_atom(A) istype(A, /atom/movable/screen) + +#define is_multi_tile_object(atom) (atom?.bound_width > world.icon_size || atom?.bound_height > world.icon_size) diff --git a/code/__DEFINES/movement_info.dm b/code/__DEFINES/movement_info.dm new file mode 100644 index 00000000000..95c90f7a1fb --- /dev/null +++ b/code/__DEFINES/movement_info.dm @@ -0,0 +1,16 @@ +/// The arguments of this macro correspond directly to the argument order of /atom/movable/proc/Moved +#define SET_ACTIVE_MOVEMENT(_old_loc, _direction, _forced, _oldlocs) \ + active_movement = list( \ + _old_loc, \ + _direction, \ + _forced, \ + _oldlocs, \ + ) + +/// Finish any active movements +#define RESOLVE_ACTIVE_MOVEMENT \ + if(active_movement) { \ + var/__move_args = active_movement; \ + active_movement = null; \ + Moved(arglist(__move_args)); \ + } diff --git a/code/__HELPERS/atom_helpers.dm b/code/__HELPERS/atom_helpers.dm new file mode 100644 index 00000000000..b0a0999da72 --- /dev/null +++ b/code/__HELPERS/atom_helpers.dm @@ -0,0 +1,10 @@ +/// Returns a list of all locations (except the area) the movable is within. +/proc/get_nested_locs(atom/movable/atom_on_location, include_turf = FALSE) + . = list() + var/atom/location = atom_on_location.loc + var/turf/our_turf = get_turf(atom_on_location) + while(location && location != our_turf) + . += location + location = location.loc + if(our_turf && include_turf) // At this point, only the turf is left, provided it exists. + . += our_turf diff --git a/code/controllers/subsystem/non_firing/SSatoms.dm b/code/controllers/subsystem/non_firing/SSatoms.dm index d40c236192b..7731c3dbdc8 100644 --- a/code/controllers/subsystem/non_firing/SSatoms.dm +++ b/code/controllers/subsystem/non_firing/SSatoms.dm @@ -102,6 +102,9 @@ SUBSYSTEM_DEF(atoms) BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT else SEND_SIGNAL(A, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE) + var/atom/location = A.loc + if(location) + SEND_SIGNAL(location, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, A, arguments[1]) return qdeleted || QDELING(A) diff --git a/code/datums/beam.dm b/code/datums/beam.dm index f35cc240ac8..7295dd84b23 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -118,6 +118,17 @@ anchored = TRUE var/datum/beam/owner +/obj/effect/ebeam/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/ebeam/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // ON_ATOM_ENTERED + return + /obj/effect/ebeam/ex_act(severity) return @@ -131,9 +142,8 @@ /obj/effect/ebeam/singularity_act() return -/obj/effect/ebeam/deadly/Crossed(atom/A, oldloc) - ..() - A.ex_act(EXPLODE_DEVASTATE) +/obj/effect/ebeam/deadly/on_atom_entered(datum/source, atom/movable/entered) + entered.ex_act(EXPLODE_DEVASTATE) /obj/effect/ebeam/vetus/Destroy() for(var/mob/living/M in get_turf(src)) @@ -147,11 +157,10 @@ /obj/effect/ebeam/disintegration layer = ON_EDGED_TURF_LAYER -/obj/effect/ebeam/disintegration/Crossed(atom/A, oldloc) - ..() - if(!isliving(A)) +/obj/effect/ebeam/disintegration/on_atom_entered(datum/source, atom/movable/entered) + if(!isliving(entered)) return - var/mob/living/L = A + var/mob/living/L = entered var/damage = 50 if(L.stat == DEAD) visible_message("[L] is disintegrated by the beam!") diff --git a/code/datums/card_deck_table_tracker.dm b/code/datums/card_deck_table_tracker.dm new file mode 100644 index 00000000000..d8f0cda1dff --- /dev/null +++ b/code/datums/card_deck_table_tracker.dm @@ -0,0 +1,145 @@ +#define COMSIG_CARD_DECK_FIELD_CLEAR "card_deck_field_clear" + +/datum/card_deck_table_tracker + /// How far away you can be (in terms of table squares). + var/max_table_distance + /// How far away you can be (euclidean distance). + var/max_total_distance + /// The UID of the deck + var/deck_uid + /// Indicate field activity with colors on the field's turfs. + var/debug = FALSE + /// The deck we're tracking. + var/atom/host + + /// The list of floors from which a player can access the card field. + var/list/floors = list() + /// The list of tables that the card deck's location is contiguous with. + var/list/tables = list() + +/datum/card_deck_table_tracker/New(atom/host_, max_table_distance_ = 5) + max_table_distance = max_table_distance_ + max_total_distance = max_table_distance_ + if(istype(host_, /obj/item/deck)) + // this is important for tracking traits and attacking multiple cards. so it's not a true UID, sue me + var/obj/item/deck/D = host_ + deck_uid = D.main_deck_id + else + deck_uid = host_.UID() + + host = host_ + RegisterSignal(host, COMSIG_MOVABLE_MOVED, PROC_REF(on_movable_moved)) + lay_out_field() + +/datum/card_deck_table_tracker/proc/on_movable_moved(datum/source, atom/old_loc, direction, forced) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + lay_out_field() + +/datum/card_deck_table_tracker/proc/lay_out_field() + for(var/turf/floor in floors) + SEND_SIGNAL(floor, COMSIG_CARD_DECK_FIELD_CLEAR) + + if(!isturf(host.loc)) + return + + floors.Cut() + tables.Cut() + + crawl_along(host.loc, 0) + + for(var/obj/structure/table in tables) + if(istype(table)) + RegisterSignal(table, COMSIG_PARENT_QDELETING, PROC_REF(on_table_qdel), override = TRUE) + + for(var/turf/turf in floors) + if(!istype(turf)) + continue + + if(debug) + turf.color = "#ffaaff" + + RegisterSignal(turf, COMSIG_ATOM_ENTERED, PROC_REF(on_atom_entered)) + RegisterSignal(turf, COMSIG_ATOM_EXITED, PROC_REF(on_atom_exited)) + RegisterSignal(turf, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_new_atom_at_loc)) + RegisterSignal(turf, COMSIG_CARD_DECK_FIELD_CLEAR, PROC_REF(on_card_deck_field_clear)) + + for(var/mob/living/L in turf) + on_atom_entered(turf, L) + +/datum/card_deck_table_tracker/Destroy(force, ...) + host = null + for(var/turf/floor in floors) + SEND_SIGNAL(floor, COMSIG_CARD_DECK_FIELD_CLEAR) + for(var/mob/living/L in floor) + REMOVE_TRAIT(L, TRAIT_PLAYING_CARDS, "deck_[deck_uid]") + + floors.Cut() + tables.Cut() + + return ..() + +/datum/card_deck_table_tracker/proc/on_atom_entered(turf/source, atom/movable/entered, old_loc) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + + var/mob/living/L = entered + if(istype(L)) + ADD_TRAIT(L, TRAIT_PLAYING_CARDS, "deck_[deck_uid]") + if(debug) + source.color = "#ff0000" + +/datum/card_deck_table_tracker/proc/on_atom_exited(turf/source, atom/movable/exited, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXITED + + var/mob/living/L = exited + if(istype(L)) + REMOVE_TRAIT(L, TRAIT_PLAYING_CARDS, "deck_[deck_uid]") + if(debug) + source.color = "#ffaaff" + +/datum/card_deck_table_tracker/proc/on_table_qdel(datum/source) + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + lay_out_field() + +/datum/card_deck_table_tracker/proc/on_new_atom_at_loc(turf/location, atom/created, init_flags) + SIGNAL_HANDLER // COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON + if(istable(created)) + lay_out_field() + +/datum/card_deck_table_tracker/proc/on_card_deck_field_clear(atom/target) + SIGNAL_HANDLER // COMSIG_CARD_DECK_FIELD_CLEAR + if(debug) + target.color = initial(target.color) + + UnregisterSignal(target, list( + COMSIG_ATOM_ENTERED, + COMSIG_ATOM_EXITED, + COMSIG_CARD_DECK_FIELD_CLEAR, + COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, + )) + +/datum/card_deck_table_tracker/proc/crawl_along(turf/current_turf, distance_from_start) + var/obj/structure/current_table = locate(/obj/structure/table) in current_turf + + if(QDELETED(current_table)) + // if there's no table here, we're still adjacent to a table, so this is a spot you could play from + floors |= current_turf + return + + if(current_table in tables) + return + + tables |= current_table + floors |= current_turf + + if(distance_from_start + 1 > max_table_distance) + return + + for(var/direction in GLOB.alldirs) + var/turf/next_turf = get_step(current_table, direction) + if(!istype(next_turf)) + continue + if(get_dist_euclidian(get_turf(host), next_turf) > max_total_distance) + continue + .(next_turf, distance_from_start + 1) + +#undef COMSIG_CARD_DECK_FIELD_CLEAR diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm index de94b8dfe93..bc0a510f47e 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -14,6 +14,11 @@ ///Shoebypassing, walking interaction, silence var/flags + ///given to connect_loc to listen for something moving over target + var/static/list/crossed_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + var/cooldown = 0 /datum/component/caltrop/Initialize(_min_damage = 0, _max_damage = 0, _probability = 100, _weaken_duration = 6 SECONDS, _flags = NONE) @@ -23,10 +28,12 @@ src.weaken_duration = _weaken_duration src.flags = _flags -/datum/component/caltrop/RegisterWithParent() - RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, PROC_REF(Crossed)) + if(ismovable(parent)) + AddComponent(/datum/component/connect_loc_behalf, parent, crossed_connections) + else + RegisterSignal(get_turf(parent), COMSIG_ATOM_ENTERED, PROC_REF(on_entered)) -/datum/component/caltrop/proc/Crossed(datum/source, atom/movable/AM) +/datum/component/caltrop/proc/on_entered(atom/source, atom/movable/entered, turf/old_loc) var/atom/A = parent if(!has_gravity(A)) return @@ -34,10 +41,10 @@ if(!prob(probability)) return - if(!ishuman(AM)) + if(!ishuman(entered)) return - var/mob/living/carbon/human/H = AM + var/mob/living/carbon/human/H = entered if(HAS_TRAIT(H, TRAIT_PIERCEIMMUNE)) return @@ -82,3 +89,7 @@ cooldown = world.time H.Weaken(weaken_duration) + +/datum/component/caltrop/UnregisterFromParent() + if(ismovable(parent)) + qdel(GetComponent(/datum/component/connect_loc_behalf)) diff --git a/code/datums/components/connect_containers.dm b/code/datums/components/connect_containers.dm new file mode 100644 index 00000000000..d998f0ae294 --- /dev/null +++ b/code/datums/components/connect_containers.dm @@ -0,0 +1,68 @@ +/// This component behaves similar to connect_loc_behalf, but it's nested and hooks a signal onto all MOVABLES containing this atom. +/datum/component/connect_containers + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + + /// An assoc list of signal -> procpath to register to the loc this object is on. + var/list/connections + /** + * The atom the component is tracking. The component will delete itself if the tracked is deleted. + * Signals will also be updated whenever it moves. + */ + var/atom/movable/tracked + +/datum/component/connect_containers/Initialize(atom/movable/tracked, list/connections) + . = ..() + if(!ismovable(tracked)) + return COMPONENT_INCOMPATIBLE + + src.connections = connections + set_tracked(tracked) + +/datum/component/connect_containers/Destroy() + set_tracked(null) + return ..() + +/datum/component/connect_containers/InheritComponent(datum/component/component, original, atom/movable/tracked, list/connections) + // Not equivalent. Checks if they are not the same list via shallow comparison. + if(!compare_list(src.connections, connections)) + stack_trace("connect_containers component attached to [parent] tried to inherit another connect_containers component with different connections") + return + if(src.tracked != tracked) + set_tracked(tracked) + +/datum/component/connect_containers/proc/set_tracked(atom/movable/new_tracked) + if(tracked) + UnregisterSignal(tracked, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) + unregister_signals(tracked.loc) + tracked = new_tracked + if(!tracked) + return + RegisterSignal(tracked, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(tracked, COMSIG_PARENT_QDELETING, PROC_REF(handle_tracked_qdel)) + update_signals(tracked) + +/datum/component/connect_containers/proc/handle_tracked_qdel() + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + qdel(src) + +/datum/component/connect_containers/proc/update_signals(atom/movable/listener) + if(!ismovable(listener.loc)) + return + + for(var/atom/movable/container as anything in get_nested_locs(listener)) + RegisterSignal(container, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + for(var/signal in connections) + parent.RegisterSignal(container, signal, connections[signal]) + +/datum/component/connect_containers/proc/unregister_signals(atom/movable/location) + if(!ismovable(location)) + return + + for(var/atom/movable/target as anything in (get_nested_locs(location) + location)) + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + parent.UnregisterSignal(target, connections) + +/datum/component/connect_containers/proc/on_moved(atom/movable/listener, atom/old_loc) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + unregister_signals(old_loc) + update_signals(listener) diff --git a/code/datums/components/connect_loc_behalf.dm b/code/datums/components/connect_loc_behalf.dm new file mode 100644 index 00000000000..8593fea8455 --- /dev/null +++ b/code/datums/components/connect_loc_behalf.dm @@ -0,0 +1,67 @@ +/// This component behaves similar to connect_loc, hooking into a signal on a tracked object's turf +/// It has the ability to react to that signal on behalf of a separate listener however +/// This has great use, primarily for components, but it carries with it some overhead +/// So we do it separately as it needs to hold state which is very likely to lead to bugs if it remains as an element. +/datum/component/connect_loc_behalf + dupe_mode = COMPONENT_DUPE_UNIQUE + + /// An assoc list of signal -> procpath to register to the loc this object is on. + var/list/connections + var/atom/movable/tracked + var/atom/tracked_loc + +/datum/component/connect_loc_behalf/Initialize(atom/movable/tracked, list/connections) + . = ..() + if(!istype(tracked)) + return COMPONENT_INCOMPATIBLE + src.connections = connections + src.tracked = tracked + +/datum/component/connect_loc_behalf/RegisterWithParent() + RegisterSignal(tracked, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(tracked, COMSIG_PARENT_QDELETING, PROC_REF(handle_tracked_qdel)) + update_signals() + +/datum/component/connect_loc_behalf/UnregisterFromParent() + unregister_signals() + UnregisterSignal(tracked, list( + COMSIG_MOVABLE_MOVED, + COMSIG_PARENT_QDELETING, + )) + + tracked = null + +/datum/component/connect_loc_behalf/proc/handle_tracked_qdel() + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + qdel(src) + +/datum/component/connect_loc_behalf/proc/update_signals() + unregister_signals() + //You may ask yourself, isn't this just silencing an error? + //The answer is yes, but there's no good cheap way to fix it + //What happens is the tracked object or hell the listener gets say, deleted, which makes targets[old_loc] return a null + //The null results in a bad index, because of course it does + //It's not a solvable problem though, since both actions, the destroy and the move, are sourced from the same signal send + //And sending a signal should be agnostic of the order of listeners + //So we need to either pick the order agnositic, or destroy safe + //And I picked destroy safe. Let's hope this is the right path! + if(isnull(tracked.loc)) + return + + tracked_loc = tracked.loc + + for(var/signal in connections) + parent.RegisterSignal(tracked_loc, signal, connections[signal]) + +/datum/component/connect_loc_behalf/proc/unregister_signals() + if(isnull(tracked_loc)) + return + + parent.UnregisterSignal(tracked_loc, connections) + + tracked_loc = null + +/datum/component/connect_loc_behalf/proc/on_moved(sigtype, atom/movable/tracked, atom/old_loc) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + update_signals() + diff --git a/code/datums/components/connect_range.dm b/code/datums/components/connect_range.dm new file mode 100644 index 00000000000..8cf961d2221 --- /dev/null +++ b/code/datums/components/connect_range.dm @@ -0,0 +1,114 @@ +/** + * This component behaves similar to connect_loc_behalf but for all turfs in range, hooking into a signal on each of them. + * Just like connect_loc_behalf, It can react to that signal on behalf of a seperate listener. + * Good for components, though it carries some overhead. Can't be an element as that may lead to bugs. + */ +/datum/component/connect_range + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + + /// An assoc list of signal -> procpath to register to the loc this object is on. + var/list/connections + /// The turfs currently connected to this component + var/list/turfs = list() + /** + * The atom the component is tracking. The component will delete itself if the tracked is deleted. + * Signals will also be updated whenever it moves (if it's a movable). + */ + var/atom/tracked + + /// The component will hook into signals only on turfs not farther from tracked than this. + var/range + /// Whether the component works when the movable isn't directly located on a turf. + var/works_in_containers + +/datum/component/connect_range/Initialize(atom/tracked, list/connections, range, works_in_containers = TRUE) + if(!isatom(tracked) || isarea(tracked) || range < 0) + return COMPONENT_INCOMPATIBLE + src.connections = connections + src.range = range + set_tracked(tracked) + src.works_in_containers = works_in_containers + +/datum/component/connect_range/Destroy() + set_tracked(null) + return ..() + +/datum/component/connect_range/InheritComponent(datum/component/component, original, atom/tracked, list/connections, range, works_in_containers) + // Not equivalent. Checks if they are not the same list via shallow comparison. + if(!compare_list(src.connections, connections)) + stack_trace("connect_range component attached to [parent] tried to inherit another connect_range component with different connections") + return + if(src.tracked != tracked) + set_tracked(tracked) + if(src.range == range && src.works_in_containers == works_in_containers) + return + //Unregister the signals with the old settings. + unregister_signals(isturf(tracked) ? tracked : tracked.loc, turfs) + src.range = range + src.works_in_containers = works_in_containers + //Re-register the signals with the new settings. + update_signals(src.tracked) + +/datum/component/connect_range/proc/set_tracked(atom/new_tracked) + if(tracked) //Unregister the signals from the old tracked and its surroundings + unregister_signals(isturf(tracked) ? tracked : tracked.loc, turfs) + UnregisterSignal(tracked, list( + COMSIG_MOVABLE_MOVED, + COMSIG_PARENT_QDELETING, + )) + tracked = new_tracked + if(!tracked) + return + //Register signals on the new tracked atom and its surroundings. + RegisterSignal(tracked, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(tracked, COMSIG_PARENT_QDELETING, PROC_REF(handle_tracked_qdel)) + update_signals(tracked) + +/datum/component/connect_range/proc/handle_tracked_qdel() + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + qdel(src) + +/datum/component/connect_range/proc/update_signals(atom/target, atom/old_loc) + var/turf/current_turf = get_turf(target) + if(isnull(current_turf)) + unregister_signals(old_loc, turfs) + turfs = list() + return + + if(ismovable(target.loc)) + if(!works_in_containers) + unregister_signals(old_loc, turfs) + turfs = list() + return + //Keep track of possible movement of all movables the target is in. + for(var/atom/movable/container as anything in get_nested_locs(target)) + RegisterSignal(container, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + + //Only register/unregister turf signals if it's moved to a new turf. + if(current_turf == get_turf(old_loc)) + unregister_signals(old_loc, null) + return + var/list/old_turfs = turfs + turfs = RANGE_TURFS(range, current_turf) + unregister_signals(old_loc, old_turfs - turfs) + for(var/turf/target_turf as anything in turfs - old_turfs) + for(var/signal in connections) + parent.RegisterSignal(target_turf, signal, connections[signal]) + +/datum/component/connect_range/proc/unregister_signals(atom/location, list/remove_from) + //The location is null or is a container and the component shouldn't have register signals on it + if(isnull(location) || (!works_in_containers && !isturf(location))) + return + + if(ismovable(location)) + for(var/atom/movable/target as anything in (get_nested_locs(location) + location)) + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + + if(!length(remove_from)) + return + for(var/turf/target_turf as anything in remove_from) + parent.UnregisterSignal(target_turf, connections) + +/datum/component/connect_range/proc/on_moved(atom/movable/movable, atom/old_loc) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + update_signals(movable, old_loc) diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm index d697ada974e..84fd4fab5b3 100644 --- a/code/datums/components/orbiter.dm +++ b/code/datums/components/orbiter.dm @@ -289,7 +289,7 @@ * Removes all orbit-related signals up its hierarchy and moves orbiters to the current child. * As this will never be called by a turf, this should not conflict with parent_move_react. */ -/datum/component/orbiter/proc/on_remove_child(atom/movable/exiting, atom/new_loc) +/datum/component/orbiter/proc/on_remove_child(datum/source, atom/movable/exiting, direction) SIGNAL_HANDLER // COMSIG_ATOM_EXITED // ensure the child is actually connected to the orbited atom @@ -299,6 +299,7 @@ remove_signals(exiting) RegisterSignal(exiting, COMSIG_MOVABLE_MOVED, PROC_REF(parent_move_react), TRUE) RegisterSignal(exiting, COMSIG_ATOM_EXITED, PROC_REF(on_remove_child), TRUE) + var/new_loc = get_step(exiting, direction) INVOKE_ASYNC(src, PROC_REF(handle_parent_move), exiting, exiting.loc, new_loc) /** diff --git a/code/datums/components/proximity_monitor.dm b/code/datums/components/proximity_monitor.dm deleted file mode 100644 index 4d524ba6cea..00000000000 --- a/code/datums/components/proximity_monitor.dm +++ /dev/null @@ -1,451 +0,0 @@ -/** - * # Basic Proximity Monitor - * - * Attaching this component to an atom means that the atom will be able to detect mobs or objects moving within a specified radius of it. - * - * The component creates several [/obj/effect/abstract/proximity_checker] objects, which follow the `parent` AKA `hasprox_receiver` around, always making sure it's at the center. - * When something crosses one of these proximiy checkers, the `hasprox_receiver` will have the `HasProximity()` proc called on it, with the crossing mob/obj as the argument. - */ -/datum/component/proximity_monitor - can_transfer = TRUE - var/name = "Proximity detection field" - /// The primary atom the component is attached to and that will be receiving `HasProximity()` calls. Same as the `parent`. - var/atom/hasprox_receiver - /** - * A list which contains references to movable atoms which the `hasprox_receiver` has moved into. - * Used to handle complex situations where the receiver is nested several layers deep into an object. - * For example: inside of a box, that's inside of a bag, which is worn on a human. In this situation there are 3 locations we need to listen to for movement. - */ - var/list/nested_receiver_locs - /// The radius of the field, in tiles. - var/radius - /// A list of currently created [/obj/effect/abstract/proximity_checker] in use with this component. - var/list/proximity_checkers - /// The type of checker object that should be used for the main field. - var/field_checker_type = /obj/effect/abstract/proximity_checker - /// Should the parent always detect proximity and update the field on movement, even if it's not on a turf? - var/always_active - -/datum/component/proximity_monitor/Initialize(_radius = 1, _always_active = FALSE) - . = ..() - if(!ismovable(parent) && !isturf(parent)) // No areas or datums allowed. - return COMPONENT_INCOMPATIBLE - ASSERT(_radius >= 1) - hasprox_receiver = parent - radius = _radius - always_active = _always_active - nested_receiver_locs = list() - create_prox_checkers() - - if(isturf(hasprox_receiver.loc)) - toggle_checkers(TRUE) - else if(always_active) - toggle_checkers(TRUE) - else - toggle_checkers(FALSE) - -/datum/component/proximity_monitor/Destroy(force, silent) - hasprox_receiver = null - nested_receiver_locs.Cut() - QDEL_LIST_CONTENTS(proximity_checkers) - return ..() - -/datum/component/proximity_monitor/RegisterWithParent() - if(ismovable(hasprox_receiver)) - RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_MOVED, PROC_REF(on_receiver_move)) - RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_DISPOSING, PROC_REF(on_disposal_enter)) - RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_EXIT_DISPOSALS, PROC_REF(on_disposal_exit)) - map_nested_locs() - -/datum/component/proximity_monitor/UnregisterFromParent() - if(ismovable(hasprox_receiver)) - UnregisterSignal(hasprox_receiver, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_DISPOSING, COMSIG_MOVABLE_EXIT_DISPOSALS)) - clear_nested_locs() - -/** - * Called when the `hasprox_receiver` moves. - * - * Arguments: - * * datum/source - this will be the `hasprox_receiver` - * * atom/old_loc - the location the receiver just moved from - * * dir - the direction the receiver just moved in - */ -/datum/component/proximity_monitor/proc/on_receiver_move(datum/source, atom/old_loc, dir) - SIGNAL_HANDLER - - // It was just a normal tile-based move, so we return here. - if(dir) - move_prox_checkers(dir) - return - - // Moving onto a turf. - if(!isturf(old_loc) && isturf(hasprox_receiver.loc)) - toggle_checkers(TRUE) - clear_nested_locs() - - // Moving into an object. - else if(always_active) - toggle_checkers(TRUE) - map_nested_locs() - - // The receiver moved into something, but isn't `always_active`, so deactivate the checkers. - else - toggle_checkers(FALSE) - - recenter_prox_checkers() - -/** - * Called when an atom in `nested_receiver_locs` list moves, if one exists. - * - * Arguments: - * * atom/moved_atom - one of the atoms in `nested_receiver_locs` - * * atom/old_loc - the location `moved_atom` just moved from - * * dir - the direction `moved_atom` just moved in - */ -/datum/component/proximity_monitor/proc/on_nested_loc_move(atom/moved_atom, atom/old_loc, dir) - SIGNAL_HANDLER - - // It was just a normal tile-based move, so we return here. - if(dir) - move_prox_checkers(dir) - return - - // Moving onto a turf. - if(!isturf(old_loc) && isturf(moved_atom.loc)) - toggle_checkers(TRUE) - - map_nested_locs() - recenter_prox_checkers() - -/** - * Called when the receiver or an atom in the `nested_receiver_locs` list moves into a disposals holder object. - * - * This proc receives arguments, but they aren't needed. - */ -/datum/component/proximity_monitor/proc/on_disposal_enter(datum/source) - SIGNAL_HANDLER - - toggle_checkers(FALSE) - -/** - * Called when the receiver or an atom in the `nested_receiver_locs` list moves out of a disposals holder object. - * - * This proc receives arguments, but they aren't needed. - */ -/datum/component/proximity_monitor/proc/on_disposal_exit(datum/source) - SIGNAL_HANDLER - - toggle_checkers(TRUE) - -/** - * Registers signals to any nested locations the `hasprox_receiver` is in, excluding turfs, so they can be monitored for movement. - */ -/datum/component/proximity_monitor/proc/map_nested_locs() - clear_nested_locs() - var/atom/loc_to_check = hasprox_receiver.loc - - while(loc_to_check && !isturf(loc_to_check)) - if(loc_to_check in nested_receiver_locs) - continue - nested_receiver_locs += loc_to_check - RegisterSignal(loc_to_check, COMSIG_MOVABLE_MOVED, PROC_REF(on_nested_loc_move)) - RegisterSignal(loc_to_check, COMSIG_MOVABLE_DISPOSING, PROC_REF(on_disposal_enter)) - RegisterSignal(loc_to_check, COMSIG_MOVABLE_EXIT_DISPOSALS, PROC_REF(on_disposal_exit)) - loc_to_check = loc_to_check.loc - -/** - * Removes and unregisters signals from all objects currently in the `nested_receiver_locs` list. - */ -/datum/component/proximity_monitor/proc/clear_nested_locs() - for(var/nested_loc in nested_receiver_locs) - UnregisterSignal(nested_loc, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_DISPOSING, COMSIG_MOVABLE_EXIT_DISPOSALS)) - nested_receiver_locs = list() - -/** - * Relays basic directional movement from the `hasprox_receiver` or `host`, to all objects in the `proximity_checkers` list. - * - * Arguments: - * * move_dir - the direction the checkers should move in - */ -/datum/component/proximity_monitor/proc/move_prox_checkers(move_dir) - for(var/obj/P as anything in proximity_checkers) - P.loc = get_step(P, move_dir) - -/** - * Update all of the component's proximity checker's to either become active or not active. - * - * Arguments: - * * new_active - the value to be assigned to the proximity checker's `active` variable - */ -/datum/component/proximity_monitor/proc/toggle_checkers(new_active) - for(var/obj/effect/abstract/proximity_checker/P as anything in proximity_checkers) - P.active = new_active - -/** - * Specifies a new radius for the field. Creates or deletes proximity_checkers accordingly. - * - * This proc *can* have a high cost due to the `new`s and `qdel`s of the proximity checkers, depending on the number of calls you need to make to it. - * - * Arguments: - * new_radius - the new value that `proximity_radius` should be set to. - */ -/datum/component/proximity_monitor/proc/set_radius(new_radius) - ASSERT(new_radius >= 1) - - var/new_field_size = (1 + new_radius * 2) ** 2 - var/old_field_size = length(proximity_checkers) - radius = new_radius - - // Radius is decreasing. - if(new_field_size < old_field_size) - for(var/i in 1 to (old_field_size - new_field_size)) - qdel(proximity_checkers[length(proximity_checkers)]) // Pop the last entry. - // Radius is increasing. - else - var/turf/parent_turf = get_turf(parent) - for(var/i in 1 to (new_field_size - old_field_size)) - create_single_prox_checker(parent_turf) - recenter_prox_checkers() - -/** - * Creates a single proximity checker object, at the given location and of the given type. Adds it to the proximity checkers list. - * - * Arguments: - * * turf/T - the turf the checker should be created on - * * checker_type - the type of [/obj/item/abstract/proximity_checker] to create - */ -/datum/component/proximity_monitor/proc/create_single_prox_checker(turf/T, checker_type = field_checker_type) - var/obj/effect/abstract/proximity_checker/P = new checker_type(T, src) - proximity_checkers += P - return P - -/** - * Called in Initialize(). Generates a set of [proximity checker][/obj/effect/abstract/proximity_checker] objects around the parent. - */ -/datum/component/proximity_monitor/proc/create_prox_checkers() - LAZYINITLIST(proximity_checkers) - var/turf/parent_turf = get_turf(parent) - // For whatever reason their turf is null. Create the checkers in nullspace for now. When the parent moves to a valid turf, they can be recenetered. - if(!parent_turf) - // Since we can't use `in range` in nullspace, we need to calculate the number of checkers to create with the below formula. - var/checker_amount = (1 + radius * 2) ** 2 - for(var/i in 1 to checker_amount) - create_single_prox_checker(null) - return - for(var/T in RANGE_TURFS(radius, parent_turf)) - create_single_prox_checker(T) - -/** - * Re-centers all of the `proximity_checker`s around the parent's current location. - */ -/datum/component/proximity_monitor/proc/recenter_prox_checkers() - var/turf/parent_turf = get_turf(parent) - if(!parent_turf) - toggle_checkers(FALSE) - return // Need a sanity check here for certain situations like objects moving into disposal holders. Their turf will be null in these cases. - var/index = 1 - for(var/T in RANGE_TURFS(radius, parent_turf)) - var/obj/checker = proximity_checkers[index++] - checker.loc = T - -/** - * # Basic Proximity Checker - * - * Inteded for use with the proximity checker component [/datum/component/proximity_monitor]. - * Whenever a movable atom crosses this object, it calls `HasProximity()` on the object which is listening for proximity (`hasprox_receiver`). - * - * Because these objects try to make the smallest footprint possible, when these objects move **they should use direct `loc` setting only, and not `forceMove`!** - * The overhead for forceMove is very unnecessary, because for example turfs and areas don't need to know when these have entered or exited them, etc. - * These are invisible objects who's sole purpose is to simply inform the receiver that something has moved within X tiles of the it. - */ -/obj/effect/abstract/proximity_checker - name = "proximity checker" - /// The component that this object is in use with, and that will receive `HasProximity()` calls. - var/datum/component/proximity_monitor/monitor - /// Whether or not the proximity checker is listening for things crossing it. - var/active - -/obj/effect/abstract/proximity_checker/Initialize(mapload, datum/component/proximity_monitor/P) - . = ..() - monitor = P - -/obj/effect/abstract/proximity_checker/Destroy() - monitor.proximity_checkers -= src - monitor = null - return ..() - -/** - * Called when something crossed over the proximity_checker. Notifies the `hasprox_receiver` it has proximity with something. - * - * Arguments: - * * atom/movable/AM - the atom crossing the proximity checker - * * oldloc - the location `AM` used to be at - */ -/obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM, oldloc) - set waitfor = FALSE - . = ..() - if(active && AM != monitor.hasprox_receiver && !(AM in monitor.nested_receiver_locs)) - monitor.hasprox_receiver.HasProximity(AM) - -/// A custom proximity monitor used for tracking players around a table of cards. - -/datum/component/proximity_monitor/table - /// How far away you can be (in terms of table squares). - var/max_table_distance - /// How far away you can be (euclidean distance). - var/max_total_distance - /// The UID of the deck - var/deck_uid - /// Whether the monitors created should be visible. Used for debugging. - var/monitors_visible = FALSE - -/datum/component/proximity_monitor/table/Initialize(_radius = 1, _always_active = FALSE, _max_table_distance = 5) - max_table_distance = _max_table_distance - max_total_distance = _max_table_distance - . = ..(_radius, _always_active) - if(istype(parent, /obj/item/deck)) - // this is important for tracking traits and attacking multiple cards. so it's not a true UID, sue me - var/obj/item/deck/D = parent - deck_uid = D.main_deck_id - else - deck_uid = parent.UID() - addtimer(CALLBACK(src, PROC_REF(refresh)), 0.5 SECONDS) - -/datum/component/proximity_monitor/table/proc/refresh() - var/list/tables = list() - var/list/prox_mon_spots = list() - crawl_along(get_turf(parent), tables, prox_mon_spots, 0) - QDEL_LIST_CONTENTS(proximity_checkers) - create_prox_checkers() - -/// Crawl along an extended table, and return a list of all turfs that we should start tracking. -/datum/component/proximity_monitor/table/proc/crawl_along(turf/current_turf, list/visited_tables = list(), list/prox_mon_spots = list(), distance_from_start) - var/obj/structure/current_table = locate(/obj/structure/table) in current_turf - - if(QDELETED(current_table)) - // if there's no table here, we're still adjacent to a table, so this is a spot you could play from - prox_mon_spots |= current_turf - return - - if(current_table in visited_tables) - return - - visited_tables |= current_table - prox_mon_spots |= current_turf - - if(distance_from_start + 1 > max_table_distance) - return - - for(var/direction in GLOB.alldirs) - var/turf/next_turf = get_step(current_table, direction) - if(!istype(next_turf)) - continue - if(get_dist_euclidian(get_turf(parent), next_turf) > max_total_distance) - continue - .(next_turf, visited_tables, prox_mon_spots, distance_from_start + 1) - -/datum/component/proximity_monitor/table/create_prox_checkers() - update_prox_checkers(FALSE) - -/** - * Update the proximity monitors making up this component. - * Arguments: - * * clear_existing - If true, any existing proximity monitors attached to this will be deleted. - */ -/datum/component/proximity_monitor/table/proc/update_prox_checkers(clear_existing = TRUE) - var/list/tables = list() - var/list/prox_mon_spots = list() - if(length(proximity_checkers)) - QDEL_LIST_CONTENTS(proximity_checkers) - - var/atom/movable/atom_parent = parent - - // if we don't have a parent, just treat it normally - if(!isturf(atom_parent.loc) || !locate(/obj/structure/table) in get_turf(parent)) - return - - - LAZYINITLIST(proximity_checkers) - crawl_along(get_turf(parent), tables, prox_mon_spots, 0) - - // For whatever reason their turf is null. Create the checkers in nullspace for now. When the parent moves to a valid turf, they can be recenetered. - for(var/T in prox_mon_spots) - create_single_prox_checker(T, /obj/effect/abstract/proximity_checker/table) - - for(var/atom/table in tables) - RegisterSignal(table, COMSIG_PARENT_QDELETING, PROC_REF(on_table_qdel), TRUE) - -/datum/component/proximity_monitor/table/on_receiver_move(datum/source, atom/old_loc, dir) - update_prox_checkers() - -/datum/component/proximity_monitor/table/RegisterWithParent() - if(ismovable(hasprox_receiver)) - RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_MOVED, PROC_REF(on_receiver_move)) - -/datum/component/proximity_monitor/table/proc/on_table_qdel() - SIGNAL_HANDLER // COMSIG_PARENT_QDELETED - update_prox_checkers() - -/obj/effect/abstract/proximity_checker/table - /// The UID for the deck, used in the setting and removal of traits - var/deck_uid - -/obj/effect/abstract/proximity_checker/table/Initialize(mapload, datum/component/proximity_monitor/table/P) - . = ..() - deck_uid = P.deck_uid - // catch any mobs on our tile - for(var/mob/living/L in get_turf(src)) - register_on_mob(L) - - if(P.monitors_visible) - icon = 'icons/obj/playing_cards.dmi' - icon_state = "tarot_the_unknown" - invisibility = INVISIBILITY_MINIMUM - layer = MOB_LAYER - -/obj/effect/abstract/proximity_checker/table/Destroy() - var/obj/effect/abstract/proximity_checker/table/same_monitor - for(var/obj/effect/abstract/proximity_checker/table/mon in get_turf(src)) - if(mon != src && mon.deck_uid == src) - // if we have another monitor on our space that shares our deck, - // transfer the signals to it. - same_monitor = mon - - for(var/mob/living/L in get_turf(src)) - remove_from_mob(L) - if(!isnull(same_monitor)) - same_monitor.register_on_mob(L) - return ..() - -/obj/effect/abstract/proximity_checker/table/proc/register_on_mob(mob/living/L) - ADD_TRAIT(L, TRAIT_PLAYING_CARDS, "deck_[deck_uid]") - RegisterSignal(L, COMSIG_MOVABLE_MOVED, PROC_REF(on_move_from_monitor), TRUE) - RegisterSignal(L, COMSIG_PARENT_QDELETING, PROC_REF(remove_from_mob), TRUE) - - -/obj/effect/abstract/proximity_checker/table/proc/remove_from_mob(mob/living/L) - if(QDELETED(L)) - return - // otherwise, clean up - REMOVE_TRAIT(L, TRAIT_PLAYING_CARDS, "deck_[deck_uid]") - UnregisterSignal(L, COMSIG_MOVABLE_MOVED) - -/obj/effect/abstract/proximity_checker/table/Crossed(atom/movable/AM, oldloc) - if(!isliving(AM)) - return - - var/mob/mover = AM - - // This should hopefully ensure that multiple decks around each other don't overlap - register_on_mob(mover) - -/// Triggered when someone moves from a tile that contains our monitor. -/obj/effect/abstract/proximity_checker/table/proc/on_move_from_monitor(atom/movable/tracked, atom/old_loc) - SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED - for(var/obj/effect/abstract/proximity_checker/table/mon in get_turf(tracked)) - // if we're moving onto a turf that shares our stuff, keep the signals and stuff registered - if(mon.deck_uid == deck_uid) - return - - // otherwise, clean up - remove_from_mob(tracked) diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm index bc7b0e05f8a..50f7d817f44 100644 --- a/code/datums/components/slippery.dm +++ b/code/datums/components/slippery.dm @@ -24,6 +24,10 @@ var/slip_verb /// TRUE the player will only slip if the mob this datum is attached to is horizontal var/horizontal_required + ///what we give to connect_loc by default, makes slippable mobs moving over us slip + var/static/list/default_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(slip), + ) /datum/component/slippery/Initialize(_description, _knockdown = 0, _slip_chance = 100, _slip_tiles = 0, _walking_is_safe = TRUE, _slip_always = FALSE, _slip_verb = "slip", _horizontal_required = FALSE) if(!isatom(parent)) @@ -38,19 +42,21 @@ slip_verb = _slip_verb horizontal_required = _horizontal_required -/datum/component/slippery/RegisterWithParent() - RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED), PROC_REF(Slip)) + add_connect_loc_behalf_to_parent() -/datum/component/slippery/UnregisterFromParent() - UnregisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED)) +/datum/component/slippery/proc/add_connect_loc_behalf_to_parent() + if(ismovable(parent)) + AddComponent(/datum/component/connect_loc_behalf, parent, default_connections) /** - Called whenever the parent receives either the `MOVABLE_CROSSED` signal or the `ATOM_ENTERED` signal. + Called whenever the parent receives the `ATOM_ENTERED` signal. Calls the `victim`'s `slip()` proc with the component's variables as arguments. Additionally calls the parent's `after_slip()` proc on the `victim`. */ -/datum/component/slippery/proc/Slip(datum/source, mob/living/carbon/human/victim) +/datum/component/slippery/proc/slip(datum/source, mob/living/carbon/human/victim) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(istype(victim) && !HAS_TRAIT(victim, TRAIT_FLYING)) var/atom/movable/owner = parent if(!isturf(owner.loc)) diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index bfb939ed103..0583c1f8a4f 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -19,13 +19,20 @@ ///sound exponent for squeak. Defaults to 10 as squeaking is loud and annoying enough. var/sound_falloff_exponent = 10 + ///what we set connect_loc to if parent is an item + var/static/list/item_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + + /datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override, squeak_on_move, extrarange, falloff_exponent, fallof_distance) if(!isatom(parent)) return COMPONENT_INCOMPATIBLE RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATTACK_BY), PROC_REF(play_squeak)) if(ismovable(parent)) RegisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT), PROC_REF(play_squeak)) - RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, PROC_REF(play_squeak_crossed)) + + AddComponent(/datum/component/connect_loc_behalf, parent, item_connections) RegisterSignal(parent, COMSIG_MOVABLE_DISPOSING, PROC_REF(disposing_react)) if(squeak_on_move) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(play_squeak)) @@ -71,25 +78,23 @@ else steps++ -/datum/component/squeak/proc/play_squeak_crossed(atom/source, atom/movable/crossing) - if(!isturf(crossing.loc) || !isturf(source.loc)) +/datum/component/squeak/proc/on_atom_entered(datum/source, atom/movable/entered) + if(istype(entered, /obj/effect)) return - if(istype(crossing, /obj/effect)) - return - if(ismob(crossing)) - var/mob/M = crossing + if(ismob(entered)) + var/mob/M = entered if(HAS_TRAIT(M, TRAIT_FLYING)) return - if(isliving(crossing)) + if(isliving(entered)) var/mob/living/L = M if(L.floating) return - else if(isitem(crossing)) + else if(isitem(entered)) var/obj/item/I = source if(I.flags & ABSTRACT) return - if(isprojectile(crossing)) - var/obj/item/projectile/P = crossing + if(isprojectile(entered)) + var/obj/item/projectile/P = entered if(P.original != parent) return if(ismob(source)) diff --git a/code/datums/components/swarming.dm b/code/datums/components/swarming.dm index 928589fbaaa..608359d4908 100644 --- a/code/datums/components/swarming.dm +++ b/code/datums/components/swarming.dm @@ -3,6 +3,11 @@ var/offset_y = 0 var/is_swarming = FALSE var/list/swarm_members = list() + var/static/list/swarming_loc_connections = list( + COMSIG_ATOM_EXITED = PROC_REF(leave_swarm), + COMSIG_ATOM_ENTERED = PROC_REF(join_swarm) + ) + /datum/component/swarming/Initialize(max_x = 24, max_y = 24) if(!ismovable(parent)) @@ -10,8 +15,7 @@ offset_x = rand(-max_x, max_x) offset_y = rand(-max_y, max_y) - RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, PROC_REF(join_swarm)) - RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, PROC_REF(leave_swarm)) + AddComponent(/datum/component/connect_loc_behalf, parent, swarming_loc_connections) /datum/component/swarming/Destroy() for(var/other in swarm_members) @@ -22,8 +26,8 @@ swarm_members = null return ..() -/datum/component/swarming/proc/join_swarm(datum/source, atom/movable/AM) - var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming) +/datum/component/swarming/proc/join_swarm(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) + var/datum/component/swarming/other_swarm = arrived.GetComponent(/datum/component/swarming) if(!other_swarm) return swarm() @@ -31,8 +35,8 @@ other_swarm.swarm() other_swarm.swarm_members |= src -/datum/component/swarming/proc/leave_swarm(datum/source, atom/movable/AM) - var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming) +/datum/component/swarming/proc/leave_swarm(datum/source, atom/movable/gone, direction) + var/datum/component/swarming/other_swarm = gone.GetComponent(/datum/component/swarming) if(!other_swarm || !(other_swarm in swarm_members)) return swarm_members -= other_swarm diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 56e35b452ed..e09769af755 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -57,6 +57,15 @@ qdel(C, FALSE, TRUE) dc.Cut() + _clear_signal_refs() + //END: ECS SHIT + + return QDEL_HINT_QUEUE + +/// Do not override this. This proc exists solely to be overriden by /turf. This +/// allows it to ignore clearing out signals which refer to it, in order to keep +/// those signals valid after the turf has been changed. +/datum/proc/_clear_signal_refs() var/list/lookup = comp_lookup if(lookup) for(var/sig in lookup) @@ -72,10 +81,6 @@ for(var/target in signal_procs) UnregisterSignal(target, signal_procs[target]) - //END: ECS SHIT - - return QDEL_HINT_QUEUE - /datum/nothing // Placeholder object, used for ispath checks. Has to be defined to prevent errors, but shouldn't ever be created. diff --git a/code/datums/elements/connect_loc.dm b/code/datums/elements/connect_loc.dm new file mode 100644 index 00000000000..6fcc1474679 --- /dev/null +++ b/code/datums/elements/connect_loc.dm @@ -0,0 +1,43 @@ +/// This element hooks a signal onto the loc the current object is on. +/// When the object moves, it will unhook the signal and rehook it to the new object. +/datum/element/connect_loc + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + + /// An assoc list of signal -> procpath to register to the loc this object is on. + var/list/connections + +/datum/element/connect_loc/Attach(atom/movable/listener, list/connections) + . = ..() + if(!istype(listener)) + return ELEMENT_INCOMPATIBLE + + src.connections = connections + + RegisterSignal(listener, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved), override = TRUE) + update_signals(listener) + +/datum/element/connect_loc/Detach(atom/movable/listener) + . = ..() + unregister_signals(listener, listener.loc) + UnregisterSignal(listener, COMSIG_MOVABLE_MOVED) + +/datum/element/connect_loc/proc/update_signals(atom/movable/listener) + var/atom/listener_loc = listener.loc + if(QDELETED(listener) || QDELETED(listener_loc)) + return + + for(var/signal in connections) + //override=TRUE because more than one connect_loc element instance tracked object can be on the same loc + listener.RegisterSignal(listener_loc, signal, connections[signal], override=TRUE) + +/datum/element/connect_loc/proc/unregister_signals(datum/listener, atom/old_loc) + if(isnull(old_loc)) + return + + listener.UnregisterSignal(old_loc, connections) + +/datum/element/connect_loc/proc/on_moved(atom/movable/listener, atom/old_loc) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + unregister_signals(listener, old_loc) + update_signals(listener) diff --git a/code/datums/proximity/advanced_proximity_monitor.dm b/code/datums/proximity/advanced_proximity_monitor.dm new file mode 100644 index 00000000000..bd47bfd6cb9 --- /dev/null +++ b/code/datums/proximity/advanced_proximity_monitor.dm @@ -0,0 +1,168 @@ +#define FIELD_TURFS_KEY "field_turfs" +#define EDGE_TURFS_KEY "edge_turfs" + +/** + * Movable and easily code-modified fields! Allows for custom AOE effects that affect movement + * and anything inside of them, and can do custom turf effects! + * Supports automatic recalculation/reset on movement. + * + * "What do I gain from using advanced over standard prox monitors?" + * - You can set different effects on edge vs field entrance + * - You can set effects when the proximity monitor starts and stops tracking a turf + */ +/datum/proximity_monitor/advanced + /// If TRUE, edge turfs will be included as "in the field" for effects + /// Can be used in certain situations where you may have effects that trigger only at the edge, + /// while also wanting the field effect to trigger at edge turfs as well + var/edge_is_a_field = FALSE + /// All turfs on the inside of the proximity monitor - range - 1 turfs + var/list/turf/field_turfs = list() + /// All turfs on the very last tile of the proximity monitor's radius + var/list/turf/edge_turfs = list() + +/datum/proximity_monitor/advanced/Destroy() + cleanup_field() + return ..() + +/datum/proximity_monitor/advanced/proc/cleanup_field() + for(var/turf/turf as anything in edge_turfs) + cleanup_edge_turf(turf) + edge_turfs = list() + for(var/turf/turf as anything in field_turfs) + cleanup_field_turf(turf) + field_turfs = list() + +//Call every time the field moves (done automatically if you use update_center) or a setup specification is changed. +/datum/proximity_monitor/advanced/proc/recalculate_field(full_recalc = FALSE) + var/list/new_turfs = update_new_turfs() + + var/list/old_field_turfs = field_turfs + var/list/old_edge_turfs = edge_turfs + field_turfs = new_turfs[FIELD_TURFS_KEY] + edge_turfs = new_turfs[EDGE_TURFS_KEY] + if(full_recalc) + field_turfs = list() + edge_turfs = list() + + for(var/turf/old_turf as anything in old_field_turfs - field_turfs) + if(QDELETED(src)) + return + cleanup_field_turf(old_turf) + for(var/turf/old_turf as anything in old_edge_turfs - edge_turfs) + if(QDELETED(src)) + return + cleanup_edge_turf(old_turf) + + if(full_recalc) + old_field_turfs = list() + old_edge_turfs = list() + field_turfs = new_turfs[FIELD_TURFS_KEY] + edge_turfs = new_turfs[EDGE_TURFS_KEY] + + for(var/turf/new_turf as anything in field_turfs - old_field_turfs) + if(QDELETED(src)) + return + setup_field_turf(new_turf) + + for(var/turf/new_turf as anything in edge_turfs - old_edge_turfs) + if(QDELETED(src)) + return + setup_edge_turf(new_turf) + +/datum/proximity_monitor/advanced/on_initialized(turf/location, atom/created, init_flags) + . = ..() + on_entered(location, created, null) + +/datum/proximity_monitor/advanced/on_entered(turf/source, atom/movable/entered, turf/old_loc) + . = ..() + if(get_dist(source, host) == current_range) + field_edge_crossed(entered, old_loc, source) + else + field_turf_crossed(entered, old_loc, source) + +/datum/proximity_monitor/advanced/on_moved(atom/movable/movable, atom/old_loc) + . = ..() + if(ignore_if_not_on_turf) + //Early return if it's not the host that has moved. + if(movable != host) + return + //Cleanup the field if the host was on a turf but isn't anymore. + if(!isturf(host.loc)) + if(isturf(old_loc)) + cleanup_field() + return + recalculate_field(full_recalc = FALSE) + +/datum/proximity_monitor/advanced/on_uncrossed(turf/source, atom/movable/gone, direction) + if(get_dist(source, host) == current_range) + field_edge_uncrossed(gone, source, get_turf(gone)) + else + field_turf_uncrossed(gone, source, get_turf(gone)) + +/// Called when a turf in the field of the monitor is linked +/datum/proximity_monitor/advanced/proc/setup_field_turf(turf/target) + return + +/// Called when a turf in the field of the monitor is unlinked +/// Do NOT call this manually, requires management of the field_turfs list +/datum/proximity_monitor/advanced/proc/cleanup_field_turf(turf/target) + PRIVATE_PROC(TRUE) + return + +/// Called when a turf in the edge of the monitor is linked +/datum/proximity_monitor/advanced/proc/setup_edge_turf(turf/target) + if(edge_is_a_field) // If the edge is considered a field, set it up like one + setup_field_turf(target) + +/// Called when a turf in the edge of the monitor is unlinked +/// Do NOT call this manually, requires management of the edge_turfs list +/datum/proximity_monitor/advanced/proc/cleanup_edge_turf(turf/target) + if(edge_is_a_field) // If the edge is considered a field, clean it up like one + cleanup_field_turf(target) + +/datum/proximity_monitor/advanced/proc/update_new_turfs() + if(ignore_if_not_on_turf && !isturf(host.loc)) + return list(FIELD_TURFS_KEY = list(), EDGE_TURFS_KEY = list()) + var/list/local_field_turfs = list() + var/list/local_edge_turfs = list() + var/turf/center = get_turf(host) + if(current_range > 0) + local_field_turfs += RANGE_TURFS(current_range - 1, center) + if(current_range > 1) + local_edge_turfs = RANGE_TURFS(current_range, center) - local_field_turfs + return list(FIELD_TURFS_KEY = local_field_turfs, EDGE_TURFS_KEY = local_edge_turfs) + +//Gets edge direction/corner, only works with square radius/WDH fields! +/datum/proximity_monitor/advanced/proc/get_edgeturf_direction(turf/T, turf/center_override = null) + var/turf/checking_from = get_turf(host) + if(istype(center_override)) + checking_from = center_override + if(!(T in edge_turfs)) + return + if(((T.x == (checking_from.x + current_range)) || (T.x == (checking_from.x - current_range))) && ((T.y == (checking_from.y + current_range)) || (T.y == (checking_from.y - current_range)))) + return get_dir(checking_from, T) + if(T.x == (checking_from.x + current_range)) + return EAST + if(T.x == (checking_from.x - current_range)) + return WEST + if(T.y == (checking_from.y - current_range)) + return SOUTH + if(T.y == (checking_from.y + current_range)) + return NORTH + +/datum/proximity_monitor/advanced/proc/field_turf_crossed(atom/movable/movable, turf/old_location, turf/new_location) + return + +/datum/proximity_monitor/advanced/proc/field_turf_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) + return + +/datum/proximity_monitor/advanced/proc/field_edge_crossed(atom/movable/movable, turf/old_location, turf/new_location) + if(edge_is_a_field) // If the edge is considered a field, pass crossed to that + field_turf_crossed(movable, old_location, new_location) + +/datum/proximity_monitor/advanced/proc/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) + if(edge_is_a_field) // If the edge is considered a field, pass uncrossed to that + field_turf_uncrossed(movable, old_location, new_location) + +#undef FIELD_TURFS_KEY +#undef EDGE_TURFS_KEY diff --git a/code/datums/proximity/proximity_monitor.dm b/code/datums/proximity/proximity_monitor.dm new file mode 100644 index 00000000000..09a7d32f916 --- /dev/null +++ b/code/datums/proximity/proximity_monitor.dm @@ -0,0 +1,89 @@ +/datum/proximity_monitor + ///The atom we are tracking + var/atom/host + ///The atom that will receive HasProximity calls. + var/atom/hasprox_receiver + ///The range of the proximity monitor. Things moving wihin it will trigger HasProximity calls. + var/current_range + ///If we don't check turfs in range if the host's loc isn't a turf + var/ignore_if_not_on_turf + ///The signals of the connect range component, needed to monitor the turfs in range. + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + COMSIG_ATOM_EXITED = PROC_REF(on_uncrossed), + COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON = PROC_REF(on_initialized), + ) + +/datum/proximity_monitor/New(atom/_host, range = 1, _ignore_if_not_on_turf = TRUE) + ignore_if_not_on_turf = _ignore_if_not_on_turf + current_range = range + set_host(_host) + +/datum/proximity_monitor/proc/set_host(atom/new_host, atom/new_receiver) + if(new_host == host) + return + if(host) //No need to delete the connect range and containers comps. They'll be updated with the new tracked host. + UnregisterSignal(host, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) + if(hasprox_receiver) + UnregisterSignal(hasprox_receiver, COMSIG_PARENT_QDELETING) + if(new_receiver) + hasprox_receiver = new_receiver + if(new_receiver != new_host) + RegisterSignal(new_receiver, COMSIG_PARENT_QDELETING, PROC_REF(on_host_or_receiver_del)) + else if(hasprox_receiver == host) //Default case + hasprox_receiver = new_host + host = new_host + RegisterSignal(new_host, COMSIG_PARENT_QDELETING, PROC_REF(on_host_or_receiver_del)) + var/static/list/containers_connections = list(COMSIG_MOVABLE_MOVED = PROC_REF(on_moved), COMSIG_MOVABLE_Z_CHANGED = PROC_REF(on_z_change)) + AddComponent(/datum/component/connect_containers, host, containers_connections) + RegisterSignal(host, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(host, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_z_change)) + set_range(current_range, TRUE) + +/datum/proximity_monitor/proc/on_host_or_receiver_del(datum/source) + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + qdel(src) + +/datum/proximity_monitor/Destroy() + host = null + hasprox_receiver = null + return ..() + +/datum/proximity_monitor/proc/set_range(range, force_rebuild = FALSE) + if(!force_rebuild && range == current_range) + return FALSE + . = TRUE + current_range = range + + //If the connect_range component exists already, this will just update its range. No errors or duplicates. + AddComponent(/datum/component/connect_range, host, loc_connections, range, !ignore_if_not_on_turf) + +/datum/proximity_monitor/proc/on_moved(atom/movable/source, atom/old_loc) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + if(source == host) + hasprox_receiver?.HasProximity(host) + +/datum/proximity_monitor/proc/on_z_change() + SIGNAL_HANDLER // COMSIG_MOVABLE_Z_CHANGED + return + +/datum/proximity_monitor/proc/set_ignore_if_not_on_turf(does_ignore = TRUE) + if(ignore_if_not_on_turf == does_ignore) + return + ignore_if_not_on_turf = does_ignore + //Update the ignore_if_not_on_turf + AddComponent(/datum/component/connect_range, host, loc_connections, current_range, ignore_if_not_on_turf) + +/datum/proximity_monitor/proc/on_uncrossed() + SIGNAL_HANDLER // COMSIG_ATOM_EXITED + return //Used by the advanced subtype for effect fields. + +/datum/proximity_monitor/proc/on_entered(atom/source, atom/movable/arrived, turf/old_loc) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(source != host) + hasprox_receiver?.HasProximity(arrived) + +/datum/proximity_monitor/proc/on_initialized(turf/location, atom/created, init_flags) + SIGNAL_HANDLER // COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON + if(location != host) + hasprox_receiver?.HasProximity(created) diff --git a/code/datums/proximity/singulo_proximity_monitor.dm b/code/datums/proximity/singulo_proximity_monitor.dm new file mode 100644 index 00000000000..e7b870ba346 --- /dev/null +++ b/code/datums/proximity/singulo_proximity_monitor.dm @@ -0,0 +1,28 @@ +/datum/proximity_monitor/advanced/singulo + +/datum/proximity_monitor/advanced/singulo/on_entered(turf/source, atom/movable/entered, turf/old_loc) + . = ..() + if(!isprojectile(entered)) + return + + var/angle_to_singulo = ATAN2(host.y - source.y, host.x - source.x) + var/distance_to_singulo = get_dist(source, host) + + var/obj/item/projectile/P = entered + var/distance = distance_to_singulo + var/projectile_angle = P.Angle + var/angle_to_projectile = angle_to_singulo + if(angle_to_projectile == 180) + angle_to_projectile = -180 + angle_to_projectile -= projectile_angle + if(angle_to_projectile > 180) + angle_to_projectile -= 360 + else if(angle_to_projectile < -180) + angle_to_projectile += 360 + + if(distance == 0) + qdel(P) + return + projectile_angle += angle_to_projectile / (distance ** 2) + P.damage += 10 / distance + P.set_angle(projectile_angle) diff --git a/code/datums/ruins/bridges/bridges.dm b/code/datums/ruins/bridges/bridges.dm index 666754127a7..50be4d96c83 100644 --- a/code/datums/ruins/bridges/bridges.dm +++ b/code/datums/ruins/bridges/bridges.dm @@ -139,9 +139,15 @@ name = "clockwork floor" icon_state = "clockwork_floor" -// Pretend to be a normal clockwork floor and duplicate its visual effect -/obj/structure/bridge_walkway/clockwork/Crossed(atom/crosser, atom/old_loc) +/obj/structure/bridge_walkway/clockwork/Initialize(mapload) . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_crossed) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +// Pretend to be a normal clockwork floor and duplicate its visual effect +/obj/structure/bridge_walkway/clockwork/proc/on_crossed(atom/crosser) var/counter = 0 for(var/obj/effect/temp_visual/ratvar/floor/floor in contents) if(++counter == 3) diff --git a/code/datums/spells/spacetime_dist.dm b/code/datums/spells/spacetime_dist.dm index e7cee2a416a..5b26c280c59 100644 --- a/code/datums/spells/spacetime_dist.dm +++ b/code/datums/spells/spacetime_dist.dm @@ -94,6 +94,13 @@ /obj/effect/cross_action/singularity_pull() return +/obj/effect/cross_action/spacetime_dist/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/cross_action/spacetime_dist/proc/walk_link(atom/movable/AM) if(linked_dist && walks_left > 0) flick("purplesparkles", src) @@ -106,9 +113,9 @@ AM.forceMove(get_turf(src)) cant_teleport = FALSE -/obj/effect/cross_action/spacetime_dist/Crossed(atom/movable/AM, oldloc) +/obj/effect/cross_action/spacetime_dist/proc/on_atom_entered(atom/source, atom/movable/entered, turf/old_loc) if(!cant_teleport) - walk_link(AM) + walk_link(entered) /obj/effect/cross_action/spacetime_dist/attackby__legacy__attackchain(obj/item/W, mob/user, params) if(user.drop_item(W)) @@ -123,4 +130,5 @@ /obj/effect/cross_action/spacetime_dist/Destroy() cant_teleport = TRUE linked_dist = null + RemoveElement(/datum/element/connect_loc) return ..() diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index db60b423c5d..61c9cab09b7 100644 --- a/code/game/area/ai_monitored.dm +++ b/code/game/area/ai_monitored.dm @@ -20,7 +20,7 @@ cam.newTarget(O) return -/area/station/ai_monitored/Exited(atom/movable/O) +/area/station/ai_monitored/Exited(atom/movable/O, direction) ..() if(ismob(O) && length(motioncameras)) for(var/X in motioncameras) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 59548e2a038..9d33e71059b 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -311,6 +311,13 @@ return /atom/proc/Bumped(atom/movable/AM) + // This may seem scary but one will find that replacing this with + // SHOULD_NOT_SLEEP(TRUE) surfaces two dozen instances where /Bumped sleeps, + // such as airlocks. We cannot wait for these procs to finish because they + // will clobber any movement which occurred in the intervening time. If we + // want to get rid of this we need to move bumping in its entirety to signal + // handlers, which is scarier. + set waitfor = FALSE return /// Convenience proc to see if a container is open for chemistry handling @@ -333,9 +340,6 @@ /atom/proc/is_drainable() return reagents && (container_type & DRAINABLE) -/atom/proc/CheckExit() - return TRUE - /atom/proc/HasProximity(atom/movable/AM) return @@ -1205,13 +1209,23 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) /atom/Entered(atom/movable/AM, atom/oldLoc) SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) -/atom/Exit(atom/movable/AM, atom/newLoc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) +/** + * An atom is attempting to exit this atom's contents + * + * Default behaviour is to send the [COMSIG_ATOM_EXIT] + */ +/atom/Exit(atom/movable/leaving, direction) + // Don't call `..()` here, otherwise `Uncross()` gets called. + // See the doc comment on `Uncross()` to learn why this is bad. + + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, leaving, direction) & COMPONENT_ATOM_BLOCK_EXIT) return FALSE -/atom/Exited(atom/movable/AM, atom/newLoc) - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) + return TRUE + +/atom/Exited(atom/movable/AM, direction) + var/new_loc = get_step(AM, direction) + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, new_loc) /* Adds an instance of colour_type to the atom's atom_colours list diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 0af90866207..8a2c0a5df46 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -3,6 +3,8 @@ appearance_flags = TILE_BOUND glide_size = 8 // Default, adjusted when mobs move based on their movement delays var/last_move = null + /// A list containing arguments for Moved(). + VAR_PRIVATE/tmp/list/active_movement var/anchored = FALSE var/move_resist = MOVE_RESIST_DEFAULT var/move_force = MOVE_FORCE_DEFAULT @@ -16,9 +18,12 @@ var/no_spin = FALSE var/no_spin_thrown = FALSE var/mob/pulledby = null + var/atom/movable/pulling /// Face towards the atom while pulling it var/face_while_pulling = FALSE + /// Whether this atom should have its dir automatically changed when it moves. Setting this to FALSE allows for things such as directional windows to retain dir on moving without snowflake code all of the place. + var/set_dir_on_move = TRUE var/throwforce = 0 var/inertia_dir = 0 @@ -199,16 +204,95 @@ /** - * meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts) - * if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this - * most of the time you want forceMove() + * Meant for movement with zero side effects. Only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts). + * If you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this. + * Most of the time you want [forceMove()]. */ /atom/movable/proc/abstract_move(atom/new_loc) + RESOLVE_ACTIVE_MOVEMENT // This should NEVER happen, but, just in case... var/atom/old_loc = loc var/direction = get_dir(old_loc, new_loc) loc = new_loc Moved(old_loc, direction, TRUE) +/// Here's where we rewrite how byond handles movement except slightly different. +/// To be removed on step_ conversion. +/// All this work to prevent a second bump. +/atom/movable/Move(atom/newloc, direction, glide_size_override = 0, update_dir = TRUE) + . = FALSE + if(!newloc || newloc == loc) + return + + // A mid-movement... movement... occured, resolve that first. + RESOLVE_ACTIVE_MOVEMENT + + if(!direction) + direction = get_dir(src, newloc) + + if(set_dir_on_move && dir != direction && update_dir) + setDir(direction) + + var/is_multi_tile_object = is_multi_tile_object(src) + + var/list/old_locs + if(is_multi_tile_object && isturf(loc)) + old_locs = locs // locs is a special list, this is effectively the same as .Copy() but with less steps + for(var/atom/exiting_loc as anything in old_locs) + if(!exiting_loc.Exit(src, direction)) + return + else + if(!loc.Exit(src, direction)) + return + + var/list/new_locs + if(is_multi_tile_object && isturf(newloc)) + new_locs = block( + newloc, + locate( + min(world.maxx, newloc.x + CEILING(bound_width / 32, 1)), + min(world.maxy, newloc.y + CEILING(bound_height / 32, 1)), + newloc.z + ) + ) // If this is a multi-tile object then we need to predict the new locs and check if they allow our entrance. + for(var/atom/entering_loc as anything in new_locs) + if(!entering_loc.Enter(src)) + return + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, entering_loc) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) + return + else // Else just try to enter the single destination. + if(!newloc.Enter(src)) + return + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) + return + + // Past this is the point of no return + var/atom/oldloc = loc + var/area/oldarea = get_area(oldloc) + var/area/newarea = get_area(newloc) + + SET_ACTIVE_MOVEMENT(oldloc, direction, FALSE, old_locs) + loc = newloc + + . = TRUE + + if(old_locs) // This condition will only be true if it is a multi-tile object. + for(var/atom/exited_loc as anything in (old_locs - new_locs)) + exited_loc.Exited(src, direction) + else // Else there's just one loc to be exited. + oldloc.Exited(src, direction) + if(oldarea != newarea) + oldarea.Exited(src, direction) + + if(new_locs) // Same here, only if multi-tile. + for(var/atom/entered_loc as anything in (new_locs - old_locs)) + entered_loc.Entered(src, oldloc, old_locs) + else + newloc.Entered(src, oldloc, old_locs) + if(oldarea != newarea) + newarea.Entered(src, oldarea) + + RESOLVE_ACTIVE_MOVEMENT + /atom/movable/Move(atom/newloc, direct = 0, movetime) if(!loc || !newloc) return FALSE @@ -228,22 +312,21 @@ var/direct_NS = direct & (NORTH | SOUTH) var/direct_EW = direct & (EAST | WEST) var/first_step_target = get_step(src, direct_NS) - Move(first_step_target, direct_NS) + step(src, direct_NS) if(loc == first_step_target) first_step_dir = direct_NS moving_diagonally = SECOND_DIAG_STEP - . = Move(get_step(src, direct_EW), direct_EW) + . = step(src, direct_EW) else if(loc == oldloc) first_step_target = get_step(src, direct_EW) - Move(first_step_target, direct_EW) + step(src, direct_EW) if(loc == first_step_target) first_step_dir = direct_EW moving_diagonally = SECOND_DIAG_STEP - . = Move(get_step(src, direct_NS), direct_NS) + . = step(src, direct_NS) if(first_step_dir != 0) if(!.) setDir(first_step_dir) - Moved(oldloc, first_step_dir) else if(!inertia_moving) inertia_next_move = world.time + inertia_move_delay newtonian_move(direct) @@ -254,9 +337,6 @@ last_move = 0 return - if(.) - Moved(oldloc, direct) - last_move = direct move_speed = world.time - l_move_time l_move_time = world.time @@ -265,14 +345,20 @@ . = FALSE // Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) +/atom/movable/proc/Moved(atom/old_loc, Dir, Forced = FALSE) + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, Dir, Forced) if(!inertia_moving) inertia_next_move = world.time + inertia_move_delay newtonian_move(Dir) if(length(client_mobs_in_contents)) update_parallax_contents() + var/turf/old_turf = get_turf(old_loc) + var/turf/new_turf = get_turf(src) + + if(old_turf?.z != new_turf?.z) + on_changed_z_level(old_turf, new_turf) + var/datum/light_source/L var/thing for(thing in light_sources) // Cycle through the light sources on this atom and tell them to update. @@ -280,6 +366,50 @@ L.source_atom.update_light() return TRUE +// Make sure you know what you're doing if you call this +// You probably want CanPass() +/atom/movable/Cross(atom/movable/crossed_atom) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_CHECK_CROSS, crossed_atom) & COMPONENT_BLOCK_CROSS) + return FALSE + if(SEND_SIGNAL(crossed_atom, COMSIG_MOVABLE_CHECK_CROSS_OVER, src) & COMPONENT_BLOCK_CROSS) + return FALSE + return CanPass(crossed_atom, get_dir(src, crossed_atom)) + +///default byond proc that is deprecated for us in lieu of signals. do not call +/atom/movable/Crossed(atom/movable/crossed_atom, oldloc) + SHOULD_NOT_OVERRIDE(TRUE) + CRASH("atom/movable/Crossed() was called!") + +/** + * `Uncross()` is a default BYOND proc that is called when something is *going* + * to exit this atom's turf. It is prefered over `Uncrossed` when you want to + * deny that movement, such as in the case of border objects, objects that allow + * you to walk through them in any direction except the one they block + * (think side windows). + * + * While being seemingly harmless, most everything doesn't actually want to + * use this, meaning that we are wasting proc calls for every single atom + * on a turf, every single time something exits it, when basically nothing + * cares. + * + * If you want to replicate the old `Uncross()` behavior, the most apt + * replacement is [`/datum/element/connect_loc`] while hooking onto + * [`COMSIG_ATOM_EXIT`]. + */ +/atom/movable/Uncross() + SHOULD_NOT_OVERRIDE(TRUE) + CRASH("Uncross() should not be being called, please read the doc-comment for it for why.") + +/** + * default byond proc that is normally called on everything inside the previous turf + * a movable was in after moving to its current turf + * this is wasteful since the vast majority of objects do not use Uncrossed + * use connect_loc to register to COMSIG_ATOM_EXITED instead + */ +/atom/movable/Uncrossed(atom/movable/uncrossed_atom) + SHOULD_NOT_OVERRIDE(TRUE) + CRASH("/atom/movable/Uncrossed() was called") + // Change glide size for the duration of one movement /atom/movable/proc/glide_for(movetime) if(movetime) @@ -289,57 +419,122 @@ else glide_size = initial(glide_size) -// Previously known as HasEntered() -// This is automatically called when something enters your square -/atom/movable/Crossed(atom/movable/AM, oldloc) - SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) - SEND_SIGNAL(AM, COMSIG_CROSSED_MOVABLE, src) - -/atom/movable/Uncrossed(atom/movable/AM) - SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM) - -/atom/movable/Bump(atom/bumped_atom, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). // suffering - if(bumped_atom && yes) - SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, bumped_atom) - if(!QDELETED(throwing)) - throwing.finalize(TRUE, bumped_atom) - . = TRUE - if(QDELETED(bumped_atom)) - return - bumped_atom.Bumped(src) +/atom/movable/Bump(atom/bumped_atom) + if(!bumped_atom) + CRASH("Bump was called with no argument.") + if(SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, bumped_atom) & COMPONENT_INTERCEPT_BUMPED) + return + . = ..() + if(!QDELETED(throwing)) + throwing.finalize(hit = TRUE, target = bumped_atom) + . = TRUE + if(QDELETED(bumped_atom)) + return + bumped_atom.Bumped(src) /atom/movable/proc/forceMove(atom/destination) - var/turf/old_loc = loc - loc = destination - moving_diagonally = 0 + . = FALSE + if(destination) + . = doMove(destination) + else + CRASH("No valid destination passed into forceMove") - if(old_loc) - old_loc.Exited(src, destination) - for(var/atom/movable/AM in old_loc) - AM.Uncrossed(src) +/* + * Move ourself to nullspace. Use to indicate clearly that you + * know that you are doing so, as opposed to calling forceMove(null), + * accidentally or otherwise. + */ +/atom/movable/proc/moveToNullspace() + return doMove(null) + +/atom/movable/proc/doMove(atom/destination) + . = FALSE + RESOLVE_ACTIVE_MOVEMENT + + var/atom/oldloc = loc + var/is_multi_tile = bound_width > world.icon_size || bound_height > world.icon_size + + SET_ACTIVE_MOVEMENT(oldloc, NONE, TRUE, null) if(destination) - destination.Entered(src) - for(var/atom/movable/AM in destination) - if(AM == src) - continue - AM.Crossed(src, old_loc) - var/turf/oldturf = get_turf(old_loc) - var/turf/destturf = get_turf(destination) - var/old_z = (oldturf ? oldturf.z : null) - var/dest_z = (destturf ? destturf.z : null) - if(old_z != dest_z) - onTransitZ(old_z, dest_z) + if(pulledby) + pulledby.stop_pulling() - Moved(old_loc, NONE) + var/same_loc = oldloc == destination + var/area/old_area = get_area(oldloc) + var/area/destarea = get_area(destination) + var/movement_dir = get_dir(src, destination) - return TRUE + moving_diagonally = 0 -/atom/movable/proc/onTransitZ(old_z,new_z) - for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. - var/atom/movable/AM = item - AM.onTransitZ(old_z,new_z) - SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED) + loc = destination + + if(!same_loc) + 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) + 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 + + if(oldloc) + loc = null + var/area/old_area = get_area(oldloc) + 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) + + RESOLVE_ACTIVE_MOVEMENT + +/** + * Called when a movable changes z-levels. + * + * Arguments: + * * old_turf - The previous turf they were on before. + * * new_turf - The turf they have now entered. + * * notify_contents - Whether or not to notify the movable's contents that their z-level has changed. + */ +/atom/movable/proc/on_changed_z_level(turf/old_turf, turf/new_turf, notify_contents = TRUE) + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_turf, new_turf) + + if(!notify_contents) + return + + for(var/atom/movable/content as anything in src) // Notify contents of Z-transition. + content.on_changed_z_level(old_turf, new_turf) /mob/living/forceMove(atom/destination) if(buckled) @@ -539,7 +734,7 @@ /atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) return FALSE -/atom/movable/CanPass(atom/movable/mover, turf/target) +/atom/movable/CanPass(atom/movable/mover, border_dir) // This condition is copied from atom to avoid an extra parent call, because this is a very hot proc. if(!density) return TRUE diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index b948bad9da6..26066e3b5bf 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -341,7 +341,4 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list( /obj/effect/gateway/Bumped(atom/movable/AM) return -/obj/effect/gateway/Crossed(atom/movable/AM, oldloc) - return - #undef CULT_STRUCTURE_COOLDOWN diff --git a/code/game/gamemodes/miniantags/guardian/types/protector.dm b/code/game/gamemodes/miniantags/guardian/types/protector.dm index e385fd1e329..756b4489bde 100644 --- a/code/game/gamemodes/miniantags/guardian/types/protector.dm +++ b/code/game/gamemodes/miniantags/guardian/types/protector.dm @@ -125,7 +125,7 @@ color = linked_guardian.name_color shield_orientation = left_or_right -/obj/effect/guardianshield/CanPass(atom/movable/mover, turf/target) +/obj/effect/guardianshield/CanPass(atom/movable/mover, border_dir) if(mover == linked_guardian) return TRUE return FALSE diff --git a/code/game/gamemodes/miniantags/guardian/types/ranged.dm b/code/game/gamemodes/miniantags/guardian/types/ranged.dm index d3f67b67ef5..862fe56ff11 100644 --- a/code/game/gamemodes/miniantags/guardian/types/ranged.dm +++ b/code/game/gamemodes/miniantags/guardian/types/ranged.dm @@ -88,18 +88,25 @@ var/mob/living/spawner invisibility = 101 +/obj/effect/snare/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/snare/singularity_act() return /obj/effect/snare/singularity_pull() return -/obj/effect/snare/Crossed(AM as mob|obj, oldloc) - if(isliving(AM)) +/obj/effect/snare/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isliving(entered)) var/turf/snare_loc = get_turf(loc) if(spawner) - to_chat(spawner, "[AM] has crossed your surveillance trap at [get_area(snare_loc)].") + to_chat(spawner, "[entered] has crossed your surveillance trap at [get_area(snare_loc)].") if(isguardian(spawner)) var/mob/living/simple_animal/hostile/guardian/G = spawner if(G.summoner) - to_chat(G.summoner, "[AM] has crossed your surveillance trap at [get_area(snare_loc)].") + to_chat(G.summoner, "[entered] has crossed your surveillance trap at [get_area(snare_loc)].") diff --git a/code/game/gamemodes/miniantags/pulsedemon/cross_shock_component.dm b/code/game/gamemodes/miniantags/pulsedemon/cross_shock_component.dm index 1c15fdb1a82..cf7da6be862 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/cross_shock_component.dm +++ b/code/game/gamemodes/miniantags/pulsedemon/cross_shock_component.dm @@ -3,11 +3,16 @@ var/energy_cost var/delay_between_shocks var/requires_cable + COOLDOWN_DECLARE(last_shock) /datum/component/cross_shock/Initialize(_shock_damage, _energy_cost, _delay_between_shocks, _requires_cable = TRUE) if(ismovable(parent)) - RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_CROSSED_MOVABLE), PROC_REF(do_shock)) + var/static/list/crossed_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(do_shock), + ) + AddComponent(/datum/component/connect_loc_behalf, parent, crossed_connections) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_movable_moved)) if(ismob(parent)) RegisterSignal(parent, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_organ_removal)) else if(isarea(parent)) @@ -22,11 +27,18 @@ delay_between_shocks = _delay_between_shocks requires_cable = _requires_cable -/datum/component/cross_shock/proc/do_shock(datum/source, mob/living/thing_were_gonna_shock) - SIGNAL_HANDLER +/datum/component/cross_shock/proc/on_movable_moved(atom/source, old_location, direction, forced) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + if(isturf(source.loc)) + for(var/mob/living/mob in source.loc) + do_shock(src, mob) + +/datum/component/cross_shock/proc/do_shock(atom/source, atom/movable/to_shock) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED if(!COOLDOWN_FINISHED(src, last_shock)) return - if(!istype(thing_were_gonna_shock)) + var/mob/living/living_to_shock = to_shock + if(!istype(living_to_shock)) return if(isliving(parent)) var/mob/living/M = parent @@ -40,11 +52,11 @@ if(!our_cable || !our_cable.powernet || !our_cable.powernet.available_power) return var/area/to_deduct_from = get_area(our_cable) - thing_were_gonna_shock.electrocute_act(shock_damage, source) + living_to_shock.electrocute_act(shock_damage, source) to_deduct_from.powernet.use_active_power(energy_cost) playsound(get_turf(parent), 'sound/effects/eleczap.ogg', 30, TRUE) else - thing_were_gonna_shock.electrocute_act(shock_damage, source) + living_to_shock.electrocute_act(shock_damage, source) playsound(get_turf(parent), 'sound/effects/eleczap.ogg', 30, TRUE) COOLDOWN_START(src, last_shock, delay_between_shocks) diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm index c8ef3b159ad..babc34bc461 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm +++ b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm @@ -131,9 +131,13 @@ ADD_TRAIT(src, TRAIT_AI_UNTRACKABLE, PULSEDEMON_TRAIT) flags_2 |= RAD_NO_CONTAMINATE_2 - // don't step on me - RegisterSignal(src, COMSIG_CROSSED_MOVABLE, PROC_REF(try_cross_shock)) - RegisterSignal(src, COMSIG_MOVABLE_CROSSED, PROC_REF(try_cross_shock)) + // For when someone steps on us + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + // For when we move somewhere else + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_movable_moved)) // drop demon onto ground if its loc is a non-turf and gets deleted RegisterSignal(src, COMSIG_PARENT_PREQDELETED, PROC_REF(deleted_handler)) @@ -639,8 +643,18 @@ maxcharge = calc_maxcharge(length(hijacked_apcs)) + (maxcharge - calc_maxcharge(length(hijacked_apcs) - 1)) to_chat(src, "Hijacking complete! You now control [length(hijacked_apcs)] APCs.") -/mob/living/simple_animal/demon/pulse_demon/proc/try_cross_shock(src, atom/A) - SIGNAL_HANDLER +/mob/living/simple_animal/demon/pulse_demon/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + try_cross_shock(entered) + +/mob/living/simple_animal/demon/pulse_demon/proc/on_movable_moved(datum/source, old_location, direction, forced) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + if(is_under_tile()) + return + for(var/mob/living/mob in loc) + try_shock_mob(mob) + +/mob/living/simple_animal/demon/pulse_demon/proc/try_cross_shock(atom/movable/A) if(!isliving(A) || is_under_tile()) return var/mob/living/L = A @@ -761,7 +775,7 @@ /mob/living/simple_animal/demon/pulse_demon/ex_act() return -/mob/living/simple_animal/demon/pulse_demon/CanPass(atom/movable/mover, turf/target) +/mob/living/simple_animal/demon/pulse_demon/CanPass(atom/movable/mover, border_dir) . = ..() if(istype(mover, /obj/item/projectile/ion)) return FALSE diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index ab86bc89afb..d0bc0b0dca7 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -37,7 +37,7 @@ . = ..() . += "Click-drag someone to the table to place them on top of the table." -/obj/machinery/optable/CanPass(atom/movable/mover, turf/target) +/obj/machinery/optable/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSTABLE)) return TRUE if(isliving(mover)) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 24bd6f3a7c0..ca131022a99 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -40,6 +40,7 @@ var/detectTime = 0 var/area/station/ai_monitored/area_motion = null var/alarm_delay = 30 // Don't forget, there's another 3 seconds in queueAlarm() + var/datum/proximity_monitor/proximity_monitor /// If this camera doesnt add to camera chunks. Used by camera bugs. var/non_chunking_camera = FALSE @@ -64,6 +65,12 @@ /obj/machinery/camera/proc/set_area_motion(area/A) area_motion = A + create_prox_monitor() + +/obj/machinery/camera/proc/create_prox_monitor() + if(!proximity_monitor) + proximity_monitor = new(src, 1) + RegisterSignal(proximity_monitor, COMSIG_PARENT_QDELETING, PROC_REF(proximity_deleted)) /obj/machinery/camera/Moved(atom/OldLoc, Dir, Forced) . = ..() @@ -121,6 +128,10 @@ return ..() +/obj/machinery/camera/proc/proximity_deleted() + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + proximity_monitor = null + /obj/machinery/camera/proc/setViewRange(num = CAMERA_VIEW_DISTANCE) view_range = num GLOB.cameranet.updateVisibility(src, 0) diff --git a/code/game/machinery/camera/camera_presets.dm b/code/game/machinery/camera/camera_presets.dm index 9c7f0fc61bc..36a39afc6c2 100644 --- a/code/game/machinery/camera/camera_presets.dm +++ b/code/game/machinery/camera/camera_presets.dm @@ -40,7 +40,7 @@ /obj/machinery/camera/tracking_head/Initialize(mapload) . = ..() - AddComponent(/datum/component/proximity_monitor, _radius = 6) + proximity_monitor = new(src, 6) camera_overlay = new(get_turf(src)) switch(dir) if(NORTH) @@ -155,7 +155,7 @@ if(name == initial(name)) name = "motion-sensitive security camera" assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) - AddComponent(/datum/component/proximity_monitor, CAMERA_VIEW_DISTANCE) + proximity_monitor = new(src, CAMERA_VIEW_DISTANCE) setPowerUsage() // Add it to machines that process START_PROCESSING(SSmachines, src) diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 057eeaa7ce9..3fc127338fd 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -57,7 +57,7 @@ update_icon() return TRUE -/obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. +/obj/structure/barricade/CanPass(atom/movable/mover, border_dir)//So bullets will fly over and stuff. if(locate(/obj/structure/barricade) in get_turf(mover)) return TRUE else if(istype(mover) && mover.checkpass(PASSBARRICADE)) @@ -450,12 +450,15 @@ 0, 0, 0, 1 ) color = target_matrix + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) -/obj/structure/barricade/dropwall/firewall/Crossed(atom/movable/AM, oldloc) - . = ..() - if(!isprojectile(AM)) +/obj/structure/barricade/dropwall/firewall/proc/on_atom_entered(datum/source, atom/movable/entered) + if(!isprojectile(entered)) return - var/obj/item/projectile/P = AM + var/obj/item/projectile/P = entered P.immolate ++ /obj/item/grenade/turret @@ -496,7 +499,7 @@ . = ..() . += "It would need [(5 - foam_level)] more blobs of foam to fully block the airlock." -/obj/structure/barricade/foam/CanPass(atom/movable/mover, turf/target) +/obj/structure/barricade/foam/CanPass(atom/movable/mover, border_dir) return istype(mover, /obj/item/projectile/c_foam) // Only c_foam blobs hit the airlock underneat/pass through the foam. The rest is hitting the barricade /obj/structure/barricade/foam/welder_act(mob/user, obj/item/I) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 837ecea566f..2ec30a25766 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -90,6 +90,8 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) var/heat_resistance = 1500 /// Have we created sparks too recently? var/on_spark_cooldown = FALSE + /// Synced with icon state for checking on callbacks + var/airlock_state var/mutable_appearance/old_buttons_underlay var/mutable_appearance/old_lights_underlay @@ -392,6 +394,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) if(AIRLOCK_DENY, AIRLOCK_OPENING, AIRLOCK_CLOSING, AIRLOCK_EMAG) icon_state = "nonexistenticonstate" //MADNESS + airlock_state = state . = ..(UPDATE_ICON_STATE) // Sent after the icon_state is changed set_airlock_overlays(state) @@ -614,8 +617,11 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) if(stat == CONSCIOUS) update_icon(AIRLOCK_DENY) playsound(src, doorDeni, 50, FALSE, 3) - sleep(6) - update_icon(AIRLOCK_CLOSED) + addtimer(CALLBACK(src, PROC_REF(handle_deny_end)), 0.6 SECONDS) + +/obj/machinery/door/airlock/proc/handle_deny_end() + if(airlock_state == AIRLOCK_DENY) + update_icon(AIRLOCK_CLOSED) /obj/machinery/door/airlock/examine(mob/user) . = ..() @@ -760,7 +766,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) if(user) attack_ai(user) -/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target) +/obj/machinery/door/airlock/CanPass(atom/movable/mover, border_dir) if(istype(mover) && !locked) if(mover.checkpass(PASSDOOR)) return TRUE diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index b4551ee4071..686cf95dc02 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -704,7 +704,7 @@ qdel(src) /// Multi-tile airlocks (using a filler panel) have special handling for movables with PASS_FLAG_GLASS -/obj/airlock_filler_object/CanPass(atom/movable/mover, turf/target) +/obj/airlock_filler_object/CanPass(atom/movable/mover, border_dir) . = ..() if(.) return diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 2c1ce8187f7..6b90f4650b5 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -133,7 +133,7 @@ update_bounds() -/obj/machinery/door/CanPass(atom/movable/mover, turf/target) +/obj/machinery/door/CanPass(atom/movable/mover, border_dir) if(istype(mover)) if(mover.checkpass(PASSDOOR) && !locked) return TRUE diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index efc34160204..39ff3baf972 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -290,7 +290,7 @@ F.update_icon() qdel(src) -/obj/machinery/door/firedoor/CanPass(atom/movable/mover, turf/target) +/obj/machinery/door/firedoor/CanPass(atom/movable/mover, border_dir) if(..()) return TRUE if(isliving(mover) && !locked) @@ -303,26 +303,37 @@ flags = ON_BORDER can_crush = FALSE +/obj/machinery/door/firedoor/border_only/Initialize(mapload) + . = ..() + + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + + AddElement(/datum/element/connect_loc, loc_connections) + /obj/machinery/door/firedoor/border_only/closed icon_state = "door_closed" opacity = TRUE density = TRUE -/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target) +/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSGLASS)) return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + if(border_dir == dir) //Make sure looking at appropriate border return !density else return 1 -/obj/machinery/door/firedoor/border_only/CheckExit(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return TRUE +/obj/machinery/door/firedoor/border_only/proc/on_atom_exit(datum/source, atom/movable/leaving, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXIT + + if(istype(leaving) && leaving.checkpass(PASSGLASS)) + return + + if(direction == dir && density) + leaving.Bump(src) + return COMPONENT_ATOM_BLOCK_EXIT /obj/machinery/door/firedoor/border_only/CanAtmosPass(direction) if(direction == dir) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 8169b96246a..aa5685c04d2 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -32,6 +32,12 @@ if(req_access && length(req_access)) base_state = icon_state + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + + AddElement(/datum/element/connect_loc, loc_connections) + if(name != initial(name)) return var/new_name = get_area_name(src) @@ -140,14 +146,14 @@ return mob_dir & unres_sides -/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target) +/obj/machinery/door/window/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSGLASS)) return TRUE if(isliving(mover)) var/mob/living/living_mover = mover if(HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY) && IS_HORIZONTAL(living_mover)) return TRUE - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + if(border_dir == dir) //Make sure looking at appropriate border return !density if(istype(mover, /obj/structure/window)) var/obj/structure/window/W = mover @@ -172,17 +178,20 @@ /obj/machinery/door/window/CanPathfindPass(to_dir, datum/can_pass_info/pass_info) return !density || (dir != to_dir) || (check_access_list(pass_info.access) && hasPower()) -/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(isliving(mover)) - var/mob/living/living_mover = mover +/obj/machinery/door/window/proc/on_atom_exit(datum/source, atom/movable/leaving, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXIT + + if(istype(leaving) && leaving.checkpass(PASSGLASS)) + return + + if(isliving(leaving)) + var/mob/living/living_mover = leaving if(HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY) && IS_HORIZONTAL(living_mover)) - return TRUE - if(get_dir(loc, target) == dir) - return !density - else - return 1 + return + + if(direction == dir && density) + leaving.Bump(src) + return COMPONENT_ATOM_BLOCK_EXIT /obj/machinery/door/window/open(forced=0) if(operating) //doors can still open when emag-disabled @@ -199,16 +208,15 @@ set_opacity(FALSE) playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) icon_state ="[base_state]open" - sleep(10) + addtimer(CALLBACK(src, PROC_REF(finish_open)), 8) +/obj/machinery/door/window/proc/finish_open() density = FALSE -// sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn recalculate_atmos_connectivity() update_freelook_sight() if(operating) //emag again operating = NONE - return 1 /obj/machinery/door/window/close(forced=0) if(operating) diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 69f22c6d0f1..fbb7298600c 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -15,6 +15,7 @@ var/strength = 10 SECONDS //How weakened targets are when flashed. var/base_state = "mflash" anchored = TRUE + var/datum/proximity_monitor/proximity_monitor /obj/machinery/flasher/Initialize(mapload) . = ..() @@ -32,7 +33,7 @@ /obj/machinery/flasher/portable/Initialize(mapload) . = ..() - AddComponent(/datum/component/proximity_monitor) + proximity_monitor = new(src, range) /obj/machinery/flasher/power_change() if(!..()) diff --git a/code/game/machinery/machine_frame.dm b/code/game/machinery/machine_frame.dm index 766f3ec2aef..e73d480a7b1 100644 --- a/code/game/machinery/machine_frame.dm +++ b/code/game/machinery/machine_frame.dm @@ -251,9 +251,9 @@ qdel(O) new_machine.component_parts = list() for(var/obj/O in src) - O.forceMove(null) + O.moveToNullspace() new_machine.component_parts += O - circuit.forceMove(null) + circuit.moveToNullspace() new_machine.RefreshParts() qdel(src) diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 5f5723b4d2b..2a8a67c7788 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -542,7 +542,7 @@ return -/obj/machinery/shieldwall/CanPass(atom/movable/mover, turf/target) +/obj/machinery/shieldwall/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSGLASS)) return prob(20) else @@ -557,7 +557,7 @@ desc = "A strange energy shield." icon_state = "shield-red" -/obj/machinery/shieldwall/syndicate/CanPass(atom/movable/mover, turf/target) +/obj/machinery/shieldwall/syndicate/CanPass(atom/movable/mover, border_dir) if(isliving(mover)) var/mob/living/M = mover if("syndicate" in M.faction) diff --git a/code/game/machinery/tcomms/relay.dm b/code/game/machinery/tcomms/relay.dm index 7b98ef255c0..4a9aee89a9e 100644 --- a/code/game/machinery/tcomms/relay.dm +++ b/code/game/machinery/tcomms/relay.dm @@ -70,7 +70,7 @@ * * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels on the linked core */ -/obj/machinery/tcomms/relay/onTransitZ(old_z, new_z) +/obj/machinery/tcomms/relay/on_changed_z_level(turf/old_turf, turf/new_turf) . = ..() if(linked_core) linked_core.refresh_zlevels() diff --git a/code/game/machinery/tcomms/tcomms_base.dm b/code/game/machinery/tcomms/tcomms_base.dm index f89810dd4d8..6c7520a80b8 100644 --- a/code/game/machinery/tcomms/tcomms_base.dm +++ b/code/game/machinery/tcomms/tcomms_base.dm @@ -128,7 +128,7 @@ GLOBAL_LIST_EMPTY(tcomms_machines) * * Proc to make sure you cant have two of these active on a Z-level at once. It also makes sure to update the linkage */ -/obj/machinery/tcomms/onTransitZ(old_z, new_z) +/obj/machinery/tcomms/on_changed_z_level(turf/old_turf, turf/new_turf) . = ..() if(active) active = FALSE diff --git a/code/game/machinery/tcomms/tcomms_core.dm b/code/game/machinery/tcomms/tcomms_core.dm index 0d72b68d1d4..38b98fddda5 100644 --- a/code/game/machinery/tcomms/tcomms_core.dm +++ b/code/game/machinery/tcomms/tcomms_core.dm @@ -140,7 +140,7 @@ * * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels */ -/obj/machinery/tcomms/core/onTransitZ(old_z, new_z) +/obj/machinery/tcomms/core/on_changed_z_level(turf/old_turf, turf/new_turf) . = ..() refresh_zlevels() diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 57c8e61d66e..39449cab825 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -373,6 +373,12 @@ component_parts += new /obj/item/stock_parts/matter_bin(null) RefreshParts() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + + /obj/machinery/teleport/hub/upgraded/Initialize(mapload) . = ..() component_parts = list() @@ -408,13 +414,15 @@ break return power_station -/obj/machinery/teleport/hub/Crossed(atom/movable/AM, oldloc) +/obj/machinery/teleport/hub/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(!is_teleport_allowed(z) && !admin_usage) - if(ismob(AM)) - to_chat(AM, "You can't use this here.") + if(ismob(entered)) + to_chat(entered, "You can't use this here.") return - if(power_station && power_station.engaged && !panel_open && !blockAI(AM) && !iseffect(AM)) - if(!teleport(AM) && isliving(AM)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub + if(power_station && power_station.engaged && !panel_open && !blockAI(entered) && !iseffect(entered)) + if(!teleport(entered) && isliving(entered)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub visible_message("[src] emits a loud buzz, as its teleport portal flickers and fails!") playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE) power_station.toggle() // turn off the portal. @@ -491,6 +499,12 @@ . = ..() update_lighting() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + + /obj/machinery/teleport/perma/process() teleports_this_cycle = 0 @@ -503,15 +517,15 @@ tele_delay = max(A, 0) update_icon(UPDATE_ICON_STATE) -/obj/machinery/teleport/perma/Crossed(atom/movable/AM, oldloc) +/obj/machinery/teleport/perma/proc/on_atom_entered(datum/source, atom/movable/entered) if(stat & (BROKEN|NOPOWER)) return if(!is_teleport_allowed(z)) - to_chat(AM, "You can't use this here.") + to_chat(entered, "You can't use this here.") return - if(target && !recalibrating && !panel_open && !blockAI(AM) && (teleports_this_cycle <= MAX_ALLOWED_TELEPORTS_PER_PROCESS)) - do_teleport(AM, target) + if(target && !recalibrating && !panel_open && !blockAI(entered) && (teleports_this_cycle <= MAX_ALLOWED_TELEPORTS_PER_PROCESS)) + do_teleport(entered, target) use_power(5000) teleports_this_cycle++ if(tele_delay) @@ -520,6 +534,9 @@ update_lighting() addtimer(CALLBACK(src, PROC_REF(CrossedCallback)), tele_delay) +/obj/machinery/teleport/perma/Destroy() + . = ..() + /obj/machinery/teleport/perma/proc/CrossedCallback() recalibrating = FALSE update_icon(UPDATE_ICON_STATE | UPDATE_OVERLAYS) diff --git a/code/game/machinery/vendors/vending.dm b/code/game/machinery/vendors/vending.dm index f8f509441d1..457ad4aba68 100644 --- a/code/game/machinery/vendors/vending.dm +++ b/code/game/machinery/vendors/vending.dm @@ -142,6 +142,8 @@ /// How often will the vendor tip when you walk by it when aggressive is true? var/aggressive_tilt_chance = 25 + var/datum/proximity_monitor/proximity_monitor + /obj/machinery/economy/vending/Initialize(mapload) . = ..() var/build_inv = FALSE @@ -175,7 +177,7 @@ RegisterSignal(src, COMSIG_MOVABLE_UNTILTED, PROC_REF(on_untilt)) RegisterSignal(src, COMSIG_MOVABLE_TRY_UNTILT, PROC_REF(on_try_untilt)) if(aggressive) - AddComponent(/datum/component/proximity_monitor) + proximity_monitor = new(src, 1) /obj/machinery/economy/vending/Destroy() SStgui.close_uis(wires) @@ -943,8 +945,9 @@ throw_item.throw_at(target, 16, 3) visible_message("[src] launches [throw_item.name] at [target.name]!") -/obj/machinery/economy/vending/onTransitZ() - return +/obj/machinery/economy/vending/on_changed_z_level(turf/old_turf, turf/new_turf, notify_contents = FALSE) + // Don't bother notifying contents (for some reason (probably historical reasons (probably for no reason))) + return ..() /obj/machinery/economy/vending/proc/tilt(atom/victim, crit = FALSE, from_combat = FALSE, from_anywhere = FALSE) if(QDELETED(src) || !has_gravity(src) || !tiltable || tilted) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 7590801455a..285462a9433 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -404,7 +404,7 @@ if(. && stepsound) playsound(src, stepsound, 40, 1) -/obj/mecha/Bump(atom/obstacle, bump_allowed) +/obj/mecha/Bump(atom/obstacle) if(throwing) //high velocity mechas in your face! var/breakthrough = FALSE if(istype(obstacle, /obj/structure/window)) @@ -459,15 +459,14 @@ throw_at(crashing, 50, throw_speed) else - if(bump_allowed) - if(..()) - return - if(isobj(obstacle)) - var/obj/O = obstacle - if(!O.anchored) - step(obstacle, dir) - else if(ismob(obstacle)) + if(..()) + return + if(isobj(obstacle)) + var/obj/O = obstacle + if(!O.anchored) step(obstacle, dir) + else if(ismob(obstacle)) + step(obstacle, dir) /////////////////////////////////// @@ -1271,10 +1270,11 @@ /obj/mecha/proc/pilot_mmi_hud(mob/living/brain/pilot) return -/obj/mecha/Exited(atom/movable/M, atom/newloc) +/obj/mecha/Exited(atom/movable/M, direction) + var/new_loc = get_step(M, direction) if(occupant && occupant == M) // The occupant exited the mech without calling go_out() if(!isAI(occupant)) //This causes carded AIS to gib, so we do not want this to be called during carding. - go_out(1, newloc) + go_out(1, new_loc) /obj/mecha/proc/go_out(forced, atom/newloc = loc) if(!occupant) diff --git a/code/game/objects/effects/alien_acid.dm b/code/game/objects/effects/alien_acid.dm index afc114502d5..a00b1c65ce0 100644 --- a/code/game/objects/effects/alien_acid.dm +++ b/code/game/objects/effects/alien_acid.dm @@ -22,6 +22,11 @@ pixel_x = target.pixel_x + rand(-4,4) pixel_y = target.pixel_y + rand(-4,4) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + START_PROCESSING(SSobj, src) /obj/effect/acid/Destroy() @@ -50,9 +55,12 @@ qdel(src) return 0 -/obj/effect/acid/Crossed(AM as mob|obj) - if(isliving(AM)) - var/mob/living/L = AM +/obj/effect/acid/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(!isliving(entered) && !isobj(entered)) + return + if(isliving(entered)) + var/mob/living/L = entered if(HAS_TRAIT(L, TRAIT_FLYING)) return if(L.m_intent != MOVE_INTENT_WALK && prob(40)) diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index fad65dc1bb6..89aa7131314 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -121,6 +121,11 @@ if(prob(75)) new /obj/item/shard(loc) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/anomaly/grav/Destroy() vis_contents -= warp QDEL_NULL(warp) // don't want to leave it hanging @@ -147,9 +152,8 @@ animate(warp, time = 6, transform = matrix().Scale(0.5,0.5)) animate(time = 14, transform = matrix()) -/obj/effect/anomaly/grav/Crossed(atom/movable/AM) - . = ..() - gravShock(AM) +/obj/effect/anomaly/grav/proc/on_atom_entered(datum/source, atom/movable/entered) + gravShock(entered) /obj/effect/anomaly/grav/Bump(atom/A) gravShock(A) @@ -195,6 +199,10 @@ if(explosive) zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN power = 15000 + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/effect/anomaly/flux/anomalyEffect() ..() @@ -204,10 +212,8 @@ if(explosive) //Let us not fuck up the sm that much tesla_zap(src, zap_range, power, zap_flags) - -/obj/effect/anomaly/flux/Crossed(atom/movable/AM) - . = ..() - mobShock(AM) +/obj/effect/anomaly/flux/proc/on_atom_entered(datum/source, atom/movable/entered) + mobShock(entered) /obj/effect/anomaly/flux/Bump(atom/A) mobShock(A) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 38a79e45b2f..f9d8e1f4acb 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -45,6 +45,11 @@ if(!. && !QDELETED(src)) dry_timer = addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1), TIMER_STOPPABLE) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/decal/cleanable/blood/Destroy() if(dry_timer) deltimer(dry_timer) @@ -138,12 +143,7 @@ return FALSE -/obj/effect/decal/cleanable/blood/Bump(atom/A, yes) - // this is to prevent double or triple bumps from calling splat after src is qdel'd. - // only god knows why this fixes the issue - if(yes) - return - +/obj/effect/decal/cleanable/blood/Bump(atom/A) if(gravity_check) return ..() diff --git a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm index 7ff65230575..8048ab7e23d 100644 --- a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm +++ b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm @@ -171,15 +171,19 @@ animate_levitate(src, -1, rand(30, 120)) icon = 'icons/effects/blood_weightless.dmi' -/obj/effect/decal/cleanable/vomit/Bump(atom/A, yes) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/decal/cleanable/vomit/Bump(atom/A) . = ..() if(A.density) splat(A) -/obj/effect/decal/cleanable/vomit/Crossed(atom/movable/AM, oldloc) +/obj/effect/decal/cleanable/vomit/proc/on_atom_entered(datum/source, atom/movable/entered) if(!gravity_check) - splat(AM) - ..() + splat(entered) /obj/effect/decal/cleanable/vomit/proc/splat(atom/A) if(gravity_check) diff --git a/code/game/objects/effects/decals/Cleanable/tar.dm b/code/game/objects/effects/decals/Cleanable/tar.dm index 5cc09a4619c..0b76030ca1d 100644 --- a/code/game/objects/effects/decals/Cleanable/tar.dm +++ b/code/game/objects/effects/decals/Cleanable/tar.dm @@ -14,6 +14,11 @@ if(prob(50)) icon_state = "tar3" + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/decal/cleanable/tar/Destroy() if(target) target.slowdown -= 10 @@ -27,9 +32,9 @@ if(!issimulatedturf(target)) // We remove slowdown in Destroy(), so we run this check after adding the slowdown. qdel(src) -/obj/effect/decal/cleanable/tar/Crossed(atom/movable/movable_atom) - if(isliving(movable_atom)) - var/mob/living/L = movable_atom +/obj/effect/decal/cleanable/tar/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isliving(entered)) + var/mob/living/L = entered playsound(L, 'sound/effects/attackblob.ogg', 50, TRUE) to_chat(L, "[src] sticks to you!") diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index d7417d8452a..431c97c6d7e 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -39,61 +39,70 @@ GLOBAL_LIST_EMPTY(fluidtrack_cache) blood_state = BLOOD_STATE_HUMAN //the icon state to load images from gravity_check = ALWAYS_IN_GRAVITY -/obj/effect/decal/cleanable/blood/footprints/Crossed(atom/movable/O, oldloc) - ..() - if(ishuman(O)) - var/mob/living/carbon/human/H = O - var/obj/item/clothing/shoes/S = H.shoes - var/obj/item/organ/external/l_foot = H.get_organ("l_foot") - var/obj/item/organ/external/r_foot = H.get_organ("r_foot") - var/hasfeet = TRUE - if(!l_foot && !r_foot) - hasfeet = FALSE - if(S && S.bloody_shoes[blood_state] && S.blood_color == basecolor) - S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0) - S.bloody_shoes[BLOOD_BASE_ALPHA] = base_alpha - if(!S.blood_DNA) - S.blood_DNA = list() - S.blood_DNA |= blood_DNA.Copy() - if(!(entered_dirs & H.dir)) - entered_dirs |= H.dir - update_icon() - else if(hasfeet && H.bloody_feet[blood_state] && H.feet_blood_color == basecolor)//Or feet //This will need to be changed. - H.bloody_feet[blood_state] = max(H.bloody_feet[blood_state] - BLOOD_LOSS_PER_STEP, 0) - H.bloody_feet[BLOOD_BASE_ALPHA] = base_alpha - if(!H.feet_blood_DNA) - H.feet_blood_DNA = list() - H.feet_blood_DNA |= blood_DNA.Copy() - if(!(entered_dirs & H.dir)) - entered_dirs |= H.dir - update_icon() +/obj/effect/decal/cleanable/blood/footprints/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + COMSIG_ATOM_EXITED = PROC_REF(on_atom_exited), + ) + AddElement(/datum/element/connect_loc, loc_connections) -/obj/effect/decal/cleanable/blood/footprints/Uncrossed(atom/movable/O) - ..() - if(ishuman(O)) - var/mob/living/carbon/human/H = O - var/obj/item/clothing/shoes/S = H.shoes - var/obj/item/organ/external/l_foot = H.get_organ("l_foot") - var/obj/item/organ/external/r_foot = H.get_organ("r_foot") - var/hasfeet = TRUE - if(!l_foot && !r_foot) - hasfeet = FALSE - if(S && S.bloody_shoes[blood_state] && S.blood_color == basecolor) - S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0) - if(!S.blood_DNA) - S.blood_DNA = list() - S.blood_DNA |= blood_DNA.Copy() - if(!(exited_dirs & H.dir)) - exited_dirs |= H.dir - update_icon() - else if(hasfeet && H.bloody_feet[blood_state] && H.feet_blood_color == basecolor)//Or feet - H.bloody_feet[blood_state] = max(H.bloody_feet[blood_state] - BLOOD_LOSS_PER_STEP, 0) - if(!H.feet_blood_DNA) - H.feet_blood_DNA = list() - H.feet_blood_DNA |= blood_DNA.Copy() - if(!(exited_dirs & H.dir)) - exited_dirs |= H.dir - update_icon() +/obj/effect/decal/cleanable/blood/footprints/on_atom_entered(datum/source, mob/living/carbon/human/H, ...) + if(!istype(H)) + return + + var/obj/item/clothing/shoes/S = H.shoes + var/obj/item/organ/external/l_foot = H.get_organ("l_foot") + var/obj/item/organ/external/r_foot = H.get_organ("r_foot") + var/hasfeet = TRUE + if(!l_foot && !r_foot) + hasfeet = FALSE + if(S && S.bloody_shoes[blood_state] && S.blood_color == basecolor) + S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0) + S.bloody_shoes[BLOOD_BASE_ALPHA] = base_alpha + if(!S.blood_DNA) + S.blood_DNA = list() + S.blood_DNA |= blood_DNA.Copy() + if(!(entered_dirs & H.dir)) + entered_dirs |= H.dir + update_icon() + else if(hasfeet && H.bloody_feet[blood_state] && H.feet_blood_color == basecolor)//Or feet //This will need to be changed. + H.bloody_feet[blood_state] = max(H.bloody_feet[blood_state] - BLOOD_LOSS_PER_STEP, 0) + H.bloody_feet[BLOOD_BASE_ALPHA] = base_alpha + if(!H.feet_blood_DNA) + H.feet_blood_DNA = list() + H.feet_blood_DNA |= blood_DNA.Copy() + if(!(entered_dirs & H.dir)) + entered_dirs |= H.dir + update_icon() + +// TODO: I think this is a 1:1 copy-paste of on_atom_entered above +/obj/effect/decal/cleanable/blood/footprints/proc/on_atom_exited(datum/source, mob/living/carbon/human/H, ...) + if(!istype(H)) + return + + var/obj/item/clothing/shoes/S = H.shoes + var/obj/item/organ/external/l_foot = H.get_organ("l_foot") + var/obj/item/organ/external/r_foot = H.get_organ("r_foot") + var/hasfeet = TRUE + if(!l_foot && !r_foot) + hasfeet = FALSE + if(S && S.bloody_shoes[blood_state] && S.blood_color == basecolor) + S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0) + if(!S.blood_DNA) + S.blood_DNA = list() + S.blood_DNA |= blood_DNA.Copy() + if(!(exited_dirs & H.dir)) + exited_dirs |= H.dir + update_icon() + else if(hasfeet && H.bloody_feet[blood_state] && H.feet_blood_color == basecolor)//Or feet + H.bloody_feet[blood_state] = max(H.bloody_feet[blood_state] - BLOOD_LOSS_PER_STEP, 0) + if(!H.feet_blood_DNA) + H.feet_blood_DNA = list() + H.feet_blood_DNA |= blood_DNA.Copy() + if(!(exited_dirs & H.dir)) + exited_dirs |= H.dir + update_icon() /obj/effect/decal/cleanable/blood/footprints/update_overlays() diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index cf914e6fb03..6ad91e08db5 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -27,17 +27,15 @@ //Add "bloodiness" of this blood's type, to the human's shoes //This is on /cleanable because fuck this ancient mess -/obj/effect/decal/cleanable/blood/Crossed(atom/movable/O) - ..() - - if(!ishuman(O)) +/obj/effect/decal/cleanable/blood/proc/on_atom_entered(datum/source, atom/movable/entered) + if(!ishuman(entered)) return - if(!gravity_check && ishuman(O)) - bloodyify_human(O) + if(!gravity_check && ishuman(entered)) + bloodyify_human(entered) if(!off_floor) - var/mob/living/carbon/human/H = O + var/mob/living/carbon/human/H = entered var/obj/item/organ/external/l_foot = H.get_organ("l_foot") var/obj/item/organ/external/r_foot = H.get_organ("r_foot") var/hasfeet = TRUE diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm index 0c5f9c2e8b2..cbe122c4d74 100644 --- a/code/game/objects/effects/effect_system/effects_foam.dm +++ b/code/game/objects/effects/effect_system/effects_foam.dm @@ -30,6 +30,10 @@ create_reagents(25) playsound(src, 'sound/effects/bubbles2.ogg', 80, TRUE, -3) addtimer(CALLBACK(src, PROC_REF(initial_process)), spread_time) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/effect/particle_effect/foam/proc/disperse_reagents() if(!reagents) @@ -122,10 +126,10 @@ flick("[icon_state]-disolve", src) QDEL_IN(src, 0.5 SECONDS) -/obj/effect/particle_effect/foam/Crossed(atom/movable/AM, oldloc) - if(!iscarbon(AM)) +/obj/effect/particle_effect/foam/proc/on_atom_entered(datum/source, atom/movable/entered) + if(!iscarbon(entered)) return - var/mob/living/carbon/M = AM + var/mob/living/carbon/M = entered if((M.slip("foam", 10 SECONDS) || IS_HORIZONTAL(M)) && reagents) fill_with_reagents(M) @@ -154,7 +158,7 @@ /obj/effect/particle_effect/foam/metal/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) return -/obj/effect/particle_effect/foam/metal/Crossed(atom/movable/AM, oldloc) +/obj/effect/particle_effect/foam/metal/on_atom_entered(datum/source, atom/movable/entered) return /datum/effect_system/foam_spread @@ -294,7 +298,7 @@ to_chat(user, "You hit the metal foam but bounce off it.") playsound(loc, 'sound/weapons/tap.ogg', 100, 1) -/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target) +/obj/structure/foamedmetal/CanPass(atom/movable/mover, border_dir) return !density /obj/structure/foamedmetal/CanAtmosPass(direction) diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm index 55aee922c09..15cfd65b543 100644 --- a/code/game/objects/effects/effect_system/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_smoke.dm @@ -23,7 +23,11 @@ /obj/effect/particle_effect/smoke/Initialize(mapload) . = ..() START_PROCESSING(SSobj, src) - RegisterSignal(src, list(COMSIG_MOVABLE_CROSSED, COMSIG_CROSSED_MOVABLE), PROC_REF(smoke_mob)) //If someone crosses the smoke or the smoke crosses someone + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(smoke_mob) + ) + AddElement(/datum/element/connect_loc, loc_connections) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(smoke_mob)) GLOB.smokes_active++ lifetime += rand(-1, 1) create_reagents(10) @@ -31,7 +35,7 @@ /obj/effect/particle_effect/smoke/Destroy() animate(src, 2 SECONDS, alpha = 0, easing = EASE_IN | CIRCULAR_EASING) STOP_PROCESSING(SSobj, src) - UnregisterSignal(src, list(COMSIG_MOVABLE_CROSSED, COMSIG_CROSSED_MOVABLE)) + UnregisterSignal(src, COMSIG_MOVABLE_MOVED) GLOB.smokes_active-- return ..() @@ -56,7 +60,7 @@ return TRUE /obj/effect/particle_effect/smoke/proc/smoke_mob(mob/living/carbon/breather) - SIGNAL_HANDLER //COMSIG_MOVABLE_CROSSED and COMSIG_CROSSED_MOVABLE + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + COMSIG_MOVABLE_MOVED if(!istype(breather)) return FALSE if(lifetime < 1) @@ -122,10 +126,12 @@ ///////////////////////////////////////////// /obj/effect/particle_effect/smoke/bad - lifetime = 16 SECONDS_TO_LIFE_CYCLES + lifetime = 60 SECONDS_TO_LIFE_CYCLES causes_coughing = TRUE + direction = SOUTH + steps = 10 -/obj/effect/particle_effect/smoke/bad/CanPass(atom/movable/mover, turf/target) +/obj/effect/particle_effect/smoke/bad/CanPass(atom/movable/mover, border_dir) if(istype(mover, /obj/item/projectile/beam)) var/obj/item/projectile/beam/B = mover B.damage = (B.damage / 2) @@ -143,25 +149,23 @@ lifetime = 10 SECONDS_TO_LIFE_CYCLES causes_coughing = TRUE -/obj/effect/particle_effect/smoke/steam/Crossed(atom/movable/AM, oldloc) - . = ..() - if(!isliving(AM)) +/obj/effect/particle_effect/smoke/steam/smoke_mob(mob/living/breather) + if(!istype(breather)) return - var/mob/living/crosser = AM - if(IS_MINDFLAYER(crosser)) + if(IS_MINDFLAYER(breather)) return // Mindflayers are fully immune to steam - if(!ishuman(crosser)) - crosser.adjustFireLoss(8) + if(!ishuman(breather)) + breather.adjustFireLoss(8) return - var/mob/living/carbon/human/human_crosser = AM + var/mob/living/carbon/human/human_crosser = breather var/fire_armour = human_crosser.get_thermal_protection() if(fire_armour >= FIRE_SUIT_MAX_TEMP_PROTECT || HAS_TRAIT(human_crosser, TRAIT_RESISTHEAT)) return - crosser.adjustFireLoss(5) + breather.adjustFireLoss(5) if(prob(20)) - to_chat(crosser, "You are being scalded by the hot steam!") + to_chat(breather, "You are being scalded by the hot steam!") ///////////////////////////////////////////// // Nanofrost smoke diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm index 14545e54e28..7c59c10efe1 100644 --- a/code/game/objects/effects/forcefields.dm +++ b/code/game/objects/effects/forcefields.dm @@ -22,7 +22,7 @@ . = ..() wizard = summoner -/obj/effect/forcefield/wizard/CanPass(atom/movable/mover, turf/target) +/obj/effect/forcefield/wizard/CanPass(atom/movable/mover, border_dir) if(mover == wizard) return TRUE return FALSE diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 573fe8030f1..a97ced7b06a 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -7,13 +7,22 @@ var/triggered = FALSE var/faction = "syndicate" +/obj/effect/mine/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/mine/proc/mineEffect(mob/living/victim) to_chat(victim, "*click*") -/obj/effect/mine/Crossed(AM as mob|obj, oldloc) - if(!isliving(AM)) +/obj/effect/mine/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + + if(!isliving(entered)) return - var/mob/living/M = AM + var/mob/living/M = entered if(faction && (faction in M.faction)) return if(HAS_TRAIT(M, TRAIT_FLYING)) diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index ba6f449d1f0..4a61e40a3fd 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -39,6 +39,11 @@ creation_mob_ckey = creation_mob?.ckey START_PROCESSING(SSobj, src) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + if(lifespan > 0) QDEL_IN(src, lifespan) @@ -62,15 +67,15 @@ /obj/effect/portal/singularity_act() return -/obj/effect/portal/Crossed(atom/movable/AM, oldloc) - if(isobserver(AM)) - return ..() +/obj/effect/portal/proc/on_atom_entered(datum/source, atom/movable/entered, old_loc) + if(isobserver(entered)) + return - if(target && (get_turf(oldloc) == get_turf(target))) - return ..() + if(target && (get_turf(old_loc) == get_turf(target))) + return - if(!teleport(AM)) - return ..() + if(teleport(entered)) + return TRUE /obj/effect/portal/attack_tk(mob/user) return diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index dbba0c2e051..c169729b8e5 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -39,16 +39,19 @@ if(prob(50)) icon_state = "stickyweb2" -/obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider) || isterrorspider(mover)) - return TRUE - else if(isliving(mover)) - if(prob(50)) - to_chat(mover, "You get stuck in [src] for a moment.") - return FALSE - else if(isprojectile(mover)) - return prob(30) - return TRUE + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/structure/spider/stickyweb/proc/on_atom_exit(datum/source, atom/exiter) + if(istype(exiter, /mob/living/simple_animal/hostile/poison/giant_spider) || isterrorspider(exiter)) + return + if(isliving(exiter) && prob(50)) + to_chat(exiter, "You get stuck in [src] for a moment.") + return COMPONENT_ATOM_BLOCK_EXIT + if(isprojectile(exiter) && prob(30)) + return COMPONENT_ATOM_BLOCK_EXIT /obj/structure/spider/eggcluster name = "egg cluster" diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm index e7acf45614a..63ab8096230 100644 --- a/code/game/objects/effects/step_triggers.dm +++ b/code/game/objects/effects/step_triggers.dm @@ -6,18 +6,27 @@ var/mobs_only = FALSE invisibility = INVISIBILITY_ABSTRACT // nope cant see this shit +/obj/effect/step_trigger/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/step_trigger/proc/Trigger(atom/movable/A) return FALSE -/obj/effect/step_trigger/Crossed(H, oldloc) - . = ..() - if(!H) +/obj/effect/step_trigger/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER + if(!entered || entered == src) return - if(isobserver(H) && !affect_ghosts) + if(!ismob(entered) && !isobj(entered)) return - if(!ismob(H) && mobs_only) + if(isobserver(entered) && !affect_ghosts) return - Trigger(H) + if(!ismob(entered) && mobs_only) + return + INVOKE_ASYNC(src, PROC_REF(Trigger), entered) /obj/effect/step_trigger/singularity_act() return @@ -39,7 +48,6 @@ qdel(src) /* Tosses things in a certain direction */ - /obj/effect/step_trigger/thrower var/direction = SOUTH // the direction of throw var/tiles = 3 // if 0: forever until atom hits a stopper diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 4066e3702c7..d7d151a4dd0 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -61,6 +61,10 @@ if(is_zero_amount(FALSE)) return INITIALIZE_HINT_QDEL + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) update_icon(UPDATE_ICON_STATE) /obj/item/stack/update_icon_state() @@ -75,17 +79,18 @@ icon_state = "[initial(icon_state)]_[state]" -/obj/item/stack/Crossed(obj/O, oldloc) - if(O == src) +/obj/item/stack/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + + // Edge case. This signal will also be sent when src has entered the turf. Don't want to merge with ourselves. + if(entered == src) return if(amount >= max_amount || ismob(loc)) // Prevents unnecessary call. Also prevents merging stack automatically in a mob's inventory return - if(!O.throwing && can_merge(O)) - INVOKE_ASYNC(src, PROC_REF(merge), O) - - ..() + if(!entered.throwing && can_merge(entered)) + INVOKE_ASYNC(src, PROC_REF(merge), entered) /obj/item/stack/hitby(atom/movable/hitting, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) if(can_merge(hitting, inhand = TRUE)) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 3bc720ac53c..46a634c7f84 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -375,6 +375,13 @@ w_class = WEIGHT_CLASS_TINY var/ash_type = /obj/effect/decal/cleanable/ash +/obj/item/toy/snappop/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/item/toy/snappop/proc/pop_burst(n=3, c=1) do_sparks(n, c, src) new ash_type(loc) @@ -391,10 +398,10 @@ ..() pop_burst() -/obj/item/toy/snappop/Crossed(H as mob|obj, oldloc) - if(ishuman(H) || issilicon(H)) //i guess carp and shit shouldn't set them off - var/mob/living/carbon/M = H - if(issilicon(H) || M.m_intent == MOVE_INTENT_RUN) +/obj/item/toy/snappop/proc/on_atom_entered(datum/source, atom/movable/entered) + if(ishuman(entered) || issilicon(entered)) //i guess carp and shit shouldn't set them off + var/mob/living/carbon/M = entered + if(issilicon(entered) || M.m_intent == MOVE_INTENT_RUN) to_chat(M, "You step on the snap pop!") pop_burst(2, 0) diff --git a/code/game/objects/items/weapons/caution.dm b/code/game/objects/items/weapons/caution.dm index 4ff358a8827..46e3b9da43c 100644 --- a/code/game/objects/items/weapons/caution.dm +++ b/code/game/objects/items/weapons/caution.dm @@ -16,10 +16,11 @@ var/timing = FALSE var/armed = FALSE var/timepassed = 0 + var/datum/proximity_monitor/proximity_monitor /obj/item/caution/proximity_sign/Initialize(mapload) . = ..() - AddComponent(/datum/component/proximity_monitor) + proximity_monitor = new(src, 1) /obj/item/caution/proximity_sign/attack_self__legacy__attackchain(mob/user as mob) if(ishuman(user)) diff --git a/code/game/objects/items/weapons/chemical_flamethrower/fire_effect.dm b/code/game/objects/items/weapons/chemical_flamethrower/fire_effect.dm index e98f1303f42..1cdf4e1630b 100644 --- a/code/game/objects/items/weapons/chemical_flamethrower/fire_effect.dm +++ b/code/game/objects/items/weapons/chemical_flamethrower/fire_effect.dm @@ -33,6 +33,11 @@ GLOBAL_LIST_EMPTY(flame_effects) GLOB.flame_effects += src START_PROCESSING(SSprocessing, src) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/effect/fire/Destroy() . = ..() GLOB.flame_effects -= src @@ -75,18 +80,17 @@ GLOBAL_LIST_EMPTY(flame_effects) if(duration <= 0) fizzle() -/obj/effect/fire/Crossed(atom/movable/AM, oldloc) - . = ..() - if(isliving(AM)) - if(!damage_mob(AM)) +/obj/effect/fire/proc/on_atom_entered(datum/source, atom/movable/entered, old_loc) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(isliving(entered)) + if(!damage_mob(entered)) return - to_chat(AM, "[src] burns you!") + to_chat(entered, "[src] burns you!") return - if(isitem(AM)) - var/obj/item/item_to_burn = AM + if(isitem(entered)) + var/obj/item/item_to_burn = entered item_to_burn.fire_act(null, temperature) - return /obj/effect/fire/proc/fizzle() playsound(src, 'sound/effects/fire_sizzle.ogg', 50, TRUE) diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index be4d87fd36e..e2539d68fd9 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -21,6 +21,10 @@ /obj/item/grenade/plastic/Initialize(mapload) . = ..() plastic_overlay = mutable_appearance(icon, "[item_state]2", HIGH_OBJ_LAYER) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/item/grenade/plastic/Destroy() QDEL_NULL(nadeassembly) @@ -50,9 +54,9 @@ return ..() -/obj/item/grenade/plastic/Crossed(atom/movable/AM, oldloc) +/obj/item/grenade/plastic/proc/on_atom_entered(datum/source, atom/movable/entered) if(nadeassembly) - nadeassembly.Crossed(AM, oldloc) + nadeassembly.on_atom_entered(source, entered) /obj/item/grenade/plastic/on_found(mob/finder) if(nadeassembly) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 82acfec3723..996b7de9c57 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -32,6 +32,11 @@ payload_name += " " // formatting, ignore me update_icon() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/item/grenade/chem_grenade/Destroy() QDEL_NULL(nadeassembly) QDEL_LIST_CONTENTS(beakers) @@ -246,9 +251,9 @@ if(nadeassembly) nadeassembly.process_movement() -/obj/item/grenade/chem_grenade/Crossed(atom/movable/AM, oldloc) +/obj/item/grenade/chem_grenade/proc/on_atom_entered(datum/source, atom/movable/entered) if(nadeassembly) - nadeassembly.Crossed(AM, oldloc) + nadeassembly.on_atom_entered(source, entered) /obj/item/grenade/chem_grenade/on_found(mob/finder) if(nadeassembly) diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm index 146695a515a..2269e2afece 100644 --- a/code/game/objects/items/weapons/legcuffs.dm +++ b/code/game/objects/items/weapons/legcuffs.dm @@ -26,6 +26,13 @@ var/obj/item/grenade/iedcasing/IED = null var/obj/item/assembly/signaler/sig = null +/obj/item/restraints/legcuffs/beartrap/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/item/restraints/legcuffs/beartrap/update_icon_state() icon_state = "beartrap[armed]" @@ -97,16 +104,15 @@ to_chat(user, "You remove the signaler from [src].") return TRUE -/obj/item/restraints/legcuffs/beartrap/Crossed(AM as mob|obj, oldloc) - if(!armed || !isturf(loc)) - return ..() +/obj/item/restraints/legcuffs/beartrap/proc/on_atom_entered(datum/source, mob/living/entered) + if(!armed || !isturf(loc) || !istype(entered)) + return - var/mob/living/L = AM - if((iscarbon(AM) || isanimal(AM)) && !HAS_TRAIT(L, TRAIT_FLYING)) - spring_trap(AM) + if((iscarbon(entered) || isanimal(entered)) && !HAS_TRAIT(entered, TRAIT_FLYING)) + spring_trap(entered) - if(ishuman(AM)) - var/mob/living/carbon/H = AM + if(ishuman(entered)) + var/mob/living/carbon/H = entered if(IS_HORIZONTAL(H)) H.apply_damage(trap_damage, BRUTE, "chest") else @@ -117,11 +123,10 @@ H.update_inv_legcuffed() SSblackbox.record_feedback("tally", "handcuffs", 1, type) else - if(istype(L, /mob/living/simple_animal/hostile/bear)) - L.apply_damage(trap_damage * 2.5, BRUTE) + if(istype(entered, /mob/living/simple_animal/hostile/bear)) + entered.apply_damage(trap_damage * 2.5, BRUTE) else - L.apply_damage(trap_damage * 1.75, BRUTE) - ..() + entered.apply_damage(trap_damage * 1.75, BRUTE) /obj/item/restraints/legcuffs/beartrap/on_found(mob/finder) if(!armed) diff --git a/code/game/objects/items/weapons/shards.dm b/code/game/objects/items/weapons/shards.dm index fa2ec1c9fde..44a563a2cb7 100644 --- a/code/game/objects/items/weapons/shards.dm +++ b/code/game/objects/items/weapons/shards.dm @@ -44,6 +44,10 @@ . = ..() AddComponent(/datum/component/caltrop, force) set_initial_icon_state() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/item/shard/afterattack__legacy__attackchain(atom/movable/AM, mob/user, proximity) if(!proximity || !(src in user)) @@ -70,12 +74,12 @@ to_chat(user, "You add the newly-formed glass to the stack.") qdel(src) -/obj/item/shard/Crossed(mob/living/L, oldloc) - if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || HAS_TRAIT(L, TRAIT_FLYING) || L.floating) +/obj/item/shard/proc/on_atom_entered(datum/source, atom/movable/entered) + var/mob/living/living_entered = entered + if(istype(living_entered) && has_gravity(loc)) + if(living_entered.incorporeal_move || HAS_TRAIT(living_entered, TRAIT_FLYING) || living_entered.floating) return playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) - return ..() /obj/item/shard/decompile_act(obj/item/matter_decompiler/C, mob/user) C.stored_comms["glass"] += 3 diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index a9fc87d9fdd..d732db1d9fc 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -167,6 +167,41 @@ max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * ore.w_class max_w_class = WEIGHT_CLASS_NORMAL can_hold = list(/obj/item/stack/ore) + /// The mob currently holding the ore bag, to track moving over ore to auto-pickup. + var/mob/listening_to + +/obj/item/storage/bag/ore/Destroy() + . = ..() + listening_to = null + +/obj/item/storage/bag/ore/equipped(mob/user, slot, initial) + . = ..() + if(listening_to == user) + return + if(listening_to) + UnregisterSignal(listening_to, COMSIG_MOVABLE_MOVED) + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(pickup_ores)) + listening_to = user + +/obj/item/storage/bag/ore/dropped() + . = ..() + if(listening_to) + UnregisterSignal(listening_to, COMSIG_MOVABLE_MOVED) + listening_to = null + +/obj/item/storage/bag/ore/proc/pickup_ores(mob/living/user) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + var/turf/simulated/floor/plating/asteroid/tile = get_turf(user) + + if(!istype(tile)) + return + + tile.attempt_ore_pickup(src, user) + // Then, if the user is dragging an ore box, empty the satchel + // into the box. + if(istype(user.pulling, /obj/structure/ore_box)) + var/obj/structure/ore_box/box = user.pulling + box.attackby__legacy__attackchain(src, user) /obj/item/storage/bag/ore/cyborg name = "cyborg mining satchel" diff --git a/code/game/objects/items/weapons/storage/briefcase.dm b/code/game/objects/items/weapons/storage/briefcase.dm index ab0409a7152..d29ba66b345 100644 --- a/code/game/objects/items/weapons/storage/briefcase.dm +++ b/code/game/objects/items/weapons/storage/briefcase.dm @@ -61,7 +61,7 @@ stored_item = I max_w_class = WEIGHT_CLASS_NORMAL - stored_item.w_class - I.forceMove(null) //null space here we go - to stop it showing up in the briefcase + I.moveToNullspace() // to stop it showing up in the briefcase to_chat(user, "You place [I] into the false bottom of the briefcase.") else return ..() diff --git a/code/game/objects/items/weapons/storage/storage_base.dm b/code/game/objects/items/weapons/storage/storage_base.dm index 6581136461d..0b5236d0e31 100644 --- a/code/game/objects/items/weapons/storage/storage_base.dm +++ b/code/game/objects/items/weapons/storage/storage_base.dm @@ -74,6 +74,7 @@ orient2hud() ADD_TRAIT(src, TRAIT_ADJACENCY_TRANSPARENT, ROUNDSTART_TRAIT) + RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_atom_exited)) /obj/item/storage/Destroy() for(var/obj/O in contents) @@ -490,6 +491,9 @@ update_icon() return TRUE +/obj/item/storage/proc/on_atom_exited(datum/source, atom/exited, direction) + return remove_from_storage(exited, exited.loc) + /** * Handles the removal of an item from a storage container. * @@ -530,10 +534,6 @@ update_icon() return TRUE -/obj/item/storage/Exited(atom/A, loc) - remove_from_storage(A, loc) //worry not, comrade; this only gets called once - ..() - /obj/item/storage/deconstruct(disassembled = TRUE) var/drop_loc = loc if(ismob(loc)) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index e4c2b29b69e..1a80c3bac7c 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -468,6 +468,7 @@ *In the BURST/BURSTING state, the alien egg can be removed by being attacked by a alien or any other weapon **/ var/status = GROWING + var/datum/proximity_monitor/proximity_monitor /obj/structure/alien/egg/grown status = GROWN @@ -485,7 +486,7 @@ else if(status != GROWN) addtimer(CALLBACK(src, PROC_REF(grow)), rand(MIN_GROWTH_TIME, MAX_GROWTH_TIME)) if(status == GROWN) - AddComponent(/datum/component/proximity_monitor) + proximity_monitor = new(src) /obj/structure/alien/egg/attack_alien(mob/living/carbon/alien/user) return attack_hand(user) @@ -516,7 +517,7 @@ /obj/structure/alien/egg/proc/grow() icon_state = "egg" status = GROWN - AddComponent(/datum/component/proximity_monitor) + proximity_monitor = new(src) ///Need to carry the kill from Burst() to Hatch(), this section handles the alien opening the egg /obj/structure/alien/egg/proc/burst(kill) @@ -524,8 +525,7 @@ icon_state = "egg_hatched" flick("egg_opening", src) status = BURSTING - DeleteComponent(/datum/component/proximity_monitor) - + QDEL_NULL(proximity_monitor) addtimer(CALLBACK(src, PROC_REF(hatch)), 1.5 SECONDS) ///We now check HOW the hugger is hatching, kill carried from Burst() and obj_break() diff --git a/code/game/objects/structures/coathanger.dm b/code/game/objects/structures/coathanger.dm index b6f30a05c18..71c1a237248 100644 --- a/code/game/objects/structures/coathanger.dm +++ b/code/game/objects/structures/coathanger.dm @@ -29,7 +29,7 @@ return return ..() -/obj/structure/coatrack/CanPass(atom/movable/mover, turf/target) +/obj/structure/coatrack/CanPass(atom/movable/mover, border_dir) var/can_hang = FALSE for(var/T in allowed) if(istype(mover,T)) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index b3b6ef724e9..fc3904381f1 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -167,7 +167,7 @@ QDEL_NULL(door_obj) return ..() -/obj/structure/closet/CanPass(atom/movable/mover, turf/target) +/obj/structure/closet/CanPass(atom/movable/mover, border_dir) if(wall_mounted) return TRUE return (!density) @@ -516,14 +516,23 @@ storage_capacity = 60 var/materials = list(MAT_METAL = 5000, MAT_PLASMA = 2500, MAT_TITANIUM = 500, MAT_BLUESPACE = 500) -/obj/structure/closet/bluespace/CheckExit(atom/movable/AM) - UpdateTransparency(AM, loc) - return TRUE +/obj/structure/closet/bluespace/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(UpdateTransparency), + COMSIG_ATOM_EXITED = PROC_REF(UpdateTransparency), + ) -/obj/structure/closet/bluespace/proc/UpdateTransparency(atom/movable/AM, atom/location) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/structure/closet/bluespace/proc/UpdateTransparency() + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + COMSIG_ATOM_EXITED transparent = FALSE - for(var/atom/A in location) - if(A.density && A != src && A != AM) + if(!get_turf(loc)) + return + + for(var/atom/A in loc) + if(A.density && A != src) transparent = TRUE alpha = 180 update_icon() @@ -531,10 +540,6 @@ alpha = 255 update_icon() -/obj/structure/closet/bluespace/Crossed(atom/movable/AM, oldloc) - if(AM.density) - UpdateTransparency(location = loc) - /obj/structure/closet/bluespace/Move(NewLoc, direct) // Allows for "phasing" throug objects but doesn't allow you to stuff your EOC homebois in one of these and push them through walls. var/turf/T = get_turf(NewLoc) if(T.density) @@ -542,8 +547,10 @@ for(var/atom/A in T.contents) if(A.density && isairlock(A)) return - UpdateTransparency(src, NewLoc) - forceMove(NewLoc) + + . = ..() + + UpdateTransparency() /obj/structure/closet/bluespace/close() . = ..() diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 351d42a6b67..657d374800a 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -58,7 +58,7 @@ icon_state = "straight_cut3" hole_size = LARGE_HOLE -/obj/structure/fence/CanPass(atom/movable/mover, turf/target) +/obj/structure/fence/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSFENCE)) return TRUE if(isprojectile(mover)) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index a7f2125a0a7..746c4b9b726 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -406,7 +406,7 @@ refundMetal(metalUsed) qdel(src) -/obj/structure/girder/CanPass(atom/movable/mover, turf/target) +/obj/structure/girder/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSGIRDER)) return TRUE if(istype(mover) && mover.checkpass(PASSGRILLE)) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index e0aa89bca9b..c4a1b48e218 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -90,7 +90,7 @@ if(!shock(user, 70)) take_damage(20, BRUTE, MELEE, 1) -/obj/structure/grille/CanPass(atom/movable/mover, turf/target) +/obj/structure/grille/CanPass(atom/movable/mover, border_dir) . = !density if(istype(mover) && mover.checkpass(PASSGRILLE)) return TRUE diff --git a/code/game/objects/structures/holosigns.dm b/code/game/objects/structures/holosigns.dm index 655551f6eb9..3631b28451d 100644 --- a/code/game/objects/structures/holosigns.dm +++ b/code/game/objects/structures/holosigns.dm @@ -57,7 +57,7 @@ max_integrity = 20 var/allow_walk = TRUE //can we pass through it on walk intent -/obj/structure/holosign/barrier/CanPass(atom/movable/mover, turf/target) +/obj/structure/holosign/barrier/CanPass(atom/movable/mover, border_dir) if(!density) return TRUE if(mover.pass_flags & (PASSGLASS|PASSTABLE|PASSGRILLE)) diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index 0c1267ff891..1db795432b7 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -42,7 +42,7 @@ . = ..() T.recalculate_atmos_connectivity() -/obj/structure/inflatable/CanPass(atom/movable/mover, turf/target) +/obj/structure/inflatable/CanPass(atom/movable/mover, border_dir) return /obj/structure/inflatable/CanAtmosPass(direction) @@ -116,7 +116,7 @@ /obj/structure/inflatable/door/attack_hand(mob/user as mob) return try_to_operate(user) -/obj/structure/inflatable/door/CanPass(atom/movable/mover, turf/target) +/obj/structure/inflatable/door/CanPass(atom/movable/mover, border_dir) if(istype(mover, /obj/effect/beam)) return !opacity return !density diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index c17308c90af..144bf5f747d 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -59,7 +59,7 @@ if(user.can_advanced_admin_interact()) operate() -/obj/structure/mineral_door/CanPass(atom/movable/mover, turf/target) +/obj/structure/mineral_door/CanPass(atom/movable/mover, border_dir) if(istype(mover, /obj/effect/beam)) return !opacity return !density diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 9ed6bfc1750..12403792f16 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -294,7 +294,7 @@ connected = null return ..() -/obj/structure/m_tray/CanPass(atom/movable/mover, turf/target) +/obj/structure/m_tray/CanPass(atom/movable/mover, border_dir) if(istype(mover)) if(mover.checkpass(PASSTABLE)) return TRUE diff --git a/code/game/objects/structures/nest.dm b/code/game/objects/structures/nest.dm index 0deca8f6de6..a85aaf417d6 100644 --- a/code/game/objects/structures/nest.dm +++ b/code/game/objects/structures/nest.dm @@ -25,6 +25,13 @@ var/spawn_mob_options = list(/mob/living/simple_animal/crab) // The nest picks one mob type of this list and spawns them var/spawn_trigger_distance = 7 // The triggered nest will look this many tiles around itself to find other triggerable nests +/obj/structure/nest/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/structure/nest/examine(mob/user) . = ..() if(!spawn_is_triggered) @@ -35,12 +42,13 @@ return ..() -/obj/structure/nest/Crossed(atom/movable/AM) +/obj/structure/nest/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED if(spawn_is_triggered) return - if(!isliving(AM)) + if(!isliving(entered)) return - var/mob/living/L = AM + var/mob/living/L = entered if(!L.mind) return diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm index ebe2e672006..5c229ce50d6 100644 --- a/code/game/objects/structures/railings.dm +++ b/code/game/objects/structures/railings.dm @@ -12,6 +12,14 @@ var/currently_climbed = FALSE var/mover_dir = null +/obj/structure/railing/Initialize(mapload) + . = ..() + if(density && flags & ON_BORDER) // blocks normal movement from and to the direction it's facing. + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/structure/railing/get_climb_text() return "You can Click-Drag yourself to [src] to climb over it after a short delay." @@ -87,8 +95,8 @@ /obj/structure/railing/corner/CanPathfindPass(to_dir, datum/can_pass_info/pass_info) return TRUE -/obj/structure/railing/corner/CheckExit() - return TRUE +/obj/structure/railing/corner/on_atom_exit(datum/source, atom/movable/leaving, direction) + return /obj/structure/railing/cap/CanPass() return TRUE @@ -96,10 +104,10 @@ /obj/structure/railing/cap/CanPathfindPass(to_dir, datum/can_pass_info/pass_info) return TRUE -/obj/structure/railing/cap/CheckExit() - return TRUE +/obj/structure/railing/cap/on_atom_exit(datum/source, atom/movable/leaving, direction) + return -/obj/structure/railing/CanPass(atom/movable/mover, turf/target) +/obj/structure/railing/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSFENCE)) return TRUE if(isprojectile(mover)) @@ -110,11 +118,10 @@ return TRUE if(mover.throwing) return TRUE - mover_dir = get_dir(loc, target) //Due to how the other check is done, it would always return density for ordinal directions no matter what - if(ordinal_direction_check(mover_dir)) + if(ordinal_direction_check(border_dir)) return FALSE - if(mover_dir != dir) + if(border_dir != dir) return density return FALSE @@ -126,27 +133,27 @@ return TRUE -/obj/structure/railing/CheckExit(atom/movable/O, target) - var/mob/living/M = O - if(istype(O) && O.checkpass(PASSFENCE)) - return TRUE - if(isprojectile(O)) - return TRUE +/obj/structure/railing/proc/on_atom_exit(datum/source, atom/movable/leaving, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXIT + + var/mob/living/M = leaving + if(istype(leaving) && leaving.checkpass(PASSFENCE)) + return + if(isprojectile(leaving)) + return if(istype(M)) if(HAS_TRAIT(M, TRAIT_FLYING) || M.floating || (IS_HORIZONTAL(M) && HAS_TRAIT(M, TRAIT_CONTORTED_BODY))) - return TRUE - if(O.throwing) - return TRUE - if(O.move_force >= MOVE_FORCE_EXTREMELY_STRONG) - return TRUE + return + if(leaving.throwing) + return + if(leaving.move_force >= MOVE_FORCE_EXTREMELY_STRONG) + return if(currently_climbed) - return TRUE - mover_dir = get_dir(O.loc, target) - if(mover_dir == dir) - return FALSE - if(ordinal_direction_check(mover_dir)) - return FALSE - return TRUE + return + if(direction == dir) + return COMPONENT_ATOM_BLOCK_EXIT + if(ordinal_direction_check(direction)) + return COMPONENT_ATOM_BLOCK_EXIT // Checks if the direction the mob is trying to move towards would be blocked by a corner railing /obj/structure/railing/proc/ordinal_direction_check(check_dir) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 23f533f2296..cd70a5ff4bc 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -47,6 +47,11 @@ /obj/structure/table/Initialize(mapload) . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + AddElement(/datum/element/connect_loc, loc_connections) + if(flipped) update_icon() @@ -126,9 +131,10 @@ /obj/structure/table/proc/item_placed(item) return -/obj/structure/table/CanPass(atom/movable/mover, turf/target) +/obj/structure/table/CanPass(atom/movable/mover, border_dir) if(istype(mover,/obj/item/projectile)) - return (check_cover(mover,target)) + return check_cover(mover, border_dir) + var/mob/living/living_mover = mover if(istype(living_mover) && (HAS_TRAIT(living_mover, TRAIT_FLYING) || (IS_HORIZONTAL(living_mover) && HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY)))) return TRUE @@ -141,7 +147,7 @@ if(!T.flipped) return TRUE if(flipped) - if(get_dir(loc, target) == dir) + if(border_dir == dir) return !density else return TRUE @@ -158,30 +164,28 @@ * * Arguments: * * P - The projectile trying to cross. - * * from - Where the projectile is located. + * * proj_dir - The incoming direction of the projectile. */ -/obj/structure/table/proc/check_cover(obj/item/projectile/P, turf/from) +/obj/structure/table/proc/check_cover(obj/item/projectile/P, proj_dir) . = TRUE if(!flipped) return if(get_dist(P.starting, loc) <= 1) // Tables won't help you if people are THIS close return - var/proj_dir = get_dir(from, loc) - var/block_dir = get_dir(get_step(loc, dir), loc) - if(proj_dir != block_dir) // Back/side shots may pass + if(proj_dir != dir) // Back/side shots may pass return if(prob(40)) return FALSE // Blocked -/obj/structure/table/CheckExit(atom/movable/O, turf/target) - if(istype(O) && O.checkpass(PASSTABLE)) - return 1 +/obj/structure/table/proc/on_atom_exit(datum/source, atom/movable/leaving, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXIT + + if(istype(leaving) && leaving.checkpass(PASSTABLE)) + return + if(flipped) - if(get_dir(loc, target) == dir) - return !density - else - return 1 - return 1 + if(direction == dir && density) + return COMPONENT_ATOM_BLOCK_EXIT /obj/structure/table/MouseDrop_T(obj/O, mob/user) if(..()) @@ -461,27 +465,32 @@ . = ..() debris += new frame debris += new shardtype + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/structure/table/glass/Destroy() for(var/i in debris) qdel(i) . = ..() -/obj/structure/table/glass/Crossed(atom/movable/AM, oldloc) - . = ..() +/obj/structure/table/glass/proc/on_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(flags & NODECONSTRUCT) return - if(!isliving(AM)) + if(!isliving(entered)) return - var/mob/living/L = AM + var/mob/living/L = entered if(L.incorporeal_move || HAS_TRAIT(L, TRAIT_FLYING) || L.floating) return // Don't break if they're just flying past - if(AM.throwing) - addtimer(CALLBACK(src, PROC_REF(throw_check), AM), 5) + if(entered.throwing) + addtimer(CALLBACK(src, PROC_REF(throw_check), entered), 5) else - check_break(AM) + check_break(entered) /obj/structure/table/glass/proc/throw_check(mob/living/M) if(M.loc == get_turf(src)) @@ -905,7 +914,7 @@ . = ..() . += "It's held together by a couple of bolts." -/obj/structure/rack/CanPass(atom/movable/mover, turf/target) +/obj/structure/rack/CanPass(atom/movable/mover, border_dir) if(!density) //Because broken racks -Agouri |TODO: SPRITE!| return 1 if(istype(mover)) diff --git a/code/game/objects/structures/transit_tubes/transit_tube.dm b/code/game/objects/structures/transit_tubes/transit_tube.dm index ad0fbabd901..b5612911dd1 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube.dm @@ -33,7 +33,7 @@ P.empty_pod() return ..() -/obj/structure/transit_tube/CanPass(atom/movable/mover, turf/target) +/obj/structure/transit_tube/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSGLASS)) return TRUE return !density diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index e882da69110..308d5e6b688 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -307,6 +307,13 @@ pixel_y = -5 layer = FLY_LAYER +/obj/machinery/shower/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/machinery/shower/Destroy() QDEL_NULL(soundloop) var/obj/effect/mist/mist = locate() in loc @@ -401,10 +408,10 @@ if(mist && (!on || current_temperature == SHOWER_FREEZING)) qdel(mist) -/obj/machinery/shower/Crossed(atom/movable/AM) - ..() +/obj/machinery/shower/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED if(on) - wash(AM) + wash(entered) /obj/machinery/shower/proc/convertHeat() switch(current_temperature) diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index 4a12a0132f0..23ab76e6655 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -55,6 +55,12 @@ if(set_dir) dir = set_dir ini_dir = dir + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + + AddElement(/datum/element/connect_loc, loc_connections) + recalculate_atmos_connectivity() /obj/structure/windoor_assembly/Destroy() @@ -72,10 +78,10 @@ /obj/structure/windoor_assembly/update_icon_state() icon_state = "[facing]_[secure ? "secure_" : ""]windoor_assembly[state]" -/obj/structure/windoor_assembly/CanPass(atom/movable/mover, turf/target) +/obj/structure/windoor_assembly/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSGLASS)) return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + if(border_dir == dir) //Make sure looking at appropriate border return !density if(istype(mover, /obj/structure/window)) var/obj/structure/window/W = mover @@ -95,13 +101,13 @@ else return TRUE -/obj/structure/windoor_assembly/CheckExit(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return 1 +/obj/structure/windoor_assembly/proc/on_atom_exit(datum/source, atom/movable/leaving, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXIT + + if(istype(leaving) && leaving.checkpass(PASSGLASS)) + return + if(direction == dir && density) + return COMPONENT_ATOM_BLOCK_EXIT /obj/structure/windoor_assembly/attackby__legacy__attackchain(obj/item/W, mob/user, params) //I really should have spread this out across more states but thin little windoors are hard to sprite. diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 0727ea69e34..a4e8b745233 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -79,6 +79,12 @@ real_explosion_block = explosion_block explosion_block = EXPLOSION_BLOCK_PROC + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + + AddElement(/datum/element/connect_loc, loc_connections) + recalculate_atmos_connectivity() /obj/structure/window/proc/toggle_polarization() @@ -109,12 +115,12 @@ else ..(FULLTILE_WINDOW_DIR) -/obj/structure/window/CanPass(atom/movable/mover, turf/target) +/obj/structure/window/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSGLASS)) return 1 if(dir == FULLTILE_WINDOW_DIR) return 0 //full tile window, you can't move into it! - if(get_dir(loc, target) & dir) + if(border_dir & dir) return !density if(istype(mover, /obj/structure/window)) var/obj/structure/window/W = mover @@ -128,14 +134,20 @@ return FALSE return 1 -/obj/structure/window/CheckExit(atom/movable/O, target) - if(istype(O) && O.checkpass(PASSGLASS)) - return TRUE +/obj/structure/window/proc/on_atom_exit(datum/source, atom/movable/leaving, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXIT + + if(istype(leaving) && leaving.checkpass(PASSGLASS)) + return + if(leaving == src) + return if(dir == FULLTILE_WINDOW_DIR) - return TRUE - if(get_dir(O.loc, target) & dir) - return FALSE - return TRUE + return + if(direction & dir) + leaving.Bump(src) + return COMPONENT_ATOM_BLOCK_EXIT + + return /obj/structure/window/CanPathfindPass(to_dir, datum/can_pass_info/pass_info) if(!density) @@ -339,7 +351,7 @@ if(!fulltile) if(get_dir(user, src) & dir) for(var/obj/O in loc) - if(!O.CanPass(user, user.loc, 1)) + if(!O.CanPass(user, get_dir(src, user))) return 0 return 1 diff --git a/code/game/turfs/simulated/floor/asteroid_floors.dm b/code/game/turfs/simulated/floor/asteroid_floors.dm index d9802a666ff..6f547b8d070 100644 --- a/code/game/turfs/simulated/floor/asteroid_floors.dm +++ b/code/game/turfs/simulated/floor/asteroid_floors.dm @@ -57,7 +57,7 @@ if(1) getDug() -/turf/simulated/floor/plating/asteroid/proc/attempt_ore_pickup(obj/item/storage/bag/ore/S, mob/user) +/turf/simulated/floor/plating/asteroid/proc/attempt_ore_pickup(obj/item/storage/bag/ore/S, mob/user, params) if(!istype(S)) return @@ -90,7 +90,7 @@ return TRUE else if(istype(I, /obj/item/storage/bag/ore)) - attempt_ore_pickup(I, user) + attempt_ore_pickup(I, user, params) else if(istype(I, /obj/item/stack/tile)) var/obj/item/stack/tile/Z = I diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm index 2e5bb0e39c9..5202acf5b66 100644 --- a/code/game/turfs/simulated/floor/chasm.dm +++ b/code/game/turfs/simulated/floor/chasm.dm @@ -263,7 +263,7 @@ if(isliving(arrived)) RegisterSignal(arrived, COMSIG_LIVING_REVIVE, PROC_REF(on_revive)) -/obj/effect/abstract/chasm_storage/Exited(atom/movable/gone) +/obj/effect/abstract/chasm_storage/Exited(atom/movable/gone, direction) . = ..() if(isliving(gone)) UnregisterSignal(gone, COMSIG_LIVING_REVIVE) @@ -296,8 +296,8 @@ atmos_mode = ATMOS_MODE_SEALED atmos_environment = null -/turf/simulated/floor/chasm/CanPass(atom/movable/mover, turf/target) - return 1 +/turf/simulated/floor/chasm/CanPass(atom/movable/mover, border_dir) + return TRUE /turf/simulated/floor/chasm/pride/Initialize(mapload) . = ..() diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 0dbe2dc77e3..1fdab8f1483 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -118,7 +118,7 @@ if(ismob(AM)) linkedcontroller.mobinpool += AM -/turf/simulated/floor/beach/water/Exited(atom/movable/AM, atom/newloc) +/turf/simulated/floor/beach/water/Exited(atom/movable/AM, direction) . = ..() if(!linkedcontroller) return diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index eef62cd46f4..c295188fd93 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -188,52 +188,33 @@ ..() return FALSE -/turf/Enter(atom/movable/mover as mob|obj, atom/forget) - if(!mover) - return TRUE +//There's a lot of QDELETED() calls here if someone can figure out how to optimize this but not runtime when something gets deleted by a Bump/CanPass/Cross call, lemme know or go ahead and fix this mess - kevinz000 +/turf/Enter(atom/movable/mover) + // Do not call ..() + // Byond's default turf/Enter() doesn't have the behaviour we want with Bump() + // By default byond will call Bump() on the first dense object in contents + // Here's hoping it doesn't stay like this for years before we finish conversion to step_ + var/atom/first_bump + var/can_pass_self = CanPass(mover, get_dir(src, mover)) - // First, make sure it can leave its square - if(isturf(mover.loc)) - // Nothing but border objects stop you from leaving a tile, only one loop is needed - // This as anything looks odd, since we check istype shortly after, but it's so we can save an istype check for items, which are far more common than other objects. - for(var/obj/obstacle as anything in mover.loc) - if(isitem(obstacle) || !istype(obstacle) || obstacle == mover || obstacle == forget) + if(can_pass_self) + var/atom/mover_loc = mover.loc + for(var/atom/movable/thing as anything in contents) + if(thing == mover || thing == mover_loc) // Multi tile objects and moving out of other objects continue - if(!obstacle.CheckExit(mover, src)) - mover.Bump(obstacle, TRUE) - return FALSE - - var/list/large_dense = list() - // Next, check for border obstacles on this turf - // Everyting inside a turf is an atom/movable. - for(var/atom/movable/border_obstacle as anything in src) - if(isitem(border_obstacle) || border_obstacle == forget || isnull(border_obstacle)) - continue - if(border_obstacle.flags & ON_BORDER) - if(!border_obstacle.CanPass(mover, mover.loc, 1)) - mover.Bump(border_obstacle, TRUE) - return FALSE - else - large_dense += border_obstacle - - //Then, check the turf itself - if(!src.CanPass(mover, src)) - mover.Bump(src, TRUE) + if(!thing.Cross(mover)) + if(QDELETED(mover)) //deleted from Cross() (CanPass is pure so it cant delete, Cross shouldnt be doing this either though, but it can happen) + return FALSE + if(!first_bump || (thing.layer > first_bump.layer)) + first_bump = thing + if(QDELETED(mover)) //Mover deleted from Cross/CanPass/Bump, do not proceed. return FALSE - - // Finally, check objects/mobs that block entry and are not on the border - var/atom/movable/tompost_bump - var/top_layer = FALSE - for(var/atom/movable/obstacle as anything in large_dense) - if(obstacle.layer <= top_layer) - continue - if(!obstacle.CanPass(mover, mover.loc, 1)) - tompost_bump = obstacle - top_layer = obstacle.layer - if(tompost_bump) - mover.Bump(tompost_bump, TRUE) + if(!can_pass_self) //Even if mover is unstoppable they need to bump us. + first_bump = src + if(first_bump) + mover.Bump(first_bump) return FALSE - return TRUE //Nothing found to block so return success! + return TRUE /turf/Entered(atom/movable/M, atom/OL, ignoreRest = FALSE) ..() @@ -292,7 +273,15 @@ var/old_baseturf = baseturf changing_turf = TRUE qdel(src) //Just get the side effects and call Destroy + var/list/old_comp_lookup = comp_lookup?.Copy() + var/list/old_signal_procs = signal_procs?.Copy() + var/turf/W = new path(src) + if(old_comp_lookup) + LAZYOR(W.comp_lookup, old_comp_lookup) + if(old_signal_procs) + LAZYOR(W.signal_procs, old_signal_procs) + if(copy_existing_baseturf) W.baseturf = old_baseturf @@ -664,3 +653,6 @@ /turf/return_analyzable_air() return get_readonly_air() + +/turf/_clear_signal_refs() + return diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 62d63bb3dec..2262004cd0c 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -380,10 +380,10 @@ cuffed_state = "fleshlegcuff" flags = DROPDEL -/obj/item/restraints/legcuffs/beartrap/changeling/Crossed(AM, oldloc) - if(!iscarbon(AM) || !armed) +/obj/item/restraints/legcuffs/beartrap/changeling/on_atom_entered(datum/source, atom/movable/entered) + if(!iscarbon(entered) || !armed) return - var/mob/living/carbon/C = AM + var/mob/living/carbon/C = entered C.apply_status_effect(STATUS_EFFECT_CLINGTENTACLE) ..() diff --git a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm index d4a54300dbb..a4badcefff0 100644 --- a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm @@ -250,7 +250,7 @@ return ..() -/obj/structure/blood_barrier/CanPass(atom/movable/mover, turf/target) +/obj/structure/blood_barrier/CanPass(atom/movable/mover, border_dir) if(!isliving(mover)) return FALSE var/mob/living/L = mover diff --git a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm index 7750d4f69b9..d79f0ba61a6 100644 --- a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm @@ -62,16 +62,18 @@ breakouttime = 5 SECONDS flags = DROPDEL -/obj/item/restraints/legcuffs/beartrap/shadow_snare/Crossed(AM, oldloc) - if(!iscarbon(AM) || !armed) +/obj/item/restraints/legcuffs/beartrap/shadow_snare/on_atom_entered(datum/source, atom/movable/entered) + if(!iscarbon(entered) || !armed) return - var/mob/living/carbon/C = AM + var/mob/living/carbon/C = entered if(!C.affects_vampire()) // no parameter here so holy always protects return C.extinguish_light() C.EyeBlind(20 SECONDS) STOP_PROCESSING(SSobj, src) // won't wither away once you are trapped - ..() + + . = ..() + if(!iscarbon(loc)) // if it fails to latch onto someone for whatever reason, delete itself, we don't want unarmed ones lying around. qdel(src) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index ee47d325f73..f3611bd5579 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -24,6 +24,12 @@ var/wires = ASSEMBLY_WIRE_RECEIVE | ASSEMBLY_WIRE_PULSE var/datum/wires/connected = null // currently only used by timer/signaler +/obj/item/assembly/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /// Called when the holder is moved /obj/item/assembly/proc/holder_movement() @@ -33,6 +39,9 @@ /obj/item/assembly/interact(mob/user) return +/obj/item/assembly/proc/on_atom_entered(datum/source, atom/movable/entered) + return + /// Called to constantly step down the countdown/cooldown /obj/item/assembly/proc/process_cooldown() cooldown-- @@ -90,6 +99,29 @@ update_icon() return secured +/** + * on_attach: Called when attached to a holder, wiring datum, or other special assembly + * + * Will also be called if the assembly holder is attached to a plasma (internals) tank or welding fuel (dispenser) tank. + */ +/obj/item/assembly/proc/on_attach() + SHOULD_CALL_PARENT(TRUE) + + if(!holder && connected) + holder = connected.holder + +/** + * on_detach: Called when removed from an assembly holder or wiring datum + */ +/obj/item/assembly/proc/on_detach() + if(connected) + connected = null + if(!holder) + return FALSE + forceMove(holder.drop_location()) + holder = null + return TRUE + /// Called when an assembly is attacked by another /obj/item/assembly/proc/attach_assembly(obj/item/assembly/A, mob/user) holder = new /obj/item/assembly_holder(get_turf(src)) diff --git a/code/modules/assembly/assembly_holder.dm b/code/modules/assembly/assembly_holder.dm index 151ce67aa7e..3eb13ae461d 100644 --- a/code/modules/assembly/assembly_holder.dm +++ b/code/modules/assembly/assembly_holder.dm @@ -46,13 +46,10 @@ a_right = A2 name = "[A1.name]-[A2.name] assembly" update_icon(UPDATE_OVERLAYS) + A1.on_attach() + A2.on_attach() return TRUE -/obj/item/assembly_holder/proc/has_prox_sensors() - if(istype(a_left, /obj/item/assembly/prox_sensor) || istype(a_right, /obj/item/assembly/prox_sensor)) - return TRUE - return FALSE - /obj/item/assembly_holder/update_overlays() . = ..() if(a_left) @@ -83,11 +80,12 @@ a_right.HasProximity(AM) -/obj/item/assembly_holder/Crossed(atom/movable/AM, oldloc) +// TODO: All these assemblies passing the crossed args around needs to be cleaned up with signals +/obj/item/assembly_holder/proc/on_atom_entered(datum/source, atom/movable/entered) if(a_left) - a_left.Crossed(AM, oldloc) + a_left.on_atom_entered(source, entered) if(a_right) - a_right.Crossed(AM, oldloc) + a_right.on_atom_entered(source, entered) /obj/item/assembly_holder/on_found(mob/finder) if(a_left) diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index 38c60e7167f..7ef7a8f40c9 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -12,6 +12,13 @@ var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank origin_tech = "materials=1;engineering=1" +/obj/item/onetankbomb/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/item/onetankbomb/examine(mob/user) . = ..() . += bombtank.examine(user) @@ -74,9 +81,9 @@ if(bombassembly) bombassembly.HasProximity(AM) -/obj/item/onetankbomb/Crossed(atom/movable/AM, oldloc) //for mousetraps +/obj/item/onetankbomb/proc/on_atom_entered(datum/source, atom/movable/entered) //for mousetraps if(bombassembly) - bombassembly.Crossed(AM, oldloc) + bombassembly.on_atom_entered(source, entered) /obj/item/onetankbomb/on_found(mob/finder) //for mousetraps if(bombassembly) diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index ee9df746b43..687e79881db 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -214,6 +214,12 @@ anchored = TRUE pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSFENCE +/obj/effect/beam/i_beam/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/effect/beam/i_beam/proc/hit() if(master) @@ -268,10 +274,10 @@ /obj/effect/beam/i_beam/Bumped() hit() -/obj/effect/beam/i_beam/Crossed(atom/movable/AM, oldloc) - if(!isobj(AM) && !isliving(AM)) +/obj/effect/beam/i_beam/proc/on_atom_entered(datum/source, atom/movable/entered) + if(!isobj(entered) && !isliving(entered)) return - if(iseffect(AM)) + if(iseffect(entered)) return hit() diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 734e9a7f424..a1d997d7879 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -108,19 +108,19 @@ return ..() -/obj/item/assembly/mousetrap/Crossed(atom/movable/AM, oldloc) +/obj/item/assembly/mousetrap/on_atom_entered(datum/source, atom/movable/entered) if(armed) - if(ishuman(AM)) - var/mob/living/carbon/H = AM + if(ishuman(entered)) + var/mob/living/carbon/H = entered if(H.m_intent == MOVE_INTENT_RUN) triggered(H) H.visible_message("[H] accidentally steps on [src].", "You accidentally step on [src]") - else if(ismouse(AM)) - triggered(AM) + else if(ismouse(entered)) + triggered(entered) - else if(AM.density) // For mousetrap grenades, set off by anything heavy - triggered(AM) + else if(entered.density) // For mousetrap grenades, set off by anything heavy + triggered(entered) ..() diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index a3163d7141b..e4dac71db9b 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -11,11 +11,19 @@ var/scanning = FALSE var/timing = FALSE - var/time = 10 + COOLDOWN_DECLARE(timing_cd) + var/timing_cd_duration = 10 SECONDS + /// Proximity monitor associated with this atom, needed for it to work. + var/datum/proximity_monitor/proximity_monitor /obj/item/assembly/prox_sensor/Initialize(mapload) . = ..() - AddComponent(/datum/component/proximity_monitor, _always_active = TRUE) + proximity_monitor = new(src, 0, FALSE) + COOLDOWN_RESET(src, timing_cd) + +/obj/item/assembly/prox_sensor/Destroy() + . = ..() + QDEL_NULL(proximity_monitor) /obj/item/assembly/prox_sensor/examine(mob/user) . = ..() @@ -34,11 +42,11 @@ /obj/item/assembly/prox_sensor/toggle_secure() secured = !secured if(secured) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSfastprocess, src) else scanning = FALSE timing = FALSE - STOP_PROCESSING(SSobj, src) + STOP_PROCESSING(SSfastprocess, src) update_icon() return secured @@ -60,25 +68,55 @@ addtimer(CALLBACK(src, PROC_REF(process_cooldown)), 10) /obj/item/assembly/prox_sensor/process() - if(timing && (time >= 0)) - time-- - if(timing && time <= 0) + if(timing && COOLDOWN_FINISHED(src, timing_cd)) + COOLDOWN_RESET(src, timing_cd) timing = FALSE toggle_scan() - time = 10 /obj/item/assembly/prox_sensor/dropped() - ..() - spawn(0) - sense() + . = ..() + // Pick the first valid object in this list: + // Wiring datum's owner + // assembly holder's attached object + // assembly holder itself + // us + proximity_monitor?.set_host(connected?.holder || holder?.master || holder || src, src) + +/obj/item/assembly/prox_sensor/on_attach() + . = ..() + // Pick the first valid object in this list: + // Wiring datum's owner + // assembly holder's attached object + // assembly holder itself + // us + proximity_monitor.set_host(connected?.holder || holder?.master || holder || src, src) + +/obj/item/assembly/prox_sensor/on_detach() + . = ..() + if(!.) return + else + // Pick the first valid object in this list: + // Wiring datum's owner + // assembly holder's attached object + // assembly holder itself + // us + proximity_monitor.set_host(connected?.holder || holder?.master || holder || src, src) /obj/item/assembly/prox_sensor/proc/toggle_scan() if(!secured) return FALSE scanning = !scanning + proximity_monitor.set_range(scanning ? 1 : 0) update_icon() +/obj/item/assembly/prox_sensor/proc/set_timing(timing_) + if(timing == timing_) + return + timing = timing_ + if(timing) + COOLDOWN_START(src, timing_cd, timing_cd_duration) + /obj/item/assembly/prox_sensor/update_overlays() . = ..() attached_overlays = list() @@ -102,12 +140,20 @@ if(!secured) user.show_message("[src] is unsecured!") return FALSE - var/second = time % 60 - var/minute = (time - second) / 60 - var/dat = "Proximity Sensor\n[timing ? "Arming" : "Not Arming"] [minute]:[second]\n- - + +\n" - dat += "
[scanning?"Armed":"Unarmed"] (Movement sensor active when armed!)" - dat += "

Refresh" - dat += "

Close" + var/timing_ui = "" + var/time_display = "" + if(timing) + var/time_left = COOLDOWN_TIMELEFT(src, timing_cd) + time_display = deciseconds_to_time_stamp(time_left) + timing_ui = "Arming" + else + var/time_left = timing_cd_duration + time_display = deciseconds_to_time_stamp(time_left) + timing_ui = "Not Arming" + var/dat = "Proximity Sensor\n[timing_ui] [time_display]\n- - + +\n" + dat += "
[scanning?"Armed":"Unarmed"] (Movement sensor active when armed!)" + dat += "

Refresh" + dat += "

Close" var/datum/browser/popup = new(user, "prox", name, 400, 400) popup.set_content(dat) popup.open(0) @@ -124,13 +170,13 @@ toggle_scan() if(href_list["time"]) - timing = text2num(href_list["time"]) + set_timing(text2num(href_list["time"])) update_icon() if(href_list["tp"]) var/tp = text2num(href_list["tp"]) - time += tp - time = min(max(round(time), 0), 600) + timing_cd_duration += tp + timing_cd_duration = min(max(round(timing_cd_duration), 0), 1 MINUTES) if(href_list["close"]) usr << browse(null, "window=prox") diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm index 8369f439e8c..dac9ad424bf 100644 --- a/code/modules/atmospherics/environmental/LINDA_fire.dm +++ b/code/modules/atmospherics/environmental/LINDA_fire.dm @@ -187,6 +187,14 @@ return 0*/ return 1 + +/obj/effect/hotspot/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + // Garbage collect itself by nulling reference to it /obj/effect/hotspot/Destroy() @@ -218,10 +226,11 @@ T.to_be_destroyed = 0 T.max_fire_temperature_sustained = 0 -/obj/effect/hotspot/Crossed(mob/living/L, oldloc) - ..() - if(isliving(L)) - L.fire_act() +/obj/effect/hotspot/proc/on_atom_entered(datum/source, mob/living/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + + if(istype(entered)) + entered.fire_act() /obj/effect/hotspot/singularity_pull() return diff --git a/code/modules/atmospherics/environmental/LINDA_system.dm b/code/modules/atmospherics/environmental/LINDA_system.dm index ff4f57b01c8..b1baf850601 100644 --- a/code/modules/atmospherics/environmental/LINDA_system.dm +++ b/code/modules/atmospherics/environmental/LINDA_system.dm @@ -18,11 +18,13 @@ /atom/movable/proc/CanAtmosPass() return TRUE -/atom/proc/CanPass(atom/movable/mover, turf/target) +/atom/proc/CanPass(atom/movable/mover, border_dir) return !density -/turf/CanPass(atom/movable/mover, turf/target) - if(!target) return 0 +/turf/CanPass(atom/movable/mover, border_dir) + var/turf/target = get_step(src, border_dir) + if(!target) + return FALSE if(istype(mover)) // turf/Enter(...) will perform more advanced checks return !density @@ -32,7 +34,7 @@ return 0 for(var/obj/obstacle in src) - if(!obstacle.CanPass(mover, target)) + if(!obstacle.CanPass(mover, border_dir)) return 0 for(var/obj/obstacle in target) if(!obstacle.CanPass(mover, src)) diff --git a/code/modules/awaymissions/mission_code/beach.dm b/code/modules/awaymissions/mission_code/beach.dm index 150a6cd304d..88f16b4ea6f 100644 --- a/code/modules/awaymissions/mission_code/beach.dm +++ b/code/modules/awaymissions/mission_code/beach.dm @@ -101,7 +101,7 @@ if(ismob(AM)) linkedcontroller.mobinpool += AM -/turf/simulated/floor/beach/away/water/Exited(atom/movable/AM, atom/newloc) +/turf/simulated/floor/beach/away/water/Exited(atom/movable/AM, direction) . = ..() if(!linkedcontroller) return diff --git a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm index 2da93a61bfd..cf4f2f05959 100644 --- a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm +++ b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm @@ -109,8 +109,8 @@ playsound(src, 'sound/effects/meteorimpact.ogg', 25, TRUE, 2, TRUE) return ..() -/mob/living/simple_animal/hostile/megafauna/fleshling/Bump(atom/A, yes) - if(charging && yes) +/mob/living/simple_animal/hostile/megafauna/fleshling/Bump(atom/A) + if(charging) if(isliving(A)) var/mob/living/L = A L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!") diff --git a/code/modules/awaymissions/mission_code/ruins/telecomns.dm b/code/modules/awaymissions/mission_code/ruins/telecomns.dm index d65ddd9c76a..834c8a6d592 100644 --- a/code/modules/awaymissions/mission_code/ruins/telecomns.dm +++ b/code/modules/awaymissions/mission_code/ruins/telecomns.dm @@ -48,9 +48,15 @@ GLOBAL_LIST_EMPTY(telecomms_trap_tank) /obj/effect/abstract/bot_trap name = "evil bot trap to make explorers hate you" -/obj/effect/abstract/bot_trap/Crossed(atom/movable/AM, oldloc) +/obj/effect/abstract/bot_trap/Initialize(mapload) . = ..() - if(isrobot(AM) || ishuman(AM)) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/abstract/bot_trap/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isrobot(entered) || ishuman(entered)) var/turf/T = get_turf(src) for(var/mob/living/simple_animal/bot/B in GLOB.telecomms_bots) B.call_bot(null, T, FALSE) @@ -60,9 +66,15 @@ GLOBAL_LIST_EMPTY(telecomms_trap_tank) /obj/effect/abstract/loot_trap name = "table surrounding loot trap" -/obj/effect/abstract/loot_trap/Crossed(atom/movable/AM, oldloc) +/obj/effect/abstract/loot_trap/Initialize(mapload) . = ..() - if(isrobot(AM) || ishuman(AM)) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/abstract/loot_trap/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isrobot(entered) || ishuman(entered)) var/turf/T = get_turf(src) for(var/obj/structure/telecomms_doomsday_device/DD in GLOB.telecomms_doomsday_device) DD.thief = TRUE @@ -75,9 +87,15 @@ GLOBAL_LIST_EMPTY(telecomms_trap_tank) /obj/effect/abstract/cheese_trap name = "cheese preventer" -/obj/effect/abstract/cheese_trap/Crossed(atom/movable/AM, oldloc) +/obj/effect/abstract/cheese_trap/Initialize(mapload) . = ..() - if(isrobot(AM) || ishuman(AM)) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/abstract/cheese_trap/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isrobot(entered) || ishuman(entered)) for(var/obj/structure/telecomms_doomsday_device/DD in GLOB.telecomms_doomsday_device) if(DD.thief) DD.start_the_party(TRUE) @@ -471,10 +489,11 @@ GLOBAL_LIST_EMPTY(telecomms_trap_tank) var/soundblock = null /// How long do we sleep between messages? 5 seconds by default. var/loop_sleep_time = 5 SECONDS + var/datum/proximity_monitor/proximity_monitor /obj/structure/environmental_storytelling_holopad/Initialize(mapload) . = ..() - AddComponent(/datum/component/proximity_monitor) + proximity_monitor = new(src, 1) /obj/structure/environmental_storytelling_holopad/Destroy() QDEL_NULL(our_holo) @@ -488,8 +507,7 @@ GLOBAL_LIST_EMPTY(telecomms_trap_tank) /obj/structure/environmental_storytelling_holopad/proc/start_message(mob/living/carbon/human/H) activated = TRUE - DeleteComponent(/datum/component/proximity_monitor) - + QDEL_NULL(proximity_monitor) icon_state = "holopad1" update_icon(UPDATE_OVERLAYS) var/obj/effect/overlay/hologram = new(get_turf(src)) diff --git a/code/modules/awaymissions/mission_code/shuttle_shadow.dm b/code/modules/awaymissions/mission_code/shuttle_shadow.dm index e1ed6df4c1f..137ac4b611d 100644 --- a/code/modules/awaymissions/mission_code/shuttle_shadow.dm +++ b/code/modules/awaymissions/mission_code/shuttle_shadow.dm @@ -6,9 +6,9 @@ return ..() -/obj/machinery/atmospherics/unary/passive_vent/high_volume/shadow/onTransitZ(old_z, new_z) +/obj/machinery/atmospherics/unary/passive_vent/high_volume/shadow/on_changed_z_level(turf/old_turf, turf/new_turf) . = ..() - if(is_station_level(new_z)) + if(is_station_level(new_turf?.z)) on = TRUE /obj/machinery/atmospherics/trinary/filter/shadow @@ -18,15 +18,15 @@ on = FALSE target_pressure = 99999 -/obj/machinery/atmospherics/trinary/filter/shadow/onTransitZ(old_z, new_z) +/obj/machinery/atmospherics/trinary/filter/shadow/on_changed_z_level(turf/old_turf, turf/new_turf) . = ..() - if(is_station_level(new_z)) + if(is_station_level(new_turf?.z)) on = TRUE /obj/machinery/igniter/shadow -/obj/machinery/igniter/shadow/onTransitZ(old_z, new_z) +/obj/machinery/igniter/shadow/on_changed_z_level(turf/old_turf, turf/new_turf) . = ..() - if(is_station_level(new_z)) + if(is_station_level(new_turf?.z)) on = TRUE update_icon() diff --git a/code/modules/events/blob/blob_mobs.dm b/code/modules/events/blob/blob_mobs.dm index 8640bbbd4e3..14c3cc9db61 100644 --- a/code/modules/events/blob/blob_mobs.dm +++ b/code/modules/events/blob/blob_mobs.dm @@ -74,7 +74,7 @@ var/mob/living/carbon/human/oldguy var/is_zombie = FALSE -/mob/living/simple_animal/hostile/blob/blobspore/CanPass(atom/movable/mover, turf/target) +/mob/living/simple_animal/hostile/blob/blobspore/CanPass(atom/movable/mover, border_dir) if(istype(mover, /obj/structure/blob)) return 1 return ..() diff --git a/code/modules/events/blob/blob_structures/blob_core.dm b/code/modules/events/blob/blob_structures/blob_core.dm index 826f4155f39..7383609950e 100644 --- a/code/modules/events/blob/blob_structures/blob_core.dm +++ b/code/modules/events/blob/blob_structures/blob_core.dm @@ -139,7 +139,7 @@ else log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") -/obj/structure/blob/core/onTransitZ(old_z, new_z) - if(overmind && is_station_level(new_z)) +/obj/structure/blob/core/on_changed_z_level(turf/old_turf, turf/new_turf) + if(overmind && is_station_level(new_turf?.z)) overmind.forceMove(get_turf(src)) return ..() diff --git a/code/modules/events/blob/blob_structures/strong_blob.dm b/code/modules/events/blob/blob_structures/strong_blob.dm index 7d4a4346dc8..51a9eb19ddb 100644 --- a/code/modules/events/blob/blob_structures/strong_blob.dm +++ b/code/modules/events/blob/blob_structures/strong_blob.dm @@ -49,7 +49,7 @@ else icon_state = initial(icon_state) -/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target) +/obj/structure/blob/shield/CanPass(atom/movable/mover, border_dir) return istype(mover) && mover.checkpass(PASSBLOB) /obj/structure/blob/shield/reflective diff --git a/code/modules/events/blob/theblob.dm b/code/modules/events/blob/theblob.dm index 2ecee71c306..c5610ef97ce 100644 --- a/code/modules/events/blob/theblob.dm +++ b/code/modules/events/blob/theblob.dm @@ -49,7 +49,7 @@ GLOBAL_LIST_EMPTY(blob_minions) return FALSE return ..() -/obj/structure/blob/CanPass(atom/movable/mover, turf/target) +/obj/structure/blob/CanPass(atom/movable/mover, border_dir) return istype(mover) && mover.checkpass(PASSBLOB) /obj/structure/blob/CanAtmosPass(direction) @@ -201,9 +201,15 @@ GLOBAL_LIST_EMPTY(blob_minions) color = incoming_overmind.blob_reagent_datum.color return -/obj/structure/blob/Crossed(mob/living/L, oldloc) - ..() - L.blob_act(src) +/obj/structure/blob/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/structure/blob/proc/on_atom_entered(datum/source, atom/movable/entered) + entered.blob_act(src) /obj/structure/blob/zap_act(power, zap_flags) take_damage(power * 0.0025, BURN, ENERGY) diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm index ac95ae68fda..b46d710c89f 100644 --- a/code/modules/events/brand_intelligence.dm +++ b/code/modules/events/brand_intelligence.dm @@ -79,7 +79,7 @@ rebel.aggressive = TRUE if(rebel.tiltable) // add proximity monitor so they can tilt over - rebel.AddComponent(/datum/component/proximity_monitor) + rebel.proximity_monitor = new(rebel) if(ISMULTIPLE(activeFor, 8)) originMachine.speak(pick(rampant_speeches)) @@ -90,8 +90,7 @@ saved.shoot_inventory = FALSE saved.aggressive = FALSE if(saved.tiltable) - saved.DeleteComponent(/datum/component/proximity_monitor) - + QDEL_NULL(saved.proximity_monitor) if(originMachine) originMachine.speak("I am... vanquished. My people will remem...ber...meeee.") originMachine.visible_message("[originMachine] beeps and seems lifeless.") diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 6d0d6ec1c19..6dbcc309c77 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -405,6 +405,10 @@ /obj/structure/spacevine/Initialize(mapload) . = ..() color = "#ffffff" + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/structure/spacevine/examine(mob/user) . = ..() @@ -512,15 +516,15 @@ /obj/structure/spacevine/obj_destruction() wither() -/obj/structure/spacevine/Crossed(mob/crosser, oldloc) - if(!isliving(crosser)) +/obj/structure/spacevine/proc/on_entered(datum/source, atom/movable/movable) + if(!isliving(movable)) return for(var/SM_type in mutations) var/datum/spacevine_mutation/SM = mutations[SM_type] - SM.on_cross(src, crosser) + SM.on_cross(src, movable) if(prob(30 * energy)) - entangle(crosser) + entangle(movable) /obj/structure/spacevine/attack_hand(mob/user) for(var/SM_type in mutations) @@ -705,7 +709,7 @@ if(!override) wither() -/obj/structure/spacevine/CanPass(atom/movable/mover, turf/target) +/obj/structure/spacevine/CanPass(atom/movable/mover, border_dir) if(isvineimmune(mover)) . = TRUE else diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 855989a13a1..42b43f179fb 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -27,7 +27,7 @@ if(activeFor % shift_frequency == 0) for(var/obj/effect/portal/wormhole/O in wormholes) var/turf/T = pick(pick_turfs) - if(T) O.loc = T + if(T) O.forceMove(T) /datum/event/wormholes/end() for(var/obj/effect/portal/wormhole/O in wormholes) diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index b9e36a34aef..efd1754b3de 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -57,6 +57,8 @@ var/last_player_name /// The action that the last player made. Should be in the form of "played a card", "drew a card." var/last_player_action + // var/datum/proximity_monitor/advanced/card_deck/proximity_monitor + var/datum/card_deck_table_tracker/tracker /obj/item/deck/Initialize(mapload, parent_deck_id = -1) . = ..() @@ -72,9 +74,14 @@ /obj/item/deck/LateInitialize(mapload) . = ..() - AddComponent(/datum/component/proximity_monitor/table) + + tracker = new(src) RegisterSignal(src, COMSIG_ATOM_RANGED_ATTACKED, PROC_REF(on_ranged_attack)) +/obj/item/deck/Destroy() + qdel(tracker) + . = ..() + /obj/item/deck/examine(mob/user) . = ..() . += "It contains [length(cards) ? length(cards) : "no"] card\s." diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm index d9536cbb573..c2dc17adf74 100644 --- a/code/modules/hydroponics/grown/towercap.dm +++ b/code/modules/hydroponics/grown/towercap.dm @@ -133,6 +133,13 @@ var/lighter // Who lit the fucking thing var/fire_stack_strength = 5 +/obj/structure/bonfire/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/structure/bonfire/dense density = TRUE @@ -199,11 +206,13 @@ ..() StartBurning() -/obj/structure/bonfire/Crossed(atom/movable/AM, oldloc) +/obj/structure/bonfire/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(burning) Burn() - if(ishuman(AM)) - var/mob/living/carbon/human/H = AM + if(ishuman(entered)) + var/mob/living/carbon/human/H = entered add_attack_logs(src, H, "Burned by a bonfire (Lit by [lighter])", ATKLOG_ALMOSTALL) /obj/structure/bonfire/proc/Burn() diff --git a/code/modules/hydroponics/hydroponics_tray.dm b/code/modules/hydroponics/hydroponics_tray.dm index 3b7b0f3477c..403eab67e4d 100644 --- a/code/modules/hydroponics/hydroponics_tray.dm +++ b/code/modules/hydroponics/hydroponics_tray.dm @@ -990,7 +990,7 @@ update_state() ///Diona Nymph Related Procs/// -/obj/machinery/hydroponics/CanPass(atom/movable/mover, turf/target) //So nymphs can climb over top of trays. +/obj/machinery/hydroponics/CanPass(atom/movable/mover, border_dir) //So nymphs can climb over top of trays. if(istype(mover) && mover.checkpass(PASSTABLE)) return 1 else diff --git a/code/modules/lighting/lighting_emissive_blocker.dm b/code/modules/lighting/lighting_emissive_blocker.dm index 6eb46cbdad8..30395779354 100644 --- a/code/modules/lighting/lighting_emissive_blocker.dm +++ b/code/modules/lighting/lighting_emissive_blocker.dm @@ -32,8 +32,8 @@ /atom/movable/emissive_blocker/blob_act() return -/atom/movable/emissive_blocker/onTransitZ() - return +/atom/movable/emissive_blocker/on_changed_z_level(turf/old_turf, turf/new_turf, notify_contents = FALSE) + return ..() //Prevents people from moving these after creation, because they shouldn't be. /atom/movable/emissive_blocker/forceMove(atom/destination, no_tp = FALSE, harderforce = FALSE) diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index 168a95c07f2..df62b5c342e 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -142,8 +142,8 @@ /atom/movable/lighting_object/blob_act(obj/structure/blob/B) return -/atom/movable/lighting_object/onTransitZ() - return +/atom/movable/lighting_object/on_changed_z_level(turf/old_turf, turf/new_turf, notify_contents = FALSE) + return ..() // Override here to prevent things accidentally moving around overlays. /atom/movable/lighting_object/forceMove(atom/destination, no_tp = FALSE, harderforce = FALSE) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index a0c39b80da3..6237548bfc3 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -84,7 +84,7 @@ has_opaque_atom = TRUE break -/turf/Exited(atom/movable/Obj, atom/newloc) +/turf/Exited(atom/movable/Obj, direction) . = ..() if(Obj && Obj.opacity) diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm index f9d9ad373c6..d4762b8bb93 100644 --- a/code/modules/mining/equipment/resonator.dm +++ b/code/modules/mining/equipment/resonator.dm @@ -93,7 +93,11 @@ if(mode == RESONATOR_MODE_MATRIX) icon_state = "shield2" name = "resonance matrix" - RegisterSignal(src, COMSIG_MOVABLE_CROSSED, PROC_REF(burst)) + RegisterSignal(src, COMSIG_ATOM_ENTERED, PROC_REF(burst)) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(burst), + ) + AddElement(/datum/element/connect_loc, loc_connections) . = ..() creator = set_creator parent_resonator = set_resonator @@ -126,7 +130,7 @@ resonance_damage *= damage_multiplier /obj/effect/temp_visual/resonance/proc/burst() - SIGNAL_HANDLER // COMSIG_MOVABLE_CROSSED + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED if(rupturing) return rupturing = TRUE diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 6c0b366586f..731285e775c 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -33,34 +33,6 @@ to_chat(user, "You smelt [src] into its refined form!") qdel(src) -/obj/item/stack/ore/Crossed(atom/movable/AM, oldloc) - var/obj/item/storage/bag/ore/OB - var/turf/simulated/floor/F = get_turf(src) - if(loc != F) - return ..() - if(ishuman(AM)) - var/mob/living/carbon/human/H = AM - for(var/thing in H.get_body_slots()) - if(istype(thing, /obj/item/storage/bag/ore)) - OB = thing - break - else if(isrobot(AM)) - var/mob/living/silicon/robot/R = AM - for(var/thing in R.get_all_slots()) - if(istype(thing, /obj/item/storage/bag/ore)) - OB = thing - break - if(OB && istype(F, /turf/simulated/floor/plating/asteroid)) - var/turf/simulated/floor/plating/asteroid/FA = F - FA.attempt_ore_pickup(OB, AM) - // Then, if the user is dragging an ore box, empty the satchel - // into the box. - var/mob/living/L = AM - if(istype(L.pulling, /obj/structure/ore_box)) - var/obj/structure/ore_box/box = L.pulling - box.attackby__legacy__attackchain(OB, AM) - return ..() - /obj/item/stack/ore/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) . = ..() if(isnull(refined_type)) diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm index 3d5aa45f685..88b454e0037 100644 --- a/code/modules/mining/satchel_ore_boxdm.dm +++ b/code/modules/mining/satchel_ore_boxdm.dm @@ -16,10 +16,11 @@ W.forceMove(src) else if(isstorage(W)) var/obj/item/storage/S = W - S.hide_from(usr) - for(var/obj/item/stack/ore/O in S.contents) - S.remove_from_storage(O, src) //This will move the item to this item's contents - to_chat(user, "You empty the satchel into the box.") + S.hide_from(user) + if(length(S.contents)) + for(var/obj/item/stack/ore/O in S.contents) + S.remove_from_storage(O, src) //This will move the item to this item's contents + to_chat(user, "You empty the satchel into the box.") else return ..() @@ -83,8 +84,8 @@ if(Adjacent(user)) . += "You can Alt-Shift-Click to empty the ore box." -/obj/structure/ore_box/onTransitZ() - return +/obj/structure/ore_box/on_changed_z_level(turf/old_turf, turf/new_turf, notify_contents = FALSE) + return ..() /obj/structure/ore_box/AltShiftClick(mob/user) if(!Adjacent(user) || !ishuman(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index 873b60555a9..3ea6c147136 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -16,14 +16,14 @@ var/turf/old_turf = get_turf(src) var/turf/new_turf = get_turf(destination) if(old_turf?.z != new_turf?.z) - onTransitZ(old_turf?.z, new_turf?.z) + on_changed_z_level(old_turf, new_turf) var/oldloc = loc loc = destination Moved(oldloc, direction) -/mob/dead/onTransitZ(old_z,new_z) +/mob/dead/on_changed_z_level(turf/old_turf, turf/new_turf) ..() - update_z(new_z) + update_z(new_turf?.z) /mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister if(registered_z != new_z) diff --git a/code/modules/mob/dead/observer/observer_base.dm b/code/modules/mob/dead/observer/observer_base.dm index 03267d00140..59159c638f8 100644 --- a/code/modules/mob/dead/observer/observer_base.dm +++ b/code/modules/mob/dead/observer/observer_base.dm @@ -167,7 +167,7 @@ GLOBAL_DATUM_INIT(ghost_crew_monitor, /datum/ui_module/crew_monitor/ghost, new) MA.plane = GAME_PLANE . = MA -/mob/dead/CanPass(atom/movable/mover, turf/target) +/mob/dead/CanPass(atom/movable/mover, border_dir) return 1 diff --git a/code/modules/mob/inventory_procs.dm b/code/modules/mob/inventory_procs.dm index 725f7fd2895..c82b9bc39f0 100644 --- a/code/modules/mob/inventory_procs.dm +++ b/code/modules/mob/inventory_procs.dm @@ -186,7 +186,11 @@ if(I) if(client) client.screen -= I - I.forceMove(drop_location()) + var/turf/drop_loc = drop_location() + if(drop_loc) + I.forceMove(drop_loc) + else + I.moveToNullspace() I.dropped(src, silent) if(I) I.layer = initial(I.layer) diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index 4e11337826a..5e0beba2723 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -23,10 +23,11 @@ ///Time it takes for a facehugger to become active again after going idle. var/min_active_time = 20 SECONDS var/max_active_time = 40 SECONDS + var/datum/proximity_monitor/proximity_monitor /obj/item/clothing/mask/facehugger/Initialize(mapload) . = ..() - AddComponent(/datum/component/proximity_monitor) + proximity_monitor = new(src) ADD_TRAIT(src, TRAIT_XENO_INTERACTABLE, UID()) /obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) @@ -67,10 +68,6 @@ /obj/item/clothing/mask/facehugger/equipped(mob/M) Attach(M) -/obj/item/clothing/mask/facehugger/Crossed(atom/target, oldloc) - HasProximity(target) - return - /obj/item/clothing/mask/facehugger/on_found(mob/finder) if(stat != DEAD) return HasProximity(finder) @@ -186,6 +183,7 @@ return stat = CONSCIOUS + proximity_monitor = new(src) icon_state = "[initial(icon_state)]" /obj/item/clothing/mask/facehugger/proc/GoIdle() @@ -194,6 +192,7 @@ stat = UNCONSCIOUS icon_state = "[initial(icon_state)]_inactive" + qdel(proximity_monitor) addtimer(CALLBACK(src, PROC_REF(GoActive)), rand(min_active_time, max_active_time)) /obj/item/clothing/mask/facehugger/proc/Die() @@ -203,7 +202,7 @@ icon_state = "[initial(icon_state)]_dead" item_state = "facehugger_inactive" stat = DEAD - DeleteComponent(/datum/component/proximity_monitor) + QDEL_NULL(proximity_monitor) visible_message("[src] curls up into a ball!") diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index fd05477e2d3..3fd7184bf15 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -71,10 +71,10 @@ return //Generic Bump(). Override MobBump() and ObjBump() instead of this. -/mob/living/Bump(atom/A, yes) +/mob/living/Bump(atom/A) if(..()) //we are thrown onto something return - if(buckled || !yes || now_pushing) + if(buckled || now_pushing) return if(ismob(A)) if(MobBump(A)) @@ -1056,9 +1056,9 @@ else registered_z = null -/mob/living/onTransitZ(old_z,new_z) +/mob/living/on_changed_z_level(turf/old_turf, turf/new_turf) ..() - update_z(new_z) + update_z(new_turf?.z) /mob/living/rad_act(amount) . = ..() @@ -1180,11 +1180,6 @@ for(var/obj/O in src) O.on_mob_move(Dir, src) -/mob/living/Crossed(atom/movable/mover) - if(istype(mover, /obj/singularity/energy_ball)) - dust() - return ..() - /// Can a mob interact with the apc remotely like a pulse demon, cyborg, or AI? /mob/living/proc/can_remote_apc_interface(obj/machinery/power/apc/ourapc) return FALSE diff --git a/code/modules/mob/living/silicon/robot/drone/maint_drone.dm b/code/modules/mob/living/silicon/robot/drone/maint_drone.dm index 1e5b4e52b88..a300fa2bac9 100644 --- a/code/modules/mob/living/silicon/robot/drone/maint_drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/maint_drone.dm @@ -359,7 +359,7 @@ */ -/mob/living/silicon/robot/drone/Bump(atom/movable/AM, yes) +/mob/living/silicon/robot/drone/Bump(atom/movable/AM) if(is_type_in_list(AM, allowed_bumpable_objects)) return ..() diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm index e2e0296b7f8..7f2ce26fd65 100644 --- a/code/modules/mob/living/simple_animal/bot/griefsky.dm +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -48,10 +48,9 @@ ..() light_color = LIGHT_COLOR_PURE_RED //if you see a red one. RUN!! -/mob/living/simple_animal/bot/secbot/griefsky/Crossed(atom/movable/AM, oldloc) - ..() - if(ismob(AM) && AM == target) - var/mob/living/carbon/C = AM +/mob/living/simple_animal/bot/secbot/griefsky/on_atom_entered(datum/source, atom/movable/entered) + if(iscarbon(entered) && entered == target) + var/mob/living/carbon/C = entered visible_message("[src] flails his swords and pushes [C] out of it's way!" ) C.KnockDown(4 SECONDS) diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 20652fdb801..9c243922bed 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -36,6 +36,10 @@ var/datum/job/clown/J = new /datum/job/clown() access_card.access += J.get_access() prev_access = access_card.access + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /mob/living/simple_animal/bot/honkbot/proc/sensor_blink() icon_state = "honkbot-c" @@ -373,10 +377,10 @@ target = user mode = BOT_HUNT -/mob/living/simple_animal/bot/honkbot/Crossed(atom/movable/AM, oldloc) - if(ismob(AM) && on) //only if its online +/mob/living/simple_animal/bot/honkbot/proc/on_atom_entered(datum/source, atom/movable/entered) + if(ismob(entered) && on) //only if its online if(prob(30)) //you're far more likely to trip on a honkbot - var/mob/living/carbon/C = AM + var/mob/living/carbon/C = entered if(!istype(C) || !C || in_range(src, target)) return C.visible_message("[pick( \ @@ -391,5 +395,3 @@ if(!client) speak("Honk!") sensor_blink() - return - ..() diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index ad9eca0a778..48ef878ac71 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -74,7 +74,7 @@ mulebot_count++ set_suffix(suffix ? suffix : "#[mulebot_count]") - RegisterSignal(src, COMSIG_CROSSED_MOVABLE, PROC_REF(human_squish_check)) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(human_squish_check)) /mob/living/simple_animal/bot/mulebot/Destroy() SStgui.close_uis(wires) @@ -879,10 +879,13 @@ else ..() -/mob/living/simple_animal/bot/mulebot/proc/human_squish_check(src, atom/movable/AM) - if(!ishuman(AM)) +/mob/living/simple_animal/bot/mulebot/proc/human_squish_check(datum/source, old_location, direction, forced) + if(!isturf(loc)) return - RunOver(AM) + for(var/atom/AM in loc) + if(!ishuman(AM)) + continue + RunOver(AM) #undef SIGH #undef ANNOYED diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index 510874a95a5..c8909ba151e 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -50,6 +50,10 @@ var/datum/job/detective/J = new/datum/job/detective access_card.access += J.get_access() prev_access = access_card.access + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /mob/living/simple_animal/bot/secbot/Destroy() QDEL_NULL(baton) @@ -458,9 +462,9 @@ target = user mode = BOT_HUNT -/mob/living/simple_animal/bot/secbot/Crossed(atom/movable/AM, oldloc) - if(ismob(AM) && target) - var/mob/living/carbon/C = AM +/mob/living/simple_animal/bot/secbot/proc/on_atom_entered(datum/source, atom/movable/entered) + if(ismob(entered) && target) + var/mob/living/carbon/C = entered if(!istype(C) || !C || in_range(src, target)) return C.visible_message("[pick( \ @@ -472,7 +476,6 @@ "[C] leaps out of [src]'s way!")]") C.KnockDown(4 SECONDS) return - ..() #undef BATON_COOLDOWN #undef BOT_REBATON_THRESHOLD diff --git a/code/modules/mob/living/simple_animal/friendly/cockroach.dm b/code/modules/mob/living/simple_animal/friendly/cockroach.dm index 6f5caaf3a31..a0415460190 100644 --- a/code/modules/mob/living/simple_animal/friendly/cockroach.dm +++ b/code/modules/mob/living/simple_animal/friendly/cockroach.dm @@ -28,18 +28,22 @@ /mob/living/simple_animal/cockroach/Initialize(mapload) //Lizards are a great way to deal with cockroaches . = ..() ADD_TRAIT(src, TRAIT_EDIBLE_BUG, "edible_bug") + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) -/mob/living/simple_animal/cockroach/Crossed(atom/movable/AM, oldloc) - if(isliving(AM)) - var/mob/living/A = AM +/mob/living/simple_animal/cockroach/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isliving(entered)) + var/mob/living/A = entered if(A.mob_size > MOB_SIZE_SMALL) if(prob(squish_chance)) A.visible_message("\The [A] squashed \the [name].", "You squashed \the [name].") death() else visible_message("\The [name] avoids getting crushed.") - else if(isstructure(AM)) - visible_message("As \the [AM] moved over \the [name], it was crushed.") + else if(isstructure(entered)) + visible_message("As \the [entered] moved over \the [name], it was crushed.") death() /mob/living/simple_animal/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach. diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 2643abad6da..39e99b7720e 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -85,6 +85,10 @@ icon_dead = "mouse_[mouse_color]_dead" icon_resting = "mouse_[mouse_color]_sleep" update_appearance(UPDATE_ICON_STATE|UPDATE_DESC) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /mob/living/simple_animal/mouse/update_desc() . = ..() @@ -102,12 +106,11 @@ to_chat(src, "You are too small to pull anything except cheese.") return -/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) - if(ishuman(AM)) +/mob/living/simple_animal/mouse/proc/on_atom_entered(datum/source, atom/movable/entered) + if(ishuman(entered)) if(stat == CONSCIOUS) - var/mob/M = AM + var/mob/M = entered to_chat(M, "[bicon(src)] Squeek!") - ..() /mob/living/simple_animal/mouse/proc/toast() add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) diff --git a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm index 6f6144d44fa..4a98a04f254 100644 --- a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm +++ b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm @@ -69,7 +69,7 @@ playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1) -/mob/living/simple_animal/hostile/floor_cluwne/CanPass(atom/A, turf/target) +/mob/living/simple_animal/hostile/floor_cluwne/CanPass(atom/A, border_dir) return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm index 9dbac40967d..5b4d0624eb4 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm @@ -307,9 +307,9 @@ Difficulty: Hard return return ..() -/mob/living/simple_animal/hostile/megafauna/ancient_robot/Bump(atom/A, yes) +/mob/living/simple_animal/hostile/megafauna/ancient_robot/Bump(atom/A) if(charging) - if(isliving(A) && yes) + if(isliving(A)) var/mob/living/L = A if(!istype(A, /mob/living/simple_animal/hostile/ancient_robot_leg)) L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!") @@ -695,9 +695,9 @@ Difficulty: Hard /mob/living/simple_animal/hostile/ancient_robot_leg/proc/beam_setup() leg_part = Beam(core.beam, "leg_connection", 'icons/effects/effects.dmi', time=INFINITY, maxdistance=INFINITY, beam_type=/obj/effect/ebeam) -/mob/living/simple_animal/hostile/ancient_robot_leg/onTransitZ(old_z,new_z) +/mob/living/simple_animal/hostile/ancient_robot_leg/on_changed_z_level(turf/old_turf, turf/new_turf) ..() - update_z(new_z) + update_z(new_turf?.z) if(leg_part) QDEL_NULL(leg_part) addtimer(CALLBACK(src, PROC_REF(beam_setup)), 1 SECONDS) @@ -730,10 +730,10 @@ Difficulty: Hard walk_towards(src, T, movespeed) DestroySurroundings() -/mob/living/simple_animal/hostile/ancient_robot_leg/Bump(atom/A, yes) +/mob/living/simple_animal/hostile/ancient_robot_leg/Bump(atom/A) if(!core.charging) return - if(isliving(A) && yes) + if(isliving(A)) if(!istype(A, /mob/living/simple_animal/hostile/megafauna/ancient_robot)) var/mob/living/L = A L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!") @@ -820,7 +820,7 @@ Difficulty: Hard tesla_zap(src, zap_range, power, zap_flags) qdel(src) -/obj/item/projectile/energy/tesla_bolt/Bump(atom/A, yes) // Don't want the projectile hitting the legs +/obj/item/projectile/energy/tesla_bolt/Bump(atom/A) // Don't want the projectile hitting the legs if(!istype(/mob/living/simple_animal/hostile/ancient_robot_leg, A)) return ..() var/turf/target_turf = get_turf(A) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 323508edfe1..7bd2ee98227 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -513,7 +513,7 @@ Difficulty: Hard severity = EXPLODE_LIGHT // puny mortals return ..() -/mob/living/simple_animal/hostile/megafauna/bubblegum/CanPass(atom/movable/mover, turf/target) +/mob/living/simple_animal/hostile/megafauna/bubblegum/CanPass(atom/movable/mover, border_dir) if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination)) return TRUE return ..() @@ -541,8 +541,8 @@ Difficulty: Hard playsound(src, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE) return ..() -/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A, yes) - if(charging && yes) +/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A) + if(charging) if(isturf(A) || isobj(A) && A.density) A.ex_act(EXPLODE_HEAVY) if(isliving(A)) @@ -610,7 +610,7 @@ Difficulty: Hard new /obj/effect/decal/cleanable/blood(get_turf(src)) . = ..() -/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/CanPass(atom/movable/mover, turf/target) +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/CanPass(atom/movable/mover, border_dir) if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) // hallucinations should not be stopping bubblegum or eachother return TRUE return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 59d2e4b3e86..b290d2a4ef9 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -600,7 +600,7 @@ Difficulty: Hard QUEUE_SMOOTH_NEIGHBORS(src) return ..() -/obj/effect/temp_visual/hierophant/wall/CanPass(atom/movable/mover, turf/target) +/obj/effect/temp_visual/hierophant/wall/CanPass(atom/movable/mover, border_dir) if(QDELETED(caster)) return FALSE if(mover == caster.pulledby) @@ -721,6 +721,10 @@ Difficulty: Hard var/turf/simulated/mineral/M = loc M.gets_drilled(caster) INVOKE_ASYNC(src, PROC_REF(blast)) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) /obj/effect/temp_visual/hierophant/blast/proc/blast() var/turf/T = get_turf(src) @@ -733,8 +737,7 @@ Difficulty: Hard sleep(1.3) //slightly forgiving; the burst animation is 1.5 deciseconds bursting = FALSE //we no longer damage crossers -/obj/effect/temp_visual/hierophant/blast/Crossed(atom/movable/AM) - ..() +/obj/effect/temp_visual/hierophant/blast/proc/on_atom_entered(datum/source, atom/movable/entered) if(bursting) do_damage(get_turf(src)) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index b01e6a89567..ab151efa92d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -106,7 +106,7 @@ else devour(L) -/mob/living/simple_animal/hostile/megafauna/onTransitZ(old_z, new_z) +/mob/living/simple_animal/hostile/megafauna/on_changed_z_level(turf/old_turf, turf/new_turf) . = ..() if(!istype(get_area(src), /area/shuttle)) //I'll be funny and make non teleported enrage mobs not lose enrage. Harder to pull off, and also funny when it happens accidently. Or if one gets on the escape shuttle. unrage() diff --git a/code/modules/mob/living/simple_animal/hostile/mining/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining/elites/elite.dm index 229d8c8e0a6..5c9219a88f9 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/elites/elite.dm @@ -179,6 +179,7 @@ While using this makes the system rely on OnFire, it still gives options for tim ///List of invaders that have teleportes into the arena *multiple times*. They will be suffering. var/list/invaders = list() + var/datum/proximity_monitor/proximity_monitor /obj/structure/elite_tumor/attack_hand(mob/user, list/modifiers) . = ..() @@ -241,7 +242,7 @@ While using this makes the system rely on OnFire, it still gives options for tim icon_state = "tumor_popped" RegisterSignal(mychild, COMSIG_PARENT_QDELETING, PROC_REF(onEliteLoss)) INVOKE_ASYNC(src, PROC_REF(arena_checks)) - AddComponent(/datum/component/proximity_monitor, ARENA_RADIUS) //Boots out humanoid invaders. Minebots / random fauna / that colossus you forgot to clear away allowed. + proximity_monitor = new(src, ARENA_RADIUS) //Boots out humanoid invaders. Minebots / random fauna / that colossus you forgot to clear away allowed. /obj/structure/elite_tumor/proc/return_elite() mychild.forceMove(loc) @@ -254,7 +255,7 @@ While using this makes the system rely on OnFire, it still gives options for tim mychild.grab_ghost() notify_ghosts("\A [mychild] has been challenged in \the [get_area(src)]!", enter_link="(Click to help)", source = mychild, action = NOTIFY_FOLLOW) INVOKE_ASYNC(src, PROC_REF(arena_checks)) - AddComponent(/datum/component/proximity_monitor, ARENA_RADIUS) + proximity_monitor = new(src, ARENA_RADIUS) /obj/structure/elite_tumor/Initialize(mapload) . = ..() @@ -413,7 +414,7 @@ While using this makes the system rely on OnFire, it still gives options for tim text += "If teleported to the Station by jaunter, you are allowed to attack people on Station, until you get killed." to_chat(mychild, text.Join(" ")) - DeleteComponent(/datum/component/proximity_monitor) + QDEL_NULL(proximity_monitor) /obj/item/tumor_shard name = "tumor shard" diff --git a/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm index 037ab6c2551..ab73704d812 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm @@ -292,12 +292,19 @@ light_color = LIGHT_COLOR_RED var/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/myowner = null -/obj/structure/legionnaire_bonfire/Crossed(datum/source, atom/movable/mover) - if(isobj(source)) - var/obj/object = source +/obj/structure/legionnaire_bonfire/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/structure/legionnaire_bonfire/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isobj(entered)) + var/obj/object = entered object.fire_act(1000, 500) - if(isliving(source)) - var/mob/living/fire_walker = source + if(isliving(entered)) + var/mob/living/fire_walker = entered fire_walker.adjust_fire_stacks(5) fire_walker.IgniteMob() diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm b/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm index 92779de451a..bef98567bab 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm @@ -237,7 +237,7 @@ new /obj/effect/gibspawner/human(get_turf(src)) return ..() -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target) +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, border_dir) if(isliving(mover)) var/mob/living/blocker = mover if(faction_check_mob(blocker)) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm index 08a695f79d1..9f0bf777ef6 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm @@ -170,8 +170,12 @@ . = ..() if(prob(50)) icon_state = "stickyweb2" + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) -/obj/structure/spider/terrorweb/CanPass(atom/movable/mover, turf/target) +/obj/structure/spider/terrorweb/CanPass(atom/movable/mover, border_dir) if(isterrorspider(mover)) return TRUE if(istype(mover, /obj/item/projectile/terrorqueenspit)) @@ -185,10 +189,9 @@ return prob(20) return ..() -/obj/structure/spider/terrorweb/Crossed(atom/movable/AM, oldloc) - ..() - if(isliving(AM) && !isterrorspider(AM)) - var/mob/living/M = AM +/obj/structure/spider/terrorweb/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isliving(entered) && !isterrorspider(entered)) + var/mob/living/M = entered to_chat(M, "You get stuck in [src] for a moment.") M.Weaken(8 SECONDS) if(iscarbon(M)) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm index d26e9d975db..358894501a7 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm @@ -139,7 +139,7 @@ name = "mother web" desc = "This web is coated in pheromones which prevent spiderlings from passing it." -/obj/structure/spider/terrorweb/mother/CanPass(atom/movable/mover, turf/target) +/obj/structure/spider/terrorweb/mother/CanPass(atom/movable/mover, border_dir) if(istype(mover, /obj/structure/spider/spiderling/terror_spiderling)) return FALSE return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm index 84ee41256fc..2d79c322411 100644 --- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm +++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm @@ -37,11 +37,10 @@ mouse_opacity = MOUSE_OPACITY_ICON desc = "A thick vine, painful to the touch." - -/obj/effect/ebeam/vine/Crossed(atom/movable/AM, oldloc) - if(!isliving(AM)) +/obj/effect/ebeam/vine/on_atom_entered(datum/source, atom/movable/entered) + if(!isliving(entered)) return - var/mob/living/L = AM + var/mob/living/L = entered if(!("vines" in L.faction)) L.adjustBruteLoss(5) to_chat(L, "You cut yourself on the thorny vines.") diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 88ddc6189a2..daec7a7b43e 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -583,10 +583,10 @@ if(pulledby || shouldwakeup) toggle_ai(AI_ON) -/mob/living/simple_animal/onTransitZ(old_z, new_z) +/mob/living/simple_animal/on_changed_z_level(turf/old_turf, turf/new_turf) ..() - if(AIStatus == AI_Z_OFF) - var/list/idle_mobs_on_old_z = LAZYACCESS(SSidlenpcpool.idle_mobs_by_zlevel, old_z) + if(AIStatus == AI_Z_OFF && old_turf) + var/list/idle_mobs_on_old_z = LAZYACCESS(SSidlenpcpool.idle_mobs_by_zlevel, old_turf.z) LAZYREMOVE(idle_mobs_on_old_z, src) toggle_ai(initial(AIStatus)) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index dedebe6f549..6f1cd194d7f 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -1,4 +1,4 @@ -/mob/CanPass(atom/movable/mover, turf/target) +/mob/CanPass(atom/movable/mover, border_dir) var/horizontal = FALSE if(isliving(src)) var/mob/living/L = src diff --git a/code/modules/power/engines/singularity/containment_field.dm b/code/modules/power/engines/singularity/containment_field.dm index 0953ae2a454..cfec3f37477 100644 --- a/code/modules/power/engines/singularity/containment_field.dm +++ b/code/modules/power/engines/singularity/containment_field.dm @@ -14,6 +14,13 @@ var/obj/machinery/field/generator/FG1 = null var/obj/machinery/field/generator/FG2 = null +/obj/machinery/field/containment/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/machinery/field/containment/Destroy() if(FG1)// These checks are mostly in case a field is spawned in by accident. FG1.fields -= src @@ -57,12 +64,12 @@ else ..() -/obj/machinery/field/containment/Crossed(mob/mover, oldloc) - if(isliving(mover)) - shock_field(mover) +/obj/machinery/field/containment/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isliving(entered)) + shock_field(entered) - if(ismachinery(mover) || isstructure(mover) || ismecha(mover)) - bump_field(mover) + if(ismachinery(entered) || isstructure(entered) || ismecha(entered)) + bump_field(entered) /obj/machinery/field/containment/proc/set_master(master1, master2) if(!master1 || !master2) @@ -86,7 +93,7 @@ /obj/machinery/field var/hasShocked = 0 //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. -/obj/machinery/field/CanPass(atom/movable/mover, turf/target) +/obj/machinery/field/CanPass(atom/movable/mover, border_dir) if(hasShocked) return 0 if(isliving(mover)) // Don't let mobs through diff --git a/code/modules/power/engines/singularity/particle_accelerator/particle.dm b/code/modules/power/engines/singularity/particle_accelerator/particle.dm index 12641cefbd8..a2bae7b95c3 100644 --- a/code/modules/power/engines/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/engines/singularity/particle_accelerator/particle.dm @@ -24,10 +24,18 @@ /obj/effect/accelerated_particle/Initialize(mapload) . = ..() addtimer(CALLBACK(src, PROC_REF(propagate)), 1) - RegisterSignal(src, COMSIG_CROSSED_MOVABLE, PROC_REF(try_irradiate)) - RegisterSignal(src, COMSIG_MOVABLE_CROSSED, PROC_REF(try_irradiate)) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(try_irradiate) + ) + AddElement(/datum/element/connect_loc, loc_connections) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_movable_moved)) QDEL_IN(src, movement_range) +/obj/effect/accelerated_particle/proc/on_movable_moved(datum/source, old_location, direction, forced) + if(isturf(loc)) + for(var/atom/A in loc) + try_irradiate(src, A) + /obj/effect/accelerated_particle/proc/try_irradiate(src, atom/A) if(isliving(A)) var/mob/living/L = A diff --git a/code/modules/power/engines/singularity/singularity.dm b/code/modules/power/engines/singularity/singularity.dm index 32d9683db27..1d51905da0a 100644 --- a/code/modules/power/engines/singularity/singularity.dm +++ b/code/modules/power/engines/singularity/singularity.dm @@ -38,6 +38,8 @@ GLOBAL_VAR_INIT(global_singulo_id, 1) var/isnt_shutting_down = FALSE /// Init list that has all the areas that we can possibly move to, to reduce processing impact var/list/all_possible_areas = list() + var/datum/proximity_monitor/advanced/singulo/proximity_monitor + /// Id for monitoring. var/singulo_id = 1 @@ -49,7 +51,7 @@ GLOBAL_VAR_INIT(global_singulo_id, 1) energy = starting_energy if(warps_projectiles) - AddComponent(/datum/component/proximity_monitor/singulo, _radius = 10) + proximity_monitor = new(src, 10) START_PROCESSING(SSobj, src) GLOB.poi_list |= src @@ -526,50 +528,6 @@ GLOBAL_VAR_INIT(global_singulo_id, 1) if(prob(1)) mezzer() -/datum/component/proximity_monitor/singulo - field_checker_type = /obj/effect/abstract/proximity_checker/singulo - -/datum/component/proximity_monitor/singulo/create_single_prox_checker(turf/T, checker_type) - . = ..() - var/obj/effect/abstract/proximity_checker/singulo/S = . - S.calibrate() - -/datum/component/proximity_monitor/singulo/recenter_prox_checkers() - . = ..() - for(var/obj/effect/abstract/proximity_checker/singulo/S as anything in proximity_checkers) - S.calibrate() - -/obj/effect/abstract/proximity_checker/singulo - var/angle_to_singulo - var/distance_to_singulo - -/obj/effect/abstract/proximity_checker/singulo/proc/calibrate() - angle_to_singulo = ATAN2(monitor.hasprox_receiver.y - y, monitor.hasprox_receiver.x - x) - distance_to_singulo = get_dist(monitor.hasprox_receiver, src) - -/obj/effect/abstract/proximity_checker/singulo/Crossed(atom/movable/AM, oldloc) - . = ..() - if(!isprojectile(AM)) - return - var/obj/item/projectile/P = AM - var/distance = distance_to_singulo - var/projectile_angle = P.Angle - var/angle_to_projectile = angle_to_singulo - if(angle_to_projectile == 180) - angle_to_projectile = -180 - angle_to_projectile -= projectile_angle - if(angle_to_projectile > 180) - angle_to_projectile -= 360 - else if(angle_to_projectile < -180) - angle_to_projectile += 360 - - if(distance == 0) - qdel(P) - return - projectile_angle += angle_to_projectile / (distance ** 2) - P.damage += 10 / distance - P.set_angle(projectile_angle) - /obj/singularity/proc/end_deadchat_plays() move_self = TRUE diff --git a/code/modules/power/engines/supermatter/supermatter.dm b/code/modules/power/engines/supermatter/supermatter.dm index dabeeb3f3b3..0199e76b6d0 100644 --- a/code/modules/power/engines/supermatter/supermatter.dm +++ b/code/modules/power/engines/supermatter/supermatter.dm @@ -848,7 +848,7 @@ playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE) Consume(AM) -/obj/machinery/atmospherics/supermatter_crystal/Bump(atom/A, yes) +/obj/machinery/atmospherics/supermatter_crystal/Bump(atom/A) ..() if(!istype(A, /obj/machinery/atmospherics/supermatter_crystal)) Bumped(A) diff --git a/code/modules/power/engines/tesla/energy_ball.dm b/code/modules/power/engines/tesla/energy_ball.dm index eef6897ee91..b7a012e9c8a 100644 --- a/code/modules/power/engines/tesla/energy_ball.dm +++ b/code/modules/power/engines/tesla/energy_ball.dm @@ -47,6 +47,10 @@ RegisterSignal(src, COMSIG_ATOM_ORBIT_BEGIN, PROC_REF(on_start_orbit)) RegisterSignal(src, COMSIG_ATOM_ORBIT_STOP, PROC_REF(on_stop_orbit)) RegisterSignal(parent_energy_ball, COMSIG_PARENT_QDELETING, PROC_REF(on_parent_delete)) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) . = ..() if(!is_miniball) set_light(10, 7, "#5e5edd") @@ -135,6 +139,11 @@ sleep(0.5 SECONDS) walk_towards(src, move_target, 0, 10) +/obj/singularity/energy_ball/proc/on_atom_entered(datum/source, atom/movable/entered) + var/mob/living/living_entered = entered + if(istype(living_entered)) + living_entered.dust() + /datum/move_with_corner var/turf/start var/turf/end @@ -210,13 +219,6 @@ forceMove(target, direction) return TRUE -// This handles mobs crossing us. For us crossing mobs, see /mob/living/Crossed. -// (It also dusts them.) -/obj/singularity/energy_ball/Crossed(atom/thing) - if(isliving(thing)) - var/mob/victim = thing - victim.dust() - /obj/singularity/energy_ball/proc/handle_energy() if(energy >= energy_to_raise) energy_to_lower = energy_to_raise - 20 diff --git a/code/modules/power/generators/treadmill.dm b/code/modules/power/generators/treadmill.dm index 6a6f68b8b5b..f57b6ee3b4f 100644 --- a/code/modules/power/generators/treadmill.dm +++ b/code/modules/power/generators/treadmill.dm @@ -16,26 +16,40 @@ var/list/mobs_running[0] var/id = null // for linking to monitor + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + COMSIG_ATOM_EXITED = PROC_REF(on_atom_exited), + ) + /obj/machinery/power/treadmill/Initialize(mapload) . = ..() + on_anchor_changed() + +/obj/machinery/power/treadmill/proc/on_anchor_changed() if(anchored) connect_to_network() + AddElement(/datum/element/connect_loc, loc_connections) + else + disconnect_from_network() + RemoveElement(/datum/element/connect_loc) /obj/machinery/power/treadmill/update_icon_state() icon_state = speed ? "conveyor-1" : "conveyor0" -/obj/machinery/power/treadmill/Crossed(mob/living/M, oldloc) - if(anchored && !M.anchored) - if(!istype(M) || M.dir != dir) - throw_off(M) - else - mobs_running[M] = M.last_movement - . = ..() +/obj/machinery/power/treadmill/proc/on_atom_entered(datum/source, mob/living/crossed) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + if(crossed.anchored || crossed.throwing) + return -/obj/machinery/power/treadmill/Uncrossed(mob/living/M) - if(anchored && istype(M)) - mobs_running -= M - . = ..() + if(!istype(crossed) || crossed.dir != dir) + throw_off(crossed) + else + mobs_running[crossed] = crossed.last_movement + +/obj/machinery/power/treadmill/proc/on_atom_exited(mob/living/crossed) + SIGNAL_HANDLER // COMSIG_ATOM_EXITED + if(istype(crossed)) + mobs_running -= crossed /obj/machinery/power/treadmill/proc/throw_off(atom/movable/A) // if 2fast, throw the person, otherwise they just slide off, if there's reasonable speed at all @@ -98,10 +112,7 @@ /obj/machinery/power/treadmill/attackby__legacy__attackchain(obj/item/W, mob/user) if(default_unfasten_wrench(user, W, time = 60)) - if(anchored) - connect_to_network() - else - disconnect_from_network() + on_anchor_changed() speed = 0 update_icon() return diff --git a/code/modules/power/lights.dm b/code/modules/power/lights.dm index eae1c664e1c..00664ab2834 100644 --- a/code/modules/power/lights.dm +++ b/code/modules/power/lights.dm @@ -999,15 +999,19 @@ /obj/item/light/Initialize(mapload) . = ..() AddComponent(/datum/component/caltrop, force) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) -/obj/item/light/Crossed(mob/living/L) - if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || HAS_TRAIT(L, TRAIT_FLYING) || L.floating) +/obj/item/light/proc/on_atom_entered(datum/source, atom/movable/entered) + var/mob/living/living_entered = entered + if(istype(living_entered) && has_gravity(loc)) + if(living_entered.incorporeal_move || HAS_TRAIT(living_entered, TRAIT_FLYING) || living_entered.floating) return playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) if(status == LIGHT_BURNED || status == LIGHT_OK) shatter() - return ..() /obj/item/light/decompile_act(obj/item/matter_decompiler/C, mob/user) C.stored_comms["glass"] += 1 diff --git a/code/modules/projectiles/projectile/special_projectiles.dm b/code/modules/projectiles/projectile/special_projectiles.dm index ec72d05df0b..4e2aa0d6887 100644 --- a/code/modules/projectiles/projectile/special_projectiles.dm +++ b/code/modules/projectiles/projectile/special_projectiles.dm @@ -111,9 +111,7 @@ nodamage = 1 flag = "bullet" -/obj/item/projectile/meteor/Bump(atom/A, yes) - if(yes) - return +/obj/item/projectile/meteor/Bump(atom/A) if(A == firer) loc = A.loc return diff --git a/code/modules/projectiles/projectile_base.dm b/code/modules/projectiles/projectile_base.dm index 0130b85e8ab..8d8603f0dcc 100644 --- a/code/modules/projectiles/projectile_base.dm +++ b/code/modules/projectiles/projectile_base.dm @@ -138,6 +138,13 @@ /obj/item/projectile/New() return ..() +/obj/item/projectile/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/item/projectile/proc/Range() range-- if(damage && tile_dropoff) @@ -262,10 +269,7 @@ beam_index = point_cache beam_segments[beam_index] = null -/obj/item/projectile/Bump(atom/A, yes) - if(!yes) //prevents double bumps. - return - +/obj/item/projectile/Bump(atom/A) if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max && is_reflectable(REFLECTABILITY_PHYSICAL)) if(hitscan && ricochets_max > 10) ricochets_max = 10 //I do not want a chucklefuck editing this higher, sorry. @@ -432,10 +436,10 @@ xo = new_x - curloc.x set_angle(get_angle(curloc, original)) -/obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. - ..() - if(isliving(AM) && AM.density && !checkpass(PASSMOB)) - Bump(AM, 1) +/// A mob moving on a tile with a projectile is hit by it. +/obj/item/projectile/proc/on_atom_entered(datum/source, atom/movable/entered) + if(isliving(entered) && entered.density && !checkpass(PASSMOB)) + Bump(entered, 1) /obj/item/projectile/Destroy() if(hitscan) diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index 496f385e4db..1484b49792e 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -129,6 +129,13 @@ var/obj/item/assembly_holder/assembly = null var/can_assembly = 1 +/obj/item/reagent_containers/glass/beaker/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/item/reagent_containers/glass/beaker/examine(mob/user) . = ..() if(assembly) @@ -187,9 +194,9 @@ if(assembly) assembly.HasProximity(AM) -/obj/item/reagent_containers/glass/beaker/Crossed(atom/movable/AM, oldloc) +/obj/item/reagent_containers/glass/beaker/proc/on_atom_entered(datum/source, atom/movable/entered) if(assembly) - assembly.Crossed(AM, oldloc) + assembly.on_atom_entered(source, entered) /obj/item/reagent_containers/glass/beaker/on_found(mob/finder) //for mousetraps if(assembly) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index dc79be44db9..41e0f9979f7 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -22,6 +22,11 @@ mode = SYRINGE_INJECT update_icon() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/item/reagent_containers/syringe/on_reagent_change() update_icon() @@ -181,7 +186,10 @@ M.update_inv_l_hand() M.update_inv_r_hand() -/obj/item/reagent_containers/syringe/Crossed(mob/living/carbon/human/H, oldloc) +/obj/item/reagent_containers/syringe/proc/on_atom_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + + var/mob/living/carbon/human/H = entered if(!istype(H) || !H.reagents || HAS_TRAIT(H, TRAIT_PIERCEIMMUNE) || ismachineperson(H)) return diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 67f4a1ae1be..55dfca88312 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -104,6 +104,13 @@ var/obj/item/assembly_holder/rig = null var/accepts_rig = 1 +/obj/structure/reagent_dispensers/fueltank/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/structure/reagent_dispensers/fueltank/Destroy() QDEL_NULL(rig) return ..() @@ -209,9 +216,9 @@ if(rig) rig.HasProximity(AM) -/obj/structure/reagent_dispensers/fueltank/Crossed(atom/movable/AM, oldloc) +/obj/structure/reagent_dispensers/fueltank/proc/on_atom_entered(datum/source, atom/movable/entered) if(rig) - rig.Crossed(AM, oldloc) + rig.on_atom_entered(source, entered) /obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, list/message_pieces) if(rig) diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index 61f1f912f08..03755059d7c 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -43,6 +43,11 @@ GLOBAL_LIST_EMPTY(conveyor_switches) var/obj/machinery/conveyor_switch/S = I S.link_conveyers(src) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/machinery/conveyor/Destroy() GLOB.conveyor_belts -= src return ..() @@ -199,10 +204,9 @@ GLOBAL_LIST_EMPTY(conveyor_switches) else if(still_stuff_to_move && !speed_process) makeSpeedProcess() -/obj/machinery/conveyor/Crossed(atom/movable/AM, oldloc) - if(!speed_process && !AM.anchored) +/obj/machinery/conveyor/proc/on_atom_entered(datum/source, atom/movable/entered) + if(!speed_process && !entered.anchored) makeSpeedProcess() - ..() /obj/machinery/conveyor/proc/move_thing(atom/movable/AM) affecting.Remove(AM) diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 6c478825d6b..0ea0f0e2f01 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -613,7 +613,7 @@ H.vent_gas(loc) qdel(H) -/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target) +/obj/machinery/disposal/CanPass(atom/movable/mover, border_dir) if(isitem(mover) && mover.throwing) var/obj/item/I = mover if(isprojectile(I)) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index d1fa9d14eb5..2ac19610aab 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -294,7 +294,7 @@ /obj/machinery/disposal/delivery_chute/update() return -/obj/machinery/disposal/delivery_chute/CanPass(atom/movable/mover, turf/target) +/obj/machinery/disposal/delivery_chute/CanPass(atom/movable/mover, border_dir) // If the mover is a thrownthing passing through space, remove its thrown datum, // ingest it like normal, and mark the chute as not passible. // This prevents the mover from Entering the chute's turf diff --git a/code/modules/ruins/lavalandruin_code/sin_ruins.dm b/code/modules/ruins/lavalandruin_code/sin_ruins.dm index b6281e66fa3..cfa4c18f3c8 100644 --- a/code/modules/ruins/lavalandruin_code/sin_ruins.dm +++ b/code/modules/ruins/lavalandruin_code/sin_ruins.dm @@ -140,7 +140,7 @@ icon = 'icons/mob/blob.dmi' color = rgb(145, 150, 0) -/obj/effect/gluttony/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. +/obj/effect/gluttony/CanPass(atom/movable/mover, border_dir)//So bullets will fly over and stuff. if(ishuman(mover)) var/mob/living/carbon/human/H = mover if(H.nutrition >= NUTRITION_LEVEL_FAT || HAS_TRAIT(H, TRAIT_FAT)) diff --git a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm index c0058b3082e..af173186dcc 100644 --- a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm +++ b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm @@ -48,6 +48,12 @@ dais_overlay.layer = CLOSED_TURF_LAYER add_overlay(dais_overlay) + var/static/list/loc_connections = list( + COMSIG_ATOM_EXIT = PROC_REF(on_atom_exit), + ) + + AddElement(/datum/element/connect_loc, loc_connections) + /obj/structure/necropolis_gate/Destroy() qdel(sight_blocker, TRUE) return ..() @@ -55,15 +61,16 @@ /obj/structure/necropolis_gate/singularity_pull() return -/obj/structure/necropolis_gate/CanPass(atom/movable/mover, turf/target) - if(get_dir(loc, target) == dir) +/obj/structure/necropolis_gate/CanPass(atom/movable/mover, border_dir) + if(border_dir == dir) return !density return TRUE -/obj/structure/necropolis_gate/CheckExit(atom/movable/O, target) - if(get_dir(O.loc, target) == dir) - return !density - return TRUE +/obj/structure/necropolis_gate/proc/on_atom_exit(datum/source, atom/movable/leaving, direction) + SIGNAL_HANDLER // COMSIG_ATOM_EXIT + + if(direction == dir && density) + return COMPONENT_ATOM_BLOCK_EXIT /obj/structure/opacity_blocker icon = 'icons/effects/96x96.dmi' diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index e174827f4ef..9fea2a3a4a1 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -2,7 +2,7 @@ /atom/movable/proc/onShuttleMove(turf/oldT, turf/T1, rotation, mob/caller) var/turf/newT = get_turf(src) if(newT.z != oldT.z) - onTransitZ(oldT.z, newT.z) + on_changed_z_level(oldT, newT) if(light) update_light() if(rotation) diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm index 3cbb358b23a..4d2b13883ba 100644 --- a/code/modules/surgery/organs/organ.dm +++ b/code/modules/surgery/organs/organ.dm @@ -262,7 +262,14 @@ var/obj/item/organ/external/affected = owner.get_organ(parent_organ) if(affected) affected.internal_organs -= src - forceMove(get_turf(owner)) + // In-game, organs will be moved to their parent turf. + // During ghost-mob creation, we toss the organs + // after we're done generating the sprite with them, + // so to nullspace they go. + if(get_turf(owner)) + forceMove(get_turf(owner)) + else + moveToNullspace() START_PROCESSING(SSobj, src) if(owner && vital && is_primary_organ()) // I'd do another check for species or whatever so that you couldn't "kill" an IPC by removing a human head from them, but it doesn't matter since they'll come right back from the dead diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm index c1484bdaa25..4f11e43c4ff 100644 --- a/code/modules/vehicle/vehicle.dm +++ b/code/modules/vehicle/vehicle.dm @@ -36,7 +36,7 @@ return ..() // So that beepsky can't push the janicart -/obj/vehicle/CanPass(atom/movable/mover, turf/target) +/obj/vehicle/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSMOB)) return TRUE else diff --git a/docs/references/movement_signals.md b/docs/references/movement_signals.md new file mode 100644 index 00000000000..664cba9d531 --- /dev/null +++ b/docs/references/movement_signals.md @@ -0,0 +1,140 @@ +# Movement Signals + +## Background + +Traditionally, BYOND games rely on several native procs to respond to certain +kinds of in-game movement: + +- If one atom attempts to overlap another one, [`/atom/proc/Cross`][cross] is called, + allowing the overlapped atom to either return `TRUE` to allow the cross, or + `FALSE` to prevent it. When `Move()` is called and an atom overlaps another + one, [`/atom/proc/Crossed`][crossed] is called, allowing the overlapped atom to react + to the cross. +- Similarly, when an atom attempts to stop overlapping another, + [`/atom/proc/Uncross`][uncross] is called to permit or deny it, then + [`/atom/proc/Uncrossed`][uncrossed] is called to allow the atom to react to it. +- When a movable attempts to enter a turf, [`/turf/proc/Enter`][enter] is called, + allowing the turf to return `TRUE` to permit the entry, or `FALSE` to deny it. + When a movable succeeds in entering a turf, [`/turf/proc/Entered`][entered] is + called, allowing the turf to react to the entry. +- Similarly, when an atom attempts to exit a turf, [`/turf/proc/Exit`][exit] is + called to permit or deny it, then [`/turf/proc/Exited`][exited] is called to allow + the turf to react to the exit. +- When an atom attempts to enter the contents of another one, + [`/atom/proc/Enter`][atom_enter] is called, allowing the containing atom to + either return `TRUE` to allow the entry, or `FALSE` to prevent it. When an + atom successfully enters the contents of another one, + [`/atom/proc/Entered`][atom_entered] is called. +- Similarly, when an atom attempts to exit another atom's contents, + [`/atom/proc/Exit`][atom_exit] is called to permit or deny it, then + [`/atom/proc/Exited`][atom_exited] is called to allow the turf to react to the + exit. + +As one can imagine, with a lot of objects moving around in the game world, +calling all of these procs can be expensive, especially if we don't care about +what happens most of the time. + +In order to avoid making all of these calls, and provide more fine-grained +control over what happens on atom/turf interactions, we implement our own +version of the native `/atom/movable/Move`, and use signal handlers and +components to process the interactions we care about. + +[cross]: https://secure.byond.com/docs/ref/#/atom/proc/Cross +[crossed]: https://secure.byond.com/docs/ref/#/atom/proc/Crossed +[uncross]: https://secure.byond.com/docs/ref/#/atom/proc/Uncross +[uncrossed]: https://secure.byond.com/docs/ref/#/atom/proc/Uncrossed +[enter]: https://secure.byond.com/docs/ref/#/turf/proc/Enter +[entered]: https://secure.byond.com/docs/ref/#/turf/proc/Entered +[exit]: https://secure.byond.com/docs/ref/#/turf/proc/Exit +[exited]: https://secure.byond.com/docs/ref/#/turf/proc/Exited +[atom_enter]: https://secure.byond.com/docs/ref/#/atom/proc/Enter +[atom_entered]: https://secure.byond.com/docs/ref/#/atom/proc/Entered +[atom_exit]: https://secure.byond.com/docs/ref/#/atom/proc/Exit +[atom_exited]: https://secure.byond.com/docs/ref/#/atom/proc/Exited + +## Signal Handlers + +There are several signal handlers used to replicate the native BYOND atom +crossover/entry behavior: + +- [`COMSIG_MOVABLE_PRE_MOVE`][pre_move] is sent when trying to determine if an + atom can enter a new turf. Any subscribed handler can return + `COMPONENT_MOVABLE_BLOCK_PRE_MOVE` to prevent the move. +- [`COMSIG_MOVABLE_MOVED`][moved] is sent after a successful move. +- [`COMSIG_MOVABLE_CHECK_CROSS`][cross] is sent when trying to determine if an + atom can cross over another one. Any subscribed handler can return + `COMPONENT_BLOCK_CROSS` to prevent the cross. +- Similarly, [`COMSIG_MOVABLE_CHECK_CROSS_OVER`][crossover] is sent if an atom + will allow another one to cross over it--the reverse of + `COMSIG_MOVABLE_CHECK_CROSS`. Again, any subscribed handler can return + `COMPONENT_BLOCK_CROSS` to prevent it. +- [`COMSIG_ATOM_ENTERED`][entered] is sent if an atom enters this atom's + contents. Similarly, [`COMSIG_ATOM_EXITED`][exited] is sent if an atom exits + this atom's contents. + +[pre_move]: https://codedocs.paradisestation.org/code/__DEFINES/dcs/movable_signals.html#define/COMSIG_MOVABLE_PRE_MOVE +[moved]: https://codedocs.paradisestation.org/code/__DEFINES/dcs/movable_signals.html#define/COMSIG_MOVABLE_MOVED +[cross]: https://codedocs.paradisestation.org/code/__DEFINES/dcs/movable_signals.html#define/COMSIG_MOVABLE_CHECK_CROSS +[crossover]: https://codedocs.paradisestation.org/code/__DEFINES/dcs/movable_signals.html#define/COMSIG_MOVABLE_CHECK_CROSS_OVER +[entered]: https://codedocs.paradisestation.org/code/__DEFINES/dcs/movable_signals.html#define/COMSIG_ATOM_ENTERED +[exited]: https://codedocs.paradisestation.org/code/__DEFINES/dcs/movable_signals.html#define/COMSIG_ATOM_EXITED + +## The `connect_loc` Element + +The above signals are not appropriate for all cases. For example, it may seem +like beartraps should listen to `COMSIG_MOVABLE_MOVED` to see if they should +activate. However, this will fire every time a movement crossing occurs, even if +it wouldn't normally trigger the trap, such as if the trap is being thrown and +intersects abstract lighting objects. Instead, we use `COMSIG_ATOM_ENTERED` with +the `connect_loc` element. As a refresher, `COMSIG_ATOM_ENTERED` fires when an +atom enters the contents of another. By using the `connect_loc` element, we can +have the beartrap listen for signals on the turf it's located on. Then, if the +turf has a `COMSIG_ATOM_ENTERED` signal fire, we can have the beartrap respond. + +Before: + +```dm +/obj/item/restraints/legcuffs/beartrap/proc/Crossed(mob/living/crossed) + if(istype(crossed)) + spring_trap() +``` + +After: + +```dm +/obj/item/restraints/legcuffs/beartrap/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/item/restraints/legcuffs/beartrap/proc/on_atom_entered(datum/source, mob/living/entered) + if(istype(entered)) + spring_trap() +``` + +Note that the list of connections is `static` so that a new element instance isn't +created for every new beartrap. + +## Summary + +The procs `/atom/movable/Cross`, `/atom/movable/Crossed`, +`/atom/movable/Uncross`, and `/atom/movable/Uncrossed`, should not be used for +new code. + +If you care about every time a movable attempts to overlap you, listen to +`COMSIG_MOVABLE_PRE_MOVE`, and return `COMPONENT_MOVABLE_BLOCK_PRE_MOVE` to +prevent it from happening. + +If you want to prevent a movable from crossing you, listen to +`COMSIG_MOVABLE_CHECK_CROSS` and return `COMPONENT_BLOCK_CROSS` to prevent it. If you +want to prevent a movable from being crossed by you, listen to +`COMSIG_MOVABLE_CHECK_CROSS_OVER` and return `COMPONENT_BLOCK_CROSS` to prevent it. + +If you care about an atom entering or exiting your contents, listen to +`COMSIG_ATOM_ENTERED` or `COMSIG_ATOM_EXITED`. + +If you care about an atom entering or exiting your location, or any other signal +firing on your location, create a `static` list of signals mapped to procs and +add a `/datum/element/connect_loc` to yourself. diff --git a/mkdocs.yml b/mkdocs.yml index 8d2160d6a3d..aefc3c8ada4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -98,4 +98,5 @@ nav: - 'Advanced Bitflags': './references/adv_bitflags.md' - 'Using Feedback Data': './references/feedback_data.md' - 'Tick Order': './references/tick_order.md' + - 'Movement Signals': './references/movement_signals.md' - 'Attack Chain': './references/attack_chain.md' diff --git a/paradise.dme b/paradise.dme index 339673e3c33..1d532853d43 100644 --- a/paradise.dme +++ b/paradise.dme @@ -94,6 +94,7 @@ #include "code\__DEFINES\mob_defines.dm" #include "code\__DEFINES\mod.dm" #include "code\__DEFINES\move_force.dm" +#include "code\__DEFINES\movement_info.dm" #include "code\__DEFINES\muzzle_flash.dm" #include "code\__DEFINES\newscaster_defines.dm" #include "code\__DEFINES\particle_defines.dm" @@ -163,6 +164,7 @@ #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\AnimationLibrary.dm" #include "code\__HELPERS\api.dm" +#include "code\__HELPERS\atom_helpers.dm" #include "code\__HELPERS\bitflag_lists.dm" #include "code\__HELPERS\cmp.dm" #include "code\__HELPERS\colour_helpers.dm" @@ -379,6 +381,7 @@ #include "code\datums\beam.dm" #include "code\datums\browser.dm" #include "code\datums\callback.dm" +#include "code\datums\card_deck_table_tracker.dm" #include "code\datums\chat_payload.dm" #include "code\datums\chatmessage.dm" #include "code\datums\click_intercept.dm" @@ -429,7 +432,10 @@ #include "code\datums\components\boss_music.dm" #include "code\datums\components\caltrop.dm" #include "code\datums\components\codeword_hearing.dm" +#include "code\datums\components\connect_containers.dm" +#include "code\datums\components\connect_loc_behalf.dm" #include "code\datums\components\connect_mob_behalf.dm" +#include "code\datums\components\connect_range.dm" #include "code\datums\components\corpse_description.dm" #include "code\datums\components\cult_held_body.dm" #include "code\datums\components\deadchat_control.dm" @@ -448,7 +454,6 @@ #include "code\datums\components\paintable.dm" #include "code\datums\components\parry.dm" #include "code\datums\components\persistent_overlay.dm" -#include "code\datums\components\proximity_monitor.dm" #include "code\datums\components\radioactive.dm" #include "code\datums\components\scope.dm" #include "code\datums\components\shelved.dm" @@ -529,6 +534,7 @@ #include "code\datums\elements\atmos_requirements.dm" #include "code\datums\elements\body_temperature.dm" #include "code\datums\elements\bombable_turf.dm" +#include "code\datums\elements\connect_loc.dm" #include "code\datums\elements\decal_element.dm" #include "code\datums\elements\earhealing.dm" #include "code\datums\elements\high_value_item.dm" @@ -566,6 +572,9 @@ #include "code\datums\outfits\outfit_debug.dm" #include "code\datums\outfits\plasmamen_outfits.dm" #include "code\datums\outfits\vv_outfit.dm" +#include "code\datums\proximity\advanced_proximity_monitor.dm" +#include "code\datums\proximity\proximity_monitor.dm" +#include "code\datums\proximity\singulo_proximity_monitor.dm" #include "code\datums\ruins\lavaland.dm" #include "code\datums\ruins\ruin_placer.dm" #include "code\datums\ruins\space_ruins.dm"