using System.Collections; using System.Collections.Generic; using Godot; using System; namespace Rokojori { [Tool] [GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/Scatterer.svg") ] public partial class Scatterer:Node3D { [Export] public bool update = false; [Export] public bool updateAlways = false; [Export] public Node3D[] containersToClearNodes; [Export] public bool clearContainers = false; [Export] public bool removeDiscarded = false; [Export] public int READ_ONLY_createdPoints = 0; [Export] public int READ_ONLY_instantiatedPoints = 0; [Export] public int READ_ONLY_reusedPoints = 0; [Export] public int READ_ONLY_remappedPoints = 0; public override void _Process( double delta ) { if ( clearContainers ) { clearContainers = false; ClearContainers(); } if ( ! ( update || updateAlways ) ) { return; } update = false; ScatterAndInstantiatePoints(); } public void ClearContainers() { Arrays.ForEach( containersToClearNodes, c => { Nodes.RemoveAndDeleteChildren( c ); } ); } class InstantiatedScatterPoint { public string hash; public ScatterPoint scatterPoint; public Node3D output; } Dictionary _instantiatedPoints = new Dictionary(); Dictionary _scattererIDs = new Dictionary(); int GetScattererID( Scatterer s ) { if ( ! _scattererIDs.ContainsKey( s ) ) { _scattererIDs[ s ] = _scattererIDs.Count; } return _scattererIDs[ s ]; } string GetScatterPointHash( ScatterPoint p ) { var scatterID = GetScattererID( p.creator ) + ":" + p.creatorID; return scatterID; } public void ScatterAndInstantiatePoints() { if ( _instantiatedPoints.Count == 0 ) { ClearContainers(); } var points = Scatter( new List() ); READ_ONLY_createdPoints = points.Count; READ_ONLY_instantiatedPoints = 0; READ_ONLY_reusedPoints = 0; READ_ONLY_remappedPoints = 0; var usedPoints = new HashSet(); points.RemoveAll( p => ! p.visible ); points.ForEach( p => { var hash = GetScatterPointHash( p ); if ( ! CanReuse( hash, p ) ) { return; } var existing = _instantiatedPoints[ hash ]; existing.scatterPoint = p; existing.scatterPoint.UpdateInstantiated( existing.output ); usedPoints.Add( hash ); READ_ONLY_reusedPoints ++; return; } ); var unused = new DictionaryList(); foreach ( var vk in _instantiatedPoints ) { var ip = vk.Value; if ( usedPoints.Contains( ip.hash ) ) { continue; } unused.Add( ip.scatterPoint.scene, ip ); } points.ForEach( p => { var hash = GetScatterPointHash( p ); if ( usedPoints.Contains( hash ) ) { return; } if ( unused.ContainsKey( p.scene ) ) { var unusedPoint = unused[ p.scene ].Find( ip => ip.scatterPoint.CanBeReusedBy( p ) ); if ( unusedPoint != null ) { unused.Remove( p.scene, unusedPoint ); unusedPoint.scatterPoint = p; unusedPoint.scatterPoint.UpdateInstantiated( unusedPoint.output ); usedPoints.Add( hash ); READ_ONLY_remappedPoints ++; return; } } var instantiatedOutput = p.Instantiate(); if ( instantiatedOutput == null ) { return; } var ip = new InstantiatedScatterPoint(); ip.hash = hash; ip.output = instantiatedOutput; ip.scatterPoint = p; if ( _instantiatedPoints.ContainsKey( hash ) ) { Nodes.RemoveAndDelete( _instantiatedPoints[ hash ].output ); } _instantiatedPoints[ hash ] = ip; usedPoints.Add( hash ); READ_ONLY_instantiatedPoints++; } ); Dictionaries.RemoveAll( _instantiatedPoints, ( k, v ) => { if ( usedPoints.Contains( k ) ) { return false; } Nodes.RemoveAndDelete( v.output ); return true; } ); } bool CanReuse( string hash, ScatterPoint sp ) { if ( ! _instantiatedPoints.ContainsKey( hash ) ) { return false; } var existing = _instantiatedPoints[ hash ]; var canBeReused = existing.scatterPoint.CanBeReusedBy( sp ); return canBeReused; } public List Scatter( List points ) { var returnedPoints = _Scatter( points ); if ( removeDiscarded ) { returnedPoints.RemoveAll( p => ! p.visible ); } return returnedPoints; } protected bool IsChildEnabled( Scatterer s, bool childrenNeedToBeVisible, bool childrenNeedToBeProcessing ) { var enabled = true; if ( childrenNeedToBeVisible ) { enabled = enabled && s.Visible; } if ( childrenNeedToBeProcessing ) { enabled = enabled && ( s.ProcessMode != ProcessModeEnum.Disabled ); } return enabled; } protected virtual List _Scatter( List points ) { return points; } } }