add uniqueness par for props
This commit is contained in:
@@ -46,6 +46,7 @@ var noise_generator: FastNoiseLite
|
|||||||
var altitude_generator: FastNoiseLite
|
var altitude_generator: FastNoiseLite
|
||||||
var wire_connections: Dictionary = {}
|
var wire_connections: Dictionary = {}
|
||||||
var chunk_candidate_cache: Dictionary = {} #node cache (metadata)
|
var chunk_candidate_cache: Dictionary = {} #node cache (metadata)
|
||||||
|
var prop_candidate_cache: Dictionary = {}
|
||||||
var pending_generation_cells: Array[Vector2i] = []
|
var pending_generation_cells: Array[Vector2i] = []
|
||||||
var pending_cleanup_cells: Array[Vector2i] = []
|
var pending_cleanup_cells: Array[Vector2i] = []
|
||||||
var pending_wire_cells: Array[Vector2i] = []
|
var pending_wire_cells: Array[Vector2i] = []
|
||||||
@@ -89,7 +90,7 @@ func _update_set_pieces() -> void:
|
|||||||
|
|
||||||
var local_biome = ""
|
var local_biome = ""
|
||||||
if manual_biome != null:
|
if manual_biome != null:
|
||||||
local_biome = manual_biome.nome
|
local_biome = manual_biome.name
|
||||||
else:
|
else:
|
||||||
var grid_x = roundi(sp.global_position.x / chunk_size)
|
var grid_x = roundi(sp.global_position.x / chunk_size)
|
||||||
var grid_z = roundi(sp.global_position.z / 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
|
#Destroy all models
|
||||||
for pos in board.keys():
|
for pos in board.keys():
|
||||||
var cella = board[pos]
|
var cella = board[pos]
|
||||||
if cella["type"] != "obstacle":
|
if not _is_persistent_obstacle(cella):
|
||||||
if cella.has("node") and is_instance_valid(cella["node"]):
|
if cella.has("node") and is_instance_valid(cella["node"]):
|
||||||
cella["node"].queue_free()
|
cella["node"].queue_free()
|
||||||
|
|
||||||
@@ -349,6 +350,8 @@ func _replace_rail_chunk_instance(chunk_root: Node3D) -> bool:
|
|||||||
chunk_root.queue_free()
|
chunk_root.queue_free()
|
||||||
return true
|
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:
|
func _refresh_rail_chunks() -> void:
|
||||||
print("update rail chunks")
|
print("update rail chunks")
|
||||||
_warm_rail_chunk_catalogue()
|
_warm_rail_chunk_catalogue()
|
||||||
@@ -376,6 +379,9 @@ func _refresh_world(center: Vector2i) -> void:
|
|||||||
_rebuild_generation_queue(center)
|
_rebuild_generation_queue(center)
|
||||||
_rebuild_cleanup_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:
|
func _rebuild_generation_queue(center: Vector2i) -> void:
|
||||||
pending_generation_cells.clear()
|
pending_generation_cells.clear()
|
||||||
|
|
||||||
@@ -390,13 +396,16 @@ func _rebuild_generation_queue(center: Vector2i) -> void:
|
|||||||
continue
|
continue
|
||||||
pending_generation_cells.append(grid_pos)
|
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:
|
func _rebuild_cleanup_queue(center: Vector2i) -> void:
|
||||||
pending_cleanup_cells.clear()
|
pending_cleanup_cells.clear()
|
||||||
var safe_margin = 2
|
var safe_margin = 2
|
||||||
|
|
||||||
for grid_pos in board.keys():
|
for grid_pos in board.keys():
|
||||||
var cell = board[grid_pos]
|
var cell = board[grid_pos]
|
||||||
if cell["type"] == "obstacle":
|
if _is_persistent_obstacle(cell):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var dist_x = abs(grid_pos.x - center.x)
|
var dist_x = abs(grid_pos.x - center.x)
|
||||||
@@ -424,7 +433,7 @@ func _drain_cleanup_queue() -> void:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
var cell = board[grid_pos]
|
var cell = board[grid_pos]
|
||||||
if cell["type"] == "obstacle":
|
if _is_persistent_obstacle(cell):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if cell.has("node") and is_instance_valid(cell["node"]):
|
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:
|
if not ce_obstacle:
|
||||||
_add_compatible_biome(grid_pos)
|
_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]:
|
func _choose_catalogue_by_cell(grid_pos: Vector2i) -> Array[PackedScene]:
|
||||||
if manual_biome != null:
|
if manual_biome != null:
|
||||||
return manual_biome.available_chunks
|
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 clampi(info_node.uniqueness, 0, MAX_CHUNK_UNIQUENESS)
|
||||||
return 0
|
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:
|
func _has_nearby_unique_chunk(grid_pos: Vector2i, uniqueness: int) -> bool:
|
||||||
if uniqueness <= 0:
|
if uniqueness <= 0:
|
||||||
return false
|
return false
|
||||||
@@ -527,6 +546,61 @@ func _pick_backup_scene(zone_catalogue: Array[PackedScene], grid_pos: Vector2i)
|
|||||||
return scene
|
return scene
|
||||||
return zone_catalogue[0]
|
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:
|
func _spawn_props_for_chunk(root: Node) -> void:
|
||||||
var prop_markers: Array[Node] = []
|
var prop_markers: Array[Node] = []
|
||||||
collect_all_propinfo(root, prop_markers)
|
collect_all_propinfo(root, prop_markers)
|
||||||
@@ -538,7 +612,7 @@ func _spawn_prop_for_marker(marker: Node) -> void:
|
|||||||
if marker.available_props.is_empty():
|
if marker.available_props.is_empty():
|
||||||
return
|
return
|
||||||
|
|
||||||
var prop_scene = marker.available_props.pick_random()
|
var prop_scene = _pick_weighted_prop_scene(marker)
|
||||||
if prop_scene == null:
|
if prop_scene == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -551,6 +625,8 @@ func _spawn_prop_for_marker(marker: Node) -> void:
|
|||||||
marker.add_child(prop_node)
|
marker.add_child(prop_node)
|
||||||
prop_node.transform = Transform3D.IDENTITY
|
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:
|
func _register_cell_with_ray(grid_pos: Vector2i) -> bool:
|
||||||
if board.has(grid_pos): return true
|
if board.has(grid_pos): return true
|
||||||
|
|
||||||
@@ -623,7 +699,8 @@ func _register_cell_with_ray(grid_pos: Vector2i) -> bool:
|
|||||||
"node": root_chunk,
|
"node": root_chunk,
|
||||||
"info": right_info_node,
|
"info": right_info_node,
|
||||||
"have_lamppost": have_lamppost,
|
"have_lamppost": have_lamppost,
|
||||||
"uniqueness": uniqueness
|
"uniqueness": uniqueness,
|
||||||
|
"persistent": true
|
||||||
}
|
}
|
||||||
|
|
||||||
if have_lamppost:
|
if have_lamppost:
|
||||||
@@ -632,6 +709,11 @@ func _register_cell_with_ray(grid_pos: Vector2i) -> bool:
|
|||||||
return true
|
return true
|
||||||
return false
|
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:
|
func _add_compatible_biome(grid_pos: Vector2i) -> void:
|
||||||
var req_conn_north = _needed_connection(grid_pos + Vector2i(0, -1), "south")
|
var req_conn_north = _needed_connection(grid_pos + Vector2i(0, -1), "south")
|
||||||
var req_conn_est = _needed_connection(grid_pos + Vector2i(1, 0), "west")
|
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)
|
_apply_river_flow_direction(new_chunk, river_flow_direction)
|
||||||
|
|
||||||
board[grid_pos] = {
|
board[grid_pos] = {
|
||||||
"type": "bioma",
|
"type": "biome",
|
||||||
"exit": choise.data["connections"],
|
"exit": choise.data["connections"],
|
||||||
"river_exit": choise.data["river_connections"],
|
"river_exit": choise.data["river_connections"],
|
||||||
"river_flow_direction": river_flow_direction,
|
"river_flow_direction": river_flow_direction,
|
||||||
@@ -772,6 +854,8 @@ func _add_compatible_biome(grid_pos: Vector2i) -> void:
|
|||||||
"uniqueness": backup_uniqueness
|
"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:
|
func _calculate_river_flow_direction(grid_pos: Vector2i, river_connections: Dictionary) -> Vector2:
|
||||||
var connected_sides: Array[String] = []
|
var connected_sides: Array[String] = []
|
||||||
for side in RIVER_SIDE_ORDER:
|
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
|
if not wire_connections.has(closest_id): wire_connections[closest_id] = 0
|
||||||
wire_connections[closest_id] += 1
|
wire_connections[closest_id] += 1
|
||||||
|
|
||||||
#draw lamppost
|
#draw lamppost wires
|
||||||
func _draw_parable(p1: Vector3, p2: Vector3, parent: Node3D) -> void:
|
func _draw_parable(p1: Vector3, p2: Vector3, parent: Node3D) -> void:
|
||||||
var segments = 15
|
var segments = 15
|
||||||
var lowering = 1.5
|
var lowering = 1.5
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ extends Marker3D
|
|||||||
class_name PropInfo
|
class_name PropInfo
|
||||||
|
|
||||||
@export var available_props: Array[PackedScene]
|
@export var available_props: Array[PackedScene]
|
||||||
|
@export_range(0, 5, 1) var uniqueness: int = 0 #0=common; 5=unique
|
||||||
|
|||||||
Reference in New Issue
Block a user