using System.Linq; using System.Threading; using Code.Infrastructure.Input; using Code.Infrastructure.Utils; using Code.OfficeGame.BattleMode.Actions.HoloCopy; using Code.OfficeGame.BattleMode.HitBoxes; using Cysharp.Threading.Tasks; using Reflex.Attributes; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.Serialization; namespace Code.OfficeGame.BattleMode.Actions.Attacks { public class BattleActionPlayerAttackMMA : BattleAction { [SerializeField] private HoloCop holoCop; [SerializeField] private Animator animator; private EntityHitBoxes _entityHitBoxes; private AttackActionConfig _attackData; public int EntryStateHash => Animator.StringToHash("WalkBlend"); private int _currentFrame; private IInputService _inputService; private CancellationTokenSource _waitForEnterTokenSource; private BattleActionDecisionResultCode _decisionResultCode; [Inject] public void Construct(MultipleConfig attackData, IInputService inputService) { _inputService = inputService; _entityHitBoxes = GetComponent(); _attackData = (AttackActionConfig) attackData.Configs.SingleOrDefault(x=>x.ActionType == ActionType); } public override async UniTask DecideAction() { _currentFrame = 0; Finished = false; _waitForEnterTokenSource = new CancellationTokenSource(); _decisionResultCode = BattleActionDecisionResultCode.CancelDecision; _inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm, ConfirmAttack); _inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel, CancelAttack); holoCop.Simulate(TickHoloCopy, _waitForEnterTokenSource.Token).Forget(); await UniTask.WaitUntilCanceled(_waitForEnterTokenSource.Token).SuppressCancellationThrow(); _inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm, ConfirmAttack); _inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel, CancelAttack); _currentFrame = 0; return new() { ResultCode = _decisionResultCode, FrameDuration = _attackData.TotalFrames}; } private void CancelAttack(InputAction.CallbackContext obj) { _decisionResultCode = BattleActionDecisionResultCode.CancelDecision; _waitForEnterTokenSource?.Cancel(); } private void ConfirmAttack(InputAction.CallbackContext obj) { _decisionResultCode = BattleActionDecisionResultCode.Decided; _waitForEnterTokenSource?.Cancel(); } public override BattleActionType ActionType => BattleActionType.PlayerAttackMMA; public override void Tick(float tickDelta) { if (Finished) return; if (_currentFrame == 0) { animator.Play(_attackData.StateHash, 0, 0f); } _currentFrame++; if (_currentFrame >= _attackData.HitboxStartFrame && _currentFrame <= _attackData.HitboxEndFrame) { _entityHitBoxes.EnableHitbox(_attackData.AttackingBodyPart); _entityHitBoxes.CheckHitsForCurrentFrame(OnEnemyHit); } else { _entityHitBoxes.DisableAllHitboxes(); } if (_currentFrame >= _attackData.TotalFrames) { CompleteOrInterruptAction(); } } private void OnEnemyHit(Collider enemyCollider) { Debug.Log($"Попадание в {enemyCollider.name} на {_currentFrame}-м кадре! Урон: {_attackData.Damage}"); var entityActor = enemyCollider.GetComponentInParent(); entityActor.ForceDecideAction(BattleActionType.Stugger); } public void CompleteOrInterruptAction() { animator.Play(EntryStateHash, 0, 0f); // Создать None State и кинуть эту штуку туда animator.Update(0f); _entityHitBoxes.DisableAllHitboxes(); // TODO С нормальными анимациями не нужен будет рут моушен animator.transform.localPosition = Vector3.zero; animator.transform.localRotation = Quaternion.identity; Finished = true; } // HoloCopy private bool TickHoloCopy(HoloCop holoCop, float tickDelta) { if (_currentFrame == 0) { holoCop.Animator.Play(_attackData.StateHash, 0, 0f); } _currentFrame++; if(_currentFrame >= _attackData.TotalFrames) { _currentFrame = 0; return true; } return false; } } }