- Open war/disaster/outbreak crisis overlays with epilogue_crisis closers - Keep Duel pairs hot only on owned cast; park sleep/freeze; drop ambient scrap pins - Require a fallen theater partner for survivor aftermath; let king_killed cut mid-fight - Gate crisis, combat cold, king cut-in, and living-partner aftermath in harness
220 lines
7.1 KiB
C#
220 lines
7.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
public sealed class WorldLogEventAuditResult
|
|
{
|
|
public bool Passed;
|
|
public string Detail = "";
|
|
public int Total;
|
|
public int MissingAuthored;
|
|
public int OrphanAuthored;
|
|
public int EmptyLabel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exhaustive live world_log_library audit for authored WorldLog event catalog coverage.
|
|
/// </summary>
|
|
public static class WorldLogEventHarness
|
|
{
|
|
public static WorldLogEventAuditResult RunAudit(string outputDirectory)
|
|
{
|
|
List<string> liveIds = ActivityAssetCatalog.EnumerateLiveWorldLogIds();
|
|
var authored = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (string id in EventCatalog.WorldLog.AuthoredIds)
|
|
{
|
|
authored.Add(id);
|
|
}
|
|
|
|
int missingAuthored = 0;
|
|
int orphanAuthored = 0;
|
|
int emptyLabel = 0;
|
|
var tsv = new StringBuilder();
|
|
tsv.AppendLine(
|
|
"id\tgroup\tlocale\tpath_icon\tdisaster_link\tauthored\tevent_strength\tcategory\tcreates_interest\tlabel_sample\tnotes");
|
|
|
|
var liveSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
for (int i = 0; i < liveIds.Count; i++)
|
|
{
|
|
string id = liveIds[i];
|
|
liveSet.Add(id);
|
|
WorldLogAsset asset = ActivityAssetCatalog.TryGetWorldLogAsset(id);
|
|
bool hasAuthored = EventCatalog.WorldLog.HasAuthored(id);
|
|
WorldLogEventEntry entry = EventCatalog.WorldLog.GetOrFallback(id);
|
|
if (!hasAuthored || entry.IsFallback)
|
|
{
|
|
missingAuthored++;
|
|
}
|
|
|
|
string group = ReadStringField(asset, "group_id")
|
|
?? ReadStringField(asset, "history_group")
|
|
?? ReadStringField(asset, "group")
|
|
?? "";
|
|
string locale = "";
|
|
try
|
|
{
|
|
locale = asset != null ? (asset.getLocaleID() ?? "") : "";
|
|
}
|
|
catch
|
|
{
|
|
locale = ReadStringField(asset, "path_locale") ?? "";
|
|
}
|
|
|
|
string pathIcon = ReadStringField(asset, "path_icon") ?? "";
|
|
string disasterLink = ReadStringField(asset, "disaster_id")
|
|
?? ReadStringField(asset, "linked_disaster")
|
|
?? "";
|
|
if (string.IsNullOrEmpty(disasterLink) && id.IndexOf("disaster", StringComparison.OrdinalIgnoreCase) >= 0)
|
|
{
|
|
disasterLink = "id_hint";
|
|
}
|
|
|
|
string labelSample = entry.MakeLabel("Alpha", "Beta");
|
|
string emptySlotSample = entry.MakeLabel("", "");
|
|
bool hanging = WorldLogEventEntry.IsHangingLabel(labelSample)
|
|
|| WorldLogEventEntry.IsHangingLabel(emptySlotSample)
|
|
|| string.IsNullOrWhiteSpace(emptySlotSample);
|
|
if (hanging)
|
|
{
|
|
emptyLabel++;
|
|
}
|
|
|
|
string notes = "ok";
|
|
if (!hasAuthored || entry.IsFallback)
|
|
{
|
|
notes = "missing_authored";
|
|
}
|
|
else if (hanging)
|
|
{
|
|
notes = "empty_label";
|
|
}
|
|
|
|
tsv.Append(id).Append('\t')
|
|
.Append(Escape(group)).Append('\t')
|
|
.Append(Escape(locale)).Append('\t')
|
|
.Append(Escape(pathIcon)).Append('\t')
|
|
.Append(Escape(disasterLink)).Append('\t')
|
|
.Append(hasAuthored ? "1" : "0").Append('\t')
|
|
.Append(entry.EventStrength.ToString("0.#")).Append('\t')
|
|
.Append(Escape(entry.Category)).Append('\t')
|
|
.Append(entry.CreatesInterest ? "1" : "0").Append('\t')
|
|
.Append(Escape(labelSample)).Append('\t')
|
|
.Append(notes).AppendLine();
|
|
}
|
|
|
|
foreach (string id in authored)
|
|
{
|
|
if (!liveSet.Contains(id))
|
|
{
|
|
orphanAuthored++;
|
|
tsv.Append(id).Append("\t\t\t\t\t1\t0\t\t0\t\torphan_authored").AppendLine();
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(outputDirectory))
|
|
{
|
|
Directory.CreateDirectory(outputDirectory);
|
|
File.WriteAllText(
|
|
Path.Combine(outputDirectory, "world-log-event-audit.tsv"),
|
|
tsv.ToString());
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Disk may be unavailable in some harness contexts.
|
|
}
|
|
|
|
bool passed = missingAuthored == 0 && orphanAuthored == 0 && emptyLabel == 0;
|
|
return new WorldLogEventAuditResult
|
|
{
|
|
Passed = passed,
|
|
Total = liveIds.Count,
|
|
MissingAuthored = missingAuthored,
|
|
OrphanAuthored = orphanAuthored,
|
|
EmptyLabel = emptyLabel,
|
|
Detail =
|
|
$"total={liveIds.Count} missingAuthored={missingAuthored} orphanAuthored={orphanAuthored} "
|
|
+ $"emptyLabel={emptyLabel}"
|
|
};
|
|
}
|
|
|
|
public static string DumpDisasterWarInventory(string outputDirectory)
|
|
{
|
|
var tsv = new StringBuilder();
|
|
tsv.AppendLine("kind\tid\tnotes");
|
|
List<string> disasters = ActivityAssetCatalog.EnumerateLiveDisasterIds();
|
|
for (int i = 0; i < disasters.Count; i++)
|
|
{
|
|
tsv.Append("disaster\t").Append(disasters[i]).Append("\tok").AppendLine();
|
|
}
|
|
|
|
List<string> warTypes = ActivityAssetCatalog.EnumerateLiveWarTypeIds();
|
|
for (int i = 0; i < warTypes.Count; i++)
|
|
{
|
|
tsv.Append("war_type\t").Append(warTypes[i]).Append("\tok").AppendLine();
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(outputDirectory))
|
|
{
|
|
Directory.CreateDirectory(outputDirectory);
|
|
File.WriteAllText(
|
|
Path.Combine(outputDirectory, "disaster-war-inventory.tsv"),
|
|
tsv.ToString());
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return $"disasters={disasters.Count} war_types={warTypes.Count}";
|
|
}
|
|
|
|
private static string ReadStringField(object asset, string fieldName)
|
|
{
|
|
if (asset == null || string.IsNullOrEmpty(fieldName))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
FieldInfo field = asset.GetType().GetField(fieldName);
|
|
if (field != null)
|
|
{
|
|
return field.GetValue(asset) as string;
|
|
}
|
|
|
|
PropertyInfo prop = asset.GetType().GetProperty(fieldName);
|
|
if (prop != null)
|
|
{
|
|
return prop.GetValue(asset, null) as string;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string Escape(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
{
|
|
return "";
|
|
}
|
|
|
|
return value.Replace('\t', ' ').Replace('\n', ' ').Replace('\r', ' ');
|
|
}
|
|
}
|