Unstable 0.17.2.0
This commit is contained in:
+3
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user