Adds basic camp generation and placement

Adds basic camp generation and placement logic to the map generation process.

It attempts to place the camp in a valid location, avoiding paths and water bodies. It also sets the player's spawn point to the center of the generated camp, including some basic camp props like a tent, campfire, and bed.

Additionally, vegetation spawning is now dependent on the `should_spawn_*` methods of the `CellDataResource`, allowing more control over what spawns where.
This commit is contained in:
Dan Baker 2025-06-29 14:05:48 +01:00
parent 3959333534
commit a1efaf6294
8 changed files with 402 additions and 125 deletions

View file

@ -5,6 +5,8 @@ var map_height: int = 200
var map_width: int = 200 var map_width: int = 200
var _map_data: Array var _map_data: Array
var _biome_data: Dictionary = {} var _biome_data: Dictionary = {}
var _camp_data: Dictionary = {}
var spawn_point: Vector3 = Vector3(0, 0, 0)
# Property for map_data with logging # Property for map_data with logging
var map_data: Array: var map_data: Array:

View file

@ -45,13 +45,13 @@ func spawn_content():
if cell_info == null: if cell_info == null:
return return
if tree_spawner: if tree_spawner and cell_info.should_spawn_trees():
tree_spawner.spawn_trees_for_cell(cell_info) tree_spawner.spawn_trees_for_cell(cell_info)
if grass_spawner: if grass_spawner and cell_info.should_spawn_grass():
grass_spawner.spawn_grass_for_cell(cell_info) grass_spawner.spawn_grass_for_cell(cell_info)
if bush_spawner: if bush_spawner and cell_info.should_spawn_bushes():
bush_spawner.spawn_bushes_for_cell(cell_info) bush_spawner.spawn_bushes_for_cell(cell_info)
if flower_spawner: if flower_spawner and cell_info.should_spawn_flowers():
flower_spawner.spawn_flowers_for_cell(cell_info) flower_spawner.spawn_flowers_for_cell(cell_info)

View file

@ -40,7 +40,7 @@ func spawn_trees_for_cell(cell_info: CellDataResource):
attempts += 1 attempts += 1
Log.pr("Spawned %d of %d trees in cell" % [spawned_count, cell_info.trees.size()]) #Log.pr("Spawned %d of %d trees in cell" % [spawned_count, cell_info.trees.size()])
func get_random_position() -> Vector3: func get_random_position() -> Vector3:
var rng = parent_ground_tile.get_rng() var rng = parent_ground_tile.get_rng()

View file

@ -6,7 +6,8 @@ extends CharacterBody3D
var target_velocity = Vector3.ZERO var target_velocity = Vector3.ZERO
func _ready() -> void: func _ready() -> void:
position = Vector3.ZERO position = Global.spawn_point
%Camp.position = Global.spawn_point
func _physics_process(delta): func _physics_process(delta):
RenderingServer.global_shader_parameter_set("player_position", position) RenderingServer.global_shader_parameter_set("player_position", position)

View file

@ -5,13 +5,134 @@ extends Resource
@export var x: int = 0 @export var x: int = 0
@export var z: int = 0 @export var z: int = 0
@export var vegetation_density: float = 0.5 # Core cell type properties - these will trigger automatic updates
@export var ground_compaction: float = 0.0 @export var camp: bool = false: set = set_camp
@export var water: float = 0 @export var path: bool = false: set = set_trail
@export var moisture_level: float = 0.6 @export var water: bool = false: set = set_water
# Dependent properties that get automatically managed
@export var vegetation_density: float = 0.5: set = set_vegetation_density
@export var ground_compaction: float = 0.0: set = set_ground_compaction
@export var moisture_level: float = 0.6
@export var trees: Array = [] @export var trees: Array = []
# Internal flag to prevent infinite recursion during initialization
var _initializing: bool = false
func _init():
_initializing = true
# Set default values
vegetation_density = 0.5
ground_compaction = 0.0
camp = false
path = false
water = false
_initializing = false
func set_camp(value: bool):
camp = value
if not _initializing:
_update_dependent_properties()
## Using a different name due to Resource using the function set_path in Godot
func set_trail(value: bool):
path = value
if not _initializing:
_update_dependent_properties()
func set_water(value: bool):
water = value
if not _initializing:
_update_dependent_properties()
func set_vegetation_density(value: float):
# Only allow manual setting if not a special cell type
if _initializing or not (camp or path or water):
vegetation_density = value
else:
vegetation_density = 0.0
func set_ground_compaction(value: float):
# Camp always has maximum compaction, others can be set manually
if camp:
ground_compaction = 1.0
elif not _initializing:
ground_compaction = value
func _update_dependent_properties():
# Update vegetation density - always 0 for camp, path, or water
if camp or path or water:
vegetation_density = 0.0
# Update ground compaction - camp always has maximum compaction
if camp:
ground_compaction = 1.0
# Utility function to set cell type and ensure only one is active
func set_cell_type(cell_type: String):
_initializing = true
# Clear all cell types first
camp = false
path = false
water = false
# Set the specified type
match cell_type.to_lower():
"camp":
camp = true
"path":
path = true
"water":
water = true
"terrain", "normal", "":
pass # Leave all false for normal terrain
_:
push_warning("Unknown cell type: " + cell_type)
_initializing = false
_update_dependent_properties()
# Utility function to get the primary cell type
func get_cell_type() -> String:
if camp:
return "camp"
elif path:
return "path"
elif water:
return "water"
else:
return "terrain"
# Check if this is a special cell type (not normal terrain)
func is_special_cell() -> bool:
return camp or path or water
func add_trees(tree: TreeDataResource, qty: int) -> void: func add_trees(tree: TreeDataResource, qty: int) -> void:
for i in qty: for i in qty:
trees.append(tree) trees.append(tree)
# Override vegetation density and ground compaction for special cases
func force_set_vegetation_density(value: float):
# Allows bypassing the automatic management if absolutely needed
vegetation_density = value
func force_set_ground_compaction(value: float):
# Allows bypassing the automatic management if absolutely needed
ground_compaction = value
func should_spawn_trees() -> bool:
# Only spawn trees if this is not a special cell type
return not (camp or path or water) and vegetation_density > 0.0
func should_spawn_grass() -> bool:
# Grass can spawn in any terrain cell, but not in special cells
return not (camp or path or water) and vegetation_density > 0.0
func should_spawn_bushes() -> bool:
# Bushes can spawn in any terrain cell, but not in special cells
return not (camp or path or water) and vegetation_density > 0.1
func should_spawn_flowers() -> bool:
# Flowers can spawn in any terrain cell, but not in special cells
return not (camp or path or water) and vegetation_density > 0.1

