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 | 119 ++--- 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 | 78 ++- .../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 | 14 +- .../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, 2494 insertions(+), 1325 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 d1dbbf2ee1ab..294e5bd5aac1 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 2de0ca3879dc..7aa2beca9330 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 a6df2f627416..e524369c4455 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 000000000000..95c90f7a1fba --- /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 000000000000..b0a0999da72b --- /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 d40c236192ba..7731c3dbdc8a 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 f35cc240ac8a..7295dd84b231 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 000000000000..d8f0cda1dff3 --- /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 de94b8dfe934..bc0a510f47ea 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 000000000000..d998f0ae2943 --- /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 000000000000..8593fea8455e --- /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 000000000000..8cf961d2221d --- /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 d697ada974e3..84fd4fab5b3c 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 4d524ba6cea8..000000000000 --- 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 bc7b0e05f8a1..50f7d817f444 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 bfb939ed1036..0583c1f8a4f2 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)) - return - if(istype(crossing, /obj/effect)) +/datum/component/squeak/proc/on_atom_entered(datum/source, atom/movable/entered) + if(istype(entered, /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 928589fbaaa8..608359d49089 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 56e35b452ed8..e09769af7552 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 000000000000..6fcc1474679c --- /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 000000000000..bd47bfd6cb90 --- /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 000000000000..09a7d32f916d --- /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 000000000000..e7b870ba346a --- /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 666754127a77..50be4d96c834 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 e7cee2a416a0..5b26c280c599 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 db60b423c5d9..61c9cab09b78 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 59548e2a038a..9d33e71059b7 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 0af908662076..8a2c0a5df463 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") + +/* + * 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 - if(old_loc) - old_loc.Exited(src, destination) - for(var/atom/movable/AM in old_loc) - AM.Uncrossed(src) + 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() + + 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) + + moving_diagonally = 0 + + 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) - Moved(old_loc, NONE) + . = TRUE - return 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 -/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) + 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 b948bad9da63..26066e3b5bf1 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 e385fd1e329d..756b4489bdea 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 d3f67b67ef53..862fe56ff111 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 1c15fdb1a820..cf7da6be862b 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 c8ef3b159adb..babc34bc4619 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 ab86bc89afbd..d0bc0b0dca76 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 24bd6f3a7c02..ca131022a999 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 9c7f0fc61bc0..36a39afc6c2e 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 057eeaa7ce90..3fc127338fdf 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 837ecea566f3..2ec30a257663 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 b4551ee40710..686cf95dc02c 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 2c1ce8187f77..6b90f4650b56 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 efc341602041..39ff3baf9723 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 8169b96246a5..aa5685c04d2e 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 69f22c6d0f16..fbb7298600cb 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 766f3ec2aefd..e73d480a7b1b 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 5f5723b4d2bc..2a8a67c77889 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 7b98ef255c04..4a9aee89a9e0 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 f89810dd4d8d..6c7520a80b80 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 0d72b68d1d40..38b98fddda5a 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 57c8e61d66e7..39449cab8259 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 f8f509441d17..457ad4aba68b 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 7590801455ad..285462a94333 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 afc114502d52..a00b1c65ce03 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 fad65dc1bb65..89aa71313140 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 38a79e45b2f6..f9d8e1f4acba 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 7ff65230575a..8048ab7e23d1 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 5cc09a4619cf..0b76030ca1df 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 d7417d8452aa..431c97c6d7e7 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/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/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/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 cf914e6fb03f..6ad91e08db51 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 0c5f9c2e8b23..cbe122c4d742 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 55aee922c096..15cfd65b5434 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 14545e54e283..7c59c10efe16 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 573fe8030f17..a97ced7b06a5 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 ba6f449d1f01..4a61e40a3fdd 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 dbba0c2e051c..c169729b8e50 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 e7acf45614a2..63ab8096230d 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 4066e3702c7b..d7d151a4dd0c 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 3bc720ac53c7..46a634c7f844 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 4ff358a88272..46e3b9da43cb 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 e98f1303f42c..1cdf4e1630b1 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 be4d87fd36eb..e2539d68fd98 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 82acfec37235..996b7de9c578 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 146695a515a7..2269e2afece1 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 fa2ec1c9fdea..44a563a2cb7f 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 a9fc87d9fdd8..d732db1d9fce 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 ab0409a71529..d29ba66b3456 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 6581136461d5..0b5236d0e316 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 e4c2b29b69ef..1a80c3bac7c2 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 b6f30a05c181..71c1a2372482 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 b3b6ef724e98..fc3904381f10 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), + ) + + AddElement(/datum/element/connect_loc, loc_connections) -/obj/structure/closet/bluespace/proc/UpdateTransparency(atom/movable/AM, atom/location) +/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 351d42a6b675..657d374800a7 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 a7f2125a0a71..746c4b9b7263 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 e0aa89bca9b1..c4a1b48e218a 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 655551f6eb95..3631b28451de 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 0c1267ff8918..1db795432b71 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 c17308c90af6..144bf5f747df 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 9ed6bfc1750b..12403792f164 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 0deca8f6de66..a85aaf417d65 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 ebe2e672006d..5c229ce50d6f 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 23f533f22967..cd70a5ff4bca 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 ad0fbabd901e..b5612911dd13 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 e882da691101..308d5e6b6882 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 4a12a0132f01..23ab76e66555 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 0727ea69e34d..a4e8b7452331 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 d9802a666ff6..6f547b8d070a 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 2e5bb0e39c99..5202acf5b661 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 0dbe2dc77e3a..1fdab8f14835 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 eef62cd46f4c..c295188fd93b 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 - - // 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) +//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)) + + 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 62d63bb3dec9..2262004cd0c9 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 d4a54300dbb6..a4badcefff03 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 7750d4f69b9f..d79f0ba61a65 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 ee47d325f737..f3611bd5579a 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 151ce67aa7e9..3eb13ae461d5 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 38c60e7167f3..7ef7a8f40c9b 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 ee9df746b436..687e79881db1 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 734e9a7f4248..a1d997d7879e 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 a3163d7141b5..e4dac71db9bd 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 8369f439e8ce..dac9ad424bf7 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 ff4f57b01c80..b1baf8506017 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 150a6cd304d8..88f16b4ea6f6 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 2da93a61bfd5..cf4f2f059591 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 d65ddd9c76ac..834c8a6d592f 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 e1ed6df4c1f7..137ac4b611db 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 8640bbbd4e31..14c3cc9db61e 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 826f4155f39d..7383609950e4 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 7d4a4346dc80..51a9eb19ddb0 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 2ecee71c3065..c5610ef97ce5 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 ac95ae68fda4..b46d710c89fd 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 6d0d6ec1c19e..6dbcc309c77d 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 855989a13a16..42b43f179fbf 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 b9e36a34aef6..efd1754b3dea 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 d9536cbb5736..c2dc17adf74b 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 3b7b0f3477cf..403eab67e4d7 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 6eb46cbdad8a..303957793540 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 168a95c07f26..df62b5c342e9 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 a0c39b80da3a..6237548bfc3c 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 f9d9ad373c61..d4762b8bb934 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 6c0b366586fa..731285e775ce 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 3d5aa45f685f..88b454e0037c 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 873b60555a91..3ea6c1471364 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 03267d00140c..59159c638f89 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 725f7fd28952..c82b9bc39f06 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 4e11337826af..5e0beba27232 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 fd05477e2d39..3fd7184bf154 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 1e5b4e52b887..a300fa2bac9b 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 e2e0296b7f80..7f2ce26fd65d 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 20652fdb801a..9c243922bed3 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 ad9eca0a7787..48ef878ac713 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 510874a95a51..c8909ba151ef 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 6f5caaf3a315..a04154601903 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 2643abad6da0..39e99b7720e6 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 6f6144d44fa6..4a98a04f2540 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 9dbac40967d7..5b4d0624eb42 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 323508edfe16..7bd2ee98227a 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 59d2e4b3e86e..b290d2a4ef98 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 b01e6a895670..ab151efa92d5 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 229d8c8e0a69..5c9219a88f9a 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 037ab6c2551f..ab73704d812b 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 92779de451a4..bef98567bab7 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 08a695f79d14..9f0bf777ef63 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 d26e9d975db3..358894501a7f 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 84ee41256fcf..2d79c3224119 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 88ddc6189a2f..daec7a7b43eb 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 dedebe6f5492..6f1cd194d7f6 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 0953ae2a4541..cfec3f374779 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 12641cefbd82..a2bae7b95c33 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 32d9683db270..1d51905da0a8 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 dabeeb3f3b3a..0199e76b6d09 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 eef6897ee91e..b7a012e9c8aa 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 6a6f68b8b5bd..f57b6ee3b4f3 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 eae1c664e1c4..00664ab28340 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) - -/obj/item/light/Crossed(mob/living/L) - if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || HAS_TRAIT(L, TRAIT_FLYING) || L.floating) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/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 ec72d05df0b1..4e2aa0d68876 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 0130b85e8ab7..8d8603f0dccf 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 496f385e4dbf..1484b49792e2 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 dc79be44db93..41e0f9979f77 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 67f4a1ae1beb..55dfca88312e 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 61f1f912f080..03755059d7c7 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 6c478825d6bc..0ea0f0e2f01e 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 d1fa9d14eb55..2ac19610aabc 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 b6281e66fa3b..cfa4c18f3c8b 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 c0058b3082e8..af173186dcc2 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 e174827f4efe..9fea2a3a4a1d 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 3cbb358b23a1..4d2b13883ba6 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 c1484bdaa251..4f11e43c4ffc 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 000000000000..664cba9d5314 --- /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 8d2160d6a3d9..aefc3c8ada41 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 339673e3c33b..1d532853d435 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"