feat: add physical animal care delivery

This commit is contained in:
Rijad Zuzo
2026-07-31 00:19:41 +02:00
parent 7d410d8193
commit ead75fca68
25 changed files with 1469 additions and 191 deletions
+107 -31
View File
@@ -119,6 +119,61 @@ func can_complete_feed(animal_id: StringName, agent_id: int) -> bool:
)
func requires_feed_pickup(npc: SimNPC) -> bool:
var definition := SimulationDefinitions.get_action(SimulationIds.ACTION_FEED_ANIMAL)
return (
npc != null
and definition != null
and definition.has_completion_cost()
and (
npc.get_inventory_amount(definition.completion_cost_resource_id)
< definition.completion_cost_amount
)
)
func requires_working_feed_recovery(npc: SimNPC) -> bool:
return (
npc != null
and npc.current_task == SimulationIds.ACTION_FEED_ANIMAL
and npc.task_state == SimNPC.TASK_STATE_WORKING
and requires_feed_pickup(npc)
and can_complete_feed(npc.target_id, npc.id)
)
func collect_feed_supply(npc: SimNPC) -> bool:
var definition := SimulationDefinitions.get_action(SimulationIds.ACTION_FEED_ANIMAL)
if (
npc == null
or npc.current_task != SimulationIds.ACTION_FEED_ANIMAL
or npc.target_id.is_empty()
or not can_complete_feed(npc.target_id, npc.id)
or definition == null
or not definition.has_completion_cost()
or economy == null
):
return false
var resource_id := definition.completion_cost_resource_id
var required_amount := definition.completion_cost_amount
var carried_amount := npc.get_inventory_amount(resource_id)
var pickup_amount := maxf(required_amount - carried_amount, 0.0)
if pickup_amount <= 0.0:
return true
var pantry: StorageStateRecord = economy.get_storage_for_resource(resource_id)
var available := pantry.get_amount(resource_id) if pantry != null else 0.0
if pantry == null or available < pickup_amount:
_emit_feed_blocked(
npc.id, pantry.get_storage_id() if pantry != null else &"", definition, available
)
return false
var withdrawn: float = economy.withdraw_exact_to_inventory(npc, resource_id, pickup_amount)
if not is_equal_approx(withdrawn, pickup_amount):
_emit_feed_blocked(npc.id, pantry.get_storage_id(), definition, available)
return false
return true
func has_available_feed_target(npc_id: int) -> bool:
if active_world_adapter == null or not active_world_adapter.has_method("get_animal_candidates"):
return false
@@ -158,11 +213,13 @@ func find_node_for_player(from_position: Vector3, max_distance: float) -> Animal
func feed(animal_id: StringName, actor_id: int, npcs: Array[SimNPC], current_tick: int) -> bool:
var animal_state := get_state(animal_id)
if animal_state == null or not animal_state.needs_feed():
if animal_state == null or not animal_state.needs_feed() or current_tick < 0:
return false
var actor_npc: SimNPC
if actor_id >= 0:
actor_npc = _find_npc(actor_id, npcs)
if (
_find_npc(actor_id, npcs) == null
actor_npc == null
or not animal_state.can_npc_feed()
or animal_state.get_reserved_by() != actor_id
):
@@ -176,37 +233,29 @@ func feed(animal_id: StringName, actor_id: int, npcs: Array[SimNPC], current_tic
var resource_id := definition.completion_cost_resource_id
var required_amount := definition.completion_cost_amount
var pantry: StorageStateRecord = economy.get_storage_for_resource(resource_id)
var available := pantry.get_amount(resource_id) if pantry != null else 0.0
if pantry == null or available < required_amount:
if actor_id >= 0:
(
narrative_event_requested
. emit(
SimulationIds.EVENT_TASK_BLOCKED,
actor_id,
pantry.get_storage_id() if pantry != null else &"",
(
"%s: needs %.0f %s (%.1f available)"
% [
definition.display_name,
required_amount,
String(resource_id).capitalize(),
available,
]
),
definition.action_id,
resource_id,
required_amount
)
var source_id := SimulationIds.STORAGE_VILLAGE_PANTRY
var consumed := 0.0
if actor_npc != null:
var carried_amount := actor_npc.get_inventory_amount(resource_id)
if carried_amount < required_amount:
_emit_feed_blocked(
actor_id, SimulationIds.npc_inventory_id(actor_id), definition, carried_amount
)
return false
var consumed: float = economy.withdraw_resource(resource_id, required_amount)
if consumed < required_amount:
economy.deposit_resource(resource_id, consumed)
return false
consumed = economy.remove_exact_from_inventory(actor_npc, resource_id, required_amount)
source_id = SimulationIds.npc_inventory_id(actor_id)
else:
var available := pantry.get_amount(resource_id) if pantry != null else 0.0
if pantry == null or available < required_amount:
return false
consumed = economy.withdraw_resource(resource_id, required_amount)
if not is_equal_approx(consumed, required_amount):
return false
if not animal_state.feed(actor_id, current_tick):
economy.deposit_resource(resource_id, consumed)
if actor_npc != null:
economy.restore_to_inventory(actor_npc, resource_id, consumed)
else:
economy.deposit_resource(resource_id, consumed)
return false
if animal_state.has_active_travel_target():
animal_state.cancel_travel(current_tick + ROUTINE_STAY_TICKS)
@@ -216,7 +265,7 @@ func feed(animal_id: StringName, actor_id: int, npcs: Array[SimNPC], current_tic
economic_event_requested.emit(
SimulationIds.EVENT_ANIMAL_FED,
actor_id,
pantry.get_storage_id(),
source_id,
animal_state.get_animal_id(),
resource_id,
consumed,
@@ -225,6 +274,33 @@ func feed(animal_id: StringName, actor_id: int, npcs: Array[SimNPC], current_tic
return true
func _emit_feed_blocked(
actor_id: int, source_id: StringName, definition: ActionDefinition, available_amount: float
) -> void:
if actor_id < 0:
return
(
narrative_event_requested
. emit(
SimulationIds.EVENT_TASK_BLOCKED,
actor_id,
source_id,
(
"%s: needs %.0f %s (%.1f available)"
% [
definition.display_name,
definition.completion_cost_amount,
String(definition.completion_cost_resource_id).capitalize(),
available_amount,
]
),
definition.action_id,
definition.completion_cost_resource_id,
definition.completion_cost_amount
)
)
func append_state_records(record: SimulationStateRecord) -> void:
var animal_ids: Array = states.keys()
animal_ids.sort()