context: using System;
  using System.Numerics;
  
  using ACE.Entity.Enum;
  using ACE.Server.Entity.Actions;
  using ACE.Server.Network.GameEvent.Events;
  using ACE.Server.Network.GameMessages.Messages;
  using ACE.Server.Physics.Animation;
  
  namespace ACE.Server.WorldObjects
  {
      partial class Player
      {
          private float _accuracyLevel;
  
          public float AccuracyLevel
          {
              get => IsExhausted ? 0.0f : _accuracyLevel;
              set => _accuracyLevel = value;
          }
  
          public Creature MissileTarget;
  
          public PowerAccuracy GetAccuracyRange()
          {
              if (AccuracyLevel < 0.33f)
                  return PowerAccuracy.Low;
              else if (AccuracyLevel < 0.66f)
                  return PowerAccuracy.Medium;
              else
                  return PowerAccuracy.High;
          }
  
          /// <summary>
          /// Called by network packet handler 0xA - GameActionTargetedMissileAttack
          /// </summary>
          /// <param name="targetGuid">The target guid</param>
          /// <param name="attackHeight">The attack height 1-3</param>
          /// <param name="accuracyLevel">The 0-1 accuracy bar level</param>
          public void HandleActionTargetedMissileAttack(uint targetGuid, uint attackHeight, float accuracyLevel)
          {
              //log.Info($"-");
  
              if (CombatMode != CombatMode.Missile)
              {
                  log.Error($"{Name}.HandleActionTargetedMissileAttack({targetGuid:X8}, {attackHeight}, {accuracyLevel}) - CombatMode mismatch {CombatMode}, LastCombatMode: {LastCombatMode}");
  
                  if (LastCombatMode == CombatMode.Missile)
                      CombatMode = CombatMode.Missile;
                  else
                  {
                      OnAttackDone();
                      return;
                  }
              }
  
              if (IsBusy || Teleporting || suicideInProgress)
              {
                  SendWeenieError(WeenieError.YoureTooBusy);
                  OnAttackDone();
                  return;
              }
  
              if (IsJumping)
              {
                  SendWeenieError(WeenieError.YouCantDoThatWhileInTheAir);
                  OnAttackDone();
                  return;
              }
  
              if (PKLogout)
              {
                  SendWeenieError(WeenieError.YouHaveBeenInPKBattleTooRecently);
                  OnAttackDone();
                  return;
              }
  
              var weapon = GetEquippedMissileWeapon();
              var ammo = GetEquippedAmmo();
  
              // sanity check
              accuracyLevel = Math.Clamp(accuracyLevel, 0.0f, 1.0f);
  
              if (weapon == null || weapon.IsAmmoLauncher && ammo == null)
              {
                  OnAttackDone();
                  return;
              }
  
              AttackHeight = (AttackHeight)attackHeight;
              AttackQueue.Add(accuracyLevel);
  
              if (MissileTarget == null)
                  AccuracyLevel = accuracyLevel;  // verify
  
              // get world object of target guid
              var target = CurrentLandblock?.GetObject(targetGuid) as Creature;
              if (target == null || target.Teleporting)
              {
                  //log.Warn($"{Name}.HandleActionTargetedMissileAttack({targetGuid:X8}, {AttackHeight}, {accuracyLevel}) - couldn't find creature target guid");
                  OnAttackDone();
                  return;
              }
  
              if (Attacking || MissileTarget != null && MissileTarget.IsAlive)
                  return;
  
              if (!CanDamage(target))
              {
                  SendTransientError($"You cannot attack {target.Name}");
                  OnAttackDone();
                  return;
              }
  
              //log.Info($"{Name}.HandleActionTargetedMissileAttack({targetGuid:X8}, {attackHeight}, {accuracyLevel})");
  
              AttackTarget = target;
              MissileTarget = target;
  
              var attackSequence = ++AttackSequence;
  
              // record stance here and pass it along
              // accounts for odd client behavior with swapping bows during repeat attacks
              var stance = CurrentMotionState.Stance;
  
              // turn if required
              var rotateTime = Rotate(target);
              var actionChain = new ActionChain();
  
              var delayTime = rotateTime;
              if (NextRefillTime > DateTime.UtcNow.AddSeconds(delayTime))
                  delayTime = (float)(NextRefillTime - DateTime.UtcNow).TotalSeconds;
  
              actionChain.AddDelaySeconds(delayTime);
  
              // do missile attack
              actionChain.AddAction(this, () => LaunchMissile(target, attackSequence, stance));
              actionChain.EnqueueChain();
          }
  
          /// <summary>
          /// Launches a missile attack from player to target
          /// </summary>
          public void LaunchMissile(WorldObject target, int attackSequence, MotionStance stance, bool subsequent = false)
          {
              if (AttackSequence != attackSequence)
                  return;
  
              var weapon = GetEquippedMissileWeapon();
              if (weapon == null || CombatMode == CombatMode.NonCombat)
              {
                  OnAttackDone();
                  return;
              }
  
              var ammo = weapon.IsAmmoLauncher ? GetEquippedAmmo() : weapon;
              if (ammo == null)
              {
                  OnAttackDone();
                  return;
              }
  
              var launcher = GetEquippedMissileLauncher();
  
              var creature = target as Creature;
              if (!IsAlive || IsBusy || MissileTarget == null || creature == null || !creature.IsAlive || suicideInProgress)
              {
                  OnAttackDone();
                  return;
              }
  
              if (!TargetInRange(target))
              {
                  // this must also be sent to actually display the transient message
                  SendWeenieError(WeenieError.MissileOutOfRange);
  
                  // this prevents the accuracy bar from refilling when 'repeat attacks' is enabled
                  OnAttackDone();
  
                  return;
              }
  
              var actionChain = new ActionChain();
  
              if (subsequent && !IsFacing(target))
              {
                  var rotateTime = Rotate(target);
                  actionChain.AddDelaySeconds(rotateTime);
              }
  
              // launch animation
              // point of no return beyond this point -- cannot be cancelled
              actionChain.AddAction(this, () => Attacking = true);
  
              if (subsequent)
              {
                  // client shows hourglass, until attack done is received
                  // retail only did this for subsequent attacks w/ repeat attacks on
                  Session.Network.EnqueueSend(new GameEventCombatCommenceAttack(Session));
              }
  
              var projectileSpeed = GetProjectileSpeed();
  
              // get z-angle for aim motion
              var aimVelocity = GetAimVelocity(target, projectileSpeed);
  
              var aimLevel = GetAimLevel(aimVelocity);
  
              // calculate projectile spawn pos and velocity
              var localOrigin = GetProjectileSpawnOrigin(ammo.WeenieClassId, aimLevel);
  
              var velocity = CalculateProjectileVelocity(localOrigin, target, projectileSpeed, out Vector3 origin, out Quaternion orientation);
  
              //Console.WriteLine($"Velocity: {velocity}");
  
  
gt:             if (velocity == Vector3.Zero)