using System.Collections.Generic;
using System.Linq;
using Cthangover.Core.Mods;
using Cthangover.Core.Utils;
using Godot;
namespace Mods.TestRunner
{
///
/// Точка входа тестового мода: переносит интеграционную обвязку диалогов
/// (tests DialogAutoDriver + SceneSwitcher) в engine-мод. Активируется
/// только при наличии пользовательских флагов --choices= или
/// --timeout= (как OS.GetCmdlineUserArgs(); флаг --scene=
/// мод не читает — навигацией по сценам управляет ядро). При активации
/// создаёт и добавляет его в корень
/// дерева через deferred-вызов: OnModResourcesReady срабатывает до
/// окончания построения сцены, прямой AddChild в этот момент отклоняется
/// движком, а драйверу всё равно нужно дождаться диалога (та же задержка
/// 0.5с, что у SceneSwitcher.ScheduleDriverAttach). Повторный
/// ModManager.Reload() не должен плодить драйверы — защищено
/// статическим флагом.
///
public class TestRunnerInitializer : IModInitializer
{
private static bool driverQueued;
/// Инициализация per-mod не требуется — драйвер ставится на resources-ready.
public void OnModLoaded(string modId) { }
///
/// Разбирает пользовательские аргументы командной строки и при
/// активационном флаге ставит драйвер автопроигрывания в корень дерева.
///
public void OnModResourcesReady()
{
if (driverQueued)
return;
var args = OS.GetCmdlineUserArgs();
var hasChoices = args.Any(a => a.StartsWith("--choices=", System.StringComparison.Ordinal));
var hasTimeout = args.Any(a => a.StartsWith("--timeout=", System.StringComparison.Ordinal));
if (!hasChoices && !hasTimeout)
return;
var choices = ParseChoices(args);
var timeout = ParseTimeout(args);
var dialogs = ParseDialogs(args);
// Godot. квалифицирован: в dev-компиляции модов (ToolsModCompileTests)
// в глобальном пространстве имён существует namespace Engine из тестовой сборки,
// и неквалифицированное имя резолвится в него вместо Godot.Engine.
var root = (Godot.Engine.GetMainLoop() as SceneTree)?.Root;
if (root == null)
{
GameLogger.Log("TEST", "TestRunnerInitializer: tree root not available, driver not attached", LogLevel.Error);
return;
}
driverQueued = true;
GameLogger.Log("TEST",
$"TestRunnerInitializer: activating dialog auto-driver, choices=[{string.Join(",", choices)}], timeout={timeout?.ToString() ?? "none"}s, dialogs={dialogs?.ToString() ?? "idle"}");
// Deferred: сцена ещё строится; драйвер внутри сам выдерживает
// стартовую паузу и лениво ищет DialogBox, поэтому порядок с
// загрузкой сцены не важен.
ModRoot.AddDeferred(new DialogAutoDriverNode(choices, timeout, dialogs));
}
private static List ParseChoices(string[] args)
{
var raw = args
.Where(a => a.StartsWith("--choices=", System.StringComparison.Ordinal))
.Select(a => a.Substring("--choices=".Length))
.FirstOrDefault();
if (string.IsNullOrEmpty(raw))
return new List();
return raw.Split(',')
.Select(s => int.TryParse(s.Trim(), out var n) ? n : -1)
.Where(n => n >= 0)
.ToList();
}
private static int? ParseDialogs(string[] args)
{
var raw = args
.Where(a => a.StartsWith("--dialogs=", System.StringComparison.Ordinal))
.Select(a => a.Substring("--dialogs=".Length))
.FirstOrDefault();
return int.TryParse(raw, out var n) && n > 0 ? n : null;
}
private static float? ParseTimeout(string[] args)
{
var raw = args
.Where(a => a.StartsWith("--timeout=", System.StringComparison.Ordinal))
.Select(a => a.Substring("--timeout=".Length))
.FirstOrDefault();
return float.TryParse(raw, System.Globalization.CultureInfo.InvariantCulture, out var t) && t > 0
? t
: null;
}
}
}