Unify fishing surfaces and presentation

This commit is contained in:
Alexander Sellite 2026-08-01 15:39:25 -04:00
parent c596b2aee7
commit 070765741c
19 changed files with 1705 additions and 372 deletions

View file

@ -124,6 +124,9 @@ position = Vector3(-9.4, 2.51, 2.1)
script = ExtResource("10_water_body")
surface_size = Vector2(16.1, 12.3675)
water_material = ExtResource("19_fresh_water")
fish_pool = ExtResource("12_pond_pool")
location_tags = Array[StringName]([&"starter_pond"])
selection_priority = 1
[node name="VisualWater" type="MeshInstance3D" parent="WaterBodies/Pond" unique_id=1034265960]
material_override = ExtResource("19_fresh_water")
@ -173,19 +176,19 @@ fish_pool = ExtResource("4_pool")
surface_height_mode = 1
[node name="WestShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=357406062]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45.5, 0, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45.5, -1, 0)
shape = SubResource("OceanWestShape")
[node name="EastShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=772224738]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 32, 0, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 32, -1, 0)
shape = SubResource("OceanEastShape")
[node name="NorthShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=646147646]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, 0, 31)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, -1, 31)
shape = SubResource("OceanNorthShape")
[node name="SouthShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=894083854]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, 0, -31)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, -1, -31)
shape = SubResource("OceanSouthShape")
[node name="RecoveryRegions" type="Node3D" parent="WaterBodies/Ocean" unique_id=463706703]

48
world/water_body.tscn Normal file
View file

@ -0,0 +1,48 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://world/water_body_authoring.gd" id="1_authoring"]
[ext_resource type="Script" path="res://world/fishable_water_region.gd" id="2_fishing"]
[ext_resource type="Script" path="res://world/player_water_trigger.gd" id="3_recovery"]
[ext_resource type="Material" path="res://world/materials/stylized_water.tres" id="4_water"]
[sub_resource type="PlaneMesh" id="WaterSurfaceMesh"]
resource_local_to_scene = true
size = Vector2(10, 10)
[sub_resource type="BoxShape3D" id="FishingVolumeShape"]
resource_local_to_scene = true
size = Vector3(10, 4, 10)
[sub_resource type="BoxShape3D" id="RecoveryVolumeShape"]
resource_local_to_scene = true
size = Vector3(10, 4.8, 10)
[node name="WaterBody" type="Node3D"]
script = ExtResource("1_authoring")
water_material = ExtResource("4_water")
[node name="VisualWater" type="MeshInstance3D" parent="."]
cast_shadow = 0
material_override = ExtResource("4_water")
mesh = SubResource("WaterSurfaceMesh")
[node name="FishingRegion" type="Area3D" parent="."]
collision_layer = 4
collision_mask = 0
monitoring = false
script = ExtResource("2_fishing")
surface_height_mode = 1
[node name="Shape" type="CollisionShape3D" parent="FishingRegion"]
position = Vector3(0, -2, 0)
shape = SubResource("FishingVolumeShape")
[node name="RecoveryRegion" type="Area3D" parent="."]
position = Vector3(0, -2.4, 0)
collision_layer = 8
collision_mask = 2
script = ExtResource("3_recovery")
surface_height_mode = 1
[node name="Shape" type="CollisionShape3D" parent="RecoveryRegion"]
shape = SubResource("RecoveryVolumeShape")

View file

@ -2,6 +2,8 @@
class_name WaterBodyAuthoring
extends Node3D
const FishPoolType = preload("res://fish/fish_pool.gd")
@export_group("Surface")
## Visible water footprint in meters.
@export_custom(
@ -19,31 +21,29 @@ var surface_size: Vector2 = Vector2(10.0, 10.0):
_sync_owned_nodes()
@export_group("Fishing Coverage")
## Extra fishable coverage beyond each edge of the visible surface.
@export_custom(
PROPERTY_HINT_RANGE,
"0.0,20.0,0.1,or_greater,suffix:m",
)
var fishing_padding: Vector2 = Vector2.ZERO:
set(value):
fishing_padding = Vector2(maxf(value.x, 0.0), maxf(value.y, 0.0))
_sync_owned_nodes()
@export_range(0.1, 20.0, 0.1, "or_greater", "suffix:m")
var fishing_depth: float = 4.0:
set(value):
fishing_depth = maxf(value, 0.1)
_sync_owned_nodes()
@export var fish_pool: FishPoolType:
set(value):
fish_pool = value
_sync_owned_nodes()
@export var water_type: WaterType.Type = WaterType.Type.FRESH_WATER:
set(value):
water_type = value
_sync_owned_nodes()
@export var location_tags: Array[StringName] = []:
set(value):
location_tags = value
_sync_owned_nodes()
@export var selection_priority: int = 0:
set(value):
selection_priority = value
_sync_owned_nodes()
@export_group("Recovery Coverage")
## Extra recovery coverage beyond each edge of the visible surface.
@export_custom(
PROPERTY_HINT_RANGE,
"0.0,20.0,0.1,or_greater,suffix:m",
)
var recovery_padding: Vector2 = Vector2.ZERO:
set(value):
recovery_padding = Vector2(maxf(value.x, 0.0), maxf(value.y, 0.0))
_sync_owned_nodes()
@export_range(0.1, 20.0, 0.1, "or_greater", "suffix:m")
var recovery_depth: float = 4.8:
set(value):
@ -56,6 +56,8 @@ var visual_water_path: NodePath = ^"VisualWater"
@export_node_path("CollisionShape3D")
var fishing_shape_path: NodePath = ^"FishingRegion/Shape"
@export_node_path("Area3D")
var fishing_region_path: NodePath = ^"FishingRegion"
@export_node_path("Area3D")
var recovery_region_path: NodePath = ^"RecoveryRegion"
@export_node_path("CollisionShape3D")
var recovery_shape_path: NodePath = ^"RecoveryRegion/Shape"
@ -80,13 +82,28 @@ func _sync_owned_nodes() -> void:
var fishing_shape_node := (
get_node_or_null(fishing_shape_path) as CollisionShape3D
)
var fishing_region := (
get_node_or_null(fishing_region_path) as FishableWaterRegion
)
if fishing_region != null:
fishing_region.fish_pool = fish_pool
fishing_region.water_type = water_type
fishing_region.location_tags = location_tags.duplicate()
fishing_region.selection_priority = selection_priority
fishing_region.surface_height_mode = (
FishableWaterRegion.SurfaceHeightMode.PARENT_GLOBAL_Y
)
if fishing_shape_node != null:
var fishing_shape := fishing_shape_node.shape as BoxShape3D
if fishing_shape != null:
# The authored root is the one water-surface height. Keep the fishable
# volume entirely below that plane so visible water and interaction
# can be moved together without a second height to tune.
fishing_shape_node.position.y = -fishing_depth * 0.5
fishing_shape.size = Vector3(
surface_size.x + fishing_padding.x * 2.0,
surface_size.x,
fishing_depth,
surface_size.y + fishing_padding.y * 2.0
surface_size.y
)
var recovery_region := (
@ -101,9 +118,9 @@ func _sync_owned_nodes() -> void:
var recovery_shape := recovery_shape_node.shape as BoxShape3D
if recovery_shape != null:
recovery_shape.size = Vector3(
surface_size.x + recovery_padding.x * 2.0,
surface_size.x,
recovery_depth,
surface_size.y + recovery_padding.y * 2.0
surface_size.y
)
if Engine.is_editor_hint():
@ -139,11 +156,9 @@ func _get_configuration_warnings() -> PackedStringArray:
)
if recovery_shape == null or not recovery_shape.shape is BoxShape3D:
warnings.append("RecoveryRegion must provide a BoxShape3D.")
var fishing_region := (
fishing_shape.get_parent() as FishableWaterRegion
if fishing_shape != null
else null
)
var fishing_region := get_node_or_null(
fishing_region_path
) as FishableWaterRegion
if fishing_region == null:
warnings.append("FishingRegion must use FishableWaterRegion.")
elif (
@ -155,4 +170,6 @@ func _get_configuration_warnings() -> PackedStringArray:
)
if water_material == null:
warnings.append("Assign a water material.")
if fish_pool == null:
warnings.append("Assign a fish pool.")
return warnings