View file

@ -14,41 +14,15 @@ var map_data: Array = Global.map_data
## Density 0.1 to 0.6 - add bushes, varying quantity TBD ## Density 0.1 to 0.6 - add bushes, varying quantity TBD
## Density 0.1 to 0.4 - add flowers, varying quantity TBD ## Density 0.1 to 0.4 - add flowers, varying quantity TBD
# Weighted random selection based on tree chances
static func select_weighted_tree(tree_preferences: Dictionary):
if tree_preferences.is_empty():
return null
# Calculate total weight
var total_weight = 0.0
for tree_name in tree_preferences.keys():
total_weight += tree_preferences[tree_name]["chance"]
if total_weight <= 0.0:
return null
# Generate random number between 0 and total_weight
var random_value = randf() * total_weight
# Find which tree this random value corresponds to
var cumulative_weight = 0.0
for tree_name in tree_preferences.keys():
cumulative_weight += tree_preferences[tree_name]["chance"]
if random_value <= cumulative_weight:
return tree_preferences[tree_name]["resource"]
# Fallback (shouldn't happen, but just in case)
var first_key = tree_preferences.keys()[0]
return tree_preferences[first_key]["resource"]
# Pre-calculate tree distribution for the cell # Pre-calculate tree distribution for the cell
static func generate_cell_with_distribution(x: int, z: int, density: float, path: bool = false, water: bool = false): static func generate_cell_with_distribution(x: int, z: int, density: float, path: bool = false, water: bool = false, camp: bool = false):
var cell_data = CellDataResource.new() var cell_data = CellDataResource.new()
cell_data.x = x cell_data.x = x
cell_data.z = z cell_data.z = z
cell_data.vegetation_density = density cell_data.vegetation_density = density
cell_data.camp = camp
if not (path or water): if not (path or water or camp):
if density >= 0.6: if density >= 0.6:
var tree_preferences = BiomeData.calculate_tree_probabilities(x, z) var tree_preferences = BiomeData.calculate_tree_probabilities(x, z)
var tree_distribution = calculate_tree_distribution(tree_preferences, density) var tree_distribution = calculate_tree_distribution(tree_preferences, density)

View file

