Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a7c05e3796 | |||
| 2aced0c61a | |||
| e1782c7b10 | |||
| e41d652631 | |||
| fe68e788b3 | |||
| 3047ef5a85 | |||
| f53181b9ad | |||
| 273093bd9e | |||
| 00b6192d19 | |||
| 02c9843e1b | |||
| 459fbb2a4d | |||
| c42940369a | |||
| 9357f757a9 | |||
| 413f2be904 |
@@ -30,6 +30,9 @@ var Templates: Dictionary = {}
|
||||
var LifeRules: Dictionary = {}
|
||||
var CivilizationRules: Dictionary = {}
|
||||
var ExtraParams: Dictionary = {}
|
||||
var ApocalypseQueue: Dictionary = {}
|
||||
var Achievements: Dictionary = {}
|
||||
|
||||
|
||||
var Music: Dictionary = {}
|
||||
var Effects: Dictionary = {}
|
||||
@@ -92,6 +95,8 @@ func _ready() -> void:
|
||||
OwniverseItems = OwniverseItemLoader.get_data()
|
||||
Templates = OwniverseTemplateLoader.get_data()
|
||||
CivilizationRules = OwniverseCivilizationRuleLoader.get_data()
|
||||
ApocalypseQueue = OwniverseApocalypseQueueLoader.get_data()
|
||||
Achievements = OwniverseAchievementLoader.get_data()
|
||||
|
||||
Music = OwniverseAudioLoader.get_music()
|
||||
Effects = OwniverseAudioLoader.get_effects()
|
||||
|
||||
@@ -250,8 +250,16 @@ static func _validate_payload(data: Variant, speed_limit: int, stages: Array, en
|
||||
# Optional additions to the current format. Saves predating indicators keep
|
||||
# their quantities and receive neutral defaults from Owniverse on load.
|
||||
for field in saved:
|
||||
if (not E.contains(required, field) and not E.contains(["world_parameters", "trends"], field)):
|
||||
if (not E.contains(required, field) and not E.contains(["world_parameters", "trends", "apocalypse"], field)):
|
||||
return "unsupported state structure"
|
||||
if saved.has("apocalypse"):
|
||||
var apocalypse: Variant = saved.apocalypse
|
||||
if not apocalypse is Dictionary: return "invalid state.apocalypse"
|
||||
for key in ["phase", "stage", "tick", "end_ticks"]:
|
||||
if not _number(apocalypse.get(key), true): return E.add("invalid state.apocalypse.", key)
|
||||
for key in ["type", "warning"]:
|
||||
if not _number(apocalypse.get(key), true, false): return E.add("invalid state.apocalypse.", key)
|
||||
if E.lt(apocalypse.phase, 0) or E.gt(apocalypse.phase, 2): return "invalid apocalypse phase"
|
||||
if saved.has("world_parameters"):
|
||||
if ((not _number_map(saved.world_parameters)) or E.ne(saved.world_parameters.size(), WorldParameters.defaults().size())):
|
||||
return "invalid state.world_parameters"
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
class_name OwniverseAchievementLoader
|
||||
|
||||
|
||||
static func get_data() -> Dictionary:
|
||||
return {
|
||||
'en': CSVLoader.load(EN),
|
||||
'ru': CSVLoader.load(RU),
|
||||
}
|
||||
|
||||
#region RU
|
||||
const RU := """
|
||||
id|Category|Points|progr: 60|Ignore|OpenCondition|AchieveCondition|Ru1|Ru2
|
||||
a1|время|1|||age::1E9|age::1E9|Хорошее начало!|Вселенная существует миллиард лет.
|
||||
a2|время|3|||age::1E10|age::1E10|Средний возраст.|Вселенная существует десять миллиардов лет.
|
||||
a3|время|6|||age::1E11|age::1E11|Внушительный срок.|Вселенная существует сто миллиардов лет.
|
||||
a4|время|10|||age::1E12|age::1E12|А не слишком ли это долго?|Вселенная существует триллион лет
|
||||
a5|время|1||1||uptime|Вселенная под вашим управлением 13 минут подряд.|Неплохо для начала!
|
||||
a6|время|3||1||uptime|Вселенная под вашим управлением 34 минуты подряд.|Без вас было бы хуже.
|
||||
a7|время|6||1||uptime|Вселенная под вашим управлением 1 час 29 минут подряд.|Не привлекайте ненужного внимания.
|
||||
a8|время|10||1||uptime|Вселенная под вашим управлением 3 часа 53 минуты подряд!|Можно и отдохнуть, это финальное достижение в данной линейке!
|
||||
a9|время|1||1||played|Вы управляете вселенной 8 часов.|Её обитатели должны быть вам благодарны.
|
||||
a10|время|3||1||played|Вы управляете вселенной 21 час.|Наверняка уже многое стало понятно.
|
||||
a11|время|6||1||played|Вы управляете вселенной 2 суток и 7 часов.|Расскажите об игре друзьям!
|
||||
a12|время|10||1||played|Вы управляете вселенной 6 суток.|Поделитесь опытом в социальных сетях!
|
||||
a13|ресурсы|1||||total:H:1000|Получи всего 1 000 водорода|
|
||||
a14|ресурсы|1|1||total:H:5E4|total:H:1E6|Получи всего 1 миллион водорода|
|
||||
a15|ресурсы|1|1||total:H:5E7|total:H:1E9|Получи всего 1 миллиард водорода|
|
||||
a16|ресурсы|3|1||total:H:5E10|total:H:1E12|Получи всего 1 триллион водорода|
|
||||
a17|ресурсы|3|1||total:H:5E13|total:H:1E15|Получи всего 1 квадриллион водорода|
|
||||
a18|ресурсы|3|1||total:H:5E16|total:H:1E18|Получи всего 1 квинтиллион водорода|
|
||||
a19|ресурсы|6|1||total:H:5E19|total:H:1E21|Получи всего 1 секстиллион водорода|
|
||||
a20|ресурсы|6|1||total:H:5E22|total:H:1E24|Получи всего 1 септиллион водорода|
|
||||
a21|ресурсы|1||||total:S:1000|Создай 1 000 кубических парсек пространства|
|
||||
a22|ресурсы|1|||total:S:5E4|total:S:1E6|Создай 1 миллион кубических парсек пространства|
|
||||
a23|ресурсы|1|||total:S:5E7|total:S:1E9|Создай 1 миллиард кубических парсек пространства|
|
||||
a24|ресурсы|3|||total:S:5E10|total:S:1E12|Создай 1 триллион кубических парсек пространства|
|
||||
a25|ресурсы|3|||total:S:5E13|total:S:1E15|Создай 1 квадриллион кубических парсек пространства|
|
||||
a26|ресурсы|3|||total:S:5E16|total:S:1E18|Создай 1 квинтиллион кубических парсек пространства|
|
||||
a27|ресурсы|6|||total:S:5E19|total:S:1E21|Создай 1 секстиллион кубических парсек пространства|
|
||||
a28|ресурсы|6|||total:S:5E22|total:S:1E24|Создай 1 септиллион кубических парсек пространства|
|
||||
a29|ресурсы|1|||total:He:1E9|total:He:1E9|Получи 1 миллиард гелия|
|
||||
a30|ресурсы|3|||total:He:1E15|total:He:1E15|Получи 1 квадриллион гелия|
|
||||
a31|ресурсы|6|||total:He:1E21|total:He:1E21|Получи 1 секстиллион гелия|
|
||||
a32|ресурсы|1||1|||Израсходуй 1 миллион гелия|
|
||||
a33|ресурсы|3||1|||Израсходуй 1 триллион гелия|
|
||||
a34|ресурсы|6||1|||Израсходуй 1 квинтиллион гелия|
|
||||
a35|ресурсы|1||1|total:Voids:5|total:Voids:25|Сформируй 25 войдов|
|
||||
a36|ресурсы|1||1|total:Voids:25|total:Voids:125|Сформируй 125 войдов|
|
||||
a37|ресурсы|1||1|total:Voids:125|total:Voids:625|Сформируй 625 войдов|
|
||||
a38|ресурсы|3||1|total:Voids:625|total:Voids:3125|Сформируй 3125 войдов|
|
||||
a39|ресурсы|3||1|total:Voids:3125|total:Voids:15625|Сформируй 15625 войдов|
|
||||
a40|ресурсы|6||1|total:Voids:15625|total:Voids:78125|Сформируй 78125 войдов|Продолжай в том же духе, без войдов вселенная обречена!
|
||||
a41|ресурсы|10||1|energy::1E27|energy::1E27|На складе пусто!|Потрачен весь запас энергии.
|
||||
a42|объекты|1||||total:Star:1|Зажги звезду|
|
||||
a43|объекты|1|||total:OD:1|total:OD:1|Зажги оранжевого карлика|
|
||||
a44|объекты|1||||total:YD:1|Зажги жёлтого карлика|
|
||||
a45|объекты|1|||total:WYD:1|total:WYD:1|Зажги бело-жёлтого карлика|
|
||||
a46|объекты|1|||total:WS:1|total:WS:1|Зажги белую звезду|
|
||||
a47|объекты|1||||total:BG:1|Зажги голубого гиганта|
|
||||
a48|объекты|1|1||total:BG:1|total:SG:1|Зажги сверхгиганта|
|
||||
a49|объекты|1|1||total:SG:1|total:HG:1|Зажги гипергиганта|
|
||||
a50|объекты|1||||total:WD:1|Получи белого карлика|
|
||||
a51|объекты|1||||total:NS:1|Получи нейтронную звезду|
|
||||
a52|объекты|1|1||total:NS:1|total:MGT:1|Получи магнетар|
|
||||
a53|объекты|1||||total:BH:1|Получи чёрную дыру|
|
||||
a54|объекты|1|1||total:BH:1|total:SMBH:1|Создай сверхмассивную чёрную дыру|
|
||||
a55|объекты|1||||total:N:1|Создай туманность|
|
||||
a56|объекты|1|1||total:N:1|total:G:1|Создай глобулу|
|
||||
a57|объекты|1|1||total:G:1|total:STN:1|Создай звёздную колыбель|
|
||||
a58|объекты|1|1||total:G:1|total:MC:1|Создай молекулярное облако|
|
||||
a59|объекты|3|1||total:STN:1|total:ASTN:1|Создай активную звёздную колыбель|
|
||||
a60|объекты|3|1||total:MC:100|total:MC:1E4|Создай 10 000 молекулярных облаков|
|
||||
a61|объекты|1|1|1|auto:Star:1|auto:Star:10|Создай 10 звёзд с помощью облаков|
|
||||
a62|объекты|1|1|1|auto:Star:10|auto:Star:1000|Создай 1 000 звёзд с помощью облаков|
|
||||
a63|объекты|3|1|1|auto:Star:1000|auto:Star:1E6|Создай 1 миллион звёзд с помощью облаков|
|
||||
a64|объекты|1|1||total:Star:30|total:Star:300|Зажги 300 звёзд|
|
||||
a65|объекты|1|1||total:OD:1|total:OD:2000|Зажги 2 000 оранжевых карликов|
|
||||
a66|объекты|1|1||total:WYD:1|total:WYD:500|Зажги 500 бело-жёлтых карликов|
|
||||
a67|объекты|1|1||total:BD:1|total:BD:1E6|Зажги 1 миллион бурых карликов|
|
||||
a68|объекты|3|||total:HG:1000|total:HG:1000|Это кому-нибудь нужно?|Зажги 1 000 гипергигантов.
|
||||
a69|объекты|3|1||total:Star:5000|total:Star:1E7|Зажги 10 миллионов звёзд|
|
||||
a70|объекты|3|||total:Star:4E9|total:Star:4E11|Зажги 400 миллиардов звёзд|
|
||||
a71|объекты|3|||total:Star:9E14|total:Star:9E16|Зажги 90 квадриллионов звёзд|
|
||||
a72|объекты|3|1||total:SMBH:1|total:SMBH:1E4|Сбрось 10 000 вещества в сверхмассивные чёрные дыры|
|
||||
a73|объекты|3|||total:SMBH:3E5|total:SMBH:4.2E7|Сбрось 42 миллиона вещества в сверхмассивные чёрные дыры|Примерно столько весит Стрелец А со звёздочкой.
|
||||
a74|объекты|3|||total:SMBH:3E9|total:SMBH:6.6E11|Сбрось 660 миллиардов вещества в сверхмассивные чёрные дыры|Примерно столько весит TON 618.
|
||||
a75|структуры|1||||total:SC:1|Создай любое звёздное скопление|
|
||||
a76|структуры|1||||total:SCM:1|Создай среднее звёздное скопление|
|
||||
a77|структуры|1|1||total:SC:1|total:SCL:1|Создай большое звёздное скопление|
|
||||
a78|структуры|3|1||total:SCL:1|total:SCG:1|Создай гигантское звёздное скопление|
|
||||
a79|структуры|3|1||total:SC:10|total:SC:100|Создай 100 звёздных скоплений|
|
||||
a80|структуры|1||||total:GAL:1|Создай любую галактику|
|
||||
a81|структуры|3|1|1|total:GAL:1|total:GALS:1|Создай спиральную галактику|
|
||||
a82|структуры|1||||total:GEM:1|Создай среднюю галактику|
|
||||
a83|структуры|1|1||total:GAL:1|total:GEL:1|Создай большую галактику|
|
||||
a84|структуры|3|1||total:GEL:1|total:GEG:1|Создай гигантскую галактику|
|
||||
a85|структуры|3|1||total:GAL:5|total:GAL:30|Создай 30 галактик|
|
||||
a86|структуры|1|1||total:GAL:30|total:LGG:1|Создай группу галактик|
|
||||
a87|структуры|3|1||total:LGG:1|total:LGG:30|Создай 30 групп галактик|
|
||||
a88|структуры|3|1||total:LGG:30|total:LSC:1|Создай галактическое сверхскопление|
|
||||
a89|структуры|3|1||total:LSC:1|total:LSC:30|Создай 30 галактических сверхскоплений|
|
||||
a90|структуры|6|1||total:LSC:30|total:LGF:1|Создай галактическую нить|
|
||||
a91|структуры|6|1||total:LGF:1|total:LGF:100|Создай 100 галактических нитей|
|
||||
a92|структуры|6|1||total:LGF:100|total:LGW:1|Создай великую стену|
|
||||
a93|структуры|6|1||total:LGW:1|total:LGW:100|Создай 100 великих стен|
|
||||
a94|структуры|6|1||total:LGW:100|total:LUB:1|Создай вселенский пузырь|
|
||||
a95|структуры|10|1|1|total:LUB:1|total:LUB:1|Это должно было когда-то закончиться!|Создана крупномасштабная структура вселенной.
|
||||
a96|жизнь|1||1||Planet:1|Создай любую планету|
|
||||
a97|жизнь|1|1|1|GG:1^AB:1|RP:1|Создай каменистую планету|
|
||||
a98|жизнь|1|1|1|RP:1|HP:1|Создай обитаемую планету|
|
||||
a99|жизнь|3||1||EON1:1|Создай жизнь|
|
||||
a100|жизнь|1|1|1|EON1:1|EON2:1|Вырасти амёб и водоросли|
|
||||
a101|жизнь|1|1|1|EON2:1|EON3:1|Вырасти трилобитов и рыб|
|
||||
a102|жизнь|1|1|1|EON3:1|EON4:1|Вырасти динозавров и птиц|
|
||||
a103|жизнь|1|1|1|EON4:1|EON5:1|Вырасти млекопитающих и приготовься...|
|
||||
a104|жизнь|1|1|1|Planet:1|Planet:1E5|Создай 100 000 планет|
|
||||
a105|жизнь|1|1|1|HP:1|HP:1E4|Создай 10 000 обитаемых планет|
|
||||
a106|жизнь|3|1|1|EON1:1|EON1:1000|Создай жизнь на 1 000 планет|
|
||||
a107|жизнь|6||1|C0.0:1|C0.0:1|Возник разум!|
|
||||
a108|жизнь|1|1|1|C0.0:1|C0.5:1|Они умеют лепить глиняные горшки!|Цивилизация достигла уровня 0.5.
|
||||
a109|жизнь|3|1|1|C0.7:1|C1.0:1|Они освоили свою родную планету!|Цивилизация достигла уровня 1.0.
|
||||
a110|жизнь|1|1|1|C1.3:1|C1.5:1|Они построили космический город!|Цивилизация достигла уровня 1.5.
|
||||
a111|жизнь|6|1|1|C1.7:1|C2.0:1|У них есть рой Дайсона!|Цивилизация достигла уровня 2.0.
|
||||
a112|жизнь|1||1|C2.2:1|C2.2:1|Вроде бы они начали расти!|Цивилизация заселила сотню звёзд.
|
||||
a113|жизнь|1||1|C2.5:1|C2.5:1|Их почти можно увидеть невооружённым глазом!|Цивилизация заселила среднее звёздное скопление.
|
||||
a114|жизнь|3||1|C2.8:1|C2.8:1|О, они наконец-то поняли, как управлять гравитацией!|Цивилизация достигла уровня 2.8.
|
||||
a115|жизнь|6||1|C3.0:1|C3.0:1|Они и правда научились летать!|Цивилизация способна путешествовать между галактиками.
|
||||
a116|жизнь|3||1|C3.2:1|C3.2:1|Они наверное так гордятся собой!|Цивилизация расселилась на масштабах порядка Туманности Андромеды.
|
||||
a117|жизнь|3||1|C3.5:1|C3.5:1|Вот это они смекалистые!|Цивилизация использует ресурсы нейтронной звезды.
|
||||
a118|жизнь|3||1|C3.8:1|C3.8:1|Теперь их и правда легко заметить.|Цивилизация заселила сотни тысяч галактик.
|
||||
a119|жизнь|6||1|C4.0:1|C4.0:1|Кажется это уже было, нет?|Цивилизация освоила манипуляции со временем.
|
||||
a120|жизнь|6||1|C4.2:1|C4.2:1|Наконец-то они перестали мелочиться.|Цивилизация использует сверхмассивную чёрную дыру для своих целей.
|
||||
a121|жизнь|10||1|C4.4:1|C4.4:1|Они справились! Интересно, чем же дальше займутся?|Цивилизация расселилась на масштабах порядка всей видимой вселенной.
|
||||
a122|жизнь|3|1|1|C1.6:1|SET:1|Они основали колонию у соседней звезды!|
|
||||
a123|жизнь|6|1|1||GSM:1&C2.8:50|Они похоже основали Галактическую Федерацию или что-то в этом роде!|
|
||||
a124|жизнь|1||1|||О нет...|Погибла цивилизация 0.8+ уровня.
|
||||
a125|жизнь|1||1|||Да сколько можно!|Погибло 100 цивилизаций 1.2+ уровня.
|
||||
a126|жизнь|1||1|||К этому вообще можно привыкнуть?|Погибло 1 000 цивилизаций 1.6+ уровня.
|
||||
a127|жизнь|3||1|||Да, в какой-то момент становится безразлично.|Погибло 10 000 цивилизаций 2.0+ уровня
|
||||
a128|жизнь|6||1|||А выглядели крепкими. Что ж поделать.|Погибла цивилизация 3.0+ уровня.
|
||||
a129|жизнь|10||1|||Моё разочарование неизмеримо.|Погибла цивилизация 4.0+ уровня.
|
||||
a130|жизнь|6||1|||Что-то это мне напоминает...|На момент 13.8 миллиардов лет существует цивилизация уровня 0.9.
|
||||
a131|события|3|1|1|высокая плотность|BigCrunch|Большое сжатие!|Вселенная схлопнулась в колоссальную чёрную дыру.
|
||||
a132|события|3|1|1|низкая плотность|BigRip|Большой разрыв!|Вселенная рассеялась в разные стороны.
|
||||
a133|события|6|1|1|BigCrunch^BigRip|BigCrunch&BigRip|Двойная неурядица!|Два разных апокалипсиса завершены.
|
||||
a134|события|10||1|||Изгнание! Вас выгнали из вселенной.|Теперь эта вселенная существует сама по себе, без вашего участия.
|
||||
a135|события|3||1|||Антитеизм: они отрицают ваше существование.|Может они в чём-то и правы?
|
||||
a136|события|3||1|||Интеграция: они стремятся к сосуществованию.|Может быть хотя бы у них получится?
|
||||
a137|события|3||1|||Ксеноцид: их воинственность приводит к ужасным результатам.|Впрочем, ничего нового.
|
||||
a138|события|3||1|||Реконструкция: они переделывают вселенную.|Это же их вселенная, да? Или нет?
|
||||
a139|события|3||1|||Духовность: они ищут глубинный смысл.|Что если они узнают правду?
|
||||
a140|события|3||1|||Исход: они хотят покинуть вселенную.|Что же им здесь не нравится?
|
||||
a141|события|6||1|||Динамичная вселенная|Все тенденции проявили себя!
|
||||
a142|события|3||1|||Изгнание: они пытаются выгнать из вселенной вас!|Их определённо что-то не устраивает.
|
||||
a143|события|3||1|||Федерация: они ищут способы мирного сосуществования.|Наверное это неплохо?
|
||||
a144|события|3||1|||Просвещение: они помогают отстающим.|Какая прелесть!
|
||||
a145|события|3||1|||Новый порядок: они закапывают топор войны.|Может оно и к лучшему?
|
||||
a146|события|3||1|||Великие войны: они устроили эпическое побоище.|Жаль, что отсюда не рассмотреть всех деталей.
|
||||
a147|события|3||1|||Истребление: они занимаются чисткой потенциальных конкурентов.|Жизнь — это возобновляемый ресурс.
|
||||
a148|события|3||1|||Опустошение: они разрушают вселенную!|Звёзды превращаются в чёрные дыры.
|
||||
a149|события|3||1|||Звёздное дробление: они перестраивают вселенную под себя.|Им удобнее жить на малых и стабильных звёздах.
|
||||
a150|события|3||1|||Звёздная накачка: они создают новые звёзды!|Из облаков и бурых карликов создаются красные карлики.
|
||||
a151|события|3||1|||Звёздное охлаждение: они стабилизируют звёзды.|Снижается активность звёздных процессов.
|
||||
a152|события|3||1|||Молитва: они невероятно истово молятся.|Вы даже смогли что-то расслышать.
|
||||
a153|события|3||1|||Диалог: они ждут ответа!|А отвечать именно вам.
|
||||
a154|события|3||1|||Живая вселенная|Произошло 10 действий цивилизаций.
|
||||
a155|события|6||1|||Очень живая вселенная|Произошло 30 действий цивилизаций.
|
||||
a156|события|10||1|||Невероятно живая вселенная!|Произошло 100 действий цивилизаций.
|
||||
a157|события|10||1|||Столько всего происходит!|Все действия произошли хотя бы один раз!
|
||||
a158|силы|1||1|сила получена|сила применена|Научись быстро копать.|Используй лопатин
|
||||
a159|силы|1||1|сила получена|сила применена|Запусти инфляцию.|Используй надувин
|
||||
a160|силы|3||1|сила получена|сила применена|Пропусти время.|Используй временит
|
||||
a161|силы|3||1|сила получена|сила применена|Утилизируй мусор.|Используй столкновит
|
||||
a162|силы|6||1|сила получена|сила применена|Подели физику на ноль.|Используй первекс
|
||||
a163|силы|1||1|сила получена|сила применена|Уничтожь пространство.|Используй сингулярит
|
||||
a164|силы|1||1|сила получена|сила применена|Укрепи структуры.|Используй обновин
|
||||
a165|силы|3||1|сила получена|сила применена|Разожги звёзды.|Используй вливанит
|
||||
a166|силы|3||1|сила получена|сила применена|Остуди вселенную.|Используй морозин
|
||||
a167|силы|6||1|сила получена|сила применена|Избавься от звёзд.|Используй дезвездит
|
||||
a168|силы|1||1|сила получена|сила применена|Сделай больше планет.|Используй камнекс
|
||||
a169|силы|1||1|сила получена|сила применена|Осемени вселенную.|Используй панспермин
|
||||
a170|силы|3||1|сила получена|сила применена|Удобри вселенную.|Используй гайанит
|
||||
a171|силы|3||1|сила получена|сила применена|Взбодри вселенную.|Используй дарвинит
|
||||
a172|силы|6||1|сила получена|сила применена|Наполни вселенную благостью.|Используй эдемекс
|
||||
a173|силы|1||1|сила получена|сила применена|Научи разумных жить в мире.|Используй пацифин
|
||||
a174|силы|1||1|сила получена|сила применена|Научи разумных сражаться за свою жизнь.|Используй озверин
|
||||
a175|силы|3||1|сила получена|сила применена|Назначь пророков среди разумных.|Используй моралит
|
||||
a176|силы|3||1|сила получена|сила применена|Покарай тех разумных, кто не следует твоим планам.|Используй дискриминит
|
||||
a177|силы|6||1|сила получена|сила применена|Подави тех разумных, которые слишком много о себе возомнили.|Используй репрекс
|
||||
a178|силы|6||1|одна любая сила получена в 4 градациях|флэш|Потрать силу одного типа по нарастанию градаций.|Зелёная, синяя, фиолетовая, оранжевая
|
||||
a179|силы|3||1|снижена вручную|Bur=1|Холодная вселенная.|Минимальная горючесть
|
||||
a180|силы|3||1|аналогично|Bur=19|Звёзды зажигают, чтобы они сгорели!|Максимальная горючесть
|
||||
a181|силы|3||1|аналогично|Exp=1|Плотный вакуум.|Минимальная взрывчатость
|
||||
a182|силы|3||1|аналогично|Exp=19|Бада-бим, бада-бум-бум-бум!|Максимальная взрывчатость
|
||||
a183|силы|3||1|аналогично|Agg=1|Мир и покой.|Минимальная агрессия
|
||||
a184|силы|3||1|аналогично|Agg=19|Как 4Е4!|Максимальная агрессия
|
||||
a185|силы|6||1|||Все силы доступны одновременно!|
|
||||
a186|силы|10||1|||Все силы использованы хотя бы по одному разу в этой вселенной.|
|
||||
a187|силы|1||1|3 обычные силы одного вида||Улучшите силу до редкой.|
|
||||
a188|силы|3||1|3 редкие силы одного вида||Улучшите силу до эпической.|
|
||||
a189|силы|6||1|3 эпические силы одного вида||Улучшите силу до легендарной.|
|
||||
a190|силы|1||1|улучш 3 силы|5|Улучшите 5 сил.|
|
||||
a191|силы|1||1|8|13|Улучшите 13 сил.|
|
||||
a192|силы|3||1|21|34|Улучшите 34 силы.|
|
||||
a193|силы|3||1|55|89|Улучшите 89 сил.|
|
||||
a194|силы|6||1|144|233|Улучшите 233 силы.|
|
||||
a195|силы|6||1|377|610|Улучшите 610 сил.|
|
||||
a196|силы|10||1|987|1597|Улучшите 1 597 сил.|Продолжайте в том же духе!
|
||||
a197|силы|1||1|1|3|Улучшите 3 сундука с силами.|
|
||||
a198|силы|3||1|5|7|Улучшите 7 сундуков с силами.|
|
||||
a199|силы|6||1|14|20|Улучшите 20 сундуков с силами.|
|
||||
a200|силы|10||1|38|55|Улучшите 55 сундуков с силами.|Спасибо за поддержку!
|
||||
"""
|
||||
#endregion
|
||||
|
||||
#region EN
|
||||
const EN := """
|
||||
id|Category|Points|progr: 60|Ignore|OpenCondition|AchieveCondition|En1|En2
|
||||
a1|время|1|||age::1E9|age::1E9|A good start!|The Universe exists for a billion years.
|
||||
a2|время|3|||age::1E10|age::1E10|Middle age.|The Universe exists for a ten billions years.
|
||||
a3|время|6|||age::1E11|age::1E11|Impressive term.|The Universe exists for a hundred billions years.
|
||||
a4|время|10|||age::1E12|age::1E12|Isn't it too long?|The Universe exists for a trillion years.
|
||||
a5|время|1||1||uptime|The universe is under your control for 13 minutes in a row.|Not bad for a start!
|
||||
a6|время|3||1||uptime|The universe is under your control for 34 minutes in a row.|It would be worse without you.
|
||||
a7|время|6||1||uptime|The universe is under your control for 1 hour 29 minutes in a row.|Don't draw unnecessary attention.
|
||||
a8|время|10||1||uptime|The universe is under your control for 3 hours 53 minutes in a row!|You can also relax, this is the final achievement in this line!
|
||||
a9|время|1||1||played|You rule the universe for 8 hours.|Its inhabitants should be grateful to you.
|
||||
a10|время|3||1||played|You rule the universe for 21 hours.|Surely a lot has become clear by now.
|
||||
a11|время|6||1||played|You rule the universe for 2 days and 7 hours.|Tell your friends about the game!
|
||||
a12|время|10||1||played|You rule the universe for 6 days.|Share your experience on social networks!
|
||||
a13|ресурсы|1||||total:H:1000|Get total 1 000 hydrogen|
|
||||
a14|ресурсы|1|1||total:H:5E4|total:H:1E6|Get total 1 million hydrogen|
|
||||
a15|ресурсы|1|1||total:H:5E7|total:H:1E9|Get total 1 billion hydrogen|
|
||||
a16|ресурсы|3|1||total:H:5E10|total:H:1E12|Get total 1 trillion hydrogen|
|
||||
a17|ресурсы|3|1||total:H:5E13|total:H:1E15|Get total 1 quadrillion hydrogen|
|
||||
a18|ресурсы|3|1||total:H:5E16|total:H:1E18|Get total 1 quintillion hydrogen|
|
||||
a19|ресурсы|6|1||total:H:5E19|total:H:1E21|Get total 1 sextillion hydrogen|
|
||||
a20|ресурсы|6|1||total:H:5E22|total:H:1E24|Get total 1 septillion hydrogen|
|
||||
a21|ресурсы|1||||total:S:1000|Create 1 000 cubic parsecs of space|
|
||||
a22|ресурсы|1|||total:S:5E4|total:S:1E6|Create 1 million cubic parsecs of space|
|
||||
a23|ресурсы|1|||total:S:5E7|total:S:1E9|Create 1 billion cubic parsecs of space|
|
||||
a24|ресурсы|3|||total:S:5E10|total:S:1E12|Create 1 trillion cubic parsecs of space|
|
||||
a25|ресурсы|3|||total:S:5E13|total:S:1E15|Create 1 quadrillion cubic parsecs of space|
|
||||
a26|ресурсы|3|||total:S:5E16|total:S:1E18|Create 1 quintillion cubic parsecs of space|
|
||||
a27|ресурсы|6|||total:S:5E19|total:S:1E21|Create 1 sextillion cubic parsecs of space|
|
||||
a28|ресурсы|6|||total:S:5E22|total:S:1E24|Create 1 septillion cubic parsecs of space|
|
||||
a29|ресурсы|1|||total:He:1E9|total:He:1E9|Obtain 1 billion helium|
|
||||
a30|ресурсы|3|||total:He:1E15|total:He:1E15|Obtain 1 quadrillion helium|
|
||||
a31|ресурсы|6|||total:He:1E21|total:He:1E21|Obtain 1 sextilion helium|
|
||||
a32|ресурсы|1||1|||Use up 1 million helium|
|
||||
a33|ресурсы|3||1|||Use up 1 trillion helium|
|
||||
a34|ресурсы|6||1|||Use up 1 quintillion helium|
|
||||
a35|ресурсы|1||1|total:Voids:5|total:Voids:25|Form 25 voids|
|
||||
a36|ресурсы|1||1|total:Voids:25|total:Voids:125|Form 125 voids|
|
||||
a37|ресурсы|1||1|total:Voids:125|total:Voids:625|Form 625 voids|
|
||||
a38|ресурсы|3||1|total:Voids:625|total:Voids:3125|Form 3125 voids|
|
||||
a39|ресурсы|3||1|total:Voids:3125|total:Voids:15625|Form 15625 voids|
|
||||
a40|ресурсы|6||1|total:Voids:15625|total:Voids:78125|Form 78125 voids|Keep up the good work, without voids the universe is doomed!
|
||||
a41|ресурсы|10||1|energy::1E27|energy::1E27|Out of ammo!|All energy cache is spent.
|
||||
a42|объекты|1||||total:Star:1|Make a Star|
|
||||
a43|объекты|1|||total:OD:1|total:OD:1|Make an Orange Dwarf|
|
||||
a44|объекты|1||||total:YD:1|Make a Yellow Dwarf|
|
||||
a45|объекты|1|||total:WYD:1|total:WYD:1|Make a White-Yellow Dwarf|
|
||||
a46|объекты|1|||total:WS:1|total:WS:1|Make a White Star|
|
||||
a47|объекты|1||||total:BG:1|Make a Blue Giant|
|
||||
a48|объекты|1|1||total:BG:1|total:SG:1|Make a Supergiant|
|
||||
a49|объекты|1|1||total:SG:1|total:HG:1|Make a Hypergiant|
|
||||
a50|объекты|1||||total:WD:1|Get a White Dwarf|
|
||||
a51|объекты|1||||total:NS:1|Get a Neutron Star|
|
||||
a52|объекты|1|1||total:NS:1|total:MGT:1|Get a Magnetar|
|
||||
a53|объекты|1||||total:BH:1|Get a Black Hole|
|
||||
a54|объекты|1|1||total:BH:1|total:SMBH:1|Make a Supermassive Black Hole|
|
||||
a55|объекты|1||||total:N:1|Make a Nebula|
|
||||
a56|объекты|1|1||total:N:1|total:G:1|Make a Globula|
|
||||
a57|объекты|1|1||total:G:1|total:STN:1|Make a Star Nursery|
|
||||
a58|объекты|1|1||total:G:1|total:MC:1|Make a Molecular Cloud|
|
||||
a59|объекты|3|1||total:STN:1|total:ASTN:1|Make an Active Star Nursery|
|
||||
a60|объекты|3|1||total:MC:100|total:MC:1E4|Make 10 000 Molecular Clouds|
|
||||
a61|объекты|1|1|1|auto:Star:1|auto:Star:10|Make 10 stars via clouds|
|
||||
a62|объекты|1|1|1|auto:Star:10|auto:Star:1000|Make 1 000 stars via clouds|
|
||||
a63|объекты|3|1|1|auto:Star:1000|auto:Star:1E6|Make 1 million stars via clouds|
|
||||
a64|объекты|1|1||total:Star:30|total:Star:300|Make 300 stars|
|
||||
a65|объекты|1|1||total:OD:1|total:OD:2000|Make 2 000 orange dwarfs|
|
||||
a66|объекты|1|1||total:WYD:1|total:WYD:500|Make 500 white-yellow dwarfs|
|
||||
a67|объекты|1|1||total:BD:1|total:BD:1E6|Make 1 million brown dwarfs|
|
||||
a68|объекты|3|||total:HG:1000|total:HG:1000|Someone must need them, of course?|Make 1 000 hypergiants.
|
||||
a69|объекты|3|1||total:Star:5000|total:Star:1E7|Make 10 million stars|
|
||||
a70|объекты|3|||total:Star:4E9|total:Star:4E11|Make 400 billion stars|
|
||||
a71|объекты|3|||total:Star:9E14|total:Star:9E16|Make 90 quadrillion stars|
|
||||
a72|объекты|3|1||total:SMBH:1|total:SMBH:1E4|Drop 10 000 matter into Supermassive Black Holes|
|
||||
a73|объекты|3|||total:SMBH:3E5|total:SMBH:4.2E7|Drop 42 million matter into Supermassive Black Holes|Sagittarius A* weighs about as much.
|
||||
a74|объекты|3|||total:SMBH:3E9|total:SMBH:6.6E11|Drop 660 billion matter into Supermassive Black Holes|TON 618 weighs about as much.
|
||||
a75|структуры|1||||total:SC:1|Make a Star Cluster|
|
||||
a76|структуры|1||||total:SCM:1|Make a Medium Star Cluster|
|
||||
a77|структуры|1|1||total:SC:1|total:SCL:1|Make a Large Star Cluster|
|
||||
a78|структуры|3|1||total:SCL:1|total:SCG:1|Make a Giant Star Cluster|
|
||||
a79|структуры|3|1||total:SC:10|total:SC:100|Make 100 Star Clusters|
|
||||
a80|структуры|1||||total:GAL:1|Make a Galaxy|
|
||||
a81|структуры|3|1|1|total:GAL:1|total:GALS:1|Make a Spiral Galaxy|
|
||||
a82|структуры|1||||total:GEM:1|Make a Medium Galaxy|
|
||||
a83|структуры|1|1||total:GAL:1|total:GEL:1|Make a Large Galaxy|
|
||||
a84|структуры|3|1||total:GEL:1|total:GEG:1|Make a Giant Galaxy|
|
||||
a85|структуры|3|1||total:GAL:5|total:GAL:30|Make 30 Galaxies|
|
||||
a86|структуры|1|1||total:GAL:30|total:LGG:1|Make a Galactic Group|
|
||||
a87|структуры|3|1||total:LGG:1|total:LGG:30|Make 30 Galactic Groups|
|
||||
a88|структуры|3|1||total:LGG:30|total:LSC:1|Make a Galactic Supercluster|
|
||||
a89|структуры|3|1||total:LSC:1|total:LSC:30|Make 30 Galactic Superclusters|
|
||||
a90|структуры|6|1||total:LSC:30|total:LGF:1|Make a Galactic Filament|
|
||||
a91|структуры|6|1||total:LGF:1|total:LGF:100|Make 100 Galactic Filaments|
|
||||
a92|структуры|6|1||total:LGF:100|total:LGW:1|Make a Great Wall|
|
||||
a93|структуры|6|1||total:LGW:1|total:LGW:100|Make 100 Great Walls|
|
||||
a94|структуры|6|1||total:LGW:100|total:LUB:1|Make an Universe Bubble|
|
||||
a95|структуры|10|1|1|total:LUB:1|total:LUB:1|It had to end sometime!|The large-scale structure of the universe has been created.
|
||||
a96|жизнь|1||1||Planet:1|Make a planet|
|
||||
a97|жизнь|1|1|1|GG:1^AB:1|RP:1|Make a rocky planet|
|
||||
a98|жизнь|1|1|1|RP:1|HP:1|Make a habitable planet|
|
||||
a99|жизнь|3||1||EON1:1|Make life|
|
||||
a100|жизнь|1|1|1|EON1:1|EON2:1|Grow amoebas and algae|
|
||||
a101|жизнь|1|1|1|EON2:1|EON3:1|Grow trilobites and fishes|
|
||||
a102|жизнь|1|1|1|EON3:1|EON4:1|Grow dinosaurs and birds|
|
||||
a103|жизнь|1|1|1|EON4:1|EON5:1|Grow mammals and prepare yourself...|
|
||||
a104|жизнь|1|1|1|Planet:1|Planet:1E5|Make 100 000 planets|
|
||||
a105|жизнь|1|1|1|HP:1|HP:1E4|Make 10 000 habitable planets|
|
||||
a106|жизнь|3|1|1|EON1:1|EON1:1000|Make life at 1 000 planets|
|
||||
a107|жизнь|6||1|C0.0:1|C0.0:1|Sapience emerged!|
|
||||
a108|жизнь|1|1|1|C0.0:1|C0.5:1|They can make pots out of clay!|A civilization has reached 0.5 level.
|
||||
a109|жизнь|3|1|1|C0.7:1|C1.0:1|They have mastered their home planet!|A civilization has reached 1.0 level.
|
||||
a110|жизнь|1|1|1|C1.3:1|C1.5:1|They have built a space city!|A civilization has reached 1.5 level.
|
||||
a111|жизнь|6|1|1|C1.7:1|C2.0:1|They have a Dyson Swarm!|A civilization has reached 2.0 level.
|
||||
a112|жизнь|1||1|C2.2:1|C2.2:1|They seem to grow at last!|A civilization has settled over a hundred of stars.
|
||||
a113|жизнь|1||1|C2.5:1|C2.5:1|They are almost noticeable by a naked eye!|A civilization has settled over a Medium Star Cluster.
|
||||
a114|жизнь|3||1|C2.8:1|C2.8:1|Oh, they grasped how to control gravity at last!|A civilization has reached 2.8 level.
|
||||
a115|жизнь|6||1|C3.0:1|C3.0:1|They really have learned to fly!|A civilization is able to travel between Galaxies.
|
||||
a116|жизнь|3||1|C3.2:1|C3.2:1|They must be so proud of themselves!|A civilization has settled on a scale of an Andromeda Galaxy.
|
||||
a117|жизнь|3||1|C3.5:1|C3.5:1|Hey, they are resourceful!|A civilization has utilized a Neutron Star resources.
|
||||
a118|жизнь|3||1|C3.8:1|C3.8:1|Now they actually easy to notice.|A civilization has settled over hundreds of thousands Galaxies.
|
||||
a119|жизнь|6||1|C4.0:1|C4.0:1|Feels like this has happened already, no?|A civilization has grasp on temporal manipulations.
|
||||
a120|жизнь|6||1|C4.2:1|C4.2:1|At last they seem to think big enough.|A civilization has utilized a Supermassive Black Hole for their goals.
|
||||
a121|жизнь|10||1|C4.4:1|C4.4:1|They've made it! What will they do next, I wonder?|A civilization has expanded on a scale of a whole visible Universe.
|
||||
a122|жизнь|3|1|1|C1.6:1|SET:1|They have made an extrasolar colony!|
|
||||
a123|жизнь|6|1|1||GSM:1&C2.8:50|They seem to have found a Galactic Federation or something!|
|
||||
a124|жизнь|1||1|||Oh no...|A civilization of 0.8+ level extinct.
|
||||
a125|жизнь|1||1|||Not again!|100 civilizations of 1.2+ level extinct.
|
||||
a126|жизнь|1||1|||Will I get used to it?|1 000 civilizations of 1.6+ level extinct.
|
||||
a127|жизнь|3||1|||Yeah, you become numb after a while.|10 000 civilizations of 2.0+ level extinct.
|
||||
a128|жизнь|6||1|||They seemed to be survivors. Oh well.|A civilization of 3.0+ level extinct.
|
||||
a129|жизнь|10||1|||My dissapointment is immeasureable.|A civilization of 4.0+ level extinct.
|
||||
a130|жизнь|6||1|||This reminds me of something...|At the date of 13.8 billions of years there is a civilization of 0.9 level.
|
||||
a131|события|3|1|1|высокая плотность|BigCrunch|Big Crunch!|The universe collapsed into a colossal black hole.
|
||||
a132|события|3|1|1|низкая плотность|BigRip|Big Rip!|The universe dissipated in different directions.
|
||||
a133|события|6|1|1|BigCrunch^BigRip|BigCrunch&BigRip|Double Trouble!|Two different apocalypses are finished.
|
||||
a134|события|10||1|||Complete banishment! You have been kicked out of the universe.|Now this universe exists by itself, without your participation.
|
||||
a135|события|3||1|||Antitheism: They deny your existence.|Maybe they are somehow right about that?
|
||||
a136|события|3||1|||Integration: They strive for coexistence.|Maybe at least they can make it?
|
||||
a137|события|3||1|||Xenocide: Their bellicosity leads to dire results.|However, nothing is new.
|
||||
a138|события|3||1|||Reconstruction: They rearrange the universe.|It is their universe, right? Or not?
|
||||
a139|события|3||1|||Spirituality: They seek deep meaning.|What if they find out the truth?
|
||||
a140|события|3||1|||Exodus: They want to leave the universe.|What do they not like here?
|
||||
a141|события|6||1|||Dynamic Universe|All trends have shown themselves!
|
||||
a142|события|3||1|||Banishment: They are trying to expel you from the universe!|They definitely don't like something.
|
||||
a143|события|3||1|||Federation: They are looking for ways to peacefully coexist.|Perhaps this is good?
|
||||
a144|события|3||1|||Enlightenment: They help those who are lagging behind.|How lovely!
|
||||
a145|события|3||1|||New Order: They bury the hatchet.|Maybe it's for the best?
|
||||
a146|события|3||1|||Grand Wars: They are engaged into carnage of epic proportions.|It is a pity that it is impossible to discern all the details from here.
|
||||
a147|события|3||1|||Extermination: They are purging potential competitors.|Life is a renewable resource.
|
||||
a148|события|3||1|||Devastation: They are destroying the universe!|Stars turn into black holes.
|
||||
a149|события|3||1|||Stellar Fragmentation: They reconstruct the universe for themselves.|It is more convenient for them to live on small and stable stars.
|
||||
a150|события|3||1|||Stellar Pumping: They create new stars!|Red dwarfs are created from clouds and brown dwarfs.
|
||||
a151|события|3||1|||Stellar Cooling: They stabilize the stars.|The activity of stellar processes is decreasing.
|
||||
a152|события|3||1|||Prayer: They pray incredibly fervently.|You were even able to hear something.
|
||||
a153|события|3||1|||Dialogue: They are waiting for an answer!|And it is you who should answer.
|
||||
a154|события|3||1|||Living Universe|10 civilization actions have taken place.
|
||||
a155|события|6||1|||Very Living Universe|30 civilization actions have taken place.
|
||||
a156|события|10||1|||Incredibly Alive Universe!|100 civilization actions have taken place.
|
||||
a157|события|10||1|||So much is happening!|All actions happened at least once!
|
||||
a158|силы|1||1|сила получена|сила применена|Learn to dig fast.|Use Shovelin
|
||||
a159|силы|1||1|сила получена|сила применена|Kickstart inflation.|Use Inflatin
|
||||
a160|силы|3||1|сила получена|сила применена|Pass time.|Use Temporite
|
||||
a161|силы|3||1|сила получена|сила применена|Dispose of garbage.|Use Collisite
|
||||
a162|силы|6||1|сила получена|сила применена|Divide physics by zero.|Use Primex
|
||||
a163|силы|1||1|сила получена|сила применена|Destroy space.|Use Singularite
|
||||
a164|силы|1||1|сила получена|сила применена|Strengthen structures.|Use Renewin
|
||||
a165|силы|3||1|сила получена|сила применена|Light up the stars.|Use Infusite
|
||||
a166|силы|3||1|сила получена|сила применена|Cool down the universe.|Use Frostin
|
||||
a167|силы|6||1|сила получена|сила применена|Get rid of the stars.|Use Destellarite
|
||||
a168|силы|1||1|сила получена|сила применена|Make more planets.|Use Rockex
|
||||
a169|силы|1||1|сила получена|сила применена|Seed the universe.|Use Panspermin
|
||||
a170|силы|3||1|сила получена|сила применена|Fertilize the universe.|Use Gaianite
|
||||
a171|силы|3||1|сила получена|сила применена|Invigorate the universe.|Use Darwinite
|
||||
a172|силы|6||1|сила получена|сила применена|Fill the universe with goodness.|Use Edenex
|
||||
a173|силы|1||1|сила получена|сила применена|Teach sapients to live in peace.|Use Pacifin
|
||||
a174|силы|1||1|сила получена|сила применена|Teach sapients to fight for their lives.|Use Enragein
|
||||
a175|силы|3||1|сила получена|сила применена|Appoint prophets among sapients.|Use Moralite
|
||||
a176|силы|3||1|сила получена|сила применена|Punish those sapients who do not follow your plans.|Use Discriminite
|
||||
a177|силы|6||1|сила получена|сила применена|Suppress those sapients who think too much of themselves.|Use Reprex
|
||||
a178|силы|6||1|одна любая сила получена в 4 градациях|флэш|Use the forces of the one kind by increase of power.|Green, blue, purple, orange
|
||||
a179|силы|3||1|снижена вручную|Bur=1|Cold universe.|Minimal Burnability
|
||||
a180|силы|3||1|аналогично|Bur=19|The stars are lit to burn out!|Maximum Burnability
|
||||
a181|силы|3||1|аналогично|Exp=1|Dense vacuum.|Minimal Explosiveness
|
||||
a182|силы|3||1|аналогично|Exp=19|Bada-bing, bada-boom-boom-boom!|Maximum Explosiveness
|
||||
a183|силы|3||1|аналогично|Agg=1|Peace and tranquility.|Minimal Aggression
|
||||
a184|силы|3||1|аналогично|Agg=19|Like 4E4!|Maximum Aggression
|
||||
a185|силы|6||1|||All forces are available at once!|
|
||||
a186|силы|10||1|||All forces are used at least once in this universe.|
|
||||
a187|силы|1||1|3 обычные силы одного вида||Upgrade a force to rare.|
|
||||
a188|силы|3||1|3 редкие силы одного вида||Upgrade a force to epic.|
|
||||
a189|силы|6||1|3 эпические силы одного вида||Upgrade a force to legendary.|
|
||||
a190|силы|1||1|улучш 3 силы|5|Upgrade 5 forces.|
|
||||
a191|силы|1||1|8|13|Upgrade 13 forces.|
|
||||
a192|силы|3||1|21|34|Upgrade 34 forces.|
|
||||
a193|силы|3||1|55|89|Upgrade 89 forces.|
|
||||
a194|силы|6||1|144|233|Upgrade 233 forces.|
|
||||
a195|силы|6||1|377|610|Upgrade 610 forces.|
|
||||
a196|силы|10||1|987|1597|Upgrade 1 597 forces.|Continue in the same spirit!
|
||||
a197|силы|1||1|1|3|Upgrade 3 force chests.|
|
||||
a198|силы|3||1|5|7|Upgrade 7 force chests.|
|
||||
a199|силы|6||1|14|20|Upgrade 20 force chests.|
|
||||
a200|силы|10||1|38|55|Upgrade 55 force chests.|Thanks for support!
|
||||
"""
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://bx5onxr8atifn
|
||||
@@ -1,3 +1,11 @@
|
||||
class_name OwniverseApocalypseQueueLoader
|
||||
|
||||
static func get_data() -> Dictionary:
|
||||
return CSVLoader.load(DATA)
|
||||
|
||||
#region Owniverse Items
|
||||
|
||||
const DATA := """
|
||||
Num|Shortcut|Stage|Screen|Tick|Desc
|
||||
1|LUB|1|3|50|Вселенский пузырь
|
||||
2|LGW|1|3|5|Великая стена
|
||||
@@ -130,3 +138,7 @@ Num|Shortcut|Stage|Screen|Tick|Desc
|
||||
129|H|11|1|5|Водород
|
||||
130|SMBH|12|2|5|СМЧД
|
||||
131|S|12|1|5|Пространство
|
||||
"""
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://8bh387weuy7a
|
||||
@@ -1008,7 +1008,7 @@ Trends.Report.Header|[b]Влияние развитых цивилизаций
|
||||
Int.Fed.Report.Max|[Интеграция] Федерация: [color=#cccc88]+%value0%[/color]. Увеличено число цивилизаций, подверженных воздействию дипломатии.|max||||
|
||||
Int.Enl.Report.Max|[Интеграция] Просвещение: [color=#cccc88]+%value0%[/color]. Ускорен прогресс на примитивных стадиях жизни.|max||||
|
||||
Int.Ord.Report.Max|[Интеграция] Новый порядок: [color=#cccc88]+%value0%[/color]. Оказывается помощь и поддержка цивилизациям, попавшим в кризисные обстоятельства.|max||||
|
||||
Emerged.Report.Header|[b]Максимальное число возникших сущностей[/b]|||||
|
||||
Emerged.Report.Header|[b]Всего возникло сущностей[/b]|||||
|
||||
Ctm.Report.Max|Трансцендентная цивилизация мистиков: [color=#cccc88]%value0%[/color]|max||||
|
||||
Ctb.Report.Max|Трансцендентная цивилизация строителей: [color=#cccc88]%value0%[/color]|max||||
|
||||
Ctd.Report.Max|Трансцендентная цивилизация дипломатов: [color=#cccc88]%value0%[/color]|max||||
|
||||
@@ -1075,7 +1075,7 @@ HP.Report.Max|Обитаемая планета: [color=#cccc88]%value0%[/color]
|
||||
RP.Report.Max|Каменистая планета: [color=#cccc88]%value0%[/color]|max||||
|
||||
AB.Report.Max|Астероиды: [color=#cccc88]%value0%[/color]|max||||
|
||||
GG.Report.Max|Газовый гигант: [color=#cccc88]%value0%[/color]|max||||
|
||||
Produced.Report.Header|[b]Максимальное число созданных объектов[/b]|||||
|
||||
Produced.Report.Header|[b]Всего создано объектов[/b]|||||
|
||||
LUB.Report.Max|Вселенский пузырь: [color=#cccc88]%value0%[/color]|max||||
|
||||
LGW.Report.Max|Великая стена: [color=#cccc88]%value0%[/color]|max||||
|
||||
LGF.Report.Max|Галактическая нить: [color=#cccc88]%value0%[/color]|max||||
|
||||
@@ -1116,7 +1116,7 @@ YD.Report.Max|Жёлтый карлик: [color=#cccc88]%value0%[/color]|max||||
|
||||
OD.Report.Max|Оранжевый карлик: [color=#cccc88]%value0%[/color]|max||||
|
||||
RD.Report.Max|Красный карлик: [color=#cccc88]%value0%[/color]|max||||
|
||||
BD.Report.Max|Бурый карлик: [color=#cccc88]%value0%[/color]|max||||
|
||||
Obtained.Report.Header|[b]Максимум за время существования вселенной[/b]|||||
|
||||
Obtained.Report.Header|[b]Всего получено ресурсов[/b]|||||
|
||||
S.Report.Max|Пространство: [color=#cccc88]%value0%[/color]|max||||
|
||||
H.Report.Max|Водород: [color=#cccc88]%value0%[/color]|max||||
|
||||
He.Report.Max|Гелий: [color=#cccc88]%value0%[/color]|max||||
|
||||
@@ -2199,7 +2199,7 @@ Trends.Header|[b]Influence of advanced civilizations on the universe[/b]|||||
|
||||
Int.Fed.Max|[Integration] Federation: [color=#cccc88]+%value0%[/color]. The number of civilizations affected by diplomacy is increased.|max||||
|
||||
Int.Enl.Max|[Integration] Enlightenment: [color=#cccc88]+%value0%[/color]. The progress during primitive stages of life development is accelerated.|max||||
|
||||
Int.Ord.Max|[Integration] New Order: [color=#cccc88]+%value0%[/color]. Civilizations in crisis obtain aid and support.|max||||
|
||||
Emerged.Header|[b]Maximum amount of emerged entities[/b]|||||
|
||||
Emerged.Header|[b]Total entities emerged[/b]|||||
|
||||
Ctm.Max|Transcendent mystics civilization: [color=#cccc88]%value0%[/color]|max||||
|
||||
Ctb.Max|Transcendent builders civilization: [color=#cccc88]%value0%[/color]|max||||
|
||||
Ctd.Max|Transcendent diplomats civilization: [color=#cccc88]%value0%[/color]|max||||
|
||||
@@ -2266,7 +2266,7 @@ HP.Max|Habitable Planet: [color=#cccc88]%value0%[/color]|max||||
|
||||
RP.Max|Rocky Planet: [color=#cccc88]%value0%[/color]|max||||
|
||||
AB.Max|Asteroids: [color=#cccc88]%value0%[/color]|max||||
|
||||
GG.Max|Gas Giant: [color=#cccc88]%value0%[/color]|max||||
|
||||
Produced.Header|[b]Maximum amount of created objects[/b]|||||
|
||||
Produced.Header|[b]Total objects created[/b]|||||
|
||||
LUB.Max|Universe Bubble: [color=#cccc88]%value0%[/color]|max||||
|
||||
LGW.Max|Great Wall: [color=#cccc88]%value0%[/color]|max||||
|
||||
LGF.Max|Galactic Filament: [color=#cccc88]%value0%[/color]|max||||
|
||||
@@ -2307,7 +2307,7 @@ YD.Max|Yellow Dwarf: [color=#cccc88]%value0%[/color]|max||||
|
||||
OD.Max|Orange Dwarf: [color=#cccc88]%value0%[/color]|max||||
|
||||
RD.Max|Red Dwarf: [color=#cccc88]%value0%[/color]|max||||
|
||||
BD.Max|Brown Dwarf: [color=#cccc88]%value0%[/color]|max||||
|
||||
Obtained.Header|[b]Maximum for all the existence of the universe[/b]|||||
|
||||
Obtained.Header|[b]Total resources obtained[/b]|||||
|
||||
S.Max|Space: [color=#cccc88]%value0%[/color]|max||||
|
||||
H.Max|Hydrogen: [color=#cccc88]%value0%[/color]|max||||
|
||||
He.Max|Helium: [color=#cccc88]%value0%[/color]|max||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter=""
|
||||
export_path="../../../Documents/Owad/Owniverse.I/Builds/owniverse.0.7.0-146.console.exe"
|
||||
export_path="../../../Documents/Owad/Owniverse.I/Builds/owniverse.0.7.0-146.exe"
|
||||
patches=PackedStringArray()
|
||||
patch_delta_encoding=false
|
||||
patch_delta_compression_level_zstd=19
|
||||
|
||||
+70
-7
@@ -46,9 +46,9 @@ float. Весь порядок E27 сохраняет целые единицы.
|
||||
|
||||
### Общие окна рождения и эволюции
|
||||
|
||||
На всех скоростях полный цикл симуляции обрабатывает 0,2 реальной секунды: пять циклов на секунду запрошенного времени. До накопления полного интервала расчёт не запускается. Игровая длительность цикла равна `0.2 × 10^(speed - 1)` лет. При отставании размер шага не увеличивается, долг сохраняется. Незавершённая транзакция из сохранения возобновляется с прежней границей. Кооперативный бюджет кадра — 40 мс; частота отрисовки не определяет частоту полных циклов.
|
||||
На всех скоростях целевая частота запуска — пять полных циклов в секунду. После накопления не менее 0,2 реальной секунды запускается расчёт за весь ожидающий игровой интервал. Время, поступающее во время расчёта, остаётся для следующего цикла. При задержках шаг увеличивается; время не отбрасывается. Смена скорости не сокращает реальный интервал между запусками. Незавершённая транзакция из сохранения возобновляется с прежней границей. Кооперативный бюджет кадра — 40 мс; фактическая частота завершения зависит от стоимости расчёта.
|
||||
|
||||
На скоростях 4–6 укрупнение звёздных партий зависит от размера цикла и прежнего окна рождения. Размер цикла — `0.2 × 10^(speed - 1)` игровых лет: 200, 2000 и 20000 лет. Если прежнее окно `max(lifespan / 40, временное разрешение)` шире цикла, оно сохраняется: долгоживущие звёзды не дробятся на более мелкую сетку. Если оно не шире цикла, новые звёзды группы `Star` (включая красные гиганты) объединяются по общей границе завершения жизни и переходят целиком.
|
||||
На скоростях 4–6 укрупнение звёздных партий зависит от размера цикла и прежнего окна рождения. Номинальная ширина сетки партий — `0.2 × 10^(speed - 1)` игровых лет: 200, 2000 и 20000 лет. Если прежнее окно `max(lifespan / 40, временное разрешение)` шире цикла, оно сохраняется: долгоживущие звёзды не дробятся на более мелкую сетку. Если оно не шире цикла, новые звёзды группы `Star` (включая красные гиганты) объединяются по общей границе завершения жизни и переходят целиком.
|
||||
|
||||
При обработке существующих партий объединяются только две или более партии одного типа, весь оставшийся интервал превращения которых попадает в один и тот же цикл. Широкие партии и партии, пересекающие границу циклов, остаются без изменений. Количества и накопленные остатки складываются точно; объединённая партия переходит целиком на границе цикла. Повторное укрупнение не меняет результат.
|
||||
|
||||
@@ -117,7 +117,7 @@ GI не хранит накопленные счётчики первичной
|
||||
|
||||
Прогресс и деградация округляются вниз с сохранением остатков. Если оба накопленных исхода конкурируют за последнюю планету, распределяются только существующие объекты, а неиспользованный остаток переносится на следующую попытку. Деградация распределяется по весам `fall`; знаковые ошибки распределения также сохраняются.
|
||||
|
||||
Для всех шести стадий базовые доли за попытку — `progress=0.00025`, `degradation=0.0025`. Интервалы HP/EON1/EON2/EON3/EON4/EON5: 5 млн, 10 млн, 10 млн, 1 млн, 1 млн, 500 тыс. лет. Используются постепенные окна GI шириной `span / 40`. Случайные множители Unity не применяются. Агрессия, порядок и просвещение изменяют вероятности по формулам из раздела индикаторов ниже.
|
||||
Базовые доли за попытку: для 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. Для существующих этических цивилизаций пользователь выбрал равномерное распределение между шестью этиками с детерминированным целым остатком.
|
||||
|
||||
@@ -139,10 +139,20 @@ HP деградирует в RP. У эонов возможен откат к п
|
||||
|
||||
Колонизация агрегирует запросы по стадии назначения (на две стадии ниже отправителя), ограничивает их общим запасом 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.
|
||||
|
||||
### Структуры и периоды
|
||||
@@ -167,9 +177,11 @@ Ct не получает часы. Инициализация отсутству
|
||||
- `1e-5 < density < 0.9`: обычная шкала, `ln(density × 1e5) / ln(9e4)`;
|
||||
- `density ≥ 0.9`: шкала сжатия, `(density − 0.9) × 10`.
|
||||
|
||||
В Unity в первой формуле было `−7`; это ошибка, не позволяющая указателю двигаться. Здесь исправлено на `+7`. Переключение шкалы меняет и заголовок/описание индикатора. Само наступление апокалипсиса индикатор не запускает.
|
||||
В 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`. В нормальном диапазоне рамка прозрачна. Она обновляется вместе с индикаторами, не перехватывает нажатия и снимается при восстановлении плотности. Необратимая последовательность апокалипсиса Unity сюда не входит.
|
||||
Общая рамка `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 тиков с нулевой энергией апокалипсис получает фазу завершения, симуляция останавливает время. Фаза, тип события, предупреждение и позиция в очереди сохраняются в состоянии вселенной.
|
||||
|
||||
### Горючесть, взрывчатость и агрессия
|
||||
|
||||
@@ -222,10 +234,61 @@ CSVLoader разбирает строки, колонка `param export` пре
|
||||
Начальная энергия относится к новым вселенным; запас энергии существующей
|
||||
вселенной остаётся частью её сохранения.
|
||||
|
||||
G, C, PI, helium_multiplier, helium_bonus_limit, space_generation, ChatPerTick
|
||||
и параметры ForceBox загружаются, но пока не используются в GI: соответствующие
|
||||
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.
|
||||
|
||||
+64
-6
@@ -7,6 +7,7 @@ const SPEED_TEXTURE_NAME_TEMPLATE = "res://ui/topmenu/time&speed/time%s.png"
|
||||
|
||||
const ItemDescription = preload("res://world/owniverse/item_description.gd")
|
||||
const WorldIndicators = preload("res://ui/indicators/world_indicators.gd")
|
||||
var achievements: Node
|
||||
var world_indicators: RefCounted
|
||||
var indicator_elapsed: float = 0.0
|
||||
var indicator_revision: int = -1
|
||||
@@ -98,7 +99,14 @@ var g_active_items: Dictionary = {}
|
||||
var pending_page: int = -1
|
||||
var active_actions_dirty: bool = true
|
||||
var active_actions_elapsed: float = 0.0
|
||||
var clock_display_elapsed: float = 0.0
|
||||
var interface_sounds_enabled: bool = false
|
||||
var apocalypse_elapsed: float = 0.0
|
||||
var apocalypse_ui_phase: int = -1
|
||||
var apocalypse_screen: int = -1
|
||||
@onready var time_minus_button: TextureButton = $World/TopMenu/TimeMinus
|
||||
@onready var time_plus_button: TextureButton = $World/TopMenu/TimePlus
|
||||
@onready var apocalypse_music: OwniverseMusicPlayer = $"../MusicPlayer"
|
||||
|
||||
var owniverse:Owniverse = Owniverse.new()
|
||||
var owniverse_items: Dictionary[String, OwniverseItem] = {}
|
||||
@@ -273,6 +281,7 @@ func _swipe_to_nearest_page() -> void:
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if E.eq(owniverse.state.get("apocalypse", {}).get("phase", 0), 1): return
|
||||
if event is InputEventScreenDrag:
|
||||
#effects_player.play_effect("swipe")
|
||||
stage_page_scroller.scroll_horizontal = clamp(
|
||||
@@ -298,7 +307,7 @@ func _set_active_page_actions() -> void:
|
||||
#var amount = owniverse.get_available_to_produce(item_id)
|
||||
|
||||
#var amount = owniverse.get_available_to_produce(entity)
|
||||
var amount = owniverse.production.calculate_max_producible(entity)
|
||||
var amount = (0 if E.gt(owniverse.state.get("apocalypse", {}).get("phase", 0), 0) else owniverse.production.calculate_max_producible(entity))
|
||||
#print(item_id, " : ", amount)
|
||||
if item_id == "SMBH":
|
||||
var preview: Callable = func(requested):
|
||||
@@ -315,7 +324,11 @@ func _page_action_pressed(page_id: int, action_amount) -> void:
|
||||
var item_id: String = g_active_items[page_id].item_id
|
||||
|
||||
if E.gt(action_amount, 0):
|
||||
owniverse.request_production(item_id, action_amount)
|
||||
if owniverse.request_production(item_id, action_amount):
|
||||
page_actions[page_id].mark_pending(action_amount)
|
||||
|
||||
func _on_owniverse_production_applied(_item_id: String) -> void:
|
||||
active_actions_dirty = true
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -327,6 +340,10 @@ func _ready() -> void:
|
||||
create_new_universe()
|
||||
load_data()
|
||||
_refresh_universe_ui()
|
||||
achievements = preload("res://ui/achievements/achievement_controller.gd").new()
|
||||
add_child(achievements)
|
||||
achievements.initialize(self)
|
||||
achievements.update(0.0)
|
||||
|
||||
g_counters.swipe.sec = 0.2
|
||||
|
||||
@@ -345,6 +362,7 @@ func _enable_interface_sounds() -> void:
|
||||
func create_new_universe() -> void:
|
||||
var previous: Owniverse = owniverse
|
||||
owniverse = Owniverse.new()
|
||||
owniverse.production_applied.connect(_on_owniverse_production_applied)
|
||||
var inited_result: Dictionary = init_enitities()
|
||||
locked_items = inited_result.locked_items
|
||||
|
||||
@@ -384,7 +402,7 @@ func create_new_universe() -> void:
|
||||
func _refresh_universe_ui() -> void:
|
||||
owniverse.presentation.refresh(owniverse.state, owniverse.catalog)
|
||||
_change_speed(0)
|
||||
set_datetime(owniverse.state.time.year)
|
||||
set_datetime(owniverse.display_clock().year)
|
||||
set_owniverse_item_value()
|
||||
_unlock_items()
|
||||
if world_indicators != null: world_indicators.update(owniverse)
|
||||
@@ -392,6 +410,12 @@ func _refresh_universe_ui() -> void:
|
||||
func _process(delta: float) -> void:
|
||||
|
||||
owniverse.evolute_frame(delta, get_tree())
|
||||
_process_apocalypse(delta)
|
||||
if achievements != null: achievements.update(delta)
|
||||
clock_display_elapsed += delta
|
||||
if clock_display_elapsed >= 0.1:
|
||||
clock_display_elapsed = fmod(clock_display_elapsed, 0.1)
|
||||
set_datetime(owniverse.display_clock().year)
|
||||
indicator_elapsed += delta
|
||||
if indicator_elapsed >= 0.1 and indicator_revision != owniverse.presentation.revision:
|
||||
indicator_elapsed = 0.0
|
||||
@@ -399,9 +423,9 @@ func _process(delta: float) -> void:
|
||||
indicator_revision = owniverse.presentation.revision
|
||||
_update_descriptions(delta)
|
||||
if not owniverse.presentation.pending_amounts.is_empty(): set_owniverse_item_value()
|
||||
if displayed_year == null or E.ne(displayed_year, owniverse.state.time.year):
|
||||
set_datetime(owniverse.state.time.year)
|
||||
displayed_year = owniverse.state.time.year
|
||||
var committed_year: Variant = owniverse.state.time.year
|
||||
if displayed_year == null or E.ne(displayed_year, committed_year):
|
||||
displayed_year = committed_year
|
||||
|
||||
#owniverse.cycle(delta)
|
||||
|
||||
@@ -421,6 +445,7 @@ func _process(delta: float) -> void:
|
||||
|
||||
|
||||
func _change_speed(delta: int) -> void:
|
||||
if E.eq(owniverse.state.get("apocalypse", {}).get("phase", 0), 1) and delta != 0: return
|
||||
var previous_speed: int = owniverse.speed
|
||||
owniverse.speed = owniverse.speed + delta
|
||||
|
||||
@@ -448,6 +473,39 @@ func _change_speed(delta: int) -> void:
|
||||
g_time_plus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % str(speed + 1))
|
||||
|
||||
|
||||
func _process_apocalypse(delta: float) -> void:
|
||||
var apocalypse: Dictionary = owniverse.state.get("apocalypse", {})
|
||||
var phase: int = int(apocalypse.get("phase", 0))
|
||||
if phase != apocalypse_ui_phase:
|
||||
apocalypse_ui_phase = phase
|
||||
if phase == 1:
|
||||
apocalypse_music.play_apocalypse()
|
||||
owniverse.speed = 1
|
||||
_change_speed(0)
|
||||
elif phase == 2:
|
||||
owniverse.speed = 1
|
||||
_change_speed(0)
|
||||
time_minus_button.disabled = phase == 1
|
||||
time_plus_button.disabled = phase == 1
|
||||
active_actions_dirty = true
|
||||
if phase == 1:
|
||||
_set_active_page_actions()
|
||||
if phase != 1:
|
||||
apocalypse_elapsed = 0.0
|
||||
return
|
||||
apocalypse_elapsed += delta
|
||||
if owniverse.frame_busy or owniverse.production_queue_busy:
|
||||
return
|
||||
while apocalypse_elapsed >= OwniverseApocalypseController.TICK_SECONDS and E.eq(owniverse.state.apocalypse.phase, 1):
|
||||
apocalypse_elapsed -= OwniverseApocalypseController.TICK_SECONDS
|
||||
var result: Dictionary = owniverse.apocalypse.process_tick(owniverse.state)
|
||||
if int(result.screen) >= 0 and int(result.screen) != apocalypse_screen:
|
||||
apocalypse_screen = int(result.screen)
|
||||
stage_page_scroller.scroll_horizontal = apocalypse_screen * PAGE_WIDTH
|
||||
if bool(result.finished):
|
||||
_change_speed(0)
|
||||
|
||||
|
||||
func _on_topmenu_button_pressed(screen_name: String) -> void:
|
||||
var current_screen: String = ""
|
||||
for screen_id in screens:
|
||||
|
||||
+23
-9
@@ -238,14 +238,33 @@ godot --path Owniverse.GI --script res://tests/benchmark_civilizations.gd -- ui
|
||||
|
||||
## Расчёт между кадрами
|
||||
|
||||
`test_flow_commit.gd` проверяет консолидированное применение переходов и колонизации: точные квоты получателей, сохранение количества у каждого владельца, профили этик, статистику создания и отсутствие привязок у Ct. Проверки используют несколько владельцев и численность 103, 10^40 + 103 и 10^400 + 103.
|
||||
|
||||
Дополнительно проверяется точное совпадение обновляемых общих количеств с полным пересчётом после переходов, колонизации и каскадной деградации при нехватке ёмкости. Входные снимки остаются неизменными.
|
||||
|
||||
`test_production_transaction.gd` проверяет, что успешное создание обновляет общий снимок, привязка сохраняет общий ресурс, а отклонённая операция сохраняет и состояние, и снимок.
|
||||
|
||||
```sh
|
||||
godot --headless --path Owniverse.GI --script res://tests/test_flow_commit.gd
|
||||
```
|
||||
|
||||
`godot --headless --path Owniverse.GI --script res://tests/test_frame_evolution.gd`
|
||||
|
||||
Stage вызывает `evolute_frame`: транзакция получает бюджет 40 мс и уступает
|
||||
следующему кадру внутри создания партий и обработки стадий. Расчёт остаётся
|
||||
на главном потоке, состояние публикуется только после полного завершения.
|
||||
Одна транзакция обрабатывает 0,2 реальной секунды на любой скорости. Запрошенное время хранится отдельно, поэтому отставание не превращается
|
||||
в один всё более тяжёлый шаг. При предельной нагрузке время симуляции может
|
||||
отставать от запрошенного; это не ограничение FPS интерфейса и не удаление событий.
|
||||
Запрошенные игровые часы и счётчик UI обновляются независимо от тяжёлого расчёта;
|
||||
Stage показывает запрошенное время раз в 0,1 секунды (10 Гц), а опубликованное
|
||||
состояние мира остаётся на последнем завершённом цикле. Расчёт запускается после
|
||||
накопления выбранного интервала и обрабатывает весь ожидающий интервал. Время,
|
||||
поступившее во время расчёта, обрабатывается следующим циклом. Контроллер нагрузки
|
||||
меняет целевой шаг между 0,2, 0,25, 0,333 и 0,5 секунды (5/4/3/2 цикла в секунду):
|
||||
вниз при EMA расчёта выше 90% текущего шага, вверх после 30 циклов ниже 65%.
|
||||
Временное разрешение укрупнения партий масштабируется с шагом, исходная точность
|
||||
сохраняется на 5 Гц. Контроллер не пропускает накопленное игровое время, но 2/с —
|
||||
нижняя целевая частота, не гарантия при перегрузке.
|
||||
|
||||
`benchmark_dynamic_cadence.gd` сравнивает пропускную способность полного цикла на двух диагностических сохранениях. `benchmark_dynamic_cadence_ui.gd` сравнивает 10 секунд работы полной сцены на сохранении со всеми объектами; результаты сохранены в `artifacts/dynamic_cadence_results.json` и `artifacts/dynamic_cadence_ui_results.json`.
|
||||
|
||||
Производство во время транзакции выполняется после её завершения. Запрошенное
|
||||
время, активный интервал, скорость и очередь производства входят в сохранение;
|
||||
@@ -292,12 +311,7 @@ Stage вызывает `evolute_frame`: транзакция получает б
|
||||
1.057. Замер включает сокращение накопленного отставания сохранения;
|
||||
это результат конкретного сценария на Mac, а не гарантия для Android.
|
||||
|
||||
Полный шаг фиксирован: 0,2 реальной секунды на всех скоростях, пять циклов
|
||||
на секунду запрошенного времени. Неполный интервал накапливается. При отставании
|
||||
шаг сохраняется, долг не отбрасывается. Возобновляемая транзакция сохраняет
|
||||
границу из checkpoint. Бюджет кадра остаётся 40 мс. `test_frame_evolution`
|
||||
проверяет расписание всех шести скоростей, сохранение долга и пять циклов
|
||||
на одну секунду.
|
||||
Целевая частота запуска — пять циклов в секунду. Размер шага равен всему накопленному интервалу. Checkpoint сохраняет границу активного расчёта и ожидающее реальное время. Тест проверяет все шесть скоростей, смену скорости, задержку запуска и накопление времени во время расчёта.
|
||||
|
||||
Дипломатический drift использует шесть взвешенных вкладов вместо матрицы
|
||||
пар этик: вес = сумма вкладов + 195 × свой вклад − противоположный вклад.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"date": "2026-10-08",
|
||||
"machine": "Apple M4 Pro",
|
||||
"godot": "4.7.2.stable.official.ed1daf0bf",
|
||||
"renderer": "headless",
|
||||
"delta_seconds": 0.2,
|
||||
"profile_enabled": true,
|
||||
"ui": false,
|
||||
"adaptation": {
|
||||
"controller": "runtime-only exponential moving average of full-cycle CPU time; 0.8 previous + 0.2 latest",
|
||||
"quality_thresholds_ms": {
|
||||
"quality_1": 200,
|
||||
"quality_2": 220,
|
||||
"quality_3": 300,
|
||||
"quality_2_to_3_when_over_ms": 200,
|
||||
"recovery_below_ms": 140,
|
||||
"recovery_cycles": 30
|
||||
},
|
||||
"quality_effects": {
|
||||
"quality_1": "one-transition civilization approximation, at most 3 stellar/life generations per cycle, temporal grouping x2",
|
||||
"quality_2": "same civilization approximation, at most 1 generation per cycle, temporal grouping x4",
|
||||
"quality_3": "same civilization approximation, at most 1 generation per cycle, temporal grouping x8, alternate halves of batch types each cycle"
|
||||
}
|
||||
},
|
||||
"inputs": {
|
||||
"all_objects_speed6.adventure.json": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||
"many_objects_fragmented_stars_cycle.adventure.json": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||
},
|
||||
"runs": [
|
||||
{"speed": 1, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 165.03665, "min_ms": 151.514, "max_ms": 170.171, "cycles_per_second": 6.05926, "game_years": 12, "validation": "OK"},
|
||||
{"speed": 1, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 128.463817, "min_ms": 124.475, "max_ms": 144.05, "cycles_per_second": 7.784293, "game_years": 12, "validation": "OK"},
|
||||
{"speed": 2, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 181.431217, "min_ms": 171.92, "max_ms": 196.005, "cycles_per_second": 5.511731, "game_years": 120, "validation": "OK"},
|
||||
{"speed": 2, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 131.78055, "min_ms": 126.963, "max_ms": 148.01, "cycles_per_second": 7.588373, "game_years": 120, "validation": "OK"},
|
||||
{"speed": 3, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 175.235683, "min_ms": 166.031, "max_ms": 203.456, "cycles_per_second": 5.7066, "game_years": 1200, "validation": "OK"},
|
||||
{"speed": 3, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 145.013367, "min_ms": 135.062, "max_ms": 162.768, "cycles_per_second": 6.895916, "game_years": 1200, "validation": "OK"},
|
||||
{"speed": 4, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 159.705883, "min_ms": 150.404, "max_ms": 221.101, "cycles_per_second": 6.26151, "game_years": 12000, "validation": "OK"},
|
||||
{"speed": 4, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 177.064483, "min_ms": 170.624, "max_ms": 189.729, "cycles_per_second": 5.64766, "game_years": 12000, "validation": "OK"},
|
||||
{"speed": 5, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 174.857517, "min_ms": 158.809, "max_ms": 270.078, "cycles_per_second": 5.718942, "game_years": 120000, "validation": "OK"},
|
||||
{"speed": 5, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 185.601233, "min_ms": 131.409, "max_ms": 273.8, "cycles_per_second": 5.387895, "game_years": 120000, "validation": "OK"},
|
||||
{"speed": 6, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 190.570967, "min_ms": 159.38, "max_ms": 345.149, "cycles_per_second": 5.247389, "game_years": 1200000, "validation": "OK"},
|
||||
{"speed": 6, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 184.426, "min_ms": 149.645, "max_ms": 366.767, "cycles_per_second": 5.422229, "game_years": 1200000, "validation": "OK"}
|
||||
],
|
||||
"notes": [
|
||||
"Each run begins in exact mode and adapts during its 60-cycle series.",
|
||||
"The mean meets 5 complete cycles/s at all six speeds in these synthetic diagnostic saves on this computer; maxima show occasional cycles still exceed 200 ms at speeds 3-6.",
|
||||
"No Android device or UI performance was measured.",
|
||||
"Diagnostic save inputs were not modified."
|
||||
]
|
||||
}
|
||||
@@ -379,3 +379,140 @@ godot --headless --path . --script res://tests/benchmark_large_star_cohorts.gd -
|
||||
```sh
|
||||
godot --headless --path . --script res://tests/benchmark_many_objects.gd -- cycles=40 repeats=2
|
||||
```
|
||||
|
||||
## Консолидация применения потоков и колонизации — 2026-10-08
|
||||
|
||||
Первый этап: исходное население распределяется по строкам владельцев одной таблицы переходов. Свободные стадии применяют готовые целые квоты напрямую. При колонизации списания планет объединяются по владельцам; каждая стадия колоний получает одну суммарную выдачу. Общие целые количества и статистика сохранены. Изменён порядок распределения единичных остатков между владельцами; полного совпадения состояния с прежним порядком округления не заявляется. Ct остаётся свободной от привязок.
|
||||
|
||||
До и после загружались одни и те же диагностические файлы с теми же количествами и партиями. Скорость 6, полный интервал 0,2 секунды (20 000 игровых лет), CPU-профилирование включено, UI отсутствует. По два прогона из исходного состояния, каждый по 20 циклов: до → после → после → до. На каждую модель и сохранение обработано 800 000 игровых лет. Валидация начальных и конечных состояний прошла; SHA-256 входных файлов до и после совпали. Сохранение игрока не использовалось.
|
||||
|
||||
| Сохранение | До, мс | После, мс | Сокращение времени | Полных циклов/с после |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Все объекты | 385,263 | 377,222 | 2,09% | 2,651 |
|
||||
| Много объектов и партий | 506,888 | 443,303 | 12,54% | 2,256 |
|
||||
|
||||
Для тяжёлого сохранения:
|
||||
|
||||
| Участок | До, мс/цикл | После, мс/цикл |
|
||||
|---|---:|---:|
|
||||
| Применение переходов | 113,011 | 91,591 |
|
||||
| Колонизация | 53,487 | 11,520 |
|
||||
| Подготовка потоков | 114,808 | 114,742 |
|
||||
| Поколения звёзд и жизни | 116,085 | 115,999 |
|
||||
|
||||
Первый этап сокращает полный тяжёлый цикл на 12.54%, но цель пяти циклов в секунду не достигнута. До 200 мс нужно ещё примерно 2.22-кратное ускорение. Следующие крупные затраты — подготовка цивилизационных потоков, поколения и оставшееся применение переходов.
|
||||
|
||||
Проверки: целочисленные квоты и владельцы, включая 10^400 + 103 — 1068; цивилизационные потоки — 1895; состояние цивилизаций — 70; кадровый планировщик — 38. Всего 3071 проверка без ошибок. Проверены сохранение населения каждого владельца при планетных переходах, точные поступления по стадиям, замена планет колониями, статистика и Ct.
|
||||
|
||||
Полные профили, отдельные прогоны и контрольные суммы: `flow_commit_step1_results.json`. Базовая версия: `413f2be9044090c878c2909dd9d0646f77513a5f`.
|
||||
|
||||
Для замера текущей версии на тех же входных файлах:
|
||||
|
||||
```sh
|
||||
godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -- res://tests/artifacts/all_objects_speed6.adventure.json 0.2 profile cycles=20
|
||||
godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -- res://tests/artifacts/many_objects_fragmented_stars_cycle.adventure.json 0.2 profile cycles=20
|
||||
```
|
||||
|
||||
## Повторное использование общих количеств — 2026-10-08
|
||||
|
||||
Второй этап: общий точный снимок передаётся между фазами цивилизационных потоков, колонизации и коррекции ёмкости. Переходы и выдачи обновляют только затронутые количества; перенос между владельцами сохраняет общую численность. Периоды стадий, дрейф и поклонение используют актуальные количества этого интервала. Автоматическое создание разных типов звёзд также использует общий снимок: успешная пробная операция вычитает расходы, отказ сохраняет исходный снимок. При внешних изменениях используется обычный пересчёт. Снимки не записываются в сохранения.
|
||||
|
||||
Сравнение с версией после первого этапа. На двух одинаковых диагностических сохранениях выполнены пары до → после → после → до: по 20 циклов на прогон, по 40 циклов на модель и сохранение. Скорость 6, интервал 0,2 секунды, CPU-профиль включён, UI отсутствует. Каждый прогон полностью обработал 400 000 игровых лет. Состояния валидны, входные SHA-256 не изменились.
|
||||
|
||||
| Сохранение | До, мс | После, мс | Сокращение | Полных циклов/с после |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Все объекты | 377,185 | 367,326 | 2,61% | 2,722 |
|
||||
| Много объектов и партий | 443,163 | 422,973 | 4,56% | 2,364 |
|
||||
|
||||
Тяжёлое сохранение:
|
||||
|
||||
| Метрика | До | После |
|
||||
|---|---:|---:|
|
||||
| Полных сборок общих количеств за цикл | 15 | 7 |
|
||||
| Сборка общих количеств, мс/цикл | 37,498 | 17,767 |
|
||||
| Производство, мс/цикл | 37,554 | 20,316 |
|
||||
| Применение потоков, мс/цикл | 91,626 | 89,259 |
|
||||
| Завершение потоков, мс/цикл | 9,821 | 6,881 |
|
||||
|
||||
Время сборки общих количеств вложено в остальные фазы и не прибавляется к ним повторно. Проверка цивилизационной части отдельно дала небольшой выигрыш; основную экономию обеспечивает исключение повторных сборок при автоматическом создании звёзд.
|
||||
|
||||
Полные состояния обеих версий совпали после 10 последовательных сравнений: по пять на каждое сохранение, интервалы 0,2 / 0,53 / 0,31 / 0,2 / 0,2 секунды. Дополнительные проверки сравнивают снимки с полным пересчётом, контролируют неизменность входных снимков, отказ производства и каскадную деградацию. Выполнено 4321 целевая проверка без ошибок; полная регрессия всех наборов не запускалась.
|
||||
|
||||
Пять полных циклов в секунду пока не достигнуты: тяжёлый цикл составляет 423.0 мс; до 200 мс нужно ещё около 2.11-кратного ускорения. Следующая крупная статья — подготовка потоков цивилизаций, около 115.2 мс/цикл.
|
||||
|
||||
Полные результаты и контрольные суммы: `physical_totals_step2_results.json`. Базовая версия: `9357f757a95db96274d4e115c7e498b58eca6902`. Сохранение игрока не использовалось.
|
||||
|
||||
## Упрощение расчёта наград потоков цивилизаций — 2026-10-08
|
||||
|
||||
Третий этап убирает промежуточное динамическое число при умножении точного целого населения на float-ставку награды. Частное и дробный остаток вычисляются напрямую, а дробный перенос сохраняет прежнее округление. Это применяется к наградам войны, урона и колонизации; размеры населения остаются точными целыми.
|
||||
|
||||
Сравнение с коммитом `c429403` (после второго этапа): те же два диагностических сохранения, скорость 6, интервал 0,2 секунды, CPU-профиль включён. Порядок пар — до → после → после → до; два прогона по 20 циклов на версию и сохранение. Каждый прогон обработал 400 000 игровых лет, валидация сохранений прошла, SHA-256 входов совпали. Сохранение игрока не использовалось.
|
||||
|
||||
| Сохранение | До, мс | После, мс | Сокращение времени | Полных циклов/с после |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Все объекты | 380,579 | 357,026 | 6,19% | 2,801 |
|
||||
| Много объектов и партий | 432,912 | 409,268 | 5,46% | 2,443 |
|
||||
|
||||
Подготовка потоков уменьшилась с 111,980 до 87,832 мс на сохранении со всеми объектами и со 117,004 до 92,100 мс на тяжёлом сохранении — примерно на 21% в обоих случаях. Остальные основные участки остались в пределах погрешности замера.
|
||||
|
||||
Полные состояния до и после совпали после пяти последовательных интервалов на каждом сохранении (включая дробные интервалы). Тест оптимизаций прошёл: 1889 проверок без ошибок. Цель пяти полных циклов в секунду ещё не достигнута: тяжёлому циклу требуется ускорение примерно в 2,05 раза, чтобы уложиться в 200 мс.
|
||||
|
||||
Полные профили и SHA-256 входов: `civilization_flow_prepare_step3_results.json`.
|
||||
|
||||
## Проверка оптимизации частичного изъятия партий — 2026-10-08
|
||||
|
||||
Перенос связанного содержимого уже агрегируется по типам внутри поколения. Проверен вариант частичного изъятия, исключающий промежуточное сложение квоты с дробным переносом и последующее вычитание целой части. Возрастные интервалы и порядок поколений сохранялись.
|
||||
|
||||
Парный замер на тех же сохранениях: скорость 6, интервал 0,2 секунды, CPU-профиль, без UI; до → после → после → до, по 20 циклов на прогон (40 циклов на версию и сохранение).
|
||||
|
||||
| Сохранение | До, мс/цикл | После, мс/цикл |
|
||||
|---|---:|---:|
|
||||
| Все объекты | 350,516 | 349,835 |
|
||||
| Много объектов и партий | 404,255 | 404,390 |
|
||||
|
||||
Изъятие партий на тяжёлом сохранении: 40,919 → 40,837 мс/цикл. Изменения полного цикла меньше 0,2%; подтверждённого выигрыша нет. Вариант **откачен**, код симуляции оставлен прежним. Полные состояния совпали в 10 последовательных сравнениях; границы партий — 117 проверок без ошибок. Входные файлы не изменены.
|
||||
|
||||
Результаты отклонённого варианта: `batch_withdrawal_step4_results.json`. Этот замер не доказывает достижение пяти полных циклов в секунду.
|
||||
|
||||
## Адаптивное упрощение симуляции — 2026-10-08
|
||||
|
||||
Добавлен runtime-контроллер качества, который измеряет сглаженное CPU-время полного цикла. Он повышает уровень упрощения при превышении бюджета 200 мс и снимает его после 30 циклов с запасом. Режим не сохраняется в файл: после загрузки точность постепенно подстраивается заново.
|
||||
|
||||
При нагрузке цивилизационный поток заменяется приближением не более чем с одним переходом на стадию за интервал. На наиболее грубом уровне типы возрастных партий делятся на две чередующиеся группы: каждая обрабатывается раз в два цикла. Временные группы при этом расширяются. Это сохраняет количества, но сдвигает сроки отдельных переходов и исключает несколько последовательных переходов цивилизации за один интервал.
|
||||
|
||||
Проверены два синтетических сохранения на Apple M4 Pro, Godot 4.7.2, headless, с CPU-профилем и интервалом 0,2 секунды. Каждый замер — 60 последовательных полных циклов; каждый запуск начинается в точном режиме и адаптируется по ходу серии.
|
||||
|
||||
| Скорость | Все объекты, мс/цикл (циклов/с) | Много объектов и партий, мс/цикл (циклов/с) |
|
||||
|---:|---:|---:|
|
||||
| 1 | 165,0 (6,06) | 128,5 (7,78) |
|
||||
| 2 | 181,4 (5,51) | 131,8 (7,59) |
|
||||
| 3 | 175,2 (5,71) | 145,0 (6,90) |
|
||||
| 4 | 159,7 (6,26) | 177,1 (5,65) |
|
||||
| 5 | 174,9 (5,72) | 185,6 (5,39) |
|
||||
| 6 | 190,6 (5,25) | 184,4 (5,42) |
|
||||
|
||||
Среднее время на всех прогонах ниже 200 мс. Отдельные циклы всё ещё бывают длиннее: максимумы составили 203–367 мс на скоростях 3–6, включая первые циклы до срабатывания адаптации. Значит, замеры подтверждают требуемую среднюю пропускную способность на этом компьютере, но не гарантируют отсутствие кратковременных задержек. Android и работа с UI отдельно не проверялись.
|
||||
|
||||
Проверки: адаптивное качество — 18, хронология поколений — 28, границы партий — 117, оптимизации симуляции — 1889, планировщик кадров — 39; ошибок нет. Все 12 конечных диагностических состояний прошли валидацию. Хеши входных файлов: `44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67` и `1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a`.
|
||||
|
||||
Детали уровней, минимумы/максимумы и воспроизводимые результаты: `adaptive_quality_results.json`. Штатный benchmark принимает параметр `speed=1..6`.
|
||||
|
||||
## Адаптивная частота циклов 5 → 4 → 3 — 2026-10-17
|
||||
|
||||
Частота запуска полного цикла теперь снижается при устойчивой нагрузке: пороги шага — 0,2, 0,25 и 0,333 секунды. EMA времени расчёта переключает частоту вниз, когда цикл занимает больше 90% текущего интервала; возврат на одну ступень происходит после 30 циклов быстрее 65% интервала. Уровень частоты временный и после загрузки начинается с 5 Гц. Игровые часы продолжают накапливать весь прошедший интервал. Временное объединение партий масштабируется с выбранным интервалом, при этом при 5 Гц прежняя точность остаётся прежней.
|
||||
|
||||
Проверка сцены на диагностическом сохранении `all_objects_speed6.adventure.json`, скорость 6, Godot 4.7.2, Mac16,8. Сравнивались фиксированные 5 Гц и адаптивные 5/4/3 Гц, два прогона каждого режима в чередующемся порядке, по 10 секунд реального времени. Последняя транзакция каждого прогона завершалась до снятия результата.
|
||||
|
||||
| Метрика | Фиксированные 5 Гц | Адаптивно, установилось 3 Гц | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| Завершённые циклы/с | 4,64 | 2,96 | −36% |
|
||||
| Средний FPS сцены | 22,60 | 64,35 | ×2,85 |
|
||||
| p95 времени кадра | 115,6 мс | 66,5 мс | −42% |
|
||||
| Средний максимум кадра | 227 мс | 205 мс | −10% |
|
||||
| Опубликованные игровые годы/с | 99 017 | 98 049 | −1,0% |
|
||||
|
||||
На этих замерах реальное игровое время почти не замедлилось: симуляция завершала около трёх более крупных циклов вместо примерно 4,6 меньших. Средний FPS заметно вырос, но отдельные кадры всё ещё превышали 200 мс; это не гарантия отсутствия коротких задержек. UI-замер выполнен в headless-сцене на компьютере, поэтому его FPS не переносится напрямую на телефон.
|
||||
|
||||
Отдельно прогнан синхронный benchmark полного CPU-цикла на тех же двух тяжёлых сохранениях (скорость 6, 30 циклов × 2 повтора). При фиксированном шаге 0,2 секунды и адаптивном шаге итоговая обработка игрового времени за CPU-секунду выросла с 1,099 до 1,656 на сохранении со всеми объектами (+50,7%) и с 1,036 до 1,697 на сохранении со множеством объектов и партий (+63,9%). В каждом адаптивном прогоне частоты установились на 3 Гц; интервалы игровых часов сверены с запрошенными, все конечные состояния прошли проверку сохранения. Синхронный показатель показывает пропускную способность расчёта, а UI-прогон — фактический темп транзакций сцены.
|
||||
|
||||
Повторяемые результаты: `dynamic_cadence_results.json` (CPU-прогоны) и `dynamic_cadence_ui_results.json` (сцена и UI). Запуски выполняются скриптами `benchmark_dynamic_cadence.gd` и `benchmark_dynamic_cadence_ui.gd`; исходные диагностические сохранения не изменялись.
|
||||
|
||||
@@ -0,0 +1,646 @@
|
||||
{
|
||||
"retained": false,
|
||||
"conclusion": "No measurable full-cycle improvement; implementation reverted",
|
||||
"delta_seconds": 0.2,
|
||||
"exact_state_differential": true,
|
||||
"input_sha256": {
|
||||
"all_objects_speed6": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||
"many_objects_fragmented_stars_cycle": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||
},
|
||||
"order": "before-after-after-before",
|
||||
"runs": [
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 382.054,
|
||||
"mean_ms": 347.474,
|
||||
"min_ms": 291.952,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23851,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
121403,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
52415,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
135271,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
880672,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1718363,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61955,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
673740,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
316094,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
190666,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
545822,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2727124,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113986,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
121039,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
285729,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
373910,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
358046,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 382.156,
|
||||
"mean_ms": 349.53305,
|
||||
"min_ms": 297.266,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23877,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
121854,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
53021,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
137725,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
883114,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1733356,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62368,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
680005,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
319290,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
192497,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
548252,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2737148,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
115930,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
121459,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
287621,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
376359,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
359971,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 387.438,
|
||||
"mean_ms": 350.1367,
|
||||
"min_ms": 298.254,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
24181,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
122008,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
53315,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
137169,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
885851,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1736978,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62264,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
680485,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
319558,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
193735,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
547473,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2743162,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
115720,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
121401,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
287832,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
376295,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
360758,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 391.136,
|
||||
"mean_ms": 353.55755,
|
||||
"min_ms": 295.752,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23999,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
125136,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
53681,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
137614,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
893234,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1749507,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63052,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
683642,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
319168,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
193901,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
550377,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2768999,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
115976,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
122734,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
292064,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
382112,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
363057,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 418.952,
|
||||
"mean_ms": 402.88125,
|
||||
"min_ms": 362.784,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18182,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
138911,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
235353,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
139454,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1808094,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1823967,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63710,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
684725,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
241433,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
79394,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
814774,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2390414,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
142892,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
55241,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
359891,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
410388,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
362721,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 425.202,
|
||||
"mean_ms": 404.3497,
|
||||
"min_ms": 355.885,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18056,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
139522,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
236599,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
140701,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1809113,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1824567,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63452,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
684152,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
242790,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
80500,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
817704,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2406807,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
143427,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
55885,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
363175,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
412428,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
365215,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 418.858,
|
||||
"mean_ms": 404.4306,
|
||||
"min_ms": 357.106,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18133,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
139021,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
237100,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
139480,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1815717,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1825515,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63298,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
685950,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
242932,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
79477,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
815781,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2400821,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
144101,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
56140,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
362394,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
412986,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
364511,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 422.492,
|
||||
"mean_ms": 405.6284,
|
||||
"min_ms": 355.699,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18208,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
139305,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
236973,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
141048,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1818465,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1832211,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63519,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
688184,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
243601,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
79839,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
822003,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2410757,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
144424,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
55500,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
363454,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
412711,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
367008,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
}
|
||||
],
|
||||
"speed": 6
|
||||
}
|
||||
@@ -0,0 +1,645 @@
|
||||
{
|
||||
"reference_commit": "c429403",
|
||||
"delta_seconds": 0.2,
|
||||
"exact_state_differential": true,
|
||||
"input_sha256": {
|
||||
"all_objects_speed6": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||
"many_objects_fragmented_stars_cycle": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||
},
|
||||
"order": "before-after-after-before",
|
||||
"runs": [
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 417.124,
|
||||
"mean_ms": 381.20745,
|
||||
"min_ms": 328.622,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
24482,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
127513,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
54148,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
140918,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
905665,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2240897,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63936,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
685115,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
323853,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
195550,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
565054,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2802836,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
117049,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
123941,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
294599,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
385593,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
363466,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 390.389,
|
||||
"mean_ms": 357.03875,
|
||||
"min_ms": 302.117,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
24490,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
125647,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
54325,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
141339,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
902399,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1755403,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63055,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
683933,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
324284,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
197296,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
562515,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2804447,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
117904,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
123892,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
296839,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
389361,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
364246,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 390.735,
|
||||
"mean_ms": 357.0124,
|
||||
"min_ms": 304.896,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
24460,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
125447,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
54216,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
139778,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
906760,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1757858,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63411,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
687170,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
325360,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
196450,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
558924,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2799057,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
117825,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
123788,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
295498,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
385114,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
367268,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 410.865,
|
||||
"mean_ms": 379.9508,
|
||||
"min_ms": 328.074,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
24504,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
125523,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
54072,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
138884,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
900719,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2238292,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63477,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
687863,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
323929,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
196146,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
557728,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2793469,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
117168,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
123830,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
294159,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
382811,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
366676,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 454.17,
|
||||
"mean_ms": 431.32255,
|
||||
"min_ms": 380.165,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18277,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
138886,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
239194,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
140538,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1823769,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2332907,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
63920,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
689691,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
244580,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
80081,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
823351,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2417052,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
145160,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
55841,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
364671,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
414028,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
365376,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 437.315,
|
||||
"mean_ms": 409.44945,
|
||||
"min_ms": 360.224,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18338,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
139951,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
238906,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
141435,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1834486,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1841420,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
64315,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
691338,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
245861,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
79738,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
825947,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2423500,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
145491,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
56085,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
371874,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
441338,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
366919,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 424.959,
|
||||
"mean_ms": 409.08685,
|
||||
"min_ms": 359.281,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18404,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
140108,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
239591,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
141361,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1836691,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
1842577,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
64001,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
692117,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
245631,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
80486,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
829783,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2438245,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
145228,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
56248,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
369443,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
416359,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
367683,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 452.639,
|
||||
"mean_ms": 434.50095,
|
||||
"min_ms": 387.05,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18271,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
140778,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
239800,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
141233,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1836252,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2347237,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
64454,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
693176,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
244735,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
80563,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
830888,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2436028,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
145261,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
56321,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
369324,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
417541,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
369444,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle"
|
||||
}
|
||||
],
|
||||
"speed": 6
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
{
|
||||
"cycles_per_run": 30,
|
||||
"godot": {
|
||||
"build": "official",
|
||||
"hash": "ed1daf0bf001b61586d9930840f2f1394092c079",
|
||||
"hex": 263938,
|
||||
"major": 4,
|
||||
"minor": 7,
|
||||
"patch": 2,
|
||||
"status": "stable",
|
||||
"string": "4.7.2-stable (official)",
|
||||
"timestamp": 0
|
||||
},
|
||||
"input_saves_unchanged": true,
|
||||
"machine": "Mac16,8",
|
||||
"repeats": 2,
|
||||
"results": [
|
||||
{
|
||||
"applied_game_years": "600000",
|
||||
"cadence_steps": {
|
||||
"0.2": 30
|
||||
},
|
||||
"cpu_cycles_per_second": 5.5818824005294605,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 5.0,
|
||||
"max_ms": 351.156,
|
||||
"mean_ms_per_cycle": 179.15103333333334,
|
||||
"mode": "fixed_5",
|
||||
"p95_ms": 188.985,
|
||||
"repeat": 0,
|
||||
"requested_simulation_seconds": 6.000000000000003,
|
||||
"save": "all_objects_speed6.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.1163764801058926
|
||||
},
|
||||
{
|
||||
"applied_game_years": "978333",
|
||||
"cadence_steps": {
|
||||
"0.2": 1,
|
||||
"0.25": 1,
|
||||
"0.33333333333333": 28
|
||||
},
|
||||
"cpu_cycles_per_second": 5.126867760649103,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 3.0,
|
||||
"max_ms": 349.806,
|
||||
"mean_ms_per_cycle": 195.05086666666668,
|
||||
"mode": "adaptive_5_4_3",
|
||||
"p95_ms": 214.441,
|
||||
"repeat": 0,
|
||||
"requested_simulation_seconds": 9.783333333333333,
|
||||
"save": "all_objects_speed6.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.671928541945013
|
||||
},
|
||||
{
|
||||
"applied_game_years": "978333",
|
||||
"cadence_steps": {
|
||||
"0.2": 1,
|
||||
"0.25": 1,
|
||||
"0.33333333333333": 28
|
||||
},
|
||||
"cpu_cycles_per_second": 5.029407785556781,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 3.0,
|
||||
"max_ms": 340.747,
|
||||
"mean_ms_per_cycle": 198.83056666666667,
|
||||
"mode": "adaptive_5_4_3",
|
||||
"p95_ms": 233.775,
|
||||
"repeat": 1,
|
||||
"requested_simulation_seconds": 9.783333333333333,
|
||||
"save": "all_objects_speed6.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.6401457611787948
|
||||
},
|
||||
{
|
||||
"applied_game_years": "600000",
|
||||
"cadence_steps": {
|
||||
"0.2": 30
|
||||
},
|
||||
"cpu_cycles_per_second": 5.403485464263869,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 5.0,
|
||||
"max_ms": 351.698,
|
||||
"mean_ms_per_cycle": 185.06573333333333,
|
||||
"mode": "fixed_5",
|
||||
"p95_ms": 195.396,
|
||||
"repeat": 1,
|
||||
"requested_simulation_seconds": 6.000000000000003,
|
||||
"save": "all_objects_speed6.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.0806970928527742
|
||||
},
|
||||
{
|
||||
"applied_game_years": "600000",
|
||||
"cadence_steps": {
|
||||
"0.2": 30
|
||||
},
|
||||
"cpu_cycles_per_second": 5.218331511555821,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 5.0,
|
||||
"max_ms": 373.477,
|
||||
"mean_ms_per_cycle": 191.63213333333334,
|
||||
"mode": "fixed_5",
|
||||
"p95_ms": 250.5,
|
||||
"repeat": 0,
|
||||
"requested_simulation_seconds": 6.000000000000003,
|
||||
"save": "many_objects_fragmented_stars_cycle.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.0436663023111648
|
||||
},
|
||||
{
|
||||
"applied_game_years": "978333",
|
||||
"cadence_steps": {
|
||||
"0.2": 1,
|
||||
"0.25": 1,
|
||||
"0.33333333333333": 28
|
||||
},
|
||||
"cpu_cycles_per_second": 5.21296979935453,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 3.0,
|
||||
"max_ms": 373.168,
|
||||
"mean_ms_per_cycle": 191.82923333333332,
|
||||
"mode": "adaptive_5_4_3",
|
||||
"p95_ms": 258.047,
|
||||
"repeat": 0,
|
||||
"requested_simulation_seconds": 9.783333333333333,
|
||||
"save": "many_objects_fragmented_stars_cycle.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.7000073734561718
|
||||
},
|
||||
{
|
||||
"applied_game_years": "978333",
|
||||
"cadence_steps": {
|
||||
"0.2": 1,
|
||||
"0.25": 1,
|
||||
"0.33333333333333": 28
|
||||
},
|
||||
"cpu_cycles_per_second": 5.195109254879333,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 3.0,
|
||||
"max_ms": 372.878,
|
||||
"mean_ms_per_cycle": 192.48873333333333,
|
||||
"mode": "adaptive_5_4_3",
|
||||
"p95_ms": 254.848,
|
||||
"repeat": 1,
|
||||
"requested_simulation_seconds": 9.783333333333333,
|
||||
"save": "many_objects_fragmented_stars_cycle.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.694182851452316
|
||||
},
|
||||
{
|
||||
"applied_game_years": "600000",
|
||||
"cadence_steps": {
|
||||
"0.2": 30
|
||||
},
|
||||
"cpu_cycles_per_second": 5.137478056546851,
|
||||
"cycles": 30,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 5.0,
|
||||
"max_ms": 375.784,
|
||||
"mean_ms_per_cycle": 194.64803333333333,
|
||||
"mode": "fixed_5",
|
||||
"p95_ms": 255.446,
|
||||
"repeat": 1,
|
||||
"requested_simulation_seconds": 6.000000000000003,
|
||||
"save": "many_objects_fragmented_stars_cycle.adventure.json",
|
||||
"simulated_seconds_per_cpu_second": 1.0274956113093707
|
||||
}
|
||||
],
|
||||
"speed": 6
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
{
|
||||
"godot": {
|
||||
"build": "official",
|
||||
"hash": "ed1daf0bf001b61586d9930840f2f1394092c079",
|
||||
"hex": 263938,
|
||||
"major": 4,
|
||||
"minor": 7,
|
||||
"patch": 2,
|
||||
"status": "stable",
|
||||
"string": "4.7.2-stable (official)",
|
||||
"timestamp": 0
|
||||
},
|
||||
"machine": "Mac16,8",
|
||||
"results": [
|
||||
{
|
||||
"commits": 48,
|
||||
"completed_cycles_per_second": 4.743091908967023,
|
||||
"cpu_profile_usec_calls": {
|
||||
"assign_homes": [
|
||||
51095,
|
||||
170
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
111005,
|
||||
48
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
131724,
|
||||
48
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
325933,
|
||||
48
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
928271,
|
||||
48
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
825706,
|
||||
48
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
148485,
|
||||
48
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
104924,
|
||||
2064
|
||||
],
|
||||
"enforce_capacity": [
|
||||
626179,
|
||||
170
|
||||
],
|
||||
"generation_products": [
|
||||
235626,
|
||||
74
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
455348,
|
||||
97
|
||||
],
|
||||
"generations": [
|
||||
3654469,
|
||||
48
|
||||
],
|
||||
"initialize": [
|
||||
215278,
|
||||
48
|
||||
],
|
||||
"newborn_batches": [
|
||||
237070,
|
||||
3088
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
630353,
|
||||
437
|
||||
],
|
||||
"production": [
|
||||
1115470,
|
||||
48
|
||||
],
|
||||
"trends": [
|
||||
865132,
|
||||
48
|
||||
]
|
||||
},
|
||||
"elapsed_seconds": 10.119981,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 5.0,
|
||||
"frames": 246,
|
||||
"mean_render_fps": 24.584772192104204,
|
||||
"mode": "fixed_5",
|
||||
"published_to_requested": 0.9943600000000001,
|
||||
"published_years": "1006667",
|
||||
"render_max_ms": 237.132,
|
||||
"render_p95_ms": 111.449,
|
||||
"requested_years": "1012372"
|
||||
},
|
||||
{
|
||||
"commits": 21,
|
||||
"completed_cycles_per_second": 2.085573876935847,
|
||||
"cpu_profile_usec_calls": {
|
||||
"assign_homes": [
|
||||
30276,
|
||||
75
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
51694,
|
||||
21
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
52773,
|
||||
21
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
142952,
|
||||
21
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
309711,
|
||||
21
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
435139,
|
||||
21
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
74385,
|
||||
21
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
87284,
|
||||
903
|
||||
],
|
||||
"enforce_capacity": [
|
||||
414865,
|
||||
75
|
||||
],
|
||||
"generation_products": [
|
||||
105449,
|
||||
33
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
152279,
|
||||
43
|
||||
],
|
||||
"generations": [
|
||||
1503309,
|
||||
21
|
||||
],
|
||||
"initialize": [
|
||||
255626,
|
||||
21
|
||||
],
|
||||
"newborn_batches": [
|
||||
114834,
|
||||
1633
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
274341,
|
||||
193
|
||||
],
|
||||
"production": [
|
||||
685165,
|
||||
21
|
||||
],
|
||||
"trends": [
|
||||
377582,
|
||||
21
|
||||
]
|
||||
},
|
||||
"elapsed_seconds": 10.069171,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 2.0,
|
||||
"frames": 879,
|
||||
"mean_render_fps": 87.80117977216634,
|
||||
"mode": "adaptive_5_4_3_2",
|
||||
"published_to_requested": 0.9889800000000001,
|
||||
"published_years": "989844",
|
||||
"render_max_ms": 203.26,
|
||||
"render_p95_ms": 42.712,
|
||||
"requested_years": "1000874"
|
||||
},
|
||||
{
|
||||
"commits": 21,
|
||||
"completed_cycles_per_second": 2.0775335521668676,
|
||||
"cpu_profile_usec_calls": {
|
||||
"assign_homes": [
|
||||
31004,
|
||||
76
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
51172,
|
||||
21
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
52782,
|
||||
21
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
142851,
|
||||
21
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
309607,
|
||||
21
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
431321,
|
||||
21
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
74854,
|
||||
21
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
86348,
|
||||
903
|
||||
],
|
||||
"enforce_capacity": [
|
||||
423638,
|
||||
76
|
||||
],
|
||||
"generation_products": [
|
||||
105537,
|
||||
34
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
151611,
|
||||
43
|
||||
],
|
||||
"generations": [
|
||||
1516916,
|
||||
21
|
||||
],
|
||||
"initialize": [
|
||||
261806,
|
||||
21
|
||||
],
|
||||
"newborn_batches": [
|
||||
112492,
|
||||
1621
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
278871,
|
||||
195
|
||||
],
|
||||
"production": [
|
||||
696352,
|
||||
21
|
||||
],
|
||||
"trends": [
|
||||
377969,
|
||||
21
|
||||
]
|
||||
},
|
||||
"elapsed_seconds": 10.10814,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 2.0,
|
||||
"frames": 880,
|
||||
"mean_render_fps": 88.00051040296032,
|
||||
"mode": "adaptive_5_4_3_2",
|
||||
"published_to_requested": 0.9933700000000001,
|
||||
"published_years": "994002",
|
||||
"render_max_ms": 201.104,
|
||||
"render_p95_ms": 42.207,
|
||||
"requested_years": "1000640"
|
||||
},
|
||||
{
|
||||
"commits": 47,
|
||||
"completed_cycles_per_second": 4.646829581875308,
|
||||
"cpu_profile_usec_calls": {
|
||||
"assign_homes": [
|
||||
50139,
|
||||
165
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
108674,
|
||||
47
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
129977,
|
||||
47
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
323458,
|
||||
47
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
911542,
|
||||
47
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
807459,
|
||||
47
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
147856,
|
||||
47
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
96154,
|
||||
2021
|
||||
],
|
||||
"enforce_capacity": [
|
||||
619397,
|
||||
165
|
||||
],
|
||||
"generation_products": [
|
||||
234744,
|
||||
71
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
451797,
|
||||
95
|
||||
],
|
||||
"generations": [
|
||||
3665554,
|
||||
47
|
||||
],
|
||||
"initialize": [
|
||||
217827,
|
||||
47
|
||||
],
|
||||
"newborn_batches": [
|
||||
265766,
|
||||
3178
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
623211,
|
||||
428
|
||||
],
|
||||
"production": [
|
||||
1142731,
|
||||
47
|
||||
],
|
||||
"trends": [
|
||||
856364,
|
||||
47
|
||||
]
|
||||
},
|
||||
"elapsed_seconds": 10.114423,
|
||||
"final_adaptive_quality": 3,
|
||||
"final_cadence_hz": 5.0,
|
||||
"frames": 239,
|
||||
"mean_render_fps": 23.87480014844331,
|
||||
"mode": "fixed_5",
|
||||
"published_to_requested": 0.9904200000000001,
|
||||
"published_years": "992039",
|
||||
"render_max_ms": 201.438,
|
||||
"render_p95_ms": 114.211,
|
||||
"requested_years": "1001635"
|
||||
}
|
||||
],
|
||||
"save": "all_objects_speed6.adventure.json",
|
||||
"speed": 6,
|
||||
"wall_seconds_per_run": 10.0
|
||||
}
|
||||
@@ -0,0 +1,763 @@
|
||||
{
|
||||
"delta_seconds": 0.2,
|
||||
"input_sha256": {
|
||||
"all_objects_speed6": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||
"many_objects_fragmented_stars_cycle": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||
},
|
||||
"order": "before-after-after-before",
|
||||
"profile_enabled": true,
|
||||
"runs": [
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 407.447,
|
||||
"mean_ms": 382.1863,
|
||||
"min_ms": 338.878,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23607,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
119827,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
100257,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
167710,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1031564,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2148765,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
60291,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
665230,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
338749,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
187536,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
520136,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2614358,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
112213,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
118460,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
478063,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
537724,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
349106,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 406.895,
|
||||
"mean_ms": 376.23395,
|
||||
"min_ms": 324.14,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23735,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
120538,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
50071,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
169132,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
892018,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2165803,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
60679,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
670541,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
340289,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
188663,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
534195,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2649493,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113482,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
118902,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
489793,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
548119,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
350991,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 413.636,
|
||||
"mean_ms": 378.2097,
|
||||
"min_ms": 326.044,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23901,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
121116,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
50174,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
170232,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
899208,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2173515,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61133,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
672190,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
341889,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
190021,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
537089,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2668071,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113520,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
120186,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
491086,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
549548,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
352056,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 410.851,
|
||||
"mean_ms": 388.3392,
|
||||
"min_ms": 346.936,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23780,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
121258,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
101386,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
171098,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1048869,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2182234,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61215,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
676192,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
343586,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
191018,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
528628,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2654320,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113911,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
120518,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
485368,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
550201,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
354035,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 540.275,
|
||||
"mean_ms": 504.8664,
|
||||
"min_ms": 445.592,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17920,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
136151,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
1064307,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
195983,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
2251097,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2284423,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62124,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
678656,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
288140,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
77749,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
795116,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2321187,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
140958,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54878,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
746203,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
744868,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
355994,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 462.134,
|
||||
"mean_ms": 441.9819,
|
||||
"min_ms": 389.761,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17865,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
137136,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
229748,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
196611,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1828223,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2286989,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62177,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
678566,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
288805,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
78203,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
805143,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2312257,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
141951,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
55057,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
746690,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
749636,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
355174,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 467.425,
|
||||
"mean_ms": 444.6246,
|
||||
"min_ms": 389.196,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18039,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
137286,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
231059,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
197868,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1835420,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2302705,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62638,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
680863,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
289856,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
78541,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
808144,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2327718,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
142049,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
55184,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
752516,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
752648,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
358218,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 532.382,
|
||||
"mean_ms": 508.9093,
|
||||
"min_ms": 451.042,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
18208,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
137370,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
1075170,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
198272,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
2269355,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2307886,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62940,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
683671,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
291171,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
78498,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
807187,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2322215,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
143015,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
55362,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
749843,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
753597,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
360333,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
}
|
||||
],
|
||||
"speed": 6,
|
||||
"reference_commit": "413f2be9044090c878c2909dd9d0646f77513a5f",
|
||||
"reference_script_sha256": "b2a04cc50ad1e77b45762264846cb546b013b09f4c204ba22e6472a38728e5b7",
|
||||
"candidate_script_sha256": "cbc65eaf4ab0e67fb9d6d5d7ad11757bdeb5f0b1e0ffb2f2631bfe3dcd9e962e",
|
||||
"summary": {
|
||||
"all_objects_speed6": {
|
||||
"before": {
|
||||
"mean_ms": 385.26275,
|
||||
"cycles_per_second": 2.5956311634073113,
|
||||
"profile_ms": {
|
||||
"assign_homes": 1.184675,
|
||||
"civilization_flow_capacity": 6.027125,
|
||||
"civilization_flow_colonies": 5.041075,
|
||||
"civilization_flow_finish": 8.4702,
|
||||
"civilization_flow_ledger": 52.010825,
|
||||
"civilization_flow_plan": 108.27497500000001,
|
||||
"civilization_operator_build": 3.03765,
|
||||
"civilization_operator_solve": 33.53555,
|
||||
"enforce_capacity": 17.058375,
|
||||
"generation_products": 9.46385,
|
||||
"generation_withdrawal": 26.219099999999997,
|
||||
"generations": 131.71695000000003,
|
||||
"initialize": 5.6531,
|
||||
"newborn_batches": 5.97445,
|
||||
"physical_amounts_build": 24.085775,
|
||||
"production": 27.198125,
|
||||
"trends": 17.578525000000003
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"mean_ms": 377.22182499999997,
|
||||
"cycles_per_second": 2.6509600816442687,
|
||||
"profile_ms": {
|
||||
"assign_homes": 1.1909,
|
||||
"civilization_flow_capacity": 6.04135,
|
||||
"civilization_flow_colonies": 2.506125,
|
||||
"civilization_flow_finish": 8.4841,
|
||||
"civilization_flow_ledger": 44.78065,
|
||||
"civilization_flow_plan": 108.48295,
|
||||
"civilization_operator_build": 3.0453,
|
||||
"civilization_operator_solve": 33.568275,
|
||||
"enforce_capacity": 17.05445,
|
||||
"generation_products": 9.4671,
|
||||
"generation_withdrawal": 26.7821,
|
||||
"generations": 132.9391,
|
||||
"initialize": 5.675050000000001,
|
||||
"newborn_batches": 5.9772,
|
||||
"physical_amounts_build": 24.521974999999998,
|
||||
"production": 27.441675,
|
||||
"trends": 17.576175
|
||||
}
|
||||
},
|
||||
"reduction_percent": 2.0871275512621024
|
||||
},
|
||||
"many_objects_fragmented_stars_cycle": {
|
||||
"before": {
|
||||
"mean_ms": 506.88784999999996,
|
||||
"cycles_per_second": 1.9728229824407906,
|
||||
"profile_ms": {
|
||||
"assign_homes": 0.9032,
|
||||
"civilization_flow_capacity": 6.838025,
|
||||
"civilization_flow_colonies": 53.486925,
|
||||
"civilization_flow_finish": 9.856375,
|
||||
"civilization_flow_ledger": 113.0113,
|
||||
"civilization_flow_plan": 114.807725,
|
||||
"civilization_operator_build": 3.1266,
|
||||
"civilization_operator_solve": 34.058175000000006,
|
||||
"enforce_capacity": 14.482775,
|
||||
"generation_products": 3.906175,
|
||||
"generation_withdrawal": 40.057575,
|
||||
"generations": 116.08505000000001,
|
||||
"initialize": 7.099324999999999,
|
||||
"newborn_batches": 2.756,
|
||||
"physical_amounts_build": 37.40115,
|
||||
"production": 37.461625,
|
||||
"trends": 17.908175
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"mean_ms": 443.30325,
|
||||
"cycles_per_second": 2.255792169355853,
|
||||
"profile_ms": {
|
||||
"assign_homes": 0.8976000000000001,
|
||||
"civilization_flow_capacity": 6.86055,
|
||||
"civilization_flow_colonies": 11.520175,
|
||||
"civilization_flow_finish": 9.861975000000001,
|
||||
"civilization_flow_ledger": 91.591075,
|
||||
"civilization_flow_plan": 114.74235,
|
||||
"civilization_operator_build": 3.120375,
|
||||
"civilization_operator_solve": 33.985724999999995,
|
||||
"enforce_capacity": 14.466524999999999,
|
||||
"generation_products": 3.9186,
|
||||
"generation_withdrawal": 40.332175,
|
||||
"generations": 115.999375,
|
||||
"initialize": 7.1,
|
||||
"newborn_batches": 2.756025,
|
||||
"physical_amounts_build": 37.48015,
|
||||
"production": 37.5571,
|
||||
"trends": 17.834799999999998
|
||||
}
|
||||
},
|
||||
"reduction_percent": 12.544116020930463
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,870 @@
|
||||
{
|
||||
"delta_seconds": 0.2,
|
||||
"input_sha256": {
|
||||
"all_objects_speed6": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||
"many_objects_fragmented_stars_cycle": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||
},
|
||||
"order": "before-after-after-before",
|
||||
"profile_enabled": true,
|
||||
"runs": [
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 409.924,
|
||||
"mean_ms": 374.42165,
|
||||
"min_ms": 321.955,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23286,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
120325,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
49609,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
167957,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
890864,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2156571,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
59939,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
667995,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
338483,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
186561,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
530120,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2635468,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
112680,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
118343,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
488368,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
543976,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
348509,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 396.498,
|
||||
"mean_ms": 366.8874,
|
||||
"min_ms": 316.948,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23679,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
121355,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
52357,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
134122,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
867762,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2171014,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
60589,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
672927,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
313233,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
188758,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
537217,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2691969,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113057,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
119560,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
283727,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
371874,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
352124,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 400.433,
|
||||
"mean_ms": 367.76455,
|
||||
"min_ms": 317.346,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23679,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
120585,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
52508,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
133714,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
873014,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2178049,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
60872,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
674461,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
312963,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
189817,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
538355,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2697406,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113617,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
119833,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
283294,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
370613,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
354430,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 412.947,
|
||||
"mean_ms": 379.94895,
|
||||
"min_ms": 327.79,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23817,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
120810,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
50245,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
170562,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
901208,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2186592,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61551,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
676571,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
342898,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
190410,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
538869,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2675372,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
114216,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
120558,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
495969,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
554663,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
353982,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 463.763,
|
||||
"mean_ms": 441.44965,
|
||||
"min_ms": 386.031,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17806,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
136652,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
230107,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
196342,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1825154,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2292865,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62146,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
680101,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
288185,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
77590,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
800537,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2298719,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
141855,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54592,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
745858,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
747261,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
356732,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 445.055,
|
||||
"mean_ms": 423.0315,
|
||||
"min_ms": 374.104,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17840,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
137440,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
231537,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
137928,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1782223,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2310391,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61962,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
687903,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
238360,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
77666,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
802637,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2360346,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
141317,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54930,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
356207,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
405364,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
357407,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 441.891,
|
||||
"mean_ms": 422.9136,
|
||||
"min_ms": 376.819,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17821,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
136949,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
231992,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
137312,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1788156,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2297486,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61993,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
683317,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
238430,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
78170,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
804912,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2364256,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
141451,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54900,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
354476,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
407287,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
358298,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 465.049,
|
||||
"mean_ms": 444.87595,
|
||||
"min_ms": 392.884,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17906,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
136963,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
229784,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
196495,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1839904,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2302987,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62518,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
683660,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
289846,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
78882,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
808021,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2328372,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
142755,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54963,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
754056,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
754898,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
358634,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
}
|
||||
],
|
||||
"speed": 6,
|
||||
"reference_commit": "9357f757a95db96274d4e115c7e498b58eca6902",
|
||||
"exact_differential": {
|
||||
"cycles": 10,
|
||||
"inputs": [
|
||||
"all_objects_speed6",
|
||||
"many_objects_fragmented_stars_cycle"
|
||||
],
|
||||
"delta_seconds": [
|
||||
0.2,
|
||||
0.53,
|
||||
0.31,
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
"equal_full_states": true
|
||||
},
|
||||
"checks": {
|
||||
"flow_commit": 1971,
|
||||
"civilization_flows": 1895,
|
||||
"civilization_state": 70,
|
||||
"frame_evolution": 38,
|
||||
"production_transaction": 14,
|
||||
"stellar_rules": 333
|
||||
},
|
||||
"summary": {
|
||||
"all_objects_speed6": {
|
||||
"before": {
|
||||
"mean_ms": 377.1853,
|
||||
"cycles_per_second": 2.651216789201488,
|
||||
"profile_ms": {
|
||||
"assign_homes": 1.177575,
|
||||
"civilization_flow_capacity": 6.028375,
|
||||
"civilization_flow_colonies": 2.49635,
|
||||
"civilization_flow_finish": 8.462975,
|
||||
"civilization_flow_ledger": 44.8018,
|
||||
"civilization_flow_plan": 108.579075,
|
||||
"civilization_operator_build": 3.03725,
|
||||
"civilization_operator_solve": 33.61415,
|
||||
"enforce_capacity": 17.034525000000002,
|
||||
"generation_products": 9.424275,
|
||||
"generation_withdrawal": 26.724725,
|
||||
"generations": 132.771,
|
||||
"initialize": 5.6724,
|
||||
"newborn_batches": 5.972524999999999,
|
||||
"physical_amounts_build": 24.608425,
|
||||
"production": 27.465975,
|
||||
"trends": 17.562275000000003
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 4.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 4.0,
|
||||
"generation_products": 2.0,
|
||||
"generation_withdrawal": 3.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 150.8,
|
||||
"physical_amounts_build": 18.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"mean_ms": 367.32597499999997,
|
||||
"cycles_per_second": 2.7223775830173733,
|
||||
"profile_ms": {
|
||||
"assign_homes": 1.18395,
|
||||
"civilization_flow_capacity": 6.0485,
|
||||
"civilization_flow_colonies": 2.621625,
|
||||
"civilization_flow_finish": 6.6959,
|
||||
"civilization_flow_ledger": 43.519400000000005,
|
||||
"civilization_flow_plan": 108.726575,
|
||||
"civilization_operator_build": 3.036525,
|
||||
"civilization_operator_solve": 33.6847,
|
||||
"enforce_capacity": 15.6549,
|
||||
"generation_products": 9.464375,
|
||||
"generation_withdrawal": 26.8893,
|
||||
"generations": 134.734375,
|
||||
"initialize": 5.66685,
|
||||
"newborn_batches": 5.984825,
|
||||
"physical_amounts_build": 14.175525,
|
||||
"production": 18.562175,
|
||||
"trends": 17.66385
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 4.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 4.0,
|
||||
"generation_products": 2.0,
|
||||
"generation_withdrawal": 3.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 150.8,
|
||||
"physical_amounts_build": 10.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"reduction_percent": 2.613920797019398
|
||||
},
|
||||
"many_objects_fragmented_stars_cycle": {
|
||||
"before": {
|
||||
"mean_ms": 443.1628,
|
||||
"cycles_per_second": 2.2565070894939736,
|
||||
"profile_ms": {
|
||||
"assign_homes": 0.8927999999999999,
|
||||
"civilization_flow_capacity": 6.840375,
|
||||
"civilization_flow_colonies": 11.497275,
|
||||
"civilization_flow_finish": 9.820924999999999,
|
||||
"civilization_flow_ledger": 91.62644999999999,
|
||||
"civilization_flow_plan": 114.8963,
|
||||
"civilization_operator_build": 3.1166,
|
||||
"civilization_operator_solve": 34.094025,
|
||||
"enforce_capacity": 14.450775,
|
||||
"generation_products": 3.9118000000000004,
|
||||
"generation_withdrawal": 40.21395,
|
||||
"generations": 115.677275,
|
||||
"initialize": 7.11525,
|
||||
"newborn_batches": 2.738875,
|
||||
"physical_amounts_build": 37.49785,
|
||||
"production": 37.553975,
|
||||
"trends": 17.88415
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 3.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 3.0,
|
||||
"generation_products": 1.0,
|
||||
"generation_withdrawal": 2.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 74.5,
|
||||
"physical_amounts_build": 15.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"mean_ms": 422.97254999999996,
|
||||
"cycles_per_second": 2.364219616615783,
|
||||
"profile_ms": {
|
||||
"assign_homes": 0.891525,
|
||||
"civilization_flow_capacity": 6.859725,
|
||||
"civilization_flow_colonies": 11.588225,
|
||||
"civilization_flow_finish": 6.881,
|
||||
"civilization_flow_ledger": 89.25947500000001,
|
||||
"civilization_flow_plan": 115.19692500000001,
|
||||
"civilization_operator_build": 3.098875,
|
||||
"civilization_operator_solve": 34.2805,
|
||||
"enforce_capacity": 11.91975,
|
||||
"generation_products": 3.8959,
|
||||
"generation_withdrawal": 40.188725,
|
||||
"generations": 118.11505,
|
||||
"initialize": 7.0691999999999995,
|
||||
"newborn_batches": 2.74575,
|
||||
"physical_amounts_build": 17.767075000000002,
|
||||
"production": 20.316275,
|
||||
"trends": 17.892625
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 3.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 3.0,
|
||||
"generation_products": 1.0,
|
||||
"generation_withdrawal": 2.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 74.5,
|
||||
"physical_amounts_build": 7.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"reduction_percent": 4.5559442263655825
|
||||
}
|
||||
},
|
||||
"candidate_script_sha256": {
|
||||
"civilization_flow.gd": "5d2b5f5a90fc9182ac1f933d473441457be28b47800a0081b1409614dd28aab5",
|
||||
"civilization_state.gd": "ae0832531c7ce244e6939793007b3d7be5bd32246be6c209a75fdb5e3e6ffe7a",
|
||||
"civilization_evolution.gd": "24efc14eedee5e8a28422262e37e6c54e1a0c403a164bd8f4c96d749f89ac61f",
|
||||
"evolution_coordinator.gd": "0b8cec7e53f047698545e9b97c986b99e0ea5bfc61a6b0177335aedb442e6048",
|
||||
"object_ledger.gd": "f7fbfeb4be905b525ad91648167ed1e32c4d3b77553549c69c3ebb808ac99b4b",
|
||||
"manual_production.gd": "138a94d3bb7d63470f2ca2eb841badef910ec27f3e247c5c9ed8e925c0971fa4",
|
||||
"automatic_production.gd": "dba9f98cbecc90ad9935653c39f7fcfad53edb3ca2670bbce4e1594b58b399a5",
|
||||
"universe_environment.gd": "e31c87b38a4ec0052f3d43f7be2af51ee7612a48fadcf0b5072ba678107e576e"
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ func restore_json_integers(value):
|
||||
func run():
|
||||
var args = OS.get_cmdline_user_args()
|
||||
if args.is_empty():
|
||||
print("Usage: --script tests/benchmark_civilization_flows.gd -- /path/to/save.adventure.json [delta_seconds] [profile] [cycles=100]")
|
||||
print("Usage: --script tests/benchmark_civilization_flows.gd -- /path/to/save.adventure.json [delta_seconds] [profile] [cycles=100] [speed=1..6]")
|
||||
quit(1)
|
||||
return
|
||||
var data = JSON.parse_string(FileAccess.get_file_as_string(args[0]))
|
||||
@@ -33,7 +33,11 @@ func run():
|
||||
if not is_finite(delta) or delta <= 0:
|
||||
quit(1)
|
||||
return
|
||||
for speed in [6]:
|
||||
var selected_speed: int = 6
|
||||
for arg in args:
|
||||
if arg.begins_with("speed="):
|
||||
selected_speed = clampi(arg.trim_prefix("speed=").to_int(), 1, root.get_node("Global").SPEED_LIMIT)
|
||||
for speed in [selected_speed]:
|
||||
var world = load("res://world/owniverse/owniverse.gd").new()
|
||||
world.catalog.load_definitions(root.get_node("Global").OwniverseEntities)
|
||||
world.state = initial.duplicate(true)
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
extends SceneTree
|
||||
## Paired, read-only throughput benchmark for adaptive 5/4/3/2 Hz cadence.
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const SAVE_PATHS: Array[String] = [
|
||||
"res://tests/artifacts/all_objects_speed6.adventure.json",
|
||||
"res://tests/artifacts/many_objects_fragmented_stars_cycle.adventure.json"
|
||||
]
|
||||
const RESULT_PATH := "res://tests/artifacts/dynamic_cadence_results.json"
|
||||
var cycles: int = 40
|
||||
var repeats: int = 2
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
|
||||
func restore_json_integers(value):
|
||||
if value is float and value == floorf(value) and absf(value) <= 9007199254740991:
|
||||
return int(value)
|
||||
if value is Dictionary:
|
||||
for key in value: value[key] = restore_json_integers(value[key])
|
||||
elif value is Array:
|
||||
for i in value.size(): value[i] = restore_json_integers(value[i])
|
||||
return value
|
||||
|
||||
func create_world(initial: Dictionary, use_adaptive_cadence: bool):
|
||||
var world = load("res://world/owniverse/owniverse.gd").new()
|
||||
world.catalog.load_definitions(root.get_node("Global").OwniverseEntities)
|
||||
world.state = initial.duplicate(true)
|
||||
world.speed = 6
|
||||
world.adaptive_cadence = use_adaptive_cadence
|
||||
world.frame_step_seconds = 0.2
|
||||
world.cadence_level = 0
|
||||
return world
|
||||
|
||||
func dispose(world) -> void:
|
||||
for entity in world.catalog.entities.values(): entity.free()
|
||||
world.free()
|
||||
|
||||
func measure(initial: Dictionary, adaptive: bool, save_name: String, repeat_index: int) -> Dictionary:
|
||||
var world = create_world(initial, adaptive)
|
||||
var elapsed_usec: int = 0
|
||||
var requested_seconds: float = 0.0
|
||||
var step_counts: Dictionary = {}
|
||||
var samples: Array[float] = []
|
||||
var initial_clock: Variant = world.state.time.datetime
|
||||
for i in cycles:
|
||||
var step: float = world.frame_step_seconds
|
||||
step_counts[str(step)] = int(step_counts.get(str(step), 0)) + 1
|
||||
requested_seconds += step
|
||||
var started: int = Time.get_ticks_usec()
|
||||
world.evolute(step)
|
||||
var usec: int = Time.get_ticks_usec() - started
|
||||
elapsed_usec += usec
|
||||
samples.append(float(usec) / 1000.0)
|
||||
var applied_years: Variant = E.subtract(world.state.time.datetime, initial_clock)
|
||||
var expected_years: float = requested_seconds * 100000.0
|
||||
assert(absf(E.approx(applied_years) - expected_years) <= maxf(1.0, expected_years * 1e-10), "Clock advance differs from accumulated cadence interval")
|
||||
var validation: String = root.get_node("SaveManager").validate_data({
|
||||
"format_version": 6,
|
||||
"app_version": "0.7.0",
|
||||
"ui": {"current_page": 0},
|
||||
"state": world.state
|
||||
})
|
||||
assert(validation.is_empty(), validation)
|
||||
samples.sort()
|
||||
var result: Dictionary = {
|
||||
"save": save_name,
|
||||
"mode": "adaptive_5_4_3_2" if adaptive else "fixed_5",
|
||||
"repeat": repeat_index,
|
||||
"cycles": cycles,
|
||||
"mean_ms_per_cycle": float(elapsed_usec) / (cycles * 1000.0),
|
||||
"p95_ms": samples[int(ceil(samples.size() * 0.95)) - 1],
|
||||
"max_ms": samples.back(),
|
||||
"cpu_cycles_per_second": float(cycles) * 1000000.0 / elapsed_usec,
|
||||
"simulated_seconds_per_cpu_second": requested_seconds * 1000000.0 / elapsed_usec,
|
||||
"requested_simulation_seconds": requested_seconds,
|
||||
"applied_game_years": E.fixed(applied_years),
|
||||
"cadence_steps": step_counts,
|
||||
"final_cadence_hz": 1.0 / world.frame_step_seconds,
|
||||
"final_adaptive_quality": world.adaptive_quality
|
||||
}
|
||||
print("CADENCE_RESULT ", JSON.stringify(result))
|
||||
dispose(world)
|
||||
return result
|
||||
|
||||
func run() -> void:
|
||||
for arg in OS.get_cmdline_user_args():
|
||||
if arg.begins_with("cycles="): cycles = maxi(1, arg.trim_prefix("cycles=").to_int())
|
||||
if arg.begins_with("repeats="): repeats = maxi(1, arg.trim_prefix("repeats=").to_int())
|
||||
var inputs: Dictionary = {}
|
||||
for path in SAVE_PATHS:
|
||||
var parsed = JSON.parse_string(FileAccess.get_file_as_string(path))
|
||||
assert(parsed is Dictionary and parsed.get("format_version") == 6, "Invalid benchmark save: " + path)
|
||||
var payload: Dictionary = E.decode(restore_json_integers(parsed))
|
||||
var validation: String = root.get_node("SaveManager").validate_data(payload)
|
||||
assert(validation.is_empty(), validation)
|
||||
inputs[path.get_file()] = payload.state
|
||||
var results: Array[Dictionary] = []
|
||||
for save_name in inputs:
|
||||
var initial: Dictionary = inputs[save_name]
|
||||
# Alternate order to limit machine warm-up and thermal bias.
|
||||
for repeat_index in repeats:
|
||||
if repeat_index % 2 == 0:
|
||||
results.append(measure(initial, false, save_name, repeat_index))
|
||||
results.append(measure(initial, true, save_name, repeat_index))
|
||||
else:
|
||||
results.append(measure(initial, true, save_name, repeat_index))
|
||||
results.append(measure(initial, false, save_name, repeat_index))
|
||||
var output := FileAccess.open(RESULT_PATH, FileAccess.WRITE)
|
||||
assert(output != null)
|
||||
output.store_string(JSON.stringify({
|
||||
"machine": OS.get_model_name(),
|
||||
"godot": Engine.get_version_info(),
|
||||
"speed": 6,
|
||||
"cycles_per_run": cycles,
|
||||
"repeats": repeats,
|
||||
"input_saves_unchanged": true,
|
||||
"results": results
|
||||
}, "\t", true, true))
|
||||
output.close()
|
||||
print("Wrote paired cadence results: ", RESULT_PATH)
|
||||
quit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://3fogff2lfmy5
|
||||
@@ -0,0 +1,116 @@
|
||||
extends SceneTree
|
||||
## Measures real Stage scheduling on the full-load diagnostic save.
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const RUN_SECONDS: float = 10.0
|
||||
const RESULT_PATH := "res://tests/artifacts/dynamic_cadence_ui_results.json"
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
|
||||
func restore(value):
|
||||
if value is float and value == floorf(value) and absf(value) <= 9007199254740991: return int(value)
|
||||
if value is Dictionary:
|
||||
for key in value: value[key] = restore(value[key])
|
||||
elif value is Array:
|
||||
for i in value.size(): value[i] = restore(value[i])
|
||||
return value
|
||||
|
||||
func prepare(stage, state: Dictionary, adaptive: bool) -> void:
|
||||
var world = stage.owniverse
|
||||
world.state = state.duplicate(true)
|
||||
world.speed = 6
|
||||
world.adaptive_cadence = adaptive
|
||||
world.adaptive_cycle_ema_ms = 0.0
|
||||
world.adaptive_quality = 0
|
||||
world.adaptive_recovery_cycles = 0
|
||||
world.cadence_level = 0
|
||||
world.cadence_recovery_cycles = 0
|
||||
world.frame_step_seconds = 0.2
|
||||
world.frame_elapsed_seconds = 0.0
|
||||
world.frame_clock = world.state.time.duplicate()
|
||||
world.frame_active_clock = {}
|
||||
world._resume_clock = {}
|
||||
world._queued_productions.clear()
|
||||
world.production_queue_busy = false
|
||||
world.frame_busy = false
|
||||
world.frame_commits = 0
|
||||
world.profile.clear()
|
||||
stage.frame_samples.clear()
|
||||
stage.process_samples.clear()
|
||||
stage.previous_frame = 0
|
||||
stage.measuring = false
|
||||
world.presentation.refresh(world.state, world.catalog)
|
||||
stage.set_owniverse_item_value()
|
||||
|
||||
func run_measurement(stage, initial: Dictionary, adaptive: bool) -> Dictionary:
|
||||
prepare(stage, initial, adaptive)
|
||||
var world = stage.owniverse
|
||||
var start_usec: int = Time.get_ticks_usec()
|
||||
var requested_start: Variant = world.frame_clock.datetime
|
||||
var published_start: Variant = world.state.time.datetime
|
||||
stage.measuring = true
|
||||
while float(Time.get_ticks_usec() - start_usec) / 1000000.0 < RUN_SECONDS:
|
||||
await process_frame
|
||||
stage.measuring = false
|
||||
var measured_frames: int = stage.frame_samples.size()
|
||||
# Let the final atomic transaction publish before starting the paired run.
|
||||
while world.frame_busy or world.production_queue_busy:
|
||||
await process_frame
|
||||
var elapsed_usec: int = Time.get_ticks_usec() - start_usec
|
||||
var requested: Variant = E.subtract(world.frame_clock.datetime, requested_start)
|
||||
var published: Variant = E.subtract(world.state.time.datetime, published_start)
|
||||
var samples: Array = stage.frame_samples.duplicate()
|
||||
var total_frame_ms: float = 0.0
|
||||
for value in samples: total_frame_ms += value
|
||||
samples.sort()
|
||||
var result: Dictionary = {
|
||||
"mode": "adaptive_5_4_3_2" if adaptive else "fixed_5",
|
||||
"frames": measured_frames,
|
||||
"elapsed_seconds": float(elapsed_usec) / 1000000.0,
|
||||
"commits": world.frame_commits,
|
||||
"completed_cycles_per_second": float(world.frame_commits) * 1000000.0 / elapsed_usec,
|
||||
"requested_years": E.fixed(requested),
|
||||
"published_years": E.fixed(published),
|
||||
"published_to_requested": snappedf(E.approx(E.divide(published, requested)), 0.00001),
|
||||
"mean_render_fps": float(samples.size()) * 1000.0 / total_frame_ms if total_frame_ms > 0.0 else 0.0,
|
||||
"render_p95_ms": samples[int(ceil(samples.size() * 0.95)) - 1] if not samples.is_empty() else 0.0,
|
||||
"render_max_ms": samples.back() if not samples.is_empty() else 0.0,
|
||||
"final_cadence_hz": 1.0 / world.frame_step_seconds,
|
||||
"final_adaptive_quality": world.adaptive_quality,
|
||||
"cpu_profile_usec_calls": world.profile.duplicate(true)
|
||||
}
|
||||
print("UI_CADENCE_RESULT ", JSON.stringify(result))
|
||||
return result
|
||||
|
||||
func run() -> void:
|
||||
var raw = JSON.parse_string(FileAccess.get_file_as_string("res://tests/artifacts/all_objects_speed6.adventure.json"))
|
||||
assert(raw is Dictionary and raw.get("format_version") == 6)
|
||||
var payload: Dictionary = E.decode(restore(raw))
|
||||
var validation: String = root.get_node("SaveManager").validate_data(payload)
|
||||
assert(validation.is_empty(), validation)
|
||||
var scene = load("res://world/world.tscn").instantiate()
|
||||
var stage = scene.get_node("Stage")
|
||||
stage.owniverse.free()
|
||||
stage.set_script(load("res://tests/fixtures/benchmark_ui_stage.gd"))
|
||||
root.add_child(scene)
|
||||
await process_frame
|
||||
stage.owniverse.profile_enabled = true
|
||||
var results: Array[Dictionary] = []
|
||||
for repeat_index in 2:
|
||||
if repeat_index % 2 == 0:
|
||||
results.append(await run_measurement(stage, payload.state, false))
|
||||
results.append(await run_measurement(stage, payload.state, true))
|
||||
else:
|
||||
results.append(await run_measurement(stage, payload.state, true))
|
||||
results.append(await run_measurement(stage, payload.state, false))
|
||||
var report: Dictionary = {"machine": OS.get_model_name(), "godot": Engine.get_version_info(), "speed": 6, "wall_seconds_per_run": RUN_SECONDS, "save": "all_objects_speed6.adventure.json", "results": results}
|
||||
var output := FileAccess.open(RESULT_PATH, FileAccess.WRITE)
|
||||
assert(output != null)
|
||||
output.store_string(JSON.stringify(report, "\t", true, true))
|
||||
output.close()
|
||||
print("UI_CADENCE_RESULTS ", JSON.stringify(report))
|
||||
print("Wrote paired UI cadence results: ", RESULT_PATH)
|
||||
for entity in stage.owniverse.catalog.entities.values(): entity.free()
|
||||
stage.owniverse.free()
|
||||
scene.free()
|
||||
quit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://olp81bbycovp
|
||||
@@ -0,0 +1,129 @@
|
||||
extends SceneTree
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const Controller = preload("res://ui/achievements/achievement_controller.gd")
|
||||
var failures := 0
|
||||
class MockWorld extends RefCounted:
|
||||
var state := {"time": {"year": 0}, "stats": {"total_amount": {}}}
|
||||
var presentation := {"revision": 0}
|
||||
class MockStage extends Control:
|
||||
const PAGE_WIDTH = 1080
|
||||
var owniverse = MockWorld.new()
|
||||
var screens: Dictionary
|
||||
var stage_page_scroller: ScrollContainer
|
||||
func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
func check(value: bool, message: String) -> void:
|
||||
if not value:
|
||||
failures += 1
|
||||
push_error(message)
|
||||
func node(parent: Node, child: Node, label: String) -> Node:
|
||||
child.name = label
|
||||
parent.add_child(child)
|
||||
return child
|
||||
func run() -> void:
|
||||
check(load("res://stage/stage.gd") != null, "Stage integration compiles")
|
||||
var scene: PackedScene = load("res://world/world.tscn")
|
||||
var scene_state: SceneState = scene.get_state()
|
||||
for language in ["English", "Russian"]:
|
||||
var connected := false
|
||||
for i in scene_state.get_connection_count():
|
||||
if str(scene_state.get_connection_source(i)) == "Stage/MainMenu/Language/" + language:
|
||||
connected = scene_state.get_connection_signal(i) == &"pressed" and str(scene_state.get_connection_target(i)) == "Stage/MainMenu" and scene_state.get_connection_method(i) == &"_on_change_locale_pressed" and scene_state.get_connection_binds(i) == ["en" if language == "English" else "ru"]
|
||||
check(connected, language + " button connects to locale handler")
|
||||
var stage := MockStage.new()
|
||||
root.add_child(stage)
|
||||
var world = node(stage, Control.new(), "World")
|
||||
stage.screens = {"World": world}
|
||||
stage.stage_page_scroller = node(world, ScrollContainer.new(), "PageScroller")
|
||||
var pages = node(stage.stage_page_scroller, HBoxContainer.new(), "HBoxContainer")
|
||||
for names in [["ForcePage", "ForceHeader"], ["ResourcePage", "ResourceHeader"], ["StarPage", "StarHeader"], ["StructurePage", "StructureHeader"], ["LifePage", "LifeHeader"]]:
|
||||
node(node(pages, VBoxContainer.new(), names[0]), HBoxContainer.new(), names[1])
|
||||
var screen = node(stage, Control.new(), "Achievements")
|
||||
var title = node(node(screen, Control.new(), "Header"), TextureRect.new(), "Title")
|
||||
node(node(screen, ScrollContainer.new(), "AchievementList"), VBoxContainer.new(), "AchievementItems")
|
||||
var controller = Controller.new()
|
||||
controller.progress_path = "/tmp/owniverse-achievements-test-%s.cfg" % OS.get_process_id()
|
||||
stage.add_child(controller)
|
||||
controller.initialize(stage)
|
||||
check(controller.definitions.size() == 72, "All 72 enabled achievements loaded")
|
||||
check(not controller.definitions.has("a95"), "Ignored achievement excluded")
|
||||
controller.update(0.0)
|
||||
check(controller.queue.is_empty() and controller.current.is_empty(), "Initially visible achievements do not notify")
|
||||
stage.owniverse.state.stats.total_amount.H = E.of("5E4")
|
||||
stage.owniverse.presentation.revision += 1
|
||||
controller.update(0.0)
|
||||
check(controller.progress.a13.unlocked, "Cumulative hydrogen threshold unlocks")
|
||||
check(controller.progress.a14.visible and not controller.progress.a14.unlocked, "Opening threshold reveals next achievement")
|
||||
controller.queue.clear()
|
||||
controller._enqueue("a14", "g")
|
||||
controller._enqueue("a14", "c")
|
||||
check(controller.queue.size() == 1 and controller.queue[0].type == "c", "Pending reveal upgrades to completion")
|
||||
controller.queue.clear()
|
||||
controller.current = {}
|
||||
stage.owniverse.state.time.year = E.of("1E12")
|
||||
stage.owniverse.state.stats.total_amount.H = E.of("1E24")
|
||||
stage.owniverse.state.stats.total_amount.Star = E.of("9E16")
|
||||
stage.owniverse.presentation.revision += 1
|
||||
controller.update(0.0)
|
||||
check(controller.progress.a4.unlocked and controller.progress.a20.unlocked and controller.progress.a71.unlocked, "Exact large thresholds unlock")
|
||||
check(controller.current.type == "c", "Immediate completion emits only completed notification")
|
||||
var count: int = controller.queue.size()
|
||||
controller.update(0.0)
|
||||
check(controller.queue.size() == count, "Repeated update does not duplicate events")
|
||||
world.hide()
|
||||
var time: float = controller.elapsed
|
||||
controller.update(5.0)
|
||||
check(controller.elapsed == time, "Menu pauses notification")
|
||||
world.show()
|
||||
controller.update(3.4)
|
||||
check(controller.current.is_empty() and not controller.panel.visible, "Notification expires")
|
||||
for header in controller.headers: check(header.modulate.a == 1.0, "Indicators restored")
|
||||
controller.update(0.0)
|
||||
var remaining: int = controller.queue.size()
|
||||
var click := InputEventMouseButton.new()
|
||||
click.button_index = MOUSE_BUTTON_LEFT
|
||||
click.pressed = true
|
||||
controller._on_notification_input(click)
|
||||
check(controller.current.is_empty() and not controller.panel.visible, "Click dismisses notification")
|
||||
check(controller.queue.size() == remaining, "Click preserves pending notifications")
|
||||
for header in controller.headers: check(header.modulate.a == 1.0, "Click restores indicators")
|
||||
stage.owniverse = MockWorld.new()
|
||||
controller.update(0.0)
|
||||
check(controller.progress.a71.unlocked, "New universe preserves unlocked achievements")
|
||||
controller.progress.clear()
|
||||
controller._load_progress()
|
||||
check(controller.progress.a71.unlocked, "Progress survives reload")
|
||||
# Every enabled definition must be reachable through its actual compiled rule.
|
||||
for id in controller.definitions:
|
||||
var rule: Dictionary = controller.conditions[id].achieve
|
||||
var source := {"time": {"year": 0}, "stats": {"total_amount": {}}}
|
||||
if rule.type == "age": source.time.year = E.subtract(rule.amount, 1)
|
||||
else: source.stats.total_amount[rule.id] = E.subtract(rule.amount, 1)
|
||||
check(not controller._matches(rule, source), id + " stays locked below threshold")
|
||||
if rule.type == "age": source.time.year = rule.amount
|
||||
else: source.stats.total_amount[rule.id] = rule.amount
|
||||
check(controller._matches(rule, source), id + " completes at exact threshold")
|
||||
var global: Node = root.get_node("Global")
|
||||
var previous_locale: String = global.Locale
|
||||
global.Locale = "ru"
|
||||
controller.update(0.0)
|
||||
check(controller.status.text.begins_with("Открыто"), "Status localizes")
|
||||
check(title.texture == Controller.TITLES.ru, "Russian title selected")
|
||||
check(controller.items.a71.event_label.text.contains("90 квадриллионов"), "Russian achievement text selected")
|
||||
global.Locale = "en"
|
||||
controller.update(0.0)
|
||||
check(controller.status.text.begins_with("Unlocked"), "English status selected")
|
||||
check(title.texture == Controller.TITLES.en, "English title selected")
|
||||
check(controller.items.a71.event_label.text.contains("90 quadrillion"), "English achievement text selected")
|
||||
controller.current = {"id": "a71", "type": "c"}
|
||||
controller._display_event(controller.current)
|
||||
check(controller.notification_item.event_label.text.contains("90 quadrillion"), "English notification text selected")
|
||||
global.Locale = "ru"
|
||||
controller.update(0.0)
|
||||
check(controller.notification_item.event_label.text.contains("90 квадриллионов"), "Active notification changes language")
|
||||
global.Locale = previous_locale
|
||||
var path: String = controller.progress_path
|
||||
stage.free()
|
||||
DirAccess.remove_absolute(path)
|
||||
print("Achievement checks: ", "PASS" if failures == 0 else "FAIL")
|
||||
quit(1 if failures else 0)
|
||||
@@ -0,0 +1 @@
|
||||
uid://uslcyxm8vpcn
|
||||
@@ -38,6 +38,10 @@ func run() -> void:
|
||||
actions.set_actions("step", 123, func ( request ) : return E.multiply(E.floor_value(E.divide(request, 5)), 5))
|
||||
check(E.eq(actions.action_values.values(), [10, 100, 123, 0, 0]), "Unproducible small requests omitted")
|
||||
check(E.eq(actions.action_labels[2].text, "120"), "Recipe preview displayed while original request retained")
|
||||
actions.mark_pending(123)
|
||||
check(actions.action_labels[2].text == "…" and actions.action_labels[2].get_parent().get_node("TextureButton").disabled, "Queued action is visibly pending and cannot be duplicated")
|
||||
actions.set_actions("step", 123, func ( request ) : return E.multiply(E.floor_value(E.divide(request, 5)), 5))
|
||||
check(actions.action_labels[2].text == "120" and not actions.action_labels[2].get_parent().get_node("TextureButton").disabled, "Action refresh clears pending state")
|
||||
var emitted: Array = []
|
||||
actions.action_pressed.connect(func ( _page , amount ) : emitted.append(amount))
|
||||
actions._on_action_button_pressed(2)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
extends SceneTree
|
||||
|
||||
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 run() -> void:
|
||||
var world = load("res://world/owniverse/owniverse.gd").new()
|
||||
world.adaptive_cadence = false
|
||||
world.update_adaptive_quality(250000)
|
||||
check(world.adaptive_quality == 2, "A slow cycle selects the medium approximation")
|
||||
world.update_adaptive_quality(250000)
|
||||
check(world.adaptive_quality == 3, "A slow medium-approximation cycle selects the coarse mode")
|
||||
check(world.adaptive_generation_phase == 1, "Coarse generation phase alternates")
|
||||
for i in 36: world.update_adaptive_quality(100000)
|
||||
check(world.adaptive_quality == 2, "Thirty consecutive recovery cycles lower quality by one level")
|
||||
for i in 30: world.update_adaptive_quality(100000)
|
||||
check(world.adaptive_quality == 1, "Sustained headroom lowers quality gradually")
|
||||
for i in 30: world.update_adaptive_quality(100000)
|
||||
check(world.adaptive_quality == 0, "The exact mode returns after sustained headroom")
|
||||
|
||||
var cadence_world = load("res://world/owniverse/owniverse.gd").new()
|
||||
for i in 3: cadence_world.update_adaptive_quality(350000)
|
||||
check(cadence_world.cadence_level == 3, "Sustained 350 ms cycles select the 2 Hz cadence")
|
||||
check(is_equal_approx(cadence_world.frame_step_seconds, 0.5), "The lowest cadence uses a 0.5 second frame step")
|
||||
cadence_world.free()
|
||||
|
||||
var rows: Array = []
|
||||
for node in 12:
|
||||
var edges: Dictionary = {}
|
||||
var outgoing: float = 0.0
|
||||
if node < 6:
|
||||
edges[node + 6] = 0.25
|
||||
outgoing = 0.25
|
||||
rows.append({"edges": edges, "outgoing": outgoing})
|
||||
var generator: Dictionary = {"rows": rows, "index": {"source": 0}}
|
||||
var solved: Dictionary = world.civilization_flow._solve_single_step(generator, "source", 2.0)
|
||||
for ethic in 6:
|
||||
var mass: float = 0.0
|
||||
var occupation: float = 0.0
|
||||
for node in range(ethic, 12, 6):
|
||||
mass += solved.mass[node]
|
||||
occupation += solved.occupation[node]
|
||||
check(absf(mass - 1.0) < 1e-12, "Approximate transition conserves ethic mass %d" % ethic)
|
||||
check(absf(occupation - 2.0) < 1e-12, "Approximate transition conserves integrated time %d" % ethic)
|
||||
|
||||
world.free()
|
||||
print("Adaptive quality: %d checks, %d failures" % [checks, failures])
|
||||
quit(1 if failures else 0)
|
||||
@@ -0,0 +1 @@
|
||||
uid://ct356urggcgal
|
||||
@@ -0,0 +1,78 @@
|
||||
extends "res://tests/test_civilization_evolution.gd"
|
||||
|
||||
func run():
|
||||
var rules: Dictionary = root.get_node("Global").CivilizationRules
|
||||
for count in [103, E.of("10000000000000000000000000000000000000103"), E.add(E.of("1e400"), 103)]:
|
||||
var world = fixture()
|
||||
var third: Variant = E.floor_value(E.divide(count, 3))
|
||||
var free: Variant = E.subtract(count, E.multiply(third, 2))
|
||||
world.state.amount["C0.2"] = free
|
||||
world.state.binds.RD = {"C0.2": third}
|
||||
world.state.binds.OD = {"C0.2": third}
|
||||
var counts: Dictionary = {"C0.2": third, "C0.3": third, "HP": free}
|
||||
var profile: Dictionary = {"m": 0.4, "l": 0.2, "w": 0.1, "d": 0.1, "c": 0.1, "b": 0.1}
|
||||
var profiles: Dictionary = {}
|
||||
for id in counts: profiles[id] = profile
|
||||
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var initial_physical: Dictionary = physical.duplicate()
|
||||
var committed: Dictionary = world.civilization_flow._commit(world.state, physical, {"C0.2": {"counts": counts, "profiles": profiles}}, rules, 100)
|
||||
check(E.eq(committed, world.ledger.physical_amounts(world.state)), "Commit snapshot matches a full physical rebuild")
|
||||
check(E.eq(physical, initial_physical), "Commit does not mutate its input snapshot")
|
||||
for id in counts:
|
||||
check(E.eq(world.ledger.physical_amount(world.state, id), counts[id]), "Commit preserves exact destination quota: " + id)
|
||||
for owner in ["RD", "OD"]:
|
||||
var held: Variant = 0
|
||||
for id in counts: held = E.add(held, world.state.binds[owner].get(id, 0))
|
||||
check(E.eq(held, third), "Commit preserves each owner's exact population")
|
||||
for id in ["C0.2", "C0.3"]:
|
||||
for ethic in profile: check(absf(world.state.ethics[id][ethic] - profile[ethic]) < 1e-12, "Commit preserves incoming ethical profile")
|
||||
invariants(world)
|
||||
dispose(world)
|
||||
world = fixture()
|
||||
world.state.binds.RD = {"C4.4": count}
|
||||
var terminal: Dictionary = {"Ct": State.empty_profile()}
|
||||
world.civilization_flow._commit(world.state, world.ledger.physical_amounts(world.state), {"C4.4": {"counts": {"Ct": count}, "profiles": terminal}}, rules, 100)
|
||||
check(E.eq(world.state.amount.Ct, count), "Transcendent population is entirely free")
|
||||
check(E.eq(world.state.binds.RD.get("Ct", 0), 0), "Transcendent population cannot retain a stellar owner")
|
||||
dispose(world)
|
||||
world = fixture()
|
||||
world.state.amount["C2.0"] = count
|
||||
var initial_capacity: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var frozen_capacity: Dictionary = initial_capacity.duplicate()
|
||||
var constrained: Dictionary = State.enforce_capacity(world, world.state, rules, 100, initial_capacity)
|
||||
check(E.eq(constrained, world.ledger.physical_amounts(world.state)), "Cascading capacity changes maintain exact physical totals")
|
||||
check(E.eq(initial_capacity, frozen_capacity), "Capacity correction does not mutate the previous snapshot")
|
||||
dispose(world)
|
||||
world = fixture()
|
||||
var original_owners: Dictionary = {}
|
||||
for owner in ["RD", "OD", "YD", "WYD"]:
|
||||
world.state.binds[owner] = {}
|
||||
for resource in ["GG", "AB", "RP", "HP"]:
|
||||
world.state.binds[owner][resource] = count
|
||||
original_owners[owner] = E.multiply(count, 4)
|
||||
var colonies: Dictionary = {
|
||||
"C1.4": {"counts": {"C1.4": E.multiply(count, 3)}, "profiles": {"C1.4": profile}},
|
||||
"C1.5": {"counts": {"C1.5": E.multiply(count, 5)}, "profiles": {"C1.5": profile}}
|
||||
}
|
||||
var physical_before: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var untouched: Dictionary = physical_before.duplicate()
|
||||
var colonized: Dictionary = world.civilization_flow._colonize(world.state, colonies, 100, rules, physical_before)
|
||||
check(E.eq(colonized, world.ledger.physical_amounts(world.state)), "Colonization snapshot matches a full physical rebuild")
|
||||
check(E.eq(physical_before, untouched), "Colonization does not mutate its input snapshot")
|
||||
var capacity: Dictionary = State.enforce_capacity(world, world.state, rules, 100, colonized)
|
||||
check(E.eq(capacity, world.ledger.physical_amounts(world.state)), "Capacity snapshot matches a full physical rebuild")
|
||||
check(E.eq(world.ledger.physical_amount(world.state, "C1.4"), E.multiply(count, 3)), "Colonization credits exact first quota")
|
||||
check(E.eq(world.ledger.physical_amount(world.state, "C1.5"), E.multiply(count, 5)), "Colonization credits exact second quota")
|
||||
check(E.eq(world.state.stats.total_amount["C1.4"], E.multiply(count, 3)), "Consolidated credit preserves creation statistics")
|
||||
check(E.eq(world.state.civilization_stats.colonies, E.multiply(count, 8)), "Colonization statistics equal exact granted total")
|
||||
for owner in original_owners:
|
||||
var held: Variant = 0
|
||||
for id in ["GG", "AB", "RP", "HP", "C1.4", "C1.5"]: held = E.add(held, world.state.binds[owner].get(id, 0))
|
||||
check(E.eq(held, original_owners[owner]), "Colonization preserves each owner's occupied worlds")
|
||||
var remaining_planets: Variant = 0
|
||||
for id in ["GG", "AB", "RP", "HP"]: remaining_planets = E.add(remaining_planets, world.ledger.physical_amount(world.state, id))
|
||||
check(E.eq(remaining_planets, E.multiply(count, 8)), "Planet loss equals colonies created")
|
||||
invariants(world)
|
||||
dispose(world)
|
||||
print("Flow commit: %d checks, %d failures" % [checks, failures])
|
||||
quit(1 if failures else 0)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bfgdsxoycr20e
|
||||
@@ -38,21 +38,25 @@ func run():
|
||||
sliced.evolute_frame(0.2, self)
|
||||
check(sliced.frame_busy, "Heavy transaction yields before publishing")
|
||||
check(E.eq(sliced.state.time.year, 0), "Published clock stays at completed transaction")
|
||||
sliced.request_production("H", 1, true)
|
||||
check(E.eq(sliced.display_clock().year, Clock.advance({"datetime": 0, "year": 0}, 0.2, sliced.speed).year), "Display clock advances independently of the heavy transaction")
|
||||
check(sliced.request_production("H", 1, true), "Production requested during a frame is queued")
|
||||
check(sliced.frame_checkpoint().productions.size() == 1, "Queued production remains part of the save checkpoint")
|
||||
check(E.eq(sliced.state.amount.H, initial_h), "Player action waits for transaction commit")
|
||||
sliced.evolute_frame(0.01, self)
|
||||
var requested: Dictionary = sliced.frame_clock.duplicate()
|
||||
sliced.speed = 3
|
||||
check(sliced.speed == 3 and sliced.state.speed == 6, "Speed change is visible and cannot mutate an unfinished transaction")
|
||||
while sliced.frame_busy: await process_frame
|
||||
while sliced.frame_busy or sliced.production_queue_busy: await process_frame
|
||||
direct.evolute(0.2)
|
||||
direct.production.produce_hs("H", 1)
|
||||
direct.speed = 3
|
||||
check(E.eq(direct.state, sliced.state), "Queued action and speed change survive publication")
|
||||
sliced.evolute_frame(0.001, self)
|
||||
var second_clock: Dictionary = Clock.advance(requested, 0.001, 3)
|
||||
check(not sliced.frame_busy, "Speed change does not bypass real-time cadence")
|
||||
sliced.evolute_frame(0.189, self)
|
||||
var second_clock: Dictionary = Clock.advance(Clock.advance(requested, 0.001, 3), 0.189, 3)
|
||||
var active: Dictionary = sliced.frame_active_clock.duplicate()
|
||||
while sliced.frame_busy: await process_frame
|
||||
while sliced.frame_busy or sliced.production_queue_busy: await process_frame
|
||||
direct.evolution.evolute_to_datetime(active.datetime, active)
|
||||
check(E.eq(direct.state, sliced.state), "Bounded frame transaction matches its exact target clock")
|
||||
check(E.eq(sliced.frame_clock, second_clock), "All elapsed time during a busy frame remains in the requested clock")
|
||||
@@ -75,7 +79,7 @@ func run():
|
||||
expected.state = published.duplicate(true)
|
||||
resumed.restore_frame_checkpoint(checkpoint)
|
||||
resumed.evolute_frame(0.0, self)
|
||||
while resumed.frame_busy: await process_frame
|
||||
while resumed.frame_busy or resumed.production_queue_busy: await process_frame
|
||||
expected.evolution.evolute_to_datetime(expected_clock.datetime, expected_clock)
|
||||
expected.production.produce_hs("H", 123)
|
||||
expected.speed = 3
|
||||
@@ -111,11 +115,25 @@ func run():
|
||||
var behind = fixture()
|
||||
behind.evolute_frame(1.0, self)
|
||||
while behind.frame_busy: await process_frame
|
||||
check(E.eq(behind.state.time.datetime, 20000) and E.eq(behind.frame_clock.datetime, 100000), "Debt keeps the fixed step and preserves all requested time")
|
||||
for cycle in 4:
|
||||
check(E.eq(behind.state.time.datetime, 100000) and E.eq(behind.state.time, behind.frame_clock), "Delayed start consumes all elapsed time in one cycle")
|
||||
behind.evolute_frame(0.0, self)
|
||||
check(behind.frame_commits == 1 and not behind.frame_busy, "Consumed interval cannot be processed twice")
|
||||
var next_interval: float = behind.frame_step_seconds + 0.03
|
||||
behind.evolute_frame(next_interval, self)
|
||||
var remained_busy: bool = behind.frame_busy
|
||||
if remained_busy: behind.evolute_frame(0.53, self)
|
||||
var pending_clock: Dictionary = behind.frame_clock.duplicate()
|
||||
if remained_busy:
|
||||
var expected_active: Dictionary = Clock.advance({"datetime": 100000, "year": 100000}, next_interval, behind.speed)
|
||||
check(E.eq(behind.frame_active_clock, expected_active), "Busy transaction reserves its original target")
|
||||
while behind.frame_busy: await process_frame
|
||||
if remained_busy:
|
||||
check(E.eq(behind.state.time, behind.frame_active_clock), "Busy transaction keeps its original target")
|
||||
behind.evolute_frame(0.0, self)
|
||||
while behind.frame_busy: await process_frame
|
||||
check(behind.frame_commits == 5 and E.eq(behind.state.time, behind.frame_clock), "One second is processed in exactly five full cycles")
|
||||
check(E.eq(behind.state.time, pending_clock), "Next cycle consumes all time accumulated during computation")
|
||||
else:
|
||||
check(E.eq(behind.state.time, pending_clock), "Completed transaction publishes the full requested interval")
|
||||
dispose(behind)
|
||||
print("Frame evolution: %d checks, %d failures" % [checks, failures])
|
||||
quit(1 if failures else 0)
|
||||
|
||||
@@ -59,14 +59,27 @@ func run() -> void:
|
||||
world.state.auto_resources = {"H": 10}
|
||||
world.state.batches.H = [{"amount": 10, "start_year": 1, "end_year": 2}]
|
||||
var before: Dictionary = world.state.duplicate(true)
|
||||
check((not world.production.try_production(world.state, world.catalog.entities.N, 1, true)), "Binding failure rejects production")
|
||||
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var frozen_physical: Dictionary = physical.duplicate()
|
||||
check((not world.production.try_production(world.state, world.catalog.entities.N, 1, true, physical)), "Binding failure rejects production")
|
||||
check(E.eq(physical, frozen_physical), "Failed production preserves the shared quantity snapshot")
|
||||
check(E.eq(world.state, before), "Failure preserves costs, batches and budget")
|
||||
world.state.amount.S = 10
|
||||
physical = world.ledger.physical_amounts(world.state)
|
||||
var old_batches: Array = world.state.batches.H
|
||||
check(world.production.try_production(world.state, world.catalog.entities.N, 2, true), "Funded production succeeds")
|
||||
check(world.production.try_production(world.state, world.catalog.entities.N, 2, true, physical), "Funded production succeeds")
|
||||
check(E.eq(physical, world.ledger.physical_amounts(world.state)), "Successful production updates exact totals without changing bound-resource totals")
|
||||
check((E.eq(world.state.amount.H, 6) and E.eq(world.state.amount.S, 4)), "Exact cost and binding")
|
||||
check(E.eq(world.state.auto_resources.H, 6), "Exact automatic budget")
|
||||
check((E.eq(old_batches[0].amount, 10) and E.eq(world.state.batches.H[0].amount, 6)), "Consumed batches isolated")
|
||||
|
||||
for member in world.catalog.members("Star"): world.state.amount[member] = 0
|
||||
var star_stocks: Array = ["1234567890123456789", "876543210987654321", "3141592653589793238", "2718281828459045235", "1414213562373095048", "1732050807568877293", "2236067977499789696", "2449489742783178098", "3162277660168379331"]
|
||||
for i in star_stocks.size(): world.state.amount[world.catalog.members("Star")[i]] = E.of(star_stocks[i])
|
||||
var scd_request: Variant = E.of("1e15")
|
||||
var scd_created: Variant = world.production.produce("SCD", scd_request)
|
||||
check(E.eq(scd_created, scd_request), "Large SCD production distributes a whole star quota exactly")
|
||||
check(E.eq(world.state.amount.SCD, scd_request), "Large SCD creation is committed")
|
||||
print(("Transaction: %d checks, %d failures" % [checks, failures]))
|
||||
dispose(world)
|
||||
quit((1 if failures else 0))
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
extends SceneTree
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const Report = preload("res://ui/report/report.gd")
|
||||
class Ledger extends RefCounted:
|
||||
func physical_amounts(_source: Dictionary) -> Dictionary:
|
||||
return {"RD": 3, "HG": 1}
|
||||
class Catalog extends RefCounted:
|
||||
var entities: Dictionary = {}
|
||||
func is_group(_id: String) -> bool: return false
|
||||
class World extends RefCounted:
|
||||
var catalog = Catalog.new()
|
||||
var ledger = Ledger.new()
|
||||
var state = {"amount": {}, "stats": {"total_amount": {"H": 1234}}, "world_parameters": {"Federation": 1, "Enlightenment": 1, "Order": 1}}
|
||||
func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
func run() -> void:
|
||||
var global: Node = root.get_node("Global")
|
||||
var world = World.new()
|
||||
world.state.stats.total_amount.H = E.of("1234567890123456789012345")
|
||||
var before: Dictionary = E.encode(world.state)
|
||||
global.Locale = "en"
|
||||
var text: String = Report.build(world, global)
|
||||
assert(text.contains("75.00%") and text.contains("25.00%"), text)
|
||||
assert(text.contains("1 234 567 890 123 456 789 012 345"), text)
|
||||
assert(text.contains("Total resources obtained") and not text.contains("Total entities emerged"), text)
|
||||
world.state.world_parameters.Federation = E.of("1.3")
|
||||
text = Report.build(world, global)
|
||||
assert(text.contains("Federation:") and text.contains("0.30"), text)
|
||||
world.state.world_parameters.Federation = 1
|
||||
global.Locale = "ru"
|
||||
text = Report.build(world, global)
|
||||
assert(text.contains("Всего получено ресурсов") and text.contains("Красные карлики"), text)
|
||||
assert(E.encode(world.state) == before, "Report mutated simulation")
|
||||
assert(load("res://stage/stage.gd") != null)
|
||||
print("Universe report checks: PASS")
|
||||
quit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://brb04x12elvqt
|
||||
@@ -0,0 +1,206 @@
|
||||
extends Node
|
||||
## Player-wide achievement progress and the shared in-game notification queue.
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const ITEM = preload("res://ui/achievements/achievement_item.tscn")
|
||||
const TITLES = {
|
||||
"en": preload("res://ui/achievements/achievements.en.png"),
|
||||
"ru": preload("res://ui/achievements/achievements.ru.png"),
|
||||
}
|
||||
var progress_path: String = "user://achievements.cfg"
|
||||
const HOLD_SECONDS = 3.0
|
||||
const FADE_SECONDS = 0.3
|
||||
|
||||
var global: Node
|
||||
var stage: Control
|
||||
var definitions: Dictionary = {}
|
||||
var conditions: Dictionary = {}
|
||||
var progress: Dictionary = {}
|
||||
var items: Dictionary = {}
|
||||
var queue: Array[Dictionary] = []
|
||||
var current: Dictionary = {}
|
||||
var elapsed: float = 0.0
|
||||
var last_revision: int = -1
|
||||
var last_world: Object
|
||||
var locale: String = ""
|
||||
var panel: PanelContainer
|
||||
var notification_item: Control
|
||||
var headers: Array[Control] = []
|
||||
var status: Label
|
||||
var list: VBoxContainer
|
||||
var storage_available: bool = true
|
||||
|
||||
func initialize(owner_stage: Control) -> void:
|
||||
stage = owner_stage
|
||||
global = get_tree().root.get_node("Global")
|
||||
locale = global.Locale
|
||||
for id in global.Achievements.en:
|
||||
var row: Dictionary = global.Achievements.en[id]
|
||||
if str(row.Ignore) == "1": continue
|
||||
definitions[id] = row
|
||||
conditions[id] = {
|
||||
"open": _parse_condition(str(row.OpenCondition)),
|
||||
"achieve": _parse_condition(str(row.AchieveCondition)),
|
||||
}
|
||||
_load_progress()
|
||||
list = stage.get_node("Achievements/AchievementList/AchievementItems")
|
||||
for child in list.get_children():
|
||||
list.remove_child(child)
|
||||
child.queue_free()
|
||||
status = Label.new()
|
||||
status.add_theme_font_size_override("font_size", 32)
|
||||
list.add_child(status)
|
||||
var pages: Node = stage.get_node("World/PageScroller/HBoxContainer")
|
||||
for path in ["ForcePage/ForceHeader", "ResourcePage/ResourceHeader", "StarPage/StarHeader", "StructurePage/StructureHeader", "LifePage/LifeHeader"]:
|
||||
headers.append(pages.get_node(path))
|
||||
panel = PanelContainer.new()
|
||||
panel.name = "AchievementNotification"
|
||||
panel.set_as_top_level(true)
|
||||
panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
panel.gui_input.connect(_on_notification_input)
|
||||
panel.z_index = 20
|
||||
stage.get_node("World").add_child(panel)
|
||||
notification_item = ITEM.instantiate()
|
||||
panel.add_child(notification_item)
|
||||
_ignore_notification_children(notification_item)
|
||||
panel.hide()
|
||||
_rebuild_list()
|
||||
|
||||
func _parse_condition(expression: String) -> Dictionary:
|
||||
if expression.is_empty(): return {}
|
||||
var parts := expression.split(":")
|
||||
assert(parts.size() == 3 and parts[0] in ["age", "total"], "Unsupported achievement condition: " + expression)
|
||||
return {"type": parts[0], "id": parts[1], "amount": E.of(parts[2])}
|
||||
|
||||
func _matches(condition: Dictionary, source: Dictionary) -> bool:
|
||||
if condition.is_empty(): return true
|
||||
var amount: Variant = source.time.year if condition.type == "age" else source.stats.total_amount.get(condition.id, 0)
|
||||
return E.ge(amount, condition.amount)
|
||||
|
||||
func update(delta: float) -> void:
|
||||
if global.Locale != locale:
|
||||
locale = global.Locale
|
||||
_rebuild_list()
|
||||
if not current.is_empty(): _display_event(current)
|
||||
var world = stage.owniverse
|
||||
if world != last_world or world.presentation.revision != last_revision:
|
||||
last_world = world
|
||||
last_revision = world.presentation.revision
|
||||
_check(world.state)
|
||||
# Menu time does not consume a notification's display time.
|
||||
if not stage.screens.World.visible: return
|
||||
if current.is_empty() and not queue.is_empty():
|
||||
current = queue.pop_front()
|
||||
elapsed = 0.0
|
||||
_display_event(current)
|
||||
if current.is_empty(): return
|
||||
elapsed += delta
|
||||
var page: int = clampi(roundi(float(stage.stage_page_scroller.scroll_horizontal) / stage.PAGE_WIDTH), 0, headers.size() - 1)
|
||||
for i in headers.size():
|
||||
headers[i].modulate.a = 0.0 if i == page else 1.0
|
||||
panel.global_position = headers[page].global_position
|
||||
panel.size = Vector2(headers[page].size.x, maxf(128.0, headers[page].size.y))
|
||||
panel.modulate.a = clampf(1.0 - (elapsed - HOLD_SECONDS) / FADE_SECONDS, 0.0, 1.0)
|
||||
if elapsed >= HOLD_SECONDS + FADE_SECONDS:
|
||||
dismiss_notification()
|
||||
|
||||
func _ignore_notification_children(node: Node) -> void:
|
||||
if node is Control: node.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
for child in node.get_children(): _ignore_notification_children(child)
|
||||
|
||||
func _on_notification_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||
panel.accept_event()
|
||||
dismiss_notification()
|
||||
elif event is InputEventScreenTouch and event.pressed:
|
||||
panel.accept_event()
|
||||
dismiss_notification()
|
||||
|
||||
func dismiss_notification() -> void:
|
||||
panel.hide()
|
||||
current = {}
|
||||
elapsed = 0.0
|
||||
for header in headers: header.modulate.a = 1.0
|
||||
|
||||
func _check(source: Dictionary) -> void:
|
||||
var changed := false
|
||||
for id in definitions:
|
||||
var saved: Dictionary = progress.get(id, {"visible": false, "unlocked": false})
|
||||
if bool(saved.unlocked): continue
|
||||
var rule: Dictionary = conditions[id]
|
||||
if _matches(rule.achieve, source):
|
||||
saved.visible = true
|
||||
saved.unlocked = true
|
||||
_enqueue(id, "c")
|
||||
changed = true
|
||||
elif not bool(saved.visible) and _matches(rule.open, source):
|
||||
saved.visible = true
|
||||
# An empty opening condition makes an item visible without an alert.
|
||||
if not rule.open.is_empty(): _enqueue(id, "g")
|
||||
changed = true
|
||||
progress[id] = saved
|
||||
if changed:
|
||||
_save_progress()
|
||||
_rebuild_list()
|
||||
|
||||
func _enqueue(id: String, event_type: String) -> void:
|
||||
for event in queue:
|
||||
if event.id == id:
|
||||
event.type = event_type
|
||||
return
|
||||
queue.append({"id": id, "type": event_type})
|
||||
|
||||
func _text(id: String) -> String:
|
||||
var row: Dictionary = global.Achievements[locale][id]
|
||||
var prefix: String = "Ru" if locale == "ru" else "En"
|
||||
var title: String = str(row[prefix + "1"])
|
||||
var description: String = str(row[prefix + "2"])
|
||||
return "[b]" + title + "[/b]" + ("\n" + description if not description.is_empty() else "")
|
||||
|
||||
func _display_event(event: Dictionary) -> void:
|
||||
notification_item.open(int(definitions[event.id].Points), event.type, _text(event.id))
|
||||
panel.modulate.a = 1.0
|
||||
panel.show()
|
||||
|
||||
func _rebuild_list() -> void:
|
||||
var title: TextureRect = stage.get_node_or_null("Achievements/Header/Title")
|
||||
if title != null: title.texture = TITLES[locale]
|
||||
var unlocked := 0
|
||||
var position := 1
|
||||
for id in definitions:
|
||||
var saved: Dictionary = progress.get(id, {})
|
||||
if not bool(saved.get("visible", false)) and not bool(saved.get("unlocked", false)): continue
|
||||
if not items.has(id):
|
||||
var item: Control = ITEM.instantiate()
|
||||
list.add_child(item)
|
||||
items[id] = item
|
||||
list.move_child(items[id], position)
|
||||
position += 1
|
||||
var completed: bool = bool(saved.get("unlocked", false))
|
||||
if completed: unlocked += 1
|
||||
items[id].open(int(definitions[id].Points), "c" if completed else "g", _text(id))
|
||||
var row: Dictionary = global.get_template("Achievement-Status-Template", "Interface", "Header")
|
||||
status.text = global.render_template(row, {"count": unlocked, "total": definitions.size()})
|
||||
|
||||
func _load_progress() -> void:
|
||||
var config := ConfigFile.new()
|
||||
var error := config.load(progress_path)
|
||||
if error == ERR_FILE_NOT_FOUND: return
|
||||
if error != OK:
|
||||
storage_available = false
|
||||
push_error("Cannot load achievement progress: " + error_string(error))
|
||||
return
|
||||
for id in definitions:
|
||||
progress[id] = {"visible": bool(config.get_value(id, "visible", false)), "unlocked": bool(config.get_value(id, "unlocked", false))}
|
||||
|
||||
func _save_progress() -> void:
|
||||
if not storage_available: return
|
||||
var config := ConfigFile.new()
|
||||
# Preserve entries for achievements temporarily disabled in the catalog.
|
||||
config.load(progress_path)
|
||||
for id in progress:
|
||||
config.set_value(id, "visible", bool(progress[id].visible))
|
||||
config.set_value(id, "unlocked", bool(progress[id].unlocked))
|
||||
var error := config.save(progress_path + ".tmp")
|
||||
if error == OK:
|
||||
error = DirAccess.rename_absolute(progress_path + ".tmp", progress_path)
|
||||
if error != OK: push_error("Cannot save achievement progress: " + error_string(error))
|
||||
@@ -0,0 +1 @@
|
||||
uid://c1x220s0rloxc
|
||||
@@ -0,0 +1,28 @@
|
||||
extends Control
|
||||
|
||||
@onready var icons = {
|
||||
"1c": $Row/Icon1c,
|
||||
"1g": $Row/Icon1g,
|
||||
"3c": $Row/Icon3c,
|
||||
"3g": $Row/Icon3g,
|
||||
"6c": $Row/Icon6c,
|
||||
"6g": $Row/Icon6g,
|
||||
"10c": $Row/Icon10c,
|
||||
"10g": $Row/Icon10g,
|
||||
}
|
||||
|
||||
@onready var event_label: RichTextLabel = $Row/Label
|
||||
|
||||
## Shows this achievement event with the matching points/state icon.
|
||||
## event_type is "c" for completed or "g" for revealed but not completed.
|
||||
func open(level: int, event_type: String, event_text: String) -> void:
|
||||
var icon_key := str(level) + event_type.to_lower()
|
||||
if not icons.has(icon_key):
|
||||
push_warning("Unknown achievement icon: %s" % icon_key)
|
||||
return
|
||||
|
||||
for key: String in icons:
|
||||
icons[key].visible = key == icon_key
|
||||
|
||||
event_label.text = event_text
|
||||
visible = true
|
||||
@@ -0,0 +1 @@
|
||||
uid://b6sqyd1tpljr3
|
||||
@@ -0,0 +1,83 @@
|
||||
[gd_scene format=3 uid="uid://cs8a5u3ecf51t"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b6sqyd1tpljr3" path="res://ui/achievements/achievement_item.gd" id="1_w8f20"]
|
||||
[ext_resource type="Texture2D" uid="uid://cfohmmlwh84gk" path="res://ui/achievements/icons/1c.png" id="2_7ntr3"]
|
||||
[ext_resource type="Texture2D" uid="uid://k6dqaewr30qe" path="res://ui/achievements/icons/1g.png" id="3_1b655"]
|
||||
[ext_resource type="Texture2D" uid="uid://bccs26gkwsio3" path="res://ui/achievements/icons/3c.png" id="4_xgo6w"]
|
||||
[ext_resource type="Texture2D" uid="uid://c7regmaoq0dr5" path="res://ui/achievements/icons/3g.png" id="5_7pkbt"]
|
||||
[ext_resource type="Texture2D" uid="uid://bl7dr5fapao0x" path="res://ui/achievements/icons/6c.png" id="6_aart1"]
|
||||
[ext_resource type="Texture2D" uid="uid://bk5morikm4k5o" path="res://ui/achievements/icons/6g.png" id="7_ndc4d"]
|
||||
[ext_resource type="Texture2D" uid="uid://bs54rppydwlvy" path="res://ui/achievements/icons/10c.png" id="8_20e5y"]
|
||||
[ext_resource type="Texture2D" uid="uid://bmp7e6lmtaex0" path="res://ui/achievements/icons/10g.png" id="9_3xyrb"]
|
||||
|
||||
[node name="AchievementItem" type="Control" unique_id=1273881744]
|
||||
custom_minimum_size = Vector2(1080, 128)
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1_w8f20")
|
||||
|
||||
[node name="Row" type="HBoxContainer" parent="." unique_id=2025386077]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="Icon1c" type="TextureRect" parent="Row" unique_id=2146478339]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("2_7ntr3")
|
||||
|
||||
[node name="Icon1g" type="TextureRect" parent="Row" unique_id=2089044466]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("3_1b655")
|
||||
|
||||
[node name="Icon3c" type="TextureRect" parent="Row" unique_id=501141005]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("4_xgo6w")
|
||||
|
||||
[node name="Icon3g" type="TextureRect" parent="Row" unique_id=844167957]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("5_7pkbt")
|
||||
|
||||
[node name="Icon6c" type="TextureRect" parent="Row" unique_id=2137074157]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("6_aart1")
|
||||
|
||||
[node name="Icon6g" type="TextureRect" parent="Row" unique_id=616115687]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("7_ndc4d")
|
||||
|
||||
[node name="Icon10c" type="TextureRect" parent="Row" unique_id=418038490]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("8_20e5y")
|
||||
|
||||
[node name="Icon10g" type="TextureRect" parent="Row" unique_id=1352890506]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("9_3xyrb")
|
||||
|
||||
[node name="Label" type="RichTextLabel" parent="Row" unique_id=230945261]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_font_sizes/normal_font_size = 30
|
||||
theme_override_font_sizes/bold_font_size = 30
|
||||
bbcode_enabled = true
|
||||
text = "123"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
vertical_alignment = 1
|
||||
Binary file not shown.
Binary file not shown.
@@ -58,6 +58,13 @@ func _set_action_values(values: Array, displayed: Array) -> void:
|
||||
action_labels[ind].text = text
|
||||
action_labels[ind].get_parent().get_node("TextureButton").disabled = E.le(values[ind], 0)
|
||||
|
||||
func mark_pending(amount: Variant) -> void:
|
||||
for ind in range(5):
|
||||
if not E.eq(action_values[ind], amount): continue
|
||||
action_labels[ind].text = "…"
|
||||
action_labels[ind].get_parent().get_node("TextureButton").disabled = true
|
||||
return
|
||||
|
||||
|
||||
func _format_action_value(value: Variant) -> String:
|
||||
if ((E.lt(value, 1) or (not E.finite(value))) or E.ne(value, E.floor_value(value))):
|
||||
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,63 @@
|
||||
extends VBoxContainer
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const Presentation = preload("res://world/owniverse/item_presentation.gd")
|
||||
const STARS = ["RD", "OD", "YD", "WYD", "WS", "BG", "SG", "HG"]
|
||||
const EFFECTS = {"Int.Fed": "Federation", "Int.Enl": "Enlightenment", "Int.Ord": "Order"}
|
||||
|
||||
var report_locale: String = ""
|
||||
|
||||
@onready var report_body: RichTextLabel = $ReportBody
|
||||
@onready var report_header: TextureRect = $Header/Header
|
||||
|
||||
func _ready() -> void:
|
||||
visibility_changed.connect(_on_visibility_changed)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if is_visible_in_tree() and report_locale != get_node("/root/Global").Locale:
|
||||
refresh()
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if is_node_ready() and is_visible_in_tree():
|
||||
refresh()
|
||||
report_body.scroll_to_line(0)
|
||||
|
||||
func refresh() -> void:
|
||||
var global: Node = get_node("/root/Global")
|
||||
report_locale = global.Locale
|
||||
report_body.text = build(get_parent().owniverse, global)
|
||||
report_header.texture = load("res://ui/report/" + report_locale + "/report.png")
|
||||
|
||||
## Formats existing committed state only. No historical counters or simulation work.
|
||||
static func build(world, global: Node) -> String:
|
||||
var source: Dictionary = world.state
|
||||
var totals: Dictionary = Presentation.amounts(source, world.catalog, true)
|
||||
var physical: Dictionary = world.ledger.physical_amounts(source)
|
||||
var star_total: Variant = 0
|
||||
for id in STARS: star_total = E.add(star_total, physical.get(id, 0))
|
||||
var blocks: PackedStringArray = []
|
||||
var lines: PackedStringArray = []
|
||||
var header: String = ""
|
||||
# Russian keys provide the common order; get_template resolves both key formats.
|
||||
for key in global.Templates.ru:
|
||||
if not str(key).contains(".Report."): continue
|
||||
var parts: PackedStringArray = str(key).rsplit(".Report.", true, 1)
|
||||
var id: String = parts[0]
|
||||
var kind: String = parts[1]
|
||||
var row: Dictionary = global.get_template(id, "Report", kind)
|
||||
if kind == "Header":
|
||||
if not lines.is_empty(): blocks.append(header + "\n\n" + "\n".join(lines))
|
||||
lines.clear()
|
||||
header = global.render_template(row, {})
|
||||
elif kind == "Fraction":
|
||||
var amount: Variant = physical.get(id, 0)
|
||||
if E.gt(amount, 0) and E.gt(star_total, 0):
|
||||
var percent: String = E.grouped(E.mul_div(amount, 100, star_total), 2, false) + "%"
|
||||
lines.append(global.render_template(row, {"proportion": percent}))
|
||||
elif kind == "Max":
|
||||
var amount: Variant = totals.get(id, 0)
|
||||
if EFFECTS.has(id):
|
||||
amount = E.subtract(source.world_parameters.get(EFFECTS[id], 1), 1)
|
||||
if E.gt(amount, 0):
|
||||
lines.append(global.render_template(row, {"max": E.grouped(amount, 2 if EFFECTS.has(id) else 0, false)}))
|
||||
if not lines.is_empty(): blocks.append(header + "\n\n" + "\n".join(lines))
|
||||
return "\n\n".join(blocks)
|
||||
@@ -0,0 +1 @@
|
||||
uid://v8h4ov33opyf
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,169 @@
|
||||
extends "res://world/owniverse/simulation_module.gd"
|
||||
class_name OwniverseApocalypseController
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
|
||||
|
||||
const TICK_SECONDS: float = 0.1
|
||||
const END_WAIT_TICKS: int = 20
|
||||
|
||||
var _schedule: Array = []
|
||||
|
||||
func defaults() -> Dictionary:
|
||||
return {"phase": 0, "type": 0, "warning": 0, "stage": 1, "tick": 0, "end_ticks": 0}
|
||||
|
||||
|
||||
func check_density(target: Dictionary) -> void:
|
||||
var apocalypse: Dictionary = target.get("apocalypse", defaults())
|
||||
if E.gt(apocalypse.get("phase", 0), 0): return
|
||||
var density: Variant = world.environment.stellar_environment(target).density
|
||||
var parameters: Dictionary = WorldParameters.extra_params()
|
||||
var warning: int = 0
|
||||
if E.ge(density, parameters.bigcrunch3): warning = 3
|
||||
elif E.ge(density, parameters.bigcrunch2): warning = 2
|
||||
elif E.ge(density, parameters.bigcrunch1): warning = 1
|
||||
elif E.le(density, parameters.bigrip3): warning = -3
|
||||
elif E.le(density, parameters.bigrip2): warning = -2
|
||||
elif E.le(density, parameters.bigrip1): warning = -1
|
||||
apocalypse.warning = warning
|
||||
target.apocalypse = apocalypse
|
||||
if E.eq(warning, 3):
|
||||
start(target, 3)
|
||||
elif E.eq(warning, -3):
|
||||
start(target, -3)
|
||||
|
||||
|
||||
func start(target: Dictionary, event_type: int) -> void:
|
||||
var apocalypse: Dictionary = target.get("apocalypse", defaults()).duplicate(true)
|
||||
if E.gt(apocalypse.phase, 0): return
|
||||
apocalypse.phase = 1
|
||||
apocalypse.type = event_type
|
||||
apocalypse.stage = 1
|
||||
apocalypse.tick = 0
|
||||
apocalypse.end_ticks = 0
|
||||
target.apocalypse = apocalypse
|
||||
target.speed = 1
|
||||
target.speed_in_year = 1
|
||||
_schedule = _make_schedule("SMBH" if event_type > 0 else "S")
|
||||
|
||||
|
||||
func process_tick(target: Dictionary) -> Dictionary:
|
||||
var result: Dictionary = {"screen": -1, "finished": false}
|
||||
var apocalypse: Dictionary = target.get("apocalypse", defaults())
|
||||
if E.ne(apocalypse.get("phase", 0), 1): return result
|
||||
if _schedule.is_empty(): _schedule = _make_schedule("SMBH" if E.gt(apocalypse.type, 0) else "S")
|
||||
apocalypse.tick = E.add(apocalypse.tick, 1)
|
||||
var stage_rows: Array = []
|
||||
for row: Dictionary in _schedule:
|
||||
if E.eq(row.stage, apocalypse.stage): stage_rows.append(row)
|
||||
if stage_rows.is_empty():
|
||||
var next_stage: Variant = INF
|
||||
for row: Dictionary in _schedule:
|
||||
if E.gt(row.stage, apocalypse.stage): next_stage = E.minimum(next_stage, row.stage)
|
||||
if E.finite(next_stage):
|
||||
apocalypse.stage = E.integer(next_stage)
|
||||
apocalypse.tick = 0
|
||||
else:
|
||||
_process_final_energy(target, apocalypse)
|
||||
if E.eq(apocalypse.end_ticks, END_WAIT_TICKS):
|
||||
apocalypse.phase = 2
|
||||
result.finished = true
|
||||
target.apocalypse = apocalypse
|
||||
return result
|
||||
|
||||
var stage_complete: bool = true
|
||||
for row: Dictionary in stage_rows:
|
||||
if E.gt(row.tick, apocalypse.tick):
|
||||
stage_complete = false
|
||||
continue
|
||||
var id: String = row.id
|
||||
if not world.catalog.entities.has(id): continue
|
||||
if E.gt(world.ledger.physical_amount(target, id), 0):
|
||||
stage_complete = false
|
||||
result.screen = int(row.screen)
|
||||
_destroy_fraction(target, id)
|
||||
if stage_complete:
|
||||
var next_stage: Variant = INF
|
||||
for row: Dictionary in _schedule:
|
||||
if E.gt(row.stage, apocalypse.stage): next_stage = E.minimum(next_stage, row.stage)
|
||||
if E.finite(next_stage):
|
||||
apocalypse.stage = E.integer(next_stage)
|
||||
apocalypse.tick = 0
|
||||
else:
|
||||
_process_final_energy(target, apocalypse)
|
||||
if E.eq(apocalypse.end_ticks, END_WAIT_TICKS):
|
||||
apocalypse.phase = 2
|
||||
result.finished = true
|
||||
target.apocalypse = apocalypse
|
||||
world.ledger.refresh_groups(target)
|
||||
world.ledger.clamp_auto_resources(target)
|
||||
world.presentation.refresh(target, world.catalog)
|
||||
return result
|
||||
|
||||
|
||||
func _make_schedule(skipped_id: String) -> Array:
|
||||
var rows: Array = Global.ApocalypseQueue.values()
|
||||
rows.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return int(a.Num) < int(b.Num))
|
||||
var result: Array = []
|
||||
var stage: int = -1
|
||||
var tick: int = 0
|
||||
for source: Dictionary in rows:
|
||||
if source.Shortcut == skipped_id: continue
|
||||
var row_stage: int = int(source.Stage)
|
||||
if row_stage != stage:
|
||||
stage = row_stage
|
||||
tick = 0
|
||||
tick += int(source.Tick)
|
||||
result.append({"id": str(source.Shortcut), "stage": row_stage, "screen": int(source.Screen), "tick": tick})
|
||||
return result
|
||||
|
||||
|
||||
func _destroy_fraction(target: Dictionary, id: String) -> void:
|
||||
var entity: OwniverseEntity = world.catalog.entities[id]
|
||||
var physical: Variant = world.ledger.physical_amount(target, id)
|
||||
if E.le(physical, 0): return
|
||||
var amount: Variant = E.round_value(E.divide(physical, 2)) if E.gt(physical, 5) else (1 if E.ge(physical, 1) else physical)
|
||||
amount = E.minimum(physical, E.maximum(0, amount))
|
||||
if E.le(amount, 0): return
|
||||
var fraction: Variant = E.clamp_value(E.divide(amount, physical), 0, 1)
|
||||
if E.gt(entity.destroy_level, 0): world.stellar.destroy_planets(target, id, fraction, entity.destroy_level, "apocalypse")
|
||||
var products: Dictionary = world.stellar.evolution_products(target, entity, amount, {"explosiveness": WorldParameters.effective(target, "Exp")})
|
||||
var normalized: Dictionary = {}
|
||||
for output in products:
|
||||
normalized[output] = E.divide(products[output], amount)
|
||||
var incoming: Dictionary = world.ledger.transfer_evolution(target, {id: fraction}, {id: normalized})
|
||||
world.batches.scale_batches(target, id, E.subtract(1, fraction))
|
||||
var terminal: String = "SMBH" if E.gt(target.apocalypse.type, 0) else "S"
|
||||
var terminal_yield: Variant = 0
|
||||
var new_owners: Dictionary = {}
|
||||
if normalized.is_empty():
|
||||
terminal_yield = E.multiply(amount, E.add(1, E.maximum(0, entity.mass))) if E.contains(["Star", "BH"], entity.group_as) else amount
|
||||
for output in normalized:
|
||||
if E.eq(output, "blast"):
|
||||
var environment: Dictionary = world.environment.stellar_environment(target)
|
||||
world.stellar.apply_blast(target, E.multiply(normalized[output], amount), environment)
|
||||
continue
|
||||
if not world.catalog.entities.has(output): continue
|
||||
var output_amount: Variant = E.maximum(0, products[output])
|
||||
if E.le(output_amount, 0): continue
|
||||
output_amount = world.ledger.credit(target, output, output_amount)
|
||||
var output_entity: OwniverseEntity = world.catalog.entities[output]
|
||||
if E.gt(output_entity.lifespan, 0) or not output_entity.life_rule.is_empty():
|
||||
world.batches.append_batch(target, output, output_amount, target.time.datetime, target.time.datetime)
|
||||
new_owners[output] = E.add(new_owners.get(output, 0), output_amount)
|
||||
world.ledger.accept_transfers(target, incoming)
|
||||
world.ledger.bind_new_owners(target, new_owners)
|
||||
if E.gt(terminal_yield, 0): world.ledger.credit(target, terminal, terminal_yield)
|
||||
world.ledger.round_object_allocations(target)
|
||||
|
||||
|
||||
func _process_final_energy(target: Dictionary, apocalypse: Dictionary) -> void:
|
||||
var energy: Variant = E.maximum(0, target.energy)
|
||||
if E.gt(energy, 0):
|
||||
var amount: Variant = E.round_value(E.divide(energy, 2)) if E.gt(energy, 5) else (1 if E.ge(energy, 1) else energy)
|
||||
amount = E.minimum(energy, amount)
|
||||
target.energy = E.maximum(0, E.subtract(energy, amount))
|
||||
world.ledger.credit(target, "SMBH" if E.gt(apocalypse.type, 0) else "S", amount)
|
||||
apocalypse.end_ticks = 0
|
||||
else:
|
||||
apocalypse.end_ticks = int(apocalypse.end_ticks) + 1
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://u5hs757qtn8c
|
||||
@@ -14,6 +14,7 @@ func yearly_products(entity: OwniverseEntity) -> Dictionary:
|
||||
|
||||
|
||||
func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Dictionary, distribution: Dictionary, from_year: Variant, to_year: Variant) -> void:
|
||||
if E.gt(target.get("apocalypse", {}).get("phase", 0), 0): return
|
||||
var basic: Dictionary = {}
|
||||
var automatic: Dictionary = {}
|
||||
for id in quantities:
|
||||
@@ -72,6 +73,7 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
|
||||
|
||||
# One common multiplier preserves the initial count ratios, even under scarcity.
|
||||
var candidates: Dictionary = {}
|
||||
var helium_share: Variant = world.production.stellar_helium_share(previous)
|
||||
for id in distribution:
|
||||
var entity: OwniverseEntity = world.catalog.entities[id]
|
||||
if E.ne(entity.group_as, "Star"):
|
||||
@@ -87,11 +89,16 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
|
||||
return
|
||||
var costs: Dictionary = {}
|
||||
var needs: Dictionary = {}
|
||||
var stellar_hydrogen_cost: Variant = 0
|
||||
for id in candidates:
|
||||
if E.le(auto_capacity(target, world.catalog.entities[id], budget), 0):
|
||||
var entity: OwniverseEntity = world.catalog.entities[id]
|
||||
if E.le(auto_capacity(target, entity, budget, helium_share), 0):
|
||||
continue
|
||||
for resource in entity.cost:
|
||||
var cost: Variant = E.multiply(E.maximum(0, entity.cost[resource]), candidates[id])
|
||||
if E.eq(resource, "H") and world.production.uses_stellar_helium(entity):
|
||||
stellar_hydrogen_cost = E.add(stellar_hydrogen_cost, cost)
|
||||
continue
|
||||
for resource in world.catalog.entities[id].cost:
|
||||
var cost: Variant = E.multiply(E.maximum(0, world.catalog.entities[id].cost[resource]), candidates[id])
|
||||
costs[resource] = E.add(costs.get(resource, 0), cost)
|
||||
needs[resource] = E.add(needs.get(resource, 0), cost)
|
||||
for resource in world.catalog.entities[id].bind:
|
||||
@@ -100,6 +107,9 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
|
||||
for resource in costs:
|
||||
if E.gt(costs[resource], 0):
|
||||
scale = E.minimum(scale, E.divide(budget.get(resource, 0), costs[resource]))
|
||||
if E.gt(stellar_hydrogen_cost, 0):
|
||||
var hydrogen_budget: Variant = E.minimum(budget.get("H", 0), world.ledger.available(target, "H"))
|
||||
scale = E.minimum(scale, world.production.stellar_hydrogen_capacity(stellar_hydrogen_cost, hydrogen_budget, world.ledger.available(target, "He"), helium_share))
|
||||
for resource in needs:
|
||||
if E.gt(needs[resource], 0):
|
||||
scale = E.minimum(scale, E.divide(world.ledger.available(target, resource), needs[resource]))
|
||||
@@ -116,7 +126,8 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
|
||||
var order: Array = candidates.keys()
|
||||
order.sort_custom(func ( a : String , b : String ) -> bool : return (E.lt(candidates[a], candidates[b]) if E.ne(candidates[a], candidates[b]) else E.lt(a, b)))
|
||||
var created: Dictionary = {}
|
||||
var metallicity: Variant = world.environment.stellar_environment(target).metallicity
|
||||
var current_quantities: Dictionary = world.ledger.physical_amounts(target)
|
||||
var metallicity: Variant = world.environment.stellar_environment(target, current_quantities).metallicity
|
||||
var total_created: Variant = 0
|
||||
for id in order:
|
||||
var entity: OwniverseEntity = world.catalog.entities[id]
|
||||
@@ -125,10 +136,10 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
|
||||
continue
|
||||
var quota: Variant = E.maximum(0, E.add(E.multiply(candidates[id], scale), deficits.get(id, 0)))
|
||||
var desired: Variant = E.multiply(E.floor_value(E.divide(quota, step)), step)
|
||||
var actual: Variant = E.minimum(desired, auto_capacity(target, entity, budget))
|
||||
var actual: Variant = E.minimum(desired, auto_capacity(target, entity, budget, helium_share))
|
||||
if E.le(actual, 0):
|
||||
continue
|
||||
if (not world.production.try_production(target, entity, actual, true)):
|
||||
if (not world.production.try_production(target, entity, actual, true, current_quantities, helium_share)):
|
||||
continue
|
||||
budget = ordinary_star_budget(target)
|
||||
created[id] = actual
|
||||
@@ -138,7 +149,8 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
|
||||
deficits[id] = E.subtract(E.add(deficits.get(id, 0), E.multiply(candidates[id], total_created)), created.get(id, 0))
|
||||
for id in created:
|
||||
world.ledger.credit(target, id, created[id])
|
||||
world.batches.append_batch(target, id, created[id], from_year, to_year)
|
||||
var lifespan_multiplier: Variant = world.production.stellar_lifespan_multiplier(world.catalog.entities[id], helium_share)
|
||||
world.batches.append_batch(target, id, created[id], from_year, to_year, {}, -INF, null, null, NAN, lifespan_multiplier)
|
||||
world.stellar.create_planets(target, id, created[id], from_year, to_year, metallicity)
|
||||
|
||||
|
||||
@@ -182,11 +194,16 @@ func ordinary_star_budget(target: Dictionary) -> Dictionary:
|
||||
return budget
|
||||
|
||||
|
||||
func auto_capacity(source: Dictionary, entity: OwniverseEntity, budget: Dictionary) -> Variant:
|
||||
func auto_capacity(source: Dictionary, entity: OwniverseEntity, budget: Dictionary, helium_share_override: Variant = NAN) -> Variant:
|
||||
var capacity: Variant = INF
|
||||
var needs: Dictionary = {}
|
||||
var helium_share: Variant = world.production.stellar_helium_share(source) if not E.finite(helium_share_override) else helium_share_override
|
||||
for resource in entity.cost:
|
||||
var cost: Variant = E.maximum(0, entity.cost[resource])
|
||||
if E.eq(resource, "H") and world.production.uses_stellar_helium(entity):
|
||||
var hydrogen_budget: Variant = E.minimum(budget.get("H", 0), world.ledger.available(source, "H"))
|
||||
capacity = E.minimum(capacity, world.production.stellar_hydrogen_capacity(cost, hydrogen_budget, world.ledger.available(source, "He"), helium_share))
|
||||
continue
|
||||
needs[resource] = cost
|
||||
if E.gt(cost, 0):
|
||||
capacity = E.minimum(capacity, E.floor_value(E.divide(budget.get(resource, 0), cost)))
|
||||
|
||||
@@ -6,9 +6,9 @@ const ETHICS = State.ETHICS
|
||||
const OPPOSITE = {"m": "l", "l": "m", "w": "d", "d": "w", "c": "b", "b": "c"}
|
||||
|
||||
# Cache the weighted float worship rate multiplied by the exact population.
|
||||
static func worship(world, target: Dictionary, rules: Dictionary) -> Variant:
|
||||
static func worship(world, target: Dictionary, rules: Dictionary, physical: Dictionary = {}) -> Variant:
|
||||
var result: Variant = 0
|
||||
var physical: Dictionary = world.ledger.physical_amounts(target)
|
||||
if physical.is_empty(): physical = world.ledger.physical_amounts(target)
|
||||
for id in rules:
|
||||
if not rules[id].worships: continue
|
||||
var pop: Dictionary = State.normalize_profile(target.ethics.get(id, {}))
|
||||
|
||||
@@ -10,7 +10,7 @@ var _generator_key: String = ""
|
||||
var _generator: Dictionary = {}
|
||||
var _solutions: Dictionary = {}
|
||||
|
||||
func _edges(target: Dictionary, rules: Dictionary) -> Dictionary:
|
||||
func _edges(target: Dictionary, rules: Dictionary, physical: Dictionary = {}) -> Dictionary:
|
||||
var modifiers: Dictionary = Parameters.life_modifiers(target)
|
||||
var order: float = maxf(1e-12, E.approx(Parameters.value(target, "Order")))
|
||||
var stages: Array = rules.keys()
|
||||
@@ -18,13 +18,13 @@ func _edges(target: Dictionary, rules: Dictionary) -> Dictionary:
|
||||
var rates: Dictionary = {}
|
||||
var spans: Dictionary = {}
|
||||
var progression: Dictionary = {}
|
||||
var budget: Dictionary = State.stellar_budget(world, target, rules)
|
||||
var budget: Dictionary = State.stellar_budget(world, target, rules, physical)
|
||||
var signature: String = str(modifiers) + ":" + str(order)
|
||||
for id in stages.duplicate():
|
||||
if rules[id].ethics.is_empty(): continue
|
||||
rates[id] = Civ.coefficients(world, rules, id, modifiers)
|
||||
progression[id] = _can_progress(id, rules[id].next, budget, rules)
|
||||
spans[id] = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules)))
|
||||
spans[id] = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules, physical)))
|
||||
signature += id + str(spans[id]) + str(rates[id]) + str(rules[id]) + str(progression[id])
|
||||
for row: Dictionary in rules[id].ethics.values():
|
||||
for destination in Q.rule_weights(str(row.fall)):
|
||||
@@ -100,11 +100,12 @@ func _can_progress(source: String, destination: String, budget: Dictionary, rule
|
||||
## Uniformization computes exp(Q*t) through a Poisson series of sparse steps.
|
||||
## Absorbing biological stages and Ct retain their mass. No one-transition cap.
|
||||
func _solve(generator: Dictionary, source: String, profile: Dictionary, years: float) -> Dictionary:
|
||||
var key: String = source + ":" + str(years)
|
||||
var approximation: int = world.adaptive_quality
|
||||
var key: String = source + ":" + str(years) + ":" + str(approximation)
|
||||
var basis: Dictionary = _solutions.get(key, {})
|
||||
if basis.is_empty():
|
||||
var started: int = world.profile_start()
|
||||
basis = _solve_basis(generator, source, years)
|
||||
basis = _solve_single_step(generator, source, years) if approximation > 0 else _solve_basis(generator, source, years)
|
||||
world.profile_end("civilization_operator_solve", started)
|
||||
if _solutions.size() >= 512: _solutions.clear()
|
||||
_solutions[key] = basis
|
||||
@@ -117,6 +118,35 @@ func _solve(generator: Dictionary, source: String, profile: Dictionary, years: f
|
||||
occupation[node] *= shares[node % 6]
|
||||
return {"mass": mass, "occupation": occupation, "occupation_scale": 1.0}
|
||||
|
||||
## Load-shedding approximation: each ethic cohort can make at most one
|
||||
## transition during this interval. The exact Markov solve remains the default.
|
||||
func _solve_single_step(generator: Dictionary, source: String, years: float) -> Dictionary:
|
||||
var size: int = generator.rows.size()
|
||||
var mass: PackedFloat64Array = []
|
||||
var occupation: PackedFloat64Array = []
|
||||
mass.resize(size)
|
||||
occupation.resize(size)
|
||||
var offset: int = generator.index[source] * 6
|
||||
for ethic in 6:
|
||||
var node: int = offset + ethic
|
||||
var row: Dictionary = generator.rows[node]
|
||||
var outgoing: float = row.outgoing
|
||||
if outgoing <= 0.0:
|
||||
mass[node] = 1.0
|
||||
occupation[node] = years
|
||||
continue
|
||||
var moved: float = clampf(1.0 - exp(-outgoing * years), 0.0, 1.0)
|
||||
var stayed: float = 1.0 - moved
|
||||
mass[node] += stayed
|
||||
occupation[node] += years * (1.0 - moved * 0.5)
|
||||
for destination in row.edges:
|
||||
var rate: float = row.edges[destination]
|
||||
if rate <= 0.0: continue
|
||||
var share: float = moved * rate / outgoing
|
||||
mass[destination] += share
|
||||
occupation[destination] += years * share * 0.5
|
||||
return {"mass": mass, "occupation": occupation}
|
||||
|
||||
func _solve_basis(generator: Dictionary, source: String, years: float) -> Dictionary:
|
||||
var size: int = generator.rows.size()
|
||||
var values: PackedFloat64Array = []
|
||||
@@ -171,11 +201,11 @@ func _solve_basis(generator: Dictionary, source: String, years: float) -> Dictio
|
||||
for node in range(e, size, 6): occupation[node] *= factor
|
||||
return {"mass": mass, "occupation": occupation}
|
||||
|
||||
func _drift(target: Dictionary, rules: Dictionary, generator: Dictionary, years: float) -> void:
|
||||
func _drift(target: Dictionary, rules: Dictionary, generator: Dictionary, years: float, physical: Dictionary = {}) -> void:
|
||||
for id in rules:
|
||||
if rules[id].ethics.is_empty() or not target.ethics.has(id): continue
|
||||
var profile: Dictionary = State.normalize_profile(target.ethics[id])
|
||||
var span: float = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules)))
|
||||
var span: float = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules, physical)))
|
||||
var duration: float = years / span
|
||||
var pair_decay: float = exp(-0.03 * duration)
|
||||
var opposite_decay: float = exp(-0.02 * duration)
|
||||
@@ -306,55 +336,92 @@ func _limit(plans: Dictionary, budget: Dictionary, rules: Dictionary) -> void:
|
||||
plans[source].counts[destination] = allowed
|
||||
_merge(plans[source], source, rejected, plans[source].profiles[destination])
|
||||
|
||||
func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules: Dictionary, year: Variant) -> void:
|
||||
## Apply one conserved source-by-owner table, then credit each destination once.
|
||||
func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules: Dictionary, year: Variant) -> Dictionary:
|
||||
var credits: Dictionary = {}
|
||||
var contributions: Dictionary = {}
|
||||
var births: Dictionary = {}
|
||||
var ownership: Dictionary = {}
|
||||
# Each source quota replaces its old physical population. Transfers between
|
||||
# owners do not change these exact totals, and credits are applied below.
|
||||
var final: Dictionary = physical.duplicate()
|
||||
for source in plans:
|
||||
if final.has(source): final[source] = plans[source].counts.get(source, 0)
|
||||
var space_stages: Dictionary = {"Ct": true}
|
||||
for id in rules: space_stages[id] = id == "Ct" or str(rules[id].home) != ""
|
||||
for source in plans:
|
||||
ownership[source] = {}
|
||||
var free: Variant = target.amount.get(source, 0)
|
||||
if E.gt(free, 0): ownership[source][""] = free
|
||||
for owner in target.binds:
|
||||
for source in target.binds[owner]:
|
||||
if not ownership.has(source): continue
|
||||
var held: Variant = target.binds[owner][source]
|
||||
if E.gt(held, 0): ownership[source][owner] = held
|
||||
for source in plans:
|
||||
var plan: Dictionary = plans[source]
|
||||
var slots: Dictionary = {"": target.amount.get(source, 0)}
|
||||
for owner in target.binds:
|
||||
var held: Variant = target.binds[owner].get(source, 0)
|
||||
if E.gt(held, 0): slots[owner] = held
|
||||
var remaining: Dictionary = {}
|
||||
for destination in plan.counts:
|
||||
var amount: Variant = plan.counts[destination]
|
||||
if E.le(amount, 0): continue
|
||||
remaining[destination] = amount
|
||||
if not contributions.has(destination): contributions[destination] = []
|
||||
contributions[destination].append({"amount": amount, "profile": plan.profiles[destination]})
|
||||
var slots: Dictionary = ownership[source]
|
||||
target.amount[source] = 0
|
||||
var owners: Array = slots.keys()
|
||||
if owners.size() == 1 and owners[0] == "":
|
||||
target.amount[source] = remaining.get(source, 0)
|
||||
for destination in remaining:
|
||||
if destination == source: continue
|
||||
var taken: Dictionary = Q.allocate_profiled(slots, amount)
|
||||
for owner in taken:
|
||||
if E.le(taken[owner], 0): continue
|
||||
slots[owner] = E.subtract(slots[owner], taken[owner])
|
||||
var host: String = owner
|
||||
if State.is_space_civilization(destination, rules) or destination == "Ct": host = ""
|
||||
elif owner == "" and State.is_space_civilization(source, rules): host = "@host"
|
||||
var host: String = "@host" if space_stages.get(source, false) and not space_stages.get(destination, false) else ""
|
||||
if not credits.has(destination): credits[destination] = {}
|
||||
credits[destination][host] = E.add(credits[destination].get(host, 0), taken[owner])
|
||||
target.amount[source] = slots[""]
|
||||
for owner in slots:
|
||||
if owner != "": target.binds[owner][source] = slots[owner]
|
||||
credits[destination][host] = E.add(credits[destination].get(host, 0), remaining[destination])
|
||||
continue
|
||||
for i in owners.size():
|
||||
var owner: String = owners[i]
|
||||
# Last row receives the remaining exact quotas; the free-only path
|
||||
# therefore needs no proportional allocation or repeated subtraction.
|
||||
var allocation: Dictionary = remaining if i == owners.size() - 1 else Q.allocate_profiled(remaining, slots[owner])
|
||||
for destination in allocation:
|
||||
var amount: Variant = allocation[destination]
|
||||
if E.le(amount, 0): continue
|
||||
if i != owners.size() - 1:
|
||||
remaining[destination] = E.subtract(remaining[destination], amount)
|
||||
if destination == source:
|
||||
if owner == "": target.amount[source] = amount
|
||||
else: target.binds[owner][source] = amount
|
||||
continue
|
||||
var host: String = owner
|
||||
if space_stages.get(destination, false): host = ""
|
||||
elif owner == "" and space_stages.get(source, false): host = "@host"
|
||||
if not credits.has(destination): credits[destination] = {}
|
||||
credits[destination][host] = E.add(credits[destination].get(host, 0), amount)
|
||||
if owner != "" and E.le(allocation.get(source, 0), 0): target.binds[owner][source] = 0
|
||||
var hosts: Dictionary = {}
|
||||
for id in physical:
|
||||
if world.catalog.entities[id].event_group == "DwStar" and E.gt(physical[id], 0): hosts[id] = physical[id]
|
||||
var host_shares: Array = _host_shares(hosts) if not hosts.is_empty() else []
|
||||
for destination in credits:
|
||||
var created: Variant = world.ledger.credit(target, destination, Q.sum(credits[destination]))
|
||||
var requested: Variant = Q.sum(credits[destination])
|
||||
var created: Variant = world.ledger.credit(target, destination, requested)
|
||||
if final.has(destination): final[destination] = E.add(final[destination], created)
|
||||
var bound: Variant = 0
|
||||
for owner in credits[destination]:
|
||||
var count: Variant = credits[destination][owner]
|
||||
if owner == "" or E.le(count, 0): continue
|
||||
if owner == "@host":
|
||||
if hosts.is_empty(): continue
|
||||
var ownership: Dictionary = Q.split_profile(target.civilization_remainders, "flow:hosts:" + destination, count, hosts.keys(), _host_shares(hosts))
|
||||
for host in ownership:
|
||||
if E.le(ownership[host], 0): continue
|
||||
var distributed: Dictionary = Q.split_profile(target.civilization_remainders, "flow:hosts:" + destination, count, hosts.keys(), host_shares)
|
||||
for host in distributed:
|
||||
if E.le(distributed[host], 0): continue
|
||||
if not target.binds.has(host): target.binds[host] = {}
|
||||
target.binds[host][destination] = E.add(target.binds[host].get(destination, 0), ownership[host])
|
||||
target.amount[destination] = E.subtract(target.amount[destination], count)
|
||||
target.binds[host][destination] = E.add(target.binds[host].get(destination, 0), distributed[host])
|
||||
else:
|
||||
target.binds[owner][destination] = E.add(target.binds[owner].get(destination, 0), count)
|
||||
target.amount[destination] = E.subtract(target.amount[destination], count)
|
||||
bound = E.add(bound, count)
|
||||
target.amount[destination] = E.subtract(target.amount[destination], bound)
|
||||
if not rules.has(destination): births[destination] = created
|
||||
var final: Dictionary = world.ledger.physical_amounts(target)
|
||||
for destination in contributions:
|
||||
if not rules.has(destination) or E.le(final.get(destination, 0), 0): continue
|
||||
var profile: Dictionary = {}
|
||||
@@ -369,6 +436,7 @@ func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules:
|
||||
for ethic in ETHICS: profile[ethic] += entry.profile[ethic] * ratio
|
||||
target.ethics[destination] = State.normalize_profile(profile)
|
||||
for destination in births: world.batches.append_batch(target, destination, births[destination], year, year)
|
||||
return final
|
||||
|
||||
func _host_shares(hosts: Dictionary) -> Array:
|
||||
var total: Variant = Q.sum(hosts)
|
||||
@@ -376,18 +444,18 @@ func _host_shares(hosts: Dictionary) -> Array:
|
||||
for value in hosts.values(): result.append(E.ratio_approx(value, total))
|
||||
return result
|
||||
|
||||
func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: Dictionary) -> void:
|
||||
if colonies.is_empty(): return
|
||||
var physical: Dictionary = world.ledger.physical_amounts(target)
|
||||
func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: Dictionary, physical: Dictionary = {}) -> Dictionary:
|
||||
if physical.is_empty(): physical = world.ledger.physical_amounts(target)
|
||||
if colonies.is_empty(): return physical
|
||||
var stocks: Dictionary = {}
|
||||
for id in ["GG", "AB", "RP", "HP"]: stocks[id] = physical.get(id, 0)
|
||||
var requests: Dictionary = {}
|
||||
for id in colonies: requests[id] = colonies[id].counts.get(id, 0)
|
||||
var granted: Dictionary = Q.allocate_profiled(requests, Q.sum(stocks))
|
||||
var total: Variant = Q.sum(granted)
|
||||
if E.le(total, 0): return
|
||||
if E.le(total, 0): return physical
|
||||
var taken_resources: Dictionary = Q.allocate_profiled(stocks, total)
|
||||
var remaining: Dictionary = granted.duplicate()
|
||||
var removed_by_owner: Dictionary = {}
|
||||
for resource in taken_resources:
|
||||
var removed: Variant = taken_resources[resource]
|
||||
if E.le(removed, 0): continue
|
||||
@@ -401,15 +469,32 @@ func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: D
|
||||
if E.le(amount, 0): continue
|
||||
if owner == "": target.amount[resource] = E.subtract(target.amount[resource], amount)
|
||||
else: target.binds[owner][resource] = E.subtract(target.binds[owner][resource], amount)
|
||||
var allocation: Dictionary = Q.allocate_profiled(remaining, amount)
|
||||
removed_by_owner[owner] = E.add(removed_by_owner.get(owner, 0), amount)
|
||||
var remaining: Dictionary = granted.duplicate()
|
||||
var placements: Dictionary = {}
|
||||
var final: Dictionary = physical.duplicate()
|
||||
var owners: Array = removed_by_owner.keys()
|
||||
for i in owners.size():
|
||||
var owner: String = owners[i]
|
||||
var allocation: Dictionary = remaining if i == owners.size() - 1 else Q.allocate_profiled(remaining, removed_by_owner[owner])
|
||||
for destination in allocation:
|
||||
var count: Variant = allocation[destination]
|
||||
if E.le(count, 0): continue
|
||||
remaining[destination] = E.subtract(remaining[destination], count)
|
||||
if i != owners.size() - 1: remaining[destination] = E.subtract(remaining[destination], count)
|
||||
if owner == "" or State.is_space_civilization(destination, rules): continue
|
||||
if not placements.has(destination): placements[destination] = {}
|
||||
placements[destination][owner] = count
|
||||
for destination in granted:
|
||||
var count: Variant = granted[destination]
|
||||
if E.le(count, 0): continue
|
||||
var created: Variant = world.ledger.credit(target, destination, count)
|
||||
if owner != "" and not State.is_space_civilization(destination, rules):
|
||||
target.amount[destination] = E.subtract(target.amount[destination], created)
|
||||
target.binds[owner][destination] = E.add(target.binds[owner].get(destination, 0), created)
|
||||
if final.has(destination): final[destination] = E.add(final[destination], created)
|
||||
var bound: Variant = 0
|
||||
for owner in placements.get(destination, {}):
|
||||
var held: Variant = placements[destination][owner]
|
||||
target.binds[owner][destination] = E.add(target.binds[owner].get(destination, 0), held)
|
||||
bound = E.add(bound, held)
|
||||
target.amount[destination] = E.subtract(target.amount[destination], bound)
|
||||
for id in granted:
|
||||
if E.le(granted[id], 0): continue
|
||||
var profile: Dictionary = colonies[id].profiles[id]
|
||||
@@ -420,6 +505,9 @@ func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: D
|
||||
drifted[ethic] += profile[source] * (0.96 if source == ethic else (0.004 if Civ.OPPOSITE[source] == ethic else 0.009))
|
||||
target.ethics[id] = State.merge_profiles(target.ethics.get(id, {}), physical.get(id, 0), drifted, granted[id])
|
||||
target.civilization_stats.colonies = E.add(target.civilization_stats.get("colonies", 0), total)
|
||||
for resource in taken_resources:
|
||||
final[resource] = E.subtract(final.get(resource, 0), taken_resources[resource])
|
||||
return final
|
||||
|
||||
func evolve(target: Dictionary, from_year: Variant, to_year: Variant, timer: RefCounted = null) -> void:
|
||||
var keep_alive: RefCounted = self
|
||||
@@ -430,12 +518,12 @@ func evolve(target: Dictionary, from_year: Variant, to_year: Variant, timer: Ref
|
||||
# Bound the Poisson mean, processing the entire requested interval.
|
||||
var maximum_years: float = years
|
||||
var cursor: Variant = from_year
|
||||
var physical: Dictionary = world.ledger.physical_amounts(target)
|
||||
while E.lt(cursor, to_year):
|
||||
if timer != null and not await timer.pause(): return
|
||||
generator = _edges(target, rules)
|
||||
generator = _edges(target, rules, physical)
|
||||
maximum_years = 32.0 / generator.lambda if generator.lambda > 0.0 else years
|
||||
var end: Variant = E.minimum(to_year, E.add(cursor, E.of(maximum_years)))
|
||||
var physical: Dictionary = world.ledger.physical_amounts(target)
|
||||
var started: int = world.profile_start()
|
||||
var transaction: Dictionary = _plans(target, physical, rules, generator, E.approx(E.subtract(end, cursor)))
|
||||
world.profile_end("civilization_flow_plan", started)
|
||||
@@ -463,18 +551,18 @@ func evolve(target: Dictionary, from_year: Variant, to_year: Variant, timer: Ref
|
||||
_limit(plans, State.stellar_budget(world, target, rules, physical), rules)
|
||||
world.profile_end("civilization_flow_capacity", started)
|
||||
started = world.profile_start()
|
||||
_commit(target, physical, plans, rules, end)
|
||||
physical = _commit(target, physical, plans, rules, end)
|
||||
world.profile_end("civilization_flow_ledger", started)
|
||||
if timer != null and not await timer.pause(): return
|
||||
started = world.profile_start()
|
||||
_colonize(target, transaction.colonies, end, rules)
|
||||
physical = _colonize(target, transaction.colonies, end, rules, physical)
|
||||
world.profile_end("civilization_flow_colonies", started)
|
||||
started = world.profile_start()
|
||||
_drift(target, rules, generator, E.approx(E.subtract(end, cursor)))
|
||||
_drift(target, rules, generator, E.approx(E.subtract(end, cursor)), physical)
|
||||
target.civilization_stats.wars = E.add(target.civilization_stats.get("wars", 0), transaction.wars)
|
||||
target.civilization_stats.collateral = E.add(target.civilization_stats.get("collateral", 0), damage_total)
|
||||
State.enforce_capacity(world, target, rules, end)
|
||||
State.reconcile(world, target, rules)
|
||||
target.civilization_stats.worship = Civ.worship(world, target, rules)
|
||||
physical = State.enforce_capacity(world, target, rules, end, physical)
|
||||
State.reconcile(world, target, rules, physical)
|
||||
target.civilization_stats.worship = Civ.worship(world, target, rules, physical)
|
||||
world.profile_end("civilization_flow_finish", started)
|
||||
cursor = end
|
||||
|
||||
@@ -133,10 +133,11 @@ static func admit_transition(budget: Dictionary, rules: Dictionary, source: Stri
|
||||
|
||||
# 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: Variant) -> void:
|
||||
static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year: Variant, physical: Dictionary = {}) -> Dictionary:
|
||||
var profile_started: int = world.profile_start()
|
||||
detach_space_civilizations(target, rules)
|
||||
var physical: Dictionary = world.ledger.physical_amounts(target)
|
||||
if physical.is_empty(): physical = world.ledger.physical_amounts(target)
|
||||
else: physical = physical.duplicate()
|
||||
reconcile(world, target, rules, physical)
|
||||
var ethical_totals: Dictionary = physical.duplicate()
|
||||
var budget: Dictionary = stellar_budget(world, target, rules, physical)
|
||||
@@ -163,7 +164,9 @@ static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year:
|
||||
var previous: String = (stages[E.subtract(index, 1)] if E.gt(index, 0) else "C1.9")
|
||||
world.batches.scale_batches(target, id, E.divide(supported, count))
|
||||
target.amount[id] = supported
|
||||
world.ledger.credit(target, previous, excess)
|
||||
var credited: Variant = world.ledger.credit(target, previous, excess)
|
||||
physical[id] = E.maximum(0, supported)
|
||||
physical[previous] = E.add(physical.get(previous, 0), credited)
|
||||
world.batches.append_batch(target, previous, excess, year, year)
|
||||
var previous_count: Variant = ethical_totals.get(previous, physical.get(previous, target.amount.get(previous, 0)))
|
||||
var previous_profile: Dictionary = target.ethics.get(previous, empty_profile())
|
||||
@@ -174,7 +177,7 @@ static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year:
|
||||
# Capacity losses are proportional, so the surviving source keeps its profile.
|
||||
if E.eq(previous, "C1.9"):
|
||||
var stars: Dictionary = {}
|
||||
var current_physical: Dictionary = world.ledger.physical_amounts(target)
|
||||
var current_physical: Dictionary = physical
|
||||
for star in current_physical:
|
||||
if (E.eq(world.catalog.entities[star].event_group, "DwStar") and E.gt(current_physical[star], 0)):
|
||||
stars[star] = current_physical[star]
|
||||
@@ -192,10 +195,10 @@ static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year:
|
||||
target.binds[owner][previous] = E.add(target.binds[owner].get(previous, 0), amount)
|
||||
target.amount[previous] = E.subtract(target.amount[previous], amount)
|
||||
if changed:
|
||||
physical = world.ledger.physical_amounts(target)
|
||||
reconcile(world, target, rules, physical)
|
||||
assign_homes(world, target, rules, physical)
|
||||
world.profile_end("enforce_capacity", profile_started)
|
||||
return physical
|
||||
|
||||
# Unity's structure chain: direct Claim resets the required structure count;
|
||||
# intervening stages require ten times more units of the preceding structure.
|
||||
@@ -247,13 +250,13 @@ static func assign_homes(world, target: Dictionary, rules: Dictionary, physical:
|
||||
world.profile_end("assign_homes", profile_started)
|
||||
|
||||
|
||||
static func effective_span(world, target: Dictionary, id: String, rules: Dictionary) -> Variant:
|
||||
static func effective_span(world, target: Dictionary, id: String, rules: Dictionary, physical: Dictionary = {}) -> Variant:
|
||||
if (not rules.has(id)): return world.catalog.entities[id].lifespan
|
||||
var base: Variant = rules[id].span
|
||||
if (not is_space_civilization(id, rules)): return base
|
||||
var population: Variant = target.amount.get(id, 0)
|
||||
if E.le(population, 0): return base
|
||||
var homes: Dictionary = stage_homes(world, target, id, world.catalog.civilization_layout(rules))
|
||||
var homes: Dictionary = stage_homes(world, target, id, world.catalog.civilization_layout(rules), physical)
|
||||
target.civilization_homes[id] = homes
|
||||
var multiplier: float = 1.0
|
||||
for home in homes:
|
||||
|
||||
@@ -5,13 +5,12 @@ const SimulationTime = preload("res://world/owniverse/simulation_time.gd")
|
||||
const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
|
||||
const QuantityMath = preload("res://world/owniverse/quantity_math.gd")
|
||||
|
||||
func initialize_life_batches() -> void:
|
||||
func initialize_life_batches() -> Dictionary:
|
||||
for id in Global.CivilizationRules:
|
||||
world.state.batches.erase(id)
|
||||
world.state.batch_origins.erase(id)
|
||||
CivilizationState.detach_space_civilizations(world.state, Global.CivilizationRules)
|
||||
CivilizationState.enforce_capacity(world, world.state, Global.CivilizationRules, world.state.time.datetime)
|
||||
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var physical: Dictionary = CivilizationState.enforce_capacity(world, world.state, Global.CivilizationRules, world.state.time.datetime)
|
||||
for id in world.catalog.entities:
|
||||
if world.catalog.entities[id].life_rule.is_empty():
|
||||
continue
|
||||
@@ -19,6 +18,7 @@ func initialize_life_batches() -> void:
|
||||
for batch : Dictionary in world.state.batches.get(id, []):
|
||||
tracked = E.add(tracked, batch.amount)
|
||||
world.batches.append_batch(world.state, id, E.maximum(0, E.subtract(physical.get(id, 0), tracked)), world.state.time.datetime, world.state.time.datetime)
|
||||
return physical
|
||||
|
||||
|
||||
func run_frame(clock: Dictionary, budget: RefCounted) -> void:
|
||||
@@ -28,13 +28,15 @@ func run_frame(clock: Dictionary, budget: RefCounted) -> void:
|
||||
|
||||
func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefCounted = null) -> void:
|
||||
var _keep_alive: RefCounted = self
|
||||
var adaptive_started: int = world.adaptive_timer_usec()
|
||||
# Reconcile only when simulation advances.
|
||||
# Fractional frame updates cannot change capacity or populations.
|
||||
var processed_year: Variant = SimulationTime.processed_year(datetime)
|
||||
var quantities: Dictionary = {}
|
||||
if E.gt(processed_year, world.state.time.year):
|
||||
if budget != null: budget.call("set_phase", "initialize_life_batches")
|
||||
var profile_initialize: int = world.profile_start()
|
||||
initialize_life_batches()
|
||||
quantities = initialize_life_batches()
|
||||
world.profile_end("initialize", profile_initialize)
|
||||
var previous: Dictionary = world.state
|
||||
if budget != null: budget.call("set_phase", "deep_copy_and_coarsen")
|
||||
@@ -46,7 +48,6 @@ func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefC
|
||||
var from_year: Variant = previous.time.year
|
||||
var to_year: Variant = processed_year
|
||||
if E.gt(to_year, from_year):
|
||||
var quantities: Dictionary = world.ledger.physical_amounts(previous)
|
||||
var distribution: Dictionary = QuantityMath.normalize_distribution(quantities)
|
||||
next.distribution = distribution
|
||||
|
||||
@@ -71,12 +72,14 @@ func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefC
|
||||
var profile_trends: int = world.profile_start()
|
||||
WorldParameters.advance(previous, next, Global.CivilizationRules, E.subtract(to_year, from_year))
|
||||
world.profile_end("trends", profile_trends)
|
||||
world.apocalypse.check_density(next)
|
||||
next.time = clock if not clock.is_empty() else {"datetime": datetime, "year": to_year}
|
||||
|
||||
# Publish only after every phase has finished; previous stays untouched.
|
||||
world.state = E.bound_state(next)
|
||||
if E.gt(to_year, from_year):
|
||||
world.presentation.refresh(world.state, world.catalog)
|
||||
world.update_adaptive_quality(world.adaptive_timer_usec() - adaptive_started)
|
||||
|
||||
|
||||
func evolve_batches(target: Dictionary, to_year: Variant, budget: RefCounted = null) -> void:
|
||||
@@ -88,7 +91,15 @@ func evolve_batches(target: Dictionary, to_year: Variant, budget: RefCounted = n
|
||||
"enlightenment": WorldParameters.value(target, "Enlightenment"),
|
||||
"explosiveness": WorldParameters.effective(target, "Exp")
|
||||
}
|
||||
var generations: int = 0
|
||||
var generation_limit: int = 0
|
||||
if world.adaptive_quality == 1: generation_limit = 3
|
||||
elif world.adaptive_quality >= 2: generation_limit = 1
|
||||
while await evolve_batch_generation(target, to_year, completed, budget, context):
|
||||
generations += 1
|
||||
# Direct terminal stellar events queued during a generation still run
|
||||
# immediately, preserving their blast and matter outputs.
|
||||
if generation_limit > 0 and generations >= generation_limit and completed.is_empty(): break
|
||||
if budget != null and not await budget.pause(): return
|
||||
|
||||
|
||||
@@ -108,6 +119,16 @@ func evolve_batch_generation(target: Dictionary, to_year: Variant, completed: Di
|
||||
var batch_ids: Array = []
|
||||
for id in target.batches:
|
||||
if not Global.CivilizationRules.has(id): batch_ids.append(id)
|
||||
if world.adaptive_quality >= 3:
|
||||
batch_ids.sort()
|
||||
var selected_ids: Array = []
|
||||
for i in batch_ids.size():
|
||||
if i % 2 == world.adaptive_generation_phase or completed.has(batch_ids[i]):
|
||||
selected_ids.append(batch_ids[i])
|
||||
for id in completed:
|
||||
if not Global.CivilizationRules.has(id) and not selected_ids.has(id):
|
||||
selected_ids.append(id)
|
||||
batch_ids = selected_ids
|
||||
var planets_changed: bool = false
|
||||
var profile_withdraw: int = world.profile_start()
|
||||
for id in batch_ids:
|
||||
|
||||
@@ -3,7 +3,7 @@ const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const CivilizationState = preload("res://world/owniverse/civilization_state.gd")
|
||||
const QuantityMath = preload("res://world/owniverse/quantity_math.gd")
|
||||
|
||||
func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant, automatic: bool = false) -> bool:
|
||||
func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant, automatic: bool = false, physical: Dictionary = {}, helium_share_override: Variant = NAN) -> bool:
|
||||
if (E.le(amount, 0) or E.ne(amount, E.floor_value(amount))):
|
||||
return false
|
||||
var trial: Dictionary = target.duplicate()
|
||||
@@ -13,10 +13,25 @@ func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant
|
||||
trial.batches = target.batches.duplicate()
|
||||
# Physical object totals do not change while resources are withdrawn from
|
||||
# this transaction. Keep one snapshot instead of scanning every resource.
|
||||
var physical: Dictionary = world.ledger.physical_amounts(trial)
|
||||
var shared_physical: bool = not physical.is_empty()
|
||||
if not shared_physical: physical = world.ledger.physical_amounts(trial)
|
||||
var next_physical: Dictionary = physical.duplicate() if shared_physical else {}
|
||||
var copied_batches: Dictionary = {}
|
||||
var helium_share: Variant = stellar_helium_share(target) if not E.finite(helium_share_override) else helium_share_override
|
||||
for resource in entity.cost:
|
||||
var cost: Variant = E.round_value(E.multiply(E.maximum(0, entity.cost[resource]), amount))
|
||||
if E.eq(resource, "H") and uses_stellar_helium(entity):
|
||||
var helium_cost: Variant = E.minimum(E.floor_value(E.multiply(cost, helium_share)), world.ledger.available(trial, "He"))
|
||||
cost = E.subtract(cost, helium_cost)
|
||||
if E.gt(helium_cost, 0):
|
||||
var helium_taken: Dictionary = world.ledger.take_available(trial, "He", helium_cost)
|
||||
var total_helium_taken: Variant = 0
|
||||
for id in helium_taken:
|
||||
total_helium_taken = E.add(total_helium_taken, helium_taken[id])
|
||||
if shared_physical and next_physical.has(id):
|
||||
next_physical[id] = E.subtract(next_physical[id], helium_taken[id])
|
||||
if E.ne(total_helium_taken, helium_cost):
|
||||
return false
|
||||
if (automatic and E.lt(trial.auto_resources.get(resource, 0), cost)):
|
||||
return false
|
||||
var taken: Dictionary = world.ledger.take_available(trial, resource, cost)
|
||||
@@ -28,6 +43,7 @@ func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant
|
||||
if automatic:
|
||||
trial.auto_resources[resource] = E.maximum(0, E.subtract(trial.auto_resources.get(resource, 0), cost))
|
||||
for id in taken:
|
||||
if shared_physical and next_physical.has(id): next_physical[id] = E.subtract(next_physical[id], taken[id])
|
||||
# Isolate only batches actually touched by this transaction.
|
||||
if (trial.batches.has(id) and (not copied_batches.has(id))):
|
||||
trial.batches[id] = trial.batches[id].duplicate(true)
|
||||
@@ -41,6 +57,7 @@ func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant
|
||||
for field in ["amount", "binds", "batches", "auto_resources"]:
|
||||
if trial.has(field):
|
||||
target[field] = trial[field]
|
||||
if shared_physical: physical.merge(next_physical, true)
|
||||
return true
|
||||
|
||||
|
||||
@@ -95,6 +112,7 @@ func plan_smbh(desired_amount: Variant) -> Dictionary:
|
||||
|
||||
|
||||
func produce_smbh(desired_amount: Variant) -> Variant:
|
||||
if E.gt(world.state.get("apocalypse", {}).get("phase", 0), 0): return 0
|
||||
var plan: Dictionary = plan_smbh(desired_amount)
|
||||
var consumed_mass: Variant = plan.amount
|
||||
var taken: Dictionary = plan.taken
|
||||
@@ -120,10 +138,12 @@ func produce_smbh(desired_amount: Variant) -> Variant:
|
||||
world.ledger.clamp_auto_resources(next)
|
||||
world.state = next
|
||||
world.presentation.refresh(world.state, world.catalog)
|
||||
world.apocalypse.check_density(world.state)
|
||||
return consumed_mass
|
||||
|
||||
|
||||
func produce(item_id: String, desired_amount: Variant) -> Variant:
|
||||
if E.gt(world.state.get("apocalypse", {}).get("phase", 0), 0): return 0
|
||||
if E.eq(item_id, "SMBH"):
|
||||
return produce_smbh(desired_amount)
|
||||
# Use special production for S and H
|
||||
@@ -143,19 +163,23 @@ func produce(item_id: String, desired_amount: Variant) -> Variant:
|
||||
return 0
|
||||
|
||||
var metallicity: Variant = world.environment.stellar_environment(world.state).metallicity
|
||||
if (not try_production(world.state, entity, max_producible)):
|
||||
var helium_share: Variant = stellar_helium_share(world.state)
|
||||
if (not try_production(world.state, entity, max_producible, false, {}, helium_share)):
|
||||
return 0
|
||||
|
||||
# Add produced entities
|
||||
world.ledger.process_entity(item_id, max_producible)
|
||||
world.batches.add_entity_to_batch(item_id, max_producible, world.state.time.datetime)
|
||||
var lifespan_multiplier: Variant = stellar_lifespan_multiplier(entity, helium_share)
|
||||
world.batches.append_batch(world.state, item_id, max_producible, world.state.time.datetime, world.state.time.datetime, {}, -INF, null, null, NAN, lifespan_multiplier)
|
||||
world.stellar.create_planets(world.state, item_id, max_producible, world.state.time.datetime, world.state.time.datetime, metallicity)
|
||||
world.ledger.refresh_groups(world.state)
|
||||
world.presentation.refresh(world.state, world.catalog)
|
||||
world.apocalypse.check_density(world.state)
|
||||
return max_producible
|
||||
|
||||
|
||||
func produce_hs(entity_id: String, value: Variant) -> Variant:
|
||||
if E.gt(world.state.get("apocalypse", {}).get("phase", 0), 0): return 0
|
||||
if E.le(value, 0):
|
||||
return 0
|
||||
|
||||
@@ -167,7 +191,8 @@ func produce_hs(entity_id: String, value: Variant) -> Variant:
|
||||
|
||||
world.state.energy = E.subtract(world.state.energy, to_produce)
|
||||
world.ledger.process_entity(entity_id, to_produce)
|
||||
|
||||
world.presentation.refresh(world.state, world.catalog)
|
||||
world.apocalypse.check_density(world.state)
|
||||
return to_produce
|
||||
|
||||
|
||||
@@ -175,6 +200,39 @@ func production_step(entity: OwniverseEntity) -> Variant:
|
||||
return QuantityMath.production_step(E.add(entity.cost.values(), entity.bind.values()))
|
||||
|
||||
|
||||
func uses_stellar_helium(entity: OwniverseEntity) -> bool:
|
||||
return E.eq(entity.group_as, "Star") and entity.cost.has("H")
|
||||
|
||||
|
||||
## Unity's Hel state is the free helium share of the H+He mixture, capped at 50%.
|
||||
func stellar_helium_share(source: Dictionary) -> Variant:
|
||||
var hydrogen: Variant = world.ledger.available(source, "H")
|
||||
var helium: Variant = world.ledger.available(source, "He")
|
||||
var total: Variant = E.add(hydrogen, helium)
|
||||
if E.le(total, 0): return 0
|
||||
var parameters: Dictionary = Global.ExtraParams
|
||||
var share: Variant = E.multiply(E.divide(helium, total), parameters.get("helium_multiplier", 1))
|
||||
share = E.minimum(share, E.of("0.5"))
|
||||
share = E.minimum(share, E.maximum(0, parameters.get("helium_bonus_limit", 1)))
|
||||
return E.clamp_value(share, 0, 1)
|
||||
|
||||
|
||||
func stellar_lifespan_multiplier(entity: OwniverseEntity, helium_share: Variant) -> Variant:
|
||||
return E.maximum(0, E.subtract(1, E.multiply(E.of("1.5"), helium_share))) if uses_stellar_helium(entity) else 1
|
||||
|
||||
|
||||
## Maximum star count when helium may replace this share of its hydrogen cost.
|
||||
func stellar_hydrogen_capacity(hydrogen_cost: Variant, hydrogen_available: Variant, helium_available: Variant, helium_share: Variant) -> Variant:
|
||||
if E.le(hydrogen_cost, 0): return INF
|
||||
if E.le(helium_share, 0) or E.le(helium_available, 0):
|
||||
return E.floor_value(E.divide(hydrogen_available, hydrogen_cost))
|
||||
var hydrogen_only_capacity: Variant = E.divide(hydrogen_available, E.multiply(hydrogen_cost, E.subtract(1, helium_share)))
|
||||
var helium_saturation_capacity: Variant = E.divide(helium_available, E.multiply(hydrogen_cost, helium_share))
|
||||
if E.le(hydrogen_only_capacity, helium_saturation_capacity):
|
||||
return E.floor_value(hydrogen_only_capacity)
|
||||
return E.floor_value(E.divide(E.add(hydrogen_available, helium_available), hydrogen_cost))
|
||||
|
||||
|
||||
func calculate_max_producible(entity: OwniverseEntity, desired_amount: Variant = null) -> Variant:
|
||||
if E.eq(entity.id, "SMBH"):
|
||||
return plan_smbh(smbh_available() if desired_amount == null else desired_amount).amount
|
||||
@@ -192,10 +250,24 @@ func calculate_max_producible(entity: OwniverseEntity, desired_amount: Variant =
|
||||
if (not has_requirements):
|
||||
return 0
|
||||
var available: Variant = INF if desired_amount == null else E.maximum(0, desired_amount)
|
||||
var helium_share: Variant = stellar_helium_share(world.state)
|
||||
for resource in requirements:
|
||||
if E.eq(resource, "H") and uses_stellar_helium(entity):
|
||||
var hydrogen_cost: Variant = E.maximum(0, entity.cost.get("H", 0))
|
||||
available = E.minimum(available, stellar_hydrogen_capacity(hydrogen_cost, world.ledger.available(world.state, "H"), world.ledger.available(world.state, "He"), helium_share))
|
||||
var hydrogen_binding: Variant = E.maximum(0, entity.bind.get("H", 0))
|
||||
if E.gt(hydrogen_binding, 0):
|
||||
available = E.minimum(available, E.floor_value(E.divide(world.ledger.available(world.state, "H"), hydrogen_binding)))
|
||||
continue
|
||||
if E.gt(requirements[resource], 0):
|
||||
available = E.minimum(available, E.floor_value(E.divide(world.ledger.available(world.state, resource), requirements[resource])))
|
||||
var concrete: Dictionary = world.ledger.expand_requirements(world.state, requirements)
|
||||
var concrete_requirements: Dictionary = requirements.duplicate()
|
||||
if uses_stellar_helium(entity):
|
||||
# The direct H cost was already handled by the H/He capacity above;
|
||||
# preserve any separate H binding requirement.
|
||||
concrete_requirements["H"] = E.maximum(0, E.subtract(concrete_requirements.get("H", 0), entity.cost.get("H", 0)))
|
||||
if E.le(concrete_requirements["H"], 0): concrete_requirements.erase("H")
|
||||
var concrete: Dictionary = world.ledger.expand_requirements(world.state, concrete_requirements)
|
||||
for resource in concrete:
|
||||
if E.gt(concrete[resource], 0):
|
||||
available = E.minimum(available, E.floor_value(E.divide(world.ledger.available(world.state, resource), concrete[resource])))
|
||||
|
||||
@@ -6,13 +6,11 @@ const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
|
||||
|
||||
## Diagnostic switch for paired measurements of the old and new timing models.
|
||||
var cycle_stars: bool = true
|
||||
var _cycle_widths: Dictionary = {}
|
||||
|
||||
func star_cycle_width(target: Dictionary) -> Variant:
|
||||
var speed: int = int(target.get("speed", 1))
|
||||
if not _cycle_widths.has(speed):
|
||||
_cycle_widths[speed] = E.multiply(E.power(10, speed - 1), world.frame_step_seconds)
|
||||
return _cycle_widths[speed]
|
||||
var width: Variant = E.multiply(E.power(10, speed - 1), world.frame_step_seconds)
|
||||
return E.multiply(width, E.power(2, world.adaptive_quality))
|
||||
|
||||
## The cycle grid may widen a short window, never subdivide a wide one.
|
||||
func uses_cycle_star(target: Dictionary, id: String, lifespan: Variant) -> bool:
|
||||
@@ -70,7 +68,11 @@ func coarsen_cycle_stars(target: Dictionary, id: String) -> void:
|
||||
|
||||
func temporal_resolution(target: Dictionary) -> Variant:
|
||||
var speed: int = int(target.get("speed", 1))
|
||||
return E.divide(E.power(10, speed - 1), 10) if speed >= 4 else 0
|
||||
if speed < 4: return 0
|
||||
# Keep the existing 0.1-second resolution at the default 5 Hz cadence,
|
||||
# and scale temporal grouping with the adaptive cycle interval.
|
||||
var width: Variant = E.multiply(E.power(10, speed - 1), world.frame_step_seconds / 2.0)
|
||||
return E.multiply(width, E.power(2, world.adaptive_quality))
|
||||
|
||||
func window_duration(target: Dictionary, lifespan: Variant) -> Variant:
|
||||
if E.le(lifespan, 10): return lifespan
|
||||
@@ -256,7 +258,7 @@ func scale_batches(target: Dictionary, id: String, factor: Variant) -> void:
|
||||
share.batch.withdraw_remainder = E.minimum(share.batch.get("withdraw_remainder", 0), share.batch.amount)
|
||||
|
||||
|
||||
func append_batch(target: Dictionary, id: String, amount: Variant, created_from: Variant, created_to: Variant, completed: Dictionary = { }, completed_before: Variant = - INF, budget: RefCounted = null, newborn_adjustment: Variant = null, newborn_until: Variant = NAN) -> Variant:
|
||||
func append_batch(target: Dictionary, id: String, amount: Variant, created_from: Variant, created_to: Variant, completed: Dictionary = { }, completed_before: Variant = - INF, budget: RefCounted = null, newborn_adjustment: Variant = null, newborn_until: Variant = NAN, lifespan_multiplier: Variant = 1) -> Variant:
|
||||
var _keep_alive: RefCounted = self
|
||||
if Global.CivilizationRules.has(id): return NAN
|
||||
if ((E.le(amount, 0) or (not world.catalog.entities.has(id))) or E.le(world.catalog.entities[id].lifespan, 0)):
|
||||
@@ -269,7 +271,7 @@ func append_batch(target: Dictionary, id: String, amount: Variant, created_from:
|
||||
if (not target.has("batch_origins")):
|
||||
target.batch_origins = {}
|
||||
var batches: Array = target.batches[id]
|
||||
var lifespan: Variant = effective_lifespan(target, id)
|
||||
var lifespan: Variant = E.multiply(effective_lifespan(target, id), E.maximum(0, lifespan_multiplier))
|
||||
if uses_cycle_star(target, id, lifespan):
|
||||
return await append_cycle_star(target, id, amount, created_from, created_to, lifespan, completed, completed_before, budget, newborn_adjustment, newborn_until)
|
||||
var batch_duration: Variant = window_duration(target, lifespan)
|
||||
|
||||
@@ -63,29 +63,41 @@ func expand_requirements(source: Dictionary, requirements: Dictionary) -> Dictio
|
||||
func take_available(target: Dictionary, id: String, amount: Variant) -> Dictionary:
|
||||
var quantities: Dictionary = {}
|
||||
for member in world.catalog.members(id):
|
||||
quantities[member] = target.amount.get(member, 0)
|
||||
var distribution: Dictionary = QuantityMath.normalize_distribution(quantities)
|
||||
quantities[member] = E.maximum(0, target.amount.get(member, 0))
|
||||
var taken: Dictionary = {}
|
||||
var total: Variant = E.minimum(amount, available(target, id))
|
||||
var discrete: bool = (not distribution.is_empty())
|
||||
for member in distribution:
|
||||
discrete = (discrete and is_discrete(member))
|
||||
if discrete:
|
||||
total = E.floor_value(total)
|
||||
var stock_total: Variant = 0
|
||||
for quantity in quantities.values(): stock_total = E.add(stock_total, quantity)
|
||||
var total: Variant = E.minimum(amount, stock_total)
|
||||
var discrete: bool = true
|
||||
for member in quantities:
|
||||
discrete = discrete and is_discrete(member)
|
||||
if discrete: total = E.floor_value(total)
|
||||
if E.le(total, 0) or E.le(stock_total, 0): return taken
|
||||
if E.eq(total, stock_total):
|
||||
taken = quantities
|
||||
elif discrete:
|
||||
var shares: Array = []
|
||||
var assigned: Variant = 0
|
||||
for member in distribution:
|
||||
var expected: Variant = E.multiply(total, distribution[member])
|
||||
taken[member] = (E.floor_value(expected) if discrete else expected)
|
||||
assigned = E.add(assigned, taken[member])
|
||||
shares.append({"id": member, "rest": E.subtract(expected, taken[member])})
|
||||
if discrete:
|
||||
shares.sort_custom(func ( a : Dictionary , b : Dictionary ) -> bool : return (E.gt(a.rest, b.rest) if E.ne(a.rest, b.rest) else E.lt(str(a.id), str(b.id))))
|
||||
for member in quantities:
|
||||
var expected: Variant = E.mul_div(total, quantities[member], stock_total)
|
||||
var whole: Variant = E.floor_value(expected)
|
||||
taken[member] = whole
|
||||
assigned = E.add(assigned, whole)
|
||||
shares.append({"id": member, "rest": E.subtract(expected, whole)})
|
||||
shares.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return E.gt(a.rest, b.rest) if E.ne(a.rest, b.rest) else E.lt(str(a.id), str(b.id)))
|
||||
var missing: Variant = E.subtract(total, assigned)
|
||||
for share: Dictionary in shares:
|
||||
if E.ge(assigned, total):
|
||||
break
|
||||
taken[share.id] = E.add(taken[share.id], 1)
|
||||
assigned = E.add(assigned, 1)
|
||||
if E.le(missing, 0): break
|
||||
var member: String = share.id
|
||||
if E.lt(taken[member], quantities[member]):
|
||||
taken[member] = E.add(taken[member], 1)
|
||||
missing = E.subtract(missing, 1)
|
||||
assert(E.eq(missing, 0), "Discrete resource allocation must exhaust its requested amount")
|
||||
else:
|
||||
var distribution: Dictionary = QuantityMath.normalize_distribution(quantities)
|
||||
for member in distribution:
|
||||
taken[member] = E.multiply(total, distribution[member])
|
||||
|
||||
for member in taken:
|
||||
target.amount[member] = E.maximum(0, E.subtract(target.amount.get(member, 0), taken[member]))
|
||||
return taken
|
||||
|
||||
+120
-18
@@ -1,5 +1,6 @@
|
||||
extends Node
|
||||
class_name Owniverse
|
||||
signal production_applied(id: String)
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
## Owns the state and modules. Rules live in the modules below.
|
||||
const SimulationTime = preload("res://world/owniverse/simulation_time.gd")
|
||||
@@ -11,12 +12,66 @@ var ledger = preload("res://world/owniverse/object_ledger.gd").new(self)
|
||||
var production = preload("res://world/owniverse/manual_production.gd").new(self)
|
||||
var automatic = preload("res://world/owniverse/automatic_production.gd").new(self)
|
||||
var stellar = preload("res://world/owniverse/stellar_rules.gd").new(self)
|
||||
var apocalypse = preload("res://world/owniverse/apocalypse_controller.gd").new(self)
|
||||
var life = preload("res://world/owniverse/life_evolution.gd").new(self)
|
||||
var environment = preload("res://world/owniverse/universe_environment.gd").new(self)
|
||||
var evolution = preload("res://world/owniverse/evolution_coordinator.gd").new(self)
|
||||
var civilization_flow = preload("res://world/owniverse/civilization_flow.gd").new(self)
|
||||
var profile_enabled: bool = false
|
||||
var profile: Dictionary = {}
|
||||
## Runtime-only quality controller. It is deliberately not serialized.
|
||||
var adaptive_quality: int = 0
|
||||
var adaptive_cycle_ema_ms: float = 0.0
|
||||
var adaptive_recovery_cycles: int = 0
|
||||
var adaptive_generation_phase: int = 0
|
||||
## Runtime-only cadence: 5, 4, 3, or 2 full simulation cycles per second.
|
||||
var adaptive_cadence: bool = true
|
||||
var cadence_level: int = 0
|
||||
var cadence_recovery_cycles: int = 0
|
||||
const CADENCE_STEPS: Array[float] = [0.2, 0.25, 1.0 / 3.0, 0.5]
|
||||
var frame_step_seconds: float = 0.2
|
||||
|
||||
func update_adaptive_quality(cycle_usec: int) -> void:
|
||||
var cycle_ms: float = float(cycle_usec) / 1000.0
|
||||
adaptive_cycle_ema_ms = cycle_ms if adaptive_cycle_ema_ms <= 0.0 else adaptive_cycle_ema_ms * 0.8 + cycle_ms * 0.2
|
||||
if adaptive_cycle_ema_ms > 300.0:
|
||||
adaptive_quality = 3
|
||||
adaptive_recovery_cycles = 0
|
||||
elif adaptive_quality >= 2 and adaptive_cycle_ema_ms > 200.0:
|
||||
adaptive_quality = 3
|
||||
adaptive_recovery_cycles = 0
|
||||
elif adaptive_cycle_ema_ms > 220.0:
|
||||
adaptive_quality = maxi(adaptive_quality, 2)
|
||||
adaptive_recovery_cycles = 0
|
||||
elif adaptive_cycle_ema_ms > 200.0:
|
||||
adaptive_quality = maxi(adaptive_quality, 1)
|
||||
adaptive_recovery_cycles = 0
|
||||
elif adaptive_cycle_ema_ms < 140.0:
|
||||
adaptive_recovery_cycles += 1
|
||||
if adaptive_recovery_cycles >= 30:
|
||||
adaptive_quality = maxi(0, adaptive_quality - 1)
|
||||
adaptive_recovery_cycles = 0
|
||||
else:
|
||||
adaptive_recovery_cycles = 0
|
||||
if adaptive_cadence:
|
||||
var step: float = CADENCE_STEPS[cadence_level]
|
||||
if adaptive_cycle_ema_ms > step * 900.0 and cadence_level < CADENCE_STEPS.size() - 1:
|
||||
cadence_level += 1
|
||||
frame_step_seconds = CADENCE_STEPS[cadence_level]
|
||||
cadence_recovery_cycles = 0
|
||||
elif adaptive_cycle_ema_ms < step * 650.0 and cadence_level > 0:
|
||||
cadence_recovery_cycles += 1
|
||||
if cadence_recovery_cycles >= 30:
|
||||
cadence_level -= 1
|
||||
frame_step_seconds = CADENCE_STEPS[cadence_level]
|
||||
cadence_recovery_cycles = 0
|
||||
else:
|
||||
cadence_recovery_cycles = 0
|
||||
if adaptive_quality >= 3:
|
||||
adaptive_generation_phase = 1 - adaptive_generation_phase
|
||||
|
||||
func adaptive_timer_usec() -> int:
|
||||
return Time.get_ticks_usec() - (frame_budget.waiting_usec if frame_busy and frame_budget != null else 0)
|
||||
|
||||
func profile_start() -> int:
|
||||
if not profile_enabled: return 0
|
||||
@@ -55,6 +110,7 @@ var state: Dictionary = {
|
||||
'civilization_stats': {},
|
||||
'civilization_homes': {},
|
||||
'ethics': {},
|
||||
'apocalypse': {'phase': 0, 'type': 0, 'warning': 0, 'stage': 1, 'tick': 0, 'end_ticks': 0},
|
||||
'stats': {'total_amount': {}},
|
||||
}
|
||||
|
||||
@@ -64,42 +120,86 @@ var frame_clock: Dictionary = {}
|
||||
var frame_budget: RefCounted
|
||||
var _requested_speed: int = 0
|
||||
var _queued_productions: Array = []
|
||||
var production_queue_busy: bool = false
|
||||
var _production_queue_cancelled: bool = false
|
||||
var frame_active_clock: Dictionary = {}
|
||||
var _resume_clock: Dictionary = {}
|
||||
const frame_step_seconds: float = 0.2
|
||||
var frame_elapsed_seconds: float = 0.0
|
||||
const production_slice_usec: int = 4000
|
||||
|
||||
## The HUD follows requested time; world state remains at the last committed cycle.
|
||||
func display_clock() -> Dictionary:
|
||||
return frame_clock if not frame_clock.is_empty() else state.time
|
||||
|
||||
## Keep the requested clock advancing while a transaction spans render frames.
|
||||
func evolute_frame(delta: float, tree: SceneTree) -> void:
|
||||
if not is_finite(delta) or delta < 0.0 or not is_finite(delta * pow(10.0, speed - 1)): return
|
||||
if E.ge(state.get("apocalypse", {}).get("phase", 0), 2):
|
||||
frame_clock = state.time.duplicate()
|
||||
frame_elapsed_seconds = 0.0
|
||||
return
|
||||
if frame_clock.is_empty(): frame_clock = state.time.duplicate()
|
||||
frame_clock = SimulationTime.advance(frame_clock, delta, speed)
|
||||
if frame_busy: return
|
||||
frame_elapsed_seconds += delta
|
||||
if frame_busy or production_queue_busy: return
|
||||
var next_clock: Dictionary = _resume_clock
|
||||
if next_clock.is_empty() and not _queued_productions.is_empty():
|
||||
_start_production_drain(tree)
|
||||
return
|
||||
if next_clock.is_empty():
|
||||
next_clock = SimulationTime.advance(state.time, frame_step_seconds, speed)
|
||||
# Start only after a full 0.2 seconds of simulation time is requested.
|
||||
if E.gt(next_clock.datetime, frame_clock.datetime): return
|
||||
# Cadence limits starts; each transaction consumes the whole pending interval.
|
||||
if frame_elapsed_seconds + 1e-12 < frame_step_seconds: return
|
||||
next_clock = frame_clock.duplicate()
|
||||
frame_elapsed_seconds = 0.0
|
||||
frame_busy = true
|
||||
frame_budget = preload("res://world/owniverse/frame_budget.gd").new(tree)
|
||||
frame_active_clock = next_clock
|
||||
_resume_clock = {}
|
||||
evolution.run_frame(frame_active_clock, frame_budget)
|
||||
|
||||
func request_production(id: String, amount: Variant, resource: bool = false) -> void:
|
||||
if frame_busy:
|
||||
func request_production(id: String, amount: Variant, resource: bool = false) -> bool:
|
||||
if E.gt(state.get("apocalypse", {}).get("phase", 0), 0): return false
|
||||
if frame_busy or production_queue_busy:
|
||||
_queued_productions.append({"id": id, "amount": amount, "resource": resource})
|
||||
elif resource:
|
||||
production.produce_hs(id, amount)
|
||||
return true
|
||||
_apply_production({"id": id, "amount": amount, "resource": resource})
|
||||
return false
|
||||
|
||||
func _apply_production(command: Dictionary) -> void:
|
||||
if command.resource:
|
||||
production.produce_hs(command.id, command.amount)
|
||||
else:
|
||||
production.produce(id, amount)
|
||||
production.produce(command.id, command.amount)
|
||||
production_applied.emit(command.id)
|
||||
|
||||
func _start_production_drain(tree: SceneTree) -> void:
|
||||
if production_queue_busy or _queued_productions.is_empty(): return
|
||||
production_queue_busy = true
|
||||
_production_queue_cancelled = false
|
||||
_drain_productions(tree)
|
||||
|
||||
func _drain_productions(tree: SceneTree) -> void:
|
||||
var keep_alive: Owniverse = self
|
||||
while not _queued_productions.is_empty():
|
||||
var slice_started: int = Time.get_ticks_usec()
|
||||
while not _queued_productions.is_empty() and Time.get_ticks_usec() - slice_started < production_slice_usec:
|
||||
var command: Dictionary = _queued_productions.pop_front()
|
||||
_apply_production(command)
|
||||
if _queued_productions.is_empty(): break
|
||||
await tree.process_frame
|
||||
if keep_alive._production_queue_cancelled: return
|
||||
production_queue_busy = false
|
||||
|
||||
## Persist work requests, never the partially calculated transaction.
|
||||
func frame_checkpoint() -> Dictionary:
|
||||
return {"requested_clock": frame_clock, "active_clock": frame_active_clock if frame_busy else {}, "speed": speed, "productions": _queued_productions}
|
||||
return {"requested_clock": frame_clock, "active_clock": frame_active_clock if frame_busy else {}, "speed": speed, "productions": _queued_productions, "elapsed_seconds": frame_elapsed_seconds}
|
||||
|
||||
func restore_frame_checkpoint(checkpoint: Dictionary) -> void:
|
||||
frame_clock = checkpoint.get("requested_clock", {})
|
||||
_resume_clock = checkpoint.get("active_clock", {})
|
||||
var reserved_clock: Dictionary = _resume_clock if not _resume_clock.is_empty() else state.time
|
||||
var pending_years: Variant = E.maximum(0, E.subtract(frame_clock.get("datetime", reserved_clock.datetime), reserved_clock.datetime))
|
||||
frame_elapsed_seconds = float(checkpoint.get("elapsed_seconds", E.approx(pending_years) / pow(10.0, int(checkpoint.get("speed", state.speed)) - 1)))
|
||||
_queued_productions = checkpoint.get("productions", [])
|
||||
if _resume_clock.is_empty():
|
||||
speed = checkpoint.get("speed", state.speed)
|
||||
@@ -112,23 +212,25 @@ func finish_frame() -> void:
|
||||
if _requested_speed != 0:
|
||||
speed = _requested_speed
|
||||
_requested_speed = 0
|
||||
var productions: Array = _queued_productions
|
||||
_queued_productions = []
|
||||
for command in productions:
|
||||
request_production(command.id, command.amount, command.resource)
|
||||
if not _queued_productions.is_empty():
|
||||
_start_production_drain(frame_budget.tree)
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_PREDELETE and frame_budget != null:
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
_production_queue_cancelled = true
|
||||
if frame_budget != null:
|
||||
frame_budget.cancelled = true
|
||||
|
||||
var speed: int :
|
||||
get:
|
||||
return _requested_speed if _requested_speed != 0 else state.speed
|
||||
set(value):
|
||||
var requested_speed: int = clampi(value, 1, Global.SPEED_LIMIT)
|
||||
if E.eq(state.get("apocalypse", {}).get("phase", 0), 1): requested_speed = 1
|
||||
if frame_busy:
|
||||
_requested_speed = clampi(value, 1, Global.SPEED_LIMIT)
|
||||
_requested_speed = requested_speed
|
||||
return
|
||||
state.speed = clampi(value, 1, Global.SPEED_LIMIT)
|
||||
state.speed = requested_speed
|
||||
state.speed_in_year = E.power(10, E.subtract(state.speed, 1))
|
||||
|
||||
func evolute(delta: Variant) -> void:
|
||||
|
||||
@@ -303,6 +303,27 @@ static func split_arrays(remainders: Dictionary, key: String, count: Variant, id
|
||||
|
||||
|
||||
static func accumulate_rate(remainders: Dictionary, key: String, count: Variant, probability: Variant) -> Variant:
|
||||
# Reward rates are floats while populations are exact whole values. Keep
|
||||
# the product as quotient plus fractional carry instead of constructing an
|
||||
# intermediate Exact Value and flooring it again.
|
||||
if probability is float and is_finite(probability) and probability >= 0.0 and (count is int or (count is E.Value and count.fraction == 0.0)) and E.ge(count, 0):
|
||||
var terms: Array = E.parts(probability)
|
||||
if terms[2] > 0:
|
||||
var population: Array = E._blocks(count)[0]
|
||||
var whole_product: Array = E.Big.multiply(population, terms[0])
|
||||
var qr: Array = E.Big.divmod(whole_product, terms[1])
|
||||
var fractional: float = E._fraction_ratio(qr[1], terms[1])
|
||||
var carry: float = E.approx(remainders.get(key, 0))
|
||||
if carry >= 0.0 and carry < 1.0:
|
||||
if qr[1] == [0]:
|
||||
remainders[key] = carry
|
||||
return E._compose(qr[0])
|
||||
fractional += carry
|
||||
if fractional > 0.5 and absf(fractional - roundf(fractional)) <= 1e-12:
|
||||
fractional = roundf(fractional)
|
||||
var extra: int = floori(fractional)
|
||||
remainders[key] = fractional - extra
|
||||
return E.add(E._compose(qr[0]), extra)
|
||||
var product: Variant = E.multiply(count, E.maximum(0, probability))
|
||||
var carry: float = E.approx(remainders.get(key, 0))
|
||||
if carry >= 0.0 and carry < 1.0 and E.ge(product, 0):
|
||||
|
||||
@@ -2,8 +2,8 @@ extends "res://world/owniverse/simulation_module.gd"
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
|
||||
|
||||
func stellar_environment(source: Dictionary) -> Dictionary:
|
||||
var quantities: Dictionary = world.ledger.physical_amounts(source)
|
||||
func stellar_environment(source: Dictionary, quantities: Dictionary = {}) -> Dictionary:
|
||||
if quantities.is_empty(): quantities = world.ledger.physical_amounts(source)
|
||||
# SMBH is a self-group in GI; its quantity stores mass, not object count.
|
||||
var mass: Variant = source.amount.get("SMBH", 0)
|
||||
# Geometric volume includes a 1e22 scale per void and can exceed E27.
|
||||
|
||||
+36
-8
@@ -64,12 +64,15 @@
|
||||
[ext_resource type="Texture2D" uid="uid://bce2bpw5642er" path="res://ui/achievements/achievements.en.png" id="61_d36xh"]
|
||||
[ext_resource type="Script" uid="uid://vy0dj2cqfmk0" path="res://ui/credits/credits.gd" id="62_o2i0n"]
|
||||
[ext_resource type="Texture2D" uid="uid://c46h78ewwlafk" path="res://ui/credits/credits.en.png" id="62_vu83j"]
|
||||
[ext_resource type="PackedScene" uid="uid://cs8a5u3ecf51t" path="res://ui/achievements/achievement_item.tscn" id="64_0drd1"]
|
||||
[ext_resource type="Texture2D" uid="uid://drvvkd724g8hy" path="res://ui/credits/kid_gp_light.png" id="64_rur7c"]
|
||||
[ext_resource type="Texture2D" uid="uid://b33ji4mpyw4ks" path="res://ui/credits/kid_gp.png" id="65_y1rwn"]
|
||||
[ext_resource type="Texture2D" uid="uid://dgt52qiyuirso" path="res://ui/indicators/density/warning_border.png" id="66_rur7c"]
|
||||
[ext_resource type="Script" uid="uid://btsrt6rw3nr04" path="res://ui/audio/owniverse_music_player.gd" id="67_music"]
|
||||
[ext_resource type="Script" uid="uid://botb0k4qr0hqo" path="res://ui/audio/owniverse_effects_player.gd" id="68_effects"]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/report/report.gd" id="69_report"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ifjjt"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_58wjt"]
|
||||
@@ -234,23 +237,23 @@ theme_override_constants/separation = 0
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="PageHeader" type="HBoxContainer" parent="Stage/World/PageScroller/HBoxContainer/ForcePage" unique_id=2102307933]
|
||||
[node name="ForceHeader" type="HBoxContainer" parent="Stage/World/PageScroller/HBoxContainer/ForcePage" unique_id=2102307933]
|
||||
custom_minimum_size = Vector2(0, 128)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 6
|
||||
size_flags_vertical = 4
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="LeftIndicator" type="TextureButton" parent="Stage/World/PageScroller/HBoxContainer/ForcePage/PageHeader" unique_id=663302862]
|
||||
[node name="LeftIndicator" type="TextureButton" parent="Stage/World/PageScroller/HBoxContainer/ForcePage/ForceHeader" unique_id=663302862]
|
||||
custom_minimum_size = Vector2(316, 64)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Header" type="TextureRect" parent="Stage/World/PageScroller/HBoxContainer/ForcePage/PageHeader" unique_id=1295272502]
|
||||
[node name="Header" type="TextureRect" parent="Stage/World/PageScroller/HBoxContainer/ForcePage/ForceHeader" unique_id=1295272502]
|
||||
custom_minimum_size = Vector2(440, 64)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("13_dp8ul")
|
||||
|
||||
[node name="RighIndicator" type="TextureButton" parent="Stage/World/PageScroller/HBoxContainer/ForcePage/PageHeader" unique_id=504419468]
|
||||
[node name="RighIndicator" type="TextureButton" parent="Stage/World/PageScroller/HBoxContainer/ForcePage/ForceHeader" unique_id=504419468]
|
||||
custom_minimum_size = Vector2(316, 64)
|
||||
layout_mode = 2
|
||||
|
||||
@@ -1011,7 +1014,8 @@ layout_mode = 2
|
||||
texture_normal = ExtResource("52_uvuuk")
|
||||
|
||||
[node name="Report" type="VBoxContainer" parent="Stage" unique_id=1422399839]
|
||||
visible = false
|
||||
script = ExtResource("69_report")
|
||||
custom_minimum_size = Vector2(1080, 1920)
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
@@ -1032,17 +1036,26 @@ custom_minimum_size = Vector2(1080, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Header" type="TextureRect" parent="Stage/Report/Header" unique_id=1321780840]
|
||||
custom_minimum_size = Vector2(824, 256)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("60_an6vj")
|
||||
|
||||
[node name="ReportBody" type="RichTextLabel" parent="Stage/Report" unique_id=1952288631]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
bbcode_enabled = true
|
||||
scroll_active = true
|
||||
theme_override_font_sizes/normal_font_size = 30
|
||||
theme_override_font_sizes/bold_font_size = 30
|
||||
|
||||
[node name="Achievements" type="VBoxContainer" parent="Stage" unique_id=1847732432]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(1080, 1920)
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
|
||||
[node name="TopMenu" type="HBoxContainer" parent="Stage/Achievements" unique_id=40287683]
|
||||
custom_minimum_size = Vector2(0, 128)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="BackButton" type="TextureButton" parent="Stage/Achievements/TopMenu" unique_id=1719885364]
|
||||
@@ -1054,18 +1067,31 @@ texture_normal = ExtResource("44_uvuuk")
|
||||
texture_pressed = ExtResource("45_an6vj")
|
||||
|
||||
[node name="Header" type="CenterContainer" parent="Stage/Achievements" unique_id=51521239]
|
||||
custom_minimum_size = Vector2(1080, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="TextureRect" parent="Stage/Achievements/Header" unique_id=1195638572]
|
||||
custom_minimum_size = Vector2(824, 256)
|
||||
custom_minimum_size = Vector2(824, 128)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
texture = ExtResource("61_d36xh")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="AchievementList" type="ScrollContainer" parent="Stage/Achievements" unique_id=1872434750]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="AchievementItems" type="VBoxContainer" parent="Stage/Achievements/AchievementList" unique_id=1878761996]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="AchievementItem" parent="Stage/Achievements/AchievementList/AchievementItems" unique_id=1952626354 instance=ExtResource("64_0drd1")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Credits" type="VBoxContainer" parent="Stage" unique_id=785042592]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_right = 1080.0
|
||||
offset_bottom = 1920.0
|
||||
@@ -1141,6 +1167,8 @@ script = ExtResource("67_music")
|
||||
[connection signal="pressed" from="Stage/MainMenu/TopMenu/BackButton" to="Stage" method="_on_topmenu_button_pressed" binds= ["World"]]
|
||||
[connection signal="pressed" from="Stage/MainMenu/CreateOwniverse" to="Stage" method="create_new_universe"]
|
||||
[connection signal="pressed" from="Stage/MainMenu/Achievements" to="Stage" method="_on_topmenu_button_pressed" binds= ["Achievements"]]
|
||||
[connection signal="pressed" from="Stage/MainMenu/Language/English" to="Stage/MainMenu" method="_on_change_locale_pressed" binds= ["en"]]
|
||||
[connection signal="pressed" from="Stage/MainMenu/Language/Russian" to="Stage/MainMenu" method="_on_change_locale_pressed" binds= ["ru"]]
|
||||
[connection signal="pressed" from="Stage/MainMenu/Media/Sound" to="Stage/MainMenu" method="_on_sound_pressed"]
|
||||
[connection signal="pressed" from="Stage/MainMenu/Media/Music" to="Stage/MainMenu" method="_on_music_pressed"]
|
||||
[connection signal="value_changed" from="Stage/MainMenu/Volume/SoundVolume" to="Stage/MainMenu" method="_on_sound_volume_value_changed"]
|
||||
|
||||
Reference in New Issue
Block a user