using System; using System.Globalization; using System.Text; namespace IdleSpectator; /// /// Sole factory for interest Labels shown as the orange dossier reason. /// Part of the Events/ module with catalogs - feeds call these helpers (or Apply) /// instead of concatenating crumbs. Director never authors reasons. /// public static class EventReason { public static string Fight(Actor a, Actor b) { string name = Name(a); if (string.IsNullOrEmpty(name)) { name = "Someone"; } string foe = Name(b); if (string.IsNullOrEmpty(foe)) { return name + " is fighting"; } return name + " is fighting " + foe; } public static string Battle(Actor focus, int fighters) { string name = Name(focus); if (string.IsNullOrEmpty(name)) { name = "Someone"; } int n = Math.Max(1, fighters); if (n <= 2) { return name + " is in a duel"; } return name + " is in a fight of " + n.ToString(CultureInfo.InvariantCulture); } public static string SeekingLover(Actor a) { return NameOrSomeone(a) + " is seeking a lover"; } public static string Hatch(Actor a) { return NameOrSomeone(a) + " hatches from an egg"; } public static string Lovers(Actor a, Actor b) { string an = NameOrSomeone(a); string bn = Name(b); if (string.IsNullOrEmpty(bn)) { return an + " found a lover"; } return an + " became lovers with " + bn; } public static string LoverParted(Actor a) { return NameOrSomeone(a) + " parted from a lover"; } public static string FamilyPack(Actor a, Actor peer, string phase) { string an = NameOrSomeone(a); string bn = Name(peer); string p = (phase ?? "").Trim().ToLowerInvariant(); switch (p) { case "join": case "family_group_join": return string.IsNullOrEmpty(bn) ? an + " joins a family pack" : an + " joins a family pack with " + bn; case "leave": case "family_group_leave": return string.IsNullOrEmpty(bn) ? an + " leaves a family pack" : an + " leaves a family pack with " + bn; case "form": case "new": case "family_group_new": return string.IsNullOrEmpty(bn) ? an + " forms a family pack" : an + " forms a family pack with " + bn; default: return an + " gathers with kin"; } } public static string NewChild(Actor a, Actor b) { string an = NameOrSomeone(a); string bn = Name(b); if (string.IsNullOrEmpty(bn)) { return an + " has a new child"; } // related is the child (Actor.addChild), not a co-parent. return an + " gains a child, " + bn; } public static string BabyBorn(Actor a) { // Subject is the baby from createBabyActorFromData. return NameOrSomeone(a) + " is created as a baby"; } public static string NewFamily(Actor a) { return NameOrSomeone(a) + " starts a new family"; } public static string FamilyEnded(Actor a) { return NameOrSomeone(a) + "'s family ends"; } public static string BuildingEat(Actor a, Building food) { string an = NameOrSomeone(a); string buildingId = BuildingId(food); string buildingType = BuildingType(food); if (string.Equals(buildingType, "type_fruits", StringComparison.OrdinalIgnoreCase)) { return string.IsNullOrEmpty(buildingId) ? an + " is foraging fruit" : an + " is foraging " + HumanizeId(buildingId); } if (!string.IsNullOrEmpty(buildingId)) { return an + " is eating a " + HumanizeId(buildingId); } return an + " is eating"; } public static string BuildingDamage(Actor a, Building building) { string an = NameOrSomeone(a); string buildingId = BuildingId(building); if (!string.IsNullOrEmpty(buildingId)) { return an + " is damaging a " + HumanizeId(buildingId); } return an + " is damaging a building"; } public static string BuildingFalls(string buildingId, Actor near) { string id = HumanizeId(buildingId); if (near != null && near.isAlive()) { string n = Name(near); if (!string.IsNullOrEmpty(n)) { return string.IsNullOrEmpty(id) ? "A building falls near " + n : "A " + id + " falls near " + n; } } return string.IsNullOrEmpty(id) ? "A building falls" : "A " + id + " falls"; } public static string Status(Actor a, string phrase) { string an = NameOrSomeone(a); string p = (phrase ?? "").Trim(); if (string.IsNullOrEmpty(p)) { return an + " changes status"; } // Prose often already includes a verb phrase ("is burning"). if (p.StartsWith("is ", StringComparison.OrdinalIgnoreCase) || p.StartsWith("are ", StringComparison.OrdinalIgnoreCase)) { return an + " " + p; } return an + " · " + p; } public static string Trait(Actor a, string traitId, bool gained) { string an = NameOrSomeone(a); string id = HumanizeId(traitId); if (string.IsNullOrEmpty(id)) { id = "a trait"; } return gained ? an + " gains " + id : an + " loses " + id; } public static string Boat(Actor a, string phase) { string an = NameOrSomeone(a); switch ((phase ?? "").Trim().ToLowerInvariant()) { case "unload": case "boat_unload": return an + " is unloading passengers"; case "load": case "boat_load": return an + " is loading passengers"; case "trade": case "boat_trade": return an + " is trading by boat"; default: return an + " is on a boat"; } } public static string MetaNew(Actor a, string kind) { string an = NameOrSomeone(a); string k = HumanizeId(kind); if (string.IsNullOrEmpty(k)) { k = "something new"; } return an + " founds a " + k; } public static string Library(Actor a, string kind, string assetId) { string an = NameOrSomeone(a); string id = HumanizeId(assetId); string k = HumanizeId(kind); if (string.IsNullOrEmpty(id)) { return an + " triggers " + (string.IsNullOrEmpty(k) ? "an event" : k); } switch ((kind ?? "").Trim().ToLowerInvariant()) { case "spell": return an + " casts " + id; case "item": return an + " forges " + id; case "decision": return an + " decides " + id; case "gene": return an + " gains gene " + id; case "phenotype": return an + " shows phenotype " + id; case "power": return an + " channels " + id; case "subspecies_trait": return an + " gains " + id; default: return an + " · " + id; } } /// /// Apply a sentence template with {a}/{b}/{id}. Prefer typed helpers for new copy. /// public static string Apply(string template, Actor a, Actor b = null, string id = "") { string t = string.IsNullOrEmpty(template) ? "{a}" : template; return t .Replace("{id}", id ?? "") .Replace("{a}", Name(a) ?? "") .Replace("{b}", Name(b) ?? ""); } public static string HumanizeId(string id) { if (string.IsNullOrEmpty(id)) { return ""; } string raw = id.Trim().Replace('-', '_'); if (raw.StartsWith("type_", StringComparison.OrdinalIgnoreCase)) { raw = raw.Substring(5); } string[] parts = raw.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 0) { return raw; } var sb = new StringBuilder(raw.Length + 4); for (int i = 0; i < parts.Length; i++) { if (i > 0) { sb.Append(' '); } string p = parts[i]; if (p.Length == 0) { continue; } sb.Append(char.ToUpperInvariant(p[0])); if (p.Length > 1) { sb.Append(p.Substring(1).ToLowerInvariant()); } } return sb.ToString(); } private static string NameOrSomeone(Actor a) { string n = Name(a); return string.IsNullOrEmpty(n) ? "Someone" : n; } private static string Name(Actor a) => EventFeedUtil.SafeName(a); private static string BuildingId(Building building) { if (building?.asset == null) { return ""; } try { return building.asset.id ?? ""; } catch { return ""; } } private static string BuildingType(Building building) { if (building?.asset == null) { return ""; } try { return building.asset.type ?? ""; } catch { return ""; } } }