Update speed limit, enable embedded PCK format, and enhance brown dwarf production logic
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
extends Node
|
||||
|
||||
const SPEED_LIMIT: int = 5
|
||||
const SPEED_LIMIT: int = 6
|
||||
|
||||
# Write a readable diagnostic copy next to the encrypted save. Never used for loading.
|
||||
var save_plaintext_copy: bool = true
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ script_export_mode=2
|
||||
custom_template/debug=""
|
||||
custom_template/release=""
|
||||
debug/export_console_wrapper=1
|
||||
binary_format/embed_pck=false
|
||||
binary_format/embed_pck=true
|
||||
texture_format/s3tc_bptc=true
|
||||
texture_format/etc2_astc=false
|
||||
shader_baker/enabled=false
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
7. Применить суммарный бласт поколения; повторять обработку поколений, пока в интервале остаются переходы.
|
||||
8. Пересчитать группы и опубликовать состояние.
|
||||
|
||||
## Автоматическое создание бурых карликов
|
||||
|
||||
На горячие бурые карлики `HBD` выделяется 0,1% **нового фактически произведённого автоматического водорода** за расчётный интервал. По текущей таблице автоматическую долю имеют STN (`auto = 0.5`) и ASTN (`auto = 0.65`). Обычный водород, ручные покупки и неиспользованный запас прошлых интервалов повторно в эту долю не входят.
|
||||
|
||||
При стоимости `c = 0.1 H` квота объектов увеличивается на `ΔQ = ΔH_auto × 0.001 / c`. Она хранится между циклами и в сохранении в `auto_star_deficits.HBD`. Доступность ограничивается автоматическим запасом и уже оплаченным топливом: `Q ≤ (auto_resources.H + HBD_fuel) / c`.
|
||||
|
||||
Карлики появляются по одному. Для `q = max(0, Q)` создаётся `floor(q)` объектов и ещё один с вероятностью `q - floor(q)`, с ограничением по доступному топливу. Созданное количество вычитается из Q; отрицательный остаток компенсирует досрочное появление на следующих шагах. Повторного случайного округления без нового автоматического водорода нет. Это исключение из общего детерминированного округления GI. Состояние генератора `auto_star_deficits.HBD_random` сохраняется, поэтому загрузка продолжает ту же последовательность.
|
||||
|
||||
Водород списывается целыми единицами авансом: `charge = ceil(max(0, count × c - HBD_fuel))`. Неиспользованный остаток `HBD_fuel += charge - count × c` сохраняется в `auto_star_deficits.HBD_fuel` и больше не входит в свободный запас или бюджет обычных звёзд. Например, 500 автоматических H позволяют создать 5 HBD, списав 1 H и оставив 0,5 H оплаченного топлива. Следующие 5 HBD используют этот остаток без повторного списания. Численность всегда целая, но не обязана быть кратной 10. При отсутствии обычных звёзд их доля остаётся в запасе, а производство HBD всё равно работает.
|
||||
|
||||
Новые HBD получают обычные жизненные партии. По таблице их срок — 5 миллиардов лет, затем они постепенно превращаются в холодные `CBD`. На экране оба типа суммируются в `BD`. Ручного рецепта у общего счётчика BD нет. Миграция сохранений не требуется: квота использует существующий словарь, отсутствующее значение равно нулю.
|
||||
|
||||
## Постепенные партии
|
||||
|
||||
Окно рождения шириной `w = lifespan / 40` при `lifespan > 10`, иначе `w = lifespan`. Окно превращения сдвинуто на `lifespan`. При частичном списании:
|
||||
|
||||
+12
-64
@@ -9,10 +9,6 @@ var description_targets: Dictionary = {}
|
||||
var description_samples: Dictionary = {}
|
||||
var description_rates: Dictionary = {}
|
||||
var description_elapsed: float = 0.0
|
||||
const UI_UPDATE_INTERVAL: float = 0.1
|
||||
var ui_elapsed: float = 0.0
|
||||
var ui_pages: Vector2i = Vector2i(-1, -1)
|
||||
var closing: bool = false
|
||||
|
||||
const PAGE_COUNT = 5
|
||||
const RESOURCE_PAGE = 1
|
||||
@@ -93,9 +89,7 @@ func set_datetime(value: float):
|
||||
# Simulation time advances in tenths of a year; displaying five decimal
|
||||
# places only adds zeroes and suggests precision the simulation does not
|
||||
# have.
|
||||
var text: String = Global.format_number(value, 1) if value != 0.0 else "0.0"
|
||||
if date_time.text != text:
|
||||
date_time.text = text
|
||||
date_time.text = Global.format_number(value, 1) if value != 0.0 else "0.0"
|
||||
|
||||
#region Item manipulations: update and visibility
|
||||
|
||||
@@ -103,27 +97,8 @@ func set_datetime(value: float):
|
||||
func set_owniverse_item_value() -> void:
|
||||
for item_id in owniverse.items_to_update:
|
||||
if owniverse_items.has(item_id):
|
||||
var item: OwniverseItem = owniverse_items[item_id]
|
||||
if item.is_visible_in_tree() and item.get_global_rect().intersects(stage_page_scroller.get_global_rect()):
|
||||
item.set_label(owniverse.items_to_update[item_id])
|
||||
|
||||
|
||||
func _refresh_ui(force: bool = false) -> void:
|
||||
if force or owniverse.item_amounts_dirty:
|
||||
owniverse.refresh_item_amounts(true)
|
||||
set_owniverse_item_value()
|
||||
set_datetime(owniverse.state.time.year)
|
||||
_set_active_page_actions()
|
||||
ui_elapsed = 0.0
|
||||
|
||||
|
||||
func _update_ui(delta: float) -> void:
|
||||
ui_elapsed += delta
|
||||
var position_in_pages: float = float(stage_page_scroller.scroll_horizontal) / PAGE_WIDTH
|
||||
var pages := Vector2i(floori(position_in_pages), ceili(position_in_pages))
|
||||
if pages != ui_pages or ui_elapsed >= UI_UPDATE_INTERVAL:
|
||||
ui_pages = pages
|
||||
_refresh_ui()
|
||||
owniverse_items[item_id].set_label(owniverse.items_to_update[item_id])
|
||||
owniverse.items_to_update.clear()
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -244,9 +219,6 @@ func item_activated(item: OwniverseItem) -> void:
|
||||
|
||||
if item.item_id == "S" or item.item_id == "H":
|
||||
var _value = owniverse.produce_hs(item.item_id, 100_000)
|
||||
_unlock_items()
|
||||
_refresh_ui(true)
|
||||
_refresh_descriptions()
|
||||
|
||||
|
||||
func _check_counters(delta: float) -> Array:
|
||||
@@ -305,8 +277,6 @@ func _input(event: InputEvent) -> void:
|
||||
func _set_active_page_actions() -> void:
|
||||
for pa in page_actions:
|
||||
if !g_active_items.has(pa): continue
|
||||
if not page_actions[pa].is_visible_in_tree() or not page_actions[pa].get_global_rect().intersects(stage_page_scroller.get_global_rect()):
|
||||
continue
|
||||
|
||||
var item_id: String = g_active_items[pa].item_id
|
||||
var entity: OwniverseEntity = owniverse.entities[item_id]
|
||||
@@ -332,17 +302,12 @@ func _page_action_pressed(page_id: int, action_amount: float) -> void:
|
||||
|
||||
if action_amount > 0:
|
||||
owniverse.produce(item_id, action_amount)
|
||||
_unlock_items()
|
||||
_refresh_ui(true)
|
||||
_refresh_descriptions()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
get_tree().auto_accept_quit = false
|
||||
owniverse.defer_item_updates = true
|
||||
var inited_result = init_enitities()
|
||||
locked_items = inited_result.locked_items
|
||||
load_data()
|
||||
@@ -352,7 +317,6 @@ func _ready() -> void:
|
||||
set_datetime(owniverse.state.time.year)
|
||||
set_owniverse_item_value()
|
||||
_unlock_items()
|
||||
_refresh_ui(true)
|
||||
|
||||
g_counters.swipe.sec = 0.2
|
||||
|
||||
@@ -365,7 +329,8 @@ func _process(delta: float) -> void:
|
||||
|
||||
owniverse.evolute(delta)
|
||||
_update_descriptions(delta)
|
||||
_update_ui(delta)
|
||||
set_owniverse_item_value()
|
||||
set_datetime(owniverse.state.time.year)
|
||||
|
||||
#owniverse.cycle(delta)
|
||||
|
||||
@@ -374,6 +339,7 @@ func _process(delta: float) -> void:
|
||||
var actions = _check_counters(delta)
|
||||
_do_actions(actions)
|
||||
|
||||
_set_active_page_actions()
|
||||
|
||||
|
||||
func _change_speed(delta: int) -> void:
|
||||
@@ -440,8 +406,10 @@ func load_data() -> bool:
|
||||
SaveManager.blocked_saves["owniverse"] = true
|
||||
push_error("Save is missing the current entity: " + id)
|
||||
return false
|
||||
owniverse.state = restored
|
||||
owniverse.new_state = restored.duplicate(true)
|
||||
var complete: Dictionary = owniverse.state.duplicate(true)
|
||||
complete.merge(restored, true)
|
||||
owniverse.state = complete
|
||||
owniverse.new_state = complete
|
||||
owniverse.initialize_life_batches()
|
||||
owniverse.refresh_item_amounts()
|
||||
pending_page = clampi(int(game_data.get("ui", {}).get("current_page", 0)), 0, PAGE_COUNT - 1)
|
||||
@@ -455,41 +423,21 @@ func _restore_page() -> void:
|
||||
if pending_page >= 0:
|
||||
stage_page_scroller.scroll_horizontal = pending_page * PAGE_WIDTH
|
||||
pending_page = -1
|
||||
_refresh_ui(true)
|
||||
|
||||
|
||||
func save_data() -> void:
|
||||
var page: int = pending_page
|
||||
if page < 0:
|
||||
page = clampi(roundi(float(stage_page_scroller.scroll_horizontal) / PAGE_WIDTH), 0, PAGE_COUNT - 1)
|
||||
SaveManager.request_save("owniverse", "qwerty", {
|
||||
SaveManager.save_data("owniverse", "qwerty", {
|
||||
"state": owniverse.state,
|
||||
"ui": {"current_page": page},
|
||||
})
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if not is_node_ready() or closing:
|
||||
return
|
||||
if what == NOTIFICATION_WM_CLOSE_REQUEST:
|
||||
_close_after_save()
|
||||
elif what == NOTIFICATION_APPLICATION_PAUSED:
|
||||
if is_node_ready() and (what == NOTIFICATION_WM_CLOSE_REQUEST or what == NOTIFICATION_APPLICATION_PAUSED):
|
||||
save_data()
|
||||
# Mobile platforms can suspend all processing immediately after this event.
|
||||
SaveManager.flush_saves()
|
||||
|
||||
|
||||
func _close_after_save() -> void:
|
||||
closing = true
|
||||
set_process(false)
|
||||
set_process_input(false)
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
# Stop GUI actions too while the final snapshot is being written.
|
||||
process_mode = Node.PROCESS_MODE_DISABLED
|
||||
save_data()
|
||||
if SaveManager.is_saving():
|
||||
await SaveManager.saves_drained
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
func init_enitities() -> Dictionary:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Проверки поэтапного переноса из Owniverse.U
|
||||
|
||||
`test_stellar_rules.gd` также проверяет автоматическое создание HBD: долю 0,1% нового автоматического водорода, появление по одному со случайным округлением, сохранение оплаченного остатка топлива, отсутствие повторного начисления на старый запас, продолжение случайной последовательности после сериализации и переход HBD → CBD с сохранением счётчика BD. Формулы описаны в `../simulation_rules.md`.
|
||||
|
||||
## Фоновое сохранение и частота обновления интерфейса
|
||||
|
||||
```sh
|
||||
|
||||
@@ -15,15 +15,33 @@ func check(value: bool, message: String) -> void:
|
||||
func run() -> void:
|
||||
var actions = load("res://ui/actions/actions.tscn").instantiate()
|
||||
root.add_child(actions)
|
||||
var values: Array = [0.1, 0.01, 0.001, 1.0, 10.0]
|
||||
for maximum in [0.0, 0.1, 0.99, 1.0, 1.9, 10.0, 100.0, 123.0, 1000.0, 10000.0, 1e9, 1.1e9, 1e150]:
|
||||
actions.set_actions("test", maximum)
|
||||
var labels: Array = []
|
||||
for value in values:
|
||||
labels.append(actions._format_action_value(value))
|
||||
check(labels == ["0.10", "0.010", "0.0010", "1", "10"], "Fractional action amounts retain precision")
|
||||
check(labels[0] != labels[1] and labels[1] != labels[2], "Adjacent fractional amounts are distinguishable")
|
||||
actions.set_actions("test", 123.0)
|
||||
check(actions.action_values[0] == 1.0 and actions.action_values[1] == 10.0, "Distribution preserves distinct requests")
|
||||
check(actions.action_labels[0].text != actions.action_labels[1].text, "Rendered adjacent buttons are distinct")
|
||||
var values: Array = []
|
||||
for i in 5:
|
||||
var value: float = actions.action_values[i]
|
||||
var text: String = actions.action_labels[i].text
|
||||
check(value == floorf(value) and value <= floorf(maximum), "Whole affordable request")
|
||||
if value > 0.0:
|
||||
check(not values.has(value) and not labels.has(text), "Unique requests and displayed labels")
|
||||
values.append(value)
|
||||
labels.append(text)
|
||||
else:
|
||||
check(text == "" and actions.action_labels[i].get_parent().get_node("TextureButton").disabled, "Unused button blank and disabled")
|
||||
if maximum >= 1.0: check(values.back() == floorf(maximum), "Maximum retained")
|
||||
actions.set_actions("test", 100.0)
|
||||
check(actions.action_values.values() == [1.0, 10.0, 100.0, 0.0, 0.0], "Exact power of ten not duplicated")
|
||||
actions.set_actions("SMBH", 25.0, func(_request): return 25.0)
|
||||
check(actions.action_values.values() == [25.0, 0.0, 0.0, 0.0, 0.0], "Identical SMBH previews collapse to one button")
|
||||
actions.set_actions("step", 123.0, func(request): return floorf(request / 5.0) * 5.0)
|
||||
check(actions.action_values.values() == [10.0, 100.0, 123.0, 0.0, 0.0], "Unproducible small requests omitted")
|
||||
check(actions.action_labels[2].text == "120", "Recipe preview displayed while original request retained")
|
||||
var emitted: Array = []
|
||||
actions.action_pressed.connect(func(_page, amount): emitted.append(amount))
|
||||
actions._on_action_button_pressed(2)
|
||||
actions._on_action_button_pressed(4)
|
||||
check(emitted == [123.0], "Only active button emits its matching request")
|
||||
actions.queue_free()
|
||||
print("Action values: %d checks, %d failures" % [checks, failures])
|
||||
quit(0 if failures == 0 else 1)
|
||||
|
||||
@@ -168,6 +168,65 @@ func run() -> void:
|
||||
check(world.state.binds.RD.HP >= 1.0, "Automatic formation also creates planets")
|
||||
dispose(world)
|
||||
|
||||
world = fixture()
|
||||
world.entities.N.auto = 1.0
|
||||
# 1000 newly produced automatic H reserve 1 H, enough for 10 HBD.
|
||||
world.evolute(100.0)
|
||||
check(world.state.amount.HBD == 10.0, "Brown dwarfs form without existing ordinary stars")
|
||||
check(world.state.amount.H == 999.0 and world.state.auto_resources.H == 999.0, "Brown dwarfs consume exactly their reserved hydrogen")
|
||||
check(world.get_item_amounts().BD == 10.0 and world.state.batches.HBD.size() > 0, "Brown dwarfs appear in counter and evolution batches")
|
||||
world.entities.N.product_per_year_amount = 0.0
|
||||
world.evolute(100.0)
|
||||
check(world.state.amount.HBD == 10.0, "Stored automatic budget is not taxed repeatedly")
|
||||
dispose(world)
|
||||
|
||||
world = fixture()
|
||||
world.entities.N.auto = 1.0
|
||||
world.state.amount.RD = 1.0
|
||||
world.evolute(50.0)
|
||||
check(world.state.amount.HBD == 5.0 and is_equal_approx(world.state.auto_star_deficits.HBD_fuel, 0.5), "Individual dwarfs retain prepaid fractional fuel")
|
||||
check(world.state.amount.H == 0.0, "Prepaid fuel is not available to ordinary stars")
|
||||
var resumed = fixture()
|
||||
resumed.entities.N.auto = 1.0
|
||||
resumed.state = bytes_to_var(var_to_bytes(world.state))
|
||||
world.evolute(50.0)
|
||||
resumed.evolute(50.0)
|
||||
check(world.state.amount.HBD == 10.0, "Accumulated quota eventually creates a whole batch")
|
||||
check(world.state == resumed.state, "Brown dwarf quota survives serialization deterministically")
|
||||
check(world.state.amount.RD == 1000.0 and world.state.amount.H == 0.0, "Shared automatic hydrogen is spent once")
|
||||
world.state.batches.HBD = [{"amount": 10.0, "start_year": 101.0, "end_year": 101.0}]
|
||||
world._evolve_batches(world.state, 101.0)
|
||||
check(world.state.amount.HBD == 0.0 and world.state.amount.CBD == 10.0 and world.get_item_amounts().BD == 10.0, "Cooling preserves aggregate brown dwarf count")
|
||||
dispose(resumed)
|
||||
dispose(world)
|
||||
|
||||
world = fixture()
|
||||
var saw_individual: bool = false
|
||||
for i in range(250):
|
||||
world.state.amount.H += 1.0
|
||||
world.state.auto_resources.H = world.state.auto_resources.get("H", 0.0) + 1.0
|
||||
var before: float = world.state.amount.HBD
|
||||
world._produce_brown_dwarfs(world.state, 1.0, float(i), float(i + 1))
|
||||
var born: float = world.state.amount.HBD - before
|
||||
check(born == 0.0 or born == 1.0, "Small fuel increments produce individual dwarfs")
|
||||
saw_individual = saw_individual or born == 1.0
|
||||
check(saw_individual, "Random rounding does not wait for ten dwarfs")
|
||||
resumed = fixture()
|
||||
resumed.state = bytes_to_var(var_to_bytes(world.state))
|
||||
for i in range(250, 500):
|
||||
for simulation in [world, resumed]:
|
||||
simulation.state.amount.H += 1.0
|
||||
simulation.state.auto_resources.H += 1.0
|
||||
simulation._produce_brown_dwarfs(simulation.state, 1.0, float(i), float(i + 1))
|
||||
check(world.state == resumed.state, "Random sequence and prepaid fuel survive saving")
|
||||
check(is_equal_approx(world.state.amount.H + world.state.amount.HBD * 0.1 + world.state.auto_star_deficits.HBD_fuel, 500.0), "Individual production conserves fuel including prepaid reserve")
|
||||
check(is_equal_approx(world.state.amount.HBD + world.state.auto_star_deficits.HBD, 5.0), "Signed remainder preserves the 0.1 percent allocation")
|
||||
var paused_count: float = world.state.amount.HBD
|
||||
for i in 10: world._produce_brown_dwarfs(world.state, 0.0, 500.0, 501.0)
|
||||
check(world.state.amount.HBD == paused_count, "No repeated random attempts without new fuel")
|
||||
dispose(resumed)
|
||||
dispose(world)
|
||||
|
||||
# Two simultaneous successors share surviving planets in the same 99:1 ratio.
|
||||
world = fixture()
|
||||
world.entities.HRG.destroy_level = 0
|
||||
|
||||
+31
-19
@@ -26,21 +26,37 @@ var action_values: Dictionary = {
|
||||
func set_actions(item_id: String, value: float, preview: Callable = Callable()) -> void:
|
||||
current_item_id = item_id
|
||||
var requests: Array = _get_distribution(value)
|
||||
var displayed: Array = requests.duplicate()
|
||||
if preview.is_valid():
|
||||
for i in range(displayed.size()):
|
||||
displayed[i] = preview.call(requests[i]) if requests[i] > 0.0 else 0.0
|
||||
_set_action_values(requests, displayed)
|
||||
var unique_requests: Array = []
|
||||
var displayed: Array = []
|
||||
var labels: Dictionary = {}
|
||||
# Prefer the largest request (including MAX) when previews or rounded
|
||||
# scientific labels coincide. Keep the request paired with its preview.
|
||||
for i in range(requests.size() - 1, -1, -1):
|
||||
if requests[i] <= 0.0: continue
|
||||
var amount: float = preview.call(requests[i]) if preview.is_valid() else requests[i]
|
||||
if not is_finite(amount) or amount < 1.0 or amount != floorf(amount): continue
|
||||
var text: String = _format_action_value(amount)
|
||||
if displayed.has(amount) or labels.has(text): continue
|
||||
labels[text] = true
|
||||
unique_requests.push_front(requests[i])
|
||||
displayed.push_front(amount)
|
||||
while unique_requests.size() < 5:
|
||||
unique_requests.append(0.0)
|
||||
displayed.append(0.0)
|
||||
_set_action_values(unique_requests, displayed)
|
||||
|
||||
func _get_distribution(value: float) -> Array:
|
||||
if value <= 0.0 or not is_finite(value):
|
||||
if value < 1.0 or not is_finite(value):
|
||||
return [0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
var distribution: Array = [value, 0, 0, 0, 0]
|
||||
|
||||
var exp_value = int(floor(log(abs(value)) / log(10.0)))
|
||||
var floor_value = max(exp_value - 4, -1)
|
||||
for ev in range(exp_value, floor_value, -1):
|
||||
distribution.push_front(pow(10.0, ev))
|
||||
value = floorf(value)
|
||||
var distribution: Array = []
|
||||
var power: float = 1.0
|
||||
while power < value:
|
||||
distribution.append(power)
|
||||
if distribution.size() > 4: distribution.pop_front()
|
||||
power *= 10.0
|
||||
distribution.append(value)
|
||||
while distribution.size() < 5: distribution.append(0.0)
|
||||
return distribution
|
||||
|
||||
func _set_action_values(values: Array, displayed: Array) -> void:
|
||||
@@ -50,19 +66,15 @@ func _set_action_values(values: Array, displayed: Array) -> void:
|
||||
var text: String = _format_action_value(displayed[ind])
|
||||
if action_labels[ind].text != text:
|
||||
action_labels[ind].text = text
|
||||
action_labels[ind].get_parent().get_node("TextureButton").disabled = values[ind] <= 0.0
|
||||
|
||||
|
||||
func _format_action_value(value: float) -> String:
|
||||
if value <= 0.0 or not is_finite(value):
|
||||
if value < 1.0 or not is_finite(value) or value != floorf(value):
|
||||
return ""
|
||||
if value >= 1.0:
|
||||
return Global.format_number(value)
|
||||
# Global's default is intended for object counts. Action values may be
|
||||
# fractional, so retain enough precision to keep adjacent buttons distinct.
|
||||
var exponent: int = int(floor(log(value) / log(10.0)))
|
||||
var digits: int = clampi(-exponent + 1, 2, 6)
|
||||
return Global.format_number(value, digits)
|
||||
|
||||
|
||||
func _on_action_button_pressed(button_id: int) -> void:
|
||||
if action_values.get(button_id, 0.0) > 0.0:
|
||||
action_pressed.emit(page_id, action_values[button_id])
|
||||
|
||||
@@ -997,6 +997,8 @@ func _produce_for_period(previous: Dictionary, target: Dictionary, quantities: D
|
||||
for id in budget:
|
||||
budget[id] = minf(maxf(0.0, budget[id]), _available(target, id))
|
||||
target.auto_resources = budget
|
||||
_produce_brown_dwarfs(target, automatic.get("H", 0.0), from_year, to_year)
|
||||
budget = _ordinary_star_budget(target)
|
||||
|
||||
# One common multiplier preserves the initial count ratios, even under scarcity.
|
||||
var candidates: Dictionary = {}
|
||||
@@ -1059,7 +1061,7 @@ func _produce_for_period(previous: Dictionary, target: Dictionary, quantities: D
|
||||
continue
|
||||
if not _try_production(target, entity, actual, true):
|
||||
continue
|
||||
budget = target.auto_resources
|
||||
budget = _ordinary_star_budget(target)
|
||||
created[id] = actual
|
||||
total_created += actual
|
||||
for id in candidates:
|
||||
@@ -1071,6 +1073,48 @@ func _produce_for_period(previous: Dictionary, target: Dictionary, quantities: D
|
||||
_create_planets(target, id, created[id], from_year, to_year, metallicity)
|
||||
|
||||
|
||||
# HBD quota is in objects. HBD_fuel is prepaid hydrogen removed from free stock.
|
||||
# Signed rounding debt and the PRNG state survive saving with this dictionary.
|
||||
func _produce_brown_dwarfs(target: Dictionary, new_hydrogen: float, from_year: float, to_year: float) -> void:
|
||||
if not entities.has("HBD"):
|
||||
return
|
||||
var entity: OwniverseEntity = entities.HBD
|
||||
var cost: float = entity.cost.get("H", 0.0)
|
||||
if cost <= 0.0:
|
||||
return
|
||||
var quota: float = target.auto_star_deficits.get("HBD", 0.0) + maxf(0.0, new_hydrogen) * 0.001 / cost
|
||||
var fuel: float = maxf(0.0, target.auto_star_deficits.get("HBD_fuel", 0.0))
|
||||
var available: float = minf(target.auto_resources.get("H", 0.0), _available(target, "H"))
|
||||
quota = minf(quota, (available + fuel) / cost)
|
||||
var expected: float = maxf(0.0, snappedf(quota, 1e-9))
|
||||
var amount: float = floorf(expected)
|
||||
# Do not retry a fractional chance on frames with no new automatic fuel.
|
||||
if new_hydrogen > 0.0 and expected > amount:
|
||||
var random_state: float = target.auto_star_deficits.get("HBD_random", 1234567.0)
|
||||
random_state = fmod(random_state * 48271.0, 2147483647.0)
|
||||
target.auto_star_deficits.HBD_random = random_state
|
||||
if random_state / 2147483647.0 < expected - amount:
|
||||
amount += 1.0
|
||||
amount = minf(amount, floorf(snappedf((available + fuel) / cost, 1e-9)))
|
||||
var charge: float = ceilf(maxf(0.0, snappedf(amount * cost - fuel, 1e-9)))
|
||||
if amount > 0.0 and charge <= available:
|
||||
_take_available(target, "H", charge)
|
||||
target.auto_resources.H = maxf(0.0, target.auto_resources.get("H", 0.0) - charge)
|
||||
target.auto_star_deficits.HBD_fuel = maxf(0.0, snappedf(fuel + charge - amount * cost, 1e-9))
|
||||
quota -= amount
|
||||
_credit(target, "HBD", amount)
|
||||
_append_batch(target, "HBD", amount, from_year, to_year)
|
||||
target.auto_star_deficits.HBD = quota
|
||||
|
||||
|
||||
func _ordinary_star_budget(target: Dictionary) -> Dictionary:
|
||||
var budget: Dictionary = target.auto_resources.duplicate()
|
||||
if entities.has("HBD"):
|
||||
var reserved: float = maxf(0.0, target.auto_star_deficits.get("HBD", 0.0) * entities.HBD.cost.get("H", 0.0) - target.auto_star_deficits.get("HBD_fuel", 0.0))
|
||||
budget.H = maxf(0.0, budget.get("H", 0.0) - reserved)
|
||||
return budget
|
||||
|
||||
|
||||
func _auto_capacity(source: Dictionary, entity: OwniverseEntity, budget: Dictionary) -> float:
|
||||
var capacity: float = INF
|
||||
var needs: Dictionary = {}
|
||||
|
||||
Reference in New Issue
Block a user