mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
138 lines
No EOL
4.8 KiB
C#
138 lines
No EOL
4.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Penumbra.Mods;
|
|
using Penumbra.Util;
|
|
|
|
namespace Penumbra.Collections;
|
|
|
|
// ModCollections can inherit from an arbitrary number of other collections.
|
|
// This is transitive, so a collection A inheriting from B also inherits from everything B inherits.
|
|
// Circular dependencies are resolved by distinctness.
|
|
public partial class ModCollection
|
|
{
|
|
// A change in inheritance usually requires complete recomputation.
|
|
// The bool signifies whether the change was in an already inherited collection.
|
|
public event Action< bool > InheritanceChanged;
|
|
|
|
private readonly List< ModCollection > _inheritance = new();
|
|
|
|
public IReadOnlyList< ModCollection > Inheritance
|
|
=> _inheritance;
|
|
|
|
// Iterate over all collections inherited from in depth-first order.
|
|
// Skip already visited collections to avoid circular dependencies.
|
|
public IEnumerable< ModCollection > GetFlattenedInheritance()
|
|
=> InheritedCollections( this ).Distinct();
|
|
|
|
// All inherited collections in application order without filtering for duplicates.
|
|
private static IEnumerable< ModCollection > InheritedCollections( ModCollection collection )
|
|
=> collection.Inheritance.SelectMany( InheritedCollections ).Prepend( collection );
|
|
|
|
// Reasons why a collection can not be inherited from.
|
|
public enum ValidInheritance
|
|
{
|
|
Valid,
|
|
Self, // Can not inherit from self
|
|
Empty, // Can not inherit from the empty collection
|
|
Contained, // Already inherited from
|
|
Circle, // Inheritance would lead to a circle.
|
|
}
|
|
|
|
// Check whether a collection can be inherited from.
|
|
public ValidInheritance CheckValidInheritance( ModCollection? collection )
|
|
{
|
|
if( collection == null || ReferenceEquals( collection, Empty ) )
|
|
{
|
|
return ValidInheritance.Empty;
|
|
}
|
|
|
|
if( ReferenceEquals( collection, this ) )
|
|
{
|
|
return ValidInheritance.Self;
|
|
}
|
|
|
|
if( _inheritance.Contains( collection ) )
|
|
{
|
|
return ValidInheritance.Contained;
|
|
}
|
|
|
|
if( InheritedCollections( collection ).Any( c => c == this ) )
|
|
{
|
|
return ValidInheritance.Circle;
|
|
}
|
|
|
|
return ValidInheritance.Valid;
|
|
}
|
|
|
|
private bool CheckForCircle( ModCollection collection )
|
|
=> ReferenceEquals( collection, this ) || _inheritance.Any( c => c.CheckForCircle( collection ) );
|
|
|
|
// Add a new collection to the inheritance list.
|
|
// We do not check if this collection would be visited before,
|
|
// only that it is unique in the list itself.
|
|
public bool AddInheritance( ModCollection collection )
|
|
{
|
|
if( CheckValidInheritance( collection ) != ValidInheritance.Valid )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_inheritance.Add( collection );
|
|
// Changes in inherited collections may need to trigger further changes here.
|
|
collection.ModSettingChanged += OnInheritedModSettingChange;
|
|
collection.InheritanceChanged += OnInheritedInheritanceChange;
|
|
InheritanceChanged.Invoke( false );
|
|
return true;
|
|
}
|
|
|
|
public void RemoveInheritance( int idx )
|
|
{
|
|
var inheritance = _inheritance[ idx ];
|
|
inheritance.ModSettingChanged -= OnInheritedModSettingChange;
|
|
inheritance.InheritanceChanged -= OnInheritedInheritanceChange;
|
|
_inheritance.RemoveAt( idx );
|
|
InheritanceChanged.Invoke( false );
|
|
}
|
|
|
|
// Order in the inheritance list is relevant.
|
|
public void MoveInheritance( int from, int to )
|
|
{
|
|
if( _inheritance.Move( from, to ) )
|
|
{
|
|
InheritanceChanged.Invoke( false );
|
|
}
|
|
}
|
|
|
|
// Carry changes in collections inherited from forward if they are relevant for this collection.
|
|
private void OnInheritedModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
|
|
{
|
|
if( _settings[ modIdx ] == null )
|
|
{
|
|
ModSettingChanged.Invoke( type, modIdx, oldValue, groupIdx, true );
|
|
}
|
|
}
|
|
|
|
private void OnInheritedInheritanceChange( bool _ )
|
|
=> InheritanceChanged.Invoke( true );
|
|
|
|
// Obtain the actual settings for a given mod via index.
|
|
// Also returns the collection the settings are taken from.
|
|
// If no collection provides settings for this mod, this collection is returned together with null.
|
|
public (ModSettings2? Settings, ModCollection Collection) this[ Index idx ]
|
|
{
|
|
get
|
|
{
|
|
foreach( var collection in GetFlattenedInheritance() )
|
|
{
|
|
var settings = collection._settings[ idx ];
|
|
if( settings != null )
|
|
{
|
|
return ( settings, collection );
|
|
}
|
|
}
|
|
|
|
return ( null, this );
|
|
}
|
|
}
|
|
} |