feat: add weak-villager and damaged-roof village needs

This commit is contained in:
Rijad Zuzo
2026-08-11 00:32:22 +02:00
parent 37f262fb84
commit 82e44a9f5d
10 changed files with 547 additions and 45 deletions
@@ -2,6 +2,7 @@ class_name VillageOpportunitySystem
extends RefCounted
const CARE_HUNGER_THRESHOLD := 80.0
const WEAK_ENERGY_THRESHOLD := 25.0
var opportunities: Array[OpportunityStateRecord] = []
var next_opportunity_id := 0
@@ -29,7 +30,13 @@ func consider_event(
return opened
if woodpile == null:
return null
return _try_open_wood(event, current_tick, woodpile, npcs, knowledge_system)
opened = _try_open_wood(event, current_tick, woodpile, npcs, knowledge_system)
if opened != null:
return opened
opened = _try_open_weak(event, current_tick, pantry, npcs, knowledge_system)
if opened != null:
return opened
return _try_open_roof(event, current_tick, woodpile, npcs, knowledge_system)
func maintain_open_opportunity(
@@ -42,7 +49,14 @@ func maintain_open_opportunity(
var opportunity := get_open_opportunity()
if opportunity == null or current_tick < opportunity.get_created_tick():
return null
if opportunity.get_opportunity_type() != SimulationIds.OPPORTUNITY_SUPPLY_MISSING_WOOD:
if (
opportunity.get_opportunity_type()
not in [
SimulationIds.OPPORTUNITY_SUPPLY_MISSING_WOOD,
SimulationIds.OPPORTUNITY_FEED_WEAK_VILLAGER,
SimulationIds.OPPORTUNITY_REPAIR_HOME_ROOF,
]
):
return null
var interested := _find_npc(opportunity.get_interested_npc_id(), npcs)
if interested == null or interested.is_dead:
@@ -53,15 +67,17 @@ func maintain_open_opportunity(
)
else null
)
var target_storage := _get_target_storage(opportunity, pantry, woodpile)
var condition_is_stale := (
target_storage == null
or (
target_storage.get_amount(opportunity.get_resource_id())
>= opportunity.get_target_amount()
var condition_is_stale := current_tick - opportunity.get_created_tick() >= maxi(max_age, 1)
if opportunity.get_opportunity_type() != SimulationIds.OPPORTUNITY_FEED_WEAK_VILLAGER:
var target_storage := _get_target_storage(opportunity, pantry, woodpile)
condition_is_stale = (
condition_is_stale
or target_storage == null
or (
target_storage.get_amount(opportunity.get_resource_id())
>= opportunity.get_target_amount()
)
)
or current_tick - opportunity.get_created_tick() >= maxi(max_age, 1)
)
if not condition_is_stale:
return null
return (
@@ -452,6 +468,95 @@ func _try_open_wood(
return opportunity
func _try_open_weak(
event: EconomicEventRecord,
current_tick: int,
pantry: StorageStateRecord,
npcs: Array[SimNPC],
knowledge_system: EventKnowledgeSystem
) -> OpportunityStateRecord:
if knowledge_system == null or get_open_opportunity() != null:
return null
if not _is_weak_trigger(event):
return null
if pantry.get_amount(SimulationIds.RESOURCE_FOOD) > 0.0:
return null
var actor := _find_npc(int(event.data["actor_id"]), npcs)
if (
actor == null
or actor.is_dead
or actor.hunger < CARE_HUNGER_THRESHOLD
or actor.energy >= WEAK_ENERGY_THRESHOLD
or not knowledge_system.knows_event(actor.id, int(event.data["event_id"]))
):
return null
var opportunity := OpportunityStateRecord.create(
next_opportunity_id,
current_tick,
int(event.data["event_id"]),
actor.id,
OpportunityStateRecord.DEFAULT_TARGET_AMOUNT,
SimulationIds.OPPORTUNITY_FEED_WEAK_VILLAGER,
SimulationIds.STORAGE_VILLAGE_PANTRY,
SimulationIds.RESOURCE_FOOD
)
next_opportunity_id += 1
opportunities.append(opportunity)
return opportunity
func _try_open_roof(
event: EconomicEventRecord,
current_tick: int,
woodpile: StorageStateRecord,
npcs: Array[SimNPC],
knowledge_system: EventKnowledgeSystem
) -> OpportunityStateRecord:
if knowledge_system == null or get_open_opportunity() != null:
return null
if not _is_home_damaged_trigger(event):
return null
if (
woodpile.get_amount(SimulationIds.RESOURCE_WOOD)
>= OpportunityStateRecord.DEFAULT_TARGET_AMOUNT
):
return null
var actor := _find_npc(int(event.data["actor_id"]), npcs)
if (
actor == null
or actor.is_dead
or not knowledge_system.knows_event(actor.id, int(event.data["event_id"]))
):
return null
var opportunity := OpportunityStateRecord.create(
next_opportunity_id,
current_tick,
int(event.data["event_id"]),
actor.id,
OpportunityStateRecord.DEFAULT_TARGET_AMOUNT,
SimulationIds.OPPORTUNITY_REPAIR_HOME_ROOF,
SimulationIds.STORAGE_VILLAGE_WOODPILE,
SimulationIds.RESOURCE_WOOD
)
next_opportunity_id += 1
opportunities.append(opportunity)
return opportunity
static func _is_weak_trigger(event: EconomicEventRecord) -> bool:
return (
StringName(event.data["event_type"]) == SimulationIds.EVENT_VILLAGER_WEAK
and int(event.data["actor_id"]) >= 0
)
static func _is_home_damaged_trigger(event: EconomicEventRecord) -> bool:
return (
StringName(event.data["event_type"]) == SimulationIds.EVENT_HOME_DAMAGED
and int(event.data["actor_id"]) >= 0
)
func _try_resolve(
event: EconomicEventRecord,
pantry: StorageStateRecord,
@@ -462,6 +567,17 @@ func _try_resolve(
var opportunity := get_open_opportunity()
if opportunity == null:
return null
if (
opportunity.get_opportunity_type() == SimulationIds.OPPORTUNITY_FEED_WEAK_VILLAGER
and _is_valid_supply(event, opportunity, npcs, resource_states)
):
if int(event.data["tick"]) < opportunity.get_created_tick():
return null
if int(event.data["event_id"]) <= opportunity.get_trigger_event_id():
return null
if not opportunity.resolve(int(event.data["event_id"]), int(event.data["tick"])):
return null
return opportunity
var target_storage := _get_target_storage(opportunity, pantry, woodpile)
if (
target_storage == null
@@ -528,6 +644,13 @@ static func _is_valid_supply(
npcs: Array[SimNPC],
resource_states: Dictionary
) -> bool:
if opportunity.get_opportunity_type() == SimulationIds.OPPORTUNITY_FEED_WEAK_VILLAGER:
return (
StringName(event.data["event_type"]) == SimulationIds.EVENT_ITEM_CONSUMED
and int(event.data["actor_id"]) == opportunity.get_interested_npc_id()
and StringName(event.data["item_id"]) == SimulationIds.RESOURCE_FOOD
and float(event.data["amount"]) > 0.0
)
if (
StringName(event.data["item_id"]) != opportunity.get_resource_id()
or StringName(event.data["destination_id"]) != opportunity.get_target_id()