/// <summary>Creates a new, AI controlled ship.</summary> /// <param name="manager">The manager.</param> /// <param name="blueprint">The blueprint.</param> /// <param name="faction">The faction the ship will belong to.</param> /// <param name="position">The position.</param> /// <param name="random">The random.</param> /// <param name="configuration">The configuration.</param> /// <returns>The new ship.</returns> public static int CreateAIShip( IManager manager, string blueprint, Factions faction, FarPosition position, IUniformRandom random, ArtificialIntelligence.AIConfiguration configuration = null) { var entity = FactoryLibrary.SampleShip(manager, blueprint, faction, position, random); var input = (ShipControl)manager.GetComponent(entity, ShipControl.TypeId); input.Stabilizing = true; manager.AddComponent <ArtificialIntelligence>(entity). Initialize(random != null ? random.NextUInt32() : 0, configuration).Enabled = false; // Add to the index from which entities will automatically removed // on cell death and mark it (for translation checks into empty space). manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); manager.AddComponent <CellDeath>(entity).Initialize(true); // Add to AI index, to allow putting the AI to sleep. manager.AddComponent <Indexable>(entity).Initialize(SleepSystem.IndexId); return(entity); }
/// <summary>Samples a new orbiter of this type.</summary> /// <param name="manager">The manager.</param> /// <param name="center">The center.</param> /// <param name="dominantAngle">The dominant angle.</param> /// <param name="random">The random.</param> /// <returns></returns> internal void Sample(IManager manager, int center, float dominantAngle, IUniformRandom random) { if (random != null && ChanceToExist <= random.NextDouble()) { return; } var radius = UnitConversion.ToSimulationUnits(SampleOrbitRadius(random)); var eccentricity = SampleEccentricity(random); var angleOffset = SampleAngleOffset(random); var travelSpeed = UnitConversion.ToSimulationUnits(SampleTravelSpeed(random)); var periodOffset = random == null ? 0f : (float)random.NextDouble(); // Compute minor and major radius. float a, b; ComputeRadii(radius, eccentricity, out a, out b); // Get period. Figure out circumference using Ramanujan's approximation. var circumference = MathHelper.Pi * (3 * (a + b) - (float)Math.Sqrt((3 * a + b) * (a + 3 * b))); var period = circumference / travelSpeed * Settings.TicksPerSecond; var entity = FactoryLibrary.SamplePlanet(manager, Name, random); // Make it move around its center. manager.AddComponent <EllipsePath>(entity) .Initialize(center, a, b, dominantAngle + angleOffset, period, MathHelper.TwoPi * periodOffset); // Recurse. if (Moons != null) { Moons.Sample(manager, entity, random); } }
/// <summary>Samples the angle of this planet's orbit.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled angle.</returns> private float SampleAngle(IUniformRandom random) { return (MathHelper.ToRadians( (random == null) ? _angle.Low : MathHelper.Lerp(_angle.Low, _angle.High, (float)random.NextDouble()))); }
/// <summary>Samples the attributes to apply to the item.</summary> /// <param name="manager">The manager.</param> /// <param name="cellCenter">The center of the cell for which the sun system is created.</param> /// <param name="random">The randomizer to use.</param> /// <return>The entity with the attributes applied.</return> public void SampleSunSystem(IManager manager, FarPosition cellCenter, IUniformRandom random) { var sun = FactoryLibrary.SampleSun(manager, _sun, cellCenter, random); if (_planets != null) { _planets.Sample(manager, sun, random); } }
/// <summary>Samples a new reactor based on these constraints.</summary> /// <param name="manager">The manager.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled reactor.</returns> public override int Sample(IManager manager, IUniformRandom random) { var entity = base.Sample(manager, random); manager.AddComponent <Reactor>(entity) .Initialize(Name, Icon, Quality, RequiredSlotSize, ModelOffset, ModelBelowParent); return(entity); }
/// <summary>Samples the mass of this sun.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled mass.</returns> private float SampleMass(IUniformRandom random) { if (_mass != null) { return((random == null) ? _mass.Low : MathHelper.Lerp(_mass.Low, _mass.High, (float)random.NextDouble())); } return(0f); }
/// <summary>Samples the eccentricity of this planet's orbit.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled major radius.</returns> private float SampleEccentricity(IUniformRandom random) { if (_eccentricity != null) { return((random == null) ? _eccentricity.Low : MathHelper.Lerp(_eccentricity.Low, _eccentricity.High, (float)random.NextDouble())); } return(0f); }
/// <summary>Samples the travel speed of this planet.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled travel speed.</returns> private float SampleTravelSpeed(IUniformRandom random) { if (_travelSpeed != null) { return((random == null) ? _travelSpeed.Low : MathHelper.Lerp(_travelSpeed.Low, _travelSpeed.High, (float)random.NextDouble())); } return(0f); }
// TODO in case we need this somewhere else it might be a good idea to move this to the EntityFactory private void CreateAsteroid(FarPosition position, IUniformRandom random, ContentManager content) { // Randomly scale and rotate it. var scale = (float)random.NextDouble(0.5f, 1f); 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); var hull = new List <Vector2>(TextureConverter.DetectVertices(texture, 8f, textureName: textureName)[0]); for (var k = 0; k < hull.Count; ++k) { hull[k] -= new Vector2(texture.Width / 2f, texture.Height / 2f); hull[k] = XnaUnitConversion.ToSimulationUnits(hull[k]) * scale; } var polygons = EarClipDecomposer.ConvexPartition(hull); // Create physical representation. var entity = Manager.AddEntity(); var body = Manager.AddBody(entity, position, angle, Body.BodyType.Dynamic); foreach (var polygon in polygons) { Manager.AttachPolygon(body, polygon, density: 1000f, restitution: 0.2f); } // Slow down to allow reaching sleep state again. body.LinearDamping = 0.05f * Space.Util.Settings.TicksPerSecond; body.AngularDamping = 0.025f * Space.Util.Settings.TicksPerSecond; // Bounds of the asteroid for rendering culling. We use the diagonal for a loose fit that // contains every possible rotated state of the texture. var width = UnitConversion.ToSimulationUnits(texture.Width); var height = UnitConversion.ToSimulationUnits(texture.Height); var diagonal = (float)Math.Sqrt(width * width + height * height); var bounds = new FarRectangle(-diagonal / 2, -diagonal / 2, diagonal, diagonal); // Rendering stuff. Manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); Manager.AddComponent <Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId); Manager.AddComponent <SimpleTextureDrawable>(entity).Initialize(textureName, scale); // Auto removal. Manager.AddComponent <CellDeath>(entity).Initialize(true); Manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); // Make it destructible. var health = Manager.AddComponent <Health>(entity); health.Value = health.MaxValue = 200 * scale; health.Regeneration = 0f; // As they don't move on their own, start asteroids as sleeping to save performance. body.IsAwake = false; }
/// <summary>Construct with the provided RNG source.</summary> public Ziggurat(IUniformRandom rng) { _rng = rng; // Initialize rectangle position data. // _x[i] and _y[i] describe the top-right position ox Box i. // Allocate storage. We add one to the length of _x so that we have an entry at _x[_blockCount], this avoids having // to do a special case test when sampling from the top box. _x = new double[BlockCount + 1]; _y = new double[BlockCount]; // Determine top right position of the base rectangle/box (the rectangle with the Gaussian tale attached). // We call this Box 0 or B0 for short. // Note. x[0] also describes the right-hand edge of B1. (See diagram). _x[0] = R; _y[0] = GaussianPdfDenorm(R); // The next box (B1) has a right hand X edge the same as B0. // Note. B1's height is the box area divided by its width, hence B1 has a smaller height than B0 because // B0's total area includes the attached distribution tail. _x[1] = R; _y[1] = _y[0] + (A / _x[1]); // Calc positions of all remaining rectangles. for (var i = 2; i < BlockCount; i++) { _x[i] = GaussianPdfDenormInv(_y[i - 1]); _y[i] = _y[i - 1] + (A / _x[i]); } // For completeness we define the right-hand edge of a notional box 6 as being zero (a box with no area). _x[BlockCount] = 0.0; // Useful precomputed values. _aDivY0 = A / _y[0]; _xComp = new uint[BlockCount]; // Special case for base box. _xComp[0] stores the area of B0 as a proportion of __R // (recalling that all segments have area __A, but that the base segment is the combination of B0 and the distribution tail). // Thus -xComp[0[ is the probability that a sample point is within the box part of the segment. _xComp[0] = (uint)(((R * _y[0]) / A) * uint.MaxValue); for (var i = 1; i < BlockCount - 1; i++) { _xComp[i] = (uint)((_x[i + 1] / _x[i]) * uint.MaxValue); } _xComp[BlockCount - 1] = 0; // Shown for completeness. // Sanity check. Test that the top edge of the topmost rectangle is at y=1.0. // Note. We expect there to be a tiny drift away from 1.0 due to the inexactness of floating // point arithmetic. Debug.Assert(System.Math.Abs(1.0 - _y[BlockCount - 1]) < 1e-10); }
/// <summary>Samples the rotation speed of this planet.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled rotation speed.</returns> private float SampleRotationSpeed(IUniformRandom random) { if (_rotationSpeed != null) { return(MathHelper.ToRadians( (random == null) ? _rotationSpeed.Low : MathHelper.Lerp(_rotationSpeed.Low, _rotationSpeed.High, (float)random.NextDouble()))); } return(0f); }
/// <summary>Samples a new ship with the specified name.</summary> /// <param name="manager">The manager.</param> /// <param name="name">The logical name of the ship to sample.</param> /// <param name="faction">The faction the ship will belong to.</param> /// <param name="position">The initial position of the ship.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled ship.</returns> public static int SampleShip( IManager manager, string name, Factions faction, FarPosition position, IUniformRandom random) { if (string.IsNullOrWhiteSpace(name) || !Factories.ContainsKey(name)) { return(0); } var factory = Factories[name] as ShipFactory; return(factory != null?factory.Sample(manager, faction, position, random) : 0); }
internal void Sample(IManager manager, int center, IUniformRandom random) { // Get the dominant angle. var dominantAngle = SampleAngle(random); // Create orbiters. foreach (var orbiter in _orbiters) { orbiter.Sample(manager, center, dominantAngle, random); } }
/// <summary> /// Shuffles the specified list using the Fisher-Yates shuffle. This only shuffles a segment in a list, as opposed /// to shuffling the complete list. /// </summary> /// <typeparam name="T">The type of element stored in the list.</typeparam> /// <param name="list">The list to shuffle.</param> /// <param name="offset">The offset at which to start shuffling.</param> /// <param name="length">The length of the interval to shuffle.</param> /// <param name="random">The random number generator to use for shuffling. If none is specified, a new one will be created.</param> public static void Shuffle <T>(this IList <T> list, int offset, int length, IUniformRandom random = null) { random = random ?? new MersenneTwister(); for (var i = offset + length - 1; i > offset; --i) { var j = random.NextInt32(i + 1); var t = list[j]; list[j] = list[i]; list[i] = t; } }
/// <summary>Samples a new weapon based on these constraints.</summary> /// <param name="manager">The manager.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled weapon.</returns> public override int Sample(IManager manager, IUniformRandom random) { var entity = base.Sample(manager, random); // Get baked list of attributes. Dictionary <AttributeType, float> attributes = null; // Iterate over all local attribute modifiers and accumulate the // additive base value, store the multiplicative values in an // extra list to apply them at the end. Dictionary <AttributeType, float> multipliers = null; if (_guaranteedLocalAttributes != null) { foreach (var attribute in _guaranteedLocalAttributes) { AccumulateModifier(attribute.SampleAttributeModifier(random), ref attributes, ref multipliers); } } if (_additionalLocalAttributes != null && _additionalLocalAttributes.Length > 0) { foreach ( var attributeModifier in SampleAttributes(SampleLocalAttributeCount(random), random)) { AccumulateModifier(attributeModifier, ref attributes, ref multipliers); } } // Done checking all local attributes, apply multipliers, if any. if (multipliers != null) { foreach (var multiplier in multipliers) { if (attributes != null && attributes.ContainsKey(multiplier.Key)) { attributes[multiplier.Key] *= multiplier.Value; } else { Logger.Warn( "Invalid local attribute for weapon {0}: {1} does not have an additive base value.", Name, multiplier.Key); } } } manager.AddComponent <Weapon>(entity) .Initialize(_sound, attributes, _projectiles) .Initialize(Name, Icon, Quality, RequiredSlotSize, ModelOffset, ModelBelowParent); return(entity); }
/// <summary>Samples the attributes to apply to the item.</summary> /// <param name="manager">The manager.</param> /// <param name="random">The randomizer to use.</param> /// <return>The entity with the attributes applied.</return> public int Sample(IManager manager, IUniformRandom random) { var entity = manager.AddEntity(); // Sample all values in advance, to allow reshuffling component creation // order in case we need to, without influencing the 'random' results. var radius = UnitConversion.ToSimulationUnits(SampleRadius(random)); var rotationSpeed = SampleRotationSpeed(random); var mass = SampleMass(random); var surfaceRotation = random != null ? (Math.Sign(random.NextDouble() - 0.5) * rotationSpeed) : rotationSpeed; // Give it a position. manager.AddComponent <Transform>(entity); // Add it to some indexes. var bounds = new FarRectangle(-radius, -radius, radius * 2, radius * 2); // Can be detected. manager.AddComponent <Indexable>(entity).Initialize(bounds, Detectable.IndexId); // Can make some noise. manager.AddComponent <Indexable>(entity).Initialize(SoundSystem.IndexId); // Must be detectable by the camera. manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Remove when out of bounds or large containing cell dies. manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); manager.AddComponent <CellDeath>(entity).Initialize(false); // Make it rotate. manager.AddComponent <Velocity>(entity).Initialize( Vector2.Zero, MathHelper.ToRadians(rotationSpeed) / Settings.TicksPerSecond); // Make it attract stuff if it has mass. if (mass > 0) { manager.AddComponent <Gravitation>(entity).Initialize(Gravitation.GravitationTypes.Attractor, mass); } // Make it detectable. manager.AddComponent <Detectable>(entity).Initialize("Textures/Radar/Icons/radar_planet"); // Make it visible. manager.AddComponent <PlanetRenderer>(entity).Initialize(this, UnitConversion.ToScreenUnits(radius), surfaceRotation); // Let it rap. manager.AddComponent <Sound>(entity).Initialize("Planet"); return(entity); }
public static RectangleF NextRectangle(this IUniformRandom random, float area, float minSize, float maxSize) { var rect = new RectangleF { Width = (float)random.NextDouble(minSize, maxSize), Height = (float)random.NextDouble(minSize, maxSize) }; rect.X = (float)random.NextDouble(-area / 2, area / 2 - rect.Width); rect.Y = (float)random.NextDouble(-area / 2, area / 2 - rect.Height); return(rect); }
/// <summary>Samples an attribute modifier from this constraint.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled attribute modifier.</returns> public AttributeModifier <TAttribute> SampleAttributeModifier(IUniformRandom random) { // Only randomize if necessary. var value = (random == null) ? _value.Low : MathHelper.Lerp(_value.Low, _value.High, (float)random.NextDouble()); if (_round) { value = (float)System.Math.Round(value); } return(new AttributeModifier <TAttribute>(_type, value, _computationType)); }
/// <summary>Samples the offset of this sun.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled offset.</returns> private Vector2 SampleOffset(IUniformRandom random) { if (_offsetRadius != null && random != null) { Vector2 offset; offset.X = (float)(random.NextDouble() - 0.5); offset.Y = (float)(random.NextDouble() - 0.5); offset.Normalize(); offset *= MathHelper.Lerp(_offsetRadius.Low, _offsetRadius.High, (float)random.NextDouble()); return(offset); } return(Vector2.Zero); }
/// <summary>Samples a new item with the specified name.</summary> /// <param name="manager">The manager.</param> /// <param name="name">The logical name of the item to sample.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled item.</returns> public static int SampleItem(IManager manager, string name, IUniformRandom random) { if (string.IsNullOrWhiteSpace(name) || !Factories.ContainsKey(name)) { return(0); } var factory = Factories[name] as ItemFactory; if (factory != null) { return(factory.Sample(manager, random)); } return(0); }
/// <summary>Samples the attributes to apply to the item.</summary> /// <param name="manager"> </param> /// <param name="faction">The faction the ship belongs to.</param> /// <param name="position">The position at which to spawn the ship.</param> /// <param name="random">The randomizer to use.</param> /// <return>The entity with the attributes applied.</return> public int Sample(IManager manager, Factions faction, FarPosition position, IUniformRandom random) { var entity = CreateShip(manager, faction, position); // Create initial equipment. var equipment = (ItemSlot)manager.GetComponent(entity, ItemSlot.TypeId); equipment.Item = FactoryLibrary.SampleItem(manager, _items.Name, position, random); if (equipment.Item > 0) { foreach (var item in _items.Slots) { SampleItems(manager, position, random, equipment.Item, item); } } // Add our attributes. var attributes = (Attributes <AttributeType>)manager.GetComponent(entity, Attributes <AttributeType> .TypeId); foreach (var attribute in _attributes) { var modifier = attribute.SampleAttributeModifier(random); if (modifier.ComputationType == AttributeComputationType.Multiplicative) { throw new InvalidOperationException("Base attributes must be additive."); } attributes.SetBaseValue(modifier.Type, modifier.Value); } // Fill up our values. var health = ((Health)manager.GetComponent(entity, Health.TypeId)); var energy = ((Energy)manager.GetComponent(entity, Energy.TypeId)); health.Value = health.MaxValue; energy.Value = energy.MaxValue; // Add experience points if we're worth any. if (_xp > 0) { manager.AddComponent <ExperiencePoints>(entity).Initialize(_xp); } return(entity); }
/// <summary>Samples the initial directed velocity.</summary> /// <param name="baseRotation">The base rotation.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled velocity.</returns> public Vector2 SampleInitialDirectedVelocity(float baseRotation, IUniformRandom random) { if (_initialDirection != null && _initialVelocity != null) { var velocity = Vector2.UnitX; var rotation = Matrix.CreateRotationZ( baseRotation + MathHelper.ToRadians( MathHelper.Lerp( _initialDirection.Low, _initialDirection.High, (random == null) ? 0 : (float)random.NextDouble()))); Vector2.Transform(ref velocity, ref rotation, out velocity); velocity.Normalize(); return(velocity * ((random == null) ? _initialVelocity.Low : MathHelper.Lerp(_initialVelocity.Low, _initialVelocity.High, (float)random.NextDouble()))); } return(Vector2.Zero); }
/// <summary>Samples the acceleration force.</summary> /// <param name="baseRotation">The base rotation.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled acceleration force.</returns> public Vector2 SampleAccelerationForce(float baseRotation, IUniformRandom random) { if (_accelerationDirection != null && _accelerationForce != null) { var acceleration = Vector2.UnitX; var rotation = Matrix.CreateRotationZ( baseRotation + MathHelper.ToRadians( MathHelper.Lerp( _accelerationDirection.Low, _accelerationDirection.High, (random == null) ? 0 : (float)random.NextDouble()))); Vector2.Transform(ref acceleration, ref rotation, out acceleration); acceleration.Normalize(); return(acceleration * ((random == null) ? _accelerationForce.Low : MathHelper.Lerp( _accelerationForce.Low, _accelerationForce.High, (float)random.NextDouble()))); } return(Vector2.Zero); }
/// <summary>Samples the items for the specified item info and children (recursively).</summary> /// <param name="manager">The manager.</param> /// <param name="position">The position.</param> /// <param name="random">The random.</param> /// <param name="parent">The parent.</param> /// <param name="itemInfo">The item info.</param> private static void SampleItems( IManager manager, FarPosition position, IUniformRandom random, int parent, ItemInfo itemInfo) { // Create the actual item. var itemId = FactoryLibrary.SampleItem(manager, itemInfo.Name, position, random); if (itemId < 1) { // No such item. return; } var item = (Item)manager.GetComponent(itemId, Item.TypeId); // Then equip it in the parent. foreach (ItemSlot slot in manager.GetComponents(parent, ItemSlot.TypeId)) { if (slot.Item == 0 && slot.Validate(item)) { // Found a suitable empty slot, equip here. slot.Item = itemId; // Recurse to generate children. foreach (var childInfo in itemInfo.Slots) { SampleItems(manager, position, random, itemId, childInfo); } // Done. return; } } // If we get here we couldn't find a slot to equip the item in. manager.RemoveEntity(itemId); Logger.Warn("Parent item did not have a slot for the requested child item."); }
/// <summary> /// Initializes a new instance of the <see cref="Behavior"/> class. /// </summary> /// <param name="ai">The AI component.</param> /// <param name="random">The randomizer to use for decision making.</param> /// <param name="pollRate">The poll rate in seconds.</param> protected Behavior(ArtificialIntelligence ai, IUniformRandom random, float pollRate) { AI = ai; Random = random; _pollRate = (int)(pollRate * Settings.TicksPerSecond); }
/// <summary>Shuffles the specified list using the Fisher-Yates shuffle.</summary> /// <typeparam name="T">The type of element stored in the list.</typeparam> /// <param name="list">The list to shuffle.</param> /// <param name="random">The random number generator to use for shuffling. If none is specified, a new one will be created.</param> public static void Shuffle <T>(this IList <T> list, IUniformRandom random = null) { list.Shuffle(0, list.Count, random); }
/// <summary> /// Initializes a new instance of the <see cref="RoamBehavior"/> class. /// </summary> /// <param name="ai">The ai component this behavior belongs to.</param> /// <param name="random">The randomizer to use for decision making.</param> public RoamBehavior(ArtificialIntelligence ai, IUniformRandom random) : base(ai, random, 0) { }
/// <summary> /// Initializes a new instance of the <see cref="MoveBehavior"/> class. /// </summary> /// <param name="ai">The ai component this behavior belongs to.</param> /// <param name="random">The randomizer to use for decision making.</param> public MoveBehavior(ArtificialIntelligence ai, IUniformRandom random) : base(ai, random, 1) { }
/// <summary>Samples a new projectile.</summary> /// <param name="manager">The manager.</param> /// <param name="emitter">The emitter that the projectile comes from.</param> /// <param name="offset">The offset.</param> /// <param name="angle">The angle.</param> /// <param name="weapon">The weapon.</param> /// <param name="faction">The faction the projectile belongs to.</param> /// <param name="random">The randomizer to use.</param> /// <returns>A new projectile.</returns> public int SampleProjectile( IManager manager, int emitter, Vector2 offset, float angle, Weapon weapon, Factions faction, IUniformRandom random) { var entity = manager.AddEntity(); // Get position and velocity of the emitter, to set initial position // and additional velocity. var emitterTransform = (ITransform)manager.GetComponent(emitter, TransformTypeId); var emitterVelocity = (IVelocity)manager.GetComponent(emitter, VelocityTypeId); // Rotate the offset. var emitterAngle = emitterTransform.Angle; var cosRadians = (float)Math.Cos(emitterAngle); var sinRadians = (float)Math.Sin(emitterAngle); FarPosition rotatedOffset; rotatedOffset.X = -offset.X * cosRadians - offset.Y * sinRadians; rotatedOffset.Y = -offset.X * sinRadians + offset.Y * cosRadians; // Set initial velocity. var velocity = SampleInitialDirectedVelocity(emitterAngle + angle, random); var accelerationForce = SampleAccelerationForce(emitterAngle + angle, random); // Adjust rotation for projectile based on its own acceleration or speed. var emitAngle = emitterAngle; if (accelerationForce != Vector2.Zero) { emitAngle = (float)Math.Atan2(accelerationForce.Y, accelerationForce.X); } else if (velocity != Vector2.Zero) { emitAngle = (float)Math.Atan2(velocity.Y, velocity.X); } // See what we must not bump into. var collisionMask = ~faction.ToCollisionGroup(); // Normally projectiles won't test against each other, but some may be // shot down, such as missiles. If that's the case, don't add us to the // common projectile group. if (!_canBeShot) { collisionMask &= ~Factions.Projectiles.ToCollisionGroup(); } // Create physical body. var body = manager.AddBody( entity, emitterTransform.Position + rotatedOffset + velocity / Settings.TicksPerSecond, emitAngle, Body.BodyType.Dynamic, isBullet: true, allowSleep: false); manager.AttachCircle( body, UnitConversion.ToSimulationUnits(_collisionRadius), 0, restitution: 0.1f, collisionCategory: faction.ToCollisionGroup() | Factions.Projectiles.ToCollisionGroup(), collisionMask: collisionMask); // Add to render system indexes. The padding these perform should be enough for any projectile. manager.AddComponent <Indexable>(entity).Initialize(CameraSystem.IndexId); manager.AddComponent <Indexable>(entity).Initialize(InterpolationSystem.IndexId); // If our emitter was moving, apply its velocity. if (emitterVelocity != null) { velocity += emitterVelocity.LinearVelocity; } // Then set the initial velocity of our bullet. body.LinearVelocity = velocity; // Sample an acceleration for this projectile. If there is any, create the // component for it, otherwise disregard. if (accelerationForce != Vector2.Zero) { // TODO add motor joint? else teach acceleration system to use ApplyForce // manager.AddComponent<Acceleration>(entity).Initialize(accelerationForce); } // Apply friction to this projectile if so desired. if (_friction > 0) { body.LinearDamping = 1f - _friction; } // If this projectile should vanish after some time, make it expire. if (_timeToLive > 0) { manager.AddComponent <Expiration>(entity).Initialize((int)(_timeToLive * Settings.TicksPerSecond)); } // Mark as applying damage on collision. manager.AddComponent <CollisionDamage>(entity).Initialize(true); // Apply attributes of the weapon, modified with emitter attribute values, // and emitter attributes to the projectile to allow damage calculation if // it hits something. var emitterAttributes = (Attributes <AttributeType>)manager.GetComponent(emitter, Attributes <AttributeType> .TypeId); Attributes <AttributeType> projectileAttributes = null; // Only create if necessary. foreach (AttributeType attributeType in Enum.GetValues(typeof(AttributeType))) { if (attributeType == AttributeType.None) { continue; } // Try to get weapon local attribute as base values. var value = 0f; if (weapon.Attributes.ContainsKey(attributeType)) { value = weapon.Attributes[attributeType]; } // Try to modify it with emitter attributes. if (emitterAttributes != null) { value = emitterAttributes.GetValue(attributeType, value); } // If we have something, copy it to the projectile. // ReSharper disable CompareOfFloatsByEqualityOperator if (value != 0f) // ReSharper restore CompareOfFloatsByEqualityOperator { if (projectileAttributes == null) { projectileAttributes = manager.AddComponent <Attributes <AttributeType> >(entity); } projectileAttributes.SetBaseValue(attributeType, value); } } // Make us visible! if (!string.IsNullOrWhiteSpace(_model)) { manager.AddComponent <SimpleTextureDrawable>(entity).Initialize(_model); } // And add some particle effects, if so desired. if (!string.IsNullOrWhiteSpace(_effect)) { manager.AddComponent <ParticleEffects>(entity) .TryAdd(0, _effect, 1f, 0, _effectOffset, ParticleEffects.EffectGroup.None, true); } // Assign owner, to track original cause when they do something (e.g. kill something). manager.AddComponent <Owner>(entity).Initialize(emitter); return(entity); }
/// <summary>Samples the angle of this planet's orbit.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled angle.</returns> private float SampleOrbitRadius(IUniformRandom random) { return((random == null) ? _orbitRadius.Low : MathHelper.Lerp(_orbitRadius.Low, _orbitRadius.High, (float)random.NextDouble())); }