327 lines
9.6 KiB
GDScript
327 lines
9.6 KiB
GDScript
class_name TestWorld
|
|
extends Node3D
|
|
|
|
const FishingShopInteractionType = preload(
|
|
"res://world/fishing_shop_interaction.gd"
|
|
)
|
|
const PlayerStorageInteractionType = preload(
|
|
"res://world/player_storage_interaction.gd"
|
|
)
|
|
const DecorShopInteractionType = preload(
|
|
"res://world/decor_shop_interaction.gd"
|
|
)
|
|
const WorldLayoutType = preload("res://world/world_layout.gd")
|
|
const GENERATED_REGION_SCENE: PackedScene = preload(
|
|
"res://world/generation/generated_world_region.tscn"
|
|
)
|
|
const STARTER_ISLAND_REGION_SCENE: PackedScene = preload(
|
|
"res://world/regions/starter_island_region.tscn"
|
|
)
|
|
## Keep the player boundary beyond the longest fishing cast. The boundary is
|
|
## solid terrain to character movement and therefore also blocks cast-arc
|
|
## probes when it sits directly against the authored shoreline.
|
|
const WORLD_BOUNDARY_SHORELINE_CLEARANCE := 18.0
|
|
|
|
@onready var _regions_root: Node3D = $Regions
|
|
@onready var _active_region: WorldRegion = _find_active_region()
|
|
@onready var _below_world_failsafe: PlayerWaterTrigger = (
|
|
$Safety/BelowWorldFailsafe
|
|
)
|
|
@onready var _world_environment: WorldEnvironment = $Environment/WorldEnvironment
|
|
@onready var _sun: DirectionalLight3D = $Environment/Sun
|
|
|
|
var _world_layout: StringName = WorldLayoutType.GENERATED
|
|
var _world_seed: int = PlayerSaveManager.DEFAULT_WORLD_SEED
|
|
var _light_performance_profile: bool = false
|
|
var _dedicated_simulation: bool = false
|
|
var _local_home_interior_presentation_active: bool = false
|
|
|
|
|
|
func get_player_water_triggers() -> Array[PlayerWaterTrigger]:
|
|
var triggers: Array[PlayerWaterTrigger] = []
|
|
if _active_region != null:
|
|
triggers.append_array(_active_region.get_water_recovery_triggers())
|
|
triggers.append(_below_world_failsafe)
|
|
return triggers
|
|
|
|
|
|
func get_safe_respawn_points() -> Array[SafeRespawnPoint]:
|
|
return (
|
|
_active_region.get_safe_respawn_points()
|
|
if _active_region != null
|
|
else []
|
|
)
|
|
|
|
|
|
func get_water_recovery_position(entry_position: Vector3) -> Vector3:
|
|
if _active_region == null:
|
|
return global_position
|
|
var fallback_position := get_player_spawn_transform().origin
|
|
return _active_region.get_water_recovery_position(
|
|
entry_position,
|
|
fallback_position,
|
|
)
|
|
|
|
|
|
func get_fishing_shop() -> FishingShopInteractionType:
|
|
return _active_region.get_fishing_shop()
|
|
|
|
|
|
func get_player_storage() -> PlayerStorageInteractionType:
|
|
return _active_region.get_player_storage()
|
|
|
|
|
|
func get_decor_shop() -> DecorShopInteractionType:
|
|
return _active_region.get_decor_shop()
|
|
|
|
|
|
func get_player_spawn_transform() -> Transform3D:
|
|
return _active_region.get_player_spawn_transform()
|
|
|
|
|
|
func get_world_environment() -> WorldEnvironment:
|
|
return _world_environment
|
|
|
|
|
|
func get_sun() -> DirectionalLight3D:
|
|
return _sun
|
|
|
|
|
|
func set_light_performance_profile(enabled: bool) -> void:
|
|
_light_performance_profile = enabled
|
|
if _active_region != null:
|
|
_active_region.set_light_performance_profile(enabled)
|
|
|
|
|
|
func set_local_home_interior_presentation_active(enabled: bool) -> void:
|
|
_local_home_interior_presentation_active = enabled
|
|
if _active_region != null and not _dedicated_simulation:
|
|
_active_region.visible = not enabled
|
|
|
|
|
|
func set_dedicated_simulation(enabled: bool) -> void:
|
|
_dedicated_simulation = enabled
|
|
if _active_region != null:
|
|
_set_region_presentation_enabled(_active_region, not enabled)
|
|
_world_environment.process_mode = (
|
|
Node.PROCESS_MODE_DISABLED if enabled else Node.PROCESS_MODE_INHERIT
|
|
)
|
|
_sun.visible = not enabled
|
|
_sun.process_mode = (
|
|
Node.PROCESS_MODE_DISABLED if enabled else Node.PROCESS_MODE_INHERIT
|
|
)
|
|
|
|
|
|
func _set_region_presentation_enabled(
|
|
region: WorldRegion,
|
|
enabled: bool,
|
|
) -> void:
|
|
for class_name_value: String in [
|
|
"WaterSurfaceMotion",
|
|
"LocalStormCloudLayer",
|
|
"WorldCharacterDisplay",
|
|
]:
|
|
for value: Node in region.find_children(
|
|
"*", class_name_value, true, false
|
|
):
|
|
value.process_mode = (
|
|
Node.PROCESS_MODE_INHERIT
|
|
if enabled
|
|
else Node.PROCESS_MODE_DISABLED
|
|
)
|
|
for value: Node in region.find_children("*", "GeometryInstance3D", true, false):
|
|
(value as GeometryInstance3D).visible = enabled
|
|
for value: Node in region.find_children("*", "GPUParticles3D", true, false):
|
|
var particles := value as GPUParticles3D
|
|
particles.emitting = enabled
|
|
particles.process_mode = (
|
|
Node.PROCESS_MODE_INHERIT if enabled else Node.PROCESS_MODE_DISABLED
|
|
)
|
|
for value: Node in region.find_children("*", "AnimationPlayer", true, false):
|
|
var animation_player := value as AnimationPlayer
|
|
animation_player.active = enabled
|
|
animation_player.process_mode = (
|
|
Node.PROCESS_MODE_INHERIT if enabled else Node.PROCESS_MODE_DISABLED
|
|
)
|
|
for class_name_value: String in ["AudioStreamPlayer", "AudioStreamPlayer3D"]:
|
|
for value: Node in region.find_children(
|
|
"*", class_name_value, true, false
|
|
):
|
|
value.call("stop")
|
|
value.process_mode = (
|
|
Node.PROCESS_MODE_INHERIT
|
|
if enabled
|
|
else Node.PROCESS_MODE_DISABLED
|
|
)
|
|
|
|
|
|
func get_fishable_water_regions() -> Array[FishableWaterRegion]:
|
|
return (
|
|
_active_region.get_fishable_water_regions()
|
|
if _active_region != null
|
|
else []
|
|
)
|
|
|
|
|
|
func get_saltwater_shoreline_mesh() -> MeshInstance3D:
|
|
return _active_region.get_saltwater_shoreline_mesh()
|
|
|
|
|
|
func get_spawn_surface_triangles(
|
|
material_names: Array[StringName],
|
|
minimum_global_y: float,
|
|
) -> Array[PackedVector3Array]:
|
|
return _active_region.get_spawn_surface_triangles(
|
|
material_names,
|
|
minimum_global_y,
|
|
)
|
|
|
|
|
|
func activate_world(layout: StringName, seed: int) -> bool:
|
|
if (
|
|
not WorldLayoutType.is_valid(layout)
|
|
or seed <= 0
|
|
or seed > PlayerSaveManager.MAX_WORLD_SEED
|
|
):
|
|
return false
|
|
if _active_region == null or _active_region.region_id != layout:
|
|
if not _replace_active_region(layout, seed):
|
|
return false
|
|
_world_layout = layout
|
|
_world_seed = seed
|
|
if layout == WorldLayoutType.GENERATED:
|
|
if not _active_region.has_method("generate_world"):
|
|
return false
|
|
if not bool(_active_region.call("generate_world", seed)):
|
|
return false
|
|
_configure_world_coverage()
|
|
return true
|
|
|
|
|
|
func generate_world(seed: int) -> bool:
|
|
return activate_world(WorldLayoutType.GENERATED, seed)
|
|
|
|
|
|
func get_world_layout() -> StringName:
|
|
return _world_layout
|
|
|
|
|
|
func get_generation_seed() -> int:
|
|
return _world_seed
|
|
|
|
|
|
func is_world_ready() -> bool:
|
|
if _active_region == null:
|
|
return false
|
|
if _world_layout != WorldLayoutType.GENERATED:
|
|
return true
|
|
return (
|
|
_active_region.has_method(&"is_world_generated")
|
|
and bool(_active_region.call(&"is_world_generated"))
|
|
)
|
|
|
|
|
|
func get_diggable_area_triangles(
|
|
area_id: StringName,
|
|
) -> Array[PackedVector3Array]:
|
|
if _active_region != null:
|
|
var area: DiggableArea3D = _active_region.get_diggable_area(area_id)
|
|
if area != null:
|
|
return area.get_surface_triangles()
|
|
return []
|
|
|
|
|
|
func is_diggable_surface_point_valid(
|
|
area_id: StringName,
|
|
point: Vector3,
|
|
) -> bool:
|
|
if _active_region == null:
|
|
return false
|
|
var area: DiggableArea3D = _active_region.get_diggable_area(area_id)
|
|
return area != null and area.is_surface_point_valid(point)
|
|
|
|
|
|
func get_gatherable_spawn_positions(
|
|
anchor_set_id: StringName,
|
|
) -> PackedVector3Array:
|
|
if _active_region != null:
|
|
var anchor_set: GatherableAnchorSet3D = (
|
|
_active_region.get_gatherable_anchor_set(anchor_set_id)
|
|
)
|
|
if anchor_set != null:
|
|
return anchor_set.get_spawn_positions()
|
|
return PackedVector3Array()
|
|
|
|
|
|
func _ready() -> void:
|
|
if _active_region != null:
|
|
_world_layout = _active_region.region_id
|
|
_configure_world_coverage()
|
|
|
|
|
|
func _find_active_region() -> WorldRegion:
|
|
for child: Node in _regions_root.get_children():
|
|
var region := child as WorldRegion
|
|
if region != null:
|
|
return region
|
|
return null
|
|
|
|
|
|
func _replace_active_region(layout: StringName, seed: int) -> bool:
|
|
var region_scene: PackedScene = (
|
|
GENERATED_REGION_SCENE
|
|
if layout == WorldLayoutType.GENERATED
|
|
else STARTER_ISLAND_REGION_SCENE
|
|
)
|
|
var replacement := region_scene.instantiate() as WorldRegion
|
|
if replacement == null:
|
|
return false
|
|
if layout == WorldLayoutType.GENERATED:
|
|
replacement.set("initial_seed", seed)
|
|
if _active_region != null:
|
|
_regions_root.remove_child(_active_region)
|
|
_active_region.free()
|
|
_active_region = replacement
|
|
_regions_root.add_child(_active_region)
|
|
_active_region.set_light_performance_profile(_light_performance_profile)
|
|
if _dedicated_simulation:
|
|
_set_region_presentation_enabled(_active_region, false)
|
|
else:
|
|
_active_region.visible = not _local_home_interior_presentation_active
|
|
return true
|
|
|
|
|
|
func _configure_world_coverage() -> void:
|
|
if _active_region == null:
|
|
return
|
|
var half: Vector2 = _active_region.get_playable_half_extents()
|
|
var wall_margin := WORLD_BOUNDARY_SHORELINE_CLEARANCE
|
|
var wall_height := 14.0
|
|
var bounds_root := $WorldBounds as Node3D
|
|
var north_south_size := Vector3(
|
|
half.x * 2.0 + wall_margin * 2.0,
|
|
wall_height,
|
|
2.0,
|
|
)
|
|
var east_west_size := Vector3(
|
|
2.0,
|
|
wall_height,
|
|
half.y * 2.0 + wall_margin * 2.0,
|
|
)
|
|
_set_bound(bounds_root.get_node("North"), Vector3(0.0, 5.0, half.y + wall_margin), north_south_size)
|
|
_set_bound(bounds_root.get_node("South"), Vector3(0.0, 5.0, -half.y - wall_margin), north_south_size)
|
|
_set_bound(bounds_root.get_node("West"), Vector3(-half.x - wall_margin, 5.0, 0.0), east_west_size)
|
|
_set_bound(bounds_root.get_node("East"), Vector3(half.x + wall_margin, 5.0, 0.0), east_west_size)
|
|
_below_world_failsafe.position = Vector3(0.0, -7.0, 0.0)
|
|
var coverage := _below_world_failsafe.get_node("Coverage") as CollisionShape3D
|
|
var coverage_shape := coverage.shape as BoxShape3D
|
|
if coverage_shape != null:
|
|
coverage_shape.size = Vector3(half.x * 2.0, 4.0, half.y * 2.0)
|
|
|
|
|
|
func _set_bound(body: Node3D, position: Vector3, size: Vector3) -> void:
|
|
body.position = position
|
|
var collision := body.get_node("Shape") as CollisionShape3D
|
|
var shape := collision.shape as BoxShape3D
|
|
if shape != null:
|
|
shape.size = size
|