Files
owniverse/simulation_rules.md
T
Kirill a7c05e3796 Add report generation functionality to the UI
- Introduced a new script `report.gd` that extends `VBoxContainer` to manage the display of reports.
- Implemented dynamic localization support for report headers and body content based on the global locale.
- Created a `refresh` method to update the report content and header image when visibility changes or locale updates.
- Added a static `build` method to format and compile report data from the world state, including calculations for star totals and effects.
- Integrated presentation logic to render report templates based on the current state of the game world.
2026-10-18 21:48:15 +03:00

51 KiB
Raw Blame History

Эволюция звёзд Owniverse.GI

Числа симуляции хранят целую часть в двух блоках int64, дробный остаток — в float. Весь порядок E27 сохраняет целые единицы. Возраст разделён на целые годы и дробную часть с шагом 10^-9 года; большие промежуточные произведения распределений вычисляются через mul_div. Границы точности, допуск переноса дробей и несовместимый со старым формат сохранения 4 описаны в документации математики.

Механики перенесены из Owniverse.U с согласованными изменениями GI: постепенные партии, детерминированные дробные остатки, уязвимость цивилизаций до C1.2 и передача выживших планет преемнику. Баланс таблицы GI сохраняется.

Порядок расчёта

  1. Накопить игровое время; обработать новые десятые доли года.
  2. Рассчитать производство существующих объектов за интервал с учётом их постепенного исчезновения; выполнить автоматическое создание звёзд и их планет.
  3. Для очередного поколения определить долю заканчивающих жизнь звёзд. Применить локальное разрушение их планет до списания партий.
  4. Снять целые объекты из партий, рассчитать основные и альтернативные выходы с накоплением дробей.
  5. Передать выжившие планеты полученным звёздам/остаткам. Если преемников несколько, распределить по их фактическим количествам; если преемника нет — освободить. Остальные связанные объекты используют существующие правила GI.
  6. Создать партии преемников. Учесть производство новорождённых за оставшуюся часть длинного шага, включая излучение короткоживущих сверхновых.
  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 нет. Миграция сохранений не требуется: квота использует существующий словарь, отсутствующее значение равно нулю.

Постепенные партии

Короткоживущие звёздные события внутри длинного шага

Для SN, DSN, BSN, HN и GRB-событий полностью завершившиеся окна рождения не создают отдельные словари партий. Их целые количества суммируются в очередь завершений текущего расчёта; обычные партии сохраняются только для окон, срок которых ещё не закончился. Состояние этой очереди не записывается в сохранение: она полностью обрабатывается до публикации нового состояния.

Окно завершено, если window_end + lifespan ≤ to_year. Его доля количества рассчитывается прежним методом разности накопленных целых значений. Для производства пространства интеграл исчезновения вычитается по окнам в прежнем порядке, чтобы сохранить округление. Здесь остаётся лёгкий арифметический проход по окнам, но отсутствуют создание, копирование, сортировка и последующая обработка тысяч временных словарей.

Ресурсы, остатки звёзд и бласты от завершившейся части применяются на следующем поколении расчёта, как раньше. Поэтому сохраняются порядок передачи привязок, среда бластов и ограничения цивилизаций. Ускорение допускается только для событий группы SNGRBB с линейным выходом и без преемников с ненулевым сроком жизни. При изменении таблиц остальные случаи автоматически используют обычный путь. Сроки жизни и ширина окон не увеличиваются, формат сохранения не меняется.

На сохранении возраста 140,55 млн лет, при шестой скорости и шаге 1/60 секунды: максимальное временное число партий SN/GRB уменьшилось с 1668 до 2; средний расчёт кадра без отрисовки — примерно с 15 до 10 мс. За 120 шагов результаты старого и нового путей совпали по всем полям состояния. Эти значения характеризуют конкретное сохранение, а не гарантированную частоту кадров на всех устройствах.

Общие окна рождения и эволюции

На всех скоростях целевая частота запуска — пять полных циклов в секунду. После накопления не менее 0,2 реальной секунды запускается расчёт за весь ожидающий игровой интервал. Время, поступающее во время расчёта, остаётся для следующего цикла. При задержках шаг увеличивается; время не отбрасывается. Смена скорости не сокращает реальный интервал между запусками. Незавершённая транзакция из сохранения возобновляется с прежней границей. Кооперативный бюджет кадра — 40 мс; фактическая частота завершения зависит от стоимости расчёта.

