(b0f5305f2) Unstable v0.9.10.0

This commit is contained in:
Juan Pablo Arce
2020-05-22 08:21:34 -03:00
parent 3d2b7bcf63
commit 4f8bd39789
75 changed files with 1023 additions and 407 deletions
@@ -44,19 +44,20 @@ namespace Barotrauma
var currentObjective = ObjectiveManager.CurrentObjective; var currentObjective = ObjectiveManager.CurrentObjective;
if (currentObjective != null) if (currentObjective != null)
{ {
if (currentOrder == null) int offset = currentOrder != null ? 20 : 0;
if (currentOrder == null || currentOrder.Priority <= 0)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20 + offset), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var subObjective = currentObjective.CurrentSubObjective; var subObjective = currentObjective.CurrentSubObjective;
if (subObjective != null) if (subObjective != null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40 + offset), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var activeObjective = ObjectiveManager.GetActiveObjective(); var activeObjective = ObjectiveManager.GetActiveObjective();
if (activeObjective != null) if (activeObjective != null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60 + offset), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
} }
} }
@@ -86,7 +87,7 @@ namespace Barotrauma
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y), new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
Color.BlueViolet, 0, 3); Color.BlueViolet, 0, 3);
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 80), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
} }
} }
} }
@@ -510,7 +510,7 @@ namespace Barotrauma
Collider.DebugDraw(spriteBatch, frozen ? GUI.Style.Red : (inWater ? Color.SkyBlue : Color.Gray)); Collider.DebugDraw(spriteBatch, frozen ? GUI.Style.Red : (inWater ? Color.SkyBlue : Color.Gray));
GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange); GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange);
foreach (RevoluteJoint joint in LimbJoints) foreach (var joint in LimbJoints)
{ {
Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA); Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true); GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
@@ -358,7 +358,16 @@ namespace Barotrauma
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun) partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun)
{ {
if (attackResult.Damage <= 1.0f || IsDead) { return; } if (IsDead) { return; }
if (attacker != null)
{
if (attackResult.Damage <= 0.01f) { return; }
}
else
{
if (attackResult.Damage <= 1.0f) { return; }
}
if (soundTimer < soundInterval * 0.5f) if (soundTimer < soundInterval * 0.5f)
{ {
PlaySound(CharacterSound.SoundType.Damage); PlaySound(CharacterSound.SoundType.Damage);
@@ -163,7 +163,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; } if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; }
if (!item.Repairables.Any(r => item.ConditionPercentage <= r.AIRepairThreshold)) { continue; } if (!item.Repairables.Any(r => item.ConditionPercentage <= r.RepairThreshold)) { continue; }
if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; } if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
Vector2 diff = item.WorldPosition - character.WorldPosition; Vector2 diff = item.WorldPosition - character.WorldPosition;
@@ -146,7 +146,8 @@ namespace Barotrauma
"+" + ((int)((newLevel - prevLevel) * 100.0f)).ToString() + " XP", "+" + ((int)((newLevel - prevLevel) * 100.0f)).ToString() + " XP",
GUI.Style.Green, GUI.Style.Green,
textPopupPos, textPopupPos,
Vector2.UnitY * 10.0f); Vector2.UnitY * 10.0f,
playSound: Character.Controlled?.Info == this);
} }
else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f) else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f)
{ {
@@ -154,7 +155,8 @@ namespace Barotrauma
"+10 XP", "+10 XP",
GUI.Style.Green, GUI.Style.Green,
textPopupPos, textPopupPos,
Vector2.UnitY * 10.0f); Vector2.UnitY * 10.0f,
playSound: Character.Controlled?.Info == this);
} }
if ((int)newLevel > (int)prevLevel) if ((int)newLevel > (int)prevLevel)
@@ -3,7 +3,6 @@ using Barotrauma.Particles;
using Barotrauma.SpriteDeformations; using Barotrauma.SpriteDeformations;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using FarseerPhysics; using FarseerPhysics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
@@ -15,13 +14,13 @@ using SpriteParams = Barotrauma.RagdollParams.SpriteParams;
namespace Barotrauma namespace Barotrauma
{ {
partial class LimbJoint : RevoluteJoint partial class LimbJoint
{ {
public void UpdateDeformations(float deltaTime) public void UpdateDeformations(float deltaTime)
{ {
float diff = Math.Abs(UpperLimit - LowerLimit); float diff = Math.Abs(UpperLimit - LowerLimit);
float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff)); float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff));
float jointAngle = this.JointAngle * strength; float jointAngle = JointAngle * strength;
JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
@@ -658,6 +658,44 @@ namespace Barotrauma
} }
}, isCheat: true)); }, isCheat: true));
commands.Add(new Command("listcloudfiles", "Lists all of your files on the Steam Cloud.", args =>
{
int i = 0;
foreach (var file in Steamworks.SteamRemoteStorage.Files)
{
NewMessage($"* {i}: {file.Filename}, {file.Size} bytes", Color.Orange);
i++;
}
NewMessage($"Bytes remaining: {Steamworks.SteamRemoteStorage.QuotaRemainingBytes}/{Steamworks.SteamRemoteStorage.QuotaBytes}", Color.Yellow);
}));
commands.Add(new Command("removefromcloud", "Removes a file from Steam Cloud.", args =>
{
if (args.Length < 1) { return; }
var files = Steamworks.SteamRemoteStorage.Files;
Steamworks.SteamRemoteStorage.RemoteFile file;
if (int.TryParse(args[0], out int index) && index>=0 && index<files.Count)
{
file = files[index];
}
else
{
file = files.Find(f => f.Filename.Equals(args[0], StringComparison.InvariantCultureIgnoreCase));
}
if (!string.IsNullOrEmpty(file.Filename))
{
if (file.Delete())
{
NewMessage($"Deleting {file.Filename}", Color.Orange);
}
else
{
ThrowError($"Failed to delete {file.Filename}");
}
}
}));
commands.Add(new Command("resetall", "Reset all items and structures to prefabs. Only applicable in the subeditor.", args => commands.Add(new Command("resetall", "Reset all items and structures to prefabs. Only applicable in the subeditor.", args =>
{ {
if (Screen.Selected == GameMain.SubEditorScreen) if (Screen.Selected == GameMain.SubEditorScreen)
@@ -1260,6 +1298,13 @@ namespace Barotrauma
TextManager.Language = "English"; TextManager.Language = "English";
})); }));
commands.Add(new Command("eventstats", "", (string[] args) =>
{
var debugLines = ScriptedEventSet.GetDebugStatistics();
string filePath = "eventstats.txt";
File.WriteAllLines(filePath, debugLines);
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
}));
#if DEBUG #if DEBUG
commands.Add(new Command("printreceivertransfers", "", (string[] args) => commands.Add(new Command("printreceivertransfers", "", (string[] args) =>
{ {
@@ -1834,7 +1879,7 @@ namespace Barotrauma
"giveperm", "giveperm",
(string[] args) => (string[] args) =>
{ {
if (args.Length < 1) return; if (args.Length < 1) { return; }
NewMessage("Valid permissions are:", Color.White); NewMessage("Valid permissions are:", Color.White);
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
@@ -1891,7 +1936,7 @@ namespace Barotrauma
{ {
if (args.Length < 1) return; if (args.Length < 1) return;
ShowQuestionPrompt("Console command permissions to grant to client " + args[0] + "? You may enter multiple commands separated with a space.", (commandNames) => ShowQuestionPrompt("Console command permissions to grant to client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to give the permission to use all console commands.", (commandNames) =>
{ {
GameMain.Client?.SendConsoleCommand("givecommandperm " + args[0] + " " + commandNames); GameMain.Client?.SendConsoleCommand("givecommandperm " + args[0] + " " + commandNames);
}, args, 1); }, args, 1);
@@ -1904,7 +1949,7 @@ namespace Barotrauma
{ {
if (args.Length < 1) return; if (args.Length < 1) return;
ShowQuestionPrompt("Console command permissions to revoke from client " + args[0] + "? You may enter multiple commands separated with a space.", (commandNames) => ShowQuestionPrompt("Console command permissions to revoke from client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to revoke the permission to use any console commands.", (commandNames) =>
{ {
GameMain.Client?.SendConsoleCommand("revokecommandperm " + args[0] + " " + commandNames); GameMain.Client?.SendConsoleCommand("revokecommandperm " + args[0] + " " + commandNames);
}, args, 1); }, args, 1);
@@ -1376,32 +1376,6 @@ namespace Barotrauma
} }
} }
if (assignmentNodeIcons.Any())
{
if (PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift))
{
if (assignmentNodeIcons.First().OrderIcon.Visible)
{
foreach (AssignmentNodeIconSet set in assignmentNodeIcons)
{
set.OrderIcon.Visible = false;
set.JobIcon.Visible = true;
}
}
}
else
{
if (assignmentNodeIcons.First().JobIcon.Visible)
{
foreach (AssignmentNodeIconSet set in assignmentNodeIcons)
{
set.JobIcon.Visible = false;
set.OrderIcon.Visible = true;
}
}
}
}
var hotkeyHit = false; var hotkeyHit = false;
foreach (Tuple<GUIComponent, Keys> node in optionNodes) foreach (Tuple<GUIComponent, Keys> node in optionNodes)
{ {
@@ -1578,17 +1552,6 @@ namespace Barotrauma
private List<OrderCategory> availableCategories; private List<OrderCategory> availableCategories;
private Stack<GUIButton> historyNodes = new Stack<GUIButton>(); private Stack<GUIButton> historyNodes = new Stack<GUIButton>();
private readonly List<Character> extraOptionCharacters = new List<Character>(); private readonly List<Character> extraOptionCharacters = new List<Character>();
private readonly List<AssignmentNodeIconSet> assignmentNodeIcons = new List<AssignmentNodeIconSet>();
private struct AssignmentNodeIconSet
{
public GUIImage OrderIcon { get; private set; }
public GUIImage JobIcon { get; private set; }
public AssignmentNodeIconSet(GUIImage orderIcon, GUIImage jobIcon)
{
OrderIcon = orderIcon;
JobIcon = jobIcon;
}
}
/// <summary> /// <summary>
/// node.Color = node.HighlightColor * nodeColorMultiplier /// node.Color = node.HighlightColor * nodeColorMultiplier
@@ -1865,7 +1828,6 @@ namespace Barotrauma
background = null; background = null;
commandFrame = null; commandFrame = null;
extraOptionCharacters.Clear(); extraOptionCharacters.Clear();
assignmentNodeIcons.Clear();
isOpeningClick = isSelectionHighlighted = false; isOpeningClick = isSelectionHighlighted = false;
characterContext = null; characterContext = null;
itemContext = null; itemContext = null;
@@ -2028,7 +1990,6 @@ namespace Barotrauma
expandNode = null; expandNode = null;
expandNodeHotkey = Keys.None; expandNodeHotkey = Keys.None;
RemoveExtraOptionNodes(); RemoveExtraOptionNodes();
assignmentNodeIcons.Clear();
} }
private void RemoveExtraOptionNodes() private void RemoveExtraOptionNodes()
@@ -2734,24 +2695,14 @@ namespace Barotrauma
GUIImage orderIcon; GUIImage orderIcon;
if (character.CurrentOrder != null && !character.CurrentOrder.Identifier.Equals("dismissed")) if (character.CurrentOrder != null && !character.CurrentOrder.Identifier.Equals("dismissed"))
{ {
orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), character.CurrentOrder.SymbolSprite, scaleToFit: true);
character.CurrentOrder.SymbolSprite, scaleToFit: true);
var tooltip = character.CurrentOrder.Name; var tooltip = character.CurrentOrder.Name;
if (!string.IsNullOrWhiteSpace(character.CurrentOrderOption)) { tooltip += " (" + character.CurrentOrder.GetOptionName(character.CurrentOrderOption) + ")"; }; if (!string.IsNullOrWhiteSpace(character.CurrentOrderOption)) { tooltip += " (" + character.CurrentOrder.GetOptionName(character.CurrentOrderOption) + ")"; };
orderIcon.ToolTip = tooltip; orderIcon.ToolTip = tooltip;
} }
else else
{ {
// TODO: Replace with an icon that symbols the characters dismissed state and their availability to new orders OR localize the text orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), "CommandIdleNode", scaleToFit: true);
orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center),
"CommandNodeContainer", scaleToFit: true);
var label = new GUITextBlock(new RectTransform(new Vector2(0.9f / 1.2f), orderIcon.RectTransform, anchor: Anchor.Center),
"FREE", textColor: jobColor * nodeColorMultiplier, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, style: null)
{
CanBeFocused = false,
ForceUpperCase = true,
HoverTextColor = jobColor
};
} }
orderIcon.Color = jobColor * nodeColorMultiplier; orderIcon.Color = jobColor * nodeColorMultiplier;
orderIcon.HoverColor = jobColor; orderIcon.HoverColor = jobColor;
@@ -2765,7 +2716,7 @@ namespace Barotrauma
var nameLabel = new GUITextBlock( var nameLabel = new GUITextBlock(
new RectTransform(new Point(width, 0), parent: node.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.BottomCenter) new RectTransform(new Point(width, 0), parent: node.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.BottomCenter)
{ {
RelativeOffset = new Vector2(0f, -0.1f) RelativeOffset = new Vector2(0f, -0.25f)
}, },
ToolBox.LimitString(character.Info?.DisplayName, font, width), textColor: jobColor * nodeColorMultiplier, font: font, textAlignment: Alignment.Center, style: null) ToolBox.LimitString(character.Info?.DisplayName, font, width), textColor: jobColor * nodeColorMultiplier, font: font, textAlignment: Alignment.Center, style: null)
{ {
@@ -2774,33 +2725,22 @@ namespace Barotrauma
HoverTextColor = jobColor HoverTextColor = jobColor
}; };
// Job icon if (character.Info?.Job?.Prefab?.IconSmall is Sprite smallJobIcon)
GUIImage jobIcon = null;
if (character?.Info?.Job?.Prefab?.Icon is Sprite sprite)
{ {
jobIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), // Job icon
"CommandNodeContainer", scaleToFit: true) new GUIImage(
new RectTransform(new Vector2(0.4f), node.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.Center)
{
RelativeOffset = new Vector2(0.0f, -((orderIcon.RectTransform.RelativeSize.Y - 1) / 2))
},
smallJobIcon, scaleToFit: true)
{ {
CanBeFocused = false, CanBeFocused = false,
Color = jobColor * nodeColorMultiplier, Color = jobColor,
HoverColor = jobColor, HoverColor = jobColor
PressedColor = jobColor,
SelectedColor = jobColor,
Visible = false
}; ;
new GUIImage(new RectTransform(new Vector2(0.9f / 1.2f), jobIcon.RectTransform, anchor: Anchor.Center),
sprite, scaleToFit: true)
{
CanBeFocused = false,
Color = jobColor * nodeColorMultiplier,
HoverColor = jobColor,
PressedColor = jobColor,
SelectedColor = jobColor
}; };
} }
assignmentNodeIcons.Add(new AssignmentNodeIconSet(orderIcon, jobIcon));
#if DEBUG #if DEBUG
bool canHear = true; bool canHear = true;
#else #else
@@ -929,24 +929,24 @@ namespace Barotrauma
//attempt to put in a free slot first //attempt to put in a free slot first
for (int i = capacity - 1; i >= 0; i--) for (int i = capacity - 1; i >= 0; i--)
{ {
if (Items[i] != null) continue; if (Items[i] != null) { continue; }
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue; if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
success = TryPutItem(item, i, true, false, Character.Controlled, true); success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) break; if (success) { break; }
} }
if (!success) if (!success)
{ {
for (int i = capacity - 1; i >= 0; i--) for (int i = capacity - 1; i >= 0; i--)
{ {
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue; if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
// something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it // something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it
if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand) if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && (SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand))
{ {
TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true); TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
} }
success = TryPutItem(item, i, true, false, Character.Controlled, true); success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) break; if (success) { break; }
} }
} }
break; break;
@@ -17,7 +17,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{ {
if (!IsActive || picker == null || !CanBeAttached() || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; } if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; }
Vector2 gridPos = picker.Position; Vector2 gridPos = picker.Position;
Vector2 roundedGridPos = new Vector2( Vector2 roundedGridPos = new Vector2(
@@ -74,6 +74,7 @@ namespace Barotrauma.Items.Components
public readonly Vector2 TransducerWorldPos; public readonly Vector2 TransducerWorldPos;
public readonly Vector2 WorldPos; public readonly Vector2 WorldPos;
public readonly float Distance; public readonly float Distance;
public double RecalculationTime;
public CachedDistance(Vector2 transducerWorldPos, Vector2 worldPos, float dist) public CachedDistance(Vector2 transducerWorldPos, Vector2 worldPos, float dist)
{ {
@@ -1333,20 +1334,22 @@ namespace Barotrauma.Items.Components
private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius) private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius)
{ {
float dist = Vector2.Distance(worldPosition, transducerPosition); float linearDist = Vector2.Distance(worldPosition, transducerPosition);
if (Vector2.DistanceSquared(worldPosition, transducerPosition) > Range * Range) float dist = linearDist;
if (linearDist > Range)
{ {
if (markerDistances.TryGetValue(targetIdentifier, out CachedDistance cachedDistance)) if (markerDistances.TryGetValue(targetIdentifier, out CachedDistance cachedDistance))
{ {
if (Vector2.DistanceSquared(cachedDistance.TransducerWorldPos, transducerPosition) > 500 * 500 || if (Timing.TotalTime > cachedDistance.RecalculationTime &&
Vector2.DistanceSquared(cachedDistance.WorldPos, worldPosition) > 500 * 500) (Vector2.DistanceSquared(cachedDistance.TransducerWorldPos, transducerPosition) > 500 * 500 ||
Vector2.DistanceSquared(cachedDistance.WorldPos, worldPosition) > 500 * 500))
{ {
markerDistances.Remove(targetIdentifier); markerDistances.Remove(targetIdentifier);
CalculateDistance(); CalculateDistance();
} }
else else
{ {
dist = cachedDistance.Distance; dist = Math.Max(cachedDistance.Distance, linearDist);
} }
} }
else else
@@ -1361,7 +1364,11 @@ namespace Barotrauma.Items.Components
var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(transducerPosition), ConvertUnits.ToSimUnits(worldPosition)); var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(transducerPosition), ConvertUnits.ToSimUnits(worldPosition));
if (!path.Unreachable) if (!path.Unreachable)
{ {
markerDistances.Add(targetIdentifier, new CachedDistance(transducerPosition, worldPosition, path.TotalLength)); var cachedDistance = new CachedDistance(transducerPosition, worldPosition, path.TotalLength)
{
RecalculationTime = Timing.TotalTime + Rand.Range(1.0f, 5.0f)
};
markerDistances.Add(targetIdentifier, cachedDistance);
dist = path.TotalLength; dist = path.TotalLength;
} }
} }
@@ -1375,16 +1382,16 @@ namespace Barotrauma.Items.Components
float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f); float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f);
Vector2 dir = Vector2.Normalize(position); Vector2 dir = Vector2.Normalize(position);
Vector2 markerPos = (dist * zoom * scale > radius) ? dir * radius : position; Vector2 markerPos = (linearDist * zoom * scale > radius) ? dir * radius : position;
markerPos += center; markerPos += center;
markerPos.X = (int)markerPos.X; markerPos.X = (int)markerPos.X;
markerPos.Y = (int)markerPos.Y; markerPos.Y = (int)markerPos.Y;
float alpha = 1.0f; float alpha = 1.0f;
if (dist * scale < radius) if (linearDist * scale < radius)
{ {
float normalizedDist = dist * scale / radius; float normalizedDist = linearDist * scale / radius;
alpha = Math.Max(normalizedDist - 0.4f, 0.0f); alpha = Math.Max(normalizedDist - 0.4f, 0.0f);
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos); float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
@@ -1395,14 +1402,6 @@ namespace Barotrauma.Items.Components
} }
} }
if (!GuiFrame.Children.First().Rect.Contains(markerPos))
{
if (MathUtils.GetLineRectangleIntersection(center, markerPos, GuiFrame.Children.First().Rect, out Vector2 intersection))
{
markerPos = intersection;
}
}
if (string.IsNullOrEmpty(iconIdentifier) || !targetIcons.ContainsKey(iconIdentifier)) if (string.IsNullOrEmpty(iconIdentifier) || !targetIcons.ContainsKey(iconIdentifier))
{ {
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2); GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2);
@@ -45,7 +45,7 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character) public override bool ShouldDrawHUD(Character character)
{ {
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false; if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false;
return !item.IsFullCondition || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition))); return item.ConditionPercentage < RepairThreshold || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition)));
} }
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
@@ -32,7 +32,19 @@ namespace Barotrauma
private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>(); private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>();
public bool FakeBroken; private bool fakeBroken;
public bool FakeBroken
{
get { return fakeBroken; }
set
{
if (value != fakeBroken)
{
fakeBroken = value;
SetActiveSprite();
}
}
}
private Sprite activeSprite; private Sprite activeSprite;
public override Sprite Sprite public override Sprite Sprite
@@ -145,11 +157,12 @@ namespace Barotrauma
} }
} }
float displayCondition = FakeBroken ? 0.0f : condition;
for (int i = 0; i < Prefab.BrokenSprites.Count;i++) for (int i = 0; i < Prefab.BrokenSprites.Count;i++)
{ {
if (Prefab.BrokenSprites[i].FadeIn) { continue; } if (Prefab.BrokenSprites[i].FadeIn) { continue; }
float minCondition = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f; float minCondition = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f;
if (condition <= minCondition || condition <= Prefab.BrokenSprites[i].MaxCondition) if (displayCondition <= minCondition || displayCondition <= Prefab.BrokenSprites[i].MaxCondition)
{ {
activeSprite = Prefab.BrokenSprites[i].Sprite; activeSprite = Prefab.BrokenSprites[i].Sprite;
break; break;
@@ -315,19 +328,25 @@ namespace Barotrauma
if (holdable.Picker.SelectedItems[0] == this) if (holdable.Picker.SelectedItems[0] == this)
{ {
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand); Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand);
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2; if (holdLimb != null)
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{ {
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Max(wearableSprite.Sprite.Depth + depthStep, depth); } depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Max(wearableSprite.Sprite.Depth + depthStep, depth); }
}
} }
} }
else if (holdable.Picker.SelectedItems[1] == this) else if (holdable.Picker.SelectedItems[1] == this)
{ {
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand); Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand);
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2; if (holdLimb != null)
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{ {
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Min(wearableSprite.Sprite.Depth - depthStep, depth); } depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Min(wearableSprite.Sprite.Depth - depthStep, depth); }
}
} }
} }
} }
@@ -370,6 +370,30 @@ namespace Barotrauma.Lights
} }
} }
public static void RecalculateAll(Submarine sub)
{
var chList = HullLists.Find(h => h.Submarine == sub);
if (chList != null)
{
foreach (ConvexHull ch in chList.List)
{
ch.overlappingHulls.Clear();
for (int i = 0; i < 4; i++)
{
ch.ignoreEdge[i] = false;
}
}
for (int i = 0; i < chList.List.Count; i++)
{
for (int j = i + 1; j < chList.List.Count; j++)
{
chList.List[i].MergeOverlappingSegments(chList.List[j]);
chList.List[j].MergeOverlappingSegments(chList.List[i]);
}
}
}
}
public void SetVertices(Vector2[] points, Matrix? rotationMatrix = null) public void SetVertices(Vector2[] points, Matrix? rotationMatrix = null)
{ {
Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported"); Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
@@ -31,7 +31,7 @@ namespace Barotrauma
Stream = sound.Stream; Stream = sound.Stream;
Range = element.GetAttributeFloat("range", 1000.0f); Range = element.GetAttributeFloat("range", 1000.0f);
Volume = element.GetAttributeFloat("volume", 1.0f); Volume = element.GetAttributeFloat("volume", 1.0f);
sound.DisableMuffle = element.GetAttributeBool("disablemuffle", false); sound.IgnoreMuffling = element.GetAttributeBool("dontmuffle", false);
} }
} }
@@ -578,7 +578,7 @@ namespace Barotrauma.Steam
if (!isInitialized) return; if (!isInitialized) return;
var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All) var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All)
.RankedByTotalUniqueSubscriptions() .RankedByTrend()
.WithLongDescription(); .WithLongDescription();
if (requireTags != null) query.WithTags(requireTags); if (requireTags != null) query.WithTags(requireTags);
@@ -1765,7 +1765,7 @@ namespace Barotrauma
var workshopPublishStatus = SteamManager.StartPublishItem(itemContentPackage, itemEditor); var workshopPublishStatus = SteamManager.StartPublishItem(itemContentPackage, itemEditor);
if (workshopPublishStatus != null) if (workshopPublishStatus != null)
{ {
if (!itemEditor.Value.Tags.Contains("unstable")) { itemEditor.Value.Tags.Add("unstable"); } if (!(itemEditor?.HasTag("unstable") ?? false)) { itemEditor = itemEditor?.WithTag("unstable"); }
CoroutineManager.StartCoroutine(WaitForPublish(workshopPublishStatus), "WaitForPublish"); CoroutineManager.StartCoroutine(WaitForPublish(workshopPublishStatus), "WaitForPublish");
} }
msgBox.Close(); msgBox.Close();
@@ -1234,8 +1234,6 @@ namespace Barotrauma
return false; return false;
} }
Submarine.MainSub.Info.Name = name;
string savePath = name + ".sub"; string savePath = name + ".sub";
string prevSavePath = null; string prevSavePath = null;
if (!string.IsNullOrEmpty(Submarine.MainSub?.Info.FilePath) && if (!string.IsNullOrEmpty(Submarine.MainSub?.Info.FilePath) &&
@@ -1740,10 +1738,24 @@ namespace Barotrauma
} }
} }
var hideInMenusTickBox = nameBox.Parent.GetChildByUserData("hideinmenus") as GUITickBox; bool hideInMenus = !(nameBox.Parent.GetChildByUserData("hideinmenus") is GUITickBox hideInMenusTickBox) ? false : hideInMenusTickBox.Selected;
bool hideInMenus = hideInMenusTickBox == null ? false : hideInMenusTickBox.Selected; #if DEBUG
string saveFolder = ItemAssemblyPrefab.VanillaSaveFolder;
string saveFolder = Path.Combine("Content", "Items", "Assemblies"); #else
string saveFolder = ItemAssemblyPrefab.SaveFolder;
if (!Directory.Exists(saveFolder))
{
try
{
Directory.CreateDirectory(saveFolder);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to create a directory for the item assmebly.", e);
return false;
}
}
#endif
string filePath = Path.Combine(saveFolder, nameBox.Text + ".xml"); string filePath = Path.Combine(saveFolder, nameBox.Text + ".xml");
if (File.Exists(filePath)) if (File.Exists(filePath))
@@ -1771,7 +1783,6 @@ namespace Barotrauma
#else #else
doc.SaveSafe(filePath); doc.SaveSafe(filePath);
#endif #endif
new ItemAssemblyPrefab(filePath); new ItemAssemblyPrefab(filePath);
UpdateEntityList(); UpdateEntityList();
} }
@@ -76,7 +76,7 @@ namespace Barotrauma.Sounds
protected set; protected set;
} }
public bool DisableMuffle { get; set; } public bool IgnoreMuffling { get; set; }
/// <summary> /// <summary>
/// How many instances of the same sound clip can be playing at the same time /// How many instances of the same sound clip can be playing at the same time
@@ -648,7 +648,7 @@ namespace Barotrauma
{ {
return null; return null;
} }
bool muffle = !sound.DisableMuffle && ShouldMuffleSound(Character.Controlled, position, far, hullGuess); bool muffle = !sound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, far, hullGuess);
return sound.Play(volume ?? sound.BaseGain, far, position, muffle: muffle); return sound.Play(volume ?? sound.BaseGain, far, position, muffle: muffle);
} }
@@ -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.9.1001.0</Version> <Version>0.9.10.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.9.1001.0</Version> <Version>0.9.10.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.9.1001.0</Version> <Version>0.9.10.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>
@@ -25,6 +25,9 @@
<DefineConstants>TRACE;DEBUG;CLIENT;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;CLIENT;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -43,12 +46,16 @@
<DefineConstants>TRACE;CLIENT;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;CLIENT;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;CLIENT;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;CLIENT;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -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.9.1001.0</Version> <Version>0.9.10.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.9.1001.0</Version> <Version>0.9.10.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>
@@ -487,7 +487,7 @@ namespace Barotrauma
if (GameMain.Server == null) return; if (GameMain.Server == null) return;
if (args.Length < 1) if (args.Length < 1)
{ {
NewMessage("giveperm [id]: Grants administrative permissions to the player with the specified client ID.", Color.Cyan); NewMessage("giveperm [id/steamid/endpoint/name]: Grants administrative permissions to the player with the specified client.", Color.Cyan);
return; return;
} }
@@ -522,7 +522,7 @@ namespace Barotrauma
if (GameMain.Server == null) return; if (GameMain.Server == null) return;
if (args.Length < 1) if (args.Length < 1)
{ {
NewMessage("revokeperm [id]: Revokes administrative permissions to the player with the specified client ID.", Color.Cyan); NewMessage("revokeperm [id/steamid/endpoint/name]: Revokes administrative permissions to the player with the specified client.", Color.Cyan);
return; return;
} }
@@ -604,7 +604,7 @@ namespace Barotrauma
if (GameMain.Server == null) return; if (GameMain.Server == null) return;
if (args.Length < 1) if (args.Length < 1)
{ {
NewMessage("givecommandperm [id]: Gives the player with the specified client ID the permission to use the specified console commands.", Color.Cyan); NewMessage("givecommandperm [id/steamid/endpoint/name]: Gives the specified client the permission to use the specified console commands.", Color.Cyan);
return; return;
} }
@@ -649,7 +649,7 @@ namespace Barotrauma
{ {
NewMessage("Gave the client \"" + client.Name + "\" the permission to use all console commands.", Color.White); NewMessage("Gave the client \"" + client.Name + "\" the permission to use all console commands.", Color.White);
} }
else else if (grantedCommands.Count > 0)
{ {
NewMessage("Gave the client \"" + client.Name + "\" the permission to use console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", Color.White); NewMessage("Gave the client \"" + client.Name + "\" the permission to use console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", Color.White);
} }
@@ -662,7 +662,7 @@ namespace Barotrauma
if (GameMain.Server == null) return; if (GameMain.Server == null) return;
if (args.Length < 1) if (args.Length < 1)
{ {
NewMessage("revokecommandperm [id]: Revokes permission to use the specified console commands from the player with the specified client ID.", Color.Cyan); NewMessage("revokecommandperm [id/steamid/endpoint/name]: Revokes permission to use the specified console commands from the specified client.", Color.Cyan);
return; return;
} }
@@ -682,23 +682,39 @@ namespace Barotrauma
{ {
string[] splitCommands = commandsStr.Split(' '); string[] splitCommands = commandsStr.Split(' ');
List<Command> revokedCommands = new List<Command>(); List<Command> revokedCommands = new List<Command>();
for (int i = 0; i < splitCommands.Length; i++) bool revokeAll = splitCommands.Length > 0 && splitCommands[0].Equals("all", StringComparison.OrdinalIgnoreCase);
if (revokeAll)
{ {
splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); revokedCommands.AddRange(commands);
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i]));
if (matchingCommand == null)
{
ThrowError("Could not find the command \"" + splitCommands[i] + "\"!");
}
else
{
revokedCommands.Add(matchingCommand);
}
} }
else
{
for (int i = 0; i < splitCommands.Length; i++)
{
splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant();
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i]));
if (matchingCommand == null)
{
ThrowError("Could not find the command \"" + splitCommands[i] + "\"!");
}
else
{
revokedCommands.Add(matchingCommand);
}
}
}
client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList()); client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList());
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
NewMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White); if (revokeAll)
{
NewMessage("Revoked \"" + client.Name + "\"'s permission to use console commands.", Color.White);
}
else if (revokedCommands.Any())
{
NewMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White);
}
}, args, 1); }, args, 1);
}); });
@@ -707,7 +723,7 @@ namespace Barotrauma
if (GameMain.Server == null) return; if (GameMain.Server == null) return;
if (args.Length < 1) if (args.Length < 1)
{ {
NewMessage("showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", Color.Cyan); NewMessage("showperm [id/steamid/endpoint/name]: Shows the current administrative permissions of the specified client.", Color.Cyan);
return; return;
} }
@@ -1808,7 +1824,7 @@ namespace Barotrauma
var client = FindClient(args[0]); var client = FindClient(args[0]);
if (client == null) if (client == null)
{ {
ThrowError("Client \"" + args[0] + "\" not found."); GameMain.Server.SendConsoleMessage("Client \"" + args[0] + "\" not found.", senderClient);
return; return;
} }
if (client.Connection == GameMain.Server.OwnerConnection) if (client.Connection == GameMain.Server.OwnerConnection)
@@ -1817,27 +1833,42 @@ namespace Barotrauma
return; return;
} }
string[] splitCommands = args.Skip(1).ToArray();
List<Command> grantedCommands = new List<Command>(); List<Command> grantedCommands = new List<Command>();
for (int i = 0; i < splitCommands.Length; i++) string[] splitCommands = args.Skip(1).ToArray();
bool giveAll = splitCommands.Length > 0 && splitCommands[0].Equals("all", StringComparison.OrdinalIgnoreCase);
if (giveAll)
{ {
splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); grantedCommands.AddRange(commands);
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); }
if (matchingCommand == null) else
{
for (int i = 0; i < splitCommands.Length; i++)
{ {
GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient); splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant();
} Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i]));
else if (matchingCommand == null)
{ {
grantedCommands.Add(matchingCommand); GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient);
}
else
{
grantedCommands.Add(matchingCommand);
}
} }
} }
client.GivePermission(ClientPermissions.ConsoleCommands); client.GivePermission(ClientPermissions.ConsoleCommands);
client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Union(grantedCommands).Distinct().ToList()); client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Union(grantedCommands).Distinct().ToList());
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
GameMain.Server.SendConsoleMessage("Gave the client \"" + client.Name + "\" the permission to use the console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", senderClient); if (giveAll)
NewMessage("Gave the client \"" + client.Name + "\" the permission to use the console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", Color.White); {
GameMain.Server.SendConsoleMessage("Gave the client \"" + client.Name + "\" the permission to use all console commands.", senderClient);
}
else if (grantedCommands.Count > 0)
{
GameMain.Server.SendConsoleMessage("Gave the client \"" + client.Name + "\" the permission to use console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", senderClient);
}
} }
); );
@@ -1845,7 +1876,7 @@ namespace Barotrauma
"revokecommandperm", "revokecommandperm",
(Client senderClient, Vector2 cursorWorldPos, string[] args) => (Client senderClient, Vector2 cursorWorldPos, string[] args) =>
{ {
if (args.Length < 2) return; if (args.Length < 2) { return; }
var client = FindClient(args[0]); var client = FindClient(args[0]);
if (client == null) if (client == null)
@@ -1858,28 +1889,43 @@ namespace Barotrauma
GameMain.Server.SendConsoleMessage("Cannot revoke command permissions from the server owner!", senderClient); GameMain.Server.SendConsoleMessage("Cannot revoke command permissions from the server owner!", senderClient);
return; return;
} }
string[] splitCommands = args.Skip(1).ToArray();
List<Command> revokedCommands = new List<Command>(); List<Command> revokedCommands = new List<Command>();
for (int i = 0; i < splitCommands.Length; i++) string[] splitCommands = args.Skip(1).ToArray();
bool revokeAll = splitCommands.Length > 0 && splitCommands[0].Equals("all", StringComparison.OrdinalIgnoreCase);
if (revokeAll)
{ {
splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); revokedCommands.AddRange(commands);
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); client.RemovePermission(ClientPermissions.ConsoleCommands);
if (matchingCommand == null) }
else
{
for (int i = 0; i < splitCommands.Length; i++)
{ {
GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient); splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant();
} Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i]));
else if (matchingCommand == null)
{ {
revokedCommands.Add(matchingCommand); GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient);
}
else
{
revokedCommands.Add(matchingCommand);
}
} }
client.GivePermission(ClientPermissions.ConsoleCommands);
} }
client.GivePermission(ClientPermissions.ConsoleCommands);
client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList()); client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList());
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
GameMain.Server.SendConsoleMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", senderClient); GameMain.Server.SendConsoleMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", senderClient);
NewMessage(senderClient.Name + " revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White); if (revokeAll)
{
GameMain.Server.SendConsoleMessage("Revoked \"" + client.Name + "\"'s permission to use console commands.", senderClient);
}
else if (revokedCommands.Count > 0)
{
GameMain.Server.SendConsoleMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", senderClient);
}
} }
); );
@@ -9,7 +9,9 @@ namespace Barotrauma
msg.Write((ushort)items.Count); msg.Write((ushort)items.Count);
foreach (Item item in items) foreach (Item item in items)
{ {
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? 0); item.WriteSpawnData(msg,
itemIDs[item],
parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID);
} }
} }
} }
@@ -1,4 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
@@ -7,6 +8,9 @@ namespace Barotrauma
{ {
private bool usedExistingItem; private bool usedExistingItem;
private UInt16 originalItemID;
private UInt16 originalInventoryID;
private readonly List<Pair<int, int>> executedEffectIndices = new List<Pair<int, int>>(); private readonly List<Pair<int, int>> executedEffectIndices = new List<Pair<int, int>>();
public override void ServerWriteInitial(IWriteMessage msg, Client c) public override void ServerWriteInitial(IWriteMessage msg, Client c)
@@ -14,11 +18,11 @@ namespace Barotrauma
msg.Write(usedExistingItem); msg.Write(usedExistingItem);
if (usedExistingItem) if (usedExistingItem)
{ {
msg.Write(item.ID); msg.Write(originalItemID);
} }
else else
{ {
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? 0); item.WriteSpawnData(msg, originalItemID, originalInventoryID);
} }
msg.Write((byte)executedEffectIndices.Count); msg.Write((byte)executedEffectIndices.Count);
@@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components
simPosition = c.Character.SimPosition + offset; simPosition = c.Character.SimPosition + offset;
Drop(false, null); Drop(false, null);
item.SetTransform(simPosition, 0.0f); item.SetTransform(simPosition, 0.0f, findNewHull: false);
AttachToWall(); AttachToWall();
item.CreateServerEvent(this); item.CreateServerEvent(this);
@@ -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.9.1001.0</Version> <Version>0.9.10.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>
@@ -24,6 +24,8 @@
<DefineConstants>TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -42,12 +44,16 @@
<DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -559,7 +559,7 @@ namespace Barotrauma
if (item.CurrentHull != hull) { continue; } if (item.CurrentHull != hull) { continue; }
if (AIObjectiveRepairItems.IsValidTarget(item, Character)) if (AIObjectiveRepairItems.IsValidTarget(item, Character))
{ {
if (item.Repairables.All(r => item.ConditionPercentage > r.AIRepairThreshold)) { continue; } if (item.Repairables.All(r => item.ConditionPercentage > r.RepairThreshold)) { continue; }
if (AddTargets<AIObjectiveRepairItems, Item>(Character, item) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRepairItem>()) if (AddTargets<AIObjectiveRepairItems, Item>(Character, item) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRepairItem>())
{ {
var orderPrefab = Order.GetPrefab("reportbrokendevices"); var orderPrefab = Order.GetPrefab("reportbrokendevices");
@@ -609,9 +609,13 @@ namespace Barotrauma
if (ObjectiveManager.CurrentObjective is AIObjectiveFightIntruders) { return; } if (ObjectiveManager.CurrentObjective is AIObjectiveFightIntruders) { return; }
if (attacker == null || attacker.IsDead || attacker.Removed) if (attacker == null || attacker.IsDead || attacker.Removed)
{ {
// Don't react on the damage if there's no attacker.
// We might consider launching the retreat combat objective in some cases, so that the bot does not just stand somewhere getting damaged and dying.
// But fires and enemies should already be handled by the FindSafetyObjective.
return;
// Ignore damage from falling etc that we shouldn't react to. // Ignore damage from falling etc that we shouldn't react to.
if (Character.LastDamageSource == null) { return; } //if (Character.LastDamageSource == null) { return; }
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); //AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
} }
else if (IsFriendly(attacker)) else if (IsFriendly(attacker))
{ {
@@ -827,7 +831,7 @@ namespace Barotrauma
if (item.CurrentHull != hull) { continue; } if (item.CurrentHull != hull) { continue; }
if (AIObjectiveRepairItems.IsValidTarget(item, character)) if (AIObjectiveRepairItems.IsValidTarget(item, character))
{ {
if (item.Repairables.All(r => item.ConditionPercentage >= r.AIRepairThreshold)) { continue; } if (item.Repairables.All(r => item.ConditionPercentage >= r.RepairThreshold)) { continue; }
AddTargets<AIObjectiveRepairItems, Item>(character, item); AddTargets<AIObjectiveRepairItems, Item>(character, item);
} }
} }
@@ -412,7 +412,11 @@ namespace Barotrauma
{ {
//the node we're heading towards is the last one in the path, and at a door //the node we're heading towards is the last one in the path, and at a door
//the door needs to be open for the character to reach the node //the door needs to be open for the character to reach the node
shouldBeOpen = true; if (currentWaypoint.ConnectedDoor.LinkedGap != null && currentWaypoint.ConnectedDoor.LinkedGap.IsRoomToRoom)
{
shouldBeOpen = true;
door = currentWaypoint.ConnectedDoor;
}
} }
else else
{ {
@@ -194,24 +194,18 @@ namespace Barotrauma
{ {
if (CurrentOrder != null) if (CurrentOrder != null)
{ {
#if DEBUG
// Note: don't automatically remove orders here. Removing orders needs to be done via dismissing.
if (CurrentOrder.IsCompleted) if (CurrentOrder.IsCompleted)
{ {
#if DEBUG DebugConsole.NewMessage($"{character.Name}: ORDER {CurrentOrder.DebugTag} IS COMPLETED. CURRENTLY ALL ORDERS SHOULD BE LOOPING.", Color.Red);
DebugConsole.NewMessage($"{character.Name}: Removing order {CurrentOrder.DebugTag}, because it is completed.", Color.LightGreen);
#endif
CurrentOrder = null;
} }
else if (!CurrentOrder.CanBeCompleted) else if (!CurrentOrder.CanBeCompleted)
{ {
#if DEBUG DebugConsole.NewMessage($"{character.Name}: ORDER {CurrentOrder.DebugTag}, CANNOT BE COMPLETED.", Color.Red);
DebugConsole.NewMessage($"{character.Name}: Removing order {CurrentOrder.DebugTag}, because it cannot be completed.", Color.Red); }
#endif #endif
CurrentOrder = null; CurrentOrder.Update(deltaTime);
}
else
{
CurrentOrder.Update(deltaTime);
}
} }
if (WaitTimer > 0) if (WaitTimer > 0)
{ {
@@ -379,7 +373,7 @@ namespace Barotrauma
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option,
requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier) requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier)
{ {
IsLoop = option != "shutdown", IsLoop = true,
// Don't override unless it's an order by a player // Don't override unless it's an order by a player
Override = orderGiver != null && orderGiver.IsPlayer Override = orderGiver != null && orderGiver.IsPlayer
}; };
@@ -28,6 +28,7 @@ namespace Barotrauma
public ItemComponent GetTarget() => useController ? controller : component; public ItemComponent GetTarget() => useController ? controller : component;
public Func<bool> completionCondition; public Func<bool> completionCondition;
private bool isDoneOperating;
public override float GetPriority() public override float GetPriority()
{ {
@@ -51,30 +52,34 @@ namespace Barotrauma
if (targetItem == null) if (targetItem == null)
{ {
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Item or component of AI Objective Operate item wass null. This shouldn't happen."); DebugConsole.ThrowError("Item or component of AI Objective Operate item was null. This shouldn't happen.");
#endif #endif
Abandon = true; Abandon = true;
Priority = 0; Priority = 0;
return Priority; return Priority;
} }
switch (Option) var reactor = component?.Item.GetComponent<Reactor>();
if (reactor != null)
{ {
case "shutdown": switch (Option)
var powered = component?.Item.GetComponent<Powered>(); {
if (powered != null && !powered.IsActive) case "shutdown":
{ if (!reactor.PowerOn)
Priority = 0; {
return Priority; Priority = 0;
} return Priority;
break; }
case "powerup": break;
// Check that we don't already have another order that is targeting the same item. case "powerup":
if (objectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder != this && operateOrder.GetTarget() == target) // Check that we don't already have another order that is targeting the same item.
{ // Without this the autonomous objective will tell the bot to turn the reactor on again.
Priority = 0; if (objectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder != this && operateOrder.GetTarget() == target)
return Priority; {
} Priority = 0;
break; return Priority;
}
break;
}
} }
if (targetItem.CurrentHull == null || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(target, out _)) if (targetItem.CurrentHull == null || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(target, out _))
{ {
@@ -87,7 +92,7 @@ namespace Barotrauma
else else
{ {
float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier); float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier);
float max = objectiveManager.CurrentOrder == this ? MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90) : AIObjectiveManager.RunPriority - 1; float max = objectiveManager.CurrentOrder == this ? MathHelper.Min(AIObjectiveManager.OrderPriority, 90) : AIObjectiveManager.RunPriority - 1;
Priority = MathHelper.Clamp(value, 0, max); Priority = MathHelper.Clamp(value, 0, max);
} }
} }
@@ -148,7 +153,7 @@ namespace Barotrauma
} }
if (component.AIOperate(deltaTime, character, this)) if (component.AIOperate(deltaTime, character, this))
{ {
IsCompleted = completionCondition == null || completionCondition(); isDoneOperating = completionCondition == null || completionCondition();
} }
} }
else else
@@ -215,12 +220,12 @@ namespace Barotrauma
} }
if (component.AIOperate(deltaTime, character, this)) if (component.AIOperate(deltaTime, character, this))
{ {
IsCompleted = completionCondition == null || completionCondition(); isDoneOperating = completionCondition == null || completionCondition();
} }
} }
} }
} }
protected override bool Check() => IsCompleted && !IsLoop; protected override bool Check() => isDoneOperating && !IsLoop;
} }
} }
@@ -76,7 +76,7 @@ namespace Barotrauma
if (item != character.SelectedConstruction) if (item != character.SelectedConstruction)
{ {
float condition = item.ConditionPercentage; float condition = item.ConditionPercentage;
if (item.Repairables.All(r => condition >= r.AIRepairThreshold)) { return false; } if (item.Repairables.All(r => condition >= r.RepairThreshold)) { return false; }
} }
} }
if (!string.IsNullOrWhiteSpace(RelevantSkill)) if (!string.IsNullOrWhiteSpace(RelevantSkill))
@@ -279,7 +279,7 @@ namespace Barotrauma
ic.PlaySound(ActionType.OnUse, character); ic.PlaySound(ActionType.OnUse, character);
#endif #endif
ic.WasUsed = true; ic.WasUsed = true;
ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb); ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb, user: character);
if (ic.DeleteOnUse) if (ic.DeleteOnUse)
{ {
remove = true; remove = true;
@@ -1713,7 +1713,7 @@ namespace Barotrauma
if (holdable.ControlPose) if (holdable.ControlPose)
{ {
head.body.SmoothRotate(itemAngle); head?.body.SmoothRotate(itemAngle);
if (TargetMovement == Vector2.Zero && inWater) if (TargetMovement == Vector2.Zero && inWater)
{ {
@@ -1735,13 +1735,13 @@ namespace Barotrauma
{ {
if (character.SelectedItems[0] == item) if (character.SelectedItems[0] == item)
{ {
if (rightHand.IsSevered) return; if (rightHand == null || rightHand.IsSevered) { return; }
transformedHoldPos = rightHand.PullJointWorldAnchorA - transformedHandlePos[0]; transformedHoldPos = rightHand.PullJointWorldAnchorA - transformedHandlePos[0];
itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir); itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
} }
else if (character.SelectedItems[1] == item) else if (character.SelectedItems[1] == item)
{ {
if (leftHand.IsSevered) return; if (leftHand == null || leftHand.IsSevered) { return; }
transformedHoldPos = leftHand.PullJointWorldAnchorA - transformedHandlePos[1]; transformedHoldPos = leftHand.PullJointWorldAnchorA - transformedHandlePos[1];
itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir); itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
} }
@@ -1750,12 +1750,12 @@ namespace Barotrauma
{ {
if (character.SelectedItems[0] == item) if (character.SelectedItems[0] == item)
{ {
if (rightHand.IsSevered) return; if (rightHand == null || rightHand.IsSevered) { return; }
rightHand.Disabled = true; rightHand.Disabled = true;
} }
if (character.SelectedItems[1] == item) if (character.SelectedItems[1] == item)
{ {
if (leftHand.IsSevered) return; if (leftHand == null || leftHand.IsSevered) { return; }
leftHand.Disabled = true; leftHand.Disabled = true;
} }
@@ -441,7 +441,7 @@ namespace Barotrauma
{ {
foreach (LimbJoint joint in LimbJoints) foreach (LimbJoint joint in LimbJoints)
{ {
if (GameMain.World.JointList.Contains(joint)) { GameMain.World.Remove(joint); } if (GameMain.World.JointList.Contains(joint.Joint)) { GameMain.World.Remove(joint.Joint); }
} }
} }
DebugConsole.Log($"Creating joints from {RagdollParams.Name}."); DebugConsole.Log($"Creating joints from {RagdollParams.Name}.");
@@ -526,7 +526,7 @@ namespace Barotrauma
public void AddJoint(JointParams jointParams) public void AddJoint(JointParams jointParams)
{ {
LimbJoint joint = new LimbJoint(Limbs[jointParams.Limb1], Limbs[jointParams.Limb2], jointParams, this); LimbJoint joint = new LimbJoint(Limbs[jointParams.Limb1], Limbs[jointParams.Limb2], jointParams, this);
GameMain.World.Add(joint); GameMain.World.Add(joint.Joint);
for (int i = 0; i < LimbJoints.Length; i++) for (int i = 0; i < LimbJoints.Length; i++)
{ {
if (LimbJoints[i] != null) continue; if (LimbJoints[i] != null) continue;
@@ -609,7 +609,7 @@ namespace Barotrauma
limb.Remove(); limb.Remove();
foreach (LimbJoint limbJoint in attachedJoints) foreach (LimbJoint limbJoint in attachedJoints)
{ {
GameMain.World.Remove(limbJoint); GameMain.World.Remove(limbJoint.Joint);
} }
} }
@@ -726,7 +726,7 @@ namespace Barotrauma
private readonly List<Limb> connectedLimbs = new List<Limb>(); private readonly List<Limb> connectedLimbs = new List<Limb>();
private readonly List<LimbJoint> checkedJoints = new List<LimbJoint>(); private readonly List<LimbJoint> checkedJoints = new List<LimbJoint>();
public bool SeverLimbJoint(LimbJoint limbJoint, bool playSound = true) public bool SeverLimbJoint(LimbJoint limbJoint)
{ {
if (!limbJoint.CanBeSevered || limbJoint.IsSevered) if (!limbJoint.CanBeSevered || limbJoint.IsSevered)
{ {
@@ -750,6 +750,14 @@ namespace Barotrauma
{ {
if (connectedLimbs.Contains(limb)) { continue; } if (connectedLimbs.Contains(limb)) { continue; }
limb.IsSevered = true; limb.IsSevered = true;
if (limb.type == LimbType.RightHand)
{
character.SelectedItems[0]?.Drop(character);
}
else if (limb.type == LimbType.LeftHand)
{
character.SelectedItems[1]?.Drop(character);
}
} }
SeverLimbJointProjSpecific(limbJoint, playSound: true); SeverLimbJointProjSpecific(limbJoint, playSound: true);
@@ -1776,11 +1784,12 @@ namespace Barotrauma
if (LimbJoints != null) if (LimbJoints != null)
{ {
foreach (RevoluteJoint joint in LimbJoints) foreach (var joint in LimbJoints)
{ {
if (GameMain.World.JointList.Contains(joint)) var j = joint.Joint;
if (GameMain.World.JointList.Contains(j))
{ {
GameMain.World.Remove(joint); GameMain.World.Remove(j);
} }
} }
LimbJoints = null; LimbJoints = null;
@@ -1150,8 +1150,11 @@ namespace Barotrauma
float reduction = 0; float reduction = 0;
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightFoot, excludeSevered: false), reduction); reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightFoot, excludeSevered: false), reduction);
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftFoot, excludeSevered: false), reduction); reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftFoot, excludeSevered: false), reduction);
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightHand, excludeSevered: false), reduction); if (!(AnimController is HumanoidAnimController))
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftHand, excludeSevered: false), reduction); {
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightHand, excludeSevered: false), reduction);
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftHand, excludeSevered: false), reduction);
}
int totalTailLimbs = 0; int totalTailLimbs = 0;
int destroyedTailLimbs = 0; int destroyedTailLimbs = 0;
foreach (var limb in AnimController.Limbs) foreach (var limb in AnimController.Limbs)
@@ -1176,7 +1179,7 @@ namespace Barotrauma
{ {
if (limb != null) if (limb != null)
{ {
sum += MathHelper.Lerp(0, max, CharacterHealth.GetLimbDamage(limb)); sum += MathHelper.Lerp(0, max, CharacterHealth.GetLimbDamage(limb, afflictionType: "damage"));
} }
return Math.Clamp(sum, 0, 1f); return Math.Clamp(sum, 0, 1f);
} }
@@ -2895,6 +2898,7 @@ namespace Barotrauma
{ {
SelectedConstruction = null; SelectedConstruction = null;
} }
HealthUpdateInterval = 0.0f;
} }
private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>(); private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
@@ -2956,6 +2960,7 @@ namespace Barotrauma
} }
CharacterHealth.ApplyAffliction(null, new Affliction(AfflictionPrefab.Pressure, AfflictionPrefab.Pressure.MaxStrength)); CharacterHealth.ApplyAffliction(null, new Affliction(AfflictionPrefab.Pressure, AfflictionPrefab.Pressure.MaxStrength));
if (isNetworkMessage && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Vitality <= CharacterHealth.MinVitality) { Kill(CauseOfDeathType.Pressure, null, isNetworkMessage: true); }
if (IsDead) if (IsDead)
{ {
BreakJoints(); BreakJoints();
@@ -2988,7 +2993,10 @@ namespace Barotrauma
foreach (var joint in AnimController.LimbJoints) foreach (var joint in AnimController.LimbJoints)
{ {
joint.LimitEnabled = false; if (joint.revoluteJoint != null)
{
joint.revoluteJoint.LimitEnabled = false;
}
} }
} }
@@ -3055,9 +3063,12 @@ namespace Barotrauma
AnimController.ResetPullJoints(); AnimController.ResetPullJoints();
foreach (RevoluteJoint joint in AnimController.LimbJoints) foreach (var joint in AnimController.LimbJoints)
{ {
joint.MotorEnabled = false; if (joint.revoluteJoint != null)
{
joint.revoluteJoint.MotorEnabled = false;
}
} }
if (GameMain.GameSession != null) if (GameMain.GameSession != null)
@@ -3088,7 +3099,11 @@ namespace Barotrauma
foreach (LimbJoint joint in AnimController.LimbJoints) foreach (LimbJoint joint in AnimController.LimbJoints)
{ {
joint.MotorEnabled = true; var revoluteJoint = joint.revoluteJoint;
if (revoluteJoint != null)
{
revoluteJoint.MotorEnabled = true;
}
joint.Enabled = true; joint.Enabled = true;
joint.IsSevered = false; joint.IsSevered = false;
} }
@@ -252,8 +252,8 @@ namespace Barotrauma
: afflictions.Where(limbHealthFilter).Union(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter))); : afflictions.Where(limbHealthFilter).Union(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter)));
} }
private LimbHealth GetMatchingLimbHealth(Limb limb) => limbHealths[limb.HealthIndex]; private LimbHealth GetMatchingLimbHealth(Limb limb) => limb == null ? null : limbHealths[limb.HealthIndex];
private LimbHealth GetMatchingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb)); private LimbHealth GetMatchingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb, excludeSevered: false));
/// <summary> /// <summary>
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb. /// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
@@ -515,7 +515,7 @@ namespace Barotrauma
if (Vitality <= MinVitality) { Kill(); } if (Vitality <= MinVitality) { Kill(); }
} }
public float GetLimbDamage(Limb limb) public float GetLimbDamage(Limb limb, string afflictionType = null)
{ {
float damageStrength; float damageStrength;
if (limb.IsSevered) if (limb.IsSevered)
@@ -528,10 +528,17 @@ namespace Barotrauma
// Therefore with e.g. 80 health, the max damage per limb would be 20. // Therefore with e.g. 80 health, the max damage per limb would be 20.
// Having at least 20 damage on both legs would cause maximum limping. // Having at least 20 damage on both legs would cause maximum limping.
float max = MaxVitality / 4; float max = MaxVitality / 4;
float damage = GetAfflictionStrength("damage", limb, true); if (string.IsNullOrEmpty(afflictionType))
float bleeding = GetAfflictionStrength("bleeding", limb, true); {
float burn = GetAfflictionStrength("burn", limb, true); float damage = GetAfflictionStrength("damage", limb, true);
damageStrength = Math.Min(damage + bleeding + burn, max); float bleeding = GetAfflictionStrength("bleeding", limb, true);
float burn = GetAfflictionStrength("burn", limb, true);
damageStrength = Math.Min(damage + bleeding + burn, max);
}
else
{
damageStrength = Math.Min(GetAfflictionStrength("damage", limb, true), max);
}
return damageStrength / max; return damageStrength / max;
} }
} }
@@ -163,6 +163,7 @@ namespace Barotrauma
} }
public Sprite Icon; public Sprite Icon;
public Sprite IconSmall;
public string FilePath { get; private set; } public string FilePath { get; private set; }
public XElement Element { get; private set; } public XElement Element { get; private set; }
@@ -207,6 +208,9 @@ namespace Barotrauma
case "jobicon": case "jobicon":
Icon = new Sprite(subElement.FirstElement()); Icon = new Sprite(subElement.FirstElement());
break; break;
case "jobiconsmall":
IconSmall = new Sprite(subElement.FirstElement());
break;
} }
} }
@@ -19,8 +19,8 @@ namespace Barotrauma
None, LeftHand, RightHand, LeftArm, RightArm, LeftForearm, RightForearm, None, LeftHand, RightHand, LeftArm, RightArm, LeftForearm, RightForearm,
LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist, Jaw LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist, Jaw
}; };
partial class LimbJoint : RevoluteJoint partial class LimbJoint
{ {
public bool IsSevered; public bool IsSevered;
public bool CanBeSevered => Params.CanBeSevered; public bool CanBeSevered => Params.CanBeSevered;
@@ -30,27 +30,135 @@ namespace Barotrauma
public float Scale => Params.Scale * ragdoll.RagdollParams.JointScale; public float Scale => Params.Scale * ragdoll.RagdollParams.JointScale;
public LimbJoint(Limb limbA, Limb limbB, JointParams jointParams, Ragdoll ragdoll) : this(limbA, limbB, Vector2.One, Vector2.One) public readonly RevoluteJoint revoluteJoint;
public readonly WeldJoint weldJoint;
public Joint Joint => revoluteJoint ?? weldJoint as Joint;
public bool Enabled
{
get => Joint.Enabled;
set => Joint.Enabled = value;
}
public Body BodyA => Joint.BodyA;
public Body BodyB => Joint.BodyB;
public Vector2 WorldAnchorA
{
get => Joint.WorldAnchorA;
set => Joint.WorldAnchorA = value;
}
public Vector2 WorldAnchorB
{
get => Joint.WorldAnchorB;
set => Joint.WorldAnchorB = value;
}
public Vector2 LocalAnchorA
{
get => revoluteJoint != null ? revoluteJoint.LocalAnchorA : weldJoint.LocalAnchorA;
set
{
if (weldJoint != null)
{
weldJoint.LocalAnchorA = value;
}
else
{
revoluteJoint.LocalAnchorA = value;
}
}
}
public Vector2 LocalAnchorB
{
get => revoluteJoint != null ? revoluteJoint.LocalAnchorB : weldJoint.LocalAnchorB;
set
{
if (weldJoint != null)
{
weldJoint.LocalAnchorB = value;
}
else
{
revoluteJoint.LocalAnchorB = value;
}
}
}
public bool LimitEnabled
{
get => revoluteJoint != null ? revoluteJoint.LimitEnabled : false;
set
{
if (revoluteJoint != null)
{
revoluteJoint.LimitEnabled = value;
}
}
}
public float LowerLimit
{
get => revoluteJoint != null ? revoluteJoint.LowerLimit : 0;
set
{
if (revoluteJoint != null)
{
revoluteJoint.LowerLimit = value;
}
}
}
public float UpperLimit
{
get => revoluteJoint != null ? revoluteJoint.UpperLimit : 0;
set
{
if (revoluteJoint != null)
{
revoluteJoint.UpperLimit = value;
}
}
}
public float JointAngle => revoluteJoint != null ? revoluteJoint.JointAngle : weldJoint.ReferenceAngle;
public LimbJoint(Limb limbA, Limb limbB, JointParams jointParams, Ragdoll ragdoll) : this(limbA, limbB, Vector2.One, Vector2.One, jointParams.WeldJoint)
{ {
Params = jointParams; Params = jointParams;
this.ragdoll = ragdoll; this.ragdoll = ragdoll;
LoadParams(); LoadParams();
} }
public LimbJoint(Limb limbA, Limb limbB, Vector2 anchor1, Vector2 anchor2) public LimbJoint(Limb limbA, Limb limbB, Vector2 anchor1, Vector2 anchor2, bool weld = false)
: base(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2)
{ {
CollideConnected = false; if (weld)
MotorEnabled = true; {
MaxMotorTorque = 0.25f; weldJoint = new WeldJoint(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2);
}
else
{
revoluteJoint = new RevoluteJoint(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2)
{
MotorEnabled = true,
MaxMotorTorque = 0.25f
};
}
Joint.CollideConnected = false;
LimbA = limbA; LimbA = limbA;
LimbB = limbB; LimbB = limbB;
} }
public void LoadParams() public void LoadParams()
{ {
MaxMotorTorque = Params.Stiffness; if (revoluteJoint != null)
LimitEnabled = Params.LimitEnabled; {
revoluteJoint.MaxMotorTorque = Params.Stiffness;
revoluteJoint.LimitEnabled = Params.LimitEnabled;
}
if (float.IsNaN(Params.LowerLimit)) if (float.IsNaN(Params.LowerLimit))
{ {
Params.LowerLimit = 0; Params.LowerLimit = 0;
@@ -61,17 +169,33 @@ namespace Barotrauma
} }
if (ragdoll.IsFlipped) if (ragdoll.IsFlipped)
{ {
LocalAnchorA = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb1Anchor.X, Params.Limb1Anchor.Y) * Scale); if (weldJoint != null)
LocalAnchorB = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb2Anchor.X, Params.Limb2Anchor.Y) * Scale); {
UpperLimit = MathHelper.ToRadians(-Params.LowerLimit); weldJoint.LocalAnchorA = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb1Anchor.X, Params.Limb1Anchor.Y) * Scale);
LowerLimit = MathHelper.ToRadians(-Params.UpperLimit); weldJoint.LocalAnchorB = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb2Anchor.X, Params.Limb2Anchor.Y) * Scale);
}
else
{
revoluteJoint.LocalAnchorA = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb1Anchor.X, Params.Limb1Anchor.Y) * Scale);
revoluteJoint.LocalAnchorB = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb2Anchor.X, Params.Limb2Anchor.Y) * Scale);
revoluteJoint.UpperLimit = MathHelper.ToRadians(-Params.LowerLimit);
revoluteJoint.LowerLimit = MathHelper.ToRadians(-Params.UpperLimit);
}
} }
else else
{ {
LocalAnchorA = ConvertUnits.ToSimUnits(Params.Limb1Anchor * Scale); if (weldJoint != null)
LocalAnchorB = ConvertUnits.ToSimUnits(Params.Limb2Anchor * Scale); {
UpperLimit = MathHelper.ToRadians(Params.UpperLimit); weldJoint.LocalAnchorA = ConvertUnits.ToSimUnits(Params.Limb1Anchor * Scale);
LowerLimit = MathHelper.ToRadians(Params.LowerLimit); weldJoint.LocalAnchorB = ConvertUnits.ToSimUnits(Params.Limb2Anchor * Scale);
}
else
{
revoluteJoint.LocalAnchorA = ConvertUnits.ToSimUnits(Params.Limb1Anchor * Scale);
revoluteJoint.LocalAnchorB = ConvertUnits.ToSimUnits(Params.Limb2Anchor * Scale);
revoluteJoint.UpperLimit = MathHelper.ToRadians(Params.UpperLimit);
revoluteJoint.LowerLimit = MathHelper.ToRadians(Params.LowerLimit);
}
} }
} }
} }
@@ -470,7 +470,7 @@ namespace Barotrauma
[Serialize(true, true), Editable] [Serialize(true, true), Editable]
public bool CanBeSevered { get; set; } public bool CanBeSevered { get; set; }
[Serialize(1f, true, description:"Modifies the severance probability (defined per item/attack) when the character is alive. Currently only affects limbs of type None, Shield, or Tail on non-humanoid ragdolls. Also note that if CanBeSevered is false, this property doesn't have any effect."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)] [Serialize(0f, true, description:"Default 0 (Can't be severed when the creature is alive). Modifies the severance probability (defined per item/attack) when the character is alive. Currently only affects non-humanoid ragdolls. Also note that if CanBeSevered is false, this property doesn't have any effect."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)]
public float SeveranceProbabilityModifier { get; set; } public float SeveranceProbabilityModifier { get; set; }
[Serialize("gore", true), Editable] [Serialize("gore", true), Editable]
@@ -497,6 +497,9 @@ namespace Barotrauma
[Serialize(1f, true, description: "CAUTION: Not fully implemented. Only use for limb joints that connect non-animated limbs!"), Editable] [Serialize(1f, true, description: "CAUTION: Not fully implemented. Only use for limb joints that connect non-animated limbs!"), Editable]
public float Scale { get; set; } public float Scale { get; set; }
[Serialize(false, false), Editable(ReadOnly = true)]
public bool WeldJoint { get; set; }
public JointParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll) { } public JointParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
} }
@@ -650,22 +650,30 @@ namespace Barotrauma
public static void SortContentPackages() public static void SortContentPackages()
{ {
List = List
.OrderByDescending(p => p.CorePackage)
.ThenBy(p => List.IndexOf(p))
.ToList();
if (GameMain.Config != null) if (GameMain.Config != null)
{ {
List = List
.OrderByDescending(p => p.CorePackage)
.ThenBy(p => GameMain.Config.SelectedContentPackages.IndexOf(p))
.ThenBy(p => List.IndexOf(p))
.ToList();
var sortedSelected = GameMain.Config.SelectedContentPackages var sortedSelected = GameMain.Config.SelectedContentPackages
.OrderByDescending(p => p.CorePackage) .OrderByDescending(p => p.CorePackage)
.ThenBy(p => List.IndexOf(p)) .ThenBy(p => GameMain.Config.SelectedContentPackages.IndexOf(p))
.ToList(); .ToList();
GameMain.Config.SelectedContentPackages.Clear(); GameMain.Config.SelectedContentPackages.AddRange(sortedSelected); GameMain.Config.SelectedContentPackages.Clear(); GameMain.Config.SelectedContentPackages.AddRange(sortedSelected);
var reportList = List.Where(p => GameMain.Config.SelectedContentPackages.Contains(p)); var reportList = GameMain.Config.SelectedContentPackages;
DebugConsole.NewMessage($"Content package load order: { string.Join(" | ", reportList.Select(cp => cp.Name)) }"); DebugConsole.NewMessage($"Content package load order: { string.Join(" | ", reportList.Select(cp => cp.Name)) }");
} }
else
{
List = List
.OrderByDescending(p => p.CorePackage)
.ThenBy(p => List.IndexOf(p))
.ToList();
}
} }
public void Delete() public void Delete()
@@ -292,7 +292,7 @@ namespace Barotrauma
commands.Add(new Command("startwhenclientsready", "startwhenclientsready [true/false]: Enable or disable automatically starting the round when clients are ready to start.", null)); commands.Add(new Command("startwhenclientsready", "startwhenclientsready [true/false]: Enable or disable automatically starting the round when clients are ready to start.", null));
commands.Add(new Command("giveperm", "giveperm [id]: Grants administrative permissions to the player with the specified client ID.", null, commands.Add(new Command("giveperm", "giveperm [id/steamid/endpoint/name]: Grants administrative permissions to the specified client.", null,
() => () =>
{ {
if (GameMain.NetworkMember == null) return null; if (GameMain.NetworkMember == null) return null;
@@ -304,7 +304,7 @@ namespace Barotrauma
}; };
})); }));
commands.Add(new Command("revokeperm", "revokeperm [id]: Revokes administrative permissions to the player with the specified client ID.", null, commands.Add(new Command("revokeperm", "revokeperm [id/steamid/endpoint/name]: Revokes administrative permissions from the specified client.", null,
() => () =>
{ {
if (GameMain.NetworkMember == null) return null; if (GameMain.NetworkMember == null) return null;
@@ -316,7 +316,7 @@ namespace Barotrauma
}; };
})); }));
commands.Add(new Command("giverank", "giverank [id]: Assigns a specific rank (= a set of administrative permissions) to the player with the specified client ID.", null, commands.Add(new Command("giverank", "giverank [id/steamid/endpoint/name]: Assigns a specific rank (= a set of administrative permissions) to the specified client.", null,
() => () =>
{ {
if (GameMain.NetworkMember == null) return null; if (GameMain.NetworkMember == null) return null;
@@ -328,12 +328,41 @@ namespace Barotrauma
}; };
})); }));
commands.Add(new Command("givecommandperm", "givecommandperm [id]: Gives the player with the specified client ID the permission to use the specified console commands.", null)); commands.Add(new Command("givecommandperm", "givecommandperm [id/steamid/endpoint/name]: Gives the specified client the permission to use the specified console commands.", null,
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
commands.Select(c => c.names[0]).ToArray()
};
}));
commands.Add(new Command("revokecommandperm", "revokecommandperm [id/steamid/endpoint/name]: Revokes permission to use the specified console commands from the specified client.", null,
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
new string[0]
};
}));
commands.Add(new Command("showperm", "showperm [id/steamid/endpoint/name]: Shows the current administrative permissions of the specified client.", null,
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray()
};
}));
commands.Add(new Command("revokecommandperm", "revokecommandperm [id]: Revokes permission to use the specified console commands from the player with the specified client ID.", null));
commands.Add(new Command("showperm", "showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", null));
commands.Add(new Command("respawnnow", "respawnnow: Trigger a respawn immediately if there are any clients waiting to respawn.", null)); commands.Add(new Command("respawnnow", "respawnnow: Trigger a respawn immediately if there are any clients waiting to respawn.", null));
commands.Add(new Command("showkarma", "showkarma: Show the current karma values of the players.", null)); commands.Add(new Command("showkarma", "showkarma: Show the current karma values of the players.", null));
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -10,6 +11,8 @@ namespace Barotrauma
private readonly XElement itemConfig; private readonly XElement itemConfig;
private readonly List<Item> items = new List<Item>(); private readonly List<Item> items = new List<Item>();
private readonly Dictionary<Item, UInt16> itemIDs = new Dictionary<Item, UInt16>();
private readonly Dictionary<Item, UInt16> parentInventoryIDs = new Dictionary<Item, UInt16>();
private int requiredDeliveryAmount; private int requiredDeliveryAmount;
@@ -23,6 +26,8 @@ namespace Barotrauma
private void InitItems() private void InitItems()
{ {
items.Clear(); items.Clear();
itemIDs.Clear();
parentInventoryIDs.Clear();
if (itemConfig == null) if (itemConfig == null)
{ {
@@ -91,8 +96,13 @@ namespace Barotrauma
var item = new Item(itemPrefab, position, cargoRoom.Submarine); var item = new Item(itemPrefab, position, cargoRoom.Submarine);
item.FindHull(); item.FindHull();
items.Add(item); items.Add(item);
itemIDs.Add(item, item.ID);
if (parent != null) parent.Combine(item, user: null);
if (parent != null)
{
parentInventoryIDs.Add(item, parent.ID);
parent.Combine(item, user: null);
}
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
@@ -103,6 +103,10 @@ namespace Barotrauma
public override void Start(Level level) public override void Start(Level level)
{ {
#if SERVER
originalItemID = Entity.NullEntityID;
originalInventoryID = Entity.NullEntityID;
#endif
if (!IsClient) if (!IsClient)
{ {
//ruin/wreck items are allowed to spawn close to the sub //ruin/wreck items are allowed to spawn close to the sub
@@ -147,6 +151,9 @@ namespace Barotrauma
item.body.FarseerBody.BodyType = BodyType.Kinematic; item.body.FarseerBody.BodyType = BodyType.Kinematic;
item.FindHull(); item.FindHull();
} }
#if SERVER
originalItemID = item.ID;
#endif
for (int i = 0; i < statusEffects.Count; i++) for (int i = 0; i < statusEffects.Count; i++)
{ {
@@ -181,7 +188,13 @@ namespace Barotrauma
} }
var itemContainer = it.GetComponent<Items.Components.ItemContainer>(); var itemContainer = it.GetComponent<Items.Components.ItemContainer>();
if (itemContainer == null) { continue; } if (itemContainer == null) { continue; }
if (itemContainer.Combine(item, user: null)) { break; } // Placement successful if (itemContainer.Combine(item, user: null))
{
#if SERVER
originalInventoryID = it.ID;
#endif
break;
} // Placement successful
} }
} }
} }
@@ -1,13 +1,28 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
namespace Barotrauma namespace Barotrauma
{ {
class ScriptedEventSet class ScriptedEventSet
{ {
internal class EventDebugStats
{
public readonly ScriptedEventSet RootSet;
public readonly Dictionary<string, int> MonsterCounts = new Dictionary<string, int>();
public EventDebugStats(ScriptedEventSet rootSet)
{
RootSet = rootSet;
}
}
public static List<ScriptedEventSet> List public static List<ScriptedEventSet> List
{ {
get; get;
@@ -131,5 +146,115 @@ namespace Barotrauma
} }
} }
} }
public static List<string> GetDebugStatistics(int simulatedRoundCount = 100)
{
List<string> debugLines = new List<string>();
foreach (var eventSet in List)
{
List<EventDebugStats> stats = new List<EventDebugStats>();
for (int i = 0; i < simulatedRoundCount; i++)
{
var newStats = new EventDebugStats(eventSet);
CheckEventSet(newStats, eventSet);
stats.Add(newStats);
}
debugLines.Add($"Event stats ({eventSet.DebugIdentifier}): ");
LogEventStats(stats, debugLines);
}
for (int difficulty = 0; difficulty <= 100; difficulty += 10)
{
debugLines.Add($"Event stats on difficulty level {difficulty}: ");
List<EventDebugStats> stats = new List<EventDebugStats>();
for (int i = 0; i < simulatedRoundCount; i++)
{
ScriptedEventSet selectedSet = List.Where(s => difficulty >= s.MinLevelDifficulty && difficulty <= s.MaxLevelDifficulty).GetRandom();
if (selectedSet == null) { continue; }
var newStats = new EventDebugStats(selectedSet);
CheckEventSet(newStats, selectedSet);
stats.Add(newStats);
}
LogEventStats(stats, debugLines);
}
return debugLines;
static void CheckEventSet(EventDebugStats stats, ScriptedEventSet thisSet)
{
if (thisSet.ChooseRandom)
{
var eventPrefab = ToolBox.SelectWeightedRandom(thisSet.EventPrefabs, thisSet.EventPrefabs.Select(e => e.Commonness).ToList(), Rand.RandSync.Unsynced);
if (eventPrefab != null)
{
AddEvent(stats, eventPrefab);
}
}
else
{
foreach (var eventPrefab in thisSet.EventPrefabs)
{
AddEvent(stats, eventPrefab);
}
}
foreach (var childSet in thisSet.ChildSets)
{
CheckEventSet(stats, childSet);
}
}
static void AddEvent(EventDebugStats stats, ScriptedEventPrefab eventPrefab)
{
if (eventPrefab.EventType == typeof(MonsterEvent))
{
float spawnProbability = eventPrefab.ConfigElement.GetAttributeFloat("spawnprobability", 1.0f);
if (Rand.Value(Rand.RandSync.Server) > spawnProbability)
{
return;
}
string character = eventPrefab.ConfigElement.GetAttributeString("characterfile", "");
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(character));
int amount = eventPrefab.ConfigElement.GetAttributeInt("amount", 0);
int minAmount = eventPrefab.ConfigElement.GetAttributeInt("minamount", amount);
int maxAmount = eventPrefab.ConfigElement.GetAttributeInt("maxamount", amount);
int count = Rand.Range(minAmount, maxAmount + 1);
if (count <= 0) { return; }
if (!stats.MonsterCounts.ContainsKey(character)) { stats.MonsterCounts[character] = 0; }
stats.MonsterCounts[character] += count;
}
}
static void LogEventStats(List<EventDebugStats> stats, List<string> debugLines)
{
if (stats.Count == 0 || stats.All(s => s.MonsterCounts.Values.Sum() == 0))
{
debugLines.Add(" No monster spawns");
debugLines.Add($" ");
}
else
{
stats.Sort((s1, s2) => { return s1.MonsterCounts.Values.Sum().CompareTo(s2.MonsterCounts.Values.Sum()); });
EventDebugStats minStats = stats.First();
EventDebugStats maxStats = stats.First();
debugLines.Add($" Minimum monster spawns: {stats.First().MonsterCounts.Values.Sum()}");
debugLines.Add($" {LogMonsterCounts(stats.First())}");
debugLines.Add($" Median monster spawns: {stats[stats.Count / 2].MonsterCounts.Values.Sum()}");
debugLines.Add($" {LogMonsterCounts(stats[stats.Count / 2])}");
debugLines.Add($" Maximum monster spawns: {stats.Last().MonsterCounts.Values.Sum()}");
debugLines.Add($" {LogMonsterCounts(stats.Last())}");
debugLines.Add($" ");
}
}
static string LogMonsterCounts(EventDebugStats stats)
{
return string.Join(", ", stats.MonsterCounts.Select(mc => mc.Key + " x " + mc.Value));
}
}
} }
} }
@@ -159,6 +159,15 @@ namespace Barotrauma
return false; return false;
#endif #endif
} }
if (item.Removed)
{
#if DEBUG
throw new Exception("Tried to put a removed item (" + item.Name + ") in an inventory");
#else
DebugConsole.ThrowError("Tried to put a removed item (" + item.Name + ") in an inventory.\n" + Environment.StackTrace);
return false;
#endif
}
bool inSuitableSlot = false; bool inSuitableSlot = false;
bool inWrongSlot = false; bool inWrongSlot = false;
@@ -192,6 +201,9 @@ namespace Barotrauma
int placedInSlot = -1; int placedInSlot = -1;
foreach (InvSlotType allowedSlot in allowedSlots) foreach (InvSlotType allowedSlot in allowedSlots)
{ {
if (allowedSlot.HasFlag(InvSlotType.RightHand) && character.AnimController.GetLimb(LimbType.RightHand) == null) { continue; }
if (allowedSlot.HasFlag(InvSlotType.LeftHand) && character.AnimController.GetLimb(LimbType.LeftHand) == null) { continue; }
//check if all the required slots are free //check if all the required slots are free
bool free = true; bool free = true;
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
@@ -205,18 +205,6 @@ namespace Barotrauma.Items.Components
DockingDir = GetDir(DockingTarget); DockingDir = GetDir(DockingTarget);
DockingTarget.DockingDir = -DockingDir; DockingTarget.DockingDir = -DockingDir;
if (door != null && DockingTarget.door != null)
{
WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => DockingTarget.door.LinkedGap == wp.ConnectedGap);
if (myWayPoint != null && targetWayPoint != null)
{
myWayPoint.linkedTo.Add(targetWayPoint);
targetWayPoint.linkedTo.Add(myWayPoint);
}
}
CreateJoint(false); CreateJoint(false);
#if SERVER #if SERVER
@@ -283,6 +271,20 @@ namespace Barotrauma.Items.Components
{ {
CreateHulls(); CreateHulls();
} }
if (door != null && DockingTarget.door != null)
{
WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => DockingTarget.door.LinkedGap == wp.ConnectedGap);
if (myWayPoint != null && targetWayPoint != null)
{
myWayPoint.FindHull();
myWayPoint.linkedTo.Add(targetWayPoint);
targetWayPoint.FindHull();
targetWayPoint.linkedTo.Add(myWayPoint);
}
}
} }
@@ -778,7 +780,9 @@ namespace Barotrauma.Items.Components
if (myWayPoint != null && targetWayPoint != null) if (myWayPoint != null && targetWayPoint != null)
{ {
myWayPoint.FindHull();
myWayPoint.linkedTo.Remove(targetWayPoint); myWayPoint.linkedTo.Remove(targetWayPoint);
targetWayPoint.FindHull();
targetWayPoint.linkedTo.Remove(myWayPoint); targetWayPoint.linkedTo.Remove(myWayPoint);
} }
} }
@@ -331,8 +331,8 @@ namespace Barotrauma.Items.Components
if (!item.body.Enabled) if (!item.body.Enabled)
{ {
Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand); Limb hand = picker.AnimController.GetLimb(LimbType.RightHand) ?? picker.AnimController.GetLimb(LimbType.LeftHand);
item.SetTransform(rightHand.SimPosition, 0.0f); item.SetTransform(hand != null ? hand.SimPosition : character.SimPosition, 0.0f);
} }
bool alreadyEquipped = character.HasEquippedItem(item); bool alreadyEquipped = character.HasEquippedItem(item);
@@ -369,17 +369,19 @@ namespace Barotrauma.Items.Components
IsActive = false; IsActive = false;
} }
public bool CanBeAttached() public bool CanBeAttached(Character user)
{ {
if (!attachable || !Reattachable) { return false; } if (!attachable || !Reattachable) { return false; }
//can be attached anywhere in sub editor //can be attached anywhere in sub editor
if (Screen.Selected == GameMain.SubEditorScreen) { return true; } if (Screen.Selected == GameMain.SubEditorScreen) { return true; }
//can be attached anywhere inside hulls Vector2 attachPos = user == null ? item.WorldPosition : GetAttachPosition(user, useWorldCoordinates: true);
if (item.CurrentHull != null) { return true; }
return Structure.GetAttachTarget(item.WorldPosition) != null; //can be attached anywhere inside hulls
if (item.CurrentHull != null && Submarine.RectContains(item.CurrentHull.WorldRect, attachPos)) { return true; }
return Structure.GetAttachTarget(attachPos) != null;
} }
public bool CanBeDeattached() public bool CanBeDeattached()
@@ -396,8 +398,14 @@ namespace Barotrauma.Items.Components
return false; return false;
} }
//don't allow deattaching if part of a sub and outside hulls if (item.CurrentHull == null)
return item.Submarine == null || item.CurrentHull != null; {
return Structure.GetAttachTarget(item.WorldPosition) != null;
}
else
{
return true;
}
} }
public override bool Pick(Character picker) public override bool Pick(Character picker)
@@ -505,7 +513,7 @@ namespace Barotrauma.Items.Components
if (character != null) if (character != null)
{ {
if (!character.IsKeyDown(InputType.Aim)) { return false; } if (!character.IsKeyDown(InputType.Aim)) { return false; }
if (!CanBeAttached()) { return false; } if (!CanBeAttached(character)) { return false; }
if (GameMain.NetworkMember != null) if (GameMain.NetworkMember != null)
{ {
@@ -534,7 +542,7 @@ namespace Barotrauma.Items.Components
else else
{ {
item.Drop(character); item.Drop(character);
item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f); item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false);
} }
} }
@@ -543,16 +551,18 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
private Vector2 GetAttachPosition(Character user) private Vector2 GetAttachPosition(Character user, bool useWorldCoordinates = false)
{ {
if (user == null) { return item.Position; } if (user == null) { return useWorldCoordinates ? item.WorldPosition : item.Position; }
Vector2 mouseDiff = user.CursorWorldPosition - user.WorldPosition; Vector2 mouseDiff = user.CursorWorldPosition - user.WorldPosition;
mouseDiff = mouseDiff.ClampLength(MaxAttachDistance); mouseDiff = mouseDiff.ClampLength(MaxAttachDistance);
Vector2 userPos = useWorldCoordinates ? user.WorldPosition : user.Position;
return new Vector2( return new Vector2(
MathUtils.RoundTowardsClosest(user.Position.X + mouseDiff.X, Submarine.GridSize.X), MathUtils.RoundTowardsClosest(userPos.X + mouseDiff.X, Submarine.GridSize.X),
MathUtils.RoundTowardsClosest(user.Position.Y + mouseDiff.Y, Submarine.GridSize.Y)); MathUtils.RoundTowardsClosest(userPos.Y + mouseDiff.Y, Submarine.GridSize.Y));
} }
public override void UpdateBroken(float deltaTime, Camera cam) public override void UpdateBroken(float deltaTime, Camera cam)
@@ -114,10 +114,6 @@ namespace Barotrauma.Items.Components
{ {
activePicker = picker; activePicker = picker;
picker.PickingItem = item; picker.PickingItem = item;
var leftHand = picker.AnimController.GetLimb(LimbType.LeftHand);
var rightHand = picker.AnimController.GetLimb(LimbType.RightHand);
pickTimer = 0.0f; pickTimer = 0.0f;
while (pickTimer < requiredTime && Screen.Selected != GameMain.SubEditorScreen) while (pickTimer < requiredTime && Screen.Selected != GameMain.SubEditorScreen)
{ {
@@ -326,6 +326,18 @@ namespace Barotrauma.Items.Components
{ {
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return false; } if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return false; }
if (f.Body?.UserData as string == "ruinroom") { return false; } if (f.Body?.UserData as string == "ruinroom") { return false; }
if (f.Body?.UserData is Item targetItem)
{
if (!HitItems) { return false; }
if (HitBrokenDoors)
{
if (targetItem.GetComponent<Door>() == null && targetItem.Condition <= 0) { return false; }
}
else
{
if (targetItem.Condition <= 0) { return false; }
}
}
return f.Body?.UserData != null; return f.Body?.UserData != null;
}, },
allowInsideFixture: true)); allowInsideFixture: true));
@@ -93,8 +93,8 @@ namespace Barotrauma.Items.Components
controlLockTimer -= deltaTime; controlLockTimer -= deltaTime;
currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption; currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption;
//pumps consume more power when in a bad condition //engines consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (powerConsumption == 0.0f) { Voltage = 1.0f; } if (powerConsumption == 0.0f) { Voltage = 1.0f; }
@@ -171,7 +171,7 @@ namespace Barotrauma.Items.Components
outputContainer.Inventory.Locked = true; outputContainer.Inventory.Locked = true;
currPowerConsumption = powerConsumption; currPowerConsumption = powerConsumption;
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (GameMain.NetworkMember?.IsServer ?? true) if (GameMain.NetworkMember?.IsServer ?? true)
{ {
@@ -42,7 +42,7 @@ namespace Barotrauma.Items.Components
CurrFlow = 0.0f; CurrFlow = 0.0f;
currPowerConsumption = powerConsumption; currPowerConsumption = powerConsumption;
//consume more power when in a bad condition //consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (powerConsumption <= 0.0f) if (powerConsumption <= 0.0f)
{ {
@@ -69,7 +69,7 @@ namespace Barotrauma.Items.Components
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f); currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
//pumps consume more power when in a bad condition //pumps consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (!HasPower) { return; } if (!HasPower) { return; }
@@ -298,10 +298,7 @@ namespace Barotrauma.Items.Components
{ {
if (user != null && user.Info != null && user.SelectedConstruction == item) if (user != null && user.Info != null && user.SelectedConstruction == item)
{ {
user.Info.IncreaseSkillLevel( IncreaseSkillLevel(user, deltaTime);
"helm",
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / Math.Max(userSkill, 1.0f) * deltaTime,
user.WorldPosition + Vector2.UnitY * 150.0f);
} }
Vector2 velocityDiff = steeringInput - targetVelocity; Vector2 velocityDiff = steeringInput - targetVelocity;
@@ -330,6 +327,18 @@ namespace Barotrauma.Items.Components
item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_y_out", null); item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_y_out", null);
} }
private void IncreaseSkillLevel(Character user, float deltaTime)
{
if (user?.Info == null) { return; }
float userSkill = user.GetSkillLevel("helm") / 100.0f;
user.Info.IncreaseSkillLevel(
"helm",
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / Math.Max(userSkill, 1.0f) * deltaTime,
user.WorldPosition + Vector2.UnitY * 150.0f);
}
private void UpdateAutoPilot(float deltaTime) private void UpdateAutoPilot(float deltaTime)
{ {
if (controlledSub == null) { return; } if (controlledSub == null) { return; }
@@ -565,6 +574,7 @@ namespace Barotrauma.Items.Components
unsentChanges = true; unsentChanges = true;
AutoPilot = true; AutoPilot = true;
} }
IncreaseSkillLevel(user, deltaTime);
switch (objective.Option.ToLowerInvariant()) switch (objective.Option.ToLowerInvariant())
{ {
case "maintainposition": case "maintainposition":
@@ -55,8 +55,8 @@ namespace Barotrauma.Items.Components
set; set;
} }
[Serialize(80.0f, true, description: "The condition of the item has to be below this for AI characters to repair it. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)] [Serialize(80.0f, true, description: "The condition of the item has to be below this for it to become repairable. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
public float AIRepairThreshold public float RepairThreshold
{ {
get; get;
set; set;
@@ -112,13 +112,14 @@ namespace Barotrauma.Items.Components
element.GetAttributeString("name", ""); element.GetAttributeString("name", "");
//backwards compatibility //backwards compatibility
var showRepairUIAttribute = element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase)); var repairThresholdAttribute =
if (showRepairUIAttribute != null) element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase)) ??
element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("airepairth44reshold", StringComparison.OrdinalIgnoreCase));
if (repairThresholdAttribute != null)
{ {
float repairThreshold; if (float.TryParse(repairThresholdAttribute.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out float repairThreshold))
if (Single.TryParse(showRepairUIAttribute.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out repairThreshold))
{ {
AIRepairThreshold = repairThreshold; RepairThreshold = repairThreshold;
} }
} }
@@ -273,7 +274,7 @@ namespace Barotrauma.Items.Components
float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills); float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills);
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it //item must have been below the repair threshold for the player to get an achievement or XP for repairing it
if (item.ConditionPercentage < AIRepairThreshold) if (item.ConditionPercentage < RepairThreshold)
{ {
wasBroken = true; wasBroken = true;
} }
@@ -357,6 +358,14 @@ namespace Barotrauma.Items.Components
partial void UpdateProjSpecific(float deltaTime); partial void UpdateProjSpecific(float deltaTime);
public void AdjustPowerConsumption(ref float powerConsumption)
{
if (item.ConditionPercentage < RepairThreshold)
{
powerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
}
}
private bool ShouldDeteriorate() private bool ShouldDeteriorate()
{ {
if (LastActiveTime > Timing.TotalTime) { return true; } if (LastActiveTime > Timing.TotalTime) { return true; }
@@ -182,8 +182,6 @@ namespace Barotrauma.Items.Components
newConnection.Item.Position : newConnection.Item.Position :
newConnection.Item.Position - refSub.HiddenSubPosition; newConnection.Item.Position - refSub.HiddenSubPosition;
nodePos = RoundNode(nodePos);
if (nodes.Count > 0 && nodes[0] == nodePos) { break; } if (nodes.Count > 0 && nodes[0] == nodePos) { break; }
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { break; } if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { break; }
@@ -2111,13 +2111,19 @@ namespace Barotrauma
public void Equip(Character character) public void Equip(Character character)
{ {
foreach (ItemComponent ic in components) ic.Equip(character); if (Removed)
{
DebugConsole.ThrowError($"Tried to equip a removed item ({Name}).\n{Environment.StackTrace}");
return;
}
foreach (ItemComponent ic in components) { ic.Equip(character); }
} }
public void Unequip(Character character) public void Unequip(Character character)
{ {
character.DeselectItem(this); character.DeselectItem(this);
foreach (ItemComponent ic in components) ic.Unequip(character); foreach (ItemComponent ic in components) { ic.Unequip(character); }
} }
public List<Pair<object, SerializableProperty>> GetProperties<T>() public List<Pair<object, SerializableProperty>> GetProperties<T>()
@@ -136,7 +136,7 @@ namespace Barotrauma
if (powered == null || !powered.VulnerableToEMP) continue; if (powered == null || !powered.VulnerableToEMP) continue;
if (item.Repairables.Any()) if (item.Repairables.Any())
{ {
item.Condition -= 100 * EmpStrength * distFactor; item.Condition -= item.MaxCondition * EmpStrength * distFactor;
} }
//discharge batteries //discharge batteries
@@ -10,11 +10,14 @@ namespace Barotrauma
{ {
partial class ItemAssemblyPrefab : MapEntityPrefab partial class ItemAssemblyPrefab : MapEntityPrefab
{ {
private string name; private readonly string name;
public override string Name { get { return name; } } public override string Name { get { return name; } }
public static readonly PrefabCollection<ItemAssemblyPrefab> Prefabs = new PrefabCollection<ItemAssemblyPrefab>(); public static readonly PrefabCollection<ItemAssemblyPrefab> Prefabs = new PrefabCollection<ItemAssemblyPrefab>();
public static readonly string VanillaSaveFolder = Path.Combine("Content", "Items", "Assemblies");
public static readonly string SaveFolder = "ItemAssemblies";
private bool disposed = false; private bool disposed = false;
public override void Dispose() public override void Dispose()
{ {
@@ -144,11 +147,14 @@ namespace Barotrauma
List<string> itemAssemblyFiles = new List<string>(); List<string> itemAssemblyFiles = new List<string>();
//find assembly files in the item assembly folder //find assembly files in the item assembly folders
string directoryPath = Path.Combine("Content", "Items", "Assemblies"); if (Directory.Exists(VanillaSaveFolder))
if (Directory.Exists(directoryPath))
{ {
itemAssemblyFiles.AddRange(Directory.GetFiles(directoryPath)); itemAssemblyFiles.AddRange(Directory.GetFiles(VanillaSaveFolder));
}
if (Directory.Exists(SaveFolder))
{
itemAssemblyFiles.AddRange(Directory.GetFiles(SaveFolder));
} }
//find assembly files in selected content packages //find assembly files in selected content packages
@@ -174,7 +174,11 @@ namespace Barotrauma
int newWidth = ResizeHorizontal ? rect.Width : (int)(defaultRect.Width * relativeScale); int newWidth = ResizeHorizontal ? rect.Width : (int)(defaultRect.Width * relativeScale);
int newHeight = ResizeVertical ? rect.Height : (int)(defaultRect.Height * relativeScale); int newHeight = ResizeVertical ? rect.Height : (int)(defaultRect.Height * relativeScale);
Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight); Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight);
if (Sections != null) if (StairDirection != Direction.None)
{
CreateStairBodies();
}
else if (Sections != null)
{ {
UpdateSections(); UpdateSections();
} }
@@ -431,6 +435,7 @@ namespace Barotrauma
private void CreateStairBodies() private void CreateStairBodies()
{ {
Bodies = new List<Body>(); Bodies = new List<Body>();
bodyDebugDimensions.Clear();
float stairAngle = MathHelper.ToRadians(Math.Min(Prefab.StairAngle, 75.0f)); float stairAngle = MathHelper.ToRadians(Math.Min(Prefab.StairAngle, 75.0f));
@@ -448,7 +453,7 @@ namespace Barotrauma
newBody.Friction = 0.8f; newBody.Friction = 0.8f;
newBody.UserData = this; newBody.UserData = this;
newBody.Position = ConvertUnits.ToSimUnits(stairPos) + BodyOffset; newBody.Position = ConvertUnits.ToSimUnits(stairPos) + BodyOffset * Scale;
bodyDebugDimensions.Add(new Vector2(bodyWidth, bodyHeight)); bodyDebugDimensions.Add(new Vector2(bodyWidth, bodyHeight));
@@ -575,12 +580,12 @@ namespace Barotrauma
{ {
foreach (MapEntity mapEntity in mapEntityList) foreach (MapEntity mapEntity in mapEntityList)
{ {
if (!(mapEntity is Structure structure)) continue; if (!(mapEntity is Structure structure)) { continue; }
if (!structure.Prefab.AllowAttachItems) continue; if (!structure.Prefab.AllowAttachItems) { continue; }
if (structure.Bodies != null && structure.Bodies.Count > 0) continue; if (structure.Bodies != null && structure.Bodies.Count > 0) { continue; }
Rectangle worldRect = mapEntity.WorldRect; Rectangle worldRect = mapEntity.WorldRect;
if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) continue; if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) { continue; }
if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) continue; if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) { continue; }
return structure; return structure;
} }
return null; return null;
@@ -817,6 +817,9 @@ namespace Barotrauma
Item.UpdateHulls(); Item.UpdateHulls();
Gap.UpdateHulls(); Gap.UpdateHulls();
#if CLIENT
Lights.ConvexHull.RecalculateAll(this);
#endif
} }
public void Update(float deltaTime) public void Update(float deltaTime)
@@ -372,10 +372,13 @@ namespace Barotrauma
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage=null) public bool SaveAs(string filePath, System.IO.MemoryStream previewImage=null)
{ {
var newElement = new XElement(SubmarineElement.Name, var newElement = new XElement(SubmarineElement.Name,
SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase)), SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase) &&
!string.Equals(a.Name.LocalName, "name", StringComparison.InvariantCultureIgnoreCase)),
SubmarineElement.Elements()); SubmarineElement.Elements());
XDocument doc = new XDocument(newElement); XDocument doc = new XDocument(newElement);
doc.Root.Add(new XAttribute("name", Name));
if (previewImage != null) if (previewImage != null)
{ {
doc.Root.Add(new XAttribute("previewimage", Convert.ToBase64String(previewImage.ToArray()))); doc.Root.Add(new XAttribute("previewimage", Convert.ToBase64String(previewImage.ToArray())));
@@ -594,6 +594,11 @@ namespace Barotrauma
return assignedWayPoints; return assignedWayPoints;
} }
public void FindHull()
{
currentHull = Hull.FindHull(WorldPosition, CurrentHull);
}
public override void OnMapLoaded() public override void OnMapLoaded()
{ {
currentHull = Hull.FindHull(WorldPosition, currentHull); currentHull = Hull.FindHull(WorldPosition, currentHull);
Binary file not shown.
+52 -25
View File
@@ -1,31 +1,18 @@
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
v0.9.1001.0 (Unstable) v0.9.10.0 (Unstable)
---------------------------------------------------------------------------------------------------------
- Overhauled level layouts and events (longer and more difficult levels).
- Added two new afflictions: medical items and poisons cause organ damage instead of internal damage and explosions cause deep tissue injuries. Both are functionally identical to internal damage, and treated with the same items.
- Always draw steering indicators at the center of the display instead of the center of the sub. Fixes indicators getting offset (sometimes even outside the display) during docking.
- Added option to give all command perms with the "givecommandperm" command by using "all" as the parameter.
- Non-raycast projectiles ignore destroyed items (= destroyed thalamus organs don't block harpoons).
- Fixed monsters staying invisible if they die far away from the camera view.
- Fixed very small limbs (mudraptor's mouth tentacles, husk appendages) launching off at a very high velocity, leading to glitchy physics behavior, when hit by a non-raycast projectile or an explosion.
- Allow subs to be saved to subdirectories of the "Submarines" folder (e.g. "Submarines/Downloaded").
- Fixed item deterioration delay not resetting when the item is repaired if the condition of the item wasn't below the repair threshold when starting to repair it.
- Fixed bots failing to use welding tools inside their toolbox when fixing leaks.
- Items that appear broken to psychotic characters revert back to normal immediately when the psychosis is healed or when switching to another character.
- Fixed wires connected to a wifi component getting moved from signal_out to set_channel in old subs.
- Fixed plasma cutter beam not going through broken doors.
- Fixed extinguishers getting blocked by broken doors.
- The explosive cargo mission that places a block of Volatile Compound N in one of the crates no longer requires delivering the volatile block to the destination.
- Disabled crush depth in the submarine test mode.
---------------------------------------------------------------------------------------------------------
v0.9.1000.0 (Unstable)
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
Additions and changes: Additions and changes:
- Added 2 new moloch variants: Black Moloch and Baby Moloch. - Added 2 new moloch variants: Black Moloch and Moloch Pupa.
- Reworked Moloch.
- Overhauled level layouts and events (longer and more difficult levels).
- Added two new afflictions: medical items and poisons cause organ damage instead of internal damage and explosions cause deep tissue injuries. Both are functionally identical to internal damage, and treated with the same items.
- Added DXT5 texture compression to reduce memory consumption. Slightly increases loading times; if you're not short on memory, you may want to disable the compression from the game settings. - Added DXT5 texture compression to reduce memory consumption. Slightly increases loading times; if you're not short on memory, you may want to disable the compression from the game settings.
- Added partial dismemberment for live creatures. Currently enabled only for non-humanoids. (Dismembering dead bodies was already in the game).
- Destructible shells/armor -> Moloch's shell can now be destroyed.
- Added a new monster AI state: Protect.
- Increased the threshold for limping and changed the calculations.
- Added limping for non-human characters.
- Modded servers show up as purple in the server list. - Modded servers show up as purple in the server list.
- Added 4 new background music tracks. - Added 4 new background music tracks.
- Added parameter autocompletion to the "spawnsub" command. - Added parameter autocompletion to the "spawnsub" command.
@@ -58,12 +45,35 @@ Additions and changes:
- Distance calculations on the navigation terminal take the shape of the path into account instead of just using the direct distance to the target. - Distance calculations on the navigation terminal take the shape of the path into account instead of just using the direct distance to the target.
- Made improvements to the manual order assignment by adding always visible name labels, displaying indicators for characters' current orders, and repositioning the nodes. - Made improvements to the manual order assignment by adding always visible name labels, displaying indicators for characters' current orders, and repositioning the nodes.
- Reduced the damage range of fires, characters don't take damage from fires if there's a closed door or a wall in between. - Reduced the damage range of fires, characters don't take damage from fires if there's a closed door or a wall in between.
- Always draw steering indicators at the center of the display instead of the center of the sub. Fixes indicators getting offset (sometimes even outside the display) during docking.
- Added option to give all command perms with the "givecommandperm" command by using "all" as the parameter.
- Wrecks with no predefined Thalamus items can no more be infested by Thalamus. Allows to create wrecks that always spawn without Thalamus.
- The "shut down" reactor order now allows the bot to continue doing other things after powering off the reactor, instead of just standing next to the reactor.
- Removed the "initiative" skill -> all bots should now react better when there's something to do. Note: this doesn't mean that they always react on everything. It's just the end of individualism. At least for now.
- Reduced the physical forces applied on characters when they are hit by melee weapons, harpoons, or frag grenades. Adjusted stun for crowbar and harpoon.
- Disabled retreat/escape behavior for the bots when they take damage from items or explosions. They still escape/fight back when attacked by other characters.
- Refactor the medic priority calculations/logic: Bots should never treat others autonomously, unless they are medics or ordered to rescue. Bots should always give a high priority for treating themselves, unless there's a medic on board.
- Adjusted the flipping logic of non-humanoids to make them flip less frequently.
- Monsters won't anymore target nasonov artifact unless it is inside the player submarine or in the player inventory.
Modding: Modding:
- Made it possible to use repair tools with StatusEffect's UseItem. - Made it possible to use repair tools with StatusEffect's UseItem.
- Made pressure deaths more moddable. Dying because of high pressure isn't hard-coded anymore, the characters are just given the barotrauma affliction which (by default) kills them. - Made pressure deaths more moddable. Dying because of high pressure isn't hard-coded anymore, the characters are just given the barotrauma affliction which (by default) kills them.
- Added "HideConditionBar" property to items. - Added "HideConditionBar" property to items.
- Fixed wearables staying on the character when the item is removed by a status effect. - Fixed wearables staying on the character when the item is removed by a status effect.
- Character light sprites can now deform. The cells of Thalamus (Leucocyte and Terminal cell) now use the deformable light sprites.
- Creature flipping parameters are now exposed. Adjusted the flipping for all creatures.
- Allow to define character joints as weld joints in addition of normal revolute joints. Weld joints don't rotate.
- Allow sound definitions to ignore the muffling effect.
- Exposed the "scatter" value and added new "offset" attribute for monster events.
- Added support for status effects in limb definitions (ragdoll file).
- Status effects defined in the character definition can now also target limbs.
- Added ActionType.OnSevered status effect for limbs.
- Creatures can now be set to disrupt sonar.
- Player attacks can now also use the conditionals (i.e. when player is controlling a character). Previously only the AI used the conditionals.
- Conditional sprites don't anymore require a texture definition.
- Conditional Sprites can now be non-exclusive -> Draw more than just one sprite at a time.
- Fixed conditional sprites not being able to target limbs.
Bugfixes: Bugfixes:
- Fixed crashing when opening the tab menu when there are clients present with no job preferences set. - Fixed crashing when opening the tab menu when there are clients present with no job preferences set.
@@ -84,7 +94,7 @@ Bugfixes:
- Fixed rare "item with the same key has already been added" errors when starting a round (particularly when playing with a submarine with very large numbers of items/structures). - Fixed rare "item with the same key has already been added" errors when starting a round (particularly when playing with a submarine with very large numbers of items/structures).
- Thalamus entities can't be selected in the sub editor when they're hidden. - Thalamus entities can't be selected in the sub editor when they're hidden.
- Fixed "spawnsub" console command not working. - Fixed "spawnsub" console command not working.
- Fixed welding tools and plasma cutters hitting destroyed Thalamus organs. - Fixed projectiles, welding tools and plasma cutters hitting destroyed Thalamus organs.
- Fixed reactor not shutting down if the turbine/fission rate are controlled via signals even the power switch is toggled off. - Fixed reactor not shutting down if the turbine/fission rate are controlled via signals even the power switch is toggled off.
- Fixed reactor sliders not moving when they're controlled by signals. - Fixed reactor sliders not moving when they're controlled by signals.
- Fixed level triggers sometimes affecting entities that have left the trigger. The most noticeable effect was characters getting burn damage indefinitely after they've been close to a hydrothermal vent. - Fixed level triggers sometimes affecting entities that have left the trigger. The most noticeable effect was characters getting burn damage indefinitely after they've been close to a hydrothermal vent.
@@ -102,7 +112,7 @@ Bugfixes:
- Fixed depth charges going through level walls. - Fixed depth charges going through level walls.
- Fixed husks attacking human husks wearing a diving suit. - Fixed husks attacking human husks wearing a diving suit.
- Fixed first shot from a firearm that uses a magazine/clip not doing anything. - Fixed first shot from a firearm that uses a magazine/clip not doing anything.
- Fixes to waypoints in Kastrull and Remora. - Fixes to waypoints in Kastrull, Berilia and Remora.
- Fixed chat-linked wifi components not working in single player. - Fixed chat-linked wifi components not working in single player.
- Fixed chat-linked wifi components not working in multiplayer outside of combat missions. - Fixed chat-linked wifi components not working in multiplayer outside of combat missions.
- Fixed Azimuth using tutorial junction boxes instead of normal ones (the tutorial variants are indestructible and don't have signal connections). - Fixed Azimuth using tutorial junction boxes instead of normal ones (the tutorial variants are indestructible and don't have signal connections).
@@ -119,6 +129,23 @@ Bugfixes:
- Fixed "attempting to remove an already removed item" errors when mass-deleting items. Happened because removing items a wire is connected to removes the orphaned wires automatically. - Fixed "attempting to remove an already removed item" errors when mass-deleting items. Happened because removing items a wire is connected to removes the orphaned wires automatically.
- Fixed multi-part subs (example case: The Aeche III) getting teleported to oblivion when flipped. - Fixed multi-part subs (example case: The Aeche III) getting teleported to oblivion when flipped.
- Fixes to bots getting stuck or killed for no apparent reason when the player is very far from them, due to the bots switching to a "simple physics mode" which prevents them from doing certain kinds of interactions. - Fixes to bots getting stuck or killed for no apparent reason when the player is very far from them, due to the bots switching to a "simple physics mode" which prevents them from doing certain kinds of interactions.
- The explosive cargo mission that places a block of Volatile Compound N in one of the crates no longer requires delivering the volatile block to the destination.
- Disabled crush depth in the submarine test mode.
- Fixed monsters staying invisible if they die far away from the camera view.
- Fixed very small limbs (mudraptor's mouth tentacles, husk appendages) launching off at a very high velocity, leading to glitchy physics behavior, when hit by a non-raycast projectile or an explosion.
- Allow subs to be saved to subdirectories of the "Submarines" folder (e.g. "Submarines/Downloaded").
- Fixed a couple of waypoints in Berilia that prevented bots from using the ladders.
- Fixed Husked Crawler bleeding red blood.
- Fixed the priority of the operate order being 69 when it should be 70, which sometimes caused bots to get stuck between two objectives (like repairing or fixing leaks).
- Fixed enemies always using the priority defined for "room" when they should use the priority for "sonar".
- Fixed monsters not being able to drop down from platforms/hatches etc. In practice they still have difficulties in getting down from the hatches when they don't swim because they are so big.
- Fixed bots ignoring themselves as a target when they are rescuing others.
- Fixed numerous issues in the monster behavior when simple physics is enabled. e.g. Ignoring targets or not being able to attack or eat them.
- Fixed hitting damage modifiers emitting a ridiculous amount of particles.
- Fixed some cases where bots fail to open the door that they should be able to open (because they skipped a waypoint without checking the doors).
- Fixed monsters not keeping inside the level.
- Fixed a crash when the command interface button was pressed while the player is controlling a custom monster that didn't have character info but was able to speak.
- Fixed monsters sometimes ignoring their target after attacking.
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
v0.9.9.1 v0.9.9.1
@@ -160,21 +160,34 @@ namespace Steamworks
/// </summary> /// </summary>
public static int FileCount => Internal.GetFileCount(); public static int FileCount => Internal.GetFileCount();
/// <summary> public struct RemoteFile
/// Get a list of filenames synchronized by Steam Cloud
/// </summary>
public static IEnumerable<string> Files
{ {
get public string Filename;
public int Size;
public bool Delete()
{ {
int _ = 0; return Internal.FileDelete(Filename);
for( int i=0; i<FileCount; i++ )
{
var filename = Internal.GetFileNameAndSize( i, ref _ );
yield return filename;
}
} }
} }
/// <summary>
/// Get a list of filenames synchronized by Steam Cloud
/// </summary>
public static List<RemoteFile> Files
{
get
{
var ret = new List<RemoteFile>();
int count = FileCount;
for( int i=0; i<count; i++ )
{
int size = -1;
var filename = Internal.GetFileNameAndSize( i, ref size );
ret.Add(new RemoteFile { Filename = filename, Size = size });
}
return ret;
}
}
} }
} }
@@ -98,6 +98,13 @@ namespace Steamworks.Ugc
return this; return this;
} }
public bool HasTag( string tag )
{
if (Tags != null && Tags.Contains(tag)) { return true; }
return false;
}
public async Task<PublishResult> SubmitAsync( IProgress<float> progress = null ) public async Task<PublishResult> SubmitAsync( IProgress<float> progress = null )
{ {
var result = default( PublishResult ); var result = default( PublishResult );
@@ -8,8 +8,18 @@ namespace Microsoft.Xna.Framework.Graphics
{ {
public sealed partial class SamplerStateCollection public sealed partial class SamplerStateCollection
{ {
private int _d3dMaxDirty;
private int _d3dDirty; private int _d3dDirty;
partial void CalculateMaxDirty()
{
_d3dMaxDirty = 0;
for (var i = 0; i < _actualSamplers.Length; i++)
{
_d3dMaxDirty |= 1 << i;
}
}
private void PlatformSetSamplerState(int index) private void PlatformSetSamplerState(int index)
{ {
_d3dDirty |= 1 << index; _d3dDirty |= 1 << index;
@@ -17,12 +27,12 @@ namespace Microsoft.Xna.Framework.Graphics
private void PlatformClear() private void PlatformClear()
{ {
_d3dDirty = int.MaxValue; _d3dDirty = _d3dMaxDirty;
} }
private void PlatformDirty() private void PlatformDirty()
{ {
_d3dDirty = int.MaxValue; _d3dDirty = _d3dMaxDirty;
} }
internal void PlatformSetSamplers(GraphicsDevice device) internal void PlatformSetSamplers(GraphicsDevice device)
@@ -60,7 +70,8 @@ namespace Microsoft.Xna.Framework.Graphics
break; break;
} }
_d3dDirty = 0; if (_d3dDirty != 0) { throw new System.Exception($"SamplerStateCollection still dirty ({_d3dDirty})"); }
//_d3dDirty = 0;
} }
} }
} }
@@ -23,6 +23,8 @@ namespace Microsoft.Xna.Framework.Graphics
private readonly SamplerState[] _actualSamplers; private readonly SamplerState[] _actualSamplers;
private readonly bool _applyToVertexStage; private readonly bool _applyToVertexStage;
partial void CalculateMaxDirty();
internal SamplerStateCollection(GraphicsDevice device, int maxSamplers, bool applyToVertexStage) internal SamplerStateCollection(GraphicsDevice device, int maxSamplers, bool applyToVertexStage)
{ {
_graphicsDevice = device; _graphicsDevice = device;
@@ -38,7 +40,8 @@ namespace Microsoft.Xna.Framework.Graphics
_actualSamplers = new SamplerState[maxSamplers]; _actualSamplers = new SamplerState[maxSamplers];
_applyToVertexStage = applyToVertexStage; _applyToVertexStage = applyToVertexStage;
Clear(); CalculateMaxDirty();
Clear();
} }
public SamplerState this [int index] public SamplerState this [int index]
@@ -16,13 +16,15 @@ namespace Microsoft.Xna.Framework.Graphics
return; return;
if (_applyToVertexStage) if (_applyToVertexStage)
ClearTargets(targets, device._d3dContext.VertexShader); ClearTargets(targets, device, device._d3dContext.VertexShader);
else else
ClearTargets(targets, device._d3dContext.PixelShader); ClearTargets(targets, device, device._d3dContext.PixelShader);
} }
private void ClearTargets(RenderTargetBinding[] targets, SharpDX.Direct3D11.CommonShaderStage shaderStage) private void ClearTargets(RenderTargetBinding[] targets, GraphicsDevice device, SharpDX.Direct3D11.CommonShaderStage shaderStage)
{ {
PlatformSetTextures(device);
// NOTE: We make the assumption here that the caller has // NOTE: We make the assumption here that the caller has
// locked the d3dContext for us to use. // locked the d3dContext for us to use.
@@ -92,7 +94,8 @@ namespace Microsoft.Xna.Framework.Graphics
break; break;
} }
_dirty = 0; if (_dirty != 0) { throw new System.Exception($"TextureCollection still dirty ({_dirty})"); }
//_dirty = 0;
} }
} }
} }
@@ -12,13 +12,18 @@ namespace Microsoft.Xna.Framework.Graphics
private readonly Texture[] _textures; private readonly Texture[] _textures;
private readonly bool _applyToVertexStage; private readonly bool _applyToVertexStage;
private int _dirty; private int _dirty;
private int _dirtyMax;
internal TextureCollection(GraphicsDevice graphicsDevice, int maxTextures, bool applyToVertexStage) internal TextureCollection(GraphicsDevice graphicsDevice, int maxTextures, bool applyToVertexStage)
{ {
_graphicsDevice = graphicsDevice; _graphicsDevice = graphicsDevice;
_textures = new Texture[maxTextures]; _textures = new Texture[maxTextures];
_applyToVertexStage = applyToVertexStage; _applyToVertexStage = applyToVertexStage;
_dirty = int.MaxValue; for (int i=0;i<maxTextures;i++)
{
_dirtyMax |= 1 << i;
}
_dirty = _dirtyMax;
PlatformInit(); PlatformInit();
} }
@@ -47,7 +52,7 @@ namespace Microsoft.Xna.Framework.Graphics
_textures[i] = null; _textures[i] = null;
PlatformClear(); PlatformClear();
_dirty = int.MaxValue; _dirty = _dirtyMax;
} }
/// <summary> /// <summary>
@@ -55,7 +60,7 @@ namespace Microsoft.Xna.Framework.Graphics
/// </summary> /// </summary>
internal void Dirty() internal void Dirty()
{ {
_dirty = int.MaxValue; _dirty = _dirtyMax;
} }
internal void SetTextures(GraphicsDevice device) internal void SetTextures(GraphicsDevice device)