Build 0.18.13.0

This commit is contained in:
Markus Isberg
2022-07-01 12:16:36 +09:00
parent 8e6c601162
commit 497045de7e
79 changed files with 717 additions and 361 deletions
@@ -308,6 +308,7 @@ namespace Barotrauma
}
}
}
if (targetSlot < 0) { return false; }
return targetInventory.TryPutItem(item, targetSlot, allowSwapping, allowCombine: false, Character);
}
else
@@ -1297,6 +1297,7 @@ namespace Barotrauma
AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage = 0, bool isWitnessing = false)
{
if (!(c.AIController is HumanAIController humanAI)) { return AIObjectiveCombat.CombatMode.None; }
if (!IsFriendly(attacker))
{
if (c.Submarine == null)
@@ -1306,12 +1307,15 @@ namespace Barotrauma
}
if (!c.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
{
// Attacked from an unconnected submarine.
return c.SelectedConstruction?.GetComponent<Turret>() != null ? AIObjectiveCombat.CombatMode.None : AIObjectiveCombat.CombatMode.Retreat;
// Attacked from an unconnected submarine (pirate/pvp)
return
humanAI.ObjectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder.GetTarget() is Controller ?
AIObjectiveCombat.CombatMode.None : AIObjectiveCombat.CombatMode.Retreat;
}
return c.AIController is HumanAIController humanAI &&
(humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || humanAI.ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders))
? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive;
return
humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() ||
humanAI.ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders) ?
AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive;
}
else
{
@@ -1362,7 +1366,7 @@ namespace Barotrauma
}
else
{
if (c.AIController is HumanAIController humanAI && humanAI.ObjectiveManager.GetActiveObjective<AIObjectiveCombat>()?.Enemy == attacker)
if (humanAI.ObjectiveManager.GetActiveObjective<AIObjectiveCombat>()?.Enemy == attacker)
{
// Already targeting the attacker -> treat as a more serious threat.
cumulativeDamage *= 2;
@@ -31,8 +31,9 @@ namespace Barotrauma
if (!pump.Item.IsInteractable(character)) { return false; }
if (pump.IsAutoControlled) { return false; }
if (pump.Item.ConditionPercentage <= 0) { return false; }
if (pump.Item.CurrentHull == null) { return false; }
if (pump.Item.CurrentHull.FireSources.Count > 0) { return false; }
if (character.Submarine != null)
if (character.Submarine != null && pump.Item.Submarine != null)
{
if (!character.Submarine.IsConnectedTo(pump.Item.Submarine)) { return false; }
}
@@ -1030,21 +1030,30 @@ namespace Barotrauma
/// <param name="hasAi">Is the character controlled by AI.</param>
/// <param name="createNetworkEvent">Should clients receive a network event about the creation of this character?</param>
/// <param name="ragdoll">Ragdoll configuration file. If null, will select the default.</param>
public static Character Create(string speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null)
public static Character Create(string speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool throwErrorIfNotFound = true)
{
if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
speciesName = Path.GetFileNameWithoutExtension(speciesName);
}
return Create(speciesName.ToIdentifier(), position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent, ragdoll);
return Create(speciesName.ToIdentifier(), position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent, ragdoll, throwErrorIfNotFound);
}
public static Character Create(Identifier speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null)
public static Character Create(Identifier speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool throwErrorIfNotFound = true)
{
var prefab = CharacterPrefab.FindBySpeciesName(speciesName);
if (prefab == null)
{
DebugConsole.ThrowError($"Failed to create character \"{speciesName}\". Matching prefab not found.\n" + Environment.StackTrace);
string errorMsg = $"Failed to create character \"{speciesName}\". Matching prefab not found.\n" + Environment.StackTrace;
if (throwErrorIfNotFound)
{
DebugConsole.ThrowError(errorMsg);
}
else
{
DebugConsole.AddWarning(errorMsg);
}
return null;
}
return Create(prefab, position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent, ragdoll);
@@ -1510,9 +1519,10 @@ namespace Barotrauma
if (skillIdentifier != null)
{
for (int i = 0; i < Inventory.Capacity; i++)
foreach (Item item in Inventory.AllItems)
{
if (Inventory.SlotTypes[i] != InvSlotType.Any && Inventory.GetItemAt(i)?.GetComponent<Wearable>() is Wearable wearable)
if (item?.GetComponent<Wearable>() is Wearable wearable &&
!Inventory.IsInLimbSlot(item, InvSlotType.Any))
{
if (wearable.SkillModifiers.TryGetValue(skillIdentifier, out float skillValue))
{
@@ -1639,8 +1639,13 @@ namespace Barotrauma
private void RefreshHeadSprites()
{
HeadSprite = null;
AttachmentSprites = null;
_headSprite = null;
LoadHeadSprite();
#if CLIENT
CalculateHeadPosition(_headSprite);
#endif
attachmentSprites?.Clear();
LoadAttachmentSprites();
}
// This could maybe be a LookUp instead?
@@ -809,8 +809,13 @@ namespace Barotrauma
if (!Character.GodMode)
{
UpdateLimbAfflictionOverlays();
UpdateSkinTint();
#if CLIENT
if (Character.IsVisible)
{
UpdateLimbAfflictionOverlays();
UpdateSkinTint();
}
#endif
CalculateVitality();
if (Vitality <= MinVitality)
@@ -820,6 +825,12 @@ namespace Barotrauma
}
}
public void ForceUpdateVisuals()
{
UpdateLimbAfflictionOverlays();
UpdateSkinTint();
}
private void UpdateDamageReductions(float deltaTime)
{
float healthRegen = Character.Params.Health.ConstantHealthRegeneration;
@@ -532,7 +532,13 @@ namespace Barotrauma
}
}
Character createdCharacter = Character.Create(SpeciesName, pos, seed, characterInfo: null, isRemotePlayer: false, hasAi: true, createNetworkEvent: true);
Character createdCharacter = Character.Create(SpeciesName, pos, seed, characterInfo: null, isRemotePlayer: false, hasAi: true, createNetworkEvent: true, throwErrorIfNotFound: false);
if (createdCharacter == null)
{
disallowed = true;
return;
}
var eventManager = GameMain.GameSession.EventManager;
if (eventManager != null)
{
@@ -28,6 +28,7 @@ namespace Barotrauma
var subs = sub.GetConnectedSubs().Where(s => s.TeamID == sub.TeamID);
CreateAndPlace(subs);
subs.ForEach(s => s.Info.InitialSuppliesSpawned = true);
sub.CheckFuel();
}
}
@@ -147,12 +147,12 @@ namespace Barotrauma
private Location Location => campaign?.Map?.CurrentLocation;
public Action OnItemsInBuyCrateChanged;
public Action OnItemsInSellCrateChanged;
public Action OnItemsInSellFromSubCrateChanged;
public Action OnPurchasedItemsChanged;
public Action OnSoldItemsChanged;
public readonly NamedEvent<CargoManager> OnItemsInBuyCrateChanged = new NamedEvent<CargoManager>();
public readonly NamedEvent<CargoManager> OnItemsInSellCrateChanged = new NamedEvent<CargoManager>();
public readonly NamedEvent<CargoManager> OnItemsInSellFromSubCrateChanged = new NamedEvent<CargoManager>();
public readonly NamedEvent<CargoManager> OnPurchasedItemsChanged = new NamedEvent<CargoManager>();
public readonly NamedEvent<CargoManager> OnSoldItemsChanged = new NamedEvent<CargoManager>();
public CargoManager(CampaignMode campaign)
{
this.campaign = campaign;
@@ -215,19 +215,19 @@ namespace Barotrauma
public void ClearItemsInBuyCrate()
{
ItemsInBuyCrate.Clear();
OnItemsInBuyCrateChanged?.Invoke();
OnItemsInBuyCrateChanged?.Invoke(this);
}
public void ClearItemsInSellCrate()
{
ItemsInSellCrate.Clear();
OnItemsInSellCrateChanged?.Invoke();
OnItemsInSellCrateChanged?.Invoke(this);
}
public void ClearItemsInSellFromSubCrate()
{
ItemsInSellFromSubCrate.Clear();
OnItemsInSellFromSubCrateChanged?.Invoke();
OnItemsInSellFromSubCrateChanged?.Invoke(this);
}
public void SetPurchasedItems(Dictionary<Identifier, List<PurchasedItem>> purchasedItems)
@@ -238,7 +238,7 @@ namespace Barotrauma
{
PurchasedItems.Add(entry.Key, entry.Value);
}
OnPurchasedItemsChanged?.Invoke();
OnPurchasedItemsChanged?.Invoke(this);
}
public void ModifyItemQuantityInBuyCrate(Identifier storeIdentifier, ItemPrefab itemPrefab, int changeInQuantity, Client client = null)
@@ -255,7 +255,7 @@ namespace Barotrauma
{
GetBuyCrateItems(storeIdentifier, create: true).Add(new PurchasedItem(itemPrefab, changeInQuantity, client));
}
OnItemsInBuyCrateChanged?.Invoke();
OnItemsInBuyCrateChanged?.Invoke(this);
}
public void ModifyItemQuantityInSubSellCrate(Identifier storeIdentifier, ItemPrefab itemPrefab, int changeInQuantity, Client client = null)
@@ -272,7 +272,7 @@ namespace Barotrauma
{
GetSubCrateItems(storeIdentifier, create: true).Add(new PurchasedItem(itemPrefab, changeInQuantity, client));
}
OnItemsInSellFromSubCrateChanged?.Invoke();
OnItemsInSellFromSubCrateChanged?.Invoke(this);
}
#if SERVER
@@ -331,7 +331,7 @@ namespace Barotrauma
}
}
}
OnPurchasedItemsChanged?.Invoke();
OnPurchasedItemsChanged?.Invoke(this);
}
public Dictionary<ItemPrefab, int> GetBuyValuesAtCurrentLocation(Identifier storeIdentifier, IEnumerable<ItemPrefab> items)
@@ -378,7 +378,7 @@ namespace Barotrauma
}
CreateItems(items, Submarine.MainSub, this);
PurchasedItems.Clear();
OnPurchasedItemsChanged?.Invoke();
OnPurchasedItemsChanged?.Invoke(this);
}
private Dictionary<ItemPrefab, int> UndeterminedSoldEntities { get; } = new Dictionary<ItemPrefab, int>();
@@ -39,8 +39,8 @@ namespace Barotrauma
float prevValue = Value;
Metadata.SetValue(metaDataIdentifier, Math.Clamp(value, MinReputation, MaxReputation));
OnReputationValueChanged?.Invoke();
OnAnyReputationValueChanged?.Invoke();
OnReputationValueChanged?.Invoke(this);
OnAnyReputationValueChanged?.Invoke(this);
#if CLIENT
int increase = (int)Value - (int)prevValue;
if (increase != 0 && Character.Controlled != null)
@@ -73,8 +73,8 @@ namespace Barotrauma
Value += reputationChange;
}
public Action OnReputationValueChanged;
public static Action OnAnyReputationValueChanged;
public readonly NamedEvent<Reputation> OnReputationValueChanged = new NamedEvent<Reputation>();
public static readonly NamedEvent<Reputation> OnAnyReputationValueChanged = new NamedEvent<Reputation>();
public readonly Faction Faction;
public readonly Location Location;
@@ -1005,7 +1005,7 @@ namespace Barotrauma
}
}
public SubmarineInfo SwitchSubs()
public void SwitchSubs()
{
if (TransferItemsOnSubSwitch)
{
@@ -1013,7 +1013,6 @@ namespace Barotrauma
}
RefreshOwnedSubmarines();
PendingSubmarineSwitch = null;
return GameMain.GameSession.SubmarineInfo;
}
/// <summary>
@@ -1039,9 +1038,12 @@ namespace Barotrauma
if (item.HiddenInGame) { continue; }
if (!connectedSubs.Contains(item.Submarine)) { continue; }
if (item.Prefab.DontTransferBetweenSubs) { continue; }
if (item.GetRootInventoryOwner() is Character) { continue; }
if (item.GetComponent<Holdable>() == null && item.GetComponent<Wearable>() == null && item.GetComponent<Projectile>() == null) { continue; }
if (item.Components.Any(c => c is Holdable h && h.Attached)) { continue; }
var rootOwner = item.GetRootInventoryOwner();
if (rootOwner is Character) { continue; }
if (rootOwner is Item ownerItem && (ownerItem.NonInteractable || ownerItem.HiddenInGame)) { continue; }
if (item.GetComponent<Door>() != null) { continue; }
if (item.Components.None(c => c is Pickable)) { continue; }
if (item.Components.Any(c => c is Pickable p && p.IsAttached)) { continue; }
if (item.Components.Any(c => c is Wire w && w.Connections.Any(c => c != null))) { continue; }
itemsToTransfer.Add((item, item.Container));
item.Submarine = null;
@@ -1054,6 +1056,7 @@ namespace Barotrauma
item.Drop(null, createNetworkEvent: false, setTransform: false);
}
}
currentSub.Info.NoItems = true;
}
// Serialize the current sub
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(currentSub);
@@ -1122,6 +1125,7 @@ namespace Barotrauma
DebugConsole.Log(msg);
#endif
}
newSub.Info.NoItems = false;
// Serialize the new sub
PendingSubmarineSwitch = new SubmarineInfo(newSub);
}
@@ -94,7 +94,7 @@ namespace Barotrauma
private CampaignMetadata Metadata => Campaign.CampaignMetadata;
private readonly CampaignMode Campaign;
public event Action? OnUpgradesChanged;
public readonly NamedEvent<UpgradeManager> OnUpgradesChanged = new NamedEvent<UpgradeManager>();
public UpgradeManager(CampaignMode campaign)
{
@@ -248,7 +248,7 @@ namespace Barotrauma
// tell the server that this item is yet to be paid for server side
PurchasedUpgrades.Add(new PurchasedUpgrade(prefab, category));
#endif
OnUpgradesChanged?.Invoke();
OnUpgradesChanged?.Invoke(this);
}
else
{
@@ -349,7 +349,7 @@ namespace Barotrauma
}
}
OnUpgradesChanged?.Invoke();
OnUpgradesChanged?.Invoke(this);
}
else
{
@@ -418,7 +418,7 @@ namespace Barotrauma
}
#if CLIENT
OnUpgradesChanged?.Invoke();
OnUpgradesChanged?.Invoke(this);
#endif
}
@@ -802,7 +802,7 @@ namespace Barotrauma
{
PendingUpgrades.Clear();
PendingUpgrades.AddRange(upgrades);
OnUpgradesChanged?.Invoke();
OnUpgradesChanged?.Invoke(this);
}
public static void DebugLog(string msg, Color? color = null)
@@ -193,6 +193,9 @@ namespace Barotrauma.Items.Components
private Vector2 prevContainedItemPositions;
private float autoInjectCooldown = 1.0f;
const float AutoInjectInterval = 1.0f;
public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)
{
@@ -412,7 +415,15 @@ namespace Barotrauma.Items.Components
if (AutoInject)
{
if (ownerInventory?.Owner is Character ownerCharacter &&
//normally autoinjection should delete the (medical) item, so it only gets applied once
//but in multiplayer clients aren't allowed to remove items themselves, so they may be able to trigger this dozens of times
//before the server notifies them of the item being removed, leading to a sharp lag spike.
//this can also happen with mods, if there's a way to autoinject something that doesn't get removed On Use.
//so let's ensure the item is only applied once per second at most.
autoInjectCooldown -= deltaTime;
if (autoInjectCooldown <= 0.0f &&
ownerInventory?.Owner is Character ownerCharacter &&
ownerCharacter.HealthPercentage / 100f <= AutoInjectThreshold &&
ownerCharacter.HasEquippedItem(item))
{
@@ -420,6 +431,7 @@ namespace Barotrauma.Items.Components
{
item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter);
item.GetComponent<GeneticMaterial>()?.Equip(ownerCharacter);
autoInjectCooldown = AutoInjectInterval;
}
}
}
@@ -338,9 +338,7 @@ namespace Barotrauma.Items.Components
if (user == null) { return; }
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
{
#if CLIENT
mpCampaign.TryPurchase(null, fabricatedItem.RequiredMoney);
#elif SERVER
#if SERVER
if (GetUsingClient() is { } client)
{
mpCampaign.TryPurchase(client, fabricatedItem.RequiredMoney);
@@ -142,9 +142,8 @@ namespace Barotrauma.Items.Components
//less effective when in a bad condition
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate;
item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate;
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 30.0f * deltaTime; }
}
public void InfectBallast(Identifier identifier, bool allowMultiplePerShip = false)
@@ -789,6 +789,11 @@ namespace Barotrauma.Items.Components
private bool ShouldIgnoreSubmarineCollision(ref Fixture target, Contact contact)
{
//not in the projectile category: the projectile has not been launched (e.g. just dropped from an inventory)
if (item.body.CollisionCategories != Physics.CollisionProjectile)
{
return false;
}
if (target.Body.UserData is Submarine sub)
{
Vector2 dir = item.body.LinearVelocity.LengthSquared() < 0.001f ?
@@ -877,6 +877,7 @@ namespace Barotrauma
//and check if the collision should be ignored in the OnCollision callback, but
//that'd make the hit detection more expensive because every item would be included)
collisionCategory = Physics.CollisionCharacter;
collidesWith |= Physics.CollisionProjectile;
}
if (collisionCategoryStr != null)
{
@@ -1265,8 +1266,8 @@ namespace Barotrauma
Vector2 displayPos = ConvertUnits.ToDisplayUnits(simPosition);
rect.X = (int)(displayPos.X - rect.Width / 2.0f);
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
rect.X = (int)MathF.Round(displayPos.X - rect.Width / 2.0f);
rect.Y = (int)MathF.Round(displayPos.Y + rect.Height / 2.0f);
if (findNewHull) { FindHull(); }
}
@@ -403,13 +403,14 @@ namespace Barotrauma.MapCreatures.Behavior
new XAttribute("health", branch.Health.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("maxhealth", branch.MaxHealth.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("sides", (int)branch.Sides),
new XAttribute("blockedsides", (int)branch.BlockedSides));
new XAttribute("blockedsides", (int)branch.BlockedSides),
new XAttribute("tile", (int)branch.Type));
if (branch.ClaimedItem != null)
{
be.Add(new XAttribute("claimed", (int)(branch.ClaimedItem?.ID ?? -1)));
}
if (branch.ParentBranch != null)
if (branch.ParentBranch != null && !branch.ParentBranch.Removed)
{
be.Add(new XAttribute("parentbranch", (int)(branch.ParentBranch?.ID ?? -1)));
}
@@ -495,14 +496,15 @@ namespace Barotrauma.MapCreatures.Behavior
int blockedSides = getInt("blockedsides");
int claimedId = branchElement.GetAttributeInt("claimed", -1);
int parentBranchId = branchElement.GetAttributeInt("parentbranch", -1);
VineTileType type = (VineTileType)branchElement.GetAttributeInt("tile", 0);
BallastFloraBranch newBranch = new BallastFloraBranch(this, null, pos, VineTileType.CrossJunction, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig))
BallastFloraBranch newBranch = new BallastFloraBranch(this, null, pos, type, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig))
{
ID = id,
Health = health,
MaxHealth = maxhealth,
Sides = (TileSide) sides,
BlockedSides = (TileSide) blockedSides,
Sides = (TileSide)sides,
BlockedSides = (TileSide)blockedSides,
IsRoot = isRoot,
IsRootGrowth = isRootGrowth
};
@@ -683,7 +685,6 @@ namespace Barotrauma.MapCreatures.Behavior
if (branch.ClaimedItem != null)
{
RemoveClaim(branch.ClaimedItem);
branch.ClaimedItem = null;
}
branch.RemoveTimer -= deltaTime;
@@ -1196,6 +1197,14 @@ namespace Barotrauma.MapCreatures.Behavior
ClaimedTargets.Remove(item);
item.Infector = null;
foreach (var branch in Branches)
{
if (branch.ClaimedItem == item)
{
branch.ClaimedItem = null;
}
}
ClaimedJunctionBoxes.ForEachMod(jb =>
{
if (jb.Item == item)
@@ -1226,10 +1235,14 @@ namespace Barotrauma.MapCreatures.Behavior
branch.DisconnectedFromRoot = true;
}
foreach (Item target in ClaimedTargets)
foreach (Item target in ClaimedTargets.ToList())
{
RemoveClaim(target);
target.Infector = null;
}
Debug.Assert(ClaimedTargets.Count == 0);
Debug.Assert(ClaimedJunctionBoxes.Count == 0);
Debug.Assert(ClaimedBatteries.Count == 0);
StateMachine?.State?.Exit();
#if SERVER
@@ -549,7 +549,7 @@ namespace Barotrauma
startPath = new Tunnel(
TunnelType.SidePath,
new List<Point>() { startExitPosition, startPosition },
minWidth / 2, parentTunnel: mainPath);
minWidth, parentTunnel: mainPath);
Tunnels.Add(startPath);
}
else
@@ -561,7 +561,7 @@ namespace Barotrauma
endPath = new Tunnel(
TunnelType.SidePath,
new List<Point>() { endPosition, endExitPosition },
minWidth / 2, parentTunnel: mainPath);
minWidth, parentTunnel: mainPath);
Tunnels.Add(endPath);
}
else
@@ -576,14 +576,14 @@ namespace Barotrauma
endHole = new Tunnel(
TunnelType.SidePath,
new List<Point>() { startPosition, startExitPosition, new Point(0, Size.Y) },
minWidth / 2, parentTunnel: mainPath);
minWidth, parentTunnel: mainPath);
}
else
{
endHole = new Tunnel(
TunnelType.SidePath,
new List<Point>() { endPosition, endExitPosition, Size },
minWidth / 2, parentTunnel: mainPath);
minWidth, parentTunnel: mainPath);
}
Tunnels.Add(endHole);
}
@@ -601,7 +601,7 @@ namespace Barotrauma
abyssTunnel = new Tunnel(
TunnelType.SidePath,
new List<Point>() { lowestPoint, new Point(lowestPoint.X, 0) },
minWidth / 2, parentTunnel: mainPath);
minWidth, parentTunnel: mainPath);
Tunnels.Add(abyssTunnel);
}
@@ -4266,6 +4266,7 @@ namespace Barotrauma
corpse.TeamID = CharacterTeamType.None;
corpse.EnableDespawn = false;
selectedPrefab.GiveItems(corpse, wreck);
corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false);
corpse.CharacterHealth.ApplyAffliction(corpse.AnimController.MainLimb, AfflictionPrefab.OxygenLow.Instantiate(200));
bool applyBurns = Rand.Value() < 0.1f;
bool applyDamage = Rand.Value() < 0.3f;
@@ -4294,7 +4295,7 @@ namespace Barotrauma
return strength;
}
}
corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false);
corpse.CharacterHealth.ForceUpdateVisuals();
corpse.GiveIdCardTags(sp);
bool isServerOrSingleplayer = GameMain.IsSingleplayer || GameMain.NetworkMember is { IsServer: true };
@@ -182,8 +182,8 @@ namespace Barotrauma
float scale = element.GetAttributeFloat("scale", prefab.Scale);
var rect = element.GetAttributeVector4("rect", Vector4.Zero);
rect.Z *= scale / prefab.Scale;
rect.W *= scale / prefab.Scale;
if (!prefab.ResizeHorizontal) { rect.Z *= scale / prefab.Scale; }
if (!prefab.ResizeVertical) { rect.W *= scale / prefab.Scale; }
points.Add(new Vector2(rect.X, rect.Y));
points.Add(new Vector2(rect.X + rect.Z, rect.Y));
@@ -212,7 +212,9 @@ namespace Barotrauma
LevelData levelData = GameMain.GameSession?.Campaign?.NextLevel ?? GameMain.GameSession?.LevelData;
linkedSub = new LinkedSubmarine(submarine, id)
{
purchasedLostShuttles = GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles,
purchasedLostShuttles =
(GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles) ||
element.GetAttributeBool("purchasedlostshuttle", false),
saveElement = element
};
@@ -282,6 +284,8 @@ namespace Barotrauma
return;
}
saveElement.Attribute("purchasedlostshuttle")?.Remove();
IdRemap parentRemap = new IdRemap(Submarine.Info.SubmarineElement, Submarine.IdOffset);
sub = Submarine.Load(info, false, parentRemap);
sub.Info.SubmarineClass = Submarine.Info.SubmarineClass;
@@ -442,14 +446,19 @@ namespace Barotrauma
saveElement.Attribute("previewimage").Remove();
}
if (saveElement.Attribute("pos") != null) { saveElement.Attribute("pos").Remove(); }
saveElement.Add(new XAttribute("pos", XMLExtensions.Vector2ToString(Position - Submarine.HiddenSubPosition)));
if (GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles)
{
saveElement.SetAttributeValue("purchasedlostshuttle", true);
}
var linkedPort = linkedTo.FirstOrDefault(lt => (lt is Item) && ((Item)lt).GetComponent<DockingPort>() != null);
saveElement.SetAttributeValue("pos", XMLExtensions.Vector2ToString(Position - Submarine.HiddenSubPosition));
var linkedPort =
linkedTo.FirstOrDefault(lt => (lt is Item item) && item.GetComponent<DockingPort>() != null) ??
FindEntityByID(linkedToID.First()) as MapEntity;
if (linkedPort != null)
{
saveElement.Attribute("linkedto")?.Remove();
saveElement.Add(new XAttribute("linkedto", linkedPort.ID));
saveElement.SetAttributeValue("linkedto", linkedPort.ID);
}
}
else
@@ -458,10 +467,8 @@ namespace Barotrauma
sub.SaveToXElement(saveElement);
}
saveElement.Attribute("originallinkedto")?.Remove();
saveElement.Add(new XAttribute("originallinkedto", originalLinkedPort != null ? originalLinkedPort.Item.ID : originalLinkedToID));
saveElement.Attribute("originalmyport")?.Remove();
saveElement.Add(new XAttribute("originalmyport", originalMyPortID));
saveElement.SetAttributeValue("originallinkedto", originalLinkedPort != null ? originalLinkedPort.Item.ID : originalLinkedToID);
saveElement.SetAttributeValue("originalmyport", originalMyPortID);
if (sub != null)
{
@@ -20,11 +20,24 @@ namespace Barotrauma
public int Height { get; private set; }
public Action<Location, LocationConnection> OnLocationSelected;
public Action<LocationConnection, IEnumerable<Mission>> OnMissionsSelected;
public readonly struct LocationChangeInfo
{
public readonly Location PrevLocation;
public readonly Location NewLocation;
public LocationChangeInfo(Location prevLocation, Location newLocation)
{
PrevLocation = prevLocation;
NewLocation = newLocation;
}
}
/// <summary>
/// From -> To
/// </summary>
public Action<Location, Location> OnLocationChanged;
public Action<LocationConnection, IEnumerable<Mission>> OnMissionsSelected;
public readonly NamedEvent<LocationChangeInfo> OnLocationChanged = new NamedEvent<LocationChangeInfo>();
public Location EndLocation { get; private set; }
@@ -766,7 +779,7 @@ namespace Barotrauma
SelectedLocation = null;
CurrentLocation.CreateStores();
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation));
if (GameMain.GameSession is { Campaign: { CampaignMetadata: { } metadata } })
{
@@ -803,7 +816,7 @@ namespace Barotrauma
{
connection.Passed = true;
}
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation));
}
}
@@ -540,6 +540,7 @@ namespace Barotrauma
mapEntityList.Remove(this);
#if CLIENT
Submarine.ForceRemoveFromVisibleEntities(this);
if (SelectedList.Contains(this))
{
SelectedList = SelectedList.Where(e => e != this).ToHashSet();
@@ -52,7 +52,9 @@ namespace Barotrauma
get { return MainSubs[0]; }
set { MainSubs[0] = value; }
}
private static List<Submarine> loaded = new List<Submarine>();
private static readonly List<Submarine> loaded = new List<Submarine>();
private readonly Identifier upgradeEventIdentifier;
private static List<MapEntity> visibleEntities;
public static IEnumerable<MapEntity> VisibleEntities
@@ -1301,6 +1303,7 @@ namespace Barotrauma
public Submarine(SubmarineInfo info, bool showWarningMessages = true, Func<Submarine, List<MapEntity>> loadEntities = null, IdRemap linkedRemap = null) : base(null, Entity.NullEntityID)
{
upgradeEventIdentifier = new Identifier($"Submarine{ID}");
Loading = true;
GameMain.World.Enabled = false;
try
@@ -1462,10 +1465,7 @@ namespace Barotrauma
}
}
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
{
GameMain.GameSession.Campaign.UpgradeManager.OnUpgradesChanged += ResetCrushDepth;
}
GameMain.GameSession?.Campaign?.UpgradeManager?.OnUpgradesChanged.Register(upgradeEventIdentifier, _ => ResetCrushDepth());
#if CLIENT
GameMain.LightManager.OnMapLoaded();
@@ -1527,6 +1527,13 @@ namespace Barotrauma
}
}
public bool CheckFuel()
{
float fuel = GetItems(true).Where(i => i.HasTag("reactorfuel")).Sum(i => i.Condition);
Info.LowFuel = fuel < 200;
return !Info.LowFuel;
}
public void SaveToXElement(XElement element)
{
element.Add(new XAttribute("name", Info.Name));
@@ -1534,7 +1541,10 @@ namespace Barotrauma
element.Add(new XAttribute("checkval", Rand.Int(int.MaxValue)));
element.Add(new XAttribute("price", Info.Price));
element.Add(new XAttribute("initialsuppliesspawned", Info.InitialSuppliesSpawned));
element.Add(new XAttribute("noitems", Info.NoItems));
element.Add(new XAttribute("lowfuel", !CheckFuel()));
element.Add(new XAttribute("type", Info.Type.ToString()));
element.Add(new XAttribute("ismanuallyoutfitted", Info.IsManuallyOutfitted));
if (Info.IsPlayer && !Info.HasTag(SubmarineTag.Shuttle))
{
element.Add(new XAttribute("class", Info.SubmarineClass.ToString()));
@@ -1623,7 +1633,6 @@ namespace Barotrauma
e.Save(element);
}
Info.CheckSubsLeftBehind(element);
}
@@ -1727,10 +1736,7 @@ namespace Barotrauma
outdoorNodes?.Clear();
outdoorNodes = null;
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
{
GameMain.GameSession.Campaign.UpgradeManager.OnUpgradesChanged -= ResetCrushDepth;
}
GameMain.GameSession?.Campaign?.UpgradeManager?.OnUpgradesChanged?.TryDeregister(upgradeEventIdentifier);
if (entityGrid != null)
{
@@ -598,7 +598,12 @@ namespace Barotrauma
if (newHull != null)
{
CoroutineManager.Invoke(() =>
character.AnimController.FindHull(newHull.WorldPosition, setSubmarine: true));
{
if (character != null && !character.Removed)
{
character.AnimController.FindHull(newHull.WorldPosition, setSubmarine: true);
}
});
}
return false;
@@ -86,6 +86,18 @@ namespace Barotrauma
set;
}
public bool NoItems
{
get;
set;
}
public bool LowFuel
{
get;
set;
}
public Version GameVersion
{
get;
@@ -94,6 +106,8 @@ namespace Barotrauma
public SubmarineType Type { get; set; }
public bool IsManuallyOutfitted { get; set; }
public SubmarineClass SubmarineClass;
public OutpostModuleInfo OutpostModuleInfo { get; set; }
@@ -272,6 +286,8 @@ namespace Barotrauma
Description = original.Description;
Price = original.Price;
InitialSuppliesSpawned = original.InitialSuppliesSpawned;
NoItems = original.NoItems;
LowFuel = original.LowFuel;
GameVersion = original.GameVersion;
Type = original.Type;
SubmarineClass = original.SubmarineClass;
@@ -286,6 +302,7 @@ namespace Barotrauma
RecommendedCrewExperience = original.RecommendedCrewExperience;
RecommendedCrewSizeMin = original.RecommendedCrewSizeMin;
RecommendedCrewSizeMax = original.RecommendedCrewSizeMax;
IsManuallyOutfitted = original.IsManuallyOutfitted;
Tags = original.Tags;
if (original.OutpostModuleInfo != null)
{
@@ -335,6 +352,9 @@ namespace Barotrauma
Price = SubmarineElement.GetAttributeInt("price", 1000);
InitialSuppliesSpawned = SubmarineElement.GetAttributeBool("initialsuppliesspawned", false);
NoItems = SubmarineElement.GetAttributeBool("noitems", false);
LowFuel = SubmarineElement.GetAttributeBool("lowfuel", false);
IsManuallyOutfitted = SubmarineElement.GetAttributeBool("ismanuallyoutfitted", false);
GameVersion = new Version(SubmarineElement.GetAttributeString("gameversion", "0.0.0.0"));
if (Enum.TryParse(SubmarineElement.GetAttributeString("tags", ""), out SubmarineTag tags))
@@ -284,7 +284,7 @@ namespace Barotrauma.Networking
if (hull.Submarine != RespawnShuttle) { continue; }
hull.OxygenPercentage = 100.0f;
hull.WaterVolume = 0.0f;
hull.BallastFlora?.Kill();
hull.BallastFlora?.Remove();
}
Dictionary<Character, Vector2> characterPositions = new Dictionary<Character, Vector2>();
@@ -1,17 +1,12 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Globalization;
using Barotrauma.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma.Networking
{
@@ -405,6 +405,8 @@ namespace Barotrauma
FarseerBody.Restitution = limbParams.Restitution;
FarseerBody.AngularDamping = limbParams.AngularDamping;
FarseerBody.UserData = this;
_collisionCategories = collisionCategory;
_collidesWith = collidesWith;
SetTransformIgnoreContacts(position, 0.0f);
LastSentPosition = position;
list.Add(this);
@@ -418,6 +420,8 @@ namespace Barotrauma
density = Math.Max(forceDensity ?? element.GetAttributeFloat("density", 10.0f), MinDensity);
Enum.TryParse(element.GetAttributeString("bodytype", "Dynamic"), out BodyType bodyType);
CreateBody(width, height, radius, density, bodyType, collisionCategory, collidesWith, findNewContacts);
_collisionCategories = collisionCategory;
_collidesWith = collidesWith;
FarseerBody.Friction = element.GetAttributeFloat("friction", 0.5f);
FarseerBody.Restitution = element.GetAttributeFloat("restitution", 0.05f);
FarseerBody.UserData = this;
@@ -456,6 +460,8 @@ namespace Barotrauma
this.width = width;
this.height = height;
this.radius = radius;
_collisionCategories = collisionCategory;
_collidesWith = collidesWith;
}
/// <summary>
@@ -54,7 +54,6 @@ namespace Barotrauma
EnableMouseLook = true,
ChatOpen = true,
CrewMenuOpen = true,
CampaignDisclaimerShown = false,
EditorDisclaimerShown = false,
ShowOffensiveServerPrompt = true,
TutorialSkipWarning = true,
@@ -127,7 +126,6 @@ namespace Barotrauma
public bool EnableMouseLook;
public bool ChatOpen;
public bool CrewMenuOpen;
public bool CampaignDisclaimerShown;
public bool EditorDisclaimerShown;
public bool ShowOffensiveServerPrompt;
public bool TutorialSkipWarning;
@@ -6,11 +6,11 @@ namespace Barotrauma
private readonly LocalizedString primary;
private readonly LocalizedString fallback;
private bool primaryIsLoaded = false;
public bool PrimaryIsLoaded { get; private set; }
public FallbackLString(LocalizedString primary, LocalizedString fallback)
{
if (primary is FallbackLString {primary: { } innerPrimary, fallback: { } innerFallback})
if (primary is FallbackLString { primary: { } innerPrimary, fallback: { } innerFallback })
{
this.primary = innerPrimary;
this.fallback = innerFallback.Fallback(fallback);
@@ -27,18 +27,27 @@ namespace Barotrauma
return base.MustRetrieveValue()
|| MustRetrieveValue(primary)
|| MustRetrieveValue(fallback)
|| primaryIsLoaded != primary.Loaded;
|| PrimaryIsLoaded != primary.Loaded;
}
public override bool Loaded => primary.Loaded || fallback.Loaded;
public override void RetrieveValue()
{
cachedValue = primary.Value;
primaryIsLoaded = primary.Loaded;
PrimaryIsLoaded = primary.Loaded;
if (!primary.Loaded)
{
cachedValue = fallback.Value;
}
}
public LocalizedString GetLastFallback()
{
if (fallback is FallbackLString innerFallback)
{
return innerFallback.GetLastFallback();
}
return fallback;
}
}
}
@@ -1,4 +1,5 @@
#nullable enable
using System;
using System.Collections.Immutable;
using System.Linq;
@@ -18,7 +19,14 @@ namespace Barotrauma
public override void RetrieveValue()
{
//TODO: possibly broken!
cachedValue = string.Format(str.Value, subStrs.Select(s => s.Value as object).ToArray());
try
{
cachedValue = string.Format(str.Value, subStrs.Select(s => s.Value as object).ToArray());
}
catch (FormatException)
{
cachedValue = str.Value;
}
UpdateLanguage();
}
}