class_name SimVillage extends RefCounted var food := 20.0 var wood := 10.0 var safety := 50.0 var knowledge := 0.0 var debug_logs := false var food_modifier := 1.0 var wood_modifier := 1.0 var safety_modifier := 1.0 var knowledge_modifier := 1.0 var food_priority := 1.0 var wood_priority := 1.0 var safety_priority := 1.0 var knowledge_priority := 1.0 func update_modifiers() -> void: food_modifier = 1.0 wood_modifier = 1.0 safety_modifier = 1.0 knowledge_modifier = 1.0 if food <= 0: food_modifier = 2.0 if wood <= 0: wood_modifier = 2.0 if _strictly_below(safety, 20.0): safety_modifier = 2.0 if knowledge > 25: knowledge_modifier = 0.75 debug_log( ( "FoodMod=%s SafetyMod=%s KnowledgeMod=%s" % [food_modifier, safety_modifier, knowledge_modifier] ) ) func update_priorities() -> void: food_priority = 1.0 wood_priority = 1.0 safety_priority = 1.0 knowledge_priority = 1.0 if _strictly_below(food, 10.0): food_priority = 3.0 elif _strictly_below(food, 20.0): food_priority = 2.0 if _strictly_below(wood, 5.0): wood_priority = 2.5 elif _strictly_below(wood, 15.0): wood_priority = 1.75 if _strictly_below(safety, 25.0): safety_priority = 3.0 elif _strictly_below(safety, 50.0): safety_priority = 1.5 if _strictly_below(knowledge, 5.0): knowledge_priority = 1.75 elif _strictly_below(knowledge, 15.0): knowledge_priority = 1.25 if debug_logs: print(get_priority_summary()) func apply_metric_delta(metric_id: StringName, amount: float) -> void: match metric_id: &"safety": safety = clamp(safety + amount, 0.0, 100.0) &"knowledge": knowledge += amount safety = clamp(safety, 0.0, 100.0) knowledge = max(knowledge, 0.0) update_modifiers() update_priorities() func apply_npc_task(npc: SimNPC) -> void: if npc.is_dead: return var productivity := 1.0 if npc.is_starving: productivity = 0.35 elif npc.energy < 20.0: productivity = 0.25 elif npc.energy < 40.0: productivity = 0.5 match npc.current_task: SimulationIds.ACTION_PATROL: safety += 1.0 * productivity SimulationIds.ACTION_STUDY: knowledge += 1.0 * productivity SimulationIds.ACTION_REST: if npc.is_starving: npc.energy = min(npc.energy + 2.0, 100.0) elif npc.hunger > 75.0: npc.energy = min(npc.energy + 12.0, 100.0) else: npc.energy = min(npc.energy + 35.0, 100.0) SimulationIds.ACTION_WANDER: safety -= 0.2 food = max(food, 0.0) wood = max(wood, 0.0) safety = clamp(safety, 0.0, 100.0) knowledge = max(knowledge, 0.0) update_modifiers() update_priorities() func get_summary() -> String: return ( "Village | food: %s | wood: %s | safety: %s | knowledge: %s | priorities F/W/S/K: %s/%s/%s/%s" % [ round(food), round(wood), round(safety), round(knowledge), food_priority, wood_priority, safety_priority, knowledge_priority ] ) func get_priority_summary() -> String: return ( "Priorities | food: %s | wood: %s | safety: %s | knowledge: %s" % [food_priority, wood_priority, safety_priority, knowledge_priority] ) func debug_log(message: String) -> void: if debug_logs: print("[Village] ", message) static func _strictly_below(value: float, threshold: float) -> bool: # JSON round trips can normalize a value a few ULPs below a threshold to the # exact decimal. Treat that boundary identically before and after saving. return value < threshold and not is_equal_approx(value, threshold)