Add playable starter island

This commit is contained in:
Alexander Sellite 2026-07-27 00:13:07 -04:00
parent 633bbd9a88
commit 8032d266ec
14 changed files with 402 additions and 173 deletions

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bk38p45urtjis"
path="res://.godot/imported/starter_island.glb-2e424e31e476d52eee139d119e5596cc.scn"
[deps]
source_file="res://art/exported/environment/terrain/starter_island.glb"
dest_files=["res://.godot/imported/starter_island.glb-2e424e31e476d52eee139d119e5596cc.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

View file

Binary file not shown.

View file

@ -72,9 +72,11 @@ var _pickup_position: Vector3
var _active_tween: Tween
var _rod_tween: Tween
var _bite_tween: Tween
var _default_water_surface_offset_y: float
func _ready() -> void:
_default_water_surface_offset_y = water_surface_offset_y
_line.mesh = _line_mesh
cleanup()
@ -94,6 +96,14 @@ func get_water_surface_height() -> float:
return global_position.y + water_surface_offset_y
func set_water_surface_height(surface_height: float) -> void:
water_surface_offset_y = surface_height - global_position.y
func reset_water_surface_height() -> void:
water_surface_offset_y = _default_water_surface_offset_y
func set_line_mode(mode: LineMode) -> void:
_line_mode = mode
_line.visible = mode != LineMode.HIDDEN

View file

@ -431,6 +431,7 @@ func _begin_aiming(player: PlayerType) -> void:
if state != FishingState.READY or _active_player != null:
return
_presentation.reset_water_surface_height()
_active_player = player
_active_player.set_movement_enabled(false)
_cast_direction = _capture_cast_direction(_active_player)
@ -894,6 +895,7 @@ func _cleanup_attempt(
_catch_controller.reset()
if visual_outcome.is_empty():
_presentation.cleanup()
_presentation.reset_water_surface_height()
else:
if visual_outcome in [&"catch", &"invalid", &"withdrawal"]:
_presentation.set_line_mode(
@ -918,6 +920,7 @@ func _cleanup_attempt(
func _return_to_ready() -> void:
_presentation.reset_water_surface_height()
state = FishingState.READY
_state_time_remaining = 0.0
_cooldown_status = ""
@ -954,11 +957,21 @@ func _capture_cast_direction(player: PlayerType) -> Vector3:
func _calculate_cast_target(distance: float) -> Vector3:
return Vector3(
var target := Vector3(
_cast_origin_position.x + _cast_direction.x * distance,
_presentation.get_water_surface_height(),
_cast_origin_position.z + _cast_direction.z * distance
)
var water_region: FishableWaterRegionType = get_fishable_water_region(
target
)
if water_region != null:
target.y = water_region.surface_height
_presentation.set_water_surface_height(target.y)
else:
_presentation.reset_water_surface_height()
target.y = _presentation.get_water_surface_height()
return target
func is_target_fishable(target: Vector3) -> bool:

View file

@ -54,6 +54,8 @@ var _quit_in_progress: bool = false
func _ready() -> void:
_player.global_transform = _test_world.get_player_spawn_transform()
_player.velocity = Vector3.ZERO
_player.fish_sale_service.setup(
_player.inventory,
_player.wallet

View file

@ -1,110 +1,73 @@
# NETFISHING holdover world
# NETFISHING test world
`world/test_world.tscn` remains the active world. It composes the complete
temporary map, lighting, paths, bounds, and six region scenes. The practical
footprint is approximately 180 × 220 world units, including the coastal water
and outer collision bounds.
`world/test_world.tscn` remains the active world composition. It currently
instances `world/regions/starter_island_region.tscn`, a Godot-owned gameplay
wrapper around the human-authored starter-island GLB.
## Composition
## Starter-island ownership
```text
TestWorld
├── Environment
├── SeabedBoundary
├── Regions
│ ├── VillageRegion (0, 0, 15)
│ ├── StarterPondRegion (-42, 0, 15)
│ ├── RiverRegion (0, 0, -40)
│ ├── LakeRegion (53, 0, -25)
│ ├── MarshRegion (56, 0, 38)
│ └── CoastalDockRegion (0, 0, -103)
├── Connectors
│ └── StarterIslandRegion
│ ├── Visuals
│ │ ├── StarterIslandModel
│ │ ├── OceanWater
│ │ └── PondWater
│ ├── StaticCollision
│ ├── FishingWater
│ ├── WaterRecovery
│ ├── SafeRespawns
│ ├── PlayerSpawn
│ └── Interactables
├── Safety
└── WorldBounds
```
Every region uses the same ownership groups:
The imported GLB is visual source content and remains at identity scale. The
wrapper creates concave static collision from its explicit mesh reference and
owns all water, fishing, recovery, spawn, and service placement.
- `Visuals`: replaceable graybox meshes and labels.
- `StaticCollision`: simple gameplay collision paired with the visuals.
- `FishingWater`: explicit `FishableWaterRegion` children.
- `WaterRecovery`: explicit `PlayerWaterTrigger` children.
- `SafeRespawns`: known-safe `SafeRespawnPoint` markers.
- `Interactables`: region-owned services, currently used by the village.
`GrayboxRegion` only exposes those explicit child groups plus a stable region
ID. `TestWorld` is the aggregation boundary; regions do not select fish,
control recovery, own progression, register globally, or stream themselves.
## Safe editing
Move a whole region by moving its region root under `TestWorld/Regions`. This
keeps visuals, collision, water, recovery triggers, safe points, and
interactables synchronized. Moving only a visual mesh does not move collision
or gameplay volumes, so update its paired collision intentionally when editing
inside a region.
Safe designer targets:
- Entire region roots under `TestWorld/Regions`
- Children of `Visuals`, together with paired `StaticCollision`
- Safe-respawn marker transforms
- Landmark and label transforms
- Connector transforms and their complete StaticBody children
Structural land and route blocks extend from the common walkable elevation at
`y=0` down to approximately `y=-6`. Adjacent blocks meet at deliberate shared
edges instead of stacking collision surfaces. Decorative plaza/path color is
non-colliding and sits just above the authoritative ground.
Gameplay-critical children that must stay aligned:
- A water plane, its `FishingWater` Area3D, and its `WaterRecovery` Area3D
- A dock or bank mesh and its paired shape in `StaticCollision`
- Safe points and the land/dock they target
- The Fishing Shop visual root and `FishingShopInteraction`
Move the complete `StarterIslandRegion` root to keep visuals and gameplay
volumes synchronized. When editing inside the wrapper, keep each water mesh
aligned with its matching `FishingWater` and `WaterRecovery` shapes. Safe
respawns and `PlayerSpawn` must remain on broad upper terrain.
Rename-sensitive paths:
- `TestWorld/Regions`
- `VillageRegion/Interactables/FishingShopWorld/FishingShopInteraction`
- `VillageRegion/Interactables/PelicanCoolerPerch`
- Each region's `FishingWater`, `WaterRecovery`, and `SafeRespawns` roots,
unless its exported NodePaths are updated at the same time
- `TestWorld/Regions/StarterIslandRegion`
- the NodePaths exported by `StarterIslandRegion`
- each region's `FishingWater`, `WaterRecovery`, and `SafeRespawns` roots
## Adding water and recovery
## Reverting to the modular graybox
1. Add a `FishableWaterRegion` Area3D under the region's `FishingWater`.
2. Give it collision layer 4, a clear location tag, and an explicit fish pool.
3. Add a matching `PlayerWaterTrigger` under `WaterRecovery`.
4. Give the trigger collision layer 8, mask 2, matching horizontal coverage,
and the correct `surface_height`.
5. Place two or more safe respawns on nearby broad walkable land.
6. Validate casting, shoreline collision, recovery entry, and nearest-point
selection from every side.
The previous region scenes remain unchanged under `world/regions/`. To restore
the prior holdover world, restore `world/test_world.tscn` and
`world/test_world.gd` from commit `633bbd9`, then remove the player-spawn
composition line added to `main/main.gd`. That composition instances:
`TestWorld/Safety/BelowWorldFailsafe` is a temporary whole-map recovery net.
It catches seam and below-world falls that escape normal regional water
coverage. It supplements rather than replaces the five region-local triggers;
the recovery controller still permits only one recovery sequence at a time.
- `village_region.tscn`
- `starter_pond_region.tscn`
- `river_region.tscn`
- `lake_region.tscn`
- `marsh_region.tscn`
- `coastal_dock_region.tscn`
## Services
No region asset needs to be reconstructed or recovered from deleted content.
The Fishing Shop is an instanced scene at the east side of the village plaza.
Move the complete `FishingShopWorld` instance, not only its booth meshes.
## Water and recovery
The Pelican perch is decorative signage, not an interaction NPC. Its sign says
“Sell to Pelicans from the Cooler”; the existing 0.25× Pelican sale remains
available from the Cooler anywhere in the world. Moving or removing the
decorative perch does not change that service.
The pond and surrounding ocean remain separate fishable regions using the
existing test pool. Ocean fishing and recovery use four collision shapes so
the raised island interior is not classified as ocean. Regional recovery is
supplemented by `TestWorld/Safety/BelowWorldFailsafe`.
## Temporary by design
The pond surface is at `y=2.10`. The ocean is at `y=-0.45`, between the
authored upper beach and submerged shelf rings. Fishable regions explicitly
provide their surface heights to the shared fishing presentation.
All terrain, buildings, docks, reeds, landmarks, signage, palette, and lighting
are primitive holdover presentation. A future artist or level designer can
replace `Visuals` and paired static collision without changing fishing,
recovery, shop, save, economy, or player progression code. Region IDs and
gameplay volume ownership should remain stable while visuals are replaced.
Water surfaces sit near `y=0.2`; the shared graybox seabed is near `y=-6`.
There is no structural walkable layer directly below the fishable surfaces.
The Fishing Shop remains a complete instanced interactable. The Pelican perch
remains convenience signage only; Pelican selling is available through the
Cooler from anywhere.

View file

@ -6,3 +6,4 @@ const FishPoolType = preload("res://fish/fish_pool.gd")
@export var location_tags: Array[StringName] = []
@export var fish_pool: FishPoolType
@export var selection_priority: int = 0
@export var surface_height: float = 0.3

View file

@ -0,0 +1,66 @@
class_name StarterIslandRegion
extends GrayboxRegion
const FishingShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
@export var visual_mesh_path: NodePath = (
^"Visuals/StarterIslandModel/starter_island"
)
@export var terrain_collision_shape_path: NodePath = (
^"StaticCollision/Terrain/Shape"
)
@export var player_spawn_path: NodePath = ^"PlayerSpawn"
@export var fishing_shop_path: NodePath = (
^"Interactables/FishingShopWorld/FishingShopInteraction"
)
@export var pelican_landmark_path: NodePath = (
^"Interactables/PelicanCoolerPerch"
)
func _ready() -> void:
_build_terrain_collision()
func get_player_spawn_transform() -> Transform3D:
var spawn: Marker3D = get_node_or_null(player_spawn_path) as Marker3D
return spawn.global_transform if spawn != null else global_transform
func get_fishing_shop() -> FishingShopInteractionType:
return get_node_or_null(fishing_shop_path) as FishingShopInteractionType
func get_pelican_landmark() -> Node3D:
return get_node_or_null(pelican_landmark_path) as Node3D
func has_terrain_collision() -> bool:
var collision_shape: CollisionShape3D = (
get_node_or_null(terrain_collision_shape_path) as CollisionShape3D
)
return collision_shape != null and collision_shape.shape != null
func _build_terrain_collision() -> void:
var visual_mesh: MeshInstance3D = (
get_node_or_null(visual_mesh_path) as MeshInstance3D
)
var collision_shape: CollisionShape3D = (
get_node_or_null(terrain_collision_shape_path) as CollisionShape3D
)
if visual_mesh == null or visual_mesh.mesh == null:
push_error("Starter island visual mesh is unavailable.")
return
if collision_shape == null:
push_error("Starter island collision owner is unavailable.")
return
var terrain_shape: ConcavePolygonShape3D = (
visual_mesh.mesh.create_trimesh_shape()
)
if terrain_shape == null or terrain_shape.get_faces().is_empty():
push_error("Starter island terrain collision could not be created.")
return
collision_shape.shape = terrain_shape

View file

@ -0,0 +1 @@
uid://j8syuqdau7vl

View file

@ -0,0 +1,185 @@
[gd_scene load_steps=20 format=3]
[ext_resource type="Script" path="res://world/regions/starter_island_region.gd" id="1_region"]
[ext_resource type="PackedScene" path="res://art/exported/environment/terrain/starter_island.glb" id="2_island"]
[ext_resource type="Script" path="res://world/fishable_water_region.gd" id="3_water"]
[ext_resource type="Resource" path="res://fish/pools/test_water_pool.tres" id="4_pool"]
[ext_resource type="Script" path="res://world/player_water_trigger.gd" id="5_trigger"]
[ext_resource type="Script" path="res://world/safe_respawn_point.gd" id="6_safe"]
[ext_resource type="PackedScene" path="res://world/interactables/fishing_shop_world.tscn" id="7_shop"]
[ext_resource type="PackedScene" path="res://world/interactables/pelican_buyer_world.tscn" id="8_pelican"]
[sub_resource type="StandardMaterial3D" id="WaterMaterial"]
transparency = 1
albedo_color = Color(0.10, 0.50, 0.72, 0.76)
roughness = 0.22
[sub_resource type="PlaneMesh" id="OceanWaterMesh"]
size = Vector2(120, 90)
[sub_resource type="PlaneMesh" id="PondWaterMesh"]
size = Vector2(11.5, 8.5)
[sub_resource type="BoxShape3D" id="OceanWestShape"]
size = Vector3(17, 2, 80)
[sub_resource type="BoxShape3D" id="OceanEastShape"]
size = Vector3(42, 2, 80)
[sub_resource type="BoxShape3D" id="OceanNorthSouthShape"]
size = Vector3(48, 2, 18)
[sub_resource type="BoxShape3D" id="PondFishingShape"]
size = Vector3(11.5, 4, 8.5)
[sub_resource type="BoxShape3D" id="OceanWestRecoveryShape"]
size = Vector3(17, 5, 80)
[sub_resource type="BoxShape3D" id="OceanEastRecoveryShape"]
size = Vector3(42, 5, 80)
[sub_resource type="BoxShape3D" id="OceanNorthSouthRecoveryShape"]
size = Vector3(48, 5, 18)
[sub_resource type="BoxShape3D" id="PondRecoveryShape"]
size = Vector3(11.5, 4.8, 8.5)
[node name="StarterIslandRegion" type="Node3D"]
script = ExtResource("1_region")
region_id = &"starter_island"
[node name="Visuals" type="Node3D" parent="."]
[node name="StarterIslandModel" parent="Visuals" instance=ExtResource("2_island")]
[node name="OceanWater" type="MeshInstance3D" parent="Visuals"]
position = Vector3(12, -0.45, 0)
mesh = SubResource("OceanWaterMesh")
material_override = SubResource("WaterMaterial")
[node name="PondWater" type="MeshInstance3D" parent="Visuals"]
position = Vector3(-9.4, 2.1, 2.1)
mesh = SubResource("PondWaterMesh")
material_override = SubResource("WaterMaterial")
[node name="StaticCollision" type="Node3D" parent="."]
[node name="Terrain" type="StaticBody3D" parent="StaticCollision"]
[node name="Shape" type="CollisionShape3D" parent="StaticCollision/Terrain"]
[node name="FishingWater" type="Node3D" parent="."]
[node name="PondWater" type="Area3D" parent="FishingWater"]
position = Vector3(-9.4, 2.1, 2.1)
collision_layer = 4
collision_mask = 0
monitoring = false
script = ExtResource("3_water")
location_tags = Array[StringName]([&"starter_pond"])
fish_pool = ExtResource("4_pool")
selection_priority = 1
surface_height = 2.1
[node name="Shape" type="CollisionShape3D" parent="FishingWater/PondWater"]
shape = SubResource("PondFishingShape")
[node name="OceanWater" type="Area3D" parent="FishingWater"]
position = Vector3(0, -0.45, 0)
collision_layer = 4
collision_mask = 0
monitoring = false
script = ExtResource("3_water")
location_tags = Array[StringName]([&"coast", &"ocean"])
fish_pool = ExtResource("4_pool")
surface_height = -0.45
[node name="WestShape" type="CollisionShape3D" parent="FishingWater/OceanWater"]
position = Vector3(-33.5, 0, 0)
shape = SubResource("OceanWestShape")
[node name="EastShape" type="CollisionShape3D" parent="FishingWater/OceanWater"]
position = Vector3(44, 0, 0)
shape = SubResource("OceanEastShape")
[node name="NorthShape" type="CollisionShape3D" parent="FishingWater/OceanWater"]
position = Vector3(-1, 0, 31)
shape = SubResource("OceanNorthSouthShape")
[node name="SouthShape" type="CollisionShape3D" parent="FishingWater/OceanWater"]
position = Vector3(-1, 0, -31)
shape = SubResource("OceanNorthSouthShape")
[node name="WaterRecovery" type="Node3D" parent="."]
[node name="PondRecovery" type="Area3D" parent="WaterRecovery"]
position = Vector3(-9.4, -0.3, 2.1)
collision_layer = 8
collision_mask = 2
script = ExtResource("5_trigger")
surface_height = 2.1
[node name="Shape" type="CollisionShape3D" parent="WaterRecovery/PondRecovery"]
shape = SubResource("PondRecoveryShape")
[node name="OceanRecovery" type="Area3D" parent="WaterRecovery"]
position = Vector3(0, -2.95, 0)
collision_layer = 8
collision_mask = 2
script = ExtResource("5_trigger")
surface_height = -0.45
[node name="WestShape" type="CollisionShape3D" parent="WaterRecovery/OceanRecovery"]
position = Vector3(-33.5, 0, 0)
shape = SubResource("OceanWestRecoveryShape")
[node name="EastShape" type="CollisionShape3D" parent="WaterRecovery/OceanRecovery"]
position = Vector3(44, 0, 0)
shape = SubResource("OceanEastRecoveryShape")
[node name="NorthShape" type="CollisionShape3D" parent="WaterRecovery/OceanRecovery"]
position = Vector3(-1, 0, 31)
shape = SubResource("OceanNorthSouthRecoveryShape")
[node name="SouthShape" type="CollisionShape3D" parent="WaterRecovery/OceanRecovery"]
position = Vector3(-1, 0, -31)
shape = SubResource("OceanNorthSouthRecoveryShape")
[node name="SafeRespawns" type="Node3D" parent="."]
[node name="SpawnPlateau" type="Marker3D" parent="SafeRespawns"]
position = Vector3(0, 3.95, 13)
script = ExtResource("6_safe")
[node name="PondEast" type="Marker3D" parent="SafeRespawns"]
position = Vector3(-0.5, 3.95, 2)
script = ExtResource("6_safe")
[node name="PondSouth" type="Marker3D" parent="SafeRespawns"]
position = Vector3(-9, 3.95, 11.5)
script = ExtResource("6_safe")
[node name="ShopApproach" type="Marker3D" parent="SafeRespawns"]
position = Vector3(13.5, 3.95, 6)
script = ExtResource("6_safe")
[node name="SouthPlateau" type="Marker3D" parent="SafeRespawns"]
position = Vector3(5, 3.95, 15)
script = ExtResource("6_safe")
[node name="EastShoreApproach" type="Marker3D" parent="SafeRespawns"]
position = Vector3(15, 3.95, -11)
script = ExtResource("6_safe")
[node name="PlayerSpawn" type="Marker3D" parent="."]
position = Vector3(0, 3.95, 13)
[node name="Interactables" type="Node3D" parent="."]
[node name="FishingShopWorld" parent="Interactables" instance=ExtResource("7_shop")]
position = Vector3(8, 3.42, 6)
rotation_degrees = Vector3(0, -25, 0)
[node name="PelicanCoolerPerch" parent="Interactables" instance=ExtResource("8_pelican")]
position = Vector3(18, 0.35, -16)
rotation_degrees = Vector3(0, -35, 0)

View file

@ -6,11 +6,14 @@ const FishingShopInteractionType = preload(
)
@onready var _regions_root: Node3D = $Regions
@onready var _starter_island: StarterIslandRegion = (
$Regions/StarterIslandRegion
)
@onready var _below_world_failsafe: PlayerWaterTrigger = (
$Safety/BelowWorldFailsafe
)
@onready var _fishing_shop: FishingShopInteractionType = (
$Regions/VillageRegion/Interactables/FishingShopWorld/FishingShopInteraction
_starter_island.get_fishing_shop()
)
@ -33,6 +36,10 @@ func get_fishing_shop() -> FishingShopInteractionType:
return _fishing_shop
func get_player_spawn_transform() -> Transform3D:
return _starter_island.get_player_spawn_transform()
func get_fishable_water_regions() -> Array[FishableWaterRegion]:
var waters: Array[FishableWaterRegion] = []
for region: GrayboxRegion in _get_regions():
@ -41,10 +48,7 @@ func get_fishable_water_regions() -> Array[FishableWaterRegion]:
func get_pelican_convenience_landmark() -> Node3D:
return (
$Regions/VillageRegion/Interactables/PelicanCoolerPerch
as Node3D
)
return _starter_island.get_pelican_landmark()
func _get_regions() -> Array[GrayboxRegion]:

View file

@ -1,13 +1,8 @@
[gd_scene load_steps=16 format=3]
[gd_scene load_steps=10 format=3]
[ext_resource type="Script" path="res://world/test_world.gd" id="1_world"]
[ext_resource type="PackedScene" path="res://world/regions/village_region.tscn" id="2_village"]
[ext_resource type="PackedScene" path="res://world/regions/starter_pond_region.tscn" id="3_pond"]
[ext_resource type="PackedScene" path="res://world/regions/river_region.tscn" id="4_river"]
[ext_resource type="PackedScene" path="res://world/regions/lake_region.tscn" id="5_lake"]
[ext_resource type="PackedScene" path="res://world/regions/marsh_region.tscn" id="6_marsh"]
[ext_resource type="PackedScene" path="res://world/regions/coastal_dock_region.tscn" id="7_coast"]
[ext_resource type="Script" path="res://world/player_water_trigger.gd" id="8_water_trigger"]
[ext_resource type="PackedScene" path="res://world/regions/starter_island_region.tscn" id="2_island"]
[ext_resource type="Script" path="res://world/player_water_trigger.gd" id="3_water_trigger"]
[sub_resource type="Environment" id="Environment"]
background_mode = 1
@ -16,30 +11,14 @@ ambient_light_source = 3
ambient_light_color = Color(0.72, 0.78, 0.84, 1)
ambient_light_energy = 0.72
[sub_resource type="BoxShape3D" id="SeabedShape"]
size = Vector3(180, 1, 220)
[sub_resource type="BoxMesh" id="SeabedMesh"]
size = Vector3(180, 1, 220)
[sub_resource type="StandardMaterial3D" id="SeabedMaterial"]
albedo_color = Color(0.18, 0.36, 0.27, 1)
[sub_resource type="BoxShape3D" id="RiverPathShape"]
size = Vector3(40, 6, 8)
[sub_resource type="BoxMesh" id="RiverPathMesh"]
size = Vector3(40, 6, 8)
[sub_resource type="BoxShape3D" id="MarshPathShape"]
size = Vector3(5, 6, 10)
[sub_resource type="BoxMesh" id="MarshPathMesh"]
size = Vector3(5, 6, 10)
[sub_resource type="StandardMaterial3D" id="PathMaterial"]
albedo_color = Color(0.72, 0.62, 0.43, 1)
[sub_resource type="BoxShape3D" id="NorthSouthBoundsShape"]
size = Vector3(180, 12, 2)
size = Vector3(120, 14, 2)
[sub_resource type="BoxShape3D" id="EastWestBoundsShape"]
size = Vector3(2, 12, 220)
size = Vector3(2, 14, 90)
[sub_resource type="BoxShape3D" id="FailsafeShape"]
size = Vector3(178, 5, 218)
size = Vector3(118, 4, 88)
[node name="TestWorld" type="Node3D"]
script = ExtResource("1_world")
@ -54,62 +33,18 @@ rotation_degrees = Vector3(-54, -32, 0)
light_energy = 1.1
shadow_enabled = true
[node name="SeabedBoundary" type="StaticBody3D" parent="."]
position = Vector3(0, -6.5, -38)
[node name="CollisionShape3D" type="CollisionShape3D" parent="SeabedBoundary"]
shape = SubResource("SeabedShape")
[node name="MeshInstance3D" type="MeshInstance3D" parent="SeabedBoundary"]
mesh = SubResource("SeabedMesh")
material_override = SubResource("SeabedMaterial")
[node name="Regions" type="Node3D" parent="."]
[node name="VillageRegion" parent="Regions" instance=ExtResource("2_village")]
position = Vector3(0, 0, 15)
[node name="StarterPondRegion" parent="Regions" instance=ExtResource("3_pond")]
position = Vector3(-42, 0, 15)
[node name="RiverRegion" parent="Regions" instance=ExtResource("4_river")]
position = Vector3(0, 0, -40)
[node name="LakeRegion" parent="Regions" instance=ExtResource("5_lake")]
position = Vector3(53, 0, -25)
[node name="MarshRegion" parent="Regions" instance=ExtResource("6_marsh")]
position = Vector3(56, 0, 38)
[node name="CoastalDockRegion" parent="Regions" instance=ExtResource("7_coast")]
position = Vector3(0, 0, -103)
[node name="Connectors" type="Node3D" parent="."]
[node name="VillageRiverPath" type="StaticBody3D" parent="Connectors"]
position = Vector3(0, -3, -7)
[node name="Shape" type="CollisionShape3D" parent="Connectors/VillageRiverPath"]
shape = SubResource("RiverPathShape")
[node name="Mesh" type="MeshInstance3D" parent="Connectors/VillageRiverPath"]
mesh = SubResource("RiverPathMesh")
material_override = SubResource("PathMaterial")
[node name="VillageMarshPath" type="StaticBody3D" parent="Connectors"]
position = Vector3(26.5, -3, 28)
[node name="Shape" type="CollisionShape3D" parent="Connectors/VillageMarshPath"]
shape = SubResource("MarshPathShape")
[node name="Mesh" type="MeshInstance3D" parent="Connectors/VillageMarshPath"]
mesh = SubResource("MarshPathMesh")
material_override = SubResource("PathMaterial")
[node name="StarterIslandRegion" parent="Regions" instance=ExtResource("2_island")]
[node name="Safety" type="Node3D" parent="."]
[node name="BelowWorldFailsafe" type="Area3D" parent="Safety"]
position = Vector3(0, -5, -38)
position = Vector3(12, -7, 0)
collision_layer = 8
collision_mask = 2
script = ExtResource("8_water_trigger")
surface_height = 0.2
script = ExtResource("3_water_trigger")
surface_height = 0.3
[node name="FailsafeShape" type="CollisionShape3D" parent="Safety/BelowWorldFailsafe"]
shape = SubResource("FailsafeShape")
@ -117,21 +52,25 @@ shape = SubResource("FailsafeShape")
[node name="WorldBounds" type="Node3D" parent="."]
[node name="North" type="StaticBody3D" parent="WorldBounds"]
position = Vector3(0, 5, 72)
position = Vector3(12, 5, 45)
[node name="Shape" type="CollisionShape3D" parent="WorldBounds/North"]
shape = SubResource("NorthSouthBoundsShape")
[node name="South" type="StaticBody3D" parent="WorldBounds"]
position = Vector3(0, 5, -148)
position = Vector3(12, 5, -45)
[node name="Shape" type="CollisionShape3D" parent="WorldBounds/South"]
shape = SubResource("NorthSouthBoundsShape")
[node name="West" type="StaticBody3D" parent="WorldBounds"]
position = Vector3(-90, 5, -38)
position = Vector3(-48, 5, 0)
[node name="Shape" type="CollisionShape3D" parent="WorldBounds/West"]
shape = SubResource("EastWestBoundsShape")
[node name="East" type="StaticBody3D" parent="WorldBounds"]
position = Vector3(90, 5, -38)
position = Vector3(72, 5, 0)
[node name="Shape" type="CollisionShape3D" parent="WorldBounds/East"]
shape = SubResource("EastWestBoundsShape")