This commit is contained in:
iisaev committed 2026-10-18 12:41:36 +03:00
commit 5e0a12b038
23 files changed
+1650

No files matched your search

@@ -0,0 +1,28 @@
{
"Items": [{
"id": "kitchen_lamp",
"texture": "lamp",
"layer": "foreground",
"zIndex": 10,
"hitArea": {
"type": "rect",
"x": 0.68,
"y": 0.40,
"width": 0.08,
"height": 0.08
},
"highlight": {
"color": "#FFFF0033",
"scale": 1.1,
"duration": 0.12
},
"cursor": "PointingHand",
"actions": {
"onClick": {
"scenario": "scenarios/kitchen_clicked.scenario"
},
"onHoverEnter": "set cursor_hint Кликни_лампу",
"onHoverLeave": "set cursor_hint \"\""
}
}]
}
@@ -0,0 +1,2 @@
mod_interactive_base__interactives_name = Test Interactives
mod_interactive_base__interactives_desc = Integration test of the interactive object system — a clickable lamp on the scene with highlight.
@@ -0,0 +1,2 @@
mod_interactive_base__interactives_name = Тест интерактивных объектов
mod_interactive_base__interactives_desc = Интеграционный тест системы интерактивных объектов — кликабельная лампа на сцене с подсветкой.
@@ -0,0 +1 @@
test_interactive/click = You clicked the lamp! The interactive object works.
@@ -0,0 +1 @@
test_interactive/click = Вы кликнули по лампе! Интерактивный объект работает.
+6
View File
@@ -0,0 +1,6 @@
{
"name": "Interactives test",
"description": "Integration test of the interactive object system: the scenario command interactive_add places a clickable lamp on interactives_test; the driver checks the definition, hover DSL and that a click runs the onClick scenario. Mounted by --itest=interactives.",
"sources": ["src/*.cs"],
"depends": ["visual_novel_core>=1", "dialog_base>=1"]
}
@@ -0,0 +1,6 @@
scene: interactives_test
priority: 10
is_one_run: true
---
interactive_add kitchen_lamp
end
@@ -0,0 +1,3 @@
set lamp_clicked yes
text "Вы кликнули по лампе! Интерактивный объект работает." key=test_interactive/click
end
@@ -0,0 +1,4 @@
{
"name": "interactives_test",
"defaultBackground": "home/kitchen"
}
@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Cthangover.Core.Bootstrap;
using Cthangover.Core.Scenes;
using Cthangover.Core.Interactive;
using Cthangover.Core.Mods;
using Cthangover.Core.Mods.Resolvers;
using Cthangover.Core.Utils;
using Cthangover.Mods.Dialog;
using Godot;
namespace Mods.InteractiveBase.Tests
{
/// <summary>
/// Entry point of the interactives itest: the suite is mounted (and this
/// initializer compiled) only with <c>--itest=interactives</c>; run it on
/// <c>--scene=interactives_test</c>, where the suite scenario adds the lamp.
/// </summary>
public class InteractivesTestInitializer : IModInitializer
{
private static bool queued;
public void OnModLoaded(string modId) { }
public void OnModResourcesReady()
{
if (queued || !OS.GetCmdlineUserArgs().Contains("--itest=interactives"))
return;
queued = true;
GameLogger.Log("TEST", "InteractivesTest: activating");
(Godot.Engine.GetMainLoop() as SceneTree)?.Root?.CallDeferred("add_child", new InteractivesTestDriverNode());
}
}
/// <summary>
/// Drives the lamp like a player: waits until the scenario command
/// <c>interactive_add kitchen_lamp</c> placed it, hovers it, clicks it
/// through the object's own input handler and expects the <c>onClick</c>
/// scenario to run in the shared DialogBox (its <c>set lamp_clicked=yes</c>
/// lands in the box runtime). Prints <c>[TEST_RESULT] name=interactives</c>.
/// </summary>
public partial class InteractivesTestDriverNode : Node
{
private const string ResultName = "interactives";
private const string LampId = "kitchen_lamp";
private const double TimeoutSeconds = 60;
private int passed;
private int failed;
private double elapsed;
private double stepStarted;
private int step;
private InteractiveObject lamp;
public override void _Process(double delta)
{
if (step < 0)
return;
elapsed += delta;
if (elapsed > TimeoutSeconds)
{
Check($"finished in {TimeoutSeconds:0}s (stuck at step {step})", false);
Finish();
return;
}
try
{
Step();
}
catch (Exception ex)
{
Check($"step {step}: no exception ({ex.GetType().Name}: {ex.Message})", false);
Finish();
}
}
private void Step()
{
switch (step)
{
case 0:
if (!Boot.IsReady || (GetTree().CurrentScene == null))
return;
Check("definition kitchen_lamp is collected", Interactives.Collect().ContainsKey(LampId));
Next();
return;
case 1:
lamp = InteractiveManager.Instance?.Get(LampId);
if (lamp == null)
{
if (Waited(15))
{
Check("interactive_add placed kitchen_lamp", false);
Finish();
}
return;
}
Check("interactive_add placed kitchen_lamp", true);
Check("lamp is enabled and inside the tree", lamp.IsEnabled && lamp.IsInsideTree());
lamp.EmitSignal(Control.SignalName.MouseEntered);
Next();
return;
case 2:
// Hover DSL runs in a throw-away runtime: the observable part is that it does not break the object.
lamp.EmitSignal(Control.SignalName.MouseExited);
Check("hover enter/exit handled, lamp still enabled", lamp.IsEnabled && lamp.IsInsideTree());
lamp._GuiInput(new InputEventMouseButton { ButtonIndex = MouseButton.Left, Pressed = true, Position = lamp.Size / 2 });
Next();
return;
case 3:
var box = SceneContextNode.FindNode<DialogBox>("DialogBox");
var clicked = box?.Runtime.GetVariable("lamp_clicked");
if (clicked != "yes" && !Waited(5))
return;
Check("click started the onClick dialog (waits on its text)", box is { IsDialogEnded: false });
Check("onClick scenario ran (lamp_clicked=yes)" + (clicked == "yes" ? string.Empty : $" (got '{clicked}')"), clicked == "yes");
Finish();
return;
}
}
private bool Waited(double seconds) => elapsed - stepStarted > seconds;
private void Next()
{
step++;
stepStarted = elapsed;
}
private void Check(string what, bool ok)
{
if (ok)
passed++;
else
failed++;
GameLogger.Log("TEST", $" [{(ok ? "PASS" : "FAIL")}] {what}");
}
private void Finish()
{
step = -1;
GameLogger.Log("TEST", $"[TEST_RESULT] name={ResultName} passed={passed} failed={failed}");
GetTree().Quit(failed == 0 ? 0 : 1);
}
}
}