108 lines
5.7 KiB
GDScript
108 lines
5.7 KiB
GDScript
|
|
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 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")
|
||
|
|
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")
|
||
|
|
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)
|