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:
+2
-2
@@ -11,7 +11,7 @@ custom_features=""
|
|||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter=""
|
include_filter=""
|
||||||
exclude_filter=""
|
exclude_filter=""
|
||||||
export_path="../../../Documents/Owad/Owniverse.I/Builds/owniverse.0.6.29-145.console.exe"
|
export_path="../../../Documents/Owad/Owniverse.I/Builds/owniverse.0.7.0-146.console.exe"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
patch_delta_encoding=false
|
patch_delta_encoding=false
|
||||||
patch_delta_compression_level_zstd=19
|
patch_delta_compression_level_zstd=19
|
||||||
@@ -112,7 +112,7 @@ architectures/armeabi-v7a=false
|
|||||||
architectures/arm64-v8a=true
|
architectures/arm64-v8a=true
|
||||||
architectures/x86=false
|
architectures/x86=false
|
||||||
architectures/x86_64=false
|
architectures/x86_64=false
|
||||||
version/code=145
|
version/code=146
|
||||||
version/name=""
|
version/name=""
|
||||||
package/unique_name="com.KIDGameProduction.Owniverse"
|
package/unique_name="com.KIDGameProduction.Owniverse"
|
||||||
package/name="Owniverse"
|
package/name="Owniverse"
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ general/updates_per_second=10
|
|||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="Owniverse"
|
config/name="Owniverse"
|
||||||
config/version="0.6.29"
|
config/version="0.7.0"
|
||||||
run/main_scene="uid://bv384dpmvjv8o"
|
run/main_scene="uid://bv384dpmvjv8o"
|
||||||
config/features=PackedStringArray("4.7", "Mobile")
|
config/features=PackedStringArray("4.7", "Mobile")
|
||||||
boot_splash/show_image=false
|
boot_splash/show_image=false
|
||||||
|
|||||||
+7
-1
@@ -94,4 +94,10 @@ HP деградирует в RP. У эонов возможен откат к п
|
|||||||
|
|
||||||
Цивилизации с непустым `home` (C2.0–C4.4) освобождаются от прежних привязок без изменения общего количества. Общий механизм переходов больше не переносит планетную привязку на такого преемника. Назначение структур и обратное размещение при деградации ещё не подключены.
|
Цивилизации с непустым `home` (C2.0–C4.4) освобождаются от прежних привязок без изменения общего количества. Общий механизм переходов больше не переносит планетную привязку на такого преемника. Назначение структур и обратное размещение при деградации ещё не подключены.
|
||||||
|
|
||||||
Открытое правило среды: Unity читает `Stars`, но не использует эту норму в ограничении количества цивилизаций. Требуется выбор между фактическим поведением Unity и количественным ограничением доступными звёздами.
|
Правило среды согласовано: число космических цивилизаций ограничено звёздным ресурсом. Для стадии `i` расход ёмкости — `population[i] * max(1, Stars[i])`. Каждое семейство `Home` имеет общий бюджет для всех использующих его стадий; звёзды внутри структур также учитываются, но повторно не суммируются. SMBH учитывается по суммарной массе в эквиваленте звёзд, согласно решению пользователя.
|
||||||
|
|
||||||
|
Переход переиспользует резерв исходной стадии. При нехватке ёмкости неподдержанные переходы остаются на исходной стадии. Лимит применяется к общему механизму выходов партий и ручному созданию; это само по себе не включает пока отсутствующий расчёт прогресса C0.2+.
|
||||||
|
|
||||||
|
При загрузке и после эволюционных поколений избыток деградирует последовательно до поддерживаемой стадии. Приоритет получают существующие более развитые цивилизации; остаток бюджета достаётся младшим. Деградация переносит количество и этики, сокращает старые партии и регистрирует новые, не уничтожая цивилизации. C2.0 при избытке становится C1.9 и распределяется по имеющимся звёздам-карликам. Если карликов нет, C1.9 остаётся свободной — отсутствующая звезда не создаётся. Планетные стадии до C2.0 не расходуют отдельную звёздную ёмкость: у одной звезды могут быть несколько планет.
|
||||||
|
|
||||||
|
Резерв ёмкости логический: звёзды не расходуются и продолжают производить ресурсы. Общий бюджет пересчитывается из физических количеств, отдельная копия бронирований в сохранении не хранится.
|
||||||
|
|||||||
@@ -60,3 +60,5 @@ godot --headless --path Owniverse.GI --script res://tests/test_life.gd
|
|||||||
```sh
|
```sh
|
||||||
godot --headless --path Owniverse.GI --script res://tests/test_civilization_state.gd
|
godot --headless --path Owniverse.GI --script res://tests/test_civilization_state.gd
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Тест цивилизаций дополнительно проверяет общий звёздный бюджет, повторное использование резерва при переходе, звёзды в структурах, массовый эквивалент SMBH, блокировку неподдержанного прогресса, каскад деградации и повторное применение ограничений без потерь.
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
extends SceneTree
|
||||||
|
const Clock = preload("res://world/owniverse/simulation_time.gd")
|
||||||
|
var checks: int = 0
|
||||||
|
var failures: int = 0
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
call_deferred("run")
|
||||||
|
|
||||||
|
func check(condition: bool, message: String) -> void:
|
||||||
|
checks += 1
|
||||||
|
if not condition:
|
||||||
|
failures += 1
|
||||||
|
push_error(message)
|
||||||
|
|
||||||
|
func fixture():
|
||||||
|
var world = load("res://world/owniverse/owniverse.gd").new()
|
||||||
|
var global: Node = root.get_node("Global")
|
||||||
|
for id in global.OwniverseEntities:
|
||||||
|
var entity = load("res://world/owniverse/owniverse_entity.gd").new(global.OwniverseEntities[id])
|
||||||
|
world.entities[id] = entity
|
||||||
|
world.state.amount[id] = 0.0
|
||||||
|
world.state.stats.total_amount[id] = 0.0
|
||||||
|
if entity.group_as != "":
|
||||||
|
world.state.amount[entity.group_as] = 0.0
|
||||||
|
world.state.stats.total_amount[entity.group_as] = 0.0
|
||||||
|
world.entities["N"].life_product = {}
|
||||||
|
world.entities["N"].product_per_year_type = "H"
|
||||||
|
world.entities["N"].product_per_year_amount = 10.0
|
||||||
|
world.entities["N"].auto = 0.0
|
||||||
|
world.state.amount.N = 1.0
|
||||||
|
return world
|
||||||
|
|
||||||
|
func dispose(world) -> void:
|
||||||
|
for entity in world.entities.values():
|
||||||
|
entity.free()
|
||||||
|
world.free()
|
||||||
|
|
||||||
|
func run() -> void:
|
||||||
|
var world = fixture()
|
||||||
|
world.state.batch_origins.LGRB = 10357034.80000000074505806
|
||||||
|
var boundary: float = 41215289.79999999701976776
|
||||||
|
for interval in [[boundary - 2.5, boundary + 2.25], [boundary, boundary + 2.0]]:
|
||||||
|
world.state.batches.LGRB = []
|
||||||
|
world._append_batch(world.state, "LGRB", 100.0, interval[0], interval[1])
|
||||||
|
var total: float = 0.0
|
||||||
|
var last: float = -INF
|
||||||
|
for batch: Dictionary in world.state.batches.LGRB:
|
||||||
|
total += batch.amount
|
||||||
|
check(batch.created_from > last, "Batch windows advance monotonically")
|
||||||
|
check(batch.created_to > batch.created_from, "Positive window duration")
|
||||||
|
check(batch.amount >= 0.0, "No negative batch amounts")
|
||||||
|
last = batch.created_from
|
||||||
|
check(total == 100.0, "Boundary correction preserves all objects")
|
||||||
|
check(world.state.batches.LGRB.size() <= 7, "Finite number of windows")
|
||||||
|
dispose(world)
|
||||||
|
print("Batch boundaries: %d checks, %d failures" % [checks, failures])
|
||||||
|
quit(0 if failures == 0 else 1)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dq86k8bto42xl
|
||||||
@@ -41,6 +41,7 @@ func run() -> void:
|
|||||||
check(global.CivilizationRules.size() == 43, "Load all ethical civilization stages")
|
check(global.CivilizationRules.size() == 43, "Load all ethical civilization stages")
|
||||||
for row: Dictionary in global.CivilizationRules.values():
|
for row: Dictionary in global.CivilizationRules.values():
|
||||||
check(row.ethics.size() == 6, "Every ethical stage has six rules")
|
check(row.ethics.size() == 6, "Every ethical stage has six rules")
|
||||||
|
world.state.amount.RD = 12.0
|
||||||
world.state.amount["C0.2"] = 8.0
|
world.state.amount["C0.2"] = 8.0
|
||||||
world.state.binds.RD = {"C2.0": 12.0, "C1.9": 3.0}
|
world.state.binds.RD = {"C2.0": 12.0, "C1.9": 3.0}
|
||||||
world.initialize_life_batches()
|
world.initialize_life_batches()
|
||||||
@@ -65,11 +66,62 @@ func run() -> void:
|
|||||||
dispose(restored)
|
dispose(restored)
|
||||||
dispose(world)
|
dispose(world)
|
||||||
world = fixture()
|
world = fixture()
|
||||||
|
world.state.amount.RD = 10.0
|
||||||
world.entities["C1.9"].output_and = {"C2.0": 1.0}
|
world.entities["C1.9"].output_and = {"C2.0": 1.0}
|
||||||
world.state.binds.RD = {"C1.9": 10.0}
|
world.state.binds.RD = {"C1.9": 10.0}
|
||||||
world.state.batches["C1.9"] = [{"amount": 10.0, "start_year": 1.0, "end_year": 1.0}]
|
world.state.batches["C1.9"] = [{"amount": 10.0, "start_year": 1.0, "end_year": 1.0}]
|
||||||
world._evolve_batches(world.state, 1.0)
|
world._evolve_batches(world.state, 1.0)
|
||||||
check(world.state.amount["C2.0"] == 10.0 and world.state.binds.RD.get("C2.0", 0.0) == 0.0, "Space successor does not inherit planetary binding")
|
check(world.state.amount["C2.0"] == 10.0 and world.state.binds.RD.get("C2.0", 0.0) == 0.0, "Space successor does not inherit planetary binding")
|
||||||
dispose(world)
|
dispose(world)
|
||||||
|
var capacity = load("res://world/owniverse/civilization_state.gd")
|
||||||
|
world = fixture()
|
||||||
|
world.state.amount.RD = 10.0
|
||||||
|
world.state.amount["C2.0"] = 8.0
|
||||||
|
var budget: Dictionary = capacity.stellar_budget(world, world.state, global.CivilizationRules)
|
||||||
|
check(capacity.admit_transition(budget, global.CivilizationRules, "C1.9", "C2.0", 5.0) == 2.0, "Growth consumes only remaining stellar capacity")
|
||||||
|
check(capacity.admit_transition(budget, global.CivilizationRules, "C1.9", "C2.0", 5.0) == 0.0, "A reservation cannot be spent twice")
|
||||||
|
world.state.amount["C2.0"] = 1.0
|
||||||
|
budget = capacity.stellar_budget(world, world.state, global.CivilizationRules)
|
||||||
|
check(capacity.admit_transition(budget, global.CivilizationRules, "C2.0", "C2.1", 1.0) == 1.0, "A transition reuses the source reservation")
|
||||||
|
check(budget.free.DwStar == 0.0, "C2.1 reserves ten dwarf stars")
|
||||||
|
world.state.amount["C2.0"] = 0.0
|
||||||
|
world.state.binds.SC = {"RD": 10.0}
|
||||||
|
budget = capacity.stellar_budget(world, world.state, global.CivilizationRules)
|
||||||
|
check(budget.capacity.DwStar == 20.0, "Stars inside structures are counted once")
|
||||||
|
dispose(world)
|
||||||
|
|
||||||
|
world = fixture()
|
||||||
|
world.state.amount.RD = 12.0
|
||||||
|
world.state.amount["C2.1"] = 2.0
|
||||||
|
world.state.amount["C2.0"] = 5.0
|
||||||
|
world.initialize_life_batches()
|
||||||
|
check(world.state.amount["C2.1"] == 1.0 and world.state.amount["C2.0"] == 2.0, "Shared budget supports advanced civilizations first")
|
||||||
|
check(world.state.binds.RD["C1.9"] == 4.0, "Excess cascades to supported planetary stage")
|
||||||
|
var snapshot: Dictionary = world.state.duplicate(true)
|
||||||
|
world.initialize_life_batches()
|
||||||
|
check(world.state == snapshot, "Capacity enforcement is idempotent")
|
||||||
|
world.state.amount.RD = 0.0
|
||||||
|
world.initialize_life_batches()
|
||||||
|
check(world.state.amount["C2.1"] == 0.0 and world.state.amount["C2.0"] == 0.0, "Vanished stars force all unsupported space civilizations down")
|
||||||
|
check(world._physical_amounts(world.state)["C1.9"] == 7.0, "Forced degradation conserves civilizations")
|
||||||
|
dispose(world)
|
||||||
|
|
||||||
|
world = fixture()
|
||||||
|
world.state.amount.SMBH = 2.0e22
|
||||||
|
world.state.amount["C4.2"] = 3.0
|
||||||
|
world.initialize_life_batches()
|
||||||
|
check(world.state.amount["C4.2"] == 2.0, "SMBH capacity uses stellar mass equivalent")
|
||||||
|
check(world._physical_amounts(world.state)["C1.9"] == 1.0, "Unsupported civilization degrades through missing habitat families")
|
||||||
|
check(root.get_node("SaveManager").validate_data({"state": world.state}) == "", "Capacity-limited state validates")
|
||||||
|
dispose(world)
|
||||||
|
|
||||||
|
world = fixture()
|
||||||
|
world.state.amount.RD = 2.0
|
||||||
|
world.entities["C1.9"].output_and = {"C2.0": 1.0}
|
||||||
|
world.state.binds.RD = {"C1.9": 5.0}
|
||||||
|
world.state.batches["C1.9"] = [{"amount": 5.0, "start_year": 1.0, "end_year": 1.0}]
|
||||||
|
world._evolve_batches(world.state, 1.0)
|
||||||
|
check(world.state.amount["C2.0"] == 2.0 and world.state.binds.RD["C1.9"] == 3.0, "Unfunded transition retains the original planetary civilization")
|
||||||
|
dispose(world)
|
||||||
print("Civilization state: %d checks, %d failures" % [checks, failures])
|
print("Civilization state: %d checks, %d failures" % [checks, failures])
|
||||||
quit(0 if failures == 0 else 1)
|
quit(0 if failures == 0 else 1)
|
||||||
|
|||||||
@@ -34,3 +34,110 @@ static func detach_space_civilizations(target: Dictionary, rules: Dictionary) ->
|
|||||||
if is_space_civilization(id, rules):
|
if is_space_civilization(id, rules):
|
||||||
target.amount[id] = target.amount.get(id, 0.0) + held[id]
|
target.amount[id] = target.amount.get(id, 0.0) + held[id]
|
||||||
held[id] = 0.0
|
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)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func migrate_time_precision() -> void:
|
|||||||
# Old saves have counts but no life ages. Start only missing cohorts at load time.
|
# Old saves have counts but no life ages. Start only missing cohorts at load time.
|
||||||
func initialize_life_batches() -> void:
|
func initialize_life_batches() -> void:
|
||||||
CivilizationState.detach_space_civilizations(state, Global.CivilizationRules)
|
CivilizationState.detach_space_civilizations(state, Global.CivilizationRules)
|
||||||
CivilizationState.reconcile(self, state, Global.CivilizationRules)
|
CivilizationState.enforce_capacity(self, state, Global.CivilizationRules, state.time.datetime)
|
||||||
if state.get("life_version", 0) >= 2:
|
if state.get("life_version", 0) >= 2:
|
||||||
return
|
return
|
||||||
var physical: Dictionary = _physical_amounts(state)
|
var physical: Dictionary = _physical_amounts(state)
|
||||||
@@ -327,10 +327,17 @@ func _append_batch(target: Dictionary, id: String, amount: float,
|
|||||||
var cursor: float = created_from
|
var cursor: float = created_from
|
||||||
var duration: float = created_to - created_from
|
var duration: float = created_to - created_from
|
||||||
var remaining_amount: float = amount
|
var remaining_amount: float = amount
|
||||||
while true:
|
# Compute the index once. Recomputing floor from a rounded boundary can
|
||||||
|
# select the preceding window forever (e.g. fractional origins at large ages).
|
||||||
var window_index: float = floorf((cursor - origin) / batch_duration)
|
var window_index: float = floorf((cursor - origin) / batch_duration)
|
||||||
|
if origin + (window_index + 1.0) * batch_duration <= cursor:
|
||||||
|
window_index += 1.0
|
||||||
|
while true:
|
||||||
var window_start: float = origin + window_index * batch_duration
|
var window_start: float = origin + window_index * batch_duration
|
||||||
var window_end: float = origin + (window_index + 1.0) * batch_duration
|
var window_end: float = origin + (window_index + 1.0) * batch_duration
|
||||||
|
if window_end <= cursor:
|
||||||
|
push_error("Batch window exceeds floating-point time precision: " + id)
|
||||||
|
return
|
||||||
var part_end: float = minf(created_to, window_end)
|
var part_end: float = minf(created_to, window_end)
|
||||||
var part_amount: float = remaining_amount
|
var part_amount: float = remaining_amount
|
||||||
if duration > 0.0 and part_end < created_to:
|
if duration > 0.0 and part_end < created_to:
|
||||||
@@ -354,6 +361,7 @@ func _append_batch(target: Dictionary, id: String, amount: float,
|
|||||||
if duration == 0.0 or part_end >= created_to:
|
if duration == 0.0 or part_end >= created_to:
|
||||||
break
|
break
|
||||||
cursor = window_end
|
cursor = window_end
|
||||||
|
window_index += 1.0
|
||||||
batches.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
|
batches.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
|
||||||
return a.created_from < b.created_from)
|
return a.created_from < b.created_from)
|
||||||
|
|
||||||
@@ -628,6 +636,7 @@ func _evolve_batch_generation(target: Dictionary, to_year: float) -> bool:
|
|||||||
return false
|
return false
|
||||||
var fractions: Dictionary = {}
|
var fractions: Dictionary = {}
|
||||||
var outputs: Dictionary = {}
|
var outputs: Dictionary = {}
|
||||||
|
var star_budget: Dictionary = CivilizationState.stellar_budget(self, target, Global.CivilizationRules)
|
||||||
for id in expired:
|
for id in expired:
|
||||||
var total: float = 0.0
|
var total: float = 0.0
|
||||||
for part: Dictionary in expired[id]:
|
for part: Dictionary in expired[id]:
|
||||||
@@ -636,6 +645,12 @@ func _evolve_batch_generation(target: Dictionary, to_year: float) -> bool:
|
|||||||
var combined: Dictionary = {}
|
var combined: Dictionary = {}
|
||||||
for part: Dictionary in expired[id]:
|
for part: Dictionary in expired[id]:
|
||||||
part.products = _evolution_products(target, entities[id], part.amount)
|
part.products = _evolution_products(target, entities[id], part.amount)
|
||||||
|
for successor in part.products.keys():
|
||||||
|
var requested: float = part.products[successor]
|
||||||
|
var admitted: float = CivilizationState.admit_transition(star_budget, Global.CivilizationRules, id, successor, requested)
|
||||||
|
part.products[successor] = admitted
|
||||||
|
if admitted < requested:
|
||||||
|
part.products[id] = part.products.get(id, 0.0) + requested - admitted
|
||||||
for successor in part.products:
|
for successor in part.products:
|
||||||
combined[successor] = combined.get(successor, 0.0) + part.products[successor]
|
combined[successor] = combined.get(successor, 0.0) + part.products[successor]
|
||||||
outputs[id] = {}
|
outputs[id] = {}
|
||||||
@@ -687,7 +702,7 @@ func _evolve_batch_generation(target: Dictionary, to_year: float) -> bool:
|
|||||||
continue
|
continue
|
||||||
if not entities.has(successor):
|
if not entities.has(successor):
|
||||||
continue
|
continue
|
||||||
if successor == id and not entities[id].life_rule.is_empty():
|
if successor == id and (not entities[id].life_rule.is_empty() or Global.CivilizationRules.has(id)):
|
||||||
target.amount[id] = target.amount.get(id, 0.0) + amount
|
target.amount[id] = target.amount.get(id, 0.0) + amount
|
||||||
else:
|
else:
|
||||||
amount = _credit(target, successor, amount)
|
amount = _credit(target, successor, amount)
|
||||||
@@ -705,7 +720,7 @@ func _evolve_batch_generation(target: Dictionary, to_year: float) -> bool:
|
|||||||
_bind_new_owners(target, new_owners)
|
_bind_new_owners(target, new_owners)
|
||||||
_round_object_allocations(target)
|
_round_object_allocations(target)
|
||||||
_apply_blast(target, blast_power, environment)
|
_apply_blast(target, blast_power, environment)
|
||||||
CivilizationState.reconcile(self, target, Global.CivilizationRules)
|
CivilizationState.enforce_capacity(self, target, Global.CivilizationRules, to_year)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
@@ -1116,6 +1131,8 @@ func produce(item_id: String, desired_amount: float) -> float:
|
|||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
var max_producible = floorf(calculate_max_producible(entity, desired_amount))
|
var max_producible = floorf(calculate_max_producible(entity, desired_amount))
|
||||||
|
var budget: Dictionary = CivilizationState.stellar_budget(self, state, Global.CivilizationRules)
|
||||||
|
max_producible = CivilizationState.admit_transition(budget, Global.CivilizationRules, "", item_id, max_producible)
|
||||||
|
|
||||||
if max_producible <= 0:
|
if max_producible <= 0:
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user