void SetCellStateNow(Point p, bool state) { Point tp = ConnectedEdgesPosition(p); if (tp != Point.OutOfBounds) { if (state != GetCellState(tp)) { if (state) { cellsAlive++; } else { cellsAlive--; } cells[tp.x, tp.y] = state; CellStateChanged?.Invoke(tp); } } }
public void OnCellStateChanged(CellStateChanged message) { // Get random generator based on world seed and cell location. var hasher = new Hasher(); hasher.Write(message.Id).Write(WorldSeed); var random = new MersenneTwister(hasher.Value); if (message.IsSubCell) { if (message.IsActive) { PopulateSubCell(message, random); } } else { if (message.IsActive) { PopulateCell(message, random); } else { // Remove cell info only if it does not deviate from the // procedural values. if (!_cellInfo[message.Id].Dirty) { _cellInfo.Remove(message.Id); } else { _cellInfo[message.Id].Stations.Clear(); } } } }
public void OnCellStateChanged(CellStateChanged message) { if (!message.IsSubCell) { return; } if (!message.IsActive) { _cellSpawns.RemoveAll(x => x.Item1 == message.Id); } else { _cellSpawns.Add(Tuple.Create(message.Id, 5)); /* * // Get the cell position. * var position = CellSystem.GetSubCellCoordinatesFromId(message.Id); * * // Get the cell info to know what faction we're spawning for. Use the large cell id for that, * // because we only store info for that. * var cellInfo = ((UniverseSystem) Manager.GetSystem(UniverseSystem.TypeId)) * .GetCellInfo(CellSystem.GetCellIdFromCoordinates(position)); * * // The area covered by the cell. * FarRectangle cellArea; * cellArea.X = position.X; * cellArea.Y = position.Y; * cellArea.Width = CellSystem.SubCellSize; * cellArea.Height = CellSystem.SubCellSize; * * // Get center point for spawn group. * FarPosition spawnPoint; * spawnPoint.X = _random.NextInt32((int) cellArea.Left, (int) cellArea.Right); * spawnPoint.Y = _random.NextInt32((int) cellArea.Top, (int) cellArea.Bottom); * * // TODO number of groups based on cell/biome type * * // Configuration for spawned ships. * string[] ships; * ArtificialIntelligence.AIConfiguration[] configurations = null; * var formation = SquadSystem.Formations.None; * * // TODO different groups, based on cell info? definable via editor maybe? * if (_random.NextDouble() < 0.5f) * { * ships = new[] * { * "L1_AI_Ship", * "L1_AI_Ship", * "L1_AI_Ship", * "L1_AI_Ship", * "L1_AI_Ship", * "L1_AI_Ship" * }; * configurations = new[] * { * new ArtificialIntelligence.AIConfiguration * { * AggroRange = UnitConversion.ToSimulationUnits(600) * } * }; * formation = SquadSystem.Formations.Block; * } * else * { * ships = new[] * { * "L1_AI_Ship", * "L1_AI_Ship", * "L1_AI_Ship", * "L1_AI_Ship", * "L1_AI_Ship" * }; * configurations = new[] * { * new ArtificialIntelligence.AIConfiguration * { * AggroRange = UnitConversion.ToSimulationUnits(800) * } * }; * formation = SquadSystem.Formations.Vee; * } * * // Generate spawns. * for (var i = 0; i < 5; ++i) * { * var entity = Manager.AddEntity(); * var body = Manager.AddBody( * entity, type: Body.BodyType.Static, worldPosition: spawnPoint); * Manager.AttachCircle( * body, * UnitConversion.ToSimulationUnits(5000), * collisionCategory: Factions.Nature.ToCollisionGroup(), * collisionMask: Factions.Players.ToCollisionGroup(), * isSensor: true); * * } */ } }
private void PopulateSubCell(CellStateChanged message, IUniformRandom random) { // We randomly spawn some asteroid fields in sub-cells. These are laid out as follows: // - we decide how many fields we want to spawn. // - we build a grid inside the cell that has enough entries for our asteroid fields, // i.e. we take the next higher squared number (e.g. if we have 5 fields, we generate // a 3x3 grid). // - For each field we randomly pick one such grid cell to determine it's basic position, // and shift it to a random position inside that grid cell. // - To position the actual asteroids, we want it to be somewhat circular, but it should // not look too regular. We lay out our asteroids in a spiral, on which we place our // asteroids in a fixed interval. Finally we add some random offset to make the spiral // non-obvious. // Get content manager to fetch textures from which to generate physics models. var content = ((ContentSystem)Manager.GetSystem(ContentSystem.TypeId)).Content; // Number of asteroid fields in this cell? var fieldCount = random.NextInt32(8, 12); // Determine number of rows and cols for base positions. var cols = (int)Math.Ceiling(Math.Sqrt(fieldCount)); // Build sampling list. var cells = new List <Tuple <int, int> >(cols * cols); for (var x = 0; x < cols; ++x) { for (var y = 0; y < cols; ++y) { cells.Add(Tuple.Create(x, y)); } } // Size of a cell in our sub grid. var gridSize = CellSystem.SubCellSize / (float)cols; // Cell top left corner position. var cellPosition = new FarPosition(message.X, message.Y) * CellSystem.SubCellSize; // Generate asteroid fields. for (var i = 0; i < fieldCount; ++i) { // Get base position. var positionIndex = random.NextInt32(cells.Count); var fieldIndex = cells[positionIndex]; cells.RemoveAt(positionIndex); var asteroidCount = random.NextInt32(30, 60); var center = cellPosition + new FarPosition( fieldIndex.Item1 * gridSize + (float)random.NextDouble(0, gridSize), fieldIndex.Item2 * gridSize + (float)random.NextDouble(0, gridSize)); // We grow our asteroid fields as spirals, with a little jitter. const float jitter = 2.5f; const float radiusStep = 0.4f; //< how fast we move outwards. const float angleStep = 2.25f; //< the asteroid interval on the spiral. var theta = angleStep / radiusStep; // Create first one at the center. CreateAsteroid(center, random, content); // Generate rest of the spiral. for (var j = 1; j < asteroidCount; ++j) { // Compute position in our spiral. var radius = radiusStep * theta; var position = center; position.X += (float)Math.Cos(theta) * radius; position.Y += (float)Math.Sin(theta) * radius; position.X += (float)random.NextDouble(-jitter / 2, jitter / 2); position.Y += (float)random.NextDouble(-jitter / 2, jitter / 2); theta += angleStep / radius; CreateAsteroid(position, random, content); } } //// Sprinkle some asteroids in the background, for depth and movement perception. //for (var i = 0; i < 1000; ++i) //{ // var center = cellPosition + new FarPosition( // (float) random.NextDouble(0, CellSystem.SubCellSize), // (float) random.NextDouble(0, CellSystem.SubCellSize)); // // Randomly scale and rotate it. // var layer = (float) random.NextDouble(0.5f, 0.75f); // var scale = (float) random.NextDouble(0.6f, 0.9f); // var angle = (float) random.NextDouble() * MathHelper.TwoPi; // // Determine shape for physics system. // var textureName = "Textures/Asteroids/rock_" + random.NextInt32(1, 14); // var texture = content.Load<Texture2D>(textureName); // // Create component that will be rendered. // var entity = Manager.AddEntity(); // Manager.AddComponent<Transform>(entity).Initialize(center, angle); // // Expand bounds based on layer for camera and interpolation system, so that they // // can see the asteroids in time (lower parallax = farther in background = moving // // slower = longer in screen = wider actual viewport necessary to check). // var width = UnitConversion.ToSimulationUnits(texture.Width); // var height = UnitConversion.ToSimulationUnits(texture.Height); // var diagonal = 8f * (float) Math.Sqrt(width * width + height * height) / layer / scale; // var bounds = new FarRectangle(-diagonal / 2f, -diagonal / 2f, diagonal, diagonal); // // Rendering stuff. // Manager.AddComponent<Parallax>(entity).Initialize(layer); // Manager.AddComponent<Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Manager.AddComponent<Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId); // Manager.AddComponent<SimpleTextureDrawable>(entity).Initialize(textureName, new Color(100, 100, 100, 255), scale); // // Auto removal. // Manager.AddComponent<CellDeath>(entity).Initialize(true); // Manager.AddComponent<Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); //} }
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); } } } } } } }