using System; using System.Collections; using BlueWater.Interfaces; using BlueWater.Maps; using BlueWater.Utility; using Sirenix.OdinInspector; using UnityEngine; using Random = UnityEngine.Random; namespace BlueWater.Enemies.Bosses.SandMole.Skills { public class SingleRoll : BaseSkill { [Title("추가 옵션")] [SerializeField] private bool _isDrawingGizmo = true; private SingleRollData _singleRollData; private AnimationController _animationController; private Rigidbody _userRigidbody; private CapsuleCollider _userCollider; private Collider _targetCollider; private ICurrentDirection _iCurrentDirection; private BossMapController _sandMoleMapController; private float _colliderRadius; private float _attackRadius; private void OnDrawGizmos() { if (!_isDrawingGizmo || !IsUsingSkill) return; Gizmos.color = Color.red; Gizmos.DrawWireSphere(SkillUser.transform.position, _attackRadius); } protected override void BasicSetting() { _animationController = SkillUser.GetComponent(); _userRigidbody = SkillUser.GetComponent(); _userCollider = SkillUser.GetComponent(); _targetCollider = SkillUser.GetComponent().Target; _sandMoleMapController = MapManager.Instance.SandMoleMapController; _iCurrentDirection = SkillUser.GetComponent(); _colliderRadius = _userCollider.radius * SkillUser.transform.localScale.x; _attackRadius = _colliderRadius * 0.5f; _singleRollData = (SingleRollData)SkillData; HitColliders = new Collider[4]; base.BasicSetting(); } public override void ActivateSkill(params Action[] actions) { Utils.StartUniqueCoroutine(this, ref SkillCoroutineInstance, SkillCoroutine(actions)); } private IEnumerator SkillCoroutine(params Action[] actions) { EnableSkill = false; _animationController.SetAnimationParameter("skillIndex", (int)SandMoleSkill.SingleRoll); var animationStarted = false; yield return StartCoroutine(_animationController.WaitForAnimationToRun("SingleRoll", success => animationStarted = success)); if (!animationStarted || !SkillUser) { EndSkill(0, actions[0]); yield break; } IsUsingSkill = true; _animationController.ResetAnimationSpeed(); var startPosition = SkillUser.transform.position; var targetPosition = _targetCollider.transform.position; targetPosition.y = startPosition.y; var targetVector = targetPosition - startPosition; var targetDirection = targetVector.normalized; if (!Physics.Raycast(startPosition, targetDirection, out var hit, 100f, _singleRollData.WallLayer)) { Debug.LogError("벽을 탐지하지 못했습니다."); EndSkill(0, actions[0]); yield break; } var targetDistance = hit.distance; transform.position = startPosition + Vector3.up * 3f; transform.localScale = new Vector3(_colliderRadius, 6f, targetDistance * 2); var angle = Mathf.Atan2(targetDirection.x, targetDirection.z) * Mathf.Rad2Deg; transform.rotation = Quaternion.Euler(0, angle, 0); _iCurrentDirection.CurrentDirection = targetDirection; ShowIndicator(); var elapsedTime = 0f; var fill = 1 / SkillData.CastingTime; while (elapsedTime <= SkillData.CastingTime) { elapsedTime += Time.deltaTime; if (IsUsingIndicator && Indicator) { var fillValue = Indicator.material.GetFloat(_fillHash) + Time.deltaTime * fill; Indicator.material.SetFloat(_fillHash, fillValue); } yield return null; } HideIndicator(); elapsedTime = 0f; var isCrashedWall = false; while (!isCrashedWall) { elapsedTime += Time.deltaTime; if (elapsedTime >= 5f || !SkillUser) { if (elapsedTime >= 5f) { print("무한 루프 버그"); } EndSkill(SkillData.Cooldown, actions[0]); yield break; } var skillUserPosition = SkillUser.transform.position; var moveDistance = _singleRollData.RollSpeed * Time.fixedDeltaTime; var hitCount = Physics.OverlapSphereNonAlloc(skillUserPosition, _attackRadius, HitColliders, _singleRollData.TargetLayer, QueryTriggerInteraction.Collide); for (var i = 0; i < hitCount; i++) { var hitCollider = HitColliders[i]; if (hitCollider.CompareTag("Wall")) { VisualFeedbackManager.Instance.CameraShake(CombatCameraManager.Instance.BaseCombatCamera, _singleRollData.CameraShakingPower, _singleRollData.CameraShakingDuration); var forceDirection = -targetDirection + Vector3.up * _singleRollData.AirJumpForce; _userRigidbody.linearVelocity = Vector3.zero; _userRigidbody.AddForce(forceDirection * _singleRollData.BounceBackForce, ForceMode.Impulse); isCrashedWall = true; break; } var iDamageable = hitCollider.transform.GetComponentInParent(); if (iDamageable == null || !iDamageable.CanDamage()) continue; iDamageable.TakeDamage(SkillData.Damage); var iPhysicMovable = hitCollider.transform.GetComponentInParent(); if (iPhysicMovable == null) continue; var hitVector = hitCollider.transform.position - SkillUser.transform.position; hitVector.y = 0f; var hitDirection = hitVector.normalized; var cross = Vector3.Cross(hitDirection, transform.forward); var addForceDirection = cross.y >= 0f ? Quaternion.Euler(0, -90, 0) * transform.forward : Quaternion.Euler(0, 90, 0) * transform.forward; iPhysicMovable.AddForce(addForceDirection * _singleRollData.PushPower, ForceMode.Impulse); } _userRigidbody.MovePosition(skillUserPosition + targetDirection * moveDistance); yield return new WaitForFixedUpdate(); } if (_singleRollData.RockfallPrefab) { var rockfallCount = (int)(targetDistance / _singleRollData.RockfallInterval); for (var i = 2; i < rockfallCount; i++) { if (!SkillUser) { EndSkill(SkillData.Cooldown, actions[0]); yield break; } var randomSide = Random.Range(_singleRollData.RandomSide.x, _singleRollData.RandomSide.y); var spawnPosition = SkillUser.transform.position + -targetDirection * _singleRollData.RockfallInterval * i + Vector3.up * _singleRollData.RockfallSpawnHeight; spawnPosition += Vector3.Cross(-targetDirection, Vector3.up).normalized * randomSide; Instantiate(_singleRollData.RockfallPrefab, spawnPosition, Quaternion.identity, _sandMoleMapController.ParticleInstanceLocation); } } elapsedTime = 0f; while (elapsedTime < 1f) { elapsedTime += Time.deltaTime; yield return null; } EndSkill(SkillData.Cooldown, actions[0]); } private void EndSkill(float cooldown, Action action) { Utils.EndUniqueCoroutine(this, ref SkillCoroutineInstance); _animationController.SetAnimationParameter("skillIndex", (int)SandMoleSkill.None); action?.Invoke(); IsUsingSkill = false; Utils.StartUniqueCoroutine(this, ref CooldownCoroutineInstance,Utils.CoolDownCoroutine(cooldown, EndCooldown)); } } }