241 lines
7.1 KiB
C#
241 lines
7.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
public sealed class WorldLogEventEntry
|
|
{
|
|
public string Id = "";
|
|
public float EventStrength = 40f;
|
|
public string Category = "WorldLog";
|
|
public bool CreatesInterest = true;
|
|
public bool ChronicleEligible;
|
|
public float MinWatch = 6f;
|
|
public float MaxWatch = 28f;
|
|
public string LabelTemplate = "{id}";
|
|
public bool IsFallback;
|
|
|
|
public string MakeLabel(string special1, string special2)
|
|
{
|
|
string a = (special1 ?? "").Trim();
|
|
string b = (special2 ?? "").Trim();
|
|
string template = string.IsNullOrEmpty(LabelTemplate) ? "{id}" : LabelTemplate;
|
|
string displayId = EventReason.HumanizeId(Id);
|
|
if (string.IsNullOrEmpty(displayId))
|
|
{
|
|
displayId = (Id ?? "").Trim();
|
|
}
|
|
|
|
string label = template
|
|
.Replace("{id}", displayId)
|
|
.Replace("{a}", a)
|
|
.Replace("{b}", b)
|
|
.Replace("{special1}", a)
|
|
.Replace("{special2}", b);
|
|
return CollapseSparseLabel(label, displayId);
|
|
}
|
|
|
|
public string MakeLabel(WorldLogMessage message)
|
|
{
|
|
if (message == null)
|
|
{
|
|
return MakeLabel("", "");
|
|
}
|
|
|
|
string a = message.special1 ?? "";
|
|
string b = message.special2 ?? "";
|
|
// Many disaster WorldLogs leave special1 empty - prefer the follow unit name,
|
|
// else CollapseSparseLabel drops hanging "Tornado: " tails.
|
|
if (string.IsNullOrWhiteSpace(a) && message.unit != null)
|
|
{
|
|
a = EventFeedUtil.SafeName(message.unit) ?? "";
|
|
}
|
|
|
|
return MakeLabel(a, b);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Drop empty-slot leftovers ("Tornado: ", "Event: ") so tips never show a bare colon.
|
|
/// </summary>
|
|
public static string CollapseSparseLabel(string label, string displayIdFallback)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(label))
|
|
{
|
|
return string.IsNullOrEmpty(displayIdFallback) ? "Event" : displayIdFallback;
|
|
}
|
|
|
|
string s = label.Trim();
|
|
while (s.IndexOf(" ", StringComparison.Ordinal) >= 0)
|
|
{
|
|
s = s.Replace(" ", " ");
|
|
}
|
|
|
|
// Trailing separator after an empty {a}/{b}.
|
|
while (s.Length > 0)
|
|
{
|
|
char last = s[s.Length - 1];
|
|
if (last == ':' || last == '-' || last == '·' || last == '|')
|
|
{
|
|
s = s.Substring(0, s.Length - 1).TrimEnd();
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Leading separator when {a} was first and empty.
|
|
while (s.Length > 0)
|
|
{
|
|
char first = s[0];
|
|
if (first == ':' || first == '-' || first == '·' || first == '|')
|
|
{
|
|
s = s.Substring(1).TrimStart();
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(s))
|
|
{
|
|
return string.IsNullOrEmpty(displayIdFallback) ? "Event" : displayIdFallback;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/// <summary>True when a rendered tip would look empty or end with a hanging colon.</summary>
|
|
public static bool IsHangingLabel(string label)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(label))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
string s = label.TrimEnd();
|
|
return s.EndsWith(":", StringComparison.Ordinal)
|
|
|| s.EndsWith(": ", StringComparison.Ordinal);
|
|
}
|
|
}
|
|
|
|
/// <summary>WorldLog dials from <c>event-catalog.json</c> <c>worldLog</c>.</summary>
|
|
public static partial class EventCatalog
|
|
{
|
|
public static class WorldLog
|
|
{
|
|
private static readonly Dictionary<string, WorldLogEventEntry> Entries =
|
|
new Dictionary<string, WorldLogEventEntry>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private static int _appliedGeneration = -1;
|
|
|
|
public static void InvalidateOverlays()
|
|
{
|
|
Entries.Clear();
|
|
_appliedGeneration = -1;
|
|
}
|
|
|
|
private static void Ensure()
|
|
{
|
|
if (_appliedGeneration == EventCatalogConfig.Generation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_appliedGeneration = EventCatalogConfig.Generation;
|
|
EventCatalogConfig.FillWorldLog(Entries);
|
|
}
|
|
|
|
public static IEnumerable<string> AuthoredIds
|
|
{
|
|
get
|
|
{
|
|
Ensure();
|
|
return Entries.Keys;
|
|
}
|
|
}
|
|
|
|
public static bool HasAuthored(string assetId)
|
|
{
|
|
Ensure();
|
|
return !string.IsNullOrEmpty(assetId) && Entries.ContainsKey(assetId.Trim());
|
|
}
|
|
|
|
public static bool TryGet(string assetId, out WorldLogEventEntry entry)
|
|
{
|
|
Ensure();
|
|
entry = null;
|
|
if (string.IsNullOrEmpty(assetId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Entries.TryGetValue(assetId.Trim(), out entry);
|
|
}
|
|
|
|
public static WorldLogEventEntry GetOrFallback(string assetId)
|
|
{
|
|
if (TryGet(assetId, out WorldLogEventEntry entry))
|
|
{
|
|
return entry;
|
|
}
|
|
|
|
string id = string.IsNullOrEmpty(assetId) ? "unknown" : assetId.Trim();
|
|
float strength = 40f;
|
|
string category = "WorldLog";
|
|
// Live disaster library / authored disaster overlays - not tip-string matching.
|
|
if (Disaster.IsLiveOrAuthored(id))
|
|
{
|
|
DisasterInterestEntry d = Disaster.GetOrFallback(id);
|
|
strength = d.EventStrength > 0f ? d.EventStrength : 95f;
|
|
category = "Disaster";
|
|
}
|
|
|
|
return new WorldLogEventEntry
|
|
{
|
|
Id = id,
|
|
EventStrength = strength,
|
|
Category = category,
|
|
CreatesInterest = true,
|
|
ChronicleEligible = strength >= 65f,
|
|
LabelTemplate = "{id}: {a}",
|
|
MinWatch = strength >= 90f ? 8f : 6f,
|
|
MaxWatch = strength >= 90f ? 35f : 28f,
|
|
IsFallback = true
|
|
};
|
|
}
|
|
|
|
/// <summary>WorldLog / disaster tips classified as Disaster via catalog or live library.</summary>
|
|
public static bool IsDisasterCategory(string assetId)
|
|
{
|
|
if (string.IsNullOrEmpty(assetId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (TryGet(assetId, out WorldLogEventEntry entry)
|
|
&& string.Equals(entry.Category, "Disaster", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return Disaster.IsLiveOrAuthored(assetId);
|
|
}
|
|
|
|
public static bool TryGetEventStrength(string assetId, out float eventStrength)
|
|
{
|
|
WorldLogEventEntry entry = GetOrFallback(assetId);
|
|
eventStrength = entry.EventStrength;
|
|
return !string.IsNullOrEmpty(assetId);
|
|
}
|
|
|
|
public static string MakeLabel(WorldLogMessage message)
|
|
{
|
|
if (message == null)
|
|
{
|
|
return "event";
|
|
}
|
|
|
|
return GetOrFallback(message.asset_id).MakeLabel(message);
|
|
}
|
|
}
|
|
}
|