На скоростях 4–6 укрупнение звёздных партий зависит от размера цикла и прежнего окна рождения. Номинальная ширина сетки партий — 0.2 × 10^(speed - 1) игровых лет: 200, 2000 и 20000 лет. Если прежнее окно max(lifespan / 40, временное разрешение) шире цикла, оно сохраняется: долгоживущие звёзды не дробятся на более мелкую сетку. Если оно не шире цикла, новые звёзды группы Star (включая красные гиганты) объединяются по общей границе завершения жизни и переходят целиком.

При обработке существующих партий объединяются только две или более партии одного типа, весь оставшийся интервал превращения которых попадает в один и тот же цикл. Широкие партии и партии, пересекающие границу циклов, остаются без изменений. Количества и накопленные остатки складываются точно; объединённая партия переходит целиком на границе цикла. Повторное укрупнение не меняет результат.

Это приближение времени перехода: задержка узкой партии не превышает одного цикла. Производство учитывает полное исчезновение объединённой партии на выбранной границе. При снижении скорости новые звёзды снова получают постепенные партии; уже созданные партии не разделяются. События SN/GRB сохраняют прежний расчёт.

На скоростях 1–3 звёзды, а также остальные объекты со сроком жизни больше 10 лет используют 40 возрастных групп: окно рождения имеет ширину w = lifespan / 40; для коротких событий ширина равна сроку жизни. Окно превращения сдвинуто на lifespan. Для облаков и первичной жизни на скоростях 4–6 сохраняется прежнее укрупнение окна по временному разрешению. При частичном списании:

expected = remainder + (remaining_count - remainder) * elapsed / remaining_window
converted = floor(expected)
remainder = expected - converted

Конец постепенного окна списывает остаток. Новые звёздные партии высоких скоростей вместо такого окна имеют одну границу полного перехода. Длительность событий SN/GRB остаётся годовой.

Планеты

При ручном и автоматическом создании звёзд:

expected = stars_created * planet_amount * planet_chance * metallicity + remainder
planets_created = floor(expected)
remainder = expected - planets_created

Остаток независим для каждой пары звезда/планета и сохраняется. Планеты сразу связаны со звездой. Эволюционный переход сам по себе не создаёт новую планетную систему. Шансы планет в таблице — доли, а проценты альтернативных выходов звёзд делятся на 100.

Среда: адаптация формул Unity к состоянию GI

GI не хранит накопленные счётчики первичной материи Unity. Поэтому stellar_environment() вычисляет среду из текущего физического состава, включая связанные объекты, без суммирующих групп интерфейса. Это приближение для баланса GI, а не побитовое воспроизведение Unity.

  • M — свободные и связанные H/He/Met плюс табличная масса конкретных объектов и масса SMBH.
  • V — физическое S плюс табличный объём объектов и voids * ExtraParams.VoidSize.
  • Z = max(0, 0.4 * ln(100 * free_Met / M + 0.11) + 0.88); при M = 0 металличность нулевая.
  • B = ExtraParams.DA * f(M), где f(0)=1, f(M)=M^0.1 при 0<M<1e10, иначе 10.
  • density = ExtraParams.NPC * sqrt(B + M) / max(1, V)^(1/6).
  • blast_factor = clamp(free_S / V, 0, 1); при нулевом объёме 1.

Планетные бонусы сил пока не перенесены. Параметры мира Bur/Exp/Agg имеют собственные сохраняемые шкалы и эффективные множители (см. раздел индикаторов ниже). Металличность для производства фиксируется до расходования ресурсов на звёзды. Среда бласта фиксируется для поколения до добавления его продуктов.

Разрушение и бласты

Локальное воздействие: ratio = dying_stars / physical_stars, без множителя плотности.

Бласт: hits = floor(power * blast_factor + saved_remainder). Распределение экспозиции между типами звёзд, имеющими планетные системы в таблице, пропорционально их физическому количеству. Для типа: ratio = hits * star_share / physical_stars * density.

