Unstable 0.17.7.0

This commit is contained in:
Markus Isberg
2022-04-08 00:34:17 +09:00
parent 95764d1fa8
commit 164d72ae3a
82 changed files with 852 additions and 385 deletions
@@ -62,9 +62,9 @@ namespace Barotrauma
private float enemycheckTimer;
/// <summary>
/// How far other characters can hear reports done by this character (e.g. reports for fires, intruders).
/// How far other characters can hear reports done by this character (e.g. reports for fires, intruders). Defaults to infinity.
/// </summary>
public float ReportRange { get; set; }
public float ReportRange { get; set; } = float.PositiveInfinity;
private float _aimSpeed = 1;
public float AimSpeed
@@ -166,7 +166,6 @@ namespace Barotrauma
objectiveManager = new AIObjectiveManager(c);
reactTimer = GetReactionTime();
SortTimer = Rand.Range(0f, sortObjectiveInterval);
ReportRange = Character.IsOnPlayerTeam ? float.PositiveInfinity : 1000;
}
public override void Update(float deltaTime)
@@ -269,6 +269,18 @@ namespace Barotrauma
if (!character.AnimController.InWater || character.Submarine != null) { return; }
if (CurrentPath == null || CurrentPath.Unreachable || CurrentPath.Finished) { return; }
if (CurrentPath.CurrentIndex < 0 || CurrentPath.CurrentIndex >= CurrentPath.Nodes.Count - 1) { return; }
var lastNode = CurrentPath.Nodes.Last();
Submarine targetSub = lastNode.Submarine;
if (targetSub != null)
{
float subSize = Math.Max(targetSub.Borders.Size.X, targetSub.Borders.Size.Y) / 2;
float margin = 500;
if (Vector2.DistanceSquared(character.WorldPosition, targetSub.WorldPosition) < MathUtils.Pow2(subSize + margin))
{
// Don't skip nodes when close to the target submarine.
return;
}
}
// Check if we could skip ahead to NextNode when the character is swimming and using waypoints outside.
// Do this to optimize the old path before creating and evaluating a new path.
// In general, this is to avoid behavior where:
@@ -280,7 +292,7 @@ namespace Barotrauma
{
var waypoint = CurrentPath.Nodes[i];
float directDistance = Vector2.DistanceSquared(character.WorldPosition, waypoint.WorldPosition);
if (directDistance > (pathDistance * pathDistance) || Submarine.PickBody(host.SimPosition, waypoint.SimPosition, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
if (directDistance > MathUtils.Pow2(pathDistance) || !character.CanSeeTarget(waypoint))
{
pathDistance -= CurrentPath.GetLength(startIndex: i - 1, endIndex: i);
continue;
@@ -336,6 +348,7 @@ namespace Barotrauma
return Vector2.Zero;
}
Vector2 pos = host.WorldPosition;
Vector2 diff = currentPath.CurrentNode.WorldPosition - pos;
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
// Only humanoids can climb ladders
bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands;
@@ -346,7 +359,7 @@ namespace Barotrauma
}
Ladder nextLadder = GetNextLadder();
var ladders = currentLadder ?? nextLadder;
bool useLadders = canClimb && ladders != null && (!isDiving || Math.Abs(steering.X) < 0.1f && steering.Y > 1);
bool useLadders = canClimb && ladders != null && steering.LengthSquared() > 0.1f && (!isDiving || steering.Y > 1);
if (useLadders && character.SelectedConstruction != ladders.Item)
{
if (character.CanInteractWith(ladders.Item))
@@ -374,7 +387,6 @@ namespace Barotrauma
}
if (character.IsClimbing && useLadders)
{
Vector2 diff = currentPath.CurrentNode.WorldPosition - pos;
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
if (nextLadderSameAsCurrent || currentLadder != null && nextLadder != null && Math.Abs(currentLadder.Item.Position.X - nextLadder.Item.Position.X) < 50)
{
@@ -398,7 +410,7 @@ namespace Barotrauma
else if (nextLadder != null && !nextLadderSameAsCurrent)
{
// Try to change the ladder (hatches between two submarines)
if (character.SelectedConstruction != nextLadder.Item && nextLadder.Item.IsInsideTrigger(character.WorldPosition))
if (character.SelectedConstruction != nextLadder.Item && character.CanInteractWith(nextLadder.Item))
{
if (nextLadder.Item.TryInteract(character, forceSelectKey: true))
{
@@ -406,7 +418,7 @@ namespace Barotrauma
}
}
}
if (isAboveFloor || nextLadderSameAsCurrent)
if (isAboveFloor || nextLadderSameAsCurrent || nextLadder == null && Math.Abs(diff.Y) < 10)
{
NextNode(!doorsChecked);
}
@@ -484,7 +496,7 @@ namespace Barotrauma
{
return Vector2.Zero;
}
return ConvertUnits.ToSimUnits(currentPath.CurrentNode.WorldPosition - pos);
return ConvertUnits.ToSimUnits(diff);
}
private void NextNode(bool checkDoors)
@@ -212,10 +212,14 @@ namespace Barotrauma
protected override bool CheckObjectiveSpecific()
{
if (sqrDistance > maxDistance * maxDistance)
if (character.Submarine == null || character.Submarine.TeamID != CharacterTeamType.FriendlyNPC)
{
// The target escaped from us.
return true;
// Can't lose the target in friendly outposts.
if (sqrDistance > maxDistance * maxDistance)
{
// The target escaped from us.
return true;
}
}
return IsEnemyDisabled || (AllowCoolDown && coolDownTimer <= 0);
}
@@ -101,7 +101,7 @@ namespace Barotrauma
{
if (InWater || !CanWalk)
{
return TargetMovement.LengthSquared() > SwimSlowParams.MovementSpeed;
return TargetMovement.LengthSquared() > MathUtils.Pow2(SwimSlowParams.MovementSpeed);
}
else
{
@@ -302,7 +302,7 @@ namespace Barotrauma
}
// used for talents/ability conditions
public Item SourceItem { get; }
public Item SourceItem { get; set; }
public List<Affliction> GetMultipliedAfflictions(float multiplier)
{
@@ -308,6 +308,10 @@ namespace Barotrauma
public bool IsHumanoid => Params.Humanoid;
public bool IsHusk => Params.Husk;
public bool IsMale => info?.IsMale ?? false;
public bool IsFemale => info?.IsFemale ?? false;
public string BloodDecalName => Params.BloodDecal;
public bool CanSpeak
@@ -557,10 +561,11 @@ namespace Barotrauma
#if CLIENT
CharacterHealth.SetHealthBarVisibility(value == null);
#elif SERVER
if (value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet })
if (value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet } && balance > 0)
{
Wallet.Give(balance);
grabbedWallet.Deduct(balance);
GameServer.Log($"{Name} grabbed {value.Name}'s body and received {grabbedWallet.Balance} mk.", ServerLog.MessageType.Money);
}
#endif
}
@@ -3587,21 +3592,8 @@ namespace Barotrauma
float attackImpulse = attack.TargetImpulse + attack.TargetForce * attack.ImpactMultiplier * deltaTime;
AbilityAttackData attackData = new AbilityAttackData(attack, this);
if (attacker != null)
{
attackData.Attacker = attacker;
attacker.CheckTalents(AbilityEffectType.OnAttack, attackData);
CheckTalents(AbilityEffectType.OnAttacked, attackData);
attackData.DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.AttackMultiplier);
if (attacker.TeamID == TeamID)
{
attackData.DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.TeamAttackMultiplier);
}
}
AbilityAttackData attackData = new AbilityAttackData(attack, this, attacker);
IEnumerable<Affliction> attackAfflictions;
if (attackData.Afflictions != null)
{
attackAfflictions = attackData.Afflictions.Union(attack.Afflictions.Keys);
@@ -4916,10 +4908,21 @@ namespace Barotrauma
public Character Character { get; set; }
public Character Attacker { get; set; }
public AbilityAttackData(Attack sourceAttack, Character character)
public AbilityAttackData(Attack sourceAttack, Character target, Character attacker)
{
SourceAttack = sourceAttack;
Character = character;
Character = target;
if (attacker != null)
{
Attacker = attacker;
attacker.CheckTalents(AbilityEffectType.OnAttack, this);
target.CheckTalents(AbilityEffectType.OnAttacked, this);
DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.AttackMultiplier);
if (attacker.TeamID == target.TeamID)
{
DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.TeamAttackMultiplier);
}
}
}
}
@@ -130,17 +130,22 @@ namespace Barotrauma
head = value;
HeadSprite = null;
AttachmentSprites = null;
IsMale = value.Preset?.TagSet?.Contains("Male".ToIdentifier()) ?? false;
IsFemale = value.Preset?.TagSet?.Contains("Female".ToIdentifier()) ?? false;
}
}
}
public bool IsMale { get; private set; }
public bool IsFemale { get; private set; }
public CharacterInfoPrefab Prefab => CharacterPrefab.Prefabs[SpeciesName].CharacterInfoPrefab;
public class HeadPreset : ISerializableEntity
{
private readonly CharacterInfoPrefab characterInfoPrefab;
public Identifier MenuCategory => TagSet.First(t => characterInfoPrefab.VarTags[characterInfoPrefab.MenuCategoryVar].Contains(t));
public ImmutableHashSet<Identifier> TagSet { get; private set; }
[Serialize("", IsPropertySaveable.No)]
@@ -71,8 +71,8 @@ namespace Barotrauma
public static Result<ContentFile, string> CreateFromXElement(ContentPackage contentPackage, XElement element)
{
static Result<ContentFile, string> fail(string error)
=> Result<ContentFile, string>.Failure(error);
static Result<ContentFile, string> fail(string error, string? stackTrace = null)
=> Result<ContentFile, string>.Failure(error, stackTrace);
Identifier elemName = element.NameAsIdentifier();
var type = Types.FirstOrDefault(t => t.Names.Contains(elemName));
@@ -99,10 +99,10 @@ namespace Barotrauma
}
catch (Exception e)
{
return fail($"Failed to load file \"{filePath}\" of type \"{elemName}\": {e.Message}\n{e.StackTrace.CleanupStackTrace()}");
return fail($"Failed to load file \"{filePath}\" of type \"{elemName}\": {e.Message}", e.StackTrace.CleanupStackTrace());
}
}
protected ContentFile(ContentPackage contentPackage, ContentPath path)
{
ContentPackage = contentPackage;
@@ -34,7 +34,15 @@ namespace Barotrauma
else if (MatchesSingular(elemName))
{
T prefab = CreatePrefab(parentElement);
prefabs.Add(prefab, overriding);
try
{
prefabs.Add(prefab, overriding);
}
catch
{
prefab.Dispose(); //clean up before rethrowing, since some prefab types might lock resources
throw;
}
}
else if (MatchesPlural(elemName))
{
@@ -1,6 +1,6 @@
#nullable enable
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Barotrauma.Steam;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -9,7 +9,6 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.Steam;
namespace Barotrauma
{
@@ -39,7 +38,7 @@ namespace Barotrauma
public readonly DateTime? InstallTime;
public readonly ImmutableArray<ContentFile> Files;
public readonly ImmutableArray<string> Errors;
public readonly ImmutableArray<(string error, string? stackTrace)> Errors;
public async Task<bool> IsUpToDate()
{
@@ -92,7 +91,7 @@ namespace Barotrauma
Errors = fileResults
.OfType<Failure<ContentFile, string>>()
.Select(f => f.Error)
.Select(f => (f.Error, f.StackTrace))
.ToImmutableArray();
HasMultiplayerSyncedContent = Files.Any(f => !f.NotSyncedInMultiplayer);
@@ -304,5 +303,24 @@ namespace Barotrauma
}
return path == LocalModsDir;
}
public void LogErrors()
{
if (Errors.Any())
{
DebugConsole.AddWarning(
$"The following errors occurred while loading the content package\"{Name}\". The package might not work correctly.\n" +
string.Join('\n', Errors.Select(e => errorToStr(e.error, e.stackTrace))));
static string errorToStr(string error, string? stackTrace)
{
string str = error;
if (stackTrace != null)
{
str += '\n' + stackTrace;
}
return str;
}
}
}
}
}
@@ -430,9 +430,9 @@ namespace Barotrauma
public static void LoadVanillaFileList()
{
VanillaCorePackage = new CorePackage(XDocument.Load(VanillaFileList), VanillaFileList);
foreach (string error in VanillaCorePackage.Errors)
foreach ((string error, string? stackTrace) in VanillaCorePackage.Errors)
{
DebugConsole.ThrowError(error);
DebugConsole.ThrowError(error + (stackTrace == null ? string.Empty : '\n' + stackTrace));
}
}
@@ -469,8 +469,17 @@ namespace Barotrauma
}
var corePackageElement = contentPackagesElement.GetChildElement(CorePackageElementName);
enabledCorePackage = findPackage(CorePackages, corePackageElement) ?? VanillaCorePackage!;
var configEnabledCorePackage = findPackage(CorePackages, corePackageElement);
if (configEnabledCorePackage == null)
{
string packageStr = corePackageElement.GetAttributeString("name", null) ?? corePackageElement.GetAttributeStringUnrestricted("path", "UNKNOWN");
DebugConsole.ThrowError($"Could not find the selected core package \"{packageStr}\". Switching to the \"{enabledCorePackage.Name}\" package.");
}
else
{
enabledCorePackage = configEnabledCorePackage;
}
var regularPackagesElement = contentPackagesElement.GetChildElement(RegularPackagesElementName);
if (regularPackagesElement != null)
{
@@ -499,5 +508,13 @@ namespace Barotrauma
yield return new LoadProgress(1.0f);
}
public static void LogEnabledRegularPackageErrors()
{
foreach (var p in EnabledPackages.Regular)
{
p.LogErrors();
}
}
}
}
@@ -1562,17 +1562,24 @@ namespace Barotrauma
commands.Add(new Command("money", "money [amount] [character]: Gives the specified amount of money to the crew when a campaign is active.", args =>
{
if (args.Length == 0) { return; }
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
if (!(GameMain.GameSession?.GameMode is CampaignMode campaign)) { return; }
Character targetCharacter = null;
if (args.Length >= 2)
{
if (int.TryParse(args[0], out int money))
{
campaign.Bank.Give(money);
GameAnalyticsManager.AddMoneyGainedEvent(money, GameAnalyticsManager.MoneySource.Cheat, "console");
}
else
{
ThrowError($"\"{args[0]}\" is not a valid numeric value.");
}
targetCharacter = FindMatchingCharacter(args.Skip(1).ToArray());
}
if (int.TryParse(args[0], out int money))
{
Wallet wallet = targetCharacter is null || GameMain.IsSingleplayer ? campaign.Bank : targetCharacter.Wallet;
wallet.Give(money);
GameAnalyticsManager.AddMoneyGainedEvent(money, GameAnalyticsManager.MoneySource.Cheat, "console");
}
else
{
ThrowError($"\"{args[0]}\" is not a valid numeric value.");
}
}, isCheat: true, getValidArgs: () => new []
{
@@ -1,10 +1,8 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -33,11 +31,11 @@ namespace Barotrauma
public static int GrimeSpriteCount { get; private set; } = 0;
public static readonly PrefabCollection<GrimeSprite> GrimeSprites = new PrefabCollection<GrimeSprite>(
onAdd: (sprite, b) => GrimeSpriteCount = Math.Max(GrimeSpriteCount, sprite.IndexInFile+1),
onAdd: (sprite, b) => GrimeSpriteCount = Math.Max(GrimeSpriteCount, sprite.IndexInFile + 1),
onRemove: (s) =>
GrimeSpriteCount = GrimeSprites.AllPrefabs
.SelectMany(kvp => kvp.Value)
.Where(p => p != s).Select(p => p.IndexInFile+1).MaxOrNull() ?? 0,
.Where(p => p != s).Select(p => p.IndexInFile + 1).MaxOrNull() ?? 0,
onSort: null, onAddOverrideFile: null, onRemoveOverrideFile: null);
public static void LoadFromFile(DecalsFile configFile)
@@ -102,7 +102,7 @@ namespace Barotrauma
item.GetComponent<PowerContainer>() != null ||
item.GetComponent<Reactor>() != null)
{
item.Indestructible = true;
item.InvulnerableToDamage = true;
}
}
@@ -210,6 +210,13 @@ namespace Barotrauma
};
}
public string GetOwnerLogName() => Owner switch
{
Some<Character> { Value: var character } => character.Name,
None<Character> _ => "the bank",
_ => throw new ArgumentOutOfRangeException(nameof(Owner))
};
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged);
private static int ClampBalance(int value) => Math.Clamp(value, 0, CampaignMode.MaxMoney);
@@ -147,22 +147,23 @@ namespace Barotrauma
var campaign = SinglePlayerCampaign.Load(subElement);
campaign.LoadNewLevel();
GameMode = campaign;
InitOwnedSubs(submarineInfo, ownedSubmarines);
break;
#endif
case "multiplayercampaign":
CrewManager = new CrewManager(false);
var mpCampaign = MultiPlayerCampaign.LoadNew(subElement);
GameMode = mpCampaign;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
mpCampaign.LoadNewLevel();
mpCampaign.LoadNewLevel();
InitOwnedSubs(submarineInfo, ownedSubmarines);
//save to ensure the campaign ID in the save file matches the one that got assigned to this campaign instance
SaveUtil.SaveGame(saveFile);
}
break;
}
}
InitOwnedSubs(submarineInfo);
}
private void InitOwnedSubs(SubmarineInfo submarineInfo, List<SubmarineInfo>? ownedSubmarines = null)
@@ -409,14 +410,16 @@ namespace Barotrauma
if (GameMain.NetworkMember?.ServerSettings?.LockAllDefaultWires ?? false)
{
foreach (Item item in Item.ItemList)
List<Item> items = new List<Item>();
items.AddRange(Submarine.MainSubs[0].GetItems(alsoFromConnectedSubs: true));
if (Submarine.MainSubs[1] != null)
{
if (item.Submarine == Submarine.MainSubs[0] ||
(Submarine.MainSubs[1] != null && item.Submarine == Submarine.MainSubs[1]))
{
Wire wire = item.GetComponent<Wire>();
if (wire != null && !wire.NoAutoLock && wire.Connections.Any(c => c != null)) { wire.Locked = true; }
}
items.AddRange(Submarine.MainSubs[1].GetItems(alsoFromConnectedSubs: true));
}
foreach (Item item in items)
{
Wire wire = item.GetComponent<Wire>();
if (wire != null && !wire.NoAutoLock && wire.Connections.Any(c => c != null)) { wire.Locked = true; }
}
}
@@ -228,9 +228,12 @@ namespace Barotrauma.Items.Components
{
if (MathUtils.GetLineRectangleIntersection(ConvertUnits.ToDisplayUnits(sourcePos), ConvertUnits.ToDisplayUnits(rayStart), item.CurrentHull.Rect, out Vector2 hullIntersection))
{
Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos);
rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f);
if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; }
if (!item.CurrentHull.ConnectedGaps.Any(g => g.Open > 0.0f && Submarine.RectContains(g.Rect, hullIntersection)))
{
Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos);
rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f);
if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; }
}
}
}
}
@@ -251,7 +251,7 @@ namespace Barotrauma
return true;
}
#endif
if (HiddenInGame) { return false; }
if (character != null && character.IsOnPlayerTeam)
{
return IsPlayerTeamInteractable;
@@ -1438,7 +1438,6 @@ namespace Barotrauma
public bool HasAccess(Character character)
{
if (HiddenInGame) { return false; }
if (character.IsBot && IgnoreByAI(character)) { return false; }
if (!IsInteractable(character)) { return false; }
var itemContainer = GetComponent<ItemContainer>();
@@ -1834,10 +1833,29 @@ namespace Barotrauma
Submarine prevSub = Submarine;
var projectile = GetComponent<Projectile>();
if (projectile?.StickTarget?.UserData is Limb limb && limb.character != null)
if (projectile?.StickTarget != null)
{
Submarine = body.Submarine = limb.character.Submarine;
currentHull = limb.character.CurrentHull;
if (projectile?.StickTarget.UserData is Limb limb && limb.character != null)
{
Submarine = body.Submarine = limb.character.Submarine;
currentHull = limb.character.CurrentHull;
}
else if (projectile.StickTarget.UserData is Structure structure)
{
Submarine = body.Submarine = structure.Submarine;
currentHull = Hull.FindHull(WorldPosition, CurrentHull);
}
else if (projectile.StickTarget.UserData is Item targetItem)
{
Submarine = body.Submarine = targetItem.Submarine;
currentHull = targetItem.CurrentHull;
}
else if (projectile.StickTarget.UserData is Submarine)
{
//attached to a sub from the outside -> don't move inside the sub
Submarine = body.Submarine = null;
currentHull = null;
}
}
else
{
@@ -133,6 +133,7 @@ namespace Barotrauma
{
displayRange *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionRadius);
Attack.DamageMultiplier *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionDamage);
Attack.SourceItem ??= sourceItem;
}
Vector2 cameraPos = GameMain.GameScreen.Cam.Position;
@@ -337,11 +338,17 @@ namespace Barotrauma
}
}
AbilityAttackData attackData = new AbilityAttackData(Attack, c, attacker);
if (attackData.Afflictions != null)
{
modifiedAfflictions.AddRange(attackData.Afflictions);
}
//use a position slightly from the limb's position towards the explosion
//ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
Vector2 dir = worldPosition - limb.WorldPosition;
Vector2 hitPos = limb.WorldPosition + (dir.LengthSquared() <= 0.001f ? Rand.Vector(1.0f) : Vector2.Normalize(dir)) * 0.01f;
AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker, damageMultiplier: attack.DamageMultiplier);
AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker, damageMultiplier: attack.DamageMultiplier * attackData.DamageMultiplier);
damages.Add(limb, attackResult.Damage);
if (attack.StatusEffects != null && attack.StatusEffects.Any())
@@ -23,6 +23,10 @@ namespace Barotrauma
public readonly Vector2 Noise;
public readonly Color DirtColor;
#if CLIENT
public Sprite GrimeSprite;
#endif
public float ColorStrength
{
get;
@@ -51,6 +55,9 @@ namespace Barotrauma
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f));
Color = DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
#if CLIENT
GrimeSprite = DecalManager.GrimeSprites[$"{nameof(GrimeSprite)}{index % DecalManager.GrimeSpriteCount}"].Sprite;
#endif
}
public BackgroundSection(Rectangle rect, ushort index, float colorStrength, Color color, ushort rowIndex)
@@ -68,6 +75,9 @@ namespace Barotrauma
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f));
DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
#if CLIENT
GrimeSprite = DecalManager.GrimeSprites[$"{nameof(GrimeSprite)}{index % DecalManager.GrimeSpriteCount}"].Sprite;
#endif
}
public bool SetColor(Color color)
@@ -274,33 +284,17 @@ namespace Barotrauma
get { return Submarine == null ? surface : surface + Submarine.Position.Y; }
}
private float dirtiedVolume = 0.0f;
public float WaterVolume
{
get { return waterVolume; }
set
{
if (!MathUtils.IsValid(value)) return;
if (!MathUtils.IsValid(value)) { return; }
waterVolume = MathHelper.Clamp(value, 0.0f, Volume * MaxCompress);
if (waterVolume < Volume) { Pressure = rect.Y - rect.Height + waterVolume / rect.Width; }
if (waterVolume > 0.0f)
{
update = true;
if (BackgroundSections != null)
{
float volumeMultiplier = Math.Clamp(waterVolume / Volume, 0f, 1f);
if (Math.Abs(volumeMultiplier - dirtiedVolume) > 0.075f)
{
RefreshSubmergedSections(new Rectangle(new Point(0, -rect.Height), new Point(rect.Width, (int)(rect.Height * volumeMultiplier))));
dirtiedVolume = volumeMultiplier;
}
}
}
else
{
submergedSections.Clear();
dirtiedVolume = 0.0f;
}
}
}
@@ -391,8 +385,6 @@ namespace Barotrauma
private readonly HashSet<int> pendingSectionUpdates = new HashSet<int>();
private readonly List<BackgroundSection> submergedSections = new List<BackgroundSection>();
public int xBackgroundMax, yBackgroundMax;
public bool SupportsPaintedColors
@@ -664,7 +656,6 @@ namespace Barotrauma
}
BackgroundSections?.Clear();
submergedSections?.Clear();
List<FireSource> fireSourcesToRemove = new List<FireSource>(FireSources);
foreach (FireSource fireSource in fireSourcesToRemove)
@@ -973,12 +964,6 @@ namespace Barotrauma
}
}
//0.016 increase every ~2000 frames = reaches full dirtiness in ~35 minutes
if (submergedSections.Count > 0 && Submarine != null && Submarine.Info.Type == SubmarineType.Player && Rand.Int(2000) == 1)
{
DirtySections(submergedSections, deltaTime);
}
if (waterVolume < Volume)
{
LethalPressure -= 10.0f * deltaTime;
@@ -1451,17 +1436,6 @@ namespace Barotrauma
}
}
public void RefreshSubmergedSections(Rectangle waterArea)
{
if (BackgroundSections == null) { return; }
submergedSections.Clear();
foreach (var section in GetBackgroundSectionsViaContaining(waterArea))
{
submergedSections.Add(section);
}
}
public bool DoesSectionMatch(int index, int row)
{
return index >= 0 && row >= 0 && BackgroundSections.Count > index && BackgroundSections[index] != null && BackgroundSections[index].RowIndex == row;
@@ -1528,15 +1502,6 @@ namespace Barotrauma
}
}
public void DirtySections(List<BackgroundSection> sections, float dirtyVal)
{
if (sections == null) { return; }
for (int i = 0; i < sections.Count; i++)
{
IncreaseSectionColorOrStrength(sections[i], sections[i].DirtColor, dirtyVal, false, false);
}
}
public void CleanSection(BackgroundSection section, float cleanVal, bool updateRequired)
{
bool decalsCleaned = false;
@@ -37,10 +37,14 @@ namespace Barotrauma.RuinGeneration
Indent = true,
NewLineOnAttributes = true
};
IEnumerable<ContentPackage> packages = ContentPackageManager.LocalPackages;
#if DEBUG
packages = packages.Union(ContentPackageManager.VanillaCorePackage.ToEnumerable());
#endif
foreach (RuinGenerationParams generationParams in RuinParams)
{
foreach (RuinConfigFile configFile in ContentPackageManager.AllPackages.SelectMany(p => p.GetFiles<RuinConfigFile>()))
foreach (RuinConfigFile configFile in packages.SelectMany(p => p.GetFiles<RuinConfigFile>()))
{
if (configFile.Path != generationParams.ContentFile.Path) { continue; }
@@ -3,8 +3,6 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
@@ -13,16 +11,20 @@ namespace Barotrauma
enum MapEntityCategory
{
Structure = 1,
Decorative = 2,
Machine = 4,
Equipment = 8,
Electrical = 16,
Material = 32,
Misc = 64,
Alien = 128,
Wrecked = 256,
ItemAssembly = 512,
Legacy = 1024
Decorative = 2,
Machine = 4,
Medical = 8,
Weapon = 16,
Diving = 32,
Equipment = 64,
Fuel = 128,
Electrical = 256,
Material = 1024,
Alien = 2048,
Wrecked = 4096,
ItemAssembly = 8192,
Legacy = 16384,
Misc = 32768
}
abstract partial class MapEntityPrefab : PrefabWithUintIdentifier
@@ -1540,8 +1540,10 @@ namespace Barotrauma
List<HumanPrefab> killedCharacters = new List<HumanPrefab>();
List<(HumanPrefab HumanPrefab, CharacterInfo CharacterInfo)> selectedCharacters
= new List<(HumanPrefab HumanPrefab, CharacterInfo CharacterInfo)>();
foreach (HumanPrefab humanPrefab in outpost.Info.OutpostGenerationParams.GetHumanPrefabs(Rand.RandSync.ServerAndClient))
var humanPrefabs = outpost.Info.OutpostGenerationParams.GetHumanPrefabs(Rand.RandSync.ServerAndClient);
foreach (HumanPrefab humanPrefab in humanPrefabs)
{
if (humanPrefab is null) { continue; }
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.ServerAndClient), randSync: Rand.RandSync.ServerAndClient);
if (location != null && location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier()))
{
@@ -153,6 +153,7 @@ namespace Barotrauma
{
Name = TextManager.GetWithVariable("wreckeditemformat", "[name]", Name);
}
Name = Name.Fallback(OriginalName);
var tags = new HashSet<Identifier>();
string joinedTags = element.GetAttributeString("tags", "");
@@ -139,7 +139,7 @@ namespace Barotrauma.Networking
}
}
public bool HasPermissions = false;
public bool HasPermissions => Permissions != ClientPermissions.None;
public VoipQueue VoipQueue
{
@@ -38,6 +38,7 @@ namespace Barotrauma.Networking
Wiring,
ServerMessage,
ConsoleUsage,
Money,
Karma,
Talent,
Error,
@@ -53,9 +54,10 @@ namespace Barotrauma.Networking
{ MessageType.Wiring, new Color(255, 157, 85) },
{ MessageType.ServerMessage, new Color(157, 225, 160) },
{ MessageType.ConsoleUsage, new Color(0, 162, 232) },
{ MessageType.Money, Color.Green },
{ MessageType.Karma, new Color(75, 88, 255) },
{ MessageType.Talent, new Color(125, 125, 255) },
{ MessageType.Error, Color.Red },
{ MessageType.Error, Color.Red }
};
private readonly Dictionary<MessageType, string> messageTypeName = new Dictionary<MessageType, string>
@@ -68,6 +70,7 @@ namespace Barotrauma.Networking
{ MessageType.Wiring, "Wiring" },
{ MessageType.ServerMessage, "ServerMessage" },
{ MessageType.ConsoleUsage, "ConsoleUsage" },
{ MessageType.Money, "Money" },
{ MessageType.Karma, "Karma" },
{ MessageType.Talent, "Talent" },
{ MessageType.Error, "Error" }
@@ -1007,6 +1007,19 @@ namespace Barotrauma
}
}
}
else if (attributeName == "move")
{
Vector2 moveAmount = subElement.GetAttributeVector2("move", Vector2.Zero);
if (entity is Structure structure)
{
structure.Move(moveAmount);
}
else if (entity is Item item)
{
item.Move(moveAmount);
}
continue;
}
if (entity.SerializableProperties.TryGetValue(attributeName, out SerializableProperty property))
{
@@ -148,6 +148,8 @@ namespace Barotrauma
public struct GraphicsSettings
{
public static readonly Point MinSupportedResolution = new Point(1024, 540);
public static GraphicsSettings GetDefault()
{
GraphicsSettings gfxSettings = new GraphicsSettings
@@ -167,16 +167,9 @@ namespace Barotrauma
var type = Type;
if (type == ConditionType.Uncertain)
{
if (AfflictionPrefab.Prefabs.ContainsKey(AttributeName))
{
type = ConditionType.Affliction;
}
else
{
type = (target?.SerializableProperties?.ContainsKey(AttributeName) ?? false)
? ConditionType.PropertyValue
: ConditionType.HasSpecifierTag;
}
type = AfflictionPrefab.Prefabs.ContainsKey(AttributeName)
? ConditionType.Affliction
: ConditionType.PropertyValue;
}
if (checkContained)
@@ -250,6 +243,14 @@ namespace Barotrauma
}
}
return Operator == OperatorType.Equals ? matches >= SplitAttributeValue.Length : matches <= 0;
case ConditionType.HasSpecifierTag:
{
if (target == null) { return Operator == OperatorType.NotEquals; }
if (!(target is Character { Info: { } characterInfo })) { return false; }
return (Operator == OperatorType.Equals) ==
SplitAttributeValue.All(v => characterInfo.Head.Preset.TagSet.Contains(v));
}
case ConditionType.SpeciesName:
{
if (target == null) { return Operator == OperatorType.NotEquals; }
@@ -309,6 +309,11 @@ namespace Barotrauma.Steam
XDocument fileListSrc = XMLExtensions.TryLoadXml(Path.Combine(itemDirectory, ContentPackage.FileListFileName));
string modName = fileListSrc.Root.GetAttributeString("name", item.Title).Trim();
string[] modPathSplit = fileListSrc.Root.GetAttributeString("path", "")
.CleanUpPathCrossPlatform(correctFilenameCase: false).Split("/");
string? modPathDirName = modPathSplit.Length > 1 && modPathSplit[0] == "Mods"
? modPathSplit[1]
: null;
string modVersion = fileListSrc.Root.GetAttributeString("modversion", ContentPackage.DefaultModVersion);
Version gameVersion = fileListSrc.Root.GetAttributeVersion("gameversion", GameMain.Version);
bool isCorePackage = fileListSrc.Root.GetAttributeBool("corepackage", false);
@@ -316,7 +321,7 @@ namespace Barotrauma.Steam
using (var copyIndicator = new CopyIndicator(copyIndicatorPath))
{
await CopyDirectory(itemDirectory, modName, itemDirectory, installDir);
await CopyDirectory(itemDirectory, modPathDirName ?? modName, itemDirectory, installDir);
string fileListDestPath = Path.Combine(installDir, ContentPackage.FileListFileName);
XDocument fileListDest = XMLExtensions.TryLoadXml(fileListDestPath);
@@ -330,9 +335,9 @@ namespace Barotrauma.Steam
new XAttribute("modversion", modVersion),
new XAttribute("gameversion", gameVersion),
new XAttribute("installtime", ToolBox.Epoch.FromDateTime(updateTime)));
if (modName.ToIdentifier() != itemTitle)
if ((modPathDirName ?? modName).ToIdentifier() != itemTitle)
{
root.Add(new XAttribute("altnames", modName));
root.Add(new XAttribute("altnames", modPathDirName ?? modName));
}
if (!expectedHash.IsNullOrEmpty())
{
@@ -83,7 +83,7 @@ namespace Barotrauma
if (Debugger.IsAttached) { Debugger.Break(); }
}
#endif
return Plain(lStr);
return Plain(lStr ?? string.Empty);
}
public static implicit operator RichString(string str) => (LocalizedString)str;
@@ -149,22 +149,34 @@ namespace Barotrauma
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Texts.Count; i++)
XDocument doc = XMLExtensions.TryLoadXml(ContentFile.Path);
if (doc == null) { return; }
List<(string key, string value)> texts = new List<(string key, string value)>();
foreach (var element in doc.Root.Elements())
{
Identifier key = Texts.Keys.ElementAt(i);
Texts.TryGetValue(key, out ImmutableArray<string> infoList);
string text = element.ElementInnerText()
.Replace("&amp;", "&")
.Replace("&lt;", "<")
.Replace("&gt;", ">")
.Replace("&quot;", "\"")
.Replace("&apos;", "'");
texts.Add((element.Name.ToString(), text));
}
foreach ((string key, string value) in texts)
{
sb.Append(key); // ID
sb.Append('*');
sb.Append(value); // Original
sb.Append('*');
// Translated
sb.Append('*');
// Comments
sb.AppendLine();
for (int j = 0; j < infoList.Length; j++)
{
sb.Append(key); // ID
sb.Append('*');
sb.Append(infoList[j]); // Original
sb.Append('*');
// Translated
sb.Append('*');
// Comments
sb.AppendLine();
}
}
Barotrauma.IO.File.WriteAllText($"csv_{Language.ToString().ToLower()}_{index}.csv", sb.ToString());
@@ -11,8 +11,8 @@ namespace Barotrauma
public static Success<T, TError> Success(T value)
=> new Success<T, TError>(value);
public static Failure<T, TError> Failure(TError error)
=> new Failure<T, TError>(error);
public static Failure<T, TError> Failure(TError error, string? stackTrace)
=> new Failure<T, TError>(error, stackTrace);
}
public sealed class Success<T, TError> : Result<T, TError>
@@ -33,11 +33,15 @@ namespace Barotrauma
where TError: notnull
{
public readonly TError Error;
public readonly string? StackTrace;
public override bool IsSuccess => false;
public Failure(TError error)
public Failure(TError error, string? stackTrace)
{
Error = error;
StackTrace = stackTrace;
}
}
}
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma.IO
{
@@ -23,9 +24,15 @@ namespace Barotrauma.IO
public static bool CanWrite(string path, bool isDirectory)
{
path = System.IO.Path.GetFullPath(path).CleanUpPath();
string localModsDir = System.IO.Path.GetFullPath(ContentPackage.LocalModsDir).CleanUpPath();
string workshopModsDir = System.IO.Path.GetFullPath(ContentPackage.WorkshopModsDir).CleanUpPath();
string getFullPath(string p)
=> System.IO.Path.GetFullPath(p).CleanUpPath();
path = getFullPath(path);
string localModsDir = getFullPath(ContentPackage.LocalModsDir);
string workshopModsDir = getFullPath(ContentPackage.WorkshopModsDir);
#if CLIENT
string tempDownloadDir = getFullPath(ModReceiver.DownloadFolder);
#endif
if (!isDirectory)
{
@@ -34,8 +41,15 @@ namespace Barotrauma.IO
{
return false;
}
if (!path.StartsWith(workshopModsDir, StringComparison.OrdinalIgnoreCase)
&& !path.StartsWith(localModsDir, StringComparison.OrdinalIgnoreCase)
bool pathStartsWith(string prefix)
=> path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
if (!pathStartsWith(workshopModsDir)
&& !pathStartsWith(localModsDir)
#if CLIENT
&& !pathStartsWith(tempDownloadDir)
#endif
&& (extension == ".dll" || extension == ".exe" || extension == ".json"))
{
return false;
@@ -13,7 +13,7 @@ using System.Text.RegularExpressions;
namespace Barotrauma
{
partial class SaveUtil
static class SaveUtil
{
private static readonly string LegacySaveFolder = Path.Combine("Data", "Saves");
private static readonly string LegacyMultiplayerSaveFolder = Path.Combine(LegacySaveFolder, "Multiplayer");