Unstable 0.17.2.0

This commit is contained in:
Markus Isberg
2022-03-18 04:20:02 +09:00
parent 6d410cc1b7
commit cefac171f5
33 changed files with 214 additions and 135 deletions
@@ -162,7 +162,9 @@ namespace Barotrauma
CloseEnough = reach,
DialogueIdentifier = Leak.FlowTargetHull != null ? "dialogcannotreachleak".ToIdentifier() : Identifier.Empty,
TargetName = Leak.FlowTargetHull?.DisplayName,
requiredCondition = () => Leak.Submarine == character.Submarine,
requiredCondition = () =>
Leak.Submarine == character.Submarine &&
(Leak.FlowTargetHull != null && character.CurrentHull == Leak.FlowTargetHull || character.CanSeeTarget(Leak)),
// The Go To objective can be abandoned if the leak is fixed (in which case we don't want to use the dialogue)
SpeakCannotReachCondition = () => !CheckObjectiveSpecific()
},
@@ -21,13 +21,25 @@ namespace Barotrauma
public CharacterInfoPrefab(ContentXElement headsElement, XElement varsElement, XElement menuCategoryElement, XElement pronounsElement)
{
Heads = headsElement.Elements().Select(e => new CharacterInfo.HeadPreset(this, e)).ToImmutableArray();
VarTags = varsElement.Elements()
.Select(e =>
(e.GetAttributeIdentifier("var", ""),
e.GetAttributeIdentifierArray("tags", Array.Empty<Identifier>()).ToImmutableHashSet()))
.ToImmutableDictionary();
MenuCategoryVar = menuCategoryElement.GetAttributeIdentifier("var", Identifier.Empty);
Pronouns = pronounsElement.GetAttributeIdentifier("vars", Identifier.Empty);
if (varsElement != null)
{
VarTags = varsElement.Elements()
.Select(e =>
(e.GetAttributeIdentifier("var", ""),
e.GetAttributeIdentifierArray("tags", Array.Empty<Identifier>()).ToImmutableHashSet()))
.ToImmutableDictionary();
}
else
{
VarTags = new[]
{
("GENDER".ToIdentifier(),
new[] { "female".ToIdentifier(), "male".ToIdentifier() }.ToImmutableHashSet())
}.ToImmutableDictionary();
}
MenuCategoryVar = menuCategoryElement?.GetAttributeIdentifier("var", Identifier.Empty) ?? "GENDER".ToIdentifier();
Pronouns = pronounsElement?.GetAttributeIdentifier("vars", Identifier.Empty) ?? "GENDER".ToIdentifier();
}
public string ReplaceVars(string str, CharacterInfo.HeadPreset headPreset)
{
@@ -135,7 +147,13 @@ namespace Barotrauma
public string Tags
{
get { return string.Join(",", TagSet); }
private set { TagSet = value.Split(",").Select(s => s.ToIdentifier()).ToImmutableHashSet(); }
private set
{
TagSet = value.Split(",")
.Select(s => s.ToIdentifier())
.Where(id => !id.IsEmpty)
.ToImmutableHashSet();
}
}
[Serialize("0,0", IsPropertySaveable.No)]
@@ -149,6 +167,20 @@ namespace Barotrauma
{
characterInfoPrefab = charInfoPrefab;
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
DetermineTagsFromLegacyFormat(element);
}
private void DetermineTagsFromLegacyFormat(XElement element)
{
void addTag(string tag)
=> TagSet = TagSet.Add(tag.ToIdentifier());
string headId = element.GetAttributeString("id", "");
string gender = element.GetAttributeString("gender", "");
string race = element.GetAttributeString("race", "");
if (!headId.IsNullOrEmpty()) { addTag($"head{headId}"); }
if (!gender.IsNullOrEmpty()) { addTag(gender); }
if (!race.IsNullOrEmpty()) { addTag(race); }
}
}
@@ -438,12 +470,37 @@ namespace Barotrauma
public readonly ImmutableArray<(Color Color, float Commonness)> FacialHairColors;
public readonly ImmutableArray<(Color Color, float Commonness)> SkinColors;
private void GetName(ContentPath namesFile, Rand.RandSync randSync, out string name)
private void GetName(Rand.RandSync randSync, out string name)
{
XDocument doc = XMLExtensions.TryLoadXml(namesFile);
name = doc.Root.GetAttributeString("format", "");
var nameElement = CharacterConfigElement.GetChildElement("names") ?? CharacterConfigElement.GetChildElement("name");
ContentPath namesXmlFile = nameElement?.GetAttributeContentPath("path") ?? ContentPath.Empty;
XElement namesXml = null;
if (!namesXmlFile.IsNullOrEmpty()) //names.xml is defined
{
XDocument doc = XMLExtensions.TryLoadXml(namesXmlFile);
namesXml = doc.Root;
}
else //the legacy firstnames.txt/lastnames.txt shit is defined
{
namesXml = new XElement("names", new XAttribute("format", "[firstname] [lastname]"));
var firstNamesPath = ReplaceVars(nameElement.GetAttributeContentPath("firstname")?.Value ?? "");
var lastNamesPath = ReplaceVars(nameElement.GetAttributeContentPath("lastname")?.Value ?? "");
if (File.Exists(firstNamesPath) && File.Exists(lastNamesPath))
{
var firstNames = File.ReadAllLines(firstNamesPath);
var lastNames = File.ReadAllLines(lastNamesPath);
namesXml.Add(firstNames.Select(n => new XElement("firstname", new XAttribute("value", n))));
namesXml.Add(lastNames.Select(n => new XElement("lastname", new XAttribute("value", n))));
}
else //the files don't exist, just fall back to the vanilla names
{
XDocument doc = XMLExtensions.TryLoadXml("Content/Characters/Human/names.xml");
namesXml = doc.Root;
}
}
name = namesXml.GetAttributeString("format", "");
Dictionary<Identifier, List<string>> entries = new Dictionary<Identifier, List<string>>();
foreach (var subElement in doc.Root.Elements())
foreach (var subElement in namesXml.Elements())
{
Identifier elemName = subElement.NameAsIdentifier();
if (!entries.ContainsKey(elemName))
@@ -477,8 +534,21 @@ namespace Barotrauma
// talent-relevant values
public int MissionsCompletedSinceDeath = 0;
private static bool ElementHasSpecifierTags(XElement element)
=> element.GetAttributeBool("specifiertags",
element.GetAttributeBool("genders",
element.GetAttributeBool("races", false)));
// Used for creating the data
public CharacterInfo(Identifier speciesName, string name = "", string originalName = "", Either<Job, JobPrefab> jobOrJobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced, Identifier npcIdentifier = default)
public CharacterInfo(
Identifier speciesName,
string name = "",
string originalName = "",
Either<Job, JobPrefab> jobOrJobPrefab = null,
string ragdollFileName = null,
int variant = 0,
Rand.RandSync randSync = Rand.RandSync.Unsynced,
Identifier npcIdentifier = default)
{
JobPrefab jobPrefab = null;
Job job = null;
@@ -494,7 +564,7 @@ namespace Barotrauma
CharacterConfigElement = CharacterPrefab.FindBySpeciesName(SpeciesName)?.ConfigElement;
if (CharacterConfigElement == null) { return; }
// TODO: support for variants
HasSpecifierTags = CharacterConfigElement.GetAttributeBool("specifiertags", false);
HasSpecifierTags = ElementHasSpecifierTags(CharacterConfigElement);
if (HasSpecifierTags)
{
HairColors = CharacterConfigElement.GetAttributeTupleArray("haircolors", new (Color, float)[] { (Color.WhiteSmoke, 100f) }).ToImmutableArray();
@@ -537,12 +607,7 @@ namespace Barotrauma
public string GetRandomName(Rand.RandSync randSync)
{
string name = "";
var nameElement = CharacterConfigElement.GetChildElement("names");
if (nameElement != null)
{
GetName(nameElement.GetAttributeContentPath("path") ?? ContentPath.Empty, randSync, out name);
}
GetName(randSync, out string name);
return name;
}
@@ -623,7 +688,7 @@ namespace Barotrauma
if (element == null) { return; }
// TODO: support for variants
CharacterConfigElement = element;
HasSpecifierTags = CharacterConfigElement.GetAttributeBool("specifiertags", false);
HasSpecifierTags = ElementHasSpecifierTags(CharacterConfigElement);
if (HasSpecifierTags)
{
RecreateHead(
@@ -647,7 +712,7 @@ namespace Barotrauma
var nameElement = CharacterConfigElement.GetChildElement("names");
if (nameElement != null)
{
GetName(nameElement.GetAttributeContentPath("path") ?? ContentPath.Empty, Rand.RandSync.ServerAndClient, out Name);
GetName(Rand.RandSync.ServerAndClient, out Name);
}
}
}
@@ -36,7 +36,7 @@ namespace Barotrauma
var menuCategoryElement = ConfigElement.GetChildElement("MenuCategory");
var pronounsElement = ConfigElement.GetChildElement("Pronouns");
if (headsElement != null && varsElement != null && menuCategoryElement != null && pronounsElement != null)
if (headsElement != null)
{
CharacterInfoPrefab = new CharacterInfoPrefab(headsElement, varsElement, menuCategoryElement, pronounsElement);
}
@@ -380,20 +380,24 @@ namespace Barotrauma
public string Tags
{
get { return string.Join(',', TagSet); }
private set { TagSet = value.Split(',').ToIdentifiers().ToImmutableHashSet(); }
private set
{
TagSet = value.Split(',')
.ToIdentifiers()
.Where(id => !id.IsEmpty)
.ToImmutableHashSet();
}
}
public ImmutableHashSet<Identifier> TagSet { get; private set; }
public SoundParams(ContentXElement element, CharacterParams character) : base(element, character)
{
HashSet<Identifier> tags = TagSet.ToHashSet();
Identifier genderFallback = element.GetAttributeIdentifier("gender", "");
if (genderFallback != Identifier.Empty && genderFallback != "None")
{
tags.Add(genderFallback);
TagSet = TagSet.Add(genderFallback);
}
TagSet = tags.ToImmutableHashSet();
}
}
@@ -446,7 +446,7 @@ namespace Barotrauma
#elif CLIENT
return characters;
#endif
static bool IsAlive(Character c) { return c.Info != null && !c.IsDead; }
static bool IsAlive(Character c) { return c?.Info != null && !c.IsDead; }
}
@@ -58,7 +58,7 @@ namespace Barotrauma
IsEquipped = new bool[capacity];
SlotTypes = new InvSlotType[capacity];
AccessibleWhenAlive = element.GetAttributeBool("accessiblewhenalive", false);
AccessibleWhenAlive = element.GetAttributeBool("accessiblewhenalive", character.Info != null);
AccessibleByOwner = element.GetAttributeBool("accessiblebyowner", AccessibleWhenAlive);
string[] slotTypeNames = ParseSlotTypes(element);
@@ -370,7 +370,7 @@ namespace Barotrauma.Items.Components
public Item GetFocusTarget()
{
var positionOut = item.Connections.Find(c => c.Name == "position_out");
var positionOut = item.Connections?.Find(c => c.Name == "position_out");
if (positionOut == null) { return null; }
item.SendSignal(new Signal(MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture), sender: user), positionOut);
@@ -57,7 +57,7 @@ namespace Barotrauma.Items.Components
}
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should structures cast shadows when light from this light source hits them. " +
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range.", alwaysUseInstanceValues: true)]
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range. Lights that are set to be drawn behind subs don't cast shadows, regardless of this setting.", alwaysUseInstanceValues: true)]
public bool CastShadows
{
get { return castShadows; }
@@ -407,8 +407,6 @@ namespace Barotrauma
Loaded = this;
Generating = true;
Rand.Tracker.Reset();
Rand.Tracker.Active = true;
EqualityCheckValues.Clear();
EntitiesBeforeGenerate = GetEntities().ToList();
EntityCountBeforeGenerate = EntitiesBeforeGenerate.Count();
@@ -1305,8 +1303,6 @@ namespace Barotrauma
//assign an ID to make entity events work
//ID = FindFreeID();
Generating = false;
Rand.Tracker.Active = false;
File.WriteAllLines(GameMain.NetworkMember is { IsServer: true } ? "serverrng.txt" : "clientrng.txt", Rand.Tracker.LogMsgs);
}
private List<Point> GeneratePathNodes(Point startPosition, Point endPosition, Rectangle pathBorders, Tunnel parentTunnel, float variance)
@@ -2443,7 +2439,6 @@ namespace Barotrauma
fixedResources.Add((itemPrefab, fixedQuantityResourceInfo));
}
}
levelResources.Sort((x, y) => x.commonness.CompareTo(y.commonness));
DebugConsole.Log("Generating level resources...");
var allValidLocations = GetAllValidClusterLocations();
@@ -2473,29 +2468,33 @@ namespace Barotrauma
//place some of the least common resources in the abyss
AbyssResources.Clear();
for (int j = 0; j < levelResources.Count && j < 5; j++)
{
for (int i = 0; i < 10; i++)
{
var (itemPrefab, commonness) = levelResources[j];
var location = allValidLocations.GetRandom(l =>
{
if (l.Cell == null || l.Edge == null) { return false; }
if (l.EdgeCenter.Y > AbyssArea.Bottom) { return false; }
l.InitializeResources();
return l.Resources.Count <= GetMaxResourcesOnEdge(itemPrefab, l, out _);
}, randSync: Rand.RandSync.ServerAndClient);
if (location.Cell == null || location.Edge == null) { break; }
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.ServerAndClient);
PlaceResources(itemPrefab, clusterSize, location, out var abyssResources);
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
abyssClusterLocation.Resources.AddRange(abyssResources);
AbyssResources.Add(abyssClusterLocation);
var locationIndex = allValidLocations.FindIndex(l => l.Equals(location));
allValidLocations.RemoveAt(locationIndex);
}
}
int abyssClusterCount = (int)MathHelper.Lerp(GenerationParams.AbyssResourceClustersMin, GenerationParams.AbyssResourceClustersMax, Difficulty / 100.0f);
for (int i = 0; i < abyssClusterCount; i++)
{
//use inverse commonness to select the abyss resources (the rarest ones are the most common in the abyss)
var selectedPrefab = ToolBox.SelectWeightedRandom(
levelResources.Select(it => it.itemPrefab).ToList(),
levelResources.Select(it => it.commonness <= 0.0f ? 0.0f : 1.0f / it.commonness).ToList(),
Rand.RandSync.ServerAndClient);
var location = allValidLocations.GetRandom(l =>
{
if (l.Cell == null || l.Edge == null) { return false; }
if (l.EdgeCenter.Y > AbyssArea.Bottom) { return false; }
l.InitializeResources();
return l.Resources.Count <= GetMaxResourcesOnEdge(selectedPrefab, l, out _);
}, randSync: Rand.RandSync.ServerAndClient);
if (location.Cell == null || location.Edge == null) { break; }
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.ServerAndClient);
PlaceResources(selectedPrefab, clusterSize, location, out var abyssResources);
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
abyssClusterLocation.Resources.AddRange(abyssResources);
AbyssResources.Add(abyssClusterLocation);
var locationIndex = allValidLocations.FindIndex(l => l.Equals(location));
allValidLocations.RemoveAt(locationIndex);
}
PathPoints.Clear();
nextPathPointId = 0;
@@ -2603,7 +2602,14 @@ namespace Barotrauma
#if DEBUG
DebugConsole.NewMessage("Level resources spawned: " + itemCount + "\n" +
"Spawn points containing resources: " + PathPoints.Where(p => p.ClusterLocations.Any()).Count() + "/" + PathPoints.Count);
" Spawn points containing resources: " + PathPoints.Where(p => p.ClusterLocations.Any()).Count() + "/" + PathPoints.Count + "\n" +
" Total value: "+ PathPoints.Sum(p => p.ClusterLocations.Sum(c => c.Resources.Sum(r => r.Prefab.DefaultPrice?.Price ?? 0)))+" mk");
if (AbyssResources.Count > 0)
{
DebugConsole.NewMessage("Abyss resources spawned: " + AbyssResources.Sum(a => a.Resources.Count) + "\n" +
" Total value: " + AbyssResources.Sum(c => c.Resources.Sum(r => r.Prefab.DefaultPrice?.Price ?? 0)) + " mk");
}
#endif
DebugConsole.Log("Level resources generated");
@@ -387,6 +387,20 @@ namespace Barotrauma
set;
}
[Serialize(3, IsPropertySaveable.Yes, description: "Minimum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
public int AbyssResourceClustersMin
{
get;
set;
}
[Serialize(20, IsPropertySaveable.Yes, description: "Maximum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
public int AbyssResourceClustersMax
{
get;
set;
}
[Serialize(-300000, IsPropertySaveable.Yes, description: "How far below the level the sea floor is placed."), Editable(MinValueFloat = Level.MaxEntityDepth, MaxValueFloat = 0.0f)]
public int SeaFloorDepth
{
@@ -11,38 +11,6 @@ namespace Barotrauma
{
public static class Rand
{
[Obsolete("TODO: remove")]
public static class Tracker
{
private readonly static List<string> logMsgs = new List<string>();
public static IReadOnlyList<string> LogMsgs => logMsgs;
public static bool Active = false;
public static void Reset()
{
logMsgs.Clear();
Active = false;
}
public static void RegisterCall(int stDepth=4)
{
if (!Active) { return; }
var st = new StackTrace(skipFrames: 2, fNeedFileInfo: true);
var frames = st.GetFrames();
string msg = string.Join("; ",
frames.Take(stDepth).Select(f =>
$"{Path.GetFileNameWithoutExtension(f.GetFileName())}:{f.GetFileLineNumber()}"));
logMsgs.Add(msg);
}
public static void Log(string msg)
{
if (!Active) { return; }
logMsgs.Add(msg);
}
}
public enum RandSync
{
Unsynced, //not synced, used for unimportant details like minor particle properties
@@ -81,8 +49,6 @@ namespace Barotrauma
public static int ThreadId = 0;
private static void CheckRandThreadSafety(RandSync sync)
{
if (sync == RandSync.ServerAndClient) { Tracker.RegisterCall(); }
if (ThreadId != 0 && sync == RandSync.Unsynced)
{
if (System.Threading.Thread.CurrentThread.ManagedThreadId != ThreadId)