using Cielonos.MainGame.Characters.AI; using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.Shared.Utility; using UnityEngine; namespace Cielonos.MainGame.Characters.AI { [Category("Cielonos/Rhythm")] [Description("Returns Success if an upcoming beat containing a tag that starts with 'EnemyAttack' is within the lead time. Otherwise returns Failure.")] public class RhythmAttackDetector : AutomataConditionalBase { [Tooltip("The lead time before the beat in seconds.")] public float leadTime = 0.1f; [Tooltip("The prefix of the tag to match.")] public string tagPrefix = "EnemyAttack"; private MusicBeatSystem _beatSystem; private BeatMarker _lastTriggeredBeat; public override void OnAwake() { base.OnAwake(); _beatSystem = CombatManager.GetCombatSystem(); } public override void OnStart() { _lastTriggeredBeat = null; } public override TaskStatus OnUpdate() { if (_beatSystem == null || !_beatSystem.IsActive || _beatSystem.CurrentBeatData == null) { return TaskStatus.Failure; } BeatMarker targetBeat = FindNextMatchingBeat(); if (targetBeat == null) { return TaskStatus.Failure; } // If we have already triggered for this specific beat, we shouldn't succeed again if (_lastTriggeredBeat == targetBeat) { return TaskStatus.Failure; } float timeUntilBeat = targetBeat.time - _beatSystem.CurrentSongTime; // Check if we are within the lead time window if (timeUntilBeat <= leadTime) { TriggerDetection(targetBeat, timeUntilBeat); return TaskStatus.Success; } return TaskStatus.Failure; } private BeatMarker FindNextMatchingBeat() { float currentTime = _beatSystem.CurrentSongTime; var markers = _beatSystem.CurrentBeatData.beatMarkers; if (markers == null) return null; for (int i = 0; i < markers.Count; i++) { BeatMarker marker = markers[i]; // Check for upcoming beats if (marker.time > currentTime) { if (marker.tags != null) { for (int j = 0; j < marker.tags.Count; j++) { if (marker.tags[j].StartsWith(tagPrefix)) { return marker; } } } } } return null; } private void TriggerDetection(BeatMarker beat, float timeUntilBeat) { _lastTriggeredBeat = beat; // Log matching tags for identification string matchedTag = ""; if (beat.tags != null) { foreach (string t in beat.tags) { if (t.StartsWith(tagPrefix)) { matchedTag = t; break; } } } Debug.Log($"[RhythmAttackDetector] Conditional MET for {self.gameObject.name} at {beat.time:F3}s " + $"(in {timeUntilBeat:F3}s) with matched tag '{matchedTag}'."); } } }