feat: let the player talk to villagers and accept or decline personal requests
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
extends SceneTree
|
||||
|
||||
var failures: Array[String] = []
|
||||
var opened_quest_ids: Array[int] = []
|
||||
var expired_quest_ids: Array[int] = []
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var manager := _create_manager()
|
||||
manager.player_quest_opened.connect(_on_quest_opened)
|
||||
manager.player_quest_expired.connect(_on_quest_expired)
|
||||
var berry := _register_resource(manager, &"talk_berry_bush")
|
||||
var actor: SimNPC = manager.npcs[0]
|
||||
var interested: SimNPC = manager.npcs[2]
|
||||
_set_pantry_amount(manager, 1.0)
|
||||
for npc in manager.npcs:
|
||||
npc.position = Vector3(40.0 + npc.id * 10.0, 0.0, 0.0)
|
||||
actor.position = Vector3.ZERO
|
||||
interested.position = Vector3(4.0, 0.0, 0.0)
|
||||
actor.hunger = 60.0
|
||||
interested.hunger = 90.0
|
||||
manager.economy.withdraw_to_inventory(actor, SimulationIds.RESOURCE_FOOD, 1.0)
|
||||
var opportunity: OpportunityStateRecord = manager.get_active_opportunity()
|
||||
_check(
|
||||
opportunity != null and opportunity.get_interested_npc_id() == interested.id,
|
||||
"The talk proof needs an open need owned by the inspected villager",
|
||||
)
|
||||
if opportunity == null:
|
||||
berry.free()
|
||||
manager.free()
|
||||
_finish()
|
||||
return
|
||||
|
||||
var talk: PlayerTalkResult = manager.get_villager_talk(interested.id)
|
||||
_check(
|
||||
(
|
||||
talk != null
|
||||
and talk.npc_id == interested.id
|
||||
and talk.npc_name == interested.npc_name
|
||||
and talk.has_need
|
||||
and talk.can_accept
|
||||
and interested.npc_name in talk.greeting
|
||||
),
|
||||
"Talking should greet the villager and surface their real need",
|
||||
)
|
||||
|
||||
var checksum_before: String = manager.get_state_checksum()
|
||||
var accepted: PlayerQuestRecord = manager.accept_villager_request(interested.id)
|
||||
print(
|
||||
" debug accepted=",
|
||||
accepted,
|
||||
" opened=",
|
||||
opened_quest_ids,
|
||||
" active=",
|
||||
manager.get_active_player_quest(),
|
||||
" checksum_same=",
|
||||
manager.get_state_checksum() == checksum_before,
|
||||
" resolved=",
|
||||
manager.get_player_standing().get_resolved_needs(),
|
||||
)
|
||||
_check(
|
||||
(
|
||||
accepted != null
|
||||
and accepted.is_open()
|
||||
and accepted.get_requester_npc_id() == interested.id
|
||||
and opened_quest_ids == [accepted.get_quest_id()]
|
||||
and manager.get_state_checksum() == checksum_before
|
||||
and manager.get_player_standing().get_resolved_needs() == 0
|
||||
),
|
||||
"Accepting a request should surface the same open quest without granting standing yet",
|
||||
)
|
||||
var accepted_again: PlayerQuestRecord = manager.accept_villager_request(interested.id)
|
||||
_check(
|
||||
accepted_again != null and accepted_again.get_quest_id() == accepted.get_quest_id(),
|
||||
"Re-accepting should not duplicate the open quest",
|
||||
)
|
||||
|
||||
var carry_state: PlayerStateRecord = manager.get_player_state()
|
||||
carry_state.add_inventory(SimulationIds.RESOURCE_FOOD, 1.0)
|
||||
var deposited: float = manager.player_deposit(SimulationIds.RESOURCE_FOOD)
|
||||
_check(
|
||||
(
|
||||
is_equal_approx(deposited, 1.0)
|
||||
and manager.get_active_player_quest() == null
|
||||
and manager.get_player_standing().get_resolved_needs() == 1
|
||||
),
|
||||
"Completing the accepted request through a real deposit should resolve it",
|
||||
)
|
||||
|
||||
var declined := _create_manager(904)
|
||||
declined.player_quest_opened.connect(_on_quest_opened)
|
||||
declined.player_quest_expired.connect(_on_quest_expired)
|
||||
var berry2 := _register_resource(declined, &"talk_berry_bush_2")
|
||||
var actor2: SimNPC = declined.npcs[0]
|
||||
var interested2: SimNPC = declined.npcs[2]
|
||||
_set_pantry_amount(declined, 1.0)
|
||||
for npc in declined.npcs:
|
||||
npc.position = Vector3(40.0 + npc.id * 10.0, 0.0, 0.0)
|
||||
actor2.position = Vector3.ZERO
|
||||
interested2.position = Vector3(4.0, 0.0, 0.0)
|
||||
actor2.hunger = 60.0
|
||||
interested2.hunger = 90.0
|
||||
declined.economy.withdraw_to_inventory(actor2, SimulationIds.RESOURCE_FOOD, 1.0)
|
||||
var declined_quest: PlayerQuestRecord = declined.get_active_player_quest()
|
||||
_check(declined_quest != null, "The decline branch needs an open quest")
|
||||
if declined_quest == null:
|
||||
berry.free()
|
||||
berry2.free()
|
||||
manager.free()
|
||||
declined.free()
|
||||
_finish()
|
||||
return
|
||||
var gratitude_before: float = declined.get_player_standing().get_gratitude(interested2.id)
|
||||
var refused: PlayerQuestRecord = declined.decline_villager_request(interested2.id)
|
||||
_check(
|
||||
(
|
||||
refused != null
|
||||
and refused.get_quest_id() == declined_quest.get_quest_id()
|
||||
and expired_quest_ids.has(declined_quest.get_quest_id())
|
||||
and declined.get_active_player_quest() == null
|
||||
and declined.get_player_standing().get_gratitude(interested2.id) <= gratitude_before
|
||||
),
|
||||
"Declining a personal request should close the quest without granting gratitude",
|
||||
)
|
||||
|
||||
berry.free()
|
||||
berry2.free()
|
||||
manager.free()
|
||||
declined.free()
|
||||
_finish()
|
||||
|
||||
|
||||
func _register_resource(manager: Node, node_id: StringName) -> ResourceNode:
|
||||
var node := ResourceNode.new()
|
||||
node.name = "TalkResource"
|
||||
node.node_id = node_id
|
||||
node.action_id = SimulationIds.ACTION_GATHER_FOOD
|
||||
node.resource_id = SimulationIds.RESOURCE_FOOD
|
||||
node.initial_amount = 2.0
|
||||
node.yield_per_action = 2.0
|
||||
node.debug_label_enabled = false
|
||||
var interaction_point := Marker3D.new()
|
||||
interaction_point.name = "InteractionPoint"
|
||||
node.add_child(interaction_point)
|
||||
root.add_child(node)
|
||||
_check(
|
||||
manager.register_resource_node(node),
|
||||
"The talk proof should bind a real finite food ResourceNode"
|
||||
)
|
||||
return node
|
||||
|
||||
|
||||
func _create_manager(seed_value: int = 901) -> Node:
|
||||
var manager: Node = load("res://simulation/SimulationManager.gd").new()
|
||||
manager.simulation_seed = seed_value
|
||||
manager.debug_logs = false
|
||||
var home_positions: Array[Vector3] = [
|
||||
Vector3(0.0, 0.0, 0.0),
|
||||
Vector3(2.0, 0.0, 0.0),
|
||||
Vector3(10.0, 0.0, 0.0),
|
||||
Vector3(12.0, 0.0, 0.0),
|
||||
Vector3(30.0, 0.0, 30.0),
|
||||
Vector3(40.0, 0.0, 40.0),
|
||||
]
|
||||
manager.home_positions = home_positions
|
||||
root.add_child(manager)
|
||||
manager.set_process(false)
|
||||
return manager
|
||||
|
||||
|
||||
func _set_pantry_amount(manager: Node, amount: float) -> void:
|
||||
var pantry: StorageStateRecord = manager.get_pantry()
|
||||
pantry.withdraw(SimulationIds.RESOURCE_FOOD, pantry.get_amount(SimulationIds.RESOURCE_FOOD))
|
||||
pantry.deposit(SimulationIds.RESOURCE_FOOD, amount)
|
||||
manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
||||
|
||||
|
||||
func _on_quest_opened(quest: PlayerQuestRecord) -> void:
|
||||
opened_quest_ids.append(quest.get_quest_id())
|
||||
|
||||
|
||||
func _on_quest_expired(quest: PlayerQuestRecord) -> void:
|
||||
expired_quest_ids.append(quest.get_quest_id())
|
||||
|
||||
|
||||
func _check(condition: bool, message: String) -> void:
|
||||
if not condition:
|
||||
failures.append(message)
|
||||
|
||||
|
||||
func _finish() -> void:
|
||||
if failures.is_empty():
|
||||
print("[TEST] Player talk and personal negotiation passed")
|
||||
quit(0)
|
||||
return
|
||||
for failure in failures:
|
||||
push_error("[TEST] " + failure)
|
||||
quit(1)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cdh2d6y6jvajo
|
||||
Reference in New Issue
Block a user