/// <summary>
        /// calculate target and update the boid info;
        /// </summary>
        /// <param name="sight"></param>
        /// <param name="targetState"></param>
        /// <param name="pos"></param>
        /// <returns></returns>
        Vector3?calc_update_boid(ref BaseUnitSight.Component sight, TargetState targetState, Vector3 pos)
        {
            Vector3?tgt        = null;
            var     boidVector = sight.BoidVector;

            if (boidVector.Potential > 0.0f)
            {
                var center = boidVector.Center.ToWorkerPosition(this.Origin);

                if ((center - pos).sqrMagnitude > boidVector.SqrtBoidRadius())
                {
                    tgt = center;
                }
                else if (targetState == TargetState.OutOfRange)
                {
                    tgt = pos + boidVector.GetVector3(sight.TargetRange);
                }
            }

            var current  = Time.ElapsedTime;
            var diffTime = (float)(current - sight.BoidUpdateTime);

            boidVector.Potential = AttackLogicDictionary.ReduceBoidPotential(boidVector.Potential, diffTime);
            sight.BoidUpdateTime = current;
            sight.BoidVector     = boidVector;

            return(tgt);
        }
        private bool SetStrategyTarget(Vector3 pos, float backBuffer, ref BaseUnitSight.Component sight, ref BaseUnitTarget.Component target)
        {
            bool isTarget = false;

            switch (target.Type)
            {
            case TargetType.Unit:
                if (target.TargetUnit.IsValid())
                {
                    sight.TargetPosition = target.TargetUnit.Position.ToFixedPointVector3();
                    sight.TargetSize     = target.TargetUnit.Size;
                    target.State         = TargetState.MovementTarget;
                    isTarget             = true;
                }
                else
                {
                    Debug.LogError("FrontLineInfo is InValid");
                }
                break;

            case TargetType.FrontLine:
                if (target.FrontLine.IsValid())
                {
                    sight.TargetPosition = target.FrontLine.GetOnLinePosition(this.Origin, pos, -backBuffer).ToWorldPosition(this.Origin);
                    sight.TargetSize     = 0.0f;
                    target.State         = TargetState.MovementTarget;
                    isTarget             = true;
                }
                else
                {
                    Debug.LogError("FrontLineInfo is InValid");
                }
                break;

            case TargetType.Hex:
                if (target.HexInfo.IsValid())
                {
                    sight.TargetPosition = HexUtils.GetHexCenter(this.Origin, target.HexInfo.HexIndex, HexDictionary.HexEdgeLength).ToWorldPosition(this.Origin);
                    sight.TargetSize     = HexDictionary.HexTargetRadius;
                    target.State         = TargetState.MovementTarget;
                    isTarget             = true;
                }
                else
                {
                    Debug.LogError("HexInfo is InValid");
                }
                break;
            }

            return(isTarget);
        }
        private void MovementQuery(Entity entity,
                                   ref MovementData movement,
                                   ref NavPathData path,
                                   ref BaseUnitSight.Component sight,
                                   ref BaseUnitStatus.Component status,
                                   ref SpatialEntityId entityId)
        {
            movement.MoveSpeed = 0.0f;
            movement.RotSpeed  = 0.0f;

            if (status.State != UnitState.Alive)
            {
                return;
            }

            if (UnitUtils.IsAutomaticallyMoving(status.Type) == false)
            {
                return;
            }

            var unit = EntityManager.GetComponentObject <UnitTransform>(entity);

            // check ground
            if (unit == null || unit.GetGrounded(out var hitInfo) == false)
            {
                return;
            }

            if (sight.State == TargetState.None)
            {
                return;
            }

            var trans = unit.transform;
            var pos   = trans.position;

            Vector3?tgt    = null;
            Vector3 spread = Vector3.zero;

            var id = entityId.EntityId;

            if (vectorDic.ContainsKey(id))
            {
                //tgt = vectorDic[id].boidTarget;
                spread = vectorDic[id].spread;
            }

            if (tgt == null)
            {
                tgt = sight.GetTargetPosition(this.Origin, pos);
            }

            tgt = CheckNavPathAndTarget(tgt.Value, pos, unit.SizeRadius, sight.State, entityId.EntityId.Id, ref path);

            if (RangeDictionary.IsSpreadValid(spread))
            {
                var length = (tgt.Value - pos).magnitude;
                tgt += spread * Mathf.Max(1.0f, (length / RangeDictionary.SpreadSize));
            }

            var positionDiff = tgt.Value - pos;

            var forward = get_forward(positionDiff, sight.TargetRange);

            MovementDictionary.TryGet(status.Type, out var speed, out var rot);

            var isRotate = rotate(rot, trans, positionDiff);

            if (forward != 0.0f)
            {
                movement.MoveSpeed = forward * speed;
            }

            if (isRotate != 0)
            {
                movement.RotSpeed = rot * isRotate;
            }
        }
        private void Query(Entity entity,
                           ref BaseUnitSight.Component sight,
                           ref UnitActionData action,
                           ref BaseUnitStatus.Component status,
                           ref BaseUnitTarget.Component target,
                           ref SpatialEntityId entityId)
        {
            if (status.State != UnitState.Alive)
            {
                return;
            }

            if (status.Order == OrderType.Idle)
            {
                return;
            }

            if (UnitUtils.IsWatcher(status.Type) == false)
            {
                return;
            }

            // initial
            target.State = TargetState.None;

            var id = entityId.EntityId.Id;

            if (enemyPositionsContainer.ContainsKey(id) == false)
            {
                enemyPositionsContainer[id] = new List <FixedPointVector3>();
            }

            var enemyPositions = enemyPositionsContainer[id];

            enemyPositions.Clear();

            var trans = EntityManager.GetComponentObject <Transform>(entity);
            var pos   = trans.position;

            UnitInfo enemy      = null;
            var      sightRange = action.SightRange;

            var backBuffer = sightRange / 2;

            if (status.Type == UnitType.Commander)
            {
                backBuffer += RangeDictionary.AllyRange / 2;
            }

            // strategy target
            SetStrategyTarget(pos, backBuffer, ref sight, ref target);

            // keep logic
            if (status.Order == OrderType.Keep)
            {
                sightRange *= target.PowerRate;
            }

            enemy = getNearestEnemy(status.Side, pos, sightRange);

            if (enemy != null)
            {
                var tgtPos = sight.TargetPosition.ToWorkerPosition(this.Origin);
                var epos   = enemy.pos.ToWorldPosition(this.Origin);

                if (Vector3.Dot(tgtPos - pos, enemy.pos - pos) > 0)
                {
                    target.State         = TargetState.ActionTarget;
                    sight.TargetPosition = epos;
                    sight.TargetSize     = enemy.size;
                }

                enemyPositions.Add(epos);
            }

            float range;

            switch (target.State)
            {
            case TargetState.ActionTarget:
                range = action.AttackRange;
                range = AttackLogicDictionary.GetOrderRange(status.Order, range) * target.PowerRate;
                break;

            default:
                range = RangeDictionary.BodySize;
                break;
            }

            // set behind
            if (status.Type == UnitType.Commander)
            {
                var addRange = RangeDictionary.AllyRange / 2;
                range += AttackLogicDictionary.RankScaled(addRange, status.Rank);
            }

            sight.TargetRange = range;
            sight.State       = target.State;
        }