Для каждого связанного типа планет накапливается held_planets * min(1, ratio). Уничтожается целая часть, не больше существующего количества. Остатки локального воздействия и бластов раздельны. Это позволяет слабым воздействиям работать при постепенной эволюции.

Уровень 1 превращает только blastability=1 в RP (HP, EON, цивилизации до C1.2). C1.3 и выше защищены от этого уровня. Уровень 2 уничтожает все планеты, включая защищённые цивилизации, без создания RP. Уничтоженные объекты удаляются из жизненных партий, чтобы не возникать заново.

Единственный источник мощности — поле blast сущности: power = expired_count × max(0, entity.blast). При blast = 0 внешнего воздействия нет. Старые вхождения blast:N в output игнорируются, включая альтернативные выходы; двойного суммирования нет. Внутренний выход blast остаётся событием, а не предметом. destroy_level независимо задаёт уровень локального разрушения у умирающего объекта; внешние бласты всегда действуют на уровне 1.

Границы этапа

Рассчитываются агрегаты по типам, а не отдельные звёздные системы; конкретная принадлежность каждой планеты отдельной звезде не моделируется. Распределения целых объектов детерминированы. Порядок внутри длинного шага — по поколениям, поэтому нелинейное воздействие бластов и среды не обязано совпадать с серией более коротких шагов. Эволюция HP и эонов подключена ниже; эволюция цивилизаций и бонусы структур описаны ниже. Глобальная система сил остаётся отдельной механикой.

Проверки: tests/test_time.gd, tests/test_stellar_rules.gd. Сохранения игрока тестами не затрагиваются.

Первичная жизнь: HP → EON1 → … → EON5 → C0.0

Правила встроены в data/owniverse_life_rule_loader.gd и загружаются через OwniverseLifeRuleLoader; исходные сроки и коэффициенты взяты из Unity civs.txt. В каждой обработанной части партии:

  • прогресс: count * progress + carry_progress;
  • деградация: count * degradation + carry_degradation;
  • остальная жизнь остаётся на прежней стадии и получает новую партию для следующей попытки.

Прогресс и деградация округляются вниз с сохранением остатков. Если оба накопленных исхода конкурируют за последнюю планету, распределяются только существующие объекты, а неиспользованный остаток переносится на следующую попытку. Деградация распределяется по весам fall; знаковые ошибки распределения также сохраняются.

Базовые доли за попытку: для HP progress=0.00025, для EON1–EON5 progress=0.025; у всех degradation=0.0025. Текущие интервалы HP/EON1/EON2/EON3/EON4/EON5: 5 млн, 1 млн, 1 млн, 100 тыс., 100 тыс., 500 тыс. лет. Используются постепенные окна GI шириной span / 40. Случайные множители Unity не применяются. Агрессия, порядок и просвещение изменяют вероятности по формулам из раздела индикаторов ниже.

HP деградирует в RP. У эонов возможен откат к предыдущим стадиям по таблице. RP не развивается сам. C0.0 и C0.1 также развиваются: период 2000 лет, прогресс 0.0175, деградация 0.025. Переходы C0.0 → C0.1 → C0.2, откат C0.0 → EON5 и C0.1 → C0.0. Развитие C0.2 и выше подключено через этические правила. Конфликт civ_bonus устранён повторным импортом: бонусы структур совпадают с Unity. Для существующих этических цивилизаций пользователь выбрал равномерное распределение между шестью этиками с детерминированным целым остатком.

Переходы сохраняют количество планет и их хозяев, включая передачу преемнику звезды и разрушение бластом. Повторная попытка той же стадии не увеличивает статистику созданных объектов. Партии новых планет регистрируются при создании; недостающие партии старых HP/эонов — однократно при загрузке с текущим возрастом вселенной, без выдумывания их прошлого возраста. Состояние содержит life_version=2 (версия 1 также загружается) и life_remainders.

Миграция версии жизни 1 → 2 добавляет только недостающие партии C0.0/C0.1, сохраняя уже существующие партии HP и эонов.

Этические и космические цивилизации

