Client LuaCsForBarotrauma
LocationTypeChange.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using System.Linq;
6 using System.Xml.Linq;
7 using static Barotrauma.LocationTypeChange;
8 
9 namespace Barotrauma
10 {
12  {
13  public class Requirement
14  {
15  public enum FunctionType
16  {
17  Add,
18  Multiply
19  }
20 
21  public readonly FunctionType Function;
22 
26  public readonly ImmutableArray<Identifier> RequiredLocations;
27 
31  public readonly int RequiredProximity;
32 
36  public readonly float Probability;
37 
42 
46  public readonly float ProximityProbabilityIncrease;
47 
51  public readonly bool RequireBeaconStation;
52 
56  public readonly bool RequireHuntingGrounds;
57 
59  {
60  RequiredLocations = element.GetAttributeIdentifierArray("requiredlocations", element.GetAttributeIdentifierArray("requiredadjacentlocations", Array.Empty<Identifier>())).ToImmutableArray();
61  RequiredProximity = Math.Max(element.GetAttributeInt("requiredproximity", 1), 0);
62  ProximityProbabilityIncrease = element.GetAttributeFloat("proximityprobabilityincrease", 0.0f);
63  RequiredProximityForProbabilityIncrease = element.GetAttributeInt("requiredproximityforprobabilityincrease", -1);
64  RequireBeaconStation = element.GetAttributeBool("requirebeaconstation", false);
65  RequireHuntingGrounds = element.GetAttributeBool("requirehuntinggrounds", false);
66 
67  string functionStr = element.GetAttributeString("function", "Add");
68  if (!Enum.TryParse(functionStr, ignoreCase: true, out Function))
69  {
70  DebugConsole.ThrowError(
71  $"Invalid location type change in location type \"{change.CurrentType}\". " +
72  $"\"{functionStr}\" is not a valid function.");
73  }
74 
75  Probability = element.GetAttributeFloat("probability", 1.0f);
76 
78  {
80  {
81  DebugConsole.AddWarning(
82  $"Invalid location type change in location type \"{change.CurrentType}\". " +
83  "Probability is configured to increase when near some other type of location, but the RequiredLocations attribute is not set.",
84  element.ContentPackage);
85  }
86  if (Probability >= 1.0f)
87  {
88  DebugConsole.AddWarning(
89  $"Invalid location type change in location type \"{change.CurrentType}\". " +
90  "Probability is configured to increase when near some other type of location, but the base probability is already 100%",
91  element.ContentPackage);
92  }
93  }
94  }
95 
96  public bool AnyWithinDistance(Location startLocation, int distance)
97  {
99  startLocation,
100  maxDistance: distance,
101  criteria: MatchesLocation,
102  connectionCriteria: MatchesConnection);
103  }
104 
105  public bool MatchesLocation(Location location)
106  {
107  return RequiredLocations.Contains(location.Type.Identifier) && !location.IsCriticallyRadiated();
108  }
109 
110  public bool MatchesConnection(LocationConnection connection)
111  {
113  {
114  return true;
115  }
117  {
118  return true;
119  }
120  return false;
121  }
122  }
123 
124  public readonly Identifier CurrentType;
125 
126  public readonly Identifier ChangeToType;
127 
131  public readonly float Probability;
132 
133  public readonly bool RequireDiscovered;
134 
135  public List<Requirement> Requirements = new List<Requirement>();
136 
137  private readonly bool requireChangeMessages;
138  private readonly string messageTag;
139 
140  public IReadOnlyList<string> GetMessages(Faction faction)
141  {
142  if (faction != null && TextManager.ContainsTag(messageTag + "." + faction.Prefab.Identifier))
143  {
144  return TextManager.GetAll(messageTag + "." + faction.Prefab.Identifier).ToImmutableArray();
145  }
146 
147  if (TextManager.ContainsTag(messageTag))
148  {
149  return TextManager.GetAll(messageTag).ToImmutableArray();
150  }
151  else
152  {
153  if (requireChangeMessages)
154  {
155  DebugConsole.ThrowError($"No messages defined for the location type change {CurrentType} -> {ChangeToType}");
156  }
157  return Enumerable.Empty<string>().ToImmutableArray();
158  }
159  }
160 
164  public readonly ImmutableArray<Identifier> DisallowedAdjacentLocations;
165 
169  public readonly int DisallowedProximity;
170 
174  public readonly int CooldownAfterChange;
175 
176  public readonly Point RequiredDurationRange;
177 
178  public LocationTypeChange(Identifier currentType, ContentXElement element, bool requireChangeMessages, float defaultProbability = 0.0f)
179  {
180  CurrentType = currentType;
181  ChangeToType = element.GetAttributeIdentifier("type", element.GetAttributeIdentifier("to", ""));
182 
183  RequireDiscovered = element.GetAttributeBool("requirediscovered", false);
184 
185  DisallowedAdjacentLocations = element.GetAttributeIdentifierArray("disallowedadjacentlocations", Array.Empty<Identifier>()).ToImmutableArray();
186  DisallowedProximity = Math.Max(element.GetAttributeInt("disallowedproximity", 1), 1);
187 
188  RequiredDurationRange = element.GetAttributePoint("requireddurationrange", Point.Zero);
189 
190  Probability = element.GetAttributeFloat("probability", defaultProbability);
191 
192  CooldownAfterChange = Math.Max(element.GetAttributeInt("cooldownafterchange", 0), 0);
193 
194  //backwards compatibility
195  if (element.GetAttribute("requiredlocations") != null)
196  {
197  Requirements.Add(new Requirement(element, this));
198  }
199 
200  //backwards compatibility
201  if (element.GetAttribute("requiredduration") != null)
202  {
203  RequiredDurationRange = new Point(element.GetAttributeInt("requiredduration", 0));
204  }
205 
206  this.requireChangeMessages = requireChangeMessages;
207  messageTag = element.GetAttributeString("messagetag", "LocationChange." + currentType + ".ChangeTo." + ChangeToType);
208 
209  foreach (var subElement in element.Elements())
210  {
211  if (subElement.Name.ToString().Equals("requirement", StringComparison.OrdinalIgnoreCase))
212  {
213  Requirements.Add(new Requirement(subElement, this));
214  }
215  }
216  }
217 
218  public float DetermineProbability(Location location)
219  {
220  if (RequireDiscovered && !location.Discovered) { return 0.0f; }
221  if (location.IsCriticallyRadiated()) { return 0.0f; }
222  if (location.LocationTypeChangeCooldown > 0) { return 0.0f; }
223  if (location.IsGateBetweenBiomes) { return 0.0f; }
224 
225  if (DisallowedAdjacentLocations.Any() &&
227  (otherLocation) => { return DisallowedAdjacentLocations.Contains(otherLocation.Type.Identifier); }))
228  {
229  return 0.0f;
230  }
231 
232  float probability = Probability;
233  foreach (Requirement requirement in Requirements)
234  {
235  if (requirement.AnyWithinDistance(location, requirement.RequiredProximity))
236  {
237  if (requirement.Function == Requirement.FunctionType.Add)
238  {
239  probability += requirement.Probability;
240  }
241  else
242  {
243  probability *= requirement.Probability;
244  }
245  }
246  if (location.ProximityTimer.ContainsKey(requirement))
247  {
248  if (requirement.AnyWithinDistance(location, requirement.RequiredProximityForProbabilityIncrease))
249  {
250  if (requirement.Function == Requirement.FunctionType.Add)
251  {
252  probability += requirement.ProximityProbabilityIncrease * location.ProximityTimer[requirement];
253  }
254  else
255  {
256  probability *= requirement.ProximityProbabilityIncrease * location.ProximityTimer[requirement];
257  }
258  }
259  }
260  }
261 
262  return probability;
263  }
264  }
265 }
string? GetAttributeString(string key, string? def)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
Point GetAttributePoint(string key, in Point def)
float GetAttributeFloat(string key, float def)
ContentPackage? ContentPackage
bool GetAttributeBool(string key, bool def)
int GetAttributeInt(string key, int def)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
FactionPrefab Prefab
Definition: Factions.cs:18
LocationType Type
Definition: Location.cs:91
bool IsGateBetweenBiomes
Definition: Location.cs:512
bool IsCriticallyRadiated()
Definition: Location.cs:1069
int LocationTypeChangeCooldown
Definition: Location.cs:78
readonly float Probability
Base probability per turn for the location to change if near one of the RequiredLocations
Requirement(ContentXElement element, LocationTypeChange change)
bool MatchesConnection(LocationConnection connection)
readonly bool RequireHuntingGrounds
Does there need to be hunting grounds within RequiredProximity
readonly float ProximityProbabilityIncrease
How much the probability increases per turn if within RequiredProximityForProbabilityIncrease steps o...
readonly int RequiredProximityForProbabilityIncrease
How close the location needs to be to one of the RequiredLocations for the probability to increase
readonly ImmutableArray< Identifier > RequiredLocations
The change can only happen if there's at least one of the given types of locations near this one
readonly bool RequireBeaconStation
Does there need to be a beacon station within RequiredProximity
bool AnyWithinDistance(Location startLocation, int distance)
readonly int RequiredProximity
How close the location needs to be to one of the RequiredLocations for the change to occur
readonly float Probability
Base probability per turn for the location to change if near one of the RequiredLocations
readonly int CooldownAfterChange
The location can't change it's type for this many turns after this location type changes occurs
readonly int DisallowedProximity
How close the location needs to be to one of the DisallowedAdjacentLocations for the change to be dis...
LocationTypeChange(Identifier currentType, ContentXElement element, bool requireChangeMessages, float defaultProbability=0.0f)
IReadOnlyList< string > GetMessages(Faction faction)
float DetermineProbability(Location location)
readonly ImmutableArray< Identifier > DisallowedAdjacentLocations
The change can't happen if there's one or more of the given types of locations near this one
static bool LocationOrConnectionWithinDistance(Location startLocation, int maxDistance, Func< Location, bool > criteria, Func< LocationConnection, bool > connectionCriteria=null)
readonly Identifier Identifier
Definition: Prefab.cs:34