feat: give the player a real carried inventory and deposit transactions
This commit is contained in:
@@ -30,6 +30,7 @@ signal player_quest_expired(quest: PlayerQuestRecord)
|
||||
signal player_standing_changed(standing: PlayerStandingRecord)
|
||||
signal player_tier_advanced(tier: int, tier_name: String)
|
||||
signal player_needs_changed(state: PlayerStateRecord)
|
||||
signal player_inventory_changed(state: PlayerStateRecord)
|
||||
signal player_died(state: PlayerStateRecord)
|
||||
|
||||
var village := SimVillage.new()
|
||||
@@ -85,6 +86,8 @@ func _ready() -> void:
|
||||
event_log.event_recorded.connect(_on_economic_event_recorded)
|
||||
event_recorder.configure(event_log, Callable(self, "get_tick_count_for_recording"))
|
||||
event_recorder.set_npcs(npcs)
|
||||
player_needs.configure(economy, event_recorder)
|
||||
player_needs.set_resource_states(resource_states)
|
||||
economy.inventory_changed.connect(_on_economy_inventory_changed)
|
||||
economy.economic_event_requested.connect(event_recorder.record_economic)
|
||||
economy.narrative_event_requested.connect(event_recorder.record_narrative)
|
||||
@@ -578,30 +581,28 @@ func _on_economic_event_recorded(event: EconomicEventRecord) -> void:
|
||||
for learned_record in learned_records:
|
||||
_apply_new_event_knowledge(learned_record, event)
|
||||
if StringName(event.data["event_type"]) == SimulationIds.EVENT_ANIMAL_FED:
|
||||
_resolve_animal_care_quest(event)
|
||||
_finish_quest_resolution(
|
||||
player_quest_system.on_animal_fed(
|
||||
animal_care.get_state(StringName(event.data["destination_id"])), event, tick_count
|
||||
)
|
||||
)
|
||||
_update_opportunity_from_event(event)
|
||||
economic_event_recorded.emit(event)
|
||||
|
||||
|
||||
func _resolve_animal_care_quest(event: EconomicEventRecord) -> void:
|
||||
var animal_state := animal_care.get_state(StringName(event.data["destination_id"]))
|
||||
var resolution := player_quest_system.on_animal_fed(animal_state, event, tick_count)
|
||||
_finish_quest_resolution(resolution)
|
||||
|
||||
|
||||
func _finish_quest_resolution(resolution: Dictionary) -> void:
|
||||
if not resolution.has("quest"):
|
||||
return
|
||||
var quest: PlayerQuestRecord = resolution["quest"]
|
||||
if resolution["completed"]:
|
||||
player_quest_completed.emit(quest)
|
||||
var standing := player_quest_system.standing
|
||||
player_standing_changed.emit(standing)
|
||||
if standing.get_tier() > _last_player_tier:
|
||||
_last_player_tier = standing.get_tier()
|
||||
player_tier_advanced.emit(standing.get_tier(), standing.get_tier_name())
|
||||
else:
|
||||
if not resolution["completed"]:
|
||||
player_quest_expired.emit(quest)
|
||||
return
|
||||
player_quest_completed.emit(quest)
|
||||
var standing := player_quest_system.standing
|
||||
player_standing_changed.emit(standing)
|
||||
if standing.get_tier() > _last_player_tier:
|
||||
_last_player_tier = standing.get_tier()
|
||||
player_tier_advanced.emit(standing.get_tier(), standing.get_tier_name())
|
||||
|
||||
|
||||
func try_communicate_at_shared_activity(speaker_id: int, listener_id: int) -> bool:
|
||||
@@ -609,12 +610,11 @@ func try_communicate_at_shared_activity(speaker_id: int, listener_id: int) -> bo
|
||||
var listener := _find_npc_by_id(listener_id)
|
||||
if not _can_communicate_at_shared_activity(speaker, listener):
|
||||
return false
|
||||
var known_event_ids := event_knowledge_system.get_communicable_event_ids(
|
||||
for event_id in event_knowledge_system.get_communicable_event_ids(
|
||||
speaker.id,
|
||||
_get_lasting_event_ids(speaker.id),
|
||||
opportunity_system.get_open_trigger_event_id()
|
||||
)
|
||||
for event_id in known_event_ids:
|
||||
):
|
||||
var event := event_log.get_by_id(event_id)
|
||||
if event == null:
|
||||
continue
|
||||
@@ -742,22 +742,21 @@ func get_active_player_quest() -> PlayerQuestRecord:
|
||||
|
||||
|
||||
func _advance_player_needs() -> void:
|
||||
if not player_needs.advance(village):
|
||||
player_needs_changed.emit(player_needs.state)
|
||||
if player_needs.advance(village):
|
||||
player_died.emit(player_needs.state)
|
||||
player_needs.apply_death_consequence(player_quest_system.standing)
|
||||
_last_player_tier = player_quest_system.standing.get_tier()
|
||||
player_standing_changed.emit(player_quest_system.standing)
|
||||
return
|
||||
player_died.emit(player_needs.state)
|
||||
player_needs.apply_death_consequence(player_quest_system.standing)
|
||||
_last_player_tier = player_quest_system.standing.get_tier()
|
||||
player_standing_changed.emit(player_quest_system.standing)
|
||||
player_needs_changed.emit(player_needs.state)
|
||||
|
||||
|
||||
func respawn_player() -> bool:
|
||||
var was_dead := player_needs.state.is_dead()
|
||||
if not player_needs.state.is_dead():
|
||||
return false
|
||||
player_needs.respawn()
|
||||
if was_dead and not player_needs.state.is_dead():
|
||||
player_needs_changed.emit(player_needs.state)
|
||||
return true
|
||||
return false
|
||||
player_needs_changed.emit(player_needs.state)
|
||||
return true
|
||||
|
||||
|
||||
func get_player_state() -> PlayerStateRecord:
|
||||
@@ -771,6 +770,21 @@ func player_eat(amount: float) -> float:
|
||||
return eaten
|
||||
|
||||
|
||||
func player_gather(node: ResourceNode) -> float:
|
||||
var gathered := player_needs.gather_node(node)
|
||||
if gathered > 0.0:
|
||||
player_inventory_changed.emit(player_needs.state)
|
||||
return gathered
|
||||
|
||||
|
||||
func player_deposit(resource_id: StringName) -> float:
|
||||
var deposited := player_needs.deposit_resource(resource_id)
|
||||
if deposited > 0.0:
|
||||
village_changed.emit(village)
|
||||
player_inventory_changed.emit(player_needs.state)
|
||||
return deposited
|
||||
|
||||
|
||||
func _consider_animal_care_quests() -> void:
|
||||
var pantry: StorageStateRecord = get_pantry()
|
||||
for animal_state in animal_care.get_all_states():
|
||||
|
||||
@@ -537,6 +537,8 @@ static func _is_valid_supply(
|
||||
var event_type := StringName(event.data["event_type"])
|
||||
var actor_id := int(event.data["actor_id"])
|
||||
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||
if actor_id < 0:
|
||||
return StringName(event.data["source_id"]) == &"player_carry"
|
||||
return (
|
||||
_find_npc(actor_id, npcs) != null
|
||||
and StringName(event.data["source_id"]) == SimulationIds.npc_inventory_id(actor_id)
|
||||
|
||||
@@ -2,6 +2,18 @@ class_name PlayerNeedsSystem
|
||||
extends RefCounted
|
||||
|
||||
var state := PlayerStateRecord.create()
|
||||
var economy: RefCounted
|
||||
var event_recorder: RefCounted
|
||||
var resource_states: Dictionary = {}
|
||||
|
||||
|
||||
func configure(economy_service: RefCounted, recorder: RefCounted) -> void:
|
||||
economy = economy_service
|
||||
event_recorder = recorder
|
||||
|
||||
|
||||
func set_resource_states(states: Dictionary) -> void:
|
||||
resource_states = states
|
||||
|
||||
|
||||
func advance(village: SimVillage) -> bool:
|
||||
@@ -15,8 +27,80 @@ func advance(village: SimVillage) -> bool:
|
||||
|
||||
|
||||
func eat(amount: float) -> float:
|
||||
var eaten := state.eat(amount)
|
||||
return eaten
|
||||
return state.eat(amount)
|
||||
|
||||
|
||||
func gather_node(node: ResourceNode) -> float:
|
||||
if node == null:
|
||||
return 0.0
|
||||
var resource_state := resource_states.get(node.node_id) as ResourceStateRecord
|
||||
if resource_state == null or not resource_state.can_player_use_resource():
|
||||
return 0.0
|
||||
var available := state.get_available_carry()
|
||||
if available <= 0.0:
|
||||
return 0.0
|
||||
var extracted := resource_state.extract(minf(resource_state.get_yield_per_action(), available))
|
||||
if extracted <= 0.0:
|
||||
return 0.0
|
||||
var carried := state.add_inventory(resource_state.get_resource_id(), extracted)
|
||||
if carried <= 0.0:
|
||||
resource_state.data["amount_remaining"] = (
|
||||
resource_state.get_amount_remaining() + extracted
|
||||
)
|
||||
return 0.0
|
||||
var event_position := (
|
||||
node.interaction_point.global_position
|
||||
if node.interaction_point != null
|
||||
else node.global_position
|
||||
)
|
||||
event_recorder.record_economic_at(
|
||||
SimulationIds.EVENT_RESOURCE_EXTRACTED,
|
||||
-1,
|
||||
resource_state.get_node_id(),
|
||||
&"player_carry",
|
||||
resource_state.get_resource_id(),
|
||||
carried,
|
||||
event_position
|
||||
)
|
||||
if resource_state.get_amount_remaining() <= 0.0:
|
||||
event_recorder.record_narrative_at(
|
||||
SimulationIds.EVENT_RESOURCE_DEPLETED,
|
||||
-1,
|
||||
resource_state.get_node_id(),
|
||||
"",
|
||||
event_position
|
||||
)
|
||||
return carried
|
||||
|
||||
|
||||
func deposit_resource(resource_id: StringName) -> float:
|
||||
if resource_id.is_empty():
|
||||
return 0.0
|
||||
var carried := state.get_inventory_amount(resource_id)
|
||||
if carried <= 0.0:
|
||||
return 0.0
|
||||
if economy == null or not economy.has_method("get_storage_for_resource"):
|
||||
return 0.0
|
||||
var storage: StorageStateRecord = economy.get_storage_for_resource(resource_id)
|
||||
if storage == null:
|
||||
return 0.0
|
||||
var deposited: float = economy.deposit_resource(resource_id, carried)
|
||||
if deposited <= 0.0:
|
||||
return 0.0
|
||||
state.remove_inventory(resource_id, deposited)
|
||||
event_recorder.record_economic(
|
||||
SimulationIds.EVENT_STORAGE_DEPOSITED,
|
||||
-1,
|
||||
&"player_carry",
|
||||
storage.get_storage_id(),
|
||||
resource_id,
|
||||
deposited
|
||||
)
|
||||
return deposited
|
||||
|
||||
|
||||
func get_carried_amount(item_id: StringName) -> float:
|
||||
return state.get_inventory_amount(item_id)
|
||||
|
||||
|
||||
func respawn() -> void:
|
||||
|
||||
@@ -7,6 +7,7 @@ const STACK_THRESHOLD := 90.0
|
||||
const DEATH_STANDING_PENALTY := 0.5
|
||||
const RESET_HUNGER := 20.0
|
||||
const RESET_ENERGY := 70.0
|
||||
const DEFAULT_CARRY_CAPACITY := 4.0
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
@@ -28,6 +29,8 @@ static func create() -> PlayerStateRecord:
|
||||
"starvation_death_threshold": 600,
|
||||
"is_dead": false,
|
||||
"deaths": 0,
|
||||
"inventory": {},
|
||||
"carry_capacity": DEFAULT_CARRY_CAPACITY,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -68,6 +71,28 @@ static func from_dictionary(record_data: Dictionary) -> PlayerStateRecord:
|
||||
or deaths < 0
|
||||
):
|
||||
return null
|
||||
var inventory := {}
|
||||
var carry_capacity := DEFAULT_CARRY_CAPACITY
|
||||
if record_data.has("inventory"):
|
||||
if not record_data["inventory"] is Dictionary:
|
||||
return null
|
||||
var normalized := {}
|
||||
for raw_item_id in record_data["inventory"]:
|
||||
var item_id := String(raw_item_id)
|
||||
var amount := float(record_data["inventory"][raw_item_id])
|
||||
if (
|
||||
item_id.is_empty()
|
||||
or normalized.has(item_id)
|
||||
or not is_finite(amount)
|
||||
or amount < 0.0
|
||||
):
|
||||
return null
|
||||
normalized[item_id] = amount
|
||||
inventory = normalized
|
||||
if record_data.has("carry_capacity"):
|
||||
carry_capacity = float(record_data["carry_capacity"])
|
||||
if not is_finite(carry_capacity) or carry_capacity < 0.0:
|
||||
return null
|
||||
return (
|
||||
PlayerStateRecord
|
||||
. new(
|
||||
@@ -80,6 +105,8 @@ static func from_dictionary(record_data: Dictionary) -> PlayerStateRecord:
|
||||
"starvation_death_threshold": death_threshold,
|
||||
"is_dead": bool(record_data["is_dead"]),
|
||||
"deaths": deaths,
|
||||
"inventory": inventory,
|
||||
"carry_capacity": carry_capacity,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -109,6 +136,47 @@ func get_deaths() -> int:
|
||||
return int(data["deaths"])
|
||||
|
||||
|
||||
func get_inventory_amount(item_id: StringName) -> float:
|
||||
return float(data["inventory"].get(String(item_id), 0.0))
|
||||
|
||||
|
||||
func get_carry_capacity() -> float:
|
||||
return float(data["carry_capacity"])
|
||||
|
||||
|
||||
func get_carried_total() -> float:
|
||||
var total := 0.0
|
||||
for amount in data["inventory"].values():
|
||||
total += float(amount)
|
||||
return total
|
||||
|
||||
|
||||
func get_available_carry() -> float:
|
||||
return maxf(get_carry_capacity() - get_carried_total(), 0.0)
|
||||
|
||||
|
||||
func add_inventory(item_id: StringName, amount: float) -> float:
|
||||
if item_id.is_empty() or not is_finite(amount) or amount <= 0.0 or is_dead():
|
||||
return 0.0
|
||||
var accepted := minf(amount, get_available_carry())
|
||||
if accepted <= 0.0:
|
||||
return 0.0
|
||||
data["inventory"][String(item_id)] = get_inventory_amount(item_id) + accepted
|
||||
return accepted
|
||||
|
||||
|
||||
func remove_inventory(item_id: StringName, requested_amount: float) -> float:
|
||||
if item_id.is_empty() or not is_finite(requested_amount) or requested_amount <= 0.0:
|
||||
return 0.0
|
||||
var removed := minf(requested_amount, get_inventory_amount(item_id))
|
||||
if removed <= 0.0:
|
||||
return 0.0
|
||||
data["inventory"][String(item_id)] = get_inventory_amount(item_id) - removed
|
||||
if is_equal_approx(get_inventory_amount(item_id), 0.0):
|
||||
data["inventory"].erase(String(item_id))
|
||||
return removed
|
||||
|
||||
|
||||
func eat(amount: float) -> float:
|
||||
if amount <= 0.0 or is_dead():
|
||||
return 0.0
|
||||
|
||||
@@ -772,6 +772,8 @@ static func _is_valid_supply_resolution(
|
||||
):
|
||||
return false
|
||||
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||
if actor_id < 0:
|
||||
return StringName(event.data["source_id"]) == &"player_carry"
|
||||
return (
|
||||
npc_ids.has(actor_id)
|
||||
and StringName(event.data["source_id"]) == SimulationIds.npc_inventory_id(actor_id)
|
||||
|
||||
Reference in New Issue
Block a user