3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
7 using System.Diagnostics;
8 using System.Globalization;
17 private readonly ImmutableArray<string> rawNames;
18 private readonly ImmutableArray<Sprite> portraits;
21 private readonly ImmutableArray<(
Identifier Identifier,
float Commonness,
bool AlwaysAvailableIfMissingFromCrew)> hireableJobs;
22 private readonly
float totalHireableWeight;
25 public readonly Dictionary<int, int>
MinCountPerZone =
new Dictionary<int, int>();
36 public readonly List<LocationTypeChange>
CanChangeTo =
new List<LocationTypeChange>();
58 private readonly ImmutableArray<Identifier>? nameIdentifiers =
null;
62 private ImmutableArray<string>? nameFormats =
null;
67 if (nameFormats ==
null || GameSettings.CurrentConfig.Language != nameFormatLanguage)
69 nameFormats = TextManager.GetAll($
"LocationNameFormat.{Identifier}").ToImmutableArray();
70 nameFormatLanguage = GameSettings.CurrentConfig.Language;
78 get {
return hireableJobs.Any(); }
102 private readonly
Identifier forceOutpostGenerationParamsIdentifier;
172 var names =
new List<string>();
175 if (rawNamePaths.Any())
177 foreach (
string rawPath
in rawNamePaths)
182 names.AddRange(File.ReadAllLines(path.Value).ToList());
186 DebugConsole.ThrowError($
"Failed to read name file \"rawPath\" for location type \"{Identifier}\"!", e);
191 names.Add(
"ERROR: No names found");
193 this.rawNames = names.ToImmutableArray();
202 foreach (
string commonnessPerZoneStr
in commonnessPerZoneStrs)
204 string[] splitCommonnessPerZone = commonnessPerZoneStr.Split(
':');
205 if (splitCommonnessPerZone.Length != 2 ||
206 !
int.TryParse(splitCommonnessPerZone[0].Trim(), out
int zoneIndex) ||
207 !
float.TryParse(splitCommonnessPerZone[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out
float zoneCommonness))
209 DebugConsole.ThrowError(
"Failed to read commonness values for location type \"" +
Identifier +
"\" - commonness should be given in the format \"zone1index: zone1commonness, zone2index: zone2commonness\"");
215 string[] minCountPerZoneStrs = element.GetAttributeStringArray(
"mincountperzone", Array.Empty<
string>());
216 foreach (
string minCountPerZoneStr
in minCountPerZoneStrs)
218 string[] splitMinCountPerZone = minCountPerZoneStr.Split(
':');
219 if (splitMinCountPerZone.Length != 2 ||
220 !
int.TryParse(splitMinCountPerZone[0].Trim(), out
int zoneIndex) ||
221 !
int.TryParse(splitMinCountPerZone[1].Trim(), out
int minCount))
223 DebugConsole.ThrowError(
"Failed to read minimum count values for location type \"" +
Identifier +
"\" - minimum counts should be given in the format \"zone1index: zone1mincount, zone2index: zone2mincount\"");
228 var portraits =
new List<Sprite>();
229 var hireableJobs =
new List<(Identifier, float, bool)>();
230 foreach (var subElement
in element.Elements())
232 switch (subElement.Name.ToString().ToLowerInvariant())
236 float jobCommonness = subElement.GetAttributeFloat(
"commonness", 1.0f);
237 bool availableIfMissing = subElement.GetAttributeBool(
"AlwaysAvailableIfMissingFromCrew",
false);
238 totalHireableWeight += jobCommonness;
239 hireableJobs.Add((jobIdentifier, jobCommonness, availableIfMissing));
243 SpriteColor = subElement.GetAttributeColor(
"color", Color.White);
245 case "radiationsymbol":
252 var portrait =
new Sprite(subElement, lazyLoad:
true);
253 if (portrait !=
null)
255 portraits.Add(portrait);
270 this.portraits = portraits.ToImmutableArray();
271 this.hireableJobs = hireableJobs.ToImmutableArray();
278 var missingJobs = hireableJobs
279 .Where(j => j.AlwaysAvailableIfMissingFromCrew)
281 if (missingJobs.Any())
283 foreach (var missingJob
in missingJobs)
296 Identifier selectedJobId = hireableJobs.GetRandomByWeight(j => j.Commonness, Rand.RandSync.ServerAndClient).Identifier;
306 if (portraits.Length == 0) {
return null; }
307 return portraits[Math.Abs(randomSeed) % portraits.Length];
312 if (nameIdentifiers ==
null)
316 List<Identifier> nameIds =
new List<Identifier>();
317 foreach (var nameId
in nameIdentifiers)
322 Identifier tag = $
"LocationName.{nameId}.{index}".ToIdentifier();
323 if (TextManager.ContainsTag(tag, TextManager.DefaultLanguage))
332 DebugConsole.ThrowError($
"Could not find any location names for the location type {Identifier}. Name identifier: {nameId}");
342 if (existingLocations !=
null)
344 var unusedNameIds = nameIds.FindAll(nameId => existingLocations.None(l => l.NameIdentifier == nameId));
345 if (unusedNameIds.Count > 0)
347 return unusedNameIds[rand.Next() % unusedNameIds.Count];
350 return nameIds[rand.Next() % nameIds.Count];
358 if (rawNames ==
null || rawNames.None()) {
return string.Empty; }
359 if (existingLocations !=
null)
361 var unusedNames = rawNames.Where(name => !existingLocations.Any(l => l.DisplayName.Value == name)).ToList();
362 if (unusedNames.Count > 0)
364 return unusedNames[rand.Next() % unusedNames.Count];
367 return rawNames[rand.Next() % rawNames.Length];
370 public static LocationType Random(
Random rand,
int? zone =
null,
bool requireOutpost =
false, Func<LocationType, bool> predicate =
null)
372 Debug.Assert(
Prefabs.Any(),
"LocationType.list.Count == 0, you probably need to initialize LocationTypes");
376 (predicate ==
null || predicate(lt)) && IsValid(lt))
377 .OrderBy(p => p.UintIdentifier).ToArray();
381 if (requireOutpost && !lt.
HasOutpost) {
return false; }
395 if (allowedLocationTypes.Length == 0)
397 DebugConsole.ThrowError(
"Could not generate a random location type - no location types for the zone " + zone +
" found!");
402 return ToolBox.SelectWeightedRandom(
403 allowedLocationTypes,
404 allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToArray(),
409 return allowedLocationTypes[rand.Next() % allowedLocationTypes.Length];
static ContentPath FromRaw(string? rawValue)
string? GetAttributeString(string key, string? def)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
float GetAttributeFloat(string key, float def)
ContentPackage? ContentPackage
bool GetAttributeBool(string key, bool def)
string?[] GetAttributeStringArray(string key, string[]? def, bool convertToLowerInvariant=false)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
IEnumerable< CharacterInfo > GetCharacterInfos()
Note: this only returns AI characters' infos in multiplayer. The infos are used to manage hiring/firi...
static GameSession?? GameSession
static readonly PrefabCollection< JobPrefab > Prefabs
readonly LocalizedString Description
readonly CharacterTeamType OutpostTeam
float RequestGoodPriceModifier
readonly ImmutableArray< Identifier > MissionIdentifiers
JobPrefab GetRandomHireable()
readonly Identifier ForceLocationName
IReadOnlyList< string > NameFormats
Identifier SecondaryFaction
If set, forces the location to be assigned to this secondary faction. Set to "None" if you don't want...
Sprite GetPortrait(int randomSeed)
float StoreSellPriceModifier
readonly List< string > HideEntitySubcategories
readonly ImmutableArray< Identifier > MissionTags
static LocationType Random(Random rand, int? zone=null, bool requireOutpost=false, Func< LocationType, bool > predicate=null)
readonly Dictionary< int, int > MinCountPerZone
readonly float BeaconStationChance
readonly LocalizedString Name
LocationType(ContentXElement element, LocationTypesFile file)
readonly List< LocationTypeChange > CanChangeTo
float StoreMaxReputationModifier
readonly Dictionary< int, float > CommonnessPerZone
OutpostGenerationParams GetForcedOutpostGenerationParams()
bool IgnoreGenericEvents
If set to true, only event sets that explicitly define this location type in EventSet....
override string ToString()
static readonly PrefabCollection< LocationType > Prefabs
readonly bool ShowSonarMarker
int StorePriceModifierRange
In percentages
bool AllowInRandomLevels
Can this location type be used in the random, non-campaign levels that don't take place in any specif...
float DailySpecialPriceModifier
IEnumerable< JobPrefab > GetHireablesMissingFromCrew()
bool UsePortraitInRandomLoadingScreens
Identifier GetRandomNameId(Random rand, IEnumerable< Location > existingLocations)
bool HasHireableCharacters
string GetRandomRawName(Random rand, IEnumerable< Location > existingLocations)
For backwards compatibility. Chooses a random name from the names defined in the ....
Identifier ReplaceInRadiation
static readonly PrefabCollection< OutpostGenerationParams > OutpostParams
readonly Identifier Identifier
Prefab that has a property serves as a deterministic hash of a prefab's identifier....