Ejemplo n.º 1
0
        public static FarPosition GetSubCellCoordinatesFromId(ulong id)
        {
            int x, y;

            BitwiseMagic.Unpack(id, out x, out y);
            return(new FarPosition(x << SubCellSizeShiftAmount, y << SubCellSizeShiftAmount));
        }
Ejemplo n.º 2
0
        /// <summary>Computes the cells the specified rectangle falls into.</summary>
        /// <param name="rectangle">The rectangle.</param>
        /// <returns>The cells the rectangle intersects with.</returns>
        private static IEnumerable <Tuple <ulong, TPoint> > ComputeCells(TRectangle rectangle)
        {
#if FALSE && FARMATH // Only works when automatically normalizing.
            var left   = rectangle.X.Segment;
            var right  = rectangle.Right.Segment;
            var top    = rectangle.Y.Segment;
            var bottom = rectangle.Bottom.Segment;
#elif FARMATH // TODO make sure this works
            var left   = rectangle.X.Segment + (int)(rectangle.X.Offset / FarValue.SegmentSizeHalf);
            var right  = rectangle.Right.Segment + (int)(rectangle.Right.Offset / FarValue.SegmentSizeHalf);
            var top    = rectangle.Y.Segment + (int)(rectangle.Y.Offset / FarValue.SegmentSizeHalf);
            var bottom = rectangle.Bottom.Segment + (int)(rectangle.Bottom.Offset / FarValue.SegmentSizeHalf);
#else
            var left   = (int)(rectangle.X / CellSize);
            var right  = (int)(rectangle.Right / CellSize);
            var top    = (int)(rectangle.Y / CellSize);
            var bottom = (int)(rectangle.Bottom / CellSize);
#endif

            TPoint center;
            for (var x = left; x <= right; x++)
            {
                center.X = -x * CellSize;
                for (var y = top; y <= bottom; y++)
                {
                    center.Y = -y * CellSize;
                    yield return(Tuple.Create(BitwiseMagic.Pack(x, y), center));
                }
            }
        }
Ejemplo n.º 3
0
        public void OnUpdate(Update message)
        {
            foreach (var info in _newCollisions)
            {
                // Check if we already have contact between the two entities.
                ActiveCollisionInfo active;
                if (_activeCollisions.TryGetValue(info.Key, out active))
                {
                    // Yes, just update the number of fixture contacts.
                    active.Count += info.Value.Count;
                }
                else
                {
                    // Nothing yet, save the contact and apply effects.
                    if (info.Value.Count > 0)
                    {
                        active = new ActiveCollisionInfo {
                            Count = info.Value.Count
                        };
                        _activeCollisions.Add(info.Key, active);
                    }

                    int entityA, entityB;
                    BitwiseMagic.Unpack(info.Key, out entityA, out entityB);

                    OnEntityContact(entityA, entityB, info.Value.IsShieldA, info.Value.Normal);
                    OnEntityContact(entityB, entityA, info.Value.IsShieldB, -info.Value.Normal);
                }
            }
            _newCollisions.Clear();
        }
Ejemplo n.º 4
0
        public void OnEndContact(EndContact message)
        {
            // Stop damage that is being applied because of this collision.
            var contact = message.Contact;

            // Get the two related entities.
            var entityA = Math.Min(contact.FixtureA.Entity, contact.FixtureB.Entity);
            var entityB = Math.Max(contact.FixtureB.Entity, contact.FixtureB.Entity);

            // See if we're tracking such a collision.
            var key = BitwiseMagic.Pack(entityA, entityB);
            ActiveCollisionInfo active;

            if (_activeCollisions.TryGetValue(key, out active))
            {
                // One less fixture contact.
                --active.Count;
                if (active.Count == 0)
                {
                    // That was the last one, stop tracking this and remove any "during contact" effects.
                    _activeCollisions.Remove(key);

                    // TODO figure out a nice way to let contact based debuffs know they can stop (radiation, ...)
                    //int effectId;
                    //if (_collisions.TryGetValue(BitwiseMagic.Pack(damagee, damager), out effectId))
                    //{
                    //    Manager.RemoveComponent(effectId);
                    //}
                }
            }
        }
Ejemplo n.º 5
0
        public static ulong GetSubCellIdFromCoordinates(FarPosition position)
        {
            const float segmentFactor = (float)FarValue.SegmentSize / (float)SubCellSize;
            const float offsetFactor  = 1f / (float)FarValue.SegmentSize * segmentFactor; // = 1f / (float) SubCellSize

            return(BitwiseMagic.Pack(
                       (int)Math.Floor(position.X.Segment * segmentFactor + position.X.Offset * offsetFactor),
                       (int)Math.Floor(position.Y.Segment * segmentFactor + position.Y.Offset * offsetFactor)));
        }
