Add report generation functionality to the UI

- Introduced a new script `report.gd` that extends `VBoxContainer` to manage the display of reports.
- Implemented dynamic localization support for report headers and body content based on the global locale.
- Created a `refresh` method to update the report content and header image when visibility changes or locale updates.
- Added a static `build` method to format and compile report data from the world state, including calculations for star totals and effects.
- Integrated presentation logic to render report templates based on the current state of the game world.
This commit is contained in:
Kirill
2026-10-18 21:48:15 +03:00
parent 2aced0c61a
commit a7c05e3796
11 changed files with 139 additions and 13 deletions
+6 -6
View File
@@ -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
View File
@@ -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
+14
View File
@@ -278,3 +278,17 @@ H списывает только оставшуюся после замещен
открытие и получение, отсутствие повторных уведомлений, паузу в меню,
возврат индикаторов, сохранение прогресса и локализацию. Файл прогресса теста
создаётся отдельно в `/tmp`.
## Отчёт о вселенной
`ui/report/report.gd` форматирует готовое состояние при открытии
экрана отчёта и смене языка. Результат хранится в `Report/ReportBody`.
Периодического пересчёта отчёта и новых счётчиков симуляции нет.
Доли восьми типов звёзд учитывают звёзды в структурах. Строки количеств
используют накопленные `stats.total_amount` с существующими алиасами;
заголовки обозначают суммарное получение, а не исторические максимумы.
Эффекты интеграции показывают прирост готовых параметров Federation,
Enlightenment и Order относительно начального значения 1.
Нулевые строки и пустые разделы скрыты. Количества выводятся полностью,
с пробелами между группами по три цифры. Тексты и заголовок поддерживают RU/EN.
+36
View File
@@ -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()
+1
View File
@@ -0,0 +1 @@
uid://brb04x12elvqt
BIN
View File
Binary file not shown.
+63
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
uid://v8h4ov33opyf
BIN
View File
Binary file not shown.
+13 -2
View File
@@ -71,6 +71,8 @@
[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"]
@@ -1012,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
@@ -1033,11 +1036,19 @@ 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