From 3c6c816f4cd1497076e810b53a64f6174c0d4e18 Mon Sep 17 00:00:00 2001 From: Doubleumc Date: Thu, 11 Jul 2024 01:13:56 -0400 Subject: [PATCH 1/2] initial --- code/_macros.dm | 3 + code/controllers/subsystem/sound.dm | 3 +- code/datums/quadtree.dm | 153 ++++++++++++++---- code/game/camera_manager/camera_manager.dm | 20 +-- .../machinery/computer/dropship_weapons.dm | 3 +- .../objects/items/devices/motion_detector.dm | 11 +- .../structures/special/egg_morpher.dm | 6 +- code/modules/defenses/bell_tower.dm | 2 +- code/modules/defenses/planted_flag.dm | 10 +- code/modules/defenses/sentry.dm | 30 ++-- code/modules/defenses/sentry_computer.dm | 3 +- code/modules/defenses/sentry_flamer.dm | 8 +- 12 files changed, 168 insertions(+), 84 deletions(-) diff --git a/code/_macros.dm b/code/_macros.dm index abfa83df7d36..e58612de8baf 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -95,6 +95,9 @@ #define GENERATE_DEBUG_ID "[rand(0, 9)][rand(0, 9)][rand(0, 9)][rand(0, 9)][pick(alphabet_lowercase)][pick(alphabet_lowercase)][pick(alphabet_lowercase)][pick(alphabet_lowercase)]" #define RECT new /datum/shape/rectangle +#define SQUARE new /datum/shape/square +#define ELLIPSE new /datum/shape/ellipse +#define CIRCLE new /datum/shape/circle #define QTREE new /datum/quadtree #define SEARCH_QTREE(qtree, shape_range, flags) qtree.query_range(shape_range, null, flags) diff --git a/code/controllers/subsystem/sound.dm b/code/controllers/subsystem/sound.dm index 024df7cc45ad..13dd6a0dddf1 100644 --- a/code/controllers/subsystem/sound.dm +++ b/code/controllers/subsystem/sound.dm @@ -19,8 +19,7 @@ SUBSYSTEM_DEF(sound) if(!run_hearers) // Initialize for handling next template run_hearers = run_queue[run_template] // get base hearers if(run_template.range) // ranging - var/datum/shape/rectangle/zone = RECT(run_template.x, run_template.y, run_template.range * 2, run_template.range * 2) - run_hearers |= SSquadtree.players_in_range(zone, run_template.z) + run_hearers |= SSquadtree.players_in_range(SQUARE(run_template.x, run_template.y, run_template.range), run_template.z) if(MC_TICK_CHECK) return while(length(run_hearers)) // Output sound to hearers diff --git a/code/datums/quadtree.dm b/code/datums/quadtree.dm index 5e5b27d57330..95e91d7aace0 100644 --- a/code/datums/quadtree.dm +++ b/code/datums/quadtree.dm @@ -49,43 +49,132 @@ ..() return QDEL_HINT_IWILLGC -/datum/shape //Leaving rectangles as a subtype if anyone decides to add circles later +/// A simple geometric shape for testing collisions and intersections. This one is a single point. +/datum/shape + /// Horizontal position of the shape's center point. var/center_x = 0 + /// Vertical position of the shape's center point. var/center_y = 0 + /// Distance from the shape's leftmost to rightmost extent. + var/bounds_x = 0 + /// Distance from the shape's topmost to bottommost extent. + var/bounds_y = 0 -/datum/shape/proc/intersects() - return -/datum/shape/proc/contains() - return +/datum/shape/New() + set_shape(arglist(args)) +/// Assign shape variables. +/datum/shape/proc/set_shape(center_x, center_y) + src.center_x = center_x + src.center_y = center_y + +/// Returns TRUE if the coordinates x, y are in or on the shape, otherwise FALSE. +/datum/shape/proc/contains_xy(x, y) + return center_x == x && center_y == y + +/// Returns TRUE if the coord datum is in or on the shape, otherwise FALSE. +/datum/shape/proc/contains_coords(datum/coords/coords) + return contains_xy(coords.x_pos, coords.y_pos) + +/// Returns TRUE if the atom is in or on the shape, otherwise FALSE. +/datum/shape/proc/contains_atom(atom/atom) + return contains_xy(atom.x, atom.y) + +/// Returns TRUE if this shape intersects the provided rectangle shape, otherwise FALSE. +/datum/shape/proc/intersects_rect(datum/shape/rectangle/range) + return range.contains_xy(src.center_x, src.center_y) + +/// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned rectangle. /datum/shape/rectangle + /// Distance from the shape's leftmost to rightmost extent. + var/width = 0 + /// Distance from the shape's topmost to bottommost extent. + var/height = 0 + +/datum/shape/rectangle/set_shape(center_x, center_y, width, height) + ..() + src.bounds_x = width + src.bounds_y = height + src.width = width + src.height = height + +/datum/shape/rectangle/contains_xy(x, y) + return (abs(center_x - x) <= width * 0.5) && (abs(center_y - y) <= height * 0.5) + +/datum/shape/rectangle/intersects_rect(datum/shape/rectangle/range) + return (abs(src.center_x - range.center_x) <= (src.width + range.width) * 0.5) && (abs(src.center_y - range.center_y) <= (src.height + range.height) * 0.5) + +/// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned square. +/datum/shape/square + /// Distance between the shape's opposing extents. + var/length = 0 + +/datum/shape/square/set_shape(center_x, center_y, length) + ..() + src.bounds_x = length + src.bounds_y = length + src.length = length + +/datum/shape/square/contains_xy(x, y) + return (abs(center_x - x) <= length * 0.5) && (abs(center_y - y) <= length * 0.5) + +/datum/shape/square/intersects_rect(datum/shape/rectangle/range) + return (abs(src.center_x - range.center_x) <= (src.length + range.width) * 0.5) && (abs(src.center_y - range.center_y) <= (src.length + range.height) * 0.5) + +/// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned ellipse. +/datum/shape/ellipse + /// Distance from the shape's leftmost to rightmost extent. var/width = 0 + /// Distance from the shape's topmost to bottommost extent. var/height = 0 + VAR_PROTECTED/_axis_x_sq = 0 + VAR_PROTECTED/_axis_y_sq = 0 + +/datum/shape/ellipse/set_shape(center_x, center_y, width, height) + ..() + src.bounds_x = width + src.bounds_y = height + src.width = width + src.height = height + src._axis_x_sq = (width * 0.5)**2 + src._axis_y_sq = (height * 0.5)**2 + +/datum/shape/ellipse/contains_xy(x, y) + return ((center_x - x)**2 / _axis_x_sq + (center_y - y)**2 / _axis_y_sq <= 1) + +/datum/shape/ellipse/intersects_rect(datum/shape/rectangle/range) + if(range.contains_xy(src.center_x, src.center_y)) + return TRUE + + var/nearest_x = clamp(src.center_x, range.center_x - range.width * 0.5, range.center_x + range.width * 0.5) + var/nearest_y = clamp(src.center_y, range.center_y - range.height * 0.5, range.center_y + range.height * 0.5) -/datum/shape/rectangle/New(x, y, w, h) + return src.contains_xy(nearest_x, nearest_y) + +/// A simple geometric shape for testing collisions and intersections. This one is a circle. +/datum/shape/circle + /// Distance from the shape's center to edge. + var/radius = 0 + VAR_PROTECTED/_radius_sq = 0 + +/datum/shape/circle/set_shape(center_x, center_y, radius) ..() - center_x = x - center_y = y - width = w - height = h - -/datum/shape/rectangle/intersects(datum/shape/rectangle/range) - return !(range.center_x + range.width/2 < center_x - width / 2|| \ - range.center_x - range.width/2 > center_x + width / 2|| \ - range.center_y + range.height/2 < center_y - height / 2|| \ - range.center_y - range.height/2 > center_y + height / 2) - -/datum/shape/rectangle/contains(datum/coords/coords) - return (coords.x_pos >= center_x - width / 2 \ - && coords.x_pos <= center_x + width / 2 \ - && coords.y_pos >= center_y - height /2 \ - && coords.y_pos <= center_y + height / 2) - -/datum/shape/rectangle/proc/contains_atom(atom/A) - return (A.x >= center_x - width / 2 \ - && A.x <= center_x + width / 2 \ - && A.y >= center_y - height /2 \ - && A.y <= center_y + height / 2) + src.bounds_x = radius * 2 + src.bounds_y = radius * 2 + src.radius = radius + src._radius_sq = radius**2 + +/datum/shape/circle/contains_xy(x, y) + return ((center_x - x)**2 + (center_y - y)**2 <= _radius_sq) + +/datum/shape/circle/intersects_rect(datum/shape/rectangle/range) + if(range.contains_xy(src.center_x, src.center_y)) + return TRUE + + var/nearest_x = clamp(src.center_x, range.center_x - range.width * 0.5, range.center_x + range.width * 0.5) + var/nearest_y = clamp(src.center_y, range.center_y - range.height * 0.5, range.center_y + range.height * 0.5) + + return src.contains_xy(nearest_x, nearest_y) /datum/quadtree/proc/subdivide() //Warning: this might give you eye cancer @@ -96,7 +185,7 @@ is_divided = TRUE /datum/quadtree/proc/insert_player(datum/coords/qtplayer/p_coords) - if(!boundary.contains(p_coords)) + if(!boundary.contains_coords(p_coords)) return FALSE if(!player_coords) @@ -118,11 +207,11 @@ player_coords.Add(p_coords) return TRUE -/datum/quadtree/proc/query_range(datum/shape/rectangle/range, list/found_players, flags = 0) +/datum/quadtree/proc/query_range(datum/shape/range, list/found_players, flags = 0) if(!found_players) found_players = list() . = found_players - if(!range?.intersects(boundary)) + if(!range?.intersects_rect(boundary)) return if(is_divided) nw_branch.query_range(range, found_players, flags) @@ -136,7 +225,7 @@ continue if((flags & QTREE_EXCLUDE_OBSERVER) && P.is_observer) continue - if(range.contains(P)) + if(range.contains_coords(P)) if(flags & QTREE_SCAN_MOBS) found_players.Add(P.player.mob) else diff --git a/code/game/camera_manager/camera_manager.dm b/code/game/camera_manager/camera_manager.dm index 9f111b0f8ec6..90e80ec7037e 100644 --- a/code/game/camera_manager/camera_manager.dm +++ b/code/game/camera_manager/camera_manager.dm @@ -6,7 +6,7 @@ /datum/component/camera_manager var/map_name var/obj/structure/machinery/camera/current - var/datum/shape/rectangle/current_area + var/datum/shape/current_area var/atom/movable/screen/map_view/cam_screen var/atom/movable/screen/background/cam_background var/list/range_turfs = list() @@ -86,7 +86,7 @@ RegisterSignal(parent, COMSIG_CAMERA_UNREGISTER_UI, PROC_REF(unregister)) RegisterSignal(parent, COMSIG_CAMERA_SET_NVG, PROC_REF(enable_nvg)) RegisterSignal(parent, COMSIG_CAMERA_CLEAR_NVG, PROC_REF(disable_nvg)) - RegisterSignal(parent, COMSIG_CAMERA_SET_AREA, PROC_REF(set_camera_rect)) + RegisterSignal(parent, COMSIG_CAMERA_SET_AREA, PROC_REF(set_camera_area)) RegisterSignal(parent, COMSIG_CAMERA_SET_TARGET, PROC_REF(set_camera)) RegisterSignal(parent, COMSIG_CAMERA_CLEAR, PROC_REF(clear_camera)) RegisterSignal(parent, COMSIG_CAMERA_REFRESH, PROC_REF(refresh_camera)) @@ -133,18 +133,18 @@ RegisterSignal(current, COMSIG_PARENT_QDELETING, PROC_REF(show_camera_static)) update_target_camera() -/datum/component/camera_manager/proc/set_camera_rect(source, x, y, z, w, h) +/datum/component/camera_manager/proc/set_camera_area(source, datum/shape/new_area, z) SIGNAL_HANDLER render_mode = RENDER_MODE_AREA if(current) UnregisterSignal(current, COMSIG_PARENT_QDELETING) current = null - current_area = RECT(x, y, w, h) - target_x = x - target_y = y + current_area = new_area + target_x = current_area.center_x + target_y = current_area.center_y target_z = z - target_width = w - target_height = h + target_width = current_area.bounds_x + target_height = current_area.bounds_y update_area_camera() /datum/component/camera_manager/proc/enable_nvg(source, power, matrixcol) @@ -221,8 +221,8 @@ // Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs. last_camera_turf = new_location - var/x_size = current_area.width - var/y_size = current_area.height + var/x_size = current_area.bounds_x + var/y_size = current_area.bounds_y var/turf/target = locate(current_area.center_x, current_area.center_y, target_z) var/list/visible_things = isXRay ? range("[x_size]x[y_size]", target) : view("[x_size]x[y_size]", target) diff --git a/code/game/machinery/computer/dropship_weapons.dm b/code/game/machinery/computer/dropship_weapons.dm index e07b415ed233..7f8f4f2b3850 100644 --- a/code/game/machinery/computer/dropship_weapons.dm +++ b/code/game/machinery/computer/dropship_weapons.dm @@ -313,9 +313,8 @@ var/obj/structure/machinery/defenses/sentry/defense = sentry.deployed_turret if(defense.has_camera) defense.set_range() - var/datum/shape/rectangle/current_bb = defense.range_bounds camera_area_equipment = sentry - SEND_SIGNAL(src, COMSIG_CAMERA_SET_AREA, current_bb.center_x, current_bb.center_y, defense.loc.z, current_bb.width, current_bb.height) + SEND_SIGNAL(src, COMSIG_CAMERA_SET_AREA, defense.range_bounds, defense.loc.z) return TRUE if("clear-camera") diff --git a/code/game/objects/items/devices/motion_detector.dm b/code/game/objects/items/devices/motion_detector.dm index 3551e3a02bef..1ced1b7bcd00 100644 --- a/code/game/objects/items/devices/motion_detector.dm +++ b/code/game/objects/items/devices/motion_detector.dm @@ -35,7 +35,7 @@ var/iff_signal = FACTION_MARINE actions_types = list(/datum/action/item_action) var/scanning = FALSE // controls if MD is in process of scan - var/datum/shape/rectangle/range_bounds + var/datum/shape/square/range_bounds var/long_range_locked = FALSE //only long-range MD var/ping_overlay @@ -48,7 +48,7 @@ /obj/item/device/motiondetector/Initialize() . = ..() - range_bounds = new //Just creating a rectangle datum + range_bounds = new //Just creating a square datum update_icon() /obj/item/device/motiondetector/Destroy() @@ -215,12 +215,7 @@ if(!istype(cur_turf)) return - if(!range_bounds) - range_bounds = new/datum/shape/rectangle - range_bounds.center_x = cur_turf.x - range_bounds.center_y = cur_turf.y - range_bounds.width = detector_range * 2 - range_bounds.height = detector_range * 2 + range_bounds.set_shape(cur_turf.x, cur_turf.y, detector_range * 2) var/list/ping_candidates = SSquadtree.players_in_range(range_bounds, cur_turf.z, QTREE_EXCLUDE_OBSERVER | QTREE_SCAN_MOBS) diff --git a/code/modules/cm_aliens/structures/special/egg_morpher.dm b/code/modules/cm_aliens/structures/special/egg_morpher.dm index c4fb5c0a900c..ac501bb5c970 100644 --- a/code/modules/cm_aliens/structures/special/egg_morpher.dm +++ b/code/modules/cm_aliens/structures/special/egg_morpher.dm @@ -14,14 +14,14 @@ var/huggers_to_grow_max = 12 var/huggers_reserved = 0 var/mob/captured_mob - var/datum/shape/rectangle/range_bounds + var/datum/shape/range_bounds appearance_flags = KEEP_TOGETHER layer = FACEHUGGER_LAYER /obj/effect/alien/resin/special/eggmorph/Initialize(mapload, hive_ref) . = ..() - range_bounds = RECT(x, y, EGGMORPG_RANGE, EGGMORPG_RANGE) + range_bounds = SQUARE(x, y, EGGMORPG_RANGE) /obj/effect/alien/resin/special/eggmorph/Destroy() if (stored_huggers && linked_hive) @@ -158,7 +158,7 @@ /obj/effect/alien/resin/special/eggmorph/proc/check_facehugger_target() if(!range_bounds) - range_bounds = RECT(x, y, EGGMORPG_RANGE, EGGMORPG_RANGE) + range_bounds = SQUARE(x, y, EGGMORPG_RANGE) var/list/targets = SSquadtree.players_in_range(range_bounds, z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER) if(isnull(targets) || !length(targets)) diff --git a/code/modules/defenses/bell_tower.dm b/code/modules/defenses/bell_tower.dm index 68d58f02b481..7ce252b3f4d7 100644 --- a/code/modules/defenses/bell_tower.dm +++ b/code/modules/defenses/bell_tower.dm @@ -257,7 +257,7 @@ STOP_PROCESSING(SSobj, src) return - var/list/targets = SSquadtree.players_in_range(RECT(M.x, M.y, area_range, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER) + var/list/targets = SSquadtree.players_in_range(SQUARE(M.x, M.y, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER) if(!targets) return diff --git a/code/modules/defenses/planted_flag.dm b/code/modules/defenses/planted_flag.dm index d2b9b23e8f3b..d44f22f38b68 100644 --- a/code/modules/defenses/planted_flag.dm +++ b/code/modules/defenses/planted_flag.dm @@ -7,7 +7,7 @@ desc = "A planted flag with the iconic USCM flag plastered all over it, you feel a burst of energy by its mere sight." handheld_type = /obj/item/defenses/handheld/planted_flag disassemble_time = 10 - var/datum/shape/rectangle/range_bounds + var/datum/shape/range_bounds var/area_range = PLANTED_FLAG_RANGE var/buff_intensity = PLANTED_FLAG_BUFF health = 200 @@ -33,7 +33,7 @@ apply_area_effect() start_processing() - range_bounds = RECT(x, y, PLANTED_FLAG_RANGE, PLANTED_FLAG_RANGE) + range_bounds = SQUARE(x, y, PLANTED_FLAG_RANGE) update_icon() /obj/structure/machinery/defenses/planted_flag/Destroy() @@ -70,9 +70,9 @@ /obj/structure/machinery/defenses/planted_flag/proc/apply_area_effect() if(!range_bounds) - range_bounds = RECT(x, y, area_range, area_range) + range_bounds = SQUARE(x, y, area_range) - var/list/targets = SSquadtree.players_in_range(RECT(x, y, area_range, area_range), z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER) + var/list/targets = SSquadtree.players_in_range(SQUARE(x, y, area_range), z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER) if(!targets) return @@ -180,7 +180,7 @@ if(!M.x && !M.y && !M.z) return - var/list/targets = SSquadtree.players_in_range(RECT(M.x, M.y, area_range, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER) + var/list/targets = SSquadtree.players_in_range(SQUARE(M.x, M.y, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER) targets |= M for(var/mob/living/carbon/human/H in targets) diff --git a/code/modules/defenses/sentry.dm b/code/modules/defenses/sentry.dm index 695b3387d909..8ad4cd407e75 100644 --- a/code/modules/defenses/sentry.dm +++ b/code/modules/defenses/sentry.dm @@ -12,7 +12,7 @@ var/list/targets = list() // Lists of current potential targets var/list/other_targets = list() //List of special target types to shoot at, if needed. var/atom/movable/target = null - var/datum/shape/rectangle/range_bounds + var/datum/shape/range_bounds var/datum/effect_system/spark_spread/spark_system //The spark system, used for generating... sparks? var/last_fired = 0 var/fire_delay = 4 @@ -93,17 +93,17 @@ /obj/structure/machinery/defenses/sentry/proc/set_range() if(omni_directional) - range_bounds = RECT(x, y, 8, 8) + range_bounds = SQUARE(x, y, 8) return switch(dir) if(EAST) - range_bounds = RECT(x + 4, y, 7, 7) + range_bounds = SQUARE(x + 4, y, 7) if(WEST) - range_bounds = RECT(x - 4, y, 7, 7) + range_bounds = SQUARE(x - 4, y, 7) if(NORTH) - range_bounds = RECT(x, y + 4, 7, 7) + range_bounds = SQUARE(x, y + 4, 7) if(SOUTH) - range_bounds = RECT(x, y - 4, 7, 7) + range_bounds = SQUARE(x, y - 4, 7) /obj/structure/machinery/defenses/sentry/proc/unset_range() SIGNAL_HANDLER @@ -614,17 +614,17 @@ var/dbl_range = range * 2 if(omni_directional) - range_bounds = RECT(x, y, dbl_range, dbl_range) + range_bounds = SQUARE(x, y, dbl_range) return switch(dir) if(EAST) - range_bounds = RECT(x+range, y, dbl_range, dbl_range) + range_bounds = SQUARE(x+range, y, dbl_range) if(WEST) - range_bounds = RECT(x-range, y, dbl_range, dbl_range) + range_bounds = SQUARE(x-range, y, dbl_range) if(NORTH) - range_bounds = RECT(x, y+range, dbl_range, dbl_range) + range_bounds = SQUARE(x, y+range, dbl_range) if(SOUTH) - range_bounds = RECT(x, y-range, dbl_range, dbl_range) + range_bounds = SQUARE(x, y-range, dbl_range) //the turret inside the shuttle sentry deployment system /obj/structure/machinery/defenses/sentry/premade/dropship @@ -672,13 +672,13 @@ /obj/structure/machinery/defenses/sentry/dmr/set_range() switch(dir) if(EAST) - range_bounds = RECT(x + (SENTRY_SNIPER_RANGE/2), y, SENTRY_SNIPER_RANGE, SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x + (SENTRY_SNIPER_RANGE/2), y, SENTRY_SNIPER_RANGE) if(WEST) - range_bounds = RECT(x - (SENTRY_SNIPER_RANGE/2), y, SENTRY_SNIPER_RANGE, SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x - (SENTRY_SNIPER_RANGE/2), y, SENTRY_SNIPER_RANGE) if(NORTH) - range_bounds = RECT(x, y + (SENTRY_SNIPER_RANGE/2), SENTRY_SNIPER_RANGE, SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x, y + (SENTRY_SNIPER_RANGE/2), SENTRY_SNIPER_RANGE) if(SOUTH) - range_bounds = RECT(x, y - (SENTRY_SNIPER_RANGE/2), SENTRY_SNIPER_RANGE, SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x, y - (SENTRY_SNIPER_RANGE/2), SENTRY_SNIPER_RANGE) #undef SENTRY_SNIPER_RANGE /obj/structure/machinery/defenses/sentry/shotgun diff --git a/code/modules/defenses/sentry_computer.dm b/code/modules/defenses/sentry_computer.dm index 59c6409d552c..639a74e6ba30 100644 --- a/code/modules/defenses/sentry_computer.dm +++ b/code/modules/defenses/sentry_computer.dm @@ -402,8 +402,7 @@ var/obj/structure/machinery/defenses/sentry/defense = sentry if (defense.has_camera) defense.set_range() - var/datum/shape/rectangle/current_bb = defense.range_bounds - SEND_SIGNAL(src, COMSIG_CAMERA_SET_AREA, current_bb.center_x, current_bb.center_y, defense.loc.z, current_bb.width, current_bb.height) + SEND_SIGNAL(src, COMSIG_CAMERA_SET_AREA, defense.range_bounds, defense.loc.z) return TRUE if("ping") diff --git a/code/modules/defenses/sentry_flamer.dm b/code/modules/defenses/sentry_flamer.dm index 979d18eb41d1..2c5e9ae62677 100644 --- a/code/modules/defenses/sentry_flamer.dm +++ b/code/modules/defenses/sentry_flamer.dm @@ -90,13 +90,13 @@ /obj/structure/machinery/defenses/sentry/flamer/plasma/set_range() switch(dir) if(EAST) - range_bounds = RECT(x + (FLAMER_SENTRY_SNIPER_RANGE/2), y, FLAMER_SENTRY_SNIPER_RANGE, FLAMER_SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x + (FLAMER_SENTRY_SNIPER_RANGE/2), y, FLAMER_SENTRY_SNIPER_RANGE) if(WEST) - range_bounds = RECT(x - (FLAMER_SENTRY_SNIPER_RANGE/2), y, FLAMER_SENTRY_SNIPER_RANGE, FLAMER_SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x - (FLAMER_SENTRY_SNIPER_RANGE/2), y, FLAMER_SENTRY_SNIPER_RANGE) if(NORTH) - range_bounds = RECT(x, y + (FLAMER_SENTRY_SNIPER_RANGE/2), FLAMER_SENTRY_SNIPER_RANGE, FLAMER_SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x, y + (FLAMER_SENTRY_SNIPER_RANGE/2), FLAMER_SENTRY_SNIPER_RANGE) if(SOUTH) - range_bounds = RECT(x, y - (FLAMER_SENTRY_SNIPER_RANGE/2), FLAMER_SENTRY_SNIPER_RANGE, FLAMER_SENTRY_SNIPER_RANGE) + range_bounds = SQUARE(x, y - (FLAMER_SENTRY_SNIPER_RANGE/2), FLAMER_SENTRY_SNIPER_RANGE) #undef FLAMER_SENTRY_SNIPER_RANGE From 29776a595b11892c46f9a645e16972102e0e8295 Mon Sep 17 00:00:00 2001 From: Doubleumc Date: Sun, 14 Jul 2024 17:59:15 -0400 Subject: [PATCH 2/2] shape inheritance square now a subtype of rect, circle now a subtype of ellipse --- code/_macros.dm | 4 +- code/datums/quadtree.dm | 72 +++++++++---------- .../objects/items/devices/motion_detector.dm | 2 +- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/code/_macros.dm b/code/_macros.dm index e58612de8baf..9b92dc8730c3 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -95,9 +95,9 @@ #define GENERATE_DEBUG_ID "[rand(0, 9)][rand(0, 9)][rand(0, 9)][rand(0, 9)][pick(alphabet_lowercase)][pick(alphabet_lowercase)][pick(alphabet_lowercase)][pick(alphabet_lowercase)]" #define RECT new /datum/shape/rectangle -#define SQUARE new /datum/shape/square +#define SQUARE new /datum/shape/rectangle/square #define ELLIPSE new /datum/shape/ellipse -#define CIRCLE new /datum/shape/circle +#define CIRCLE new /datum/shape/ellipse/circle #define QTREE new /datum/quadtree #define SEARCH_QTREE(qtree, shape_range, flags) qtree.query_range(shape_range, null, flags) diff --git a/code/datums/quadtree.dm b/code/datums/quadtree.dm index 95e91d7aace0..200a51b358a0 100644 --- a/code/datums/quadtree.dm +++ b/code/datums/quadtree.dm @@ -60,8 +60,8 @@ /// Distance from the shape's topmost to bottommost extent. var/bounds_y = 0 -/datum/shape/New() - set_shape(arglist(args)) +/datum/shape/New(center_x, center_y) + set_shape(center_x, center_y) /// Assign shape variables. /datum/shape/proc/set_shape(center_x, center_y) @@ -80,9 +80,13 @@ /datum/shape/proc/contains_atom(atom/atom) return contains_xy(atom.x, atom.y) +/// Returns TRUE if this shape's bounding box intersects the provided shape's bounding box, otherwise FALSE. Generally faster than a full intersection test. +/datum/shape/proc/intersects_aabb(datum/shape/aabb) + return (abs(src.center_x - aabb.center_x) <= (src.bounds_x + aabb.bounds_x) * 0.5) && (abs(src.center_y - aabb.center_y) <= (src.bounds_x + aabb.bounds_x) * 0.5) + /// Returns TRUE if this shape intersects the provided rectangle shape, otherwise FALSE. -/datum/shape/proc/intersects_rect(datum/shape/rectangle/range) - return range.contains_xy(src.center_x, src.center_y) +/datum/shape/proc/intersects_rect(datum/shape/rectangle/rect) + return rect.contains_xy(src.center_x, src.center_y) /// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned rectangle. /datum/shape/rectangle @@ -91,6 +95,9 @@ /// Distance from the shape's topmost to bottommost extent. var/height = 0 +/datum/shape/rectangle/New(center_x, center_y, width, height) + set_shape(center_x, center_y, width, height) + /datum/shape/rectangle/set_shape(center_x, center_y, width, height) ..() src.bounds_x = width @@ -101,25 +108,20 @@ /datum/shape/rectangle/contains_xy(x, y) return (abs(center_x - x) <= width * 0.5) && (abs(center_y - y) <= height * 0.5) -/datum/shape/rectangle/intersects_rect(datum/shape/rectangle/range) - return (abs(src.center_x - range.center_x) <= (src.width + range.width) * 0.5) && (abs(src.center_y - range.center_y) <= (src.height + range.height) * 0.5) +/datum/shape/rectangle/intersects_rect(datum/shape/rectangle/rect) + return intersects_aabb(rect) /// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned square. -/datum/shape/square +/datum/shape/rectangle/square /// Distance between the shape's opposing extents. var/length = 0 -/datum/shape/square/set_shape(center_x, center_y, length) - ..() - src.bounds_x = length - src.bounds_y = length - src.length = length - -/datum/shape/square/contains_xy(x, y) - return (abs(center_x - x) <= length * 0.5) && (abs(center_y - y) <= length * 0.5) +/datum/shape/rectangle/square/New(center_x, center_y, length) + set_shape(center_x, center_y, length) -/datum/shape/square/intersects_rect(datum/shape/rectangle/range) - return (abs(src.center_x - range.center_x) <= (src.length + range.width) * 0.5) && (abs(src.center_y - range.center_y) <= (src.length + range.height) * 0.5) +/datum/shape/rectangle/square/set_shape(center_x, center_y, length) + ..(center_x, center_y, length, length) + src.length = length /// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned ellipse. /datum/shape/ellipse @@ -130,6 +132,9 @@ VAR_PROTECTED/_axis_x_sq = 0 VAR_PROTECTED/_axis_y_sq = 0 +/datum/shape/ellipse/New(center_x, center_y, width, height) + set_shape(center_x, center_y, width, height) + /datum/shape/ellipse/set_shape(center_x, center_y, width, height) ..() src.bounds_x = width @@ -142,39 +147,26 @@ /datum/shape/ellipse/contains_xy(x, y) return ((center_x - x)**2 / _axis_x_sq + (center_y - y)**2 / _axis_y_sq <= 1) -/datum/shape/ellipse/intersects_rect(datum/shape/rectangle/range) - if(range.contains_xy(src.center_x, src.center_y)) +/datum/shape/ellipse/intersects_rect(datum/shape/rectangle/rect) + if(..()) return TRUE - var/nearest_x = clamp(src.center_x, range.center_x - range.width * 0.5, range.center_x + range.width * 0.5) - var/nearest_y = clamp(src.center_y, range.center_y - range.height * 0.5, range.center_y + range.height * 0.5) + var/nearest_x = clamp(src.center_x, rect.center_x - rect.width * 0.5, rect.center_x + rect.width * 0.5) + var/nearest_y = clamp(src.center_y, rect.center_y - rect.height * 0.5, rect.center_y + rect.height * 0.5) return src.contains_xy(nearest_x, nearest_y) /// A simple geometric shape for testing collisions and intersections. This one is a circle. -/datum/shape/circle +/datum/shape/ellipse/circle /// Distance from the shape's center to edge. var/radius = 0 - VAR_PROTECTED/_radius_sq = 0 -/datum/shape/circle/set_shape(center_x, center_y, radius) - ..() - src.bounds_x = radius * 2 - src.bounds_y = radius * 2 - src.radius = radius - src._radius_sq = radius**2 - -/datum/shape/circle/contains_xy(x, y) - return ((center_x - x)**2 + (center_y - y)**2 <= _radius_sq) - -/datum/shape/circle/intersects_rect(datum/shape/rectangle/range) - if(range.contains_xy(src.center_x, src.center_y)) - return TRUE +/datum/shape/ellipse/circle/New(center_x, center_y, radius) + set_shape(center_x, center_y, radius) - var/nearest_x = clamp(src.center_x, range.center_x - range.width * 0.5, range.center_x + range.width * 0.5) - var/nearest_y = clamp(src.center_y, range.center_y - range.height * 0.5, range.center_y + range.height * 0.5) - - return src.contains_xy(nearest_x, nearest_y) +/datum/shape/ellipse/circle/set_shape(center_x, center_y, radius) + ..(center_x, center_y, radius * 2, radius * 2) + src.radius = radius /datum/quadtree/proc/subdivide() //Warning: this might give you eye cancer diff --git a/code/game/objects/items/devices/motion_detector.dm b/code/game/objects/items/devices/motion_detector.dm index 1ced1b7bcd00..dcbcc0dd8bc7 100644 --- a/code/game/objects/items/devices/motion_detector.dm +++ b/code/game/objects/items/devices/motion_detector.dm @@ -35,7 +35,7 @@ var/iff_signal = FACTION_MARINE actions_types = list(/datum/action/item_action) var/scanning = FALSE // controls if MD is in process of scan - var/datum/shape/square/range_bounds + var/datum/shape/rectangle/square/range_bounds var/long_range_locked = FALSE //only long-range MD var/ping_overlay