/// <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);
        }
        protected override void OnUpdate()
        {
            if (CheckTime(ref inter) == false)
            {
                return;
            }

            Entities.With(group).ForEach((Entity entity,
                                          ref BoidComponent.Component boid,
                                          ref BaseUnitStatus.Component status,
                                          ref BaseUnitTarget.Component target,
                                          ref CommanderStatus.Component commander,
                                          ref BaseUnitMovement.Component movement,
                                          ref SpatialEntityId entityId) =>
            {
                if (status.State != UnitState.Alive)
                {
                    return;
                }

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

                var side          = status.Side;
                var speed         = movement.MoveSpeed;
                var forward       = trans.forward;
                var commanderRank = status.Rank;

                var soldierRange = RangeDictionary.BaseBoidsRange;
                var soldiers     = getAllyUnits(side, pos, soldierRange, allowDead: false, selfId: entityId.EntityId, GetSingleUnitTypes(UnitType.Soldier));

                float f_length = boid.ForwardLength;
                boid_calculate(pos + forward * f_length, pos, soldierRange, forward * speed,
                               boid.SepareteWeight, boid.AlignmentWeight, boid.CohesionWeight, soldiers);

                if (commanderRank < 1)
                {
                    return;
                }

                var commanderRange = RangeDictionary.GetBoidsRange(commanderRank);
                var coms           = getAllyUnits(side, pos, commanderRange, allowDead: false, selfId: entityId.EntityId, GetSingleUnitTypes(UnitType.Commander));
                commanders.Clear();
                foreach (var c in coms)
                {
                    if (c.rank == commanderRank - 1)
                    {
                        commanders.Add(c);
                    }
                }

                f_length  = AttackLogicDictionary.RankScaled(f_length, commanderRank);
                f_length += commander.AllyRange;

                boid_calculate(pos + forward * f_length, pos, commanderRange, forward * speed,
                               boid.SepareteWeight, boid.AlignmentWeight, boid.CohesionWeight, commanders);
            });
        }
        public override void Initialize()
        {
            Instance = this;

            foreach (var o in orderRates)
            {
                orderDic[o.order] = o;
            }
        }
        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;
        }
        private void boid_calculate(Vector3 center, Vector3 pos, float range, Vector3 centerMove,
                                    float separeteWeight, float alignmentWeight, float cohesionWeight, List <UnitInfo> allies)
        {
            var rate            = range / RangeDictionary.BaseBoidsRange;
            var alignmentVector = centerMove.normalized;
            var centerMoveSpeed = centerMove.magnitude;

            floatInfoDic.Clear();
            float totalLength = 1.0f;

            foreach (var unit in allies)
            {
                if (TryGetComponentObject <Transform>(unit.id, out var t) == false)
                {
                    continue;
                }

                if (TryGetComponent <BaseUnitMovement.Component>(unit.id, out var move) == false)
                {
                    continue;
                }

                var length = Mathf.Max((pos - unit.pos).magnitude, 1.0f);
                floatInfoDic[unit.id] = new ValueTuple <float, float>(move.Value.MoveSpeed, length);

                // scaled balance
                alignmentVector += t.forward * 1.0f / length;

                totalLength += 1.0f / length;
            }

            alignmentVector /= totalLength;

            foreach (var unit in allies)
            {
                if (TryGetComponent <BaseUnitSight.Component>(unit.id, out var sight) == false)
                {
                    continue;
                }

                if (floatInfoDic.TryGetValue(unit.id, out var info) == false)
                {
                    continue;
                }

                var selfSpeed  = info.Item1;
                var length     = info.Item2;
                var boidVector = sight.Value.BoidVector;
                var baseVec    = boidVector.Vector.ToUnityVector();
                var boidVec    = Vector3.zero;

                var inter = RangeDictionary.UnitInter;

                var potential = AttackLogicDictionary.BoidPotential(1, length, range);
                if (potential <= boidVector.Potential)
                {
                    continue;
                }

                var scaledLength = length / rate;
                var syncSpeed    = (centerMoveSpeed * 1.0f + selfSpeed * scaledLength) / (scaledLength + 1.0f);

                foreach (var other in allies)
                {
                    if (other.id == unit.id)
                    {
                        continue;
                    }

                    var sep = (unit.pos - other.pos) / rate;
                    boidVec += sep;
                }

                // length scaled weight
                separeteWeight *= 1.0f / scaledLength;
                cohesionWeight *= scaledLength / 1.0f;

                boidVec  = (boidVec / allies.Count) * separeteWeight;
                boidVec += alignmentVector * alignmentWeight;
                boidVec += ((center - unit.pos) / rate) * cohesionWeight;

                boidVec = boidVec.normalized * syncSpeed * 10.0f;

                var diffVec    = boidVec - baseVec;
                var diffCenter = center - sight.Value.BoidVector.Center.ToUnityVector();
                if (diffVec.sqrMagnitude < diffMinVec && diffCenter.sqrMagnitude < diffMinPos)
                {
                    continue;
                }

                boidVector = new BoidVector(boidVec.ToFixedPointVector3(), center.ToWorldPosition(this.Origin), range, potential);
                this.UpdateSystem.SendEvent(new BaseUnitSight.BoidDiffed.Event(boidVector), unit.id);
            }
        }