@ -16,6 +16,11 @@ var map_height: int = Global.map_height
@export var min_branch_length_ratio: float = 0.2 # Minimum branch length as ratio of main path @export var min_branch_length_ratio: float = 0.2 # Minimum branch length as ratio of main path
@export var max_branch_length_ratio: float = 0.4 # Maximum branch length as ratio of main path @export var max_branch_length_ratio: float = 0.4 # Maximum branch length as ratio of main path
# Camp generation settings
@export var camp_size: int = 2 # Size of the camp (camp_size x camp_size)
@export var camp_placement_attempts: int = 100 # Max attempts to place the camp
@export var camp_buffer_zone: int = 1 # Minimum distance from camp to other features
# Water generation settings # Water generation settings
@export var num_water_bodies: int = 5 # Number of water bodies to generate @export var num_water_bodies: int = 5 # Number of water bodies to generate
@export var min_water_size: int = 15 # Minimum radius of water bodies @export var min_water_size: int = 15 # Minimum radius of water bodies
@ -32,6 +37,11 @@ var map: Array = []
var map_data: Array = [] var map_data: Array = []
var path_data: Array = [] # Separate array to track paths var path_data: Array = [] # Separate array to track paths
var water_data: Array = [] # Separate array to track water bodies var water_data: Array = [] # Separate array to track water bodies
var camp_data: Array = [] # Separate array to track camp
# Camp position storage
var camp_position: Vector2 = Vector2(-1, -1) # Top-left corner of the camp
var camp_center: Vector2 = Vector2(-1, -1) # Center of the camp
# New data structures for enhanced path generation # New data structures for enhanced path generation
var path_segments: Array = [] # Store all path segments for better branching var path_segments: Array = [] # Store all path segments for better branching
@ -39,21 +49,26 @@ var path_id_counter: int = 0
func _ready(): func _ready():
generate_map() generate_map()
generate_camp()
generate_paths() generate_paths()
generate_water_bodies() generate_water_bodies()
BiomeGenerationClass.generate_environment_maps(map_width, map_height) BiomeGenerationClass.generate_environment_maps(map_width, map_height)
generate_final_map_data() generate_final_map_data()
#if export_image: if export_image:
# export_map_as_image() export_map_as_image()
func generate_final_map_data(): func generate_final_map_data():
var objects_before = Performance.get_monitor(Performance.OBJECT_COUNT) var objects_before = Performance.get_monitor(Performance.OBJECT_COUNT)
for y in range(map_height): for y in range(map_height):
for x in range(map_width): for x in range(map_width):
MapPopulationClass.generate_cell_with_distribution(x, y, map_data[y][x], is_path_at(x, y), is_water_at(x, y)) MapPopulationClass.generate_cell_with_distribution(x, y, map_data[y][x], is_path_at(x, y), is_water_at(x, y), is_camp_at(x, y))
Log.pr(camp_center)
Global.spawn_point = Vector3(camp_position.x * 2, 0, camp_position.y * 2) # Set spawn point to camp center
Log.pr(Global.spawn_point)
# Check immediately after # Check immediately after
await get_tree().process_frame await get_tree().process_frame
@ -86,14 +101,17 @@ func generate_map():
map_data.resize(map_height) map_data.resize(map_height)
path_data.resize(map_height) path_data.resize(map_height)
water_data.resize(map_height) water_data.resize(map_height)
camp_data.resize(map_height)
for y in range(map_height): for y in range(map_height):
map_data[y] = [] map_data[y] = []
path_data[y] = [] path_data[y] = []
water_data[y] = [] water_data[y] = []
camp_data[y] = []
map_data[y].resize(map_width) map_data[y].resize(map_width)
path_data[y].resize(map_width) path_data[y].resize(map_width)
water_data[y].resize(map_width) water_data[y].resize(map_width)
camp_data[y].resize(map_width)
for x in range(map_width): for x in range(map_width):
# Get noise value (-1 to 1) and normalize to (0 to 1) # Get noise value (-1 to 1) and normalize to (0 to 1)
var noise_value = noise.get_noise_2d(x, y) var noise_value = noise.get_noise_2d(x, y)
@ -101,7 +119,66 @@ func generate_map():
map_data[y][x] = round(normalized_value * 10.0) / 10.0 map_data[y][x] = round(normalized_value * 10.0) / 10.0
path_data[y][x] = false # Initialize path data path_data[y][x] = false # Initialize path data
water_data[y][x] = false # Initialize water data water_data[y][x] = false # Initialize water data
camp_data[y][x] = false # Initialize camp data
func generate_camp():
print("Generating camp...")
var attempts = 0
var placed = false
while attempts < camp_placement_attempts and not placed:
# Random position with margin to ensure camp fits within map bounds
var margin = camp_size + camp_buffer_zone
var camp_x = randi_range(margin, map_width - margin - camp_size)
var camp_y = randi_range(margin, map_height - margin - camp_size)
# Check if this location is valid for camp placement
if is_valid_camp_location(camp_x, camp_y):
place_camp(camp_x, camp_y)
placed = true
print("Camp placed at (", camp_x, ",", camp_y, ") with size ", camp_size, "x", camp_size)
attempts += 1
if not placed:
print("Could not place camp after ", camp_placement_attempts, " attempts")
# Fallback: place camp in the center of the map
var fallback_x = (map_width - camp_size) / 2
var fallback_y = (map_height - camp_size) / 2
place_camp(fallback_x, fallback_y)
print("Camp placed at fallback location (", fallback_x, ",", fallback_y, ")")
func is_valid_camp_location(camp_x: int, camp_y: int) -> bool:
# Check if the camp area and buffer zone are clear
var check_size = camp_size + (camp_buffer_zone * 2)
var start_x = camp_x - camp_buffer_zone
var start_y = camp_y - camp_buffer_zone
for y in range(start_y, start_y + check_size):
for x in range(start_x, start_x + check_size):
if x >= 0 and x < map_width and y >= 0 and y < map_height:
# For now, just check if we're within map bounds
# Later we'll prevent paths and water from being placed here
continue
else:
# Outside map bounds
return false
return true
func place_camp(camp_x: int, camp_y: int):
# Store camp position
camp_position = Vector2(camp_x, camp_y)
camp_center = Vector2(camp_x + camp_size / 2.0, camp_y + camp_size / 2.0)
Log.pr("Placing camp at position: ", camp_position, " with center: ", camp_center)
# Mark camp area in camp_data
for y in range(camp_y, camp_y + camp_size):
for x in range(camp_x, camp_x + camp_size):
if x >= 0 and x < map_width and y >= 0 and y < map_height:
camp_data[y][x] = true
func generate_paths(): func generate_paths():
print("Generating natural paths with branching...") print("Generating natural paths with branching...")
@ -416,6 +493,10 @@ func evaluate_catmull_rom_spline(points: Array, t: float) -> Vector2:
return result return result
func place_organic_path_segment(x: int, y: int, path_id: int, is_main_path: bool = false): func place_organic_path_segment(x: int, y: int, path_id: int, is_main_path: bool = false):
# Don't place paths in camp area
if is_camp_at(x, y):
return
# Base path placement - always place the center tile # Base path placement - always place the center tile
path_data[y][x] = true path_data[y][x] = true
@ -440,6 +521,10 @@ func place_organic_path_segment(x: int, y: int, path_id: int, is_main_path: bool
var ny = y + dy var ny = y + dy
if nx >= 0 and nx < map_width and ny >= 0 and ny < map_height: if nx >= 0 and nx < map_width and ny >= 0 and ny < map_height:
# Don't place paths in camp area
if is_camp_at(nx, ny):
continue
# Use noise to create organic edges # Use noise to create organic edges
var edge_noise = width_noise.get_noise_2d(nx, ny) var edge_noise = width_noise.get_noise_2d(nx, ny)
var edge_chance = 1.0 - (distance / float(path_width)) var edge_chance = 1.0 - (distance / float(path_width))
@ -483,7 +568,9 @@ func smooth_paths():
var interp_x = x + int(float(dx * step) / float(steps)) var interp_x = x + int(float(dx * step) / float(steps))
var interp_y = y + int(float(dy * step) / float(steps)) var interp_y = y + int(float(dy * step) / float(steps))
if interp_x >= 0 and interp_x < map_width and interp_y >= 0 and interp_y < map_height: if interp_x >= 0 and interp_x < map_width and interp_y >= 0 and interp_y < map_height:
new_path_data[interp_y][interp_x] = true # Don't place paths in camp area
if not is_camp_at(interp_x, interp_y):
new_path_data[interp_y][interp_x] = true
connected = true connected = true
break break
if connected: if connected:
@ -509,9 +596,9 @@ func generate_single_water_body(body_index: int):
var center_y = randi_range(max_water_size, map_height - max_water_size) var center_y = randi_range(max_water_size, map_height - max_water_size)
# Random size within configured range # Random size within configured range
var water_radius = randi_range(min_water_size, max_water_size) var water_radius = randi_range(min_water_size, max_water_size) - attempts
# Check if this location is valid (no paths in the area) # Check if this location is valid (no paths or camp in the area)
if is_valid_water_location(center_x, center_y, water_radius): if is_valid_water_location(center_x, center_y, water_radius):
place_water_body(center_x, center_y, water_radius, body_index) place_water_body(center_x, center_y, water_radius, body_index)
placed = true placed = true
@ -523,7 +610,7 @@ func generate_single_water_body(body_index: int):
print("Could not place water body ", body_index + 1, " after ", water_placement_attempts, " attempts") print("Could not place water body ", body_index + 1, " after ", water_placement_attempts, " attempts")
func is_valid_water_location(center_x: int, center_y: int, radius: int) -> bool: func is_valid_water_location(center_x: int, center_y: int, radius: int) -> bool:
# Check if any paths would be intersected by this water body # Check if any paths or camp would be intersected by this water body
# Use a slightly larger radius for safety buffer # Use a slightly larger radius for safety buffer
var safety_buffer = 2 var safety_buffer = 2
var check_radius = radius + safety_buffer var check_radius = radius + safety_buffer
@ -537,6 +624,12 @@ func is_valid_water_location(center_x: int, center_y: int, radius: int) -> bool:
if distance <= check_radius: if distance <= check_radius:
return false return false
# Check if this position has camp
if camp_data[y][x]:
var distance = sqrt(pow(x - center_x, 2) + pow(y - center_y, 2))
if distance <= check_radius:
return false
# Also check if there's already water here (prevent overlap) # Also check if there's already water here (prevent overlap)
if water_data[y][x]: if water_data[y][x]:
var distance = sqrt(pow(x - center_x, 2) + pow(y - center_y, 2)) var distance = sqrt(pow(x - center_x, 2) + pow(y - center_y, 2))
@ -556,7 +649,7 @@ func place_water_body(center_x: int, center_y: int, radius: int, body_index: int
for y in range(center_y - radius - 2, center_y + radius + 3): for y in range(center_y - radius - 2, center_y + radius + 3):
for x in range(center_x - radius - 2, center_x + radius + 3): for x in range(center_x - radius - 2, center_x + radius + 3):
if x >= 0 and x < map_width and y >= 0 and y < map_height: if x >= 0 and x < map_width and y >= 0 and y < map_height:
if path_data[y][x]: # Don't place water on paths if path_data[y][x] or camp_data[y][x]: # Don't place water on paths or camp
continue continue
# Calculate distance from center # Calculate distance from center
@ -574,7 +667,7 @@ func place_water_body(center_x: int, center_y: int, radius: int, body_index: int
func print_visual_map_to_console(): func print_visual_map_to_console():
print("") print("")
print("==================================================") print("==================================================")
print("VISUAL MAP WITH BRANCHING PATHS AND DEAD ENDS") print("VISUAL MAP WITH BRANCHING PATHS, DEAD ENDS, AND CAMP")
print("==================================================") print("==================================================")
print("Path Statistics:") print("Path Statistics:")
print("- Total path segments: ", path_segments.size()) print("- Total path segments: ", path_segments.size())
@ -585,11 +678,16 @@ func print_visual_map_to_console():
print("- Branches: ", branches.size()) print("- Branches: ", branches.size())
print("- Dead ends: ", dead_ends.size()) print("- Dead ends: ", dead_ends.size())
print("") print("")
print("Camp Statistics:")
print("- Camp position: (", camp_position.x, ",", camp_position.y, ")")
print("- Camp center: (", camp_center.x, ",", camp_center.y, ")")
print("- Camp size: ", camp_size, "x", camp_size)
print("")
print_block_map_with_paths_safe() print_block_map_with_paths_safe()
func print_block_map_with_paths_safe(): func print_block_map_with_paths_safe():
print("Block character map with paths (X = paths, W = water):") print("Block character map with paths (X = paths, W = water, C = camp):")
print("") print("")
# Print in smaller chunks # Print in smaller chunks
@ -600,8 +698,10 @@ func print_block_map_with_paths_safe():
for y in range(chunk_start, chunk_end): for y in range(chunk_start, chunk_end):
var row_string = "" var row_string = ""
for x in range(map_width): for x in range(map_width):
# Check priority: paths first, then water, then terrain # Check priority: camp first, then paths, then water, then terrain
if path_data[y][x]: if camp_data[y][x]:
row_string += "C"
elif path_data[y][x]:
row_string += "X" row_string += "X"
elif water_data[y][x]: elif water_data[y][x]:
row_string += "W" row_string += "W"
@ -639,19 +739,35 @@ func is_water_at(x: int, y: int) -> bool:
return water_data[y][x] return water_data[y][x]
return false return false
func is_camp_at(x: int, y: int) -> bool:
if x >= 0 and x < map_width and y >= 0 and y < map_height:
return camp_data[y][x]
return false
func get_terrain_at(x: int, y: int) -> float: func get_terrain_at(x: int, y: int) -> float:
if x >= 0 and x < map_width and y >= 0 and y < map_height: if x >= 0 and x < map_width and y >= 0 and y < map_height:
return map_data[y][x] return map_data[y][x]
return 0.0 return 0.0
func get_tile_type_at(x: int, y: int) -> String: func get_tile_type_at(x: int, y: int) -> String:
if is_path_at(x, y): if is_camp_at(x, y):
return "camp"
elif is_path_at(x, y):
return "path" return "path"
elif is_water_at(x, y): elif is_water_at(x, y):
return "water" return "water"
else: else:
return "terrain" return "terrain"
func get_camp_position() -> Vector2:
return camp_position
func get_camp_center() -> Vector2:
return camp_center
func get_camp_size() -> int:
return camp_size
func get_path_segments() -> Array: func get_path_segments() -> Array:
return path_segments return path_segments
@ -687,6 +803,26 @@ func export_map_as_image():
var image_y = map_y * tile_size + pixel_y var image_y = map_y * tile_size + pixel_y
image.set_pixel(image_x, image_y, color) image.set_pixel(image_x, image_y, color)
# Draw camp as a red dot at the center
if camp_position.x >= 0 and camp_position.y >= 0:
var camp_center_pixel_x = int(camp_center.x * tile_size)
var camp_center_pixel_y = int(camp_center.y * tile_size)
# Draw a red dot (3x3 pixels minimum, scaled with tile size)
var dot_size = 20
var half_dot = dot_size / 2
for dy in range(-half_dot, half_dot + 1):
for dx in range(-half_dot, half_dot + 1):
var pixel_x = camp_center_pixel_x + dx
var pixel_y = camp_center_pixel_y + dy
# Make sure we're within image bounds
if pixel_x >= 0 and pixel_x < image_width and pixel_y >= 0 and pixel_y < image_height:
# Create a circular dot
if dx * dx + dy * dy <= half_dot * half_dot:
image.set_pixel(pixel_x, pixel_y, Color.RED)
# Save the image - you can choose different locations: # Save the image - you can choose different locations:
var file_path = "user://" + image_filename var file_path = "user://" + image_filename
@ -700,11 +836,14 @@ func export_map_as_image():
print("Image dimensions: ", image_width, "x", image_height, " pixels") print("Image dimensions: ", image_width, "x", image_height, " pixels")
print("Map dimensions: ", map_width, "x", map_height, " tiles") print("Map dimensions: ", map_width, "x", map_height, " tiles")
print("Tile size: ", tile_size, "x", tile_size, " pixels per tile") print("Tile size: ", tile_size, "x", tile_size, " pixels per tile")
print("Camp represented as red dot at center: (", camp_center.x, ",", camp_center.y, ")")
else: else:
print("Error saving image: ", error) print("Error saving image: ", error)
func get_tile_color(x: int, y: int) -> Color: func get_tile_color(x: int, y: int) -> Color:
if is_path_at(x, y): if is_camp_at(x, y):
return Color(0.4, 0.7, 0.3)
elif is_path_at(x, y):
# Brown color for paths # Brown color for paths
return Color(0.6, 0.4, 0.2) # Brown return Color(0.6, 0.4, 0.2) # Brown
elif is_water_at(x, y): elif is_water_at(x, y):

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=46 format=4 uid="uid://bwsugg4p50fjr"] [gd_scene load_steps=53 format=4 uid="uid://bwsugg4p50fjr"]
[ext_resource type="Environment" uid="uid://cm77bbr0io118" path="res://Stages/Test3D/new_environment.tres" id="1_8ph61"] [ext_resource type="Environment" uid="uid://cm77bbr0io118" path="res://Stages/Test3D/new_environment.tres" id="1_8ph61"]
[ext_resource type="Script" uid="uid://bwed2dwogfmxv" path="res://Entities/Player/scripts/player.gd" id="1_d602n"] [ext_resource type="Script" uid="uid://bwed2dwogfmxv" path="res://Entities/Player/scripts/player.gd" id="1_d602n"]
@ -6,6 +6,13 @@
[ext_resource type="Script" uid="uid://bbjv6a7yg7m02" path="res://Stages/Test3D/camera_pivot.gd" id="2_sdmks"] [ext_resource type="Script" uid="uid://bbjv6a7yg7m02" path="res://Stages/Test3D/camera_pivot.gd" id="2_sdmks"]
[ext_resource type="Shader" uid="uid://bsemnmdracd4m" path="res://Common/shaders/outline.gdshader" id="4_feu7y"] [ext_resource type="Shader" uid="uid://bsemnmdracd4m" path="res://Common/shaders/outline.gdshader" id="4_feu7y"]
[ext_resource type="Script" uid="uid://bjco8musjqog4" path="res://Stages/Test3D/particles.gd" id="9_oiyue"] [ext_resource type="Script" uid="uid://bjco8musjqog4" path="res://Stages/Test3D/particles.gd" id="9_oiyue"]
[ext_resource type="PackedScene" uid="uid://cdbnr0jg2icaj" path="res://Entities/Tree/assets/temp/campfire_bricks.glb" id="13_qasnx"]
[ext_resource type="PackedScene" uid="uid://c4ovjmemnepdy" path="res://Entities/Tree/assets/temp/campfire_logs.glb" id="14_2xm50"]
[ext_resource type="PackedScene" uid="uid://e8wa72ulhegx" path="res://Entities/Tree/assets/temp/log.glb" id="15_e0hgm"]
[ext_resource type="PackedScene" uid="uid://by2v67khppn0j" path="res://Entities/Tree/assets/temp/bed_floor.glb" id="15_pbfwi"]
[ext_resource type="PackedScene" uid="uid://d3y1g22hjq7ch" path="res://Entities/Tree/assets/temp/statue_column.glb" id="16_ynokf"]
[ext_resource type="PackedScene" uid="uid://b0yuw5v7ytyd0" path="res://Entities/Tree/assets/temp/statue_columnDamaged.glb" id="17_pbfwi"]
[ext_resource type="PackedScene" uid="uid://c06cgs47ulyjs" path="res://Entities/Tree/assets/temp/stone_smallC.glb" id="19_ynokf"]
[ext_resource type="Texture2D" uid="uid://c78jcjh8fjndd" path="res://Stages/Test3D/assets/3d/particles/flamelet_smooth.png" id="21_xvexm"] [ext_resource type="Texture2D" uid="uid://c78jcjh8fjndd" path="res://Stages/Test3D/assets/3d/particles/flamelet_smooth.png" id="21_xvexm"]
[ext_resource type="Script" uid="uid://dglvt140rhg00" path="res://Stages/Test3D/omni_light_3d.gd" id="22_ukp6m"] [ext_resource type="Script" uid="uid://dglvt140rhg00" path="res://Stages/Test3D/omni_light_3d.gd" id="22_ukp6m"]
[ext_resource type="PackedScene" uid="uid://mdxkaqaoybjv" path="res://Stages/Test3D/assets/tent-canvas.glb" id="23_5r2bu"] [ext_resource type="PackedScene" uid="uid://mdxkaqaoybjv" path="res://Stages/Test3D/assets/tent-canvas.glb" id="23_5r2bu"]
@ -365,6 +372,42 @@ size = Vector2(2, 2)
[sub_resource type="BoxShape3D" id="BoxShape3D_tfa5t"] [sub_resource type="BoxShape3D" id="BoxShape3D_tfa5t"]
size = Vector3(60, 0, 20) size = Vector3(60, 0, 20)
[sub_resource type="Curve" id="Curve_5r2bu"]
_data = [Vector2(0.003125, 0.0237797), 0.0, 0.0, 0, 0, Vector2(0.21875, 0.877972), 0.0, 0.0, 0, 0, Vector2(0.41875, 0.194618), 0.0, 0.0, 0, 0, Vector2(0.478125, 0.576971), -7.27116, -7.27116, 0, 0, Vector2(0.621875, 0.227159), 0.0, 0.0, 0, 0, Vector2(0.7625, 0.365457), 0.0, 0.0, 0, 0, Vector2(1, 0.04005), 0.0, 0.0, 0, 0]
point_count = 7
[sub_resource type="CurveTexture" id="CurveTexture_23r73"]
curve = SubResource("Curve_5r2bu")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_p5fn2"]
emission_shape = 6
emission_ring_axis = Vector3(0, 1, 0)
emission_ring_height = 1.0
emission_ring_radius = 5.0
emission_ring_inner_radius = 0.0
emission_ring_cone_angle = 90.0
direction = Vector3(0.2, -1, 0)
spread = 5.0
initial_velocity_max = 10.0
scale_min = 0.1
alpha_curve = SubResource("CurveTexture_23r73")
collision_mode = 2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p5fn2"]
transparency = 1
cull_mode = 2
shading_mode = 0
vertex_color_use_as_albedo = true
albedo_color = Color(1, 1, 1, 0.807843)
albedo_texture = ExtResource("23_23r73")
use_particle_trails = true
[sub_resource type="RibbonTrailMesh" id="RibbonTrailMesh_5r2bu"]
material = SubResource("StandardMaterial3D_p5fn2")
size = 0.025
sections = 2
section_segments = 1
[sub_resource type="Curve" id="Curve_ukp6m"] [sub_resource type="Curve" id="Curve_ukp6m"]
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.515625, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] _data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.515625, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 3 point_count = 3
@ -407,42 +450,6 @@ material = SubResource("StandardMaterial3D_hvb1l")
size = Vector2(0.2, 0.2) size = Vector2(0.2, 0.2)
orientation = 0 orientation = 0
[sub_resource type="Curve" id="Curve_5r2bu"]
_data = [Vector2(0.003125, 0.0237797), 0.0, 0.0, 0, 0, Vector2(0.21875, 0.877972), 0.0, 0.0, 0, 0, Vector2(0.41875, 0.194618), 0.0, 0.0, 0, 0, Vector2(0.478125, 0.576971), -7.27116, -7.27116, 0, 0, Vector2(0.621875, 0.227159), 0.0, 0.0, 0, 0, Vector2(0.7625, 0.365457), 0.0, 0.0, 0, 0, Vector2(1, 0.04005), 0.0, 0.0, 0, 0]
point_count = 7
[sub_resource type="CurveTexture" id="CurveTexture_23r73"]
curve = SubResource("Curve_5r2bu")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_p5fn2"]
emission_shape = 6
emission_ring_axis = Vector3(0, 1, 0)
emission_ring_height = 1.0
emission_ring_radius = 5.0
emission_ring_inner_radius = 0.0
emission_ring_cone_angle = 90.0
direction = Vector3(0.2, -1, 0)
spread = 5.0
initial_velocity_max = 10.0
scale_min = 0.1
alpha_curve = SubResource("CurveTexture_23r73")
collision_mode = 2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p5fn2"]
transparency = 1
cull_mode = 2
shading_mode = 0
vertex_color_use_as_albedo = true
albedo_color = Color(1, 1, 1, 0.807843)
albedo_texture = ExtResource("23_23r73")
use_particle_trails = true
[sub_resource type="RibbonTrailMesh" id="RibbonTrailMesh_5r2bu"]
material = SubResource("StandardMaterial3D_p5fn2")
size = 0.025
sections = 2
section_segments = 1
[node name="Test3d" type="Node3D"] [node name="Test3d" type="Node3D"]
[node name="WorldEnvironment" type="WorldEnvironment" parent="."] [node name="WorldEnvironment" type="WorldEnvironment" parent="."]
@ -761,31 +768,50 @@ shape = SubResource("BoxShape3D_tfa5t")
[node name="VFX" type="Node3D" parent="SubViewportContainer/SubViewport"] [node name="VFX" type="Node3D" parent="SubViewportContainer/SubViewport"]
[node name="Fire" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/VFX"] [node name="FloatingParticles" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/VFX"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.08083, 0.0837402, 0.501403) script = ExtResource("9_oiyue")
visible = false
amount = 50
lifetime = 0.4
speed_scale = 0.4
process_material = SubResource("ParticleProcessMaterial_xvexm")
draw_pass_1 = SubResource("QuadMesh_hvb1l")
[node name="OmniLight3D" type="OmniLight3D" parent="SubViewportContainer/SubViewport/VFX/Fire"] [node name="TileGround" type="Node3D" parent="SubViewportContainer/SubViewport"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000509977, 0.121094, -0.00151992) unique_name_in_owner = true
light_color = Color(0.89, 0.461613, 0.2136, 1) script = ExtResource("24_vyi1v")
light_energy = 0.590552 ground_tile = ExtResource("25_caaui")
light_indirect_energy = 1.084
light_volumetric_fog_energy = 3.764
light_size = 0.105
shadow_enabled = true
distance_fade_enabled = true
distance_fade_begin = 386.01
distance_fade_shadow = 45.9
distance_fade_length = 28.05
script = ExtResource("22_ukp6m")
[node name="Rain" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/VFX"] [node name="Camp" type="Node3D" parent="SubViewportContainer/SubViewport"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 10, 0) unique_name_in_owner = true
[node name="Objects" type="Node3D" parent="SubViewportContainer/SubViewport/Camp"]
[node name="tent-canvas2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("23_5r2bu")]
transform = Transform3D(0.964438, 0, -0.264311, 0, 1, 0, 0.264311, 0, 0.964438, -1.40178, 0, -1.67128)
[node name="campfire_bricks2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("13_qasnx")]
transform = Transform3D(0.7, 0, 0, 0, 0.7, 0, 0, 0, 0.7, -0.93138, 0.05, -0.326702)
[node name="campfire_logs2" parent="SubViewportContainer/SubViewport/Camp/Objects/campfire_bricks2" instance=ExtResource("14_2xm50")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0053246, 0, 0.0117908)
[node name="log2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("15_e0hgm")]
transform = Transform3D(0.440003, 0, 0.237482, 0, 0.5, 0, -0.237482, 0, 0.440003, -0.565749, 0.0532002, -0.598678)
[node name="statue_column2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("16_ynokf")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.654, 0, -2.707)
[node name="statue_column3" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("16_ynokf")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.589154, 0, -2.703)
[node name="statue_column4" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("16_ynokf")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.727, 0, 0.611)
[node name="statue_columnDamaged2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("17_pbfwi")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.609224, 0, 0.72)
[node name="bed_floor2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("15_pbfwi")]
transform = Transform3D(0.72094, 0, -0.346764, 0, 0.8, 0, 0.346764, 0, 0.72094, -1.54675, 0.0359214, -1.37165)
[node name="VFX" type="Node3D" parent="SubViewportContainer/SubViewport/Camp"]
[node name="Rain" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/Camp/VFX"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.545105, 10.1451, 0.937134)
visible = false visible = false
amount = 1000 amount = 1000
preprocess = 10.0 preprocess = 10.0
@ -795,16 +821,27 @@ trail_lifetime = 0.1
process_material = SubResource("ParticleProcessMaterial_p5fn2") process_material = SubResource("ParticleProcessMaterial_p5fn2")
draw_pass_1 = SubResource("RibbonTrailMesh_5r2bu") draw_pass_1 = SubResource("RibbonTrailMesh_5r2bu")
[node name="FloatingParticles" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/VFX"] [node name="Fire" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/Camp/VFX"]
script = ExtResource("9_oiyue") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.923367, 0.0346675, -0.309572)
amount = 50
lifetime = 0.4
speed_scale = 0.4
process_material = SubResource("ParticleProcessMaterial_xvexm")
draw_pass_1 = SubResource("QuadMesh_hvb1l")
[node name="TileGround" type="Node3D" parent="SubViewportContainer/SubViewport"] [node name="OmniLight3D" type="OmniLight3D" parent="SubViewportContainer/SubViewport/Camp/VFX/Fire"]
unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000509977, 0.121094, -0.00151992)
script = ExtResource("24_vyi1v") light_color = Color(0.89, 0.461613, 0.2136, 1)
ground_tile = ExtResource("25_caaui") light_energy = 0.716329
light_indirect_energy = 1.084
[node name="tent-canvas2" parent="." instance=ExtResource("23_5r2bu")] light_volumetric_fog_energy = 3.764
transform = Transform3D(0.964438, 0, -0.264311, 0, 1, 0, 0.264311, 0, 0.964438, 1.49756, 1.86265e-09, -3.10828) light_size = 0.105
shadow_enabled = true
distance_fade_enabled = true
distance_fade_begin = 386.01
distance_fade_shadow = 45.9
distance_fade_length = 28.05
script = ExtResource("22_ukp6m")
[node name="UISubViewportContainer" type="SubViewportContainer" parent="."] [node name="UISubViewportContainer" type="SubViewportContainer" parent="."]
anchors_preset = 15 anchors_preset = 15
@ -852,3 +889,6 @@ fit_content = true
[node name="LoadedTreesLabel" type="RichTextLabel" parent="UISubViewportContainer/UISubViewport/UIContainer/PanelContainer/VBoxContainer"] [node name="LoadedTreesLabel" type="RichTextLabel" parent="UISubViewportContainer/UISubViewport/UIContainer/PanelContainer/VBoxContainer"]
layout_mode = 2 layout_mode = 2
fit_content = true fit_content = true
[node name="stone_smallC2" parent="." instance=ExtResource("19_ynokf")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.109824, 0.0499998, 1.05729)