using System; using System.Collections; using BlueWater.Audios; using BlueWater.Interfaces; using BlueWater.Maps; using BlueWater.Players; 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 SpineController _spineController; private Rigidbody _userRigidbody; private CapsuleCollider _userCollider; private Collider _targetCollider; private ICurrentDirection _iCurrentDirection; private Transform _particleInstantiateLocation; private float _colliderRadius; private float _attackRadius; private void OnDrawGizmos() { if (!_isDrawingGizmo || !IsUsingSkill || !SkillUser) return; Gizmos.color = Color.red; Gizmos.DrawWireSphere(SkillUser.transform.position, _attackRadius); } protected override void BasicSetting() { _singleRollData = (SingleRollData)SkillData; _spineController = SkillUser.GetComponent(); _userRigidbody = SkillUser.GetComponent(); _userCollider = SkillUser.GetComponent(); _targetCollider = SkillUser.GetComponent().Target; _particleInstantiateLocation = MapManager.Instance.SandMoleMapController.ParticleInstanceLocation; _iCurrentDirection = SkillUser.GetComponent(); _colliderRadius = _userCollider.radius * SkillUser.transform.localScale.x; _attackRadius = _colliderRadius * 0.5f; 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; _spineController.SetSkin(SandMoleSkin.Idle.ToString()); var spinReady2Track = _spineController.PlayAnimation(SandMoleAnimation.SpinReady2.ToString(), false); if (spinReady2Track == null) { EndSkill(0, actions[0]); yield break; } AudioManager.Instance.PlaySfx("SandMoleRoll"); yield return new WaitUntil(() => spinReady2Track.IsComplete); _spineController.SetSkin(SandMoleSkin.Spin.ToString()); var spinTrack = _spineController.PlayAnimation(SandMoleAnimation.Spin.ToString(), true); if (spinTrack == null || !SkillUser) { EndSkill(0, actions[0]); yield break; } IsUsingSkill = true; 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")) { AudioManager.Instance.PlaySfx("JumpSlamDown"); 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; iPhysicMovable.SetPush(hitDirection, _singleRollData.PushPower); // 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 = 3; 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, _particleInstantiateLocation); } } 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); _spineController.SetSkin(SandMoleSkin.Normal.ToString()); _spineController.PlayAnimation(SandMoleAnimation.Idle.ToString(), true); action?.Invoke(); IsUsingSkill = false; Utils.StartUniqueCoroutine(this, ref CooldownCoroutineInstance,Utils.CoolDownCoroutine(cooldown, EndCooldown)); } } }