Ejemplo n.º 6
0
 /// <summary>
 ///     Adds the combined coordinates for all neighboring cells and the specified cell itself to the specified set of
 ///     cells.
 /// </summary>
 /// <param name="x">The x coordinate of the main cell.</param>
 /// <param name="y">The y coordinate of the main cell.</param>
 /// <param name="cells">The set of cells to add to.</param>
 private static void AddCellAndNeighbors(int x, int y, ISet <ulong> cells)
 {
     for (var ny = y - 1; ny <= y + 1; ny++)
     {
         for (var nx = x - 1; nx <= x + 1; nx++)
         {
             cells.Add(BitwiseMagic.Pack(nx, ny));
         }
     }
 }
Ejemplo n.º 7
0
        public void OnDraw(Draw message)
        {
            if (!Enabled)
            {
                return;
            }

            var camera = (CameraSystem)Manager.GetSystem(CameraSystem.TypeId);

            // Get camera transform.
            var cameraTransform   = camera.Transform;
            var cameraTranslation = camera.Translation;
            var interpolation     = (InterpolationSystem)Manager.GetSystem(InterpolationSystem.TypeId);

            // Iterate over all visible entities.
            _spriteBatch.Begin(
                SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, cameraTransform);
            foreach (var entity in camera.VisibleEntities)
            {
                var transform = (ITransform)Manager.GetComponent(entity, TransformTypeId);
                if (transform != null)
                {
                    int x, y, subX, subY;
                    BitwiseMagic.Unpack(CellSystem.GetCellIdFromCoordinates(transform.Position), out x, out y);
                    BitwiseMagic.Unpack(CellSystem.GetSubCellIdFromCoordinates(transform.Position), out subX, out subY);
                    var text = string.Format(
                        "ID: {0} @ {1} / {2}\nCell: {3}:{4}, SubCell: {5}:{6}",
                        transform.Entity,
                        transform.Position,
                        transform.Angle,
                        x,
                        y,
                        subX,
                        subY);

                    FarPosition position;
                    float       angle;
                    interpolation.GetInterpolatedTransform(transform.Entity, out position, out angle);
                    position = FarUnitConversion.ToScreenUnits(position + cameraTranslation);
                    _spriteBatch.DrawString(
                        _font,
                        text,
                        (Vector2)position,
                        Color.White,
                        0,
                        Vector2.Zero,
                        1f / camera.CameraZoom,
                        SpriteEffects.None,
                        1);
                }
            }
            _spriteBatch.End();
        }
Ejemplo n.º 8
0
        /// <summary>
        ///     A utility enumerator allowing the iteration over all trees in the index. This also yields the position for
        ///     each tree.
        /// </summary>
        /// <returns>An enumerator over all trees in the index.</returns>
        /// <remarks>This is mainly intended for debugging purposes, to allow rendering the tree bounds.</remarks>
        public IEnumerable <Tuple <TPoint, Collections.DynamicQuadTree <T> > > GetTreeEnumerable()
        {
            foreach (var entry in _cells)
            {
                int segmentX, segmentY;
                BitwiseMagic.Unpack(entry.Key, out segmentX, out segmentY);

                TPoint center;
                center.X = segmentX * CellSize;
                center.Y = segmentY * CellSize;
                yield return(Tuple.Create(center, entry.Value));
            }
        }
