2 Commits

2 changed files with 93 additions and 8 deletions

View File

@@ -46,6 +46,7 @@ var noise_generator: FastNoiseLite
var altitude_generator: FastNoiseLite
var wire_connections: Dictionary = {}
var chunk_candidate_cache: Dictionary = {} #node cache (metadata)
var prop_candidate_cache: Dictionary = {}
var pending_generation_cells: Array[Vector2i] = []
var pending_cleanup_cells: Array[Vector2i] = []
var pending_wire_cells: Array[Vector2i] = []
@@ -89,7 +90,7 @@ func _update_set_pieces() -> void:
var local_biome = ""
if manual_biome != null:
local_biome = manual_biome.nome
local_biome = manual_biome.name
else:
var grid_x = roundi(sp.global_position.x / chunk_size)
var grid_z = roundi(sp.global_position.z / chunk_size)
@@ -115,7 +116,7 @@ func _destroy_and_regenrate_world() -> void:
#Destroy all models
for pos in board.keys():
var cella = board[pos]
if cella["type"] != "obstacle":
if not _is_persistent_obstacle(cella):
if cella.has("node") and is_instance_valid(cella["node"]):
cella["node"].queue_free()
@@ -349,6 +350,8 @@ func _replace_rail_chunk_instance(chunk_root: Node3D) -> bool:
chunk_root.queue_free()
return true
#rebuild the scene catalogue from res://tgcc/chunk/railway/scene,
#than serach on the current scene which chunks can be changed and for each one chose a new scene (the same kind)
func _refresh_rail_chunks() -> void:
print("update rail chunks")
_warm_rail_chunk_catalogue()
@@ -376,6 +379,9 @@ func _refresh_world(center: Vector2i) -> void:
_rebuild_generation_queue(center)
_rebuild_cleanup_queue(center)
#check cells around the train by rings:
#first the center, then borders with distance = 1, the distance = 2 and so to eye_line.
#Use maxi(abs(x), abs(z)) to know the border of the ring. Cells closest to the train have more priority
func _rebuild_generation_queue(center: Vector2i) -> void:
pending_generation_cells.clear()
@@ -390,13 +396,16 @@ func _rebuild_generation_queue(center: Vector2i) -> void:
continue
pending_generation_cells.append(grid_pos)
#For each cells add to cleanup queue the cells too far
#Use eye_line plus a margin value to hide chunks not instantly when go out of the eye line
#Persistent obstacle cells are not deleted (rails or set_pieces)
func _rebuild_cleanup_queue(center: Vector2i) -> void:
pending_cleanup_cells.clear()
var safe_margin = 2
for grid_pos in board.keys():
var cell = board[grid_pos]
if cell["type"] == "obstacle":
if _is_persistent_obstacle(cell):
continue
var dist_x = abs(grid_pos.x - center.x)
@@ -424,7 +433,7 @@ func _drain_cleanup_queue() -> void:
continue
var cell = board[grid_pos]
if cell["type"] == "obstacle":
if _is_persistent_obstacle(cell):
continue
if cell.has("node") and is_instance_valid(cell["node"]):
@@ -462,6 +471,8 @@ func _generate_pieces_around_train(center: Vector2i) -> void:
if not ce_obstacle:
_add_compatible_biome(grid_pos)
#Decice which catalogue of chunks use for a cell. If manual_biome is set use always it
#Otherwise read noise_generator.get_noise_2d to give an index for biome_list
func _choose_catalogue_by_cell(grid_pos: Vector2i) -> Array[PackedScene]:
if manual_biome != null:
return manual_biome.available_chunks
@@ -484,6 +495,14 @@ func _get_chunk_uniqueness_from_info(info_node: Node) -> int:
return clampi(info_node.uniqueness, 0, MAX_CHUNK_UNIQUENESS)
return 0
func _get_prop_uniqueness_from_info(info_node: Node) -> int:
if info_node != null and "uniqueness" in info_node:
return clampi(info_node.uniqueness, 0, MAX_CHUNK_UNIQUENESS)
return -1
func _is_persistent_obstacle(cell: Dictionary) -> bool:
return cell.get("type", "") == "obstacle" and cell.get("persistent", true)
func _has_nearby_unique_chunk(grid_pos: Vector2i, uniqueness: int) -> bool:
if uniqueness <= 0:
return false
@@ -527,6 +546,61 @@ func _pick_backup_scene(zone_catalogue: Array[PackedScene], grid_pos: Vector2i)
return scene
return zone_catalogue[0]
func _get_prop_scene_cache_key(scene: PackedScene) -> String:
if scene == null:
return ""
if scene.resource_path != "":
return scene.resource_path
return "prop_scene_%s" % scene.get_instance_id()
func _get_prop_scene_uniqueness(scene: PackedScene) -> int:
if scene == null:
return -1
var key = _get_prop_scene_cache_key(scene)
if prop_candidate_cache.has(key):
return prop_candidate_cache[key]
var preview_prop = scene.instantiate()
var uniqueness = _get_prop_uniqueness_from_info(preview_prop)
if uniqueness == -1:
var prop_info_list: Array[Node] = []
collect_all_propinfo(preview_prop, prop_info_list)
if not prop_info_list.is_empty():
uniqueness = _get_prop_uniqueness_from_info(prop_info_list[0])
preview_prop.queue_free()
prop_candidate_cache[key] = uniqueness
return uniqueness
func _get_marker_prop_uniqueness(marker: Node) -> int:
var uniqueness = _get_prop_uniqueness_from_info(marker)
if uniqueness == -1:
return 0
return uniqueness
func _pick_weighted_prop_scene(marker: Node) -> PackedScene:
var candidates = []
var fallback_uniqueness = _get_marker_prop_uniqueness(marker)
for prop_scene in marker.available_props:
if prop_scene == null:
continue
var uniqueness = _get_prop_scene_uniqueness(prop_scene)
if uniqueness == -1:
uniqueness = fallback_uniqueness
candidates.append({
"scene": prop_scene,
"weight": _get_uniqueness_pick_weight(uniqueness),
"uniqueness": uniqueness
})
if candidates.is_empty():
return null
return _pick_weighted_candidate(candidates).scene
func _spawn_props_for_chunk(root: Node) -> void:
var prop_markers: Array[Node] = []
collect_all_propinfo(root, prop_markers)
@@ -538,7 +612,7 @@ func _spawn_prop_for_marker(marker: Node) -> void:
if marker.available_props.is_empty():
return
var prop_scene = marker.available_props.pick_random()
var prop_scene = _pick_weighted_prop_scene(marker)
if prop_scene == null:
return
@@ -551,6 +625,8 @@ func _spawn_prop_for_marker(marker: Node) -> void:
marker.add_child(prop_node)
prop_node.transform = Transform3D.IDENTITY
#Using a vertical raycast from the top to the bottom at the center of the cell
#If there is a collision search for a new chunk node
func _register_cell_with_ray(grid_pos: Vector2i) -> bool:
if board.has(grid_pos): return true
@@ -623,7 +699,8 @@ func _register_cell_with_ray(grid_pos: Vector2i) -> bool:
"node": root_chunk,
"info": right_info_node,
"have_lamppost": have_lamppost,
"uniqueness": uniqueness
"uniqueness": uniqueness,
"persistent": true
}
if have_lamppost:
@@ -632,6 +709,11 @@ func _register_cell_with_ray(grid_pos: Vector2i) -> bool:
return true
return false
#For the cell to fill check the contrains for the four neighbors:
#If the neighbor at north have and exit on south then new chunk should have exit to north (the same for east and ovest)
#Contrains: 1 -> connection is mandatory; 0 -> no connection; -1 -> no contrain because there is no neighbor or it already exits
#When generator know the contrains take all chunks and try all different rotations
#A candidate is valid only if all contrains are correct
func _add_compatible_biome(grid_pos: Vector2i) -> void:
var req_conn_north = _needed_connection(grid_pos + Vector2i(0, -1), "south")
var req_conn_est = _needed_connection(grid_pos + Vector2i(1, 0), "west")
@@ -730,7 +812,7 @@ func _add_compatible_biome(grid_pos: Vector2i) -> void:
_apply_river_flow_direction(new_chunk, river_flow_direction)
board[grid_pos] = {
"type": "bioma",
"type": "biome",
"exit": choise.data["connections"],
"river_exit": choise.data["river_connections"],
"river_flow_direction": river_flow_direction,
@@ -772,6 +854,8 @@ func _add_compatible_biome(grid_pos: Vector2i) -> void:
"uniqueness": backup_uniqueness
}
#Check if a neighbors have a river direction: if yes try to continue it,
#otherwise create a direction based on connections
func _calculate_river_flow_direction(grid_pos: Vector2i, river_connections: Dictionary) -> Vector2:
var connected_sides: Array[String] = []
for side in RIVER_SIDE_ORDER:
@@ -1006,7 +1090,7 @@ func _connect_lamppost_wires(new_board_pos: Vector2i) -> void:
if not wire_connections.has(closest_id): wire_connections[closest_id] = 0
wire_connections[closest_id] += 1
#draw lamppost
#draw lamppost wires
func _draw_parable(p1: Vector3, p2: Vector3, parent: Node3D) -> void:
var segments = 15
var lowering = 1.5

View File

@@ -2,3 +2,4 @@ extends Marker3D
class_name PropInfo
@export var available_props: Array[PackedScene]
@export_range(0, 5, 1) var uniqueness: int = 0 #0=common; 5=unique