Unstable 0.1500.7.0 (No edition)

This commit is contained in:
Markus Isberg
2021-10-14 00:42:06 +09:00
parent c8943ef9c4
commit de917c5d74
105 changed files with 871 additions and 443 deletions
@@ -342,7 +342,7 @@ namespace Barotrauma
partial void SetupDrawOrder() partial void SetupDrawOrder()
{ {
//make sure every character gets drawn at a distinct "layer" //make sure every character gets drawn at a distinct "layer"
//(instead of having some of the limbs appear behind and some in front of other characters) //(instead of having some of the limbs appear behind and some in front of other characters)
float startDepth = 0.1f; float startDepth = 0.1f;
float increment = 0.001f; float increment = 0.001f;
@@ -355,8 +355,16 @@ namespace Barotrauma
List<Limb> depthSortedLimbs = Limbs.OrderBy(l => l.DefaultSpriteDepth).ToList(); List<Limb> depthSortedLimbs = Limbs.OrderBy(l => l.DefaultSpriteDepth).ToList();
foreach (Limb limb in Limbs) foreach (Limb limb in Limbs)
{ {
if (limb.ActiveSprite == null) { continue; } var sprite = limb.GetActiveSprite();
limb.ActiveSprite.Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f; if (sprite == null) { continue; }
sprite.Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f;
foreach (var conditionalSprite in limb.ConditionalSprites)
{
if (conditionalSprite.Exclusive)
{
conditionalSprite.ActiveSprite.Depth = sprite.Depth;
}
}
} }
foreach (Limb limb in Limbs) foreach (Limb limb in Limbs)
{ {
@@ -128,6 +128,50 @@ namespace Barotrauma
get { return gibEmitters; } get { return gibEmitters; }
} }
private class GUIMessage
{
public string RawText;
public string Identifier;
public string Text;
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
Text = RawText.Replace("[value]", _value.ToString());
Size = GUI.Font.MeasureString(Text);
}
}
public Color Color;
public float Lifetime;
public float Timer;
public Vector2 Size;
public bool PlaySound;
public GUIMessage(string rawText, Color color, float delay, string identifier = null, int? value = null)
{
RawText = Text = rawText;
if (value.HasValue)
{
Text = rawText.Replace("[value]", value.Value.ToString());
Value = value.Value;
}
Timer = -delay;
Size = GUI.Font.MeasureString(Text);
Color = color;
Identifier = identifier;
Lifetime = 3.0f;
}
}
private List<GUIMessage> guiMessages = new List<GUIMessage>();
public static bool IsMouseOnUI => GUI.MouseOn != null || public static bool IsMouseOnUI => GUI.MouseOn != null ||
(CharacterInventory.IsMouseOnInventory && !CharacterInventory.DraggingItemToWorld); (CharacterInventory.IsMouseOnInventory && !CharacterInventory.DraggingItemToWorld);
@@ -618,6 +662,17 @@ namespace Barotrauma
} }
} }
foreach (GUIMessage message in guiMessages)
{
bool wasPending = message.Timer < 0.0f;
message.Timer += deltaTime;
if (wasPending && message.Timer >= 0.0f && message.PlaySound)
{
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
}
}
guiMessages.RemoveAll(m => m.Timer >= m.Lifetime);
if (!enabled) { return; } if (!enabled) { return; }
if (!IsIncapacitated) if (!IsIncapacitated)
@@ -736,6 +791,27 @@ namespace Barotrauma
CharacterHUD.Draw(spriteBatch, this, cam); CharacterHUD.Draw(spriteBatch, this, cam);
if (drawHealth && !CharacterHUD.IsCampaignInterfaceOpen) { CharacterHealth.DrawHUD(spriteBatch); } if (drawHealth && !CharacterHUD.IsCampaignInterfaceOpen) { CharacterHealth.DrawHUD(spriteBatch); }
} }
public void DrawGUIMessages(SpriteBatch spriteBatch, Camera cam)
{
if (info == null || !Enabled || InvisibleTimer > 0.0f)
{
return;
}
Vector2 messagePos = DrawPosition;
messagePos.Y += hudInfoHeight;
messagePos = cam.WorldToScreen(messagePos) - Vector2.UnitY * GUI.IntScale(60);
foreach (GUIMessage message in guiMessages)
{
if (message.Timer < 0) { continue; }
Vector2 drawPos = messagePos + Vector2.UnitX * (GUI.IntScale(60) - message.Size.X);
drawPos = new Vector2((int)drawPos.X, (int)drawPos.Y);
float alpha = MathHelper.SmoothStep(1.0f, 0.0f, message.Timer / message.Lifetime);
GUI.DrawString(spriteBatch, drawPos, message.Text, message.Color * alpha);
messagePos -= Vector2.UnitY * message.Size.Y * 1.2f;
}
}
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam) public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{ {
@@ -942,6 +1018,55 @@ namespace Barotrauma
return nameColor; return nameColor;
} }
public void AddMessage(string rawText, Color color, bool playSound, string identifier = null, int? value = null)
{
GUIMessage existingMessage = null;
float delay = 0.0f;
if (guiMessages.Any())
{
delay = guiMessages.Min(m => m.Timer) - 0.5f;
if (delay < 0)
{
delay = -delay;
if (guiMessages.Count > 5)
{
//reduce delays if there's lots of messages
guiMessages.Where(m => m.Timer < 0.0f).ForEach(m => m.Timer *= 0.9f);
}
}
else
{
delay = 0;
}
}
if (identifier != null)
{
existingMessage = guiMessages.Find(m => m.Identifier == identifier && m.Timer < m.Lifetime * 0.5f);
}
if (existingMessage == null || !value.HasValue)
{
var newMessage = new GUIMessage(rawText, color, delay, identifier, value);
guiMessages.Insert(0, newMessage);
if (playSound)
{
if (delay > 0.0f)
{
newMessage.PlaySound = true;
}
else
{
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
}
}
}
else
{
existingMessage.Value += value.Value;
}
}
/// <summary> /// <summary>
/// Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's one already linked to the object) /// Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's one already linked to the object)
/// The progress bar will automatically fade out after 1 sec if the method hasn't been called during that time /// The progress bar will automatically fade out after 1 sec if the method hasn't been called during that time
@@ -1046,24 +1171,14 @@ namespace Barotrauma
if (newAmount > prevAmount) if (newAmount > prevAmount)
{ {
int increase = newAmount - prevAmount; int increase = newAmount - prevAmount;
GUI.AddMessage( AddMessage("+" + TextManager.GetWithVariable("currencyformat", "[credits]", "[value]"),
"+" + TextManager.GetWithVariable("currencyformat", "[credits]", increase.ToString()), GUI.Style.Yellow, playSound: this == Controlled, "money", increase);
GUI.Style.Yellow,
Position + Vector2.UnitY * 150.0f,
Vector2.UnitY * 10.0f,
playSound: true,
subId: Submarine?.ID ?? -1);
} }
} }
partial void OnTalentGiven(string talentIdentifier) partial void OnTalentGiven(string talentIdentifier)
{ {
GUI.AddMessage(TextManager.Get("talentname." + talentIdentifier.ToString()), AddMessage(TextManager.Get("talentname." + talentIdentifier.ToString()), GUI.Style.Yellow, playSound: this == Controlled);
GUI.Style.Yellow,
Position + Vector2.UnitY * 150.0f,
Vector2.UnitY * 10.0f,
playSound: true,
subId: Submarine?.ID ?? -1);
} }
} }
} }
@@ -187,7 +187,7 @@ namespace Barotrauma
return frame; return frame;
} }
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos) partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel)
{ {
if (TeamID == CharacterTeamType.FriendlyNPC) { return; } if (TeamID == CharacterTeamType.FriendlyNPC) { return; }
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; } if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
@@ -198,17 +198,14 @@ namespace Barotrauma
if ((int)newLevel > (int)prevLevel) if ((int)newLevel > (int)prevLevel)
{ {
int increase = Math.Max((int)newLevel - (int)prevLevel, 1); int increase = Math.Max((int)newLevel - (int)prevLevel, 1);
GUI.AddMessage( Character?.AddMessage(
string.Format("+{0} {1}", increase, TextManager.Get("SkillName." + skillIdentifier)), "+[value] "+ TextManager.Get("SkillName." + skillIdentifier),
specialIncrease ? GUI.Style.Orange : GUI.Style.Green, specialIncrease ? GUI.Style.Orange : GUI.Style.Green,
textPopupPos, playSound: Character == Character.Controlled, skillIdentifier, increase);
Vector2.UnitY * 10.0f,
playSound: specialIncrease,
subId: Character?.Submarine?.ID ?? -1);
} }
} }
partial void OnExperienceChanged(int prevAmount, int newAmount, Vector2 textPopupPos) partial void OnExperienceChanged(int prevAmount, int newAmount)
{ {
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; } if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
@@ -217,13 +214,9 @@ namespace Barotrauma
if (newAmount > prevAmount) if (newAmount > prevAmount)
{ {
int increase = newAmount - prevAmount; int increase = newAmount - prevAmount;
GUI.AddMessage( Character?.AddMessage(
string.Format("+{0} {1}", increase, TextManager.Get("experienceshort")), "+[value] " + TextManager.Get("experienceshort"),
GUI.Style.Blue, GUI.Style.Blue, playSound: Character == Character.Controlled, "exp", increase);
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: true,
subId: Character?.Submarine?.ID ?? -1);
} }
} }
@@ -358,7 +358,7 @@ namespace Barotrauma
{ {
string skillIdentifier = msg.ReadString(); string skillIdentifier = msg.ReadString();
float skillLevel = msg.ReadSingle(); float skillLevel = msg.ReadSingle();
info?.SetSkillLevel(skillIdentifier, skillLevel, Position + Vector2.UnitY * 150.0f); info?.SetSkillLevel(skillIdentifier, skillLevel);
} }
break; break;
case 4: // NetEntityEvent.Type.SetAttackTarget case 4: // NetEntityEvent.Type.SetAttackTarget
@@ -390,7 +390,7 @@ namespace Barotrauma
} }
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex]; targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
} }
if (attackLimb?.attack != null) if (attackLimb?.attack != null && Controlled != this)
{ {
if (eventType == 4) if (eventType == 4)
{ {
@@ -467,8 +467,9 @@ namespace Barotrauma
ushort talentCount = msg.ReadUInt16(); ushort talentCount = msg.ReadUInt16();
for (int i = 0; i < talentCount; i++) for (int i = 0; i < talentCount; i++)
{ {
bool addedThisRound = msg.ReadBoolean();
UInt32 talentIdentifier = msg.ReadUInt32(); UInt32 talentIdentifier = msg.ReadUInt32();
GiveTalent(talentIdentifier); GiveTalent(talentIdentifier, addedThisRound);
} }
break; break;
case 12: //NetEntityEvent.Type.UpdateMoney: case 12: //NetEntityEvent.Type.UpdateMoney:
@@ -1969,8 +1969,8 @@ namespace Barotrauma
if (limbHealths[limb.HealthIndex].Afflictions.Count == 0) continue; if (limbHealths[limb.HealthIndex].Afflictions.Count == 0) continue;
foreach (Affliction a in limbHealths[limb.HealthIndex].Afflictions) foreach (Affliction a in limbHealths[limb.HealthIndex].Afflictions)
{ {
limb.BurnOverlayStrength += a.Strength / a.Prefab.MaxStrength * a.Prefab.BurnOverlayAlpha; limb.BurnOverlayStrength += a.Strength / Math.Min(a.Prefab.MaxStrength, 100) * a.Prefab.BurnOverlayAlpha;
limb.DamageOverlayStrength += a.Strength / a.Prefab.MaxStrength * a.Prefab.DamageOverlayAlpha; limb.DamageOverlayStrength += a.Strength / Math.Min(a.Prefab.MaxStrength, 100) * a.Prefab.DamageOverlayAlpha;
} }
limb.BurnOverlayStrength /= limbHealths[limb.HealthIndex].Afflictions.Count; limb.BurnOverlayStrength /= limbHealths[limb.HealthIndex].Afflictions.Count;
limb.DamageOverlayStrength /= limbHealths[limb.HealthIndex].Afflictions.Count; limb.DamageOverlayStrength /= limbHealths[limb.HealthIndex].Afflictions.Count;
@@ -167,6 +167,10 @@ namespace Barotrauma
} }
} }
public Sprite GetActiveSprite(bool excludeConditionalSprites = true)
=> excludeConditionalSprites ? (_deformSprite != null ? _deformSprite.Sprite : Sprite)
: ActiveSprite;
public float DefaultSpriteDepth { get; private set; } public float DefaultSpriteDepth { get; private set; }
public WearableSprite HuskSprite { get; private set; } public WearableSprite HuskSprite { get; private set; }
@@ -397,7 +401,7 @@ namespace Barotrauma
return deformations; return deformations;
} }
} }
DefaultSpriteDepth = ActiveSprite.Depth; DefaultSpriteDepth = GetActiveSprite()?.Depth ?? 0.0f;
LightSource?.CheckConditionals(); LightSource?.CheckConditionals();
} }
@@ -901,7 +905,7 @@ namespace Barotrauma
} }
foreach (WearableSprite wearable in WearingItems) foreach (WearableSprite wearable in WearingItems)
{ {
if (onlyDrawable != null && onlyDrawable != wearable) continue; if (onlyDrawable != null && onlyDrawable != wearable && wearable.CanBeHiddenByOtherWearables) { continue; }
DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect); DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
//if there are multiple sprites on this limb, make the successive ones be drawn in front //if there are multiple sprites on this limb, make the successive ones be drawn in front
depthStep += step; depthStep += step;
@@ -695,7 +695,12 @@ namespace Barotrauma
AssignOnExecute("control", (string[] args) => AssignOnExecute("control", (string[] args) =>
{ {
if (args.Length < 1) return; if (args.Length < 1) { return; }
if (GameMain.NetworkMember != null)
{
GameMain.Client?.SendConsoleCommand("control " + string.Join(' ', args[0]));
return;
}
var character = FindMatchingCharacter(args, true); var character = FindMatchingCharacter(args, true);
if (character != null) if (character != null)
{ {
@@ -941,7 +941,7 @@ namespace Barotrauma
inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame); inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame);
} }
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || prevMouseOn == null) if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || (prevMouseOn == null && !PlayerInput.SecondaryMouseButtonHeld()))
{ {
for (var i = updateList.Count - 1; i > inventoryIndex; i--) for (var i = updateList.Count - 1; i > inventoryIndex; i--)
{ {
@@ -2454,7 +2454,7 @@ namespace Barotrauma
{ {
Submarine sub = Submarine.Loaded.FirstOrDefault(s => s.ID == subId); Submarine sub = Submarine.Loaded.FirstOrDefault(s => s.ID == subId);
var newMessage = new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, LargeFont, sub: sub); var newMessage = new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, Font, sub: sub);
if (playSound) { SoundPlayer.PlayUISound(soundType); } if (playSound) { SoundPlayer.PlayUISound(soundType); }
bool overlapFound = true; bool overlapFound = true;
int tries = 0; int tries = 0;
@@ -2477,8 +2477,7 @@ namespace Barotrauma
moveDir = Rand.Vector(1.0f); moveDir = Rand.Vector(1.0f);
} }
moveDir.Y = -Math.Abs(moveDir.Y); moveDir.Y = -Math.Abs(moveDir.Y);
newMessage.Pos += moveDir * 20; newMessage.Pos -= Vector2.UnitY * 10;
overlapFound = true;
} }
tries++; tries++;
if (tries > 20) { break; } if (tries > 20) { break; }
@@ -174,7 +174,7 @@ namespace Barotrauma
if (BlendState != null) if (BlendState != null)
{ {
spriteBatch.End(); spriteBatch.End();
spriteBatch.Begin(blendState: BlendState, samplerState: GUI.SamplerState); spriteBatch.Begin(blendState: BlendState, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
} }
if (style != null) if (style != null)
@@ -1026,7 +1026,6 @@ namespace Barotrauma
ContentBackground.DrawManually(spriteBatch, alsoChildren: false); ContentBackground.DrawManually(spriteBatch, alsoChildren: false);
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle; Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
RasterizerState prevRasterizerState = spriteBatch.GraphicsDevice.RasterizerState;
if (HideChildrenOutsideFrame) if (HideChildrenOutsideFrame)
{ {
spriteBatch.End(); spriteBatch.End();
@@ -1054,7 +1053,7 @@ namespace Barotrauma
{ {
spriteBatch.End(); spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect; spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: prevRasterizerState); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
} }
if (ScrollBarVisible) if (ScrollBarVisible)
@@ -511,5 +511,22 @@ namespace Barotrauma
targetComponent.ApplyStyle(componentStyle); targetComponent.ApplyStyle(componentStyle);
} }
public Color GetQualityColor(int quality)
{
switch (quality)
{
case 1:
return ItemQualityColorGood;
case 2:
return ItemQualityColorExcellent;
case 3:
return ItemQualityColorMasterwork;
case -1:
return ItemQualityColorPoor;
default:
return ItemQualityColorNormal;
}
}
} }
} }
@@ -444,9 +444,9 @@ namespace Barotrauma.Items.Components
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { } public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
public virtual void AddToGUIUpdateList() public virtual void AddToGUIUpdateList(int order = 0)
{ {
GuiFrame?.AddToGUIUpdateList(); GuiFrame?.AddToGUIUpdateList(order: order);
} }
public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { } public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { }
@@ -410,13 +410,13 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList(int order = 0)
{ {
base.AddToGUIUpdateList(); base.AddToGUIUpdateList(order);
hullInfoFrame.AddToGUIUpdateList(order: 1); hullInfoFrame.AddToGUIUpdateList(order: order + 1);
if (currentMode == MiniMapMode.ItemFinder && searchBar.Selected) if (currentMode == MiniMapMode.ItemFinder && searchBar.Selected)
{ {
searchAutoComplete.AddToGUIUpdateList(order: 1); searchAutoComplete.AddToGUIUpdateList(order: order + 1);
} }
} }
@@ -14,9 +14,9 @@ namespace Barotrauma.Items.Components
currentTarget?.UpdateHUD(cam, character,deltaTime); currentTarget?.UpdateHUD(cam, character,deltaTime);
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList(int order = 0)
{ {
currentTarget?.AddToGUIUpdateList(); currentTarget?.AddToGUIUpdateList(order: -1);
} }
} }
} }
@@ -88,11 +88,6 @@ namespace Barotrauma.Items.Components
return character == Character.Controlled && character == user && character.SelectedConstruction == item; return character == Character.Controlled && character == user && character.SelectedConstruction == item;
} }
public override void AddToGUIUpdateList()
{
GuiFrame?.AddToGUIUpdateList();
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{ {
if (character != Character.Controlled || character != user || character.SelectedConstruction != item) { return; } if (character != Character.Controlled || character != user || character.SelectedConstruction != item) { return; }
@@ -118,9 +118,9 @@ namespace Barotrauma.Items.Components
// This method is overrided instead of the UpdateHUD method because this ensures the input box is selected // This method is overrided instead of the UpdateHUD method because this ensures the input box is selected
// even when the terminal component is selected for the very first time. Doing the input box selection in the // even when the terminal component is selected for the very first time. Doing the input box selection in the
// UpdateHUD method only selects the input box on every terminal selection except for the very first time. // UpdateHUD method only selects the input box on every terminal selection except for the very first time.
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList(int order = 0)
{ {
base.AddToGUIUpdateList(); base.AddToGUIUpdateList(order: order);
if (shouldSelectInputBox) if (shouldSelectInputBox)
{ {
inputBox.Select(); inputBox.Select();
@@ -5,9 +5,9 @@ namespace Barotrauma.Items.Components
{ {
partial class Wearable partial class Wearable
{ {
private void GetDamageModifierText(ref string description, float damageMultiplier, string afflictionIdentifier) private void GetDamageModifierText(ref string description, DamageModifier damageModifier, string afflictionIdentifier)
{ {
int roundedValue = (int)Math.Round((1 - damageMultiplier) * 100); int roundedValue = (int)Math.Round((1 - damageModifier.DamageMultiplier * damageModifier.ProbabilityMultiplier) * 100);
if (roundedValue == 0) { return; } if (roundedValue == 0) { return; }
string colorStr = XMLExtensions.ColorToString(GUI.Style.Green); string colorStr = XMLExtensions.ColorToString(GUI.Style.Green);
description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, StringComparison.OrdinalIgnoreCase))?.Name ?? afflictionIdentifier}"; description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, StringComparison.OrdinalIgnoreCase))?.Name ?? afflictionIdentifier}";
@@ -15,7 +15,7 @@ namespace Barotrauma.Items.Components
public override void AddTooltipInfo(ref string name, ref string description) public override void AddTooltipInfo(ref string name, ref string description)
{ {
if (damageModifiers.Any(d => !MathUtils.NearlyEqual(d.DamageMultiplier, 1f)) || SkillModifiers.Any()) if (damageModifiers.Any(d => !MathUtils.NearlyEqual(d.DamageMultiplier, 1f) || !MathUtils.NearlyEqual(d.ProbabilityMultiplier, 1f)) || SkillModifiers.Any())
{ {
description += "\n"; description += "\n";
} }
@@ -31,11 +31,11 @@ namespace Barotrauma.Items.Components
foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionIdentifiers) foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionIdentifiers)
{ {
GetDamageModifierText(ref description, damageModifier.DamageMultiplier, afflictionIdentifier); GetDamageModifierText(ref description, damageModifier, afflictionIdentifier);
} }
foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionTypes) foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionTypes)
{ {
GetDamageModifierText(ref description, damageModifier.DamageMultiplier, afflictionIdentifier); GetDamageModifierText(ref description, damageModifier, afflictionIdentifier);
} }
} }
} }
@@ -601,7 +601,10 @@ namespace Barotrauma
{ {
var slotRef = new SlotReference(this, slot, slotIndex, isSubSlot, slots[slotIndex].FirstOrDefault()?.GetComponent<ItemContainer>()?.Inventory); var slotRef = new SlotReference(this, slot, slotIndex, isSubSlot, slots[slotIndex].FirstOrDefault()?.GetComponent<ItemContainer>()?.Inventory);
if (Screen.Selected is SubEditorScreen editor && !editor.WiringMode && slotRef.ParentInventory is CharacterInventory) { return; } if (Screen.Selected is SubEditorScreen editor && !editor.WiringMode && slotRef.ParentInventory is CharacterInventory) { return; }
selectedSlot = slotRef; if (CanSelectSlot(slotRef))
{
selectedSlot = slotRef;
}
} }
if (!DraggingItems.Any()) if (!DraggingItems.Any())
@@ -1302,39 +1305,46 @@ namespace Barotrauma
DraggingItems.Clear(); DraggingItems.Clear();
} }
if (selectedSlot != null) if (selectedSlot != null && !CanSelectSlot(selectedSlot))
{ {
if (!selectedSlot.Slot.MouseOn()) selectedSlot = null;
}
}
private static bool CanSelectSlot(SlotReference selectedSlot)
{
if (!selectedSlot.Slot.MouseOn())
{
return false;
}
else
{
var rootOwner = (selectedSlot.ParentInventory?.Owner as Item)?.GetRootInventoryOwner();
if (selectedSlot.ParentInventory?.Owner != Character.Controlled &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedCharacter &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(selectedSlot.ParentInventory?.Owner) ?? false) &&
rootOwner != Character.Controlled &&
rootOwner != Character.Controlled.SelectedCharacter &&
rootOwner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(rootOwner) ?? false))
{ {
selectedSlot = null; return false;
} }
else var parentItem = (selectedSlot?.ParentInventory?.Owner as Item) ?? selectedSlot?.Item;
if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) &&
ownerCharacter == Character.Controlled &&
CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface))
{ {
var rootOwner = (selectedSlot.ParentInventory?.Owner as Item)?.GetRootInventoryOwner(); highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem);
if (selectedSlot.ParentInventory?.Owner != Character.Controlled && return false;
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedCharacter &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(selectedSlot.ParentInventory?.Owner) ?? false) &&
rootOwner != Character.Controlled &&
rootOwner != Character.Controlled.SelectedCharacter &&
rootOwner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(rootOwner) ?? false))
{
selectedSlot = null;
}
var parentItem = (selectedSlot?.ParentInventory?.Owner as Item) ?? selectedSlot?.Item;
if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) &&
ownerCharacter == Character.Controlled &&
CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface))
{
highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem);
selectedSlot = null;
}
} }
} }
return true;
} }
protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot) protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
{ {
Rectangle hoverArea; Rectangle hoverArea;
@@ -1548,7 +1558,7 @@ namespace Barotrauma
var indicatorStyle = GUI.Style.GetComponentStyle("ContainedStateIndicator.Default"); var indicatorStyle = GUI.Style.GetComponentStyle("ContainedStateIndicator.Default");
Sprite indicatorSprite = indicatorStyle?.GetDefaultSprite(); Sprite indicatorSprite = indicatorStyle?.GetDefaultSprite();
Sprite emptyIndicatorSprite = indicatorStyle?.GetSprite(GUIComponent.ComponentState.Hover); Sprite emptyIndicatorSprite = indicatorStyle?.GetSprite(GUIComponent.ComponentState.Hover);
DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, conditionIndicatorArea, item.Condition / item.MaxCondition); DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, conditionIndicatorArea, item.Condition / item.MaxCondition);
} }
if (itemContainer != null && itemContainer.ShowContainedStateIndicator) if (itemContainer != null && itemContainer.ShowContainedStateIndicator)
@@ -1591,6 +1601,19 @@ namespace Barotrauma
DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, containedIndicatorArea, containedState, DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, containedIndicatorArea, containedState,
pulsate: !usingDefaultSprite && containedState >= 0.0f && containedState < 0.25f && inventory == Character.Controlled?.Inventory && Character.Controlled.HasEquippedItem(item)); pulsate: !usingDefaultSprite && containedState >= 0.0f && containedState < 0.25f && inventory == Character.Controlled?.Inventory && Character.Controlled.HasEquippedItem(item));
} }
if (item.Quality != 0)
{
var style = GUI.Style.GetComponentStyle("InnerGlowSmall");
if (style == null)
{
GUI.DrawRectangle(spriteBatch, rect, GUI.Style.GetQualityColor(item.Quality) * 0.7f);
}
else
{
style.Sprites[GUIComponent.ComponentState.None].FirstOrDefault()?.Draw(spriteBatch, rect, GUI.Style.GetQualityColor(item.Quality) * 0.5f);
}
}
} }
else else
{ {
@@ -1199,7 +1199,7 @@ namespace Barotrauma
return texts; return texts;
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList(int order = 0)
{ {
if (Screen.Selected is SubEditorScreen) if (Screen.Selected is SubEditorScreen)
{ {
@@ -1231,7 +1231,7 @@ namespace Barotrauma
bool wasUsingAlternativeLayout = ic.UseAlternativeLayout; bool wasUsingAlternativeLayout = ic.UseAlternativeLayout;
ic.UseAlternativeLayout = useAlternativeLayout; ic.UseAlternativeLayout = useAlternativeLayout;
needsLayoutUpdate |= ic.UseAlternativeLayout != wasUsingAlternativeLayout; needsLayoutUpdate |= ic.UseAlternativeLayout != wasUsingAlternativeLayout;
ic.AddToGUIUpdateList(); ic.AddToGUIUpdateList(order);
} }
if (itemInUseWarning != null && itemInUseWarning.Visible) if (itemInUseWarning != null && itemInUseWarning.Visible)
@@ -1021,9 +1021,9 @@ namespace Barotrauma
return newEntities; return newEntities;
} }
public virtual void AddToGUIUpdateList() public virtual void AddToGUIUpdateList(int order = 0)
{ {
if (editingHUD != null && editingHUD.UserData == this) editingHUD.AddToGUIUpdateList(); if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(order: order); }
} }
public virtual void UpdateEditing(Camera cam, float deltaTime) { } public virtual void UpdateEditing(Camera cam, float deltaTime) { }
@@ -497,7 +497,6 @@ namespace Barotrauma.CharacterEditor
} }
if (PlayerInput.KeyHit(InputType.Run)) if (PlayerInput.KeyHit(InputType.Run))
{ {
// TODO: refactor this horrible hacky index manipulation mess
int index = 0; int index = 0;
bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow; bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow;
bool isMovingFast = character.AnimController.ForceSelectAnimationType == AnimationType.Run || character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast; bool isMovingFast = character.AnimController.ForceSelectAnimationType == AnimationType.Run || character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast;
@@ -505,23 +504,25 @@ namespace Barotrauma.CharacterEditor
{ {
if (isSwimming || !character.AnimController.CanWalk) if (isSwimming || !character.AnimController.CanWalk)
{ {
index = !character.AnimController.CanWalk ? 0 : (int)AnimationType.SwimSlow - 1; index = !character.AnimController.CanWalk ? (int)AnimationType.SwimFast : (int)AnimationType.SwimSlow;
} }
else else
{ {
index = (int)AnimationType.Walk - 1; index = (int)AnimationType.Walk;
} }
index -= 1;
} }
else else
{ {
if (isSwimming || !character.AnimController.CanWalk) if (isSwimming || !character.AnimController.CanWalk)
{ {
index = !character.AnimController.CanWalk ? 1 : (int)AnimationType.SwimFast - 1; index = !character.AnimController.CanWalk ? (int)AnimationType.SwimSlow : (int)AnimationType.SwimFast;
} }
else else
{ {
index = (int)AnimationType.Run - 1; index = (int)AnimationType.Run;
} }
index -= 1;
} }
if (animSelection.SelectedIndex != index) if (animSelection.SelectedIndex != index)
{ {
@@ -92,8 +92,7 @@ namespace Barotrauma
} }
} }
if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList(); GameMain.GameSession?.AddToGUIUpdateList();
Character.AddAllToGUIUpdateList(); Character.AddAllToGUIUpdateList();
} }
@@ -139,7 +138,7 @@ namespace Barotrauma
for (int i = 0; i < Submarine.MainSubs.Length; i++) for (int i = 0; i < Submarine.MainSubs.Length; i++)
{ {
if (Submarine.MainSubs[i] == null) continue; if (Submarine.MainSubs[i] == null) continue;
if (Level.Loaded != null && Submarine.MainSubs[i].WorldPosition.Y < Level.MaxEntityDepth) continue; if (Level.Loaded != null && Submarine.MainSubs[i].WorldPosition.Y < Level.MaxEntityDepth) { continue; }
Vector2 position = Submarine.MainSubs[i].SubBody != null ? Submarine.MainSubs[i].WorldPosition : Submarine.MainSubs[i].HiddenSubPosition; Vector2 position = Submarine.MainSubs[i].SubBody != null ? Submarine.MainSubs[i].WorldPosition : Submarine.MainSubs[i].HiddenSubPosition;
@@ -151,6 +150,14 @@ namespace Barotrauma
} }
} }
if (!GUI.DisableHUD)
{
foreach (Character c in Character.CharacterList)
{
c.DrawGUIMessages(spriteBatch, cam);
}
}
GUI.Draw(cam, spriteBatch); GUI.Draw(cam, spriteBatch);
spriteBatch.End(); spriteBatch.End();
@@ -1737,9 +1737,10 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.GetWithVariable("startingequipmentname", "[number]", (variant + 1).ToString()), font: GUI.SubHeadingFont, textAlignment: Alignment.Center); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.GetWithVariable("startingequipmentname", "[number]", (variant + 1).ToString()), font: GUI.SubHeadingFont, textAlignment: Alignment.Center);
var itemIdentifiers = jobPrefab.ItemIdentifiers[variant] var itemIdentifiers = jobPrefab.PreviewItems[variant]
.Distinct() .Where(it => it.ShowPreview)
.Where(id => jobPrefab.ShowItemPreview[variant][id]); .Select(it => it.ItemIdentifier)
.Distinct();
int itemsPerRow = 5; int itemsPerRow = 5;
int rows = (int)Math.Max(Math.Ceiling(itemIdentifiers.Count() / (float)itemsPerRow), 1); int rows = (int)Math.Max(Math.Ceiling(itemIdentifiers.Count() / (float)itemsPerRow), 1);
@@ -2624,9 +2625,10 @@ namespace Barotrauma
private void DrawJobVariantItems(SpriteBatch spriteBatch, GUICustomComponent component, Pair<JobPrefab, int> jobPrefab, int itemsPerRow) private void DrawJobVariantItems(SpriteBatch spriteBatch, GUICustomComponent component, Pair<JobPrefab, int> jobPrefab, int itemsPerRow)
{ {
var itemIdentifiers = jobPrefab.First.ItemIdentifiers[jobPrefab.Second] var itemIdentifiers = jobPrefab.First.PreviewItems[jobPrefab.Second]
.Distinct() .Where(it => it.ShowPreview)
.Where(id => jobPrefab.First.ShowItemPreview[jobPrefab.Second][id]); .Select(it => it.ItemIdentifier)
.Distinct();
Point slotSize = new Point(component.Rect.Height); Point slotSize = new Point(component.Rect.Height);
int spacing = (int)(5 * GUI.Scale); int spacing = (int)(5 * GUI.Scale);
@@ -2645,7 +2647,7 @@ namespace Barotrauma
int i = 0; int i = 0;
Rectangle tooltipRect = Rectangle.Empty; Rectangle tooltipRect = Rectangle.Empty;
string tooltip = null; string tooltip = null;
foreach (string itemIdentifier in itemIdentifiers) foreach (var itemIdentifier in itemIdentifiers)
{ {
if (!(MapEntityPrefab.Find(null, identifier: itemIdentifier, showErrorMessages: false) is ItemPrefab itemPrefab)) { continue; } if (!(MapEntityPrefab.Find(null, identifier: itemIdentifier, showErrorMessages: false) is ItemPrefab itemPrefab)) { continue; }
@@ -2664,7 +2666,7 @@ namespace Barotrauma
float iconScale = Math.Min(Math.Min(slotSize.X / icon.size.X, slotSize.Y / icon.size.Y), 2.0f) * 0.9f; float iconScale = Math.Min(Math.Min(slotSize.X / icon.size.X, slotSize.Y / icon.size.Y), 2.0f) * 0.9f;
icon.Draw(spriteBatch, slotPos + slotSize.ToVector2() * 0.5f, scale: iconScale); icon.Draw(spriteBatch, slotPos + slotSize.ToVector2() * 0.5f, scale: iconScale);
int count = jobPrefab.First.ItemIdentifiers[jobPrefab.Second].Count(id => id == itemIdentifier); int count = jobPrefab.First.PreviewItems[jobPrefab.Second].Count(it => it.ShowPreview && it.ItemIdentifier == itemIdentifier);
if (count > 1) if (count > 1)
{ {
string itemCountText = "x" + count; string itemCountText = "x" + count;
@@ -169,7 +169,7 @@ namespace Barotrauma
} }
else else
{ {
DebugConsole.ThrowError("Sprite \"" + file + "\" not found!"); DebugConsole.ThrowError($"Sprite \"{file}\" not found! {Environment.StackTrace.CleanupStackTrace()}");
} }
return null; return null;
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.1500.6.0</Version> <Version>0.1500.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.1500.6.0</Version> <Version>0.1500.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.1500.6.0</Version> <Version>0.1500.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.1500.6.0</Version> <Version>0.1500.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.1500.6.0</Version> <Version>0.1500.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -10,7 +10,7 @@ namespace Barotrauma
{ {
private readonly Dictionary<string, float> prevSentSkill = new Dictionary<string, float>(); private readonly Dictionary<string, float> prevSentSkill = new Dictionary<string, float>();
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos) partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel)
{ {
if (Character == null || Character.Removed) { return; } if (Character == null || Character.Removed) { return; }
if (!prevSentSkill.ContainsKey(skillIdentifier)) if (!prevSentSkill.ContainsKey(skillIdentifier))
@@ -24,9 +24,9 @@ namespace Barotrauma
} }
} }
partial void OnExperienceChanged(int prevAmount, int newAmount, Vector2 textPopupPos) partial void OnExperienceChanged(int prevAmount, int newAmount)
{ {
if (Math.Abs(prevAmount - newAmount) > 0) if (prevAmount != newAmount)
{ {
GameMain.NetworkMember.CreateEntityEvent(Character, new object[] { NetEntityEvent.Type.UpdateExperience }); GameMain.NetworkMember.CreateEntityEvent(Character, new object[] { NetEntityEvent.Type.UpdateExperience });
} }
@@ -430,6 +430,7 @@ namespace Barotrauma
msg.Write((ushort)characterTalents.Count); msg.Write((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents) foreach (var unlockedTalent in characterTalents)
{ {
msg.Write(unlockedTalent.AddedThisRound);
msg.Write(unlockedTalent.Prefab.UIntIdentifier); msg.Write(unlockedTalent.Prefab.UIntIdentifier);
} }
break; break;
@@ -1731,7 +1731,9 @@ namespace Barotrauma
"givetalent", "givetalent",
(Client client, Vector2 cursorWorldPos, string[] args) => (Client client, Vector2 cursorWorldPos, string[] args) =>
{ {
Character targetCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false); if (args.Length == 0) { return; }
Character targetCharacter = (args.Length >= 2) ? FindMatchingCharacter(args.Skip(1).ToArray(), false) : client.Character;
if (targetCharacter == null) { return; } if (targetCharacter == null) { return; }
TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c => TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c =>
@@ -1845,7 +1847,7 @@ namespace Barotrauma
"control", "control",
(Client client, Vector2 cursorWorldPos, string[] args) => (Client client, Vector2 cursorWorldPos, string[] args) =>
{ {
if (args.Length < 1) return; if (args.Length < 1) { return; }
var character = FindMatchingCharacter(args, ignoreRemotePlayers: true, allowedRemotePlayer: client); var character = FindMatchingCharacter(args, ignoreRemotePlayers: true, allowedRemotePlayer: client);
if (character != null) if (character != null)
{ {
@@ -2259,13 +2261,13 @@ namespace Barotrauma
{ {
foreach (Skill skill in character.Info.Job.Skills) foreach (Skill skill in character.Info.Job.Skills)
{ {
character.Info.SetSkillLevel(skill.Identifier, level, character.WorldPosition); character.Info.SetSkillLevel(skill.Identifier, level);
} }
GameMain.Server.SendConsoleMessage($"Set all {character.Name}'s skills to {level}", senderClient); GameMain.Server.SendConsoleMessage($"Set all {character.Name}'s skills to {level}", senderClient);
} }
else else
{ {
character.Info.SetSkillLevel(skillIdentifier, level, character.WorldPosition); character.Info.SetSkillLevel(skillIdentifier, level);
GameMain.Server.SendConsoleMessage($"Set {character.Name}'s {skillIdentifier} level to {level}", senderClient); GameMain.Server.SendConsoleMessage($"Set {character.Name}'s {skillIdentifier} level to {level}", senderClient);
} }
@@ -1181,6 +1181,10 @@ namespace Barotrauma.Networking
{ {
c.Character.ServerRead(objHeader, inc, c); c.Character.ServerRead(objHeader, inc, c);
} }
else
{
DebugConsole.AddWarning($"Received character inputs from a client who's not controlling a character ({c.Name}).");
}
break; break;
case ClientNetObject.ENTITY_STATE: case ClientNetObject.ENTITY_STATE:
entityEventManager.Read(inc, c); entityEventManager.Read(inc, c);
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.1500.6.0</Version> <Version>0.1500.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -103,6 +103,9 @@ namespace Barotrauma
!pathSteering.CurrentPath.Unreachable && !pathSteering.CurrentPath.Unreachable &&
(!requireNonDirty || !pathSteering.IsPathDirty); (!requireNonDirty || !pathSteering.IsPathDirty);
public bool IsCurrentPathUnreachable => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable;
public bool IsCurrentPathFinished => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Finished;
protected readonly float colliderWidth; protected readonly float colliderWidth;
protected readonly float minGapSize; protected readonly float minGapSize;
protected readonly float colliderLength; protected readonly float colliderLength;
@@ -412,7 +415,7 @@ namespace Barotrauma
} }
else if (EscapeTarget != null && EscapeTarget.FlowTargetHull != Character.CurrentHull) else if (EscapeTarget != null && EscapeTarget.FlowTargetHull != Character.CurrentHull)
{ {
if (pathSteering.CurrentPath != null && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable) if (IsCurrentPathUnreachable)
{ {
unreachableGaps.Add(EscapeTarget); unreachableGaps.Add(EscapeTarget);
EscapeTarget = null; EscapeTarget = null;
@@ -453,7 +453,7 @@ namespace Barotrauma
if (SelectedAiTarget?.Entity != null || EscapeTarget != null) if (SelectedAiTarget?.Entity != null || EscapeTarget != null)
{ {
Entity t = SelectedAiTarget?.Entity ?? EscapeTarget; Entity t = SelectedAiTarget?.Entity ?? EscapeTarget;
float referencePos = Vector2.DistanceSquared(Character.WorldPosition, t.WorldPosition) > 100 * 100 && HasValidPath(true) ? PathSteering.CurrentPath.CurrentNode.WorldPosition.X : t.WorldPosition.X; float referencePos = Vector2.DistanceSquared(Character.WorldPosition, t.WorldPosition) > 100 * 100 && HasValidPath(requireNonDirty: true) ? PathSteering.CurrentPath.CurrentNode.WorldPosition.X : t.WorldPosition.X;
Character.AnimController.TargetDir = Character.WorldPosition.X < referencePos ? Direction.Right : Direction.Left; Character.AnimController.TargetDir = Character.WorldPosition.X < referencePos ? Direction.Right : Direction.Left;
} }
else else
@@ -916,9 +916,7 @@ namespace Barotrauma
{ {
if (SteeringManager is IndoorsSteeringManager pathSteering) if (SteeringManager is IndoorsSteeringManager pathSteering)
{ {
if (patrolTarget == null || if (patrolTarget == null || IsCurrentPathUnreachable || IsCurrentPathFinished)
pathSteering.CurrentPath == null ||
!pathSteering.IsPathDirty && (pathSteering.CurrentPath.Finished || pathSteering.CurrentPath.Unreachable))
{ {
newPatrolTargetTimer = Math.Min(newPatrolTargetTimer, newPatrolTargetIntervalMin); newPatrolTargetTimer = Math.Min(newPatrolTargetTimer, newPatrolTargetIntervalMin);
} }
@@ -936,8 +934,7 @@ namespace Barotrauma
else if (targetHulls.Any()) else if (targetHulls.Any())
{ {
patrolTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced); patrolTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
var path = PathSteering.PathFinder.FindPath(Character.SimPosition, patrolTarget.SimPosition, minGapSize: minGapSize * 1.5f, nodeFilter: n => PatrolNodeFilter(n)); var path = PathSteering.PathFinder.FindPath(Character.SimPosition, patrolTarget.SimPosition, Character.Submarine, minGapSize: minGapSize * 1.5f, nodeFilter: n => PatrolNodeFilter(n));
if (path.Unreachable) if (path.Unreachable)
{ {
//can't go to this room, remove it from the list and try another room //can't go to this room, remove it from the list and try another room
@@ -2331,34 +2328,32 @@ namespace Barotrauma
SelectedAiTarget.Entity is Character c && VisibleHulls.Contains(c.CurrentHull)) SelectedAiTarget.Entity is Character c && VisibleHulls.Contains(c.CurrentHull))
{ {
// Steer towards the target if in the same room and swimming // Steer towards the target if in the same room and swimming
Vector2 dir = Vector2.Normalize(SelectedAiTarget.Entity.WorldPosition - Character.WorldPosition); SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(SelectedAiTarget.Entity.WorldPosition - Character.WorldPosition));
if (MathUtils.IsValid(dir))
{
SteeringManager.SteeringManual(deltaTime, dir);
}
} }
else else
{ {
// Use path finding // Use path finding
PathSteering.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), weight: 2, minGapWidth: minGapSize); PathSteering.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), weight: 2, minGapWidth: minGapSize);
if (!PathSteering.IsPathDirty && PathSteering.CurrentPath.Unreachable)
{
// Can't reach
State = AIState.Idle;
IgnoreTarget(SelectedAiTarget);
return;
}
} }
} }
else else
{ {
// Outside // Outside
SteeringManager.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), 5); SteeringManager.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), 5);
if (Character.AnimController.InWater) }
if (steeringManager is IndoorsSteeringManager pathSteering)
{
if (!pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
{ {
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15); // Can't reach
State = AIState.Idle;
IgnoreTarget(SelectedAiTarget);
} }
} }
else if (Character.AnimController.InWater)
{
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
}
} }
#region Targeting #region Targeting
@@ -2510,6 +2505,11 @@ namespace Barotrauma
else if (targetingFromOutsideToInside) else if (targetingFromOutsideToInside)
{ {
targetingTag = "room"; targetingTag = "room";
if (item.Submarine?.Info.IsRuin != null)
{
// Ignore ruin items when the creature is outside.
continue;
}
} }
} }
else if (targetingTag == "nasonov") else if (targetingTag == "nasonov")
@@ -115,6 +115,11 @@ namespace Barotrauma
IsPathDirty = true; IsPathDirty = true;
} }
public void SteeringSeekSimple(Vector2 targetSimPos, float weight = 1)
{
steering += base.DoSteeringSeek(targetSimPos, weight);
}
public void SteeringSeek(Vector2 target, float weight, float minGapWidth = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true) public void SteeringSeek(Vector2 target, float weight, float minGapWidth = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true)
{ {
steering += CalculateSteeringSeek(target, weight, minGapWidth, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity); steering += CalculateSteeringSeek(target, weight, minGapWidth, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
@@ -164,7 +169,7 @@ namespace Barotrauma
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true) private Vector2 CalculateSteeringSeek(Vector2 target, float weight, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
{ {
bool needsNewPath = currentPath == null || currentPath.Unreachable; bool needsNewPath = currentPath == null || currentPath.Unreachable || currentPath.Finished;
if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f) if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f)
{ {
Vector2 targetDiff = target - currentTarget; Vector2 targetDiff = target - currentTarget;
@@ -194,16 +199,8 @@ namespace Barotrauma
SkipCurrentPathNodes(); SkipCurrentPathNodes();
currentTarget = target; currentTarget = target;
Vector2 currentPos = host.SimPosition; Vector2 currentPos = host.SimPosition;
if (character != null && character.Submarine == null) pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin;
{ pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0;
var targetHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(target), null, false);
if (targetHull != null && targetHull.Submarine != null)
{
currentPos -= targetHull.Submarine.SimPosition;
}
}
pathFinder.InsideSubmarine = character.Submarine != null;
pathFinder.ApplyPenaltyToOutsideNodes = character.PressureProtection <= 0;
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility); var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0; bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0;
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable) if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
@@ -218,7 +215,7 @@ namespace Barotrauma
// Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us). // Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us).
float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1); float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1);
useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t); useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t);
if (!useNewPath && character.Submarine != null) if (!useNewPath)
{ {
// It's possible that the current path was calculated from a start point that is no longer valid. // It's possible that the current path was calculated from a start point that is no longer valid.
// Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node. // Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node.
@@ -322,15 +319,26 @@ namespace Barotrauma
doorsChecked = true; doorsChecked = true;
} }
Vector2 pos = host.SimPosition; Vector2 pos = host.SimPosition;
if (character != null && CurrentPath.CurrentNode?.Submarine != null) if (character != null && CurrentPath.CurrentNode != null)
{ {
if (character.Submarine == null) var nodeSub = CurrentPath.CurrentNode.Submarine;
if (nodeSub != null)
{ {
pos -= CurrentPath.CurrentNode.Submarine.SimPosition; if (character.Submarine == null)
{
// Going inside
pos -= ConvertUnits.ToSimUnits(nodeSub.Position);
}
else if (character.Submarine != nodeSub)
{
// Different subs
pos -= ConvertUnits.ToSimUnits(nodeSub.Position - character.Submarine.Position);
}
} }
else if (character.Submarine != currentPath.CurrentNode.Submarine) else if (character.Submarine != null)
{ {
pos -= ConvertUnits.ToSimUnits(currentPath.CurrentNode.Submarine.Position - character.Submarine.Position); // Going outside
pos += ConvertUnits.ToSimUnits(character.Submarine.Position);
} }
} }
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater; bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
@@ -724,7 +724,10 @@ namespace Barotrauma
} }
if (retreatTarget != null && character.CurrentHull != retreatTarget) if (retreatTarget != null && character.CurrentHull != retreatTarget)
{ {
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true), TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true)
{
UsePathingOutside = false
},
onAbandon: () => onAbandon: () =>
{ {
if (Enemy != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull)) if (Enemy != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull))
@@ -783,6 +786,7 @@ namespace Barotrauma
TryAddSubObjective(ref followTargetObjective, TryAddSubObjective(ref followTargetObjective,
constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true, closeEnough: 50) constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true, closeEnough: 50)
{ {
UsePathingOutside = false,
IgnoreIfTargetDead = true, IgnoreIfTargetDead = true,
DialogueIdentifier = "dialogcannotreachtarget", DialogueIdentifier = "dialogcannotreachtarget",
TargetName = Enemy.DisplayName, TargetName = Enemy.DisplayName,
@@ -334,7 +334,7 @@ namespace Barotrauma
continue; continue;
} }
// Don't allow to go outside if not already outside. // Don't allow to go outside if not already outside.
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition, nodeFilter: node => node.Waypoint.CurrentHull != null); var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition, character.Submarine, nodeFilter: node => node.Waypoint.CurrentHull != null);
if (path.Unreachable) if (path.Unreachable)
{ {
HumanAIController.UnreachableHulls.Add(hull); HumanAIController.UnreachableHulls.Add(hull);
@@ -323,7 +323,7 @@ namespace Barotrauma
// This is relatively expensive, so let's do this only when it significantly improves the behavior. // This is relatively expensive, so let's do this only when it significantly improves the behavior.
// Only allow one path find call per frame. // Only allow one path find call per frame.
hasCalledPathFinder = true; hasCalledPathFinder = true;
var path = PathSteering.PathFinder.FindPath(character.SimPosition, item.SimPosition, errorMsgStr: $"AIObjectiveGetItem {character.DisplayName}", nodeFilter: node => node.Waypoint.CurrentHull != null); var path = PathSteering.PathFinder.FindPath(character.SimPosition, item.SimPosition, character.Submarine, errorMsgStr: $"AIObjectiveGetItem {character.DisplayName}", nodeFilter: node => node.Waypoint.CurrentHull != null);
if (path.Unreachable) { continue; } if (path.Unreachable) { continue; }
} }
currItemPriority = itemPriority; currItemPriority = itemPriority;
@@ -27,6 +27,7 @@ namespace Barotrauma
public bool followControlledCharacter; public bool followControlledCharacter;
public bool mimic; public bool mimic;
public bool SpeakIfFails { get; set; } = true; public bool SpeakIfFails { get; set; } = true;
public bool UsePathingOutside { get; set; } = true;
public float extraDistanceWhileSwimming; public float extraDistanceWhileSwimming;
public float extraDistanceOutsideSub; public float extraDistanceOutsideSub;
@@ -121,13 +122,14 @@ namespace Barotrauma
} }
private readonly float avoidLookAheadDistance = 5; private readonly float avoidLookAheadDistance = 5;
private readonly float pathWaitingTime = 3;
public AIObjectiveGoTo(ISpatialEntity target, Character character, AIObjectiveManager objectiveManager, bool repeat = false, bool getDivingGearIfNeeded = true, float priorityModifier = 1, float closeEnough = 0) public AIObjectiveGoTo(ISpatialEntity target, Character character, AIObjectiveManager objectiveManager, bool repeat = false, bool getDivingGearIfNeeded = true, float priorityModifier = 1, float closeEnough = 0)
: base(character, objectiveManager, priorityModifier) : base(character, objectiveManager, priorityModifier)
{ {
Target = target; Target = target;
this.repeat = repeat; this.repeat = repeat;
waitUntilPathUnreachable = 3.0f; waitUntilPathUnreachable = pathWaitingTime;
this.getDivingGearIfNeeded = getDivingGearIfNeeded; this.getDivingGearIfNeeded = getDivingGearIfNeeded;
if (Target is Item i) if (Target is Item i)
{ {
@@ -186,7 +188,6 @@ namespace Barotrauma
// Wait // Wait
character.AIController.SteeringManager.Reset(); character.AIController.SteeringManager.Reset();
} }
waitUntilPathUnreachable -= deltaTime;
if (!character.IsClimbing) if (!character.IsClimbing)
{ {
character.SelectedConstruction = null; character.SelectedConstruction = null;
@@ -222,11 +223,13 @@ namespace Barotrauma
{ {
Abandon = true; Abandon = true;
} }
else if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && PathSteering.CurrentPath.Unreachable && !PathSteering.IsPathDirty) else if (HumanAIController.IsCurrentPathUnreachable)
{ {
waitUntilPathUnreachable -= deltaTime;
SteeringManager.Reset(); SteeringManager.Reset();
if (waitUntilPathUnreachable < 0) if (waitUntilPathUnreachable < 0)
{ {
waitUntilPathUnreachable = pathWaitingTime;
if (repeat) if (repeat)
{ {
SpeakCannotReach(); SpeakCannotReach();
@@ -325,25 +328,29 @@ namespace Barotrauma
} }
else else
{ {
SeekGaps(maxGapDistance); bool isRuins = character.Submarine?.Info.IsRuin != null || Target.Submarine?.Info.IsRuin != null;
seekGapsTimer = seekGapsInterval * Rand.Range(0.1f, 1.1f); if (!isRuins || !HumanAIController.HasValidPath(requireNonDirty: true, requireUnfinished: true))
if (TargetGap != null)
{ {
// Check that nothing is blocking the way SeekGaps(maxGapDistance);
Vector2 rayStart = character.SimPosition; seekGapsTimer = seekGapsInterval * Rand.Range(0.1f, 1.1f);
Vector2 rayEnd = TargetGap.SimPosition; if (TargetGap != null)
if (TargetGap.Submarine != null && character.Submarine == null)
{ {
rayStart -= TargetGap.Submarine.SimPosition; // Check that nothing is blocking the way
} Vector2 rayStart = character.SimPosition;
else if (TargetGap.Submarine == null && character.Submarine != null) Vector2 rayEnd = TargetGap.SimPosition;
{ if (TargetGap.Submarine != null && character.Submarine == null)
rayEnd -= character.Submarine.SimPosition; {
} rayStart -= TargetGap.Submarine.SimPosition;
var closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true); }
if (closestBody != null) else if (TargetGap.Submarine == null && character.Submarine != null)
{ {
TargetGap = null; rayEnd -= character.Submarine.SimPosition;
}
var closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true);
if (closestBody != null)
{
TargetGap = null;
}
} }
} }
} }
@@ -454,19 +461,25 @@ namespace Barotrauma
} }
else if (!isInside && HumanAIController.UseIndoorSteeringOutside) else if (!isInside && HumanAIController.UseIndoorSteeringOutside)
{ {
if (character.Submarine == null && Target.Submarine != null) nodeFilter = n => n.Waypoint.Submarine == null;
{
targetPos += Target.Submarine.SimPosition;
}
nodeFilter = n => n.Waypoint.Tunnel != null;
} }
PathSteering.SteeringSeek(targetPos, weight: 1, if (!isInside && !UsePathingOutside)
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null), {
endNodeFilter: endNodeFilter, PathSteering.SteeringSeekSimple(character.GetRelativeSimPosition(Target), 10);
nodeFilter: nodeFilter, if (character.AnimController.InWater)
checkVisiblity: CheckVisibility); {
SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 15);
}
}
else
{
PathSteering.SteeringSeek(targetPos, weight: 1,
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null),
endNodeFilter: endNodeFilter,
nodeFilter: nodeFilter,
checkVisiblity: CheckVisibility);
}
if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable)) if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable))
{ {
if (useScooter) if (useScooter)
@@ -251,7 +251,7 @@ namespace Barotrauma
currentTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced); currentTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
bool isInWrongSub = (character.TeamID == CharacterTeamType.FriendlyNPC && !character.IsEscorted) && character.Submarine.TeamID != character.TeamID; bool isInWrongSub = (character.TeamID == CharacterTeamType.FriendlyNPC && !character.IsEscorted) && character.Submarine.TeamID != character.TeamID;
bool isCurrentHullAllowed = !isInWrongSub && !IsForbidden(character.CurrentHull); bool isCurrentHullAllowed = !isInWrongSub && !IsForbidden(character.CurrentHull);
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsgStr: null, nodeFilter: node => var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, character.Submarine, nodeFilter: node =>
{ {
if (node.Waypoint.CurrentHull == null) { return false; } if (node.Waypoint.CurrentHull == null) { return false; }
// Check that there is no unsafe hulls on the way to the target // Check that there is no unsafe hulls on the way to the target
@@ -1,7 +1,6 @@
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -69,7 +68,7 @@ namespace Barotrauma
HumanAIController.ResetEscape(); HumanAIController.ResetEscape();
} }
HumanAIController.Escape(deltaTime); HumanAIController.Escape(deltaTime);
if (HumanAIController.EscapeTarget == null || !HumanAIController.HasValidPath(requireNonDirty: true, requireUnfinished: false)) if (HumanAIController.EscapeTarget == null || HumanAIController.IsCurrentPathUnreachable)
{ {
Abandon = true; Abandon = true;
} }
@@ -92,14 +91,16 @@ namespace Barotrauma
{ {
RemoveSubObjective(ref moveInCaveObjective); RemoveSubObjective(ref moveInCaveObjective);
RemoveSubObjective(ref moveOutsideObjective); RemoveSubObjective(ref moveOutsideObjective);
// TODO: Check 'repeat' and 'onAbandon' parameters
TryAddSubObjective(ref moveInsideObjective, TryAddSubObjective(ref moveInsideObjective,
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager), constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
onCompleted: () => moveInsideObjective = null); onCompleted: () => RemoveSubObjective(ref moveInsideObjective),
onAbandon: () => Abandon = true);
} }
else else
{ {
#if DEBUG
DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveInsideObjective'"); DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveInsideObjective'");
#endif
} }
} }
} }
@@ -117,8 +118,7 @@ namespace Barotrauma
float closestDistance = float.MaxValue; float closestDistance = float.MaxValue;
foreach (var w in WayPoint.WayPointList) foreach (var w in WayPoint.WayPointList)
{ {
if (w.Tunnel == null) { continue; } if (w.Tunnel != null && w.Tunnel.Type == Level.TunnelType.Cave) { continue; }
if (w.Tunnel.Type == Level.TunnelType.Cave) { continue; }
if (w.linkedTo.None(l => l is WayPoint linkedWaypoint && linkedWaypoint.Tunnel?.Type == Level.TunnelType.Cave)) { continue; } if (w.linkedTo.None(l => l is WayPoint linkedWaypoint && linkedWaypoint.Tunnel?.Type == Level.TunnelType.Cave)) { continue; }
float distance = Vector2.DistanceSquared(character.WorldPosition, w.WorldPosition); float distance = Vector2.DistanceSquared(character.WorldPosition, w.WorldPosition);
if (closestOutsideWaypoint == null || distance < closestDistance) if (closestOutsideWaypoint == null || distance < closestDistance)
@@ -131,17 +131,19 @@ namespace Barotrauma
{ {
RemoveSubObjective(ref moveInsideObjective); RemoveSubObjective(ref moveInsideObjective);
RemoveSubObjective(ref moveOutsideObjective); RemoveSubObjective(ref moveOutsideObjective);
// TODO: Check 'repeat' and 'onAbandon' parameters
TryAddSubObjective(ref moveInCaveObjective, TryAddSubObjective(ref moveInCaveObjective,
constructor: () => new AIObjectiveGoTo(closestOutsideWaypoint, character, objectiveManager) constructor: () => new AIObjectiveGoTo(closestOutsideWaypoint, character, objectiveManager)
{ {
endNodeFilter = n => n.Waypoint == closestOutsideWaypoint endNodeFilter = n => n.Waypoint == closestOutsideWaypoint
}, },
onCompleted: () => moveInCaveObjective = null); onCompleted: () => RemoveSubObjective(ref moveInCaveObjective),
onAbandon: () => Abandon = true);
} }
else else
{ {
#if DEBUG
DebugConsole.ThrowError("Error with a Return objective: no suitable main or side path node target found for 'moveOutsideObjective'"); DebugConsole.ThrowError("Error with a Return objective: no suitable main or side path node target found for 'moveOutsideObjective'");
#endif
} }
} }
else else
@@ -167,14 +169,16 @@ namespace Barotrauma
{ {
RemoveSubObjective(ref moveInsideObjective); RemoveSubObjective(ref moveInsideObjective);
RemoveSubObjective(ref moveInCaveObjective); RemoveSubObjective(ref moveInCaveObjective);
// TODO: Check 'repeat' and 'onAbandon' parameters
TryAddSubObjective(ref moveOutsideObjective, TryAddSubObjective(ref moveOutsideObjective,
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager), constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
onCompleted: () => moveOutsideObjective = null); onCompleted: () => RemoveSubObjective(ref moveOutsideObjective),
onAbandon: () => Abandon = true);
} }
else else
{ {
#if DEBUG
DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveOutsideObjective'"); DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveOutsideObjective'");
#endif
} }
} }
} }
@@ -182,19 +186,11 @@ namespace Barotrauma
{ {
if (HumanAIController.IsInsideCave) if (HumanAIController.IsInsideCave)
{ {
if (moveOutsideObjective != null) RemoveSubObjective(ref moveOutsideObjective);
{
RemoveSubObjective(ref moveOutsideObjective);
moveOutsideObjective = null;
}
} }
else else
{ {
if (moveInCaveObjective != null) RemoveSubObjective(ref moveInCaveObjective);
{
RemoveSubObjective(ref moveInCaveObjective);
moveInCaveObjective = null;
}
} }
} }
usingEscapeBehavior = shouldUseEscapeBehavior; usingEscapeBehavior = shouldUseEscapeBehavior;
@@ -119,7 +119,8 @@ namespace Barotrauma
public PathFinder(List<WayPoint> wayPoints, bool isCharacter) public PathFinder(List<WayPoint> wayPoints, bool isCharacter)
{ {
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => (w.Submarine != null == isCharacter) || (isCharacter && w.Tunnel != null)), removeOrphans: true); var filtered = isCharacter ? wayPoints : wayPoints.FindAll(w => w.Submarine == null);
nodes = PathNode.GenerateNodes(filtered, removeOrphans: true);
foreach (WayPoint wp in wayPoints) foreach (WayPoint wp in wayPoints)
{ {
wp.OnLinksChanged += WaypointLinksChanged; wp.OnLinksChanged += WaypointLinksChanged;
@@ -179,17 +180,37 @@ namespace Barotrauma
foreach (PathNode node in nodes) foreach (PathNode node in nodes)
{ {
node.TempPosition = node.Position; node.TempPosition = node.Position;
if (hostSub != null) var wpSub = node.Waypoint.Submarine;
if (hostSub != null && wpSub == null)
{ {
Vector2 diff = node.Waypoint.Submarine != null ? // inside and targeting outside
hostSub.SimPosition - node.Waypoint.Submarine.SimPosition : node.TempPosition -= hostSub.SimPosition;
hostSub.SimPosition - node.Waypoint.SimPosition; }
node.TempPosition -= diff; else if (wpSub != null && hostSub != null && wpSub != hostSub)
{
// different subs
node.TempPosition -= hostSub.SimPosition - wpSub.SimPosition;
}
else if (hostSub == null && wpSub != null)
{
// Outside and targeting inside
node.TempPosition += wpSub.SimPosition;
} }
float xDiff = Math.Abs(start.X - node.TempPosition.X); float xDiff = Math.Abs(start.X - node.TempPosition.X);
float yDiff = Math.Abs(start.Y - node.TempPosition.Y); float yDiff = Math.Abs(start.Y - node.TempPosition.Y);
if (yDiff > 1.0f && node.Waypoint.Ladders == null && node.Waypoint.Stairs == null) { yDiff += 10.0f; } if (InsideSubmarine)
node.TempDistance = xDiff + (InsideSubmarine ? yDiff * 10.0f : yDiff); //higher cost for vertical movement when inside the sub {
//higher cost for vertical movement when inside the sub
if (yDiff > 1.0f && node.Waypoint.Ladders == null && node.Waypoint.Stairs == null)
{
yDiff += 10.0f;
}
node.TempDistance = xDiff + yDiff * 10.0f;
}
else
{
node.TempDistance = xDiff + yDiff;
}
//much higher cost to waypoints that are outside //much higher cost to waypoints that are outside
if (node.Waypoint.CurrentHull == null && ApplyPenaltyToOutsideNodes) { node.TempDistance *= 10.0f; } if (node.Waypoint.CurrentHull == null && ApplyPenaltyToOutsideNodes) { node.TempDistance *= 10.0f; }
@@ -57,7 +57,10 @@ namespace Barotrauma
public void SteeringManual(float deltaTime, Vector2 velocity) public void SteeringManual(float deltaTime, Vector2 velocity)
{ {
steering += velocity; if (MathUtils.IsValid(velocity))
{
steering += velocity;
}
} }
public void Reset() public void Reset()
@@ -374,7 +374,7 @@ namespace Barotrauma
{ {
//pull the character's mouth to the target character (again with a fluctuating force) //pull the character's mouth to the target character (again with a fluctuating force)
float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f)); float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength);
} }
else else
{ {
@@ -746,7 +746,7 @@ namespace Barotrauma
var arm = GetLimb(armType); var arm = GetLimb(armType);
if (arm != null && Math.Abs(arm.body.AngularVelocity) < 10.0f) if (arm != null && Math.Abs(arm.body.AngularVelocity) < 10.0f)
{ {
arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.5f, 0.5f), arm.Mass * 50.0f); arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.5f, 0.5f), arm.Mass * 50.0f * CurrentGroundedParams.ArmMoveStrength);
} }
//get the elbow to a neutral rotation //get the elbow to a neutral rotation
@@ -757,7 +757,7 @@ namespace Barotrauma
if (elbow != null) if (elbow != null)
{ {
float diff = elbow.JointAngle - (Dir > 0 ? elbow.LowerLimit : elbow.UpperLimit); float diff = elbow.JointAngle - (Dir > 0 ? elbow.LowerLimit : elbow.UpperLimit);
forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f); forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f * CurrentGroundedParams.ArmMoveStrength);
} }
} }
} }
@@ -809,8 +809,8 @@ namespace Barotrauma
{ {
foreach (Gap gap in currentHull.ConnectedGaps) foreach (Gap gap in currentHull.ConnectedGaps)
{ {
if (gap.IsHorizontal || gap.Open <= 0.0f) continue; if (gap.IsHorizontal || gap.Open <= 0.0f) { continue; }
if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) continue; if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) { continue; }
//if the gap is above us and leads outside, there's no surface to limit the movement //if the gap is above us and leads outside, there's no surface to limit the movement
if (!gap.IsRoomToRoom && gap.Position.Y > currentHull.Position.Y) if (!gap.IsRoomToRoom && gap.Position.Y > currentHull.Position.Y)
@@ -830,7 +830,7 @@ namespace Barotrauma
} }
} }
surfaceLimiter = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 0.4f) - surfacePos; surfaceLimiter = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 1.0f) - surfacePos;
surfaceLimiter = Math.Max(1.0f, surfaceLimiter); surfaceLimiter = Math.Max(1.0f, surfaceLimiter);
if (surfaceLimiter > 50.0f) { return; } if (surfaceLimiter > 50.0f) { return; }
} }
@@ -921,7 +921,7 @@ namespace Barotrauma
head.body.ApplyTorque(Dir); head.body.ApplyTorque(Dir);
} }
movement.Y = movement.Y - (surfaceLimiter - 1.0f) * 0.01f; movement.Y = movement.Y * (1.0f - ((surfaceLimiter - 1.0f) / 50.0f));
} }
bool isNotRemote = true; bool isNotRemote = true;
@@ -1003,7 +1003,10 @@ namespace Barotrauma
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X); rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix); rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetRightHandPenalty()), 1.0f); float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetRightHandPenalty()), 1.0f);
// Limb hand, Vector2 pos, float force = 1.0f if (character.Inventory != null && character.Inventory.GetItemInLimbSlot(InvSlotType.RightHand) != null)
{
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
}
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier); HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
} }
@@ -1013,6 +1016,10 @@ namespace Barotrauma
leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X); leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix); leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty()), 1.0f); float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty()), 1.0f);
if (character.Inventory != null && character.Inventory.GetItemInLimbSlot(InvSlotType.LeftHand) != null)
{
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
}
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier); HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
} }
} }
@@ -1154,7 +1161,7 @@ namespace Barotrauma
if (character.SimPosition.Y > ladderSimPos.Y) { climbForce.Y = Math.Min(0.0f, climbForce.Y); } if (character.SimPosition.Y > ladderSimPos.Y) { climbForce.Y = Math.Min(0.0f, climbForce.Y); }
//apply forces to the collider to move the Character up/down //apply forces to the collider to move the Character up/down
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass);
float movementMultiplier = targetMovement.Y < 0 ? 0 : 1; float movementMultiplier = targetMovement.Y < 0 ? 0 : 1;
head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, WalkParams.HeadTorque); head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, WalkParams.HeadTorque);
@@ -1383,7 +1390,7 @@ namespace Barotrauma
target.CharacterHealth.CalculateVitality(); target.CharacterHealth.CalculateVitality();
if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f) if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
{ {
character.Info?.IncreaseSkillLevel("medical", SkillSettings.Current.SkillIncreasePerCprRevive, character.Position + Vector2.UnitY * 150.0f); character.Info?.IncreaseSkillLevel("medical", SkillSettings.Current.SkillIncreasePerCprRevive);
SteamAchievementManager.OnCharacterRevived(target, character); SteamAchievementManager.OnCharacterRevived(target, character);
lastReviveTime = (float)Timing.TotalTime; lastReviveTime = (float)Timing.TotalTime;
#if SERVER #if SERVER
@@ -1348,19 +1348,19 @@ namespace Barotrauma
string errorMsg = null; string errorMsg = null;
if (!MathUtils.IsValid(body.SimPosition) || Math.Abs(body.SimPosition.X) > 1e10f || Math.Abs(body.SimPosition.Y) > 1e10f) if (!MathUtils.IsValid(body.SimPosition) || Math.Abs(body.SimPosition.X) > 1e10f || Math.Abs(body.SimPosition.Y) > 1e10f)
{ {
errorMsg = GetBodyName() + " position invalid (" + body.SimPosition + ", character: " + character.Name + "), resetting the ragdoll."; errorMsg = GetBodyName() + " position invalid (" + body.SimPosition + ", character: " + character.Name + ").";
} }
else if (!MathUtils.IsValid(body.LinearVelocity) || Math.Abs(body.LinearVelocity.X) > 1000f || Math.Abs(body.LinearVelocity.Y) > 1000f) else if (!MathUtils.IsValid(body.LinearVelocity) || Math.Abs(body.LinearVelocity.X) > 1000f || Math.Abs(body.LinearVelocity.Y) > 1000f)
{ {
errorMsg = GetBodyName() + " velocity invalid (" + body.LinearVelocity + ", character: " + character.Name + "), resetting the ragdoll."; errorMsg = GetBodyName() + " velocity invalid (" + body.LinearVelocity + ", character: " + character.Name + ").";
} }
else if (!MathUtils.IsValid(body.Rotation)) else if (!MathUtils.IsValid(body.Rotation))
{ {
errorMsg = GetBodyName() + " rotation invalid (" + body.Rotation + ", character: " + character.Name + "), resetting the ragdoll."; errorMsg = GetBodyName() + " rotation invalid (" + body.Rotation + ", character: " + character.Name + ").";
} }
else if (!MathUtils.IsValid(body.AngularVelocity) || Math.Abs(body.AngularVelocity) > 1000f) else if (!MathUtils.IsValid(body.AngularVelocity) || Math.Abs(body.AngularVelocity) > 1000f)
{ {
errorMsg = GetBodyName() + " angular velocity invalid (" + body.AngularVelocity + ", character: " + character.Name + "), resetting the ragdoll."; errorMsg = GetBodyName() + " angular velocity invalid (" + body.AngularVelocity + ", character: " + character.Name + ").";
} }
if (errorMsg != null) if (errorMsg != null)
{ {
@@ -1469,11 +1469,11 @@ namespace Barotrauma
if (flowForce.LengthSquared() > 0.001f) if (flowForce.LengthSquared() > 0.001f)
{ {
Collider.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); Collider.ApplyForce(flowForce);
foreach (Limb limb in limbs) foreach (Limb limb in limbs)
{ {
if (!limb.InWater) { continue; } if (!limb.InWater) { continue; }
limb.body.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); limb.body.ApplyForce(flowForce);
} }
} }
} }
@@ -1506,7 +1506,6 @@ namespace Barotrauma
if (TorsoPosition.HasValue && MathUtils.IsValid(TorsoPosition.Value)) { height = Math.Max(height, TorsoPosition.Value); } if (TorsoPosition.HasValue && MathUtils.IsValid(TorsoPosition.Value)) { height = Math.Max(height, TorsoPosition.Value); }
Vector2 rayEnd = rayStart - new Vector2(0.0f, height); Vector2 rayEnd = rayStart - new Vector2(0.0f, height);
Vector2 onGroundRayEnd = rayStart - Vector2.UnitY * (Collider.height * 0.5f + Collider.radius + ColliderHeightFromFloor * 1.2f);
Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom()); Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom());
Fixture standOnFloorFixture = null; Fixture standOnFloorFixture = null;
@@ -1587,7 +1586,25 @@ namespace Barotrauma
if (closestFraction == 1) //raycast didn't hit anything if (closestFraction == 1) //raycast didn't hit anything
{ {
floorNormal = Vector2.UnitY; floorNormal = Vector2.UnitY;
return (currentHull == null) ? -1000.0f : ConvertUnits.ToSimUnits(currentHull.Rect.Y - currentHull.Rect.Height); if (CurrentHull == null)
{
return -1000.0f;
}
else
{
float hullBottom = currentHull.Rect.Y - currentHull.Rect.Height;
//check if there's a connected hull below
foreach (var gap in currentHull.ConnectedGaps)
{
if (!gap.IsRoomToRoom || gap.Open < 1.0f || gap.ConnectedDoor != null || gap.IsHorizontal) { continue; }
if (WorldPosition.X > gap.WorldRect.X && WorldPosition.X < gap.WorldRect.Right && gap.WorldPosition.Y < WorldPosition.Y)
{
var lowerHull = gap.linkedTo[0] == currentHull ? gap.linkedTo[1] : gap.linkedTo[0];
hullBottom = Math.Min(hullBottom, lowerHull.Rect.Y - lowerHull.Rect.Height);
}
}
return ConvertUnits.ToSimUnits(hullBottom);
}
} }
else else
{ {
@@ -1745,8 +1745,12 @@ namespace Barotrauma
} }
else if (IsKeyDown(InputType.Attack)) else if (IsKeyDown(InputType.Attack))
{ {
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Controlled != this)
{ {
if ((currentAttackTarget.DamageTarget as Entity)?.Removed ?? false)
{
currentAttackTarget = default(AttackTargetData);
}
currentAttackTarget.AttackLimb?.UpdateAttack(deltaTime, currentAttackTarget.AttackPos, currentAttackTarget.DamageTarget, out _); currentAttackTarget.AttackLimb?.UpdateAttack(deltaTime, currentAttackTarget.AttackPos, currentAttackTarget.DamageTarget, out _);
} }
else if (IsPlayer) else if (IsPlayer)
@@ -3670,16 +3674,14 @@ namespace Barotrauma
{ {
float attackerSkillLevel = attacker.GetSkillLevel("weapons"); float attackerSkillLevel = attacker.GetSkillLevel("weapons");
attacker.Info?.IncreaseSkillLevel("weapons", attacker.Info?.IncreaseSkillLevel("weapons",
-healthChange * SkillSettings.Current.SkillIncreasePerHostileDamage / Math.Max(attackerSkillLevel, 1.0f), -healthChange * SkillSettings.Current.SkillIncreasePerHostileDamage / Math.Max(attackerSkillLevel, 1.0f));
attacker.Position + Vector2.UnitY * 100.0f);
} }
} }
else if (healthChange > 0.0f) else if (healthChange > 0.0f)
{ {
float attackerSkillLevel = attacker.GetSkillLevel("medical"); float attackerSkillLevel = attacker.GetSkillLevel("medical");
attacker.Info?.IncreaseSkillLevel("medical", attacker.Info?.IncreaseSkillLevel("medical",
healthChange * SkillSettings.Current.SkillIncreasePerFriendlyHealed / Math.Max(attackerSkillLevel, 1.0f), healthChange * SkillSettings.Current.SkillIncreasePerFriendlyHealed / Math.Max(attackerSkillLevel, 1.0f));
attacker.Position + Vector2.UnitY * 100.0f);
} }
} }
@@ -3927,7 +3929,7 @@ namespace Barotrauma
} }
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log); partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log);
public void Revive() public void Revive(bool removeAllAfflictions = true)
{ {
if (Removed) if (Removed)
{ {
@@ -3938,7 +3940,14 @@ namespace Barotrauma
aiTarget?.Remove(); aiTarget?.Remove();
aiTarget = new AITarget(this); aiTarget = new AITarget(this);
CharacterHealth.RemoveAllAfflictions(); if (removeAllAfflictions)
{
CharacterHealth.RemoveAllAfflictions();
}
else
{
CharacterHealth.RemoveNegativeAfflictions();
}
SetAllDamage(0.0f, 0.0f, 0.0f); SetAllDamage(0.0f, 0.0f, 0.0f);
Oxygen = 100.0f; Oxygen = 100.0f;
Bloodloss = 0.0f; Bloodloss = 0.0f;
@@ -4284,12 +4293,8 @@ namespace Barotrauma
} }
if (Submarine == null && target.Submarine != null) if (Submarine == null && target.Submarine != null)
{ {
if (AIController == null || !(AIController.SteeringManager is IndoorsSteeringManager)) // outside and targeting inside
{ targetPos += target.Submarine.SimPosition;
// outside and targeting inside
// doesn't work with inside steering
targetPos += target.Submarine.SimPosition;
}
} }
else if (Submarine != null && target.Submarine == null) else if (Submarine != null && target.Submarine == null)
{ {
@@ -4376,14 +4381,14 @@ namespace Barotrauma
public bool GiveTalent(TalentPrefab talentPrefab, bool addingFirstTime = true) public bool GiveTalent(TalentPrefab talentPrefab, bool addingFirstTime = true)
{ {
if (addingFirstTime) if (info == null) { return false; }
{ info.UnlockedTalents.Add(talentPrefab.Identifier);
if (!info.UnlockedTalents.Add(talentPrefab.Identifier)) { return false; } if (characterTalents.Any(t => t.Prefab == talentPrefab)) { return false; }
}
CharacterTalent characterTalent = new CharacterTalent(talentPrefab, this); CharacterTalent characterTalent = new CharacterTalent(talentPrefab, this);
characterTalent.ActivateTalent(addingFirstTime); characterTalent.ActivateTalent(addingFirstTime);
characterTalents.Add(characterTalent); characterTalents.Add(characterTalent);
characterTalent.AddedThisRound = addingFirstTime;
#if SERVER #if SERVER
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents }); GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
@@ -1162,7 +1162,7 @@ namespace Barotrauma
return (int)(salary * Job.Prefab.PriceMultiplier); return (int)(salary * Job.Prefab.PriceMultiplier);
} }
public void IncreaseSkillLevel(string skillIdentifier, float increase, Vector2 pos, bool gainedFromApprenticeship = false) public void IncreaseSkillLevel(string skillIdentifier, float increase, bool gainedFromApprenticeship = false)
{ {
if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) || Character == null) { return; } if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) || Character == null) { return; }
@@ -1190,10 +1190,10 @@ namespace Barotrauma
} }
} }
OnSkillChanged(skillIdentifier, prevLevel, newLevel, pos); OnSkillChanged(skillIdentifier, prevLevel, newLevel);
} }
public void SetSkillLevel(string skillIdentifier, float level, Vector2 pos) public void SetSkillLevel(string skillIdentifier, float level)
{ {
if (Job == null) { return; } if (Job == null) { return; }
@@ -1201,19 +1201,19 @@ namespace Barotrauma
if (skill == null) if (skill == null)
{ {
Job.Skills.Add(new Skill(skillIdentifier, level)); Job.Skills.Add(new Skill(skillIdentifier, level));
OnSkillChanged(skillIdentifier, 0.0f, level, pos); OnSkillChanged(skillIdentifier, 0.0f, level);
} }
else else
{ {
float prevLevel = skill.Level; float prevLevel = skill.Level;
skill.Level = level; skill.Level = level;
OnSkillChanged(skillIdentifier, prevLevel, skill.Level, pos); OnSkillChanged(skillIdentifier, prevLevel, skill.Level);
} }
} }
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos); partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel);
public void GiveExperience(int amount, float popupOffset = 0f, bool isMissionExperience = false) public void GiveExperience(int amount, bool isMissionExperience = false)
{ {
int prevAmount = ExperiencePoints; int prevAmount = ExperiencePoints;
@@ -1229,7 +1229,7 @@ namespace Barotrauma
if (amount < 0) { return; } if (amount < 0) { return; }
ExperiencePoints += amount; ExperiencePoints += amount;
OnExperienceChanged(prevAmount, ExperiencePoints, Character.Position + Vector2.UnitY * (150.0f + popupOffset)); OnExperienceChanged(prevAmount, ExperiencePoints);
} }
public void SetExperience(int newExperience) public void SetExperience(int newExperience)
@@ -1238,7 +1238,7 @@ namespace Barotrauma
int prevAmount = ExperiencePoints; int prevAmount = ExperiencePoints;
ExperiencePoints = newExperience; ExperiencePoints = newExperience;
OnExperienceChanged(prevAmount, ExperiencePoints, Character.Position + Vector2.UnitY * 150.0f); OnExperienceChanged(prevAmount, ExperiencePoints);
} }
const int BaseExperienceRequired = 50; const int BaseExperienceRequired = 50;
@@ -1295,7 +1295,7 @@ namespace Barotrauma
return BaseExperienceRequired + AddedExperienceRequiredPerLevel * level; return BaseExperienceRequired + AddedExperienceRequiredPerLevel * level;
} }
partial void OnExperienceChanged(int prevAmount, int newAmount, Vector2 textPopupPos); partial void OnExperienceChanged(int prevAmount, int newAmount);
public void Rename(string newName) public void Rename(string newName)
{ {
@@ -206,7 +206,11 @@ namespace Barotrauma
public float Stun public float Stun
{ {
get { return stunAffliction.Strength; } get { return stunAffliction.Strength; }
set { stunAffliction.Strength = MathHelper.Clamp(value, 0.0f, stunAffliction.Prefab.MaxStrength); } set
{
if (Character.GodMode) { return; }
stunAffliction.Strength = MathHelper.Clamp(value, 0.0f, stunAffliction.Prefab.MaxStrength);
}
} }
public float StunTimer { get; private set; } public float StunTimer { get; private set; }
@@ -629,6 +633,22 @@ namespace Barotrauma
CalculateVitality(); CalculateVitality();
} }
public void RemoveNegativeAfflictions()
{
// also don't remove genetic effects, even if they're negative
foreach (LimbHealth limbHealth in limbHealths)
{
limbHealth.Afflictions.RemoveAll(a => !a.Prefab.IsBuff && a.Prefab.AfflictionType != "geneticmaterialbuff" && a.Prefab.AfflictionType != "geneticmaterialdebuff");
}
afflictions.RemoveAll(a => !irremovableAfflictions.Contains(a) && !a.Prefab.IsBuff && a.Prefab.AfflictionType != "geneticmaterialbuff" && a.Prefab.AfflictionType != "geneticmaterialdebuff");
foreach (Affliction affliction in irremovableAfflictions)
{
affliction.Strength = 0.0f;
}
CalculateVitality();
}
private void AddLimbAffliction(Limb limb, Affliction newAffliction, bool allowStacking = true) private void AddLimbAffliction(Limb limb, Affliction newAffliction, bool allowStacking = true)
{ {
if (!newAffliction.Prefab.LimbSpecific || limb == null) { return; } if (!newAffliction.Prefab.LimbSpecific || limb == null) { return; }
@@ -68,9 +68,20 @@ namespace Barotrauma
} }
} }
public class PreviewItem
{
public readonly string ItemIdentifier;
public readonly bool ShowPreview;
public PreviewItem(string itemIdentifier, bool showPreview)
{
ItemIdentifier = itemIdentifier;
ShowPreview = showPreview;
}
}
public readonly Dictionary<int, XElement> ItemSets = new Dictionary<int, XElement>(); public readonly Dictionary<int, XElement> ItemSets = new Dictionary<int, XElement>();
public readonly Dictionary<int, List<string>> ItemIdentifiers = new Dictionary<int, List<string>>(); public readonly Dictionary<int, List<PreviewItem>> PreviewItems = new Dictionary<int, List<PreviewItem>>();
public readonly Dictionary<int, Dictionary<string, bool>> ShowItemPreview = new Dictionary<int, Dictionary<string, bool>>();
public readonly List<SkillPrefab> Skills = new List<SkillPrefab>(); public readonly List<SkillPrefab> Skills = new List<SkillPrefab>();
public readonly List<AutonomousObjective> AutonomousObjectives = new List<AutonomousObjective>(); public readonly List<AutonomousObjective> AutonomousObjectives = new List<AutonomousObjective>();
public readonly List<string> AppropriateOrders = new List<string>(); public readonly List<string> AppropriateOrders = new List<string>();
@@ -220,8 +231,7 @@ namespace Barotrauma
{ {
case "itemset": case "itemset":
ItemSets.Add(variant, subElement); ItemSets.Add(variant, subElement);
ItemIdentifiers[variant] = new List<string>(); PreviewItems[variant] = new List<PreviewItem>();
ShowItemPreview[variant] = new Dictionary<string, bool>();
loadItemIdentifiers(subElement, variant); loadItemIdentifiers(subElement, variant);
variant++; variant++;
break; break;
@@ -264,8 +274,7 @@ namespace Barotrauma
} }
else else
{ {
ItemIdentifiers[variant].Add(itemIdentifier); PreviewItems[variant].Add(new PreviewItem(itemIdentifier, itemElement.GetAttributeBool("showpreview", true)));
ShowItemPreview[variant][itemIdentifier] = itemElement.GetAttributeBool("showpreview", true);
} }
loadItemIdentifiers(itemElement, variant); loadItemIdentifiers(itemElement, variant);
} }
@@ -759,6 +759,10 @@ namespace Barotrauma
appliedDamageModifiers.AddRange(tempModifiers); appliedDamageModifiers.AddRange(tempModifiers);
} }
var result = new AttackResult(afflictionsCopy, this, appliedDamageModifiers); var result = new AttackResult(afflictionsCopy, this, appliedDamageModifiers);
if (result.Afflictions.None())
{
playSound = false;
}
AddDamageProjSpecific(playSound, result); AddDamageProjSpecific(playSound, result);
float bleedingDamage = 0; float bleedingDamage = 0;
@@ -11,12 +11,12 @@ namespace Barotrauma
{ {
public enum AnimationType public enum AnimationType
{ {
NotDefined, NotDefined = 0,
Walk, Walk = 1,
Run, Run = 2,
SwimSlow, Crouch = 3,
SwimFast, SwimSlow = 4,
Crouch SwimFast = 5
} }
abstract class GroundedMovementParams : AnimationParams abstract class GroundedMovementParams : AnimationParams
@@ -597,7 +597,7 @@ namespace Barotrauma
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)] [Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
public float SpriteOrientation { get; set; } public float SpriteOrientation { get; set; }
[Serialize(LimbType.None, true, description: "If set, the limb sprite will use the same sprite depth as the specified limb. Generally only useful for limbs that get added on the ragdoll on the fly (e.g. extra limbs added via gene splicing).")] [Serialize(LimbType.None, true, description: "If set, the limb sprite will use the same sprite depth as the specified limb. Generally only useful for limbs that get added on the ragdoll on the fly (e.g. extra limbs added via gene splicing).")]
public LimbType InheritLimbDepth { get; set; } public LimbType InheritLimbDepth { get; set; }
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)] [Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
@@ -12,9 +12,12 @@ namespace Barotrauma.Abilities
Ranged = 2 Ranged = 2
}; };
private readonly bool hittingCountsAsAiming;
private readonly WeaponType weapontype; private readonly WeaponType weapontype;
public AbilityConditionIsAiming(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) public AbilityConditionIsAiming(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{ {
hittingCountsAsAiming = conditionElement.GetAttributeBool("hittingcountsasaiming", false);
switch (conditionElement.GetAttributeString("weapontype", "")) switch (conditionElement.GetAttributeString("weapontype", ""))
{ {
case "melee": case "melee":
@@ -28,7 +31,6 @@ namespace Barotrauma.Abilities
protected override bool MatchesConditionSpecific() protected override bool MatchesConditionSpecific()
{ {
bool aimingCorrectItem = false;
if (character.AnimController is HumanoidAnimController animController) if (character.AnimController is HumanoidAnimController animController)
{ {
foreach (Item item in character.HeldItems) foreach (Item item in character.HeldItems)
@@ -36,19 +38,23 @@ namespace Barotrauma.Abilities
switch (weapontype) switch (weapontype)
{ {
case WeaponType.Melee: case WeaponType.Melee:
aimingCorrectItem |= item.GetComponent<MeleeWeapon>() != null && animController.IsAimingMelee; var meleeWeapon = item.GetComponent<MeleeWeapon>();
if (meleeWeapon != null)
{
if (animController.IsAimingMelee || (meleeWeapon.Hitting && hittingCountsAsAiming)) { return true; }
}
break; break;
case WeaponType.Ranged: case WeaponType.Ranged:
aimingCorrectItem |= item.GetComponent<RangedWeapon>() != null && animController.IsAiming; if (animController.IsAiming && item.GetComponent<RangedWeapon>() != null) { return true; }
break; break;
default: default:
aimingCorrectItem |= animController.IsAiming || animController.IsAimingMelee; if (animController.IsAiming || animController.IsAimingMelee) { return true; }
break; break;
} }
} }
} }
return aimingCorrectItem; return false;
} }
} }
} }
@@ -16,7 +16,7 @@ namespace Barotrauma.Abilities
{ {
if ((abilityObject as IAbilityValue)?.Value is float skillIncrease) if ((abilityObject as IAbilityValue)?.Value is float skillIncrease)
{ {
Character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, Character.Position + Vector2.UnitY * 175.0f); Character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease);
} }
else else
{ {
@@ -1,21 +0,0 @@
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityGiveMissionCount : CharacterAbility
{
private readonly int amount;
public CharacterAbilityGiveMissionCount(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
amount = abilityElement.GetAttributeInt("amount", 0);
}
public override void InitializeAbility(bool addingFirstTime)
{
if (!addingFirstTime) { return; }
if (!(GameMain.GameSession?.Campaign is CampaignMode campaign)) { return; }
campaign.Settings.AddedMissionCount += amount;
}
}
}
@@ -49,13 +49,12 @@ namespace Barotrauma.Abilities
{ {
var skill = character.Info?.Job?.Skills?.GetRandom(); var skill = character.Info?.Job?.Skills?.GetRandom();
if (skill == null) { return; } if (skill == null) { return; }
character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f); character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease);
} }
else else
{ {
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f); character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease);
} }
} }
} }
} }
@@ -13,7 +13,7 @@ namespace Barotrauma.Abilities
private void ApplyEffectSpecific() private void ApplyEffectSpecific()
{ {
Character.Revive(); Character.Revive(removeAllAfflictions: false);
} }
protected override void ApplyEffect() protected override void ApplyEffect()
@@ -21,6 +21,7 @@ namespace Barotrauma.Abilities
{ {
foreach (var talent in talentOption.Talents) foreach (var talent in talentOption.Talents)
{ {
if (talent == CharacterTalent.Prefab) { continue; }
Character.GiveTalent(talent); Character.GiveTalent(talent);
} }
} }
@@ -14,7 +14,7 @@ namespace Barotrauma.Abilities
{ {
if (abilityObject is AbilitySkillGain abilitySkillGain && !abilitySkillGain.GainedFromApprenticeship && abilitySkillGain.Character != Character) if (abilityObject is AbilitySkillGain abilitySkillGain && !abilitySkillGain.GainedFromApprenticeship && abilitySkillGain.Character != Character)
{ {
Character.Info?.IncreaseSkillLevel(abilitySkillGain.String, 1.0f, Character.Position + Vector2.UnitY * 175.0f, gainedFromApprenticeship: true); Character.Info?.IncreaseSkillLevel(abilitySkillGain.String, 1.0f, gainedFromApprenticeship: true);
} }
} }
} }
@@ -18,7 +18,7 @@ namespace Barotrauma.Abilities
if (skillIdentifier != lastSkillIdentifier) if (skillIdentifier != lastSkillIdentifier)
{ {
lastSkillIdentifier = skillIdentifier; lastSkillIdentifier = skillIdentifier;
Character.Info?.IncreaseSkillLevel(skillIdentifier, 1.0f, Character.Position + Vector2.UnitY * 175.0f); Character.Info?.IncreaseSkillLevel(skillIdentifier, 1.0f);
} }
} }
} }
@@ -13,6 +13,8 @@ namespace Barotrauma
public readonly TalentPrefab Prefab; public readonly TalentPrefab Prefab;
public bool AddedThisRound = true;
private readonly Dictionary<AbilityEffectType, List<CharacterAbilityGroupEffect>> characterAbilityGroupEffectDictionary = new Dictionary<AbilityEffectType, List<CharacterAbilityGroupEffect>>(); private readonly Dictionary<AbilityEffectType, List<CharacterAbilityGroupEffect>> characterAbilityGroupEffectDictionary = new Dictionary<AbilityEffectType, List<CharacterAbilityGroupEffect>>();
private readonly List<CharacterAbilityGroupInterval> characterAbilityGroupIntervals = new List<CharacterAbilityGroupInterval>(); private readonly List<CharacterAbilityGroupInterval> characterAbilityGroupIntervals = new List<CharacterAbilityGroupInterval>();
@@ -809,13 +809,13 @@ namespace Barotrauma
{ {
foreach (Skill skill in character.Info.Job.Skills) foreach (Skill skill in character.Info.Job.Skills)
{ {
character.Info.SetSkillLevel(skill.Identifier, level, character.WorldPosition); character.Info.SetSkillLevel(skill.Identifier, level);
} }
NewMessage($"Set all {character.Name}'s skills to {level}", Color.Green); NewMessage($"Set all {character.Name}'s skills to {level}", Color.Green);
} }
else else
{ {
character.Info.SetSkillLevel(skillIdentifier, level, character.WorldPosition); character.Info.SetSkillLevel(skillIdentifier, level);
NewMessage($"Set {character.Name}'s {skillIdentifier} level to {level}", Color.Green); NewMessage($"Set {character.Name}'s {skillIdentifier} level to {level}", Color.Green);
} }
} }
@@ -839,7 +839,7 @@ namespace Barotrauma
NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White); NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White);
}, isCheat: true)); }, isCheat: true));
commands.Add(new Command("givetalent", "give [player] testing [talent]", (string[] args) => commands.Add(new Command("givetalent", "givetalent [talent] [player]: give the talent to the specified character. If the character argument is omitted, the talent is given to the controlled character.", (string[] args) =>
{ {
if (args.Length == 0) { return; } if (args.Length == 0) { return; }
var character = args.Length >= 2 ? FindMatchingCharacter(args.Skip(1).ToArray()) : Character.Controlled; var character = args.Length >= 2 ? FindMatchingCharacter(args.Skip(1).ToArray()) : Character.Controlled;
@@ -1886,13 +1886,15 @@ namespace Barotrauma
} }
return; return;
} }
#if !DEBUG
if (!IsCommandPermitted(splitCommand[0].ToLowerInvariant(), GameMain.Client)) if (!IsCommandPermitted(splitCommand[0].ToLowerInvariant(), GameMain.Client))
{ {
#if DEBUG
AddWarning("You're not permitted to use the command \"{matchingCommand.Name}\". Executing the command anyway because this is a debug build.");
#else
ThrowError("You're not permitted to use the command \"" + splitCommand[0].ToLowerInvariant() + "\"!"); ThrowError("You're not permitted to use the command \"" + splitCommand[0].ToLowerInvariant() + "\"!");
return; return;
}
#endif #endif
}
} }
#endif #endif
@@ -119,6 +119,7 @@
MissionMoneyGainMultiplier, MissionMoneyGainMultiplier,
ExperienceGainMultiplier, ExperienceGainMultiplier,
MissionExperienceGainMultiplier, MissionExperienceGainMultiplier,
ExtraMissionCount,
ExtraSpecialSalesCount, ExtraSpecialSalesCount,
ApplyTreatmentsOnSelfFraction, ApplyTreatmentsOnSelfFraction,
MaxAttachableCount, MaxAttachableCount,
@@ -40,7 +40,7 @@ namespace Barotrauma
var targets = ParentEvent.GetTargets(TargetTag).Where(e => e is Character).Select(e => e as Character); var targets = ParentEvent.GetTargets(TargetTag).Where(e => e is Character).Select(e => e as Character);
foreach (var target in targets) foreach (var target in targets)
{ {
target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount, target.Position + Vector2.UnitY * 150.0f); target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount);
} }
isFinished = true; isFinished = true;
} }
@@ -165,12 +165,12 @@ namespace Barotrauma
public override void End() public override void End()
{ {
if (AllTargetsEliminated()) if (State == 2)
{ {
GiveReward(); GiveReward();
completed = true; completed = true;
} }
failed = !completed && state > 0; failed = !completed && State > 0;
} }
} }
} }
@@ -18,7 +18,7 @@ namespace Barotrauma
private readonly int targetsToScan; private readonly int targetsToScan;
private readonly Dictionary<WayPoint, bool> scanTargets = new Dictionary<WayPoint, bool>(); private readonly Dictionary<WayPoint, bool> scanTargets = new Dictionary<WayPoint, bool>();
private readonly HashSet<WayPoint> newTargetsScanned = new HashSet<WayPoint>(); private readonly HashSet<WayPoint> newTargetsScanned = new HashSet<WayPoint>();
private readonly float minTargetDistance, minTargetDistanceSquared; private readonly float minTargetDistance;
private Ruin TargetRuin { get; set; } private Ruin TargetRuin { get; set; }
@@ -58,7 +58,6 @@ namespace Barotrauma
itemConfig = prefab.ConfigElement.Element("Items"); itemConfig = prefab.ConfigElement.Element("Items");
targetsToScan = prefab.ConfigElement.GetAttributeInt("targets", 1); targetsToScan = prefab.ConfigElement.GetAttributeInt("targets", 1);
minTargetDistance = prefab.ConfigElement.GetAttributeFloat("mintargetdistance", 0.0f); minTargetDistance = prefab.ConfigElement.GetAttributeFloat("mintargetdistance", 0.0f);
minTargetDistanceSquared = minTargetDistance * minTargetDistance;
} }
protected override void StartMissionSpecific(Level level) protected override void StartMissionSpecific(Level level)
@@ -86,28 +85,57 @@ namespace Barotrauma
return; return;
} }
var availableWaypoints = TargetRuin.Submarine.GetWaypoints(false); var ruinWaypoints = TargetRuin.Submarine.GetWaypoints(false);
availableWaypoints.RemoveAll(wp => wp.CurrentHull == null); ruinWaypoints.RemoveAll(wp => wp.CurrentHull == null);
if (availableWaypoints.Count < targetsToScan) if (ruinWaypoints.Count < targetsToScan)
{ {
DebugConsole.ThrowError($"Failed to initialize a Scan mission: target ruin has less waypoints than required as scan targets ({availableWaypoints.Count} < {targetsToScan})"); DebugConsole.ThrowError($"Failed to initialize a Scan mission: target ruin has less waypoints than required as scan targets ({ruinWaypoints.Count} < {targetsToScan})");
return; return;
} }
for (int i = 0; i < targetsToScan; i++) var availableWaypoints = new List<WayPoint>();
float minTargetDistanceSquared = minTargetDistance * minTargetDistance;
for (int tries = 0; tries < 15; tries++)
{ {
var selectedWaypoint = availableWaypoints.GetRandom(randSync: Rand.RandSync.Server); scanTargets.Clear();
scanTargets.Add(selectedWaypoint, false); availableWaypoints.Clear();
availableWaypoints.Remove(selectedWaypoint); availableWaypoints.AddRange(ruinWaypoints);
if (i < (targetsToScan - 1)) for (int i = 0; i < targetsToScan; i++)
{ {
availableWaypoints.RemoveAll(wp => wp.CurrentHull == selectedWaypoint.CurrentHull); var selectedWaypoint = availableWaypoints.GetRandom(randSync: Rand.RandSync.Server);
availableWaypoints.RemoveAll(wp => Vector2.DistanceSquared(wp.WorldPosition, selectedWaypoint.WorldPosition) < minTargetDistanceSquared); scanTargets.Add(selectedWaypoint, false);
if (availableWaypoints.None()) availableWaypoints.Remove(selectedWaypoint);
if (i < (targetsToScan - 1))
{ {
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets available to reach the required scan target count (current targets: {scanTargets.Count}, required targets: {targetsToScan})"); availableWaypoints.RemoveAll(wp => wp.CurrentHull == selectedWaypoint.CurrentHull);
break; availableWaypoints.RemoveAll(wp => Vector2.DistanceSquared(wp.WorldPosition, selectedWaypoint.WorldPosition) < minTargetDistanceSquared);
if (availableWaypoints.None())
{
#if DEBUG
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets available on try #{tries + 1} to reach the required scan target count (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
#endif
break;
}
} }
} }
if (scanTargets.Count >= targetsToScan)
{
#if DEBUG
DebugConsole.NewMessage($"Successfully initialized a Scan mission: targets set on try #{tries + 1}", Color.Green);
#endif
break;
}
if ((tries + 1) % 5 == 0)
{
float reducedMinTargetDistance = (1.0f - (((tries + 1) / 5) * 0.1f)) * minTargetDistance;
minTargetDistanceSquared = reducedMinTargetDistance * reducedMinTargetDistance;
#if DEBUG
DebugConsole.NewMessage($"Reducing minimum distance between Scan mission targets (new min: {reducedMinTargetDistance}) to reach the required target count", Color.Yellow);
#endif
}
}
if (scanTargets.Count < targetsToScan)
{
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
} }
} }
@@ -218,7 +246,7 @@ namespace Barotrauma
public override void End() public override void End()
{ {
if (AllTargetsScanned && AllScannersReturned()) if (State == 2 && AllScannersReturned())
{ {
GiveReward(); GiveReward();
completed = true; completed = true;
@@ -489,6 +489,11 @@ namespace Barotrauma
continue; continue;
} }
} }
if (orderInfo.Order.TargetEntity == null || (orderInfo.Order.IsIgnoreOrder && ignoreTarget == null))
{
// The order target doesn't exist anymore, just discard the loaded order
continue;
}
if (ignoreTarget != null) if (ignoreTarget != null)
{ {
ignoreTarget.OrderedToBeIgnored = true; ignoreTarget.OrderedToBeIgnored = true;
@@ -51,7 +51,7 @@ namespace Barotrauma
if (reputationChange > 0f) if (reputationChange > 0f)
{ {
float reputationGainMultiplier = 1f; float reputationGainMultiplier = 1f;
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == CharacterTeamType.Team1)) foreach (Character character in GameSession.GetSessionCrewCharacters())
{ {
reputationGainMultiplier += character.GetStatValue(StatTypes.ReputationGainMultiplier); reputationGainMultiplier += character.GetStatValue(StatTypes.ReputationGainMultiplier);
} }
@@ -17,9 +17,7 @@ namespace Barotrauma
public static CampaignSettings Unsure = Empty; public static CampaignSettings Unsure = Empty;
public bool RadiationEnabled { get; set; } public bool RadiationEnabled { get; set; }
public int AddedMissionCount { get; set; } public int TotalMaxMissionCount => MaxMissionCount + GetAddedMissionCount();
public int TotalMaxMissionCount => MaxMissionCount + AddedMissionCount;
private int maxMissionCount; private int maxMissionCount;
public int MaxMissionCount public int MaxMissionCount
@@ -36,7 +34,6 @@ namespace Barotrauma
{ {
maxMissionCount = DefaultMaxMissionCount; maxMissionCount = DefaultMaxMissionCount;
RadiationEnabled = inc.ReadBoolean(); RadiationEnabled = inc.ReadBoolean();
AddedMissionCount = inc.ReadInt32();
MaxMissionCount = inc.ReadInt32(); MaxMissionCount = inc.ReadInt32();
} }
@@ -44,7 +41,6 @@ namespace Barotrauma
{ {
maxMissionCount = DefaultMaxMissionCount; maxMissionCount = DefaultMaxMissionCount;
RadiationEnabled = element.GetAttributeBool(nameof(RadiationEnabled).ToLowerInvariant(), true); RadiationEnabled = element.GetAttributeBool(nameof(RadiationEnabled).ToLowerInvariant(), true);
AddedMissionCount = element.GetAttributeInt(nameof(AddedMissionCount).ToLowerInvariant(), 0);
MaxMissionCount = element.GetAttributeInt(nameof(MaxMissionCount).ToLowerInvariant(), DefaultMaxMissionCount); MaxMissionCount = element.GetAttributeInt(nameof(MaxMissionCount).ToLowerInvariant(), DefaultMaxMissionCount);
} }
@@ -52,12 +48,21 @@ namespace Barotrauma
{ {
msg.Write(RadiationEnabled); msg.Write(RadiationEnabled);
msg.Write(MaxMissionCount); msg.Write(MaxMissionCount);
msg.Write(AddedMissionCount); }
public int GetAddedMissionCount()
{
int count = 0;
foreach (Character character in GameSession.GetSessionCrewCharacters())
{
count += (int)character.GetStatValue(StatTypes.ExtraMissionCount);
}
return count;
} }
public XElement Save() public XElement Save()
{ {
return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLowerInvariant(), RadiationEnabled), new XAttribute(nameof(MaxMissionCount).ToLowerInvariant(), MaxMissionCount), new XAttribute(nameof(AddedMissionCount).ToLowerInvariant(), AddedMissionCount)); return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLowerInvariant(), RadiationEnabled), new XAttribute(nameof(MaxMissionCount).ToLowerInvariant(), MaxMissionCount));
} }
} }
@@ -661,10 +661,10 @@ namespace Barotrauma
public static IEnumerable<Character> GetSessionCrewCharacters() public static IEnumerable<Character> GetSessionCrewCharacters()
{ {
#if SERVER #if SERVER
return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null); return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead);
#else #else
if (GameMain.GameSession == null) { return Enumerable.Empty<Character>(); } if (GameMain.GameSession == null) { return Enumerable.Empty<Character>(); }
return GameMain.GameSession.CrewManager.GetCharacters().Where(c => c?.Info != null); return GameMain.GameSession.CrewManager.GetCharacters().Where(c => c?.Info != null && !c.IsDead);
#endif #endif
} }
@@ -49,13 +49,13 @@ namespace Barotrauma.Items.Components
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the spawn area from the center of the item")] [Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the spawn area from the center of the item")]
public Vector2 SpawnAreaOffset { get; set; } public Vector2 SpawnAreaOffset { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", true, "Time range between spawn attempts in seconds")] [Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", true, "Time range between spawn attempts in seconds. Set both to a negative value to disable automatic spawning.")]
public Vector2 SpawnTimerRange { get; set; } public Vector2 SpawnTimerRange { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", true, "Minumum and maximum amount of items or creatures to spawn in one attempt")] [Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", true, "Minumum and maximum amount of items or creatures to spawn in one attempt")]
public Vector2 SpawnAmountRange { get; set; } public Vector2 SpawnAmountRange { get; set; }
[Editable(MinValueInt = 0), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned")] [Editable(MinValueInt = int.MinValue, MaxValueInt = int.MaxValue), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned")]
public int MaximumAmount { get; set; } public int MaximumAmount { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")] [Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
@@ -110,7 +110,12 @@ namespace Barotrauma.Items.Components
if (GameMain.NetworkMember is { IsClient: true }) { return; } if (GameMain.NetworkMember is { IsClient: true }) { return; }
SpawnTimerGoal ??= Rand.Range(Math.Min(SpawnTimerRange.X, SpawnTimerRange.Y), Math.Max(SpawnTimerRange.X, SpawnTimerRange.Y), Rand.RandSync.Unsynced); float minTime = Math.Min(SpawnTimerRange.X, SpawnTimerRange.Y),
maxTime = Math.Max(SpawnTimerRange.X, SpawnTimerRange.Y);
if (minTime < 0 && maxTime < 0) { return; }
SpawnTimerGoal ??= Rand.Range(minTime, maxTime, Rand.RandSync.Unsynced);
SpawnTimer += deltaTime; SpawnTimer += deltaTime;
@@ -120,12 +125,12 @@ namespace Barotrauma.Items.Components
SpawnTimerGoal = null; SpawnTimerGoal = null;
SpawnTimer = 0; SpawnTimer = 0;
} }
} }
public override void ReceiveSignal(Signal signal, Connection connection) public override void ReceiveSignal(Signal signal, Connection connection)
{ {
bool isNonZero = signal.value != "0"; bool isNonZero = signal.value != "0";
bool isClient = GameMain.NetworkMember is { IsClient: true };
switch (connection.Name) switch (connection.Name)
{ {
@@ -135,6 +140,9 @@ namespace Barotrauma.Items.Components
case "toggle" when isNonZero: case "toggle" when isNonZero:
CanSpawn = !CanSpawn; CanSpawn = !CanSpawn;
break; break;
case "trigger_in" when isNonZero && !isClient:
Spawn();
break;
} }
} }
@@ -163,6 +171,8 @@ namespace Barotrauma.Items.Components
} }
} }
if (MaximumAmount < 0) { return true; }
int amount; int amount;
if (!string.IsNullOrWhiteSpace(SpeciesName)) if (!string.IsNullOrWhiteSpace(SpeciesName))
@@ -59,6 +59,7 @@ namespace Barotrauma.Items.Components
[Editable, Serialize("3.0, -1.0", false)] [Editable, Serialize("3.0, -1.0", false)]
public Vector2 SwingForce { get; set; } public Vector2 SwingForce { get; set; }
public bool Hitting { get { return hitting; } }
/// <summary> /// <summary>
/// Defines items that boost the weapon functionality, like battery cell for stun batons. /// Defines items that boost the weapon functionality, like battery cell for stun batons.
@@ -66,18 +66,18 @@ namespace Barotrauma.Items.Components
foreach (Limb limb in character.AnimController.Limbs) foreach (Limb limb in character.AnimController.Limbs)
{ {
if (limb.WearingItems.Find(w => w.WearableComponent.Item == item) == null) { continue; } if (limb.WearingItems.Find(w => w.WearableComponent.Item == item) == null) { continue; }
limb.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); limb.body.ApplyForce(propulsion);
} }
character.AnimController.Collider.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); character.AnimController.Collider.ApplyForce(propulsion);
if (character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand)) if (character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand))
{ {
character.AnimController.GetLimb(LimbType.RightHand)?.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); character.AnimController.GetLimb(LimbType.RightHand)?.body.ApplyForce(propulsion);
} }
if (character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand)) if (character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand))
{ {
character.AnimController.GetLimb(LimbType.LeftHand)?.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); character.AnimController.GetLimb(LimbType.LeftHand)?.body.ApplyForce(propulsion);
} }
#if CLIENT #if CLIENT
@@ -200,7 +200,8 @@ namespace Barotrauma.Items.Components
void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems) void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems)
{ {
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health; float percentageHealth = targetItem.Condition / targetItem.MaxCondition;
if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; } if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; }
if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab)) if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab))
@@ -391,8 +391,7 @@ namespace Barotrauma.Items.Components
user.Info.IncreaseSkillLevel( user.Info.IncreaseSkillLevel(
skill.Identifier, skill.Identifier,
addedSkill, addedSkill);
user.Position + Vector2.UnitY * 150.0f);
} }
} }
@@ -11,9 +11,12 @@ namespace Barotrauma.Items.Components
private float generatedAmount; private float generatedAmount;
//key = vent, float = total volume of the hull the vent is in and the hulls connected to it //key = vent, float = total volume of the hull the vent is in and the hulls connected to it
private Dictionary<Vent, float> ventList; private List<(Vent vent, float hullVolume)> ventList;
private float totalHullVolume; private float totalHullVolume;
private float ventUpdateTimer;
const float VentUpdateInterval = 5.0f;
public float CurrFlow public float CurrFlow
{ {
@@ -64,7 +67,7 @@ namespace Barotrauma.Items.Components
//20% condition = 4% //20% condition = 4%
CurrFlow *= conditionMult * conditionMult; CurrFlow *= conditionMult * conditionMult;
UpdateVents(CurrFlow); UpdateVents(CurrFlow, deltaTime);
} }
public override void UpdateBroken(float deltaTime, Camera cam) public override void UpdateBroken(float deltaTime, Camera cam)
@@ -75,7 +78,8 @@ namespace Barotrauma.Items.Components
private void GetVents() private void GetVents()
{ {
ventList = new Dictionary<Vent, float>(); ventList ??= new List<(Vent vent, float hullVolume)>();
ventList.Clear();
foreach (MapEntity entity in item.linkedTo) foreach (MapEntity entity in item.linkedTo)
{ {
if (!(entity is Item linkedItem)) { continue; } if (!(entity is Item linkedItem)) { continue; }
@@ -83,30 +87,39 @@ namespace Barotrauma.Items.Components
Vent vent = linkedItem.GetComponent<Vent>(); Vent vent = linkedItem.GetComponent<Vent>();
if (vent?.Item.CurrentHull == null) { continue; } if (vent?.Item.CurrentHull == null) { continue; }
ventList.Add(vent, 0.0f); ventList.Add((vent, vent.Item.CurrentHull.Volume));
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: true, searchDepth: 10, ignoreClosedGaps: true)) }
{
for (int i = 0; i < ventList.Count; i++)
{
Vent vent = ventList[i].vent;
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: false, searchDepth: 5, ignoreClosedGaps: true))
{
//another vent in the connected hull -> don't add it to this vent's total hull volume
if (ventList.Any(v => v.vent != vent && v.vent.Item.CurrentHull == connectedHull)) { continue; }
totalHullVolume += connectedHull.Volume; totalHullVolume += connectedHull.Volume;
ventList[vent] += connectedHull.Volume; ventList[i] = (ventList[i].vent, ventList[i].hullVolume + connectedHull.Volume);
} }
} }
} }
private void UpdateVents(float deltaOxygen) private void UpdateVents(float deltaOxygen, float deltaTime)
{ {
if (ventList == null) if (ventList == null || ventUpdateTimer < 0.0f)
{ {
GetVents(); GetVents();
ventUpdateTimer = VentUpdateInterval;
} }
ventUpdateTimer -= deltaTime;
if (!ventList.Any() || totalHullVolume <= 0.0f) { return; } if (!ventList.Any() || totalHullVolume <= 0.0f) { return; }
foreach (KeyValuePair<Vent, float> v in ventList) foreach ((Vent vent, float hullVolume) in ventList)
{ {
if (v.Key?.Item.CurrentHull == null) { continue; } if (vent.Item.CurrentHull == null) { continue; }
v.Key.OxygenFlow = deltaOxygen * (v.Value / totalHullVolume); vent.OxygenFlow = deltaOxygen * (hullVolume / totalHullVolume);
v.Key.IsActive = true; vent.IsActive = true;
} }
} }
} }
@@ -392,8 +392,7 @@ namespace Barotrauma.Items.Components
float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f; float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f;
user.Info.IncreaseSkillLevel( user.Info.IncreaseSkillLevel(
"helm", "helm",
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime, SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime);
user.Position + Vector2.UnitY * 150.0f);
} }
private void UpdateAutoPilot(float deltaTime) private void UpdateAutoPilot(float deltaTime)
@@ -340,7 +340,7 @@ namespace Barotrauma.Items.Components
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange; item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
} }
item.Drop(null); item.Drop(null, createNetworkEvent: false);
launchPos = item.SimPosition; launchPos = item.SimPosition;
@@ -35,7 +35,6 @@ namespace Barotrauma.Items.Components
public RemoteController(Item item, XElement element) public RemoteController(Item item, XElement element)
: base(item, element) : base(item, element)
{ {
DrawHudWhenEquipped = false;
} }
public override bool Select(Character character) public override bool Select(Character character)
@@ -440,8 +440,7 @@ namespace Barotrauma.Items.Components
{ {
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier); float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier, CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier,
SkillSettings.Current.SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f), SkillSettings.Current.SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f));
CurrentFixer.Position + Vector2.UnitY * 100.0f);
} }
SteamAchievementManager.OnItemRepaired(item, CurrentFixer); SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
CurrentFixer.CheckTalents(AbilityEffectType.OnRepairComplete); CurrentFixer.CheckTalents(AbilityEffectType.OnRepairComplete);
@@ -472,8 +471,7 @@ namespace Barotrauma.Items.Components
{ {
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier); float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier, CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier,
SkillSettings.Current.SkillIncreasePerSabotage / Math.Max(characterSkillLevel, 1.0f), SkillSettings.Current.SkillIncreasePerSabotage / Math.Max(characterSkillLevel, 1.0f));
CurrentFixer.Position + Vector2.UnitY * 100.0f);
} }
deteriorationTimer = 0.0f; deteriorationTimer = 0.0f;
@@ -159,6 +159,8 @@ namespace Barotrauma.Items.Components
ApplyForce(i.body); ApplyForce(i.body);
} }
} }
item.SendSignal(IsActive ? "1" : "0", "state_out");
} }
private void ApplyForce(PhysicsBody body) private void ApplyForce(PhysicsBody body)
@@ -438,8 +438,7 @@ namespace Barotrauma.Items.Components
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedOutpost)) if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedOutpost))
{ {
user.Info.IncreaseSkillLevel("weapons", user.Info.IncreaseSkillLevel("weapons",
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f), SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f));
user.Position + Vector2.UnitY * 150.0f);
} }
float rotMidDiff = MathHelper.WrapAngle(rotation - (minRotation + maxRotation) / 2.0f); float rotMidDiff = MathHelper.WrapAngle(rotation - (minRotation + maxRotation) / 2.0f);
@@ -46,6 +46,7 @@ namespace Barotrauma
public LimbType Limb { get; private set; } public LimbType Limb { get; private set; }
public bool HideLimb { get; private set; } public bool HideLimb { get; private set; }
public bool HideOtherWearables { get; private set; } public bool HideOtherWearables { get; private set; }
public bool CanBeHiddenByOtherWearables { get; private set; }
public List<WearableType> HideWearablesOfType { get; private set; } public List<WearableType> HideWearablesOfType { get; private set; }
public bool InheritLimbDepth { get; private set; } public bool InheritLimbDepth { get; private set; }
/// <summary> /// <summary>
@@ -176,6 +177,7 @@ namespace Barotrauma
Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true); Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true);
HideLimb = SourceElement.GetAttributeBool("hidelimb", false); HideLimb = SourceElement.GetAttributeBool("hidelimb", false);
HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false); HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false);
CanBeHiddenByOtherWearables = SourceElement.GetAttributeBool("canbehiddenbyotherwearables", true);
InheritLimbDepth = SourceElement.GetAttributeBool("inheritlimbdepth", true); InheritLimbDepth = SourceElement.GetAttributeBool("inheritlimbdepth", true);
var scale = SourceElement.GetAttribute("inheritscale"); var scale = SourceElement.GetAttribute("inheritscale");
if (scale != null) if (scale != null)
@@ -1803,7 +1803,7 @@ namespace Barotrauma
Vector2 drag = body.LinearVelocity * volume; Vector2 drag = body.LinearVelocity * volume;
body.ApplyForce((uplift - drag) * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); body.ApplyForce((uplift - drag) * 10.0f);
//apply simple angular drag //apply simple angular drag
body.ApplyTorque(body.AngularVelocity * volume * -0.05f); body.ApplyTorque(body.AngularVelocity * volume * -0.05f);
@@ -314,6 +314,8 @@ namespace Barotrauma
/// </summary> /// </summary>
public bool IsOverride; public bool IsOverride;
public readonly ItemPrefab VariantOf;
public XElement ConfigElement public XElement ConfigElement
{ {
get; get;
@@ -783,6 +785,7 @@ namespace Barotrauma
} }
else else
{ {
VariantOf = basePrefab;
ConfigElement = element = CreateVariantXML(element, basePrefab); ConfigElement = element = CreateVariantXML(element, basePrefab);
} }
} }
@@ -36,6 +36,8 @@ namespace Barotrauma
/// </summary> /// </summary>
public bool ExcludeBroken { get; private set; } public bool ExcludeBroken { get; private set; }
private bool allowVariants = true;
public RelationType Type public RelationType Type
{ {
get { return type; } get { return type; }
@@ -82,13 +84,13 @@ namespace Barotrauma
{ {
if (item == null) { return false; } if (item == null) { return false; }
if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { 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)); return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (allowVariants && item.Prefab.VariantOf?.Identifier == id));
} }
public bool MatchesItem(ItemPrefab itemPrefab) public bool MatchesItem(ItemPrefab itemPrefab)
{ {
if (itemPrefab == null) { return false; } if (itemPrefab == null) { return false; }
if (excludedIdentifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id))) { 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)); return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (allowVariants && itemPrefab.VariantOf?.Identifier == id));
} }
public RelatedItem(string[] identifiers, string[] excludedIdentifiers) public RelatedItem(string[] identifiers, string[] excludedIdentifiers)
@@ -168,7 +170,8 @@ namespace Barotrauma
new XAttribute("optional", IsOptional), new XAttribute("optional", IsOptional),
new XAttribute("ignoreineditor", IgnoreInEditor), new XAttribute("ignoreineditor", IgnoreInEditor),
new XAttribute("excludebroken", ExcludeBroken), new XAttribute("excludebroken", ExcludeBroken),
new XAttribute("targetslot", TargetSlot)); new XAttribute("targetslot", TargetSlot),
new XAttribute("allowvariants", allowVariants));
if (excludedIdentifiers.Length > 0) if (excludedIdentifiers.Length > 0)
{ {
@@ -231,7 +234,8 @@ namespace Barotrauma
RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers) RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers)
{ {
ExcludeBroken = element.GetAttributeBool("excludebroken", true) ExcludeBroken = element.GetAttributeBool("excludebroken", true),
allowVariants = element.GetAttributeBool("allowvariants", true)
}; };
string typeStr = element.GetAttributeString("type", ""); string typeStr = element.GetAttributeString("type", "");
if (string.IsNullOrEmpty(typeStr)) if (string.IsNullOrEmpty(typeStr))
@@ -934,7 +934,7 @@ namespace Barotrauma
foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0)) foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0))
{ {
var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f); var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f);
item.body.ApplyForce((gap.LerpedFlowForce / distance) * deltaTime, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); item.body.ApplyForce((gap.LerpedFlowForce / distance) * deltaTime);
} }
} }
@@ -1888,7 +1888,10 @@ namespace Barotrauma
{ {
for (float x = waypointArea.X + outSideWaypointInterval; x < waypointArea.Right - outSideWaypointInterval; x += outSideWaypointInterval) for (float x = waypointArea.X + outSideWaypointInterval; x < waypointArea.Right - outSideWaypointInterval; x += outSideWaypointInterval)
{ {
var wayPoint = new WayPoint(new Vector2(x, waypointArea.Y + waypointArea.Height * i), SpawnType.Path, null); var wayPoint = new WayPoint(new Vector2(x, waypointArea.Y + waypointArea.Height * i), SpawnType.Path, null)
{
Ruin = ruin
};
wayPoints.Add(wayPoint); wayPoints.Add(wayPoint);
if (x == waypointArea.X + outSideWaypointInterval) if (x == waypointArea.X + outSideWaypointInterval)
{ {
@@ -1907,7 +1910,10 @@ namespace Barotrauma
WayPoint wayPoint = null; WayPoint wayPoint = null;
for (float y = waypointArea.Y; y < waypointArea.Y + waypointArea.Height; y += outSideWaypointInterval) for (float y = waypointArea.Y; y < waypointArea.Y + waypointArea.Height; y += outSideWaypointInterval)
{ {
wayPoint = new WayPoint(new Vector2(waypointArea.X + waypointArea.Width * i, y), SpawnType.Path, null); wayPoint = new WayPoint(new Vector2(waypointArea.X + waypointArea.Width * i, y), SpawnType.Path, null)
{
Ruin = ruin
};
wayPoints.Add(wayPoint); wayPoints.Add(wayPoint);
if (y == waypointArea.Y) if (y == waypointArea.Y)
{ {
@@ -1940,12 +1946,33 @@ namespace Barotrauma
//connect ruin entrances to the outside waypoints //connect ruin entrances to the outside waypoints
foreach (Gap g in Gap.GapList) foreach (Gap g in Gap.GapList)
{ {
if (g.Submarine != ruin.Submarine || g.IsRoomToRoom) { continue; } if (g.Submarine != ruin.Submarine || g.IsRoomToRoom || g.linkedTo.Count == 0) { continue; }
var gapWaypoint = WayPoint.WayPointList.Find(wp => wp.ConnectedGap == g); var gapWaypoint = WayPoint.WayPointList.Find(wp => wp.ConnectedGap == g);
if (gapWaypoint == null) { continue; } if (gapWaypoint == null) { continue; }
var closestWp = FindClosestWayPoint(gapWaypoint.WorldPosition, wayPoints);
//place another waypoint in front of the entrance
Vector2 entranceDir = Vector2.Zero;
if (g.IsHorizontal)
{
entranceDir = Vector2.UnitX * Math.Sign(g.WorldPosition.X - g.linkedTo[0].WorldPosition.X);
}
else
{
entranceDir = Vector2.UnitY * Math.Sign(g.WorldPosition.Y - g.linkedTo[0].WorldPosition.Y);
}
var entranceWayPoint = new WayPoint(g.WorldPosition + entranceDir * 64.0f, SpawnType.Path, null)
{
Ruin = ruin
};
entranceWayPoint.ConnectTo(gapWaypoint);
var closestWp = FindClosestWayPoint(entranceWayPoint.WorldPosition, wayPoints, (wp) =>
{
return Submarine.PickBody(
ConvertUnits.ToSimUnits(wp.WorldPosition),
ConvertUnits.ToSimUnits(entranceWayPoint.WorldPosition), collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) == null;
});
if (closestWp == null) { continue; } if (closestWp == null) { continue; }
gapWaypoint.ConnectTo(closestWp); entranceWayPoint.ConnectTo(closestWp);
} }
//create a waypoint path from the ruin to the closest tunnel //create a waypoint path from the ruin to the closest tunnel
@@ -666,19 +666,19 @@ namespace Barotrauma
if (ForceVelocityLimit < 1000.0f) if (ForceVelocityLimit < 1000.0f)
body.ApplyForce(Force * currentForceFluctuation * distFactor, ForceVelocityLimit); body.ApplyForce(Force * currentForceFluctuation * distFactor, ForceVelocityLimit);
else else
body.ApplyForce(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); body.ApplyForce(Force * currentForceFluctuation * distFactor);
break; break;
case TriggerForceMode.Acceleration: case TriggerForceMode.Acceleration:
if (ForceVelocityLimit < 1000.0f) if (ForceVelocityLimit < 1000.0f)
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, ForceVelocityLimit); body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, ForceVelocityLimit);
else else
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor);
break; break;
case TriggerForceMode.Impulse: case TriggerForceMode.Impulse:
if (ForceVelocityLimit < 1000.0f) if (ForceVelocityLimit < 1000.0f)
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: ForceVelocityLimit); body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: ForceVelocityLimit);
else else
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor);
break; break;
case TriggerForceMode.LimitVelocity: case TriggerForceMode.LimitVelocity:
float maxVel = ForceVelocityLimit * currentForceFluctuation * distFactor; float maxVel = ForceVelocityLimit * currentForceFluctuation * distFactor;
@@ -495,7 +495,7 @@ namespace Barotrauma
float GetLevelDifficulty(float areaDifficulty) float GetLevelDifficulty(float areaDifficulty)
{ {
const float CurveModifier = 1.5f; const float CurveModifier = 1.5f;
const float DifficultyMultiplier = 1.1f; const float DifficultyMultiplier = 1.14f;
const float BaseDifficulty = -3f; const float BaseDifficulty = -3f;
return (float)(1 - Math.Pow(1 - areaDifficulty, CurveModifier)) * DifficultyMultiplier * 100f + BaseDifficulty; return (float)(1 - Math.Pow(1 - areaDifficulty, CurveModifier)) * DifficultyMultiplier * 100f + BaseDifficulty;
} }
@@ -77,7 +77,7 @@ namespace Barotrauma
locationType = location.GetLocationType(); locationType = location.GetLocationType();
} }
//load the infos of the outpost module files //load the infos of the outpost module files
List<SubmarineInfo> outpostModules = new List<SubmarineInfo>(); List<SubmarineInfo> outpostModules = new List<SubmarineInfo>();
foreach (ContentFile outpostModuleFile in outpostModuleFiles) foreach (ContentFile outpostModuleFile in outpostModuleFiles)
@@ -85,15 +85,19 @@ namespace Barotrauma
var subInfo = new SubmarineInfo(outpostModuleFile.Path); var subInfo = new SubmarineInfo(outpostModuleFile.Path);
if (subInfo.OutpostModuleInfo != null) if (subInfo.OutpostModuleInfo != null)
{ {
if (generationParams is RuinGeneration.RuinGenerationParams) if (generationParams is RuinGeneration.RuinGenerationParams)
{ {
//if the module doesn't have the ruin flag or any other flag used in the generation params, don't use it in ruins //if the module doesn't have the ruin flag or any other flag used in the generation params, don't use it in ruins
if (!subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin") && if (!subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin") &&
!generationParams.ModuleCounts.Any(m => subInfo.OutpostModuleInfo.ModuleFlags.Contains(m.Key))) !generationParams.ModuleCounts.Any(m => subInfo.OutpostModuleInfo.ModuleFlags.Contains(m.Key)))
{ {
continue; continue;
} }
} }
else if (subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin"))
{
continue;
}
outpostModules.Add(subInfo); outpostModules.Add(subInfo);
} }
} }
@@ -1177,8 +1177,7 @@ namespace Barotrauma
if (damageDiff < 0.0f) if (damageDiff < 0.0f)
{ {
attacker.Info?.IncreaseSkillLevel("mechanical", attacker.Info?.IncreaseSkillLevel("mechanical",
-damageDiff * SkillSettings.Current.SkillIncreasePerRepairedStructureDamage / Math.Max(attacker.GetSkillLevel("mechanical"), 1.0f), -damageDiff * SkillSettings.Current.SkillIncreasePerRepairedStructureDamage / Math.Max(attacker.GetSkillLevel("mechanical"), 1.0f));
SectionPosition(sectionIndex));
} }
} }
} }
@@ -452,7 +452,7 @@ namespace Barotrauma
public void ApplyForce(Vector2 force) public void ApplyForce(Vector2 force)
{ {
Body.ApplyForce(force, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); Body.ApplyForce(force);
} }
public void SetPosition(Vector2 position) public void SetPosition(Vector2 position)
@@ -46,6 +46,7 @@ namespace Barotrauma
public Hull CurrentHull { get; private set; } public Hull CurrentHull { get; private set; }
public Level.Tunnel Tunnel; public Level.Tunnel Tunnel;
public RuinGeneration.Ruin Ruin;
public SpawnType SpawnType public SpawnType SpawnType
{ {
@@ -1097,6 +1098,7 @@ namespace Barotrauma
CurrentHull = null; CurrentHull = null;
ConnectedGap = null; ConnectedGap = null;
Tunnel = null; Tunnel = null;
Ruin = null;
Stairs = null; Stairs = null;
Ladders = null; Ladders = null;
OnLinksChanged = null; OnLinksChanged = null;
@@ -238,7 +238,7 @@ namespace Barotrauma.Networking
{ {
byte retval = NetBitWriter.ReadByte(buf, 1, bitPos); byte retval = NetBitWriter.ReadByte(buf, 1, bitPos);
bitPos++; bitPos++;
return (retval > 0 ? true : false); return retval > 0;
} }
internal static void ReadPadBits(byte[] buf, ref int bitPos) internal static void ReadPadBits(byte[] buf, ref int bitPos)
@@ -672,14 +672,28 @@ namespace Barotrauma.Networking
} }
} }
buf = new byte[decompressedData.Length]; buf = new byte[decompressedData.Length];
Array.Copy(decompressedData, 0, buf, 0, decompressedData.Length); try
{
Array.Copy(decompressedData, 0, buf, 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);
}
lengthBits = decompressedData.Length * 8; lengthBits = decompressedData.Length * 8;
DebugConsole.Log("Decompressing message: " + inLength + " to " + LengthBytes); DebugConsole.Log("Decompressing message: " + inLength + " to " + LengthBytes);
} }
else else
{ {
buf = new byte[inBuf.Length]; buf = new byte[inBuf.Length];
Array.Copy(inBuf, startPos, buf, 0, inLength); try
{
Array.Copy(inBuf, startPos, buf, 0, inLength);
}
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);
}
lengthBits = inLength * 8; lengthBits = inLength * 8;
} }
seekPos = 0; seekPos = 0;
@@ -641,18 +641,9 @@ namespace Barotrauma
NetConfig.MaxPhysicsBodyAngularVelocity); NetConfig.MaxPhysicsBodyAngularVelocity);
} }
public void ApplyForce(Vector2 force) public void ApplyForce(Vector2 force, float maxVelocity = NetConfig.MaxPhysicsBodyVelocity)
{ {
if (!IsValidValue(force, "force", -1e10f, 1e10f)) return; if (!IsValidValue(maxVelocity, "max velocity")) { return; }
FarseerBody.ApplyForce(force);
}
/// <summary>
/// Apply a force to the body without increasing it's velocity above a specific limit.
/// </summary>
public void ApplyForce(Vector2 force, float maxVelocity)
{
if (!IsValidValue(maxVelocity, "max velocity")) return;
Vector2 velocityAddition = force / Mass * (float)Timing.Step; Vector2 velocityAddition = force / Mass * (float)Timing.Step;
Vector2 newVelocity = FarseerBody.LinearVelocity + velocityAddition; Vector2 newVelocity = FarseerBody.LinearVelocity + velocityAddition;
@@ -666,20 +657,20 @@ namespace Barotrauma
force = velocityAddition.ClampLength(maxVelAddition) * Mass / (float)Timing.Step; force = velocityAddition.ClampLength(maxVelAddition) * Mass / (float)Timing.Step;
} }
if (!IsValidValue(force, "clamped force", -1e10f, 1e10f)) return; if (!IsValidValue(force, "clamped force", -1e10f, 1e10f)) { return; }
FarseerBody.ApplyForce(force); FarseerBody.ApplyForce(force);
} }
public void ApplyForce(Vector2 force, Vector2 point) public void ApplyForce(Vector2 force, Vector2 point)
{ {
if (!IsValidValue(force, "force", -1e10f, 1e10f)) return; if (!IsValidValue(force, "force", -1e10f, 1e10f)) { return; }
if (!IsValidValue(point, "point")) return; if (!IsValidValue(point, "point")) { return; }
FarseerBody.ApplyForce(force, point); FarseerBody.ApplyForce(force, point);
} }
public void ApplyTorque(float torque) public void ApplyTorque(float torque)
{ {
if (!IsValidValue(torque, "torque")) return; if (!IsValidValue(torque, "torque")) { return; }
FarseerBody.ApplyTorque(torque); FarseerBody.ApplyTorque(torque);
} }
@@ -689,8 +680,8 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.X) < 1000000.0f); System.Diagnostics.Debug.Assert(Math.Abs(simPosition.X) < 1000000.0f);
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.Y) < 1000000.0f); System.Diagnostics.Debug.Assert(Math.Abs(simPosition.Y) < 1000000.0f);
if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) return false; if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) { return false; }
if (!IsValidValue(rotation, "rotation")) return false; if (!IsValidValue(rotation, "rotation")) { return false; }
FarseerBody.SetTransform(simPosition, rotation); FarseerBody.SetTransform(simPosition, rotation);
if (setPrevTransform) { SetPrevTransform(simPosition, rotation); } if (setPrevTransform) { SetPrevTransform(simPosition, rotation); }
@@ -703,8 +694,8 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.X) < 1000000.0f); System.Diagnostics.Debug.Assert(Math.Abs(simPosition.X) < 1000000.0f);
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.Y) < 1000000.0f); System.Diagnostics.Debug.Assert(Math.Abs(simPosition.Y) < 1000000.0f);
if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) return false; if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) { return false; }
if (!IsValidValue(rotation, "rotation")) return false; if (!IsValidValue(rotation, "rotation")) { return false; }
FarseerBody.SetTransformIgnoreContacts(ref simPosition, rotation); FarseerBody.SetTransformIgnoreContacts(ref simPosition, rotation);
if (setPrevTransform) { SetPrevTransform(simPosition, rotation); } if (setPrevTransform) { SetPrevTransform(simPosition, rotation); }
@@ -789,7 +780,7 @@ namespace Barotrauma
dragForce = Math.Min(drag, Mass * 500.0f) * -velDir; dragForce = Math.Min(drag, Mass * 500.0f) * -velDir;
} }
ApplyForce(dragForce + buoyancy, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); ApplyForce(dragForce + buoyancy);
ApplyTorque(FarseerBody.AngularVelocity * FarseerBody.Mass * -0.08f); ApplyTorque(FarseerBody.AngularVelocity * FarseerBody.Mass * -0.08f);
} }

Some files were not shown because too many files have changed in this diff Show More