Unstable v0.19.3.0
This commit is contained in:
@@ -3847,7 +3847,7 @@ namespace Barotrauma
|
||||
|
||||
public override void ServerWrite(IWriteMessage msg)
|
||||
{
|
||||
msg.Write((byte)State);
|
||||
msg.WriteByte((byte)State);
|
||||
PetBehavior?.ServerWrite(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -529,6 +530,15 @@ namespace Barotrauma
|
||||
}
|
||||
return canEquip;
|
||||
}
|
||||
protected bool CheckItemIdentifiersOrTags(Item item, ImmutableHashSet<Identifier> identifiersOrTags)
|
||||
{
|
||||
if (identifiersOrTags.Contains(item.Prefab.Identifier)) { return true; }
|
||||
foreach (var identifier in identifiersOrTags)
|
||||
{
|
||||
if (item.HasTag(identifier)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool CanEquip(Item item) => CanEquip(character, item);
|
||||
}
|
||||
|
||||
+5
-4
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using static Barotrauma.AIObjectiveFindSafety;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -967,7 +968,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Seeks for more ammunition. Creates a new subobjective.
|
||||
/// </summary>
|
||||
private void SeekAmmunition(Identifier[] ammunitionIdentifiers)
|
||||
private void SeekAmmunition(ImmutableHashSet<Identifier> ammunitionIdentifiers)
|
||||
{
|
||||
retreatTarget = null;
|
||||
RemoveSubObjective(ref retreatObjective);
|
||||
@@ -1002,7 +1003,7 @@ namespace Barotrauma
|
||||
HumanAIController.UnequipEmptyItems(Weapon);
|
||||
RelatedItem item = null;
|
||||
Item ammunition = null;
|
||||
Identifier[] ammunitionIdentifiers = null;
|
||||
ImmutableHashSet<Identifier> ammunitionIdentifiers = null;
|
||||
if (WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
|
||||
{
|
||||
foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
|
||||
@@ -1028,8 +1029,8 @@ namespace Barotrauma
|
||||
if (ammunitionIdentifiers != null)
|
||||
{
|
||||
// Try reload ammunition from inventory
|
||||
bool IsInsideHeadset(Item i) => i.ParentInventory?.Owner is Item ownerItem && ownerItem.HasTag("mobileradio");
|
||||
ammunition = character.Inventory.FindItem(i => ammunitionIdentifiers.Any(id => id == i.Prefab.Identifier || i.HasTag(id)) && i.Condition > 0 && !IsInsideHeadset(i), recursive: true);
|
||||
static bool IsInsideHeadset(Item i) => i.ParentInventory?.Owner is Item ownerItem && ownerItem.HasTag("mobileradio");
|
||||
ammunition = character.Inventory.FindItem(i => CheckItemIdentifiersOrTags(i, ammunitionIdentifiers) && i.Condition > 0 && !IsInsideHeadset(i), recursive: true);
|
||||
if (ammunition != null)
|
||||
{
|
||||
var container = Weapon.GetComponent<ItemContainer>();
|
||||
|
||||
+11
-6
@@ -1,6 +1,8 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -11,14 +13,14 @@ namespace Barotrauma
|
||||
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
public Identifier[] ignoredContainerIdentifiers;
|
||||
public ImmutableHashSet<Identifier> ignoredContainerIdentifiers;
|
||||
public bool checkInventory = true;
|
||||
|
||||
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs and in some cases also enemy NPCs, like pirates)
|
||||
private readonly bool spawnItemIfNotFound;
|
||||
|
||||
//can either be a tag or an identifier
|
||||
public readonly Identifier[] itemIdentifiers;
|
||||
public readonly ImmutableHashSet<Identifier> itemIdentifiers;
|
||||
public readonly ItemContainer container;
|
||||
private readonly Item item;
|
||||
public Item ItemToContain { get; private set; }
|
||||
@@ -61,9 +63,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public AIObjectiveContainItem(Character character, Identifier itemIdentifier, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: this(character, new Identifier[] { itemIdentifier }, container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
|
||||
: this(character, itemIdentifier.ToEnumerable().ToImmutableHashSet(), container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
|
||||
|
||||
public AIObjectiveContainItem(Character character, Identifier[] itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
public AIObjectiveContainItem(Character character, ImmutableHashSet<Identifier> itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
this.itemIdentifiers = itemIdentifiers;
|
||||
@@ -102,7 +104,10 @@ namespace Barotrauma
|
||||
return containedItemCount >= ItemCount;
|
||||
}
|
||||
|
||||
private bool CheckItem(Item i) => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)) && i.ConditionPercentage >= ConditionLevel && i.HasAccess(character);
|
||||
private bool CheckItem(Item item)
|
||||
{
|
||||
return CheckItemIdentifiersOrTags(item, itemIdentifiers) && item.ConditionPercentage >= ConditionLevel && item.HasAccess(character);
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -13,7 +15,7 @@ namespace Barotrauma
|
||||
//can either be a tag or an identifier
|
||||
private readonly string[] itemIdentifiers;
|
||||
private readonly ItemContainer sourceContainer;
|
||||
private ItemContainer targetContainer;
|
||||
private readonly ItemContainer targetContainer;
|
||||
private readonly Item targetItem;
|
||||
|
||||
private AIObjectiveGetItem getItemObjective;
|
||||
@@ -127,7 +129,7 @@ namespace Barotrauma
|
||||
RemoveExistingPredicate = RemoveExistingPredicate,
|
||||
RemoveMax = RemoveExistingMax,
|
||||
GetItemPriority = GetItemPriority,
|
||||
ignoredContainerIdentifiers = sourceContainer != null ? new Identifier[] { sourceContainer.Item.Prefab.Identifier } : null
|
||||
ignoredContainerIdentifiers = sourceContainer?.Item.Prefab.Identifier.ToEnumerable().ToImmutableHashSet()
|
||||
},
|
||||
onCompleted: () => IsCompleted = true,
|
||||
onAbandon: () => Abandon = true);
|
||||
|
||||
+1
-1
@@ -177,7 +177,7 @@ namespace Barotrauma
|
||||
TargetName = Leak.FlowTargetHull?.DisplayName,
|
||||
requiredCondition = () =>
|
||||
Leak.Submarine == character.Submarine &&
|
||||
Leak.linkedTo.Any(e => e is Hull h && character.CurrentHull == h),
|
||||
Leak.linkedTo.Any(e => e is Hull h && (character.CurrentHull == h || h.linkedTo.Contains(character.CurrentHull))),
|
||||
endNodeFilter = n => n.Waypoint.CurrentHull != null && Leak.linkedTo.Any(e => e is Hull h && h == n.Waypoint.CurrentHull),
|
||||
// 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()
|
||||
|
||||
+8
-8
@@ -21,10 +21,10 @@ namespace Barotrauma
|
||||
public float TargetCondition { get; set; } = 1;
|
||||
public bool AllowDangerousPressure { get; set; }
|
||||
|
||||
public readonly ImmutableArray<Identifier> IdentifiersOrTags;
|
||||
public readonly ImmutableHashSet<Identifier> IdentifiersOrTags;
|
||||
|
||||
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs)
|
||||
private bool spawnItemIfNotFound = false;
|
||||
private readonly bool spawnItemIfNotFound = false;
|
||||
|
||||
private Item targetItem;
|
||||
private readonly Item originalTarget;
|
||||
@@ -32,8 +32,8 @@ namespace Barotrauma
|
||||
private bool isDoneSeeking;
|
||||
public Item TargetItem => targetItem;
|
||||
private int currSearchIndex;
|
||||
public Identifier[] ignoredContainerIdentifiers;
|
||||
public Identifier[] ignoredIdentifiersOrTags;
|
||||
public ImmutableHashSet<Identifier> ignoredContainerIdentifiers;
|
||||
public ImmutableHashSet<Identifier> ignoredIdentifiersOrTags;
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private float currItemPriority;
|
||||
private readonly bool checkInventory;
|
||||
@@ -93,8 +93,8 @@ namespace Barotrauma
|
||||
Equip = equip;
|
||||
this.spawnItemIfNotFound = spawnItemIfNotFound;
|
||||
this.checkInventory = checkInventory;
|
||||
IdentifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableArray();
|
||||
ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToArray();
|
||||
IdentifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableHashSet();
|
||||
ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
public static IEnumerable<Identifier> ParseGearTags(IEnumerable<Identifier> identifiersOrTags)
|
||||
@@ -558,11 +558,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (!item.HasAccess(character)) { return false; }
|
||||
if (ignoredItems.Contains(item)) { return false; };
|
||||
if (ignoredIdentifiersOrTags != null && ignoredIdentifiersOrTags.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; }
|
||||
if (ignoredIdentifiersOrTags != null && CheckItemIdentifiersOrTags(item, ignoredIdentifiersOrTags)) { return false; }
|
||||
if (item.Condition < TargetCondition) { return false; }
|
||||
if (ItemFilter != null && !ItemFilter(item)) { return false; }
|
||||
if (RequireLoaded && item.Components.Any(i => !i.IsLoaded(character))) { return false; }
|
||||
return IdentifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && item.Prefab.VariantOf == id));
|
||||
return CheckItemIdentifiersOrTags(item, IdentifiersOrTags) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && IdentifiersOrTags.Contains(item.Prefab.VariantOf));
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ namespace Barotrauma
|
||||
public bool RequireAllItems { get; set; }
|
||||
|
||||
private readonly ImmutableArray<Identifier> gearTags;
|
||||
private readonly Identifier[] ignoredTags;
|
||||
private readonly ImmutableHashSet<Identifier> ignoredTags;
|
||||
private bool subObjectivesCreated;
|
||||
|
||||
public readonly HashSet<Item> achievedItems = new HashSet<Item>();
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma
|
||||
public AIObjectiveGetItems(Character character, AIObjectiveManager objectiveManager, IEnumerable<Identifier> identifiersOrTags, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
gearTags = AIObjectiveGetItem.ParseGearTags(identifiersOrTags).ToImmutableArray();
|
||||
ignoredTags = AIObjectiveGetItem.ParseIgnoredTags(identifiersOrTags).ToArray();
|
||||
ignoredTags = AIObjectiveGetItem.ParseIgnoredTags(identifiersOrTags).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
protected override bool CheckObjectiveSpecific() => subObjectivesCreated && subObjectives.None();
|
||||
|
||||
+5
-2
@@ -259,7 +259,8 @@ namespace Barotrauma
|
||||
// Check that there is no unsafe hulls on the way to the target
|
||||
if (node.Waypoint.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(node.Waypoint.CurrentHull)) { return false; }
|
||||
return true;
|
||||
}, endNodeFilter: node => !isCurrentHullAllowed | !IsForbidden(node.Waypoint.CurrentHull));
|
||||
//don't stop at ladders when idling
|
||||
}, endNodeFilter: node => node.Waypoint.Ladders == null && (!isCurrentHullAllowed || !IsForbidden(node.Waypoint.CurrentHull)));
|
||||
if (path.Unreachable)
|
||||
{
|
||||
//can't go to this room, remove it from the list and try another room
|
||||
@@ -290,7 +291,9 @@ namespace Barotrauma
|
||||
}
|
||||
else if (currentTarget != null)
|
||||
{
|
||||
PathSteering.SteeringSeek(character.GetRelativeSimPosition(currentTarget), weight: 1, nodeFilter: node => node.Waypoint.CurrentHull != null);
|
||||
PathSteering.SteeringSeek(character.GetRelativeSimPosition(currentTarget), weight: 1,
|
||||
nodeFilter: node => node.Waypoint.CurrentHull != null,
|
||||
endNodeFilter: node => node.Waypoint.Ladders == null);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -444,7 +444,7 @@ namespace Barotrauma
|
||||
#if SERVER
|
||||
public void ServerEventWrite(IWriteMessage msg, Client client, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(IsAlive);
|
||||
msg.WriteBoolean(IsAlive);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+42
-38
@@ -331,39 +331,41 @@ namespace Barotrauma
|
||||
Collider.SetTransform(Collider.SimPosition, Collider.Rotation + angleDiff);
|
||||
}
|
||||
}
|
||||
|
||||
if (character.LockHands)
|
||||
{
|
||||
var leftHand = GetLimb(LimbType.LeftHand);
|
||||
var rightHand = GetLimb(LimbType.RightHand);
|
||||
|
||||
var waist = GetLimb(LimbType.Waist) ?? GetLimb(LimbType.Torso);
|
||||
|
||||
rightHand.Disabled = true;
|
||||
leftHand.Disabled = true;
|
||||
|
||||
Vector2 midPos = waist.SimPosition;
|
||||
Matrix torsoTransform = Matrix.CreateRotationZ(waist.Rotation);
|
||||
|
||||
midPos += Vector2.Transform(new Vector2(-0.3f * Dir, -0.2f), torsoTransform);
|
||||
|
||||
if (rightHand.PullJointEnabled) midPos = (midPos + rightHand.PullJointWorldAnchorB) / 2.0f;
|
||||
HandIK(rightHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||
HandIK(leftHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||
}
|
||||
else if (character.AnimController.AnimationTestPose)
|
||||
|
||||
if (character.AnimController.AnimationTestPose)
|
||||
{
|
||||
ApplyTestPose();
|
||||
}
|
||||
else if (Anim != Animation.UsingItem)
|
||||
else
|
||||
{
|
||||
if (Anim != Animation.UsingItemWhileClimbing)
|
||||
if (character.LockHands)
|
||||
{
|
||||
ResetPullJoints();
|
||||
var leftHand = GetLimb(LimbType.LeftHand);
|
||||
var rightHand = GetLimb(LimbType.RightHand);
|
||||
|
||||
var waist = GetLimb(LimbType.Waist) ?? GetLimb(LimbType.Torso);
|
||||
|
||||
rightHand.Disabled = true;
|
||||
leftHand.Disabled = true;
|
||||
|
||||
Vector2 midPos = waist.SimPosition;
|
||||
Matrix torsoTransform = Matrix.CreateRotationZ(waist.Rotation);
|
||||
|
||||
midPos += Vector2.Transform(new Vector2(-0.3f * Dir, -0.2f), torsoTransform);
|
||||
if (rightHand.PullJointEnabled) midPos = (midPos + rightHand.PullJointWorldAnchorB) / 2.0f;
|
||||
HandIK(rightHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||
HandIK(leftHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||
}
|
||||
else
|
||||
if (Anim != Animation.UsingItem && character.SelectedBy == null)
|
||||
{
|
||||
ResetPullJoints(l => l.IsLowerBody);
|
||||
if (Anim != Animation.UsingItemWhileClimbing)
|
||||
{
|
||||
ResetPullJoints();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetPullJoints(l => l.IsLowerBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1489,17 +1491,17 @@ namespace Barotrauma
|
||||
|
||||
public override void DragCharacter(Character target, float deltaTime)
|
||||
{
|
||||
if (target == null) return;
|
||||
if (target == null) { return; }
|
||||
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
Limb leftHand = GetLimb(LimbType.LeftHand);
|
||||
Limb rightHand = GetLimb(LimbType.RightHand);
|
||||
|
||||
Limb targetLeftHand = target.AnimController.GetLimb(LimbType.LeftHand);
|
||||
Limb targetLeftHand = target.AnimController.GetLimb(LimbType.LeftForearm);
|
||||
if (targetLeftHand == null) targetLeftHand = target.AnimController.GetLimb(LimbType.Torso);
|
||||
if (targetLeftHand == null) targetLeftHand = target.AnimController.MainLimb;
|
||||
|
||||
Limb targetRightHand = target.AnimController.GetLimb(LimbType.RightHand);
|
||||
Limb targetRightHand = target.AnimController.GetLimb(LimbType.RightForearm);
|
||||
if (targetRightHand == null) targetRightHand = target.AnimController.GetLimb(LimbType.Torso);
|
||||
if (targetRightHand == null) targetRightHand = target.AnimController.MainLimb;
|
||||
|
||||
@@ -1628,7 +1630,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//only pull with one hand when swimming
|
||||
if (i > 0 && inWater) continue;
|
||||
if (i > 0 && inWater) { continue; }
|
||||
|
||||
Vector2 diff = ConvertUnits.ToSimUnits(targetLimb.WorldPosition - pullLimb.WorldPosition);
|
||||
|
||||
@@ -1680,14 +1682,15 @@ namespace Barotrauma
|
||||
targetForce = 5000.0f;
|
||||
}
|
||||
|
||||
if (!target.AllowInput)
|
||||
{
|
||||
targetLimb.PullJointEnabled = true;
|
||||
targetLimb.PullJointMaxForce = targetForce;
|
||||
targetLimb.PullJointWorldAnchorB = targetAnchor;
|
||||
}
|
||||
targetLimb.PullJointEnabled = true;
|
||||
targetLimb.PullJointMaxForce = targetForce;
|
||||
targetLimb.PullJointWorldAnchorB = targetAnchor;
|
||||
targetLimb.Disabled = true;
|
||||
|
||||
target.AnimController.movement = -diff;
|
||||
if (diff.LengthSquared() > 0.1f)
|
||||
{
|
||||
target.AnimController.movement = -diff;
|
||||
}
|
||||
}
|
||||
|
||||
float dist = ConvertUnits.ToSimUnits(Vector2.Distance(target.WorldPosition, WorldPosition));
|
||||
@@ -1714,7 +1717,8 @@ namespace Barotrauma
|
||||
else if (target is AICharacter && target != Character.Controlled)
|
||||
{
|
||||
target.AnimController.TargetDir = WorldPosition.X > target.WorldPosition.X ? Direction.Right : Direction.Left;
|
||||
target.AnimController.TargetMovement = (character.SimPosition + Vector2.UnitX * Dir) - target.SimPosition;
|
||||
Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Dir) - target.SimPosition;
|
||||
target.AnimController.TargetMovement = movement.LengthSquared() > 0.01f ? movement : Vector2.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2969,7 +2969,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if ((GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) &&
|
||||
PressureProtection < (Level.Loaded?.GetRealWorldDepth(WorldPosition.Y) ?? 1.0f) &&
|
||||
WorldPosition.Y < CharacterHealth.CrushDepth)
|
||||
WorldPosition.Y < CharacterHealth.CrushDepth && !HasAbilityFlag(AbilityFlags.ImmuneToPressure))
|
||||
{
|
||||
//implode if below crush depth, and either outside or in a high-pressure hull
|
||||
if (AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure >= 80.0f)
|
||||
@@ -4776,7 +4776,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (TalentOption talentOption in talentSubTree.TalentOptionStages)
|
||||
{
|
||||
if (talentOption.Talents.None(t => HasTalent(t.Identifier)))
|
||||
if (talentOption.TalentIdentifiers.None(t => HasTalent(t)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -809,8 +809,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
HumanPrefabIds = (
|
||||
element.GetAttributeIdentifier("npcsetid", Identifier.Empty),
|
||||
element.GetAttributeIdentifier("npcid", Identifier.Empty));
|
||||
infoElement.GetAttributeIdentifier("npcsetid", Identifier.Empty),
|
||||
infoElement.GetAttributeIdentifier("npcid", Identifier.Empty));
|
||||
|
||||
MissionsCompletedSinceDeath = infoElement.GetAttributeInt("missionscompletedsincedeath", 0);
|
||||
|
||||
|
||||
@@ -1146,14 +1146,14 @@ namespace Barotrauma
|
||||
activeAfflictions.Add(affliction);
|
||||
}
|
||||
}
|
||||
msg.Write((byte)activeAfflictions.Count);
|
||||
msg.WriteByte((byte)activeAfflictions.Count);
|
||||
foreach (Affliction affliction in activeAfflictions)
|
||||
{
|
||||
msg.Write(affliction.Prefab.UintIdentifier);
|
||||
msg.WriteUInt32(affliction.Prefab.UintIdentifier);
|
||||
msg.WriteRangedSingle(
|
||||
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
|
||||
0.0f, affliction.Prefab.MaxStrength, 8);
|
||||
msg.Write((byte)affliction.Prefab.PeriodicEffects.Count());
|
||||
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count());
|
||||
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects)
|
||||
{
|
||||
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);
|
||||
@@ -1170,15 +1170,15 @@ namespace Barotrauma
|
||||
limbAfflictions.Add((limbHealth, limbAffliction));
|
||||
}
|
||||
|
||||
msg.Write((byte)limbAfflictions.Count);
|
||||
msg.WriteByte((byte)limbAfflictions.Count);
|
||||
foreach (var (limbHealth, affliction) in limbAfflictions)
|
||||
{
|
||||
msg.WriteRangedInteger(limbHealths.IndexOf(limbHealth), 0, limbHealths.Count - 1);
|
||||
msg.Write(affliction.Prefab.UintIdentifier);
|
||||
msg.WriteUInt32(affliction.Prefab.UintIdentifier);
|
||||
msg.WriteRangedSingle(
|
||||
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
|
||||
0.0f, affliction.Prefab.MaxStrength, 8);
|
||||
msg.Write((byte)affliction.Prefab.PeriodicEffects.Count());
|
||||
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count());
|
||||
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects)
|
||||
{
|
||||
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Barotrauma
|
||||
public readonly List<(XElement element, float commonness)> ItemSets = new List<(XElement element, float commonness)>();
|
||||
public readonly List<(XElement element, float commonness)> CustomCharacterInfos = new List<(XElement element, float commonness)>();
|
||||
|
||||
private readonly Identifier npcSetIdentifier;
|
||||
public readonly Identifier NpcSetIdentifier;
|
||||
|
||||
public HumanPrefab(ContentXElement element, ContentFile file, Identifier npcSetIdentifier) : base(file, element.GetAttributeIdentifier("identifier", ""))
|
||||
{
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma
|
||||
element.GetChildElements("itemset").ForEach(e => ItemSets.Add((e, e.GetAttributeFloat("commonness", 1))));
|
||||
element.GetChildElements("character").ForEach(e => CustomCharacterInfos.Add((e, e.GetAttributeFloat("commonness", 1))));
|
||||
PreferredOutpostModuleTypes = element.GetAttributeIdentifierArray("preferredoutpostmoduletypes", Array.Empty<Identifier>());
|
||||
this.npcSetIdentifier = npcSetIdentifier;
|
||||
this.NpcSetIdentifier = npcSetIdentifier;
|
||||
}
|
||||
|
||||
public IEnumerable<Identifier> GetModuleFlags()
|
||||
@@ -183,7 +183,7 @@ namespace Barotrauma
|
||||
{
|
||||
characterInfo = new CharacterInfo(characterElement, Identifier);
|
||||
}
|
||||
characterInfo.HumanPrefabIds = (npcSetIdentifier, Identifier);
|
||||
characterInfo.HumanPrefabIds = (NpcSetIdentifier, Identifier);
|
||||
return characterInfo;
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -1,6 +1,4 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionAboveVitality : AbilityConditionDataless
|
||||
{
|
||||
@@ -13,7 +11,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
return character.HealthPercentage / 100f > vitalityPercentage;
|
||||
return character.Vitality / character.MaxVitality > vitalityPercentage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,11 +1,10 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionAlliesAboveVitality : AbilityConditionDataless
|
||||
{
|
||||
float vitalityPercentage;
|
||||
readonly float vitalityPercentage;
|
||||
|
||||
public AbilityConditionAlliesAboveVitality(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionCoauthor : AbilityConditionDataless
|
||||
{
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionCrouched : AbilityConditionDataless
|
||||
{
|
||||
|
||||
+5
-11
@@ -1,28 +1,22 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionHasAffliction : AbilityConditionDataless
|
||||
{
|
||||
private string afflictionIdentifier;
|
||||
private float minimumPercentage;
|
||||
|
||||
private readonly Identifier afflictionIdentifier;
|
||||
private readonly float minimumPercentage;
|
||||
|
||||
public AbilityConditionHasAffliction(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
afflictionIdentifier = conditionElement.GetAttributeString("afflictionidentifier", "");
|
||||
afflictionIdentifier = conditionElement.GetAttributeIdentifier("afflictionidentifier", Identifier.Empty);
|
||||
minimumPercentage = conditionElement.GetAttributeFloat("minimumpercentage", 0f);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(afflictionIdentifier))
|
||||
if (!afflictionIdentifier.IsEmpty)
|
||||
{
|
||||
var affliction = character.CharacterHealth.GetAffliction(afflictionIdentifier);
|
||||
|
||||
if (affliction == null) { return false; }
|
||||
|
||||
return minimumPercentage <= affliction.Strength / affliction.Prefab.MaxStrength;
|
||||
}
|
||||
return false;
|
||||
|
||||
+15
-27
@@ -3,55 +3,43 @@ using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionHasItem : AbilityConditionDataless
|
||||
{
|
||||
// not used for anything atm, will be used for clown subclass
|
||||
private readonly string[] tags;
|
||||
private InvSlotType? invSlotType;
|
||||
bool requireAll;
|
||||
|
||||
private List<Item> items = new List<Item>();
|
||||
readonly bool requireAll;
|
||||
|
||||
public AbilityConditionHasItem(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
tags = conditionElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
|
||||
tags = conditionElement.GetAttributeStringArray("tags", Array.Empty<string>());
|
||||
requireAll = conditionElement.GetAttributeBool("requireall", false);
|
||||
//this.invSlotType = invSlotType;
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
items.Clear();
|
||||
if (tags.Any())
|
||||
if (tags.None())
|
||||
{
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
// there is a better method, should use that instead
|
||||
if (character.GetEquippedItem(tag, invSlotType) is Item foundItem)
|
||||
{
|
||||
items.Add(foundItem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (character.GetEquippedItem(null, invSlotType) is Item foundItem)
|
||||
{
|
||||
items.Add(foundItem);
|
||||
}
|
||||
return character.GetEquippedItem(null) is Item;
|
||||
}
|
||||
|
||||
if (requireAll)
|
||||
{
|
||||
return (items.Count >= tags.Count());
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
if (character.GetEquippedItem(tag) == null) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return items.Any();
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
if (character.GetEquippedItem(tag) != null) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionHasVelocity : AbilityConditionDataless
|
||||
{
|
||||
|
||||
+9
-6
@@ -1,7 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionShipFlooded : AbilityConditionDataless
|
||||
{
|
||||
@@ -14,8 +11,14 @@ namespace Barotrauma.Abilities
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
if (!character.IsInFriendlySub) { return false; }
|
||||
float currentFloodPercentage = character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
|
||||
return currentFloodPercentage / 100 > floodPercentage;
|
||||
float waterVolume = 0.0f, totalVolume = 0.0f;
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine != character.Submarine) { continue; }
|
||||
waterVolume += hull.WaterVolume;
|
||||
totalVolume += hull.Volume;
|
||||
}
|
||||
return (waterVolume / totalVolume) > floodPercentage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-6
@@ -1,7 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityModifyStatToFlooding : CharacterAbility
|
||||
{
|
||||
@@ -22,8 +19,14 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (conditionsMatched && Character.IsInFriendlySub)
|
||||
{
|
||||
float currentFloodPercentage = Character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
|
||||
lastValue = currentFloodPercentage / 100f * maxValue;
|
||||
float waterVolume = 0.0f, totalVolume = 0.0f;
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine != Character.Submarine) { continue; }
|
||||
waterVolume += hull.WaterVolume;
|
||||
totalVolume += hull.Volume;
|
||||
}
|
||||
lastValue = (totalVolume == 0.0f ? 1.0f : waterVolume / totalVolume) * maxValue;
|
||||
Character.ChangeStat(statType, lastValue);
|
||||
}
|
||||
else
|
||||
|
||||
+7
-14
@@ -1,8 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityUnlockTree : CharacterAbility
|
||||
{
|
||||
@@ -14,22 +10,19 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (!TalentTree.JobTalentTrees.TryGet(Character.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
|
||||
|
||||
var subTree = talentTree.TalentSubTrees.Find(t => t.TalentOptionStages.Any(ts => ts.Talents.Contains(CharacterTalent.Prefab)));
|
||||
var subTree = talentTree.TalentSubTrees.Find(t => t.AllTalentIdentifiers.Contains(CharacterTalent.Prefab.Identifier));
|
||||
if (subTree == null) { return; }
|
||||
|
||||
subTree.ForceUnlock = true;
|
||||
if (!addingFirstTime) { return; }
|
||||
|
||||
foreach (var talentOption in subTree.TalentOptionStages)
|
||||
foreach (var talentId in subTree.AllTalentIdentifiers)
|
||||
{
|
||||
foreach (var talent in talentOption.Talents)
|
||||
if (talentId == CharacterTalent.Prefab.Identifier) { continue; }
|
||||
if (Character.GiveTalent(talentId))
|
||||
{
|
||||
if (talent == CharacterTalent.Prefab) { continue; }
|
||||
if (Character.GiveTalent(talent))
|
||||
{
|
||||
Character.Info.AdditionalTalentPoints++;
|
||||
}
|
||||
}
|
||||
Character.Info.AdditionalTalentPoints++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-9
@@ -1,10 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityInsurancePolicy : CharacterAbility
|
||||
{
|
||||
@@ -19,10 +13,10 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if (Character?.Info is CharacterInfo info)
|
||||
if (Character?.Info is CharacterInfo info && GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
int totalAmount = moneyPerMission * info.MissionsCompletedSinceDeath;
|
||||
Character.GiveMoney(totalAmount);
|
||||
campaign.Bank.Give(totalAmount);
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(totalAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier.Value);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -15,7 +15,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
public readonly AbilityEffectType AbilityEffectType;
|
||||
|
||||
protected int maxTriggerCount { get; }
|
||||
protected readonly int maxTriggerCount;
|
||||
protected int timesTriggered = 0;
|
||||
|
||||
|
||||
@@ -88,8 +88,6 @@ namespace Barotrauma.Abilities
|
||||
// XML
|
||||
private AbilityCondition ConstructCondition(CharacterTalent characterTalent, ContentXElement conditionElement, bool errorMessages = true)
|
||||
{
|
||||
AbilityCondition newCondition = null;
|
||||
|
||||
Type conditionType;
|
||||
string type = conditionElement.Name.ToString().ToLowerInvariant();
|
||||
try
|
||||
@@ -109,6 +107,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
object[] args = { characterTalent, conditionElement };
|
||||
|
||||
AbilityCondition newCondition;
|
||||
try
|
||||
{
|
||||
newCondition = (AbilityCondition)Activator.CreateInstance(conditionType, args);
|
||||
@@ -210,8 +209,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
public static AbilityFlags ParseFlagType(string flagTypeString, string debugIdentifier)
|
||||
{
|
||||
AbilityFlags flagType = AbilityFlags.None;
|
||||
if (!Enum.TryParse(flagTypeString, true, out flagType))
|
||||
if (!Enum.TryParse(flagTypeString, true, out AbilityFlags flagType))
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid flag type type \"" + flagTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -19,7 +18,12 @@ namespace Barotrauma
|
||||
|
||||
public static readonly PrefabCollection<TalentTree> JobTalentTrees = new PrefabCollection<TalentTree>();
|
||||
|
||||
public readonly List<TalentSubTree> TalentSubTrees = new List<TalentSubTree>();
|
||||
public readonly ImmutableArray<TalentSubTree> TalentSubTrees;
|
||||
|
||||
/// <summary>
|
||||
/// Talent identifiers of all the talents in this tree
|
||||
/// </summary>
|
||||
public readonly ImmutableHashSet<Identifier> AllTalentIdentifiers;
|
||||
|
||||
public ContentXElement ConfigElement
|
||||
{
|
||||
@@ -36,16 +40,19 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"No job defined for talent tree in \"{file.Path}\"!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
List<TalentSubTree> subTrees = new List<TalentSubTree>();
|
||||
foreach (var subTreeElement in element.GetChildElements("subtree"))
|
||||
{
|
||||
TalentSubTrees.Add(new TalentSubTree(subTreeElement));
|
||||
subTrees.Add(new TalentSubTree(subTreeElement));
|
||||
}
|
||||
TalentSubTrees = subTrees.ToImmutableArray();
|
||||
AllTalentIdentifiers = TalentSubTrees.SelectMany(t => t.AllTalentIdentifiers).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
public bool TalentIsInTree(Identifier talentIdentifier)
|
||||
{
|
||||
return TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier))).Any(c => c == talentIdentifier);
|
||||
return AllTalentIdentifiers.Contains(talentIdentifier);
|
||||
}
|
||||
|
||||
public static bool IsViableTalentForCharacter(Character character, Identifier talentIdentifier)
|
||||
@@ -54,6 +61,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// i hate this function - markus
|
||||
// me too - joonas
|
||||
public static TalentTreeStageState GetTalentOptionStageState(Character character, Identifier subTreeIdentifier, int index, List<Identifier> selectedTalents)
|
||||
{
|
||||
if (character?.Info?.Job.Prefab is null) { return TalentTreeStageState.Invalid; }
|
||||
@@ -66,12 +74,12 @@ namespace Barotrauma
|
||||
|
||||
TalentOption targetTalentOption = subTree.TalentOptionStages[index];
|
||||
|
||||
if (targetTalentOption.Talents.Any(t => character.HasTalent(t.Identifier)))
|
||||
if (targetTalentOption.TalentIdentifiers.Any(t => character.HasTalent(t)))
|
||||
{
|
||||
return TalentTreeStageState.Unlocked;
|
||||
}
|
||||
|
||||
if (targetTalentOption.Talents.Any(t => selectedTalents.Contains(t.Identifier)))
|
||||
if (targetTalentOption.TalentIdentifiers.Any(t => selectedTalents.Contains(t)))
|
||||
{
|
||||
return TalentTreeStageState.Highlighted;
|
||||
}
|
||||
@@ -83,8 +91,8 @@ namespace Barotrauma
|
||||
if (lastindex >= 0)
|
||||
{
|
||||
TalentOption lastLatentOption = subTree.TalentOptionStages[lastindex];
|
||||
hasTalentInLastTier = lastLatentOption.Talents.Any(HasTalent);
|
||||
isLastTalentPurchased = lastLatentOption.Talents.Any(t => character.HasTalent(t.Identifier));
|
||||
hasTalentInLastTier = lastLatentOption.TalentIdentifiers.Any(HasTalent);
|
||||
isLastTalentPurchased = lastLatentOption.TalentIdentifiers.Any(t => character.HasTalent(t));
|
||||
}
|
||||
|
||||
if (!hasTalentInLastTier)
|
||||
@@ -101,9 +109,9 @@ namespace Barotrauma
|
||||
|
||||
return TalentTreeStageState.Locked;
|
||||
|
||||
bool HasTalent(TalentPrefab t)
|
||||
bool HasTalent(Identifier talentId)
|
||||
{
|
||||
return selectedTalents.Contains(t.Identifier);
|
||||
return selectedTalents.Contains(talentId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,14 +125,14 @@ namespace Barotrauma
|
||||
|
||||
foreach (var subTree in talentTree.TalentSubTrees)
|
||||
{
|
||||
if (subTree.ForceUnlock && subTree.TalentOptionStages.Any(option => option.Talents.Any(t => t.Identifier == talentIdentifier))) { return true; }
|
||||
if (subTree.ForceUnlock && subTree.TalentOptionStages.Any(option => option.TalentIdentifiers.Contains(talentIdentifier))) { return true; }
|
||||
|
||||
foreach (var talentOptionStage in subTree.TalentOptionStages)
|
||||
{
|
||||
bool hasTalentInThisTier = talentOptionStage.Talents.Any(t => selectedTalents.Contains(t.Identifier));
|
||||
bool hasTalentInThisTier = talentOptionStage.TalentIdentifiers.Any(t => selectedTalents.Contains(t));
|
||||
if (!hasTalentInThisTier)
|
||||
{
|
||||
if (talentOptionStage.Talents.Any(t => t.Identifier == talentIdentifier))
|
||||
if (talentOptionStage.TalentIdentifiers.Contains(talentIdentifier))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -170,18 +178,21 @@ namespace Barotrauma
|
||||
|
||||
public bool ForceUnlock;
|
||||
|
||||
public readonly List<TalentOption> TalentOptionStages = new List<TalentOption>();
|
||||
public readonly ImmutableArray<TalentOption> TalentOptionStages;
|
||||
|
||||
public readonly ImmutableHashSet<Identifier> AllTalentIdentifiers;
|
||||
|
||||
public TalentSubTree(ContentXElement subTreeElement)
|
||||
{
|
||||
Identifier = subTreeElement.GetAttributeIdentifier("identifier", "");
|
||||
|
||||
DisplayName = TextManager.Get("talenttree." + Identifier).Fallback(Identifier.Value);
|
||||
|
||||
List<TalentOption> talentOptionStages = new List<TalentOption>();
|
||||
foreach (var talentOptionsElement in subTreeElement.GetChildElements("talentoptions"))
|
||||
{
|
||||
TalentOptionStages.Add(new TalentOption(talentOptionsElement, Identifier));
|
||||
talentOptionStages.Add(new TalentOption(talentOptionsElement, Identifier));
|
||||
}
|
||||
TalentOptionStages = talentOptionStages.ToImmutableArray();
|
||||
AllTalentIdentifiers = TalentOptionStages.SelectMany(t => t.TalentIdentifiers).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -190,8 +201,12 @@ namespace Barotrauma
|
||||
{
|
||||
private readonly ImmutableHashSet<Identifier> talentIdentifiers;
|
||||
|
||||
public IEnumerable<TalentPrefab> Talents
|
||||
=> talentIdentifiers.Select(id => TalentPrefab.TalentPrefabs[id]);
|
||||
public IEnumerable<Identifier> TalentIdentifiers => talentIdentifiers;
|
||||
|
||||
public bool HasTalent(Identifier talentIdentifier)
|
||||
{
|
||||
return talentIdentifiers.Contains(talentIdentifier);
|
||||
}
|
||||
|
||||
public TalentOption(ContentXElement talentOptionsElement, Identifier debugIdentifier)
|
||||
{
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
[RequiredByCorePackage]
|
||||
sealed class TutorialsFile : GenericPrefabFile<TutorialPrefab>
|
||||
{
|
||||
protected override PrefabCollection<TutorialPrefab> Prefabs => TutorialPrefab.Prefabs;
|
||||
|
||||
public TutorialsFile(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path) { }
|
||||
|
||||
protected override bool MatchesSingular(Identifier identifier) => identifier == "Tutorial";
|
||||
|
||||
protected override bool MatchesPlural(Identifier identifier) => identifier == "Tutorials";
|
||||
|
||||
protected override TutorialPrefab CreatePrefab(ContentXElement element) => new TutorialPrefab(this, element);
|
||||
}
|
||||
}
|
||||
@@ -965,16 +965,10 @@ namespace Barotrauma
|
||||
|
||||
foreach (var talentTree in talentTrees)
|
||||
{
|
||||
foreach (var subTree in talentTree.TalentSubTrees)
|
||||
foreach (var talentId in talentTree.AllTalentIdentifiers)
|
||||
{
|
||||
foreach (var option in subTree.TalentOptionStages)
|
||||
{
|
||||
foreach (var talent in option.Talents)
|
||||
{
|
||||
character.GiveTalent(talent);
|
||||
NewMessage($"Unlocked talent \"{talent.DisplayName}\".");
|
||||
}
|
||||
}
|
||||
character.GiveTalent(talentId);
|
||||
NewMessage($"Unlocked talent \"{talentId}\".");
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1304,102 +1298,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}, null, true));
|
||||
|
||||
commands.Add(new Command("upgradeitem", "upgradeitem [upgrade] [level] [items]: Adds an upgrade to the current targeted item.", args =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
int level;
|
||||
if (args.Length > 1)
|
||||
{
|
||||
if (int.TryParse(args[1], out int result))
|
||||
{
|
||||
level = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError($"\"{args[1]}\" is not a valid level.");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError("Parameter \"level\" is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
var upgradePrefab = UpgradePrefab.Find(args[0].ToIdentifier());
|
||||
|
||||
if (upgradePrefab == null)
|
||||
{
|
||||
ThrowError($"Unknown upgrade: {args[0]}.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<MapEntity> targetItems = new List<MapEntity>();
|
||||
|
||||
if (upgradePrefab.IsWallUpgrade)
|
||||
{
|
||||
targetItems.AddRange(Submarine.MainSub.GetWalls(true).Cast<MapEntity>());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args.Length > 2)
|
||||
{
|
||||
targetItems.AddRange(Item.ItemList.Where(item => item.Submarine == Submarine.MainSub).Where(item => item.HasTag(args[2])).Cast<MapEntity>());
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError("Argument \"tag\" is required.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetItems.Any())
|
||||
{
|
||||
ThrowError("No valid items found.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (MapEntity targetItem in targetItems)
|
||||
{
|
||||
Upgrade existingUpgrade = targetItem.GetUpgrade(args[0].ToIdentifier());
|
||||
|
||||
if (!(targetItem is ISerializableEntity sEntity)) { continue; }
|
||||
|
||||
var upgrade = new Upgrade(sEntity, upgradePrefab, level);
|
||||
if (targetItem.AddUpgrade(upgrade, true))
|
||||
{
|
||||
if (existingUpgrade == null)
|
||||
{
|
||||
NewMessage($"Added {upgradePrefab.Identifier}:{level} to {sEntity.Name}.", Color.Green);
|
||||
upgrade.ApplyUpgrade();
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Set {sEntity.Name}'s {upgradePrefab.Identifier} upgrade to level {existingUpgrade.Level}.", Color.Cyan);
|
||||
existingUpgrade.ApplyUpgrade();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError($"{upgrade.Prefab.Identifier} cannot be applied to {sEntity.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError("Parameter \"upgrade\" is required.");
|
||||
}
|
||||
}, () =>
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
UpgradePrefab.Prefabs.Select(c => c.Identifier).Distinct().Select(i => i.Value).ToArray()
|
||||
};
|
||||
}, true));
|
||||
|
||||
commands.Add(new Command("maxupgrades", "maxupgrades [category] [prefab]: Maxes out all upgrades or only specific one if given arguments.", args =>
|
||||
{
|
||||
UpgradeManager upgradeManager = GameMain.GameSession?.Campaign?.UpgradeManager;
|
||||
@@ -2362,7 +2261,7 @@ namespace Barotrauma
|
||||
{
|
||||
NewMessage(msg, color.Value, isCommand: false, isError: false);
|
||||
}
|
||||
#if DEBUG
|
||||
#if DEBUG && CLIENT
|
||||
Console.WriteLine(msg);
|
||||
#endif
|
||||
}
|
||||
@@ -2547,7 +2446,7 @@ namespace Barotrauma
|
||||
#endif
|
||||
|
||||
fileName += DateTime.Now.ToShortDateString() + "_" + DateTime.Now.ToShortTimeString();
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
var invalidChars = Path.GetInvalidFileNameCharsCrossPlatform();
|
||||
foreach (char invalidChar in invalidChars)
|
||||
{
|
||||
fileName = fileName.Replace(invalidChar.ToString(), "");
|
||||
|
||||
+6
-4
@@ -1,8 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -20,6 +18,9 @@ namespace Barotrauma
|
||||
[Serialize(true, IsPropertySaveable.Yes, "When set to false when TargetLimb is not specified prevent checking limb-specific afflictions")]
|
||||
public bool AllowLimbAfflictions { get; set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, "Minimum strength of the affliction")]
|
||||
public float MinStrength { get; set; }
|
||||
|
||||
public CheckAfflictionAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
@@ -32,14 +33,15 @@ namespace Barotrauma
|
||||
if (target.CharacterHealth == null) { continue; }
|
||||
if (TargetLimb == LimbType.None)
|
||||
{
|
||||
if (target.CharacterHealth.GetAffliction(Identifier, AllowLimbAfflictions) != null) { return true; }
|
||||
var affliction = target.CharacterHealth.GetAffliction(Identifier, AllowLimbAfflictions);
|
||||
if (affliction != null && affliction.Strength >= MinStrength) { return true; }
|
||||
}
|
||||
IEnumerable<Affliction> afflictions = target.CharacterHealth.GetAllAfflictions().Where(affliction =>
|
||||
{
|
||||
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
|
||||
if (limbType == null) { return false; }
|
||||
|
||||
return limbType == TargetLimb || true;
|
||||
return limbType == TargetLimb && affliction.Strength >= MinStrength;
|
||||
});
|
||||
|
||||
if (afflictions.Any(a => a.Identifier == Identifier)) { return true; }
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class CheckConditionalAction : BinaryOptionAction
|
||||
{
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
private PropertyConditional Conditional { get; }
|
||||
|
||||
public CheckConditionalAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
if (TargetTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no target tag! This will cause the check to automatically succeed.");
|
||||
}
|
||||
foreach (var attribute in element.Attributes())
|
||||
{
|
||||
if (PropertyConditional.IsValid(attribute) && !IsTargetTagAttribute(attribute))
|
||||
{
|
||||
Conditional = new PropertyConditional(attribute);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Conditional == null)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.");
|
||||
}
|
||||
|
||||
static bool IsTargetTagAttribute(XAttribute attribute)
|
||||
{
|
||||
return attribute.Name.ToString().Equals("targettag", System.StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetEventName()
|
||||
{
|
||||
return ParentEvent?.Prefab?.Identifier is { IsEmpty: false } identifier ? $"the event \"{identifier}\"" : "an unknown event";
|
||||
}
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
ISerializableEntity target = null;
|
||||
if (!TargetTag.IsEmpty)
|
||||
{
|
||||
foreach (var t in ParentEvent.GetTargets(TargetTag))
|
||||
{
|
||||
if (t is ISerializableEntity e)
|
||||
{
|
||||
target = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.");
|
||||
}
|
||||
if (target == null || Conditional == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (target is Item item)
|
||||
{
|
||||
return item.ConditionalMatches(Conditional);
|
||||
}
|
||||
return Conditional.Matches(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
class CheckOrderAction : BinaryOptionAction
|
||||
{
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderIdentifier { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderOption { get; set; }
|
||||
|
||||
public CheckOrderAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
ISerializableEntity target = null;
|
||||
if (!TargetTag.IsEmpty)
|
||||
{
|
||||
foreach (var t in ParentEvent.GetTargets(TargetTag))
|
||||
{
|
||||
if (t is ISerializableEntity e)
|
||||
{
|
||||
target = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.");
|
||||
return true;
|
||||
}
|
||||
if (target is Character character)
|
||||
{
|
||||
var currentOrderInfo = character.GetCurrentOrderWithTopPriority();
|
||||
if (currentOrderInfo?.Identifier == OrderIdentifier)
|
||||
{
|
||||
if (OrderOption.IsEmpty)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return currentOrderInfo?.Option == OrderOption;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetEventName()
|
||||
{
|
||||
return ParentEvent?.Prefab?.Identifier is { IsEmpty: false } identifier ? $"the event \"{identifier}\"" : "an unknown event";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class MessageBoxAction : EventAction
|
||||
{
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier Header { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier Text { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string IconStyle { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool HideCloseButton { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string CloseOnInput { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier CloseOnInteractTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier CloseOnPickUpTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier CloseOnEquipTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier CloseOnExitRoomName { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool IsTutorialObjective { get; set; }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public MessageBoxAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
#if CLIENT
|
||||
CreateMessageBox();
|
||||
if (IsTutorialObjective && GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
tutorialMode.Tutorial?.TriggerTutorialSegment(new Tutorials.Tutorial.Segment(Text, CreateMessageBox));
|
||||
}
|
||||
#endif
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public void CreateMessageBox()
|
||||
{
|
||||
new GUIMessageBox(
|
||||
headerText: TextManager.Get(Header),
|
||||
text: RichString.Rich(TextManager.ParseInputTypes(TextManager.Get(Text).Fallback(Text.ToString()), useColorHighlight: true)),
|
||||
buttons: Array.Empty<LocalizedString>(),
|
||||
type: GUIMessageBox.Type.Tutorial,
|
||||
iconStyle: IconStyle,
|
||||
autoCloseCondition: GetAutoCloseCondition(),
|
||||
hideCloseButton: HideCloseButton);
|
||||
}
|
||||
#endif
|
||||
|
||||
private Func<bool> GetAutoCloseCondition()
|
||||
{
|
||||
var character = ParentEvent.GetTargets(TargetTag).FirstOrDefault() as Character;
|
||||
Func<bool> autoCloseCondition = null;
|
||||
if (!string.IsNullOrEmpty(CloseOnInput) && Enum.TryParse(CloseOnInput, true, out InputType closeOnInput))
|
||||
{
|
||||
#if CLIENT
|
||||
autoCloseCondition = () => PlayerInput.KeyDown(closeOnInput);
|
||||
#endif
|
||||
}
|
||||
else if (!CloseOnInteractTag.IsEmpty)
|
||||
{
|
||||
autoCloseCondition = () => character?.SelectedItem != null && character.SelectedItem.HasTag(CloseOnInteractTag);
|
||||
}
|
||||
else if (!CloseOnPickUpTag.IsEmpty)
|
||||
{
|
||||
autoCloseCondition = () => character?.Inventory != null && character.Inventory.FindItemByTag(CloseOnPickUpTag, recursive: true) != null;
|
||||
}
|
||||
else if (!CloseOnEquipTag.IsEmpty)
|
||||
{
|
||||
autoCloseCondition = () => character != null && character.HasEquippedItem(CloseOnEquipTag);
|
||||
}
|
||||
else if (!CloseOnExitRoomName.IsEmpty)
|
||||
{
|
||||
autoCloseCondition = () => character?.CurrentHull != null && character.CurrentHull.RoomName.ToIdentifier() != CloseOnExitRoomName;
|
||||
}
|
||||
return autoCloseCondition;
|
||||
}
|
||||
|
||||
public override bool IsFinished(ref string goToLabel)
|
||||
{
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(MessageBoxAction)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,9 +145,9 @@ namespace Barotrauma
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.Write((byte)EventManager.NetworkEventType.MISSION);
|
||||
outmsg.Write(prefab.Identifier);
|
||||
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.WriteByte((byte)EventManager.NetworkEventType.MISSION);
|
||||
outmsg.WriteIdentifier(prefab.Identifier);
|
||||
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,9 @@ namespace Barotrauma
|
||||
|
||||
private readonly HashSet<Identifier> targetModuleTags = new HashSet<Identifier>();
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "If false, we won't spawn another character if one with the same identifier has already been spawned.")]
|
||||
public bool AllowDuplicates { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, "What outpost module tags does the entity prefer to spawn in.")]
|
||||
public string TargetModuleTags
|
||||
{
|
||||
@@ -115,6 +118,12 @@ namespace Barotrauma
|
||||
HumanPrefab humanPrefab = NPCSet.Get(NPCSetIdentifier, NPCIdentifier);
|
||||
if (humanPrefab != null)
|
||||
{
|
||||
if (!AllowDuplicates &&
|
||||
Character.CharacterList.Any(c => c.Info?.HumanPrefabIds.NpcIdentifier == NPCIdentifier && c.Info?.HumanPrefabIds.NpcSetIdentifier == NPCSetIdentifier))
|
||||
{
|
||||
spawned = true;
|
||||
return;
|
||||
}
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
@@ -145,6 +154,11 @@ namespace Barotrauma
|
||||
}
|
||||
else if (!SpeciesName.IsEmpty)
|
||||
{
|
||||
if (!AllowDuplicates && Character.CharacterList.Any(c => c.SpeciesName == SpeciesName))
|
||||
{
|
||||
spawned = true;
|
||||
return;
|
||||
}
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -36,6 +33,7 @@ namespace Barotrauma
|
||||
("crew", v => TagCrew()),
|
||||
("humanprefabidentifier", TagHumansByIdentifier),
|
||||
("structureidentifier", TagStructuresByIdentifier),
|
||||
("structurespecialtag", TagStructuresBySpecialTag),
|
||||
("itemidentifier", TagItemsByIdentifier),
|
||||
("itemtag", TagItemsByTag),
|
||||
("hullname", TagHullsByName)
|
||||
@@ -100,6 +98,11 @@ namespace Barotrauma
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.Prefab.Identifier == identifier);
|
||||
}
|
||||
|
||||
private void TagStructuresBySpecialTag(Identifier tag)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.SpecialTag.ToIdentifier() == tag);
|
||||
}
|
||||
|
||||
private void TagItemsByIdentifier(Identifier identifier)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && SubmarineTypeMatches(it.Submarine) && it.Prefab.Identifier == identifier);
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
class TutorialCompleteAction : EventAction
|
||||
{
|
||||
private bool isFinished;
|
||||
|
||||
public TutorialCompleteAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
tutorialMode.Tutorial?.Complete();
|
||||
}
|
||||
#endif
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override bool IsFinished(ref string goToLabel)
|
||||
{
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
isFinished = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#if CLIENT
|
||||
using Barotrauma.Tutorials;
|
||||
#endif
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class TutorialSegmentAction : EventAction
|
||||
{
|
||||
public enum SegmentActionType { Trigger, Complete, Remove };
|
||||
|
||||
[Serialize(SegmentActionType.Trigger, IsPropertySaveable.Yes)]
|
||||
public SegmentActionType Type { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier Id { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier ObjectiveTextTag { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool AutoPlayVideo { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TextTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string VideoFile { get; set; }
|
||||
|
||||
[Serialize(450, IsPropertySaveable.Yes)]
|
||||
public int Width { get; set; }
|
||||
|
||||
[Serialize(80, IsPropertySaveable.Yes)]
|
||||
public int Height { get; set; }
|
||||
|
||||
#if CLIENT
|
||||
private readonly Tutorial.Segment segment;
|
||||
#endif
|
||||
private bool isFinished;
|
||||
|
||||
public TutorialSegmentAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
#if CLIENT
|
||||
// Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance)
|
||||
if (Type == SegmentActionType.Trigger)
|
||||
{
|
||||
segment = new Tutorial.Segment(Id, ObjectiveTextTag, AutoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No,
|
||||
new Tutorial.Segment.Text(TextTag, Width, Height, Anchor.Center),
|
||||
new Tutorial.Segment.Video(VideoFile, TextTag, Width, Height));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
if (tutorialMode.Tutorial is Tutorial tutorial)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case SegmentActionType.Trigger:
|
||||
tutorial.TriggerTutorialSegment(segment);
|
||||
break;
|
||||
case SegmentActionType.Complete:
|
||||
tutorial.CompleteTutorialSegment(Id);
|
||||
break;
|
||||
case SegmentActionType.Remove:
|
||||
tutorial.RemoveTutorialSegment(Id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ShowError($"Error in event \"{ParentEvent.Prefab.Identifier}\": attempting to use TutorialSegmentAction during a non-Tutorial game mode!");
|
||||
}
|
||||
#endif
|
||||
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override bool IsFinished(ref string goToLabel)
|
||||
{
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
isFinished = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,9 +55,9 @@ namespace Barotrauma
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.Write((byte)EventManager.NetworkEventType.UNLOCKPATH);
|
||||
outmsg.Write((UInt16)GameMain.GameSession.Map.Connections.IndexOf(connection));
|
||||
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.WriteByte((byte)EventManager.NetworkEventType.UNLOCKPATH);
|
||||
outmsg.WriteUInt16((UInt16)GameMain.GameSession.Map.Connections.IndexOf(connection));
|
||||
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ namespace Barotrauma
|
||||
subs[1].FlipX();
|
||||
#if SERVER
|
||||
crews = new List<Character>[] { new List<Character>(), new List<Character>() };
|
||||
roundEndTimer = RoundEndDuration;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using PositionType = Barotrauma.Level.PositionType;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -29,6 +31,25 @@ namespace Barotrauma
|
||||
|
||||
private readonly HashSet<Level.Cave> caves = new HashSet<Level.Cave>();
|
||||
|
||||
private readonly PositionType positionType = PositionType.Cave;
|
||||
/// <remarks>
|
||||
/// The list order is important.
|
||||
/// It defines the order in which we "override" <see cref="positionType"/> in case no valid position types are found
|
||||
/// in the level when generating them in <see cref="Level.GenerateMissionResources(ItemPrefab, int, PositionType, out float)"/>.
|
||||
/// </remarks>
|
||||
public static readonly ImmutableArray<PositionType> ValidPositionTypes = new PositionType[]
|
||||
{
|
||||
PositionType.Cave,
|
||||
PositionType.SidePath,
|
||||
PositionType.MainPath,
|
||||
PositionType.AbyssCave,
|
||||
}.ToImmutableArray();
|
||||
|
||||
/// <summary>
|
||||
/// Percentage. Value between 0 and 1.
|
||||
/// </summary>
|
||||
private readonly float resourceHandoverAmount;
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get
|
||||
@@ -39,8 +60,23 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override LocalizedString SuccessMessage => ModifyMessage(base.SuccessMessage);
|
||||
public override LocalizedString FailureMessage => ModifyMessage(base.FailureMessage);
|
||||
public override LocalizedString Description => ModifyMessage(description);
|
||||
public override LocalizedString Name => ModifyMessage(base.Name, false);
|
||||
public override LocalizedString SonarLabel => ModifyMessage(base.SonarLabel, false);
|
||||
|
||||
public MineralMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
|
||||
{
|
||||
var positionType = prefab.ConfigElement.GetAttributeEnum("PositionType", in this.positionType);
|
||||
if (ValidPositionTypes.Contains(positionType))
|
||||
{
|
||||
this.positionType = positionType;
|
||||
}
|
||||
|
||||
float handoverAmount = prefab.ConfigElement.GetAttributeFloat("ResourceHandoverAmount", 0.0f);
|
||||
resourceHandoverAmount = Math.Clamp(handoverAmount, 0.0f, 1.0f);
|
||||
|
||||
var configElement = prefab.ConfigElement.GetChildElement("Items");
|
||||
foreach (var c in configElement.GetChildElements("Item"))
|
||||
{
|
||||
@@ -92,27 +128,28 @@ namespace Barotrauma
|
||||
caves.Clear();
|
||||
|
||||
if (IsClient) { return; }
|
||||
foreach (var kvp in resourceClusters)
|
||||
|
||||
foreach ((Identifier identifier, ResourceCluster cluster) in resourceClusters)
|
||||
{
|
||||
var prefab = ItemPrefab.Find(null, kvp.Key);
|
||||
if (prefab == null)
|
||||
if (!(MapEntityPrefab.FindByIdentifier(identifier) is ItemPrefab prefab))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"couldn't find an item prefab with the identifier " + kvp.Key);
|
||||
DebugConsole.ThrowError($"Error in MineralMission: couldn't find an item prefab (identifier: \"{identifier}\")");
|
||||
continue;
|
||||
}
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, kvp.Value.Amount, out float rotation);
|
||||
if (spawnedResources.Count < kvp.Value.Amount)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"spawned " + spawnedResources.Count + "/" + kvp.Value.Amount + " of " + prefab.Name);
|
||||
}
|
||||
if (spawnedResources.None()) { continue; }
|
||||
this.spawnedResources.Add(kvp.Key, spawnedResources);
|
||||
|
||||
foreach (Level.Cave cave in Level.Loaded.Caves)
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, cluster.Amount, positionType, out float rotation);
|
||||
if (spawnedResources.Count < cluster.Amount)
|
||||
{
|
||||
foreach (Item spawnedResource in spawnedResources)
|
||||
DebugConsole.ThrowError($"Error in MineralMission: spawned only {spawnedResources.Count}/{cluster.Amount} of {prefab.Name}");
|
||||
}
|
||||
|
||||
if (spawnedResources.None()) { continue; }
|
||||
|
||||
this.spawnedResources.Add(identifier, spawnedResources);
|
||||
|
||||
foreach (var cave in Level.Loaded.Caves)
|
||||
{
|
||||
foreach (var spawnedResource in spawnedResources)
|
||||
{
|
||||
if (cave.Area.Contains(spawnedResource.WorldPosition))
|
||||
{
|
||||
@@ -123,6 +160,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CalculateMissionClusterPositions();
|
||||
FindRelevantLevelResources();
|
||||
}
|
||||
@@ -151,6 +189,28 @@ namespace Barotrauma
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
if (!IsClient)
|
||||
{
|
||||
// When mission is completed successfully, half of the resources will be removed from the player (i.e. given to the outpost as a part of the mission)
|
||||
var handoverResources = new List<Item>();
|
||||
foreach (Identifier identifier in resourceClusters.Keys)
|
||||
{
|
||||
if (relevantLevelResources.TryGetValue(identifier, out var availableResources))
|
||||
{
|
||||
var collectedResources = availableResources.Where(HasBeenCollected);
|
||||
if (collectedResources.Count() < 1) { continue; }
|
||||
int handoverCount = (int)MathF.Round(resourceHandoverAmount * collectedResources.Count());
|
||||
for (int i = 0; i < handoverCount; i++)
|
||||
{
|
||||
handoverResources.Add(collectedResources.ElementAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var resource in handoverResources)
|
||||
{
|
||||
resource.Remove();
|
||||
}
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
@@ -237,5 +297,27 @@ namespace Barotrauma
|
||||
missionClusterPositions.Add((kvp.Key, pos));
|
||||
}
|
||||
}
|
||||
|
||||
protected override LocalizedString ModifyMessage(LocalizedString message, bool color = true)
|
||||
{
|
||||
int i = 1;
|
||||
foreach ((Identifier identifier, ResourceCluster cluster) in resourceClusters)
|
||||
{
|
||||
Replace($"[resourcename{i}]", ItemPrefab.FindByIdentifier(identifier)?.Name.Value ?? "");
|
||||
Replace($"[resourcequantity{i}]", cluster.Amount.ToString());
|
||||
i++;
|
||||
}
|
||||
Replace("[handoverpercentage]", ToolBox.GetFormattedPercentage(resourceHandoverAmount));
|
||||
return message;
|
||||
|
||||
void Replace(string find, string replace)
|
||||
{
|
||||
if (color)
|
||||
{
|
||||
replace = $"‖color:gui.orange‖{replace}‖end‖";
|
||||
}
|
||||
message = message.Replace(find, replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
public readonly ImmutableArray<LocalizedString> Headers;
|
||||
public readonly ImmutableArray<LocalizedString> Messages;
|
||||
|
||||
public LocalizedString Name => Prefab.Name;
|
||||
public virtual LocalizedString Name => Prefab.Name;
|
||||
|
||||
private readonly LocalizedString successMessage;
|
||||
public virtual LocalizedString SuccessMessage
|
||||
@@ -276,6 +276,10 @@ namespace Barotrauma
|
||||
|
||||
partial void ShowMessageProjSpecific(int missionState);
|
||||
|
||||
protected virtual LocalizedString ModifyMessage(LocalizedString message, bool color = true)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
private void TryTriggerEvents(int state)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
#if CLIENT
|
||||
using Barotrauma.Tutorials;
|
||||
#endif
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -348,6 +351,9 @@ namespace Barotrauma
|
||||
private void UpdateConversations(float deltaTime)
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode?.Preset == GameModePreset.TestMode) { return; }
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial is Tutorial tutorial && tutorial.TutorialPrefab.DisableBotConversations) { return; }
|
||||
#endif
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.ServerSettings.DisableBotConversations) { return; }
|
||||
|
||||
conversationTimer -= deltaTime;
|
||||
|
||||
+12
-14
@@ -10,8 +10,6 @@ namespace Barotrauma
|
||||
{
|
||||
partial class MultiPlayerCampaign : CampaignMode
|
||||
{
|
||||
public const int MinimumInitialMoney = 500;
|
||||
|
||||
[Flags]
|
||||
public enum NetFlags : UInt16
|
||||
{
|
||||
@@ -294,14 +292,14 @@ namespace Barotrauma
|
||||
|
||||
private static void WriteItems(IWriteMessage msg, Dictionary<Identifier, List<PurchasedItem>> purchasedItems)
|
||||
{
|
||||
msg.Write((byte)purchasedItems.Count);
|
||||
msg.WriteByte((byte)purchasedItems.Count);
|
||||
foreach (var storeItems in purchasedItems)
|
||||
{
|
||||
msg.Write(storeItems.Key);
|
||||
msg.Write((UInt16)storeItems.Value.Count);
|
||||
msg.WriteIdentifier(storeItems.Key);
|
||||
msg.WriteUInt16((UInt16)storeItems.Value.Count);
|
||||
foreach (var item in storeItems.Value)
|
||||
{
|
||||
msg.Write(item.ItemPrefabIdentifier);
|
||||
msg.WriteIdentifier(item.ItemPrefabIdentifier);
|
||||
msg.WriteRangedInteger(item.Quantity, 0, CargoManager.MaxQuantity);
|
||||
}
|
||||
}
|
||||
@@ -328,18 +326,18 @@ namespace Barotrauma
|
||||
|
||||
private static void WriteItems(IWriteMessage msg, Dictionary<Identifier, List<SoldItem>> soldItems)
|
||||
{
|
||||
msg.Write((byte)soldItems.Count);
|
||||
msg.WriteByte((byte)soldItems.Count);
|
||||
foreach (var storeItems in soldItems)
|
||||
{
|
||||
msg.Write(storeItems.Key);
|
||||
msg.Write((UInt16)storeItems.Value.Count);
|
||||
msg.WriteIdentifier(storeItems.Key);
|
||||
msg.WriteUInt16((UInt16)storeItems.Value.Count);
|
||||
foreach (var item in storeItems.Value)
|
||||
{
|
||||
msg.Write(item.ItemPrefab.Identifier);
|
||||
msg.Write((UInt16)item.ID);
|
||||
msg.Write(item.Removed);
|
||||
msg.Write(item.SellerID);
|
||||
msg.Write((byte)item.Origin);
|
||||
msg.WriteIdentifier(item.ItemPrefab.Identifier);
|
||||
msg.WriteUInt16((UInt16)item.ID);
|
||||
msg.WriteBoolean(item.Removed);
|
||||
msg.WriteByte(item.SellerID);
|
||||
msg.WriteByte((byte)item.Origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -11,37 +13,37 @@ namespace Barotrauma
|
||||
|
||||
public void AssignTeamIDs(IEnumerable<Client> clients)
|
||||
{
|
||||
int teamWeight = 0;
|
||||
List<Client> randList = new List<Client>(clients);
|
||||
for (int i = 0; i < randList.Count; i++)
|
||||
int team1Count = 0, team2Count = 0;
|
||||
//if a client has a preference, assign them to that team
|
||||
List<Client> unassignedClients = new List<Client>(clients);
|
||||
for (int i = 0; i < unassignedClients.Count; i++)
|
||||
{
|
||||
if (randList[i].PreferredTeam == CharacterTeamType.Team1 ||
|
||||
randList[i].PreferredTeam == CharacterTeamType.Team2)
|
||||
if (unassignedClients[i].PreferredTeam == CharacterTeamType.Team1 ||
|
||||
unassignedClients[i].PreferredTeam == CharacterTeamType.Team2)
|
||||
{
|
||||
randList[i].TeamID = randList[i].PreferredTeam;
|
||||
teamWeight += randList[i].PreferredTeam == CharacterTeamType.Team1 ? -1 : 1;
|
||||
randList.RemoveAt(i);
|
||||
assignTeam(unassignedClients[i], unassignedClients[i].PreferredTeam);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i<randList.Count; i++)
|
||||
{
|
||||
Client a = randList[i];
|
||||
int oi = Rand.Range(0, randList.Count - 1);
|
||||
Client b = randList[oi];
|
||||
randList[i] = b;
|
||||
randList[oi] = a;
|
||||
}
|
||||
int halfPlayers = (randList.Count / 2) + teamWeight;
|
||||
for (int i = 0; i < randList.Count; i++)
|
||||
|
||||
//assign the rest of the clients to the team that has the least players
|
||||
while (unassignedClients.Any())
|
||||
{
|
||||
if (i < halfPlayers)
|
||||
var randomClient = unassignedClients.GetRandom(Rand.RandSync.Unsynced);
|
||||
assignTeam(randomClient, team1Count < team2Count ? CharacterTeamType.Team1 : CharacterTeamType.Team2);
|
||||
}
|
||||
|
||||
void assignTeam(Client client, CharacterTeamType team)
|
||||
{
|
||||
client.TeamID = team;
|
||||
unassignedClients.Remove(client);
|
||||
if (team == CharacterTeamType.Team1)
|
||||
{
|
||||
randList[i].TeamID = CharacterTeamType.Team1;
|
||||
team1Count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
randList[i].TeamID = CharacterTeamType.Team2;
|
||||
team2Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class TutorialPrefab : Prefab
|
||||
{
|
||||
public static readonly PrefabCollection<TutorialPrefab> Prefabs = new PrefabCollection<TutorialPrefab>();
|
||||
|
||||
public readonly int Order;
|
||||
public readonly bool DisableBotConversations;
|
||||
public readonly bool AllowCharacterSwitch;
|
||||
|
||||
public readonly ContentPath SubmarinePath = ContentPath.FromRaw("Content/Tutorials/Dugong_Tutorial.sub");
|
||||
public readonly ContentPath OutpostPath = ContentPath.FromRaw("Content/Tutorials/TutorialOutpost.sub");
|
||||
public readonly string LevelSeed;
|
||||
public readonly string LevelParams;
|
||||
|
||||
private readonly ContentXElement tutorialCharacterElement;
|
||||
public readonly ImmutableArray<Identifier> StartingItemTags;
|
||||
|
||||
public readonly Identifier EventIdentifier;
|
||||
|
||||
public TutorialPrefab(ContentFile file, ContentXElement element) : base(file, element.GetAttributeIdentifier("identifier", ""))
|
||||
{
|
||||
Order = element.GetAttributeInt("order", int.MaxValue);
|
||||
DisableBotConversations = element.GetAttributeBool("disablebotconversations", true);
|
||||
AllowCharacterSwitch = element.GetAttributeBool("allowcharacterswitch", false);
|
||||
|
||||
SubmarinePath = element.GetAttributeContentPath("submarinepath") ?? SubmarinePath;
|
||||
OutpostPath = element.GetAttributeContentPath("outpostpath") ?? OutpostPath;
|
||||
LevelSeed = element.GetAttributeString("levelseed", "nLoZLLtza");
|
||||
LevelParams = element.GetAttributeString("levelparams", "ColdCavernsTutorial");
|
||||
|
||||
tutorialCharacterElement = element.GetChildElement("characterinfo");
|
||||
if (tutorialCharacterElement != null)
|
||||
{
|
||||
StartingItemTags = tutorialCharacterElement
|
||||
.GetAttributeIdentifierArray("startingitemtags", new Identifier[0])
|
||||
.ToImmutableArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartingItemTags = ImmutableArray<Identifier>.Empty;
|
||||
}
|
||||
|
||||
EventIdentifier = element.GetChildElement("scriptedevent")?.GetAttributeIdentifier("identifier", "") ?? Identifier.Empty;
|
||||
}
|
||||
|
||||
public CharacterInfo GetTutorialCharacterInfo()
|
||||
{
|
||||
if (tutorialCharacterElement == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Identifier speciesName = tutorialCharacterElement.GetAttributeIdentifier("speciesname", CharacterPrefab.HumanSpeciesName);
|
||||
string jobPrefabIdentifier = tutorialCharacterElement.GetAttributeString("jobidentifier", "assistant");
|
||||
var jobPrefab = JobPrefab.Prefabs.FirstOrDefault(p => p.Identifier == jobPrefabIdentifier) ?? JobPrefab.Prefabs.First();
|
||||
int jobVariant = tutorialCharacterElement.GetAttributeInt("variant", 0);
|
||||
var characterInfo = new CharacterInfo(speciesName, jobOrJobPrefab: jobPrefab, variant: jobVariant);
|
||||
foreach (var skillElement in tutorialCharacterElement.GetChildElements("skill"))
|
||||
{
|
||||
Identifier skillIdentifier = skillElement.GetAttributeIdentifier("identifier", "");
|
||||
if (skillIdentifier.IsEmpty) { continue; }
|
||||
float level = skillElement.GetAttributeFloat("level", 0.0f);
|
||||
characterInfo.SetSkillLevel(skillIdentifier, level);
|
||||
}
|
||||
return characterInfo;
|
||||
}
|
||||
|
||||
public override void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ namespace Barotrauma
|
||||
if (selectedSub != null)
|
||||
{
|
||||
campaign.Bank.Deduct(selectedSub.Price);
|
||||
campaign.Bank.Balance = Math.Max(campaign.Bank.Balance, MultiPlayerCampaign.MinimumInitialMoney);
|
||||
campaign.Bank.Balance = Math.Max(campaign.Bank.Balance, 0);
|
||||
}
|
||||
return campaign;
|
||||
}
|
||||
@@ -222,7 +222,7 @@ namespace Barotrauma
|
||||
if (selectedSub != null)
|
||||
{
|
||||
campaign.Bank.TryDeduct(selectedSub.Price);
|
||||
campaign.Bank.Balance = Math.Max(campaign.Bank.Balance, MultiPlayerCampaign.MinimumInitialMoney);
|
||||
campaign.Bank.Balance = Math.Max(campaign.Bank.Balance, 0);
|
||||
}
|
||||
return campaign;
|
||||
}
|
||||
@@ -602,7 +602,7 @@ namespace Barotrauma
|
||||
if (GameMode is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.UpgradeManager.ApplyUpgrades();
|
||||
mpCampaign.UpgradeManager.SanityCheckUpgrades(Submarine);
|
||||
mpCampaign.UpgradeManager.SanityCheckUpgrades();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -512,8 +512,11 @@ namespace Barotrauma
|
||||
/// Should be called after every round start right after <see cref="ApplyUpgrades"/>
|
||||
/// </summary>
|
||||
/// <param name="submarine"></param>
|
||||
public void SanityCheckUpgrades(Submarine submarine)
|
||||
public void SanityCheckUpgrades()
|
||||
{
|
||||
Submarine submarine = GameMain.GameSession?.Submarine ?? Submarine.MainSub;
|
||||
if (submarine is null) { return; }
|
||||
|
||||
// check walls
|
||||
foreach (Structure wall in submarine.GetWalls(UpgradeAlsoConnectedSubs))
|
||||
{
|
||||
@@ -521,23 +524,8 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
|
||||
{
|
||||
int level = GetRealUpgradeLevel(prefab, category);
|
||||
if (level == 0 || !prefab.IsWallUpgrade) { continue; }
|
||||
|
||||
Upgrade? upgrade = wall.GetUpgrade(prefab.Identifier);
|
||||
|
||||
bool isOverMax = IsOverMaxLevel(level, prefab);
|
||||
if (isOverMax)
|
||||
{
|
||||
SetUpgradeLevel(prefab, category, prefab.MaxLevel);
|
||||
level = prefab.MaxLevel;
|
||||
}
|
||||
|
||||
if (upgrade == null || upgrade.Level != level || isOverMax)
|
||||
{
|
||||
DebugLog($"{((MapEntity)wall).Prefab.Name} has incorrect \"{prefab.Name}\" level! Expected {level} but got {upgrade?.Level ?? 0}. Fixing...");
|
||||
FixUpgradeOnItem(wall, prefab, level);
|
||||
}
|
||||
if (!prefab.IsWallUpgrade) { continue; }
|
||||
TryFixUpgrade(wall, category, prefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -549,35 +537,38 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
|
||||
{
|
||||
if (!category.CanBeApplied(item, prefab)) { continue; }
|
||||
|
||||
int level = GetRealUpgradeLevel(prefab, category);
|
||||
if (level == 0) { continue; }
|
||||
|
||||
Upgrade? upgrade = item.GetUpgrade(prefab.Identifier);
|
||||
bool isOverMax = IsOverMaxLevel(level, prefab);
|
||||
if (isOverMax)
|
||||
{
|
||||
SetUpgradeLevel(prefab, category, prefab.MaxLevel);
|
||||
level = prefab.MaxLevel;
|
||||
}
|
||||
|
||||
if (upgrade == null || upgrade.Level != level || isOverMax)
|
||||
{
|
||||
DebugLog($"{((MapEntity)item).Prefab.Name} has incorrect \"{prefab.Name}\" level! Expected {level} but got {upgrade?.Level ?? 0}{(isOverMax ? " (Over max level!)" : string.Empty)}. Fixing...");
|
||||
FixUpgradeOnItem(item, prefab, level);
|
||||
}
|
||||
TryFixUpgrade(item, category, prefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsOverMaxLevel(int level, UpgradePrefab prefab) => level > prefab.MaxLevel;
|
||||
void TryFixUpgrade(MapEntity entity, UpgradeCategory category, UpgradePrefab prefab)
|
||||
{
|
||||
if (!category.CanBeApplied(entity, prefab)) { return; }
|
||||
|
||||
int level = GetRealUpgradeLevel(prefab, category);
|
||||
int maxLevel = submarine.Info is { } info ? prefab.GetMaxLevel(info) : prefab.MaxLevel;
|
||||
if (maxLevel < level) { level = maxLevel; }
|
||||
|
||||
if (level == 0) { return; }
|
||||
|
||||
Upgrade? upgrade = entity.GetUpgrade(prefab.Identifier);
|
||||
|
||||
if (upgrade == null || upgrade.Level != level)
|
||||
{
|
||||
DebugLog($"{entity.Prefab.Name} has incorrect \"{prefab.Name}\" level! Expected {level} but got {upgrade?.Level ?? 0}. Fixing...");
|
||||
FixUpgradeOnItem((ISerializableEntity)entity, prefab, level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixUpgradeOnItem(ISerializableEntity target, UpgradePrefab prefab, int level)
|
||||
{
|
||||
if (target is MapEntity mapEntity)
|
||||
{
|
||||
// do not fix what's not broken
|
||||
if (level == 0) { return; }
|
||||
|
||||
mapEntity.SetUpgrade(new Upgrade(target, prefab, level), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,5 +23,6 @@ namespace Barotrauma
|
||||
PreviousFireMode,
|
||||
ActiveChat,
|
||||
ToggleChatMode,
|
||||
ChatBox
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,7 +1191,7 @@ namespace Barotrauma.Items.Components
|
||||
//trying to dock/undock from an outpost and the signal was sent by some automated system instead of a character
|
||||
// -> ask if the player really wants to dock/undock to prevent a softlock if someone's wired the docking port
|
||||
// in a way that makes always makes it dock/undock immediately at the start of the roun
|
||||
if (tryingToToggleOutpostDocking && signal.sender == null)
|
||||
if (GameMain.NetworkMember != null && tryingToToggleOutpostDocking && signal.sender == null)
|
||||
{
|
||||
if (allowOutpostAutoDocking == AllowOutpostAutoDocking.Ask)
|
||||
{
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace Barotrauma.Items.Components
|
||||
public static FoliageConfig CreateRandomConfig(int maxVariants, float minScale, float maxScale, Random? random = null)
|
||||
{
|
||||
int flowerVariant = Growable.RandomInt(0, maxVariants, random);
|
||||
float flowerScale = (float) Growable.RandomDouble(minScale, maxScale, random);
|
||||
float flowerRotation = (float) Growable.RandomDouble(0, MathHelper.TwoPi, random);
|
||||
float flowerScale = (float)Growable.RandomDouble(minScale, maxScale, random);
|
||||
float flowerRotation = (float)Growable.RandomDouble(0, MathHelper.TwoPi, random);
|
||||
return new FoliageConfig { Variant = flowerVariant, Scale = flowerScale, Rotation = flowerRotation };
|
||||
}
|
||||
}
|
||||
@@ -169,10 +169,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
const float limit = 1.0f;
|
||||
growthStep = value;
|
||||
VineStep = Math.Min((float) Math.Pow(value, 2), limit);
|
||||
VineStep = Math.Min((float)Math.Pow(value, 2), limit);
|
||||
if (value > limit)
|
||||
{
|
||||
FlowerStep = Math.Min((float) Math.Pow(value - limit, 2), limit);
|
||||
FlowerStep = Math.Min((float)Math.Pow(value - limit, 2), limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (Type == VineTileType.Stem) { return; }
|
||||
|
||||
Type = (VineTileType) Sides;
|
||||
Type = (VineTileType)Sides;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -310,7 +310,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool IsSideBlocked(TileSide side) => BlockedSides.HasFlag(side) || Sides.HasFlag(side);
|
||||
|
||||
public static Rectangle CreatePlantRect(Vector2 pos) => new Rectangle((int) pos.X - Size / 2, (int) pos.Y + Size / 2, Size, Size);
|
||||
public static Rectangle CreatePlantRect(Vector2 pos) => new Rectangle((int)pos.X - Size / 2, (int)pos.Y + Size / 2, Size, Size);
|
||||
}
|
||||
|
||||
internal static class GrowthSideExtension
|
||||
@@ -318,7 +318,7 @@ namespace Barotrauma.Items.Components
|
||||
// K&R algorithm for counting how many bits are set in a bit field
|
||||
public static int Count(this TileSide side)
|
||||
{
|
||||
int n = (int) side;
|
||||
int n = (int)side;
|
||||
int count = 0;
|
||||
while (n != 0)
|
||||
{
|
||||
@@ -607,7 +607,7 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
foreach (VineTile vine in Vines)
|
||||
{
|
||||
vine.DecayDelay = (float) RandomDouble(0f, 30f);
|
||||
vine.DecayDelay = (float)RandomDouble(0f, 30f);
|
||||
}
|
||||
#endif
|
||||
#if SERVER
|
||||
@@ -742,7 +742,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
var (x, y, z, w) = GrowthWeights;
|
||||
float[] weights = { x, y, z, w };
|
||||
int index = (int) Math.Log2((int) side);
|
||||
int index = (int)Math.Log2((int)side);
|
||||
if (MathUtils.NearlyEqual(weights[index], 0f))
|
||||
{
|
||||
oldVines.FailedGrowthAttempts++;
|
||||
@@ -778,7 +778,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (VineTile otherVine in Vines)
|
||||
{
|
||||
var (distX, distY) = pos - otherVine.Position;
|
||||
int absDistX = (int) Math.Abs(distX), absDistY = (int) Math.Abs(distY);
|
||||
int absDistX = (int)Math.Abs(distX), absDistY = (int)Math.Abs(distY);
|
||||
|
||||
// check if the tile is within the with or height distance from us but ignore diagonals
|
||||
if (absDistX > newVine.Rect.Width || absDistY > newVine.Rect.Height || absDistX > 0 && absDistY > 0) { continue; }
|
||||
@@ -872,10 +872,10 @@ namespace Barotrauma.Items.Components
|
||||
foreach (VineTile vine in Vines)
|
||||
{
|
||||
XElement vineElement = new XElement("Vine");
|
||||
vineElement.Add(new XAttribute("sides", (int) vine.Sides));
|
||||
vineElement.Add(new XAttribute("blockedsides", (int) vine.BlockedSides));
|
||||
vineElement.Add(new XAttribute("sides", (int)vine.Sides));
|
||||
vineElement.Add(new XAttribute("blockedsides", (int)vine.BlockedSides));
|
||||
vineElement.Add(new XAttribute("pos", XMLExtensions.Vector2ToString(vine.Position)));
|
||||
vineElement.Add(new XAttribute("tile", (int) vine.Type));
|
||||
vineElement.Add(new XAttribute("tile", (int)vine.Type));
|
||||
vineElement.Add(new XAttribute("failedattempts", vine.FailedGrowthAttempts));
|
||||
#if SERVER
|
||||
vineElement.Add(new XAttribute("growthscale", Decayed ? 1.0f : 2.0f));
|
||||
@@ -902,10 +902,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (element.Name.ToString().Equals("vine", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
VineTileType type = (VineTileType) element.GetAttributeInt("tile", 0);
|
||||
VineTileType type = (VineTileType)element.GetAttributeInt("tile", 0);
|
||||
Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);
|
||||
TileSide sides = (TileSide) element.GetAttributeInt("sides", 0);
|
||||
TileSide blockedSides = (TileSide) element.GetAttributeInt("blockedsides", 0);
|
||||
TileSide sides = (TileSide)element.GetAttributeInt("sides", 0);
|
||||
TileSide blockedSides = (TileSide)element.GetAttributeInt("blockedsides", 0);
|
||||
int failedAttempts = element.GetAttributeInt("failedattempts", 0);
|
||||
float growthscale = element.GetAttributeFloat("growthscale", 0f);
|
||||
int flowerConfig = element.GetAttributeInt("flowerconfig", FoliageConfig.EmptyConfigValue);
|
||||
|
||||
@@ -4,6 +4,7 @@ using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -62,7 +63,7 @@ namespace Barotrauma.Items.Components
|
||||
/// <summary>
|
||||
/// Defines items that boost the weapon functionality, like battery cell for stun batons.
|
||||
/// </summary>
|
||||
public readonly Identifier[] PreferredContainedItems;
|
||||
public readonly ImmutableHashSet<Identifier> PreferredContainedItems;
|
||||
|
||||
public MeleeWeapon(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
@@ -77,7 +78,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
item.IsShootable = true;
|
||||
item.RequireAimToUse = element.Parent.GetAttributeBool("requireaimtouse", true);
|
||||
PreferredContainedItems = element.GetAttributeIdentifierArray("preferredcontaineditems", Array.Empty<Identifier>());
|
||||
PreferredContainedItems = element.GetAttributeIdentifierArray("preferredcontaineditems", Array.Empty<Identifier>()).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
@@ -387,14 +388,16 @@ namespace Barotrauma.Items.Components
|
||||
User = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
float damageMultiplier = 1 + User.GetStatValue(StatTypes.MeleeAttackMultiplier);
|
||||
damageMultiplier *= 1.0f + item.GetQualityModifier(Quality.StatType.StrikingPowerMultiplier);
|
||||
|
||||
Limb targetLimb = target.UserData as Limb;
|
||||
Character targetCharacter = targetLimb?.character ?? target.UserData as Character;
|
||||
if (Attack != null)
|
||||
{
|
||||
Attack.SetUser(User);
|
||||
Attack.DamageMultiplier = 1 + User.GetStatValue(StatTypes.MeleeAttackMultiplier);
|
||||
Attack.DamageMultiplier *= 1.0f + item.GetQualityModifier(Quality.StatType.StrikingPowerMultiplier);
|
||||
Attack.DamageMultiplier = damageMultiplier;
|
||||
|
||||
if (targetLimb != null)
|
||||
{
|
||||
@@ -467,7 +470,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (targetCharacter != null) //TODO: Allow OnUse to happen on structures too maybe??
|
||||
{
|
||||
ApplyStatusEffects(success ? ActionType.OnUse : ActionType.OnFailure, 1.0f, targetCharacter, targetLimb, user: User);
|
||||
ApplyStatusEffects(success ? ActionType.OnUse : ActionType.OnFailure, 1.0f, targetCharacter, targetLimb, user: User, afflictionMultiplier: damageMultiplier);
|
||||
}
|
||||
|
||||
if (DeleteOnUse)
|
||||
|
||||
@@ -287,7 +287,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(activePicker?.ID ?? (ushort)0);
|
||||
msg.WriteUInt16(activePicker?.ID ?? (ushort)0);
|
||||
}
|
||||
|
||||
public virtual void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
|
||||
@@ -229,8 +229,6 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
public virtual bool RecreateGUIOnResolutionChange => false;
|
||||
|
||||
/// <summary>
|
||||
/// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
|
||||
/// </summary>
|
||||
@@ -397,7 +395,7 @@ namespace Barotrauma.Items.Components
|
||||
RelatedItem ri = RelatedItem.Load(element, returnEmpty, item.Name);
|
||||
if (ri != null)
|
||||
{
|
||||
if (ri.Identifiers.Length == 0)
|
||||
if (ri.Identifiers.Count == 0)
|
||||
{
|
||||
DisabledRequiredItems.Add(ri);
|
||||
}
|
||||
@@ -816,7 +814,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null, float applyOnUserFraction = 0.0f)
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null, float afflictionMultiplier = 1.0f, float applyOnUserFraction = 0.0f)
|
||||
{
|
||||
if (statusEffectLists == null) { return; }
|
||||
|
||||
@@ -828,13 +826,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (broken && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { continue; }
|
||||
if (user != null) { effect.SetUser(user); }
|
||||
effect.AfflictionMultiplier = afflictionMultiplier;
|
||||
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, useTarget, isNetworkEvent: false, checkCondition: false, worldPosition);
|
||||
if (user != null && applyOnUserFraction > 0.0f && effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
effect.AfflictionMultiplier = applyOnUserFraction;
|
||||
item.ApplyStatusEffect(effect, type, deltaTime, user, targetLimb == null ? null : user.AnimController.GetLimb(targetLimb.type), useTarget, false, false, worldPosition);
|
||||
effect.AfflictionMultiplier = 1.0f;
|
||||
}
|
||||
effect.AfflictionMultiplier = 1.0f;
|
||||
reducesCondition |= effect.ReducesItemCondition();
|
||||
}
|
||||
//if any of the effects reduce the item's condition, set the user for OnBroken effects as well
|
||||
@@ -1070,7 +1069,7 @@ namespace Barotrauma.Items.Components
|
||||
AIObjectiveContainItem containObjective = null;
|
||||
if (character.AIController is HumanAIController aiController)
|
||||
{
|
||||
containObjective = new AIObjectiveContainItem(character, container.ContainableItemIdentifiers.ToArray(), container, currentObjective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
containObjective = new AIObjectiveContainItem(character, container.ContainableItemIdentifiers, container, currentObjective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
{
|
||||
ItemCount = itemCount,
|
||||
Equip = equip,
|
||||
|
||||
@@ -216,9 +216,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private ImmutableHashSet<Identifier> containableItemIdentifiers;
|
||||
public IEnumerable<Identifier> ContainableItemIdentifiers => containableItemIdentifiers;
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
public ImmutableHashSet<Identifier> ContainableItemIdentifiers => containableItemIdentifiers;
|
||||
|
||||
public List<RelatedItem> ContainableItems { get; }
|
||||
|
||||
|
||||
@@ -40,8 +40,6 @@ namespace Barotrauma.Items.Components
|
||||
[Editable, Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float DeconstructionSpeed { get; set; }
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
public Deconstructor(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -122,7 +120,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if ((Entity.Spawner?.IsInRemoveQueue(targetItem) ?? false) || !inputContainer.Inventory.AllItems.Contains(targetItem)) { continue; }
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
|
||||
(it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)) &&
|
||||
it.IsValidDeconstructor(item) &&
|
||||
(it.RequiredOtherItem.Length == 0 || it.RequiredOtherItem.Any(r => items.Any(it => it != targetItem && (it.HasTag(r) || it.Prefab.Identifier == r))))).ToList();
|
||||
|
||||
ProcessItem(targetItem, items, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.Prefab.DeconstructItems.Any());
|
||||
@@ -140,9 +138,7 @@ namespace Barotrauma.Items.Components
|
||||
var targetItem = inputContainer.Inventory.LastOrDefault();
|
||||
if (targetItem == null) { return; }
|
||||
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
|
||||
it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)).ToList();
|
||||
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it => it.IsValidDeconstructor(item)).ToList();
|
||||
float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * deconstructionSpeedModifier) : 1.0f;
|
||||
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
|
||||
@@ -76,8 +76,6 @@ namespace Barotrauma.Items.Components
|
||||
get { return outputContainer; }
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
private float progressState;
|
||||
|
||||
private readonly Dictionary<uint, int> fabricationLimits = new Dictionary<uint, int>();
|
||||
@@ -647,10 +645,13 @@ namespace Barotrauma.Items.Components
|
||||
if (skills.Length == 0) { return 1.0f; }
|
||||
if (character == null) { return 0.0f; }
|
||||
|
||||
float skillSum = (from t in skills let characterLevel = character.GetSkillLevel(t.Identifier) select (characterLevel - (t.Level * SkillRequirementMultiplier))).Sum();
|
||||
float average = skillSum / skills.Length;
|
||||
|
||||
return (average + 100.0f) / 2.0f / 100.0f;
|
||||
float minDegreeOfSuccess = 1.0f;
|
||||
foreach (var skill in skills)
|
||||
{
|
||||
float characterLevel = character.GetSkillLevel(skill.Identifier);
|
||||
minDegreeOfSuccess = Math.Min(minDegreeOfSuccess, (characterLevel - (skill.Level * SkillRequirementMultiplier) + 100.0f) / 2.0f / 100.0f);
|
||||
}
|
||||
return minDegreeOfSuccess;
|
||||
}
|
||||
|
||||
public override float GetSkillMultiplier()
|
||||
@@ -658,13 +659,16 @@ namespace Barotrauma.Items.Components
|
||||
return SkillRequirementMultiplier;
|
||||
}
|
||||
|
||||
|
||||
private readonly HashSet<Inventory> linkedInventories = new HashSet<Inventory>();
|
||||
|
||||
private void RefreshAvailableIngredients()
|
||||
{
|
||||
Character user = this.user;
|
||||
#if CLIENT
|
||||
user ??= Character.Controlled;
|
||||
#endif
|
||||
|
||||
linkedInventories.Clear();
|
||||
List<Item> itemList = new List<Item>();
|
||||
itemList.AddRange(inputContainer.Inventory.AllItems);
|
||||
foreach (MapEntity linkedTo in item.linkedTo)
|
||||
@@ -684,6 +688,7 @@ namespace Barotrauma.Items.Components
|
||||
itemContainer = deconstructor.OutputContainer;
|
||||
}
|
||||
|
||||
linkedInventories.Add(itemContainer.Inventory);
|
||||
itemList.AddRange(itemContainer.Inventory.AllItems);
|
||||
}
|
||||
}
|
||||
@@ -698,6 +703,7 @@ namespace Barotrauma.Items.Components
|
||||
if (user?.Inventory != null)
|
||||
{
|
||||
itemList.AddRange(user.Inventory.AllItems);
|
||||
linkedInventories.Add(user.Inventory);
|
||||
}
|
||||
availableIngredients.Clear();
|
||||
foreach (Item item in itemList)
|
||||
|
||||
@@ -48,6 +48,9 @@ namespace Barotrauma.Items.Components
|
||||
private Vector2 optimalFissionRate, allowedFissionRate;
|
||||
private Vector2 optimalTurbineOutput, allowedTurbineOutput;
|
||||
|
||||
private float? signalControlledTargetFissionRate, signalControlledTargetTurbineOutput;
|
||||
private double lastReceivedFissionRateSignalTime, lastReceivedTurbineOutputSignalTime;
|
||||
|
||||
private float temperatureBoost;
|
||||
|
||||
private bool _powerOn;
|
||||
@@ -245,7 +248,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
|
||||
if (signalControlledTargetFissionRate.HasValue && Math.Abs(signalControlledTargetFissionRate.Value - TargetFissionRate) > 1.0f)
|
||||
if (signalControlledTargetFissionRate.HasValue && lastReceivedFissionRateSignalTime > Timing.TotalTime - 1)
|
||||
{
|
||||
TargetFissionRate = adjustValueWithoutOverShooting(TargetFissionRate, signalControlledTargetFissionRate.Value, deltaTime * 5.0f);
|
||||
#if CLIENT
|
||||
@@ -256,7 +259,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
signalControlledTargetFissionRate = null;
|
||||
}
|
||||
if (signalControlledTargetTurbineOutput.HasValue && Math.Abs(signalControlledTargetTurbineOutput.Value - TargetTurbineOutput) > 1.0f)
|
||||
if (signalControlledTargetTurbineOutput.HasValue && lastReceivedTurbineOutputSignalTime > Timing.TotalTime - 1)
|
||||
{
|
||||
TargetTurbineOutput = adjustValueWithoutOverShooting(TargetTurbineOutput, signalControlledTargetTurbineOutput.Value, deltaTime * 5.0f);
|
||||
#if CLIENT
|
||||
@@ -841,6 +844,7 @@ namespace Barotrauma.Items.Components
|
||||
if (PowerOn && float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float newFissionRate))
|
||||
{
|
||||
signalControlledTargetFissionRate = MathHelper.Clamp(newFissionRate, 0.0f, 100.0f);
|
||||
lastReceivedFissionRateSignalTime = Timing.TotalTime;
|
||||
registerUnsentChanges();
|
||||
}
|
||||
break;
|
||||
@@ -848,6 +852,7 @@ namespace Barotrauma.Items.Components
|
||||
if (PowerOn && float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float newTurbineOutput))
|
||||
{
|
||||
signalControlledTargetTurbineOutput = MathHelper.Clamp(newTurbineOutput, 0.0f, 100.0f);
|
||||
lastReceivedTurbineOutputSignalTime = Timing.TotalTime;
|
||||
registerUnsentChanges();
|
||||
}
|
||||
break;
|
||||
@@ -858,7 +863,5 @@ namespace Barotrauma.Items.Components
|
||||
if (GameMain.NetworkMember is { IsServer: true }) { unsentChanges = true; }
|
||||
}
|
||||
}
|
||||
|
||||
private float? signalControlledTargetFissionRate, signalControlledTargetTurbineOutput;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +113,23 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have mineral scanning mode. " +
|
||||
"Only available in-game when the Item has no Steering component.")]
|
||||
public bool HasMineralScanner { get; set; }
|
||||
private bool hasMineralScanner;
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have mineral scanning mode. ")]
|
||||
public bool HasMineralScanner
|
||||
{
|
||||
get => hasMineralScanner;
|
||||
set
|
||||
{
|
||||
#if CLIENT
|
||||
if (controlContainer != null && !hasMineralScanner && value)
|
||||
{
|
||||
AddMineralScannerSwitchToGUI();
|
||||
}
|
||||
#endif
|
||||
hasMineralScanner = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
@@ -144,8 +158,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
public Sonar(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -396,17 +408,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(currentMode == Mode.Active);
|
||||
msg.WriteBoolean(currentMode == Mode.Active);
|
||||
if (currentMode == Mode.Active)
|
||||
{
|
||||
msg.WriteRangedSingle(zoom, MinZoom, MaxZoom, 8);
|
||||
msg.Write(useDirectionalPing);
|
||||
msg.WriteBoolean(useDirectionalPing);
|
||||
if (useDirectionalPing)
|
||||
{
|
||||
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
|
||||
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
|
||||
}
|
||||
msg.Write(useMineralScanner);
|
||||
msg.WriteBoolean(useMineralScanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,6 @@ namespace Barotrauma.Items.Components
|
||||
set { posToMaintain = value; }
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
struct ObstacleDebugInfo
|
||||
{
|
||||
public Vector2 Point1;
|
||||
|
||||
@@ -401,14 +401,14 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteVariableUInt32((uint)connection.Wires.Count);
|
||||
foreach (Wire wire in connection.Wires)
|
||||
{
|
||||
msg.Write(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
msg.WriteUInt16(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((ushort)DisconnectedWires.Count);
|
||||
msg.WriteUInt16((ushort)DisconnectedWires.Count);
|
||||
foreach (Wire disconnectedWire in DisconnectedWires)
|
||||
{
|
||||
msg.Write(disconnectedWire.Item.ID);
|
||||
msg.WriteUInt16(disconnectedWire.Item.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-2
@@ -155,7 +155,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//use semicolon as a separator because comma may be needed in the signals (for color or vector values for example)
|
||||
//kind of hacky, we should probably add support for (string) arrays to SerializableEntityEditor so this wouldn't be needed
|
||||
get { return signals == null ? "" : string.Join(";", signals); }
|
||||
get { return signals == null ? string.Empty : string.Join(";", signals); }
|
||||
set
|
||||
{
|
||||
if (value == null) { return; }
|
||||
@@ -167,7 +167,31 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
private bool[] elementStates;
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "", alwaysUseInstanceValues: true)]
|
||||
public string ElementStates
|
||||
{
|
||||
get { return elementStates == null ? string.Empty : string.Join(",", elementStates); }
|
||||
set
|
||||
{
|
||||
if (value == null) { return; }
|
||||
if (customInterfaceElementList.Count > 0)
|
||||
{
|
||||
string[] splitValues = value == "" ? Array.Empty<string>() : value.Split(',');
|
||||
for (int i = 0; i < customInterfaceElementList.Count && i < splitValues.Length; i++)
|
||||
{
|
||||
if (!bool.TryParse(splitValues[i], out bool val)) { continue; }
|
||||
customInterfaceElementList[i].State = val;
|
||||
#if CLIENT
|
||||
if (uiElements != null && i < uiElements.Count && uiElements[i] is GUITickBox tickBox)
|
||||
{
|
||||
tickBox.Selected = val;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<CustomInterfaceElement> customInterfaceElementList = new List<CustomInterfaceElement>();
|
||||
|
||||
@@ -207,8 +231,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
IsActive = true;
|
||||
InitProjSpecific();
|
||||
//load these here to ensure the UI elements (created in InitProjSpecific) are up-to-date
|
||||
Labels = element.GetAttributeString("labels", "");
|
||||
Signals = element.GetAttributeString("signals", "");
|
||||
ElementStates = element.GetAttributeString("elementstates", "");
|
||||
}
|
||||
|
||||
private void UpdateLabels(string[] newLabels)
|
||||
@@ -386,6 +412,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
labels = customInterfaceElementList.Select(ci => ci.Label).ToArray();
|
||||
signals = customInterfaceElementList.Select(ci => ci.Signal).ToArray();
|
||||
elementStates = customInterfaceElementList.Select(ci => ci.State).ToArray();
|
||||
return base.Save(parentElement);
|
||||
}
|
||||
|
||||
|
||||
@@ -253,7 +253,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void CheckIfNeedsUpdate()
|
||||
{
|
||||
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null && IsOn &&
|
||||
if (!IsOn)
|
||||
{
|
||||
base.IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null &&
|
||||
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
|
||||
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
|
||||
{
|
||||
@@ -268,7 +274,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
IsActive = true;
|
||||
base.IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(isOn);
|
||||
msg.WriteBoolean(isOn);
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -1151,7 +1152,7 @@ namespace Barotrauma.Items.Components
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
var loadItemsObjective = AIContainItems<Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true, dropItemOnDeselected: true);
|
||||
loadItemsObjective.ignoredContainerIdentifiers = new Identifier[] { ((MapEntity)containerItem).Prefab.Identifier };
|
||||
loadItemsObjective.ignoredContainerIdentifiers = ((MapEntity)containerItem).Prefab.Identifier.ToEnumerable().ToImmutableHashSet();
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, formatCapitals: FormatCapitals.Yes).Value,
|
||||
@@ -1340,7 +1341,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.AIController.SelectedAiTarget == null && !hadCurrentTarget)
|
||||
{
|
||||
if (CreatureMetrics.Instance.RecentlyEncountered.Contains(closestEnemy.SpeciesName))
|
||||
if (CreatureMetrics.Instance.RecentlyEncountered.Contains(closestEnemy.SpeciesName) || closestEnemy.IsHuman)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogNewTargetSpotted").Value,
|
||||
identifier: "newtargetspotted".ToIdentifier(),
|
||||
@@ -1611,6 +1612,7 @@ namespace Barotrauma.Items.Components
|
||||
targetRotation = rotation = (minRotation + maxRotation) / 2;
|
||||
|
||||
UpdateTransformedBarrelPos();
|
||||
UpdateLightComponents();
|
||||
}
|
||||
|
||||
public override void FlipY(bool relativeToSub)
|
||||
@@ -1632,6 +1634,7 @@ namespace Barotrauma.Items.Components
|
||||
targetRotation = rotation = (minRotation + maxRotation) / 2;
|
||||
|
||||
UpdateTransformedBarrelPos();
|
||||
UpdateLightComponents();
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
@@ -1714,12 +1717,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (TryExtractEventData(extraData, out EventData eventData))
|
||||
{
|
||||
msg.Write(eventData.Projectile.ID);
|
||||
msg.WriteUInt16(eventData.Projectile.ID);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(rotation, minRotation, maxRotation), minRotation, maxRotation, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write((ushort)0);
|
||||
msg.WriteUInt16((ushort)0);
|
||||
float wrappedTargetRotation = targetRotation;
|
||||
while (wrappedTargetRotation < minRotation && MathUtils.IsValid(wrappedTargetRotation))
|
||||
{
|
||||
|
||||
@@ -560,7 +560,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
public override void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write((byte)Variant);
|
||||
msg.WriteByte((byte)Variant);
|
||||
base.ServerEventWrite(msg, c, extraData);
|
||||
}
|
||||
|
||||
|
||||
@@ -959,14 +959,14 @@ namespace Barotrauma
|
||||
|
||||
public void SharedWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write((byte)capacity);
|
||||
msg.WriteByte((byte)capacity);
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
msg.WriteRangedInteger(slots[i].Items.Count, 0, MaxStackSize);
|
||||
for (int j = 0; j < Math.Min(slots[i].Items.Count, MaxStackSize); j++)
|
||||
{
|
||||
var item = slots[i].Items[j];
|
||||
msg.Write(item?.ID ?? (ushort)0);
|
||||
msg.WriteUInt16(item?.ID ?? (ushort)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1536,7 +1536,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ConditionalMatches(PropertyConditional conditional)
|
||||
public bool ConditionalMatches(PropertyConditional conditional)
|
||||
{
|
||||
if (string.IsNullOrEmpty(conditional.TargetItemComponentName))
|
||||
{
|
||||
@@ -2861,77 +2861,77 @@ namespace Barotrauma
|
||||
var propertyOwner = allProperties.Find(p => p.property == property);
|
||||
if (allProperties.Count > 1)
|
||||
{
|
||||
msg.Write((byte)allProperties.FindIndex(p => p.property == property));
|
||||
msg.WriteByte((byte)allProperties.FindIndex(p => p.property == property));
|
||||
}
|
||||
|
||||
object value = property.GetValue(propertyOwner.obj);
|
||||
if (value is string stringVal)
|
||||
{
|
||||
msg.Write(stringVal);
|
||||
msg.WriteString(stringVal);
|
||||
}
|
||||
else if (value is Identifier idValue)
|
||||
{
|
||||
msg.Write(idValue);
|
||||
msg.WriteIdentifier(idValue);
|
||||
}
|
||||
else if (value is float floatVal)
|
||||
{
|
||||
msg.Write(floatVal);
|
||||
msg.WriteSingle(floatVal);
|
||||
}
|
||||
else if (value is int intVal)
|
||||
{
|
||||
msg.Write(intVal);
|
||||
msg.WriteInt32(intVal);
|
||||
}
|
||||
else if (value is bool boolVal)
|
||||
{
|
||||
msg.Write(boolVal);
|
||||
msg.WriteBoolean(boolVal);
|
||||
}
|
||||
else if (value is Color color)
|
||||
{
|
||||
msg.Write(color.R);
|
||||
msg.Write(color.G);
|
||||
msg.Write(color.B);
|
||||
msg.Write(color.A);
|
||||
msg.WriteByte(color.R);
|
||||
msg.WriteByte(color.G);
|
||||
msg.WriteByte(color.B);
|
||||
msg.WriteByte(color.A);
|
||||
}
|
||||
else if (value is Vector2 vector2)
|
||||
{
|
||||
msg.Write(vector2.X);
|
||||
msg.Write(vector2.Y);
|
||||
msg.WriteSingle(vector2.X);
|
||||
msg.WriteSingle(vector2.Y);
|
||||
}
|
||||
else if (value is Vector3 vector3)
|
||||
{
|
||||
msg.Write(vector3.X);
|
||||
msg.Write(vector3.Y);
|
||||
msg.Write(vector3.Z);
|
||||
msg.WriteSingle(vector3.X);
|
||||
msg.WriteSingle(vector3.Y);
|
||||
msg.WriteSingle(vector3.Z);
|
||||
}
|
||||
else if (value is Vector4 vector4)
|
||||
{
|
||||
msg.Write(vector4.X);
|
||||
msg.Write(vector4.Y);
|
||||
msg.Write(vector4.Z);
|
||||
msg.Write(vector4.W);
|
||||
msg.WriteSingle(vector4.X);
|
||||
msg.WriteSingle(vector4.Y);
|
||||
msg.WriteSingle(vector4.Z);
|
||||
msg.WriteSingle(vector4.W);
|
||||
}
|
||||
else if (value is Point point)
|
||||
{
|
||||
msg.Write(point.X);
|
||||
msg.Write(point.Y);
|
||||
msg.WriteInt32(point.X);
|
||||
msg.WriteInt32(point.Y);
|
||||
}
|
||||
else if (value is Rectangle rect)
|
||||
{
|
||||
msg.Write(rect.X);
|
||||
msg.Write(rect.Y);
|
||||
msg.Write(rect.Width);
|
||||
msg.Write(rect.Height);
|
||||
msg.WriteInt32(rect.X);
|
||||
msg.WriteInt32(rect.Y);
|
||||
msg.WriteInt32(rect.Width);
|
||||
msg.WriteInt32(rect.Height);
|
||||
}
|
||||
else if (value is Enum)
|
||||
{
|
||||
msg.Write((int)value);
|
||||
msg.WriteInt32((int)value);
|
||||
}
|
||||
else if (value is string[] a)
|
||||
{
|
||||
msg.Write(a.Length);
|
||||
msg.WriteInt32(a.Length);
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
msg.Write(a[i] ?? "");
|
||||
msg.WriteString(a[i] ?? "");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -53,6 +53,11 @@ namespace Barotrauma
|
||||
InfoText = element.GetAttributeString("infotext", string.Empty);
|
||||
InfoTextOnOtherItemMissing = element.GetAttributeString("infotextonotheritemmissing", string.Empty);
|
||||
}
|
||||
|
||||
public bool IsValidDeconstructor(Item deconstructor)
|
||||
{
|
||||
return RequiredDeconstructor.Length == 0 || RequiredDeconstructor.Any(r => deconstructor.HasTag(r) || deconstructor.Prefab.Identifier == r);
|
||||
}
|
||||
}
|
||||
|
||||
class FabricationRecipe
|
||||
@@ -62,6 +67,10 @@ namespace Barotrauma
|
||||
public abstract IEnumerable<ItemPrefab> ItemPrefabs { get; }
|
||||
public abstract UInt32 UintIdentifier { get; }
|
||||
|
||||
public abstract bool MatchesItem(Item item);
|
||||
|
||||
public abstract ItemPrefab FirstMatchingPrefab { get; }
|
||||
|
||||
public RequiredItem(int amount, float minCondition, float maxCondition, bool useCondition)
|
||||
{
|
||||
Amount = amount;
|
||||
@@ -92,12 +101,21 @@ namespace Barotrauma
|
||||
public class RequiredItemByIdentifier : RequiredItem
|
||||
{
|
||||
public readonly Identifier ItemPrefabIdentifier;
|
||||
|
||||
public ItemPrefab ItemPrefab => ItemPrefab.Prefabs.TryGet(ItemPrefabIdentifier, out var prefab) ? prefab
|
||||
: MapEntityPrefab.FindByName(ItemPrefabIdentifier.Value) as ItemPrefab ?? throw new Exception($"No ItemPrefab with identifier or name \"{ItemPrefabIdentifier}\"");
|
||||
|
||||
public override UInt32 UintIdentifier { get; }
|
||||
|
||||
public override IEnumerable<ItemPrefab> ItemPrefabs => ItemPrefab.ToEnumerable();
|
||||
|
||||
public override ItemPrefab FirstMatchingPrefab => ItemPrefab;
|
||||
|
||||
public override bool MatchesItem(Item item)
|
||||
{
|
||||
return item?.Prefab.Identifier == ItemPrefabIdentifier;
|
||||
}
|
||||
|
||||
public RequiredItemByIdentifier(Identifier itemPrefab, int amount, float minCondition, float maxCondition, bool useCondition) : base(amount, minCondition, maxCondition, useCondition)
|
||||
{
|
||||
ItemPrefabIdentifier = itemPrefab;
|
||||
@@ -109,10 +127,19 @@ namespace Barotrauma
|
||||
public class RequiredItemByTag : RequiredItem
|
||||
{
|
||||
public readonly Identifier Tag;
|
||||
|
||||
public override UInt32 UintIdentifier { get; }
|
||||
|
||||
public override IEnumerable<ItemPrefab> ItemPrefabs => ItemPrefab.Prefabs.Where(p => p.Tags.Contains(Tag));
|
||||
|
||||
public override ItemPrefab FirstMatchingPrefab => ItemPrefab.Prefabs.FirstOrDefault(p => p.Tags.Contains(Tag));
|
||||
|
||||
public override bool MatchesItem(Item item)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
return item.HasTag(Tag);
|
||||
}
|
||||
|
||||
public RequiredItemByTag(Identifier tag, int amount, float minCondition, float maxCondition, bool useCondition) : base(amount, minCondition, maxCondition, useCondition)
|
||||
{
|
||||
Tag = tag;
|
||||
@@ -208,10 +235,10 @@ namespace Barotrauma
|
||||
if (requiredItemIdentifier != Identifier.Empty)
|
||||
{
|
||||
var existing = requiredItems.FindIndex(r =>
|
||||
r is RequiredItemByIdentifier ri &&
|
||||
ri.ItemPrefabIdentifier == requiredItemIdentifier &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
r is RequiredItemByIdentifier ri &&
|
||||
ri.ItemPrefabIdentifier == requiredItemIdentifier &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
if (existing >= 0)
|
||||
{
|
||||
amount += requiredItems[existing].Amount;
|
||||
@@ -222,10 +249,10 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
var existing = requiredItems.FindIndex(r =>
|
||||
r is RequiredItemByTag rt &&
|
||||
rt.Tag == requiredItemTag &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
r is RequiredItemByTag rt &&
|
||||
rt.Tag == requiredItemTag &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
if (existing >= 0)
|
||||
{
|
||||
amount += requiredItems[existing].Amount;
|
||||
@@ -783,19 +810,12 @@ namespace Barotrauma
|
||||
//works the same as nameIdentifier, but just replaces the description
|
||||
Identifier descriptionIdentifier = ConfigElement.GetAttributeIdentifier("descriptionidentifier", "");
|
||||
|
||||
if (string.IsNullOrEmpty(OriginalName))
|
||||
{
|
||||
name = TextManager.Get(nameIdentifier.IsEmpty
|
||||
? $"EntityName.{Identifier}"
|
||||
: $"EntityName.{nameIdentifier}",
|
||||
$"EntityName.{fallbackNameIdentifier}");
|
||||
}
|
||||
else if (Category.HasFlag(MapEntityCategory.Legacy))
|
||||
{
|
||||
// Legacy items use names as identifiers, so we have to define them in the xml. But we also want to support the translations. Therefore
|
||||
name = TextManager.Get(nameIdentifier.IsEmpty
|
||||
name = TextManager.Get(nameIdentifier.IsEmpty
|
||||
? $"EntityName.{Identifier}"
|
||||
: $"EntityName.{nameIdentifier}");
|
||||
: $"EntityName.{nameIdentifier}",
|
||||
$"EntityName.{fallbackNameIdentifier}");
|
||||
if (!string.IsNullOrEmpty(OriginalName))
|
||||
{
|
||||
name = name.Fallback(OriginalName);
|
||||
}
|
||||
|
||||
@@ -838,20 +858,17 @@ namespace Barotrauma
|
||||
|
||||
SerializableProperty.DeserializeProperties(this, ConfigElement);
|
||||
|
||||
if (Description.IsNullOrEmpty())
|
||||
if (descriptionIdentifier != Identifier.Empty)
|
||||
{
|
||||
if (descriptionIdentifier != Identifier.Empty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}");
|
||||
}
|
||||
else if (nameIdentifier == Identifier.Empty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{Identifier}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{nameIdentifier}");
|
||||
}
|
||||
Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}").Fallback(Description);
|
||||
}
|
||||
else if (nameIdentifier == Identifier.Empty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{Identifier}").Fallback(Description);
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{nameIdentifier}").Fallback(Description);
|
||||
}
|
||||
|
||||
var allowDroppingOnSwapWith = ConfigElement.GetAttributeIdentifierArray("allowdroppingonswapwith", Array.Empty<Identifier>());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -22,7 +23,7 @@ namespace Barotrauma
|
||||
|
||||
public bool IgnoreInEditor { get; set; }
|
||||
|
||||
private Identifier[] excludedIdentifiers;
|
||||
private ImmutableHashSet<Identifier> excludedIdentifiers;
|
||||
|
||||
private RelationType type;
|
||||
|
||||
@@ -60,11 +61,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
Identifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToArray();
|
||||
Identifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToImmutableHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
public Identifier[] Identifiers { get; private set; }
|
||||
public ImmutableHashSet<Identifier> Identifiers { get; private set; }
|
||||
|
||||
public string JoinedExcludedIdentifiers
|
||||
{
|
||||
@@ -73,27 +74,53 @@ namespace Barotrauma
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
excludedIdentifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToArray();
|
||||
excludedIdentifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToImmutableHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
public bool MatchesItem(Item item)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; }
|
||||
return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && item.Prefab.VariantOf == id));
|
||||
if (excludedIdentifiers.Contains(item.Prefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
||||
{
|
||||
if (item.HasTag(excludedIdentifier)) { return false; }
|
||||
}
|
||||
if (Identifiers.Contains(item.Prefab.Identifier)) { return true; }
|
||||
foreach (var identifier in Identifiers)
|
||||
{
|
||||
if (item.HasTag(identifier)) { return true; }
|
||||
}
|
||||
if (AllowVariants && !item.Prefab.VariantOf.IsEmpty)
|
||||
{
|
||||
if (Identifiers.Contains(item.Prefab.VariantOf)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool MatchesItem(ItemPrefab itemPrefab)
|
||||
{
|
||||
if (itemPrefab == null) { return false; }
|
||||
if (excludedIdentifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id))) { return false; }
|
||||
return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (AllowVariants && !itemPrefab.VariantOf.IsEmpty && itemPrefab.VariantOf == id));
|
||||
if (excludedIdentifiers.Contains(itemPrefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
||||
{
|
||||
if (itemPrefab.Tags.Contains(excludedIdentifier)) { return false; }
|
||||
}
|
||||
if (Identifiers.Contains(itemPrefab.Identifier)) { return true; }
|
||||
foreach (var identifier in Identifiers)
|
||||
{
|
||||
if (itemPrefab.Tags.Contains(identifier)) { return true; }
|
||||
}
|
||||
if (AllowVariants && !itemPrefab.VariantOf.IsEmpty)
|
||||
{
|
||||
if (Identifiers.Contains(itemPrefab.VariantOf)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public RelatedItem(Identifier[] identifiers, Identifier[] excludedIdentifiers)
|
||||
{
|
||||
this.Identifiers = identifiers.Select(id => id.Value.Trim().ToIdentifier()).ToArray();
|
||||
this.excludedIdentifiers = excludedIdentifiers.Select(id => id.Value.Trim().ToIdentifier()).ToArray();
|
||||
this.Identifiers = identifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
||||
this.excludedIdentifiers = excludedIdentifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
||||
|
||||
statusEffects = new List<StatusEffect>();
|
||||
}
|
||||
@@ -161,7 +188,7 @@ namespace Barotrauma
|
||||
new XAttribute("targetslot", TargetSlot),
|
||||
new XAttribute("allowvariants", AllowVariants));
|
||||
|
||||
if (excludedIdentifiers.Length > 0)
|
||||
if (excludedIdentifiers.Count > 0)
|
||||
{
|
||||
element.Add(new XAttribute("excludedidentifiers", JoinedExcludedIdentifiers));
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "Diagonal" gaps are used on sloped walls to allow characters to pass through them either horizontally or vertically.
|
||||
/// Water still flows through them only horizontally or vertically
|
||||
/// </summary>
|
||||
public bool IsDiagonal { get; }
|
||||
|
||||
//a value between 0.0f-1.0f (0.0 = closed, 1.0f = open)
|
||||
private float open;
|
||||
|
||||
@@ -135,12 +141,13 @@ namespace Barotrauma
|
||||
: this(rect, rect.Width < rect.Height, submarine)
|
||||
{ }
|
||||
|
||||
public Gap(Rectangle rect, bool isHorizontal, Submarine submarine, ushort id = Entity.NullEntityID)
|
||||
public Gap(Rectangle rect, bool isHorizontal, Submarine submarine, bool isDiagonal = false, ushort id = Entity.NullEntityID)
|
||||
: base(CoreEntityPrefab.GapPrefab, submarine, id)
|
||||
{
|
||||
this.rect = rect;
|
||||
flowForce = Vector2.Zero;
|
||||
IsHorizontal = isHorizontal;
|
||||
IsDiagonal = isDiagonal;
|
||||
open = 1.0f;
|
||||
|
||||
FindHulls();
|
||||
@@ -666,15 +673,15 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Gap gap in gaps)
|
||||
{
|
||||
if (gap.Open == 0.0f || gap.IsRoomToRoom) continue;
|
||||
if (gap.Open == 0.0f || gap.IsRoomToRoom) { continue; }
|
||||
|
||||
if (gap.ConnectedWall != null)
|
||||
{
|
||||
int sectionIndex = gap.ConnectedWall.FindSectionIndex(gap.Position);
|
||||
if (sectionIndex > -1 && !gap.ConnectedWall.SectionBodyDisabled(sectionIndex)) continue;
|
||||
if (sectionIndex > -1 && !gap.ConnectedWall.SectionBodyDisabled(sectionIndex)) { continue; }
|
||||
}
|
||||
|
||||
if (gap.IsHorizontal)
|
||||
if (gap.IsHorizontal || gap.IsDiagonal)
|
||||
{
|
||||
if (worldPos.Y < gap.WorldRect.Y && worldPos.Y > gap.WorldRect.Y - gap.WorldRect.Height &&
|
||||
Math.Abs(gap.WorldRect.Center.X - worldPos.X) < allowedOrthogonalDist)
|
||||
@@ -682,7 +689,7 @@ namespace Barotrauma
|
||||
return gap;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!gap.IsHorizontal || gap.IsDiagonal)
|
||||
{
|
||||
if (worldPos.X > gap.WorldRect.X && worldPos.X < gap.WorldRect.Right &&
|
||||
Math.Abs(gap.WorldRect.Y - gap.WorldRect.Height / 2 - worldPos.Y) < allowedOrthogonalDist)
|
||||
@@ -754,7 +761,7 @@ namespace Barotrauma
|
||||
isHorizontal = horizontalAttribute.Value.ToString() == "true";
|
||||
}
|
||||
|
||||
Gap g = new Gap(rect, isHorizontal, submarine, idRemap.GetOffsetId(element))
|
||||
Gap g = new Gap(rect, isHorizontal, submarine, id: idRemap.GetOffsetId(element))
|
||||
{
|
||||
linkedToID = new List<ushort>(),
|
||||
};
|
||||
|
||||
@@ -759,7 +759,7 @@ namespace Barotrauma
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
msg.WriteRangedSingle(BackgroundSections[i].ColorStrength, 0.0f, 1.0f, 8);
|
||||
msg.Write(BackgroundSections[i].Color.PackedValue);
|
||||
msg.WriteUInt32(BackgroundSections[i].Color.PackedValue);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -994,7 +994,11 @@ namespace Barotrauma
|
||||
foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0))
|
||||
{
|
||||
var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f);
|
||||
item.body.ApplyForce((gap.LerpedFlowForce / distance) * deltaTime);
|
||||
Vector2 force = (gap.LerpedFlowForce / distance) * deltaTime;
|
||||
if (force.LengthSquared() > 0.01f)
|
||||
{
|
||||
item.body.ApplyForce(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1545,7 +1549,7 @@ namespace Barotrauma
|
||||
|
||||
var hull = new Hull(rect, submarine, idRemap.GetOffsetId(element))
|
||||
{
|
||||
WaterVolume = element.GetAttributeFloat("pressure", 0.0f)
|
||||
WaterVolume = element.GetAttributeFloat("water", 0.0f)
|
||||
};
|
||||
hull.linkedToID = new List<ushort>();
|
||||
|
||||
|
||||
@@ -1972,7 +1972,7 @@ namespace Barotrauma
|
||||
|
||||
List<Tunnel> caveBranches = new List<Tunnel>();
|
||||
|
||||
var tunnel = new Tunnel(TunnelType.Cave, SegmentsToNodes(caveSegments), 100, parentTunnel);
|
||||
var tunnel = new Tunnel(TunnelType.Cave, SegmentsToNodes(caveSegments), 150, parentTunnel);
|
||||
Tunnels.Add(tunnel);
|
||||
caveBranches.Add(tunnel);
|
||||
|
||||
@@ -1989,7 +1989,7 @@ namespace Barotrauma
|
||||
bounds: caveArea);
|
||||
if (!branchSegments.Any()) { continue; }
|
||||
|
||||
var branch = new Tunnel(TunnelType.Cave, SegmentsToNodes(branchSegments), 0, parentBranch);
|
||||
var branch = new Tunnel(TunnelType.Cave, SegmentsToNodes(branchSegments), 150, parentBranch);
|
||||
Tunnels.Add(branch);
|
||||
caveBranches.Add(branch);
|
||||
}
|
||||
@@ -2569,34 +2569,38 @@ namespace Barotrauma
|
||||
AbyssResources.Clear();
|
||||
|
||||
var abyssResourcePrefabs = levelResources.Where(r => r.commonnessInfo.AbyssCommonness > 0.0f);
|
||||
int abyssClusterCount = (int)MathHelper.Lerp(GenerationParams.AbyssResourceClustersMin, GenerationParams.AbyssResourceClustersMax, MathUtils.InverseLerp(LevelData.Biome.MinDifficulty, LevelData.Biome.AdjustedMaxDifficulty, Difficulty));
|
||||
for (int i = 0; i < abyssClusterCount; i++)
|
||||
if (abyssResourcePrefabs.Any())
|
||||
{
|
||||
var selectedPrefab = ToolBox.SelectWeightedRandom(
|
||||
abyssResourcePrefabs.Select(r => r.itemPrefab).ToList(),
|
||||
abyssResourcePrefabs.Select(r => r.commonnessInfo.AbyssCommonness).ToList(),
|
||||
Rand.RandSync.ServerAndClient);
|
||||
|
||||
var location = allValidLocations.GetRandom(l =>
|
||||
int abyssClusterCount = (int)MathHelper.Lerp(GenerationParams.AbyssResourceClustersMin, GenerationParams.AbyssResourceClustersMax, MathUtils.InverseLerp(LevelData.Biome.MinDifficulty, LevelData.Biome.AdjustedMaxDifficulty, Difficulty));
|
||||
for (int i = 0; i < abyssClusterCount; i++)
|
||||
{
|
||||
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);
|
||||
var selectedPrefab = ToolBox.SelectWeightedRandom(
|
||||
abyssResourcePrefabs.Select(r => r.itemPrefab).ToList(),
|
||||
abyssResourcePrefabs.Select(r => r.commonnessInfo.AbyssCommonness).ToList(),
|
||||
Rand.RandSync.ServerAndClient);
|
||||
|
||||
if (location.Cell == null || location.Edge == null) { break; }
|
||||
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);
|
||||
|
||||
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.ServerAndClient);
|
||||
PlaceResources(selectedPrefab, clusterSize, location, out var placedResources, maxResourceOverlap: 0);
|
||||
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
|
||||
abyssClusterLocation.Resources.AddRange(placedResources);
|
||||
AbyssResources.Add(abyssClusterLocation);
|
||||
if (location.Cell == null || location.Edge == null) { break; }
|
||||
|
||||
var locationIndex = allValidLocations.FindIndex(l => l.Equals(location));
|
||||
allValidLocations.RemoveAt(locationIndex);
|
||||
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.ServerAndClient);
|
||||
PlaceResources(selectedPrefab, clusterSize, location, out var placedResources, maxResourceOverlap: 0);
|
||||
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
|
||||
abyssClusterLocation.Resources.AddRange(placedResources);
|
||||
AbyssResources.Add(abyssClusterLocation);
|
||||
|
||||
var locationIndex = allValidLocations.FindIndex(l => l.Equals(location));
|
||||
allValidLocations.RemoveAt(locationIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PathPoints.Clear();
|
||||
nextPathPointId = 0;
|
||||
|
||||
@@ -2928,50 +2932,74 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <param name="rotation">Used by clients to set the rotation for the resources</param>
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, out float rotation)
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, PositionType positionType, out float rotation)
|
||||
{
|
||||
var allValidLocations = GetAllValidClusterLocations();
|
||||
var placedResources = new List<Item>();
|
||||
rotation = 0.0f;
|
||||
|
||||
if (allValidLocations.None()) { return placedResources; } // TODO: WHAT?!
|
||||
|
||||
// Make sure not to pick a spot that already has other level resources
|
||||
for (int i = allValidLocations.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var location = allValidLocations[i];
|
||||
var locationHasResources = PathPoints.Any(p =>
|
||||
p.ClusterLocations.Any(c =>
|
||||
c.Equals(location) &&
|
||||
c.Resources.Any(r => r != null && !r.Removed &&
|
||||
(!(r.GetComponent<Holdable>() is Holdable h) || (h.Attachable && h.Attached)))));
|
||||
if (locationHasResources)
|
||||
if (HasResources(allValidLocations[i]))
|
||||
{
|
||||
allValidLocations.RemoveAt(i);
|
||||
}
|
||||
|
||||
bool HasResources(ClusterLocation clusterLocation)
|
||||
{
|
||||
foreach (var p in PathPoints)
|
||||
{
|
||||
foreach (var c in p.ClusterLocations)
|
||||
{
|
||||
if (!c.Equals(clusterLocation)) { continue; }
|
||||
foreach (var r in c.Resources)
|
||||
{
|
||||
if (r == null) { continue; }
|
||||
if (r.Removed) { continue; }
|
||||
if (!(r.GetComponent<Holdable>() is Holdable h) || (h.Attachable && h.Attached)) { return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var positionType = PositionType.MainPath;
|
||||
if (PositionsOfInterest.Any(p => p.PositionType == PositionType.Cave))
|
||||
if (PositionsOfInterest.None(p => p.PositionType == positionType))
|
||||
{
|
||||
positionType = PositionType.Cave;
|
||||
if (allValidLocations.Any(l => l.Edge.NextToCave))
|
||||
foreach (var validType in MineralMission.ValidPositionTypes)
|
||||
{
|
||||
allValidLocations.RemoveAll(l => !l.Edge.NextToCave);
|
||||
if (validType != positionType && PositionsOfInterest.Any(p => p.PositionType == validType))
|
||||
{
|
||||
positionType = validType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PositionsOfInterest.Any(p => p.PositionType == PositionType.SidePath))
|
||||
|
||||
try
|
||||
{
|
||||
positionType = PositionType.SidePath;
|
||||
if (allValidLocations.Any(l => l.Edge.NextToSidePath))
|
||||
RemoveInvalidLocations(positionType switch
|
||||
{
|
||||
allValidLocations.RemoveAll(l => !l.Edge.NextToSidePath);
|
||||
}
|
||||
PositionType.MainPath => IsOnMainPath,
|
||||
PositionType.SidePath => IsOnSidePath,
|
||||
PositionType.Cave => IsInCave,
|
||||
PositionType.AbyssCave => IsInAbyssCave,
|
||||
_ => throw new NotImplementedException(),
|
||||
});
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
DebugConsole.ThrowError($"Unexpected PositionType (\"{positionType}\") for mineral mission resources: mineral spawning might not work as expected.");
|
||||
}
|
||||
|
||||
var poi = PositionsOfInterest.GetRandom(p => p.PositionType == positionType, randSync: Rand.RandSync.ServerAndClient);
|
||||
var poiPos = poi.Position.ToVector2();
|
||||
Vector2 poiPos = poi.Position.ToVector2();
|
||||
allValidLocations.Sort((x, y) => Vector2.DistanceSquared(poiPos, x.EdgeCenter)
|
||||
.CompareTo(Vector2.DistanceSquared(poiPos, y.EdgeCenter)));
|
||||
var maxResourceOverlap = 0.4f;
|
||||
float maxResourceOverlap = 0.4f;
|
||||
var selectedLocation = allValidLocations.FirstOrDefault(l =>
|
||||
Vector2.Distance(l.Edge.Point1, l.Edge.Point2) is float edgeLength &&
|
||||
requiredAmount <= (int)Math.Floor(edgeLength / ((1.0f - maxResourceOverlap) * prefab.Size.X)));
|
||||
@@ -2993,9 +3021,18 @@ namespace Barotrauma
|
||||
throw new Exception("Failed to find a suitable level wall edge to place level resources on.");
|
||||
}
|
||||
PlaceResources(prefab, requiredAmount, selectedLocation, out placedResources);
|
||||
var edgeNormal = selectedLocation.Edge.GetNormal(selectedLocation.Cell);
|
||||
Vector2 edgeNormal = selectedLocation.Edge.GetNormal(selectedLocation.Cell);
|
||||
rotation = MathHelper.ToDegrees(-MathUtils.VectorToAngle(edgeNormal) + MathHelper.PiOver2);
|
||||
return placedResources;
|
||||
|
||||
static bool IsOnMainPath(ClusterLocation location) => location.Edge.NextToMainPath;
|
||||
static bool IsOnSidePath(ClusterLocation location) => location.Edge.NextToSidePath;
|
||||
static bool IsInCave(ClusterLocation location) => location.Edge.NextToCave;
|
||||
bool IsInAbyssCave(ClusterLocation location) => location.EdgeCenter.Y > AbyssArea.Bottom;
|
||||
void RemoveInvalidLocations(Predicate<ClusterLocation> match)
|
||||
{
|
||||
allValidLocations.RemoveAll(match);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ClusterLocation> GetAllValidClusterLocations()
|
||||
@@ -4015,17 +4052,21 @@ namespace Barotrauma
|
||||
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
}
|
||||
|
||||
float outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X;
|
||||
//don't try to compensate if the port is very far from the outpost's center of mass
|
||||
if (Math.Abs(outpostDockingPortOffset) > 5000.0f)
|
||||
float? outpostDockingPortOffset = null;
|
||||
if (outpostPort != null)
|
||||
{
|
||||
outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Info.Name + ", dist: " + outpostDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
|
||||
DebugConsole.NewMessage(warningMsg, Color.Orange);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:OutpostDockingPortVeryFar" + outpost.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X;
|
||||
//don't try to compensate if the port is very far from the outpost's center of mass
|
||||
if (Math.Abs(outpostDockingPortOffset.Value) > 5000.0f)
|
||||
{
|
||||
outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset.Value, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Info.Name + ", dist: " + outpostDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
|
||||
DebugConsole.NewMessage(warningMsg, Color.Orange);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:OutpostDockingPortVeryFar" + outpost.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 spawnPos = outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset - outpostDockingPortOffset, verticalMoveDir: 1);
|
||||
Vector2 spawnPos = outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, outpostDockingPortOffset != null ? subDockingPortOffset - outpostDockingPortOffset.Value : 0.0f, verticalMoveDir: 1);
|
||||
if (Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
spawnPos.Y = Math.Min(Size.Y - outpost.Borders.Height * 0.6f, spawnPos.Y + outpost.Borders.Height / 2);
|
||||
|
||||
@@ -357,7 +357,9 @@ namespace Barotrauma
|
||||
float closestDistance = 0.0f;
|
||||
foreach (DockingPort port in DockingPort.List)
|
||||
{
|
||||
if (port.Item.Submarine != sub || port.IsHorizontal != linkedPort.IsHorizontal) { continue; }
|
||||
if (port.Item.Submarine != sub) { continue; }
|
||||
if (port.IsHorizontal != linkedPort.IsHorizontal) { continue; }
|
||||
if (port.ForceDockingDirection != DockingPort.DirectionType.None && port.ForceDockingDirection == linkedPort.ForceDockingDirection) { continue; }
|
||||
float dist = Vector2.Distance(port.Item.WorldPosition, linkedPort.Item.WorldPosition);
|
||||
if (myPort == null || dist < closestDistance)
|
||||
{
|
||||
@@ -453,22 +455,22 @@ namespace Barotrauma
|
||||
|
||||
saveElement.SetAttributeValue("pos", XMLExtensions.Vector2ToString(Position - Submarine.HiddenSubPosition));
|
||||
|
||||
if (linkedTo.Any() || linkedToID.Any())
|
||||
{
|
||||
var linkedPort =
|
||||
linkedTo.FirstOrDefault(lt => (lt is Item item) && item.GetComponent<DockingPort>() != null) ??
|
||||
FindEntityByID(linkedToID.First()) as MapEntity;
|
||||
if (linkedPort != null)
|
||||
{
|
||||
saveElement.SetAttributeValue("linkedto", linkedPort.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
saveElement = new XElement("LinkedSubmarine");
|
||||
sub.SaveToXElement(saveElement);
|
||||
}
|
||||
if (linkedTo.Any() || linkedToID.Any())
|
||||
{
|
||||
var linkedPort =
|
||||
linkedTo.FirstOrDefault(lt => (lt is Item item) && item.GetComponent<DockingPort>() != null) ??
|
||||
FindEntityByID(linkedToID.First()) as MapEntity;
|
||||
if (linkedPort != null)
|
||||
{
|
||||
saveElement.SetAttributeValue("linkedto", linkedPort.ID);
|
||||
}
|
||||
}
|
||||
|
||||
saveElement.SetAttributeValue("originallinkedto", originalLinkedPort != null ? originalLinkedPort.Item.ID : originalLinkedToID);
|
||||
saveElement.SetAttributeValue("originalmyport", originalMyPortID);
|
||||
|
||||
@@ -338,7 +338,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public virtual bool AddUpgrade(Upgrade upgrade, bool createNetworkEvent = false)
|
||||
{
|
||||
if (this is Item item && !upgrade.Prefab.UpgradeCategories.Any(category => category.CanBeApplied(item, upgrade.Prefab)))
|
||||
if (!upgrade.Prefab.UpgradeCategories.Any(category => category.CanBeApplied(this, upgrade.Prefab)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -359,16 +359,6 @@ namespace Barotrauma
|
||||
Upgrades.Add(upgrade);
|
||||
}
|
||||
|
||||
// not used anymore
|
||||
#if SERVER
|
||||
// if (createNetworkEvent)
|
||||
// {
|
||||
// if (this is IServerSerializable serializable)
|
||||
// {
|
||||
// GameMain.Server.CreateEntityEvent(serializable, new object[] { NetEntityEvent.Type.Upgrade, upgrade });
|
||||
// }
|
||||
// }
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,17 @@ namespace Barotrauma
|
||||
|
||||
public ImmutableHashSet<Identifier> Tags => Prefab.Tags;
|
||||
|
||||
#if DEBUG
|
||||
[Editable, Serialize("", IsPropertySaveable.Yes)]
|
||||
#else
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
#endif
|
||||
public string SpecialTag
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
protected Color spriteColor;
|
||||
[Editable, Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes)]
|
||||
public Color SpriteColor
|
||||
@@ -574,6 +585,11 @@ namespace Barotrauma
|
||||
int xsections = 1, ysections = 1;
|
||||
int width = rect.Width, height = rect.Height;
|
||||
|
||||
WallSection[] prevSections = null;
|
||||
if (Sections != null)
|
||||
{
|
||||
prevSections = Sections.ToArray();
|
||||
}
|
||||
if (!HasBody)
|
||||
{
|
||||
if (FlippedX && IsHorizontal)
|
||||
@@ -657,6 +673,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSections != null && Sections.Length == prevSections.Length)
|
||||
{
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
Sections[i].damage = prevSections[i].damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle GenerateMergedRect(List<WallSection> mergedSections)
|
||||
@@ -829,27 +853,33 @@ namespace Barotrauma
|
||||
|
||||
public WallSection GetSection(int sectionIndex)
|
||||
{
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) return null;
|
||||
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) { return null; }
|
||||
return Sections[sectionIndex];
|
||||
|
||||
}
|
||||
|
||||
public bool SectionBodyDisabled(int sectionIndex)
|
||||
{
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) return false;
|
||||
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) { return false; }
|
||||
return (Sections[sectionIndex].damage >= MaxHealth);
|
||||
}
|
||||
|
||||
public bool AllSectionBodiesDisabled()
|
||||
{
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
if (Sections[i].damage < MaxHealth) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sections that are leaking have a gap placed on them
|
||||
/// </summary>
|
||||
public bool SectionIsLeaking(int sectionIndex)
|
||||
{
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) return false;
|
||||
|
||||
return (Sections[sectionIndex].damage >= MaxHealth * LeakThreshold);
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) { return false; }
|
||||
return Sections[sectionIndex].damage >= MaxHealth * LeakThreshold;
|
||||
}
|
||||
|
||||
public int SectionLength(int sectionIndex)
|
||||
@@ -1139,21 +1169,22 @@ namespace Barotrauma
|
||||
gapRect.Height += 20;
|
||||
|
||||
bool horizontalGap = !IsHorizontal;
|
||||
bool diagonalGap = false;
|
||||
if (Prefab.BodyRotation != 0.0f)
|
||||
{
|
||||
//rotation within a 90 deg sector (e.g. 100 -> 10, 190 -> 10, -10 -> 80)
|
||||
float sectorizedRotation = MathUtils.WrapAngleTwoPi(BodyRotation) % MathHelper.PiOver2;
|
||||
//diagonal if 30 < angle < 60
|
||||
bool diagonal = sectorizedRotation > MathHelper.Pi / 6 && sectorizedRotation < MathHelper.Pi / 3;
|
||||
diagonalGap = sectorizedRotation > MathHelper.Pi / 6 && sectorizedRotation < MathHelper.Pi / 3;
|
||||
//gaps on the lower half of a diagonal wall are horizontal, ones on the upper half are vertical
|
||||
if (diagonal)
|
||||
if (diagonalGap)
|
||||
{
|
||||
horizontalGap = gapRect.Y - gapRect.Height / 2 < Position.Y;
|
||||
if (FlippedY) { horizontalGap = !horizontalGap; }
|
||||
}
|
||||
}
|
||||
|
||||
Sections[sectionIndex].gap = new Gap(gapRect, horizontalGap, Submarine);
|
||||
Sections[sectionIndex].gap = new Gap(gapRect, horizontalGap, Submarine, isDiagonal: diagonalGap);
|
||||
|
||||
//free the ID, because if we give gaps IDs we have to make sure they always match between the clients and the server and
|
||||
//that clients create them in the correct order along with every other entity created/removed during the round
|
||||
|
||||
@@ -271,20 +271,17 @@ namespace Barotrauma
|
||||
tags.Add("wall".ToIdentifier());
|
||||
}
|
||||
|
||||
if (Description.IsNullOrEmpty())
|
||||
if (!descriptionIdentifier.IsEmpty)
|
||||
{
|
||||
if (!descriptionIdentifier.IsEmpty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}");
|
||||
}
|
||||
else if (nameIdentifier.IsEmpty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{Identifier}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{nameIdentifier}");
|
||||
}
|
||||
Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}").Fallback(Description);
|
||||
}
|
||||
else if (nameIdentifier.IsEmpty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{Identifier}").Fallback(Description);
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{nameIdentifier}").Fallback(Description);
|
||||
}
|
||||
|
||||
//backwards compatibility
|
||||
|
||||
@@ -187,7 +187,6 @@ namespace Barotrauma
|
||||
if (structure.Submarine != this || !structure.HasBody || structure.Indestructible) { continue; }
|
||||
realWorldCrushDepth = Math.Min(structure.CrushDepth, realWorldCrushDepth.Value);
|
||||
}
|
||||
realWorldCrushDepth *= Info.GetRealWorldCrushDepthMultiplier();
|
||||
}
|
||||
return realWorldCrushDepth.Value;
|
||||
}
|
||||
@@ -452,10 +451,27 @@ namespace Barotrauma
|
||||
verticalMoveDir = Math.Sign(verticalMoveDir);
|
||||
//do a raycast towards the top/bottom of the level depending on direction
|
||||
Vector2 potentialPos = new Vector2(spawnPos.X, verticalMoveDir > 0 ? Level.Loaded.Size.Y : 0);
|
||||
if (PickBody(ConvertUnits.ToSimUnits(spawnPos), ConvertUnits.ToSimUnits(potentialPos), collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
|
||||
|
||||
//3 raycasts (left, middle and right side of the sub, so we don't accidentally raycast up a passage too narrow for the sub)
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
//if the raycast hit a wall, attempt to place the spawnpos there
|
||||
potentialPos.Y = ConvertUnits.ToDisplayUnits(LastPickedPosition.Y) - 10;
|
||||
Vector2 xOffset = Vector2.UnitX * minWidth / 2 * x;
|
||||
if (PickBody(
|
||||
ConvertUnits.ToSimUnits(spawnPos + xOffset),
|
||||
ConvertUnits.ToSimUnits(potentialPos + xOffset),
|
||||
collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
|
||||
{
|
||||
int offsetFromWall = 10 * -verticalMoveDir;
|
||||
//if the raycast hit a wall, attempt to place the spawnpos there
|
||||
if (verticalMoveDir > 0)
|
||||
{
|
||||
potentialPos.Y = Math.Min(potentialPos.Y, ConvertUnits.ToDisplayUnits(LastPickedPosition.Y) + offsetFromWall);
|
||||
}
|
||||
else
|
||||
{
|
||||
potentialPos.Y = Math.Max(potentialPos.Y, ConvertUnits.ToDisplayUnits(LastPickedPosition.Y) + offsetFromWall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//step away from the top/bottom of the level, or from whatever wall the raycast hit,
|
||||
|
||||
@@ -505,7 +505,6 @@ namespace Barotrauma
|
||||
if (wall.Submarine != submarine) { continue; }
|
||||
|
||||
float wallCrushDepth = wall.CrushDepth;
|
||||
if (submarine.Info.SubmarineClass == SubmarineClass.DeepDiver) { wallCrushDepth *= 1.2f; }
|
||||
float pastCrushDepth = submarine.RealWorldDepth - wallCrushDepth;
|
||||
if (pastCrushDepth > 0)
|
||||
{
|
||||
@@ -587,9 +586,13 @@ namespace Barotrauma
|
||||
newHull = Hull.FindHull(targetPos, null);
|
||||
}
|
||||
|
||||
var gaps = newHull?.ConnectedGaps ?? Gap.GapList.Where(g => g.Submarine == submarine);
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, ConvertUnits.ToDisplayUnits(points[0]), 200.0f);
|
||||
if (adjacentGap == null) { return true; }
|
||||
//if all the bodies of a wall have been disabled, we don't need to care about gaps (can always pass through)
|
||||
if (!(contact.FixtureA.UserData is Structure wall) || !wall.AllSectionBodiesDisabled())
|
||||
{
|
||||
var gaps = newHull?.ConnectedGaps ?? Gap.GapList.Where(g => g.Submarine == submarine);
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, ConvertUnits.ToDisplayUnits(points[0]), 200.0f);
|
||||
if (adjacentGap == null) { return true; }
|
||||
}
|
||||
|
||||
if (newHull != null)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public enum SubmarineType { Player, Outpost, OutpostModule, Wreck, BeaconStation, EnemySubmarine, Ruin }
|
||||
public enum SubmarineClass { Undefined, Scout, Attack, Transport, DeepDiver }
|
||||
public enum SubmarineClass { Undefined, Scout, Attack, Transport }
|
||||
|
||||
partial class SubmarineInfo : IDisposable
|
||||
{
|
||||
@@ -49,6 +49,12 @@ namespace Barotrauma
|
||||
}
|
||||
public CrewExperienceLevel RecommendedCrewExperience;
|
||||
|
||||
public int Tier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A random int that gets assigned when saving the sub. Used in mp campaign to verify that sub files match
|
||||
/// </summary>
|
||||
@@ -305,6 +311,7 @@ namespace Barotrauma
|
||||
RecommendedCrewExperience = original.RecommendedCrewExperience;
|
||||
RecommendedCrewSizeMin = original.RecommendedCrewSizeMin;
|
||||
RecommendedCrewSizeMax = original.RecommendedCrewSizeMax;
|
||||
Tier = original.Tier;
|
||||
IsManuallyOutfitted = original.IsManuallyOutfitted;
|
||||
Tags = original.Tags;
|
||||
if (original.OutpostModuleInfo != null)
|
||||
@@ -386,6 +393,7 @@ namespace Barotrauma
|
||||
{
|
||||
Enum.TryParse(recommendedCrewExperience.Value, ignoreCase: true, out RecommendedCrewExperience);
|
||||
}
|
||||
Tier = SubmarineElement.GetAttributeInt("tier", GetDefaultTier(Price));
|
||||
|
||||
if (SubmarineElement?.Attribute("type") != null)
|
||||
{
|
||||
@@ -407,7 +415,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (SubmarineElement?.Attribute("class") != null)
|
||||
{
|
||||
if (Enum.TryParse(SubmarineElement.GetAttributeString("class", "Undefined"), out SubmarineClass submarineClass))
|
||||
string classStr = SubmarineElement.GetAttributeString("class", "Undefined");
|
||||
if (classStr == "DeepDiver")
|
||||
{
|
||||
//backwards compatibility
|
||||
SubmarineClass = SubmarineClass.Scout;
|
||||
}
|
||||
else if (Enum.TryParse(classStr, out SubmarineClass submarineClass))
|
||||
{
|
||||
SubmarineClass = submarineClass;
|
||||
}
|
||||
@@ -538,25 +552,9 @@ namespace Barotrauma
|
||||
{
|
||||
realWorldCrushDepth = Level.DefaultRealWorldCrushDepth;
|
||||
}
|
||||
realWorldCrushDepth *= GetRealWorldCrushDepthMultiplier();
|
||||
return realWorldCrushDepth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on <see cref="SubmarineClass"/>
|
||||
/// </summary>
|
||||
public float GetRealWorldCrushDepthMultiplier()
|
||||
{
|
||||
if (SubmarineClass == SubmarineClass.DeepDiver)
|
||||
{
|
||||
return 1.2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//saving/loading ----------------------------------------------------
|
||||
public void SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
|
||||
{
|
||||
@@ -691,7 +689,7 @@ namespace Barotrauma
|
||||
System.IO.Stream stream;
|
||||
try
|
||||
{
|
||||
stream = SaveUtil.DecompressFiletoStream(file);
|
||||
stream = SaveUtil.DecompressFileToStream(file);
|
||||
}
|
||||
catch (System.IO.FileNotFoundException e)
|
||||
{
|
||||
@@ -748,5 +746,7 @@ namespace Barotrauma
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static int GetDefaultTier(int price) => price > 20000 ? 3 : price > 10000 ? 2 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (byte b in Buffer)
|
||||
{
|
||||
msg.Write(b);
|
||||
msg.WriteByte(b);
|
||||
}
|
||||
|
||||
Dispose();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma.Networking
|
||||
Task<int> readTask = readStream?.ReadAsync(readTempBytes, 0, readTempBytes.Length, readCancellationToken.Token);
|
||||
if (readTask is null) { return -1; }
|
||||
|
||||
TimeSpan timeOut = TimeSpan.FromMilliseconds(100);
|
||||
int timeOutMilliseconds = 100;
|
||||
for (int i = 0; i < 150; i++)
|
||||
{
|
||||
if (shutDown)
|
||||
@@ -106,12 +106,9 @@ namespace Barotrauma.Networking
|
||||
readCancellationToken?.Cancel();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// BUG workaround for crash when closing the server under .NET 6.0, not sure if this is the proper way to fix it but it prevents it from crashing the client. - Markus
|
||||
#if NET6_0
|
||||
try
|
||||
{
|
||||
if (readTask.IsCompleted || readTask.Wait(100, readCancellationToken.Token))
|
||||
if (readTask.IsCompleted || readTask.Wait(timeOutMilliseconds, readCancellationToken.Token))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -120,12 +117,6 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
if (readTask.IsCompleted || readTask.Wait(timeOut))
|
||||
{
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (readTask.Status != TaskStatus.RanToCompletion)
|
||||
|
||||
@@ -177,8 +177,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
public bool HasSpawned; //has the client spawned as a character during the current round
|
||||
|
||||
private readonly List<Client> kickVoters;
|
||||
|
||||
public HashSet<Identifier> GivenAchievements = new HashSet<Identifier>();
|
||||
|
||||
public ClientPermissions Permissions = ClientPermissions.None;
|
||||
@@ -186,11 +184,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
private readonly object[] votes;
|
||||
|
||||
public int KickVoteCount
|
||||
{
|
||||
get { return kickVoters.Count; }
|
||||
}
|
||||
|
||||
partial void InitProjSpecific();
|
||||
partial void DisposeProjSpecific();
|
||||
public Client(string name, byte sessionId)
|
||||
@@ -198,8 +191,6 @@ namespace Barotrauma.Networking
|
||||
this.Name = name;
|
||||
this.SessionId = sessionId;
|
||||
|
||||
kickVoters = new List<Client>();
|
||||
|
||||
votes = new object[Enum.GetNames(typeof(VoteType)).Length];
|
||||
|
||||
InitProjSpecific();
|
||||
@@ -221,53 +212,22 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
votes[i] = null;
|
||||
}
|
||||
|
||||
kickVoters.Clear();
|
||||
}
|
||||
|
||||
public void AddKickVote(Client voter)
|
||||
{
|
||||
if (voter != null && !kickVoters.Contains(voter)) { kickVoters.Add(voter); }
|
||||
}
|
||||
|
||||
|
||||
public void RemoveKickVote(Client voter)
|
||||
{
|
||||
kickVoters.Remove(voter);
|
||||
}
|
||||
|
||||
public bool HasKickVoteFrom(Client voter)
|
||||
{
|
||||
return kickVoters.Contains(voter);
|
||||
}
|
||||
|
||||
public bool HasKickVoteFromSessionId(int id)
|
||||
{
|
||||
return kickVoters.Any(k => k.SessionId == id);
|
||||
}
|
||||
|
||||
|
||||
public bool SessionOrAccountIdMatches(string userId)
|
||||
=> (AccountId.IsSome() && Networking.AccountId.Parse(userId) == AccountId)
|
||||
|| (byte.TryParse(userId, out byte sessionId) && SessionId == sessionId);
|
||||
|
||||
public static void UpdateKickVotes(IReadOnlyList<Client> connectedClients)
|
||||
{
|
||||
foreach (Client client in connectedClients)
|
||||
{
|
||||
client.kickVoters.RemoveAll(voter => !connectedClients.Contains(voter));
|
||||
}
|
||||
}
|
||||
|
||||
public void WritePermissions(IWriteMessage msg)
|
||||
{
|
||||
msg.Write(SessionId);
|
||||
msg.WriteByte(SessionId);
|
||||
msg.WriteRangedInteger((int)Permissions, 0, (int)ClientPermissions.All);
|
||||
if (HasPermission(ClientPermissions.ConsoleCommands))
|
||||
{
|
||||
msg.Write((UInt16)PermittedConsoleCommands.Count);
|
||||
msg.WriteUInt16((UInt16)PermittedConsoleCommands.Count);
|
||||
foreach (DebugConsole.Command command in PermittedConsoleCommands)
|
||||
{
|
||||
msg.Write(command.names[0]);
|
||||
msg.WriteString(command.names[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,9 +325,14 @@ namespace Barotrauma
|
||||
Range<int> range = GetEnumRange(type);
|
||||
int enumIndex = bitField.ReadInteger(range.Start, range.End);
|
||||
|
||||
if (typeof(T).GetCustomAttribute<FlagsAttribute>() != null)
|
||||
{
|
||||
return (T)(object)enumIndex;
|
||||
}
|
||||
|
||||
foreach (T e in (T[])Enum.GetValues(type))
|
||||
{
|
||||
if ((int)Convert.ChangeType(e, e.GetTypeCode()) == enumIndex) { return e; }
|
||||
if (((int)(object)e) == enumIndex) { return e; }
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"An enum {type} with value {enumIndex} could not be found in {nameof(ReadEnum)}");
|
||||
@@ -388,18 +393,18 @@ namespace Barotrauma
|
||||
|
||||
private static bool ReadBoolean(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => bitField.ReadBoolean();
|
||||
private static void WriteBoolean(bool b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { bitField.WriteBoolean(b); }
|
||||
|
||||
|
||||
private static byte ReadByte(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadByte();
|
||||
private static void WriteByte(byte b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteByte(byte b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteByte(b); }
|
||||
|
||||
private static ushort ReadUInt16(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadUInt16();
|
||||
private static void WriteUInt16(ushort b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteUInt16(ushort b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteUInt16(b); }
|
||||
|
||||
private static short ReadInt16(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadInt16();
|
||||
private static void WriteInt16(short b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteInt16(short b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteInt16(b); }
|
||||
|
||||
private static uint ReadUInt32(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadUInt32();
|
||||
private static void WriteUInt32(uint b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteUInt32(uint b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteUInt32(b); }
|
||||
|
||||
private static int ReadInt32(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField)
|
||||
{
|
||||
@@ -421,14 +426,14 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
msg.Write(i);
|
||||
msg.WriteInt32(i);
|
||||
}
|
||||
|
||||
private static ulong ReadUInt64(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadUInt64();
|
||||
private static void WriteUInt64(ulong b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteUInt64(ulong b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteUInt64(b); }
|
||||
|
||||
private static long ReadInt64(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadInt64();
|
||||
private static void WriteInt64(long b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteInt64(long b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteInt64(b); }
|
||||
|
||||
private static float ReadSingle(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField)
|
||||
{
|
||||
@@ -450,17 +455,17 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
msg.Write(f);
|
||||
msg.WriteSingle(f);
|
||||
}
|
||||
|
||||
private static double ReadDouble(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadDouble();
|
||||
private static void WriteDouble(double b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteDouble(double b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteDouble(b); }
|
||||
|
||||
private static string ReadString(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadString();
|
||||
private static void WriteString(string b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteString(string b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteString(b); }
|
||||
|
||||
private static Identifier ReadIdentifier(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => inc.ReadIdentifier();
|
||||
private static void WriteIdentifier(Identifier b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.Write(b); }
|
||||
private static void WriteIdentifier(Identifier b, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField) { msg.WriteIdentifier(b); }
|
||||
|
||||
private static AccountId ReadAccountId(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField)
|
||||
{
|
||||
@@ -472,7 +477,7 @@ namespace Barotrauma
|
||||
|
||||
private static void WriteAccountId(AccountId accountId, NetworkSerialize attribute, IWriteMessage msg, IWritableBitField bitField)
|
||||
{
|
||||
msg.Write(accountId.StringRepresentation);
|
||||
msg.WriteString(accountId.StringRepresentation);
|
||||
}
|
||||
|
||||
private static Color ReadColor(IReadMessage inc, NetworkSerialize attribute, IReadableBitField bitField) => attribute.IncludeColorAlpha ? inc.ReadColorR8G8B8A8() : inc.ReadColorR8G8B8();
|
||||
@@ -519,7 +524,7 @@ namespace Barotrauma
|
||||
private static bool TryFindBehavior<T>(out ReadWriteBehavior<T> behavior) where T : notnull
|
||||
{
|
||||
bool found = TryFindBehavior(typeof(T), out var bhvr);
|
||||
behavior = (ReadWriteBehavior<T>)bhvr;
|
||||
behavior = found ? (ReadWriteBehavior<T>)bhvr : default;
|
||||
return found;
|
||||
}
|
||||
|
||||
@@ -589,8 +594,8 @@ namespace Barotrauma
|
||||
return array;
|
||||
|
||||
bool HasAttribute(MemberInfo info) => (info.GetCustomAttribute<NetworkSerialize>() ?? type.GetCustomAttribute<NetworkSerialize>()) != null;
|
||||
|
||||
bool NotStatic(MemberInfo info)
|
||||
|
||||
static bool NotStatic(MemberInfo info)
|
||||
=> info switch
|
||||
{
|
||||
PropertyInfo property => property.GetGetMethod() is { IsStatic: false },
|
||||
@@ -656,7 +661,7 @@ namespace Barotrauma
|
||||
/// Using <see cref="Nullable{T}"/> or <see cref="Option{T}"/> will make the field or property optional.
|
||||
/// </remarks>
|
||||
/// <seealso cref="NetworkSerialize"/>
|
||||
interface INetSerializableStruct
|
||||
internal interface INetSerializableStruct
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes a network message into a struct.
|
||||
@@ -743,7 +748,7 @@ namespace Barotrauma
|
||||
IWriteMessage structWriteMsg = new WriteOnlyMessage();
|
||||
WriteInternal(structWriteMsg, bitField);
|
||||
bitField.WriteToMessage(msg);
|
||||
msg.Write(structWriteMsg.Buffer, 0, structWriteMsg.LengthBytes);
|
||||
msg.WriteBytes(structWriteMsg.Buffer, 0, structWriteMsg.LengthBytes);
|
||||
}
|
||||
|
||||
public void WriteInternal(IWriteMessage msg, IWritableBitField bitField)
|
||||
@@ -757,25 +762,4 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class WriteOnlyMessageExtensions
|
||||
{
|
||||
#if CLIENT
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ClientPacketHeader header)
|
||||
{
|
||||
msg.Write((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#elif SERVER
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ServerPacketHeader header)
|
||||
{
|
||||
msg.Write((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#endif
|
||||
public static void Write(this IWriteMessage msg, INetSerializableStruct serializableStruct)
|
||||
{
|
||||
serializableStruct.Write(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
static class NetBufferExtensions
|
||||
{
|
||||
//public static void WriteEnum(this NetBuffer buffer, Enum value)
|
||||
//{
|
||||
// buffer.WriteRangedInteger(0, Enum.GetValues(value.GetType()).Length - 1, Convert.ToInt32(value));
|
||||
//}
|
||||
|
||||
//public static TEnum ReadEnum<TEnum>(this NetBuffer buffer)
|
||||
//{
|
||||
// return (TEnum)(object)buffer.ReadRangedInteger(0, Enum.GetValues(typeof(TEnum)).Length - 1);
|
||||
//}
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -37,7 +37,7 @@ namespace Barotrauma.Networking
|
||||
//write an empty event to avoid messing up IDs
|
||||
//(otherwise the clients might read the next event in the message and think its ID
|
||||
//is consecutive to the previous one, even though we skipped over this broken event)
|
||||
tempBuffer.Write(Entity.NullEntityID);
|
||||
tempBuffer.WriteUInt16(Entity.NullEntityID);
|
||||
eventCount++;
|
||||
continue;
|
||||
}
|
||||
@@ -49,9 +49,9 @@ namespace Barotrauma.Networking
|
||||
break;
|
||||
}
|
||||
|
||||
tempBuffer.Write(e.EntityID);
|
||||
tempBuffer.WriteUInt16(e.EntityID);
|
||||
tempBuffer.WriteVariableUInt32((uint)tempEventBuffer.LengthBytes);
|
||||
tempBuffer.Write(tempEventBuffer.Buffer, 0, tempEventBuffer.LengthBytes);
|
||||
tempBuffer.WriteBytes(tempEventBuffer.Buffer, 0, tempEventBuffer.LengthBytes);
|
||||
sentEvents.Add(e);
|
||||
|
||||
eventCount++;
|
||||
@@ -60,9 +60,9 @@ namespace Barotrauma.Networking
|
||||
if (eventCount > 0)
|
||||
{
|
||||
msg.WritePadBits();
|
||||
msg.Write(eventsToSync[0].ID);
|
||||
msg.Write((byte)eventCount);
|
||||
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
msg.WriteUInt16(eventsToSync[0].ID);
|
||||
msg.WriteByte((byte)eventCount);
|
||||
msg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,19 @@ namespace Barotrauma.Networking
|
||||
public static bool IdMoreRecentOrMatches(ushort newId, ushort oldId)
|
||||
=> !IdMoreRecent(oldId, newId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns some ID that is older than the input ID. There are no guarantees
|
||||
/// regarding its relation to values other than the input.
|
||||
/// </summary>
|
||||
public static ushort GetIdOlderThan(ushort id)
|
||||
#if DEBUG
|
||||
// Debug implementation has some RNG to discourage bad assumptions about the return value
|
||||
=> unchecked((ushort)(id - 1 - Rand.Int(500, sync: Rand.RandSync.Unsynced)));
|
||||
#else
|
||||
// Release implementation favors performance
|
||||
=> unchecked((ushort)(id - 1));
|
||||
#endif
|
||||
|
||||
public static ushort Difference(ushort id1, ushort id2)
|
||||
{
|
||||
int diff = id2 > id1 ? id2 - id1 : id1 - id2;
|
||||
|
||||
@@ -47,32 +47,32 @@ namespace Barotrauma.Networking
|
||||
|
||||
public static void WriteOrder(IWriteMessage msg, Order order, Character targetCharacter, bool isNewOrder)
|
||||
{
|
||||
msg.Write(order.Prefab.Identifier);
|
||||
msg.Write(targetCharacter == null ? (UInt16)0 : targetCharacter.ID);
|
||||
msg.Write(order.TargetSpatialEntity is Entity ? (order.TargetEntity as Entity).ID : (UInt16)0);
|
||||
msg.WriteIdentifier(order.Prefab.Identifier);
|
||||
msg.WriteUInt16(targetCharacter == null ? (UInt16)0 : targetCharacter.ID);
|
||||
msg.WriteUInt16(order.TargetSpatialEntity is Entity ? (order.TargetEntity as Entity).ID : (UInt16)0);
|
||||
|
||||
// The option of a Dismiss order is written differently so we know what order we target
|
||||
// now that the game supports multiple current orders simultaneously
|
||||
if (!order.IsDismissal)
|
||||
{
|
||||
msg.Write((byte)order.Options.IndexOf(order.Option));
|
||||
msg.WriteByte((byte)order.Options.IndexOf(order.Option));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (order.Option != Identifier.Empty)
|
||||
{
|
||||
msg.Write(true);
|
||||
msg.WriteBoolean(true);
|
||||
string[] dismissedOrder = order.Option.Value.Split('.');
|
||||
msg.Write((byte)dismissedOrder.Length);
|
||||
msg.WriteByte((byte)dismissedOrder.Length);
|
||||
if (dismissedOrder.Length > 0)
|
||||
{
|
||||
Identifier dismissedOrderIdentifier = dismissedOrder[0].ToIdentifier();
|
||||
var orderPrefab = OrderPrefab.Prefabs[dismissedOrderIdentifier];
|
||||
msg.Write(dismissedOrderIdentifier);
|
||||
msg.WriteIdentifier(dismissedOrderIdentifier);
|
||||
if (dismissedOrder.Length > 1)
|
||||
{
|
||||
Identifier dismissedOrderOption = dismissedOrder[1].ToIdentifier();
|
||||
msg.Write((byte)orderPrefab.Options.IndexOf(dismissedOrderOption));
|
||||
msg.WriteByte((byte)orderPrefab.Options.IndexOf(dismissedOrderOption));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,29 +80,29 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
// If the order option is not specified for a Dismiss order,
|
||||
// we dismiss all current orders for the character
|
||||
msg.Write(false);
|
||||
msg.WriteBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((byte)order.ManualPriority);
|
||||
msg.Write((byte)order.TargetType);
|
||||
msg.WriteByte((byte)order.ManualPriority);
|
||||
msg.WriteByte((byte)order.TargetType);
|
||||
if (order.TargetType == Order.OrderTargetType.Position && order.TargetSpatialEntity is OrderTarget orderTarget)
|
||||
{
|
||||
msg.Write(true);
|
||||
msg.Write(orderTarget.Position.X);
|
||||
msg.Write(orderTarget.Position.Y);
|
||||
msg.Write(orderTarget.Hull == null ? (UInt16)0 : orderTarget.Hull.ID);
|
||||
msg.WriteBoolean(true);
|
||||
msg.WriteSingle(orderTarget.Position.X);
|
||||
msg.WriteSingle(orderTarget.Position.Y);
|
||||
msg.WriteUInt16(orderTarget.Hull == null ? (UInt16)0 : orderTarget.Hull.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(false);
|
||||
msg.WriteBoolean(false);
|
||||
if (order.TargetType == Order.OrderTargetType.WallSection)
|
||||
{
|
||||
msg.Write((byte)(order.WallSectionIndex ?? 0));
|
||||
msg.WriteByte((byte)(order.WallSectionIndex ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write(isNewOrder);
|
||||
msg.WriteBoolean(isNewOrder);
|
||||
}
|
||||
|
||||
private void WriteOrder(IWriteMessage msg)
|
||||
|
||||
+14
-14
@@ -4,27 +4,27 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
interface IWriteMessage
|
||||
{
|
||||
void Write(bool val);
|
||||
void WriteBoolean(bool val);
|
||||
void WritePadBits();
|
||||
void Write(byte val);
|
||||
void Write(Int16 val);
|
||||
void Write(UInt16 val);
|
||||
void Write(Int32 val);
|
||||
void Write(UInt32 val);
|
||||
void Write(Int64 val);
|
||||
void Write(UInt64 val);
|
||||
void Write(Single val);
|
||||
void Write(Double val);
|
||||
void WriteByte(byte val);
|
||||
void WriteInt16(Int16 val);
|
||||
void WriteUInt16(UInt16 val);
|
||||
void WriteInt32(Int32 val);
|
||||
void WriteUInt32(UInt32 val);
|
||||
void WriteInt64(Int64 val);
|
||||
void WriteUInt64(UInt64 val);
|
||||
void WriteSingle(Single val);
|
||||
void WriteDouble(Double val);
|
||||
void WriteColorR8G8B8(Microsoft.Xna.Framework.Color val);
|
||||
void WriteColorR8G8B8A8(Microsoft.Xna.Framework.Color val);
|
||||
void WriteVariableUInt32(UInt32 val);
|
||||
void Write(string val);
|
||||
void Write(Identifier val);
|
||||
void WriteString(string val);
|
||||
void WriteIdentifier(Identifier val);
|
||||
void WriteRangedInteger(int val, int min, int max);
|
||||
void WriteRangedSingle(Single val, Single min, Single max, int bitCount);
|
||||
void Write(byte[] val, int startIndex, int length);
|
||||
void WriteBytes(byte[] val, int startIndex, int length);
|
||||
|
||||
void PrepareForSending(ref byte[] outBuf, bool compressPastThreshold, out bool isCompressed, out int outLength);
|
||||
byte[] PrepareForSending(bool compressPastThreshold, out bool isCompressed, out int outLength);
|
||||
|
||||
int BitPosition { get; set; }
|
||||
int BytePosition { get; }
|
||||
|
||||
+159
-234
@@ -1,7 +1,6 @@
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
@@ -58,7 +57,7 @@ namespace Barotrauma.Networking
|
||||
bool testVal = MsgReader.ReadBoolean(buf, ref resetPos);
|
||||
if (testVal != val || resetPos != bitPos)
|
||||
{
|
||||
DebugConsole.ThrowError("Boolean written incorrectly! " + testVal + ", " + val + "; " + resetPos + ", " + bitPos);
|
||||
DebugConsole.ThrowError($"Boolean written incorrectly! {testVal}, {val}; {resetPos}, {bitPos}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -125,7 +124,7 @@ namespace Barotrauma.Networking
|
||||
SingleUIntUnion su;
|
||||
su.UIntValue = 0; // must initialize every member of the union to avoid warning
|
||||
su.SingleValue = val;
|
||||
|
||||
|
||||
EnsureBufferSize(ref buf, bitPos + 32);
|
||||
|
||||
NetBitWriter.WriteUInt32(su.UIntValue, 32, buf, bitPos);
|
||||
@@ -140,50 +139,48 @@ namespace Barotrauma.Networking
|
||||
WriteBytes(ref buf, ref bitPos, bytes, 0, 8);
|
||||
}
|
||||
|
||||
internal static void WriteColorR8G8B8(ref byte[] buf, ref int bitPos, Microsoft.Xna.Framework.Color val)
|
||||
internal static void WriteColorR8G8B8(ref byte[] buf, ref int bitPos, Color val)
|
||||
{
|
||||
EnsureBufferSize(ref buf, bitPos + 24);
|
||||
|
||||
|
||||
Write(ref buf, ref bitPos, val.R);
|
||||
Write(ref buf, ref bitPos, val.G);
|
||||
Write(ref buf, ref bitPos, val.B);
|
||||
}
|
||||
|
||||
internal static void WriteColorR8G8B8A8(ref byte[] buf, ref int bitPos, Microsoft.Xna.Framework.Color val)
|
||||
|
||||
internal static void WriteColorR8G8B8A8(ref byte[] buf, ref int bitPos, Color val)
|
||||
{
|
||||
EnsureBufferSize(ref buf, bitPos + 32);
|
||||
|
||||
|
||||
Write(ref buf, ref bitPos, val.R);
|
||||
Write(ref buf, ref bitPos, val.G);
|
||||
Write(ref buf, ref bitPos, val.B);
|
||||
Write(ref buf, ref bitPos, val.A);
|
||||
}
|
||||
|
||||
|
||||
internal static void Write(ref byte[] buf, ref int bitPos, string val)
|
||||
{
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
WriteVariableUInt32(ref buf, ref bitPos, (uint)0);
|
||||
WriteVariableUInt32(ref buf, ref bitPos, 0u);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(val);
|
||||
WriteVariableUInt32(ref buf, ref bitPos, (uint)bytes.Length);
|
||||
WriteBytes(ref buf, ref bitPos, bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
internal static int WriteVariableUInt32(ref byte[] buf, ref int bitPos, uint value)
|
||||
internal static void WriteVariableUInt32(ref byte[] buf, ref int bitPos, uint value)
|
||||
{
|
||||
int retval = 1;
|
||||
uint remainingValue = (uint)value;
|
||||
uint remainingValue = value;
|
||||
while (remainingValue >= 0x80)
|
||||
{
|
||||
Write(ref buf, ref bitPos, (byte)(remainingValue | 0x80));
|
||||
remainingValue = remainingValue >> 7;
|
||||
retval++;
|
||||
remainingValue >>= 7;
|
||||
}
|
||||
|
||||
Write(ref buf, ref bitPos, (byte)remainingValue);
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal static void WriteRangedInteger(ref byte[] buf, ref int bitPos, int val, int min, int max)
|
||||
@@ -206,7 +203,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
EnsureBufferSize(ref buf, bitPos + numberOfBits);
|
||||
|
||||
NetBitWriter.WriteUInt32((UInt32)((float)maxVal * unit), numberOfBits, buf, bitPos);
|
||||
NetBitWriter.WriteUInt32((UInt32)(maxVal * unit), numberOfBits, buf, bitPos);
|
||||
bitPos += numberOfBits;
|
||||
}
|
||||
|
||||
@@ -225,9 +222,10 @@ namespace Barotrauma.Networking
|
||||
buf = new byte[byteLen + MsgConstants.BufferOverAllocateAmount];
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf.Length < byteLen)
|
||||
{
|
||||
Array.Resize<byte>(ref buf, byteLen + MsgConstants.BufferOverAllocateAmount);
|
||||
Array.Resize(ref buf, byteLen + MsgConstants.BufferOverAllocateAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,7 +239,7 @@ namespace Barotrauma.Networking
|
||||
return retval > 0;
|
||||
}
|
||||
|
||||
internal static void ReadPadBits(byte[] buf, ref int bitPos)
|
||||
internal static void ReadPadBits(ref int bitPos)
|
||||
{
|
||||
int bitOffset = bitPos % 8;
|
||||
bitPos += (8 - bitOffset) % 8;
|
||||
@@ -326,15 +324,15 @@ namespace Barotrauma.Networking
|
||||
return BitConverter.ToDouble(bytes, 0);
|
||||
}
|
||||
|
||||
internal static Microsoft.Xna.Framework.Color ReadColorR8G8B8(byte[] buf, ref int bitPos)
|
||||
internal static Color ReadColorR8G8B8(byte[] buf, ref int bitPos)
|
||||
{
|
||||
byte r = ReadByte(buf, ref bitPos);
|
||||
byte g = ReadByte(buf, ref bitPos);
|
||||
byte b = ReadByte(buf, ref bitPos);
|
||||
return new Color(r, g, b, (byte)255);
|
||||
}
|
||||
|
||||
internal static Microsoft.Xna.Framework.Color ReadColorR8G8B8A8(byte[] buf, ref int bitPos)
|
||||
|
||||
internal static Color ReadColorR8G8B8A8(byte[] buf, ref int bitPos)
|
||||
{
|
||||
byte r = ReadByte(buf, ref bitPos);
|
||||
byte g = ReadByte(buf, ref bitPos);
|
||||
@@ -354,8 +352,7 @@ namespace Barotrauma.Networking
|
||||
byte chunk = ReadByte(buf, ref bitPos);
|
||||
result |= (chunk & 0x7f) << shift;
|
||||
shift += 7;
|
||||
if ((chunk & 0x80) == 0)
|
||||
return (uint)result;
|
||||
if ((chunk & 0x80) == 0) { return (uint)result; }
|
||||
}
|
||||
|
||||
// ouch; failed to find enough bytes; malformed variable length number?
|
||||
@@ -378,23 +375,23 @@ namespace Barotrauma.Networking
|
||||
if ((bitPos & 7) == 0)
|
||||
{
|
||||
// read directly
|
||||
string retval = System.Text.Encoding.UTF8.GetString(buf, bitPos >> 3, byteLen);
|
||||
string retval = Encoding.UTF8.GetString(buf, bitPos >> 3, byteLen);
|
||||
bitPos += (8 * byteLen);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = ReadBytes(buf, ref bitPos, byteLen);
|
||||
return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
||||
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
internal static int ReadRangedInteger(byte[] buf, ref int bitPos, int min, int max)
|
||||
{
|
||||
uint range = (uint)(max - min);
|
||||
int numBits = NetUtility.BitsToHoldUInt(range);
|
||||
uint range = (uint)(max - min);
|
||||
int numBits = NetUtility.BitsToHoldUInt(range);
|
||||
|
||||
uint rvalue = NetBitWriter.ReadUInt32(buf, numBits, bitPos);
|
||||
uint rvalue = NetBitWriter.ReadUInt32(buf, numBits, bitPos);
|
||||
bitPos += numBits;
|
||||
|
||||
|
||||
return (int)(min + rvalue);
|
||||
}
|
||||
|
||||
@@ -403,51 +400,33 @@ namespace Barotrauma.Networking
|
||||
int maxInt = (1 << bitCount) - 1;
|
||||
int intVal = ReadRangedInteger(buf, ref bitPos, 0, maxInt);
|
||||
Single range = max - min;
|
||||
return min + (range * ((Single)intVal) / ((Single)maxInt));
|
||||
return min + range * intVal / maxInt;
|
||||
}
|
||||
|
||||
internal static byte[] ReadBytes(byte[] buf, ref int bitPos, int numberOfBytes)
|
||||
{
|
||||
byte[] retval = new byte[numberOfBytes];
|
||||
NetBitWriter.ReadBytes(buf, numberOfBytes, bitPos, retval, 0);
|
||||
bitPos += (8 * numberOfBytes);
|
||||
bitPos += 8 * numberOfBytes;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
class WriteOnlyMessage : IWriteMessage
|
||||
internal sealed class WriteOnlyMessage : IWriteMessage
|
||||
{
|
||||
private byte[] buf = new byte[MsgConstants.InitialBufferSize];
|
||||
private int seekPos = 0;
|
||||
private int lengthBits = 0;
|
||||
private int seekPos;
|
||||
private int lengthBits;
|
||||
|
||||
public int BitPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return seekPos;
|
||||
}
|
||||
set
|
||||
{
|
||||
seekPos = value;
|
||||
}
|
||||
get => seekPos;
|
||||
set => seekPos = value;
|
||||
}
|
||||
|
||||
public int BytePosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return seekPos / 8;
|
||||
}
|
||||
}
|
||||
public int BytePosition => seekPos / 8;
|
||||
|
||||
public byte[] Buffer
|
||||
{
|
||||
get
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
public byte[] Buffer => buf;
|
||||
|
||||
public int LengthBits
|
||||
{
|
||||
@@ -464,15 +443,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public int LengthBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return (LengthBits + 7) / 8;
|
||||
}
|
||||
}
|
||||
public int LengthBytes => (LengthBits + 7) / 8;
|
||||
|
||||
public void Write(bool val)
|
||||
public void WriteBoolean(bool val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
@@ -482,47 +455,47 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.WritePadBits(ref buf, ref seekPos);
|
||||
}
|
||||
|
||||
public void Write(byte val)
|
||||
public void WriteByte(byte val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(UInt16 val)
|
||||
public void WriteUInt16(UInt16 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Int16 val)
|
||||
public void WriteInt16(Int16 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(UInt32 val)
|
||||
public void WriteUInt32(UInt32 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Int32 val)
|
||||
public void WriteInt32(Int32 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(UInt64 val)
|
||||
public void WriteUInt64(UInt64 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Int64 val)
|
||||
public void WriteInt64(Int64 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Single val)
|
||||
public void WriteSingle(Single val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Double val)
|
||||
public void WriteDouble(Double val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
@@ -531,7 +504,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
|
||||
public void WriteColorR8G8B8A8(Color val)
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8A8(ref buf, ref seekPos, val);
|
||||
@@ -542,14 +515,14 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(String val)
|
||||
public void WriteString(String val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Identifier val)
|
||||
public void WriteIdentifier(Identifier val)
|
||||
{
|
||||
Write(val.Value);
|
||||
WriteString(val.Value);
|
||||
}
|
||||
|
||||
public void WriteRangedInteger(int val, int min, int max)
|
||||
@@ -562,85 +535,67 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.WriteRangedSingle(ref buf, ref seekPos, val, min, max, bitCount);
|
||||
}
|
||||
|
||||
public void Write(byte[] val, int startPos, int length)
|
||||
public void WriteBytes(byte[] val, int startPos, int length)
|
||||
{
|
||||
MsgWriter.WriteBytes(ref buf, ref seekPos, val, startPos, length);
|
||||
}
|
||||
|
||||
public void PrepareForSending(ref byte[] outBuf, bool compressPastThreshold, out bool isCompressed, out int length)
|
||||
|
||||
public byte[] PrepareForSending(bool compressPastThreshold, out bool isCompressed, out int length)
|
||||
{
|
||||
byte[] outBuf;
|
||||
if (LengthBytes <= MsgConstants.CompressionThreshold || !compressPastThreshold)
|
||||
{
|
||||
isCompressed = false;
|
||||
if (LengthBytes > outBuf.Length) { Array.Resize(ref outBuf, LengthBytes); }
|
||||
outBuf = new byte[LengthBytes];
|
||||
Array.Copy(buf, outBuf, LengthBytes);
|
||||
length = LengthBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
using (System.IO.MemoryStream output = new System.IO.MemoryStream())
|
||||
using MemoryStream output = new MemoryStream();
|
||||
|
||||
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Fastest))
|
||||
{
|
||||
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Fastest))
|
||||
{
|
||||
dstream.Write(buf, 0, LengthBytes);
|
||||
}
|
||||
|
||||
byte[] compressedBuf = output.ToArray();
|
||||
//don't send the data as compressed if the data takes up more space after compression
|
||||
//(which may happen when sending a sub/save file that's already been compressed with a better compression ratio)
|
||||
if (compressedBuf.Length >= outBuf.Length)
|
||||
{
|
||||
isCompressed = false;
|
||||
if (LengthBytes > outBuf.Length) { Array.Resize(ref outBuf, LengthBytes); }
|
||||
Array.Copy(buf, outBuf, LengthBytes);
|
||||
length = LengthBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
isCompressed = true;
|
||||
if (compressedBuf.Length > outBuf.Length) { Array.Resize(ref outBuf, compressedBuf.Length); }
|
||||
Array.Copy(compressedBuf, outBuf, compressedBuf.Length);
|
||||
length = compressedBuf.Length;
|
||||
DebugConsole.Log("Compressed message: " + LengthBytes + " to " + length);
|
||||
}
|
||||
dstream.Write(buf, 0, LengthBytes);
|
||||
}
|
||||
|
||||
byte[] compressedBuf = output.ToArray();
|
||||
//don't send the data as compressed if the data takes up more space after compression
|
||||
//(which may happen when sending a sub/save file that's already been compressed with a better compression ratio)
|
||||
if (compressedBuf.Length >= LengthBytes)
|
||||
{
|
||||
isCompressed = false;
|
||||
outBuf = new byte[LengthBytes];
|
||||
Array.Copy(buf, outBuf, LengthBytes);
|
||||
length = LengthBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
isCompressed = true;
|
||||
outBuf = compressedBuf;
|
||||
length = outBuf.Length;
|
||||
DebugConsole.Log($"Compressed message: {LengthBytes} to {length}");
|
||||
}
|
||||
}
|
||||
|
||||
return outBuf;
|
||||
}
|
||||
}
|
||||
|
||||
class ReadOnlyMessage : IReadMessage
|
||||
internal sealed class ReadOnlyMessage : IReadMessage
|
||||
{
|
||||
private readonly byte[] buf;
|
||||
private int seekPos = 0;
|
||||
private int lengthBits = 0;
|
||||
private int seekPos;
|
||||
private int lengthBits;
|
||||
|
||||
public int BitPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return seekPos;
|
||||
}
|
||||
set
|
||||
{
|
||||
seekPos = value;
|
||||
}
|
||||
get => seekPos;
|
||||
set => seekPos = value;
|
||||
}
|
||||
|
||||
public int BytePosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return seekPos / 8;
|
||||
}
|
||||
}
|
||||
public int BytePosition => seekPos / 8;
|
||||
|
||||
public byte[] Buffer
|
||||
{
|
||||
get
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
public byte[] Buffer { get; }
|
||||
|
||||
public int LengthBits
|
||||
{
|
||||
@@ -656,129 +611,126 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public int LengthBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return (LengthBits + 7) / 8;
|
||||
}
|
||||
}
|
||||
public int LengthBytes => (LengthBits + 7) / 8;
|
||||
|
||||
public NetworkConnection Sender { get; private set; }
|
||||
|
||||
public ReadOnlyMessage(byte[] inBuf, bool isCompressed, int startPos, int inLength, NetworkConnection sender)
|
||||
public NetworkConnection Sender { get; }
|
||||
|
||||
public ReadOnlyMessage(byte[] inBuf, bool isCompressed, int startPos, int byteLength, NetworkConnection sender)
|
||||
{
|
||||
Sender = sender;
|
||||
if (isCompressed)
|
||||
{
|
||||
byte[] decompressedData;
|
||||
using (System.IO.MemoryStream input = new System.IO.MemoryStream(inBuf, startPos, inLength))
|
||||
using (MemoryStream input = new MemoryStream(inBuf, startPos, byteLength))
|
||||
{
|
||||
using (System.IO.MemoryStream output = new System.IO.MemoryStream())
|
||||
using (MemoryStream output = new MemoryStream())
|
||||
{
|
||||
using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress))
|
||||
{
|
||||
dstream.CopyTo(output);
|
||||
}
|
||||
|
||||
decompressedData = output.ToArray();
|
||||
}
|
||||
}
|
||||
buf = new byte[decompressedData.Length];
|
||||
|
||||
Buffer = new byte[decompressedData.Length];
|
||||
try
|
||||
{
|
||||
Array.Copy(decompressedData, 0, buf, 0, decompressedData.Length);
|
||||
Array.Copy(decompressedData, 0, Buffer, 0, decompressedData.Length);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw new ArgumentException($"Failed to copy the incoming compressed buffer. Source buffer length: {decompressedData.Length}, start position: {0}, length: {decompressedData.Length}, destination buffer length: {buf.Length}.", e);
|
||||
throw new ArgumentException(
|
||||
$"Failed to copy the incoming compressed buffer. Source buffer length: {decompressedData.Length}, start position: {0}, length: {decompressedData.Length}, destination buffer length: {Buffer.Length}.", e);
|
||||
}
|
||||
|
||||
lengthBits = decompressedData.Length * 8;
|
||||
DebugConsole.Log("Decompressing message: " + inLength + " to " + LengthBytes);
|
||||
DebugConsole.Log("Decompressing message: " + byteLength + " to " + LengthBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
buf = new byte[inBuf.Length];
|
||||
Buffer = new byte[inBuf.Length];
|
||||
try
|
||||
{
|
||||
Array.Copy(inBuf, startPos, buf, 0, inLength);
|
||||
Array.Copy(inBuf, startPos, Buffer, 0, byteLength);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw new ArgumentException($"Failed to copy the incoming uncompressed buffer. Source buffer length: {inBuf.Length}, start position: {startPos}, length: {inLength}, destination buffer length: {buf.Length}.", e);
|
||||
throw new ArgumentException($"Failed to copy the incoming uncompressed buffer. Source buffer length: {inBuf.Length}, start position: {startPos}, length: {byteLength}, destination buffer length: {Buffer.Length}.", e);
|
||||
}
|
||||
lengthBits = inLength * 8;
|
||||
|
||||
lengthBits = byteLength * 8;
|
||||
}
|
||||
|
||||
seekPos = 0;
|
||||
}
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
return MsgReader.ReadBoolean(buf, ref seekPos);
|
||||
return MsgReader.ReadBoolean(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public void ReadPadBits()
|
||||
{
|
||||
MsgReader.ReadPadBits(buf, ref seekPos);
|
||||
}
|
||||
public void ReadPadBits() { MsgReader.ReadPadBits(ref seekPos); }
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return MsgReader.ReadByte(buf, ref seekPos);
|
||||
return MsgReader.ReadByte(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public byte PeekByte()
|
||||
{
|
||||
return MsgReader.PeekByte(buf, ref seekPos);
|
||||
return MsgReader.PeekByte(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public UInt16 ReadUInt16()
|
||||
{
|
||||
return MsgReader.ReadUInt16(buf, ref seekPos);
|
||||
return MsgReader.ReadUInt16(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public Int16 ReadInt16()
|
||||
{
|
||||
return MsgReader.ReadInt16(buf, ref seekPos);
|
||||
return MsgReader.ReadInt16(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public UInt32 ReadUInt32()
|
||||
{
|
||||
return MsgReader.ReadUInt32(buf, ref seekPos);
|
||||
return MsgReader.ReadUInt32(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public Int32 ReadInt32()
|
||||
{
|
||||
return MsgReader.ReadInt32(buf, ref seekPos);
|
||||
return MsgReader.ReadInt32(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public UInt64 ReadUInt64()
|
||||
{
|
||||
return MsgReader.ReadUInt64(buf, ref seekPos);
|
||||
return MsgReader.ReadUInt64(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public Int64 ReadInt64()
|
||||
{
|
||||
return MsgReader.ReadInt64(buf, ref seekPos);
|
||||
return MsgReader.ReadInt64(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public Single ReadSingle()
|
||||
{
|
||||
return MsgReader.ReadSingle(buf, ref seekPos);
|
||||
return MsgReader.ReadSingle(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public Double ReadDouble()
|
||||
{
|
||||
return MsgReader.ReadDouble(buf, ref seekPos);
|
||||
return MsgReader.ReadDouble(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public UInt32 ReadVariableUInt32()
|
||||
{
|
||||
return MsgReader.ReadVariableUInt32(buf, ref seekPos);
|
||||
return MsgReader.ReadVariableUInt32(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public String ReadString()
|
||||
{
|
||||
return MsgReader.ReadString(buf, ref seekPos);
|
||||
return MsgReader.ReadString(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public Identifier ReadIdentifier()
|
||||
@@ -788,35 +740,35 @@ namespace Barotrauma.Networking
|
||||
|
||||
public Color ReadColorR8G8B8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8(buf, ref seekPos);
|
||||
return MsgReader.ReadColorR8G8B8(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
|
||||
public Color ReadColorR8G8B8A8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8A8(buf, ref seekPos);
|
||||
return MsgReader.ReadColorR8G8B8A8(Buffer, ref seekPos);
|
||||
}
|
||||
|
||||
public int ReadRangedInteger(int min, int max)
|
||||
{
|
||||
return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max);
|
||||
return MsgReader.ReadRangedInteger(Buffer, ref seekPos, min, max);
|
||||
}
|
||||
|
||||
public Single ReadRangedSingle(Single min, Single max, int bitCount)
|
||||
{
|
||||
return MsgReader.ReadRangedSingle(buf, ref seekPos, min, max, bitCount);
|
||||
return MsgReader.ReadRangedSingle(Buffer, ref seekPos, min, max, bitCount);
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int numberOfBytes)
|
||||
{
|
||||
return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes);
|
||||
return MsgReader.ReadBytes(Buffer, ref seekPos, numberOfBytes);
|
||||
}
|
||||
}
|
||||
|
||||
class ReadWriteMessage : IWriteMessage, IReadMessage
|
||||
internal sealed class ReadWriteMessage : IWriteMessage, IReadMessage
|
||||
{
|
||||
private byte[] buf;
|
||||
private int seekPos = 0;
|
||||
private int lengthBits = 0;
|
||||
private int seekPos;
|
||||
private int lengthBits;
|
||||
|
||||
public ReadWriteMessage()
|
||||
{
|
||||
@@ -825,40 +777,22 @@ namespace Barotrauma.Networking
|
||||
lengthBits = 0;
|
||||
}
|
||||
|
||||
public ReadWriteMessage(byte[] b, int sPos, int lBits, bool copyBuf)
|
||||
public ReadWriteMessage(byte[] b, int bitPos, int lBits, bool copyBuf)
|
||||
{
|
||||
buf = copyBuf ? (byte[])b.Clone() : b;
|
||||
seekPos = sPos;
|
||||
seekPos = bitPos;
|
||||
lengthBits = lBits;
|
||||
}
|
||||
|
||||
public int BitPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return seekPos;
|
||||
}
|
||||
set
|
||||
{
|
||||
seekPos = value;
|
||||
}
|
||||
get => seekPos;
|
||||
set => seekPos = value;
|
||||
}
|
||||
|
||||
public int BytePosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return seekPos / 8;
|
||||
}
|
||||
}
|
||||
public int BytePosition => seekPos / 8;
|
||||
|
||||
public byte[] Buffer
|
||||
{
|
||||
get
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
public byte[] Buffer => buf;
|
||||
|
||||
public int LengthBits
|
||||
{
|
||||
@@ -874,17 +808,11 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public int LengthBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return (LengthBits + 7) / 8;
|
||||
}
|
||||
}
|
||||
public int LengthBytes => (LengthBits + 7) / 8;
|
||||
|
||||
public NetworkConnection Sender { get { return null; } }
|
||||
public NetworkConnection Sender => null;
|
||||
|
||||
public void Write(bool val)
|
||||
public void WriteBoolean(bool val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
@@ -894,47 +822,47 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.WritePadBits(ref buf, ref seekPos);
|
||||
}
|
||||
|
||||
public void Write(byte val)
|
||||
public void WriteByte(byte val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(UInt16 val)
|
||||
public void WriteUInt16(UInt16 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Int16 val)
|
||||
public void WriteInt16(Int16 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(UInt32 val)
|
||||
public void WriteUInt32(UInt32 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Int32 val)
|
||||
public void WriteInt32(Int32 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(UInt64 val)
|
||||
public void WriteUInt64(UInt64 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Int64 val)
|
||||
public void WriteInt64(Int64 val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Single val)
|
||||
public void WriteSingle(Single val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Double val)
|
||||
public void WriteDouble(Double val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
@@ -943,7 +871,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
|
||||
public void WriteColorR8G8B8A8(Color val)
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8A8(ref buf, ref seekPos, val);
|
||||
@@ -954,14 +882,14 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(String val)
|
||||
public void WriteString(String val)
|
||||
{
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Identifier val)
|
||||
public void WriteIdentifier(Identifier val)
|
||||
{
|
||||
Write(val.Value);
|
||||
WriteString(val.Value);
|
||||
}
|
||||
|
||||
public void WriteRangedInteger(int val, int min, int max)
|
||||
@@ -974,7 +902,7 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.WriteRangedSingle(ref buf, ref seekPos, val, min, max, bitCount);
|
||||
}
|
||||
|
||||
public void Write(byte[] val, int startPos, int length)
|
||||
public void WriteBytes(byte[] val, int startPos, int length)
|
||||
{
|
||||
MsgWriter.WriteBytes(ref buf, ref seekPos, val, startPos, length);
|
||||
}
|
||||
@@ -984,10 +912,7 @@ namespace Barotrauma.Networking
|
||||
return MsgReader.ReadBoolean(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public void ReadPadBits()
|
||||
{
|
||||
MsgReader.ReadPadBits(buf, ref seekPos);
|
||||
}
|
||||
public void ReadPadBits() { MsgReader.ReadPadBits(ref seekPos); }
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
@@ -1058,7 +983,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8(buf, ref seekPos);
|
||||
}
|
||||
|
||||
|
||||
public Color ReadColorR8G8B8A8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8A8(buf, ref seekPos);
|
||||
@@ -1079,10 +1004,10 @@ namespace Barotrauma.Networking
|
||||
return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes);
|
||||
}
|
||||
|
||||
public void PrepareForSending(ref byte[] outBuf, bool compressPastThreshold, out bool isCompressed, out int outLength)
|
||||
public byte[] PrepareForSending(bool compressPastThreshold, out bool isCompressed, out int outLength)
|
||||
{
|
||||
throw new InvalidOperationException("ReadWriteMessages are not to be sent");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -40,6 +40,7 @@ namespace Barotrauma.Networking
|
||||
public static bool IsCompressed(this PacketHeader h)
|
||||
=> h.HasFlag(PacketHeader.IsCompressed);
|
||||
|
||||
#warning TODO: remove?
|
||||
public static bool IsConnectionInitializationStep(this PacketHeader h)
|
||||
=> h.HasFlag(PacketHeader.IsConnectionInitializationStep);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
internal static class WriteOnlyMessageExtensions
|
||||
{
|
||||
#if CLIENT
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ClientPacketHeader header)
|
||||
{
|
||||
msg.WriteByte((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#elif SERVER
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ServerPacketHeader header)
|
||||
{
|
||||
msg.WriteByte((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#endif
|
||||
public static void WriteNetSerializableStruct(this IWriteMessage msg, INetSerializableStruct serializableStruct)
|
||||
{
|
||||
serializableStruct.Write(msg);
|
||||
}
|
||||
|
||||
public static NetOutgoingMessage ToLidgren(this IWriteMessage msg, NetPeer peer)
|
||||
{
|
||||
NetOutgoingMessage outMsg = peer.CreateMessage();
|
||||
outMsg.Write(msg.Buffer, 0, msg.LengthBytes);
|
||||
return outMsg;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class NetIncomingMessageExtensions
|
||||
{
|
||||
public static T ReadHeader<T>(this NetIncomingMessage msg) where T : Enum
|
||||
{
|
||||
byte header = msg.ReadByte();
|
||||
return Unsafe.As<byte, T>(ref header);
|
||||
}
|
||||
|
||||
public static IReadMessage ToReadMessage(this NetIncomingMessage msg)
|
||||
{
|
||||
return new ReadWriteMessage(msg.Data, 0, msg.LengthBits, copyBuf: false);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class DeliveryMethodExtensions
|
||||
{
|
||||
public static NetDeliveryMethod ToLidgren(this DeliveryMethod deliveryMethod) =>
|
||||
deliveryMethod switch
|
||||
{
|
||||
DeliveryMethod.Unreliable => NetDeliveryMethod.Unreliable,
|
||||
DeliveryMethod.Reliable => NetDeliveryMethod.ReliableUnordered,
|
||||
DeliveryMethod.ReliableOrdered => NetDeliveryMethod.ReliableOrdered,
|
||||
_ => NetDeliveryMethod.Unreliable
|
||||
};
|
||||
|
||||
public static Steamworks.P2PSend ToSteam(this DeliveryMethod deliveryMethod) =>
|
||||
deliveryMethod switch
|
||||
{
|
||||
DeliveryMethod.Reliable => Steamworks.P2PSend.Reliable,
|
||||
DeliveryMethod.ReliableOrdered => Steamworks.P2PSend.Unreliable,
|
||||
_ => Steamworks.P2PSend.Unreliable
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
[NetworkSerialize]
|
||||
internal struct PeerPacketHeaders : INetSerializableStruct
|
||||
{
|
||||
public DeliveryMethod DeliveryMethod;
|
||||
public PacketHeader PacketHeader;
|
||||
public ConnectionInitialization? Initialization;
|
||||
|
||||
public readonly void Deconstruct(
|
||||
out DeliveryMethod deliveryMethod,
|
||||
out PacketHeader packetHeader,
|
||||
out ConnectionInitialization? initialization)
|
||||
{
|
||||
deliveryMethod = DeliveryMethod;
|
||||
packetHeader = PacketHeader;
|
||||
initialization = Initialization;
|
||||
}
|
||||
}
|
||||
|
||||
[NetworkSerialize(ArrayMaxSize = ushort.MaxValue)]
|
||||
internal struct ClientSteamTicketAndVersionPacket : INetSerializableStruct
|
||||
{
|
||||
public string Name;
|
||||
public Option<int> OwnerKey;
|
||||
|
||||
#warning TODO: do something about the type of this
|
||||
// It probably should be Option<SteamId> but we shouldn't build support for
|
||||
// writing SteamIDs to INetSerializableStruct; we should consider adding
|
||||
// attributes to give custom behaviors to specific members of a struct
|
||||
public Option<AccountId> SteamId;
|
||||
|
||||
public Option<byte[]> SteamAuthTicket;
|
||||
public string GameVersion;
|
||||
public Identifier Language;
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
internal struct SteamP2PInitializationRelayPacket : INetSerializableStruct
|
||||
{
|
||||
public ulong LobbyID;
|
||||
public PeerPacketMessage Message;
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
internal struct SteamP2PInitializationOwnerPacket : INetSerializableStruct
|
||||
{
|
||||
public string OwnerName;
|
||||
}
|
||||
|
||||
|
||||
[NetworkSerialize(ArrayMaxSize = ushort.MaxValue)]
|
||||
internal struct ServerPeerContentPackageOrderPacket : INetSerializableStruct
|
||||
{
|
||||
public string ServerName;
|
||||
public ImmutableArray<ServerContentPackage> ContentPackages;
|
||||
}
|
||||
|
||||
[NetworkSerialize(ArrayMaxSize = ushort.MaxValue)]
|
||||
internal struct PeerPacketMessage : INetSerializableStruct
|
||||
{
|
||||
public byte[] Buffer;
|
||||
public readonly int Length => Buffer.Length;
|
||||
|
||||
public readonly IReadMessage GetReadMessage() => new ReadWriteMessage(Buffer, 0, Length, copyBuf: false);
|
||||
public readonly IReadMessage GetReadMessage(bool isCompressed, NetworkConnection conn) => new ReadOnlyMessage(Buffer, isCompressed, 0, Length, conn);
|
||||
}
|
||||
|
||||
[NetworkSerialize(ArrayMaxSize = byte.MaxValue)]
|
||||
internal struct ClientPeerPasswordPacket : INetSerializableStruct
|
||||
{
|
||||
public byte[] Password;
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
internal struct ServerPeerPasswordPacket : INetSerializableStruct
|
||||
{
|
||||
public Option<int> Salt;
|
||||
public Option<int> RetriesLeft;
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
internal struct PeerDisconnectPacket : INetSerializableStruct
|
||||
{
|
||||
public string Message;
|
||||
}
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global, FieldCanBeMadeReadOnly.Global, UnassignedField.Global
|
||||
public sealed class ServerContentPackage : INetSerializableStruct
|
||||
{
|
||||
[NetworkSerialize]
|
||||
public string Name = "";
|
||||
|
||||
[NetworkSerialize(ArrayMaxSize = ushort.MaxValue)]
|
||||
public byte[] HashBytes = Array.Empty<byte>();
|
||||
|
||||
[NetworkSerialize]
|
||||
public ulong WorkshopId;
|
||||
|
||||
[NetworkSerialize]
|
||||
public uint InstallTimeDiffInSeconds;
|
||||
|
||||
private Md5Hash? cachedHash;
|
||||
private DateTime? cachedDateTime;
|
||||
|
||||
public Md5Hash Hash
|
||||
{
|
||||
get => cachedHash ??= Md5Hash.BytesAsHash(HashBytes);
|
||||
set
|
||||
{
|
||||
cachedHash = value;
|
||||
HashBytes = value.ByteRepresentation;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime InstallTime => cachedDateTime ??= DateTime.UtcNow + TimeSpan.FromSeconds(InstallTimeDiffInSeconds);
|
||||
public RegularPackage? RegularPackage => ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Hash.Equals(Hash));
|
||||
public CorePackage? CorePackage => ContentPackageManager.CorePackages.FirstOrDefault(p => p.Hash.Equals(Hash));
|
||||
public ContentPackage? ContentPackage => (ContentPackage?)RegularPackage ?? CorePackage;
|
||||
|
||||
public ServerContentPackage() { }
|
||||
|
||||
public ServerContentPackage(ContentPackage contentPackage, DateTime referenceTime)
|
||||
{
|
||||
Name = contentPackage.Name;
|
||||
Hash = contentPackage.Hash;
|
||||
WorkshopId = contentPackage.SteamWorkshopId;
|
||||
InstallTimeDiffInSeconds =
|
||||
contentPackage.InstallTime is { } installTime
|
||||
? (uint)(installTime - referenceTime).TotalSeconds
|
||||
: 0;
|
||||
}
|
||||
|
||||
public string GetPackageStr() => $"\"{Name}\" (hash {Hash.ShortRepresentation})";
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
partial class RespawnManager : Entity, IServerSerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// How much skills drop towards the job's default skill levels when dying
|
||||
/// </summary>
|
||||
const float SkillReductionOnDeath = 0.75f;
|
||||
|
||||
public enum State
|
||||
{
|
||||
Waiting,
|
||||
|
||||
@@ -209,48 +209,48 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
case "float":
|
||||
msg.WriteVariableUInt32(4);
|
||||
msg.Write((float)overrideValue);
|
||||
msg.WriteSingle((float)overrideValue);
|
||||
break;
|
||||
case "int":
|
||||
msg.WriteVariableUInt32(4);
|
||||
msg.Write((int)overrideValue);
|
||||
msg.WriteInt32((int)overrideValue);
|
||||
break;
|
||||
case "vector2":
|
||||
msg.WriteVariableUInt32(8);
|
||||
msg.Write(((Vector2)overrideValue).X);
|
||||
msg.Write(((Vector2)overrideValue).Y);
|
||||
msg.WriteSingle(((Vector2)overrideValue).X);
|
||||
msg.WriteSingle(((Vector2)overrideValue).Y);
|
||||
break;
|
||||
case "vector3":
|
||||
msg.WriteVariableUInt32(12);
|
||||
msg.Write(((Vector3)overrideValue).X);
|
||||
msg.Write(((Vector3)overrideValue).Y);
|
||||
msg.Write(((Vector3)overrideValue).Z);
|
||||
msg.WriteSingle(((Vector3)overrideValue).X);
|
||||
msg.WriteSingle(((Vector3)overrideValue).Y);
|
||||
msg.WriteSingle(((Vector3)overrideValue).Z);
|
||||
break;
|
||||
case "vector4":
|
||||
msg.WriteVariableUInt32(16);
|
||||
msg.Write(((Vector4)overrideValue).X);
|
||||
msg.Write(((Vector4)overrideValue).Y);
|
||||
msg.Write(((Vector4)overrideValue).Z);
|
||||
msg.Write(((Vector4)overrideValue).W);
|
||||
msg.WriteSingle(((Vector4)overrideValue).X);
|
||||
msg.WriteSingle(((Vector4)overrideValue).Y);
|
||||
msg.WriteSingle(((Vector4)overrideValue).Z);
|
||||
msg.WriteSingle(((Vector4)overrideValue).W);
|
||||
break;
|
||||
case "color":
|
||||
msg.WriteVariableUInt32(4);
|
||||
msg.Write(((Color)overrideValue).R);
|
||||
msg.Write(((Color)overrideValue).G);
|
||||
msg.Write(((Color)overrideValue).B);
|
||||
msg.Write(((Color)overrideValue).A);
|
||||
msg.WriteByte(((Color)overrideValue).R);
|
||||
msg.WriteByte(((Color)overrideValue).G);
|
||||
msg.WriteByte(((Color)overrideValue).B);
|
||||
msg.WriteByte(((Color)overrideValue).A);
|
||||
break;
|
||||
case "rectangle":
|
||||
msg.WriteVariableUInt32(16);
|
||||
msg.Write(((Rectangle)overrideValue).X);
|
||||
msg.Write(((Rectangle)overrideValue).Y);
|
||||
msg.Write(((Rectangle)overrideValue).Width);
|
||||
msg.Write(((Rectangle)overrideValue).Height);
|
||||
msg.WriteInt32(((Rectangle)overrideValue).X);
|
||||
msg.WriteInt32(((Rectangle)overrideValue).Y);
|
||||
msg.WriteInt32(((Rectangle)overrideValue).Width);
|
||||
msg.WriteInt32(((Rectangle)overrideValue).Height);
|
||||
break;
|
||||
default:
|
||||
string strVal = overrideValue.ToString();
|
||||
|
||||
msg.Write(strVal);
|
||||
msg.WriteString(strVal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -550,7 +550,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public bool HasPassword
|
||||
{
|
||||
get { return password != null; }
|
||||
get { return !string.IsNullOrEmpty(password); }
|
||||
#if CLIENT
|
||||
set
|
||||
{
|
||||
@@ -953,14 +953,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void SetPassword(string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(password))
|
||||
{
|
||||
this.password = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
this.password = string.IsNullOrEmpty(password) ? null : password;
|
||||
}
|
||||
|
||||
public static byte[] SaltPassword(byte[] password, int salt)
|
||||
@@ -977,14 +970,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
public bool IsPasswordCorrect(byte[] input, int salt)
|
||||
{
|
||||
if (!HasPassword) return true;
|
||||
if (!HasPassword) { return true; }
|
||||
byte[] saltedPw = SaltPassword(Encoding.UTF8.GetBytes(password), salt);
|
||||
if (input.Length != saltedPw.Length) return false;
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
if (input[i] != saltedPw[i]) return false;
|
||||
}
|
||||
return true;
|
||||
return saltedPw.SequenceEqual(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1039,7 +1027,7 @@ namespace Barotrauma.Networking
|
||||
msg.WriteVariableUInt32((uint)monsterNames.Count);
|
||||
foreach (Identifier s in monsterNames)
|
||||
{
|
||||
msg.Write(monsterEnabled[s]);
|
||||
msg.WriteBoolean(monsterEnabled[s]);
|
||||
}
|
||||
msg.WritePadBits();
|
||||
}
|
||||
@@ -1071,15 +1059,15 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (ExtraCargo == null)
|
||||
{
|
||||
msg.Write((UInt32)0);
|
||||
msg.WriteUInt32((UInt32)0);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.Write((UInt32)ExtraCargo.Count);
|
||||
msg.WriteUInt32((UInt32)ExtraCargo.Count);
|
||||
foreach (KeyValuePair<ItemPrefab, int> kvp in ExtraCargo)
|
||||
{
|
||||
msg.Write(kvp.Key.Identifier);
|
||||
msg.Write((byte)kvp.Value);
|
||||
msg.WriteIdentifier(kvp.Key.Identifier);
|
||||
msg.WriteByte((byte)kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1109,7 +1097,7 @@ namespace Barotrauma.Networking
|
||||
msg.WriteVariableUInt32((uint)HiddenSubs.Count);
|
||||
foreach (string submarineName in HiddenSubs)
|
||||
{
|
||||
msg.Write((UInt16)subList.FindIndex(s => s.Name.Equals(submarineName, StringComparison.OrdinalIgnoreCase)));
|
||||
msg.WriteUInt16((UInt16)subList.FindIndex(s => s.Name.Equals(submarineName, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,16 +119,16 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!CanSend) { throw new Exception("Called Write on a VoipQueue not set up for sending"); }
|
||||
|
||||
msg.Write((UInt16)LatestBufferID);
|
||||
msg.Write(ForceLocal); msg.WritePadBits();
|
||||
msg.WriteUInt16((UInt16)LatestBufferID);
|
||||
msg.WriteBoolean(ForceLocal); msg.WritePadBits();
|
||||
lock (buffers)
|
||||
{
|
||||
for (int i = 0; i < BUFFER_COUNT; i++)
|
||||
{
|
||||
int index = (newestBufferInd + i + 1) % BUFFER_COUNT;
|
||||
|
||||
msg.Write((byte)bufferLengths[index]);
|
||||
msg.Write(buffers[index], 0, bufferLengths[index]);
|
||||
msg.WriteByte((byte)bufferLengths[index]);
|
||||
msg.WriteBytes(buffers[index], 0, bufferLengths[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class WhiteListedPlayer
|
||||
{
|
||||
public string Name;
|
||||
public string IP;
|
||||
|
||||
public UInt16 UniqueIdentifier;
|
||||
}
|
||||
|
||||
partial class WhiteList
|
||||
{
|
||||
const string SavePath = "Data/whitelist.txt";
|
||||
|
||||
private List<WhiteListedPlayer> whitelistedPlayers;
|
||||
public List<WhiteListedPlayer> WhiteListedPlayers
|
||||
{
|
||||
get { return whitelistedPlayers; }
|
||||
}
|
||||
|
||||
public bool Enabled;
|
||||
|
||||
partial void InitProjSpecific();
|
||||
public WhiteList()
|
||||
{
|
||||
Enabled = false;
|
||||
whitelistedPlayers = new List<WhiteListedPlayer>();
|
||||
|
||||
InitProjSpecific();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -508,7 +508,14 @@ namespace Barotrauma
|
||||
|
||||
try
|
||||
{
|
||||
return (float)PropertyInfo.GetValue(parentObject, null);
|
||||
if (PropertyType == typeof(int))
|
||||
{
|
||||
return (int)PropertyInfo.GetValue(parentObject, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (float)PropertyInfo.GetValue(parentObject, null);
|
||||
}
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user