private void ProcessOpenRooms(bool testOptional, float targetGrowth)
        {
            var collisionMask = testOptional ? RoomCollisionFlag.Optional : RoomCollisionFlag.NoOptional;

            m_errorByType.Clear();
            m_weightedChoice.Clear();
            var entrySeedError = m_construction.ComputeErrorAgainstSeed();

            m_manager.Debug("Target growth {0}", targetGrowth);

            var boundingBox = BoundingBox.CreateInvalid();

            foreach (var k in m_construction.Rooms)
            {
                boundingBox = boundingBox.Include(k.BoundingBoxBoth);
            }

            foreach (var room in m_openRooms.Values)
            {
                if (room.CollisionMask.HasFlag(collisionMask))
                {
                    continue;
                }
                if (CollidesPredictive(room.Room, true, testOptional))
                {
                    room.CollisionMask |= collisionMask;
                    continue;
                }

                using (m_construction.RegisterRoomUsing(room.Room))
                {
                    var randomScore = 1e1 * m_construction.Seed.DeterministicNoise(room.Room.Part.Name.GetHashCode() ^ room.Room.Transform.GetHashCode());

                    double growthScore = 0;
                    { // Based on the target growth and destruct rates
                        var count = 0;
                        var freeMountPointCount = m_possibleRooms.Backing.Keys.Count;
                        foreach (var point in room.Room.MountPoints)
                        {
                            if (point.AttachedTo != null)
                            {
                                count--;
                            }
                            else
                            {
                                count++;
                            }
                        }

                        // Are we trying to shrink but this room makes us grow?
                        if (targetGrowth < 0 && count > 0)
                        {
                            growthScore -= 1e20;
                        }
                        // Is the future mount count going to drop below zero while we are trying to grow?
                        if (freeMountPointCount + count <= 0 && targetGrowth >= 0)
                        {
                            growthScore -= 1e20;
                        }

                        var error = count - targetGrowth;
                        // We are reducing the number of mounts.  Divide by total mounts since this isn't an issue when we have lots of choices.
                        if (count <= 0)
                        {
                            growthScore -= error * error * 1e3 / Math.Sqrt(1 + freeMountPointCount);
                        }
                        else // increasing the number of mounts.  The more mounts we have the larger of an issue this is.
                        {
                            growthScore -= error * error * Math.Sqrt(1 + freeMountPointCount);
                        }
                    }

                    var sizeError      = Vector3.DistanceSquared(boundingBox.Center, room.Room.BoundingBox.Center);
                    var boundingBoxNew = BoundingBox.CreateMerged(boundingBox, room.Room.BoundingBox);
                    sizeError += boundingBoxNew.Extents.Dot(boundingBoxNew.Extents);
                    sizeError *= 1e3f;

                    double roomError;
                    if (!m_errorByType.TryGetValue(room.Room.Part, out roomError))
                    {
                        var mySeedError = m_construction.ComputeErrorAgainstSeed();
                        roomError = m_errorByType[room.Room.Part] =
                            mySeedError - entrySeedError;
                        m_manager.Debug("    Type {0} has error {1:e}", room.Room.Part.Name, roomError);
                    }

                    double totalScore = 0;
                    totalScore += randomScore;
                    totalScore += growthScore;
                    totalScore -= sizeError;
                    totalScore -= roomError;

                    m_weightedChoice.Add(room.Room, (float)totalScore);
                }
            }
        }