data/owniverse_civilization_rule_loader.gd содержит 43 стадии по шесть этик из Unity. OwniverseCivilizationRuleLoader группирует строки по стадиям, формирует связи между ними и передаёт готовый словарь в Global.CivilizationRules. После C4.4 следует Ct: конечная стадия без новых жизненных партий.

Стадия хранит точное целое количество в физическом реестре и шесть долей float в ethics[stage]. C0.2–C4.4 не используют возрастные партии. Значение span задаёт средний интервал попыток: интенсивности прогресса, деградации, войны и колонизации равны соответствующим коэффициентам таблицы, делённым на эффективный span.

civilization_flow.gd вычисляет ожидаемые потоки по состоянию начала интервала. Разреженный оператор непрерывных переходов интегрируется рядом Пуассона (uniformization): один интервал может пройти несколько последовательных стадий, включая достижение Ct. Число членов ряда зависит от интенсивности и длительности интервала, а не от количества цивилизаций. Большой интервал полностью обрабатывается частями с параметром Пуассона не более 32. Промежуточные массы и составы потоков — float; конечные количества распределяются целыми квотами с сохранением точной общей суммы. Доли квот округляются до 15 десятичных знаков; остатки округления сохраняются между циклами.

Обычный дрейф этик интегрируется аналитически: уход 2% за средний интервал попыток распределяется между четырьмя не противоположными этиками. После смешивания приходящих потоков дипломатия постепенно смещает доли в соответствии с diplomacy_rate. Ct сохраняет этику без дрейфа. Это приближённое разделение процессов: переходы и дрейф используют один интервал, но не чередуются для каждого отдельного поколения.

Войны включены в интенсивности переходов. На столкновение ожидается 17/36 успешного прогресса и 31/18 суммарных потерь сторон, разделённых на Order; потери распределяются по fall. Счётчик столкновений и колониальные попытки интегрируются по времени пребывания на каждой стадии, включая посещённые внутри интервала. Побочный ущерб 40/3 на столкновение применяется к остающейся популяции предыдущей стадии и ограничивается её количеством. Отдельных партий победителей, нападающих и жертв нет.

Колонизация агрегирует запросы по стадии назначения (на две стадии ниже отправителя), ограничивает их общим запасом GG, AB, RP и HP и заменяет ровно выделенное число планет. Целые колонии сохраняют владельца целевой планеты; состав получает дрейф 960/4/9. Исходное население не расходуется.

Применение потоков сначала строит таблицу исходных владельцев. Для каждой исходной стадии готовые целые квоты распределяются по строкам владельцев; последняя строка получает точный остаток. Стадии с единственным свободным запасом применяют готовые квоты напрямую. Поступления объединяются по стадии и владельцу; физическая выдача и статистика создания выполняются один раз на получателя. Ct всегда остаётся свободной от привязок.

При колонизации планеты сначала списываются по типам и владельцам, затем суммарное списание каждого владельца распределяется между стадиями колоний. Каждая стадия получает одну суммарную выдачу. Общие целые квоты, число списанных планет и количество колоний сохраняются точно. Из-за объединения распределений единичные остатки между владельцами могут отличаться от прежнего последовательного порядка; вероятности переходов и формулы этик не меняются.

В пределах цивилизационной транзакции точные общие количества передаются между фазами. Применение переходов, колонизация и коррекция ёмкости возвращают обновлённый снимок; перемещение между владельцами само по себе общую численность не меняет. Этот снимок используется для периодов, ограничений, нормализации этик и поклонения. После изменения запасов другими подсистемами снимок строится заново. Снимки не сохраняются в файл и не заменяют проверку состояния при внешних изменениях словарей.

Автоматическое создание нескольких типов звёзд также использует один текущий снимок. Успешная пробная операция вычитает фактически израсходованные количества; перенос ресурса в привязку сохраняет общий запас. При отказе пробной операции снимок остаётся неизменным. Новые звёзды выдаются после завершения отбора типов, как и прежде.