Ejemplo n.º 9
0
        private void UpdateCellState(long frame, bool isSubCell, int size, ISet <ulong> living, IDictionary <ulong, long> pending)
        {
            // Check the positions of all avatars to check which cells
            // should live, and which should die / stay dead.
            var avatarSystem = (AvatarSystem)Manager.GetSystem(AvatarSystem.TypeId);

            foreach (var avatar in avatarSystem.Avatars)
            {
                var transform = (ITransform)Manager.GetComponent(avatar, TransformTypeId);
                int x, y;
                if (isSubCell)
                {
                    BitwiseMagic.Unpack(GetSubCellIdFromCoordinates(transform.Position), out x, out y);
                }
                else
                {
                    BitwiseMagic.Unpack(GetCellIdFromCoordinates(transform.Position), out x, out y);
                }
                AddCellAndNeighbors(x, y, _reusableNewCellIds);
            }

            // Get the cells that became alive, notify systems and components.
            _reusableBornCellsIds.UnionWith(_reusableNewCellIds);
            _reusableBornCellsIds.ExceptWith(living);
            CellStateChanged changedMessage;

            changedMessage.IsSubCell = isSubCell;
            foreach (var cellId in _reusableBornCellsIds)
            {
                // If its in there, remove it from the pending list.
                if (!pending.Remove(cellId))
                {
                    // Notify if cell wasn't alive already.
                    changedMessage.Id = cellId;
                    BitwiseMagic.Unpack(cellId, out changedMessage.X, out changedMessage.Y);
                    changedMessage.IsActive = true;
                    Manager.SendMessage(changedMessage);
                }
            }
            _reusableBornCellsIds.Clear();

            // Check pending list, kill off old cells, notify systems etc.
            var cellDeathIndex = ((IndexSystem)Manager.GetSystem(IndexSystem.TypeId))[CellDeathAutoRemoveIndexId];

            _reusablePendingList.AddRange(pending.Keys);
            foreach (var cellId in _reusablePendingList)
            {
                // Are we still delaying?
                if (frame - pending[cellId] <= CellDeathDelay)
                {
                    continue;
                }

                // Timed out, kill it for good.
                pending.Remove(cellId);

                int x, y;
                BitwiseMagic.Unpack(cellId, out x, out y);

                // Notify.
                changedMessage.Id       = cellId;
                changedMessage.X        = x;
                changedMessage.Y        = y;
                changedMessage.IsActive = false;
                Manager.SendMessage(changedMessage);

                // Kill any remaining entities in the area covered by the
                // cell that just died.
                FarRectangle cellBounds;
                cellBounds.X      = x * size;
                cellBounds.Y      = y * size;
                cellBounds.Width  = size;
                cellBounds.Height = size;
                cellDeathIndex.Find(cellBounds, _reusableComponentList);
                foreach (IIndexable neighbor in _reusableComponentList.Select(Manager.GetComponentById))
                {
                    var cellDeath = (CellDeath)Manager.GetComponent(neighbor.Entity, CellDeath.TypeId);
                    if (cellDeath.IsForSubCell == isSubCell)
                    {
                        Manager.RemoveEntity(neighbor.Entity);
                    }
                }
                _reusableComponentList.Clear();
            }
            _reusablePendingList.Clear();

            // Get the cells that died, put to pending list.
            _reusableDeceasedCellsIds.UnionWith(living);
            _reusableDeceasedCellsIds.ExceptWith(_reusableNewCellIds);
            foreach (var cellId in _reusableDeceasedCellsIds)
            {
                // Add it to the pending list.
                pending.Add(cellId, frame);
            }
            _reusableDeceasedCellsIds.Clear();

            living.Clear();
            living.UnionWith(_reusableNewCellIds);

            _reusableNewCellIds.Clear();
        }
Ejemplo n.º 10
0
        public void OnBeginContact(BeginContact message)
        {
            // We only get one message for a collision pair, so we handle it for both parties.
            var contact = message.Contact;

            // Get the two involved entities. We only handle one collision between
            // two bodies per tick, to avoid damage being applied multiple times.
            var entityA = Math.Min(contact.FixtureA.Entity, contact.FixtureB.Entity);
            var entityB = Math.Max(contact.FixtureA.Entity, contact.FixtureB.Entity);

            // See if either one does some damage and/or should be removed.
            var damageA = (CollisionDamage)Manager.GetComponent(entityA, CollisionDamage.TypeId);
            var damageB = (CollisionDamage)Manager.GetComponent(entityB, CollisionDamage.TypeId);

            var healthA = (Health)Manager.GetComponent(entityA, Health.TypeId);
            var healthB = (Health)Manager.GetComponent(entityB, Health.TypeId);

            // See if the hit entity should be removed on collision.
            var persistent = true;

            if (damageA != null && damageA.RemoveOnCollision)
            {
                // Entity gets removed, so forget about handling the collision physically.
                ((DeathSystem)Manager.GetSystem(DeathSystem.TypeId)).MarkForRemoval(entityA);
                contact.Disable();
                persistent = false;
            }
            if (damageB != null && damageB.RemoveOnCollision)
            {
                // Entity gets removed, so forget about handling the collision physically.
                ((DeathSystem)Manager.GetSystem(DeathSystem.TypeId)).MarkForRemoval(entityB);
                contact.Disable();
                persistent = false;
            }

            // Ignore contacts where no damage is involved at all.
            if ((damageA == null || healthB == null) &&
                (damageB == null || healthA == null))
            {
                return;
            }

            // See if we already know something about this collision.
            var key = BitwiseMagic.Pack(entityA, entityB);
            NewCollisionInfo info;

            if (!_newCollisions.TryGetValue(key, out info))
            {
                info = new NewCollisionInfo();
                _newCollisions.Add(key, info);
            }

            // Track the number of persistent contacts. This is necessary for damage "fields",
            // such as radiation in a certain area.
            if (persistent)
            {
                ++info.Count;
            }

            // See if the shield was hit.
            var shielded = false;
            var shieldA  = Manager.GetComponent(entityA, ShieldEnergyStatusEffect.TypeId) as ShieldEnergyStatusEffect;

            if (shieldA != null && (contact.FixtureA.Id == shieldA.Fixture || contact.FixtureB.Id == shieldA.Fixture))
            {
                info.IsShieldA = true;
                shielded       = true;
            }
            var shieldB = Manager.GetComponent(entityB, ShieldEnergyStatusEffect.TypeId) as ShieldEnergyStatusEffect;

            if (shieldB != null && (contact.FixtureA.Id == shieldB.Fixture || contact.FixtureA.Id == shieldB.Fixture))
            {
                info.IsShieldB = true;
                shielded       = true;
            }

            // For at least one of the involved entities a shield was hit, so get the normal.
            // Note that this can potentially lead to 'wrong' results, if an entity is hit by
            // multiple fixtures in the same frame. This is a problematic case, logically speaking:
            // what to do if this happens?
            if (shielded)
            {
                IList <FarPosition> points;
                contact.ComputeWorldManifold(out info.Normal, out points);
            }
        }
