private AsteroidOrMineralDefinition[] DetermineDestroyedChildrenMinerals(HullVoronoiExploder_Response shards, double overDamage, Vector3D parentRadius, double minChildRadius, Func<double, ITriangleIndexed[], double> getMassByRadius, Func<double, MineralDNA[]> getMineralsByDestroyedMass) { const double MAXOVERDMG = 13; // overdamage of 1 is the smallest value (the asteroid was barely destroyed). Larger values are overkill, and the asteroid becomes more fully destroyed if (shards == null || shards.Shards == null || shards.Shards.Length == 0) { // There was problem, so just pop out some minerals return DetermineDestroyedChildrenMinerals_Minerals(parentRadius, getMassByRadius, getMineralsByDestroyedMass); } Random rand = StaticRandom.GetRandomForThread(); #region calculate volumes var shardVolumes = shards.Shards. Select(o => { Vector3D radius = GetEllipsoidRadius(o.Hull_Centered); return new { Radius = radius, Volume = GetEllipsoidVolume(radius) }; }). ToArray(); // Figure out how much volume should permanently be destroyed double parentVolume = GetEllipsoidVolume(parentRadius); double volumeToDestroy = GetVolumeToDestroy(parentVolume, overDamage, MAXOVERDMG); #endregion double destroyedVolume = 0; bool[] shouldSelfDestruct = new bool[shards.Shards.Length]; #region detect too small // Get rid of any that are too small //TODO: Also get rid of any that are too thin for (int cntr = 0; cntr < shards.Shards.Length; cntr++) { if (shards.Shards[cntr].Radius < minChildRadius) { shouldSelfDestruct[cntr] = true; destroyedVolume += shardVolumes[cntr].Volume; } } #endregion if (destroyedVolume < volumeToDestroy) { #region remove more // Find the shards that could be removed var candidates = Enumerable.Range(0, shards.Shards.Length). Select(o => new { Index = o, Shard = shards.Shards[o], Volume = shardVolumes[o] }). Where(o => !shouldSelfDestruct[o.Index] && o.Volume.Volume < volumeToDestroy - destroyedVolume). OrderBy(o => o.Volume.Volume). ToList(); while (candidates.Count > 0 && destroyedVolume < volumeToDestroy) { // Figure out which to self destruct (the rand power will favor inicies closer to zero) int index = UtilityCore.GetIndexIntoList(rand.NextPow(2), candidates.Count); // Remove it shouldSelfDestruct[candidates[index].Index] = true; destroyedVolume += candidates[index].Volume.Volume; candidates.RemoveAt(index); // Remove the items at the end that are now too large index = candidates.Count - 1; while (index >= 0) { if (candidates[index].Volume.Volume < volumeToDestroy - destroyedVolume) { break; // it's sorted, so the rest will also be under } else { candidates.RemoveAt(index); index--; } } } #endregion } #region distribute minerals // Figure out the mineral value of the destroyed volume MineralDNA[] mineralDefinitions = null; if (destroyedVolume > 0 && getMineralsByDestroyedMass != null) { double inferredRadius = GetEllipsoidRadius(destroyedVolume); double destroyedMass = getMassByRadius(inferredRadius, null); if (destroyedMass > 0) { mineralDefinitions = getMineralsByDestroyedMass(destroyedMass); } } // Figure out which of the temp asteroids should contain minerals var packedMinerals = new Tuple<int, MineralDNA[]>[0]; if (mineralDefinitions != null && mineralDefinitions.Length > 0) { int[] destroyedIndicies = Enumerable.Range(0, shouldSelfDestruct.Length). Where(o => shouldSelfDestruct[o]). ToArray(); packedMinerals = DistributeMinerals(mineralDefinitions, destroyedIndicies); } #endregion #region final array AsteroidOrMineralDefinition[] retVal = new AsteroidOrMineralDefinition[shards.Shards.Length]; for (int cntr = 0; cntr < retVal.Length; cntr++) { Vector3D velocity = new Vector3D(0, 0, 0); if (shards.Velocities != null && shards.Velocities.Length == shards.Shards.Length) { velocity = shards.Velocities[cntr]; } //NOTE: Only position is needed (The first attempt created random asteroids and pulled them apart. This second attempt //doesn't need to pull them apart) PartSeparator_Part part = new PartSeparator_Part(new[] { new Point3D() }, 0, shards.Shards[cntr].Center_ParentCoords, Quaternion.Identity); //MineralDNA[] mineralsAfter = packedMinerals?.FirstOrDefault(o => o.Item1 == cntr)?.Item2; MineralDNA[] mineralsAfter = null; if (packedMinerals != null) { var found = packedMinerals.FirstOrDefault(o => o.Item1 == cntr); if (found != null) { mineralsAfter = found.Item2; } } retVal[cntr] = new AsteroidOrMineralDefinition(part, shards.Shards[cntr].Hull_Centered, shards.Shards[cntr].Radius, velocity, shouldSelfDestruct[cntr], mineralsAfter); } #endregion return retVal; }
private IMapObject[] ConvertToMapObjects(AsteroidOrMineralDefinition[] items, Point3D parentPos, Vector3D parentVel, Quaternion parentRot) { IMapObject[] retVal = new IMapObject[items.Length]; RotateTransform3D rotate = new RotateTransform3D(new QuaternionRotation3D(parentRot)); for (int cntr = 0; cntr < retVal.Length; cntr++) { Point3D position = parentPos + rotate.Transform(items[cntr].Part.Position.ToVector()); if (items[cntr].IsAsteroid) { // Asteroid AsteroidExtra extra = new AsteroidExtra() { Triangles = TriangleIndexed.Clone_Transformed(items[cntr].AsteroidTriangles, rotate), GetMineralsByDestroyedMass = _getMineralsByDestroyedMass, MineralMaterialID = _mineralMaterialID, MinChildRadius = _minChildRadius, RandomRotation = false, SelfDestructAfterElapse = items[cntr].ShouldAsteroidSelfDestruct ? StaticRandom.NextPercent(SELFDESTRUCTTIME, .5) : (double?)null, MineralsWhenSelfDestruct = items[cntr].MineralsAfterSelfDestruct, }; retVal[cntr] = new Asteroid(items[cntr].AsteroidRadius, _getMassByRadius, position, _world, _map, _materialID, extra); } else { // Mineral MineralDNA mindef = items[cntr].MineralDefinition; double densityMult = mindef.Density / Mineral.GetSettingsForMineralType(mindef.MineralType).Density; retVal[cntr] = new Mineral(mindef.MineralType, position, mindef.Volume, _world, _mineralMaterialID, _sharedVisuals.Value, densityMult, mindef.Scale); } retVal[cntr].PhysicsBody.Velocity = parentVel + rotate.Transform(items[cntr].Velocity); // Need to be careful if setting this. Too much, and it will come apart unnaturally //retVal[cntr].PhysicsBody.AngularVelocity = ; } return retVal; }