feat: add player resource node harvesting
This commit is contained in:
@@ -77,13 +77,11 @@ skeleton = NodePath("../../..")
|
|||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.18380833, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.18380833, 0)
|
||||||
shape = SubResource("BoxShape3D_lquwl")
|
shape = SubResource("BoxShape3D_lquwl")
|
||||||
|
|
||||||
[node name="Player" type="CharacterBody3D" parent="." unique_id=2022843760 node_paths=PackedStringArray("camera_rig", "simulation_manager", "farm_zone", "forest_zone", "food_zone", "guard_zone", "study_zone")]
|
[node name="Player" type="CharacterBody3D" parent="." unique_id=2022843760 node_paths=PackedStringArray("camera_rig", "simulation_manager", "food_zone", "guard_zone", "study_zone")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
|
||||||
script = ExtResource("1_h2yge")
|
script = ExtResource("1_h2yge")
|
||||||
camera_rig = NodePath("../CameraRig")
|
camera_rig = NodePath("../CameraRig")
|
||||||
simulation_manager = NodePath("../SimulationManager")
|
simulation_manager = NodePath("../SimulationManager")
|
||||||
farm_zone = NodePath("../TaskZone/FarmZone")
|
|
||||||
forest_zone = NodePath("../TaskZone/ForestZone")
|
|
||||||
food_zone = NodePath("../TaskZone/FoodZone")
|
food_zone = NodePath("../TaskZone/FoodZone")
|
||||||
guard_zone = NodePath("../TaskZone/GuardZone")
|
guard_zone = NodePath("../TaskZone/GuardZone")
|
||||||
study_zone = NodePath("../TaskZone/StudyZone")
|
study_zone = NodePath("../TaskZone/StudyZone")
|
||||||
|
|||||||
+29
-10
@@ -7,8 +7,6 @@ extends CharacterBody3D
|
|||||||
|
|
||||||
@export var simulation_manager: Node
|
@export var simulation_manager: Node
|
||||||
|
|
||||||
@export var farm_zone: Marker3D
|
|
||||||
@export var forest_zone: Marker3D
|
|
||||||
@export var food_zone: Marker3D
|
@export var food_zone: Marker3D
|
||||||
@export var guard_zone: Marker3D
|
@export var guard_zone: Marker3D
|
||||||
@export var study_zone: Marker3D
|
@export var study_zone: Marker3D
|
||||||
@@ -62,13 +60,10 @@ func try_interact() -> void:
|
|||||||
if simulation_manager == null:
|
if simulation_manager == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
if is_near(farm_zone):
|
if try_harvest_resource_node():
|
||||||
simulation_manager.add_food(5.0)
|
return
|
||||||
print("Player gathered food for the village.")
|
|
||||||
elif is_near(forest_zone):
|
if is_near(guard_zone):
|
||||||
simulation_manager.add_wood(5.0)
|
|
||||||
print("Player gathered wood for the village.")
|
|
||||||
elif is_near(guard_zone):
|
|
||||||
simulation_manager.add_safety(3.0)
|
simulation_manager.add_safety(3.0)
|
||||||
print("Player helped guard the village.")
|
print("Player helped guard the village.")
|
||||||
elif is_near(study_zone):
|
elif is_near(study_zone):
|
||||||
@@ -76,7 +71,31 @@ func try_interact() -> void:
|
|||||||
print("Player shared knowledge.")
|
print("Player shared knowledge.")
|
||||||
elif is_near(food_zone):
|
elif is_near(food_zone):
|
||||||
simulation_manager.eat_food(stomach_capacity_for_food)
|
simulation_manager.eat_food(stomach_capacity_for_food)
|
||||||
print("Player eating shit up.")
|
print("Player ate food from the village supply.")
|
||||||
|
|
||||||
|
func try_harvest_resource_node() -> bool:
|
||||||
|
var node := ResourceNode.find_nearest_for_player(global_position, interaction_range)
|
||||||
|
if node == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
if not node.enabled:
|
||||||
|
print("%s is unavailable." % node.node_id)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if node.is_depleted():
|
||||||
|
print("%s is depleted." % node.node_id)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if not simulation_manager.has_method("harvest_resource_node"):
|
||||||
|
push_error("Player: SimulationManager cannot harvest ResourceNodes")
|
||||||
|
return true
|
||||||
|
|
||||||
|
var extracted: float = simulation_manager.harvest_resource_node(node)
|
||||||
|
if extracted > 0.0:
|
||||||
|
print("Player gathered %s %s from %s." % [extracted, node.resource_id, node.node_id])
|
||||||
|
else:
|
||||||
|
print("%s could not be harvested." % node.node_id)
|
||||||
|
return true
|
||||||
|
|
||||||
func is_near(zone: Marker3D) -> bool:
|
func is_near(zone: Marker3D) -> bool:
|
||||||
if zone == null:
|
if zone == null:
|
||||||
|
|||||||
@@ -208,24 +208,40 @@ func notify_npc_navigation_failed(npc_id: int) -> void:
|
|||||||
func notify_npc_target_unavailable(npc_id: int) -> void:
|
func notify_npc_target_unavailable(npc_id: int) -> void:
|
||||||
notify_npc_navigation_failed(npc_id)
|
notify_npc_navigation_failed(npc_id)
|
||||||
|
|
||||||
|
func harvest_resource_node(node: ResourceNode) -> float:
|
||||||
|
if node == null or not node.can_player_use:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
var extracted := node.extract()
|
||||||
|
if extracted <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
village.apply_resource_delta(node.resource_id, extracted)
|
||||||
|
village_changed.emit(village)
|
||||||
|
|
||||||
|
if DEBUG_LOGS:
|
||||||
|
print("[SimulationManager] Player extracted ", extracted, " ", node.resource_id, " from ", node.node_id)
|
||||||
|
|
||||||
|
return extracted
|
||||||
|
|
||||||
func eat_food(amount: float) -> void:
|
func eat_food(amount: float) -> void:
|
||||||
village.food -= amount
|
village.apply_resource_delta(&"food", -amount)
|
||||||
village_changed.emit(village)
|
village_changed.emit(village)
|
||||||
|
|
||||||
func add_food(amount: float) -> void:
|
func add_food(amount: float) -> void:
|
||||||
village.food += amount
|
village.apply_resource_delta(&"food", amount)
|
||||||
village_changed.emit(village)
|
village_changed.emit(village)
|
||||||
|
|
||||||
func add_wood(amount: float) -> void:
|
func add_wood(amount: float) -> void:
|
||||||
village.wood += amount
|
village.apply_resource_delta(&"wood", amount)
|
||||||
village_changed.emit(village)
|
village_changed.emit(village)
|
||||||
|
|
||||||
func add_safety(amount: float) -> void:
|
func add_safety(amount: float) -> void:
|
||||||
village.safety = clamp(village.safety + amount, 0.0, 100.0)
|
village.apply_resource_delta(&"safety", amount)
|
||||||
village_changed.emit(village)
|
village_changed.emit(village)
|
||||||
|
|
||||||
func add_knowledge(amount: float) -> void:
|
func add_knowledge(amount: float) -> void:
|
||||||
village.knowledge += amount
|
village.apply_resource_delta(&"knowledge", amount)
|
||||||
village_changed.emit(village)
|
village_changed.emit(village)
|
||||||
|
|
||||||
func get_starving_count() -> int:
|
func get_starving_count() -> int:
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
var main_scene: Node
|
||||||
|
var failures: Array[String] = []
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
call_deferred("_run")
|
||||||
|
|
||||||
|
func _run() -> void:
|
||||||
|
main_scene = load("res://main.tscn").instantiate()
|
||||||
|
root.add_child(main_scene)
|
||||||
|
await process_frame
|
||||||
|
await physics_frame
|
||||||
|
|
||||||
|
var simulation_manager := main_scene.get_node("SimulationManager")
|
||||||
|
var player := main_scene.get_node("Player")
|
||||||
|
var bush := main_scene.get_node("ResourceNodes/BerryBush_01") as ResourceNode
|
||||||
|
var tree := main_scene.get_node("ResourceNodes/Tree_01") as ResourceNode
|
||||||
|
simulation_manager.set_process(false)
|
||||||
|
|
||||||
|
bush.amount_remaining = 1.0
|
||||||
|
_check(bush.reserve(999), "Player contention setup should reserve the bush")
|
||||||
|
player.global_position = bush.interaction_point.global_position
|
||||||
|
var food_before: float = simulation_manager.village.food
|
||||||
|
player.try_interact()
|
||||||
|
|
||||||
|
_check(is_equal_approx(bush.amount_remaining, 0.0), "Player should deplete the nearby bush")
|
||||||
|
_check(is_equal_approx(simulation_manager.village.food, food_before + 1.0), "Village should receive exactly the bush's remaining food")
|
||||||
|
_check(bush.reserved_by == -1, "Depletion should release the NPC reservation")
|
||||||
|
|
||||||
|
player.global_position = tree.interaction_point.global_position
|
||||||
|
var wood_before: float = simulation_manager.village.wood
|
||||||
|
var tree_before := tree.amount_remaining
|
||||||
|
player.try_interact()
|
||||||
|
|
||||||
|
_check(is_equal_approx(tree.amount_remaining, tree_before - tree.yield_per_action), "Player should extract the tree's configured yield")
|
||||||
|
_check(is_equal_approx(simulation_manager.village.wood, wood_before + tree.yield_per_action), "Village should receive exactly the extracted wood")
|
||||||
|
|
||||||
|
if failures.is_empty():
|
||||||
|
print("[TEST] ResourceNode player parity passed")
|
||||||
|
quit(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
for failure in failures:
|
||||||
|
push_error("[TEST] " + failure)
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
func _check(condition: bool, message: String) -> void:
|
||||||
|
if not condition:
|
||||||
|
failures.append(message)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dunn1mc6772fq
|
||||||
@@ -67,6 +67,9 @@ func extract(requested_amount: float = yield_per_action) -> float:
|
|||||||
amount_remaining -= extracted
|
amount_remaining -= extracted
|
||||||
amount_changed.emit(node_id, amount_remaining)
|
amount_changed.emit(node_id, amount_remaining)
|
||||||
if is_depleted():
|
if is_depleted():
|
||||||
|
if reserved_by >= 0:
|
||||||
|
reserved_by = -1
|
||||||
|
reservation_changed.emit(node_id, -1)
|
||||||
depleted.emit(node_id)
|
depleted.emit(node_id)
|
||||||
if hide_visual_when_depleted and has_node("Visual"):
|
if hide_visual_when_depleted and has_node("Visual"):
|
||||||
$Visual.visible = false
|
$Visual.visible = false
|
||||||
@@ -104,3 +107,15 @@ static func find_available(action: StringName, agent_id: int, from_position: Vec
|
|||||||
best_dist = dist
|
best_dist = dist
|
||||||
best = node
|
best = node
|
||||||
return best
|
return best
|
||||||
|
|
||||||
|
static func find_nearest_for_player(from_position: Vector3, max_distance: float) -> ResourceNode:
|
||||||
|
var best: ResourceNode
|
||||||
|
var best_dist := max_distance * max_distance
|
||||||
|
for node in _all:
|
||||||
|
if not node.can_player_use:
|
||||||
|
continue
|
||||||
|
var dist := from_position.distance_squared_to(node.interaction_point.global_position)
|
||||||
|
if dist <= best_dist:
|
||||||
|
best_dist = dist
|
||||||
|
best = node
|
||||||
|
return best
|
||||||
|
|||||||
Reference in New Issue
Block a user