Космические стадии C2.0–C4.4 свободны от планетных привязок. Общие бюджеты звёздных семейств и структур учитывают уже занятые резервы. Неподдерживаемый промежуточный переход блокируется в операторе; конечные положительные запросы делят свободную ёмкость одновременно. Отклонённые количества возвращаются к источнику. Освободившаяся при переходах ёмкость доступна следующему шагу. Если внешние изменения уменьшили ёмкость ниже существующего населения, избыток последовательно деградирует с приоритетом старших стадий. При возвращении в планетные стадии используются реальные карликовые звёзды, при отсутствии хозяина население остаётся свободным.

Порядок полного шага: производство за интервал → непрерывные потоки существующих цивилизаций → поколения звёзд и биологической жизни и бласты → итоговые ограничения среды → тренды. Возникшие из C0.1 цивилизации C0.2 получают равномерный профиль, смешанный с существующим населением, и начинают непрерывную эволюцию со следующего шага. Разные размеры полного шага могут отличаться из-за этого порядка, ограничений среды и нелинейных эффектов; чистая непрерывная цепочка без ограничений согласована при разделении интервала.

Счётчик времени UI показывает запрошенные часы и обновляется с частотой до 10 Гц независимо от тяжёлой транзакции. Состояние мира и его индикаторы публикуются по завершении транзакции; при высокой нагрузке отображаемые часы поэтому могут немного опережать рассчитанные объекты. Тяжёлые циклы адаптивно запускаются с целевым интервалом 0,2–0,5 реальной секунды, каждый использует весь накопленный игровой интервал.

В civilization_remainders сохраняются квоты и дробные остатки потоков; в civilization_stats — войны, колонии, побочный ущерб и поклонение. Возрастные партии сохраняются для звёзд, HP, эонов, C0.0 и C0.1.

Структуры и периоды

Прямой claim задаёт структуру стадии и норму 1; каждая промежуточная стадия умножает норму на 10 до следующего claim. Начиная с C3.0 требуется соответствующий запас структур, общий для использующих его стадий. При потере запаса избыток деградирует. Этот бюджет действует одновременно со звёздным.

Размещение выбирает структуры в порядке убывания civ_bonus, начиная с соответствующего стадии claim. Прямое размещение ограничено числом структур; остальные разрешённые структуры при наличии дают общий бонус, как в Unity. Эффективный средний интервал попыток равен span / средневзвешенный civ_bonus, для неразмещённой части множитель равен 1. Бонус входит в оператор следующего расчётного интервала.

Ct не получает часы. Инициализация отсутствующих партий жизни выполняется при загрузке и перед продвижением симуляции. Параметры мира и тенденции описаны ниже; интерфейс применения сил в этот перенос не входит.

Параметры мира, тенденции и индикаторы

Источник формул — Unity OwniverseInstance, Life, LifeActions, Trends и классы индикаторов. Расчёты находятся в world_parameters.gd; показ — в ui/indicators/world_indicators.gd. Шкалы обновляются каждые 0,1 реальной секунды и сразу при создании/загрузке вселенной. Подписи используют текущую локаль и шаблоны.

Энергия и плотность

Энергия сохраняет экономику GI: запас расходуется на H/S. Заполнение индикатора — clamp(energy / 1e14, 0, 1). Это не коэффициент производства Unity; дополнительное замедление производства при уменьшении запаса не вводится. В описании выводятся остаток и процент.

Плотность берётся из stellar_environment. Положение указателя ограничено диапазоном 0…1:

  • density ≤ 1e-5: шкала разрыва, (log10(max(density, 1e-7)) + 7) / 2;
  • 1e-5 < density < 0.9: обычная шкала, ln(density × 1e5) / ln(9e4);
  • density ≥ 0.9: шкала сжатия, (density − 0.9) × 10.

В Unity в первой формуле было −7; это ошибка, не позволяющая указателю двигаться. Здесь исправлено на +7. Переключение шкалы меняет и заголовок/описание индикатора.

Общая рамка Background/WarningBorder получает цвета Unity WarningUI в формате RGBA: при плотности ≤1e-5 — C040407F, ≤1e-6 — DF2020BF, ≤1e-7 — FF0000FF; при плотности ≥0,9 — 40A0C07F, ≥0,97 — 20B0DFBF, ≥1 — 00C0FFFF. В нормальном диапазоне рамка прозрачна. Она обновляется вместе с индикаторами и не перехватывает нажатия.

