using Godot; using System.Collections.Generic; using Rokojori.Extensions; namespace Rokojori { public enum LockOnCycleMode { None, ClosestCycle, Directional } // Only tracks the current candidate list and the single selected pointable (sticky - stays on // the selection until it is cycled away from or stops being a valid candidate). Owns no concept // of "locked" - that is CameraLocker's job, reading/driving this pointer's pointable. [Tool] [GlobalClass] public partial class LockOnPointer : Pointer { public int test = 0; List _candidates = new List(); public int numCandidates => _candidates.Count; public Pointable GetCandidate( int index ) { return _candidates[ index ]; } public bool IsCandidate( Pointable p ) { return p != null && _candidates.Contains( p ); } protected override void _OnTrigger() { if ( caster == null ) { return; } if ( pointable != null && ! IsInstanceValid( pointable ) ) { pointable = null; } RefreshCandidates(); if ( pointable == null || ! _candidates.Contains( pointable ) ) { SetPointable( ClosestCandidate() ); } } void RefreshCandidates() { _candidates.Clear(); for ( int i = 0; i < caster.NumColliders(); i++ ) { var p = Nodes.Find( caster.GetCollider( i ) ); if ( p != null && p.enabled ) { _candidates.Add( p ); } } } Pointable ClosestCandidate() { return _candidates.Count == 0 ? null : _candidates[ 0 ]; } public void CycleNext() { if ( _candidates.Count == 0 ) { return; } var currentIndex = _candidates.IndexOf( pointable ); var nextIndex = currentIndex == -1 ? 0 : ( currentIndex + 1 ) % _candidates.Count; SetPointable( _candidates[ nextIndex ] ); } public void CyclePrevious() { if ( _candidates.Count == 0 ) { return; } var currentIndex = _candidates.IndexOf( pointable ); var previousIndex = currentIndex == -1 ? 0 : ( currentIndex - 1 + _candidates.Count ) % _candidates.Count; SetPointable( _candidates[ previousIndex ] ); } public void CycleInScreenSpaceDirection( Vector2 screenSpaceDirection ) { if ( pointable == null ) { SetPointable( ClosestCandidate() ); return; } var right = caster.GlobalTransform.Basis.X; var up = caster.GlobalTransform.Basis.Y; var currentPosition = pointable.GlobalPosition; Pointable best = null; var bestScore = float.NegativeInfinity; for ( int i = 0; i < _candidates.Count; i++ ) { var candidate = _candidates[ i ]; if ( candidate == pointable ) { continue; } var offset = candidate.GlobalPosition - currentPosition; var projected = new Vector2( offset.Dot( right ), offset.Dot( up ) ); if ( projected.LengthSquared() < 0.0001f ) { continue; } var score = projected.Normalized().Dot( screenSpaceDirection ); if ( score > bestScore ) { bestScore = score; best = candidate; } } if ( best != null ) { SetPointable( best ); } } } }