ad8fc41ace
Work-Package: M03-RND-STREAMING-PLANNER-001 Agent: sindo-main-codex Tests: stale-class-cache headless planner verifier passes Fidelity: type-resolution-only fix; planning behavior unchanged
105 lines
3.7 KiB
GDScript
105 lines
3.7 KiB
GDScript
class_name StreamingTargetPlanner
|
|
extends RefCounted
|
|
|
|
## Scene-free deterministic calculator for runtime/editor ADT target sets.
|
|
## It creates no tasks or renderer resources and never mutates its inputs.
|
|
|
|
const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd")
|
|
const ADT_TILE_COORDINATE_SCRIPT := preload("res://src/domain/coordinates/adt_tile_coordinate.gd")
|
|
const TARGET_PLAN_SCRIPT := preload("res://src/render/streaming/streaming_target_plan.gd")
|
|
|
|
|
|
## Builds runtime wanted and retained sets, including the existing edge/corner
|
|
## prefetch centers. `available_tile_paths_by_key` is read only for membership.
|
|
func plan_runtime(
|
|
available_tile_paths_by_key: Dictionary,
|
|
focus_position: GodotWorldPosition,
|
|
policy
|
|
):
|
|
var focus_tile = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile(focus_position)
|
|
var prefetch_centers := _prefetch_centers(focus_position, focus_tile, policy)
|
|
var wanted_tile_keys := _available_keys_within_radius(
|
|
available_tile_paths_by_key,
|
|
prefetch_centers,
|
|
policy.load_tile_radius()
|
|
)
|
|
var retained_tile_keys := _available_keys_within_radius(
|
|
available_tile_paths_by_key,
|
|
prefetch_centers,
|
|
policy.retained_tile_radius()
|
|
)
|
|
return TARGET_PLAN_SCRIPT.new(focus_tile, wanted_tile_keys, retained_tile_keys)
|
|
|
|
|
|
## Builds the editor square preview set. Editor wanted and retained sets are
|
|
## identical and a negative radius is clamped to the focus tile.
|
|
func plan_editor(
|
|
available_tile_paths_by_key: Dictionary,
|
|
focus_position: GodotWorldPosition,
|
|
editor_preview_tile_radius: int
|
|
):
|
|
var focus_tile = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile(focus_position)
|
|
var centers: Array[AdtTileCoordinate] = [focus_tile]
|
|
var wanted_tile_keys := _available_keys_within_radius(
|
|
available_tile_paths_by_key,
|
|
centers,
|
|
maxi(0, editor_preview_tile_radius)
|
|
)
|
|
return TARGET_PLAN_SCRIPT.new(focus_tile, wanted_tile_keys, wanted_tile_keys)
|
|
|
|
|
|
func _prefetch_centers(
|
|
focus_position: GodotWorldPosition,
|
|
focus_tile: AdtTileCoordinate,
|
|
policy
|
|
) -> Array[AdtTileCoordinate]:
|
|
var centers: Array[AdtTileCoordinate] = [focus_tile]
|
|
var threshold: float = policy.clamped_boundary_prefetch_threshold()
|
|
if threshold <= 0.0:
|
|
return centers
|
|
|
|
var tile_local_position = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile_local(focus_position)
|
|
var local_x_fraction: float = tile_local_position.east_yards / COORDINATE_MAPPER_SCRIPT.ADT_TILE_SIZE_YARDS
|
|
var local_z_fraction: float = tile_local_position.south_yards / COORDINATE_MAPPER_SCRIPT.ADT_TILE_SIZE_YARDS
|
|
var tile_x_offsets: Array[int] = [0]
|
|
var tile_y_offsets: Array[int] = [0]
|
|
|
|
if local_x_fraction <= threshold:
|
|
tile_x_offsets.append(-1)
|
|
elif local_x_fraction >= 1.0 - threshold:
|
|
tile_x_offsets.append(1)
|
|
|
|
if local_z_fraction <= threshold:
|
|
tile_y_offsets.append(-1)
|
|
elif local_z_fraction >= 1.0 - threshold:
|
|
tile_y_offsets.append(1)
|
|
|
|
for tile_x_offset in tile_x_offsets:
|
|
for tile_y_offset in tile_y_offsets:
|
|
if tile_x_offset == 0 and tile_y_offset == 0:
|
|
continue
|
|
centers.append(ADT_TILE_COORDINATE_SCRIPT.new(
|
|
focus_tile.tile_x + tile_x_offset,
|
|
focus_tile.tile_y + tile_y_offset
|
|
))
|
|
return centers
|
|
|
|
|
|
func _available_keys_within_radius(
|
|
available_tile_paths_by_key: Dictionary,
|
|
centers: Array[AdtTileCoordinate],
|
|
tile_radius: int
|
|
) -> Dictionary:
|
|
var available_tile_keys: Dictionary = {}
|
|
for center in centers:
|
|
for tile_y in range(center.tile_y - tile_radius, center.tile_y + tile_radius + 1):
|
|
for tile_x in range(center.tile_x - tile_radius, center.tile_x + tile_radius + 1):
|
|
var tile_key := _tile_key(tile_x, tile_y)
|
|
if available_tile_paths_by_key.has(tile_key):
|
|
available_tile_keys[tile_key] = true
|
|
return available_tile_keys
|
|
|
|
|
|
func _tile_key(tile_x: int, tile_y: int) -> String:
|
|
return "%d_%d" % [tile_x, tile_y]
|