Implement apocalypse mechanics: add apocalypse queue, controller, and integrate into production logic
This commit is contained in:
@@ -30,6 +30,7 @@ var Templates: Dictionary = {}
|
|||||||
var LifeRules: Dictionary = {}
|
var LifeRules: Dictionary = {}
|
||||||
var CivilizationRules: Dictionary = {}
|
var CivilizationRules: Dictionary = {}
|
||||||
var ExtraParams: Dictionary = {}
|
var ExtraParams: Dictionary = {}
|
||||||
|
var ApocalypseQueue: Dictionary = {}
|
||||||
|
|
||||||
var Music: Dictionary = {}
|
var Music: Dictionary = {}
|
||||||
var Effects: Dictionary = {}
|
var Effects: Dictionary = {}
|
||||||
@@ -92,6 +93,7 @@ func _ready() -> void:
|
|||||||
OwniverseItems = OwniverseItemLoader.get_data()
|
OwniverseItems = OwniverseItemLoader.get_data()
|
||||||
Templates = OwniverseTemplateLoader.get_data()
|
Templates = OwniverseTemplateLoader.get_data()
|
||||||
CivilizationRules = OwniverseCivilizationRuleLoader.get_data()
|
CivilizationRules = OwniverseCivilizationRuleLoader.get_data()
|
||||||
|
ApocalypseQueue = ApocalypseQueueLoader.get_data()
|
||||||
|
|
||||||
Music = OwniverseAudioLoader.get_music()
|
Music = OwniverseAudioLoader.get_music()
|
||||||
Effects = OwniverseAudioLoader.get_effects()
|
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
|
# Optional additions to the current format. Saves predating indicators keep
|
||||||
# their quantities and receive neutral defaults from Owniverse on load.
|
# their quantities and receive neutral defaults from Owniverse on load.
|
||||||
for field in saved:
|
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"
|
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 saved.has("world_parameters"):
|
||||||
if ((not _number_map(saved.world_parameters)) or E.ne(saved.world_parameters.size(), WorldParameters.defaults().size())):
|
if ((not _number_map(saved.world_parameters)) or E.ne(saved.world_parameters.size(), WorldParameters.defaults().size())):
|
||||||
return "invalid state.world_parameters"
|
return "invalid state.world_parameters"
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
class_name ApocalypseQueueLoader
|
||||||
|
|
||||||
|
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|Великая стена
|
||||||
|
3|LGF|1|3|5|Галактическая нить
|
||||||
|
4|LSC|1|3|5|Сверхскопление
|
||||||
|
5|LGG|1|3|5|Группа галактик
|
||||||
|
6|GSG|2|3|5|Гигантская спиральная галактика
|
||||||
|
7|GSL|2|3|5|Большая спиральная галактика
|
||||||
|
8|GSM|2|3|5|Средняя спиральная галактика
|
||||||
|
9|GSS|2|3|5|Малая спиральная галактика
|
||||||
|
10|GSD|2|3|5|Карликовая спиральная галактика
|
||||||
|
11|GEG|2|3|5|Гигантская эллиптическая галактика
|
||||||
|
12|GEL|2|3|5|Большая эллиптическая галактика
|
||||||
|
13|GEM|2|3|5|Средняя эллиптическая галактика
|
||||||
|
14|GES|2|3|5|Малая эллиптическая галактика
|
||||||
|
15|GED|2|3|5|Карликовая эллиптическая галактика
|
||||||
|
16|SCG|2|3|5|Гигантское звёздное скопление
|
||||||
|
17|SCL|2|3|5|Большое звёздное скопление
|
||||||
|
18|SCM|2|3|5|Среднее звёздное скопление
|
||||||
|
19|SCS|2|3|5|Малое звёздное скопление
|
||||||
|
20|SCD|2|3|5|Рассеянное звёздное скопление
|
||||||
|
21|C4.4|3|4|20|Цивилизация 4.4
|
||||||
|
22|C4.3|3|4|0|Цивилизация 4.3
|
||||||
|
23|C4.2|3|4|0|Цивилизация 4.2
|
||||||
|
24|C4.1|3|4|0|Цивилизация 4.1
|
||||||
|
25|C4.0|3|4|0|Цивилизация 4.0
|
||||||
|
26|C4|3|4|0|Цивилизация типа 4
|
||||||
|
27|C3.9|4|4|5|Цивилизация 3.9
|
||||||
|
28|C3.8|4|4|0|Цивилизация 3.8
|
||||||
|
29|C3.7|4|4|0|Цивилизация 3.7
|
||||||
|
30|C3.6|4|4|0|Цивилизация 3.6
|
||||||
|
31|C3.5|4|4|0|Цивилизация 3.5
|
||||||
|
32|C3.4|4|4|0|Цивилизация 3.4
|
||||||
|
33|C3.3|4|4|0|Цивилизация 3.3
|
||||||
|
34|C3.2|4|4|0|Цивилизация 3.2
|
||||||
|
35|C3.1|4|4|0|Цивилизация 3.1
|
||||||
|
36|C3.0|4|4|0|Цивилизация 3.0
|
||||||
|
37|C3|4|4|0|Цивилизация типа 3
|
||||||
|
38|C2.9|5|4|5|Цивилизация 2.9
|
||||||
|
39|C2.8|5|4|0|Цивилизация 2.8
|
||||||
|
40|C2.7|5|4|0|Цивилизация 2.7
|
||||||
|
41|C2.6|5|4|0|Цивилизация 2.6
|
||||||
|
42|C2.5|5|4|0|Цивилизация 2.5
|
||||||
|
43|C2.4|5|4|0|Цивилизация 2.4
|
||||||
|
44|C2.3|5|4|0|Цивилизация 2.3
|
||||||
|
45|C2.2|5|4|0|Цивилизация 2.2
|
||||||
|
46|C2.1|5|4|0|Цивилизация 2.1
|
||||||
|
47|C2.0|5|4|0|Цивилизация 2.0
|
||||||
|
48|C2|5|4|0|Цивилизация типа 2
|
||||||
|
49|HG|6|2|5|Гипергигант
|
||||||
|
50|SG|6|2|5|Сверхгигант
|
||||||
|
51|BG|6|2|5|Голубой гигант
|
||||||
|
52|YG|6|2|5|Жёлтый гигант
|
||||||
|
53|RHG|6|2|5|Красный гипергигант
|
||||||
|
54|RSG|6|2|5|Красный сверхгигант
|
||||||
|
55|WYD|6|2|5|Бело-жёлтый карлик
|
||||||
|
56|YD|6|2|5|Жёлтый карлик
|
||||||
|
57|OD|6|2|5|Оранжевый карлик
|
||||||
|
58|RD|6|2|5|Красный карлик
|
||||||
|
59|HRG|6|2|5|Огромный красный гигант
|
||||||
|
60|LRG|6|2|0|Большой красный гигант
|
||||||
|
61|MRG|6|2|0|Средний красный гигант
|
||||||
|
62|NRG|6|2|0|Обычный Красный гигант
|
||||||
|
64|SRG|6|2|0|Малый красный гигант
|
||||||
|
63|RG|6|2|0|Красный гигант
|
||||||
|
65|HN|6|2|0|Гиперновая
|
||||||
|
66|BSN|6|2|0|Яркая сверхновая
|
||||||
|
67|SN|6|2|0|Сверхновая
|
||||||
|
68|DSN|6|2|0|Тусклая сверхновая
|
||||||
|
69|HGRB|6|2|0|Гипер гамма-всплеск
|
||||||
|
70|LGRB|6|2|0|Большой гамма-всплеск
|
||||||
|
71|GRB|6|2|0|Гамма-всплеск
|
||||||
|
72|SGRB|6|2|0|Малый гамма-всплеск
|
||||||
|
73|ASTN|6|2|5|Активная звёздная колыбель
|
||||||
|
74|MC|6|2|5|Молекулярное облако
|
||||||
|
75|STN|6|2|5|Звёздная колыбель
|
||||||
|
76|G|6|2|5|Глобула
|
||||||
|
77|N|6|2|5|Туманность
|
||||||
|
78|HBD|7|2|5|Горячий бурый карлик
|
||||||
|
79|BD|7|2|0|Бурый карлик
|
||||||
|
80|LWD|7|2|0|Большой белый карлик
|
||||||
|
81|NWD|7|2|0|Обычный белый карлик
|
||||||
|
82|SWD|7|2|0|Малый белый карлик
|
||||||
|
83|TWD|7|2|0|Крошечный белый карлик
|
||||||
|
84|WD|7|2|0|Белый карлик
|
||||||
|
85|CBD|7|2|0|Холодный бурый карлик
|
||||||
|
86|MGT|7|2|0|Магнетар
|
||||||
|
87|LNS|7|2|0|Большая нейтронная звезда
|
||||||
|
88|SNS|7|2|0|Малая нейтронная звезда
|
||||||
|
89|NS|7|2|5|Нейтронная звезда
|
||||||
|
90|BH60|7|2|5|Чёрная дыра 60
|
||||||
|
91|BH40|7|2|0|Чёрная дыра 40
|
||||||
|
92|BH30|7|2|0|Чёрная дыра 30
|
||||||
|
93|BH25|7|2|0|Чёрная дыра 25
|
||||||
|
94|BH|7|2|0|Чёрная дыра
|
||||||
|
95|C1.9|8|4|5|Цивилизация 1.9
|
||||||
|
96|C1.8|8|4|0|Цивилизация 1.8
|
||||||
|
97|C1.7|8|4|0|Цивилизация 1.7
|
||||||
|
98|C1.6|8|4|0|Цивилизация 1.6
|
||||||
|
99|C1.5|8|4|0|Цивилизация 1.5
|
||||||
|
100|C1.4|8|4|0|Цивилизация 1.4
|
||||||
|
101|C1.3|8|4|0|Цивилизация 1.3
|
||||||
|
102|C1.2|8|4|0|Цивилизация 1.2
|
||||||
|
103|C1.1|8|4|0|Цивилизация 1.1
|
||||||
|
104|C1.0|8|4|0|Цивилизация 1.0
|
||||||
|
105|C1|8|4|0|Цивилизация типа 1
|
||||||
|
106|C0.9|8|4|5|Цивилизация 0.9
|
||||||
|
107|C0.8|8|4|0|Цивилизация 0.8
|
||||||
|
108|C0.7|8|4|0|Цивилизация 0.7
|
||||||
|
109|C0.6|8|4|0|Цивилизация 0.6
|
||||||
|
110|C0.5|8|4|0|Цивилизация 0.5
|
||||||
|
111|C0.4|8|4|0|Цивилизация 0.4
|
||||||
|
112|C0.3|8|4|0|Цивилизация 0.3
|
||||||
|
113|C0.2|8|4|0|Цивилизация 0.2
|
||||||
|
114|C0.1|8|4|0|Цивилизация 0.1
|
||||||
|
115|C0.0|8|4|0|Цивилизация 0.0
|
||||||
|
116|C0|8|4|0|Цивилизация типа 0
|
||||||
|
117|EON5|8|4|5|Эон 5: Кайнозой
|
||||||
|
118|EON4|8|4|0|Эон 4: Мезозой
|
||||||
|
119|EON3|8|4|0|Эон 3: Палеозой
|
||||||
|
120|EON2|8|4|0|Эон 2: Протерозой
|
||||||
|
121|EON1|8|4|0|Эон 1: Архей
|
||||||
|
122|LP|9|4|5|Живая
|
||||||
|
123|HP|9|4|0|Обитаемая
|
||||||
|
124|RP|9|4|5|Каменистая
|
||||||
|
125|AB|9|4|0|Астероиды
|
||||||
|
126|GG|9|4|0|Газовый гигант
|
||||||
|
127|Met|10|1|5|Металлы
|
||||||
|
128|He|10|1|0|Гелий
|
||||||
|
129|H|11|1|5|Водород
|
||||||
|
130|SMBH|12|2|5|СМЧД
|
||||||
|
131|S|12|1|5|Пространство
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://8bh387weuy7a
|
||||||
+4
-2
@@ -177,9 +177,11 @@ Ct не получает часы. Инициализация отсутству
|
|||||||
- `1e-5 < density < 0.9`: обычная шкала, `ln(density × 1e5) / ln(9e4)`;
|
- `1e-5 < density < 0.9`: обычная шкала, `ln(density × 1e5) / ln(9e4)`;
|
||||||
- `density ≥ 0.9`: шкала сжатия, `(density − 0.9) × 10`.
|
- `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 тиков с нулевой энергией апокалипсис получает фазу завершения, симуляция останавливает время. Фаза, тип события, предупреждение и позиция в очереди сохраняются в состоянии вселенной.
|
||||||
|
|
||||||
### Горючесть, взрывчатость и агрессия
|
### Горючесть, взрывчатость и агрессия
|
||||||
|
|
||||||
|
|||||||
+43
-1
@@ -100,6 +100,12 @@ var active_actions_dirty: bool = true
|
|||||||
var active_actions_elapsed: float = 0.0
|
var active_actions_elapsed: float = 0.0
|
||||||
var clock_display_elapsed: float = 0.0
|
var clock_display_elapsed: float = 0.0
|
||||||
var interface_sounds_enabled: bool = false
|
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:Owniverse = Owniverse.new()
|
||||||
var owniverse_items: Dictionary[String, OwniverseItem] = {}
|
var owniverse_items: Dictionary[String, OwniverseItem] = {}
|
||||||
@@ -274,6 +280,7 @@ func _swipe_to_nearest_page() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
if E.eq(owniverse.state.get("apocalypse", {}).get("phase", 0), 1): return
|
||||||
if event is InputEventScreenDrag:
|
if event is InputEventScreenDrag:
|
||||||
#effects_player.play_effect("swipe")
|
#effects_player.play_effect("swipe")
|
||||||
stage_page_scroller.scroll_horizontal = clamp(
|
stage_page_scroller.scroll_horizontal = clamp(
|
||||||
@@ -299,7 +306,7 @@ func _set_active_page_actions() -> void:
|
|||||||
#var amount = owniverse.get_available_to_produce(item_id)
|
#var amount = owniverse.get_available_to_produce(item_id)
|
||||||
|
|
||||||
#var amount = owniverse.get_available_to_produce(entity)
|
#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)
|
#print(item_id, " : ", amount)
|
||||||
if item_id == "SMBH":
|
if item_id == "SMBH":
|
||||||
var preview: Callable = func(requested):
|
var preview: Callable = func(requested):
|
||||||
@@ -398,6 +405,7 @@ func _refresh_universe_ui() -> void:
|
|||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
|
|
||||||
owniverse.evolute_frame(delta, get_tree())
|
owniverse.evolute_frame(delta, get_tree())
|
||||||
|
_process_apocalypse(delta)
|
||||||
clock_display_elapsed += delta
|
clock_display_elapsed += delta
|
||||||
if clock_display_elapsed >= 0.1:
|
if clock_display_elapsed >= 0.1:
|
||||||
clock_display_elapsed = fmod(clock_display_elapsed, 0.1)
|
clock_display_elapsed = fmod(clock_display_elapsed, 0.1)
|
||||||
@@ -431,6 +439,7 @@ func _process(delta: float) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _change_speed(delta: int) -> 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
|
var previous_speed: int = owniverse.speed
|
||||||
owniverse.speed = owniverse.speed + delta
|
owniverse.speed = owniverse.speed + delta
|
||||||
|
|
||||||
@@ -458,6 +467,39 @@ func _change_speed(delta: int) -> void:
|
|||||||
g_time_plus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % str(speed + 1))
|
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:
|
func _on_topmenu_button_pressed(screen_name: String) -> void:
|
||||||
var current_screen: String = ""
|
var current_screen: String = ""
|
||||||
for screen_id in screens:
|
for screen_id in screens:
|
||||||
|
|||||||
@@ -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:
|
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 basic: Dictionary = {}
|
||||||
var automatic: Dictionary = {}
|
var automatic: Dictionary = {}
|
||||||
for id in quantities:
|
for id in quantities:
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefC
|
|||||||
var profile_trends: int = world.profile_start()
|
var profile_trends: int = world.profile_start()
|
||||||
WorldParameters.advance(previous, next, Global.CivilizationRules, E.subtract(to_year, from_year))
|
WorldParameters.advance(previous, next, Global.CivilizationRules, E.subtract(to_year, from_year))
|
||||||
world.profile_end("trends", profile_trends)
|
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}
|
next.time = clock if not clock.is_empty() else {"datetime": datetime, "year": to_year}
|
||||||
|
|
||||||
# Publish only after every phase has finished; previous stays untouched.
|
# Publish only after every phase has finished; previous stays untouched.
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ func plan_smbh(desired_amount: Variant) -> Dictionary:
|
|||||||
|
|
||||||
|
|
||||||
func produce_smbh(desired_amount: Variant) -> Variant:
|
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 plan: Dictionary = plan_smbh(desired_amount)
|
||||||
var consumed_mass: Variant = plan.amount
|
var consumed_mass: Variant = plan.amount
|
||||||
var taken: Dictionary = plan.taken
|
var taken: Dictionary = plan.taken
|
||||||
@@ -137,10 +138,12 @@ func produce_smbh(desired_amount: Variant) -> Variant:
|
|||||||
world.ledger.clamp_auto_resources(next)
|
world.ledger.clamp_auto_resources(next)
|
||||||
world.state = next
|
world.state = next
|
||||||
world.presentation.refresh(world.state, world.catalog)
|
world.presentation.refresh(world.state, world.catalog)
|
||||||
|
world.apocalypse.check_density(world.state)
|
||||||
return consumed_mass
|
return consumed_mass
|
||||||
|
|
||||||
|
|
||||||
func produce(item_id: String, desired_amount: Variant) -> Variant:
|
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"):
|
if E.eq(item_id, "SMBH"):
|
||||||
return produce_smbh(desired_amount)
|
return produce_smbh(desired_amount)
|
||||||
# Use special production for S and H
|
# Use special production for S and H
|
||||||
@@ -171,10 +174,12 @@ func produce(item_id: String, desired_amount: Variant) -> Variant:
|
|||||||
world.stellar.create_planets(world.state, item_id, max_producible, world.state.time.datetime, world.state.time.datetime, metallicity)
|
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.ledger.refresh_groups(world.state)
|
||||||
world.presentation.refresh(world.state, world.catalog)
|
world.presentation.refresh(world.state, world.catalog)
|
||||||
|
world.apocalypse.check_density(world.state)
|
||||||
return max_producible
|
return max_producible
|
||||||
|
|
||||||
|
|
||||||
func produce_hs(entity_id: String, value: Variant) -> Variant:
|
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):
|
if E.le(value, 0):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -186,7 +191,8 @@ func produce_hs(entity_id: String, value: Variant) -> Variant:
|
|||||||
|
|
||||||
world.state.energy = E.subtract(world.state.energy, to_produce)
|
world.state.energy = E.subtract(world.state.energy, to_produce)
|
||||||
world.ledger.process_entity(entity_id, 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
|
return to_produce
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ var ledger = preload("res://world/owniverse/object_ledger.gd").new(self)
|
|||||||
var production = preload("res://world/owniverse/manual_production.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 automatic = preload("res://world/owniverse/automatic_production.gd").new(self)
|
||||||
var stellar = preload("res://world/owniverse/stellar_rules.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 life = preload("res://world/owniverse/life_evolution.gd").new(self)
|
||||||
var environment = preload("res://world/owniverse/universe_environment.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 evolution = preload("res://world/owniverse/evolution_coordinator.gd").new(self)
|
||||||
@@ -109,6 +110,7 @@ var state: Dictionary = {
|
|||||||
'civilization_stats': {},
|
'civilization_stats': {},
|
||||||
'civilization_homes': {},
|
'civilization_homes': {},
|
||||||
'ethics': {},
|
'ethics': {},
|
||||||
|
'apocalypse': {'phase': 0, 'type': 0, 'warning': 0, 'stage': 1, 'tick': 0, 'end_ticks': 0},
|
||||||
'stats': {'total_amount': {}},
|
'stats': {'total_amount': {}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +134,10 @@ func display_clock() -> Dictionary:
|
|||||||
## Keep the requested clock advancing while a transaction spans render frames.
|
## Keep the requested clock advancing while a transaction spans render frames.
|
||||||
func evolute_frame(delta: float, tree: SceneTree) -> void:
|
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 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()
|
if frame_clock.is_empty(): frame_clock = state.time.duplicate()
|
||||||
frame_clock = SimulationTime.advance(frame_clock, delta, speed)
|
frame_clock = SimulationTime.advance(frame_clock, delta, speed)
|
||||||
frame_elapsed_seconds += delta
|
frame_elapsed_seconds += delta
|
||||||
@@ -152,6 +158,7 @@ func evolute_frame(delta: float, tree: SceneTree) -> void:
|
|||||||
evolution.run_frame(frame_active_clock, frame_budget)
|
evolution.run_frame(frame_active_clock, frame_budget)
|
||||||
|
|
||||||
func request_production(id: String, amount: Variant, resource: bool = false) -> bool:
|
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:
|
if frame_busy or production_queue_busy:
|
||||||
_queued_productions.append({"id": id, "amount": amount, "resource": resource})
|
_queued_productions.append({"id": id, "amount": amount, "resource": resource})
|
||||||
return true
|
return true
|
||||||
@@ -218,10 +225,12 @@ var speed: int :
|
|||||||
get:
|
get:
|
||||||
return _requested_speed if _requested_speed != 0 else state.speed
|
return _requested_speed if _requested_speed != 0 else state.speed
|
||||||
set(value):
|
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:
|
if frame_busy:
|
||||||
_requested_speed = clampi(value, 1, Global.SPEED_LIMIT)
|
_requested_speed = requested_speed
|
||||||
return
|
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))
|
state.speed_in_year = E.power(10, E.subtract(state.speed, 1))
|
||||||
|
|
||||||
func evolute(delta: Variant) -> void:
|
func evolute(delta: Variant) -> void:
|
||||||
|
|||||||
Reference in New Issue
Block a user