init mod
This commit is contained in:
commit
b1ecbdf51b
22 files changed
+1384
No files matched your search
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cthangover.Core.Characters;
|
||||
using Cthangover.Core.Settings;
|
||||
using Cthangover.Core.Utils;
|
||||
|
||||
namespace Cthangover.Core.Relationship
|
||||
{
|
||||
/// <summary>
|
||||
/// Central registry and lifecycle dispatcher for recruit behaviours
|
||||
/// and conditions. Behaviours (<see cref="IRecruitBehaviour"/>) and
|
||||
/// conditions (<see cref="IRecruitCondition"/>) of every mod arrive
|
||||
/// through the kernel's assembly consumers
|
||||
/// (<see cref="RecruitBehaviourConsumer"/>, <see cref="RecruitConditionConsumer"/>)
|
||||
/// as mods load, and leave again when their mod unloads — the kernel
|
||||
/// stays the only place that enumerates assemblies.
|
||||
///
|
||||
/// <b>Condition gating</b> — <see cref="CanRecruit"/> evaluates every
|
||||
/// registered condition conjunctively (logical AND), so a single
|
||||
/// <c>false</c> blocks recruitment. This allows mods to compose
|
||||
/// recruitment gates without coordination.
|
||||
///
|
||||
/// <b>Id-based deduplication</b> — a registration replaces any earlier
|
||||
/// implementation with the same <c>Id</c>, so two mods shipping the same
|
||||
/// id never double-dispatch notifications.
|
||||
///
|
||||
/// <b>Tick registration</b> — the first call to any lifecycle method
|
||||
/// triggers <see cref="RecruitTickController.EnsureRegistered"/>,
|
||||
/// which registers for the scene's timer event. This avoids paying
|
||||
/// the tick overhead unless at least one behaviour is active.
|
||||
///
|
||||
/// <b>Singleton</b> — accessed via the static <c>Instance</c> field
|
||||
/// rather than a <c>Lazy<T></c> wrapper because Godot
|
||||
/// reflection-based discovery must tolerate partial-initialisation
|
||||
/// scenarios where <c>Lazy</c>'s thread-safety check could deadlock.
|
||||
/// </summary>
|
||||
public class RecruitBehaviourRegistry
|
||||
{
|
||||
/// <summary>Eagerly-initialised singleton for reflection-safe access.</summary>
|
||||
public static readonly RecruitBehaviourRegistry Instance = new();
|
||||
|
||||
private readonly List<IRecruitBehaviour> _behaviours = new();
|
||||
private readonly List<IRecruitCondition> _conditions = new();
|
||||
private readonly RecruitTickController _tickController = new();
|
||||
|
||||
private void EnsureTickRegistered()
|
||||
{
|
||||
_tickController.EnsureRegistered();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a behaviour, replacing an earlier one with the same Id. Fed
|
||||
/// by <see cref="RecruitBehaviourConsumer"/> for every mod assembly.
|
||||
/// </summary>
|
||||
public void RegisterBehaviour(IRecruitBehaviour behaviour)
|
||||
{
|
||||
if (behaviour == null)
|
||||
return;
|
||||
_behaviours.RemoveAll(existing => existing.Id == behaviour.Id);
|
||||
_behaviours.Add(behaviour);
|
||||
}
|
||||
|
||||
/// <summary>Removes exactly <paramref name="behaviour"/> (its mod is being unloaded).</summary>
|
||||
public void UnregisterBehaviour(IRecruitBehaviour behaviour)
|
||||
{
|
||||
_behaviours.Remove(behaviour);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a condition, replacing an earlier one with the same Id. Fed
|
||||
/// by <see cref="RecruitConditionConsumer"/> for every mod assembly.
|
||||
/// </summary>
|
||||
public void RegisterCondition(IRecruitCondition condition)
|
||||
{
|
||||
if (condition == null)
|
||||
return;
|
||||
_conditions.RemoveAll(existing => existing.Id == condition.Id);
|
||||
_conditions.Add(condition);
|
||||
}
|
||||
|
||||
/// <summary>Removes exactly <paramref name="condition"/> (its mod is being unloaded).</summary>
|
||||
public void UnregisterCondition(IRecruitCondition condition)
|
||||
{
|
||||
_conditions.Remove(condition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conjunctive gate: returns <c>true</c> only if every registered
|
||||
/// <see cref="IRecruitCondition"/> returns <c>true</c> for the
|
||||
/// given enemy and runtime state.
|
||||
/// </summary>
|
||||
public bool CanRecruit(Character enemy, RuntimeData runtime)
|
||||
{
|
||||
return _conditions.All(c => c.CanRecruit(enemy, runtime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a behaviour by its <c>Id</c> (linear scan —
|
||||
/// behaviour lists are small enough that a dictionary is
|
||||
/// unnecessary). Returns <c>null</c> if not found.
|
||||
/// </summary>
|
||||
public IRecruitBehaviour Get(string id)
|
||||
{
|
||||
return _behaviours.Find(b => b.Id == id);
|
||||
}
|
||||
|
||||
/// <summary>Returns all registered behaviours for external enumeration.</summary>
|
||||
public IEnumerable<IRecruitBehaviour> All()
|
||||
{
|
||||
return _behaviours;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches <see cref="IRecruitBehaviour.OnTick"/> to every
|
||||
/// behaviour for every active recruit. Ensures tick registration
|
||||
/// on first call via lazy init.
|
||||
/// </summary>
|
||||
public void OnTick()
|
||||
{
|
||||
EnsureTickRegistered();
|
||||
var runtime = GameData.Instance.Runtime;
|
||||
if (runtime == null)
|
||||
return;
|
||||
var recruits = RecruitingData.Instance.Data;
|
||||
foreach (var behaviour in _behaviours)
|
||||
{
|
||||
foreach (var recruit in recruits)
|
||||
{
|
||||
behaviour.OnTick(recruit, runtime, runtime.Time.Tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches <see cref="IRecruitBehaviour.ConfigureRecruit"/> to
|
||||
/// every behaviour for the newly-added <paramref name="recruit"/>.
|
||||
/// </summary>
|
||||
public void OnConfigure(Recruit recruit)
|
||||
{
|
||||
EnsureTickRegistered();
|
||||
var runtime = GameData.Instance.Runtime;
|
||||
if (runtime == null)
|
||||
return;
|
||||
foreach (var behaviour in _behaviours)
|
||||
behaviour.ConfigureRecruit(recruit, runtime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches <see cref="IRecruitBehaviour.OnRemove"/> to every
|
||||
/// behaviour for the departing <paramref name="recruit"/>.
|
||||
/// </summary>
|
||||
public void OnRemove(Recruit recruit)
|
||||
{
|
||||
EnsureTickRegistered();
|
||||
var runtime = GameData.Instance.Runtime;
|
||||
if (runtime == null)
|
||||
return;
|
||||
foreach (var behaviour in _behaviours)
|
||||
behaviour.OnRemove(recruit, runtime);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user