182 lines
4.5 KiB
C#
182 lines
4.5 KiB
C#
using UnityEngine;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
/// <summary>Whether an interest candidate may steal the idle camera.</summary>
|
|
public enum InterestOwnership
|
|
{
|
|
/// <summary>May cut in / own EventLed camera focus.</summary>
|
|
Signal,
|
|
/// <summary>Tracked for fill/enrichment; fill-capped so it does not preempt Signal drama.</summary>
|
|
Ambient
|
|
}
|
|
|
|
/// <summary>Shared registration helpers for domain event feeds.</summary>
|
|
public static class EventFeedUtil
|
|
{
|
|
public static InterestCandidate Register(
|
|
string key,
|
|
string source,
|
|
string category,
|
|
string label,
|
|
string assetId,
|
|
float eventStrength,
|
|
InterestOwnership ownership,
|
|
Vector3 position,
|
|
Actor follow,
|
|
float minWatch = 4f,
|
|
float maxWatch = 22f,
|
|
InterestCompletionKind completion = InterestCompletionKind.FixedDwell)
|
|
{
|
|
if (string.IsNullOrEmpty(key) || position == Vector3.zero && follow == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (follow == null && position == Vector3.zero)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Vector3 pos = follow != null ? follow.current_position : position;
|
|
if (pos == Vector3.zero)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
bool ambient = ownership == InterestOwnership.Ambient;
|
|
float strength = eventStrength;
|
|
if (ambient)
|
|
{
|
|
strength = Mathf.Min(strength, InterestScoringConfig.W.fillScoreMax - 1f);
|
|
if (strength < 8f)
|
|
{
|
|
strength = 8f;
|
|
}
|
|
}
|
|
|
|
var candidate = new InterestCandidate
|
|
{
|
|
Key = key,
|
|
LeadKind = ambient ? InterestLeadKind.CharacterLed : InterestLeadKind.EventLed,
|
|
Category = string.IsNullOrEmpty(category) ? "Event" : category,
|
|
Source = source ?? "event",
|
|
EventStrength = strength,
|
|
CharacterSignificance = ambient ? strength * 0.5f : 0f,
|
|
VisualConfidence = follow != null ? 0.7f : 0.35f,
|
|
Position = pos,
|
|
FollowUnit = follow,
|
|
SubjectId = follow != null ? SafeId(follow) : 0,
|
|
Label = label ?? assetId ?? key,
|
|
AssetId = assetId ?? "",
|
|
SpeciesId = follow?.asset != null ? follow.asset.id : "",
|
|
CreatedAt = Time.unscaledTime,
|
|
LastSeenAt = Time.unscaledTime,
|
|
ExpiresAt = Time.unscaledTime + (ambient ? 18f : 35f),
|
|
MinWatch = minWatch,
|
|
MaxWatch = maxWatch,
|
|
Completion = completion
|
|
};
|
|
InterestScoring.ScoreCheap(candidate);
|
|
return InterestRegistry.Upsert(candidate);
|
|
}
|
|
|
|
public static long SafeId(Actor actor)
|
|
{
|
|
if (actor == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
try
|
|
{
|
|
return actor.getID();
|
|
}
|
|
catch
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static string SafeName(Actor actor)
|
|
{
|
|
if (actor == null)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
try
|
|
{
|
|
return actor.getName() ?? "";
|
|
}
|
|
catch
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public static Actor NearestUnit(Vector3 near, float radius = 200f)
|
|
{
|
|
try
|
|
{
|
|
return WorldActivityScanner.FindNearestAliveUnit(near, radius);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static Actor AnyAliveUnit()
|
|
{
|
|
try
|
|
{
|
|
if (World.world?.units == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
foreach (Actor actor in World.world.units)
|
|
{
|
|
if (actor != null && actor.isAlive())
|
|
{
|
|
return actor;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static bool TryResolveAnchor(Actor preferred, Vector3 position, out Actor follow, out Vector3 pos)
|
|
{
|
|
follow = preferred;
|
|
pos = position;
|
|
if (follow != null && follow.isAlive())
|
|
{
|
|
pos = follow.current_position;
|
|
return pos != Vector3.zero || true;
|
|
}
|
|
|
|
if (position != Vector3.zero)
|
|
{
|
|
follow = NearestUnit(position) ?? AnyAliveUnit();
|
|
pos = position;
|
|
return true;
|
|
}
|
|
|
|
follow = AnyAliveUnit();
|
|
if (follow == null)
|
|
{
|
|
pos = Vector3.zero;
|
|
return false;
|
|
}
|
|
|
|
pos = follow.current_position;
|
|
return true;
|
|
}
|
|
}
|