Skip to content

Commit

Permalink
Merge branch 'cmss13-devs:master' into newpizzacutter
Browse files Browse the repository at this point in the history
  • Loading branch information
Mensla authored Jul 16, 2024
2 parents 4df1a71 + 6a7fc16 commit 7aca07d
Show file tree
Hide file tree
Showing 20 changed files with 190 additions and 102 deletions.
3 changes: 3 additions & 0 deletions code/_macros.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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/rectangle/square
#define ELLIPSE new /datum/shape/ellipse
#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)

Expand Down
3 changes: 1 addition & 2 deletions code/controllers/subsystem/sound.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
145 changes: 113 additions & 32 deletions code/datums/quadtree.dm
Original file line number Diff line number Diff line change
Expand Up @@ -49,43 +49,124 @@
..()
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(center_x, center_y)
set_shape(center_x, center_y)

/// 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'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/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
/// 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/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
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/rect)
return intersects_aabb(rect)

/// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned square.
/datum/shape/rectangle/square
/// Distance between the shape's opposing extents.
var/length = 0

/datum/shape/rectangle/square/New(center_x, center_y, length)
set_shape(center_x, center_y, length)

/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
/// 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/New(center_x, center_y, width, height)
set_shape(center_x, center_y, width, height)

/datum/shape/rectangle/New(x, y, w, h)
/datum/shape/ellipse/set_shape(center_x, center_y, width, height)
..()
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 = 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/rect)
if(..())
return TRUE

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/ellipse/circle
/// Distance from the shape's center to edge.
var/radius = 0

/datum/shape/ellipse/circle/New(center_x, center_y, radius)
set_shape(center_x, center_y, radius)

/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
Expand All @@ -96,7 +177,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)
Expand All @@ -118,11 +199,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)
Expand All @@ -136,7 +217,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
Expand Down
20 changes: 10 additions & 10 deletions code/game/camera_manager/camera_manager.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions code/game/machinery/computer/dropship_weapons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
11 changes: 3 additions & 8 deletions code/game/objects/items/devices/motion_detector.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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/rectangle/square/range_bounds
var/long_range_locked = FALSE //only long-range MD
var/ping_overlay

Expand All @@ -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()
Expand Down Expand Up @@ -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)

Expand Down
6 changes: 3 additions & 3 deletions code/modules/cm_aliens/structures/special/egg_morpher.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand Down
6 changes: 2 additions & 4 deletions code/modules/cm_marines/equipment/kit_boxes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,9 @@
return TRUE

/obj/item/spec_kit/proc/select_and_spawn(mob/living/carbon/human/user)
var/selection = tgui_input_list(user, "Pick your specialist equipment type.", "Specialist Kit Selection", GLOB.available_specialist_kit_boxes)
var/selection = tgui_input_list(user, "Pick your specialist equipment type.", "Specialist Kit Selection", GLOB.available_specialist_kit_boxes, 10 SECONDS)
if(!selection || QDELETED(src))
return FALSE
if(!skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_TRAINED) && !skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_ALL))
to_chat(user, SPAN_WARNING("You already unwrapped your [name], give this one to someone else!"))
return
if(!GLOB.available_specialist_kit_boxes[selection] || GLOB.available_specialist_kit_boxes[selection] <= 0)
to_chat(user, SPAN_WARNING("No more kits of this type may be chosen!"))
return FALSE
Expand Down Expand Up @@ -299,6 +296,7 @@
user.put_in_hands(spec_box)
card.set_assignment((user.assigned_squad && squad_assignment_update ? (user.assigned_squad.name + " ") : "") + card.assignment + " ([specialist_assignment])")
GLOB.data_core.manifest_modify(user.real_name, WEAKREF(user), card.assignment)
GLOB.available_specialist_kit_boxes[selection]--
return TRUE
return FALSE

Expand Down
2 changes: 1 addition & 1 deletion code/modules/defenses/bell_tower.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 5 additions & 5 deletions code/modules/defenses/planted_flag.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 7aca07d

Please sign in to comment.