134 lines
3.6 KiB
C#
134 lines
3.6 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
/// <summary>
|
|
/// Decaying per-unit heat from featured cast. Soft-spreads to live relations and Chronicle links.
|
|
/// </summary>
|
|
public static class CausalHeat
|
|
{
|
|
private struct HeatEntry
|
|
{
|
|
public float Value;
|
|
public float TouchedAt;
|
|
}
|
|
|
|
private static readonly Dictionary<long, HeatEntry> HeatById = new Dictionary<long, HeatEntry>(64);
|
|
private static readonly List<long> ScratchIds = new List<long>(16);
|
|
private const float FeaturedAmount = 1f;
|
|
private const float RelatedSpread = 0.45f;
|
|
|
|
public static void Clear()
|
|
{
|
|
HeatById.Clear();
|
|
}
|
|
|
|
public static void NoteFeatured(long unitId, float amount = FeaturedAmount)
|
|
{
|
|
if (unitId == 0 || amount <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float now = Time.unscaledTime;
|
|
AddHeat(unitId, amount, now);
|
|
SpreadRelated(unitId, amount * RelatedSpread, now);
|
|
}
|
|
|
|
public static void NoteCast(IList<long> castIds, float amount = FeaturedAmount)
|
|
{
|
|
if (castIds == null || castIds.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < castIds.Count; i++)
|
|
{
|
|
NoteFeatured(castIds[i], amount);
|
|
}
|
|
}
|
|
|
|
public static float Get(long unitId)
|
|
{
|
|
if (unitId == 0 || !HeatById.TryGetValue(unitId, out HeatEntry entry))
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
return Decayed(entry, Time.unscaledTime);
|
|
}
|
|
|
|
public static float ScoreBonus(InterestCandidate c)
|
|
{
|
|
if (c == null)
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
StoryWeights s = InterestScoringConfig.Story;
|
|
float subject = Get(c.SubjectId);
|
|
float related = c.RelatedId != 0 && c.RelatedId != c.SubjectId ? Get(c.RelatedId) : 0f;
|
|
return subject * s.causalHeatWeight + related * s.causalRelatedWeight;
|
|
}
|
|
|
|
private static float Decayed(HeatEntry entry, float now)
|
|
{
|
|
StoryWeights s = InterestScoringConfig.Story;
|
|
float halfLife = s.causalHeatHalfLifeSeconds > 1f ? s.causalHeatHalfLifeSeconds : 180f;
|
|
float age = Mathf.Max(0f, now - entry.TouchedAt);
|
|
return entry.Value * Mathf.Pow(0.5f, age / halfLife);
|
|
}
|
|
|
|
private static void AddHeat(long unitId, float amount, float now)
|
|
{
|
|
float cur = 0f;
|
|
if (HeatById.TryGetValue(unitId, out HeatEntry entry))
|
|
{
|
|
cur = Decayed(entry, now);
|
|
}
|
|
|
|
HeatById[unitId] = new HeatEntry
|
|
{
|
|
Value = Mathf.Min(4f, cur + amount),
|
|
TouchedAt = now
|
|
};
|
|
}
|
|
|
|
private static void SpreadRelated(long unitId, float amount, float now)
|
|
{
|
|
if (amount < 0.05f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Actor actor = EventFeedUtil.FindAliveById(unitId);
|
|
if (actor != null)
|
|
{
|
|
Actor lover = ActorRelation.GetLover(actor);
|
|
if (lover != null)
|
|
{
|
|
AddHeat(EventFeedUtil.SafeId(lover), amount, now);
|
|
}
|
|
|
|
Actor friend = ActorRelation.GetBestFriend(actor);
|
|
if (friend != null)
|
|
{
|
|
AddHeat(EventFeedUtil.SafeId(friend), amount * 0.75f, now);
|
|
}
|
|
}
|
|
|
|
ScratchIds.Clear();
|
|
Chronicle.EnumerateRelatedIds(unitId, ScratchIds, max: 6);
|
|
for (int i = 0; i < ScratchIds.Count; i++)
|
|
{
|
|
long other = ScratchIds[i];
|
|
if (other == 0 || other == unitId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHeat(other, amount * 0.5f, now);
|
|
}
|
|
}
|
|
}
|