class_name AdtTerrainQuery extends TerrainQuery ## TerrainQuery adapter for existing native ADT dictionaries. ## Parsed tiles, including unavailable results, are cached for this query's ## lifetime. The adapter is main-thread owned and creates no scene resources. const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd") const CHUNK_SIZE_UNITS := COORDINATE_MAPPER_SCRIPT.ADT_CHUNK_SIZE_YARDS const OUTER_GRID_UNIT_SIZE := CHUNK_SIZE_UNITS / 8.0 var _extracted_directory: String var _map_directory_name: String var _adt_tile_loader_override: Callable var _adt_tile_cache: Dictionary = {} var _failure_code_by_tile_key: Dictionary = {} ## Creates an ADT query for one map. `adt_tile_loader_override` is an optional ## test/data-source seam receiving the absolute ADT path and returning a Dictionary. func _init( extracted_directory: String, map_directory_name: String, adt_tile_loader_override: Callable = Callable() ) -> void: _extracted_directory = extracted_directory _map_directory_name = map_directory_name _adt_tile_loader_override = adt_tile_loader_override ## Samples the existing 9x9 outer-height grid with bilinear interpolation. func sample_ground_height(godot_world_position: GodotWorldPosition) -> TerrainGroundSample: var tile_coordinate = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile(godot_world_position) var tile_local_position = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile_local(godot_world_position) var chunk_coordinate = COORDINATE_MAPPER_SCRIPT.adt_tile_local_to_chunk( tile_coordinate, tile_local_position ) var tile_key := _tile_key(tile_coordinate.tile_x, tile_coordinate.tile_y) var adt_tile_data := _load_adt_tile(tile_coordinate.tile_x, tile_coordinate.tile_y) if adt_tile_data.is_empty(): return TerrainGroundSample.unavailable( _failure_code_by_tile_key.get(tile_key, &"adt_tile_unavailable") ) var tile_origin := _find_tile_origin(adt_tile_data) var chunk := _find_chunk( adt_tile_data, clampi(chunk_coordinate.chunk_x, 0, 15), clampi(chunk_coordinate.chunk_y, 0, 15), tile_origin ) if chunk.is_empty(): return TerrainGroundSample.unavailable(&"adt_chunk_missing") var heights: PackedFloat32Array = chunk.get("heights", PackedFloat32Array()) if heights.size() < 145: return TerrainGroundSample.unavailable(&"adt_chunk_heights_invalid") var chunk_origin: Vector3 = chunk.get("origin", Vector3.ZERO) var chunk_local_x := godot_world_position.x_units - chunk_origin.x var chunk_local_z := godot_world_position.z_units - chunk_origin.z var grid_x := clampf(chunk_local_x / OUTER_GRID_UNIT_SIZE, 0.0, 8.0) var grid_z := clampf(chunk_local_z / OUTER_GRID_UNIT_SIZE, 0.0, 8.0) var x0 := clampi(int(floor(grid_x)), 0, 8) var z0 := clampi(int(floor(grid_z)), 0, 8) var x1 := mini(x0 + 1, 8) var z1 := mini(z0 + 1, 8) var fraction_x := grid_x - float(x0) var fraction_z := grid_z - float(z0) var height_x0 := lerpf( heights[_outer_height_index(z0, x0)], heights[_outer_height_index(z0, x1)], fraction_x ) var height_x1 := lerpf( heights[_outer_height_index(z1, x0)], heights[_outer_height_index(z1, x1)], fraction_x ) return TerrainGroundSample.available( chunk_origin.y + lerpf(height_x0, height_x1, fraction_z) ) func _load_adt_tile(tile_x: int, tile_y: int) -> Dictionary: var tile_key := _tile_key(tile_x, tile_y) if _adt_tile_cache.has(tile_key): return _adt_tile_cache[tile_key] var resource_path := _extracted_directory.path_join( "World/Maps/%s/%s_%d_%d.adt" % [_map_directory_name, _map_directory_name, tile_x, tile_y] ) var absolute_path := ProjectSettings.globalize_path(resource_path) var adt_tile_data: Dictionary = {} var failure_code: StringName = &"" if _adt_tile_loader_override.is_valid(): var override_result = _adt_tile_loader_override.call(absolute_path) if override_result is Dictionary: adt_tile_data = override_result else: failure_code = &"adt_loader_result_invalid" elif not ClassDB.class_exists("ADTLoader"): failure_code = &"adt_loader_unavailable" elif not FileAccess.file_exists(absolute_path): failure_code = &"adt_source_missing" else: var loader = ClassDB.instantiate("ADTLoader") if loader != null: var load_result = loader.call("load_adt", absolute_path) if load_result is Dictionary: adt_tile_data = load_result if adt_tile_data.is_empty(): failure_code = &"adt_load_failed" if adt_tile_data.is_empty() and failure_code.is_empty(): failure_code = &"adt_tile_unavailable" _adt_tile_cache[tile_key] = adt_tile_data _failure_code_by_tile_key[tile_key] = failure_code return adt_tile_data func _find_chunk( adt_tile_data: Dictionary, chunk_x: int, chunk_y: int, tile_origin: Vector3 ) -> Dictionary: for chunk in adt_tile_data.get("chunks", []) as Array: if chunk.is_empty(): continue var chunk_origin: Vector3 = chunk.get("origin", Vector3.ZERO) var local_origin := chunk_origin - tile_origin var candidate_chunk_x := clampi(int(round(local_origin.x / CHUNK_SIZE_UNITS)), 0, 15) var candidate_chunk_y := clampi(int(round(local_origin.z / CHUNK_SIZE_UNITS)), 0, 15) if candidate_chunk_x == chunk_x and candidate_chunk_y == chunk_y: return chunk return {} func _find_tile_origin(adt_tile_data: Dictionary) -> Vector3: var has_origin := false var minimum_x := 0.0 var minimum_z := 0.0 for chunk in adt_tile_data.get("chunks", []) as Array: if chunk.is_empty(): continue var chunk_origin: Vector3 = chunk.get("origin", Vector3.ZERO) if not has_origin: minimum_x = chunk_origin.x minimum_z = chunk_origin.z has_origin = true else: minimum_x = minf(minimum_x, chunk_origin.x) minimum_z = minf(minimum_z, chunk_origin.z) return Vector3(minimum_x, 0.0, minimum_z) if has_origin else Vector3.ZERO func _tile_key(tile_x: int, tile_y: int) -> String: return "%d_%d" % [tile_x, tile_y] func _outer_height_index(row: int, column: int) -> int: return row * 17 + column