Update export presets, project version, and enhance civilization simulation rules
- Updated export path and version code in export_presets.cfg - Changed project version in project.godot to 0.7.0 - Expanded simulation rules in simulation_rules.md to clarify resource management - Improved testing framework in tests/README.md to include new civilization budget checks - Added new tests for batch boundaries and civilization state transitions - Enhanced civilization_state.gd with shared budget and transition logic - Refactored owniverse.gd to enforce capacity during life batch initialization
This commit is contained in:
@@ -34,3 +34,110 @@ static func detach_space_civilizations(target: Dictionary, rules: Dictionary) ->
|
||||
if is_space_civilization(id, rules):
|
||||
target.amount[id] = target.amount.get(id, 0.0) + held[id]
|
||||
held[id] = 0.0
|
||||
|
||||
# A shared budget per stellar family, including stars bound into structures.
|
||||
# Reservations affect capacity, not the physical stock or its production.
|
||||
static func stellar_budget(world, target: Dictionary, rules: Dictionary) -> Dictionary:
|
||||
var physical: Dictionary = world._physical_amounts(target)
|
||||
var capacity: Dictionary = {}
|
||||
for id in physical:
|
||||
var entity = world.entities[id]
|
||||
for home in ["DwStar", "GiStar", "WD", "NS", "MGT", "BH"]:
|
||||
if entity.event_group == home or entity.count_as == home or id == home:
|
||||
capacity[home] = capacity.get(home, 0.0) + physical[id]
|
||||
capacity["SMBH"] = maxf(0.0, target.amount.get("SMBH", 0.0))
|
||||
for held: Dictionary in target.binds.values():
|
||||
capacity["SMBH"] += maxf(0.0, held.get("SMBH", 0.0))
|
||||
var populations: Dictionary = {}
|
||||
var requested: Dictionary = {}
|
||||
for id in rules:
|
||||
if not is_space_civilization(id, rules):
|
||||
continue
|
||||
populations[id] = physical.get(id, 0.0)
|
||||
var home: String = rules[id].home
|
||||
requested[home] = requested.get(home, 0.0) + populations[id] * maxf(1.0, rules[id].stars)
|
||||
var free: Dictionary = {}
|
||||
for home in capacity:
|
||||
free[home] = maxf(0.0, capacity[home] - requested.get(home, 0.0))
|
||||
return {"capacity": capacity, "requested": requested, "free": free, "populations": populations}
|
||||
|
||||
# Account for the source's existing reservation when changing stages. A failed
|
||||
# transition retains its population at the source, rather than losing objects.
|
||||
static func admit_transition(budget: Dictionary, rules: Dictionary, source: String,
|
||||
destination: String, requested: float) -> float:
|
||||
if source == destination or not is_space_civilization(destination, rules):
|
||||
return requested
|
||||
var home: String = rules[destination].home
|
||||
var cost: float = maxf(1.0, rules[destination].stars)
|
||||
var old_home: String = ""
|
||||
var old_cost: float = 0.0
|
||||
if is_space_civilization(source, rules):
|
||||
old_home = rules[source].home
|
||||
old_cost = maxf(1.0, rules[source].stars)
|
||||
# Existing overcapacity is handled by the separate population policy.
|
||||
if budget.requested.get(home, 0.0) > budget.capacity.get(home, 0.0):
|
||||
return 0.0
|
||||
var net_cost: float = cost - old_cost if old_home == home else cost
|
||||
var admitted: float = floorf(maxf(0.0, requested))
|
||||
if net_cost > 0.0:
|
||||
admitted = minf(admitted, floorf(budget.free.get(home, 0.0) / net_cost))
|
||||
if old_home != "":
|
||||
budget.requested[old_home] = maxf(0.0, budget.requested.get(old_home, 0.0) - admitted * old_cost)
|
||||
budget.free[old_home] = maxf(0.0, budget.capacity.get(old_home, 0.0) - budget.requested[old_home])
|
||||
budget.requested[home] = budget.requested.get(home, 0.0) + admitted * cost
|
||||
budget.free[home] = maxf(0.0, budget.capacity.get(home, 0.0) - budget.requested[home])
|
||||
return admitted
|
||||
|
||||
# Resolve shortages from the most advanced stages downwards. This gives existing
|
||||
# advanced populations priority and lets a demotion cascade in the same cycle.
|
||||
static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year: float) -> void:
|
||||
detach_space_civilizations(target, rules)
|
||||
reconcile(world, target, rules)
|
||||
var budget: Dictionary = stellar_budget(world, target, rules)
|
||||
var remaining: Dictionary = budget.capacity.duplicate()
|
||||
var stages: Array = []
|
||||
for id in rules:
|
||||
if is_space_civilization(id, rules):
|
||||
stages.append(id)
|
||||
stages.sort()
|
||||
for index in range(stages.size() - 1, -1, -1):
|
||||
var id: String = stages[index]
|
||||
var home: String = rules[id].home
|
||||
var cost: float = maxf(1.0, rules[id].stars)
|
||||
var count: float = target.amount.get(id, 0.0)
|
||||
var supported: float = minf(count, floorf(remaining.get(home, 0.0) / cost))
|
||||
remaining[home] = maxf(0.0, remaining.get(home, 0.0) - supported * cost)
|
||||
var excess: float = count - supported
|
||||
if excess <= 0.0:
|
||||
continue
|
||||
var previous: String = stages[index - 1] if index > 0 else "C1.9"
|
||||
world._scale_batches(target, id, supported / count)
|
||||
target.amount[id] = supported
|
||||
world._credit(target, previous, excess)
|
||||
world._append_batch(target, previous, excess, year, year)
|
||||
var removed: Dictionary = world._allocate_whole(target.ethics[id], excess)
|
||||
if not target.ethics.has(previous):
|
||||
target.ethics[previous] = {}
|
||||
for ethic in ETHICS:
|
||||
target.ethics[id][ethic] -= removed[ethic]
|
||||
target.ethics[previous][ethic] = target.ethics[previous].get(ethic, 0.0) + removed[ethic]
|
||||
if previous == "C1.9":
|
||||
var stars: Dictionary = {}
|
||||
var physical: Dictionary = world._physical_amounts(target)
|
||||
for star in physical:
|
||||
if world.entities[star].event_group == "DwStar" and physical[star] > 0.0:
|
||||
stars[star] = physical[star]
|
||||
var weights: Dictionary = world._normalize_distribution(stars)
|
||||
# Worlds are aggregates: a star may host several inhabited planets.
|
||||
var assigned: float = 0.0
|
||||
var owners: Array = weights.keys()
|
||||
owners.sort()
|
||||
for i in owners.size():
|
||||
var owner: String = owners[i]
|
||||
var amount: float = floorf(excess * weights[owner]) if i + 1 < owners.size() else excess - assigned
|
||||
assigned += amount
|
||||
if not target.binds.has(owner):
|
||||
target.binds[owner] = {}
|
||||
target.binds[owner][previous] = target.binds[owner].get(previous, 0.0) + amount
|
||||
target.amount[previous] -= amount
|
||||
reconcile(world, target, rules)
|
||||
|
||||
Reference in New Issue
Block a user