public static FarPosition GetSubCellCoordinatesFromId(ulong id) { int x, y; BitwiseMagic.Unpack(id, out x, out y); return(new FarPosition(x << SubCellSizeShiftAmount, y << SubCellSizeShiftAmount)); }
/// <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)); } } }
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(); }
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); //} } } }
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))); }
/// <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)); } } }
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(); }
/// <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)); } }
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(); }
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); } }
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); } } } } } } }