feat: derive resource visuals from state
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
class_name HarvestableTreePresentation
|
||||
extends ResourceAmountVisual
|
||||
|
||||
const THINNED_CANOPIES := [&"CanopyLeft", &"CanopyRight"]
|
||||
|
||||
@onready var standing_tree: Node3D = $StandingTree
|
||||
@onready var stump: Node3D = $Stump
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
super()
|
||||
|
||||
|
||||
func is_standing_visible() -> bool:
|
||||
return standing_tree.visible
|
||||
|
||||
|
||||
func is_stump_visible() -> bool:
|
||||
return stump.visible
|
||||
|
||||
|
||||
func get_visible_canopy_count() -> int:
|
||||
if not standing_tree.visible:
|
||||
return 0
|
||||
var visible_count := 0
|
||||
for child in standing_tree.get_children():
|
||||
if String(child.name).begins_with("Canopy") and child.visible:
|
||||
visible_count += 1
|
||||
return visible_count
|
||||
|
||||
|
||||
func _apply_resource_amount_visual(
|
||||
_amount_remaining: float, _remaining_ratio: float, tier: AmountTier
|
||||
) -> void:
|
||||
var depleted := tier == AmountTier.DEPLETED
|
||||
standing_tree.visible = not depleted
|
||||
stump.visible = depleted
|
||||
for canopy_name in THINNED_CANOPIES:
|
||||
var canopy := standing_tree.get_node_or_null(NodePath(canopy_name)) as Node3D
|
||||
if canopy != null:
|
||||
canopy.visible = tier == AmountTier.FULL
|
||||
@@ -0,0 +1 @@
|
||||
uid://cpue05wbg6roj
|
||||
@@ -0,0 +1,42 @@
|
||||
[gd_scene load_steps=7 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/jajce/HarvestableTreePresentation.gd" id="1_script"]
|
||||
[ext_resource type="PackedScene" path="res://world/jajce/StylizedTree.tscn" id="2_tree"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="Material_stump_bark"]
|
||||
albedo_color = Color(0.29, 0.17, 0.09, 1)
|
||||
roughness = 0.96
|
||||
|
||||
[sub_resource type="CylinderMesh" id="Mesh_stump"]
|
||||
material = SubResource("Material_stump_bark")
|
||||
top_radius = 0.38
|
||||
bottom_radius = 0.48
|
||||
height = 0.82
|
||||
radial_segments = 10
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="Material_stump_cut"]
|
||||
albedo_color = Color(0.69, 0.49, 0.27, 1)
|
||||
roughness = 0.9
|
||||
|
||||
[sub_resource type="CylinderMesh" id="Mesh_stump_cut"]
|
||||
material = SubResource("Material_stump_cut")
|
||||
top_radius = 0.36
|
||||
bottom_radius = 0.37
|
||||
height = 0.055
|
||||
radial_segments = 10
|
||||
|
||||
[node name="HarvestableTreePresentation" type="Node3D"]
|
||||
script = ExtResource("1_script")
|
||||
|
||||
[node name="StandingTree" parent="." instance=ExtResource("2_tree")]
|
||||
|
||||
[node name="Stump" type="Node3D" parent="."]
|
||||
visible = false
|
||||
|
||||
[node name="Bark" type="MeshInstance3D" parent="Stump"]
|
||||
position = Vector3(0, 0.41, 0)
|
||||
mesh = SubResource("Mesh_stump")
|
||||
|
||||
[node name="CutFace" type="MeshInstance3D" parent="Stump"]
|
||||
position = Vector3(0, 0.835, 0)
|
||||
mesh = SubResource("Mesh_stump_cut")
|
||||
@@ -0,0 +1,16 @@
|
||||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="PackedScene" path="res://world/jajce/JajceWorld.tscn" id="1_world"]
|
||||
[ext_resource type="Script" path="res://world/jajce/beauty_camera.gd" id="2_camera"]
|
||||
|
||||
[node name="JajceResourceStateLookdev" type="Node3D"]
|
||||
|
||||
[node name="JajceWorld" parent="." instance=ExtResource("1_world")]
|
||||
|
||||
[node name="ResourceCamera" type="Camera3D" parent="."]
|
||||
position = Vector3(14, 9, 7)
|
||||
current = true
|
||||
fov = 40.0
|
||||
script = ExtResource("2_camera")
|
||||
focus_point = Vector3(34, 3.3, 7)
|
||||
animate_orbit = false
|
||||
@@ -1,10 +1,11 @@
|
||||
[gd_scene load_steps=6 format=3]
|
||||
[gd_scene load_steps=7 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/jajce/RiverbankResourceCluster.gd" id="1_script"]
|
||||
[ext_resource type="PackedScene" path="res://world/resource_nodes/ResourceNode.tscn" id="2_resource"]
|
||||
[ext_resource type="PackedScene" path="res://world/jajce/StylizedTree.tscn" id="3_tree"]
|
||||
[ext_resource type="PackedScene" path="res://world/jajce/StylizedBerryPatch.tscn" id="4_berry"]
|
||||
[ext_resource type="PackedScene" path="res://assets/foliage/tree.glb" id="5_conifer"]
|
||||
[ext_resource type="PackedScene" path="res://world/jajce/HarvestableTreePresentation.tscn" id="6_harvestable_tree"]
|
||||
|
||||
[node name="RiverbankResourceCluster" type="Node3D"]
|
||||
script = ExtResource("1_script")
|
||||
@@ -66,7 +67,7 @@ discovery_priority = 0.1
|
||||
[node name="MeshInstance3D" parent="ResourceAnchors/Tree_River_Resource_01/Visual"]
|
||||
visible = false
|
||||
|
||||
[node name="TreePresentation" parent="ResourceAnchors/Tree_River_Resource_01/Visual" instance=ExtResource("3_tree")]
|
||||
[node name="TreePresentation" parent="ResourceAnchors/Tree_River_Resource_01/Visual" instance=ExtResource("6_harvestable_tree")]
|
||||
scale = Vector3(1.08, 1.08, 1.08)
|
||||
|
||||
[node name="InteractionPoint" parent="ResourceAnchors/Tree_River_Resource_01"]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class_name StylizedBerryPatch
|
||||
extends Node3D
|
||||
extends ResourceAmountVisual
|
||||
|
||||
const WIND_SHADER := preload("res://world/jajce/materials/wind.gdshader")
|
||||
const LEAF_POSITIONS := [
|
||||
@@ -31,12 +31,37 @@ const BERRY_POSITIONS := [
|
||||
func _ready() -> void:
|
||||
_build_leaves()
|
||||
_build_berries()
|
||||
super()
|
||||
|
||||
|
||||
func get_presentation_instance_count() -> int:
|
||||
return LEAF_POSITIONS.size() + BERRY_POSITIONS.size()
|
||||
|
||||
|
||||
func get_visible_leaf_count() -> int:
|
||||
return $Leaves.multimesh.visible_instance_count
|
||||
|
||||
|
||||
func get_visible_berry_count() -> int:
|
||||
return $Berries.multimesh.visible_instance_count
|
||||
|
||||
|
||||
func _apply_resource_amount_visual(
|
||||
_amount_remaining: float, remaining_ratio: float, tier: AmountTier
|
||||
) -> void:
|
||||
var visible_leaves := LEAF_POSITIONS.size()
|
||||
if tier == AmountTier.LOW:
|
||||
visible_leaves = 3
|
||||
elif tier == AmountTier.DEPLETED:
|
||||
visible_leaves = 2
|
||||
$Leaves.multimesh.visible_instance_count = visible_leaves
|
||||
|
||||
var visible_berries := ceili(remaining_ratio * BERRY_POSITIONS.size())
|
||||
if tier == AmountTier.DEPLETED:
|
||||
visible_berries = 0
|
||||
$Berries.multimesh.visible_instance_count = visible_berries
|
||||
|
||||
|
||||
func _build_leaves() -> void:
|
||||
var mesh := SphereMesh.new()
|
||||
mesh.radius = 1.0
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
class_name ResourceAmountVisual
|
||||
extends Node3D
|
||||
|
||||
enum AmountTier {
|
||||
FULL,
|
||||
LOW,
|
||||
DEPLETED,
|
||||
}
|
||||
|
||||
@export_range(0.0, 1.0, 0.05) var low_amount_ratio := 0.5
|
||||
|
||||
var resource_node: ResourceNode
|
||||
var amount_ratio := 1.0
|
||||
var amount_tier := AmountTier.FULL
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
call_deferred("_bind_resource_node")
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if (
|
||||
resource_node != null
|
||||
and resource_node.amount_changed.is_connected(_on_resource_amount_changed)
|
||||
):
|
||||
resource_node.amount_changed.disconnect(_on_resource_amount_changed)
|
||||
resource_node = null
|
||||
|
||||
|
||||
func refresh_from_resource() -> void:
|
||||
if resource_node == null:
|
||||
return
|
||||
_apply_amount(resource_node.get_amount_remaining())
|
||||
|
||||
|
||||
func get_amount_tier() -> AmountTier:
|
||||
return amount_tier
|
||||
|
||||
|
||||
func get_amount_ratio() -> float:
|
||||
return amount_ratio
|
||||
|
||||
|
||||
func _bind_resource_node() -> void:
|
||||
resource_node = _find_resource_node()
|
||||
if resource_node == null:
|
||||
return
|
||||
if not resource_node.amount_changed.is_connected(_on_resource_amount_changed):
|
||||
resource_node.amount_changed.connect(_on_resource_amount_changed)
|
||||
refresh_from_resource()
|
||||
|
||||
|
||||
func _find_resource_node() -> ResourceNode:
|
||||
var candidate := get_parent()
|
||||
while candidate != null:
|
||||
if candidate is ResourceNode:
|
||||
return candidate as ResourceNode
|
||||
candidate = candidate.get_parent()
|
||||
return null
|
||||
|
||||
|
||||
func _on_resource_amount_changed(changed_id: StringName, amount_remaining: float) -> void:
|
||||
if resource_node != null and changed_id == resource_node.node_id:
|
||||
_apply_amount(amount_remaining)
|
||||
|
||||
|
||||
func _apply_amount(amount_remaining: float) -> void:
|
||||
var initial_amount := maxf(resource_node.initial_amount, 0.0)
|
||||
amount_ratio = (
|
||||
clampf(amount_remaining / initial_amount, 0.0, 1.0) if initial_amount > 0.0 else 0.0
|
||||
)
|
||||
if amount_remaining <= 0.0:
|
||||
amount_tier = AmountTier.DEPLETED
|
||||
elif amount_ratio <= low_amount_ratio:
|
||||
amount_tier = AmountTier.LOW
|
||||
else:
|
||||
amount_tier = AmountTier.FULL
|
||||
_apply_resource_amount_visual(amount_remaining, amount_ratio, amount_tier)
|
||||
|
||||
|
||||
func _apply_resource_amount_visual(
|
||||
_amount_remaining: float, _remaining_ratio: float, _tier: AmountTier
|
||||
) -> void:
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
uid://bls5qs3s3omgh
|
||||
@@ -74,6 +74,7 @@ func bind_state(resource_state: ResourceStateRecord) -> bool:
|
||||
if not state.depleted.is_connected(_on_state_depleted):
|
||||
state.depleted.connect(_on_state_depleted)
|
||||
_update_presentation()
|
||||
amount_changed.emit(node_id, state.get_amount_remaining())
|
||||
_notify_world_adapters()
|
||||
return true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user