Пороговые состояния управляют запуском апокалипсиса: при density ≤ bigrip3 начинается Big Rip, при density ≥ bigcrunch3 — Big Crunch. Последовательность берётся из Global.ApocalypseQueue, сортируется по Num и использует указанные Stage, Screen и Tick. Каждые 0,1 секунды активные объекты выбранной страницы теряют половину количества (для остатков до 5 — по одному). На время последовательности скорость фиксируется на 1, ручное и автоматическое создание блокируется, а экран переключается по очереди. Big Rip пропускает S, Big Crunch — SMBH; остаток энергии постепенно переводится в соответствующий объект. После 20 тиков с нулевой энергией апокалипсис получает фазу завершения, симуляция останавливает время. Фаза, тип события, предупреждение и позиция в очереди сохраняются в состоянии вселенной.

Горючесть, взрывчатость и агрессия

world_parameters хранит Bur, Exp и Agg в диапазоне 1…19, начальное значение — 10. Эффективный множитель M(x) = 10^((x−10)/9): крайние значения 0,1 и 10, нейтральное — 1.

  • Bur делит срок жизни новых звёзд на M(Bur). modify_world_parameter("Bur", delta) также пересчитывает оставшееся время существующих звёзд с коэффициентом старый M / новый M, сохраняя количество и остатки округления. Производство звёзд умножается на M, если M ≤ 1, иначе на sqrt(M).
  • Exp умножает вероятность альтернативного исхода (с ограничением до 1), мощность entity.blast и производство событий группы SNGRBB. Локальный destroy_level от него не меняется.
  • Для Agg обозначим a = M(Agg). Вероятность прогресса умножается на a, деградации — на (1 − (1−a)/20) / Order, войны — на 1 − (1−a)/5, колонизации — на 1 − (1−a)/2. Если сумма прогресса и деградации превышает 1, они пропорционально нормируются. Вероятности войны/колонизации ограничены единицей.
  • Для первичной жизни дополнительный множитель прогресса — Enlightenment. Order дополнительно уменьшает потери в войнах и побочный ущерб. Начальные Enlightenment и Order равны 1.

Изменять шкалы следует через owniverse.modify_world_parameter(key, delta), чтобы горючесть корректно обновляла партии. Готовых кнопок применения сил этот метод не добавляет.

Тенденции

Пять направлений: Antitheism, Integration, Xenocide, Reconstruction, Spirituality. Участвуют космические стадии с непустым home; вес первой — 1, каждой следующей — вдвое больше. Для каждой тенденции:

rate = Σ_стадий (вес стадии × Σ_этик(доля этики × вес этики))

прирост = rate × прошедшие игровые годы × 1e-9.

Доли берутся на начало интервала, после проверки вместимости. Умножение на время исправляет ошибку Unity, где аргумент period не использовался. Накопление само по себе не зависит от FPS; изменение состава цивилизаций внутри крупных шагов остаётся дискретным, как остальная симуляция.

Веса этик в порядке пяти направлений:

Этика Антитеизм Интеграция Ксеноцид Реконструкция Духовность
m 0 2 2 0 6
l 6 1 1 2 0
w 2 0 6 0 2
d 0 6 0 1 3
c 1 3 3 3 0
b 3 0 0 6 1

При достижении 1 учитываются все целые срабатывания, дробный остаток сохраняется. Только Integration имеет последствия в исходной Unity: равновероятно выбирается Federation (+0,1 и Agg −1), Enlightenment (+0,2 и Agg −1) или Order (+0,1). Генератор сохраняет своё состояние. До 256 событий за шаг выбор выполняется по одному; для больших количеств используется нормальная аппроксимация мультиномиального распределения с сохранением точного суммарного числа событий, чтобы высокие скорости не блокировали кадр. Она может менять последовательность исходов при другом разбиении больших интервалов.

Federation сохраняется и увеличивается как в Unity, но в исходном коде не применяется к дипломатии. Дополнительное правило для него не придумано. Остальные четыре тенденции имеют счётчики срабатываний и сброс, но не последствия — так же, как в исходной реализации. Параметры и остатки тенденций сохраняются; у существующего сохранения текущего формата без этих полей используются нейтральные значения и нулевые тенденции, без изменения ресурсов или возраста.

