- Implement checks for civilized terminology in camera focus events - Update event handling to suppress inappropriate pack language for civilized characters - Enhance narrative presentation to differentiate between family and pack language - Introduce new scenarios to validate significance of beats and relationships - Improve documentation to clarify narrative structure and event significance
87 lines
3.2 KiB
C#
87 lines
3.2 KiB
C#
using System;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
public enum NarrativeInterruptClass
|
|
{
|
|
None,
|
|
RelatedEscalation,
|
|
StoryPayoff,
|
|
WorldCritical,
|
|
OrdinaryInteresting,
|
|
TextureNoise
|
|
}
|
|
|
|
/// <summary>Explicit editorial interrupt classification for episode scheduling.</summary>
|
|
public static class InterruptPolicy
|
|
{
|
|
public static NarrativeInterruptClass Classify(
|
|
InterestCandidate candidate,
|
|
EpisodePlan episode,
|
|
NarrativeThread thread)
|
|
{
|
|
if (candidate == null) return NarrativeInterruptClass.None;
|
|
NarrativeFunction function = NarrativeFunctionPolicy.Classify(candidate);
|
|
if (function == NarrativeFunction.Noise || function == NarrativeFunction.CharacterTexture)
|
|
{
|
|
return NarrativeInterruptClass.TextureNoise;
|
|
}
|
|
if (IsRelated(candidate, episode, thread)
|
|
&& NarrativeFunctionPolicy.ChangesStoryState(function))
|
|
{
|
|
return NarrativeInterruptClass.RelatedEscalation;
|
|
}
|
|
if (WorldCriticalPolicy.IsWorldCritical(candidate))
|
|
return NarrativeInterruptClass.WorldCritical;
|
|
if (IsStoryPayoff(candidate)) return NarrativeInterruptClass.StoryPayoff;
|
|
|
|
return NarrativeInterruptClass.OrdinaryInteresting;
|
|
}
|
|
|
|
public static bool MayInterrupt(NarrativeInterruptClass kind)
|
|
{
|
|
return kind == NarrativeInterruptClass.WorldCritical
|
|
|| kind == NarrativeInterruptClass.StoryPayoff
|
|
|| kind == NarrativeInterruptClass.RelatedEscalation;
|
|
}
|
|
|
|
public static bool IsRelated(InterestCandidate candidate, EpisodePlan episode, NarrativeThread thread)
|
|
{
|
|
if (candidate == null || episode == null) return false;
|
|
if (Touches(candidate, episode.ProtagonistId)) return true;
|
|
for (int i = 0; i < episode.EligibleCastIds.Count; i++)
|
|
{
|
|
if (Touches(candidate, episode.EligibleCastIds[i])) return true;
|
|
}
|
|
|
|
if (thread == null || string.IsNullOrEmpty(thread.AnchorKey)) return false;
|
|
return EqualsKey(candidate.CorrelationKey, thread.AnchorKey)
|
|
|| EqualsKey(candidate.CityKey, thread.AnchorKey)
|
|
|| EqualsKey(candidate.KingdomKey, thread.AnchorKey)
|
|
|| (candidate.Key ?? "").IndexOf(thread.AnchorKey, StringComparison.OrdinalIgnoreCase) >= 0;
|
|
}
|
|
|
|
private static bool Touches(InterestCandidate c, long id)
|
|
{
|
|
if (id == 0) return false;
|
|
if (c.SubjectId == id || c.RelatedId == id || c.PairOwnerId == id
|
|
|| c.PairPartnerId == id || c.TheaterLeadId == id
|
|
|| EventFeedUtil.SafeId(c.FollowUnit) == id) return true;
|
|
for (int i = 0; i < c.ParticipantIds.Count; i++)
|
|
{
|
|
if (c.ParticipantIds[i] == id) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsStoryPayoff(InterestCandidate c)
|
|
{
|
|
return StoryReason.IsStoryAsset(c.AssetId)
|
|
|| (c.Source ?? "").IndexOf("story", StringComparison.OrdinalIgnoreCase) >= 0;
|
|
}
|
|
|
|
private static bool EqualsKey(string a, string b) =>
|
|
!string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b)
|
|
&& string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
|
|
}
|