Example #2
0
        public ProceduralConstructionSeed(ProceduralFactionSeed faction, Vector4D locationDepth, Quaternion?orientation, long seed,
                                          int?populationOverride = null)
        {
            Seed     = seed;
            Location = locationDepth.XYZ();
            Faction  = faction;
            var tmpRandom = new Random((int)seed);

            Population = populationOverride ?? (int)MyMath.Clamp((float)(Math.Round(5 * tmpRandom.NextExponential() * locationDepth.W)), 1, 100);
            var sqrtPopulation = Math.Sqrt(Population);

            var choice = new WeightedChoice <ProceduralStationSpeciality>();

            foreach (var kv in Faction.Specialities)
            {
                foreach (var kt in kv.Key.StationSpecialities)
                {
                    choice.Add(kt.Key, kv.Value * kt.Value);
                }
            }
            Speciality = choice.Choose(tmpRandom.NextDouble(), WeightedChoice <ProceduralStationSpeciality> .WeightedNormalization.ClampToZero);
            m_exports  = new Dictionary <MyDefinitionId, TradeRequirements>(MyDefinitionId.Comparer);
            m_imports  = new Dictionary <MyDefinitionId, TradeRequirements>(MyDefinitionId.Comparer);
            List <MyDefinitionBase> rchoice = null;
            var specialExport = tmpRandom.NextDouble() <= Speciality.SpecializationChance;

            if (specialExport)
            {
                rchoice = new List <MyDefinitionBase>();
            }
            foreach (var kv in MyDefinitionManager.Static.GetAllDefinitions())
            {
                if (Speciality.CanExport(kv))
                {
                    m_exports.Add(kv.Id, default(TradeRequirements));
                    rchoice?.Add(kv);
                }
                if (Speciality.CanImport(kv))
                {
                    m_imports.Add(kv.Id, default(TradeRequirements));
                }
            }
            SpecialityExport = null;
            if (specialExport)
            {
                // ReSharper disable once PossibleNullReferenceException
                SpecialityExport = rchoice[tmpRandom.Next(0, rchoice.Count)].Id;
                m_exports.Clear();
                m_exports.Add(SpecialityExport.Value, default(TradeRequirements));
            }

            var nameBuilder = new StringBuilder();

            nameBuilder.Append(Faction.FounderName).Append("'s ");
            if (SpecialityExport.HasValue)
            {
                nameBuilder.Append(MyDefinitionManager.Static.GetDefinition(SpecialityExport.Value).DisplayNameText).Append(" ");
            }
            else if (Speciality.GeneralizedPrefixes != null && Speciality.GeneralizedPrefixes.Length > 0)
            {
                nameBuilder.Append(tmpRandom.NextUniformChoice(Speciality.GeneralizedPrefixes)).Append(" ");
            }
            nameBuilder.Append(tmpRandom.NextUniformChoice(Speciality.Suffixes));
            Name = nameBuilder.ToString();

            // Compute hotlisted import amounts
            {
                var keys = m_imports.Keys.ToList();
                foreach (var kv in keys)
                {
                    var baseMult = Population * tmpRandom.NextExponential() / keys.Count;
                    var k        = MyDefinitionManager.Static.GetDefinition(kv);
                    var pi       = k as MyPhysicalItemDefinition;
                    if (k is MyComponentDefinition)
                    {
                        baseMult *= 100 / pi.Mass;
                    }
                    else if (k.Id.TypeId == typeof(MyObjectBuilder_Ore))
                    {
                        baseMult *= 10000 / (1 + Math.Sqrt(OreUtilities.GetRarity(k.Id)));
                    }
                    else if (k.Id.TypeId == typeof(MyObjectBuilder_Ingot))
                    {
                        baseMult *= 5000 * OreUtilities.GetOutputRatio(k.Id);
                    }
                    else if (k.Id.TypeId == typeof(MyObjectBuilder_GasProperties))
                    {
                        baseMult *= 250000; // 1/10th a large tank
                    }
                    else if (pi != null)
                    {
                        baseMult *= 10 / pi.Mass;
                    }

                    m_imports[kv] = new TradeRequirements(baseMult, Math.Sqrt(baseMult));
                }
            }

            // Compute exported amounts
            {
                var keys = m_exports.Keys.ToList();
                foreach (var kv in keys)
                {
                    var baseMult = Population * Population * tmpRandom.NextExponential() / keys.Count;
                    var k        = MyDefinitionManager.Static.GetDefinition(kv);
                    var pi       = k as MyPhysicalItemDefinition;
                    if (k is MyComponentDefinition)
                    {
                        baseMult *= 100 / pi.Mass;
                    }
                    else if (k.Id.TypeId == typeof(MyObjectBuilder_Ore))
                    {
                        baseMult *= 10000 / (1 + Math.Sqrt(OreUtilities.GetRarity(k.Id)));
                    }
                    else if (k.Id.TypeId == typeof(MyObjectBuilder_Ingot))
                    {
                        baseMult *= 5000 * OreUtilities.GetOutputRatio(k.Id);
                    }
                    else if (k.Id.TypeId == typeof(MyObjectBuilder_GasProperties))
                    {
                        baseMult *= 250000; // 1/10th a large tank
                    }
                    m_exports[kv] = new TradeRequirements(baseMult, Math.Sqrt(baseMult));
                }
            }

            // Using exports, compute imports
            {
                foreach (var kv in m_exports)
                {
                    var producer = BlueprintIndex.Instance.GetAllProducing(kv.Key, true).ToList();
                    foreach (var blueprint in producer)
                    {
                        foreach (var ingredient in blueprint.Ingredients)
                        {
                            var aStorage    = kv.Value.Storage * (double)ingredient.Value / producer.Count;
                            var aThroughput = kv.Value.Throughput * (double)ingredient.Value / producer.Count;
                            TradeRequirements current;
                            m_imports[ingredient.Key] = m_imports.TryGetValue(ingredient.Key, out current) ?
                                                        new TradeRequirements(current.Storage + aStorage, current.Throughput + aThroughput) :
                                                        new TradeRequirements(aStorage, aThroughput);
                        }
                    }
                }
            }

            m_localStorage = new Dictionary <MyDefinitionId, TradeRequirements>(MyDefinitionId.Comparer)
            {
                // ~0.5MWH / person stored, ~1MW / person produced
                [MyResourceDistributorComponent.ElectricityId] = new TradeRequirements(Population * 0.5 * tmpRandom.NextNormal(1, 0.1), Population * tmpRandom.NextNormal(1, 0.1), 10, 10),
                // one large O2 tank / 10 person stored (~2 days), 1 O2/sec/person produced
                [MyResourceDistributorComponent.OxygenId] = new TradeRequirements(Population * 1e4 * tmpRandom.NextNormal(1, 0.1), Population * 1 * tmpRandom.NextNormal(1, 0.1), 10, 10),
                // I don't even know how I'd guess this
                [MyResourceDistributorComponent.HydrogenId] = new TradeRequirements(Population * 1e4 * tmpRandom.NextExponential(), tmpRandom.NextExponential() * sqrtPopulation)
            };

            // Compute mass & volume of storage
            ComputeStorageVolumeMass(out StorageVolume, out StorageMass);

            // ReSharper disable once UseObjectOrCollectionInitializer
            m_blockCountRequirements = new Dictionary <SupportedBlockTypes, BlockRequirement>(SupportedBlockTypesEquality.Instance);
            // living quarters.
            var addlLiving = Faction.AttributeWeight(ProceduralFactionSpeciality.Housing);

            m_blockCountRequirements[SupportedBlockTypes.CryoChamber]    = Population + Math.Max(0, (int)Math.Round(sqrtPopulation * (tmpRandom.NextNormal(1, 2) + addlLiving)));
            m_blockCountRequirements[SupportedBlockTypes.MedicalRoom]    = new BlockRequirement(Math.Max(1, (int)Math.Round(sqrtPopulation * (tmpRandom.NextNormal(0.5, 0.5) + Math.Max(addlLiving, Faction.AttributeWeight(ProceduralFactionSpeciality.Military))))), 1e3);
            m_blockCountRequirements[SupportedBlockTypes.ShipController] = Math.Max(1, (int)Math.Round(Population * tmpRandom.NextNormal(0.5, 0.5)));
            // how "defensive" this group is
            m_blockCountRequirements[SupportedBlockTypes.Weapon] = Math.Max(0, (int)Math.Round(Population * tmpRandom.NextNormal(2, 2) * Faction.AttributeWeight(ProceduralFactionSpeciality.Military)));
            // ship repair?
            m_blockCountRequirements[SupportedBlockTypes.ShipConstruction] = Math.Max(0, (int)Math.Round(Population * tmpRandom.NextNormal(4, 3) * Faction.AttributeWeight(ProceduralFactionSpeciality.Repair)));
            // docking?
            m_blockCountRequirements[SupportedBlockTypes.Docking] = Math.Max(1, (int)Math.Round(sqrtPopulation * tmpRandom.NextNormal(1, 1) * Faction.AttributeWeight(ProceduralFactionSpeciality.Housing)));
            // comms?
            m_blockCountRequirements[SupportedBlockTypes.Communications] = new BlockRequirement(Math.Max(1, (int)Math.Round(sqrtPopulation * MyMath.Clamp((float)tmpRandom.NextNormal(), 0, 1))), 1e6);

            Orientation = tmpRandom.NextQuaternion();
            // Branched, since we want to consume a quaternion from the random.
            if (orientation != null)
            {
                Orientation = orientation.Value;
            }
        }