Ejemplo n.º 11
0
        private void PopulateCell(CellStateChanged message, IUniformRandom random)
        {
            // Check if we have a changed cell info.
            if (!_cellInfo.ContainsKey(message.Id))
            {
                // No, generate the default one. Get an independent
                // randomizer to avoid different results in other
                // sampling operations from when we have an existing
                // cell info.
                var hasher = new Hasher();
                hasher.Write(message.Id).Write(WorldSeed);
                var independentRandom = new MersenneTwister(hasher.Value);

                // Figure out which faction should own this cell.
                Factions faction;
                switch (independentRandom.NextInt32(3))
                {
                case 0:
                    faction = Factions.NPCFactionA;
                    break;

                case 1:
                    faction = Factions.NPCFactionB;
                    break;

                default:
                    faction = Factions.NPCFactionC;
                    break;
                }

                // Figure out our tech level.
                // TODO make dependent on distance to center / start system.
                var techLevel = independentRandom.NextInt32(3);

                // Create the cell and push it.
                _cellInfo.Add(message.Id, new CellInfo(faction, techLevel));
            }

            // Get center of our cell.
            const int cellSize = CellSystem.CellSize;
            var       center   = new FarPosition(
                cellSize * message.X + (cellSize >> 1),
                cellSize * message.Y + (cellSize >> 1));

            // Check if it's the start system or not.
            if (message.X == 0 && message.Y == 0)
            {
                // It is, use a predefined number of planets and moons,
                // and make sure it's a solar system.
                FactoryLibrary.SampleSunSystem(Manager, "solar_system", center, random);
            }
            else
            {
                // It isn't, randomize.
                FactoryLibrary.SampleSunSystem(Manager, "sunsystem_1", center, random);
            }

            // Find nearby active cells and the stations in them, mark
            // them as possible targets for all station sin this cell,
            // and let them know about our existence, as well.
            var cellSystem = (CellSystem)Manager.GetSystem(CellSystem.TypeId);
            var stations   = _cellInfo[message.Id].Stations;

            for (var ny = message.Y - 1; ny <= message.Y + 1; ny++)
            {
                for (var nx = message.X - 1; nx <= message.X + 1; nx++)
                {
                    // Don't fly to cells that are diagonal to
                    // ourselves, which we do by checking if the
                    // sum of the coordinates is uneven.
                    // This becomes more obvious when considering this:
                    // +-+-+-+-+
                    // |0|1|0|1|
                    // +-+-+-+-+
                    // |1|0|1|0|
                    // +-+-+-+-+
                    // |0|1|0|1|
                    // +-+-+-+-+
                    // |1|0|1|0|
                    // +-+-+-+-+
                    // Where 0 means the sum of the own coordinate is
                    // even, 1 means it is odd. Then we can see that
                    // the sum of diagonal pairs of cells is always
                    // even, and the one of straight neighbors is
                    // always odd.
                    if (((message.X + message.Y + ny + nx) & 1) == 0)
                    {
                        // Get the id, only mark the station if we have
                        // info on it and it's an enemy cell.
                        var id = BitwiseMagic.Pack(nx, ny);
                        if (cellSystem.IsCellActive(id) &&
                            _cellInfo.ContainsKey(id) &&
                            (_cellInfo[id].Faction.IsAlliedTo(_cellInfo[message.Id].Faction)))
                        {
                            // Tell the other stations.
                            foreach (var stationId in _cellInfo[id].Stations)
                            {
                                var spawn = ((ShipSpawner)Manager.GetComponent(stationId, ShipSpawner.TypeId));
                                foreach (var otherStationId in stations)
                                {
                                    spawn.Targets.Add(otherStationId);
                                }
                            }
                            // Tell our own stations.
                            foreach (var stationId in stations)
                            {
                                var spawner = ((ShipSpawner)Manager.GetComponent(stationId, ShipSpawner.TypeId));
                                foreach (var otherStationId in _cellInfo[id].Stations)
                                {
                                    spawner.Targets.Add(otherStationId);
                                }
                            }
                        }
                    }
                }
            }
        }