Дополнительные параметры

Рабочая таблица встроена в data/owniverse_extra_param_loader.gd как const DATA, по аналогии с остальными таблицами. В Global._init() вызывается get_data() до создания главной сцены: CSVLoader разбирает строки, колонка param export преобразуется через E.of. Результат хранится только в Global.ExtraParams. После редактирования таблицы требуется перезапуск игры; отдельного CSV и статического кэша загрузчика нет.

Из таблицы берутся начальная энергия E, добавочная масса DA, коэффициент NPC, размер пустот VoidSize, показатель степени combustibility_bonus для горючести выше единицы, пороги bigrip1–3 и bigcrunch1–3. Интервалы индикатора плотности вычисляются из тех же порогов, без дублирующих числовых констант. Начальная энергия относится к новым вселенным; запас энергии существующей вселенной остаётся частью её сохранения.

G, C, space_generation, ChatPerTick и параметры ForceBox загружаются, но пока не используются в GI: соответствующие формулы и механики старой Unity-версии здесь отсутствуют. Производство объектов в GI задаётся их собственной таблицей life_product и lifespan.

Для звёзд с прямой стоимостью H доля He в свободной смеси H+He заменяет часть водородной стоимости. Применяются helium_multiplier и helium_bonus_limit, а замещение ограничено 50%, как в Unity. Недостающий He не блокирует создание: непокрытая часть оплачивается H. При создании таких звёзд срок жизни умножается на 1 − 1.5 × доля замещения; тот же коэффициент задаёт сроки их партий. Автоматическое создание использует свободный He, а бюджет автоматического H списывает только оставшуюся после замещения водородную часть.

tests/test_extra_params.gd проверяет загрузку точных чисел и изменение энергии, плотности, объёма пустот, порогов индикатора и бонуса горючести.

Достижения и уведомления

OwniverseAchievementLoader задаёт локализованные достижения. Строки с Ignore=1 исключены из проверок, списка и общего количества. Для включённых строк поддерживаются age::порог и total:ID:порог. total — накопленные поступления stats.total_amount, включая автоматическое производство и эволюцию; расход и гибель объектов не уменьшают этот счётчик. Возраст берётся из завершённого состояния симуляции. Пороги разбираются точной арифметикой.

ui/achievements/achievement_controller.gd проверяет условия при смене ревизии состояния и после создания новой вселенной. Пустое OpenCondition делает достижение видимым без уведомления; непустое открывает его с серой иконкой g. Выполнение AchieveCondition выдаёт цветную иконку c. Если оба условия выполнены одновременно, показывается только цветное уведомление. Уже полученные достижения повторно не выдаются.

Состояния видимости и получения сохраняются отдельно в user://achievements.cfg и сохраняются между вселенными. Очередь уведомлений не сохраняется. Единая панель поверх индикаторов следует за выбранной страницей: показ 3 секунды, затем затухание 0,3 секунды. Меню приостанавливает таймер. Клик или касание закрывает текущее уведомление; очередь сохраняется. После показа индикаторы возвращаются. Тексты списка и уведомления обновляются при смене языка; список прокручивается.

tests/test_achievements.gd проверяет все включённые пороги, фильтр Ignore, открытие и получение, отсутствие повторных уведомлений, паузу в меню, возврат индикаторов, сохранение прогресса и локализацию. Файл прогресса теста создаётся отдельно в /tmp.

Отчёт о вселенной

ui/report/report.gd форматирует готовое состояние при открытии экрана отчёта и смене языка. Результат хранится в Report/ReportBody. Периодического пересчёта отчёта и новых счётчиков симуляции нет. Доли восьми типов звёзд учитывают звёзды в структурах. Строки количеств используют накопленные stats.total_amount с существующими алиасами; заголовки обозначают суммарное получение, а не исторические максимумы. Эффекты интеграции показывают прирост готовых параметров Federation, Enlightenment и Order относительно начального значения 1. Нулевые строки и пустые разделы скрыты. Количества выводятся полностью, с пробелами между группами по три цифры. Тексты и заголовок поддерживают RU/EN.